1use core::{
4 cmp::Ordering,
5 convert::TryFrom,
6 ffi::{c_char, CStr},
7 fmt,
8 iter::FusedIterator,
9 ops, ptr, slice, str,
10};
11
12use crate::path;
13
14#[derive(PartialEq, Eq)]
22#[repr(transparent)]
23pub struct Path {
24 inner: CStr,
25}
26
27impl Path {
28 pub const fn const_eq(&self, path: &Path) -> bool {
44 let a = self.inner.to_bytes();
45 let b = path.inner.to_bytes();
46
47 if a.len() != b.len() {
48 return false;
49 }
50
51 let mut i = 0;
52 while i < a.len() {
53 if a[i] != b[i] {
54 return false;
55 }
56 i += 1;
57 }
58
59 true
60 }
61
62 pub fn cmp_str(&self, other: &Path) -> Ordering {
81 self.inner.cmp(&other.inner)
82 }
83
84 pub fn cmp_lfs(&self, other: &Path) -> Ordering {
101 let this = self.inner.to_bytes();
102 let other = other.inner.to_bytes();
103
104 let min_len = this.len().min(other.len());
105
106 match this[0..min_len].cmp(&other[0..min_len]) {
107 Ordering::Less => Ordering::Less,
109 Ordering::Greater => Ordering::Greater,
111 Ordering::Equal => other.len().cmp(&this.len()),
113 }
114 }
115}
116
117pub struct Ancestors<'a> {
121 path: &'a str,
122}
123
124impl Iterator for Ancestors<'_> {
125 type Item = PathBuf;
126 fn next(&mut self) -> Option<PathBuf> {
127 if self.path.is_empty() {
128 return None;
129 } else if self.path == "/" {
130 self.path = "";
131 return Some(path!("/").into());
132 }
133
134 let item = self.path;
135
136 let Some((rem, item_name)) = self.path.rsplit_once('/') else {
137 self.path = "";
138 return item.try_into().ok();
139 };
140
141 if self.path.starts_with('/') && rem.is_empty() {
142 self.path = "/";
143 } else {
144 self.path = rem;
145 }
146
147 if item_name.is_empty() {
149 self.next();
150 }
151 item.try_into().ok()
152 }
153}
154
155impl FusedIterator for Ancestors<'_> {}
156
157pub struct Iter<'a> {
161 path: &'a str,
162}
163
164impl Iterator for Iter<'_> {
165 type Item = PathBuf;
166 fn next(&mut self) -> Option<PathBuf> {
167 if self.path.is_empty() {
168 return None;
169 }
170 if self.path.starts_with('/') {
171 self.path = &self.path[1..];
172 return Some(path!("/").into());
173 }
174
175 let Some((path, rem)) = self.path.split_once('/') else {
176 let ret_val = self.path.try_into().ok();
177 self.path = "";
178 return ret_val;
179 };
180
181 self.path = rem;
182 path.try_into().ok()
183 }
184}
185
186impl Path {
187 pub const fn is_empty(&self) -> bool {
196 self.inner.to_bytes().is_empty()
197 }
198
199 pub fn file_name(&self) -> Option<&Path> {
216 if self.is_empty() {
217 return None;
218 }
219
220 let this = self.as_str_ref_with_trailing_nul();
221 match this.rsplit_once('/') {
222 None | Some((_, "\x00")) => None,
223 Some((_, path)) => {
224 debug_assert!(path.ends_with('\x00'));
225 unsafe {
226 let cstr = CStr::from_bytes_with_nul_unchecked(path.as_bytes());
227 Some(Path::from_cstr_unchecked(cstr))
228 }
229 }
230 }
231 }
232
233 pub fn ancestors(&self) -> Ancestors {
246 Ancestors {
247 path: self.as_str(),
248 }
249 }
250
251 pub fn iter(&self) -> Iter {
264 Iter {
265 path: self.as_str(),
266 }
267 }
268
269 pub const fn from_str_with_nul(s: &str) -> Result<&Self> {
275 Self::from_bytes_with_nul(s.as_bytes())
276 }
277
278 pub const fn from_bytes_with_nul(bytes: &[u8]) -> Result<&Self> {
284 match CStr::from_bytes_with_nul(bytes) {
285 Ok(cstr) => Self::from_cstr(cstr),
286 Err(_) => Err(PathError::NotCStr),
287 }
288 }
289
290 pub const fn from_cstr(cstr: &CStr) -> Result<&Self> {
297 let bytes = cstr.to_bytes();
298 let n = cstr.to_bytes().len();
299 if n > PathBuf::MAX_SIZE {
300 Err(PathError::TooLarge)
301 } else if bytes.is_ascii() {
302 Ok(unsafe { Self::from_cstr_unchecked(cstr) })
303 } else {
304 Err(PathError::NotAscii)
305 }
306 }
307
308 pub const unsafe fn from_cstr_unchecked(cstr: &CStr) -> &Self {
314 &*(cstr as *const CStr as *const Path)
315 }
316
317 pub const fn as_ptr(&self) -> *const c_char {
319 self.inner.as_ptr()
320 }
321
322 pub fn join(&self, path: &Path) -> PathBuf {
324 let mut p = PathBuf::from(self);
325 p.push(path);
326 p
327 }
328
329 pub const fn as_str_ref_with_trailing_nul(&self) -> &str {
331 unsafe { str::from_utf8_unchecked(self.inner.to_bytes_with_nul()) }
333 }
334
335 pub const fn as_str(&self) -> &str {
336 unsafe { str::from_utf8_unchecked(self.inner.to_bytes()) }
338 }
339
340 pub fn parent(&self) -> Option<PathBuf> {
341 let rk_path_bytes = self.as_ref()[..].as_bytes();
342 match rk_path_bytes.iter().rposition(|x| *x == b'/') {
343 Some(0) if rk_path_bytes.len() != 1 => Some(path!("/").into()),
344 Some(slash_index) => {
345 if slash_index + 1 == rk_path_bytes.len() {
348 PathBuf::try_from(&rk_path_bytes[..slash_index])
349 .ok()?
350 .parent()
351 } else {
352 PathBuf::try_from(&rk_path_bytes[..slash_index]).ok()
353 }
354 }
355 None => None,
356 }
357 }
358}
359
360impl AsRef<str> for Path {
361 fn as_ref(&self) -> &str {
362 self.as_str()
363 }
364}
365
366impl fmt::Debug for Path {
367 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
368 write!(f, "p{:?}", self.as_str_ref_with_trailing_nul())
370 }
371}
372
373impl fmt::Display for Path {
374 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
375 f.write_str(self.as_ref())
376 }
377}
378
379impl<'b> TryFrom<&'b [u8]> for &'b Path {
380 type Error = PathError;
381
382 fn try_from(bytes: &[u8]) -> Result<&Path> {
383 Path::from_bytes_with_nul(bytes)
384 }
385}
386
387impl PartialEq<str> for Path {
388 fn eq(&self, rhs: &str) -> bool {
389 self.as_ref() == rhs
390 }
391}
392
393macro_rules! array_impls {
395 ($($N:expr),+) => {
396 $(
397 impl<'b> TryFrom<&'b [u8; $N]> for &'b Path {
398 type Error = PathError;
399
400 fn try_from(bytes: &[u8; $N]) -> Result<&Path> {
401 Path::from_bytes_with_nul(&bytes[..])
402 }
403 }
404
405 impl TryFrom<&[u8; $N]> for PathBuf {
406 type Error = PathError;
407
408 fn try_from(bytes: &[u8; $N]) -> Result<Self> {
409 Self::try_from(&bytes[..])
410 }
411 }
412
413 impl PartialEq<[u8; $N]> for Path {
414 fn eq(&self, rhs: &[u8; $N]) -> bool {
415 self.as_ref().as_bytes() == &rhs[..]
416 }
417 }
418
419 )+
420 }
421}
422
423array_impls!(
424 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
425 27, 28, 29, 30, 31, 32
426);
427
428#[derive(Clone)]
437pub struct PathBuf {
438 buf: [c_char; PathBuf::MAX_SIZE_PLUS_ONE],
439 len: usize,
441}
442
443const unsafe fn strlen(mut s: *const c_char) -> usize {
446 let mut n = 0;
447 while *s != 0 {
448 s = s.add(1);
449 n += 1;
450 }
451 n
452}
453
454impl Default for PathBuf {
455 fn default() -> Self {
456 Self::new()
457 }
458}
459
460impl PathBuf {
461 pub const MAX_SIZE: usize = 255;
462 pub const MAX_SIZE_PLUS_ONE: usize = Self::MAX_SIZE + 1;
463
464 pub const fn new() -> Self {
465 Self {
466 buf: [0; Self::MAX_SIZE_PLUS_ONE],
467 len: 1,
468 }
469 }
470
471 pub const fn from_path(path: &Path) -> Self {
487 let bytes = path.inner.to_bytes();
488
489 let mut buf = [0; Self::MAX_SIZE_PLUS_ONE];
490 let len = bytes.len();
491 assert!(len < Self::MAX_SIZE_PLUS_ONE);
492
493 let mut i = 0;
494 while i < len {
495 buf[i] = bytes[i] as _;
496 i += 1;
497 }
498
499 Self { buf, len: len + 1 }
500 }
501
502 pub const fn as_path(&self) -> &Path {
503 unsafe {
504 let bytes = slice::from_raw_parts(self.buf.as_ptr().cast(), self.len);
505 let cstr = CStr::from_bytes_with_nul_unchecked(bytes);
506 Path::from_cstr_unchecked(cstr)
507 }
508 }
509
510 pub const fn as_str(&self) -> &str {
511 self.as_path().as_str()
512 }
513
514 pub fn clear(&mut self) {
515 self.buf = [0; Self::MAX_SIZE_PLUS_ONE];
516 self.len = 1;
517 }
518
519 pub const unsafe fn from_buffer_unchecked(buf: [c_char; Self::MAX_SIZE_PLUS_ONE]) -> Self {
525 let len = strlen(buf.as_ptr()) + 1 ;
526 PathBuf { buf, len }
527 }
528
529 pub fn push(&mut self, path: &Path) {
531 match path.as_ref() {
532 "" => return,
534
535 #[allow(clippy::unnecessary_cast)]
538 "/" => {
539 self.buf[0] = b'/' as c_char;
540 self.buf[1] = 0;
541 self.len = 2;
542 return;
543 }
544 _ => {}
545 }
546
547 let src = path.as_ref().as_bytes();
548 let needs_separator = self
549 .as_ref()
550 .as_bytes()
551 .last()
552 .map(|byte| *byte != b'/')
553 .unwrap_or(false);
554 let slen = src.len();
555 assert!(
556 self.len
557 + slen
558 + if needs_separator {
559 1
561 } else {
562 0
563 }
564 <= Self::MAX_SIZE_PLUS_ONE
565 );
566
567 let len = self.len;
568 unsafe {
569 let mut p = self.buf.as_mut_ptr().cast::<u8>().add(len - 1);
570 if needs_separator {
571 p.write(b'/');
572 p = p.add(1);
573 self.len += 1;
574 }
575 ptr::copy_nonoverlapping(src.as_ptr(), p, slen);
576 p.add(slen).write(0); self.len += slen;
578 }
579 }
580}
581
582impl From<&Path> for PathBuf {
583 #[inline(never)]
584 fn from(path: &Path) -> Self {
585 let bytes = path.as_ref().as_bytes();
586
587 let mut buf = [0; Self::MAX_SIZE_PLUS_ONE];
588 let len = bytes.len();
589 assert!(len <= Self::MAX_SIZE_PLUS_ONE);
590 unsafe { ptr::copy_nonoverlapping(bytes.as_ptr(), buf.as_mut_ptr().cast(), len + 1) }
591 Self { buf, len: len + 1 }
592 }
593}
594
595impl TryFrom<&[u8]> for PathBuf {
597 type Error = PathError;
598
599 fn try_from(bytes: &[u8]) -> Result<Self> {
600 let bytes = if !bytes.is_empty() && bytes[bytes.len() - 1] == b'\0' {
603 &bytes[..bytes.len() - 1]
604 } else {
605 bytes
606 };
607 if bytes.len() > Self::MAX_SIZE {
608 return Err(PathError::TooLarge);
609 }
610 for byte in bytes {
611 if *byte == 0 {
612 return Err(PathError::NotCStr);
613 }
614 if !byte.is_ascii() {
615 return Err(PathError::NotAscii);
616 }
617 }
618
619 let mut buf = [0; Self::MAX_SIZE_PLUS_ONE];
620 let len = bytes.len();
621 unsafe { ptr::copy_nonoverlapping(bytes.as_ptr(), buf.as_mut_ptr().cast(), len) }
622 Ok(Self { buf, len: len + 1 })
623 }
624}
625
626impl TryFrom<&str> for PathBuf {
628 type Error = PathError;
629
630 fn try_from(s: &str) -> Result<Self> {
631 PathBuf::try_from(s.as_bytes())
632 }
633}
634
635impl ops::Deref for PathBuf {
636 type Target = Path;
637
638 fn deref(&self) -> &Path {
639 self.as_path()
640 }
641}
642
643#[cfg(feature = "serde")]
644impl serde::Serialize for PathBuf {
645 fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
646 where
647 S: serde::Serializer,
648 {
649 serializer.serialize_bytes(self.as_ref().as_bytes())
650 }
651}
652
653#[cfg(feature = "serde")]
654impl<'de> serde::Deserialize<'de> for PathBuf {
655 fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
656 where
657 D: serde::Deserializer<'de>,
658 {
659 use core::marker::PhantomData;
660
661 struct ValueVisitor<'de>(PhantomData<&'de ()>);
662
663 impl<'de> serde::de::Visitor<'de> for ValueVisitor<'de> {
664 type Value = PathBuf;
665
666 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
667 formatter.write_str("a path buffer")
668 }
669
670 fn visit_bytes<E>(self, v: &[u8]) -> core::result::Result<Self::Value, E>
671 where
672 E: serde::de::Error,
673 {
674 if v.len() > PathBuf::MAX_SIZE {
675 return Err(E::invalid_length(v.len(), &self));
676 }
677 PathBuf::try_from(v).map_err(|_| E::custom("invalid path buffer"))
678 }
679 }
680
681 deserializer.deserialize_bytes(ValueVisitor(PhantomData))
682 }
683}
684
685impl fmt::Debug for PathBuf {
686 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
687 <Path as fmt::Debug>::fmt(self, f)
688 }
689}
690
691impl fmt::Display for PathBuf {
692 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
693 <Path as fmt::Display>::fmt(self, f)
694 }
695}
696
697impl core::cmp::PartialEq for PathBuf {
698 fn eq(&self, other: &Self) -> bool {
699 self.as_ref() == other.as_ref()
701
702 }
708}
709
710impl core::cmp::Eq for PathBuf {}
711
712#[derive(Clone, Copy, Debug)]
728pub enum PathError {
729 NotAscii,
731 NotCStr,
733 TooLarge,
735}
736
737type Result<T> = core::result::Result<T, PathError>;
738
739#[cfg(test)]
740mod tests {
741 use super::{Path, PathBuf};
742 use crate::path;
743
744 const EMPTY: &Path = path!("");
745 const SLASH: &Path = path!("/");
746
747 #[test]
748 fn path_macro() {
749 assert_eq!(EMPTY, &*PathBuf::try_from("").unwrap());
750 assert_eq!(SLASH, &*PathBuf::try_from("/").unwrap());
751 }
752
753 #[test]
758 fn nul_in_from_str_with_nul() {
759 assert!(Path::from_str_with_nul("ub\0er").is_err());
760 }
761
762 #[test]
763 fn non_ascii_in_from_str_with_nul() {
764 assert!(Path::from_str_with_nul("über").is_err());
765 }
766
767 #[test]
768 fn join() {
769 let empty = Path::from_bytes_with_nul(b"\0").unwrap();
770 let slash = Path::from_bytes_with_nul(b"/\0").unwrap();
771 let a = Path::from_bytes_with_nul(b"a\0").unwrap();
772 let b = Path::from_bytes_with_nul(b"b\0").unwrap();
773
774 assert_eq!(empty.join(empty).as_ref(), "");
775 assert_eq!(empty.join(slash).as_ref(), "/");
776 assert_eq!(empty.join(a).as_ref(), "a");
777 assert_eq!(empty.join(b).as_ref(), "b");
778
779 assert_eq!(slash.join(empty).as_ref(), "/");
780 assert_eq!(slash.join(slash).as_ref(), "/");
781 assert_eq!(slash.join(a).as_ref(), "/a");
782 assert_eq!(slash.join(b).as_ref(), "/b");
783
784 assert_eq!(a.join(empty).as_ref(), "a");
785 assert_eq!(a.join(slash).as_ref(), "/");
786 assert_eq!(a.join(a).as_ref(), "a/a");
787 assert_eq!(a.join(b).as_ref(), "a/b");
788
789 assert_eq!(b.join(empty).as_ref(), "b");
790 assert_eq!(b.join(slash).as_ref(), "/");
791 assert_eq!(b.join(a).as_ref(), "b/a");
792 assert_eq!(b.join(b).as_ref(), "b/b");
793 }
794
795 #[test]
796 fn nulls() {
797 assert!(Path::from_bytes_with_nul(b"abc\0def").is_err());
798 }
799
800 #[test]
801 fn trailing_nuls() {
802 assert_eq!(
803 PathBuf::try_from("abc").unwrap(),
804 PathBuf::try_from("abc\0").unwrap()
805 );
806 }
807
808 #[test]
809 fn ancestors() {
810 fn assert_ancestor_parent(path: &Path) {
811 let mut ancestors = path.ancestors();
812 if !path.as_str().is_empty() {
813 assert_eq!(&*ancestors.next().unwrap(), path);
814 }
815 let mut buf = PathBuf::from(path);
816 loop {
817 let parent = buf.parent();
818 assert_eq!(parent, ancestors.next());
819 match parent {
820 Some(p) => buf = p,
821 None => return,
822 }
823 }
824 }
825
826 let path = path!("/some/path/.././file.extension");
827 assert_ancestor_parent(path);
828 let mut ancestors = path.ancestors();
829 assert_eq!(
830 &*ancestors.next().unwrap(),
831 path!("/some/path/.././file.extension")
832 );
833 assert_eq!(&*ancestors.next().unwrap(), path!("/some/path/../."));
834 assert_eq!(&*ancestors.next().unwrap(), path!("/some/path/.."));
835 assert_eq!(&*ancestors.next().unwrap(), path!("/some/path"));
836 assert_eq!(&*ancestors.next().unwrap(), path!("/some"));
837 assert_eq!(&*ancestors.next().unwrap(), path!("/"));
838 assert!(ancestors.next().is_none());
839
840 let path = path!("/some/path/.././file.extension/");
841 assert_ancestor_parent(path);
842 let mut ancestors = path.ancestors();
843 assert_eq!(
844 &*ancestors.next().unwrap(),
845 path!("/some/path/.././file.extension/")
846 );
847 assert_eq!(&*ancestors.next().unwrap(), path!("/some/path/../."));
848 assert_eq!(&*ancestors.next().unwrap(), path!("/some/path/.."));
849 assert_eq!(&*ancestors.next().unwrap(), path!("/some/path"));
850 assert_eq!(&*ancestors.next().unwrap(), path!("/some"));
851 assert_eq!(&*ancestors.next().unwrap(), path!("/"));
852 assert!(ancestors.next().is_none());
853
854 let path = path!("some/path/.././file.extension");
855 assert_ancestor_parent(path);
856 let mut ancestors = path.ancestors();
857 assert_eq!(
858 &*ancestors.next().unwrap(),
859 path!("some/path/.././file.extension")
860 );
861 assert_eq!(&*ancestors.next().unwrap(), path!("some/path/../."));
862 assert_eq!(&*ancestors.next().unwrap(), path!("some/path/.."));
863 assert_eq!(&*ancestors.next().unwrap(), path!("some/path"));
864 assert_eq!(&*ancestors.next().unwrap(), path!("some"));
865 assert!(ancestors.next().is_none());
866 }
867
868 #[test]
869 fn iter() {
870 let path = path!("/some/path/.././file.extension");
871 let mut ancestors = path.iter();
872 assert_eq!(&*ancestors.next().unwrap(), path!("/"));
873 assert_eq!(&*ancestors.next().unwrap(), path!("some"));
874 assert_eq!(&*ancestors.next().unwrap(), path!("path"));
875 assert_eq!(&*ancestors.next().unwrap(), path!(".."));
876 assert_eq!(&*ancestors.next().unwrap(), path!("."));
877 assert_eq!(&*ancestors.next().unwrap(), path!("file.extension"));
878 assert!(ancestors.next().is_none());
879 let path = path!("some/path/.././file.extension/");
880 let mut ancestors = path.iter();
881 assert_eq!(&*ancestors.next().unwrap(), path!("some"));
882 assert_eq!(&*ancestors.next().unwrap(), path!("path"));
883 assert_eq!(&*ancestors.next().unwrap(), path!(".."));
884 assert_eq!(&*ancestors.next().unwrap(), path!("."));
885 assert_eq!(&*ancestors.next().unwrap(), path!("file.extension"));
886 assert!(ancestors.next().is_none());
887 }
888
889 #[test]
890 fn file_name() {
891 let path = path!("/some/path/.././file.extension");
892 assert_eq!(path.file_name(), Some(path!("file.extension")));
893
894 let path = path!("/");
895 assert_eq!(path.file_name(), None);
896
897 let path = path!("");
898 assert_eq!(path.file_name(), None);
899
900 let path = path!("/some/path/.././file.extension/");
901 assert_eq!(path.file_name(), None);
902 }
903}