Skip to main content

mahler_core/json/
path.rs

1use jsonptr::{EncodingError, Pointer, PointerBuf, Token};
2use std::cmp::Ordering;
3use std::fmt::Display;
4use std::ops::{Deref, DerefMut};
5use std::sync::Arc;
6
7use crate::serde::{Serialize, Serializer};
8
9#[derive(Clone, Default, PartialEq, Eq, Debug)]
10pub struct Path(PointerBuf);
11
12impl Serialize for Path {
13    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
14    where
15        S: Serializer,
16    {
17        self.0.serialize(serializer)
18    }
19}
20
21impl PartialOrd for Path {
22    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
23        Some(self.cmp(other))
24    }
25}
26
27impl Ord for Path {
28    fn cmp(&self, other: &Self) -> Ordering {
29        // Compare first by path length (number of tokens)
30        // shorter paths come before longer paths no matter the
31        // lexicographic order
32        self.0
33            .count()
34            .cmp(&other.0.count())
35            // Then lexicographically
36            .then(self.0.cmp(&other.0))
37    }
38}
39impl Path {
40    pub fn new(pointer: &Pointer) -> Self {
41        Self(pointer.to_buf())
42    }
43
44    /// Create a static path from a string
45    ///
46    /// # Panics
47    ///
48    /// This will panic if the string is not a valid path
49    pub fn from_static(s: &'static str) -> Self {
50        Path(Pointer::from_static(s).to_buf())
51    }
52
53    /// Get the internal string representation for the Path
54    pub fn as_str(&self) -> &str {
55        self.0.as_str()
56    }
57
58    /// Returns true if this path is a prefix of the other path.
59    /// A path is a prefix of another if all its tokens match the beginning of the other's tokens.
60    /// For example, "/a" is a prefix of "/a/b", but "/a" is NOT a prefix of "/aa".
61    pub fn is_prefix_of(&self, other: &Path) -> bool {
62        let self_tokens: Vec<_> = self.0.tokens().collect();
63        let other_tokens: Vec<_> = other.0.tokens().collect();
64
65        // Empty path is a prefix of everything
66        if self_tokens.is_empty() {
67            return true;
68        }
69
70        // Can't be a prefix if we have more tokens
71        if self_tokens.len() > other_tokens.len() {
72            return false;
73        }
74
75        // Check if all our tokens match the beginning of other's tokens
76        self_tokens
77            .iter()
78            .zip(other_tokens.iter())
79            .all(|(a, b)| a == b)
80    }
81}
82
83impl Display for Path {
84    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85        write!(f, "{}", self.0)
86    }
87}
88
89impl From<Path> for String {
90    fn from(path: Path) -> String {
91        path.0.to_string()
92    }
93}
94
95impl From<Path> for PointerBuf {
96    fn from(path: Path) -> Self {
97        path.0
98    }
99}
100
101impl AsRef<Pointer> for Path {
102    fn as_ref(&self) -> &Pointer {
103        &self.0
104    }
105}
106
107impl Deref for Path {
108    type Target = str;
109
110    fn deref(&self) -> &Self::Target {
111        self.as_str()
112    }
113}
114
115/// Structure to store path arguments that can be matched
116/// against a route
117#[derive(Clone, Default, Debug, PartialEq, Eq)]
118pub struct PathArgs(Vec<(Arc<str>, String)>);
119
120impl PathArgs {
121    pub fn insert(&mut self, key: impl AsRef<str>, value: impl Into<String>) -> Option<String> {
122        let existing = self.0.iter_mut().find(|(k, _)| k.as_ref() == key.as_ref());
123        if let Some((_, v)) = existing {
124            let old = v.clone();
125            *v = value.into();
126            return Some(old);
127        } else {
128            self.0.push((Arc::from(key.as_ref()), value.into()))
129        }
130        None
131    }
132
133    pub fn contains_key(&self, key: impl AsRef<str>) -> bool {
134        self.0.iter().any(|(k, _)| k.as_ref() == key.as_ref())
135    }
136
137    /// Try to decode args assuming RFC 6901 JSON encoding
138    pub fn try_as_decoded(&self) -> Result<PathArgs, EncodingError> {
139        let items: Result<Vec<(&str, String)>, EncodingError> = self
140            .iter()
141            .map(|(key, value)| {
142                let decoded = Token::from_encoded(value)?.decoded().to_string();
143                Ok((key.as_ref(), decoded))
144            })
145            .collect();
146
147        items.map(PathArgs::from)
148    }
149}
150
151impl Display for PathArgs {
152    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153        let entries = self
154            .0
155            .iter()
156            .map(|(key, value)| format!("\"{key}\": \"{value}\""))
157            .collect::<Vec<_>>()
158            .join(", ");
159
160        write!(f, "{{{entries}}}")
161    }
162}
163
164impl Deref for PathArgs {
165    type Target = Vec<(Arc<str>, String)>;
166
167    fn deref(&self) -> &Self::Target {
168        &self.0
169    }
170}
171
172impl DerefMut for PathArgs {
173    fn deref_mut(&mut self) -> &mut Self::Target {
174        &mut self.0
175    }
176}
177
178impl From<matchit::Params<'_, '_>> for PathArgs {
179    fn from(params: matchit::Params) -> PathArgs {
180        let params: Vec<(Arc<str>, String)> = params
181            .iter()
182            .map(|(k, v)| (Arc::from(k), String::from(v)))
183            .collect();
184
185        PathArgs(params)
186    }
187}
188
189impl<K: AsRef<str>, V: Into<String>> From<Vec<(K, V)>> for PathArgs {
190    fn from(args: Vec<(K, V)>) -> Self {
191        PathArgs(
192            args.into_iter()
193                .map(|(k, v)| (Arc::from(k.as_ref()), v.into()))
194                .collect(),
195        )
196    }
197}
198
199#[cfg(test)]
200mod tests {
201    use super::*;
202
203    #[test]
204    fn it_sorts_path_by_length_and_then_lexicographically() {
205        let mut paths = vec![
206            Path::from_static("/a/b/c"),
207            Path::from_static(""),
208            Path::from_static("/a/b"),
209            Path::from_static("/"),
210            Path::from_static("/a/f"),
211            Path::from_static("/x"),
212            Path::from_static("/a/b/d"),
213        ];
214
215        paths.sort();
216        assert_eq!(
217            paths,
218            vec![
219                Path::from_static(""),
220                Path::from_static("/"),
221                Path::from_static("/x"),
222                Path::from_static("/a/b"),
223                Path::from_static("/a/f"),
224                Path::from_static("/a/b/c"),
225                Path::from_static("/a/b/d"),
226            ]
227        )
228    }
229
230    #[test]
231    fn it_converts_a_path_to_string() {
232        assert_eq!(Path::from_static("/a/b/c").to_string(), "/a/b/c");
233        assert_eq!(Path::from_static("/").to_string(), "/");
234        assert_eq!(Path::from_static("").to_string(), "");
235    }
236
237    #[test]
238    fn it_converts_a_str_to_a_path() {
239        let path = Path::from_static("/a/b/c");
240        assert_eq!(String::from(path), "/a/b/c");
241    }
242
243    #[test]
244    #[should_panic]
245    fn it_panics_on_an_invalid_path() {
246        Path::from_static("a/b/c");
247    }
248
249    #[test]
250    fn it_replaces_existing_key_with_insert() {
251        let mut args = PathArgs::default();
252        args.insert("one", "a");
253        args.insert("two", "b");
254
255        assert_eq!(
256            args.0,
257            vec![
258                (Arc::from("one"), "a".to_string()),
259                (Arc::from("two"), "b".to_string())
260            ]
261        );
262
263        // Replace the key
264        let old = args.insert("one", "c");
265        assert_eq!(
266            args.0,
267            vec![
268                (Arc::from("one"), "c".to_string()),
269                (Arc::from("two"), "b".to_string())
270            ]
271        );
272        assert_eq!(old, Some("a".to_string()))
273    }
274
275    #[test]
276    fn test_is_prefix_of() {
277        // Basic prefix relationship
278        let path_a = Path::from_static("/a");
279        let path_a_b = Path::from_static("/a/b");
280        let path_a_b_c = Path::from_static("/a/b/c");
281        let path_aa = Path::from_static("/aa");
282        let path_b = Path::from_static("/b");
283        let root = Path::from_static("");
284
285        // True cases - proper path prefixes
286        assert!(path_a.is_prefix_of(&path_a_b));
287        assert!(path_a.is_prefix_of(&path_a_b_c));
288        assert!(path_a_b.is_prefix_of(&path_a_b_c));
289        assert!(root.is_prefix_of(&path_a));
290        assert!(root.is_prefix_of(&path_a_b));
291        assert!(path_a.is_prefix_of(&path_a)); // Self prefix
292
293        // False cases - not path prefixes
294        assert!(!path_a.is_prefix_of(&path_aa)); // "/a" is NOT a prefix of "/aa"
295        assert!(!path_a.is_prefix_of(&path_b));
296        assert!(!path_a_b.is_prefix_of(&path_a)); // Longer path cannot be prefix of shorter
297        assert!(!path_aa.is_prefix_of(&path_a));
298    }
299}