iref_core/uri/
path_mut.rs

1use crate::common::path_mut::PathMutImpl;
2use std::ops::Deref;
3
4use super::{path::Segment, Path, PathBuf};
5
6/// Mutable IRI path.
7pub struct PathMut<'a>(PathMutImpl<'a, Path>);
8
9impl<'a> Deref for PathMut<'a> {
10	type Target = Path;
11
12	fn deref(&self) -> &Self::Target {
13		self.0.deref()
14	}
15}
16
17impl<'a> PathMut<'a> {
18	pub(crate) fn from_impl(i: PathMutImpl<'a, Path>) -> Self {
19		Self(i)
20	}
21
22	pub fn from_path(path: &'a mut PathBuf) -> Self {
23		Self(PathMutImpl::from_path(path))
24	}
25
26	/// Add a segment at the end of the path.
27	///
28	/// # Ambiguities
29	///
30	/// Adding a segment to an empty path may introduce ambiguities and several
31	/// cases. Here is how this function deals with those cases.
32	///
33	/// ## Empty segment
34	///
35	/// Adding an empty segment on an empty path may add ambiguity in two
36	/// cases:
37	/// 1. if the path is relative, adding a `/` would make the path
38	///    absolute (e.g. `scheme:` becomes `scheme:/`) ;
39	/// 2. if the path is absolute adding a `/` would add two empty segments
40	///    (e.g. `scheme:/` becomes `scheme://`), and it may be confused with an
41	///    authority part ;
42	///
43	/// To avoid such ambiguity, in both cases this function will add a `.`
44	/// segment to the path, preserving its semantics:
45	/// 1. `scheme:` becomes `scheme:./` instead of `scheme:/` ;
46	/// 2. `scheme:/` becomes `scheme:/./` instead of `scheme://`.
47	///
48	/// ## Segment containing a `:`
49	///
50	/// If the path does not follow a scheme and/or authority part, a `:` in
51	/// the first segment may be confused with a scheme separator
52	/// (e.g. `looks-like-a-scheme:rest`).
53	/// To avoid such ambiguity, this function will add a `.` segment to the
54	/// path, preserving its semantics (e.g. `./looks-like-a-scheme:rest`).
55	pub fn push(&mut self, segment: &Segment) {
56		self.0.push(segment)
57	}
58
59	/// Pop the last non-`..` segment of the path.
60	///
61	/// If the path is empty or ends in `..`, then a `..` segment
62	/// will be added instead.
63	pub fn pop(&mut self) {
64		self.0.pop();
65	}
66
67	pub fn clear(&mut self) {
68		self.0.clear()
69	}
70
71	/// Push the given segment to this path using the `.` and `..` segments semantics.
72	#[inline]
73	pub fn symbolic_push(&mut self, segment: &Segment) {
74		if self.0.symbolic_push(segment) && !self.0.is_empty() {
75			self.0.push(Segment::EMPTY)
76		}
77	}
78
79	/// Append the given path to this path using the `.` and `..` segments semantics.
80	///
81	/// Note that this does not normalize the segments already in the path.
82	/// For instance `'/a/b/.'.symbolc_append('../')` will return `/a/b/` and not
83	/// `a/` because the semantics of `..` is applied on the last `.` in the path.
84	#[inline]
85	pub fn symbolic_append<'s, P: IntoIterator<Item = &'s Segment>>(&mut self, path: P) {
86		self.0.symbolic_append(path)
87	}
88
89	#[inline]
90	pub fn normalize(&mut self) {
91		self.0.normalize()
92	}
93}
94
95#[cfg(test)]
96mod tests {
97	use super::*;
98
99	#[test]
100	fn push() {
101		let vectors: [(&[u8], &[u8], &[u8]); 12] = [
102			(b"", b"foo", b"foo"),
103			(b"/", b"foo", b"/foo"),
104			(b"", b"", b"./"),
105			(b"/", b"", b"/./"),
106			(b"foo", b"bar", b"foo/bar"),
107			(b"/foo", b"bar", b"/foo/bar"),
108			(b"foo", b"", b"foo/"),
109			(b"foo/bar", b"", b"foo/bar/"),
110			(b"foo/", b"", b"foo//"),
111			(b"a/b/c", b"d", b"a/b/c/d"),
112			(b"/a/b/c", b"d", b"/a/b/c/d"),
113			(b"a/b/c/", b"d", b"a/b/c//d"),
114		];
115
116		for (path, segment, expected) in vectors {
117			let mut path = PathBuf::new(path.to_vec()).unwrap();
118			let mut path_mut = PathMut::from_path(&mut path);
119			let segment = Segment::new(&segment).unwrap();
120			path_mut.push(segment);
121			assert_eq!(path_mut.as_bytes(), expected)
122		}
123	}
124
125	#[test]
126	fn pop() {
127		let vectors: [(&[u8], &[u8]); 6] = [
128			(b"", b".."),
129			(b"/", b"/"),
130			(b"/..", b"/../.."),
131			(b"foo", b""),
132			(b"foo/bar", b"foo"),
133			(b"foo/bar/", b"foo/bar"),
134		];
135
136		for (path, expected) in vectors {
137			let mut path = PathBuf::new(path.to_vec()).unwrap();
138			let mut path_mut = PathMut::from_path(&mut path);
139			path_mut.pop();
140			assert_eq!(path_mut.as_bytes(), expected)
141		}
142	}
143
144	#[test]
145	fn normalized() {
146		let vectors: [(&[u8], &[u8]); 9] = [
147			(b"", b""),
148			(b"a/b/c", b"a/b/c"),
149			(b"a/..", b""),
150			(b"a/b/..", b"a"),
151			(b"a/b/../", b"a/"),
152			(b"a/b/c/..", b"a/b"),
153			(b"a/b/c/.", b"a/b/c"),
154			(b"a/../..", b".."),
155			(b"/a/../..", b"/"),
156		];
157
158		for (input, expected) in vectors {
159			let mut path = PathBuf::new(input.to_vec()).unwrap();
160			let mut path_mut = PathMut::from_path(&mut path);
161			path_mut.normalize();
162			assert_eq!(path_mut.as_bytes(), expected);
163		}
164	}
165}