From 62dd22b8daee4fa90dfcc94b1d5d39e1ead3ba0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Sat, 2 May 2026 22:15:25 +0800 Subject: [PATCH] fix: put package cache in cache dir instead of state dir (#3930) --- yazi-boot/src/actions/clear_cache.rs | 2 +- yazi-boot/src/actions/debug.rs | 2 +- yazi-boot/src/boot.rs | 23 +----- yazi-cli/src/package/dependency.rs | 2 +- yazi-cli/src/package/mod.rs | 2 +- yazi-config/src/preview/preview.rs | 2 +- yazi-dds/src/state.rs | 18 ++--- yazi-dds/src/stream.rs | 34 ++++++--- yazi-fs/src/cwd.rs | 2 +- yazi-fs/src/lib.rs | 2 + yazi-fs/src/scheme.rs | 4 +- yazi-fs/src/xdg.rs | 104 +++++++++++++++++++++------ yazi-plugin/src/standard.rs | 4 +- yazi-runner/src/loader/loader.rs | 5 +- yazi-watcher/src/watched.rs | 2 +- 15 files changed, 131 insertions(+), 77 deletions(-) diff --git a/yazi-boot/src/actions/clear_cache.rs b/yazi-boot/src/actions/clear_cache.rs index 2902fbf3..b2c2d488 100644 --- a/yazi-boot/src/actions/clear_cache.rs +++ b/yazi-boot/src/actions/clear_cache.rs @@ -5,7 +5,7 @@ use super::Actions; impl Actions { pub(super) fn clear_cache() { - if YAZI.preview.cache_dir == *Xdg::cache_dir() { + if YAZI.preview.cache_dir == *Xdg::temp_dir() { println!("Clearing cache directory: \n{:?}", YAZI.preview.cache_dir); std::fs::remove_dir_all(&YAZI.preview.cache_dir).unwrap(); } else { diff --git a/yazi-boot/src/actions/debug.rs b/yazi-boot/src/actions/debug.rs index 66080b89..cc6c058f 100644 --- a/yazi-boot/src/actions/debug.rs +++ b/yazi-boot/src/actions/debug.rs @@ -165,7 +165,7 @@ impl Actions { fn file1_output() -> String { use std::io::Write; - let p = env::temp_dir().join(format!(".yazi-debug-{}.tmp", timestamp_us())); + let p = Xdg::temp_dir().join(format!(".debug-{}.tmp", timestamp_us())); std::fs::File::create_new(&p).map(|mut f| f.write_all(b"Hello, World!")).ok(); let program = env::var_os("YAZI_FILE_ONE").unwrap_or("file".into()); diff --git a/yazi-boot/src/boot.rs b/yazi-boot/src/boot.rs index a9b48bb7..867644e6 100644 --- a/yazi-boot/src/boot.rs +++ b/yazi-boot/src/boot.rs @@ -1,8 +1,6 @@ -use std::path::PathBuf; - use futures::executor::block_on; use hashbrown::HashSet; -use yazi_fs::{CWD, Xdg, path::clean_url}; +use yazi_fs::{CWD, path::clean_url}; use yazi_shared::{strand::StrandBuf, url::{UrlBuf, UrlLike}}; use yazi_vfs::provider; @@ -13,11 +11,6 @@ pub struct Boot { pub local_events: HashSet, pub remote_events: HashSet, - - pub config_dir: PathBuf, - pub flavor_dir: PathBuf, - pub plugin_dir: PathBuf, - pub state_dir: PathBuf, } impl Boot { @@ -52,7 +45,6 @@ impl Boot { impl From<&crate::Args> for Boot { fn from(args: &crate::Args) -> Self { - let config_dir = Xdg::config_dir(); let (cwds, files) = block_on(Self::parse_entries(&args.entries)); let local_events = args @@ -66,17 +58,6 @@ impl From<&crate::Args> for Boot { .map(|s| s.split(',').map(|s| s.to_owned()).collect()) .unwrap_or_default(); - Self { - cwds, - files, - - local_events, - remote_events, - - flavor_dir: config_dir.join("flavors"), - plugin_dir: config_dir.join("plugins"), - config_dir, - state_dir: Xdg::state_dir(), - } + Self { cwds, files, local_events, remote_events } } } diff --git a/yazi-cli/src/package/dependency.rs b/yazi-cli/src/package/dependency.rs index 1529b3e1..08e78177 100644 --- a/yazi-cli/src/package/dependency.rs +++ b/yazi-cli/src/package/dependency.rs @@ -23,7 +23,7 @@ pub(crate) struct Dependency { impl Dependency { pub(super) fn local(&self) -> PathBuf { - Xdg::state_dir() + Xdg::cache_dir() .join("packages") .join(format!("{:x}", XxHash3_128::oneshot(self.remote().as_bytes()))) } diff --git a/yazi-cli/src/package/mod.rs b/yazi-cli/src/package/mod.rs index 44c4f203..4eec2b3f 100644 --- a/yazi-cli/src/package/mod.rs +++ b/yazi-cli/src/package/mod.rs @@ -4,7 +4,7 @@ use anyhow::Context; use yazi_fs::Xdg; pub(super) fn init() -> anyhow::Result<()> { - let packages_dir = Xdg::state_dir().join("packages"); + let packages_dir = Xdg::cache_dir().join("packages"); std::fs::create_dir_all(&packages_dir) .with_context(|| format!("failed to create packages directory: {packages_dir:?}"))?; diff --git a/yazi-config/src/preview/preview.rs b/yazi-config/src/preview/preview.rs index ae2fbe3a..87bd5645 100644 --- a/yazi-config/src/preview/preview.rs +++ b/yazi-config/src/preview/preview.rs @@ -63,7 +63,7 @@ where { let path = PathBuf::deserialize(deserializer)?; if path.as_os_str().is_empty() { - Ok(Xdg::cache_dir().to_owned()) + Ok(Xdg::temp_dir().to_owned()) } else { normalize_path(path).ok_or_else(|| { serde::de::Error::custom("cache_dir must be either empty or an absolute path.") diff --git a/yazi-dds/src/state.rs b/yazi-dds/src/state.rs index 25298b27..830f9901 100644 --- a/yazi-dds/src/state.rs +++ b/yazi-dds/src/state.rs @@ -4,8 +4,7 @@ use anyhow::Result; use hashbrown::HashMap; use parking_lot::RwLock; use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader, BufWriter}; -use yazi_boot::BOOT; -use yazi_fs::provider::{FileBuilder, Provider, local::{Gate, Local}}; +use yazi_fs::{Xdg, provider::{FileBuilder, Provider, local::{Gate, Local}}}; use yazi_shared::timestamp_us; use yazi_shim::cell::RoCell; @@ -59,14 +58,11 @@ impl State { return Ok(()); } - Local::regular(&BOOT.state_dir).create_dir_all().await?; + let state_dir = Xdg::state_dir(); + Local::regular(&state_dir).create_dir_all().await?; + let mut buf = BufWriter::new( - Gate::default() - .write(true) - .create(true) - .truncate(true) - .open(BOOT.state_dir.join(".dds")) - .await?, + Gate::default().write(true).create(true).truncate(true).open(state_dir.join(".dds")).await?, ); let mut state = inner.into_iter().collect::>(); @@ -80,7 +76,7 @@ impl State { } async fn load(&self) -> Result<()> { - let mut file = BufReader::new(Local::regular(&BOOT.state_dir.join(".dds")).open().await?); + let mut file = BufReader::new(Local::regular(&Xdg::state_dir().join(".dds")).open().await?); let mut buf = String::new(); let mut inner = HashMap::new(); @@ -104,7 +100,7 @@ impl State { } async fn skip(&self) -> Result { - let cha = Local::regular(&BOOT.state_dir.join(".dds")).symlink_metadata().await?; + let cha = Local::regular(&Xdg::state_dir().join(".dds")).symlink_metadata().await?; let modified = cha.mtime_dur()?.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 dcd4a650..4b2915da 100644 --- a/yazi-dds/src/stream.rs +++ b/yazi-dds/src/stream.rs @@ -1,3 +1,5 @@ +use std::io; + use tokio::io::{BufReader, Lines, ReadHalf, WriteHalf}; pub struct Stream; @@ -21,41 +23,51 @@ pub(super) type ServerListener = tokio::net::TcpListener; impl Stream { #[cfg(unix)] - pub async fn connect() -> std::io::Result<(ClientReader, ClientWriter)> { - let stream = tokio::net::UnixStream::connect(Self::socket_file()).await?; + pub async fn connect() -> io::Result<(ClientReader, ClientWriter)> { + let stream = tokio::net::UnixStream::connect(Self::socket_file().await?).await?; let (reader, writer) = tokio::io::split(stream); Ok((BufReader::new(reader).lines(), writer)) } #[cfg(not(unix))] - pub async fn connect() -> std::io::Result<(ClientReader, ClientWriter)> { + pub async fn connect() -> io::Result<(ClientReader, ClientWriter)> { let stream = tokio::net::TcpStream::connect("127.0.0.1:33581").await?; let (reader, writer) = tokio::io::split(stream); Ok((BufReader::new(reader).lines(), writer)) } #[cfg(unix)] - pub(super) async fn bind() -> std::io::Result { + pub(super) async fn bind() -> io::Result { use yazi_fs::provider::Provider; - let p = Self::socket_file(); + let p = Self::socket_file().await?; yazi_fs::provider::local::Local::regular(&p).remove_file().await.ok(); tokio::net::UnixListener::bind(p) } #[cfg(not(unix))] - pub(super) async fn bind() -> std::io::Result { + pub(super) async fn bind() -> io::Result { tokio::net::TcpListener::bind("127.0.0.1:33581").await } #[cfg(unix)] - fn socket_file() -> std::path::PathBuf { - use std::env::temp_dir; + async fn socket_file() -> io::Result<&'static std::path::PathBuf> { + use tokio::{fs::DirBuilder, sync::OnceCell}; + use yazi_fs::Xdg; - use uzers::Users; - use yazi_shared::USERS_CACHE; + static ONCE: tokio::sync::OnceCell = OnceCell::const_new(); + ONCE + .get_or_try_init(|| async move { + let p = Xdg::runtime_dir(); - temp_dir().join(format!(".yazi_dds-{}.sock", USERS_CACHE.get_current_uid())) + #[cfg(unix)] + DirBuilder::new().mode(0o700).recursive(true).create(p).await?; + #[cfg(not(unix))] + DirBuilder::new().recursive(true).create(p).await?; + + Ok(p.join(".dds.sock")) + }) + .await } } diff --git a/yazi-fs/src/cwd.rs b/yazi-fs/src/cwd.rs index e7725e91..d5f727c3 100644 --- a/yazi-fs/src/cwd.rs +++ b/yazi-fs/src/cwd.rs @@ -56,7 +56,7 @@ impl Cwd { return cache.into(); } - let latter = cache.strip_prefix(Xdg::cache_dir()).expect("under cache dir"); + let latter = cache.strip_prefix(Xdg::temp_dir()).expect("under temp dir"); let mut it = latter.components().peekable(); while it.peek() == Some(&C::CurDir) { it.next().unwrap(); diff --git a/yazi-fs/src/lib.rs b/yazi-fs/src/lib.rs index 36d5962a..1ef59fc3 100644 --- a/yazi-fs/src/lib.rs +++ b/yazi-fs/src/lib.rs @@ -6,4 +6,6 @@ pub fn init() { CWD.init(<_>::default()); mounts::init(); + + Xdg::load(); } diff --git a/yazi-fs/src/scheme.rs b/yazi-fs/src/scheme.rs index 4eb65431..a957ad4a 100644 --- a/yazi-fs/src/scheme.rs +++ b/yazi-fs/src/scheme.rs @@ -13,10 +13,10 @@ impl FsScheme for SchemeRef<'_> { match self { Self::Regular { .. } | Self::Search { .. } => None, Self::Archive { domain, .. } => Some( - Xdg::cache_dir().join(format!("archive-{}", yazi_shared::scheme::Encode::domain(domain))), + Xdg::temp_dir().join(format!("archive-{}", yazi_shared::scheme::Encode::domain(domain))), ), Self::Sftp { domain, .. } => { - Some(Xdg::cache_dir().join(format!("sftp-{}", yazi_shared::scheme::Encode::domain(domain)))) + Some(Xdg::temp_dir().join(format!("sftp-{}", yazi_shared::scheme::Encode::domain(domain)))) } } } diff --git a/yazi-fs/src/xdg.rs b/yazi-fs/src/xdg.rs index 9e977aaa..c596bdf5 100644 --- a/yazi-fs/src/xdg.rs +++ b/yazi-fs/src/xdg.rs @@ -1,9 +1,24 @@ use std::{env, path::PathBuf, sync::OnceLock}; +use yazi_macro::unix_either; + pub struct Xdg; impl Xdg { - pub fn config_dir() -> PathBuf { + pub(super) fn load() { + Self::config_dir(); + Self::cache_dir(); + Self::state_dir(); + Self::runtime_dir(); + Self::temp_dir(); + } + + pub fn config_dir() -> &'static PathBuf { + static ONCE: OnceLock = OnceLock::new(); + ONCE.get_or_init(Self::load_config_dir) + } + + fn load_config_dir() -> PathBuf { if let Some(p) = env::var_os("YAZI_CONFIG_HOME").map(PathBuf::from) && p.is_absolute() { @@ -12,53 +27,102 @@ impl Xdg { #[cfg(windows)] { - dirs::config_dir() - .map(|p| p.join("yazi").join("config")) - .expect("Failed to get config directory") + dirs::config_dir().map(|p| p.join("yazi\\config")).expect("Failed to get config directory") } #[cfg(unix)] { env::var_os("XDG_CONFIG_HOME") .map(PathBuf::from) .filter(|p| p.is_absolute()) - .or_else(|| dirs::home_dir().map(|h| h.join(".config"))) .map(|p| p.join("yazi")) + .or_else(|| dirs::home_dir().map(|h| h.join(".config/yazi"))) .expect("Failed to get config directory") } } - pub fn state_dir() -> PathBuf { + pub fn cache_dir() -> &'static PathBuf { + static ONCE: OnceLock = OnceLock::new(); + ONCE.get_or_init(Self::load_cache_dir) + } + + fn load_cache_dir() -> PathBuf { #[cfg(windows)] { - dirs::data_dir().map(|p| p.join("yazi").join("state")).expect("Failed to get state directory") + dirs::cache_dir().map(|p| p.join("yazi")).expect("Failed to get cache directory") + } + #[cfg(unix)] + { + env::var_os("XDG_CACHE_HOME") + .map(PathBuf::from) + .filter(|p| p.is_absolute()) + .map(|p| p.join("yazi")) + .or_else(|| dirs::home_dir().map(|h| h.join(".cache/yazi"))) + .expect("Failed to get cache directory") + } + } + + pub fn state_dir() -> &'static PathBuf { + static ONCE: OnceLock = OnceLock::new(); + ONCE.get_or_init(Self::load_state_dir) + } + + fn load_state_dir() -> PathBuf { + #[cfg(windows)] + { + dirs::data_dir().map(|p| p.join("yazi\\state")).expect("Failed to get state directory") } #[cfg(unix)] { env::var_os("XDG_STATE_HOME") .map(PathBuf::from) .filter(|p| p.is_absolute()) - .or_else(|| dirs::home_dir().map(|h| h.join(".local/state"))) .map(|p| p.join("yazi")) + .or_else(|| dirs::home_dir().map(|h| h.join(".local/state/yazi"))) .expect("Failed to get state directory") } } - pub fn cache_dir() -> &'static PathBuf { - static CACHE: OnceLock = OnceLock::new(); + pub fn runtime_dir() -> &'static PathBuf { + static ONCE: OnceLock = OnceLock::new(); + ONCE.get_or_init(Self::load_runtime_dir) + } - CACHE.get_or_init(|| { - let mut p = env::temp_dir(); - assert!(p.is_absolute(), "Temp dir is not absolute"); + fn load_runtime_dir() -> PathBuf { + let mut p = env::var_os("XDG_RUNTIME_DIR") + .map(PathBuf::from) + .filter(|p| p.is_absolute()) + .unwrap_or_else(|| env::temp_dir()); - #[cfg(unix)] + let uid = unix_either!( { use uzers::Users; - p.push(format!("yazi-{}", yazi_shared::USERS_CACHE.get_current_uid())) - } - #[cfg(not(unix))] - p.push("yazi"); + yazi_shared::USERS_CACHE.get_current_uid() + }, + 0 + ); - p - }) + p.push(format!("yazi+{uid}")); + p + } + + pub fn temp_dir() -> &'static PathBuf { + static ONCE: OnceLock = OnceLock::new(); + ONCE.get_or_init(Self::load_temp_dir) + } + + fn load_temp_dir() -> PathBuf { + let mut p = env::temp_dir(); + assert!(p.is_absolute(), "Temporary directory path is not absolute"); + + let uid = unix_either!( + { + use uzers::Users; + yazi_shared::USERS_CACHE.get_current_uid() + }, + 0 + ); + + p.push(format!("yazi-{uid}")); + p } } diff --git a/yazi-plugin/src/standard.rs b/yazi-plugin/src/standard.rs index d2a9a0c4..811b1673 100644 --- a/yazi-plugin/src/standard.rs +++ b/yazi-plugin/src/standard.rs @@ -2,7 +2,7 @@ use anyhow::{Context, Result}; use futures::executor::block_on; use mlua::Lua; use yazi_binding::{Runtime, runtime_scope}; -use yazi_boot::BOOT; +use yazi_fs::Xdg; use yazi_macro::plugin_preset as preset; use yazi_shim::cell::RoCell; @@ -67,7 +67,7 @@ fn stage_2(lua: &Lua) -> mlua::Result<()> { lua.load(preset!("setup")).set_name("setup.lua").exec()?; lua.load(preset!("compat")).set_name("compat.lua").exec()?; - if let Ok(b) = std::fs::read(BOOT.config_dir.join("init.lua")) { + if let Ok(b) = std::fs::read(Xdg::config_dir().join("init.lua")) { runtime_scope!(lua, "init", block_on(lua.load(b).set_name("init.lua").exec_async()))?; } diff --git a/yazi-runner/src/loader/loader.rs b/yazi-runner/src/loader/loader.rs index c8c38d6c..9a07bbf1 100644 --- a/yazi-runner/src/loader/loader.rs +++ b/yazi-runner/src/loader/loader.rs @@ -4,8 +4,7 @@ use anyhow::{Context, Result, bail, ensure}; use hashbrown::HashMap; use mlua::{ChunkMode, ExternalError, Lua, Table}; use parking_lot::RwLock; -use yazi_boot::BOOT; -use yazi_fs::provider::local::Local; +use yazi_fs::{Xdg, provider::local::Local}; use yazi_macro::plugin_preset as preset; use yazi_shared::{BytesExt, LOG_LEVEL}; use yazi_shim::cell::RoCell; @@ -89,7 +88,7 @@ impl Loader { return Self::compatible_or_error(id, c).map(|_| f(c)); } - let p = BOOT.plugin_dir.join(format!("{plugin}.yazi/{entry}.lua")); + let p = Xdg::config_dir().join(format!("plugins/{plugin}.yazi/{entry}.lua")); let chunk = Local::regular(&p) .read() .await diff --git a/yazi-watcher/src/watched.rs b/yazi-watcher/src/watched.rs index 1334a4a3..d2d58ead 100644 --- a/yazi-watcher/src/watched.rs +++ b/yazi-watcher/src/watched.rs @@ -40,7 +40,7 @@ impl Watched { } pub(super) fn find_by_cache(&self, cache: PathDyn) -> Option { - let mut it = cache.try_strip_prefix(Xdg::cache_dir()).ok()?.components(); + let mut it = cache.try_strip_prefix(Xdg::temp_dir()).ok()?.components(); // Parse domain let domain = it.next()?.as_normal()?.to_str().ok()?;