warpgate_api/
virtual_path.rs1use serde::{Deserialize, Serialize};
2use std::ffi::OsStr;
3use std::fmt;
4use std::path::{Path, PathBuf};
5
6#[macro_export]
7macro_rules! inherit_methods {
8 (comparator, [$($method:ident),+ $(,)?]) => {
9 $(
10 #[doc = concat!("Inherited from [`PathBuf.", stringify!($method), "`].")]
11 pub fn $method(&self, value: impl AsRef<Path>) -> bool {
12 self.any_path().$method(value)
13 }
14 )*
15 };
16 (getter, [$($method:ident),+ $(,)?]) => {
17 $(
18 #[doc = concat!("Inherited from [`PathBuf.", stringify!($method), "`].")]
19 pub fn $method(&self) -> Option<&OsStr> {
20 self.any_path().$method()
21 }
22 )*
23 };
24 (setter, [$($method:ident),+ $(,)?]) => {
25 $(
26 #[doc = concat!("Inherited from [`PathBuf.", stringify!($method), "`].")]
27 pub fn $method(&mut self, value: impl AsRef<OsStr>) {
28 let path = match self {
29 Self::Real(base) => base,
30 Self::Virtual { path: base, .. } => base,
31 };
32
33 path.$method(value);
34 }
35 )*
36 };
37 ([$($method:ident),+ $(,)?]) => {
38 $(
39 #[doc = concat!("Inherited from [`PathBuf.", stringify!($method), "`].")]
40 pub fn $method(&self) -> bool {
41 self.any_path().$method()
42 }
43 )*
44 };
45}
46
47#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
49#[serde(untagged)]
50pub enum VirtualPath {
51 Virtual {
53 path: PathBuf,
54
55 #[serde(alias = "v")]
56 virtual_prefix: PathBuf,
57
58 #[serde(alias = "r")]
59 real_prefix: PathBuf,
60 },
61
62 Real(PathBuf),
64}
65
66impl VirtualPath {
67 inherit_methods!([exists, has_root, is_absolute, is_dir, is_file, is_relative]);
68 inherit_methods!(getter, [extension, file_name, file_stem]);
69 inherit_methods!(setter, [set_extension, set_file_name]);
70 inherit_methods!(comparator, [ends_with, starts_with]);
71
72 pub fn join<P: AsRef<Path>>(&self, path: P) -> VirtualPath {
74 match self {
75 Self::Real(base) => Self::Real(base.join(path.as_ref())),
76 Self::Virtual {
77 path: base,
78 virtual_prefix,
79 real_prefix,
80 } => Self::Virtual {
81 path: base.join(path.as_ref()),
82 virtual_prefix: virtual_prefix.clone(),
83 real_prefix: real_prefix.clone(),
84 },
85 }
86 }
87
88 pub fn parent(&self) -> Option<VirtualPath> {
90 match self {
91 Self::Real(base) => base.parent().map(|parent| Self::Real(parent.to_owned())),
92 Self::Virtual {
93 path: base,
94 virtual_prefix,
95 real_prefix,
96 } => base.parent().map(|parent| Self::Virtual {
97 path: parent.to_owned(),
98 virtual_prefix: virtual_prefix.clone(),
99 real_prefix: real_prefix.clone(),
100 }),
101 }
102 }
103
104 pub fn any_path(&self) -> &PathBuf {
107 match self {
108 Self::Real(path) => path,
109 Self::Virtual { path, .. } => path,
110 }
111 }
112
113 pub fn real_path(&self) -> Option<PathBuf> {
116 let path = match self {
117 Self::Real(path) => Some(path.to_path_buf()),
118 Self::Virtual { real_prefix, .. } => {
119 self.without_prefix().map(|path| real_prefix.join(path))
120 }
121 };
122
123 path.and_then(|path| {
124 if path.is_absolute() && path.exists() {
125 Some(path.to_owned())
126 } else {
127 None
128 }
129 })
130 }
131
132 pub fn virtual_path(&self) -> Option<PathBuf> {
134 match self {
135 Self::Real(_) => None,
136 Self::Virtual { path, .. } => Some(path.to_owned()),
137 }
138 }
139
140 pub fn without_prefix(&self) -> Option<&Path> {
143 match self {
144 Self::Real(_) => None,
145 Self::Virtual {
146 path,
147 virtual_prefix,
148 ..
149 } => path.strip_prefix(virtual_prefix).ok(),
150 }
151 }
152}
153
154#[cfg(feature = "schematic")]
155impl schematic::Schematic for VirtualPath {
156 fn schema_name() -> Option<String> {
157 Some("VirtualPath".into())
158 }
159
160 fn build_schema(mut schema: schematic::SchemaBuilder) -> schematic::Schema {
161 schema.set_description("A container for WASI virtual paths that can also keep a reference to the original real path.");
162 schema.string(schematic::schema::StringType {
163 format: Some("path".into()),
164 ..Default::default()
165 })
166 }
167}
168
169impl Default for VirtualPath {
170 fn default() -> Self {
171 Self::Real(PathBuf::new())
172 }
173}
174
175impl fmt::Display for VirtualPath {
176 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177 write!(f, "{}", self.any_path().display())
178 }
179}
180
181impl AsRef<VirtualPath> for VirtualPath {
182 fn as_ref(&self) -> &VirtualPath {
183 self
184 }
185}
186
187impl AsRef<PathBuf> for VirtualPath {
188 fn as_ref(&self) -> &PathBuf {
189 self.any_path()
190 }
191}
192
193impl AsRef<Path> for VirtualPath {
194 fn as_ref(&self) -> &Path {
195 self.any_path()
196 }
197}