1use std::borrow::{Borrow, Cow};
2use std::convert::Infallible;
3use std::cmp::Ordering;
4use std::ffi::{OsStr, OsString};
5use std::fmt::{self, Debug};
6use std::hash::{Hash, Hasher};
7use std::iter::{Extend, FromIterator};
8use std::ops::{Deref, Index, IndexMut, Range, RangeFrom, RangeFull, RangeTo};
9use std::path::{Path, PathBuf};
10use std::str::FromStr;
11
12pub trait PathRange<T: ?Sized> {
24 fn range(&self, range: T) -> Self;
25}
26
27fn os_str_as_u8_slice(s: &OsStr) -> &[u8] {
28 unsafe { &*(s as *const OsStr as *const [u8]) }
29}
30
31#[derive(Clone)]
32pub struct SmartPathBuf {
33 inner: PathBuf,
34 len: usize,
35 init: Option<usize>,
36 segments: Vec<OsString>,
37 indexes: Vec<usize>,
38}
39
40impl SmartPathBuf {
41 pub fn new() -> SmartPathBuf {
50 Self {
51 inner: PathBuf::new(),
52 len: 0,
53 init: None,
54 segments: Vec::new(),
55 indexes: Vec::new(),
56 }
57 }
58 fn with_inner(inner: PathBuf, init: Option<usize>, len: usize) -> Self {
59 let segments = inner.iter().map(|s| s.to_os_string()).collect();
60 SmartPathBuf {
61 inner,
62 len,
63 init,
64 segments,
65 indexes: Vec::new(),
66 }
67 }
68
69 #[cfg(feature = "unstable")]
70 pub fn with_capacity(cap: usize) -> SmartPathBuf {
71 Self {
72 inner: PathBuf::with_capacity(cap),
73 len: 0,
74 init: None,
75 segments: Vec::new(),
76 indexes: Vec::new(),
77 }
78 }
79 pub fn as_path(&self) -> &Path {
83 self
84 }
85 pub fn len(&self) -> usize {
88 self.len
89 }
90 pub fn is_empty(&self) -> bool {
92 self.len == 0
93 }
94 pub fn push<P: AsRef<Path>>(&mut self, path: P) {
112 if self.init.is_some() {
113 let seg = path
114 .as_ref()
115 .iter()
116 .map(|os| os.to_os_string())
117 .collect::<Vec<_>>();
118
119 self.len += seg.len();
120 self.indexes.push(seg.len());
121 self.segments.extend(seg);
122
123 self.inner.push(path);
124 } else {
125 let seg = path.as_ref().iter();
126 self.segments = seg.map(|s| s.to_os_string()).collect();
127 self.len += self.segments.len();
128 self.init = Some(self.len);
129
130 self.inner.push(path);
131 }
132 }
133 pub fn pop(&mut self) -> bool {
136 if let Some(last_idx) = self.indexes.last_mut() {
137 if *last_idx == 1 {
138 self.indexes.pop();
139 } else {
140 *last_idx -= 1;
141 }
142 }
143 self.len -= 1;
144 self.segments.pop();
145 self.inner.pop()
146 }
147 pub fn pop_last(&mut self) {
161 if let Some(last) = self.indexes.pop() {
162 for _ in 0..last {
163 self.pop();
164 }
165 }
166 }
167 pub fn initial(&mut self) {
182 if let Some(init) = self.init {
183 let start = self.len - init;
184 for _ in 0..start {
185 self.pop();
186 }
187 }
188 }
189 pub fn set_file_name<S: AsRef<OsStr>>(&mut self, file_name: S) {
211 if self.inner.file_name().is_some() {
212 self.pop();
213 }
214 self.push(file_name.as_ref());
215 }
216 pub fn set_extension<S: AsRef<OsStr>>(&mut self, extension: S) -> bool {
236 if self.inner.file_name().is_none() {
237 return false;
238 }
239 let mut stem = match self.file_stem() {
240 Some(stem) => stem.to_os_string(),
241 None => OsString::new(),
242 };
243
244 if !os_str_as_u8_slice(extension.as_ref()).is_empty() {
245 stem.push(".");
246 stem.push(extension);
247 }
248 self.set_file_name(&stem);
250 true
251 }
252 pub fn into_boxed_path(self) -> Box<Path> {
254 self.inner.into_boxed_path()
255 }
256 pub fn into_os_string(self) -> OsString {
258 self.inner.into_os_string()
259 }
260
261 #[cfg(feature = "unstable")]
262 pub fn capacity(&self) -> usize {
264 self.inner.capacity()
265 }
266
267 #[cfg(feature = "unstable")]
268 pub fn clear(&mut self) {
270 self.inner.clear();
271 self.segments.clear();
272 self.indexes.clear();
273 self.len = 0;
274 self.init = None;
275 }
276 #[cfg(feature = "unstable")]
279 pub fn reserve(&mut self, more: usize) {
280 self.inner.reserve(more);
281 self.segments.reserve(more);
282 }
283
284 #[cfg(feature = "unstable")]
285 pub fn reserve_exact(&mut self, more: usize) {
287 self.inner.reserve_exact(more);
288 self.segments.reserve_exact(more);
289 }
290
291 #[cfg(feature = "unstable")]
292 pub fn shrink_to_fit(&mut self) {
294 self.inner.shrink_to_fit();
295 self.segments.shrink_to_fit();
296 }
297
298 #[cfg(feature = "unstable")]
299 pub fn shrink_to(&mut self, min_cap: usize) {
305 self.inner.shrink_to(min_cap);
306 self.segments.shrink_to(min_cap);
307 }
308}
309
310impl<T> From<&T> for SmartPathBuf
311where
312 T: ?Sized + AsRef<OsStr>,
313{
314 fn from(s: &T) -> SmartPathBuf {
315 SmartPathBuf::from(s.as_ref().to_os_string())
316 }
317}
318
319impl From<OsString> for SmartPathBuf {
320 fn from(s: OsString) -> SmartPathBuf {
321 let inner = PathBuf::from(s);
322 let len = inner.iter().count();
323 let init = Some(len);
324 SmartPathBuf::with_inner(inner, init, len)
325 }
326}
327
328impl From<String> for SmartPathBuf {
329 fn from(s: String) -> SmartPathBuf {
330 let inner = PathBuf::from(s);
331 let len = inner.iter().count();
332 let init = Some(len);
333 SmartPathBuf::with_inner(inner, init, len)
334 }
335}
336
337impl From<PathBuf> for SmartPathBuf {
338 fn from(s: PathBuf) -> SmartPathBuf {
339 let len = s.iter().count();
340 let init = Some(len);
341 SmartPathBuf::with_inner(s, init, len)
342 }
343}
344
345impl FromStr for SmartPathBuf {
346 type Err = Infallible;
347
348 fn from_str(s: &str) -> Result<Self, Self::Err> {
349 Ok(SmartPathBuf::from(s))
350 }
351}
352
353impl Borrow<Path> for SmartPathBuf {
354 fn borrow(&self) -> &Path {
355 self.deref()
356 }
357}
358
359impl<P: AsRef<Path>> FromIterator<P> for SmartPathBuf {
360 fn from_iter<I: IntoIterator<Item = P>>(iter: I) -> SmartPathBuf {
361 let mut s_path = SmartPathBuf::new();
362 s_path.extend(iter);
363 s_path
364 }
365}
366
367impl<P: AsRef<Path>> Extend<P> for SmartPathBuf {
368 fn extend<I: IntoIterator<Item = P>>(&mut self, iter: I) {
369 iter.into_iter().for_each(move |p| self.push(p.as_ref()));
370 }
371}
372
373impl<'a> IntoIterator for &'a SmartPathBuf {
374 type Item = &'a OsStr;
375 type IntoIter = std::path::Iter<'a>;
376 fn into_iter(self) -> std::path::Iter<'a> { self.iter() }
377}
378
379impl Debug for SmartPathBuf {
380 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
381 fmt::Debug::fmt(&**self, formatter)
382 }
383}
384
385impl Deref for SmartPathBuf {
386 type Target = Path;
387 fn deref(&self) -> &Path {
388 Path::new(&self.inner)
389 }
390}
391
392impl Default for SmartPathBuf {
393 fn default() -> Self {
395 SmartPathBuf::new()
396 }
397}
398
399impl Hash for SmartPathBuf {
400 fn hash<H: Hasher>(&self, h: &mut H) {
402 self.as_path().hash(h)
403 }
404}
405
406impl PartialOrd for SmartPathBuf {
407 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
408 self.components().partial_cmp(other.components())
409 }
410}
411
412impl Ord for SmartPathBuf {
413 fn cmp(&self, other: &Self) -> Ordering {
414 self.components().cmp(other.components())
415 }
416}
417
418impl Eq for SmartPathBuf {}
419
420impl PartialEq for SmartPathBuf {
421 fn eq(&self, other: &Self) -> bool {
422 self.inner.eq(other.as_path())
423 }
424}
425
426macro_rules! impl_cmp {
427 ($lhs:ty, $rhs: ty) => {
428 impl<'a, 'b> PartialEq<$rhs> for $lhs {
429 #[inline]
430 fn eq(&self, other: &$rhs) -> bool { <Path as PartialEq>::eq(self, other) }
431 }
432
433 impl<'a, 'b> PartialEq<$lhs> for $rhs {
434 #[inline]
435 fn eq(&self, other: &$lhs) -> bool { <Path as PartialEq>::eq(self, other) }
436 }
437
438 impl<'a, 'b> PartialOrd<$rhs> for $lhs {
439 #[inline]
440 fn partial_cmp(&self, other: &$rhs) -> Option<Ordering> {
441 <Path as PartialOrd>::partial_cmp(self, other)
442 }
443 }
444
445 impl<'a, 'b> PartialOrd<$lhs> for $rhs {
446 #[inline]
447 fn partial_cmp(&self, other: &$lhs) -> Option<Ordering> {
448 <Path as PartialOrd>::partial_cmp(self, other)
449 }
450 }
451 }
452}
453
454impl_cmp!(SmartPathBuf, PathBuf);
455impl_cmp!(SmartPathBuf, Path);
456impl_cmp!(SmartPathBuf, &'a Path);
457
458macro_rules! impl_cmp_os_str {
459 ($lhs:ty, $rhs: ty) => {
460 impl<'a, 'b> PartialEq<$rhs> for $lhs {
461 #[inline]
462 fn eq(&self, other: &$rhs) -> bool { <Path as PartialEq>::eq(self, other.as_ref()) }
463 }
464
465 impl<'a, 'b> PartialEq<$lhs> for $rhs {
466 #[inline]
467 fn eq(&self, other: &$lhs) -> bool { <Path as PartialEq>::eq(self.as_ref(), other) }
468 }
469
470 impl<'a, 'b> PartialOrd<$rhs> for $lhs {
471 #[inline]
472 fn partial_cmp(&self, other: &$rhs) -> Option<Ordering> {
473 <Path as PartialOrd>::partial_cmp(self, other.as_ref())
474 }
475 }
476
477 impl<'a, 'b> PartialOrd<$lhs> for $rhs {
478 #[inline]
479 fn partial_cmp(&self, other: &$lhs) -> Option<Ordering> {
480 <Path as PartialOrd>::partial_cmp(self.as_ref(), other)
481 }
482 }
483 }
484}
485
486impl_cmp_os_str!(SmartPathBuf, OsStr);
487impl_cmp_os_str!(SmartPathBuf, &'a OsStr);
488impl_cmp_os_str!(SmartPathBuf, Cow<'a, OsStr>);
489impl_cmp_os_str!(SmartPathBuf, OsString);
490
491macro_rules! index_impl {
492 ($typ:ty, $out:ty) => {
493 impl Index<$typ> for SmartPathBuf {
494 type Output = $out;
495 #[inline]
497 fn index(&self, rng: $typ) -> &Self::Output {
498 &self.segments[rng]
499 }
500 }
501 };
502}
503
504macro_rules! index_mut_impl {
505 ($typ:ty, $out:ty) => {
506 impl IndexMut<$typ> for SmartPathBuf {
507 #[inline]
509 fn index_mut(&mut self, rng: $typ) -> &mut Self::Output {
510 &mut self.segments[rng]
511 }
512 }
513 };
514}
515
516impl Index<usize> for SmartPathBuf {
517 type Output = OsString;
518 #[inline]
519 fn index(&self, idx: usize) -> &Self::Output {
520 &self.segments[idx]
521 }
522}
523index_impl!(Range<usize>, [OsString]);
524index_impl!(RangeFull, [OsString]);
525index_impl!(RangeFrom<usize>, [OsString]);
526index_impl!(RangeTo<usize>, [OsString]);
527
528impl IndexMut<usize> for SmartPathBuf {
529 #[inline]
530 fn index_mut(&mut self, idx: usize) -> &mut Self::Output {
531 &mut self.segments[idx]
532 }
533}
534index_mut_impl!(Range<usize>, [OsString]);
535index_mut_impl!(RangeFull, [OsString]);
536index_mut_impl!(RangeFrom<usize>, [OsString]);
537index_mut_impl!(RangeTo<usize>, [OsString]);
538
539macro_rules! index_mut_smartpath_impl {
540 ($typ:ty, #[$meta:meta]) => {
541 impl PathRange<$typ> for SmartPathBuf {
542 #[$meta]
543 fn range(&self, range: $typ) -> Self {
544 let sep = if std::path::is_separator('/') {
545 "/"
546 } else {
547 "\\"
548 };
549 let is_root = self.segments.first() == Some(&OsString::from(sep));
550 let p = self.segments[range]
551 .iter()
552 .enumerate()
553 .map(|(i, p)| {
554 let seg = p.to_str().unwrap();
555 if i == 1 && is_root || i == 0 {
556 seg.to_string()
557 } else {
558 format!("{}{}", sep, seg)
559 }
560 })
561 .collect::<String>();
562 SmartPathBuf::from_str(&p).unwrap_or_default()
564 }
565 }
566 };
567}
568
569index_mut_smartpath_impl!(
570 RangeFull,
571 #[doc="Returns a new `SmartPath` from the range provided"]
572);
573
574index_mut_smartpath_impl!(
575 RangeTo<usize>,
576 #[doc="Returns a new `SmartPath` from the range provided"]
577);
578
579impl PathRange<RangeFrom<usize>> for SmartPathBuf {
580 fn range(&self, range: RangeFrom<usize>) -> Self {
582 let sep = if std::path::is_separator('/') {
583 "/"
584 } else {
585 "\\"
586 };
587 let is_root = self.segments.get(range.start) == Some(&OsString::from(sep));
588 let p = self.segments[range]
589 .iter()
590 .enumerate()
591 .map(|(i, p)| {
592 let seg = p.to_str().unwrap();
593 if i == 1 && is_root || i == 0 {
594 seg.to_string()
595 } else {
596 format!("{}{}", sep, seg)
597 }
598 })
599 .collect::<String>();
600 SmartPathBuf::from_str(&p).unwrap_or_default()
602 }
603}
604impl PathRange<Range<usize>> for SmartPathBuf {
605 fn range(&self, range: Range<usize>) -> Self {
607 let sep = if std::path::is_separator('/') {
608 "/"
609 } else {
610 "\\"
611 };
612 let is_root = self.segments.get(range.start) == Some(&OsString::from(sep));
613 let p = self.segments[range]
614 .iter()
615 .enumerate()
616 .map(|(i, p)| {
617 let seg = p.to_str().unwrap();
618 if i == 1 && is_root || i == 0 {
619 seg.to_string()
620 } else {
621 format!("{}{}", sep, seg)
622 }
623 })
624 .collect::<String>();
625 SmartPathBuf::from_str(&p).unwrap_or_default()
627 }
628}
629
630#[cfg(test)]
631mod tests {
632 use super::*;
633
634 macro_rules! testy {
635 (
637 start: $start:expr,
638 init_len: $init_len:expr,
639 push: $push:expr,
640 push_len: $push_len:expr,
641 pop_count: $pop_count:expr,
642 pop_len: $pop_len:expr,
643 cmp: $cmp:expr,
644 sep_char: $sep_char:expr,
645 ) => {
646 let mut s_path = SmartPathBuf::from($start);
647 assert_eq!(
648 $init_len, s_path.len,
649 "initial smart path len {} init {}",
650 s_path.len, $init_len
651 );
652 assert_eq!(
653 $init_len,
654 s_path.segments.len(),
655 "segments init len {} {}",
656 s_path.segments.len(),
657 $init_len,
658 );
659
660 for p in $push.iter() {
661 s_path.push(p)
662 }
663 assert_eq!($push_len, s_path.len);
664 assert_eq!($push_len, s_path.segments.len());
665
666 for _ in 0..$pop_count {
667 s_path.pop();
668 }
669 assert_eq!($pop_len, s_path.len);
670 assert_eq!($pop_len, s_path.segments.len());
671
672 let expected = PathBuf::from(&$cmp);
673 assert_eq!(&expected, s_path.as_path());
674 let seg_path = PathBuf::from_str(s_path.range(..).as_os_str().to_str().unwrap());
675 assert_eq!(expected, seg_path.unwrap(), "segments collected");
676 };
677 (
679 start: $start:expr,
680 init_len: $init_len:expr,
681 push: $push:expr,
682 push_len: $push_len:expr,
683 cmp: $cmp:expr,
684 ) => {
685 let mut s_path = SmartPathBuf::from($start);
686 assert_eq!($init_len, s_path.len);
687 assert_eq!(Some($init_len), s_path.init);
688 assert_eq!($init_len, s_path.segments.len());
689
690 for p in $push.iter() {
691 s_path.push(p)
692 }
693
694 assert_eq!($push_len, s_path.len);
695 assert_eq!($push_len, s_path.segments.len());
696
697 s_path.initial();
698
699 assert_eq!($init_len, s_path.len, "Initial len");
700 assert_eq!(Some($init_len), s_path.init);
701 assert_eq!($init_len, s_path.segments.len());
702
703 assert_eq!(&PathBuf::from(&$cmp), s_path.as_path());
704 };
705 (
707 start: $start:expr,
708 init_len: $init_len:expr,
709 push: $push:expr,
710 push_len: $push_len:expr,
711 call: $call:ident,
712 pop_len: $pop_len:expr,
713 cmp: $cmp:expr,
714 ) => {
715 let mut s_path = SmartPathBuf::from($start);
716 assert_eq!($init_len, s_path.len);
717 assert_eq!(Some($init_len), s_path.init);
718 assert_eq!($init_len, s_path.segments.len());
719
720 for p in $push.iter() {
721 s_path.push(p)
722 }
723
724 assert_eq!($push_len, s_path.len);
725 assert_eq!($push_len, s_path.segments.len());
726
727 s_path.$call();
728
729 assert_eq!($pop_len, s_path.len);
730 assert_eq!($pop_len, s_path.segments.len());
731
732 assert_eq!(&PathBuf::from(&$cmp), s_path.as_path());
733 };
734 (
736 start: $start:expr,
737 cmp: $cmp:expr,
738 file_name: $file_name:expr,
739 extension: $extension:expr,
740 ) => {
741 let mut s_path = SmartPathBuf::from($start);
742
743 s_path.set_file_name(&$file_name);
744 let stem = s_path.file_stem().map(|p| p.to_str().unwrap()).unwrap();
745 let expected_stem: &str = $file_name;
746 assert_eq!(expected_stem, stem);
747
748 s_path.set_extension(&$extension);
749 let ext = s_path.extension().map(|p| p.to_str().unwrap()).unwrap();
750 let expected_ext: &str = $extension;
751 assert_eq!(expected_ext, ext);
752
753 assert_eq!(&PathBuf::from(&$cmp), s_path.as_path());
754 };
755 }
756
757 #[test]
758 fn test_eq_ord() {
759 let s = SmartPathBuf::from_str("folder/file").unwrap();
760
761 assert!(s == Path::new("folder/file"));
762 assert!(s == PathBuf::from("folder/file"));
763 assert!(s == OsStr::new("folder/file"));
764 assert!(s == OsString::from("folder/file"));
765 assert!(s == *OsStr::new("folder/file"));
766
767 assert!(s > Path::new("a"));
768 assert!(s > PathBuf::from("a"));
769 assert!(s > OsStr::new("a"));
770 assert!(s > OsString::from("a"));
771 assert!(s > *OsStr::new("a"));
772 }
773
774 #[test]
775 fn test_range() {
776 let s = SmartPathBuf::from_str("/home/hello/../knuth").unwrap();
777
778 assert_eq!(Path::new("/home/hello/../knuth"), s.range(..).as_path());
779 assert_eq!(Path::new("home/hello/../knuth"), s.range(1..).as_path());
780 assert_eq!(Path::new("hello/../knuth"), s.range(2..).as_path());
781 assert_eq!(Path::new("/home/hello/"), s.range(..3).as_path());
782 assert_eq!(Path::new("home"), s.range(1..2).as_path());
783 assert_eq!(
784 Path::new("/home/hello/../knuth"),
785 s.range(0..s.len()).as_path()
786 );
787 }
788
789 #[test]
790 fn seg_join() {
791 let s = SmartPathBuf::from_str("/home/hello/../knuth").unwrap();
792 let seg_path = s.range(..);
793 assert_eq!(s, seg_path, "segments collected");
794 }
795
796 #[test]
797 #[cfg(not(windows))]
798 fn test_unix() {
799 testy!(
800 start: "/home/file.txt",
801 cmp: "/home/file.txt",
802 file_name: "file",
803 extension: "txt",
804 );
805
806 testy!(
807 start: "/home",
808 init_len: 2,
809 push: ["linux", "hello", "bye"],
810 push_len: 5,
811 cmp: "/home",
812 );
813
814 testy!(
815 start: "/home",
816 init_len: 2,
817 push: ["linux", "hello/bye"],
818 push_len: 5,
819 call: pop_last,
820 pop_len: 3,
821 cmp: "/home/linux",
822 );
823
824 testy!(
825 start: "/home",
826 init_len: 2,
827 push: ["linux", "hello", "bye"],
828 push_len: 5,
829 pop_count: 2,
830 pop_len: 3,
831 cmp: "/home/linux",
832 sep_char: "/",
833 );
834 }
835
836 #[test]
837 #[cfg(windows)]
838 fn test_windows() {
839 testy!(
840 start: "c:\\",
841 init_len: 2,
842 push: ["windows", "hello", "bye"],
843 push_len: 5,
844 pop_count: 2,
845 pop_len: 3,
846 cmp: "c:\\windows",
847 sep_char: "",
848 );
849 }
850
851 #[test]
852 fn test_froms() {
853 let pb = PathBuf::from_str("hello/bye").expect("pathbuf failed");
854 let os = OsString::from("hello/bye");
855
856 let spb = SmartPathBuf::from(&pb);
857 let sos = SmartPathBuf::from(&os);
858
859 let spb_ref: &OsStr = spb.as_ref();
860 let os_ref: &OsStr = os.as_ref();
861 assert_eq!(spb_ref, os_ref);
862
863 let sos_ref: &Path = sos.as_ref();
864 let p_ref: &Path = pb.as_ref();
865 assert_eq!(sos_ref, p_ref);
866 }
867
868 #[test]
869 fn test_push() {
870 let mut path = SmartPathBuf::new();
871 assert!(path.init.is_none());
872 path.push("hello/world/bye");
873 assert_eq!(path.init, Some(3));
874 assert!(path.indexes.is_empty());
875 assert_eq!(
876 path.segments,
877 [
878 OsString::from("hello"),
879 OsString::from("world"),
880 OsString::from("bye")
881 ]
882 );
883
884 let p: &Path = path.as_ref();
885 assert_eq!(p, Path::new("hello/world/bye"));
886 }
887
888 #[test]
889 fn test_pop() {
890 let mut tp = SmartPathBuf::from("");
891 tp.push("some");
892 tp.pop();
893
894 let mut path = SmartPathBuf::from("from/you");
895 assert_eq!(path.len, 2);
896 assert_eq!(path.segments.len(), 2);
897 path.push("hello");
898 path.push("hello/bye");
899 assert_eq!(path.segments.len(), 5);
900 assert_eq!(path.len, 5);
901 path.push("hello");
902 path.pop();
904 assert_eq!(path.segments.len(), 5);
905 assert_eq!(path.len, 5);
906 assert_eq!(path.init, Some(2));
907 assert_eq!(path.indexes, vec![1, 2]);
908 let p: &Path = path.as_ref();
909 assert_eq!(p, Path::new("from/you/hello/hello/bye"));
910 }
911
912 #[test]
913 fn test_initial() {
914 let mut path = SmartPathBuf::from("from/you");
915 assert_eq!(path.len, 2);
916 path.push("hello");
917 path.push("hello/bye");
918 assert_eq!(path.len, 5);
919 path.push("hello");
920
921 path.initial();
922 assert!(path.indexes.is_empty());
923 let s: &OsStr = path.as_ref();
924 let s = s.to_str().expect("");
925 assert_eq!(s, "from/you")
926 }
927
928 #[test]
929 fn test_pop_last() {
930 let mut path = SmartPathBuf::from("from/you");
931 assert_eq!(path.len, 2);
932 path.push("hello");
933 path.push("hello/bye");
934 assert_eq!(path.init, Some(2));
935 assert_eq!(path.len, 5);
936 path.pop_last();
937 assert_eq!(path.len, 3);
938 let p: &Path = path.as_ref();
939 assert_eq!(p, Path::new("from/you/hello"));
940 }
941
942}