refactor: use Cha for better cross-platform consistency (#3174)

This commit is contained in:
三咲雅 misaki masa 2025-09-17 23:18:54 +08:00 committed by GitHub
parent 6cfa92f112
commit ece59974fe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 175 additions and 155 deletions

97
Cargo.lock generated
View file

@ -549,9 +549,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_complete" name = "clap_complete"
version = "4.5.57" version = "4.5.58"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d9501bd3f5f09f7bbee01da9a511073ed30a80cd7a509f1214bb74eadea71ad" checksum = "75bf0b32ad2e152de789bb635ea4d3078f6b838ad7974143e99b99f45a04af4a"
dependencies = [ dependencies = [
"clap", "clap",
] ]
@ -1412,7 +1412,7 @@ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"r-efi", "r-efi",
"wasi 0.14.5+wasi-0.2.4", "wasi 0.14.7+wasi-0.2.4",
] ]
[[package]] [[package]]
@ -1619,13 +1619,14 @@ checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.11.1" version = "2.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "206a8042aec68fa4a62e8d3f7aa4ceb508177d9324faf261e1959e495b7a1921" checksum = "92119844f513ffa41556430369ab02c295a3578af21cf945caa3e9e0c2481ac3"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown 0.15.5", "hashbrown 0.15.5",
"serde", "serde",
"serde_core",
] ]
[[package]] [[package]]
@ -1778,9 +1779,9 @@ dependencies = [
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.78" version = "0.3.80"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c0b063578492ceec17683ef2f8c5e89121fbd0b172cbc280635ab7567db2738" checksum = "852f13bec5eba4ba9afbeb93fd7c13fe56147f055939ae21c43a29a0ecb2702e"
dependencies = [ dependencies = [
"once_cell", "once_cell",
"wasm-bindgen", "wasm-bindgen",
@ -2561,9 +2562,9 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]] [[package]]
name = "plist" name = "plist"
version = "1.7.4" version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3af6b589e163c5a788fab00ce0c0366f6efbb9959c2f9874b224936af7fce7e1" checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07"
dependencies = [ dependencies = [
"base64", "base64",
"indexmap", "indexmap",
@ -3202,9 +3203,9 @@ checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.223" version = "1.0.225"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a505d71960adde88e293da5cb5eda57093379f64e61cf77bf0e6a63af07a7bac" checksum = "fd6c24dee235d0da097043389623fb913daddf92c76e9f5a1db88607a0bcbd1d"
dependencies = [ dependencies = [
"serde_core", "serde_core",
"serde_derive", "serde_derive",
@ -3222,18 +3223,18 @@ dependencies = [
[[package]] [[package]]
name = "serde_core" name = "serde_core"
version = "1.0.223" version = "1.0.225"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20f57cbd357666aa7b3ac84a90b4ea328f1d4ddb6772b430caa5d9e1309bb9e9" checksum = "659356f9a0cb1e529b24c01e43ad2bdf520ec4ceaf83047b83ddcc2251f96383"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.223" version = "1.0.225"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d428d07faf17e306e699ec1e91996e5a165ba5d6bce5b5155173e91a8a01a56" checksum = "0ea936adf78b1f766949a4977b91d2f5595825bd6ec079aa9543ad2685fc4516"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -3264,11 +3265,11 @@ dependencies = [
[[package]] [[package]]
name = "serde_spanned" name = "serde_spanned"
version = "1.0.0" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" checksum = "2789234a13a53fc4be1b51ea1bab45a3c338bdb884862a257d10e5a74ae009e6"
dependencies = [ dependencies = [
"serde", "serde_core",
] ]
[[package]] [[package]]
@ -3715,14 +3716,14 @@ dependencies = [
[[package]] [[package]]
name = "toml" name = "toml"
version = "0.9.5" version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8" checksum = "ae2a4cf385da23d1d53bc15cdfa5c2109e93d8d362393c801e87da2f72f0e201"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"serde", "serde_core",
"serde_spanned 1.0.0", "serde_spanned 1.0.1",
"toml_datetime 0.7.0", "toml_datetime 0.7.1",
"toml_parser", "toml_parser",
"toml_writer", "toml_writer",
"winnow", "winnow",
@ -3739,11 +3740,11 @@ dependencies = [
[[package]] [[package]]
name = "toml_datetime" name = "toml_datetime"
version = "0.7.0" version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" checksum = "a197c0ec7d131bfc6f7e82c8442ba1595aeab35da7adbf05b6b73cd06a16b6be"
dependencies = [ dependencies = [
"serde", "serde_core",
] ]
[[package]] [[package]]
@ -4045,27 +4046,27 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.14.5+wasi-0.2.4" version = "0.14.7+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4494f6290a82f5fe584817a676a34b9d6763e8d9d18204009fb31dceca98fd4" checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c"
dependencies = [ dependencies = [
"wasip2", "wasip2",
] ]
[[package]] [[package]]
name = "wasip2" name = "wasip2"
version = "1.0.0+wasi-0.2.4" version = "1.0.1+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03fa2761397e5bd52002cd7e73110c71af2109aca4e521a9f40473fe685b0a24" checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
dependencies = [ dependencies = [
"wit-bindgen", "wit-bindgen",
] ]
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.101" version = "0.2.103"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e14915cadd45b529bb8d1f343c4ed0ac1de926144b746e2710f9cd05df6603b" checksum = "ab10a69fbd0a177f5f649ad4d8d3305499c42bab9aef2f7ff592d0ec8f833819"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"once_cell", "once_cell",
@ -4076,9 +4077,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-backend" name = "wasm-bindgen-backend"
version = "0.2.101" version = "0.2.103"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e28d1ba982ca7923fd01448d5c30c6864d0a14109560296a162f80f305fb93bb" checksum = "0bb702423545a6007bbc368fde243ba47ca275e549c8a28617f56f6ba53b1d1c"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"log", "log",
@ -4090,9 +4091,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-futures" name = "wasm-bindgen-futures"
version = "0.4.51" version = "0.4.53"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ca85039a9b469b38336411d6d6ced91f3fc87109a2a27b0c197663f5144dffe" checksum = "a0b221ff421256839509adbb55998214a70d829d3a28c69b4a6672e9d2a42f67"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"js-sys", "js-sys",
@ -4103,9 +4104,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro" name = "wasm-bindgen-macro"
version = "0.2.101" version = "0.2.103"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c3d463ae3eff775b0c45df9da45d68837702ac35af998361e2c84e7c5ec1b0d" checksum = "fc65f4f411d91494355917b605e1480033152658d71f722a90647f56a70c88a0"
dependencies = [ dependencies = [
"quote", "quote",
"wasm-bindgen-macro-support", "wasm-bindgen-macro-support",
@ -4113,9 +4114,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro-support" name = "wasm-bindgen-macro-support"
version = "0.2.101" version = "0.2.103"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bb4ce89b08211f923caf51d527662b75bdc9c9c7aab40f86dcb9fb85ac552aa" checksum = "ffc003a991398a8ee604a401e194b6b3a39677b3173d6e74495eb51b82e99a32"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -4126,18 +4127,18 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-shared" name = "wasm-bindgen-shared"
version = "0.2.101" version = "0.2.103"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f143854a3b13752c6950862c906306adb27c7e839f7414cec8fea35beab624c1" checksum = "293c37f4efa430ca14db3721dfbe48d8c33308096bd44d80ebaa775ab71ba1cf"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]] [[package]]
name = "web-sys" name = "web-sys"
version = "0.3.78" version = "0.3.80"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77e4b637749ff0d92b8fad63aa1f7cff3cbe125fd49c175cd6345e7272638b12" checksum = "fbe734895e869dc429d78c4b433f8d17d95f8d05317440b4fad5ab2d33e596dc"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",
@ -4566,9 +4567,9 @@ checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
[[package]] [[package]]
name = "wit-bindgen" name = "wit-bindgen"
version = "0.45.1" version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c573471f125075647d03df72e026074b7203790d41351cd6edc96f46bcccd36" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
[[package]] [[package]]
name = "wyz" name = "wyz"
@ -4693,7 +4694,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"tokio", "tokio",
"toml 0.9.5", "toml 0.9.6",
"twox-hash", "twox-hash",
"vergen-gitcl", "vergen-gitcl",
"yazi-boot", "yazi-boot",
@ -4725,7 +4726,7 @@ dependencies = [
"ratatui", "ratatui",
"regex", "regex",
"serde", "serde",
"toml 0.9.5", "toml 0.9.6",
"tracing", "tracing",
"yazi-codegen", "yazi-codegen",
"yazi-fs", "yazi-fs",

View file

@ -32,7 +32,7 @@ foldhash = "0.2.0"
futures = "0.3.31" futures = "0.3.31"
globset = "0.4.16" globset = "0.4.16"
hashbrown = { version = "0.16.0", features = [ "serde" ] } hashbrown = { version = "0.16.0", features = [ "serde" ] }
indexmap = { version = "2.11.1", features = [ "serde" ] } indexmap = { version = "2.11.3", features = [ "serde" ] }
libc = "0.2.175" libc = "0.2.175"
lru = "0.16.1" lru = "0.16.1"
mlua = { version = "0.11.3", features = [ "anyhow", "async", "error-send", "lua54", "macros", "serde" ] } mlua = { version = "0.11.3", features = [ "anyhow", "async", "error-send", "lua54", "macros", "serde" ] }
@ -44,13 +44,13 @@ ratatui = { version = "0.29.0", features = [ "unstable-rendered-line
regex = "1.11.2" regex = "1.11.2"
russh = { version = "0.54.3", default-features = false, features = [ "ring", "rsa" ] } russh = { version = "0.54.3", default-features = false, features = [ "ring", "rsa" ] }
scopeguard = "1.2.0" scopeguard = "1.2.0"
serde = { version = "1.0.223", features = [ "derive" ] } serde = { version = "1.0.225", features = [ "derive" ] }
serde_json = "1.0.145" serde_json = "1.0.145"
syntect = { version = "5.2.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] } syntect = { version = "5.2.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] }
tokio = { version = "1.47.1", features = [ "full" ] } tokio = { version = "1.47.1", features = [ "full" ] }
tokio-stream = "0.1.17" tokio-stream = "0.1.17"
tokio-util = "0.7.16" tokio-util = "0.7.16"
toml = { version = "0.9.5" } toml = { version = "0.9.6" }
tracing = { version = "0.1.41", features = [ "max_level_debug", "release_max_level_debug" ] } tracing = { version = "0.1.41", features = [ "max_level_debug", "release_max_level_debug" ] }
unicode-width = { version = "0.2.0", default-features = false } unicode-width = { version = "0.2.0", default-features = false }
twox-hash = { version = "2.1.2", default-features = false, features = [ "std", "random", "xxhash3_128" ] } twox-hash = { version = "2.1.2", default-features = false, features = [ "std", "random", "xxhash3_128" ] }

View file

@ -1,4 +1,4 @@
use std::{ops::Deref, time::{Duration, SystemTime, UNIX_EPOCH}}; use std::{ops::Deref, time::{Duration, SystemTime}};
use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, UserData, UserDataFields, UserDataMethods}; use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, UserData, UserDataFields, UserDataMethods};
use yazi_fs::cha::{ChaKind, ChaMode}; use yazi_fs::cha::{ChaKind, ChaMode};
@ -66,18 +66,10 @@ impl UserData for Cha {
fields.add_field_method_get("is_sticky", |_, me| Ok(me.is_sticky())); fields.add_field_method_get("is_sticky", |_, me| Ok(me.is_sticky()));
fields.add_field_method_get("len", |_, me| Ok(me.len)); fields.add_field_method_get("len", |_, me| Ok(me.len));
fields.add_field_method_get("atime", |_, me| { fields.add_field_method_get("atime", |_, me| Ok(me.atime_dur().ok().map(|d| d.as_secs_f64())));
Ok(me.atime.and_then(|t| t.duration_since(UNIX_EPOCH).map(|d| d.as_secs_f64()).ok())) fields.add_field_method_get("btime", |_, me| Ok(me.btime_dur().ok().map(|d| d.as_secs_f64())));
}); fields.add_field_method_get("ctime", |_, me| Ok(me.ctime_dur().ok().map(|d| d.as_secs_f64())));
fields.add_field_method_get("btime", |_, me| { fields.add_field_method_get("mtime", |_, me| Ok(me.mtime_dur().ok().map(|d| d.as_secs_f64())));
Ok(me.btime.and_then(|t| t.duration_since(UNIX_EPOCH).map(|d| d.as_secs_f64()).ok()))
});
fields.add_field_method_get("ctime", |_, me| {
Ok(me.ctime.and_then(|t| t.duration_since(UNIX_EPOCH).map(|d| d.as_secs_f64()).ok()))
});
fields.add_field_method_get("mtime", |_, me| {
Ok(me.mtime.and_then(|t| t.duration_since(UNIX_EPOCH).map(|d| d.as_secs_f64()).ok()))
});
fields.add_field_method_get("dev", |_, me| Ok(me.dev)); fields.add_field_method_get("dev", |_, me| Ok(me.dev));
fields.add_field_method_get("uid", |_, me| Ok(me.uid)); fields.add_field_method_get("uid", |_, me| Ok(me.uid));
fields.add_field_method_get("gid", |_, me| Ok(me.gid)); fields.add_field_method_get("gid", |_, me| Ok(me.gid));

View file

@ -27,7 +27,7 @@ yazi-shared = { path = "../yazi-shared", version = "25.9.15" }
# External dependencies # External dependencies
clap = { workspace = true } clap = { workspace = true }
clap_complete = "4.5.57" clap_complete = "4.5.58"
clap_complete_fig = "4.5.2" clap_complete_fig = "4.5.2"
clap_complete_nushell = "4.5.8" clap_complete_nushell = "4.5.8"
vergen-gitcl = { version = "1.0.8", features = [ "build", "rustc" ] } vergen-gitcl = { version = "1.0.8", features = [ "build", "rustc" ] }

View file

@ -41,7 +41,7 @@ yazi-shared = { path = "../yazi-shared", version = "25.9.15" }
# External build dependencies # External build dependencies
anyhow = { workspace = true } anyhow = { workspace = true }
clap = { workspace = true } clap = { workspace = true }
clap_complete = "4.5.57" clap_complete = "4.5.58"
clap_complete_fig = "4.5.2" clap_complete_fig = "4.5.2"
clap_complete_nushell = "4.5.8" clap_complete_nushell = "4.5.8"
serde_json = { workspace = true } serde_json = { workspace = true }

View file

@ -34,7 +34,7 @@ pub async fn copy_and_seal(from: &Path, to: &Path) -> io::Result<()> {
pub async fn remove_sealed(p: &Path) -> io::Result<()> { pub async fn remove_sealed(p: &Path) -> io::Result<()> {
#[cfg(windows)] #[cfg(windows)]
{ {
let mut perm = Local::metadata(p).await?.permissions(); let mut perm = tokio::fs::metadata(p).await?.permissions();
perm.set_readonly(false); perm.set_readonly(false);
tokio::fs::set_permissions(p, perm).await?; tokio::fs::set_permissions(p, perm).await?;
} }

View file

@ -1,4 +1,4 @@
use std::{mem, ops::Deref, sync::atomic::{AtomicU64, Ordering}, time::UNIX_EPOCH}; use std::{mem, ops::Deref, sync::atomic::{AtomicU64, Ordering}};
use anyhow::Result; use anyhow::Result;
use hashbrown::HashMap; use hashbrown::HashMap;
@ -103,8 +103,8 @@ impl State {
} }
async fn skip(&self) -> Result<bool> { async fn skip(&self) -> Result<bool> {
let meta = Local::symlink_metadata(BOOT.state_dir.join(".dds")).await?; let cha = Local::symlink_metadata(BOOT.state_dir.join(".dds")).await?;
let modified = meta.modified()?.duration_since(UNIX_EPOCH)?.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,5 +1,6 @@
use std::{fs::{FileType, Metadata}, ops::Deref, time::SystemTime}; use std::{ffi::OsStr, fs::{FileType, Metadata}, ops::Deref, time::{Duration, SystemTime, UNIX_EPOCH}};
use anyhow::bail;
use yazi_macro::{unix_either, win_either}; use yazi_macro::{unix_either, win_either};
use yazi_shared::url::Url; use yazi_shared::url::Url;
@ -47,11 +48,8 @@ impl Default for Cha {
impl Cha { impl Cha {
#[inline] #[inline]
pub fn new<'a, U>(url: U, meta: Metadata) -> Self pub fn new(name: &OsStr, meta: Metadata) -> Self {
where Self::from_bare(&meta).attach(ChaKind::hidden(name, &meta))
U: Into<Url<'a>>,
{
Self::from_bare(&meta).attach(ChaKind::hidden(url, &meta))
} }
#[inline] #[inline]
@ -60,22 +58,21 @@ impl Cha {
Ok(Self::from_follow(url, provider::symlink_metadata(url).await?).await) Ok(Self::from_follow(url, provider::symlink_metadata(url).await?).await)
} }
pub async fn from_follow<'a, U>(url: U, mut meta: Metadata) -> Self pub async fn from_follow<'a, U>(url: U, mut cha: Self) -> Self
where where
U: Into<Url<'a>>, U: Into<Url<'a>>,
{ {
let url = url.into(); let url: Url = url.into();
let mut attached = ChaKind::hidden(url, &meta); let mut retain = cha.kind & (ChaKind::HIDDEN | ChaKind::SYSTEM | ChaKind::LINK);
if meta.is_symlink() { if cha.is_link() {
attached |= ChaKind::LINK; cha = provider::metadata(url).await.unwrap_or(cha);
meta = provider::metadata(url).await.unwrap_or(meta);
} }
if meta.is_symlink() { if cha.is_link() {
attached |= ChaKind::ORPHAN; retain |= ChaKind::ORPHAN;
} }
Self::from_bare(&meta).attach(attached) cha.attach(retain)
} }
pub fn from_dummy<'a, U>(_url: U, ft: Option<FileType>) -> Self pub fn from_dummy<'a, U>(_url: U, ft: Option<FileType>) -> Self
@ -99,7 +96,7 @@ impl Cha {
fn from_bare(m: &Metadata) -> Self { fn from_bare(m: &Metadata) -> Self {
#[cfg(unix)] #[cfg(unix)]
use std::{os::unix::fs::MetadataExt, time::{Duration, UNIX_EPOCH}}; use std::os::unix::fs::MetadataExt;
#[cfg(unix)] #[cfg(unix)]
let mode = { let mode = {
@ -169,4 +166,36 @@ impl Cha {
#[inline] #[inline]
pub const fn is_dummy(&self) -> bool { self.kind.contains(ChaKind::DUMMY) } pub const fn is_dummy(&self) -> bool { self.kind.contains(ChaKind::DUMMY) }
pub fn atime_dur(&self) -> anyhow::Result<Duration> {
if let Some(atime) = self.atime {
Ok(atime.duration_since(UNIX_EPOCH)?)
} else {
bail!("atime not supported on this platform");
}
}
pub fn mtime_dur(&self) -> anyhow::Result<Duration> {
if let Some(mtime) = self.mtime {
Ok(mtime.duration_since(UNIX_EPOCH)?)
} else {
bail!("mtime not supported on this platform");
}
}
pub fn btime_dur(&self) -> anyhow::Result<Duration> {
if let Some(btime) = self.btime {
Ok(btime.duration_since(UNIX_EPOCH)?)
} else {
bail!("btime not supported on this platform");
}
}
pub fn ctime_dur(&self) -> anyhow::Result<Duration> {
if let Some(ctime) = self.ctime {
Ok(ctime.duration_since(UNIX_EPOCH)?)
} else {
bail!("ctime not supported on this platform");
}
}
} }

View file

@ -1,7 +1,6 @@
use std::fs::Metadata; use std::{ffi::OsStr, fs::Metadata};
use bitflags::bitflags; use bitflags::bitflags;
use yazi_shared::url::Url;
bitflags! { bitflags! {
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
@ -18,15 +17,15 @@ bitflags! {
impl ChaKind { impl ChaKind {
#[inline] #[inline]
pub(super) fn hidden<'a, U>(_url: U, _meta: &Metadata) -> Self pub(super) fn hidden(_name: &OsStr, _meta: &Metadata) -> Self {
where
U: Into<Url<'a>>,
{
let mut me = Self::empty(); let mut me = Self::empty();
#[cfg(unix)] #[cfg(unix)]
if _url.into().urn().is_hidden() { {
me |= Self::HIDDEN; use std::os::unix::ffi::OsStrExt;
if _name.as_bytes().starts_with(b".") {
me |= Self::HIDDEN;
}
} }
#[cfg(windows)] #[cfg(windows)]
{ {

View file

@ -1,4 +1,4 @@
use std::{ffi::OsStr, fs::{FileType, Metadata}, hash::{BuildHasher, Hash, Hasher}, ops::Deref, path::{Path, PathBuf}}; use std::{ffi::OsStr, fs::FileType, hash::{BuildHasher, Hash, Hasher}, ops::Deref, path::{Path, PathBuf}};
use anyhow::Result; use anyhow::Result;
use yazi_shared::url::{Uri, UrlBuf, UrlCow, Urn}; use yazi_shared::url::{Uri, UrlBuf, UrlCow, Urn};
@ -22,15 +22,15 @@ impl File {
#[inline] #[inline]
pub async fn new(url: impl Into<UrlCow<'_>>) -> Result<Self> { pub async fn new(url: impl Into<UrlCow<'_>>) -> Result<Self> {
let url = url.into(); let url = url.into();
let meta = provider::symlink_metadata(&url).await?; let cha = provider::symlink_metadata(&url).await?;
Ok(Self::from_follow(url.into_owned(), meta).await) Ok(Self::from_follow(url.into_owned(), cha).await)
} }
#[inline] #[inline]
pub async fn from_follow(url: UrlBuf, meta: Metadata) -> Self { pub async fn from_follow(url: UrlBuf, cha: Cha) -> Self {
let link_to = if meta.is_symlink() { provider::read_link(&url).await.ok() } else { None }; let link_to = if cha.is_link() { provider::read_link(&url).await.ok() } else { None };
let cha = Cha::from_follow(&url, meta).await; let cha = Cha::from_follow(&url, cha).await;
Self { url, cha, link_to } Self { url, cha, link_to }
} }

View file

@ -46,7 +46,7 @@ impl Files {
result = item.metadata() => { result = item.metadata() => {
let url = item.url(); let url = item.url();
_ = tx.send(match result { _ = tx.send(match result {
Ok(meta) => File::from_follow(url, meta).await, Ok(cha) => File::from_follow(url, cha).await,
Err(_) => File::from_dummy(url, item.file_type().await.ok()) Err(_) => File::from_dummy(url, item.file_type().await.ok())
}); });
} }
@ -70,7 +70,7 @@ impl Files {
for entry in entries { for entry in entries {
let url = entry.url(); let url = entry.url();
files.push(match entry.metadata().await { files.push(match entry.metadata().await {
Ok(meta) => File::from_follow(url, meta).await, Ok(cha) => File::from_follow(url, cha).await,
Err(_) => File::from_dummy(url, entry.file_type().await.ok()), Err(_) => File::from_dummy(url, entry.file_type().await.ok()),
}); });
} }

View file

@ -68,7 +68,7 @@ pub fn copy_with_progress(
None => {} None => {}
} }
let len = provider::symlink_metadata(&to).await.map(|m| m.len()).unwrap_or(0); let len = provider::symlink_metadata(&to).await.map(|m| m.len).unwrap_or(0);
if len > last { if len > last {
prog_tx.send(Ok(len - last)).await.ok(); prog_tx.send(Ok(len - last)).await.ok();
last = len; last = len;

View file

@ -15,11 +15,11 @@ impl SizeCalculator {
U: Into<Url<'a>>, U: Into<Url<'a>>,
{ {
let url: Url = url.into(); let url: Url = url.into();
let meta = provider::symlink_metadata(url).await?; let cha = provider::symlink_metadata(url).await?;
Ok(if meta.is_dir() { Ok(if cha.is_dir() {
Self::Dir(VecDeque::from([Either::Left(url.to_owned())])) Self::Dir(VecDeque::from([Either::Left(url.to_owned())]))
} else { } else {
Self::File(Some(meta.len())) Self::File(Some(cha.len))
}) })
} }
@ -72,8 +72,8 @@ impl SizeCalculator {
let Ok(ft) = ent.file_type().await else { continue }; let Ok(ft) = ent.file_type().await else { continue };
if ft.is_dir() { if ft.is_dir() {
buf.push_back(Either::Left(ent.url())); buf.push_back(Either::Left(ent.url()));
} else if let Ok(meta) = ent.metadata().await { } else if let Ok(cha) = ent.metadata().await {
size += meta.len(); size += cha.len;
} }
} }
Some(size) Some(size)

View file

@ -2,7 +2,7 @@ use std::{borrow::Cow, ffi::OsStr, io};
use yazi_shared::url::UrlBuf; use yazi_shared::url::UrlBuf;
use crate::provider::FileHolder; use crate::{cha::Cha, provider::FileHolder};
pub enum DirEntry { pub enum DirEntry {
Local(super::local::DirEntry), Local(super::local::DirEntry),
@ -27,7 +27,7 @@ impl DirEntry {
} }
} }
pub async fn metadata(&self) -> io::Result<std::fs::Metadata> { pub async fn metadata(&self) -> io::Result<Cha> {
match self { match self {
Self::Local(local) => local.metadata().await, Self::Local(local) => local.metadata().await,
} }

View file

@ -1,6 +1,6 @@
use std::{borrow::Cow, ffi::OsStr, io, path::PathBuf}; use std::{borrow::Cow, ffi::OsStr, io, path::PathBuf};
use crate::provider::FileHolder; use crate::{cha::Cha, provider::FileHolder};
pub struct DirEntry(pub(super) tokio::fs::DirEntry); pub struct DirEntry(pub(super) tokio::fs::DirEntry);
@ -9,7 +9,10 @@ impl FileHolder for DirEntry {
fn name(&self) -> Cow<'_, OsStr> { self.0.file_name().into() } fn name(&self) -> Cow<'_, OsStr> { self.0.file_name().into() }
async fn metadata(&self) -> io::Result<std::fs::Metadata> { self.0.metadata().await } async fn metadata(&self) -> io::Result<Cha> {
let name = self.name(); // TODO: use `file_name_os_str` when stabilized
Ok(Cha::new(&name, self.0.metadata().await?))
}
async fn file_type(&self) -> io::Result<std::fs::FileType> { self.0.file_type().await } async fn file_type(&self) -> io::Result<std::fs::FileType> { self.0.file_type().await }
} }

View file

@ -62,11 +62,12 @@ impl Provider for Local {
} }
#[inline] #[inline]
async fn metadata<P>(path: P) -> io::Result<std::fs::Metadata> async fn metadata<P>(path: P) -> io::Result<Cha>
where where
P: AsRef<Path>, P: AsRef<Path>,
{ {
tokio::fs::metadata(path).await let path = path.as_ref();
Ok(Cha::new(path.file_name().unwrap_or_default(), tokio::fs::metadata(path).await?))
} }
#[inline] #[inline]
@ -170,11 +171,12 @@ impl Provider for Local {
} }
#[inline] #[inline]
async fn symlink_metadata<P>(path: P) -> io::Result<std::fs::Metadata> async fn symlink_metadata<P>(path: P) -> io::Result<Cha>
where where
P: AsRef<Path>, P: AsRef<Path>,
{ {
tokio::fs::symlink_metadata(path).await let path = path.as_ref();
Ok(Cha::new(path.file_name().unwrap_or_default(), tokio::fs::symlink_metadata(path).await?))
} }
async fn trash<P>(path: P) -> io::Result<()> async fn trash<P>(path: P) -> io::Result<()>

View file

@ -125,7 +125,7 @@ where
} }
#[inline] #[inline]
pub async fn metadata<'a, U>(url: U) -> io::Result<std::fs::Metadata> pub async fn metadata<'a, U>(url: U) -> io::Result<Cha>
where where
U: Into<Url<'a>>, U: Into<Url<'a>>,
{ {
@ -256,7 +256,7 @@ where
} }
#[inline] #[inline]
pub async fn symlink_metadata<'a, U>(url: U) -> io::Result<std::fs::Metadata> pub async fn symlink_metadata<'a, U>(url: U) -> io::Result<Cha>
where where
U: Into<Url<'a>>, U: Into<Url<'a>>,
{ {

View file

@ -1,6 +1,6 @@
use std::{borrow::Cow, ffi::OsStr, io, path::PathBuf}; use std::{borrow::Cow, ffi::OsStr, io, path::PathBuf};
use crate::provider::{DirReader, FileHolder}; use crate::{cha::Cha, provider::{DirReader, FileHolder}};
pub struct ReadDir(pub(super) yazi_sftp::fs::ReadDir); pub struct ReadDir(pub(super) yazi_sftp::fs::ReadDir);
@ -20,7 +20,7 @@ impl FileHolder for DirEntry<'_> {
fn name(&self) -> Cow<'_, OsStr> { self.0.name() } fn name(&self) -> Cow<'_, OsStr> { self.0.name() }
async fn metadata(&self) -> io::Result<std::fs::Metadata> { todo!() } async fn metadata(&self) -> io::Result<Cha> { todo!() }
async fn file_type(&self) -> io::Result<std::fs::FileType> { todo!() } async fn file_type(&self) -> io::Result<std::fs::FileType> { todo!() }
} }

View file

@ -1,4 +1,4 @@
use std::{io, path::{Path, PathBuf}, time::UNIX_EPOCH}; use std::{io, path::{Path, PathBuf}};
use yazi_sftp::fs::{Attrs, Flags}; use yazi_sftp::fs::{Attrs, Flags};
@ -36,14 +36,8 @@ impl Provider for Sftp {
uid: Some(cha.uid), uid: Some(cha.uid),
gid: Some(cha.gid), gid: Some(cha.gid),
perm: Some(cha.mode.bits() as _), perm: Some(cha.mode.bits() as _),
atime: cha atime: cha.atime_dur().ok().map(|d| d.as_secs() as u32),
.atime mtime: cha.mtime_dur().ok().map(|d| d.as_secs() as u32),
.and_then(|t| t.duration_since(UNIX_EPOCH).ok())
.map(|d| d.as_secs() as u32),
mtime: cha
.mtime
.and_then(|t| t.duration_since(UNIX_EPOCH).ok())
.map(|d| d.as_secs() as u32),
extended: Default::default(), extended: Default::default(),
}; };
@ -70,7 +64,7 @@ impl Provider for Sftp {
Ok(Self::op().await?.hardlink(&original, &link).await?) Ok(Self::op().await?.hardlink(&original, &link).await?)
} }
async fn metadata<P>(path: P) -> io::Result<std::fs::Metadata> async fn metadata<P>(path: P) -> io::Result<Cha>
where where
P: AsRef<Path>, P: AsRef<Path>,
{ {
@ -122,7 +116,7 @@ impl Provider for Sftp {
Ok(Self::op().await?.symlink(&original, &link).await?) Ok(Self::op().await?.symlink(&original, &link).await?)
} }
async fn symlink_metadata<P>(path: P) -> io::Result<std::fs::Metadata> async fn symlink_metadata<P>(path: P) -> io::Result<Cha>
where where
P: AsRef<Path>, P: AsRef<Path>,
{ {

View file

@ -67,7 +67,7 @@ pub trait Provider {
P: AsRef<Path>, P: AsRef<Path>,
Q: AsRef<Path>; Q: AsRef<Path>;
fn metadata<P>(path: P) -> impl Future<Output = io::Result<std::fs::Metadata>> fn metadata<P>(path: P) -> impl Future<Output = io::Result<Cha>>
where where
P: AsRef<Path>; P: AsRef<Path>;
@ -115,8 +115,8 @@ pub trait Provider {
async move { async move {
let path = path.as_ref(); let path = path.as_ref();
let ft = ok_or_not_found!(Self::symlink_metadata(path).await, return Ok(())); let cha = ok_or_not_found!(Self::symlink_metadata(path).await, return Ok(()));
if ft.is_symlink() { if cha.is_link() {
Self::remove_file(path).await Self::remove_file(path).await
} else { } else {
remove_dir_all_impl::<Self>(path).await remove_dir_all_impl::<Self>(path).await
@ -155,7 +155,7 @@ pub trait Provider {
Self::symlink(original, link, async || Ok(false)) Self::symlink(original, link, async || Ok(false))
} }
fn symlink_metadata<P>(path: P) -> impl Future<Output = io::Result<std::fs::Metadata>> fn symlink_metadata<P>(path: P) -> impl Future<Output = io::Result<Cha>>
where where
P: AsRef<Path>; P: AsRef<Path>;
@ -187,7 +187,7 @@ pub trait FileHolder {
fn name(&self) -> Cow<'_, OsStr>; fn name(&self) -> Cow<'_, OsStr>;
fn metadata(&self) -> impl Future<Output = io::Result<std::fs::Metadata>>; fn metadata(&self) -> impl Future<Output = io::Result<Cha>>;
fn file_type(&self) -> impl Future<Output = io::Result<std::fs::FileType>>; fn file_type(&self) -> impl Future<Output = io::Result<std::fs::FileType>>;
} }

View file

@ -50,14 +50,14 @@ fn cwd(lua: &Lua) -> mlua::Result<Function> {
fn cha(lua: &Lua) -> mlua::Result<Function> { fn cha(lua: &Lua) -> mlua::Result<Function> {
lua.create_async_function(|lua, (url, follow): (UrlRef, Option<bool>)| async move { lua.create_async_function(|lua, (url, follow): (UrlRef, Option<bool>)| async move {
let meta = if follow.unwrap_or(false) { let cha = if follow.unwrap_or(false) {
provider::metadata(&*url).await provider::metadata(&*url).await
} else { } else {
provider::symlink_metadata(&*url).await provider::symlink_metadata(&*url).await
}; };
match meta { match cha {
Ok(m) => Cha(yazi_fs::cha::Cha::new(&*url, m)).into_lua_multi(&lua), Ok(c) => Cha(c).into_lua_multi(&lua),
Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua), Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua),
} }
}) })
@ -129,8 +129,8 @@ fn read_dir(lua: &Lua) -> mlua::Result<Function> {
let file = if !resolve { let file = if !resolve {
yazi_fs::File::from_dummy(url, next.file_type().await.ok()) yazi_fs::File::from_dummy(url, next.file_type().await.ok())
} else if let Ok(meta) = next.metadata().await { } else if let Ok(cha) = next.metadata().await {
yazi_fs::File::from_follow(url, meta).await yazi_fs::File::from_follow(url, cha).await
} else { } else {
yazi_fs::File::from_dummy(url, next.file_type().await.ok()) yazi_fs::File::from_dummy(url, next.file_type().await.ok())
}; };

View file

@ -71,7 +71,7 @@ impl File {
let mut it = continue_unless_ok!(provider::read_dir(&src).await); let mut it = continue_unless_ok!(provider::read_dir(&src).await);
while let Ok(Some(entry)) = it.next_entry().await { while let Ok(Some(entry)) = it.next_entry().await {
let from = entry.url(); let from = entry.url();
let cha = continue_unless_ok!(Self::cha_from(entry, &from, task.follow).await); let cha = continue_unless_ok!(Self::entry_cha(entry, &from, task.follow).await);
if cha.is_dir() { if cha.is_dir() {
dirs.push_back(from); dirs.push_back(from);
@ -205,7 +205,7 @@ impl File {
let mut it = continue_unless_ok!(provider::read_dir(&src).await); let mut it = continue_unless_ok!(provider::read_dir(&src).await);
while let Ok(Some(entry)) = it.next_entry().await { while let Ok(Some(entry)) = it.next_entry().await {
let from = entry.url(); let from = entry.url();
let cha = continue_unless_ok!(Self::cha_from(entry, &from, task.follow).await); let cha = continue_unless_ok!(Self::entry_cha(entry, &from, task.follow).await);
if cha.is_dir() { if cha.is_dir() {
dirs.push_back(from); dirs.push_back(from);
@ -237,11 +237,11 @@ impl File {
} }
pub(crate) async fn delete(&self, mut task: FileInDelete) -> Result<(), FileOutDelete> { pub(crate) async fn delete(&self, mut task: FileInDelete) -> Result<(), FileOutDelete> {
let meta = provider::symlink_metadata(&task.target).await?; let cha = provider::symlink_metadata(&task.target).await?;
if !meta.is_dir() { if !cha.is_dir() {
let id = task.id; let id = task.id;
task.length = meta.len(); task.length = cha.len;
self.ops.out(id, FileOutDelete::New(meta.len())); self.ops.out(id, FileOutDelete::New(cha.len));
self.queue(task, NORMAL); self.queue(task, NORMAL);
self.ops.out(id, FileOutDelete::Init); self.ops.out(id, FileOutDelete::Init);
return Ok(()); return Ok(());
@ -252,16 +252,16 @@ impl File {
let Ok(mut it) = provider::read_dir(&target).await else { continue }; let Ok(mut it) = provider::read_dir(&target).await else { continue };
while let Ok(Some(entry)) = it.next_entry().await { while let Ok(Some(entry)) = it.next_entry().await {
let Ok(meta) = entry.metadata().await else { continue }; let Ok(cha) = entry.metadata().await else { continue };
if meta.is_dir() { if cha.is_dir() {
dirs.push_front(entry.url()); dirs.push_front(entry.url());
continue; continue;
} }
task.target = entry.url(); task.target = entry.url();
task.length = meta.len(); task.length = cha.len;
self.ops.out(task.id, FileOutDelete::New(meta.len())); self.ops.out(task.id, FileOutDelete::New(cha.len));
self.queue(task.clone(), NORMAL); self.queue(task.clone(), NORMAL);
} }
} }
@ -291,16 +291,16 @@ impl File {
#[inline] #[inline]
async fn cha<'a>(url: impl Into<Url<'a>>, follow: bool) -> io::Result<Cha> { async fn cha<'a>(url: impl Into<Url<'a>>, follow: bool) -> io::Result<Cha> {
let url = url.into(); let url = url.into();
let meta = provider::symlink_metadata(url).await?; let cha = provider::symlink_metadata(url).await?;
Ok(if follow { Cha::from_follow(url, meta).await } else { Cha::new(url, meta) }) Ok(if follow { Cha::from_follow(url, cha).await } else { cha })
} }
#[inline] #[inline]
async fn cha_from(entry: DirEntry, url: &UrlBuf, follow: bool) -> io::Result<Cha> { async fn entry_cha(entry: DirEntry, url: &UrlBuf, follow: bool) -> io::Result<Cha> {
Ok(if follow { Ok(if follow {
Cha::from_follow(url, entry.metadata().await?).await Cha::from_follow(url, entry.metadata().await?).await
} else { } else {
Cha::new(url, entry.metadata().await?) entry.metadata().await?
}) })
} }
} }