~ knotless Search/
↑↓ move↵ open⇧↵ all results

johnandersen777.bsky.social/knotless

git that lives in your Atmosphere account

Forked from chadtmiller.com/knotless

0
0
68 lines · 2.3 KB Raw History
//! 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 `<details>` 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<Arc<App>> {
    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<String>,
}

/// `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<RenderInput>) -> Result<Response> {
    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<String> {
    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://<authority>/{}/<name>",
            nsid::REPO
        ))),
    }
}