Skip to main content

midenc_session/
path.rs

1#[cfg(feature = "std")]
2pub use std::path::{Path, PathBuf};
3
4#[cfg(not(feature = "std"))]
5pub use self::fallback::{Path, PathBuf};
6
7#[cfg(not(feature = "std"))]
8mod fallback {
9    use alloc::{borrow::Cow, boxed::Box, vec::Vec};
10    use core::{borrow::Borrow, fmt::Display, ops::Deref};
11
12    #[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
13    pub struct PathBuf(Vec<u8>);
14
15    impl PathBuf {
16        pub const fn new() -> Self {
17            Self(Vec::new())
18        }
19
20        #[inline]
21        pub fn as_path(&self) -> &Path {
22            // SAFETY: Path just wraps [u8] and &*self.0 is &[u8], which is safe to transmute to &Path
23            unsafe { core::mem::transmute(&*self.0) }
24        }
25
26        #[inline]
27        pub fn into_boxed_path(self) -> Box<Path> {
28            unsafe { core::mem::transmute(self.0.into_boxed_slice()) }
29        }
30    }
31
32    impl From<&str> for PathBuf {
33        fn from(s: &str) -> Self {
34            Self(s.as_bytes().to_vec())
35        }
36    }
37
38    impl core::fmt::Debug for PathBuf {
39        fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
40            let s = self.as_path().to_string_lossy();
41            write!(f, "{s}")
42        }
43    }
44
45    impl Deref for PathBuf {
46        type Target = Path;
47
48        #[inline]
49        fn deref(&self) -> &Self::Target {
50            self.as_path()
51        }
52    }
53
54    impl AsRef<Path> for PathBuf {
55        #[inline]
56        fn as_ref(&self) -> &Path {
57            self.as_path()
58        }
59    }
60
61    impl Borrow<Path> for PathBuf {
62        #[inline]
63        fn borrow(&self) -> &Path {
64            self.as_path()
65        }
66    }
67
68    #[derive(PartialEq, Eq, PartialOrd, Ord)]
69    #[repr(transparent)]
70    pub struct Path([u8]);
71
72    impl core::fmt::Debug for Path {
73        fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
74            let s = self.to_string_lossy();
75            write!(f, "{s}")
76        }
77    }
78
79    impl Path {
80        pub fn to_str(&self) -> Option<&str> {
81            core::str::from_utf8(&self.0).ok()
82        }
83
84        pub fn to_string_lossy(&self) -> Cow<'_, str> {
85            alloc::string::String::from_utf8_lossy(&self.0)
86        }
87
88        pub fn to_path_buf(&self) -> PathBuf {
89            PathBuf(self.0.to_vec())
90        }
91
92        pub fn display(&self) -> impl Display + '_ {
93            self.to_string_lossy()
94        }
95
96        pub fn is_absolute(&self) -> bool {
97            self.0.starts_with(b"/")
98        }
99
100        pub fn parent(&self) -> Option<&Self> {
101            if self.0.is_empty() || self.0.ends_with(b"/") {
102                return None;
103            }
104            match self.to_str() {
105                None => match self.0.rsplit_once(|b| *b == b'/') {
106                    None => Some(Self::from_bytes(&self.0)),
107                    Some((before, _)) => Some(Self::from_bytes(before)),
108                },
109                Some(s) => match s.rsplit_once('/') {
110                    None => Some(s.as_ref()),
111                    Some((before, _)) => Some(before.as_ref()),
112                },
113            }
114        }
115
116        pub fn file_name(&self) -> Option<&Self> {
117            if self.0.ends_with(b"..") {
118                return None;
119            }
120            match self.to_str() {
121                None => match self.0.rsplit_once(|b| *b == b'/') {
122                    None => Some(Self::from_bytes(&self.0)),
123                    Some((_, after)) => Some(Self::from_bytes(after)),
124                },
125                Some(s) => match s.rsplit_once('/') {
126                    None => Some(s.as_ref()),
127                    Some((_, after)) => Some(after.as_ref()),
128                },
129            }
130        }
131
132        pub fn file_stem(&self) -> Option<&Self> {
133            let file_name = self.file_name()?;
134            match file_name.0.rsplit_once(|b| *b == b'.') {
135                None => Some(file_name),
136                Some(([], _)) => Some(file_name),
137                Some((stem, _)) => Some(Self::from_bytes(stem)),
138            }
139        }
140
141        pub fn extension(&self) -> Option<&Self> {
142            let file_name = self.file_name()?;
143            match file_name.0.rsplit_once(|b| *b == b'.') {
144                None => None,
145                Some(([], _)) => None,
146                Some((_, ext)) => Some(Self::from_bytes(ext)),
147            }
148        }
149
150        pub fn is_dir(&self) -> bool {
151            self.extension().is_none()
152        }
153
154        pub fn join<P>(&self, path: P) -> PathBuf
155        where
156            P: AsRef<Path>,
157        {
158            let path = path.as_ref();
159            if self.0.is_empty() {
160                return path.to_path_buf();
161            }
162
163            let mut buf = Vec::with_capacity(self.0.len() + path.0.len() + 1);
164            buf.extend_from_slice(&self.0);
165            buf.push(b'/');
166            buf.extend_from_slice(&path.0);
167
168            PathBuf(buf)
169        }
170
171        pub fn with_stem<S>(&self, stem: S) -> PathBuf
172        where
173            S: AsRef<str>,
174        {
175            let stem = stem.as_ref().as_bytes();
176            match self.file_name() {
177                None => {
178                    let mut buf = Vec::with_capacity(self.0.len() + stem.len());
179                    buf.extend_from_slice(&self.0);
180                    buf.extend_from_slice(stem);
181                    PathBuf(buf)
182                }
183                Some(file_name) => {
184                    let (prefix, ext) = match file_name.0.rsplit_once(|b| *b == b'.') {
185                        None => (self.0.strip_suffix(&file_name.0).unwrap(), None),
186                        Some((name, ext)) => {
187                            let len = self.0.len() - name.len() - 1 - ext.len();
188                            (&self.0[..len], Some(ext))
189                        }
190                    };
191                    let mut buf = Vec::with_capacity(
192                        prefix.len() + ext.map(|ext| ext.len()).unwrap_or_default() + stem.len(),
193                    );
194                    buf.extend_from_slice(prefix);
195                    buf.extend_from_slice(stem);
196                    if let Some(ext) = ext {
197                        buf.push(b'.');
198                        buf.extend_from_slice(ext)
199                    }
200                    PathBuf(buf)
201                }
202            }
203        }
204
205        pub fn with_extension<S>(&self, extension: S) -> PathBuf
206        where
207            S: AsRef<str>,
208        {
209            let extension = extension.as_ref().as_bytes();
210            match self.extension() {
211                None => {
212                    let mut buf = self.to_path_buf();
213                    buf.0.push(b'.');
214                    buf.0.extend_from_slice(extension);
215                    buf
216                }
217                Some(prev) => {
218                    let bytes = self.0.strip_suffix(&prev.0).unwrap();
219                    let mut buf = Vec::with_capacity(bytes.len() + extension.len());
220                    buf.extend_from_slice(bytes);
221                    buf.extend_from_slice(extension);
222                    PathBuf(buf)
223                }
224            }
225        }
226
227        pub fn with_stem_and_extension<S, E>(&self, stem: S, extension: E) -> PathBuf
228        where
229            S: AsRef<str>,
230            E: AsRef<str>,
231        {
232            let stem = stem.as_ref().as_bytes();
233            let extension = extension.as_ref().as_bytes();
234            match self.file_name() {
235                None => {
236                    let mut buf =
237                        Vec::with_capacity(self.0.len() + stem.len() + 1 + extension.len());
238                    buf.extend_from_slice(&self.0);
239                    buf.extend_from_slice(stem);
240                    buf.push(b'.');
241                    buf.extend_from_slice(extension);
242                    PathBuf(buf)
243                }
244                Some(file_name) => {
245                    let bytes = self.0.strip_suffix(&file_name.0).unwrap();
246                    let mut buf =
247                        Vec::with_capacity(bytes.len() + stem.len() + 1 + extension.len());
248                    buf.extend_from_slice(bytes);
249                    buf.extend_from_slice(stem);
250                    buf.push(b'.');
251                    buf.extend_from_slice(extension);
252                    PathBuf(buf)
253                }
254            }
255        }
256
257        #[inline(always)]
258        fn from_bytes(bytes: &[u8]) -> &Self {
259            unsafe { core::mem::transmute(bytes) }
260        }
261    }
262
263    impl AsRef<Path> for str {
264        fn as_ref(&self) -> &Path {
265            unsafe { core::mem::transmute(self.as_bytes()) }
266        }
267    }
268
269    impl AsRef<Path> for alloc::string::String {
270        fn as_ref(&self) -> &Path {
271            unsafe { core::mem::transmute(self.as_bytes()) }
272        }
273    }
274
275    impl Clone for Box<Path> {
276        fn clone(&self) -> Self {
277            self.to_path_buf().into_boxed_path()
278        }
279    }
280}