1use std::fmt::{Display, Formatter};
2
3use crate::parse::Error;
4use crate::parse::Error::*;
5
6#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
14pub struct Path<'a> {
15 path: &'a str,
16}
17
18impl Default for Path<'static> {
19 fn default() -> Self {
20 Self { path: "/" }
21 }
22}
23
24impl<'a> Path<'a> {
25 pub unsafe fn new(path: &'a str) -> Self {
32 debug_assert!(Self::is_valid(path));
33
34 Self { path }
35 }
36}
37
38impl<'a> TryFrom<&'a str> for Path<'a> {
39 type Error = Error;
40
41 fn try_from(path: &'a str) -> Result<Self, Self::Error> {
42 if Self::is_valid(path) {
43 Ok(Self { path })
44 } else {
45 Err(InvalidPath)
46 }
47 }
48}
49
50impl<'a> Path<'a> {
51 fn is_valid_char(c: u8) -> bool {
55 c.is_ascii_alphanumeric() || (c.is_ascii_punctuation() && c != b'?' && c != b'#')
56 }
57
58 pub fn is_valid(path: &str) -> bool {
60 !path.is_empty()
61 && path.as_bytes()[0] == b'/'
62 && path.as_bytes()[1..].iter().all(|c| Self::is_valid_char(*c))
63 }
64}
65
66impl<'a> Path<'a> {
67 pub const fn as_str(&self) -> &str {
71 self.path
72 }
73}
74
75impl<'a> AsRef<str> for Path<'a> {
76 fn as_ref(&self) -> &str {
77 self.path
78 }
79}
80
81impl<'a> Display for Path<'a> {
82 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
83 write!(f, "{}", self.path)
84 }
85}
86
87impl<'a> Path<'a> {
88 pub const fn iter_segments(&self) -> impl Iterator<Item = &'a str> {
95 SegmentIterator {
96 remaining: self.path,
97 }
98 }
99}
100
101struct SegmentIterator<'a> {
102 remaining: &'a str,
103}
104
105impl<'a> Iterator for SegmentIterator<'a> {
106 type Item = &'a str;
107
108 fn next(&mut self) -> Option<Self::Item> {
109 if self.remaining.is_empty() {
110 None
111 } else {
112 self.remaining = &self.remaining[1..];
113 if let Some(slash) = self.remaining.as_bytes().iter().position(|c| *c == b'/') {
114 let segment: &str = &self.remaining[..slash];
115 self.remaining = &self.remaining[slash..];
116 Some(segment)
117 } else {
118 let segment: &str = self.remaining;
119 self.remaining = "";
120 Some(segment)
121 }
122 }
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use crate::Path;
129
130 #[test]
131 fn new() {
132 let path: Path = unsafe { Path::new("/the/path") };
133 assert_eq!(path.path, "/the/path");
134 }
135
136 #[test]
137 fn is_valid() {
138 let test_cases: &[(&str, bool)] = &[
139 ("", false),
140 ("/", true),
141 ("///", true),
142 ("/azAZ09", true),
143 ("/!/&/=/~/", true),
144 ("/?", false),
145 ("/#", false),
146 ];
147 for (path, expected) in test_cases {
148 let result: bool = Path::is_valid(path);
149 assert_eq!(result, *expected, "path={}", path);
150 }
151 }
152
153 #[test]
154 fn display() {
155 let path: Path = unsafe { Path::new("/the/path") };
156 assert_eq!(path.as_str(), "/the/path");
157 assert_eq!(path.as_ref(), "/the/path");
158 assert_eq!(path.to_string(), "/the/path");
159 }
160
161 #[test]
162 fn iter_segments() {
163 let path: Path = unsafe { Path::new("/") };
164 let result: Vec<&str> = path.iter_segments().collect();
165 let expected: Vec<&str> = vec![""];
166 assert_eq!(result, expected);
167
168 let path: Path = unsafe { Path::new("/the/path") };
169 let result: Vec<&str> = path.iter_segments().collect();
170 let expected: Vec<&str> = vec!["the", "path"];
171 assert_eq!(result, expected);
172
173 let path: Path = unsafe { Path::new("/the/path/") };
174 let result: Vec<&str> = path.iter_segments().collect();
175 let expected: Vec<&str> = vec!["the", "path", ""];
176 assert_eq!(result, expected)
177 }
178}