mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-09-22 21:30:44 +02:00
refactor: add methods and implement traits for FilePath and SafeFilePath (#1727)
* refactor: add methods and implement traits for `FilePath` and `SafeFilePath` closes #1726 * clippy * path -> as_path * fix prettierignore * Discard changes to Cargo.lock * Discard changes to Cargo.toml * update tauri deps
This commit is contained in:
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
"fs": patch
|
||||||
|
"dialog": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Add utility methods on `FilePath` and `SafeFilePath` enums which are:
|
||||||
|
|
||||||
|
- `path`
|
||||||
|
- `simplified`
|
||||||
|
- `into_path`
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
"fs": patch
|
||||||
|
"dialog": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Implement `Serialize`, `Deserialize`, `From`, `TryFrom` and `FromStr` traits for `FilePath` and `SafeFilePath` enums.
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
"fs": patch
|
||||||
|
"dialog": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Mark `Error` enum as `#[non_exhuastive]`.
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
"fs": patch
|
||||||
|
"dialog": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Add `SafeFilePath` enum.
|
||||||
+1
-1
@@ -12,7 +12,7 @@ pnpm-lock.yaml
|
|||||||
|
|
||||||
# examples gen directory
|
# examples gen directory
|
||||||
examples/*/src-tauri/gen/
|
examples/*/src-tauri/gen/
|
||||||
plugins/examples/*/src-tauri/gen/
|
plugins/*/examples/*/src-tauri/gen/
|
||||||
|
|
||||||
# autogenerated files
|
# autogenerated files
|
||||||
**/autogenerated/**/*.md
|
**/autogenerated/**/*.md
|
||||||
|
|||||||
Generated
+1
-1
@@ -6546,7 +6546,6 @@ dependencies = [
|
|||||||
name = "tauri-plugin-dialog"
|
name = "tauri-plugin-dialog"
|
||||||
version = "2.0.0-rc.4"
|
version = "2.0.0-rc.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dunce",
|
|
||||||
"log",
|
"log",
|
||||||
"raw-window-handle 0.6.2",
|
"raw-window-handle 0.6.2",
|
||||||
"rfd",
|
"rfd",
|
||||||
@@ -6564,6 +6563,7 @@ name = "tauri-plugin-fs"
|
|||||||
version = "2.0.0-rc.2"
|
version = "2.0.0-rc.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"dunce",
|
||||||
"glob",
|
"glob",
|
||||||
"notify",
|
"notify",
|
||||||
"notify-debouncer-full",
|
"notify-debouncer-full",
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ serde_json = { workspace = true }
|
|||||||
tauri = { workspace = true }
|
tauri = { workspace = true }
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
dunce = { workspace = true }
|
|
||||||
url = { workspace = true }
|
url = { workspace = true }
|
||||||
tauri-plugin-fs = { path = "../fs", version = "2.0.0-rc.2" }
|
tauri-plugin-fs = { path = "../fs", version = "2.0.0-rc.2" }
|
||||||
|
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ pub(crate) async fn open<R: Runtime>(
|
|||||||
let folders = dialog_builder.blocking_pick_folders();
|
let folders = dialog_builder.blocking_pick_folders();
|
||||||
if let Some(folders) = &folders {
|
if let Some(folders) = &folders {
|
||||||
for folder in folders {
|
for folder in folders {
|
||||||
if let Ok(path) = folder.path() {
|
if let Ok(path) = folder.clone().into_path() {
|
||||||
if let Some(s) = window.try_fs_scope() {
|
if let Some(s) = window.try_fs_scope() {
|
||||||
s.allow_directory(path, options.recursive);
|
s.allow_directory(path, options.recursive);
|
||||||
}
|
}
|
||||||
@@ -149,7 +149,7 @@ pub(crate) async fn open<R: Runtime>(
|
|||||||
} else {
|
} else {
|
||||||
let folder = dialog_builder.blocking_pick_folder();
|
let folder = dialog_builder.blocking_pick_folder();
|
||||||
if let Some(folder) = &folder {
|
if let Some(folder) = &folder {
|
||||||
if let Ok(path) = folder.path() {
|
if let Ok(path) = folder.clone().into_path() {
|
||||||
if let Some(s) = window.try_fs_scope() {
|
if let Some(s) = window.try_fs_scope() {
|
||||||
s.allow_directory(path, options.recursive);
|
s.allow_directory(path, options.recursive);
|
||||||
}
|
}
|
||||||
@@ -164,7 +164,7 @@ pub(crate) async fn open<R: Runtime>(
|
|||||||
let files = dialog_builder.blocking_pick_files();
|
let files = dialog_builder.blocking_pick_files();
|
||||||
if let Some(files) = &files {
|
if let Some(files) = &files {
|
||||||
for file in files {
|
for file in files {
|
||||||
if let Ok(path) = file.path() {
|
if let Ok(path) = file.clone().into_path() {
|
||||||
if let Some(s) = window.try_fs_scope() {
|
if let Some(s) = window.try_fs_scope() {
|
||||||
s.allow_file(&path);
|
s.allow_file(&path);
|
||||||
}
|
}
|
||||||
@@ -178,7 +178,7 @@ pub(crate) async fn open<R: Runtime>(
|
|||||||
let file = dialog_builder.blocking_pick_file();
|
let file = dialog_builder.blocking_pick_file();
|
||||||
|
|
||||||
if let Some(file) = &file {
|
if let Some(file) = &file {
|
||||||
if let Ok(path) = file.path() {
|
if let Ok(path) = file.clone().into_path() {
|
||||||
if let Some(s) = window.try_fs_scope() {
|
if let Some(s) = window.try_fs_scope() {
|
||||||
s.allow_file(&path);
|
s.allow_file(&path);
|
||||||
}
|
}
|
||||||
@@ -218,7 +218,7 @@ pub(crate) async fn save<R: Runtime>(
|
|||||||
|
|
||||||
let path = dialog_builder.blocking_save_file();
|
let path = dialog_builder.blocking_save_file();
|
||||||
if let Some(p) = &path {
|
if let Some(p) = &path {
|
||||||
if let Ok(path) = p.path() {
|
if let Ok(path) = p.clone().into_path() {
|
||||||
if let Some(s) = window.try_fs_scope() {
|
if let Some(s) = window.try_fs_scope() {
|
||||||
s.allow_file(&path);
|
s.allow_file(&path);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use serde::{ser::Serializer, Serialize};
|
|||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[non_exhaustive]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Tauri(#[from] tauri::Error),
|
Tauri(#[from] tauri::Error),
|
||||||
@@ -20,8 +21,6 @@ pub enum Error {
|
|||||||
FolderPickerNotImplemented,
|
FolderPickerNotImplemented,
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Fs(#[from] tauri_plugin_fs::Error),
|
Fs(#[from] tauri_plugin_fs::Error),
|
||||||
#[error("URL is not a valid path")]
|
|
||||||
InvalidPathUrl,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for Error {
|
impl Serialize for Error {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/app-icon.png"
|
html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/app-icon.png"
|
||||||
)]
|
)]
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::Serialize;
|
||||||
use tauri::{
|
use tauri::{
|
||||||
plugin::{Builder, TauriPlugin},
|
plugin::{Builder, TauriPlugin},
|
||||||
Manager, Runtime,
|
Manager, Runtime,
|
||||||
@@ -24,6 +24,7 @@ use std::{
|
|||||||
|
|
||||||
pub use models::*;
|
pub use models::*;
|
||||||
|
|
||||||
|
pub use tauri_plugin_fs::FilePath;
|
||||||
#[cfg(desktop)]
|
#[cfg(desktop)]
|
||||||
mod desktop;
|
mod desktop;
|
||||||
#[cfg(mobile)]
|
#[cfg(mobile)]
|
||||||
@@ -294,57 +295,6 @@ impl<R: Runtime> MessageDialogBuilder<R> {
|
|||||||
blocking_fn!(self, show)
|
blocking_fn!(self, show)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents either a filesystem path or a URI pointing to a file
|
|
||||||
/// such as `file://` URIs or Android `content://` URIs.
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
pub enum FilePath {
|
|
||||||
Url(url::Url),
|
|
||||||
Path(PathBuf),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PathBuf> for FilePath {
|
|
||||||
fn from(value: PathBuf) -> Self {
|
|
||||||
Self::Path(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<url::Url> for FilePath {
|
|
||||||
fn from(value: url::Url) -> Self {
|
|
||||||
Self::Url(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<FilePath> for tauri_plugin_fs::FilePath {
|
|
||||||
fn from(value: FilePath) -> Self {
|
|
||||||
match value {
|
|
||||||
FilePath::Path(p) => tauri_plugin_fs::FilePath::Path(p),
|
|
||||||
FilePath::Url(url) => tauri_plugin_fs::FilePath::Url(url),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FilePath {
|
|
||||||
fn simplified(self) -> Self {
|
|
||||||
match self {
|
|
||||||
Self::Url(url) => Self::Url(url),
|
|
||||||
Self::Path(p) => Self::Path(dunce::simplified(&p).to_path_buf()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn path(&self) -> Result<PathBuf> {
|
|
||||||
match self {
|
|
||||||
Self::Url(url) => url
|
|
||||||
.to_file_path()
|
|
||||||
.map(PathBuf::from)
|
|
||||||
.map_err(|_| Error::InvalidPathUrl),
|
|
||||||
Self::Path(p) => Ok(p.to_owned()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub(crate) struct Filter {
|
pub(crate) struct Filter {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ uuid = { version = "1", features = ["v4"] }
|
|||||||
glob = "0.3"
|
glob = "0.3"
|
||||||
notify = { version = "6", optional = true, features = ["serde"] }
|
notify = { version = "6", optional = true, features = ["serde"] }
|
||||||
notify-debouncer-full = { version = "0.3", optional = true }
|
notify-debouncer-full = { version = "0.3", optional = true }
|
||||||
|
dunce = { workspace = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
watch = ["notify", "notify-debouncer-full"]
|
watch = ["notify", "notify-debouncer-full"]
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize, Serializer};
|
|||||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||||
use tauri::{
|
use tauri::{
|
||||||
ipc::{CommandScope, GlobalScope},
|
ipc::{CommandScope, GlobalScope},
|
||||||
path::{BaseDirectory, SafePathBuf},
|
path::BaseDirectory,
|
||||||
utils::config::FsScope,
|
utils::config::FsScope,
|
||||||
AppHandle, Manager, Resource, ResourceId, Runtime, Webview,
|
AppHandle, Manager, Resource, ResourceId, Runtime, Webview,
|
||||||
};
|
};
|
||||||
@@ -22,80 +22,7 @@ use std::{
|
|||||||
time::{SystemTime, UNIX_EPOCH},
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{scope::Entry, Error, FilePath, FsExt};
|
use crate::{scope::Entry, Error, FsExt, SafeFilePath};
|
||||||
|
|
||||||
// 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 {
|
|
||||||
SafeFilePath::Url(url) => FilePath::Url(url),
|
|
||||||
SafeFilePath::Path(p) => FilePath::Path(p.as_ref().to_owned()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
if url.scheme().len() != 1 {
|
|
||||||
return Ok(Self::Url(url));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Self::Path(SafePathBuf::new(s.into())?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SafeFilePath {
|
|
||||||
#[inline]
|
|
||||||
fn into_path(self) -> CommandResult<SafePathBuf> {
|
|
||||||
match self {
|
|
||||||
Self::Url(url) => SafePathBuf::new(
|
|
||||||
url.to_file_path()
|
|
||||||
.map_err(|_| format!("failed to get path from {url}"))?,
|
|
||||||
)
|
|
||||||
.map_err(Into::into),
|
|
||||||
Self::Path(p) => Ok(p),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum CommandError {
|
pub enum CommandError {
|
||||||
@@ -1052,7 +979,7 @@ pub fn resolve_path<R: Runtime>(
|
|||||||
let path = if let Some(base_dir) = base_dir {
|
let path = if let Some(base_dir) = base_dir {
|
||||||
webview.path().resolve(&path, base_dir)?
|
webview.path().resolve(&path, base_dir)?
|
||||||
} else {
|
} else {
|
||||||
path.as_ref().to_path_buf()
|
path
|
||||||
};
|
};
|
||||||
|
|
||||||
let scope = tauri::scope::fs::Scope::new(
|
let scope = tauri::scope::fs::Scope::new(
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use std::path::PathBuf;
|
|||||||
use serde::{Serialize, Serializer};
|
use serde::{Serialize, Serializer};
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[non_exhaustive]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Json(#[from] serde_json::Error),
|
Json(#[from] serde_json::Error),
|
||||||
@@ -26,6 +27,10 @@ pub enum Error {
|
|||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
PluginInvoke(#[from] tauri::plugin::mobile::PluginInvokeError),
|
PluginInvoke(#[from] tauri::plugin::mobile::PluginInvokeError),
|
||||||
|
#[error("URL is not a valid path")]
|
||||||
|
InvalidPathUrl,
|
||||||
|
#[error("Unsafe PathBuf: {0}")]
|
||||||
|
UnsafePathBuf(&'static str),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for Error {
|
impl Serialize for Error {
|
||||||
|
|||||||
@@ -0,0 +1,314 @@
|
|||||||
|
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
convert::Infallible,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
str::FromStr,
|
||||||
|
};
|
||||||
|
|
||||||
|
use serde::Serialize;
|
||||||
|
use tauri::path::SafePathBuf;
|
||||||
|
|
||||||
|
use crate::{Error, Result};
|
||||||
|
|
||||||
|
/// Represents either a filesystem path or a URI pointing to a file
|
||||||
|
/// such as `file://` URIs or Android `content://` URIs.
|
||||||
|
#[derive(Debug, Serialize, Clone)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum FilePath {
|
||||||
|
/// `file://` URIs or Android `content://` URIs.
|
||||||
|
Url(url::Url),
|
||||||
|
/// Regular [`PathBuf`]
|
||||||
|
Path(PathBuf),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents either a safe filesystem path or a URI pointing to a file
|
||||||
|
/// such as `file://` URIs or Android `content://` URIs.
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub enum SafeFilePath {
|
||||||
|
/// `file://` URIs or Android `content://` URIs.
|
||||||
|
Url(url::Url),
|
||||||
|
/// Safe [`PathBuf`], see [`SafePathBuf``].
|
||||||
|
Path(SafePathBuf),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FilePath {
|
||||||
|
/// Get a reference to the contaiend [`Path`] if the variant is [`FilePath::Path`].
|
||||||
|
///
|
||||||
|
/// Use [`FilePath::into_path`] to try to convert the [`FilePath::Url`] variant as well.
|
||||||
|
#[inline]
|
||||||
|
pub fn as_path(&self) -> Option<&Path> {
|
||||||
|
match self {
|
||||||
|
Self::Url(_) => None,
|
||||||
|
Self::Path(p) => Some(p),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try to convert into [`PathBuf`] if possible.
|
||||||
|
///
|
||||||
|
/// This calls [`Url::to_file_path`](url::Url::to_file_path) if the variant is [`FilePath::Url`],
|
||||||
|
/// otherwise returns the contained [PathBuf] as is.
|
||||||
|
#[inline]
|
||||||
|
pub fn into_path(self) -> Result<PathBuf> {
|
||||||
|
match self {
|
||||||
|
Self::Url(url) => url
|
||||||
|
.to_file_path()
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.map_err(|_| Error::InvalidPathUrl),
|
||||||
|
Self::Path(p) => Ok(p),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes the contained [`PathBuf`] if the variant is [`FilePath::Path`],
|
||||||
|
/// and when possible, converts Windows UNC paths to regular paths.
|
||||||
|
#[inline]
|
||||||
|
pub fn simplified(self) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::Url(url) => Self::Url(url),
|
||||||
|
Self::Path(p) => Self::Path(dunce::simplified(&p).to_path_buf()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SafeFilePath {
|
||||||
|
/// Get a reference to the contaiend [`Path`] if the variant is [`SafeFilePath::Path`].
|
||||||
|
///
|
||||||
|
/// Use [`SafeFilePath::into_path`] to try to convert the [`SafeFilePath::Url`] variant as well.
|
||||||
|
#[inline]
|
||||||
|
pub fn as_path(&self) -> Option<&Path> {
|
||||||
|
match self {
|
||||||
|
Self::Url(_) => None,
|
||||||
|
Self::Path(p) => Some(p.as_ref()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try to convert into [`PathBuf`] if possible.
|
||||||
|
///
|
||||||
|
/// This calls [`Url::to_file_path`](url::Url::to_file_path) if the variant is [`SafeFilePath::Url`],
|
||||||
|
/// otherwise returns the contained [PathBuf] as is.
|
||||||
|
#[inline]
|
||||||
|
pub fn into_path(self) -> Result<PathBuf> {
|
||||||
|
match self {
|
||||||
|
Self::Url(url) => url
|
||||||
|
.to_file_path()
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.map_err(|_| Error::InvalidPathUrl),
|
||||||
|
Self::Path(p) => Ok(p.as_ref().to_owned()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes the contained [`PathBuf`] if the variant is [`SafeFilePath::Path`],
|
||||||
|
/// and when possible, converts Windows UNC paths to regular paths.
|
||||||
|
#[inline]
|
||||||
|
pub fn simplified(self) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::Url(url) => Self::Url(url),
|
||||||
|
Self::Path(p) => {
|
||||||
|
// Safe to unwrap since it was a safe file path already
|
||||||
|
Self::Path(SafePathBuf::new(dunce::simplified(p.as_ref()).to_path_buf()).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for FilePath {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Url(u) => u.fmt(f),
|
||||||
|
Self::Path(p) => p.display().fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for SafeFilePath {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Url(u) => u.fmt(f),
|
||||||
|
Self::Path(p) => p.display().fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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<'de> serde::Deserialize<'de> for SafeFilePath {
|
||||||
|
fn deserialize<D>(deserializer: D) -> std::result::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 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) {
|
||||||
|
if url.scheme().len() != 1 {
|
||||||
|
return Ok(Self::Url(url));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Self::Path(PathBuf::from(s)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for SafeFilePath {
|
||||||
|
type Err = Error;
|
||||||
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
|
if let Ok(url) = url::Url::from_str(s) {
|
||||||
|
if url.scheme().len() != 1 {
|
||||||
|
return Ok(Self::Url(url));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SafePathBuf::new(s.into())
|
||||||
|
.map(SafeFilePath::Path)
|
||||||
|
.map_err(Error::UnsafePathBuf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PathBuf> for FilePath {
|
||||||
|
fn from(value: PathBuf) -> Self {
|
||||||
|
Self::Path(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<PathBuf> for SafeFilePath {
|
||||||
|
type Error = Error;
|
||||||
|
fn try_from(value: PathBuf) -> Result<Self> {
|
||||||
|
SafePathBuf::new(value)
|
||||||
|
.map(SafeFilePath::Path)
|
||||||
|
.map_err(Error::UnsafePathBuf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Path> for FilePath {
|
||||||
|
fn from(value: &Path) -> Self {
|
||||||
|
Self::Path(value.to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&Path> for SafeFilePath {
|
||||||
|
type Error = Error;
|
||||||
|
fn try_from(value: &Path) -> Result<Self> {
|
||||||
|
SafePathBuf::new(value.to_path_buf())
|
||||||
|
.map(SafeFilePath::Path)
|
||||||
|
.map_err(Error::UnsafePathBuf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&PathBuf> for FilePath {
|
||||||
|
fn from(value: &PathBuf) -> Self {
|
||||||
|
Self::Path(value.to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&PathBuf> for SafeFilePath {
|
||||||
|
type Error = Error;
|
||||||
|
fn try_from(value: &PathBuf) -> Result<Self> {
|
||||||
|
SafePathBuf::new(value.to_owned())
|
||||||
|
.map(SafeFilePath::Path)
|
||||||
|
.map_err(Error::UnsafePathBuf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<url::Url> for FilePath {
|
||||||
|
fn from(value: url::Url) -> Self {
|
||||||
|
Self::Url(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<url::Url> for SafeFilePath {
|
||||||
|
fn from(value: url::Url) -> Self {
|
||||||
|
Self::Url(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<FilePath> for PathBuf {
|
||||||
|
type Error = Error;
|
||||||
|
fn try_from(value: FilePath) -> Result<Self> {
|
||||||
|
value.into_path()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<SafeFilePath> for PathBuf {
|
||||||
|
type Error = Error;
|
||||||
|
fn try_from(value: SafeFilePath) -> Result<Self> {
|
||||||
|
value.into_path()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SafeFilePath> for FilePath {
|
||||||
|
fn from(value: SafeFilePath) -> Self {
|
||||||
|
match value {
|
||||||
|
SafeFilePath::Url(url) => FilePath::Url(url),
|
||||||
|
SafeFilePath::Path(p) => FilePath::Path(p.as_ref().to_owned()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<FilePath> for SafeFilePath {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(value: FilePath) -> Result<Self> {
|
||||||
|
match value {
|
||||||
|
FilePath::Url(url) => Ok(SafeFilePath::Url(url)),
|
||||||
|
FilePath::Path(p) => SafePathBuf::new(p)
|
||||||
|
.map(SafeFilePath::Path)
|
||||||
|
.map_err(Error::UnsafePathBuf),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
-92
@@ -11,13 +11,7 @@
|
|||||||
html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/app-icon.png"
|
html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/app-icon.png"
|
||||||
)]
|
)]
|
||||||
|
|
||||||
use std::{
|
use std::io::Read;
|
||||||
convert::Infallible,
|
|
||||||
fmt,
|
|
||||||
io::Read,
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
str::FromStr,
|
|
||||||
};
|
|
||||||
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use tauri::{
|
use tauri::{
|
||||||
@@ -32,6 +26,7 @@ mod config;
|
|||||||
#[cfg(not(target_os = "android"))]
|
#[cfg(not(target_os = "android"))]
|
||||||
mod desktop;
|
mod desktop;
|
||||||
mod error;
|
mod error;
|
||||||
|
mod file_path;
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
mod mobile;
|
mod mobile;
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
@@ -48,93 +43,11 @@ pub use mobile::Fs;
|
|||||||
pub use error::Error;
|
pub use error::Error;
|
||||||
pub use scope::{Event as ScopeEvent, Scope};
|
pub use scope::{Event as ScopeEvent, Scope};
|
||||||
|
|
||||||
|
pub use file_path::FilePath;
|
||||||
|
pub use file_path::SafeFilePath;
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, Error>;
|
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)]
|
|
||||||
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) {
|
|
||||||
if url.scheme().len() != 1 {
|
|
||||||
return Ok(Self::Url(url));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Self::Path(PathBuf::from(s)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PathBuf> for FilePath {
|
|
||||||
fn from(value: PathBuf) -> Self {
|
|
||||||
Self::Path(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&Path> for FilePath {
|
|
||||||
fn from(value: &Path) -> Self {
|
|
||||||
Self::Path(value.to_owned())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&PathBuf> for FilePath {
|
|
||||||
fn from(value: &PathBuf) -> Self {
|
|
||||||
Self::Path(value.to_owned())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<url::Url> for FilePath {
|
|
||||||
fn from(value: url::Url) -> Self {
|
|
||||||
Self::Url(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for FilePath {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::Url(u) => u.fmt(f),
|
|
||||||
Self::Path(p) => p.display().fmt(f),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Deserialize)]
|
#[derive(Debug, Default, Clone, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct OpenOptions {
|
pub struct OpenOptions {
|
||||||
@@ -151,8 +64,10 @@ pub struct OpenOptions {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
create_new: bool,
|
create_new: bool,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
#[allow(unused)]
|
||||||
mode: Option<u32>,
|
mode: Option<u32>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
#[allow(unused)]
|
||||||
custom_flags: Option<i32>,
|
custom_flags: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,9 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
commands::{resolve_path, CommandResult, SafeFilePath},
|
commands::{resolve_path, CommandResult},
|
||||||
scope::Entry,
|
scope::Entry,
|
||||||
|
SafeFilePath,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct InnerWatcher {
|
struct InnerWatcher {
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#![cfg(feature = "semver")]
|
|
||||||
|
|
||||||
/// Takes a version and spits out a String with trailing _x, thus only considering the digits
|
/// Takes a version and spits out a String with trailing _x, thus only considering the digits
|
||||||
/// relevant regarding semver compatibility
|
/// relevant regarding semver compatibility
|
||||||
pub fn semver_compat_string(version: semver::Version) -> String {
|
pub fn semver_compat_string(version: semver::Version) -> String {
|
||||||
|
|||||||
Reference in New Issue
Block a user