diff --git a/.changes/fs-dialog-file-path-methods.md b/.changes/fs-dialog-file-path-methods.md new file mode 100644 index 000000000..1adfbbf33 --- /dev/null +++ b/.changes/fs-dialog-file-path-methods.md @@ -0,0 +1,10 @@ +--- +"fs": patch +"dialog": patch +--- + +Add utility methods on `FilePath` and `SafeFilePath` enums which are: + +- `path` +- `simplified` +- `into_path` diff --git a/.changes/fs-dialog-file-path-traits.md b/.changes/fs-dialog-file-path-traits.md new file mode 100644 index 000000000..3dfa4ee9c --- /dev/null +++ b/.changes/fs-dialog-file-path-traits.md @@ -0,0 +1,6 @@ +--- +"fs": patch +"dialog": patch +--- + +Implement `Serialize`, `Deserialize`, `From`, `TryFrom` and `FromStr` traits for `FilePath` and `SafeFilePath` enums. diff --git a/.changes/fs-dialog-non-exhaustive-error.md b/.changes/fs-dialog-non-exhaustive-error.md new file mode 100644 index 000000000..d17791935 --- /dev/null +++ b/.changes/fs-dialog-non-exhaustive-error.md @@ -0,0 +1,6 @@ +--- +"fs": patch +"dialog": patch +--- + +Mark `Error` enum as `#[non_exhuastive]`. diff --git a/.changes/fs-dialog-safe-file-path.md b/.changes/fs-dialog-safe-file-path.md new file mode 100644 index 000000000..4e4606832 --- /dev/null +++ b/.changes/fs-dialog-safe-file-path.md @@ -0,0 +1,6 @@ +--- +"fs": patch +"dialog": patch +--- + +Add `SafeFilePath` enum. diff --git a/.prettierignore b/.prettierignore index 82c1a32ef..ff5627141 100644 --- a/.prettierignore +++ b/.prettierignore @@ -12,7 +12,7 @@ pnpm-lock.yaml # examples gen directory examples/*/src-tauri/gen/ -plugins/examples/*/src-tauri/gen/ +plugins/*/examples/*/src-tauri/gen/ # autogenerated files **/autogenerated/**/*.md diff --git a/Cargo.lock b/Cargo.lock index 8e5dca326..2ed68deac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6546,7 +6546,6 @@ dependencies = [ name = "tauri-plugin-dialog" version = "2.0.0-rc.4" dependencies = [ - "dunce", "log", "raw-window-handle 0.6.2", "rfd", @@ -6564,6 +6563,7 @@ name = "tauri-plugin-fs" version = "2.0.0-rc.2" dependencies = [ "anyhow", + "dunce", "glob", "notify", "notify-debouncer-full", diff --git a/plugins/dialog/Cargo.toml b/plugins/dialog/Cargo.toml index a05bf8e43..681e6650e 100644 --- a/plugins/dialog/Cargo.toml +++ b/plugins/dialog/Cargo.toml @@ -26,7 +26,6 @@ serde_json = { workspace = true } tauri = { workspace = true } log = { workspace = true } thiserror = { workspace = true } -dunce = { workspace = true } url = { workspace = true } tauri-plugin-fs = { path = "../fs", version = "2.0.0-rc.2" } diff --git a/plugins/dialog/src/commands.rs b/plugins/dialog/src/commands.rs index 2d884b6e0..76a92e098 100644 --- a/plugins/dialog/src/commands.rs +++ b/plugins/dialog/src/commands.rs @@ -136,7 +136,7 @@ pub(crate) async fn open( let folders = dialog_builder.blocking_pick_folders(); if let Some(folders) = &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() { s.allow_directory(path, options.recursive); } @@ -149,7 +149,7 @@ pub(crate) async fn open( } else { let folder = dialog_builder.blocking_pick_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() { s.allow_directory(path, options.recursive); } @@ -164,7 +164,7 @@ pub(crate) async fn open( let files = dialog_builder.blocking_pick_files(); if let Some(files) = &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() { s.allow_file(&path); } @@ -178,7 +178,7 @@ pub(crate) async fn open( let file = dialog_builder.blocking_pick_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() { s.allow_file(&path); } @@ -218,7 +218,7 @@ pub(crate) async fn save( let path = dialog_builder.blocking_save_file(); 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() { s.allow_file(&path); } diff --git a/plugins/dialog/src/error.rs b/plugins/dialog/src/error.rs index cb70e714f..0c3ed5b86 100644 --- a/plugins/dialog/src/error.rs +++ b/plugins/dialog/src/error.rs @@ -7,6 +7,7 @@ use serde::{ser::Serializer, Serialize}; pub type Result = std::result::Result; #[derive(Debug, thiserror::Error)] +#[non_exhaustive] pub enum Error { #[error(transparent)] Tauri(#[from] tauri::Error), @@ -20,8 +21,6 @@ pub enum Error { FolderPickerNotImplemented, #[error(transparent)] Fs(#[from] tauri_plugin_fs::Error), - #[error("URL is not a valid path")] - InvalidPathUrl, } impl Serialize for Error { diff --git a/plugins/dialog/src/lib.rs b/plugins/dialog/src/lib.rs index 976878ccd..26048eca5 100644 --- a/plugins/dialog/src/lib.rs +++ b/plugins/dialog/src/lib.rs @@ -11,7 +11,7 @@ html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/app-icon.png" )] -use serde::{Deserialize, Serialize}; +use serde::Serialize; use tauri::{ plugin::{Builder, TauriPlugin}, Manager, Runtime, @@ -24,6 +24,7 @@ use std::{ pub use models::*; +pub use tauri_plugin_fs::FilePath; #[cfg(desktop)] mod desktop; #[cfg(mobile)] @@ -294,57 +295,6 @@ impl MessageDialogBuilder { 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 for FilePath { - fn from(value: PathBuf) -> Self { - Self::Path(value) - } -} - -impl From for FilePath { - fn from(value: url::Url) -> Self { - Self::Url(value) - } -} - -impl From 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 { - 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)] pub(crate) struct Filter { pub name: String, diff --git a/plugins/fs/Cargo.toml b/plugins/fs/Cargo.toml index f4719dc19..5d99eaead 100644 --- a/plugins/fs/Cargo.toml +++ b/plugins/fs/Cargo.toml @@ -30,6 +30,7 @@ uuid = { version = "1", features = ["v4"] } glob = "0.3" notify = { version = "6", optional = true, features = ["serde"] } notify-debouncer-full = { version = "0.3", optional = true } +dunce = { workspace = true } [features] watch = ["notify", "notify-debouncer-full"] diff --git a/plugins/fs/src/commands.rs b/plugins/fs/src/commands.rs index c59706e14..b72e3b3f5 100644 --- a/plugins/fs/src/commands.rs +++ b/plugins/fs/src/commands.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize, Serializer}; use serde_repr::{Deserialize_repr, Serialize_repr}; use tauri::{ ipc::{CommandScope, GlobalScope}, - path::{BaseDirectory, SafePathBuf}, + path::BaseDirectory, utils::config::FsScope, AppHandle, Manager, Resource, ResourceId, Runtime, Webview, }; @@ -22,80 +22,7 @@ use std::{ time::{SystemTime, UNIX_EPOCH}, }; -use crate::{scope::Entry, Error, FilePath, FsExt}; - -// TODO: Combine this with FilePath -#[derive(Debug)] -pub enum SafeFilePath { - Url(url::Url), - Path(SafePathBuf), -} - -impl<'de> serde::Deserialize<'de> for SafeFilePath { - fn deserialize(deserializer: D) -> Result - 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(self, s: &str) -> std::result::Result - 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 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 { - 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 { - 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), - } - } -} +use crate::{scope::Entry, Error, FsExt, SafeFilePath}; #[derive(Debug, thiserror::Error)] pub enum CommandError { @@ -1052,7 +979,7 @@ pub fn resolve_path( let path = if let Some(base_dir) = base_dir { webview.path().resolve(&path, base_dir)? } else { - path.as_ref().to_path_buf() + path }; let scope = tauri::scope::fs::Scope::new( diff --git a/plugins/fs/src/error.rs b/plugins/fs/src/error.rs index d62729a44..0c98e83fc 100644 --- a/plugins/fs/src/error.rs +++ b/plugins/fs/src/error.rs @@ -7,6 +7,7 @@ use std::path::PathBuf; use serde::{Serialize, Serializer}; #[derive(Debug, thiserror::Error)] +#[non_exhaustive] pub enum Error { #[error(transparent)] Json(#[from] serde_json::Error), @@ -26,6 +27,10 @@ pub enum Error { #[cfg(target_os = "android")] #[error(transparent)] 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 { diff --git a/plugins/fs/src/file_path.rs b/plugins/fs/src/file_path.rs new file mode 100644 index 000000000..967696b9a --- /dev/null +++ b/plugins/fs/src/file_path.rs @@ -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 { + 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 { + 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(deserializer: D) -> std::result::Result + 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(self, s: &str) -> std::result::Result + 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(deserializer: D) -> std::result::Result + 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(self, s: &str) -> std::result::Result + 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 { + 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 { + 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 for FilePath { + fn from(value: PathBuf) -> Self { + Self::Path(value) + } +} + +impl TryFrom for SafeFilePath { + type Error = Error; + fn try_from(value: PathBuf) -> Result { + 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 { + 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 { + SafePathBuf::new(value.to_owned()) + .map(SafeFilePath::Path) + .map_err(Error::UnsafePathBuf) + } +} + +impl From for FilePath { + fn from(value: url::Url) -> Self { + Self::Url(value) + } +} + +impl From for SafeFilePath { + fn from(value: url::Url) -> Self { + Self::Url(value) + } +} + +impl TryFrom for PathBuf { + type Error = Error; + fn try_from(value: FilePath) -> Result { + value.into_path() + } +} + +impl TryFrom for PathBuf { + type Error = Error; + fn try_from(value: SafeFilePath) -> Result { + value.into_path() + } +} + +impl From 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 for SafeFilePath { + type Error = Error; + + fn try_from(value: FilePath) -> Result { + match value { + FilePath::Url(url) => Ok(SafeFilePath::Url(url)), + FilePath::Path(p) => SafePathBuf::new(p) + .map(SafeFilePath::Path) + .map_err(Error::UnsafePathBuf), + } + } +} diff --git a/plugins/fs/src/lib.rs b/plugins/fs/src/lib.rs index 975c12625..5cb903f8e 100644 --- a/plugins/fs/src/lib.rs +++ b/plugins/fs/src/lib.rs @@ -11,13 +11,7 @@ html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/app-icon.png" )] -use std::{ - convert::Infallible, - fmt, - io::Read, - path::{Path, PathBuf}, - str::FromStr, -}; +use std::io::Read; use serde::Deserialize; use tauri::{ @@ -32,6 +26,7 @@ mod config; #[cfg(not(target_os = "android"))] mod desktop; mod error; +mod file_path; #[cfg(target_os = "android")] mod mobile; #[cfg(target_os = "android")] @@ -48,92 +43,10 @@ pub use mobile::Fs; pub use error::Error; pub use scope::{Event as ScopeEvent, Scope}; -type Result = std::result::Result; - -// 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(deserializer: D) -> std::result::Result - 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(self, s: &str) -> std::result::Result - 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 { - 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 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 for FilePath { - fn from(value: url::Url) -> Self { - Self::Url(value) - } -} +pub use file_path::FilePath; +pub use file_path::SafeFilePath; -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), - } - } -} +type Result = std::result::Result; #[derive(Debug, Default, Clone, Deserialize)] #[serde(rename_all = "camelCase")] @@ -151,8 +64,10 @@ pub struct OpenOptions { #[serde(default)] create_new: bool, #[serde(default)] + #[allow(unused)] mode: Option, #[serde(default)] + #[allow(unused)] custom_flags: Option, } diff --git a/plugins/fs/src/watcher.rs b/plugins/fs/src/watcher.rs index 5849cdf82..cf2af5039 100644 --- a/plugins/fs/src/watcher.rs +++ b/plugins/fs/src/watcher.rs @@ -22,8 +22,9 @@ use std::{ }; use crate::{ - commands::{resolve_path, CommandResult, SafeFilePath}, + commands::{resolve_path, CommandResult}, scope::Entry, + SafeFilePath, }; struct InnerWatcher { diff --git a/plugins/single-instance/src/semver_compat.rs b/plugins/single-instance/src/semver_compat.rs index c613fdd7c..80487cae6 100644 --- a/plugins/single-instance/src/semver_compat.rs +++ b/plugins/single-instance/src/semver_compat.rs @@ -2,8 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -#![cfg(feature = "semver")] - /// Takes a version and spits out a String with trailing _x, thus only considering the digits /// relevant regarding semver compatibility pub fn semver_compat_string(version: semver::Version) -> String {