brume/vfs/
virtual_path.rs1use std::{borrow::Borrow, ops::Deref, path::Path};
4
5use serde::{Deserialize, Serialize};
6use thiserror::Error;
7
8use super::NodeKind;
9
10#[derive(Error, Debug)]
11pub enum InvalidPathError {
12 #[error("path {0:?} not found in the VFS")]
13 NotFound(VirtualPathBuf),
14 #[error("expected a directory at {0:?}")]
15 NotADir(VirtualPathBuf),
16 #[error("expected a file at {0:?}")]
17 NotAFile(VirtualPathBuf),
18}
19
20impl InvalidPathError {
21 pub fn for_kind(kind: NodeKind, path: &VirtualPath) -> Self {
22 match kind {
23 NodeKind::Dir => Self::NotADir(path.to_owned()),
24 NodeKind::File => Self::NotAFile(path.to_owned()),
25 }
26 }
27}
28
29#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
32#[repr(transparent)]
33pub struct VirtualPath {
34 path: str,
35}
36
37#[derive(Error, Debug)]
38pub enum VirtualPathError {
39 #[error("this string cannot be converted into a path: {0}")]
40 InvalidString(String),
41 #[error("the path {subpath} is not a valid subpath for {base}")]
42 NotASubpath { base: String, subpath: String },
43}
44
45impl VirtualPath {
46 pub const fn root() -> &'static Self {
48 unsafe { &*("/" as *const str as *const Self) }
50 }
51
52 pub fn is_root(&self) -> bool {
54 &self.path == "/"
55 }
56
57 pub fn len(&self) -> usize {
59 self.iter().count()
60 }
61
62 pub fn is_empty(&self) -> bool {
64 self.is_root()
65 }
66
67 fn new(path: &str) -> &Self {
69 unsafe { &*(path as *const str as *const Self) }
71 }
72
73 fn trimmed_path(&self) -> &str {
75 if let Some(prefix) = self.path.strip_suffix('/') {
76 prefix
77 } else {
78 &self.path
79 }
80 }
81
82 pub fn chroot(&self, new_root: &VirtualPath) -> Result<&Self, VirtualPathError> {
84 let new_root_path = new_root.trimmed_path();
85
86 if let Some(new_path) = self.path.strip_prefix(new_root_path) {
87 new_path.try_into()
88 } else {
89 Err(VirtualPathError::NotASubpath {
90 base: self.path.to_string(),
91 subpath: new_root.path.to_string(),
92 })
93 }
94 }
95
96 pub fn is_inside(&self, other: &VirtualPath) -> bool {
99 if let Some(suffix) = self.path.strip_prefix(other.trimmed_path()) {
100 suffix.is_empty() || suffix.starts_with('/')
103 } else {
104 false
105 }
106 }
107
108 pub fn name(&self) -> &str {
110 let path = self.trimmed_path();
111
112 path.rsplit_once('/')
115 .map(|(_, suffix)| suffix)
116 .unwrap_or("")
117 }
118
119 pub fn parent(&self) -> Option<&Self> {
121 let path = self.trimmed_path();
122
123 path.rsplit_once('/').map(|(prefix, _)| {
125 if prefix.is_empty() {
126 Self::root()
127 } else {
128 prefix.try_into().unwrap()
129 }
130 })
131 }
132
133 pub fn top_level(&self) -> Option<&str> {
136 if self.is_root() {
137 return None;
138 }
139
140 let noroot = &self.path[1..];
141
142 if let Some(end_pos) = noroot.find('/') {
143 Some(&noroot[..end_pos])
144 } else {
145 Some(self.name())
146 }
147 }
148
149 pub fn top_level_split(&self) -> Option<(&str, &Self)> {
152 if self.is_root() {
153 return None;
154 }
155
156 let noroot = &self.path[1..];
157
158 if let Some(end_pos) = noroot.find('/') {
159 let top_level = &noroot[..end_pos];
161 let remainder = self.path[(end_pos + 1)..].try_into().unwrap();
162 Some((top_level, remainder))
163 } else {
164 Some((self.name(), Self::root()))
165 }
166 }
167
168 pub fn iter(&self) -> VirtualPathIterator {
170 VirtualPathIterator { path: self }
171 }
172}
173
174impl<'a> TryFrom<&'a str> for &'a VirtualPath {
175 type Error = VirtualPathError;
176
177 fn try_from(value: &'a str) -> Result<Self, Self::Error> {
178 if !value.starts_with('/') {
179 return Err(VirtualPathError::InvalidString(value.to_string()));
180 }
181
182 if value.contains("//") {
183 return Err(VirtualPathError::InvalidString(value.to_string()));
184 }
185
186 Ok(VirtualPath::new(value))
187 }
188}
189
190impl<'a> From<&'a VirtualPath> for &'a str {
191 fn from(value: &'a VirtualPath) -> Self {
192 &value.path
193 }
194}
195
196impl Borrow<VirtualPath> for VirtualPathBuf {
197 fn borrow(&self) -> &VirtualPath {
198 self.deref()
199 }
200}
201
202impl Deref for VirtualPathBuf {
203 type Target = VirtualPath;
204
205 fn deref(&self) -> &VirtualPath {
206 VirtualPath::new(&self.path)
207 }
208}
209
210impl AsRef<VirtualPath> for VirtualPathBuf {
211 fn as_ref(&self) -> &VirtualPath {
212 self.deref()
213 }
214}
215
216impl ToOwned for VirtualPath {
217 type Owned = VirtualPathBuf;
218
219 fn to_owned(&self) -> Self::Owned {
220 VirtualPathBuf {
221 path: self.path.to_string(),
222 }
223 }
224}
225
226impl<'a> PartialEq<&'a VirtualPath> for VirtualPathBuf {
227 fn eq(&self, other: &&'a VirtualPath) -> bool {
228 self.path == other.path
229 }
230}
231
232impl PartialEq<VirtualPathBuf> for &VirtualPath {
233 fn eq(&self, other: &VirtualPathBuf) -> bool {
234 self.path == other.path
235 }
236}
237
238impl AsRef<Path> for VirtualPath {
239 fn as_ref(&self) -> &Path {
240 self.path.as_ref()
241 }
242}
243
244pub struct VirtualPathIterator<'a> {
245 path: &'a VirtualPath,
246}
247
248impl<'a> Iterator for VirtualPathIterator<'a> {
249 type Item = &'a str;
250
251 fn next(&mut self) -> Option<Self::Item> {
252 let (top_level, remainder) = self.path.top_level_split()?;
253
254 self.path = remainder;
255
256 Some(top_level)
257 }
258}
259
260#[derive(Debug, Hash, Eq, PartialEq, Ord, PartialOrd, Clone, Serialize, Deserialize)]
263#[repr(transparent)]
264pub struct VirtualPathBuf {
265 path: String,
266}
267
268impl VirtualPathBuf {
269 pub fn root() -> Self {
270 Self {
271 path: String::from('/'),
272 }
273 }
274
275 pub fn new(path: &str) -> Result<Self, VirtualPathError> {
276 let ref_path: &VirtualPath = path.try_into()?;
277
278 Ok(ref_path.to_owned())
279 }
280
281 pub fn push(&mut self, value: &str) {
283 if self.path.ends_with('/') {
284 self.path.push_str(value.trim_matches('/'));
285 } else {
286 self.path.push('/');
287 self.path.push_str(value.trim_matches('/'));
288 }
289 }
290}
291
292#[cfg(test)]
293mod test {
294 use super::*;
295
296 #[test]
297 fn new_path() {
298 let bad = VirtualPathBuf::new("a/bad/path");
299
300 assert!(bad.is_err());
301
302 let bad = VirtualPathBuf::new("/a//bad/path");
303
304 assert!(bad.is_err());
305
306 let bad = VirtualPathBuf::new("/a///bad/path");
307
308 assert!(bad.is_err());
309
310 VirtualPathBuf::new("/a/good/path").unwrap();
311
312 VirtualPathBuf::new("/a/good/path/").unwrap();
313 }
314
315 #[test]
316 fn test_chroot() {
317 let base = VirtualPathBuf::new("/a/random/path").unwrap();
318
319 let good_root = VirtualPathBuf::new("/a/random").unwrap();
320
321 assert_eq!(
322 base.chroot(&good_root).unwrap(),
323 VirtualPathBuf::new("/path").unwrap()
324 );
325
326 let other_good_root = VirtualPathBuf::new("/a/random/").unwrap();
327
328 assert_eq!(
329 base.chroot(&other_good_root).unwrap(),
330 VirtualPathBuf::new("/path").unwrap()
331 );
332
333 let bad_root = VirtualPathBuf::new("/b/random").unwrap();
334
335 assert!(base.chroot(&bad_root).is_err());
336 }
337
338 #[test]
339 fn test_name() {
340 let base = VirtualPathBuf::new("/a/random/path").unwrap();
341
342 assert_eq!(base.name(), "path");
343
344 let trailing = VirtualPathBuf::new("/a/random/path/").unwrap();
345
346 assert_eq!(trailing.name(), "path");
347
348 let root = VirtualPath::root();
349
350 assert_eq!(root.name(), "");
351 }
352
353 #[test]
354 fn test_parent() {
355 let base = VirtualPathBuf::new("/a/random/path").unwrap();
356
357 assert_eq!(
358 base.parent().unwrap(),
359 VirtualPathBuf::new("/a/random").unwrap()
360 );
361
362 let trailing = VirtualPathBuf::new("/a/random/path/").unwrap();
363
364 assert_eq!(
365 trailing.parent().unwrap(),
366 VirtualPathBuf::new("/a/random").unwrap()
367 );
368
369 let first_level = VirtualPathBuf::new("/a").unwrap();
370
371 assert_eq!(first_level.parent().unwrap(), VirtualPath::root());
372
373 let root = VirtualPathBuf::new("/").unwrap();
374
375 assert_eq!(root.parent(), None);
376 }
377
378 #[test]
379 fn test_push() {
380 let reference = VirtualPathBuf::new("/a/random/path").unwrap();
381 let mut base = VirtualPathBuf::new("/a/random").unwrap();
382 base.push("path");
383
384 assert_eq!(base, reference);
385
386 let mut base = VirtualPathBuf::new("/a/random/").unwrap();
387 base.push("path");
388
389 assert_eq!(base, reference);
390
391 let mut base = VirtualPathBuf::new("/a/random").unwrap();
392 base.push("/path");
393
394 assert_eq!(base, reference);
395
396 let mut base = VirtualPathBuf::new("/a/random/").unwrap();
397 base.push("/path");
398
399 assert_eq!(base, reference);
400 }
401
402 #[test]
403 fn test_top_level() {
404 let base = VirtualPathBuf::new("/a/random/path").unwrap();
405
406 assert_eq!(base.top_level().unwrap(), "a");
407
408 let root = VirtualPath::root();
409
410 assert!(root.top_level().is_none());
411 }
412
413 #[test]
414 fn test_top_level_split() {
415 let base = VirtualPathBuf::new("/a/random/path").unwrap();
416
417 assert_eq!(
418 base.top_level_split().unwrap(),
419 ("a", VirtualPathBuf::new("/random/path").unwrap().as_ref())
420 );
421
422 let base = VirtualPathBuf::new("/a/random/path/").unwrap();
423
424 assert_eq!(
425 base.top_level_split().unwrap(),
426 ("a", VirtualPathBuf::new("/random/path/").unwrap().as_ref())
427 );
428
429 let base = VirtualPathBuf::new("/a/").unwrap();
430
431 assert_eq!(base.top_level_split().unwrap(), ("a", VirtualPath::root()));
432
433 let base = VirtualPathBuf::new("/a").unwrap();
434
435 assert_eq!(base.top_level_split().unwrap(), ("a", VirtualPath::root()));
436
437 let root = VirtualPath::root();
438
439 assert!(root.top_level_split().is_none());
440 }
441
442 #[test]
443 fn test_is_inside() {
444 let base = VirtualPathBuf::new("/a/b").unwrap();
445 let elem = VirtualPathBuf::new("/a/b/c").unwrap();
446
447 assert!(elem.is_inside(&base));
448
449 let elem = VirtualPathBuf::new("/a/b/c/d/e").unwrap();
450
451 assert!(elem.is_inside(&base));
452
453 let elem = VirtualPathBuf::new("/a/f/g").unwrap();
454
455 assert!(!elem.is_inside(&base));
456
457 let elem = VirtualPathBuf::new("/a/baba").unwrap();
458
459 assert!(!elem.is_inside(&base));
460
461 let elem = VirtualPathBuf::new("/a/b").unwrap();
462
463 assert!(elem.is_inside(&base));
464 }
465
466 #[test]
467 fn test_iter() {
468 let elem = VirtualPathBuf::new("/a/b/c").unwrap();
469 let mut iter = elem.iter();
470
471 assert_eq!(iter.next(), Some("a"));
472 assert_eq!(iter.next(), Some("b"));
473 assert_eq!(iter.next(), Some("c"));
474 assert_eq!(iter.next(), None);
475 }
476
477 #[test]
478 fn test_len() {
479 let elem = VirtualPathBuf::new("/a/b/c").unwrap();
480
481 assert_eq!(elem.len(), 3);
482
483 let elem = VirtualPathBuf::new("/a").unwrap();
484
485 assert_eq!(elem.len(), 1);
486
487 let elem = VirtualPathBuf::new("/").unwrap();
488
489 assert_eq!(elem.len(), 0);
490 assert!(elem.is_empty());
491 }
492}