1#[derive(Debug, Clone, PartialEq)]
2pub enum PathElement {
3 Literal(String),
4 Placeholder(String),
5}
6
7impl From<&str> for PathElement {
8 fn from(value: &str) -> Self {
9 if value.starts_with('{') && value.ends_with('}') {
10 Self::Placeholder(value[1..value.len() - 1].to_string())
11 } else {
12 Self::Literal(value.to_string())
13 }
14 }
15}
16
17impl core::fmt::Display for PathElement {
18 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19 match self {
20 PathElement::Literal(l) => write!(f, "{l}"),
21 PathElement::Placeholder(p) => write!(f, "{{{p}}}"),
22 }
23 }
24}
25
26impl PathElement {
27 pub fn matches(&self, other: &Self) -> bool {
28 match (self, other) {
29 (Self::Placeholder(_), _) => true,
30 (Self::Literal(s), Self::Literal(o)) => s == o,
31 (Self::Literal(_), Self::Placeholder(_)) => false,
32 }
33 }
34
35 pub fn as_string_without_brackets(&self) -> &str {
36 match self {
37 PathElement::Literal(v) | PathElement::Placeholder(v) => &v,
38 }
39 }
40}
41
42#[derive(Debug, Clone, PartialEq)]
43pub struct Path {
44 elements: Vec<PathElement>,
45}
46
47impl core::fmt::Display for Path {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 if let Some(err) = self.elements.iter().find_map(|v| write!(f, "/{v}").err()) {
50 return Err(err);
51 }
52
53 Ok(())
54 }
55}
56
57impl TryFrom<&str> for Path {
58 type Error = ();
59
60 fn try_from(value: &str) -> Result<Self, ()> {
61 if !value.starts_with('/') {
62 return Err(());
63 }
64
65 let elements = value
66 .split('/')
67 .skip(1)
68 .map(|v| PathElement::from(v))
69 .collect();
70
71 Ok(Self { elements })
72 }
73}
74
75impl Path {
76 pub fn matches<'a>(&self, other: impl IntoIterator<Item = &'a str>) -> bool {
77 let mut elements = self.elements.iter();
78 let mut other = other.into_iter();
79 let mut zipped = (&mut elements).zip(&mut other);
80
81 for (l, r) in &mut zipped {
82 if let PathElement::Literal(literal) = l {
83 if literal != r {
84 return false;
85 }
86 }
87 }
88
89 other.next().is_none() && elements.next().is_none()
90 }
91
92 pub fn iter(&self) -> impl Iterator<Item = &'_ PathElement> + '_ {
93 self.elements.iter()
94 }
95}