tide_disco/
dispatch.rs

1use itertools::Itertools;
2use snafu::Snafu;
3use std::{
4    collections::{btree_map::Entry, BTreeMap},
5    ops::Index,
6};
7
8pub use crate::join;
9
10#[derive(Clone, Debug, PartialEq, Eq)]
11pub(crate) struct Module<Api> {
12    pub(crate) prefix: Vec<String>,
13    pub(crate) versions: BTreeMap<u64, Api>,
14}
15
16impl<Api> Module<Api> {
17    fn new(prefix: Vec<String>) -> Self {
18        Self {
19            prefix,
20            versions: Default::default(),
21        }
22    }
23
24    pub(crate) fn path(&self) -> String {
25        self.prefix.join("/")
26    }
27}
28
29#[derive(Clone, Debug, Snafu, PartialEq, Eq)]
30pub enum DispatchError {
31    #[snafu(display("duplicate module {prefix} v{version}"))]
32    ModuleAlreadyExists { prefix: String, version: u64 },
33    #[snafu(display("module {prefix} cannot be a prefix of module {conflict}"))]
34    ConflictingModules { prefix: String, conflict: String },
35}
36
37/// Mapping from route prefixes to APIs.
38#[derive(Debug)]
39pub(crate) enum Trie<Api> {
40    Branch {
41        /// The route prefix represented by this node.
42        prefix: Vec<String>,
43        /// APIs with this prefix, indexed by the next route segment.
44        children: BTreeMap<String, Box<Self>>,
45    },
46    Leaf {
47        /// APIs available at this prefix, sorted by version.
48        module: Module<Api>,
49    },
50}
51
52impl<Api> Default for Trie<Api> {
53    fn default() -> Self {
54        Self::Branch {
55            prefix: vec![],
56            children: Default::default(),
57        }
58    }
59}
60
61impl<Api> Trie<Api> {
62    /// Whether this is a singleton [`Trie`].
63    ///
64    /// A singleton [`Trie`] is one with only one module, registered under the empty prefix. Note
65    /// that any [`Trie`] with a module with an empty prefix must be singleton, because no other
66    /// modules would be permitted: the empty prefix is a prefix of every other module path.
67    pub(crate) fn is_singleton(&self) -> bool {
68        matches!(self, Self::Leaf { .. })
69    }
70
71    /// Insert a new API with a certain version under the given prefix.
72    pub(crate) fn insert<I>(
73        &mut self,
74        prefix: I,
75        version: u64,
76        api: Api,
77    ) -> Result<(), DispatchError>
78    where
79        I: IntoIterator,
80        I::Item: Into<String>,
81    {
82        let mut prefix = prefix.into_iter().map(|segment| segment.into());
83
84        // Traverse to a leaf matching `prefix`.
85        let mut curr = self;
86        while let Some(segment) = prefix.next() {
87            // If there are more segments in the prefix, we must be at a branch.
88            match curr {
89                Self::Branch { prefix, children } => {
90                    // Move to the child associated with the next path segment, inserting an empty
91                    // child if this is the first module we've seen that has this path as a prefix.
92                    curr = children.entry(segment.clone()).or_insert_with(|| {
93                        let mut prefix = prefix.clone();
94                        prefix.push(segment);
95                        Box::new(Trie::Branch {
96                            prefix,
97                            children: Default::default(),
98                        })
99                    });
100                }
101                Self::Leaf { module } => {
102                    // If there is a leaf here, then there is already a module registered which is a
103                    // prefix of the new module. This is not allowed.
104                    return Err(DispatchError::ConflictingModules {
105                        prefix: module.path(),
106                        conflict: join!(&module.path(), &segment, &prefix.join("/")),
107                    });
108                }
109            }
110        }
111
112        // If we have reached the end of the prefix, we must be at either a leaf or a temporary
113        // empty branch that we can turn into a leaf.
114        if let Self::Branch { prefix, children } = curr {
115            if children.is_empty() {
116                *curr = Self::Leaf {
117                    module: Module::new(prefix.clone()),
118                };
119            } else {
120                // If we have a non-trival branch at the end of the desired prefix, there is already
121                // a module registered for which `prefix` is a strict prefix of the registered path.
122                // This is not allowed. To give a useful error message, follow the existing trie
123                // down to a leaf so we can give an example of a module which conflicts with this
124                // prefix.
125                let prefix = prefix.join("/");
126                let conflict = loop {
127                    match curr {
128                        Self::Branch { children, .. } => {
129                            curr = children
130                                .values_mut()
131                                .next()
132                                .expect("malformed dispatch trie: empty branch");
133                        }
134                        Self::Leaf { module } => {
135                            break module.path();
136                        }
137                    }
138                };
139                return Err(DispatchError::ConflictingModules { prefix, conflict });
140            }
141        }
142        let Self::Leaf { module } = curr else {
143            unreachable!();
144        };
145
146        // Insert the new API, as long as there isn't already an API with the same version in this
147        // module.
148        let Entry::Vacant(e) = module.versions.entry(version) else {
149            return Err(DispatchError::ModuleAlreadyExists {
150                prefix: module.path(),
151                version,
152            });
153        };
154        e.insert(api);
155        Ok(())
156    }
157
158    /// Get the module named by `prefix`.
159    ///
160    /// This function is similar to [`search`](Self::search), except the given `prefix` must exactly
161    /// match the prefix under which a module is registered.
162    pub(crate) fn get<I>(&self, prefix: I) -> Option<&Module<Api>>
163    where
164        I: IntoIterator,
165        I::Item: AsRef<str>,
166    {
167        let mut iter = prefix.into_iter();
168        let module = self.traverse(&mut iter)?;
169        // Check for exact match.
170        if iter.next().is_some() {
171            None
172        } else {
173            Some(module)
174        }
175    }
176
177    /// Get the supported versions of the API identified by the given request path.
178    ///
179    /// If a prefix of `path` uniquely identifies a registered module, the module (with all
180    /// supported versions) is returned.
181    pub(crate) fn search<I>(&self, path: I) -> Option<&Module<Api>>
182    where
183        I: IntoIterator,
184        I::Item: AsRef<str>,
185    {
186        self.traverse(&mut path.into_iter())
187    }
188
189    /// Iterate over registered modules and their supported versions.
190    pub(crate) fn iter(&self) -> Iter<'_, Api> {
191        Iter { stack: vec![self] }
192    }
193
194    /// Internal implementation of `get` and `search`.
195    ///
196    /// Returns the matching module and advances the iterator past all the segments used in the
197    /// match.
198    fn traverse<I>(&self, iter: &mut I) -> Option<&Module<Api>>
199    where
200        I: Iterator,
201        I::Item: AsRef<str>,
202    {
203        let mut curr = self;
204        loop {
205            match curr {
206                Self::Branch { children, .. } => {
207                    // Traverse to the next child based on the next segment in the path.
208                    let segment = iter.next()?;
209                    curr = children.get(segment.as_ref())?;
210                }
211                Self::Leaf { module } => return Some(module),
212            }
213        }
214    }
215}
216
217pub(crate) struct Iter<'a, Api> {
218    stack: Vec<&'a Trie<Api>>,
219}
220
221impl<'a, Api> Iterator for Iter<'a, Api> {
222    type Item = &'a Module<Api>;
223
224    fn next(&mut self) -> Option<Self::Item> {
225        loop {
226            match self.stack.pop()? {
227                Trie::Branch { children, .. } => {
228                    // Push children onto the stack and start visiting them. We add them in reverse
229                    // order so that we will visit the lexicographically first children first.
230                    self.stack
231                        .extend(children.values().rev().map(|boxed| &**boxed));
232                }
233                Trie::Leaf { module } => return Some(module),
234            }
235        }
236    }
237}
238
239impl<'a, Api> IntoIterator for &'a Trie<Api> {
240    type IntoIter = Iter<'a, Api>;
241    type Item = &'a Module<Api>;
242
243    fn into_iter(self) -> Self::IntoIter {
244        self.iter()
245    }
246}
247
248impl<I, Api> Index<I> for Trie<Api>
249where
250    I: IntoIterator,
251    I::Item: AsRef<str>,
252{
253    type Output = Module<Api>;
254
255    fn index(&self, index: I) -> &Self::Output {
256        self.get(index).unwrap()
257    }
258}
259
260/// Split a path prefix into its segments.
261///
262/// Leading and trailing slashes are ignored. That is, `/prefix/` yields only the single segment
263/// `prefix`, with no preceding or following empty segments.
264pub(crate) fn split(s: &str) -> impl '_ + Iterator<Item = &str> {
265    s.split('/').filter(|seg| !seg.is_empty())
266}
267
268/// Join two path strings, ensuring there are no leading or trailing slashes.
269pub(crate) fn join(s1: &str, s2: &str) -> String {
270    let s1 = s1.strip_prefix('/').unwrap_or(s1);
271    let s1 = s1.strip_suffix('/').unwrap_or(s1);
272    let s2 = s2.strip_prefix('/').unwrap_or(s2);
273    let s2 = s2.strip_suffix('/').unwrap_or(s2);
274    if s1.is_empty() {
275        s2.to_string()
276    } else if s2.is_empty() {
277        s1.to_string()
278    } else {
279        format!("{s1}/{s2}")
280    }
281}
282
283#[macro_export]
284macro_rules! join {
285    () => { String::new() };
286    ($s:expr) => { $s };
287    ($head:expr$(, $($tail:expr),*)?) => {
288        $crate::dispatch::join($head, &$crate::join!($($($tail),*)?))
289    }
290}
291
292#[cfg(test)]
293mod test {
294    use super::*;
295
296    #[test]
297    fn test_empty_trie() {
298        let t = Trie::<()>::default();
299        assert_eq!(t.iter().next(), None);
300        assert_eq!(t.get(["mod"]), None);
301    }
302
303    #[test]
304    fn test_branch_trie() {
305        let mut t = Trie::default();
306
307        let mod_a = Module {
308            prefix: vec!["mod".into(), "a".into()],
309            versions: [(0, 0)].into(),
310        };
311        let mod_b = Module {
312            prefix: vec!["mod".into(), "b".into()],
313            versions: [(1, 1)].into(),
314        };
315
316        t.insert(["mod", "a"], 0, 0).unwrap();
317        t.insert(["mod", "b"], 1, 1).unwrap();
318
319        assert_eq!(t.iter().collect::<Vec<_>>(), [&mod_a, &mod_b]);
320
321        assert_eq!(t.search(["mod", "a", "route"]), Some(&mod_a));
322        assert_eq!(t.get(["mod", "a"]), Some(&mod_a));
323        assert_eq!(t.get(["mod", "a", "route"]), None);
324
325        assert_eq!(t.search(["mod", "b", "route"]), Some(&mod_b));
326        assert_eq!(t.get(["mod", "b"]), Some(&mod_b));
327        assert_eq!(t.get(["mod", "b", "route"]), None);
328
329        // Cannot register a module which is a prefix or suffix of the already registered modules.
330        t.insert(["mod"], 0, 0).unwrap_err();
331        t.insert(Vec::<String>::new(), 0, 0).unwrap_err();
332        t.insert(["mod", "a", "b"], 0, 0).unwrap_err();
333    }
334
335    #[test]
336    fn test_null_prefix() {
337        let mut t = Trie::default();
338
339        let module = Module {
340            prefix: vec![],
341            versions: [(0, 0)].into(),
342        };
343        t.insert(Vec::<String>::new(), 0, 0).unwrap();
344
345        assert_eq!(t.iter().collect::<Vec<_>>(), [&module]);
346        assert_eq!(t.search(["anything"]), Some(&module));
347        assert_eq!(t.get(Vec::<String>::new()), Some(&module));
348        assert_eq!(t.get(["anything"]), None);
349
350        // Any other module has the null module as a prefix and is thus not allowed.
351        t.insert(["anything"], 1, 1).unwrap_err();
352    }
353}