diff --git a/Cargo.lock b/Cargo.lock index 493ecafd..0836a497 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3281,11 +3281,24 @@ dependencies = [ "yazi-config", "yazi-core", "yazi-dds", + "yazi-fs", "yazi-plugin", "yazi-proxy", "yazi-shared", ] +[[package]] +name = "yazi-fs" +version = "0.2.5" +dependencies = [ + "anyhow", + "opendal", + "serde", + "tokio", + "toml", + "yazi-shared", +] + [[package]] name = "yazi-plugin" version = "0.2.5" diff --git a/cspell.json b/cspell.json index c7b1bfd3..bbe1650b 100644 --- a/cspell.json +++ b/cspell.json @@ -1 +1 @@ -{"language":"en","version":"0.2","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath"]} \ No newline at end of file +{"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","opendal"],"language":"en","flagWords":[],"version":"0.2"} \ No newline at end of file diff --git a/yazi-config/src/lib.rs b/yazi-config/src/lib.rs index 5b0a1615..513f88fb 100644 --- a/yazi-config/src/lib.rs +++ b/yazi-config/src/lib.rs @@ -13,7 +13,6 @@ pub mod popup; mod preset; pub mod preview; mod priority; -pub mod schemes; mod tasks; pub mod theme; mod validation; @@ -23,9 +22,6 @@ pub use layout::*; pub(crate) use pattern::*; pub(crate) use preset::*; pub use priority::*; -use yazi_shared::fs::SCHEMES; - -use crate::schemes::Schemes; static MERGED_YAZI: RoCell = RoCell::new(); static MERGED_KEYMAP: RoCell = RoCell::new(); @@ -65,9 +61,5 @@ pub fn init() -> anyhow::Result<()> { SELECT.with(Default::default); WHICH.with(Default::default); - // Load schemes. - let schemes = Schemes::default().make()?; - SCHEMES.init(schemes); - Ok(()) } diff --git a/yazi-config/src/schemes/mod.rs b/yazi-config/src/schemes/mod.rs deleted file mode 100644 index 891965a1..00000000 --- a/yazi-config/src/schemes/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod schemes; - -pub use schemes::*; diff --git a/yazi-config/src/schemes/schemes.rs b/yazi-config/src/schemes/schemes.rs deleted file mode 100644 index 6f235fd8..00000000 --- a/yazi-config/src/schemes/schemes.rs +++ /dev/null @@ -1,56 +0,0 @@ -use std::collections::HashMap; - -use anyhow::Result; -use serde::{Deserialize, Deserializer}; - -/// Schemes in configuration file. -#[derive(Debug)] -pub struct Schemes { - pub rules: Vec, -} - -impl Default for Schemes { - fn default() -> Self { - let schemes_path = yazi_shared::Xdg::state_dir().join("schemes.toml"); - // Ignore any errors, as it's fine to have an empty file. - let schemes_content = std::fs::read_to_string(&schemes_path).unwrap_or_default(); - toml::from_str(&schemes_content).unwrap() - } -} - -impl Schemes { - /// Consume loaded config to build yazi_shared::fs::Schemes. - pub fn make(self) -> Result { - yazi_shared::fs::Schemes::from_iter(self.rules.into_iter().map(|s| (s.name, s.typ, s.config))) - } -} - -impl<'de> Deserialize<'de> for Schemes { - fn deserialize(deserializer: D) -> std::result::Result - where - D: Deserializer<'de>, - { - #[derive(Deserialize, Default)] - #[serde(default)] - struct Outer { - schemes: Shadow, - } - #[derive(Deserialize, Default)] - struct Shadow { - rules: Vec, - } - - let outer = Outer::deserialize(deserializer)?; - - Ok(Self { rules: outer.schemes.rules }) - } -} - -/// Scheme in configuration file. -#[derive(Debug, Deserialize)] -pub struct Scheme { - pub name: String, - #[serde(rename = "type")] - pub typ: String, - pub config: HashMap, -} diff --git a/yazi-fm/Cargo.toml b/yazi-fm/Cargo.toml index eaab5478..22351ece 100644 --- a/yazi-fm/Cargo.toml +++ b/yazi-fm/Cargo.toml @@ -18,6 +18,7 @@ yazi-boot = { path = "../yazi-boot", version = "0.2.5" } yazi-config = { path = "../yazi-config", version = "0.2.5" } yazi-core = { path = "../yazi-core", version = "0.2.5" } yazi-dds = { path = "../yazi-dds", version = "0.2.5" } +yazi-fs = { path = "../yazi-fs", version = "0.2.5" } yazi-plugin = { path = "../yazi-plugin", version = "0.2.5" } yazi-proxy = { path = "../yazi-proxy", version = "0.2.5" } yazi-shared = { path = "../yazi-shared", version = "0.2.5" } diff --git a/yazi-fs/Cargo.toml b/yazi-fs/Cargo.toml new file mode 100644 index 00000000..15b342a5 --- /dev/null +++ b/yazi-fs/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "yazi-fs" +version = "0.2.5" +edition = "2021" +license = "MIT" +authors = [ "sxyazi " ] +description = "Yazi file system compatibility layer" +homepage = "https://yazi-rs.github.io" +repository = "https://github.com/sxyazi/yazi" + +[dependencies] +yazi-shared = { path = "../yazi-shared", version = "0.2.5" } + +# External dependencies +anyhow = "1.0.86" +opendal = "0.47.0" +serde = { version = "1.0.203", features = [ "derive" ] } +tokio = { version = "1.38.0", features = [ "full" ] } +toml = { version = "0.8.14", features = [ "preserve_order" ] } diff --git a/yazi-fs/src/lib.rs b/yazi-fs/src/lib.rs new file mode 100644 index 00000000..10b96f49 --- /dev/null +++ b/yazi-fs/src/lib.rs @@ -0,0 +1,3 @@ +#![allow(clippy::option_map_unit_fn)] + +pub mod providers; diff --git a/yazi-fs/src/providers/mod.rs b/yazi-fs/src/providers/mod.rs new file mode 100644 index 00000000..3780a19c --- /dev/null +++ b/yazi-fs/src/providers/mod.rs @@ -0,0 +1,7 @@ +#![allow(clippy::module_inception)] + +mod provider; +mod providers; + +pub use provider::*; +pub use providers::*; diff --git a/yazi-fs/src/providers/provider.rs b/yazi-fs/src/providers/provider.rs new file mode 100644 index 00000000..30ba71a2 --- /dev/null +++ b/yazi-fs/src/providers/provider.rs @@ -0,0 +1,20 @@ +use std::{collections::HashMap, str::FromStr}; + +use serde::Deserialize; + +#[derive(Debug, Deserialize)] +pub struct Provider { + pub name: String, + #[serde(rename = "type")] + pub type_: String, + pub config: HashMap, +} + +impl TryFrom for opendal::Operator { + type Error = anyhow::Error; + + fn try_from(value: Provider) -> Result { + let scheme = opendal::Scheme::from_str(&value.name)?; + Ok(opendal::Operator::via_map(scheme, value.config)?) + } +} diff --git a/yazi-fs/src/providers/providers.rs b/yazi-fs/src/providers/providers.rs new file mode 100644 index 00000000..03068a96 --- /dev/null +++ b/yazi-fs/src/providers/providers.rs @@ -0,0 +1,47 @@ +use std::{collections::HashMap, ops::Deref}; + +use anyhow::Result; +use serde::{Deserialize, Deserializer}; +use yazi_shared::Xdg; + +use crate::providers::Provider; + +pub struct Providers(HashMap); + +impl Deref for Providers { + type Target = HashMap; + + fn deref(&self) -> &Self::Target { &self.0 } +} + +impl Default for Providers { + fn default() -> Self { + let s = std::fs::read_to_string(Xdg::state_dir().join(".secret.toml")).unwrap_or_default(); + toml::from_str(&s).unwrap() + } +} + +impl<'de> Deserialize<'de> for Providers { + fn deserialize(deserializer: D) -> std::result::Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize, Default)] + struct Outer { + providers: Shadow, + } + #[derive(Deserialize, Default)] + struct Shadow { + rules: Vec, + } + + let outer = Outer::deserialize(deserializer)?; + let result: Result<_> = + outer.providers.rules.into_iter().map(|p| Ok((p.name.clone(), p.try_into()?))).collect(); + + match result { + Ok(v) => Ok(Providers(v)), + Err(e) => Err(serde::de::Error::custom(e)), + } + } +} diff --git a/yazi-shared/src/fs/file.rs b/yazi-shared/src/fs/file.rs index 4488c4c4..003f1045 100644 --- a/yazi-shared/src/fs/file.rs +++ b/yazi-shared/src/fs/file.rs @@ -1,9 +1,9 @@ -use std::{cell::Cell, ffi::OsStr, fs::Metadata, ops::{Add, Deref}, time::{Duration, SystemTime}}; +use std::{cell::Cell, ffi::OsStr, fs::Metadata, ops::Deref}; -use anyhow::{anyhow, Result}; +use anyhow::Result; use tokio::fs; -use crate::{fs::{Cha, ChaKind, Url, SCHEMES}, theme::IconCache}; +use crate::{fs::{Cha, ChaKind, Url}, theme::IconCache}; #[derive(Clone, Debug, Default)] pub struct File { @@ -23,9 +23,10 @@ impl Deref for File { impl File { #[inline] pub async fn from(url: Url) -> Result { - if url.is_remote().is_some() { - return Self::from_remote(url).await; - } + // TODO: refactor this + // if url.is_remote().is_some() { + // return Self::from_remote(url).await; + // } let meta = fs::symlink_metadata(&url).await?; Ok(Self::from_meta(url, meta).await) @@ -61,37 +62,38 @@ impl File { Self { url, cha: Cha::from(meta).with_kind(ck), link_to, icon: Default::default() } } + // TODO: refactor this /// Build a new file from remote. - pub async fn from_remote(url: Url) -> Result { - let scheme = url.is_remote().ok_or(anyhow!("not a remote file"))?; - let op = SCHEMES.get(scheme)?; + // pub async fn from_remote(url: Url) -> Result { + // let scheme = url.is_remote().ok_or(anyhow!("not a remote file"))?; + // let op = SCHEMES.get(scheme)?; - let meta = op.stat(&url.as_path().to_string_lossy()).await?; - let mut kind = ChaKind::default(); - if meta.is_dir() { - kind |= ChaKind::DIR; - } - let cha = Cha { - kind, - len: meta.content_length(), - accessed: None, - created: None, - modified: meta - .last_modified() - .map(|v| SystemTime::UNIX_EPOCH.add(Duration::from_micros(v.timestamp_micros() as u64))), - // Always return 774 for remote files. - #[cfg(unix)] - permissions: 0774, - // Always return current user for remote files. - #[cfg(unix)] - uid: unsafe { libc::getuid().into() }, - // Always return current group for remote files. - #[cfg(unix)] - gid: unsafe { libc::getgid().into() }, - }; + // let meta = op.stat(&url.as_path().to_string_lossy()).await?; + // let mut kind = ChaKind::default(); + // if meta.is_dir() { + // kind |= ChaKind::DIR; + // } + // let cha = Cha { + // kind, + // len: meta.content_length(), + // accessed: None, + // created: None, + // modified: meta + // .last_modified() + // .map(|v| SystemTime::UNIX_EPOCH.add(Duration::from_micros(v.timestamp_micros() as u64))), + // // Always return 774 for remote files. + // #[cfg(unix)] + // permissions: 0774, + // // Always return current user for remote files. + // #[cfg(unix)] + // uid: unsafe { libc::getuid().into() }, + // // Always return current group for remote files. + // #[cfg(unix)] + // gid: unsafe { libc::getgid().into() }, + // }; - Ok(Self { url, cha, link_to: None, icon: Default::default() }) - } + // Ok(Self { url, cha, link_to: None, icon: Default::default() }) + // } #[inline] pub fn from_dummy(url: &Url) -> Self { Self { url: url.to_owned(), ..Default::default() } } diff --git a/yazi-shared/src/fs/mod.rs b/yazi-shared/src/fs/mod.rs index 5f80dcca..9cd7865e 100644 --- a/yazi-shared/src/fs/mod.rs +++ b/yazi-shared/src/fs/mod.rs @@ -3,7 +3,6 @@ mod file; mod fns; mod op; mod path; -mod schemes; mod url; pub use cha::*; @@ -11,5 +10,4 @@ pub use file::*; pub use fns::*; pub use op::*; pub use path::*; -pub use schemes::*; pub use url::*; diff --git a/yazi-shared/src/fs/schemes.rs b/yazi-shared/src/fs/schemes.rs deleted file mode 100644 index 84954d46..00000000 --- a/yazi-shared/src/fs/schemes.rs +++ /dev/null @@ -1,33 +0,0 @@ -use std::{collections::HashMap, str::FromStr}; - -use anyhow::{anyhow, Result}; -use opendal::Operator; - -use crate::RoCell; - -/// SCHEMES is the read only cell of schemes that already loaded. -pub static SCHEMES: RoCell = RoCell::new(); - -/// Schemes carries all the schemes defined in the configuration file. -#[derive(Debug)] -pub struct Schemes(HashMap); - -impl Schemes { - /// Build schemes from iterator. - pub fn from_iter( - iter: impl IntoIterator)>, - ) -> Result { - let mut schemes = HashMap::new(); - for (name, typ, config) in iter { - let scheme = opendal::Scheme::from_str(&typ)?; - let operator = Operator::via_map(scheme, config)?; - schemes.insert(name, operator); - } - Ok(Self(schemes)) - } - - /// Get operator by scheme name. - pub fn get(&self, scheme: &str) -> Result { - self.0.get(scheme).cloned().ok_or(anyhow!("storage scheme {scheme} is not configured")) - } -}