gix_path/
relative_path.rs

1use std::path::Path;
2
3use bstr::{BStr, BString, ByteSlice};
4use gix_validate::path::component::Options;
5
6use crate::{os_str_into_bstr, try_from_bstr, try_from_byte_slice};
7
8pub(super) mod types {
9    use bstr::{BStr, ByteSlice};
10    /// A wrapper for `BStr`. It is used to enforce the following constraints:
11    ///
12    /// - The path separator always is `/`, independent of the platform.
13    /// - Only normal components are allowed.
14    /// - It is always represented as a bunch of bytes.
15    #[derive()]
16    pub struct RelativePath {
17        inner: BStr,
18    }
19
20    impl AsRef<[u8]> for RelativePath {
21        #[inline]
22        fn as_ref(&self) -> &[u8] {
23            self.inner.as_bytes()
24        }
25    }
26}
27use types::RelativePath;
28
29impl RelativePath {
30    fn new_unchecked(value: &BStr) -> Result<&RelativePath, Error> {
31        // SAFETY: `RelativePath` is transparent and equivalent to a `&BStr` if provided as reference.
32        #[allow(unsafe_code)]
33        unsafe {
34            std::mem::transmute(value)
35        }
36    }
37}
38
39/// The error used in [`RelativePath`].
40#[derive(Debug, thiserror::Error)]
41#[allow(missing_docs)]
42pub enum Error {
43    #[error("A RelativePath is not allowed to be absolute")]
44    IsAbsolute,
45    #[error(transparent)]
46    ContainsInvalidComponent(#[from] gix_validate::path::component::Error),
47    #[error(transparent)]
48    IllegalUtf8(#[from] crate::Utf8Error),
49}
50
51fn relative_path_from_value_and_path<'a>(path_bstr: &'a BStr, path: &Path) -> Result<&'a RelativePath, Error> {
52    if path.is_absolute() {
53        return Err(Error::IsAbsolute);
54    }
55
56    let options = Options::default();
57
58    for component in path.components() {
59        let component = os_str_into_bstr(component.as_os_str())?;
60        gix_validate::path::component(component, None, options)?;
61    }
62
63    RelativePath::new_unchecked(BStr::new(path_bstr.as_bytes()))
64}
65
66impl<'a> TryFrom<&'a str> for &'a RelativePath {
67    type Error = Error;
68
69    fn try_from(value: &'a str) -> Result<Self, Self::Error> {
70        relative_path_from_value_and_path(value.into(), Path::new(value))
71    }
72}
73
74impl<'a> TryFrom<&'a BStr> for &'a RelativePath {
75    type Error = Error;
76
77    fn try_from(value: &'a BStr) -> Result<Self, Self::Error> {
78        let path = try_from_bstr(value)?;
79        relative_path_from_value_and_path(value, &path)
80    }
81}
82
83impl<'a> TryFrom<&'a [u8]> for &'a RelativePath {
84    type Error = Error;
85
86    #[inline]
87    fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
88        let path = try_from_byte_slice(value)?;
89        relative_path_from_value_and_path(value.as_bstr(), path)
90    }
91}
92
93impl<'a, const N: usize> TryFrom<&'a [u8; N]> for &'a RelativePath {
94    type Error = Error;
95
96    #[inline]
97    fn try_from(value: &'a [u8; N]) -> Result<Self, Self::Error> {
98        let path = try_from_byte_slice(value.as_bstr())?;
99        relative_path_from_value_and_path(value.as_bstr(), path)
100    }
101}
102
103impl<'a> TryFrom<&'a BString> for &'a RelativePath {
104    type Error = Error;
105
106    fn try_from(value: &'a BString) -> Result<Self, Self::Error> {
107        let path = try_from_bstr(value.as_bstr())?;
108        relative_path_from_value_and_path(value.as_bstr(), &path)
109    }
110}