//! Markdown as the pages will show it once posted: //! `dev.knotless.renderMarkdown`, which a composer's preview asks. It is //! the same renderer the pages' comments, bodies and READMEs are drawn //! with, raw HTML shown as text but for `
` folds, and links //! kept to the web's and the repository's own, so what is previewed is //! what is shown. It reads nothing and fetches nothing: no login is //! needed, and the text it takes is no longer than a body may be. use super::repo::invalid; use super::*; /// The longest text it renders, in bytes: as long as a comment's, an /// issue's or a pull's body may be. const LONGEST: usize = 20_000; /// The most a request may carry: the text at its longest, each byte of it /// escaped in JSON as six, and the repository beside it. const BODY_LIMIT: usize = 8 * LONGEST; /// The preview the composers ask for. pub fn routes() -> Router> { Router::new().route( "/xrpc/dev.knotless.renderMarkdown", post(render_markdown).layer(axum::extract::DefaultBodyLimit::max(BODY_LIMIT)), ) } #[derive(Deserialize)] pub struct RenderInput { text: String, repo: Option, } /// `dev.knotless.renderMarkdown`'s answer. #[derive(Serialize)] pub struct Rendered { html: String, } /// `dev.knotless.renderMarkdown`. pub async fn render_markdown(axum::Json(input): axum::Json) -> Result { if input.text.len() > LONGEST { let why = format!("text is longer than {LONGEST} bytes"); return Err(invalid(why)); } let base = match input.repo.as_deref() { Some(uri) => root(uri)?, None => String::new(), }; let html = written(&input.text, &base, None, &HashMap::new()); Ok(answer(Rendered { html })) } /// Where the pages show the repository whose record `uri` names, as its /// thread's page names it: what relative links in what is said there /// resolve under. Only the name is read; the repository is not looked up. fn root(uri: &str) -> Result { match knotless::aturi::AtUri::parse(uri) { Ok(at) if at.collection == nsid::REPO => { let (authority, name) = (url_path(&at.author), url_path(repo_name(&at.rkey))); Ok(format!("/{authority}/{name}")) } _ => Err(invalid(format!( "{uri} is not at:///{}/", nsid::REPO ))), } }