tauri_plugin_fs/
file_path.rs1use std::{
6 convert::Infallible,
7 path::{Path, PathBuf},
8 str::FromStr,
9};
10
11use serde::Serialize;
12use tauri::path::SafePathBuf;
13
14use crate::{Error, Result};
15
16#[derive(Debug, Serialize, Clone)]
19#[serde(untagged)]
20pub enum FilePath {
21 Url(url::Url),
23 Path(PathBuf),
25}
26
27#[derive(Debug, Clone, Serialize)]
30pub enum SafeFilePath {
31 Url(url::Url),
33 Path(SafePathBuf),
35}
36
37impl FilePath {
38 #[inline]
42 pub fn as_path(&self) -> Option<&Path> {
43 match self {
44 Self::Url(_) => None,
45 Self::Path(p) => Some(p),
46 }
47 }
48
49 #[inline]
54 pub fn into_path(self) -> Result<PathBuf> {
55 match self {
56 Self::Url(url) => url.to_file_path().map_err(|_| Error::InvalidPathUrl),
57 Self::Path(p) => Ok(p),
58 }
59 }
60
61 #[inline]
64 pub fn simplified(self) -> Self {
65 match self {
66 Self::Url(url) => Self::Url(url),
67 Self::Path(p) => Self::Path(dunce::simplified(&p).to_path_buf()),
68 }
69 }
70}
71
72impl SafeFilePath {
73 #[inline]
77 pub fn as_path(&self) -> Option<&Path> {
78 match self {
79 Self::Url(_) => None,
80 Self::Path(p) => Some(p.as_ref()),
81 }
82 }
83
84 #[inline]
89 pub fn into_path(self) -> Result<PathBuf> {
90 match self {
91 Self::Url(url) => url.to_file_path().map_err(|_| Error::InvalidPathUrl),
92 Self::Path(p) => Ok(p.as_ref().to_owned()),
93 }
94 }
95
96 #[inline]
99 pub fn simplified(self) -> Self {
100 match self {
101 Self::Url(url) => Self::Url(url),
102 Self::Path(p) => {
103 Self::Path(SafePathBuf::new(dunce::simplified(p.as_ref()).to_path_buf()).unwrap())
105 }
106 }
107 }
108}
109
110impl std::fmt::Display for FilePath {
111 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112 match self {
113 Self::Url(u) => u.fmt(f),
114 Self::Path(p) => p.display().fmt(f),
115 }
116 }
117}
118
119impl std::fmt::Display for SafeFilePath {
120 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121 match self {
122 Self::Url(u) => u.fmt(f),
123 Self::Path(p) => p.display().fmt(f),
124 }
125 }
126}
127
128impl<'de> serde::Deserialize<'de> for FilePath {
129 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
130 where
131 D: serde::Deserializer<'de>,
132 {
133 struct FilePathVisitor;
134
135 impl serde::de::Visitor<'_> for FilePathVisitor {
136 type Value = FilePath;
137
138 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
139 formatter.write_str("a string representing an file URL or a path")
140 }
141
142 fn visit_str<E>(self, s: &str) -> std::result::Result<Self::Value, E>
143 where
144 E: serde::de::Error,
145 {
146 FilePath::from_str(s).map_err(|e| {
147 serde::de::Error::invalid_value(
148 serde::de::Unexpected::Str(s),
149 &e.to_string().as_str(),
150 )
151 })
152 }
153 }
154
155 deserializer.deserialize_str(FilePathVisitor)
156 }
157}
158
159impl<'de> serde::Deserialize<'de> for SafeFilePath {
160 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
161 where
162 D: serde::Deserializer<'de>,
163 {
164 struct SafeFilePathVisitor;
165
166 impl serde::de::Visitor<'_> for SafeFilePathVisitor {
167 type Value = SafeFilePath;
168
169 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
170 formatter.write_str("a string representing an file URL or a path")
171 }
172
173 fn visit_str<E>(self, s: &str) -> std::result::Result<Self::Value, E>
174 where
175 E: serde::de::Error,
176 {
177 SafeFilePath::from_str(s).map_err(|e| {
178 serde::de::Error::invalid_value(
179 serde::de::Unexpected::Str(s),
180 &e.to_string().as_str(),
181 )
182 })
183 }
184 }
185
186 deserializer.deserialize_str(SafeFilePathVisitor)
187 }
188}
189
190impl FromStr for FilePath {
191 type Err = Infallible;
192 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
193 if let Ok(url) = url::Url::from_str(s) {
194 if url.scheme().len() != 1 {
195 return Ok(Self::Url(url));
196 }
197 }
198 Ok(Self::Path(PathBuf::from(s)))
199 }
200}
201
202impl FromStr for SafeFilePath {
203 type Err = Error;
204 fn from_str(s: &str) -> Result<Self> {
205 if let Ok(url) = url::Url::from_str(s) {
206 if url.scheme().len() != 1 {
207 return Ok(Self::Url(url));
208 }
209 }
210
211 SafePathBuf::new(s.into())
212 .map(SafeFilePath::Path)
213 .map_err(Error::UnsafePathBuf)
214 }
215}
216
217impl From<PathBuf> for FilePath {
218 fn from(value: PathBuf) -> Self {
219 Self::Path(value)
220 }
221}
222
223impl TryFrom<PathBuf> for SafeFilePath {
224 type Error = Error;
225 fn try_from(value: PathBuf) -> Result<Self> {
226 SafePathBuf::new(value)
227 .map(SafeFilePath::Path)
228 .map_err(Error::UnsafePathBuf)
229 }
230}
231
232impl From<&Path> for FilePath {
233 fn from(value: &Path) -> Self {
234 Self::Path(value.to_owned())
235 }
236}
237
238impl TryFrom<&Path> for SafeFilePath {
239 type Error = Error;
240 fn try_from(value: &Path) -> Result<Self> {
241 SafePathBuf::new(value.to_path_buf())
242 .map(SafeFilePath::Path)
243 .map_err(Error::UnsafePathBuf)
244 }
245}
246
247impl From<&PathBuf> for FilePath {
248 fn from(value: &PathBuf) -> Self {
249 Self::Path(value.to_owned())
250 }
251}
252
253impl TryFrom<&PathBuf> for SafeFilePath {
254 type Error = Error;
255 fn try_from(value: &PathBuf) -> Result<Self> {
256 SafePathBuf::new(value.to_owned())
257 .map(SafeFilePath::Path)
258 .map_err(Error::UnsafePathBuf)
259 }
260}
261
262impl From<url::Url> for FilePath {
263 fn from(value: url::Url) -> Self {
264 Self::Url(value)
265 }
266}
267
268impl From<url::Url> for SafeFilePath {
269 fn from(value: url::Url) -> Self {
270 Self::Url(value)
271 }
272}
273
274impl TryFrom<FilePath> for PathBuf {
275 type Error = Error;
276 fn try_from(value: FilePath) -> Result<Self> {
277 value.into_path()
278 }
279}
280
281impl TryFrom<SafeFilePath> for PathBuf {
282 type Error = Error;
283 fn try_from(value: SafeFilePath) -> Result<Self> {
284 value.into_path()
285 }
286}
287
288impl From<SafeFilePath> for FilePath {
289 fn from(value: SafeFilePath) -> Self {
290 match value {
291 SafeFilePath::Url(url) => FilePath::Url(url),
292 SafeFilePath::Path(p) => FilePath::Path(p.as_ref().to_owned()),
293 }
294 }
295}
296
297impl TryFrom<FilePath> for SafeFilePath {
298 type Error = Error;
299
300 fn try_from(value: FilePath) -> Result<Self> {
301 match value {
302 FilePath::Url(url) => Ok(SafeFilePath::Url(url)),
303 FilePath::Path(p) => SafePathBuf::new(p)
304 .map(SafeFilePath::Path)
305 .map_err(Error::UnsafePathBuf),
306 }
307 }
308}