Skip to main content

semora_cli/
docref.rs

1//! Document references: a bare id, a page URL, or a websocket URL.
2
3use anyhow::bail;
4
5fn is_id(s: &str) -> bool {
6    s.len() == 10 && s.bytes().all(|b| b.is_ascii_alphanumeric())
7}
8
9/// `k3F9xQ2mLp`, `https://semora/d/k3F9xQ2mLp/page.md`, `wss://semora/ws/d/k3F9xQ2mLp` → `k3F9xQ2mLp`.
10pub fn parse(s: &str) -> anyhow::Result<String> {
11    let s = s.trim();
12    if is_id(s) {
13        return Ok(s.to_string());
14    }
15    if let Ok(u) = url::Url::parse(s) {
16        let segs: Vec<&str> = u.path_segments().map(|p| p.collect()).unwrap_or_default();
17        for w in segs.windows(2) {
18            if w[0] == "d" && is_id(w[1]) {
19                return Ok(w[1].to_string());
20            }
21        }
22    }
23    bail!("`{s}` is not a document id or Semora URL")
24}
25
26#[cfg(test)]
27mod tests {
28    use super::parse;
29
30    #[test]
31    fn parses_ids_and_urls() {
32        assert_eq!(parse("k3F9xQ2mLp").unwrap(), "k3F9xQ2mLp");
33        assert_eq!(
34            parse("https://semora.example.com/d/k3F9xQ2mLp/").unwrap(),
35            "k3F9xQ2mLp"
36        );
37        assert_eq!(
38            parse("https://semora.example.com/d/k3F9xQ2mLp/guide/a.md").unwrap(),
39            "k3F9xQ2mLp"
40        );
41        assert_eq!(
42            parse("wss://semora.example.com/ws/d/k3F9xQ2mLp").unwrap(),
43            "k3F9xQ2mLp"
44        );
45        assert!(parse("nope").is_err());
46        assert!(parse("https://x/d/short/").is_err());
47    }
48}