fix(fs): can't use Windows path (#1710)

* Fix fs can't use Windows path

* Add change file

* Implement `Deserialize` instead

* Rename FilePath's visitor to FilePathVisitor

* Add todo and test

* Clippy

* Unused variable
pull/1721/head
Tony 9 months ago committed by GitHub
parent 0cb99bdaf1
commit f7280c8830
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
"fs": patch
---
Fix can't use Windows paths like `C:/Users/UserName/file.txt`

@ -23,13 +23,44 @@ use std::{
use crate::{scope::Entry, Error, FilePath, FsExt};
#[derive(Debug, serde::Deserialize)]
#[serde(untagged)]
// TODO: Combine this with FilePath
#[derive(Debug)]
pub enum SafeFilePath {
Url(url::Url),
Path(SafePathBuf),
}
impl<'de> serde::Deserialize<'de> for SafeFilePath {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct SafeFilePathVisitor;
impl<'de> serde::de::Visitor<'de> for SafeFilePathVisitor {
type Value = SafeFilePath;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a string representing an file URL or a path")
}
fn visit_str<E>(self, s: &str) -> std::result::Result<Self::Value, E>
where
E: serde::de::Error,
{
SafeFilePath::from_str(s).map_err(|e| {
serde::de::Error::invalid_value(
serde::de::Unexpected::Str(s),
&e.to_string().as_str(),
)
})
}
}
deserializer.deserialize_str(SafeFilePathVisitor)
}
}
impl From<SafeFilePath> for FilePath {
fn from(value: SafeFilePath) -> Self {
match value {
@ -43,10 +74,11 @@ impl FromStr for SafeFilePath {
type Err = CommandError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Ok(url) = url::Url::from_str(s) {
Ok(Self::Url(url))
} else {
Ok(Self::Path(SafePathBuf::new(s.into())?))
if url.scheme().len() != 1 {
return Ok(Self::Url(url));
}
}
Ok(Self::Path(SafePathBuf::new(s.into())?))
}
}
@ -1168,3 +1200,19 @@ fn get_stat(metadata: std::fs::Metadata) -> FileInfo {
blocks: usm!(blocks),
}
}
mod test {
#[test]
fn safe_file_path_parse() {
use super::SafeFilePath;
assert!(matches!(
serde_json::from_str::<SafeFilePath>("\"C:/Users\""),
Ok(SafeFilePath::Path(_))
));
assert!(matches!(
serde_json::from_str::<SafeFilePath>("\"file:///C:/Users\""),
Ok(SafeFilePath::Url(_))
));
}
}

@ -50,23 +50,55 @@ pub use scope::{Event as ScopeEvent, Scope};
type Result<T> = std::result::Result<T, Error>;
// TODO: Combine this with SafeFilePath
/// Represents either a filesystem path or a URI pointing to a file
/// such as `file://` URIs or Android `content://` URIs.
#[derive(Debug, serde::Deserialize)]
#[serde(untagged)]
#[derive(Debug)]
pub enum FilePath {
Url(url::Url),
Path(PathBuf),
}
impl<'de> serde::Deserialize<'de> for FilePath {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct FilePathVisitor;
impl<'de> serde::de::Visitor<'de> for FilePathVisitor {
type Value = FilePath;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a string representing an file URL or a path")
}
fn visit_str<E>(self, s: &str) -> std::result::Result<Self::Value, E>
where
E: serde::de::Error,
{
FilePath::from_str(s).map_err(|e| {
serde::de::Error::invalid_value(
serde::de::Unexpected::Str(s),
&e.to_string().as_str(),
)
})
}
}
deserializer.deserialize_str(FilePathVisitor)
}
}
impl FromStr for FilePath {
type Err = Infallible;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
if let Ok(url) = url::Url::from_str(s) {
Ok(Self::Url(url))
} else {
Ok(Self::Path(PathBuf::from(s)))
if url.scheme().len() != 1 {
return Ok(Self::Url(url));
}
}
Ok(Self::Path(PathBuf::from(s)))
}
}

Loading…
Cancel
Save