ic_http_certification/tree/
certification_tree_path.rs1use crate::utils::{
2 EXACT_PATH_TERMINATOR, EXACT_PATH_TERMINATOR_BYTES, PATH_PREFIX, WILDCARD_PATH_TERMINATOR,
3 WILDCARD_PATH_TERMINATOR_BYTES,
4};
5use std::borrow::Cow;
6
7pub(super) type CertificationTreePathSegment = Vec<u8>;
8pub(super) type InnerTreePath = Vec<CertificationTreePathSegment>;
9
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub(super) enum HttpCertificationPathType<'a> {
12 Exact(Cow<'a, str>),
13 Wildcard(Cow<'a, str>),
14}
15
16#[derive(Debug, Clone, PartialEq, Eq)]
28pub struct HttpCertificationPath<'a>(HttpCertificationPathType<'a>);
29
30impl<'a> HttpCertificationPath<'a> {
31 pub fn exact(path: impl Into<Cow<'a, str>>) -> Self {
35 Self(HttpCertificationPathType::Exact(path.into()))
36 }
37
38 pub fn wildcard(path: impl Into<Cow<'a, str>>) -> Self {
42 Self(HttpCertificationPathType::Wildcard(path.into()))
43 }
44
45 pub(super) fn to_tree_path(&self) -> InnerTreePath {
46 match &self.0 {
47 HttpCertificationPathType::Exact(path) => {
48 Self::path_to_segments(path.as_ref(), EXACT_PATH_TERMINATOR_BYTES)
49 }
50 HttpCertificationPathType::Wildcard(path) => {
51 Self::path_to_segments(path.as_ref(), WILDCARD_PATH_TERMINATOR_BYTES)
52 }
53 }
54 }
55
56 pub(super) fn get_type(&self) -> &HttpCertificationPathType<'a> {
57 &self.0
58 }
59
60 pub fn to_expr_path(&self) -> Vec<String> {
62 match &self.0 {
63 HttpCertificationPathType::Exact(path) => {
64 Self::path_to_string_segments(path.as_ref(), EXACT_PATH_TERMINATOR)
65 }
66 HttpCertificationPathType::Wildcard(path) => {
67 Self::path_to_string_segments(path.as_ref(), WILDCARD_PATH_TERMINATOR)
68 }
69 }
70 }
71
72 fn path_to_segments(path: &str, terminator: &[u8]) -> InnerTreePath {
73 let mut path_segments = path
74 .split('/')
75 .filter(|e| !e.is_empty())
76 .map(str::as_bytes)
77 .map(Vec::from)
78 .collect::<InnerTreePath>();
79 if path.ends_with('/') {
80 path_segments.push("".as_bytes().to_vec());
81 }
82
83 path_segments.push(terminator.to_vec());
84
85 path_segments
86 }
87
88 fn path_to_string_segments(path: &str, terminator: &str) -> Vec<String> {
89 let mut path_segments = vec![PATH_PREFIX.to_string()];
90 path_segments.append(
91 &mut path
92 .split('/')
93 .filter(|e| !e.is_empty())
94 .map(String::from)
95 .collect(),
96 );
97 if path.ends_with('/') {
98 path_segments.push("".to_string());
99 }
100
101 path_segments.push(terminator.to_string());
102
103 path_segments
104 }
105}
106
107impl<'a> From<HttpCertificationPath<'a>> for Cow<'a, HttpCertificationPath<'a>> {
108 fn from(path: HttpCertificationPath<'a>) -> Cow<'a, HttpCertificationPath<'a>> {
109 Cow::Owned(path)
110 }
111}
112
113impl<'a> From<&'a HttpCertificationPath<'a>> for Cow<'a, HttpCertificationPath<'a>> {
114 fn from(path: &'a HttpCertificationPath<'a>) -> Cow<'a, HttpCertificationPath<'a>> {
115 Cow::Borrowed(path)
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122 use rstest::*;
123 use rstest_reuse::*;
124
125 #[template]
126 #[rstest]
127 #[case("", vec!["<$>"])]
128 #[case("/", vec!["", "<$>"])]
129 #[case("/foo", vec!["foo", "<$>"])]
130 #[case("foo", vec!["foo", "<$>"])]
131 #[case("/foo/", vec!["foo", "", "<$>"])]
132 #[case("foo/", vec!["foo", "", "<$>"])]
133 #[case("/foo/bar", vec!["foo", "bar", "<$>"])]
134 #[case("foo/bar", vec!["foo", "bar", "<$>"])]
135 #[case("/foo/bar/", vec!["foo", "bar", "", "<$>"])]
136 #[case("foo/bar/", vec!["foo", "bar", "", "<$>"])]
137 fn exact_paths(#[case] path: &str, #[case] expected: Vec<&str>) {}
138
139 #[template]
140 #[rstest]
141 #[case("", vec!["<*>"])]
142 #[case("/", vec!["", "<*>"])]
143 #[case("/foo", vec!["foo", "<*>"])]
144 #[case("foo", vec!["foo", "<*>"])]
145 #[case("/foo/", vec!["foo", "", "<*>"])]
146 #[case("foo/", vec!["foo", "", "<*>"])]
147 #[case("/foo/bar", vec!["foo", "bar", "<*>"])]
148 #[case("foo/bar", vec!["foo", "bar", "<*>"])]
149 #[case("/foo/bar/", vec!["foo", "bar", "", "<*>"])]
150 #[case("foo/bar/", vec!["foo", "bar", "", "<*>"])]
151 fn wildcard_paths(#[case] path: &str, #[case] expected: Vec<&str>) {}
152
153 #[apply(exact_paths)]
154 fn exact_path_to_tree_path(#[case] path: &str, #[case] expected: Vec<&str>) {
155 let path = HttpCertificationPath::exact(path);
156
157 let result = path.to_tree_path();
158 let expected = expected
159 .iter()
160 .map(|segment| segment.as_bytes().to_vec())
161 .collect::<InnerTreePath>();
162
163 assert_eq!(result, expected);
164 }
165
166 #[apply(wildcard_paths)]
167 fn wildcard_path_to_tree_path(#[case] path: &str, #[case] expected: Vec<&str>) {
168 let path = HttpCertificationPath::wildcard(path);
169
170 let result = path.to_tree_path();
171 let expected = expected
172 .iter()
173 .map(|segment| segment.as_bytes().to_vec())
174 .collect::<InnerTreePath>();
175
176 assert_eq!(result, expected);
177 }
178
179 #[apply(exact_paths)]
180 fn exact_path_to_expr_path(#[case] path: &str, #[case] expected: Vec<&str>) {
181 let path = HttpCertificationPath::exact(path);
182
183 let result = path.to_expr_path();
184 let expected = [PATH_PREFIX]
185 .iter()
186 .chain(expected.iter())
187 .map(|segment| segment.to_string())
188 .collect::<Vec<_>>();
189
190 assert_eq!(result, expected);
191 }
192
193 #[apply(wildcard_paths)]
194 fn wildcard_path_to_expr_path(#[case] path: &str, #[case] expected: Vec<&str>) {
195 let path = HttpCertificationPath::wildcard(path);
196
197 let result = path.to_expr_path();
198 let expected = [PATH_PREFIX]
199 .iter()
200 .chain(expected.iter())
201 .map(|segment| segment.to_string())
202 .collect::<Vec<_>>();
203
204 assert_eq!(result, expected);
205 }
206}