smart_path/
s_path_buf.rs

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
12/// Allows `SmartPathBuf`s to be sliced using `Range` syntax.
13///
14/// # Example
15/// ```
16/// use std::path::PathBuf;
17/// use smart_path::{SmartPathBuf, PathRange};
18///
19/// let mut path = SmartPathBuf::from("hello/world/bye");
20/// let p = path.range(..path.len() - 1);
21/// assert_eq!(p.as_path(), PathBuf::from("hello/world").as_path());
22/// ```
23pub 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    /// Constructs a new `SmartPathBuf`.
42    /// 
43    /// # Examples
44    /// ```
45    /// use smart_path::SmartPathBuf;
46    /// 
47    /// let mut smart_path = SmartPathBuf::new();
48    /// ```
49    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    /// Returns a `Path` same as [`as_ref`] but more explicit.
80    /// 
81    /// [`as_ref`]: struct.SmartPathBuf.html#method.pop
82    pub fn as_path(&self) -> &Path {
83        self
84    }
85    /// `len` is the number of segments, on "*nix" systems
86    /// if absolute this includes "/".
87    pub fn len(&self) -> usize {
88        self.len
89    }
90    /// If there are no segments in this path returns true.
91    pub fn is_empty(&self) -> bool {
92        self.len == 0
93    }
94    /// When pushing segments to a new `SmartPathBuf` first push sets the initial size,
95    /// using one of the from methods also sets initial size.
96    ///
97    /// # Examples
98    /// ```
99    /// use std::path::PathBuf;
100    /// use smart_path::SmartPathBuf;
101    ///
102    /// let mut path = SmartPathBuf::new();
103    /// path.push("hello/world/bye");
104    /// // or
105    /// let mut path = SmartPathBuf::from("hello/world/bye");
106    ///
107    /// path.push("more/stuff");
108    /// path.initial();
109    /// assert_eq!(path.as_path(), PathBuf::from("hello/world/bye").as_path());
110    /// ```
111    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    // TODO make more efficient ??
134    /// Remove segment from end of path.
135    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    /// Remove the last added segment(s) from end of path.
148    /// 
149    /// # Examples
150    /// ```
151    /// use std::path::PathBuf;
152    /// use smart_path::SmartPathBuf;
153    ///
154    /// let mut path = SmartPathBuf::new();
155    /// path.push("hello/world/bye");
156    /// path.push("more/stuff");
157    /// path.pop_last();
158    /// assert_eq!(path.as_path(), PathBuf::from("hello/world/bye").as_path());
159    /// ```
160    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    /// Resets `SmartPathBuf` to the initial path.
168    /// 
169    /// # Examples
170    /// ```
171    /// use std::path::PathBuf;
172    /// use smart_path::SmartPathBuf;
173    ///
174    /// let mut path = SmartPathBuf::new();
175    /// path.push("hello/world/bye");
176    /// path.push("more/stuff");
177    /// path.push("even/more");
178    /// path.initial();
179    /// assert_eq!(path.as_path(), PathBuf::from("hello/world/bye").as_path());
180    /// ```
181    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    /// Updates `file_name` to the given file name.
190    ///
191    /// If `file_name` was `None`, this is equivalent to pushing
192    /// another segment that is a file name.
193    ///
194    /// Otherwise it is equivalent to calling `pop` and then pushing
195    /// a file name.
196    ///
197    /// # Examples
198    ///
199    /// ```
200    /// use smart_path::SmartPathBuf;
201    ///
202    /// let mut buf = SmartPathBuf::from("/");
203    /// assert!(buf.file_name() == None);
204    /// buf.set_file_name("bar");
205    /// assert!(buf == SmartPathBuf::from("/bar"));
206    /// assert!(buf.file_name().is_some());
207    /// buf.set_file_name("baz.txt");
208    /// assert!(buf == SmartPathBuf::from("/baz.txt"));
209    /// ```
210    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    /// Updates `extension` to the given extension.
217    ///
218    /// Returns `false` and does nothing if `file_name` is `None`,
219    /// returns `true` and updates the extension otherwise.
220    ///
221    /// # Examples
222    ///
223    /// ```
224    /// use std::path::Path;
225    /// use smart_path::SmartPathBuf;
226    ///
227    /// let mut p = SmartPathBuf::from("/feel/the");
228    ///
229    /// p.set_extension("force");
230    /// assert_eq!(Path::new("/feel/the.force"), p.as_path());
231    ///
232    /// p.set_extension("dark_side");
233    /// assert_eq!(Path::new("/feel/the.dark_side"), p.as_path());
234    /// ```
235    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        // add to segments and inner
249        self.set_file_name(&stem);
250        true
251    }
252    /// Boxes a `Path` up for you.
253    pub fn into_boxed_path(self) -> Box<Path> {
254        self.inner.into_boxed_path()
255    }
256    /// Returns a new `OsString`.
257    pub fn into_os_string(self) -> OsString {
258        self.inner.into_os_string()
259    }
260
261    #[cfg(feature = "unstable")]
262    /// The amount the inner `Vec` is capable of holding.
263    pub fn capacity(&self) -> usize {
264        self.inner.capacity()
265    }
266
267    #[cfg(feature = "unstable")]
268    /// Remove and reset everything.
269    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    /// Reserve about `more` amount of capacity, the compiler may
277    /// decide to reserve more than the suggested amount.
278    #[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    /// Reserve exactly `more` amount of capacity.
286    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    /// Shrink the inner `Vec` so there is no excess capacity. 
293    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    /// Shrink the inner `Vec` to specified capacity.
300    /// 
301    /// # Safety
302    /// this will panic if the given `min_cap` is larger than
303    /// current capacity (you can not use this to grow the `Vec`). 
304    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    /// Constructs an empty `SmartPathBuf`.
394    fn default() -> Self {
395        SmartPathBuf::new()
396    }
397}
398
399impl Hash for SmartPathBuf {
400    /// Hashes the inner `Path` value not `SmartPathBuf`.
401    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            /// On unix the `/` is always the first element in a Path
496            #[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            /// On unix the `/` is always the first element in a Path
508            #[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                // unwrap should be ok we had a valid path before
563                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    /// Returns a new `SmartPath` from the range provided
581    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        // unwrap should be ok we had a valid path before
601        SmartPathBuf::from_str(&p).unwrap_or_default()
602    }
603}
604impl PathRange<Range<usize>> for SmartPathBuf {
605    /// Returns a new `SmartPath` from the range provided
606    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        // unwrap should be ok we had a valid path before
626        SmartPathBuf::from_str(&p).unwrap_or_default()
627    }
628}
629
630#[cfg(test)]
631mod tests {
632    use super::*;
633    
634    macro_rules! testy {
635        // tests push and pop counts for segments, initial and len
636        (
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        // tests initial
678        (
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        // tests pop_* methods
706        (
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        // tests file name and extension methods
735        (
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.push("hello/world/bye");
903        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}