samod_core/
automerge_url.rs

1use std::str::FromStr;
2
3use automerge as am;
4
5use crate::DocumentId;
6
7#[derive(Clone, PartialEq, Eq)]
8pub struct AutomergeUrl {
9    document_id: DocumentId,
10    path: Option<Vec<am::Prop>>,
11}
12
13impl AutomergeUrl {
14    pub fn document_id(&self) -> &DocumentId {
15        &self.document_id
16    }
17}
18
19impl From<&DocumentId> for AutomergeUrl {
20    fn from(id: &DocumentId) -> Self {
21        AutomergeUrl {
22            document_id: id.clone(),
23            path: None,
24        }
25    }
26}
27
28impl std::fmt::Display for AutomergeUrl {
29    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30        write!(f, "automerge:{}", self.document_id)?;
31        if let Some(path) = &self.path {
32            for prop in path {
33                match prop {
34                    am::Prop::Seq(i) => write!(f, "/{i}")?,
35                    am::Prop::Map(s) => write!(f, "/{s}")?,
36                }
37            }
38        }
39        Ok(())
40    }
41}
42
43impl std::fmt::Debug for AutomergeUrl {
44    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45        write!(f, "AutomergeUrl({self})")
46    }
47}
48
49impl FromStr for AutomergeUrl {
50    type Err = InvalidUrlError;
51
52    fn from_str(s: &str) -> Result<Self, Self::Err> {
53        let Some(suffix) = s.strip_prefix("automerge:") else {
54            return Err(InvalidUrlError(format!("invalid automerge url: {s}")));
55        };
56        let mut parts = suffix.split("/");
57        let Some(doc_id_part) = parts.next() else {
58            return Err(InvalidUrlError(format!("invalid automerge url: {s}")));
59        };
60        let Ok(id) = DocumentId::from_str(doc_id_part) else {
61            return Err(InvalidUrlError(format!("invalid automerge url: {s}")));
62        };
63        let props = parts
64            .map(|p| {
65                if let Ok(i) = usize::from_str(p) {
66                    am::Prop::Seq(i)
67                } else {
68                    am::Prop::Map(p.to_string())
69                }
70            })
71            .collect::<Vec<_>>();
72        Ok(AutomergeUrl {
73            document_id: id,
74            path: if props.is_empty() { None } else { Some(props) },
75        })
76    }
77}
78
79pub struct InvalidUrlError(String);
80
81impl std::fmt::Display for InvalidUrlError {
82    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83        write!(f, "Invalid Automerge URL: {}", self.0)
84    }
85}
86
87impl std::fmt::Debug for InvalidUrlError {
88    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89        write!(f, "InvalidUrlError({})", self.0)
90    }
91}
92
93impl std::error::Error for InvalidUrlError {}