1use std::fmt;
2use std::sync::Arc;
3
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5
6use crate::{FsError, FsResult};
7
8#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
10pub struct VirtualPath {
11 canonical: Arc<str>,
12}
13
14impl VirtualPath {
15 pub fn parse(input: &str) -> FsResult<Self> {
17 if !input.starts_with('/') || input.contains('\0') {
18 return Err(FsError::invalid_path_or_name(input));
19 }
20
21 Ok(Self {
22 canonical: normalize(input.split('/')),
23 })
24 }
25
26 pub fn root() -> Self {
28 Self {
29 canonical: Arc::from("/"),
30 }
31 }
32
33 pub fn as_str(&self) -> &str {
35 &self.canonical
36 }
37
38 pub fn name(&self) -> Option<&str> {
40 if self.canonical.len() == 1 {
41 None
42 } else {
43 self.canonical.rsplit('/').next()
44 }
45 }
46
47 pub fn parent(&self) -> Option<Self> {
49 let separator = self.canonical.rfind('/')?;
50 if separator == 0 {
51 if self.canonical.len() == 1 {
52 None
53 } else {
54 Some(Self::root())
55 }
56 } else {
57 Some(Self {
58 canonical: Arc::from(&self.canonical[..separator]),
59 })
60 }
61 }
62
63 pub fn join(&self, relative: &str) -> FsResult<Self> {
65 if relative.starts_with('/') || relative.contains('\0') {
66 return Err(FsError::invalid_path_or_name(relative));
67 }
68
69 Ok(Self {
70 canonical: normalize(self.segments().chain(relative.split('/'))),
71 })
72 }
73
74 pub fn segments(&self) -> impl Iterator<Item = &str> {
76 self.canonical[1..]
77 .split('/')
78 .filter(|segment| !segment.is_empty())
79 }
80}
81
82impl AsRef<str> for VirtualPath {
83 fn as_ref(&self) -> &str {
84 self.as_str()
85 }
86}
87
88impl fmt::Display for VirtualPath {
89 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
90 formatter.write_str(self.as_str())
91 }
92}
93
94impl Serialize for VirtualPath {
95 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
96 where
97 S: Serializer,
98 {
99 serializer.serialize_str(self.as_str())
100 }
101}
102
103impl<'de> Deserialize<'de> for VirtualPath {
104 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
105 where
106 D: Deserializer<'de>,
107 {
108 let input = String::deserialize(deserializer)?;
109 Self::parse(&input).map_err(serde::de::Error::custom)
110 }
111}
112
113#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
119pub struct LinkTarget {
120 canonical: Arc<str>,
121}
122
123impl LinkTarget {
124 pub fn parse(input: &str) -> FsResult<Self> {
130 if input.is_empty() || input.contains('\0') {
131 return Err(FsError::invalid_path_or_name(input));
132 }
133
134 let canonical = if input.starts_with('/') {
135 normalize(input.split('/'))
136 } else {
137 normalize_relative(input.split('/'))
138 };
139
140 Ok(Self { canonical })
141 }
142
143 pub fn as_str(&self) -> &str {
145 &self.canonical
146 }
147
148 pub fn is_absolute(&self) -> bool {
150 self.canonical.starts_with('/')
151 }
152}
153
154impl AsRef<str> for LinkTarget {
155 fn as_ref(&self) -> &str {
156 self.as_str()
157 }
158}
159
160impl fmt::Display for LinkTarget {
161 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
162 formatter.write_str(self.as_str())
163 }
164}
165
166impl Serialize for LinkTarget {
167 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
168 where
169 S: Serializer,
170 {
171 serializer.serialize_str(self.as_str())
172 }
173}
174
175impl<'de> Deserialize<'de> for LinkTarget {
176 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
177 where
178 D: Deserializer<'de>,
179 {
180 let input = String::deserialize(deserializer)?;
181 Self::parse(&input).map_err(serde::de::Error::custom)
182 }
183}
184
185fn normalize<'a>(segments: impl IntoIterator<Item = &'a str>) -> Arc<str> {
186 let mut normalized = Vec::new();
187
188 for segment in segments {
189 match segment {
190 "" | "." => {}
191 ".." => {
192 normalized.pop();
193 }
194 _ => normalized.push(segment),
195 }
196 }
197
198 if normalized.is_empty() {
199 return Arc::from("/");
200 }
201
202 let capacity = normalized.iter().map(|segment| segment.len() + 1).sum();
203 let mut canonical = String::with_capacity(capacity);
204 for segment in normalized {
205 canonical.push('/');
206 canonical.push_str(segment);
207 }
208 Arc::from(canonical)
209}
210
211fn normalize_relative<'a>(segments: impl IntoIterator<Item = &'a str>) -> Arc<str> {
212 let mut normalized = Vec::new();
213
214 for segment in segments {
215 match segment {
216 "" | "." => {}
217 ".." if normalized.last().is_some_and(|last| *last != "..") => {
218 normalized.pop();
219 }
220 ".." => normalized.push(segment),
221 _ => normalized.push(segment),
222 }
223 }
224
225 if normalized.is_empty() {
226 return Arc::from(".");
227 }
228
229 Arc::from(normalized.join("/"))
230}