relative_path/
relative_path_buf.rs

1use core::cmp;
2use core::fmt;
3use core::hash::{Hash, Hasher};
4use core::iter::FromIterator;
5use core::ops;
6use core::str;
7
8use alloc::borrow::{Borrow, Cow, ToOwned};
9use alloc::boxed::Box;
10use alloc::string::String;
11
12#[cfg(feature = "std")]
13use std::path;
14
15use super::{Component, RelativePath, PARENT_STR, SEP, STEM_SEP};
16
17#[cfg(feature = "std")]
18use super::{FromPathError, FromPathErrorKind};
19
20/// Traverse the given components and apply to the provided stack.
21///
22/// This takes '.', and '..' into account. Where '.' doesn't change the stack, and '..' pops the
23/// last item or further adds parent components.
24#[inline(always)]
25pub(super) fn relative_traversal<'a, C>(buf: &mut RelativePathBuf, components: C)
26where
27    C: IntoIterator<Item = Component<'a>>,
28{
29    use self::Component::{CurDir, Normal, ParentDir};
30
31    for c in components {
32        match c {
33            CurDir => (),
34            ParentDir => match buf.components().next_back() {
35                Some(Component::ParentDir) | None => {
36                    buf.push(PARENT_STR);
37                }
38                _ => {
39                    buf.pop();
40                }
41            },
42            Normal(name) => {
43                buf.push(name);
44            }
45        }
46    }
47}
48
49/// An owned, mutable relative path.
50///
51/// This type provides methods to manipulate relative path objects.
52#[derive(Clone)]
53pub struct RelativePathBuf {
54    inner: String,
55}
56
57impl RelativePathBuf {
58    /// Create a new relative path buffer.
59    #[must_use]
60    #[inline]
61    pub fn new() -> RelativePathBuf {
62        RelativePathBuf {
63            inner: String::new(),
64        }
65    }
66
67    /// Internal constructor to allocate a relative path buf with the given capacity.
68    #[inline]
69    pub(super) fn with_capacity(cap: usize) -> RelativePathBuf {
70        RelativePathBuf {
71            inner: String::with_capacity(cap),
72        }
73    }
74
75    /// Get the length of the underlying string in bytes.
76    #[inline]
77    pub(super) fn len(&self) -> usize {
78        self.inner.len()
79    }
80
81    /// Try to convert a [`Path`] to a [`RelativePathBuf`].
82    ///
83    /// [`Path`]: https://doc.rust-lang.org/std/path/struct.Path.html
84    ///
85    /// # Examples
86    ///
87    /// ```
88    /// use relative_path::{RelativePath, RelativePathBuf, FromPathErrorKind};
89    /// use std::path::Path;
90    ///
91    /// assert_eq!(
92    ///     Ok(RelativePath::new("foo/bar").to_owned()),
93    ///     RelativePathBuf::from_path(Path::new("foo/bar"))
94    /// );
95    /// ```
96    ///
97    /// # Errors
98    ///
99    /// This will error in case the provided path is not a relative path, which
100    /// is identifier by it having a [`Prefix`] or [`RootDir`] component.
101    ///
102    /// [`Prefix`]: std::path::Component::Prefix
103    /// [`RootDir`]: std::path::Component::RootDir
104    #[cfg(feature = "std")]
105    #[cfg_attr(relative_path_docsrs, doc(cfg(feature = "std")))]
106    #[inline]
107    pub fn from_path<P>(path: P) -> Result<RelativePathBuf, FromPathError>
108    where
109        P: AsRef<path::Path>,
110    {
111        use std::path::Component::{CurDir, Normal, ParentDir, Prefix, RootDir};
112
113        let mut buffer = RelativePathBuf::new();
114
115        for c in path.as_ref().components() {
116            match c {
117                Prefix(_) | RootDir => return Err(FromPathErrorKind::NonRelative.into()),
118                CurDir => continue,
119                ParentDir => buffer.push(PARENT_STR),
120                Normal(s) => buffer.push(s.to_str().ok_or(FromPathErrorKind::NonUtf8)?),
121            }
122        }
123
124        Ok(buffer)
125    }
126
127    /// Extends `self` with `path`.
128    ///
129    /// # Examples
130    ///
131    /// ```
132    /// use relative_path::RelativePathBuf;
133    ///
134    /// let mut path = RelativePathBuf::new();
135    /// path.push("foo");
136    /// path.push("bar");
137    ///
138    /// assert_eq!("foo/bar", path);
139    ///
140    /// let mut path = RelativePathBuf::new();
141    /// path.push("foo");
142    /// path.push("/bar");
143    ///
144    /// assert_eq!("foo/bar", path);
145    /// ```
146    pub fn push<P>(&mut self, path: P)
147    where
148        P: AsRef<RelativePath>,
149    {
150        let other = path.as_ref();
151
152        let other = if other.starts_with_sep() {
153            &other.inner[1..]
154        } else {
155            &other.inner[..]
156        };
157
158        if !self.inner.is_empty() && !self.ends_with_sep() {
159            self.inner.push(SEP);
160        }
161
162        self.inner.push_str(other);
163    }
164
165    /// Updates [`file_name`] to `file_name`.
166    ///
167    /// If [`file_name`] was [`None`], this is equivalent to pushing
168    /// `file_name`.
169    ///
170    /// Otherwise it is equivalent to calling [`pop`] and then pushing
171    /// `file_name`. The new path will be a sibling of the original path. (That
172    /// is, it will have the same parent.)
173    ///
174    /// [`file_name`]: RelativePath::file_name
175    /// [`pop`]: RelativePathBuf::pop
176    /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html
177    ///
178    /// # Examples
179    ///
180    /// ```
181    /// use relative_path::RelativePathBuf;
182    ///
183    /// let mut buf = RelativePathBuf::from("");
184    /// assert!(buf.file_name() == None);
185    /// buf.set_file_name("bar");
186    /// assert_eq!(RelativePathBuf::from("bar"), buf);
187    ///
188    /// assert!(buf.file_name().is_some());
189    /// buf.set_file_name("baz.txt");
190    /// assert_eq!(RelativePathBuf::from("baz.txt"), buf);
191    ///
192    /// buf.push("bar");
193    /// assert!(buf.file_name().is_some());
194    /// buf.set_file_name("bar.txt");
195    /// assert_eq!(RelativePathBuf::from("baz.txt/bar.txt"), buf);
196    /// ```
197    pub fn set_file_name<S>(&mut self, file_name: S)
198    where
199        S: AsRef<str>,
200    {
201        if self.file_name().is_some() {
202            let popped = self.pop();
203            debug_assert!(popped);
204        }
205
206        self.push(file_name.as_ref());
207    }
208
209    /// Updates [`extension`] to `extension`.
210    ///
211    /// Returns `false` and does nothing if
212    /// [`file_name`][RelativePath::file_name] is [`None`], returns `true` and
213    /// updates the extension otherwise.
214    ///
215    /// If [`extension`] is [`None`], the extension is added; otherwise it is
216    /// replaced.
217    ///
218    /// [`extension`]: RelativePath::extension
219    ///
220    /// # Examples
221    ///
222    /// ```
223    /// use relative_path::{RelativePath, RelativePathBuf};
224    ///
225    /// let mut p = RelativePathBuf::from("feel/the");
226    ///
227    /// p.set_extension("force");
228    /// assert_eq!(RelativePath::new("feel/the.force"), p);
229    ///
230    /// p.set_extension("dark_side");
231    /// assert_eq!(RelativePath::new("feel/the.dark_side"), p);
232    ///
233    /// assert!(p.pop());
234    /// p.set_extension("nothing");
235    /// assert_eq!(RelativePath::new("feel.nothing"), p);
236    /// ```
237    pub fn set_extension<S>(&mut self, extension: S) -> bool
238    where
239        S: AsRef<str>,
240    {
241        let file_stem = match self.file_stem() {
242            Some(stem) => stem,
243            None => return false,
244        };
245
246        let end_file_stem = file_stem[file_stem.len()..].as_ptr() as usize;
247        let start = self.inner.as_ptr() as usize;
248        self.inner.truncate(end_file_stem.wrapping_sub(start));
249
250        let extension = extension.as_ref();
251
252        if !extension.is_empty() {
253            self.inner.push(STEM_SEP);
254            self.inner.push_str(extension);
255        }
256
257        true
258    }
259
260    /// Truncates `self` to [`parent`][RelativePath::parent].
261    ///
262    /// # Examples
263    ///
264    /// ```
265    /// use relative_path::{RelativePath, RelativePathBuf};
266    ///
267    /// let mut p = RelativePathBuf::from("test/test.rs");
268    ///
269    /// assert_eq!(true, p.pop());
270    /// assert_eq!(RelativePath::new("test"), p);
271    /// assert_eq!(true, p.pop());
272    /// assert_eq!(RelativePath::new(""), p);
273    /// assert_eq!(false, p.pop());
274    /// assert_eq!(RelativePath::new(""), p);
275    /// ```
276    pub fn pop(&mut self) -> bool {
277        match self.parent().map(|p| p.inner.len()) {
278            Some(len) => {
279                self.inner.truncate(len);
280                true
281            }
282            None => false,
283        }
284    }
285
286    /// Coerce to a [`RelativePath`] slice.
287    #[must_use]
288    #[inline]
289    pub fn as_relative_path(&self) -> &RelativePath {
290        self
291    }
292
293    /// Consumes the `RelativePathBuf`, yielding its internal [`String`] storage.
294    ///
295    /// # Examples
296    ///
297    /// ```
298    /// use relative_path::RelativePathBuf;
299    ///
300    /// let p = RelativePathBuf::from("/the/head");
301    /// let string = p.into_string();
302    /// assert_eq!(string, "/the/head".to_owned());
303    /// ```
304    #[must_use]
305    #[inline]
306    pub fn into_string(self) -> String {
307        self.inner
308    }
309
310    /// Converts this `RelativePathBuf` into a [boxed][alloc::boxed::Box]
311    /// [`RelativePath`].
312    #[must_use]
313    #[inline]
314    pub fn into_boxed_relative_path(self) -> Box<RelativePath> {
315        let rw = Box::into_raw(self.inner.into_boxed_str()) as *mut RelativePath;
316        unsafe { Box::from_raw(rw) }
317    }
318}
319
320impl Default for RelativePathBuf {
321    #[inline]
322    fn default() -> Self {
323        RelativePathBuf::new()
324    }
325}
326
327impl<'a> From<&'a RelativePath> for Cow<'a, RelativePath> {
328    #[inline]
329    fn from(s: &'a RelativePath) -> Cow<'a, RelativePath> {
330        Cow::Borrowed(s)
331    }
332}
333
334impl<'a> From<RelativePathBuf> for Cow<'a, RelativePath> {
335    #[inline]
336    fn from(s: RelativePathBuf) -> Cow<'a, RelativePath> {
337        Cow::Owned(s)
338    }
339}
340
341impl fmt::Debug for RelativePathBuf {
342    #[inline]
343    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
344        write!(fmt, "{:?}", &self.inner)
345    }
346}
347
348impl AsRef<RelativePath> for RelativePathBuf {
349    #[inline]
350    fn as_ref(&self) -> &RelativePath {
351        RelativePath::new(&self.inner)
352    }
353}
354
355impl AsRef<str> for RelativePath {
356    #[inline]
357    fn as_ref(&self) -> &str {
358        &self.inner
359    }
360}
361
362impl Borrow<RelativePath> for RelativePathBuf {
363    #[inline]
364    fn borrow(&self) -> &RelativePath {
365        self
366    }
367}
368
369impl<'a, T: ?Sized + AsRef<str>> From<&'a T> for RelativePathBuf {
370    #[inline]
371    fn from(path: &'a T) -> RelativePathBuf {
372        RelativePathBuf {
373            inner: path.as_ref().to_owned(),
374        }
375    }
376}
377
378impl From<String> for RelativePathBuf {
379    #[inline]
380    fn from(path: String) -> RelativePathBuf {
381        RelativePathBuf { inner: path }
382    }
383}
384
385impl From<RelativePathBuf> for String {
386    #[inline]
387    fn from(path: RelativePathBuf) -> String {
388        path.into_string()
389    }
390}
391
392impl ops::Deref for RelativePathBuf {
393    type Target = RelativePath;
394
395    #[inline]
396    fn deref(&self) -> &RelativePath {
397        RelativePath::new(&self.inner)
398    }
399}
400
401impl cmp::PartialEq for RelativePathBuf {
402    #[inline]
403    fn eq(&self, other: &RelativePathBuf) -> bool {
404        self.components() == other.components()
405    }
406}
407
408impl cmp::Eq for RelativePathBuf {}
409
410impl cmp::PartialOrd for RelativePathBuf {
411    #[inline]
412    fn partial_cmp(&self, other: &RelativePathBuf) -> Option<cmp::Ordering> {
413        Some(self.cmp(other))
414    }
415}
416
417impl cmp::Ord for RelativePathBuf {
418    #[inline]
419    fn cmp(&self, other: &RelativePathBuf) -> cmp::Ordering {
420        self.components().cmp(other.components())
421    }
422}
423
424impl Hash for RelativePathBuf {
425    #[inline]
426    fn hash<H>(&self, h: &mut H)
427    where
428        H: Hasher,
429    {
430        self.as_relative_path().hash(h);
431    }
432}
433
434impl<P> Extend<P> for RelativePathBuf
435where
436    P: AsRef<RelativePath>,
437{
438    #[inline]
439    fn extend<I>(&mut self, iter: I)
440    where
441        I: IntoIterator<Item = P>,
442    {
443        iter.into_iter().for_each(move |p| self.push(p.as_ref()));
444    }
445}
446
447impl<P> FromIterator<P> for RelativePathBuf
448where
449    P: AsRef<RelativePath>,
450{
451    #[inline]
452    fn from_iter<I>(iter: I) -> RelativePathBuf
453    where
454        I: IntoIterator<Item = P>,
455    {
456        let mut buf = RelativePathBuf::new();
457        buf.extend(iter);
458        buf
459    }
460}
461
462impl fmt::Display for RelativePathBuf {
463    #[inline]
464    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
465        fmt::Display::fmt(&self.inner, f)
466    }
467}
468
469/// [`AsRef<str>`] implementation for [`RelativePathBuf`].
470///
471/// # Examples
472///
473/// ```
474/// use relative_path::RelativePathBuf;
475///
476/// let path = RelativePathBuf::from("foo/bar");
477/// let string: &str = path.as_ref();
478/// assert_eq!(string, "foo/bar");
479/// ```
480impl AsRef<str> for RelativePathBuf {
481    #[inline]
482    fn as_ref(&self) -> &str {
483        &self.inner
484    }
485}
486
487/// [`serde::ser::Serialize`] implementation for [`RelativePathBuf`].
488///
489/// ```
490/// use serde::Serialize;
491/// use relative_path::RelativePathBuf;
492///
493/// #[derive(Serialize)]
494/// struct Document {
495///     path: RelativePathBuf,
496/// }
497/// ```
498#[cfg(feature = "serde")]
499#[cfg_attr(relative_path_docsrs, doc(cfg(feature = "serde")))]
500impl serde::ser::Serialize for RelativePathBuf {
501    #[inline]
502    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
503    where
504        S: serde::ser::Serializer,
505    {
506        serializer.serialize_str(&self.inner)
507    }
508}
509
510/// [`serde::de::Deserialize`] implementation for [`RelativePathBuf`].
511///
512/// ```
513/// use serde::Deserialize;
514/// use relative_path::RelativePathBuf;
515///
516/// #[derive(Deserialize)]
517/// struct Document {
518///     path: RelativePathBuf,
519/// }
520/// ```
521#[cfg(feature = "serde")]
522#[cfg_attr(relative_path_docsrs, doc(cfg(feature = "serde")))]
523impl<'de> serde::de::Deserialize<'de> for RelativePathBuf {
524    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
525    where
526        D: serde::de::Deserializer<'de>,
527    {
528        struct Visitor;
529
530        impl serde::de::Visitor<'_> for Visitor {
531            type Value = RelativePathBuf;
532
533            #[inline]
534            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
535                formatter.write_str("a relative path")
536            }
537
538            #[inline]
539            fn visit_string<E>(self, input: String) -> Result<Self::Value, E>
540            where
541                E: serde::de::Error,
542            {
543                Ok(RelativePathBuf::from(input))
544            }
545
546            #[inline]
547            fn visit_str<E>(self, input: &str) -> Result<Self::Value, E>
548            where
549                E: serde::de::Error,
550            {
551                Ok(RelativePathBuf::from(input.to_owned()))
552            }
553        }
554
555        deserializer.deserialize_str(Visitor)
556    }
557}