From c2883f1e05bdafead994d5d28098e58de0ad514b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Wed, 23 Jul 2025 22:43:11 +0800 Subject: [PATCH] refactor: pull filesystem calls out into a designated `yazi_fs` crate (#3013) --- Cargo.lock | 1 + yazi-actor/src/cmp/trigger.rs | 6 +- yazi-actor/src/mgr/bulk_rename.rs | 1 + yazi-actor/src/mgr/create.rs | 15 +++-- yazi-actor/src/mgr/rename.rs | 7 +-- yazi-adapter/Cargo.toml | 1 + yazi-adapter/src/adapter.rs | 18 +++--- yazi-adapter/src/drivers/iip.rs | 7 ++- yazi-adapter/src/drivers/kgp.rs | 7 ++- yazi-adapter/src/drivers/kgp_old.rs | 7 ++- yazi-adapter/src/drivers/sixel.rs | 7 ++- yazi-adapter/src/image.rs | 28 +++++---- yazi-binding/src/url.rs | 5 ++ yazi-cli/src/package/delete.rs | 7 +-- yazi-cli/src/package/deploy.rs | 9 ++- yazi-cli/src/package/hash.rs | 9 ++- yazi-cli/src/package/package.rs | 7 +-- yazi-core/src/mgr/watcher.rs | 13 ++-- yazi-dds/src/state.rs | 10 +-- yazi-dds/src/stream.rs | 2 +- yazi-fm/src/app/commands/quit.rs | 6 +- yazi-fs/src/cha/cha.rs | 16 ++--- yazi-fs/src/cha/kind.rs | 7 ++- yazi-fs/src/file.rs | 8 +-- yazi-fs/src/files.rs | 10 +-- yazi-fs/src/fns.rs | 2 + yazi-fs/src/lib.rs | 2 +- yazi-fs/src/path.rs | 24 ++++--- yazi-fs/src/services/local.rs | 83 +++++++++++++++++++++++++ yazi-fs/src/services/mod.rs | 1 + yazi-fs/src/services/services.rs | 75 ++++++++++++++++++++++ yazi-plugin/src/external/highlighter.rs | 7 ++- yazi-plugin/src/fs/fs.rs | 21 +++---- yazi-plugin/src/loader/loader.rs | 4 +- yazi-scheduler/src/file/file.rs | 17 ++--- yazi-scheduler/src/scheduler.rs | 6 +- yazi-shared/src/url/loc.rs | 36 +++++++++++ yazi-shared/src/url/url.rs | 7 ++- 38 files changed, 354 insertions(+), 145 deletions(-) create mode 100644 yazi-fs/src/services/local.rs create mode 100644 yazi-fs/src/services/mod.rs create mode 100644 yazi-fs/src/services/services.rs diff --git a/Cargo.lock b/Cargo.lock index b52f9a9b..53ecdc9c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3530,6 +3530,7 @@ dependencies = [ "tokio", "tracing", "yazi-config", + "yazi-fs", "yazi-macro", "yazi-shared", "yazi-term", diff --git a/yazi-actor/src/cmp/trigger.rs b/yazi-actor/src/cmp/trigger.rs index 80e511d3..d2e715ca 100644 --- a/yazi-actor/src/cmp/trigger.rs +++ b/yazi-actor/src/cmp/trigger.rs @@ -1,8 +1,7 @@ use std::{ffi::OsString, mem, path::{MAIN_SEPARATOR_STR, Path, PathBuf}}; use anyhow::Result; -use tokio::fs; -use yazi_fs::{CWD, expand_path}; +use yazi_fs::{CWD, expand_path, services::Local}; use yazi_macro::{act, render, succ}; use yazi_parser::cmp::{CmpItem, ShowOpt, TriggerOpt}; use yazi_proxy::CmpProxy; @@ -37,7 +36,8 @@ impl Actor for Trigger { let ticket = cmp.ticket; tokio::spawn(async move { - let mut dir = fs::read_dir(&parent).await?; + // TODO: support VFS + let mut dir = Local::read_dir(&parent).await?; let mut cache = vec![]; // "/" is both a directory separator and the root directory per se diff --git a/yazi-actor/src/mgr/bulk_rename.rs b/yazi-actor/src/mgr/bulk_rename.rs index 27adba4d..941fda27 100644 --- a/yazi-actor/src/mgr/bulk_rename.rs +++ b/yazi-actor/src/mgr/bulk_rename.rs @@ -22,6 +22,7 @@ impl Actor for BulkRename { const NAME: &str = "bulk_rename"; + // FIXME: VFS fn act(cx: &mut Ctx, _: Self::Options) -> Result { let Some(opener) = YAZI.opener.block(YAZI.open.all("bulk-rename.txt", "text/plain")) else { succ!(AppProxy::notify_warn("Bulk rename", "No text opener found")); diff --git a/yazi-actor/src/mgr/create.rs b/yazi-actor/src/mgr/create.rs index 2b423361..58e0793c 100644 --- a/yazi-actor/src/mgr/create.rs +++ b/yazi-actor/src/mgr/create.rs @@ -1,7 +1,6 @@ use anyhow::Result; -use tokio::fs; use yazi_config::popup::{ConfirmCfg, InputCfg}; -use yazi_fs::{File, FilesOp, maybe_exists, ok_or_not_found, realname}; +use yazi_fs::{File, FilesOp, maybe_exists, ok_or_not_found, realname, services}; use yazi_macro::succ; use yazi_parser::mgr::CreateOpt; use yazi_proxy::{ConfirmProxy, InputProxy, MgrProxy, WATCHER}; @@ -46,15 +45,15 @@ impl Create { let _permit = WATCHER.acquire().await.unwrap(); if dir { - fs::create_dir_all(&new).await?; + services::create_dir_all(&new).await?; } else if let Some(real) = realname(&new).await { - ok_or_not_found(fs::remove_file(&new).await)?; + ok_or_not_found(services::remove_file(&new).await)?; FilesOp::Deleting(parent.clone(), [UrnBuf::from(real)].into()).emit(); - fs::File::create(&new).await?; + services::create(&new).await?; } else { - fs::create_dir_all(&parent).await.ok(); - ok_or_not_found(fs::remove_file(&new).await)?; - fs::File::create(&new).await?; + services::create_dir_all(&parent).await.ok(); + ok_or_not_found(services::remove_file(&new).await)?; + services::create(&new).await?; } if let Ok(f) = File::new(new.clone()).await { diff --git a/yazi-actor/src/mgr/rename.rs b/yazi-actor/src/mgr/rename.rs index 05f27ef8..c54ad464 100644 --- a/yazi-actor/src/mgr/rename.rs +++ b/yazi-actor/src/mgr/rename.rs @@ -1,8 +1,7 @@ use anyhow::Result; -use tokio::fs; use yazi_config::popup::{ConfirmCfg, InputCfg}; use yazi_dds::Pubsub; -use yazi_fs::{File, FilesOp, maybe_exists, ok_or_not_found, paths_to_same_file, realname}; +use yazi_fs::{File, FilesOp, maybe_exists, ok_or_not_found, paths_to_same_file, realname, services}; use yazi_macro::{act, err, succ}; use yazi_parser::mgr::RenameOpt; use yazi_proxy::{ConfirmProxy, InputProxy, MgrProxy, WATCHER}; @@ -66,10 +65,10 @@ impl Rename { let _permit = WATCHER.acquire().await.unwrap(); let overwritten = realname(&new).await; - fs::rename(&old, &new).await?; + services::rename(&old, &new).await?; if let Some(o) = overwritten { - ok_or_not_found(fs::rename(p_new.join(&o), &new).await)?; + ok_or_not_found(services::rename(p_new.join(&o), &new).await)?; FilesOp::Deleting(p_new.clone(), [UrnBuf::from(o)].into()).emit(); } diff --git a/yazi-adapter/Cargo.toml b/yazi-adapter/Cargo.toml index f63b76c5..fcbd406e 100644 --- a/yazi-adapter/Cargo.toml +++ b/yazi-adapter/Cargo.toml @@ -10,6 +10,7 @@ repository = "https://github.com/sxyazi/yazi" [dependencies] yazi-config = { path = "../yazi-config", version = "25.6.11" } +yazi-fs = { path = "../yazi-fs", version = "25.6.11" } yazi-macro = { path = "../yazi-macro", version = "25.6.11" } yazi-shared = { path = "../yazi-shared", version = "25.6.11" } yazi-term = { path = "../yazi-term", version = "25.6.11" } diff --git a/yazi-adapter/src/adapter.rs b/yazi-adapter/src/adapter.rs index 658f2def..582091a5 100644 --- a/yazi-adapter/src/adapter.rs +++ b/yazi-adapter/src/adapter.rs @@ -1,9 +1,9 @@ -use std::{env, fmt::Display, path::Path}; +use std::{env, fmt::Display}; use anyhow::Result; use ratatui::layout::Rect; use tracing::warn; -use yazi_shared::env_exists; +use yazi_shared::{env_exists, url::Url}; use crate::{Emulator, SHOWN, TMUX, drivers}; @@ -35,18 +35,18 @@ impl Display for Adapter { } impl Adapter { - pub async fn image_show(self, path: &Path, max: Rect) -> Result { + pub async fn image_show(self, url: &Url, max: Rect) -> Result { if max.is_empty() { return Ok(Rect::default()); } match self { - Self::Kgp => drivers::Kgp::image_show(path, max).await, - Self::KgpOld => drivers::KgpOld::image_show(path, max).await, - Self::Iip => drivers::Iip::image_show(path, max).await, - Self::Sixel => drivers::Sixel::image_show(path, max).await, - Self::X11 | Self::Wayland => drivers::Ueberzug::image_show(path, max).await, - Self::Chafa => drivers::Chafa::image_show(path, max).await, + Self::Kgp => drivers::Kgp::image_show(url, max).await, + Self::KgpOld => drivers::KgpOld::image_show(url, max).await, + Self::Iip => drivers::Iip::image_show(url, max).await, + Self::Sixel => drivers::Sixel::image_show(url, max).await, + Self::X11 | Self::Wayland => drivers::Ueberzug::image_show(url, max).await, + Self::Chafa => drivers::Chafa::image_show(url, max).await, } } diff --git a/yazi-adapter/src/drivers/iip.rs b/yazi-adapter/src/drivers/iip.rs index 5c76fa63..8295326b 100644 --- a/yazi-adapter/src/drivers/iip.rs +++ b/yazi-adapter/src/drivers/iip.rs @@ -1,4 +1,4 @@ -use std::{fmt::Write, io::Write as ioWrite, path::Path}; +use std::{fmt::Write, io::Write as ioWrite}; use anyhow::Result; use base64::{Engine, engine::{Config, general_purpose::STANDARD}}; @@ -6,14 +6,15 @@ use crossterm::{cursor::MoveTo, queue}; use image::{DynamicImage, ExtendedColorType, ImageEncoder, codecs::{jpeg::JpegEncoder, png::PngEncoder}}; use ratatui::layout::Rect; use yazi_config::YAZI; +use yazi_shared::url::Url; use crate::{CLOSE, Emulator, Image, START, adapter::Adapter}; pub(crate) struct Iip; impl Iip { - pub(crate) async fn image_show(path: &Path, max: Rect) -> Result { - let img = Image::downscale(path, max).await?; + pub(crate) async fn image_show(url: &Url, max: Rect) -> Result { + let img = Image::downscale(url, max).await?; let area = Image::pixel_area((img.width(), img.height()), max); let b = Self::encode(img).await?; diff --git a/yazi-adapter/src/drivers/kgp.rs b/yazi-adapter/src/drivers/kgp.rs index 4d39cb4f..b05ff992 100644 --- a/yazi-adapter/src/drivers/kgp.rs +++ b/yazi-adapter/src/drivers/kgp.rs @@ -1,11 +1,12 @@ use core::str; -use std::{io::Write, path::Path}; +use std::io::Write; use anyhow::Result; use base64::{Engine, engine::general_purpose}; use crossterm::{cursor::MoveTo, queue}; use image::DynamicImage; use ratatui::layout::Rect; +use yazi_shared::url::Url; use crate::{CLOSE, ESCAPE, Emulator, START, adapter::Adapter, image::Image}; @@ -312,8 +313,8 @@ static DIACRITICS: [char; 297] = [ pub(crate) struct Kgp; impl Kgp { - pub(crate) async fn image_show(path: &Path, max: Rect) -> Result { - let img = Image::downscale(path, max).await?; + pub(crate) async fn image_show(url: &Url, max: Rect) -> Result { + let img = Image::downscale(url, max).await?; let area = Image::pixel_area((img.width(), img.height()), max); let b1 = Self::encode(img).await?; diff --git a/yazi-adapter/src/drivers/kgp_old.rs b/yazi-adapter/src/drivers/kgp_old.rs index fe4d58f1..89865b45 100644 --- a/yazi-adapter/src/drivers/kgp_old.rs +++ b/yazi-adapter/src/drivers/kgp_old.rs @@ -1,10 +1,11 @@ use core::str; -use std::{io::Write, path::Path}; +use std::io::Write; use anyhow::Result; use base64::{Engine, engine::general_purpose}; use image::DynamicImage; use ratatui::layout::Rect; +use yazi_shared::url::Url; use yazi_term::tty::TTY; use crate::{CLOSE, ESCAPE, Emulator, Image, START, adapter::Adapter}; @@ -12,8 +13,8 @@ use crate::{CLOSE, ESCAPE, Emulator, Image, START, adapter::Adapter}; pub(crate) struct KgpOld; impl KgpOld { - pub(crate) async fn image_show(path: &Path, max: Rect) -> Result { - let img = Image::downscale(path, max).await?; + pub(crate) async fn image_show(url: &Url, max: Rect) -> Result { + let img = Image::downscale(url, max).await?; let area = Image::pixel_area((img.width(), img.height()), max); let b = Self::encode(img).await?; diff --git a/yazi-adapter/src/drivers/sixel.rs b/yazi-adapter/src/drivers/sixel.rs index 6d7db947..79876626 100644 --- a/yazi-adapter/src/drivers/sixel.rs +++ b/yazi-adapter/src/drivers/sixel.rs @@ -1,4 +1,4 @@ -use std::{io::Write, path::Path}; +use std::io::Write; use anyhow::{Result, bail}; use crossterm::{cursor::MoveTo, queue}; @@ -6,14 +6,15 @@ use image::{DynamicImage, GenericImageView, RgbImage}; use palette::{Srgb, cast::ComponentsAs}; use quantette::{ColorSlice, PaletteSize, QuantizeOutput, wu::UIntBinner}; use ratatui::layout::Rect; +use yazi_shared::url::Url; use crate::{CLOSE, ESCAPE, Emulator, Image, START, adapter::Adapter}; pub(crate) struct Sixel; impl Sixel { - pub(crate) async fn image_show(path: &Path, max: Rect) -> Result { - let img = Image::downscale(path, max).await?; + pub(crate) async fn image_show(url: &Url, max: Rect) -> Result { + let img = Image::downscale(url, max).await?; let area = Image::pixel_area((img.width(), img.height()), max); let b = Self::encode(img).await?; diff --git a/yazi-adapter/src/image.rs b/yazi-adapter/src/image.rs index 9df206f0..802f328f 100644 --- a/yazi-adapter/src/image.rs +++ b/yazi-adapter/src/image.rs @@ -1,17 +1,19 @@ -use std::path::Path; +use std::io::BufReader; use anyhow::Result; -use image::{DynamicImage, ExtendedColorType, ImageDecoder, ImageEncoder, ImageError, ImageReader, ImageResult, Limits, codecs::{jpeg::JpegEncoder, png::PngEncoder}, imageops::FilterType, metadata::Orientation}; +use image::{DynamicImage, ExtendedColorType, ImageDecoder, ImageEncoder, ImageError, ImageFormat, ImageReader, ImageResult, Limits, codecs::{jpeg::JpegEncoder, png::PngEncoder}, imageops::FilterType, metadata::Orientation}; use ratatui::layout::Rect; use yazi_config::YAZI; +use yazi_fs::services; +use yazi_shared::url::Url; use crate::Dimension; pub struct Image; impl Image { - pub async fn precache(path: &Path, cache: &Path) -> Result<()> { - let (mut img, orientation, icc) = Self::decode_from(path).await?; + pub async fn precache(src: &Url, cache: &Url) -> Result<()> { + let (mut img, orientation, icc) = Self::decode_from(src).await?; let (w, h) = Self::flip_size(orientation, (YAZI.preview.max_width, YAZI.preview.max_height)); let buf = tokio::task::spawn_blocking(move || { @@ -38,11 +40,11 @@ impl Image { }) .await??; - Ok(tokio::fs::write(cache, buf).await?) + Ok(services::write(cache, buf).await?) } - pub(super) async fn downscale(path: &Path, rect: Rect) -> Result { - let (mut img, orientation, _) = Self::decode_from(path).await?; + pub(super) async fn downscale(url: &Url, rect: Rect) -> Result { + let (mut img, orientation, _) = Self::decode_from(url).await?; let (w, h) = Self::flip_size(orientation, Self::max_pixel(rect)); // Fast path. @@ -96,7 +98,7 @@ impl Image { } } - async fn decode_from(path: &Path) -> ImageResult<(DynamicImage, Orientation, Option>)> { + async fn decode_from(url: &Url) -> ImageResult<(DynamicImage, Orientation, Option>)> { let mut limits = Limits::no_limits(); if YAZI.tasks.image_alloc > 0 { limits.max_alloc = Some(YAZI.tasks.image_alloc as u64); @@ -108,11 +110,13 @@ impl Image { limits.max_image_height = Some(YAZI.tasks.image_bound[1] as u32); } - let path = path.to_owned(); - tokio::task::spawn_blocking(move || { - let mut reader = ImageReader::open(path)?; - reader.limits(limits); + let mut reader = ImageReader::new(BufReader::new(services::open(&url).await?.into_std().await)); + if let Ok(format) = ImageFormat::from_path(url) { + reader.set_format(format); + } + reader.limits(limits); + tokio::task::spawn_blocking(move || { let mut decoder = reader.with_guessed_format()?.into_decoder()?; let orientation = decoder.orientation().unwrap_or(Orientation::NoTransforms); let icc = decoder.icc_profile().unwrap_or_default(); diff --git a/yazi-binding/src/url.rs b/yazi-binding/src/url.rs index 7b01b827..eed87519 100644 --- a/yazi-binding/src/url.rs +++ b/yazi-binding/src/url.rs @@ -25,6 +25,11 @@ impl Deref for Url { fn deref(&self) -> &Self::Target { &self.inner } } +impl AsRef for Url { + fn as_ref(&self) -> &yazi_shared::url::Url { &self.inner } +} + +// FIXME: remove impl AsRef for Url { fn as_ref(&self) -> &Path { self.inner.as_path() } } diff --git a/yazi-cli/src/package/delete.rs b/yazi-cli/src/package/delete.rs index 17ac7588..d42b6caf 100644 --- a/yazi-cli/src/package/delete.rs +++ b/yazi-cli/src/package/delete.rs @@ -1,6 +1,5 @@ use anyhow::{Context, Result}; -use tokio::fs; -use yazi_fs::{maybe_exists, ok_or_not_found, remove_dir_clean, remove_sealed}; +use yazi_fs::{maybe_exists, ok_or_not_found, remove_dir_clean, remove_sealed, services::Local}; use yazi_macro::outln; use super::Dependency; @@ -23,7 +22,7 @@ impl Dependency { pub(super) async fn delete_assets(&self) -> Result<()> { let assets = self.target().join("assets"); - match fs::read_dir(&assets).await { + match Local::read_dir(&assets).await { Ok(mut it) => { while let Some(entry) = it.next_entry().await? { remove_sealed(&entry.path()) @@ -52,7 +51,7 @@ impl Dependency { .with_context(|| format!("failed to delete `{}`", p.display()))?; } - if ok_or_not_found(fs::remove_dir(&dir).await).is_ok() { + if ok_or_not_found(Local::remove_dir(&dir).await).is_ok() { outln!("Done!")?; } else { outln!( diff --git a/yazi-cli/src/package/deploy.rs b/yazi-cli/src/package/deploy.rs index a5b75bd2..226e1192 100644 --- a/yazi-cli/src/package/deploy.rs +++ b/yazi-cli/src/package/deploy.rs @@ -1,8 +1,7 @@ use std::path::{Path, PathBuf}; use anyhow::{Context, Result}; -use tokio::fs; -use yazi_fs::{copy_and_seal, maybe_exists, remove_dir_clean}; +use yazi_fs::{copy_and_seal, maybe_exists, remove_dir_clean, services::Local}; use yazi_macro::outln; use super::Dependency; @@ -20,7 +19,7 @@ impl Dependency { self.hash_check().await?; } - fs::create_dir_all(&to).await?; + Local::create_dir_all(&to).await?; self.delete_assets().await?; let res1 = Self::deploy_assets(from.join("assets"), to.join("assets")).await; @@ -40,9 +39,9 @@ impl Dependency { } async fn deploy_assets(from: PathBuf, to: PathBuf) -> Result<()> { - match fs::read_dir(&from).await { + match Local::read_dir(&from).await { Ok(mut it) => { - fs::create_dir_all(&to).await?; + Local::create_dir_all(&to).await?; while let Some(entry) = it.next_entry().await? { let (src, dist) = (entry.path(), to.join(entry.file_name())); copy_and_seal(&src, &dist).await.with_context(|| { diff --git a/yazi-cli/src/package/hash.rs b/yazi-cli/src/package/hash.rs index 841f112a..cae08fe5 100644 --- a/yazi-cli/src/package/hash.rs +++ b/yazi-cli/src/package/hash.rs @@ -1,7 +1,6 @@ use anyhow::{Context, Result, bail}; -use tokio::fs; use twox_hash::XxHash3_128; -use yazi_fs::ok_or_not_found; +use yazi_fs::{ok_or_not_found, services::Local}; use super::Dependency; @@ -26,17 +25,17 @@ impl Dependency { for &file in files { h.write(file.as_bytes()); h.write(b"VpvFw9Atb7cWGOdqhZCra634CcJJRlsRl72RbZeV0vpG1\0"); - h.write(&ok_or_not_found(fs::read(dir.join(file)).await)?); + h.write(&ok_or_not_found(Local::read(dir.join(file)).await)?); } let mut assets = vec![]; - match fs::read_dir(dir.join("assets")).await { + match Local::read_dir(dir.join("assets")).await { Ok(mut it) => { while let Some(entry) = it.next_entry().await? { let Ok(name) = entry.file_name().into_string() else { bail!("asset path is not valid UTF-8: {}", entry.path().display()); }; - assets.push((name, fs::read(entry.path()).await?)); + assets.push((name, Local::read(entry.path()).await?)); } } Err(e) if e.kind() == std::io::ErrorKind::NotFound => {} diff --git a/yazi-cli/src/package/package.rs b/yazi-cli/src/package/package.rs index f211253a..9d70678e 100644 --- a/yazi-cli/src/package/package.rs +++ b/yazi-cli/src/package/package.rs @@ -2,8 +2,7 @@ use std::{path::PathBuf, str::FromStr}; use anyhow::{Context, Result, bail}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use tokio::fs; -use yazi_fs::Xdg; +use yazi_fs::{Xdg, services::Local}; use yazi_macro::outln; use super::Dependency; @@ -16,7 +15,7 @@ pub(crate) struct Package { impl Package { pub(crate) async fn load() -> Result { - Ok(match fs::read_to_string(Self::toml()).await { + Ok(match Local::read_to_string(Self::toml()).await { Ok(s) => toml::from_str(&s)?, Err(e) if e.kind() == std::io::ErrorKind::NotFound => Self::default(), Err(e) => Err(e)?, @@ -136,7 +135,7 @@ impl Package { async fn save(&self) -> Result<()> { let s = toml::to_string_pretty(self)?; - fs::write(Self::toml(), s).await.context("Failed to write package.toml") + Local::write(Self::toml(), s).await.context("Failed to write package.toml") } #[inline] diff --git a/yazi-core/src/mgr/watcher.rs b/yazi-core/src/mgr/watcher.rs index a151647f..68c56987 100644 --- a/yazi-core/src/mgr/watcher.rs +++ b/yazi-core/src/mgr/watcher.rs @@ -3,10 +3,10 @@ use std::{collections::{HashMap, HashSet}, time::Duration}; use anyhow::Result; use notify::{PollWatcher, RecommendedWatcher, RecursiveMode, Watcher as _Watcher}; use parking_lot::RwLock; -use tokio::{fs, pin, sync::{mpsc::{self, UnboundedReceiver}, watch}}; +use tokio::{pin, sync::{mpsc::{self, UnboundedReceiver}, watch}}; use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream}; use tracing::error; -use yazi_fs::{File, Files, FilesOp, cha::Cha, realname_unchecked}; +use yazi_fs::{File, Files, FilesOp, cha::Cha, realname_unchecked, services}; use yazi_proxy::WATCHER; use yazi_shared::{RoCell, url::Url}; @@ -21,6 +21,7 @@ pub struct Watcher { out_tx: mpsc::UnboundedSender, } +// FIXME: VFS impl Watcher { pub(super) fn serve() -> Self { let (in_tx, in_rx) = watch::channel(Default::default()); @@ -138,7 +139,7 @@ impl Watcher { }; let u = &file.url; - let eq = (!file.is_link() && fs::canonicalize(u).await.is_ok_and(|p| p == ***u)) + let eq = (!file.is_link() && services::canonicalize(u).await.is_ok_and(|c| c == *u)) || realname_unchecked(u, &mut cached).await.is_ok_and(|s| urn.as_urn() == s); if !eq { @@ -193,10 +194,10 @@ impl Watcher { async fn go(todo: HashSet) { for from in todo { - let Ok(to) = fs::canonicalize(&from).await else { continue }; + let Ok(to) = services::canonicalize(&from).await else { continue }; - if to != **from && WATCHED.read().contains(&from) { - LINKED.write().insert(from, Url::from(to)); + if to != from && WATCHED.read().contains(&from) { + LINKED.write().insert(from, to); } } } diff --git a/yazi-dds/src/state.rs b/yazi-dds/src/state.rs index dcf23ac7..d02d87ac 100644 --- a/yazi-dds/src/state.rs +++ b/yazi-dds/src/state.rs @@ -2,8 +2,9 @@ use std::{collections::HashMap, mem, ops::Deref, sync::atomic::{AtomicU64, Order use anyhow::Result; use parking_lot::RwLock; -use tokio::{fs::{self, File, OpenOptions}, io::{AsyncBufReadExt, AsyncWriteExt, BufReader, BufWriter}}; +use tokio::{fs::OpenOptions, io::{AsyncBufReadExt, AsyncWriteExt, BufReader, BufWriter}}; use yazi_boot::BOOT; +use yazi_fs::services::Local; use yazi_shared::{RoCell, timestamp_us}; use crate::CLIENTS; @@ -56,8 +57,9 @@ impl State { return Ok(()); } - fs::create_dir_all(&BOOT.state_dir).await?; + Local::create_dir_all(&BOOT.state_dir).await?; let mut buf = BufWriter::new( + // TODO: VFS OpenOptions::new() .write(true) .create(true) @@ -77,7 +79,7 @@ impl State { } async fn load(&self) -> Result<()> { - let mut file = BufReader::new(File::open(BOOT.state_dir.join(".dds")).await?); + let mut file = BufReader::new(Local::open(BOOT.state_dir.join(".dds")).await?); let mut buf = String::new(); let mut inner = HashMap::new(); @@ -101,7 +103,7 @@ impl State { } async fn skip(&self) -> Result { - let meta = fs::symlink_metadata(BOOT.state_dir.join(".dds")).await?; + let meta = Local::symlink_metadata(BOOT.state_dir.join(".dds")).await?; let modified = meta.modified()?.duration_since(UNIX_EPOCH)?.as_micros(); Ok(modified >= self.last.load(Ordering::Relaxed) as u128) } diff --git a/yazi-dds/src/stream.rs b/yazi-dds/src/stream.rs index 15c1176f..5544d321 100644 --- a/yazi-dds/src/stream.rs +++ b/yazi-dds/src/stream.rs @@ -38,7 +38,7 @@ impl Stream { pub(super) async fn bind() -> std::io::Result { let p = Self::socket_file(); - tokio::fs::remove_file(&p).await.ok(); + yazi_fs::services::Local::remove_file(&p).await.ok(); tokio::net::UnixListener::bind(p) } diff --git a/yazi-fm/src/app/commands/quit.rs b/yazi-fm/src/app/commands/quit.rs index 33928ea6..3dd39157 100644 --- a/yazi-fm/src/app/commands/quit.rs +++ b/yazi-fm/src/app/commands/quit.rs @@ -1,7 +1,7 @@ use std::ffi::OsString; -use tokio::fs; use yazi_boot::ARGS; +use yazi_fs::services::Local; use yazi_shared::event::EventQuit; use crate::{Term, app::App}; @@ -26,13 +26,13 @@ impl App { async fn cwd_to_file(&self, no: bool) { if let Some(p) = ARGS.cwd_file.as_ref().filter(|_| !no) { let cwd = self.core.mgr.cwd().as_os_str(); - fs::write(p, cwd.as_encoded_bytes()).await.ok(); + Local::write(p, cwd.as_encoded_bytes()).await.ok(); } } async fn selected_to_file(&self, selected: Option) { if let (Some(s), Some(p)) = (selected, &ARGS.chooser_file) { - fs::write(p, s.as_encoded_bytes()).await.ok(); + Local::write(p, s.as_encoded_bytes()).await.ok(); } } } diff --git a/yazi-fs/src/cha/cha.rs b/yazi-fs/src/cha/cha.rs index b71b87a6..873dcdcf 100644 --- a/yazi-fs/src/cha/cha.rs +++ b/yazi-fs/src/cha/cha.rs @@ -1,10 +1,10 @@ -use std::{fs::{FileType, Metadata}, path::Path, time::SystemTime}; +use std::{fs::{FileType, Metadata}, time::SystemTime}; -use tokio::fs; use yazi_macro::{unix_either, win_either}; use yazi_shared::url::Url; use super::ChaKind; +use crate::services; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Cha { @@ -53,20 +53,20 @@ impl Default for Cha { impl Cha { #[inline] - pub fn new(path: &Path, meta: Metadata) -> Self { - Self::from_just_meta(&meta).attach(ChaKind::hidden(path, &meta)) + pub fn new(url: &Url, meta: Metadata) -> Self { + Self::from_just_meta(&meta).attach(ChaKind::hidden(url, &meta)) } #[inline] pub async fn from_url(url: &Url) -> std::io::Result { - Ok(Self::from_follow(url, fs::symlink_metadata(url).await?).await) + Ok(Self::from_follow(url, services::symlink_metadata(url).await?).await) } - pub async fn from_follow(path: &Path, mut meta: Metadata) -> Self { - let mut attached = ChaKind::hidden(path, &meta); + pub async fn from_follow(url: &Url, mut meta: Metadata) -> Self { + let mut attached = ChaKind::hidden(url, &meta); if meta.is_symlink() { attached |= ChaKind::LINK; - meta = fs::metadata(path).await.unwrap_or(meta); + meta = services::metadata(url).await.unwrap_or(meta); } if meta.is_symlink() { attached |= ChaKind::ORPHAN; diff --git a/yazi-fs/src/cha/kind.rs b/yazi-fs/src/cha/kind.rs index 8b71aa41..2a7861bd 100644 --- a/yazi-fs/src/cha/kind.rs +++ b/yazi-fs/src/cha/kind.rs @@ -1,6 +1,7 @@ -use std::{fs::Metadata, path::Path}; +use std::fs::Metadata; use bitflags::bitflags; +use yazi_shared::url::Url; bitflags! { #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] @@ -19,11 +20,11 @@ bitflags! { impl ChaKind { #[inline] - pub(super) fn hidden(_path: &Path, _meta: &Metadata) -> Self { + pub(super) fn hidden(_url: &Url, _meta: &Metadata) -> Self { let mut me = Self::empty(); #[cfg(unix)] - if yazi_shared::url::Urn::new(_path).is_hidden() { + if _url.urn().is_hidden() { me |= Self::HIDDEN; } #[cfg(windows)] diff --git a/yazi-fs/src/file.rs b/yazi-fs/src/file.rs index 480e66b6..598877f6 100644 --- a/yazi-fs/src/file.rs +++ b/yazi-fs/src/file.rs @@ -1,10 +1,9 @@ use std::{ffi::OsStr, fs::{FileType, Metadata}, hash::{BuildHasher, Hash, Hasher}, ops::Deref}; use anyhow::Result; -use tokio::fs; use yazi_shared::url::{Url, Urn, UrnBuf}; -use crate::cha::Cha; +use crate::{cha::Cha, services}; #[derive(Clone, Debug, Default)] pub struct File { @@ -23,14 +22,13 @@ impl Deref for File { impl File { #[inline] pub async fn new(url: Url) -> Result { - let meta = fs::symlink_metadata(&url).await?; + let meta = services::symlink_metadata(&url).await?; Ok(Self::from_follow(url, meta).await) } #[inline] pub async fn from_follow(url: Url, meta: Metadata) -> Self { - let link_to = - if meta.is_symlink() { fs::read_link(&url).await.map(Url::from).ok() } else { None }; + let link_to = if meta.is_symlink() { services::read_link(&url).await.ok() } else { None }; let cha = Cha::from_follow(&url, meta).await; diff --git a/yazi-fs/src/files.rs b/yazi-fs/src/files.rs index 7769bcbc..8a6aee7f 100644 --- a/yazi-fs/src/files.rs +++ b/yazi-fs/src/files.rs @@ -1,10 +1,10 @@ use std::{collections::{HashMap, HashSet}, mem, ops::{Deref, Not}}; -use tokio::{fs::{self, DirEntry}, select, sync::mpsc::{self, UnboundedReceiver}}; +use tokio::{select, sync::mpsc::{self, UnboundedReceiver}}; use yazi_shared::{Id, url::{Url, Urn, UrnBuf}}; use super::{FilesSorter, Filter}; -use crate::{FILES_TICKET, File, FilesOp, SortBy, cha::Cha, mounts::PARTITIONS}; +use crate::{FILES_TICKET, File, FilesOp, SortBy, cha::Cha, mounts::PARTITIONS, services}; #[derive(Default)] pub struct Files { @@ -31,7 +31,7 @@ impl Files { pub fn new(show_hidden: bool) -> Self { Self { show_hidden, ..Default::default() } } pub async fn from_dir(dir: &Url) -> std::io::Result> { - let mut it = fs::read_dir(dir).await?; + let mut it = services::read_dir(dir).await?; let (tx, rx) = mpsc::unbounded_channel(); tokio::spawn(async move { @@ -52,7 +52,7 @@ impl Files { } pub async fn from_dir_bulk(dir: &Url) -> std::io::Result> { - let mut it = fs::read_dir(dir).await?; + let mut it = services::read_dir(dir).await?; let mut entries = Vec::with_capacity(5000); while let Ok(Some(entry)) = it.next_entry().await { entries.push(entry); @@ -60,7 +60,7 @@ impl Files { let (first, rest) = entries.split_at(entries.len() / 3); let (second, third) = rest.split_at(entries.len() / 3); - async fn go(entries: &[DirEntry]) -> Vec { + async fn go(entries: &[tokio::fs::DirEntry]) -> Vec { let mut files = Vec::with_capacity(entries.len()); for entry in entries { let url = Url::from(entry.path()); diff --git a/yazi-fs/src/fns.rs b/yazi-fs/src/fns.rs index 91b59b4e..68b14980 100644 --- a/yazi-fs/src/fns.rs +++ b/yazi-fs/src/fns.rs @@ -1,3 +1,5 @@ +// FIXME: VFS + use std::{borrow::Cow, collections::{HashMap, HashSet}, ffi::{OsStr, OsString}, path::{Path, PathBuf}}; use anyhow::{Result, bail}; diff --git a/yazi-fs/src/lib.rs b/yazi-fs/src/lib.rs index 9bfaf558..03587eb3 100644 --- a/yazi-fs/src/lib.rs +++ b/yazi-fs/src/lib.rs @@ -1,6 +1,6 @@ #![allow(clippy::if_same_then_else, clippy::option_map_unit_fn)] -yazi_macro::mod_pub!(cha mounts); +yazi_macro::mod_pub!(cha mounts services); yazi_macro::mod_flat!(calculator cwd file files filter fns op path sorter sorting stage xdg); diff --git a/yazi-fs/src/path.rs b/yazi-fs/src/path.rs index 1e9782b5..0194d2e7 100644 --- a/yazi-fs/src/path.rs +++ b/yazi-fs/src/path.rs @@ -1,9 +1,8 @@ use std::{borrow::Cow, env, ffi::{OsStr, OsString}, future::Future, io, path::{Component, Path, PathBuf}}; -use tokio::fs; -use yazi_shared::url::{Loc, Url}; +use yazi_shared::url::Url; -use crate::CWD; +use crate::{CWD, services}; #[inline] pub fn clean_url(url: &Url) -> Url { Url::from(clean_path(url)) } @@ -83,19 +82,19 @@ pub async fn unique_name(u: Url, append: F) -> io::Result where F: Future, { - match fs::symlink_metadata(&u).await { + match services::symlink_metadata(&u).await { Ok(_) => _unique_name(u, append.await).await, Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(u), Err(e) => Err(e), } } -async fn _unique_name(mut u: Url, append: bool) -> io::Result { - let Some(stem) = u.file_stem().map(|s| s.to_owned()) else { +async fn _unique_name(mut url: Url, append: bool) -> io::Result { + let Some(stem) = url.file_stem().map(|s| s.to_owned()) else { return Err(io::Error::new(io::ErrorKind::InvalidInput, "empty file stem")); }; - let dot_ext = u.extension().map_or_else(OsString::new, |e| { + let dot_ext = url.extension().map_or_else(OsString::new, |e| { let mut s = OsString::with_capacity(e.len() + 1); s.push("."); s.push(e); @@ -103,9 +102,9 @@ async fn _unique_name(mut u: Url, append: bool) -> io::Result { }); let mut i = 1u64; - let mut p = u.to_path(); + let mut name = OsString::with_capacity(stem.len() + dot_ext.len() + 5); loop { - let mut name = OsString::with_capacity(stem.len() + dot_ext.len() + 5); + name.clear(); name.push(&stem); if append { @@ -118,16 +117,15 @@ async fn _unique_name(mut u: Url, append: bool) -> io::Result { name.push(&dot_ext); } - p.set_file_name(name); - match fs::symlink_metadata(&p).await { + url.set_name(&name); + match services::symlink_metadata(&url).await { Ok(_) => i += 1, Err(e) if e.kind() == io::ErrorKind::NotFound => break, Err(e) => return Err(e), } } - u.set_loc(Loc::from(u.base(), p)); - Ok(u) + Ok(url) } // Parameters diff --git a/yazi-fs/src/services/local.rs b/yazi-fs/src/services/local.rs new file mode 100644 index 00000000..1770dfbe --- /dev/null +++ b/yazi-fs/src/services/local.rs @@ -0,0 +1,83 @@ +use std::{io, path::{Path, PathBuf}}; + +pub struct Local; + +impl Local { + #[inline] + pub async fn canonicalize(path: impl AsRef) -> io::Result { + tokio::fs::canonicalize(path).await + } + + #[inline] + pub async fn create(path: impl AsRef) -> io::Result { + tokio::fs::File::create(path).await + } + + #[inline] + pub async fn create_dir(path: impl AsRef) -> io::Result<()> { + tokio::fs::create_dir(path).await + } + + #[inline] + pub async fn create_dir_all(path: impl AsRef) -> io::Result<()> { + tokio::fs::create_dir_all(path).await + } + + #[inline] + pub async fn metadata(url: impl AsRef) -> io::Result { + tokio::fs::metadata(url).await + } + + #[inline] + pub async fn open(path: impl AsRef) -> io::Result { + tokio::fs::File::open(path).await + } + + #[inline] + pub async fn read(path: impl AsRef) -> io::Result> { tokio::fs::read(path).await } + + #[inline] + pub async fn read_dir(path: impl AsRef) -> io::Result { + tokio::fs::read_dir(path).await + } + + #[inline] + pub async fn read_link(url: impl AsRef) -> io::Result { + tokio::fs::read_link(url).await + } + + #[inline] + pub async fn read_to_string(path: impl AsRef) -> io::Result { + tokio::fs::read_to_string(path).await + } + + #[inline] + pub async fn remove_dir(path: impl AsRef) -> io::Result<()> { + tokio::fs::remove_dir(path).await + } + + #[inline] + pub async fn remove_dir_all(path: impl AsRef) -> io::Result<()> { + tokio::fs::remove_dir_all(path).await + } + + #[inline] + pub async fn remove_file(path: impl AsRef) -> io::Result<()> { + tokio::fs::remove_file(path).await + } + + #[inline] + pub async fn rename(from: impl AsRef, to: impl AsRef) -> io::Result<()> { + tokio::fs::rename(from, to).await + } + + #[inline] + pub async fn symlink_metadata(path: impl AsRef) -> io::Result { + tokio::fs::symlink_metadata(path).await + } + + #[inline] + pub async fn write(path: impl AsRef, contents: impl AsRef<[u8]>) -> io::Result<()> { + tokio::fs::write(path, contents).await + } +} diff --git a/yazi-fs/src/services/mod.rs b/yazi-fs/src/services/mod.rs new file mode 100644 index 00000000..288105e4 --- /dev/null +++ b/yazi-fs/src/services/mod.rs @@ -0,0 +1 @@ +yazi_macro::mod_flat!(local services); diff --git a/yazi-fs/src/services/services.rs b/yazi-fs/src/services/services.rs new file mode 100644 index 00000000..4eaac448 --- /dev/null +++ b/yazi-fs/src/services/services.rs @@ -0,0 +1,75 @@ +use std::io; + +use yazi_shared::url::Url; + +use crate::services::Local; + +#[inline] +pub async fn canonicalize(url: impl AsRef) -> io::Result { + Local::canonicalize(url.as_ref()).await.map(Into::into) +} + +#[inline] +pub async fn create(url: impl AsRef) -> io::Result { + Local::create(url.as_ref()).await +} + +#[inline] +pub async fn create_dir(url: impl AsRef) -> io::Result<()> { + Local::create_dir(url.as_ref()).await +} + +#[inline] +pub async fn create_dir_all(url: impl AsRef) -> io::Result<()> { + Local::create_dir_all(url.as_ref()).await +} + +#[inline] +pub async fn metadata(url: impl AsRef) -> io::Result { + Local::metadata(url.as_ref()).await +} + +#[inline] +pub async fn open(url: impl AsRef) -> io::Result { + Local::open(url.as_ref()).await +} + +#[inline] +pub async fn read_dir(url: impl AsRef) -> io::Result { + Local::read_dir(url.as_ref()).await +} + +#[inline] +pub async fn read_link(url: impl AsRef) -> io::Result { + Local::read_link(url.as_ref()).await.map(Into::into) +} + +#[inline] +pub async fn remove_dir(url: impl AsRef) -> io::Result<()> { + Local::remove_dir(url.as_ref()).await +} + +#[inline] +pub async fn remove_dir_all(url: impl AsRef) -> io::Result<()> { + Local::remove_dir_all(url.as_ref()).await +} + +#[inline] +pub async fn remove_file(url: impl AsRef) -> io::Result<()> { + Local::remove_file(url.as_ref()).await +} + +#[inline] +pub async fn rename(from: impl AsRef, to: impl AsRef) -> io::Result<()> { + Local::rename(from.as_ref(), to.as_ref()).await +} + +#[inline] +pub async fn symlink_metadata(url: impl AsRef) -> io::Result { + Local::symlink_metadata(url.as_ref()).await +} + +#[inline] +pub async fn write(url: impl AsRef, contents: impl AsRef<[u8]>) -> io::Result<()> { + Local::write(url.as_ref(), contents).await +} diff --git a/yazi-plugin/src/external/highlighter.rs b/yazi-plugin/src/external/highlighter.rs index 43d1650d..1c177cee 100644 --- a/yazi-plugin/src/external/highlighter.rs +++ b/yazi-plugin/src/external/highlighter.rs @@ -3,8 +3,9 @@ use std::{borrow::Cow, io::Cursor, mem, path::{Path, PathBuf}, sync::OnceLock}; use anyhow::{Result, anyhow}; use ratatui::{layout::Size, text::{Line, Span, Text}}; use syntect::{LoadingError, dumps, easy::HighlightLines, highlighting::{self, Theme, ThemeSet}, parsing::{SyntaxReference, SyntaxSet}}; -use tokio::{fs::File, io::{AsyncBufReadExt, BufReader}}; +use tokio::io::{AsyncBufReadExt, BufReader}; use yazi_config::{THEME, YAZI, preview::PreviewWrap}; +use yazi_fs::services::Local; use yazi_shared::{Ids, errors::PeekError, replace_to_printable}; static INCR: Ids = Ids::new(); @@ -38,7 +39,7 @@ impl Highlighter { pub fn abort() { INCR.next(); } pub async fn highlight(&self, skip: usize, size: Size) -> Result, PeekError> { - let mut reader = BufReader::new(File::open(&self.path).await?); + let mut reader = BufReader::new(Local::open(&self.path).await?); let syntax = Self::find_syntax(&self.path).await; let mut plain = syntax.is_err(); @@ -142,7 +143,7 @@ impl Highlighter { } let mut line = String::new(); - let mut reader = BufReader::new(File::open(&path).await?); + let mut reader = BufReader::new(Local::open(&path).await?); reader.read_line(&mut line).await?; syntaxes.find_syntax_by_first_line(&line).ok_or_else(|| anyhow!("No syntax found")) } diff --git a/yazi-plugin/src/fs/fs.rs b/yazi-plugin/src/fs/fs.rs index c81c9e0c..7340c3b4 100644 --- a/yazi-plugin/src/fs/fs.rs +++ b/yazi-plugin/src/fs/fs.rs @@ -1,8 +1,7 @@ use globset::GlobBuilder; use mlua::{ExternalError, ExternalResult, Function, IntoLua, IntoLuaMulti, Lua, Table, Value}; -use tokio::fs; use yazi_binding::{Cha, Composer, ComposerGet, ComposerSet, Error, File, Url, UrlRef}; -use yazi_fs::{mounts::PARTITIONS, remove_dir_clean}; +use yazi_fs::{mounts::PARTITIONS, remove_dir_clean, services}; use crate::bindings::SizeCalculator; @@ -49,9 +48,9 @@ fn cwd(lua: &Lua) -> mlua::Result { fn cha(lua: &Lua) -> mlua::Result { lua.create_async_function(|lua, (url, follow): (UrlRef, Option)| async move { let meta = if follow.unwrap_or(false) { - fs::metadata(&*url).await + services::metadata(&*url).await } else { - fs::symlink_metadata(&*url).await + services::symlink_metadata(&*url).await }; match meta { @@ -63,7 +62,7 @@ fn cha(lua: &Lua) -> mlua::Result { fn write(lua: &Lua) -> mlua::Result { lua.create_async_function(|lua, (url, data): (UrlRef, mlua::String)| async move { - match fs::write(&*url, data.as_bytes()).await { + match services::write(&*url, data.as_bytes()).await { Ok(()) => true.into_lua_multi(&lua), Err(e) => (false, Error::Io(e)).into_lua_multi(&lua), } @@ -73,8 +72,8 @@ fn write(lua: &Lua) -> mlua::Result { fn create(lua: &Lua) -> mlua::Result { lua.create_async_function(|lua, (r#type, url): (mlua::String, UrlRef)| async move { let result = match r#type.as_bytes().as_ref() { - b"dir" => fs::create_dir(&*url).await, - b"dir_all" => fs::create_dir_all(&*url).await, + b"dir" => services::create_dir(&*url).await, + b"dir_all" => services::create_dir_all(&*url).await, _ => Err("Creation type must be 'dir' or 'dir_all'".into_lua_err())?, }; @@ -88,9 +87,9 @@ fn create(lua: &Lua) -> mlua::Result { fn remove(lua: &Lua) -> mlua::Result { lua.create_async_function(|lua, (r#type, url): (mlua::String, UrlRef)| async move { let result = match r#type.as_bytes().as_ref() { - b"file" => fs::remove_file(&*url).await, - b"dir" => fs::remove_dir(&*url).await, - b"dir_all" => fs::remove_dir_all(&*url).await, + b"file" => services::remove_file(&*url).await, + b"dir" => services::remove_dir(&*url).await, + b"dir_all" => services::remove_dir_all(&*url).await, b"dir_clean" => Ok(remove_dir_clean(&url).await), _ => Err("Removal type must be 'file', 'dir', 'dir_all', or 'dir_clean'".into_lua_err())?, }; @@ -122,7 +121,7 @@ fn read_dir(lua: &Lua) -> mlua::Result { let limit = options.raw_get("limit").unwrap_or(usize::MAX); let resolve = options.raw_get("resolve").unwrap_or(false); - let mut it = match fs::read_dir(&*dir).await { + let mut it = match services::read_dir(&*dir).await { Ok(it) => it, Err(e) => return (Value::Nil, Error::Io(e)).into_lua_multi(&lua), }; diff --git a/yazi-plugin/src/loader/loader.rs b/yazi-plugin/src/loader/loader.rs index 1cf7aa3a..609fa8e0 100644 --- a/yazi-plugin/src/loader/loader.rs +++ b/yazi-plugin/src/loader/loader.rs @@ -3,8 +3,8 @@ use std::{borrow::Cow, collections::HashMap, ops::Deref}; use anyhow::{Context, Result, bail}; use mlua::{ChunkMode, ExternalError, Lua, Table}; use parking_lot::RwLock; -use tokio::fs; use yazi_boot::BOOT; +use yazi_fs::services::Local; use yazi_macro::plugin_preset as preset; use yazi_shared::{LOG_LEVEL, RoCell}; @@ -61,7 +61,7 @@ impl Loader { let p = BOOT.plugin_dir.join(format!("{name}.yazi/main.lua")); let chunk = - fs::read(&p).await.with_context(|| format!("Failed to load plugin from {p:?}"))?.into(); + Local::read(&p).await.with_context(|| format!("Failed to load plugin from {p:?}"))?.into(); let result = Self::compatible_or_error(name, &chunk); let inspect = f(&chunk); diff --git a/yazi-scheduler/src/file/file.rs b/yazi-scheduler/src/file/file.rs index 1806fe71..03fa47e7 100644 --- a/yazi-scheduler/src/file/file.rs +++ b/yazi-scheduler/src/file/file.rs @@ -1,10 +1,11 @@ -use std::{borrow::Cow, collections::VecDeque, path::Path}; +// FIXME: VFS, depends on yazi_fs::fns +use std::{borrow::Cow, collections::VecDeque}; use anyhow::{Result, anyhow}; use tokio::{fs::{self, DirEntry}, io::{self, ErrorKind::{AlreadyExists, NotFound}}, sync::mpsc}; use tracing::warn; use yazi_config::YAZI; -use yazi_fs::{SizeCalculator, cha::Cha, copy_with_progress, maybe_exists, ok_or_not_found, path_relative_to, skip_path}; +use yazi_fs::{SizeCalculator, cha::Cha, copy_with_progress, maybe_exists, ok_or_not_found, path_relative_to, services, skip_path}; use yazi_shared::{Id, url::Url}; use super::{FileIn, FileInDelete, FileInHardlink, FileInLink, FileInPaste, FileInTrash}; @@ -327,17 +328,17 @@ impl File { } #[inline] - async fn cha(path: &Path, follow: bool) -> io::Result { - let meta = fs::symlink_metadata(path).await?; - Ok(if follow { Cha::from_follow(path, meta).await } else { Cha::new(path, meta) }) + async fn cha(url: &Url, follow: bool) -> io::Result { + let meta = services::symlink_metadata(url).await?; + Ok(if follow { Cha::from_follow(url, meta).await } else { Cha::new(url, meta) }) } #[inline] - async fn cha_from(entry: DirEntry, path: &Path, follow: bool) -> io::Result { + async fn cha_from(entry: DirEntry, url: &Url, follow: bool) -> io::Result { Ok(if follow { - Cha::from_follow(path, entry.metadata().await?).await + Cha::from_follow(url, entry.metadata().await?).await } else { - Cha::new(path, entry.metadata().await?) + Cha::new(url, entry.metadata().await?) }) } } diff --git a/yazi-scheduler/src/scheduler.rs b/yazi-scheduler/src/scheduler.rs index c29b9156..2320a74d 100644 --- a/yazi-scheduler/src/scheduler.rs +++ b/yazi-scheduler/src/scheduler.rs @@ -3,10 +3,10 @@ use std::{ffi::OsString, future::Future, sync::Arc, time::Duration}; use anyhow::Result; use futures::{FutureExt, future::BoxFuture}; use parking_lot::Mutex; -use tokio::{fs, select, sync::mpsc::{self, UnboundedReceiver}, task::JoinHandle}; +use tokio::{select, sync::mpsc::{self, UnboundedReceiver}, task::JoinHandle}; use yazi_config::{YAZI, plugin::{Fetcher, Preloader}}; use yazi_dds::Pump; -use yazi_fs::{must_be_dir, remove_dir_clean, unique_name}; +use yazi_fs::{must_be_dir, remove_dir_clean, services, unique_name}; use yazi_parser::{app::PluginOpt, tasks::ProcessExecOpt}; use yazi_proxy::MgrProxy; use yazi_shared::{Id, Throttle, url::Url}; @@ -166,7 +166,7 @@ impl Scheduler { move |canceled: bool| { async move { if !canceled { - fs::remove_dir_all(&target).await.ok(); + services::remove_dir_all(&target).await.ok(); MgrProxy::update_tasks(&target); Pump::push_delete(target); } diff --git a/yazi-shared/src/url/loc.rs b/yazi-shared/src/url/loc.rs index 19894ca1..c106dab5 100644 --- a/yazi-shared/src/url/loc.rs +++ b/yazi-shared/src/url/loc.rs @@ -95,6 +95,22 @@ impl Loc { } } + pub fn set_name(&mut self, name: impl AsRef) { + let name = name.as_ref(); + if name == self.name() { + return; + } + + if self.name > name.len() { + self.urn -= self.name - name.len(); + } else { + self.urn += name.len() - self.name; + } + + self.name = name.len(); + self.path.set_file_name(name); + } + #[inline] pub fn base(&self) -> &Path { Path::new(unsafe { @@ -122,6 +138,8 @@ impl Loc { #[cfg(test)] mod tests { + use std::path::MAIN_SEPARATOR; + use super::*; #[test] @@ -159,4 +177,22 @@ mod tests { assert_eq!(loc.name(), OsStr::new("foo")); assert_eq!(loc.base().as_os_str(), OsStr::new("/root/")); } + + #[test] + fn test_set_name() { + let mut loc = Loc::from(Path::new("/root"), "/root/code/foo/".into()); + assert_eq!(loc.urn().as_os_str(), OsStr::new("code/foo")); + assert_eq!(loc.name(), OsStr::new("foo")); + assert_eq!(loc.base().as_os_str(), OsStr::new("/root/")); + + loc.set_name("bar.txt"); + assert_eq!(loc.urn().as_os_str(), OsString::from(format!("code{MAIN_SEPARATOR}bar.txt"))); + assert_eq!(loc.name(), OsStr::new("bar.txt")); + assert_eq!(loc.base().as_os_str(), OsStr::new("/root/")); + + loc.set_name("baz"); + assert_eq!(loc.urn().as_os_str(), OsString::from(format!("code{MAIN_SEPARATOR}baz"))); + assert_eq!(loc.name(), OsStr::new("baz")); + assert_eq!(loc.base().as_os_str(), OsStr::new("/root/")); + } } diff --git a/yazi-shared/src/url/url.rs b/yazi-shared/src/url/url.rs index a12cb1ff..29854c72 100644 --- a/yazi-shared/src/url/url.rs +++ b/yazi-shared/src/url/url.rs @@ -167,6 +167,9 @@ impl Url { }) } + #[inline] + pub fn set_name(&mut self, name: impl AsRef) { self.loc.set_name(name); } + #[inline] pub fn pair(&self) -> Option<(Self, UrnBuf)> { Some((self.parent_url()?, self.loc.urn_owned())) } @@ -240,9 +243,7 @@ impl Url { #[inline] pub fn set_loc(&mut self, loc: Loc) { self.loc = loc; } - #[inline] - pub fn to_path(&self) -> PathBuf { self.loc.to_path_buf() } - + // FIXME: remove #[inline] pub fn into_path(self) -> PathBuf { self.loc.into_path() }