tauri_plugin_fs/
file_path.rsuse std::{
convert::Infallible,
path::{Path, PathBuf},
str::FromStr,
};
use serde::Serialize;
use tauri::path::SafePathBuf;
use crate::{Error, Result};
#[derive(Debug, Serialize, Clone)]
#[serde(untagged)]
pub enum FilePath {
Url(url::Url),
Path(PathBuf),
}
#[derive(Debug, Clone, Serialize)]
pub enum SafeFilePath {
Url(url::Url),
Path(SafePathBuf),
}
impl FilePath {
#[inline]
pub fn as_path(&self) -> Option<&Path> {
match self {
Self::Url(_) => None,
Self::Path(p) => Some(p),
}
}
#[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),
}
}
#[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 {
#[inline]
pub fn as_path(&self) -> Option<&Path> {
match self {
Self::Url(_) => None,
Self::Path(p) => Some(p.as_ref()),
}
}
#[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()),
}
}
#[inline]
pub fn simplified(self) -> Self {
match self {
Self::Url(url) => Self::Url(url),
Self::Path(p) => {
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),
}
}
}