Skip to content

Commit

Permalink
fix(fs): can't use Windows path (tauri-apps#1710)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Legend-Master authored and Sir-Thom committed Oct 22, 2024
1 parent 8bb1ec4 commit 75808b9
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 10 deletions.
5 changes: 5 additions & 0 deletions .changes/fs-windows-path.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"fs": patch
---

Fix can't use Windows paths like `C:/Users/UserName/file.txt`
58 changes: 53 additions & 5 deletions plugins/fs/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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())?))
}
}

Expand Down Expand Up @@ -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(_))
));
}
}
42 changes: 37 additions & 5 deletions plugins/fs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
}
}

Expand Down

0 comments on commit 75808b9

Please sign in to comment.