webmachine_rust/
paths.rs

1//! Utilities for matching URI paths
2
3use itertools::EitherOrBoth::{Both, Left};
4use itertools::Itertools;
5
6/// Maps a request path against a template path, populating any variables from the template.
7/// Returns None if the paths don't match.
8pub fn map_path(path: &str, path_template: &str) -> Option<Vec<(String, Option<String>)>> {
9  if path.is_empty() || path_template.is_empty() {
10    return None;
11  }
12
13  let path_in = path.split('/').filter(|part| !part.is_empty()).collect_vec();
14  let path_template = path_template.split('/').filter(|part| !part.is_empty()).collect_vec();
15  if path_in.len() >= path_template.len() {
16    let mut path_map = vec![];
17    for item in path_in.iter().zip_longest(path_template) {
18      if let Both(a, b) = item {
19        if b.starts_with('{') && b.ends_with('}') {
20          path_map.push((a.to_string(), Some(b[1..(b.len() - 1)].to_string())));
21        } else if *a == b {
22          path_map.push((a.to_string(), None));
23        } else {
24          return None
25        }
26      } else if let Left(a) = item {
27        path_map.push((a.to_string(), None));
28      } else {
29        return None
30      }
31    }
32    Some(path_map)
33  } else {
34    None
35  }
36}
37
38#[cfg(test)]
39mod tests {
40  use expectest::prelude::*;
41
42  use crate::paths::map_path;
43
44  #[test]
45  fn map_path_simple_values() {
46    expect!(map_path("", "")).to(be_none());
47    expect!(map_path("/", "/")).to(be_equal_to(Some(vec![])));
48    expect!(map_path("/a", "/a")).to(be_equal_to(Some(vec![("a".to_string(), None)])));
49    expect!(map_path("/a", "/a")).to(be_equal_to(Some(vec![("a".to_string(), None)])));
50    expect!(map_path("/a/", "/a")).to(be_equal_to(Some(vec![("a".to_string(), None)])));
51    expect!(map_path("/a/b", "/a/b")).to(be_equal_to(Some(vec![("a".to_string(), None),
52      ("b".to_string(), None)])));
53    expect!(map_path("/a/b/c", "/a/b/c")).to(be_equal_to(Some(vec![("a".to_string(), None),
54      ("b".to_string(), None), ("c".to_string(), None)])));
55
56    expect!(map_path("", "/")).to(be_none());
57    expect!(map_path("/", "")).to(be_none());
58    expect!(map_path("/", "/a")).to(be_none());
59    expect!(map_path("/a", "/")).to(be_some().value(vec![("a".to_string(), None)]));
60    expect!(map_path("/a/b", "/a")).to(be_some().value(vec![("a".to_string(), None), ("b".to_string(), None)]));
61    expect!(map_path("/a/b", "/a/b/c")).to(be_none());
62  }
63
64  #[test]
65  fn map_path_with_variables() {
66    expect!(map_path("/a", "/{id}")).to(be_equal_to(Some(vec![("a".to_string(), Some("id".to_string()))])));
67    expect!(map_path("/a/", "/{id}")).to(be_equal_to(Some(vec![("a".to_string(), Some("id".to_string()))])));
68    expect!(map_path("/a", "/{id}/")).to(be_equal_to(Some(vec![("a".to_string(), Some("id".to_string()))])));
69    expect!(map_path("/a/b", "/a/{id}")).to(be_equal_to(Some(vec![("a".to_string(), None),
70      ("b".to_string(), Some("id".to_string()))])));
71    expect!(map_path("/a/b", "/{id}/b")).to(be_equal_to(Some(vec![("a".to_string(), Some("id".to_string())),
72      ("b".to_string(), None)])));
73    expect!(map_path("/a/b", "/{id}/{id}")).to(be_equal_to(Some(vec![("a".to_string(), Some("id".to_string())),
74      ("b".to_string(), Some("id".to_string()))])));
75    expect!(map_path("/a/b/c", "/a/{b}/c")).to(be_equal_to(Some(vec![("a".to_string(), None),
76      ("b".to_string(), Some("b".to_string())), ("c".to_string(), None)])));
77
78    expect!(map_path("/", "/{id}")).to(be_none());
79    expect!(map_path("/a/b", "/{id}")).to(be_some().value(vec![
80      ("a".to_string(), Some("id".to_string())),
81      ("b".to_string(), None)
82    ]));
83    expect!(map_path("/a", "/{id}/b")).to(be_none());
84    expect!(map_path("/a", "/{id}/{id}")).to(be_none());
85    expect!(map_path("/a/b/c", "/{id}/{id}")).to(be_some().value(vec![
86      ("a".to_string(), Some("id".to_string())),
87      ("b".to_string(), Some("id".to_string())),
88      ("c".to_string(), None)
89    ]));
90  }
91}