1use std::ops::{Deref, DerefMut};
4use std::str::FromStr;
5use std::{fmt, iter};
6
7use get_size::GetSize;
8use smallvec::*;
9
10use super::{label, Id, Label, ParseError, Segments};
11
12pub type PathSegment = Id;
14
15pub struct PathLabel {
17 segments: &'static [&'static str],
18}
19
20impl PathLabel {
21 pub fn len(&self) -> usize {
23 self.segments.len()
24 }
25}
26
27impl<Idx: std::slice::SliceIndex<[&'static str]>> std::ops::Index<Idx> for PathLabel {
28 type Output = Idx::Output;
29
30 fn index(&self, index: Idx) -> &Self::Output {
31 &self.segments[index]
32 }
33}
34
35pub const fn path_label(segments: &'static [&'static str]) -> PathLabel {
37 PathLabel { segments }
38}
39
40impl fmt::Display for PathLabel {
41 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
42 f.write_str("/")?;
43
44 let mut segments = self.segments.iter();
45 let last = segments.next_back();
46
47 for id in segments {
48 f.write_str(id)?;
49 f.write_str("/")?;
50 }
51
52 if let Some(last) = last {
53 f.write_str(last)?;
54 }
55
56 Ok(())
57 }
58}
59
60impl From<PathLabel> for Id {
61 fn from(path: PathLabel) -> Self {
62 Label::from(path).into()
63 }
64}
65
66impl From<PathLabel> for Label {
67 fn from(path: PathLabel) -> Self {
68 match path.segments {
69 [id] => label(id),
70 _ => panic!("not an Id: {}", PathBuf::from(path)),
71 }
72 }
73}
74
75impl From<PathLabel> for PathBuf {
76 fn from(path: PathLabel) -> Self {
77 let segments = path
78 .segments
79 .into_iter()
80 .map(|segment| label(*segment))
81 .map(PathSegment::from)
82 .collect();
83
84 Self { segments }
85 }
86}
87
88impl<const N: usize> From<[PathSegment; N]> for PathBuf {
89 fn from(segments: [PathSegment; N]) -> Self {
90 Self {
91 segments: segments.into_iter().collect(),
92 }
93 }
94}
95
96pub struct Path<'a> {
98 inner: &'a [PathSegment],
99}
100
101impl Default for Path<'static> {
102 fn default() -> Self {
103 Self { inner: &[] }
104 }
105}
106
107impl<'a> Deref for Path<'a> {
108 type Target = [PathSegment];
109
110 fn deref(&self) -> &Self::Target {
111 &self.inner
112 }
113}
114
115impl<'a> From<&'a [PathSegment]> for Path<'a> {
116 fn from(inner: &'a [PathSegment]) -> Path<'a> {
117 Path { inner }
118 }
119}
120
121impl<'a, Idx: std::slice::SliceIndex<[PathSegment]>> std::ops::Index<Idx> for Path<'a> {
122 type Output = Idx::Output;
123
124 fn index(&self, index: Idx) -> &Self::Output {
125 &self.inner[index]
126 }
127}
128
129impl<'a> fmt::Debug for Path<'a> {
130 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
131 fmt::Display::fmt(self, f)
132 }
133}
134
135impl<'a> fmt::Display for Path<'a> {
136 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
137 f.write_str("/")?;
138
139 for i in 0..self.len() {
140 write!(f, "{}", self[i])?;
141
142 if i < self.len() - 1 {
143 f.write_str("/")?;
144 }
145 }
146
147 Ok(())
148 }
149}
150
151#[derive(Clone, Default, Hash, Eq, PartialEq)]
153pub struct PathBuf {
154 segments: Segments<PathSegment>,
155}
156
157impl PathBuf {
158 pub fn new() -> Self {
160 Self {
161 segments: Segments::new(),
162 }
163 }
164
165 pub fn from_slice(segments: &[PathSegment]) -> Self {
167 Self {
168 segments: segments.into_iter().cloned().collect(),
169 }
170 }
171
172 pub fn with_capacity(capacity: usize) -> Self {
174 Self {
175 segments: Segments::with_capacity(capacity),
176 }
177 }
178
179 pub fn into_inner(self) -> Segments<PathSegment> {
181 self.segments
182 }
183
184 pub fn append<S: Into<PathSegment>>(mut self, suffix: S) -> Self {
186 self.segments.push(suffix.into());
187 self
188 }
189
190 pub fn pop(&mut self) -> Option<PathSegment> {
192 self.segments.pop()
193 }
194
195 pub fn suffix<'a>(&self, path: &'a [PathSegment]) -> Option<&'a [PathSegment]> {
197 if path.starts_with(&self.segments) {
198 Some(&path[self.segments.len()..])
199 } else {
200 None
201 }
202 }
203}
204
205impl GetSize for PathBuf {
206 fn get_size(&self) -> usize {
207 self.segments.iter().map(|segment| segment.get_size()).sum()
208 }
209}
210
211impl Extend<PathSegment> for PathBuf {
212 fn extend<T: IntoIterator<Item = PathSegment>>(&mut self, iter: T) {
213 self.segments.extend(iter)
214 }
215}
216
217impl<Idx: std::slice::SliceIndex<[PathSegment]>> std::ops::Index<Idx> for PathBuf {
218 type Output = Idx::Output;
219
220 fn index(&self, index: Idx) -> &Self::Output {
221 &self.segments[index]
222 }
223}
224
225#[cfg(feature = "hash")]
226impl<D: async_hash::Digest> async_hash::Hash<D> for PathBuf {
227 fn hash(self) -> async_hash::Output<D> {
228 async_hash::Hash::<D>::hash(&self)
229 }
230}
231
232#[cfg(feature = "hash")]
233impl<'a, D: async_hash::Digest> async_hash::Hash<D> for &'a PathBuf {
234 fn hash(self) -> async_hash::Output<D> {
235 if self == &PathBuf::default() {
236 return async_hash::default_hash::<D>();
237 } else {
238 async_hash::Hash::<D>::hash(self.to_string())
239 }
240 }
241}
242
243impl PartialEq<String> for PathBuf {
244 fn eq(&self, other: &String) -> bool {
245 self == other.as_str()
246 }
247}
248
249impl PartialEq<str> for PathBuf {
250 fn eq(&self, other: &str) -> bool {
251 if other.is_empty() {
252 return false;
253 } else if self.segments.is_empty() {
254 return other == "/";
255 }
256
257 let mut i = 0;
258 for segment in other.split('/') {
259 if i >= self.segments.len() {
260 return false;
261 } else if segment == self.segments[i] {
262 i += 1;
263 } else {
264 return false;
265 }
266 }
267
268 self.segments.len() == i
269 }
270}
271
272impl IntoIterator for PathBuf {
273 type Item = PathSegment;
274 type IntoIter = <Segments<PathSegment> as IntoIterator>::IntoIter;
275
276 fn into_iter(self) -> Self::IntoIter {
277 self.segments.into_iter()
278 }
279}
280
281impl std::borrow::Borrow<[PathSegment]> for PathBuf {
282 fn borrow(&self) -> &[PathSegment] {
283 &self.segments[..]
284 }
285}
286
287impl Deref for PathBuf {
288 type Target = [PathSegment];
289
290 fn deref(&self) -> &Self::Target {
291 &self.segments
292 }
293}
294
295impl DerefMut for PathBuf {
296 fn deref_mut(&mut self) -> &mut Self::Target {
297 &mut self.segments
298 }
299}
300
301impl PartialEq<[PathSegment]> for PathBuf {
302 fn eq(&self, other: &[PathSegment]) -> bool {
303 self.segments.as_slice() == other
304 }
305}
306
307impl From<PathSegment> for PathBuf {
308 fn from(segment: PathSegment) -> PathBuf {
309 PathBuf {
310 segments: iter::once(segment).collect(),
311 }
312 }
313}
314
315impl From<Label> for PathBuf {
316 fn from(segment: Label) -> PathBuf {
317 PathBuf {
318 segments: iter::once(segment.into()).collect(),
319 }
320 }
321}
322
323impl FromStr for PathBuf {
324 type Err = ParseError;
325
326 #[inline]
327 fn from_str(to: &str) -> Result<Self, Self::Err> {
328 if to == "/" {
329 Ok(PathBuf {
330 segments: smallvec![],
331 })
332 } else if to.ends_with('/') {
333 Err(format!("Path {} cannot end with a slash", to).into())
334 } else if to.starts_with('/') {
335 let segments = to
336 .split('/')
337 .skip(1)
338 .map(PathSegment::from_str)
339 .collect::<Result<Segments<PathSegment>, ParseError>>()?;
340
341 Ok(PathBuf { segments })
342 } else {
343 to.parse()
344 .map(|id| PathBuf {
345 segments: iter::once(id).collect(),
346 })
347 .map_err(|cause| format!("invalid path: {}", cause).into())
348 }
349 }
350}
351
352impl From<Segments<PathSegment>> for PathBuf {
353 fn from(segments: Segments<PathSegment>) -> Self {
354 Self { segments }
355 }
356}
357
358impl iter::FromIterator<PathSegment> for PathBuf {
359 fn from_iter<T: IntoIterator<Item = PathSegment>>(iter: T) -> Self {
360 PathBuf {
361 segments: iter.into_iter().collect(),
362 }
363 }
364}
365
366impl fmt::Debug for PathBuf {
367 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
368 write!(f, "{}", Path::from(&self[..]))
369 }
370}
371
372impl fmt::Display for PathBuf {
373 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
374 write!(f, "{}", Path::from(&self[..]))
375 }
376}
377
378#[cfg(test)]
379mod test {
380 use super::*;
381
382 #[test]
383 fn test_path_label_to_string() {
384 let path = path_label(&[]);
385 assert_eq!(path.to_string(), "/".to_string());
386
387 let path = path_label(&["one"]);
388 assert_eq!(path.to_string(), "/one".to_string());
389
390 let path = path_label(&["one", "two"]);
391 assert_eq!(path.to_string(), "/one/two".to_string());
392 }
393}