Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//! Interface to use paths in the filesystem
use alloc::borrow::ToOwned;
use alloc::string::String;
use derive_more::{Deref, DerefMut};
use super::node::{Interface, Ref};
/// Memory allocated non-empty path structure
#[derive(Debug, Deref, DerefMut, Clone, PartialEq, Eq)]
pub struct Path(String);
impl From<Ref<dyn Interface>> for Path {
fn from(value: Ref<dyn Interface>) -> Self {
let node = value.lock();
if node.is_root() {
Self::new_absolute()
} else {
let mut parent_path = Self::from(node.get_parent_dir() as Ref<dyn Interface>);
parent_path.push(&node.name());
parent_path
}
}
}
impl Path {
/// Returns a new absolute path (the path of the filesystem's root)
pub fn new_absolute() -> Self {
Self("/".to_owned())
}
/// Returns a new relative path (the path of the "current" node)
#[allow(unused)]
pub fn new_relative() -> Self {
Self(".".to_owned())
}
/// Returns whether `self` is relative or not
#[allow(unused)]
pub fn is_relative(&self) -> bool {
// SAFETY: it is ensure in this structure that no empty path can be created
unsafe { self.chars().next().unwrap_unchecked() == '.' }
}
/// Returns whether `self` is absolute or not
#[allow(unused)]
pub fn is_absolute(&self) -> bool {
!self.is_relative()
}
/// Returns whether `self` represents the root or not
///
/// Is `is_relative` if `true`, then this function will always return `false`.
#[allow(unused)]
pub fn is_root(&self) -> bool {
self.is_absolute() && self.ends_with('/')
}
/// Extends `self` with a node represented as a [`String`]
pub fn push(&mut self, node: &str) {
if self.0 != "/" {
self.extend_one('/');
}
self.extend(node.chars());
}
/// Truncates `self` to its parent dir
///
/// Returns the name of the node truncated, and [`None`] if it does nothing
#[allow(unused)]
pub fn pop(&mut self) -> Option<String> {
if self.0 == "." || self.0 == "/" {
return None;
};
let binding = self.clone();
let relative = self.is_relative();
let mut ancestors = binding.split('/');
// SAFETY: the path is non empty and not reduced to "/" or "."
let node = unsafe { ancestors.next_back().unwrap_unchecked() }.to_owned();
if relative {
ancestors.next();
};
self.clone_from(&if relative { Self::new_relative() } else { Self::new_absolute() });
ancestors.for_each(|ancestor| self.push(ancestor));
Some(node)
}
}
#[cfg(test)]
mod test {
use super::Path;
#[test_case]
fn absolute() {
let root = Path::new_absolute();
assert!(root.is_absolute());
assert!(root.is_root());
assert_eq!(&root.0, "/");
}
#[test_case]
fn relative() {
let current = Path::new_relative();
assert!(current.is_relative());
assert!(!current.is_root());
assert_eq!(¤t.0, ".");
}
#[test_case]
fn absolute_manipulation() {
let mut path = Path::new_absolute();
path.push("bin");
path.push("ls");
assert_eq!(&path.0, "/bin/ls");
assert_eq!(&path.pop().unwrap(), "ls");
assert_eq!(&path.0, "/bin");
}
#[test_case]
fn relative_manipulation() {
let mut path = Path::new_relative();
path.push("foo");
path.push("bar");
assert_eq!(&path.0, "./foo/bar");
assert_eq!(&path.pop().unwrap(), "bar");
assert_eq!(&path.0, "./foo");
}
}