semora-cli 0.3.0

Semora CLI: publish Markdown and HTML, and hear back from the page
Documentation
//! Document references: a bare id, a page URL, or a websocket URL.

use anyhow::bail;

fn is_id(s: &str) -> bool {
    s.len() == 10 && s.bytes().all(|b| b.is_ascii_alphanumeric())
}

/// `k3F9xQ2mLp`, `https://semora/d/k3F9xQ2mLp/page.md`, `wss://semora/ws/d/k3F9xQ2mLp` → `k3F9xQ2mLp`.
pub fn parse(s: &str) -> anyhow::Result<String> {
    let s = s.trim();
    if is_id(s) {
        return Ok(s.to_string());
    }
    if let Ok(u) = url::Url::parse(s) {
        let segs: Vec<&str> = u.path_segments().map(|p| p.collect()).unwrap_or_default();
        for w in segs.windows(2) {
            if w[0] == "d" && is_id(w[1]) {
                return Ok(w[1].to_string());
            }
        }
    }
    bail!("`{s}` is not a document id or Semora URL")
}

#[cfg(test)]
mod tests {
    use super::parse;

    #[test]
    fn parses_ids_and_urls() {
        assert_eq!(parse("k3F9xQ2mLp").unwrap(), "k3F9xQ2mLp");
        assert_eq!(
            parse("https://semora.example.com/d/k3F9xQ2mLp/").unwrap(),
            "k3F9xQ2mLp"
        );
        assert_eq!(
            parse("https://semora.example.com/d/k3F9xQ2mLp/guide/a.md").unwrap(),
            "k3F9xQ2mLp"
        );
        assert_eq!(
            parse("wss://semora.example.com/ws/d/k3F9xQ2mLp").unwrap(),
            "k3F9xQ2mLp"
        );
        assert!(parse("nope").is_err());
        assert!(parse("https://x/d/short/").is_err());
    }
}