fix: put package cache in cache dir instead of state dir (#3930)

This commit is contained in:
三咲雅 misaki masa 2026-05-02 22:15:25 +08:00 committed by GitHub
parent 740d891989
commit 62dd22b8da
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 131 additions and 77 deletions

View file

@ -5,7 +5,7 @@ use super::Actions;
impl Actions { impl Actions {
pub(super) fn clear_cache() { 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); println!("Clearing cache directory: \n{:?}", YAZI.preview.cache_dir);
std::fs::remove_dir_all(&YAZI.preview.cache_dir).unwrap(); std::fs::remove_dir_all(&YAZI.preview.cache_dir).unwrap();
} else { } else {

View file

@ -165,7 +165,7 @@ impl Actions {
fn file1_output() -> String { fn file1_output() -> String {
use std::io::Write; 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(); 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()); let program = env::var_os("YAZI_FILE_ONE").unwrap_or("file".into());

View file

@ -1,8 +1,6 @@
use std::path::PathBuf;
use futures::executor::block_on; use futures::executor::block_on;
use hashbrown::HashSet; 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_shared::{strand::StrandBuf, url::{UrlBuf, UrlLike}};
use yazi_vfs::provider; use yazi_vfs::provider;
@ -13,11 +11,6 @@ pub struct Boot {
pub local_events: HashSet<String>, pub local_events: HashSet<String>,
pub remote_events: HashSet<String>, pub remote_events: HashSet<String>,
pub config_dir: PathBuf,
pub flavor_dir: PathBuf,
pub plugin_dir: PathBuf,
pub state_dir: PathBuf,
} }
impl Boot { impl Boot {
@ -52,7 +45,6 @@ impl Boot {
impl From<&crate::Args> for Boot { impl From<&crate::Args> for Boot {
fn from(args: &crate::Args) -> Self { fn from(args: &crate::Args) -> Self {
let config_dir = Xdg::config_dir();
let (cwds, files) = block_on(Self::parse_entries(&args.entries)); let (cwds, files) = block_on(Self::parse_entries(&args.entries));
let local_events = args let local_events = args
@ -66,17 +58,6 @@ impl From<&crate::Args> for Boot {
.map(|s| s.split(',').map(|s| s.to_owned()).collect()) .map(|s| s.split(',').map(|s| s.to_owned()).collect())
.unwrap_or_default(); .unwrap_or_default();
Self { Self { cwds, files, local_events, remote_events }
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(),
}
} }
} }

View file

@ -23,7 +23,7 @@ pub(crate) struct Dependency {
impl Dependency { impl Dependency {
pub(super) fn local(&self) -> PathBuf { pub(super) fn local(&self) -> PathBuf {
Xdg::state_dir() Xdg::cache_dir()
.join("packages") .join("packages")
.join(format!("{:x}", XxHash3_128::oneshot(self.remote().as_bytes()))) .join(format!("{:x}", XxHash3_128::oneshot(self.remote().as_bytes())))
} }

View file

@ -4,7 +4,7 @@ use anyhow::Context;
use yazi_fs::Xdg; use yazi_fs::Xdg;
pub(super) fn init() -> anyhow::Result<()> { 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) std::fs::create_dir_all(&packages_dir)
.with_context(|| format!("failed to create packages directory: {packages_dir:?}"))?; .with_context(|| format!("failed to create packages directory: {packages_dir:?}"))?;

View file

@ -63,7 +63,7 @@ where
{ {
let path = PathBuf::deserialize(deserializer)?; let path = PathBuf::deserialize(deserializer)?;
if path.as_os_str().is_empty() { if path.as_os_str().is_empty() {
Ok(Xdg::cache_dir().to_owned()) Ok(Xdg::temp_dir().to_owned())
} else { } else {
normalize_path(path).ok_or_else(|| { normalize_path(path).ok_or_else(|| {
serde::de::Error::custom("cache_dir must be either empty or an absolute path.") serde::de::Error::custom("cache_dir must be either empty or an absolute path.")

View file

@ -4,8 +4,7 @@ use anyhow::Result;
use hashbrown::HashMap; use hashbrown::HashMap;
use parking_lot::RwLock; use parking_lot::RwLock;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader, BufWriter}; use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader, BufWriter};
use yazi_boot::BOOT; use yazi_fs::{Xdg, provider::{FileBuilder, Provider, local::{Gate, Local}}};
use yazi_fs::provider::{FileBuilder, Provider, local::{Gate, Local}};
use yazi_shared::timestamp_us; use yazi_shared::timestamp_us;
use yazi_shim::cell::RoCell; use yazi_shim::cell::RoCell;
@ -59,14 +58,11 @@ impl State {
return Ok(()); 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( let mut buf = BufWriter::new(
Gate::default() Gate::default().write(true).create(true).truncate(true).open(state_dir.join(".dds")).await?,
.write(true)
.create(true)
.truncate(true)
.open(BOOT.state_dir.join(".dds"))
.await?,
); );
let mut state = inner.into_iter().collect::<Vec<_>>(); let mut state = inner.into_iter().collect::<Vec<_>>();
@ -80,7 +76,7 @@ impl State {
} }
async fn load(&self) -> Result<()> { 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 buf = String::new();
let mut inner = HashMap::new(); let mut inner = HashMap::new();
@ -104,7 +100,7 @@ impl State {
} }
async fn skip(&self) -> Result<bool> { async fn skip(&self) -> Result<bool> {
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(); let modified = cha.mtime_dur()?.as_micros();
Ok(modified >= self.last.load(Ordering::Relaxed) as u128) Ok(modified >= self.last.load(Ordering::Relaxed) as u128)
} }

View file

@ -1,3 +1,5 @@
use std::io;
use tokio::io::{BufReader, Lines, ReadHalf, WriteHalf}; use tokio::io::{BufReader, Lines, ReadHalf, WriteHalf};
pub struct Stream; pub struct Stream;
@ -21,41 +23,51 @@ pub(super) type ServerListener = tokio::net::TcpListener;
impl Stream { impl Stream {
#[cfg(unix)] #[cfg(unix)]
pub async fn connect() -> std::io::Result<(ClientReader, ClientWriter)> { pub async fn connect() -> io::Result<(ClientReader, ClientWriter)> {
let stream = tokio::net::UnixStream::connect(Self::socket_file()).await?; let stream = tokio::net::UnixStream::connect(Self::socket_file().await?).await?;
let (reader, writer) = tokio::io::split(stream); let (reader, writer) = tokio::io::split(stream);
Ok((BufReader::new(reader).lines(), writer)) Ok((BufReader::new(reader).lines(), writer))
} }
#[cfg(not(unix))] #[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 stream = tokio::net::TcpStream::connect("127.0.0.1:33581").await?;
let (reader, writer) = tokio::io::split(stream); let (reader, writer) = tokio::io::split(stream);
Ok((BufReader::new(reader).lines(), writer)) Ok((BufReader::new(reader).lines(), writer))
} }
#[cfg(unix)] #[cfg(unix)]
pub(super) async fn bind() -> std::io::Result<ServerListener> { pub(super) async fn bind() -> io::Result<ServerListener> {
use yazi_fs::provider::Provider; 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(); yazi_fs::provider::local::Local::regular(&p).remove_file().await.ok();
tokio::net::UnixListener::bind(p) tokio::net::UnixListener::bind(p)
} }
#[cfg(not(unix))] #[cfg(not(unix))]
pub(super) async fn bind() -> std::io::Result<ServerListener> { pub(super) async fn bind() -> io::Result<ServerListener> {
tokio::net::TcpListener::bind("127.0.0.1:33581").await tokio::net::TcpListener::bind("127.0.0.1:33581").await
} }
#[cfg(unix)] #[cfg(unix)]
fn socket_file() -> std::path::PathBuf { async fn socket_file() -> io::Result<&'static std::path::PathBuf> {
use std::env::temp_dir; use tokio::{fs::DirBuilder, sync::OnceCell};
use yazi_fs::Xdg;
use uzers::Users; static ONCE: tokio::sync::OnceCell<std::path::PathBuf> = OnceCell::const_new();
use yazi_shared::USERS_CACHE; 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
} }
} }

View file

@ -56,7 +56,7 @@ impl Cwd {
return cache.into(); 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(); let mut it = latter.components().peekable();
while it.peek() == Some(&C::CurDir) { while it.peek() == Some(&C::CurDir) {
it.next().unwrap(); it.next().unwrap();

View file

@ -6,4 +6,6 @@ pub fn init() {
CWD.init(<_>::default()); CWD.init(<_>::default());
mounts::init(); mounts::init();
Xdg::load();
} }

View file

@ -13,10 +13,10 @@ impl FsScheme for SchemeRef<'_> {
match self { match self {
Self::Regular { .. } | Self::Search { .. } => None, Self::Regular { .. } | Self::Search { .. } => None,
Self::Archive { domain, .. } => Some( 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, .. } => { 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))))
} }
} }
} }

View file

@ -1,9 +1,24 @@
use std::{env, path::PathBuf, sync::OnceLock}; use std::{env, path::PathBuf, sync::OnceLock};
use yazi_macro::unix_either;
pub struct Xdg; pub struct Xdg;
impl 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<PathBuf> = 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) if let Some(p) = env::var_os("YAZI_CONFIG_HOME").map(PathBuf::from)
&& p.is_absolute() && p.is_absolute()
{ {
@ -12,53 +27,102 @@ impl Xdg {
#[cfg(windows)] #[cfg(windows)]
{ {
dirs::config_dir() dirs::config_dir().map(|p| p.join("yazi\\config")).expect("Failed to get config directory")
.map(|p| p.join("yazi").join("config"))
.expect("Failed to get config directory")
} }
#[cfg(unix)] #[cfg(unix)]
{ {
env::var_os("XDG_CONFIG_HOME") env::var_os("XDG_CONFIG_HOME")
.map(PathBuf::from) .map(PathBuf::from)
.filter(|p| p.is_absolute()) .filter(|p| p.is_absolute())
.or_else(|| dirs::home_dir().map(|h| h.join(".config")))
.map(|p| p.join("yazi")) .map(|p| p.join("yazi"))
.or_else(|| dirs::home_dir().map(|h| h.join(".config/yazi")))
.expect("Failed to get config directory") .expect("Failed to get config directory")
} }
} }
pub fn state_dir() -> PathBuf { pub fn cache_dir() -> &'static PathBuf {
static ONCE: OnceLock<PathBuf> = OnceLock::new();
ONCE.get_or_init(Self::load_cache_dir)
}
fn load_cache_dir() -> PathBuf {
#[cfg(windows)] #[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<PathBuf> = 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)] #[cfg(unix)]
{ {
env::var_os("XDG_STATE_HOME") env::var_os("XDG_STATE_HOME")
.map(PathBuf::from) .map(PathBuf::from)
.filter(|p| p.is_absolute()) .filter(|p| p.is_absolute())
.or_else(|| dirs::home_dir().map(|h| h.join(".local/state")))
.map(|p| p.join("yazi")) .map(|p| p.join("yazi"))
.or_else(|| dirs::home_dir().map(|h| h.join(".local/state/yazi")))
.expect("Failed to get state directory") .expect("Failed to get state directory")
} }
} }
pub fn cache_dir() -> &'static PathBuf { pub fn runtime_dir() -> &'static PathBuf {
static CACHE: OnceLock<PathBuf> = OnceLock::new(); static ONCE: OnceLock<PathBuf> = OnceLock::new();
ONCE.get_or_init(Self::load_runtime_dir)
}
CACHE.get_or_init(|| { fn load_runtime_dir() -> PathBuf {
let mut p = env::temp_dir(); let mut p = env::var_os("XDG_RUNTIME_DIR")
assert!(p.is_absolute(), "Temp dir is not absolute"); .map(PathBuf::from)
.filter(|p| p.is_absolute())
.unwrap_or_else(|| env::temp_dir());
#[cfg(unix)] let uid = unix_either!(
{ {
use uzers::Users; use uzers::Users;
p.push(format!("yazi-{}", yazi_shared::USERS_CACHE.get_current_uid())) yazi_shared::USERS_CACHE.get_current_uid()
} },
#[cfg(not(unix))] 0
p.push("yazi"); );
p p.push(format!("yazi+{uid}"));
}) p
}
pub fn temp_dir() -> &'static PathBuf {
static ONCE: OnceLock<PathBuf> = 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
} }
} }

View file

@ -2,7 +2,7 @@ use anyhow::{Context, Result};
use futures::executor::block_on; use futures::executor::block_on;
use mlua::Lua; use mlua::Lua;
use yazi_binding::{Runtime, runtime_scope}; use yazi_binding::{Runtime, runtime_scope};
use yazi_boot::BOOT; use yazi_fs::Xdg;
use yazi_macro::plugin_preset as preset; use yazi_macro::plugin_preset as preset;
use yazi_shim::cell::RoCell; 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!("setup")).set_name("setup.lua").exec()?;
lua.load(preset!("compat")).set_name("compat.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()))?; runtime_scope!(lua, "init", block_on(lua.load(b).set_name("init.lua").exec_async()))?;
} }

View file

@ -4,8 +4,7 @@ use anyhow::{Context, Result, bail, ensure};
use hashbrown::HashMap; use hashbrown::HashMap;
use mlua::{ChunkMode, ExternalError, Lua, Table}; use mlua::{ChunkMode, ExternalError, Lua, Table};
use parking_lot::RwLock; use parking_lot::RwLock;
use yazi_boot::BOOT; use yazi_fs::{Xdg, provider::local::Local};
use yazi_fs::provider::local::Local;
use yazi_macro::plugin_preset as preset; use yazi_macro::plugin_preset as preset;
use yazi_shared::{BytesExt, LOG_LEVEL}; use yazi_shared::{BytesExt, LOG_LEVEL};
use yazi_shim::cell::RoCell; use yazi_shim::cell::RoCell;
@ -89,7 +88,7 @@ impl Loader {
return Self::compatible_or_error(id, c).map(|_| f(c)); 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) let chunk = Local::regular(&p)
.read() .read()
.await .await

View file

@ -40,7 +40,7 @@ impl Watched {
} }
pub(super) fn find_by_cache(&self, cache: PathDyn) -> Option<UrlBuf> { pub(super) fn find_by_cache(&self, cache: PathDyn) -> Option<UrlBuf> {
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 // Parse domain
let domain = it.next()?.as_normal()?.to_str().ok()?; let domain = it.next()?.as_normal()?.to_str().ok()?;