iref_core/iri/
path.rs

1use static_regular_grammar::RegularGrammar;
2
3mod segment;
4
5pub use segment::*;
6
7use crate::common::path::{NormalizedSegmentsImpl, PathBufImpl, PathImpl, SegmentsImpl};
8
9use super::PathMut;
10
11/// IRI path.
12#[derive(RegularGrammar)]
13#[grammar(
14	file = "src/iri/grammar.abnf",
15	entry_point = "ipath",
16	name = "IRI path",
17	cache = "automata/iri/path.aut.cbor"
18)]
19#[grammar(sized(PathBuf, derive(Debug, Display)))]
20#[cfg_attr(feature = "serde", grammar(serde))]
21#[cfg_attr(feature = "ignore-grammars", grammar(disable))]
22pub struct Path(str);
23
24impl PathImpl for Path {
25	const EMPTY: &'static Self = Self::EMPTY;
26
27	const EMPTY_ABSOLUTE: &'static Self = Self::EMPTY_ABSOLUTE;
28
29	type Segment = Segment;
30
31	type Owned = PathBuf;
32
33	unsafe fn new_unchecked(bytes: &[u8]) -> &Self {
34		Self::new_unchecked(std::str::from_utf8_unchecked(bytes))
35	}
36
37	#[inline(always)]
38	fn as_bytes(&self) -> &[u8] {
39		self.as_bytes()
40	}
41
42	fn to_path_buf(&self) -> Self::Owned {
43		unsafe { PathBuf::new_unchecked(self.to_string()) }
44	}
45}
46
47impl PathBufImpl for PathBuf {
48	type Borrowed = Path;
49
50	unsafe fn as_mut_vec(&mut self) -> &mut Vec<u8> {
51		self.0.as_mut_vec()
52	}
53
54	fn as_bytes(&self) -> &[u8] {
55		self.0.as_bytes()
56	}
57}
58
59impl Path {
60	/// The empty absolute path `/`.
61	pub const EMPTY_ABSOLUTE: &'static Self = unsafe { Self::new_unchecked("/") };
62
63	/// Returns the number of segments in the path.
64	///
65	/// This computes in linear time w.r.t the number of segments. It is
66	/// equivalent to `path.segments().count()`.
67	#[inline]
68	pub fn segment_count(&self) -> usize {
69		self.segments().count()
70	}
71
72	/// Checks if the path is empty.
73	///
74	/// Returns `true` if the path has no segments.
75	/// The absolute path `/` is empty.
76	#[inline]
77	pub fn is_empty(&self) -> bool {
78		PathImpl::is_empty(self)
79	}
80
81	/// Checks if the path is absolute.
82	///
83	/// A path is absolute if it starts with a `/`.
84	#[inline]
85	pub fn is_absolute(&self) -> bool {
86		PathImpl::is_absolute(self)
87	}
88
89	/// Checks if the path is relative.
90	///
91	/// A path is relative if it does not start with a `/`.
92	#[inline]
93	pub fn is_relative(&self) -> bool {
94		PathImpl::is_relative(self)
95	}
96
97	pub fn first(&self) -> Option<&Segment> {
98		PathImpl::first(self)
99	}
100
101	pub fn last(&self) -> Option<&Segment> {
102		PathImpl::last(self)
103	}
104
105	/// Produces an iterator over the segments of the IRI path.
106	///
107	/// Empty segments are preserved: the path `a//b` will raise the three
108	/// segments `a`, `` and `b`. The absolute path `/` has no segments, but
109	/// the path `/a/` has two segments, `a` and ``.
110	///
111	/// No normalization occurs with `.` and `..` segments. See the
112	/// [`Self::normalized_segments`] to iterate over the normalized segments
113	/// of a path.
114	#[inline]
115	pub fn segments(&self) -> Segments {
116		Segments(PathImpl::segments(self))
117	}
118
119	/// Iterate over the normalized segments of the path.
120	///
121	/// Remove the special dot segments `..` and `.` from the iteration using
122	/// the usual path semantics for dot segments. This may be expensive for
123	/// large paths since it will need to internally normalize the path first.
124	#[inline]
125	pub fn normalized_segments(&self) -> NormalizedSegments {
126		NormalizedSegments(PathImpl::normalized_segments(self))
127	}
128
129	#[inline]
130	pub fn normalized(&self) -> PathBuf {
131		PathImpl::normalized(self)
132	}
133
134	/// Returns the last segment of the path, if there is one, unless it is
135	/// empty.
136	///
137	/// This does not consider the normalized version of the path, dot segments
138	/// are preserved.
139	#[inline]
140	pub fn file_name(&self) -> Option<&Segment> {
141		PathImpl::file_name(self)
142	}
143
144	/// Returns the directory path, which is the path without the file name.
145	///
146	/// # Example
147	///
148	/// ```
149	/// # use iref_core::iri::Path;
150	/// assert_eq!(Path::new("/foo/bar").unwrap().directory(), "/foo/");
151	/// assert_eq!(Path::new("/foo").unwrap().directory(), "/");
152	/// assert_eq!(Path::new("//foo").unwrap().directory(), "//");
153	/// assert_eq!(Path::new("/").unwrap().directory(), "/");
154	/// ```
155	pub fn directory(&self) -> &Self {
156		PathImpl::directory(self)
157	}
158
159	/// Returns the path without its final segment, if there is one.
160	///
161	/// ```
162	/// # use iref_core::iri::Path;
163	/// assert_eq!(Path::new("/foo/bar").unwrap().parent().unwrap(), "/foo");
164	/// assert_eq!(Path::new("/foo").unwrap().parent().unwrap(), "/");
165	/// assert_eq!(Path::new("//foo").unwrap().parent().unwrap(), "/./");
166	/// assert_eq!(Path::new("/").unwrap().parent(), None);
167	/// ```
168	#[inline]
169	pub fn parent(&self) -> Option<&Self> {
170		PathImpl::parent(self)
171	}
172
173	/// Returns the path without its final segment, if there is one.
174	///
175	/// ```
176	/// # use iref_core::iri::Path;
177	/// assert_eq!(Path::new("/foo/bar").unwrap().parent_or_empty(), "/foo");
178	/// assert_eq!(Path::new("/foo").unwrap().parent_or_empty(), "/");
179	/// assert_eq!(Path::new("//foo").unwrap().parent_or_empty(), "/./");
180	/// assert_eq!(Path::new("/").unwrap().parent_or_empty(), "/");
181	/// assert_eq!(Path::new("").unwrap().parent_or_empty(), "");
182	/// ```
183	#[inline]
184	pub fn parent_or_empty(&self) -> &Self {
185		PathImpl::parent_or_empty(self)
186	}
187
188	/// Get the suffix part of this path, if any, with regard to the given prefix path.
189	///
190	/// Returns `Some(suffix)` if this path is of the form `prefix/suffix` where `prefix` is given
191	/// as parameter. Returns `None` otherwise.
192	///
193	/// Both paths are normalized during the process.
194	/// The result is a normalized suffix path.
195	///
196	/// # Example
197	/// ```
198	/// # use std::convert::TryFrom;
199	/// # use iref_core as iref;
200	/// use iref::iri::{Path, PathBuf};
201	///
202	/// let prefix = Path::new("/foo/bar").unwrap();
203	/// let path = Path::new("/foo/bar/baz").unwrap();
204	/// let suffix: PathBuf = path.suffix(prefix).unwrap();
205	///
206	/// assert_eq!(suffix.as_str(), "baz");
207	/// ```
208	#[inline]
209	pub fn suffix(&self, prefix: &Self) -> Option<PathBuf> {
210		PathImpl::suffix(self, prefix)
211	}
212}
213
214impl<'a> IntoIterator for &'a Path {
215	type Item = &'a Segment;
216	type IntoIter = Segments<'a>;
217
218	#[inline]
219	fn into_iter(self) -> Segments<'a> {
220		self.segments()
221	}
222}
223
224impl PartialEq for Path {
225	#[inline]
226	fn eq(&self, other: &Path) -> bool {
227		if self.is_absolute() == other.is_absolute() {
228			let self_segments = self.normalized_segments();
229			let other_segments = other.normalized_segments();
230			self_segments.len() == other_segments.len()
231				&& self_segments.zip(other_segments).all(|(a, b)| a == b)
232		} else {
233			false
234		}
235	}
236}
237
238impl PartialEq<str> for Path {
239	fn eq(&self, other: &str) -> bool {
240		self.as_str() == other
241	}
242}
243
244impl<'a> PartialEq<&'a str> for Path {
245	fn eq(&self, other: &&'a str) -> bool {
246		self.as_str() == *other
247	}
248}
249
250impl PartialEq<String> for Path {
251	fn eq(&self, other: &String) -> bool {
252		self.as_str() == other.as_str()
253	}
254}
255
256impl Eq for Path {}
257
258impl PartialOrd for Path {
259	#[inline]
260	fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
261		Some(self.cmp(other))
262	}
263}
264
265impl Ord for Path {
266	#[inline]
267	fn cmp(&self, other: &Self) -> std::cmp::Ordering {
268		use std::cmp::Ordering;
269		if self.is_absolute() == other.is_absolute() {
270			let mut self_segments = self.normalized_segments();
271			let mut other_segments = other.normalized_segments();
272
273			loop {
274				match (self_segments.next(), other_segments.next()) {
275					(None, None) => return Ordering::Equal,
276					(Some(_), None) => return Ordering::Greater,
277					(None, Some(_)) => return Ordering::Less,
278					(Some(a), Some(b)) => match a.cmp(b) {
279						Ordering::Greater => return Ordering::Greater,
280						Ordering::Less => return Ordering::Less,
281						Ordering::Equal => (),
282					},
283				}
284			}
285		} else if self.is_absolute() {
286			Ordering::Greater
287		} else {
288			Ordering::Less
289		}
290	}
291}
292
293impl std::hash::Hash for Path {
294	#[inline]
295	fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
296		self.is_absolute().hash(hasher);
297		self.normalized_segments().for_each(move |s| s.hash(hasher))
298	}
299}
300
301pub struct Segments<'a>(SegmentsImpl<'a, Path>);
302
303impl<'a> Iterator for Segments<'a> {
304	type Item = &'a Segment;
305
306	fn next(&mut self) -> Option<Self::Item> {
307		self.0.next()
308	}
309}
310
311impl<'a> DoubleEndedIterator for Segments<'a> {
312	fn next_back(&mut self) -> Option<Self::Item> {
313		self.0.next_back()
314	}
315}
316
317pub struct NormalizedSegments<'a>(NormalizedSegmentsImpl<'a, Path>);
318
319impl<'a> Iterator for NormalizedSegments<'a> {
320	type Item = &'a Segment;
321
322	fn size_hint(&self) -> (usize, Option<usize>) {
323		self.0.size_hint()
324	}
325
326	#[inline]
327	fn next(&mut self) -> Option<&'a Segment> {
328		self.0.next()
329	}
330}
331
332impl<'a> DoubleEndedIterator for NormalizedSegments<'a> {
333	#[inline]
334	fn next_back(&mut self) -> Option<Self::Item> {
335		self.0.next_back()
336	}
337}
338
339impl<'a> ExactSizeIterator for NormalizedSegments<'a> {}
340
341impl PathBuf {
342	/// Returns a mutable reference to the interior bytes.
343	///
344	/// # Safety
345	///
346	/// This function is unsafe because the returned `&mut Vec` allows writing
347	/// bytes which are not valid in a path. If this constraint is violated,
348	/// using the original `PathBuf` after dropping the `&mut Vec` may violate
349	/// memory safety, as the rest of the library assumes that `PathBuf` are
350	/// valid paths.
351	pub unsafe fn as_mut_vec(&mut self) -> &mut Vec<u8> {
352		self.0.as_mut_vec()
353	}
354
355	pub fn as_path_mut(&mut self) -> PathMut {
356		PathMut::from_path(self)
357	}
358
359	pub fn push(&mut self, segment: &Segment) {
360		self.as_path_mut().push(segment)
361	}
362
363	/// Pop the last non-`..` segment of the path.
364	///
365	/// If the path is empty or ends in `..`, then a `..` segment
366	/// will be added instead.
367	pub fn pop(&mut self) {
368		self.as_path_mut().pop()
369	}
370
371	pub fn clear(&mut self) {
372		self.as_path_mut().clear()
373	}
374
375	/// Push the given segment to this path using the `.` and `..` segments semantics.
376	#[inline]
377	pub fn symbolic_push(&mut self, segment: &Segment) {
378		self.as_path_mut().symbolic_push(segment)
379	}
380
381	/// Append the given path to this path using the `.` and `..` segments semantics.
382	///
383	/// Note that this does not normalize the segments already in the path.
384	/// For instance `'/a/b/.'.symbolc_append('../')` will return `/a/b/` and not
385	/// `a/` because the semantics of `..` is applied on the last `.` in the path.
386	#[inline]
387	pub fn symbolic_append<'s, P: IntoIterator<Item = &'s Segment>>(&mut self, path: P) {
388		self.as_path_mut().symbolic_append(path)
389	}
390
391	#[inline]
392	pub fn normalize(&mut self) {
393		self.as_path_mut().normalize()
394	}
395}
396
397impl PartialEq<str> for PathBuf {
398	fn eq(&self, other: &str) -> bool {
399		self.as_str() == other
400	}
401}
402
403impl<'a> PartialEq<&'a str> for PathBuf {
404	fn eq(&self, other: &&'a str) -> bool {
405		self.as_str() == *other
406	}
407}
408
409impl PartialEq<String> for PathBuf {
410	fn eq(&self, other: &String) -> bool {
411		self.as_str() == other.as_str()
412	}
413}
414
415#[cfg(test)]
416mod tests {
417	use super::*;
418
419	#[test]
420	fn empty() {
421		let path = Path::EMPTY;
422		assert!(path.is_empty());
423		assert!(!path.is_absolute());
424		assert!(path.segments().next().is_none());
425	}
426
427	#[test]
428	fn empty_absolute() {
429		let path = Path::EMPTY_ABSOLUTE;
430		assert!(path.is_empty());
431		assert!(path.is_absolute());
432		assert!(path.segments().next().is_none());
433	}
434
435	#[test]
436	fn non_empty() {
437		let path = Path::new("a/b").unwrap();
438
439		assert!(!path.is_empty());
440		assert!(!path.is_absolute());
441
442		let mut segments = path.segments();
443		assert!(segments.next().unwrap().as_str() == "a");
444		assert!(segments.next().unwrap().as_str() == "b");
445		assert!(segments.next().is_none());
446	}
447
448	#[test]
449	fn non_empty_absolute() {
450		let path = Path::new("/foo/bar").unwrap();
451		assert!(!path.is_empty());
452		assert!(path.is_absolute());
453
454		let mut segments = path.segments();
455		assert!(segments.next().unwrap().as_str() == "foo");
456		assert!(segments.next().unwrap().as_str() == "bar");
457		assert!(segments.next().is_none());
458	}
459
460	#[test]
461	fn next_segment() {
462		let vectors = [
463			("foo/bar", 0, Some(("foo", 4))),
464			("foo/bar", 4, Some(("bar", 8))),
465			("foo/bar", 8, None),
466			("foo/bar/", 8, Some(("", 9))),
467			("foo/bar/", 9, None),
468			("//foo", 1, Some(("", 2))),
469		];
470
471		for (input, offset, expected) in vectors {
472			unsafe {
473				assert_eq!(
474					Path::new(input).unwrap().next_segment_from(offset),
475					expected.map(|(e, i)| (Segment::new(e).unwrap(), i))
476				)
477			}
478		}
479	}
480
481	#[test]
482	fn previous_segment() {
483		let vectors = [
484			("/foo/bar", 1, None),
485			("foo/bar", 0, None),
486			("foo/bar", 4, Some(("foo", 0))),
487			("foo/bar", 8, Some(("bar", 4))),
488			("foo/bar/", 8, Some(("bar", 4))),
489			("foo/bar/", 9, Some(("", 8))),
490			("//a/b", 4, Some(("a", 2))),
491		];
492
493		for (input, offset, expected) in vectors {
494			unsafe {
495				assert_eq!(
496					Path::new(input).unwrap().previous_segment_from(offset),
497					expected.map(|(e, i)| (Segment::new(e).unwrap(), i))
498				)
499			}
500		}
501	}
502
503	#[test]
504	fn first_segment() {
505		let vectors = [
506			("", None),
507			("/", None),
508			("//", Some("")),
509			("/foo/bar", Some("foo")),
510		];
511
512		for (input, expected) in vectors {
513			assert_eq!(
514				Path::new(input).unwrap().first(),
515				expected.map(|e| Segment::new(e).unwrap())
516			)
517		}
518	}
519
520	#[test]
521	fn segments() {
522		let vectors: [(&str, &[&str]); 8] = [
523			("", &[]),
524			("foo", &["foo"]),
525			("/foo", &["foo"]),
526			("foo/", &["foo", ""]),
527			("/foo/", &["foo", ""]),
528			("a/b/c/d", &["a", "b", "c", "d"]),
529			("a/b//c/d", &["a", "b", "", "c", "d"]),
530			("//a/b/foo//bar/", &["", "a", "b", "foo", "", "bar", ""]),
531		];
532
533		for (input, expected) in vectors {
534			let path = Path::new(input).unwrap();
535			let segments: Vec<_> = path.segments().collect();
536			assert_eq!(segments.len(), expected.len());
537			assert!(segments
538				.into_iter()
539				.zip(expected)
540				.all(|(a, b)| a.as_str() == *b))
541		}
542	}
543
544	#[test]
545	fn segments_rev() {
546		let vectors: [(&str, &[&str]); 8] = [
547			("", &[]),
548			("foo", &["foo"]),
549			("/foo", &["foo"]),
550			("foo/", &["foo", ""]),
551			("/foo/", &["foo", ""]),
552			("a/b/c/d", &["a", "b", "c", "d"]),
553			("a/b//c/d", &["a", "b", "", "c", "d"]),
554			("//a/b/foo//bar/", &["", "a", "b", "foo", "", "bar", ""]),
555		];
556
557		for (input, expected) in vectors {
558			let path = Path::new(input).unwrap();
559			let segments: Vec<_> = path.segments().rev().collect();
560			assert_eq!(segments.len(), expected.len());
561			assert!(segments
562				.into_iter()
563				.zip(expected.into_iter().rev())
564				.all(|(a, b)| a.as_str() == *b))
565		}
566	}
567
568	#[test]
569	fn normalized() {
570		let vectors = [
571			("", ""),
572			("a/b/c", "a/b/c"),
573			("a/..", ""),
574			("a/b/..", "a/"),
575			("a/b/../", "a/"),
576			("a/b/c/..", "a/b/"),
577			("a/b/c/.", "a/b/c/"),
578			("a/../..", "../"),
579			("/a/../..", "/"), // Errata 4547 is only implemented for relative paths.
580		];
581
582		for (input, expected) in vectors {
583			// eprintln!("{input}, {expected}");
584			let path = Path::new(input).unwrap();
585			let output = path.normalized();
586			assert_eq!(output.as_str(), expected);
587		}
588	}
589
590	#[test]
591	fn eq() {
592		let vectors = [
593			("a/b/c", "a/b/c"),
594			("a/b/c", "a/b/c/."),
595			("a/b/c/", "a/b/c/./"),
596			("a/b/c", "a/b/../b/c"),
597			("a/b/c/..", "a/b"),
598			("a/..", ""),
599			("/a/..", "/"),
600			("a/../..", ".."),
601			("/a/../..", "/.."),
602			("a/b/c/./", "a/b/c/"),
603			("a/b/c/../", "a/b/"),
604		];
605
606		for (a, b) in vectors {
607			let a = Path::new(a).unwrap();
608			let b = Path::new(b).unwrap();
609			assert_eq!(a, b)
610		}
611	}
612
613	#[test]
614	fn ne() {
615		let vectors = [
616			("a/b/c", "a/b/c/"),
617			("a/b/c/", "a/b/c/."),
618			("a/b/c/../", "a/b"),
619		];
620
621		for (a, b) in vectors {
622			let a = Path::new(a).unwrap();
623			let b = Path::new(b).unwrap();
624			assert_ne!(a, b)
625		}
626	}
627
628	#[test]
629	fn file_name() {
630		let vectors = [("//a/b/foo//bar/", None), ("//a/b/foo//bar", Some("bar"))];
631
632		for (input, expected) in vectors {
633			let input = Path::new(input).unwrap();
634			assert_eq!(input.file_name().map(Segment::as_str), expected)
635		}
636	}
637
638	#[test]
639	fn parent() {
640		let vectors = [
641			("", None),
642			("/", None),
643			(".", None),
644			("//a/b/foo//bar", Some("//a/b/foo/")),
645			("//a/b/foo//", Some("//a/b/foo/")),
646			("//a/b/foo/", Some("//a/b/foo")),
647			("//a/b/foo", Some("//a/b")),
648			("//a/b", Some("//a")),
649			("//a", Some("/./")),
650			("/./", Some("/.")),
651			("/.", Some("/")),
652		];
653
654		for (input, expected) in vectors {
655			let input = Path::new(input).unwrap();
656			assert_eq!(input.parent().map(Path::as_str), expected)
657		}
658	}
659
660	#[test]
661	fn suffix() {
662		let vectors = [
663			("/foo/bar/baz", "/foo/bar", Some("baz")),
664			("//foo", "/", Some(".//foo")),
665			("/a/b/baz", "/foo/bar", None),
666		];
667
668		for (path, prefix, expected_suffix) in vectors {
669			// eprintln!("{path} over {prefix} => {expected_suffix:?}");
670			let path = Path::new(path).unwrap();
671			let suffix = path.suffix(Path::new(prefix).unwrap());
672			assert_eq!(suffix.as_deref().map(Path::as_str), expected_suffix)
673		}
674	}
675}