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