From 7b36b7fe14a153dd6a88a99f8f207e64f2e089d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Sun, 28 Sep 2025 15:32:38 +0800 Subject: [PATCH] refactor: invert the dependency relationship between `yazi-fs` and `yazi-vfs` (#3215) --- Cargo.lock | 213 +++++++++--------- Cargo.toml | 8 +- README.md | 38 ++-- yazi-actor/Cargo.toml | 1 + yazi-actor/src/cmp/trigger.rs | 3 +- yazi-actor/src/mgr/bulk_rename.rs | 3 +- yazi-actor/src/mgr/cd.rs | 1 + yazi-actor/src/mgr/create.rs | 3 +- yazi-actor/src/mgr/open.rs | 1 + yazi-actor/src/mgr/refresh.rs | 1 + yazi-actor/src/mgr/rename.rs | 3 +- yazi-binding/src/macros.rs | 7 +- yazi-boot/src/actions/clear_cache.rs | 2 +- yazi-boot/src/boot.rs | 4 +- yazi-cli/Cargo.toml | 1 - yazi-cli/src/package/delete.rs | 4 +- yazi-cli/src/package/dependency.rs | 3 +- yazi-cli/src/package/deploy.rs | 4 +- yazi-cli/src/package/mod.rs | 2 +- yazi-cli/src/package/package.rs | 3 +- yazi-config/Cargo.toml | 1 - yazi-config/src/keymap/keymap.rs | 3 +- yazi-config/src/preview/preview.rs | 3 +- yazi-config/src/theme/flavor.rs | 2 +- yazi-config/src/theme/theme.rs | 3 +- yazi-config/src/yazi.rs | 3 +- yazi-core/Cargo.toml | 1 + yazi-core/src/tab/preview.rs | 1 + yazi-fm/src/logs.rs | 2 +- yazi-fm/src/main.rs | 2 + yazi-fs/Cargo.toml | 7 +- yazi-fs/src/cha/cha.rs | 25 +- yazi-fs/src/cwd.rs | 6 +- yazi-fs/src/file.rs | 21 +- yazi-fs/src/files.rs | 69 +----- yazi-fs/src/fns.rs | 91 +------- yazi-fs/src/lib.rs | 4 +- yazi-fs/src/op.rs | 13 +- yazi-fs/src/path/path.rs | 55 +---- yazi-fs/src/provider/local/gate.rs | 4 +- yazi-fs/src/provider/local/metadata.rs | 6 +- yazi-fs/src/provider/mod.rs | 6 +- yazi-fs/src/provider/traits.rs | 24 ++ yazi-fs/src/url.rs | 32 +++ {yazi-vfs/src/local => yazi-fs/src}/xdg.rs | 0 yazi-plugin/Cargo.toml | 3 +- yazi-plugin/preset/plugins/mime.lua | 2 +- yazi-plugin/src/bindings/calculator.rs | 2 +- yazi-plugin/src/external/fd.rs | 1 + yazi-plugin/src/external/rg.rs | 1 + yazi-plugin/src/external/rga.rs | 1 + yazi-plugin/src/fs/fs.rs | 11 +- yazi-scheduler/Cargo.toml | 1 + yazi-scheduler/src/file/file.rs | 3 +- yazi-scheduler/src/prework/prework.rs | 3 +- yazi-scheduler/src/process/shell.rs | 4 +- yazi-scheduler/src/scheduler.rs | 4 +- yazi-shared/Cargo.toml | 4 +- yazi-term/Cargo.toml | 2 +- yazi-vfs/Cargo.toml | 17 +- yazi-vfs/src/cha.rs | 37 +++ yazi-vfs/src/config/vfs.rs | 2 +- yazi-vfs/src/file.rs | 29 +++ yazi-vfs/src/files.rs | 79 +++++++ yazi-vfs/src/fns.rs | 122 ++++++++++ yazi-vfs/src/lib.rs | 6 +- yazi-vfs/src/local/mod.rs | 1 - yazi-vfs/src/op.rs | 23 ++ .../src/provider/calculator.rs | 7 +- .../src/provider/dir_entry.rs | 7 +- {yazi-fs => yazi-vfs}/src/provider/gate.rs | 13 +- yazi-vfs/src/provider/mod.rs | 5 + .../src/provider/provider.rs | 37 +-- .../src/provider/providers.rs | 5 +- .../src/provider/read_dir.rs | 7 +- {yazi-fs => yazi-vfs}/src/provider/rw_file.rs | 5 +- .../src/provider/sftp/gate.rs | 6 +- .../src/provider/sftp/metadata.rs | 46 ++-- .../src/provider/sftp/mod.rs | 2 +- .../src/provider/sftp/read_dir.rs | 10 +- .../src/provider/sftp/sftp.rs | 17 +- yazi-watcher/Cargo.toml | 1 + yazi-watcher/src/watcher.rs | 1 + 83 files changed, 662 insertions(+), 554 deletions(-) create mode 100644 yazi-fs/src/url.rs rename {yazi-vfs/src/local => yazi-fs/src}/xdg.rs (100%) create mode 100644 yazi-vfs/src/cha.rs create mode 100644 yazi-vfs/src/file.rs create mode 100644 yazi-vfs/src/files.rs create mode 100644 yazi-vfs/src/fns.rs delete mode 100644 yazi-vfs/src/local/mod.rs create mode 100644 yazi-vfs/src/op.rs rename {yazi-fs => yazi-vfs}/src/provider/calculator.rs (91%) rename {yazi-fs => yazi-vfs}/src/provider/dir_entry.rs (88%) rename {yazi-fs => yazi-vfs}/src/provider/gate.rs (85%) create mode 100644 yazi-vfs/src/provider/mod.rs rename {yazi-fs => yazi-vfs}/src/provider/provider.rs (89%) rename {yazi-fs => yazi-vfs}/src/provider/providers.rs (98%) rename {yazi-fs => yazi-vfs}/src/provider/read_dir.rs (80%) rename {yazi-fs => yazi-vfs}/src/provider/rw_file.rs (96%) rename {yazi-fs => yazi-vfs}/src/provider/sftp/gate.rs (91%) rename {yazi-fs => yazi-vfs}/src/provider/sftp/metadata.rs (66%) rename {yazi-fs => yazi-vfs}/src/provider/sftp/mod.rs (86%) rename {yazi-fs => yazi-vfs}/src/provider/sftp/read_dir.rs (62%) rename {yazi-fs => yazi-vfs}/src/provider/sftp/sftp.rs (94%) diff --git a/Cargo.lock b/Cargo.lock index 73046b65..bea6b393 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 4 [[package]] name = "addr2line" -version = "0.24.2" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" dependencies = [ "gimli", ] @@ -244,9 +244,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.75" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" dependencies = [ "addr2line", "cfg-if", @@ -254,7 +254,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets 0.52.6", + "windows-link 0.2.0", ] [[package]] @@ -459,9 +459,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.38" +version = "1.2.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80f41ae168f955c12fb8960b057d70d0ca153fb83182b57d86380443527be7e9" +checksum = "e1354349954c6fc9cb0deab020f27f783cf0b604e8bb754dc4658ecf0d29c35f" dependencies = [ "find-msvc-tools", "jobserver", @@ -1008,7 +1008,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.61.0", + "windows-sys 0.61.1", ] [[package]] @@ -1154,7 +1154,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.0", + "windows-sys 0.61.1", ] [[package]] @@ -1437,9 +1437,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.31.1" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" [[package]] name = "globset" @@ -1561,7 +1561,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.0", + "windows-core 0.62.1", ] [[package]] @@ -1779,9 +1779,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.80" +version = "0.3.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852f13bec5eba4ba9afbeb93fd7c13fe56147f055939ae21c43a29a0ecb2702e" +checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" dependencies = [ "once_cell", "wasm-bindgen", @@ -1969,9 +1969,9 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] name = "memchr" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "minimal-lexical" @@ -2268,9 +2268,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.7" +version = "0.37.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" dependencies = [ "memchr", ] @@ -2396,10 +2396,11 @@ dependencies = [ [[package]] name = "pageant" -version = "0.0.3" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdd27df01428302f915ea74737fe88170dd1bab4cbd00ff9548ca85618fcd4e4" +checksum = "bb28bd89a207e5cad59072ac4b364b08459d05f90ccfbcdaa920a95857d94430" dependencies = [ + "byteorder", "bytes", "delegate", "futures", @@ -2407,7 +2408,7 @@ dependencies = [ "rand 0.8.5", "thiserror 1.0.69", "tokio", - "windows 0.58.0", + "windows 0.59.0", ] [[package]] @@ -2913,9 +2914,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.2" +version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" +checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c" dependencies = [ "aho-corasick", "memchr", @@ -2925,9 +2926,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" +checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad" dependencies = [ "aho-corasick", "memchr", @@ -2993,9 +2994,9 @@ dependencies = [ [[package]] name = "russh" -version = "0.54.3" +version = "0.54.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00897b69ab623d39b396af89f1acbb775fb5a730f0db91833da297d5a6cd3f8d" +checksum = "54b1296a0e9d970321e59d77367b8d1511311445f3dd5d42f24e7d9a1706cf51" dependencies = [ "aes", "base64ct", @@ -3122,7 +3123,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.61.0", + "windows-sys 0.61.1", ] [[package]] @@ -3203,9 +3204,9 @@ checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" [[package]] name = "serde" -version = "1.0.226" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", "serde_derive", @@ -3223,18 +3224,18 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.226" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.226" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -3502,12 +3503,11 @@ dependencies = [ [[package]] name = "syntect" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "874dcfa363995604333cf947ae9f751ca3af4522c60886774c4963943b4746b1" +checksum = "656b45c05d95a5704399aeef6bd0ddec7b2b3531b7c9e900abbf7c4d2190c925" dependencies = [ "bincode", - "bitflags 1.3.2", "flate2", "fnv", "once_cell", @@ -3517,7 +3517,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.16", "walkdir", ] @@ -4065,9 +4065,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.103" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab10a69fbd0a177f5f649ad4d8d3305499c42bab9aef2f7ff592d0ec8f833819" +checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" dependencies = [ "cfg-if", "once_cell", @@ -4078,9 +4078,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.103" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb702423545a6007bbc368fde243ba47ca275e549c8a28617f56f6ba53b1d1c" +checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" dependencies = [ "bumpalo", "log", @@ -4092,9 +4092,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.53" +version = "0.4.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0b221ff421256839509adbb55998214a70d829d3a28c69b4a6672e9d2a42f67" +checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c" dependencies = [ "cfg-if", "js-sys", @@ -4105,9 +4105,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.103" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc65f4f411d91494355917b605e1480033152658d71f722a90647f56a70c88a0" +checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4115,9 +4115,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.103" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc003a991398a8ee604a401e194b6b3a39677b3173d6e74495eb51b82e99a32" +checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" dependencies = [ "proc-macro2", "quote", @@ -4128,18 +4128,18 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.103" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "293c37f4efa430ca14db3721dfbe48d8c33308096bd44d80ebaa775ab71ba1cf" +checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.80" +version = "0.3.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbe734895e869dc429d78c4b433f8d17d95f8d05317440b4fad5ab2d33e596dc" +checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120" dependencies = [ "js-sys", "wasm-bindgen", @@ -4195,7 +4195,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.0", + "windows-sys 0.61.1", ] [[package]] @@ -4216,12 +4216,12 @@ dependencies = [ [[package]] name = "windows" -version = "0.58.0" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +checksum = "7f919aee0a93304be7f62e8e5027811bbba96bcb1de84d6618be56e43f8a32a1" dependencies = [ - "windows-core 0.58.0", - "windows-targets 0.52.6", + "windows-core 0.59.0", + "windows-targets 0.53.4", ] [[package]] @@ -4238,25 +4238,25 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.58.0" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +checksum = "810ce18ed2112484b0d4e15d022e5f598113e220c53e373fb31e67e21670c1ce" dependencies = [ - "windows-implement 0.58.0", - "windows-interface 0.58.0", - "windows-result 0.2.0", - "windows-strings 0.1.0", - "windows-targets 0.52.6", + "windows-implement 0.59.0", + "windows-interface 0.59.2", + "windows-result 0.3.4", + "windows-strings 0.3.1", + "windows-targets 0.53.4", ] [[package]] name = "windows-core" -version = "0.62.0" +version = "0.62.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57fe7168f7de578d2d8a05b07fd61870d2e73b4020e9f49aa00da8471723497c" +checksum = "6844ee5416b285084d3d3fffd743b925a6c9385455f64f6d4fa3031c4c2749a9" dependencies = [ - "windows-implement 0.60.0", - "windows-interface 0.59.1", + "windows-implement 0.60.1", + "windows-interface 0.59.2", "windows-link 0.2.0", "windows-result 0.4.0", "windows-strings 0.5.0", @@ -4275,9 +4275,9 @@ dependencies = [ [[package]] name = "windows-implement" -version = "0.58.0" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1" dependencies = [ "proc-macro2", "quote", @@ -4286,9 +4286,9 @@ dependencies = [ [[package]] name = "windows-implement" -version = "0.60.0" +version = "0.60.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +checksum = "edb307e42a74fb6de9bf3a02d9712678b22399c87e6fa869d6dfcd8c1b7754e0" dependencies = [ "proc-macro2", "quote", @@ -4308,20 +4308,9 @@ dependencies = [ [[package]] name = "windows-interface" -version = "0.58.0" +version = "0.59.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-interface" -version = "0.59.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +checksum = "c0abd1ddbc6964ac14db11c7213d6532ef34bd9aa042c2e5935f59d7908b46a5" dependencies = [ "proc-macro2", "quote", @@ -4351,11 +4340,11 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.2.0" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-targets 0.52.6", + "windows-link 0.1.3", ] [[package]] @@ -4369,12 +4358,11 @@ dependencies = [ [[package]] name = "windows-strings" -version = "0.1.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" dependencies = [ - "windows-result 0.2.0", - "windows-targets 0.52.6", + "windows-link 0.1.3", ] [[package]] @@ -4410,14 +4398,14 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.3", + "windows-targets 0.53.4", ] [[package]] name = "windows-sys" -version = "0.61.0" +version = "0.61.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" +checksum = "6f109e41dd4a3c848907eb83d5a42ea98b3769495597450cf6d153507b166f0f" dependencies = [ "windows-link 0.2.0", ] @@ -4440,11 +4428,11 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.3" +version = "0.53.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +checksum = "2d42b7b7f66d2a06854650af09cfdf8713e427a439c97ad65a6375318033ac4b" dependencies = [ - "windows-link 0.1.3", + "windows-link 0.2.0", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -4609,6 +4597,7 @@ dependencies = [ "yazi-scheduler", "yazi-shared", "yazi-term", + "yazi-vfs", "yazi-watcher", "yazi-widgets", ] @@ -4704,7 +4693,6 @@ dependencies = [ "yazi-fs", "yazi-macro", "yazi-shared", - "yazi-vfs", ] [[package]] @@ -4736,7 +4724,6 @@ dependencies = [ "yazi-macro", "yazi-shared", "yazi-term", - "yazi-vfs", ] [[package]] @@ -4764,6 +4751,7 @@ dependencies = [ "yazi-proxy", "yazi-scheduler", "yazi-shared", + "yazi-vfs", "yazi-watcher", "yazi-widgets", ] @@ -4853,16 +4841,13 @@ dependencies = [ "arc-swap", "bitflags 2.9.4", "core-foundation-sys", - "deadpool", "dirs", "foldhash 0.2.0", - "futures", "hashbrown 0.16.0", "libc", "objc", "parking_lot", "regex", - "russh", "scopeguard", "serde", "tokio", @@ -4870,12 +4855,10 @@ dependencies = [ "trash", "twox-hash", "uzers", - "windows-sys 0.61.0", + "windows-sys 0.61.1", "yazi-ffi", "yazi-macro", - "yazi-sftp", "yazi-shared", - "yazi-vfs", ] [[package]] @@ -4928,7 +4911,7 @@ dependencies = [ "twox-hash", "unicode-width 0.2.0", "uzers", - "windows-sys 0.61.0", + "windows-sys 0.61.1", "yazi-adapter", "yazi-binding", "yazi-boot", @@ -4940,6 +4923,7 @@ dependencies = [ "yazi-prebuilt", "yazi-proxy", "yazi-shared", + "yazi-vfs", "yazi-widgets", ] @@ -4987,6 +4971,7 @@ dependencies = [ "yazi-plugin", "yazi-proxy", "yazi-shared", + "yazi-vfs", ] [[package]] @@ -5018,7 +5003,7 @@ dependencies = [ "serde", "tokio", "uzers", - "windows-sys 0.61.0", + "windows-sys 0.61.1", "yazi-macro", ] @@ -5030,7 +5015,7 @@ dependencies = [ "libc", "parking_lot", "tracing", - "windows-sys 0.61.0", + "windows-sys 0.61.1", "yazi-macro", "yazi-shared", ] @@ -5039,13 +5024,20 @@ dependencies = [ name = "yazi-vfs" version = "25.9.15" dependencies = [ - "dirs", + "anyhow", + "deadpool", + "futures", "hashbrown 0.16.0", + "parking_lot", + "russh", "serde", "tokio", "toml 0.9.7", + "tracing", "uzers", + "yazi-fs", "yazi-macro", + "yazi-sftp", "yazi-shared", ] @@ -5065,6 +5057,7 @@ dependencies = [ "yazi-fs", "yazi-macro", "yazi-shared", + "yazi-vfs", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 297afb39..8372be97 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,12 +41,12 @@ ordered-float = { version = "5.0.0", features = [ "serde" ] } parking_lot = "0.12.4" paste = "1.0.15" ratatui = { version = "0.29.0", features = [ "unstable-rendered-line-info", "unstable-widget-ref" ] } -regex = "1.11.2" -russh = { version = "0.54.3", default-features = false, features = [ "ring", "rsa" ] } +regex = "1.11.3" +russh = { version = "0.54.4", default-features = false, features = [ "ring", "rsa" ] } scopeguard = "1.2.0" -serde = { version = "1.0.226", features = [ "derive" ] } +serde = { version = "1.0.228", features = [ "derive" ] } serde_json = "1.0.145" -syntect = { version = "5.2.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] } +syntect = { version = "5.3.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] } tokio = { version = "1.47.1", features = [ "full" ] } tokio-stream = "0.1.17" tokio-util = "0.7.16" diff --git a/README.md b/README.md index 67b220ee..8e309793 100644 --- a/README.md +++ b/README.md @@ -52,25 +52,25 @@ Yazi is currently in heavy development, expect breaking changes. ## Image Preview -| Platform | Protocol | Support | -| ---------------------------------------------------------------------------- | -------------------------------------- | --------------------------------- | -| [kitty](https://github.com/kovidgoyal/kitty) (>= 0.28.0) | [Kitty unicode placeholders][kgp] | ✅ Built-in | -| [iTerm2](https://iterm2.com) | [Inline images protocol][iip] | ✅ Built-in | -| [WezTerm](https://github.com/wez/wezterm) | [Inline images protocol][iip] | ✅ Built-in | -| [Konsole](https://invent.kde.org/utilities/konsole) | [Kitty old protocol][kgp-old] | ✅ Built-in | -| [foot](https://codeberg.org/dnkl/foot) | [Sixel graphics format][sixel] | ✅ Built-in | -| [Ghostty](https://github.com/ghostty-org/ghostty) | [Kitty unicode placeholders][kgp] | ✅ Built-in | -| [Windows Terminal](https://github.com/microsoft/terminal) (>= v1.22.10352.0) | [Sixel graphics format][sixel] | ✅ Built-in | -| [st with Sixel patch](https://github.com/bakkeby/st-flexipatch) | [Sixel graphics format][sixel] | ✅ Built-in | -| [Warp](https://www.warp.dev) (macOS/Linux only) | [Inline images protocol][iip] | ✅ Built-in | -| [Tabby](https://github.com/Eugeny/tabby) | [Inline images protocol][iip] | ✅ Built-in | -| [VSCode](https://github.com/microsoft/vscode) | [Inline images protocol][iip] | ✅ Built-in | -| [Rio](https://github.com/raphamorim/rio) | [Inline images protocol][iip] | ✅ Built-in | -| [Black Box](https://gitlab.gnome.org/raggesilver/blackbox) | [Sixel graphics format][sixel] | ✅ Built-in | -| [Hyper](https://github.com/vercel/hyper) | [Inline images protocol][iip] | ✅ Built-in | -| [Bobcat](https://github.com/ismail-yilmaz/Bobcat) | [Inline images protocol][iip] | ✅ Built-in | -| X11 / Wayland | Window system protocol | ☑️ [Überzug++][ueberzug] required | -| Fallback | [ASCII art (Unicode block)][ascii-art] | ☑️ [Chafa][chafa] required | +| Platform | Protocol | Support | +| ---------------------------------------------------------------------------- | -------------------------------------- | ---------------------------------------- | +| [kitty](https://github.com/kovidgoyal/kitty) (>= 0.28.0) | [Kitty unicode placeholders][kgp] | ✅ Built-in | +| [iTerm2](https://iterm2.com) | [Inline images protocol][iip] | ✅ Built-in | +| [WezTerm](https://github.com/wez/wezterm) | [Inline images protocol][iip] | ✅ Built-in | +| [Konsole](https://invent.kde.org/utilities/konsole) | [Kitty old protocol][kgp-old] | ✅ Built-in | +| [foot](https://codeberg.org/dnkl/foot) | [Sixel graphics format][sixel] | ✅ Built-in | +| [Ghostty](https://github.com/ghostty-org/ghostty) | [Kitty unicode placeholders][kgp] | ✅ Built-in | +| [Windows Terminal](https://github.com/microsoft/terminal) (>= v1.22.10352.0) | [Sixel graphics format][sixel] | ✅ Built-in | +| [st with Sixel patch](https://github.com/bakkeby/st-flexipatch) | [Sixel graphics format][sixel] | ✅ Built-in | +| [Warp](https://www.warp.dev) (macOS/Linux only) | [Inline images protocol][iip] | ✅ Built-in | +| [Tabby](https://github.com/Eugeny/tabby) | [Inline images protocol][iip] | ✅ Built-in | +| [VSCode](https://github.com/microsoft/vscode) | [Inline images protocol][iip] | ✅ Built-in | +| [Rio](https://github.com/raphamorim/rio) | [Inline images protocol][iip] | ❌ Rio renders images at incorrect sizes | +| [Black Box](https://gitlab.gnome.org/raggesilver/blackbox) | [Sixel graphics format][sixel] | ✅ Built-in | +| [Hyper](https://github.com/vercel/hyper) | [Inline images protocol][iip] | ✅ Built-in | +| [Bobcat](https://github.com/ismail-yilmaz/Bobcat) | [Inline images protocol][iip] | ✅ Built-in | +| X11 / Wayland | Window system protocol | ☑️ [Überzug++][ueberzug] required | +| Fallback | [ASCII art (Unicode block)][ascii-art] | ☑️ [Chafa][chafa] required | See https://yazi-rs.github.io/docs/image-preview for details. diff --git a/yazi-actor/Cargo.toml b/yazi-actor/Cargo.toml index b23fdc8a..6cad1e14 100644 --- a/yazi-actor/Cargo.toml +++ b/yazi-actor/Cargo.toml @@ -26,6 +26,7 @@ yazi-proxy = { path = "../yazi-proxy", version = "25.9.15" } yazi-scheduler = { path = "../yazi-scheduler", version = "25.9.15" } yazi-shared = { path = "../yazi-shared", version = "25.9.15" } yazi-term = { path = "../yazi-term", version = "25.9.15" } +yazi-vfs = { path = "../yazi-vfs", version = "25.9.15" } yazi-watcher = { path = "../yazi-watcher", version = "25.9.15" } yazi-widgets = { path = "../yazi-widgets", version = "25.9.15" } diff --git a/yazi-actor/src/cmp/trigger.rs b/yazi-actor/src/cmp/trigger.rs index fd2dd8dd..0ca7a0a5 100644 --- a/yazi-actor/src/cmp/trigger.rs +++ b/yazi-actor/src/cmp/trigger.rs @@ -1,11 +1,12 @@ use std::{ffi::OsString, mem, path::MAIN_SEPARATOR_STR}; use anyhow::Result; -use yazi_fs::{CWD, path::expand_url, provider::{self, DirReader, FileHolder}}; +use yazi_fs::{CWD, path::expand_url, provider::{DirReader, FileHolder}}; use yazi_macro::{act, render, succ}; use yazi_parser::cmp::{CmpItem, ShowOpt, TriggerOpt}; use yazi_proxy::CmpProxy; use yazi_shared::{OsStrSplit, event::Data, natsort, url::{UrlBuf, UrlCow, UrnBuf}}; +use yazi_vfs::provider; use crate::{Actor, Ctx}; diff --git a/yazi-actor/src/mgr/bulk_rename.rs b/yazi-actor/src/mgr/bulk_rename.rs index 8d529028..dc135e72 100644 --- a/yazi-actor/src/mgr/bulk_rename.rs +++ b/yazi-actor/src/mgr/bulk_rename.rs @@ -7,12 +7,13 @@ use scopeguard::defer; use tokio::io::AsyncWriteExt; use yazi_config::{YAZI, opener::OpenerRule}; use yazi_dds::Pubsub; -use yazi_fs::{File, FilesOp, max_common_root, maybe_exists, path::skip_url, provider::{self, FileBuilder, Provider, local::{Gate, Local}}}; +use yazi_fs::{File, FilesOp, max_common_root, path::skip_url, provider::{FileBuilder, Provider, local::{Gate, Local}}}; use yazi_macro::{err, succ}; use yazi_parser::VoidOpt; use yazi_proxy::{AppProxy, HIDER, TasksProxy}; use yazi_shared::{OsStrJoin, event::Data, terminal_clear, url::{Component, UrlBuf}}; use yazi_term::tty::TTY; +use yazi_vfs::{VfsFile, maybe_exists, provider}; use yazi_watcher::WATCHER; use crate::{Actor, Ctx}; diff --git a/yazi-actor/src/mgr/cd.rs b/yazi-actor/src/mgr/cd.rs index 8a623d81..5a7031b0 100644 --- a/yazi-actor/src/mgr/cd.rs +++ b/yazi-actor/src/mgr/cd.rs @@ -10,6 +10,7 @@ use yazi_macro::{act, err, render, succ}; use yazi_parser::mgr::CdOpt; use yazi_proxy::{CmpProxy, InputProxy, MgrProxy}; use yazi_shared::{Debounce, errors::InputError, event::Data, url::UrlBuf}; +use yazi_vfs::VfsFile; use crate::{Actor, Ctx}; diff --git a/yazi-actor/src/mgr/create.rs b/yazi-actor/src/mgr/create.rs index d79341aa..c14c3077 100644 --- a/yazi-actor/src/mgr/create.rs +++ b/yazi-actor/src/mgr/create.rs @@ -1,10 +1,11 @@ use anyhow::{Result, bail}; use yazi_config::popup::{ConfirmCfg, InputCfg}; -use yazi_fs::{File, FilesOp, maybe_exists, ok_or_not_found, provider}; +use yazi_fs::{File, FilesOp, ok_or_not_found}; use yazi_macro::succ; use yazi_parser::mgr::CreateOpt; use yazi_proxy::{ConfirmProxy, InputProxy, MgrProxy}; use yazi_shared::{event::Data, url::UrlBuf}; +use yazi_vfs::{VfsFile, maybe_exists, provider}; use yazi_watcher::WATCHER; use crate::{Actor, Ctx}; diff --git a/yazi-actor/src/mgr/open.rs b/yazi-actor/src/mgr/open.rs index 5de5583f..3013ca26 100644 --- a/yazi-actor/src/mgr/open.rs +++ b/yazi-actor/src/mgr/open.rs @@ -9,6 +9,7 @@ use yazi_macro::{act, succ}; use yazi_parser::mgr::{OpenDoOpt, OpenOpt}; use yazi_proxy::{MgrProxy, PickProxy, TasksProxy}; use yazi_shared::{MIME_DIR, event::Data, url::UrlBuf}; +use yazi_vfs::VfsFile; use crate::{Actor, Ctx, mgr::Quit}; diff --git a/yazi-actor/src/mgr/refresh.rs b/yazi-actor/src/mgr/refresh.rs index d62657d0..379bf5ba 100644 --- a/yazi-actor/src/mgr/refresh.rs +++ b/yazi-actor/src/mgr/refresh.rs @@ -7,6 +7,7 @@ use yazi_macro::{act, succ}; use yazi_parser::VoidOpt; use yazi_shared::{event::Data, url::UrlBuf}; use yazi_term::tty::TTY; +use yazi_vfs::{VfsFiles, VfsFilesOp}; use crate::{Actor, Ctx}; diff --git a/yazi-actor/src/mgr/rename.rs b/yazi-actor/src/mgr/rename.rs index 19febb59..fcd2da14 100644 --- a/yazi-actor/src/mgr/rename.rs +++ b/yazi-actor/src/mgr/rename.rs @@ -1,11 +1,12 @@ use anyhow::Result; use yazi_config::popup::{ConfirmCfg, InputCfg}; use yazi_dds::Pubsub; -use yazi_fs::{File, FilesOp, maybe_exists, ok_or_not_found, provider}; +use yazi_fs::{File, FilesOp, ok_or_not_found}; use yazi_macro::{act, err, succ}; use yazi_parser::mgr::RenameOpt; use yazi_proxy::{ConfirmProxy, InputProxy, MgrProxy}; use yazi_shared::{Id, event::Data, url::UrlBuf}; +use yazi_vfs::{VfsFile, maybe_exists, provider}; use yazi_watcher::WATCHER; use crate::{Actor, Ctx}; diff --git a/yazi-binding/src/macros.rs b/yazi-binding/src/macros.rs index 84f43711..3ad48c5d 100644 --- a/yazi-binding/src/macros.rs +++ b/yazi-binding/src/macros.rs @@ -156,9 +156,10 @@ macro_rules! impl_file_fields { $crate::cached_field!($fields, name, |lua, me| { me.name().map(|s| lua.create_string(s.as_encoded_bytes())).transpose() }); - $crate::cached_field!($fields, cache, |_, me| Ok( - yazi_fs::provider::cache(&me.url).map($crate::Url::new) - )); + $crate::cached_field!($fields, cache, |_, me| { + use yazi_fs::FsUrl; + Ok(me.url.cache().map($crate::Url::new)) + }); }; } diff --git a/yazi-boot/src/actions/clear_cache.rs b/yazi-boot/src/actions/clear_cache.rs index ee864632..90b03b46 100644 --- a/yazi-boot/src/actions/clear_cache.rs +++ b/yazi-boot/src/actions/clear_cache.rs @@ -1,5 +1,5 @@ use yazi_config::YAZI; -use yazi_vfs::local::Xdg; +use yazi_fs::Xdg; use super::Actions; diff --git a/yazi-boot/src/boot.rs b/yazi-boot/src/boot.rs index 20806294..7e738956 100644 --- a/yazi-boot/src/boot.rs +++ b/yazi-boot/src/boot.rs @@ -3,9 +3,9 @@ use std::path::PathBuf; use futures::executor::block_on; use hashbrown::HashSet; use serde::Serialize; -use yazi_fs::{CWD, path::expand_url, provider}; +use yazi_fs::{CWD, Xdg, path::expand_url}; use yazi_shared::url::{UrlBuf, UrlCow, UrnBuf}; -use yazi_vfs::local::Xdg; +use yazi_vfs::provider; #[derive(Debug, Default, Serialize)] pub struct Boot { diff --git a/yazi-cli/Cargo.toml b/yazi-cli/Cargo.toml index bd4c69a3..6351bc46 100644 --- a/yazi-cli/Cargo.toml +++ b/yazi-cli/Cargo.toml @@ -24,7 +24,6 @@ yazi-dds = { path = "../yazi-dds", version = "25.9.15" } yazi-fs = { path = "../yazi-fs", version = "25.9.15" } yazi-macro = { path = "../yazi-macro", version = "25.9.15" } yazi-shared = { path = "../yazi-shared", version = "25.9.15" } -yazi-vfs = { path = "../yazi-vfs", version = "25.9.15" } # External dependencies anyhow = { workspace = true } diff --git a/yazi-cli/src/package/delete.rs b/yazi-cli/src/package/delete.rs index e71b83ef..02782bc0 100644 --- a/yazi-cli/src/package/delete.rs +++ b/yazi-cli/src/package/delete.rs @@ -1,5 +1,5 @@ use anyhow::{Context, Result}; -use yazi_fs::{ok_or_not_found, provider::{DirReader, FileHolder, Provider, local::Local}, remove_dir_clean}; +use yazi_fs::{ok_or_not_found, provider::{DirReader, FileHolder, Provider, local::Local}}; use yazi_macro::outln; use super::Dependency; @@ -35,7 +35,7 @@ impl Dependency { Err(e) => Err(e).context(format!("failed to read `{}`", assets.display()))?, }; - remove_dir_clean(&assets.into()).await; + Local.remove_dir_clean(assets).await; Ok(()) } diff --git a/yazi-cli/src/package/dependency.rs b/yazi-cli/src/package/dependency.rs index 951d6f60..aee07cca 100644 --- a/yazi-cli/src/package/dependency.rs +++ b/yazi-cli/src/package/dependency.rs @@ -3,9 +3,8 @@ use std::{io::BufWriter, path::{Path, PathBuf}, str::FromStr}; use anyhow::{Result, bail}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use twox_hash::XxHash3_128; -use yazi_fs::provider::{DirReader, FileHolder, Provider, local::Local}; +use yazi_fs::{Xdg, provider::{DirReader, FileHolder, Provider, local::Local}}; use yazi_shared::BytesExt; -use yazi_vfs::local::Xdg; #[derive(Clone, Default)] pub(crate) struct Dependency { diff --git a/yazi-cli/src/package/deploy.rs b/yazi-cli/src/package/deploy.rs index a48f1a73..84d758bc 100644 --- a/yazi-cli/src/package/deploy.rs +++ b/yazi-cli/src/package/deploy.rs @@ -1,7 +1,7 @@ use std::path::{Path, PathBuf}; use anyhow::{Context, Result}; -use yazi_fs::{provider::{DirReader, FileHolder, Provider, local::Local}, remove_dir_clean}; +use yazi_fs::provider::{DirReader, FileHolder, Provider, local::Local}; use yazi_macro::outln; use super::Dependency; @@ -30,7 +30,7 @@ impl Dependency { self.delete_sources().await?; } - remove_dir_clean(&to.into()).await; + Local.remove_dir_clean(to).await; self.hash = self.hash().await?; res2?; res1?; diff --git a/yazi-cli/src/package/mod.rs b/yazi-cli/src/package/mod.rs index 5ceeedb4..7dde8fb2 100644 --- a/yazi-cli/src/package/mod.rs +++ b/yazi-cli/src/package/mod.rs @@ -3,7 +3,7 @@ yazi_macro::mod_flat!(add delete dependency deploy git hash install package upgrade); use anyhow::Context; -use yazi_vfs::local::Xdg; +use yazi_fs::Xdg; pub(super) fn init() -> anyhow::Result<()> { let root = Xdg::state_dir().join("packages"); diff --git a/yazi-cli/src/package/package.rs b/yazi-cli/src/package/package.rs index a6d79ca0..f07cfe26 100644 --- a/yazi-cli/src/package/package.rs +++ b/yazi-cli/src/package/package.rs @@ -2,9 +2,8 @@ use std::{path::PathBuf, str::FromStr}; use anyhow::{Context, Result, bail}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use yazi_fs::provider::{Provider, local::Local}; +use yazi_fs::{Xdg, provider::{Provider, local::Local}}; use yazi_macro::outln; -use yazi_vfs::local::Xdg; use super::Dependency; diff --git a/yazi-config/Cargo.toml b/yazi-config/Cargo.toml index e1b1fc42..ee023832 100644 --- a/yazi-config/Cargo.toml +++ b/yazi-config/Cargo.toml @@ -14,7 +14,6 @@ yazi-fs = { path = "../yazi-fs", version = "25.9.15" } yazi-macro = { path = "../yazi-macro", version = "25.9.15" } yazi-shared = { path = "../yazi-shared", version = "25.9.15" } yazi-term = { path = "../yazi-term", version = "25.9.15" } -yazi-vfs = { path = "../yazi-vfs", version = "25.9.15" } # External dependencies anyhow = { workspace = true } diff --git a/yazi-config/src/keymap/keymap.rs b/yazi-config/src/keymap/keymap.rs index c4c2bde7..79de6303 100644 --- a/yazi-config/src/keymap/keymap.rs +++ b/yazi-config/src/keymap/keymap.rs @@ -1,9 +1,8 @@ use anyhow::{Context, Result}; use serde::Deserialize; use yazi_codegen::DeserializeOver1; -use yazi_fs::ok_or_not_found; +use yazi_fs::{Xdg, ok_or_not_found}; use yazi_shared::Layer; -use yazi_vfs::local::Xdg; use super::{Chord, KeymapRules}; diff --git a/yazi-config/src/preview/preview.rs b/yazi-config/src/preview/preview.rs index b4825a25..0ae0a0a5 100644 --- a/yazi-config/src/preview/preview.rs +++ b/yazi-config/src/preview/preview.rs @@ -3,9 +3,8 @@ use std::{borrow::Cow, path::PathBuf}; use anyhow::{Context, Result, bail}; use serde::{Deserialize, Serialize}; use yazi_codegen::DeserializeOver2; -use yazi_fs::path::expand_url; +use yazi_fs::{Xdg, path::expand_url}; use yazi_shared::{SStr, timestamp_us, url::Url}; -use yazi_vfs::local::Xdg; use super::PreviewWrap; diff --git a/yazi-config/src/theme/flavor.rs b/yazi-config/src/theme/flavor.rs index 06f5f7e5..bc81c60d 100644 --- a/yazi-config/src/theme/flavor.rs +++ b/yazi-config/src/theme/flavor.rs @@ -4,7 +4,7 @@ use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; use toml::Value; use yazi_codegen::DeserializeOver2; -use yazi_vfs::local::Xdg; +use yazi_fs::Xdg; #[derive(Default, Deserialize, DeserializeOver2, Serialize)] pub struct Flavor { diff --git a/yazi-config/src/theme/theme.rs b/yazi-config/src/theme/theme.rs index 48fc5db9..b3a8369f 100644 --- a/yazi-config/src/theme/theme.rs +++ b/yazi-config/src/theme/theme.rs @@ -3,9 +3,8 @@ use std::path::PathBuf; use anyhow::{Context, Result, anyhow, bail}; use serde::Deserialize; use yazi_codegen::{DeserializeOver1, DeserializeOver2}; -use yazi_fs::{ok_or_not_found, path::expand_url}; +use yazi_fs::{Xdg, ok_or_not_found, path::expand_url}; use yazi_shared::url::UrlBuf; -use yazi_vfs::local::Xdg; use super::{Filetype, Flavor, Icon}; use crate::Style; diff --git a/yazi-config/src/yazi.rs b/yazi-config/src/yazi.rs index 620a7834..ba346ccf 100644 --- a/yazi-config/src/yazi.rs +++ b/yazi-config/src/yazi.rs @@ -1,8 +1,7 @@ use anyhow::{Context, Result}; use serde::Deserialize; use yazi_codegen::DeserializeOver1; -use yazi_fs::ok_or_not_found; -use yazi_vfs::local::Xdg; +use yazi_fs::{Xdg, ok_or_not_found}; use crate::{mgr, open, opener, plugin, popup, preview, tasks, which}; diff --git a/yazi-core/Cargo.toml b/yazi-core/Cargo.toml index 208e3d47..0a0106a8 100644 --- a/yazi-core/Cargo.toml +++ b/yazi-core/Cargo.toml @@ -19,6 +19,7 @@ yazi-plugin = { path = "../yazi-plugin", version = "25.9.15" } yazi-proxy = { path = "../yazi-proxy", version = "25.9.15" } yazi-scheduler = { path = "../yazi-scheduler", version = "25.9.15" } yazi-shared = { path = "../yazi-shared", version = "25.9.15" } +yazi-vfs = { path = "../yazi-vfs", version = "25.9.15" } yazi-watcher = { path = "../yazi-watcher", version = "25.9.15" } yazi-widgets = { path = "../yazi-widgets", version = "25.9.15" } diff --git a/yazi-core/src/tab/preview.rs b/yazi-core/src/tab/preview.rs index 3d8f5fdf..19ecee73 100644 --- a/yazi-core/src/tab/preview.rs +++ b/yazi-core/src/tab/preview.rs @@ -10,6 +10,7 @@ use yazi_macro::render; use yazi_parser::mgr::PreviewLock; use yazi_plugin::{external::Highlighter, isolate}; use yazi_shared::{MIME_DIR, pool::{InternStr, Symbol}, url::UrlBuf}; +use yazi_vfs::{VfsFiles, VfsFilesOp}; #[derive(Default)] pub struct Preview { diff --git a/yazi-fm/src/logs.rs b/yazi-fm/src/logs.rs index 48dfc8c3..7e4be580 100644 --- a/yazi-fm/src/logs.rs +++ b/yazi-fm/src/logs.rs @@ -4,8 +4,8 @@ use anyhow::Context; use crossterm::style::{Color, Print, ResetColor, SetForegroundColor}; use tracing_appender::non_blocking::WorkerGuard; use tracing_subscriber::EnvFilter; +use yazi_fs::Xdg; use yazi_shared::{LOG_LEVEL, RoCell}; -use yazi_vfs::local::Xdg; static _GUARD: RoCell = RoCell::new(); diff --git a/yazi-fm/src/main.rs b/yazi-fm/src/main.rs index 2c321ed2..1d43bf6c 100644 --- a/yazi-fm/src/main.rs +++ b/yazi-fm/src/main.rs @@ -22,6 +22,8 @@ async fn main() -> anyhow::Result<()> { yazi_config::init()?; + yazi_vfs::init(); + yazi_adapter::init()?; yazi_boot::init(); diff --git a/yazi-fs/Cargo.toml b/yazi-fs/Cargo.toml index ddd5a828..011a316f 100644 --- a/yazi-fs/Cargo.toml +++ b/yazi-fs/Cargo.toml @@ -11,22 +11,17 @@ repository = "https://github.com/sxyazi/yazi" [dependencies] yazi-ffi = { path = "../yazi-ffi", version = "25.9.15" } yazi-macro = { path = "../yazi-macro", version = "25.9.15" } -yazi-sftp = { path = "../yazi-sftp", version = "0.1.0" } yazi-shared = { path = "../yazi-shared", version = "25.9.15" } -yazi-vfs = { path = "../yazi-vfs", version = "25.9.15" } # External dependencies anyhow = { workspace = true } arc-swap = "1.7.1" bitflags = { workspace = true } -deadpool = { version = "0.12.3", default-features = false, features = [ "managed", "rt_tokio_1" ] } dirs = { workspace = true } foldhash = { workspace = true } -futures = { workspace = true } hashbrown = { workspace = true } parking_lot = { workspace = true } regex = { workspace = true } -russh = { workspace = true } scopeguard = { workspace = true } serde = { workspace = true } tokio = { workspace = true } @@ -38,7 +33,7 @@ libc = { workspace = true } uzers = { workspace = true } [target.'cfg(windows)'.dependencies] -windows-sys = { version = "0.61.0", features = [ "Win32_Storage_FileSystem" ] } +windows-sys = { version = "0.61.1", features = [ "Win32_Storage_FileSystem" ] } [target.'cfg(target_os = "macos")'.dependencies] core-foundation-sys = { workspace = true } diff --git a/yazi-fs/src/cha/cha.rs b/yazi-fs/src/cha/cha.rs index 875ccc87..43c81430 100644 --- a/yazi-fs/src/cha/cha.rs +++ b/yazi-fs/src/cha/cha.rs @@ -5,7 +5,7 @@ use yazi_macro::{unix_either, win_either}; use yazi_shared::url::Url; use super::ChaKind; -use crate::{cha::{ChaMode, ChaType}, provider}; +use crate::cha::{ChaMode, ChaType}; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Cha { @@ -52,27 +52,6 @@ impl Cha { Self::from_bare(&meta).attach(ChaKind::hidden(name, &meta)) } - #[inline] - pub async fn from_url<'a>(url: impl Into>) -> std::io::Result { - let url = url.into(); - Ok(Self::from_follow(url, provider::symlink_metadata(url).await?).await) - } - - pub async fn from_follow<'a, U>(url: U, mut cha: Self) -> Self - where - U: Into>, - { - let url: Url = url.into(); - let mut retain = cha.kind & (ChaKind::HIDDEN | ChaKind::SYSTEM); - - if cha.is_link() { - retain |= ChaKind::FOLLOW; - cha = provider::metadata(url).await.unwrap_or(cha); - } - - cha.attach(retain) - } - pub fn from_dummy<'a, U>(_url: U, r#type: Option) -> Self where U: Into>, @@ -140,7 +119,7 @@ impl Cha { } #[inline] - fn attach(mut self, kind: ChaKind) -> Self { + pub fn attach(mut self, kind: ChaKind) -> Self { self.kind |= kind; self } diff --git a/yazi-fs/src/cwd.rs b/yazi-fs/src/cwd.rs index eccd5101..f884a338 100644 --- a/yazi-fs/src/cwd.rs +++ b/yazi-fs/src/cwd.rs @@ -3,7 +3,7 @@ use std::{env::{current_dir, set_current_dir}, ops::Deref, path::PathBuf, sync:: use arc_swap::ArcSwap; use yazi_shared::{RoCell, url::UrlBuf}; -use crate::provider; +use crate::FsUrl; pub static CWD: RoCell = RoCell::new(); @@ -30,7 +30,7 @@ impl Default for Cwd { impl Cwd { pub fn path(&self) -> PathBuf { let url = self.0.load(); - provider::cache(url.as_ref()).unwrap_or_else(|| url.loc.to_path()) + url.cache().unwrap_or_else(|| url.loc.to_path()) } pub fn set(&self, url: &UrlBuf) -> bool { @@ -71,7 +71,7 @@ impl Cwd { fn ensure_cache(&self) -> PathBuf { let url = self.0.load(); - if let Some(p) = provider::cache(url.as_ref()) { + if let Some(p) = url.cache() { std::fs::create_dir_all(&p).ok(); p } else { diff --git a/yazi-fs/src/file.rs b/yazi-fs/src/file.rs index b9b75814..9666824c 100644 --- a/yazi-fs/src/file.rs +++ b/yazi-fs/src/file.rs @@ -1,9 +1,8 @@ use std::{ffi::OsStr, hash::{BuildHasher, Hash, Hasher}, ops::Deref, path::{Path, PathBuf}}; -use anyhow::Result; -use yazi_shared::url::{Uri, UrlBuf, UrlCow, Urn}; +use yazi_shared::url::{Uri, UrlBuf, Urn}; -use crate::{cha::{Cha, ChaType}, provider}; +use crate::cha::{Cha, ChaType}; #[derive(Clone, Debug, Default)] pub struct File { @@ -19,22 +18,6 @@ impl Deref for File { } impl File { - #[inline] - pub async fn new(url: impl Into>) -> Result { - let url = url.into(); - let cha = provider::symlink_metadata(&url).await?; - Ok(Self::from_follow(url.into_owned(), cha).await) - } - - #[inline] - pub async fn from_follow(url: UrlBuf, cha: Cha) -> Self { - let link_to = if cha.is_link() { provider::read_link(&url).await.ok() } else { None }; - - let cha = Cha::from_follow(&url, cha).await; - - Self { url, cha, link_to } - } - #[inline] pub fn from_dummy(url: impl Into, r#type: Option) -> Self { let url = url.into(); diff --git a/yazi-fs/src/files.rs b/yazi-fs/src/files.rs index bf4b6138..f014382d 100644 --- a/yazi-fs/src/files.rs +++ b/yazi-fs/src/files.rs @@ -1,11 +1,10 @@ use std::{mem, ops::{Deref, DerefMut, Not}}; use hashbrown::{HashMap, HashSet}; -use tokio::{select, sync::mpsc::{self, UnboundedReceiver}}; -use yazi_shared::{Id, url::{UrlBuf, Urn, UrnBuf}}; +use yazi_shared::{Id, url::{Urn, UrnBuf}}; use super::{FilesSorter, Filter}; -use crate::{FILES_TICKET, File, FilesOp, SortBy, cha::Cha, mounts::PARTITIONS, provider::{self, DirEntry, DirReader, FileHolder}}; +use crate::{FILES_TICKET, File, SortBy}; #[derive(Default)] pub struct Files { @@ -35,70 +34,6 @@ impl DerefMut for Files { impl Files { pub fn new(show_hidden: bool) -> Self { Self { show_hidden, ..Default::default() } } - pub async fn from_dir(dir: &UrlBuf) -> std::io::Result> { - let mut it = provider::read_dir(dir).await?; - let (tx, rx) = mpsc::unbounded_channel(); - - tokio::spawn(async move { - while let Ok(Some(ent)) = it.next().await { - select! { - _ = tx.closed() => break, - result = ent.metadata() => { - let url = ent.url(); - _ = tx.send(match result { - Ok(cha) => File::from_follow(url, cha).await, - Err(_) => File::from_dummy(url, ent.file_type().await.ok()) - }); - } - } - } - }); - Ok(rx) - } - - pub async fn from_dir_bulk(dir: &UrlBuf) -> std::io::Result> { - let mut it = provider::read_dir(dir).await?; - let mut entries = Vec::with_capacity(5000); - while let Ok(Some(entry)) = it.next().await { - entries.push(entry); - } - - let (first, rest) = entries.split_at(entries.len() / 3); - let (second, third) = rest.split_at(entries.len() / 3); - async fn go(entries: &[DirEntry]) -> Vec { - let mut files = Vec::with_capacity(entries.len()); - for ent in entries { - let url = ent.url(); - files.push(match ent.metadata().await { - Ok(cha) => File::from_follow(url, cha).await, - Err(_) => File::from_dummy(url, ent.file_type().await.ok()), - }); - } - files - } - - Ok( - futures::future::join_all([go(first), go(second), go(third)]) - .await - .into_iter() - .flatten() - .collect(), - ) - } - - pub async fn assert_stale(dir: &UrlBuf, cha: Cha) -> Option { - use std::io::ErrorKind; - match Cha::from_url(dir).await { - Ok(c) if !c.is_dir() => FilesOp::issue_error(dir, ErrorKind::NotADirectory).await, - Ok(c) if c.hits(cha) && PARTITIONS.read().heuristic(cha) => {} - Ok(c) => return Some(c), - Err(e) => FilesOp::issue_error(dir, e.kind()).await, - } - None - } -} - -impl Files { pub fn update_full(&mut self, files: Vec) { self.ticket = FILES_TICKET.next(); diff --git a/yazi-fs/src/fns.rs b/yazi-fs/src/fns.rs index 316ae137..543c0acc 100644 --- a/yazi-fs/src/fns.rs +++ b/yazi-fs/src/fns.rs @@ -1,21 +1,5 @@ -use anyhow::Result; -use tokio::{io, select, sync::{mpsc, oneshot}, time}; -use yazi_shared::url::{Component, Url, UrlBuf}; - -use crate::{cha::Cha, provider::{self, DirReader, FileHolder}}; - -#[inline] -pub async fn maybe_exists<'a>(url: impl Into>) -> bool { - match provider::symlink_metadata(url).await { - Ok(_) => true, - Err(e) => e.kind() != io::ErrorKind::NotFound, - } -} - -#[inline] -pub async fn must_be_dir<'a>(url: impl Into>) -> bool { - provider::metadata(url).await.is_ok_and(|m| m.is_dir()) -} +use tokio::io; +use yazi_shared::url::{Component, UrlBuf}; #[inline] pub fn ok_or_not_found(result: io::Result) -> io::Result { @@ -26,74 +10,6 @@ pub fn ok_or_not_found(result: io::Result) -> io::Result { } } -pub fn copy_with_progress( - from: &UrlBuf, - to: &UrlBuf, - cha: Cha, -) -> mpsc::Receiver> { - let (prog_tx, prog_rx) = mpsc::channel(1); - let (done_tx, mut done_rx) = oneshot::channel(); - - tokio::spawn({ - let (from, to) = (from.clone(), to.clone()); - async move { - done_tx.send(provider::copy(&from, &to, cha).await).ok(); - } - }); - - tokio::spawn({ - let (prog_tx, to) = (prog_tx.clone(), to.clone()); - async move { - let mut last = 0; - let mut done = None; - loop { - select! { - res = &mut done_rx => done = Some(res.unwrap()), - _ = prog_tx.closed() => break, - _ = time::sleep(time::Duration::from_secs(3)) => {}, - } - - match done { - Some(Ok(len)) => { - if len > last { - prog_tx.send(Ok(len - last)).await.ok(); - } - prog_tx.send(Ok(0)).await.ok(); - break; - } - Some(Err(e)) => { - prog_tx.send(Err(e)).await.ok(); - break; - } - None => {} - } - - let len = provider::symlink_metadata(&to).await.map(|m| m.len).unwrap_or(0); - if len > last { - prog_tx.send(Ok(len - last)).await.ok(); - last = len; - } - } - } - }); - - prog_rx -} - -pub async fn remove_dir_clean(dir: &UrlBuf) { - let Ok(mut it) = provider::read_dir(dir).await else { return }; - - while let Ok(Some(ent)) = it.next().await { - if ent.file_type().await.is_ok_and(|t| t.is_dir()) { - let url = ent.url(); - Box::pin(remove_dir_clean(&url)).await; - provider::remove_dir(&url).await.ok(); - } - } - - provider::remove_dir(dir).await.ok(); -} - // Find the max common root in a list of urls // e.g. /a/b/c, /a/b/d -> /a/b // /aa/bb/cc, /aa/dd/ee -> /aa @@ -138,7 +54,8 @@ pub fn max_common_root(urls: &[UrlBuf]) -> usize { fn test_max_common_root() { fn assert(input: &[&str], expected: &str) { use std::{ffi::OsStr, str::FromStr}; - let urls: Vec<_> = input.iter().copied().map(UrlBuf::from_str).collect::>().unwrap(); + let urls: Vec<_> = + input.iter().copied().map(UrlBuf::from_str).collect::>().unwrap(); let mut comp = urls[0].components(); for _ in 0..comp.clone().count() - max_common_root(&urls) { diff --git a/yazi-fs/src/lib.rs b/yazi-fs/src/lib.rs index a5dc1fa1..85e9b336 100644 --- a/yazi-fs/src/lib.rs +++ b/yazi-fs/src/lib.rs @@ -2,12 +2,10 @@ yazi_macro::mod_pub!(cha mounts provider path); -yazi_macro::mod_flat!(cwd file files filter fns op sorter sorting stage); +yazi_macro::mod_flat!(cwd file files filter fns op sorter sorting stage url xdg); pub fn init() { CWD.init(<_>::default()); mounts::init(); - - provider::init(); } diff --git a/yazi-fs/src/op.rs b/yazi-fs/src/op.rs index 079f6de2..e7dbbd52 100644 --- a/yazi-fs/src/op.rs +++ b/yazi-fs/src/op.rs @@ -5,7 +5,7 @@ use yazi_macro::relay; use yazi_shared::{Id, Ids, url::{UrlBuf, UrnBuf}}; use super::File; -use crate::{cha::Cha, maybe_exists}; +use crate::cha::Cha; pub static FILES_TICKET: Ids = Ids::new(); @@ -121,17 +121,6 @@ impl FilesOp { } } - pub async fn issue_error(cwd: &UrlBuf, kind: std::io::ErrorKind) { - use std::io::ErrorKind; - if kind != ErrorKind::NotFound { - Self::IOErr(cwd.clone(), kind).emit(); - } else if maybe_exists(cwd).await { - Self::IOErr(cwd.clone(), kind).emit(); - } else if let Some((p, n)) = cwd.pair() { - Self::Deleting(p.into(), [n.into()].into()).emit(); - } - } - pub fn diff_recoverable(&self, contains: impl Fn(&UrlBuf) -> bool) -> (Vec, Vec) { match self { Self::Deleting(cwd, urns) => (urns.iter().map(|u| cwd.join(u)).collect(), vec![]), diff --git a/yazi-fs/src/path/path.rs b/yazi-fs/src/path/path.rs index ab3036aa..fccea699 100644 --- a/yazi-fs/src/path/path.rs +++ b/yazi-fs/src/path/path.rs @@ -1,9 +1,7 @@ -use std::{borrow::Cow, ffi::{OsStr, OsString}, future::Future, io}; +use std::{borrow::Cow, ffi::OsStr}; use yazi_shared::url::UrlBuf; -use crate::provider; - pub fn skip_url(url: &UrlBuf, n: usize) -> Cow<'_, OsStr> { let mut it = url.components(); for _ in 0..n { @@ -14,56 +12,9 @@ pub fn skip_url(url: &UrlBuf, n: usize) -> Cow<'_, OsStr> { it.os_str() } -pub async fn unique_name(u: UrlBuf, append: F) -> io::Result -where - F: Future, -{ - match provider::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 url: UrlBuf, append: bool) -> io::Result { - let Some(stem) = url.stem().map(|s| s.to_owned()) else { - return Err(io::Error::new(io::ErrorKind::InvalidInput, "empty file stem")); - }; - - let dot_ext = url.ext().map_or_else(OsString::new, |e| { - let mut s = OsString::with_capacity(e.len() + 1); - s.push("."); - s.push(e); - s - }); - - let mut i = 1u64; - let mut name = OsString::with_capacity(stem.len() + dot_ext.len() + 5); - loop { - name.clear(); - name.push(&stem); - - if append { - name.push(&dot_ext); - name.push(format!("_{i}")); - } else { - name.push(format!("_{i}")); - name.push(&dot_ext); - } - - url.set_name(&name); - match provider::symlink_metadata(&url).await { - Ok(_) => i += 1, - Err(e) if e.kind() == io::ErrorKind::NotFound => break, - Err(e) => return Err(e), - } - } - - Ok(url) -} - #[cfg(windows)] pub fn backslash_to_slash(p: &std::path::Path) -> Cow<'_, std::path::Path> { + use std::{ffi::OsString, path::PathBuf}; let bytes = p.as_os_str().as_encoded_bytes(); // Fast path to skip if there are no backslashes @@ -80,7 +31,7 @@ pub fn backslash_to_slash(p: &std::path::Path) -> Cow<'_, std::path::Path> { for &b in rest { out.push(if b == b'\\' { b'/' } else { b }); } - Cow::Owned(std::path::PathBuf::from(unsafe { OsString::from_encoded_bytes_unchecked(out) })) + Cow::Owned(PathBuf::from(unsafe { OsString::from_encoded_bytes_unchecked(out) })) } #[cfg(test)] diff --git a/yazi-fs/src/provider/local/gate.rs b/yazi-fs/src/provider/local/gate.rs index 322ee19f..d1bea5c8 100644 --- a/yazi-fs/src/provider/local/gate.rs +++ b/yazi-fs/src/provider/local/gate.rs @@ -15,9 +15,9 @@ impl FileBuilder for Gate { self } - fn cha(&mut self, cha: Cha) -> &mut Self { + fn cha(&mut self, _cha: Cha) -> &mut Self { #[cfg(unix)] - self.0.mode(cha.mode.bits() as _); + self.0.mode(_cha.mode.bits() as _); self } diff --git a/yazi-fs/src/provider/local/metadata.rs b/yazi-fs/src/provider/local/metadata.rs index 3f09be55..4944a2ce 100644 --- a/yazi-fs/src/provider/local/metadata.rs +++ b/yazi-fs/src/provider/local/metadata.rs @@ -1,4 +1,4 @@ -use std::fs::{FileTimes, Permissions}; +use std::fs::FileTimes; use crate::cha::Cha; @@ -31,10 +31,10 @@ impl From for FileTimes { } #[cfg(unix)] -impl From for Permissions { +impl From for std::fs::Permissions { fn from(cha: Cha) -> Self { use std::os::unix::fs::PermissionsExt; - Permissions::from_mode(cha.mode.bits() as _) + std::fs::Permissions::from_mode(cha.mode.bits() as _) } } diff --git a/yazi-fs/src/provider/mod.rs b/yazi-fs/src/provider/mod.rs index 4e1154a9..5db9cfd1 100644 --- a/yazi-fs/src/provider/mod.rs +++ b/yazi-fs/src/provider/mod.rs @@ -1,5 +1,3 @@ -yazi_macro::mod_pub!(local sftp); +yazi_macro::mod_pub!(local); -yazi_macro::mod_flat!(calculator dir_entry gate provider providers read_dir rw_file traits); - -pub fn init() { sftp::init(); } +yazi_macro::mod_flat!(traits); diff --git a/yazi-fs/src/provider/traits.rs b/yazi-fs/src/provider/traits.rs index a6649429..1574b864 100644 --- a/yazi-fs/src/provider/traits.rs +++ b/yazi-fs/src/provider/traits.rs @@ -142,6 +142,30 @@ pub trait Provider { } } + fn remove_dir_clean

(&self, root: P) -> impl Future + where + P: AsRef, + { + let root = root.as_ref().to_path_buf(); + + async move { + let mut stack = vec![(root, false)]; + + while let Some((dir, visited)) = stack.pop() { + if visited { + self.remove_dir(&dir).await.ok(); + } else if let Ok(mut it) = self.read_dir(&dir).await { + stack.push((dir, true)); + while let Ok(Some(ent)) = it.next().await { + if ent.file_type().await.is_ok_and(|t| t.is_dir()) { + stack.push((ent.path(), false)); + } + } + } + } + } + } + fn remove_file

(&self, path: P) -> impl Future> where P: AsRef; diff --git a/yazi-fs/src/url.rs b/yazi-fs/src/url.rs new file mode 100644 index 00000000..3b4137ad --- /dev/null +++ b/yazi-fs/src/url.rs @@ -0,0 +1,32 @@ +use std::path::PathBuf; + +use twox_hash::XxHash3_128; +use yazi_shared::{scheme::SchemeRef, url::{Url, UrlBuf}}; + +use crate::Xdg; + +pub trait FsUrl { + fn cache(&self) -> Option; +} + +impl FsUrl for Url<'_> { + fn cache(&self) -> Option { + match self.scheme { + SchemeRef::Regular | SchemeRef::Search(_) => None, + SchemeRef::Archive(name) => Some( + Xdg::cache_dir() + .join(format!("archive-{}", yazi_shared::url::Encode::domain(name))) + .join(format!("{:x}", XxHash3_128::oneshot(self.loc.bytes()))), + ), + SchemeRef::Sftp(name) => Some( + Xdg::cache_dir() + .join(format!("sftp-{}", yazi_shared::url::Encode::domain(name))) + .join(format!("{:x}", XxHash3_128::oneshot(self.loc.bytes()))), + ), + } + } +} + +impl FsUrl for UrlBuf { + fn cache(&self) -> Option { self.as_url().cache() } +} diff --git a/yazi-vfs/src/local/xdg.rs b/yazi-fs/src/xdg.rs similarity index 100% rename from yazi-vfs/src/local/xdg.rs rename to yazi-fs/src/xdg.rs diff --git a/yazi-plugin/Cargo.toml b/yazi-plugin/Cargo.toml index d82c7bc2..ba258653 100644 --- a/yazi-plugin/Cargo.toml +++ b/yazi-plugin/Cargo.toml @@ -23,6 +23,7 @@ yazi-macro = { path = "../yazi-macro", version = "25.9.15" } yazi-parser = { path = "../yazi-parser", version = "25.9.15" } yazi-proxy = { path = "../yazi-proxy", version = "25.9.15" } yazi-shared = { path = "../yazi-shared", version = "25.9.15" } +yazi-vfs = { path = "../yazi-vfs", version = "25.9.15" } yazi-widgets = { path = "../yazi-widgets", version = "25.9.15" } # External dependencies @@ -49,7 +50,7 @@ libc = { workspace = true } uzers = { workspace = true } [target."cfg(windows)".dependencies] -windows-sys = { version = "0.61.0", features = [ "Win32_System_JobObjects" ] } +windows-sys = { version = "0.61.1", features = [ "Win32_System_JobObjects" ] } [target.'cfg(target_os = "macos")'.dependencies] crossterm = { workspace = true, features = [ "use-dev-tty", "libc" ] } diff --git a/yazi-plugin/preset/plugins/mime.lua b/yazi-plugin/preset/plugins/mime.lua index d2a3f960..4c7f45ef 100644 --- a/yazi-plugin/preset/plugins/mime.lua +++ b/yazi-plugin/preset/plugins/mime.lua @@ -5,7 +5,7 @@ local M = {} local function match_mimetype(line) for _, pat in ipairs(TYPE_PATS) do - local typ, sub = line:match(string.format("(%s/)([+-.a-z0-9]+)%%s+$", pat)) + local typ, sub = line:match(string.format("(%s/)([+-.a-zA-Z0-9]+)%%s+$", pat)) if not sub then elseif line:find(typ .. sub, 1, true) == 1 then return typ:gsub("^x%-", "", 1) .. sub:gsub("^x%-", "", 1):gsub("^vnd%.", "", 1) diff --git a/yazi-plugin/src/bindings/calculator.rs b/yazi-plugin/src/bindings/calculator.rs index 2c9f9163..540b119c 100644 --- a/yazi-plugin/src/bindings/calculator.rs +++ b/yazi-plugin/src/bindings/calculator.rs @@ -3,7 +3,7 @@ use yazi_binding::Error; pub enum SizeCalculator { Local(yazi_fs::provider::local::SizeCalculator), - Remote(yazi_fs::provider::SizeCalculator), + Remote(yazi_vfs::provider::SizeCalculator), } impl UserData for SizeCalculator { diff --git a/yazi-plugin/src/external/fd.rs b/yazi-plugin/src/external/fd.rs index 2ac725f2..971a02ac 100644 --- a/yazi-plugin/src/external/fd.rs +++ b/yazi-plugin/src/external/fd.rs @@ -4,6 +4,7 @@ use anyhow::Result; use tokio::{io::{AsyncBufReadExt, BufReader}, process::{Child, Command}, sync::mpsc::{self, UnboundedReceiver}}; use yazi_fs::File; use yazi_shared::url::UrlBuf; +use yazi_vfs::VfsFile; pub struct FdOpt { pub cwd: UrlBuf, diff --git a/yazi-plugin/src/external/rg.rs b/yazi-plugin/src/external/rg.rs index 8fde2a2e..1bdaee94 100644 --- a/yazi-plugin/src/external/rg.rs +++ b/yazi-plugin/src/external/rg.rs @@ -4,6 +4,7 @@ use anyhow::{Result, bail}; use tokio::{io::{AsyncBufReadExt, BufReader}, process::Command, sync::mpsc::{self, UnboundedReceiver}}; use yazi_fs::File; use yazi_shared::url::UrlBuf; +use yazi_vfs::VfsFile; pub struct RgOpt { pub cwd: UrlBuf, diff --git a/yazi-plugin/src/external/rga.rs b/yazi-plugin/src/external/rga.rs index ae450b89..d0409b51 100644 --- a/yazi-plugin/src/external/rga.rs +++ b/yazi-plugin/src/external/rga.rs @@ -4,6 +4,7 @@ use anyhow::{Result, bail}; use tokio::{io::{AsyncBufReadExt, BufReader}, process::Command, sync::mpsc::{self, UnboundedReceiver}}; use yazi_fs::File; use yazi_shared::url::UrlBuf; +use yazi_vfs::VfsFile; pub struct RgaOpt { pub cwd: UrlBuf, diff --git a/yazi-plugin/src/fs/fs.rs b/yazi-plugin/src/fs/fs.rs index 0ba2cd83..064ec03c 100644 --- a/yazi-plugin/src/fs/fs.rs +++ b/yazi-plugin/src/fs/fs.rs @@ -3,8 +3,9 @@ use std::str::FromStr; use mlua::{ExternalError, Function, IntoLua, IntoLuaMulti, Lua, Table, Value}; use yazi_binding::{Cha, Composer, ComposerGet, ComposerSet, Error, File, Url, UrlRef}; use yazi_config::Pattern; -use yazi_fs::{mounts::PARTITIONS, provider::{self, DirReader, FileHolder}, remove_dir_clean}; +use yazi_fs::{mounts::PARTITIONS, provider::{DirReader, FileHolder}}; use yazi_shared::url::UrlCow; +use yazi_vfs::{VfsFile, provider}; use crate::bindings::SizeCalculator; @@ -93,7 +94,7 @@ fn remove(lua: &Lua) -> mlua::Result { b"file" => provider::remove_file(&*url).await, b"dir" => provider::remove_dir(&*url).await, b"dir_all" => provider::remove_dir_all(&*url).await, - b"dir_clean" => Ok(remove_dir_clean(&url).await), + b"dir_clean" => provider::remove_dir_clean(&*url).await, _ => Err("Removal type must be 'file', 'dir', 'dir_all', or 'dir_clean'".into_lua_err())?, }; @@ -148,9 +149,9 @@ fn read_dir(lua: &Lua) -> mlua::Result { fn calc_size(lua: &Lua) -> mlua::Result { lua.create_async_function(|lua, url: UrlRef| async move { let it = if let Some(path) = url.as_path() { - provider::local::SizeCalculator::new(path).await.map(SizeCalculator::Local) + yazi_fs::provider::local::SizeCalculator::new(path).await.map(SizeCalculator::Local) } else { - provider::SizeCalculator::new(&*url).await.map(SizeCalculator::Remote) + yazi_vfs::provider::SizeCalculator::new(&*url).await.map(SizeCalculator::Remote) }; match it { @@ -173,7 +174,7 @@ fn expand_url(lua: &Lua) -> mlua::Result { fn unique_name(lua: &Lua) -> mlua::Result { lua.create_async_function(|lua, url: UrlRef| async move { - match yazi_fs::path::unique_name(url.clone(), async { false }).await { + match yazi_vfs::unique_name(url.clone(), async { false }).await { Ok(u) => Url::new(u).into_lua_multi(&lua), Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua), } diff --git a/yazi-scheduler/Cargo.toml b/yazi-scheduler/Cargo.toml index 25db85a5..0943a4b3 100644 --- a/yazi-scheduler/Cargo.toml +++ b/yazi-scheduler/Cargo.toml @@ -17,6 +17,7 @@ yazi-parser = { path = "../yazi-parser", version = "25.9.15" } yazi-plugin = { path = "../yazi-plugin", version = "25.9.15" } yazi-proxy = { path = "../yazi-proxy", version = "25.9.15" } yazi-shared = { path = "../yazi-shared", version = "25.9.15" } +yazi-vfs = { path = "../yazi-vfs", version = "25.9.15" } # External dependencies anyhow = { workspace = true } diff --git a/yazi-scheduler/src/file/file.rs b/yazi-scheduler/src/file/file.rs index 6be89955..733ad83d 100644 --- a/yazi-scheduler/src/file/file.rs +++ b/yazi-scheduler/src/file/file.rs @@ -4,8 +4,9 @@ use anyhow::{Result, anyhow}; use tokio::{io::{self, ErrorKind::{AlreadyExists, NotFound}}, sync::mpsc}; use tracing::warn; use yazi_config::YAZI; -use yazi_fs::{cha::Cha, copy_with_progress, maybe_exists, ok_or_not_found, path::{path_relative_to, skip_url}, provider::{self, DirEntry, DirReader, FileHolder}}; +use yazi_fs::{cha::Cha, ok_or_not_found, path::{path_relative_to, skip_url}, provider::{DirReader, FileHolder}}; use yazi_shared::url::{Url, UrlBuf, UrlCow}; +use yazi_vfs::{VfsCha, copy_with_progress, maybe_exists, provider::{self, DirEntry}}; use super::{FileInDelete, FileInHardlink, FileInLink, FileInPaste, FileInTrash}; use crate::{LOW, NORMAL, TaskIn, TaskOp, TaskOps, file::{FileOutDelete, FileOutDeleteDo, FileOutHardlink, FileOutHardlinkDo, FileOutLink, FileOutPaste, FileOutPasteDo, FileOutTrash}}; diff --git a/yazi-scheduler/src/prework/prework.rs b/yazi-scheduler/src/prework/prework.rs index 866b0a1c..2750da77 100644 --- a/yazi-scheduler/src/prework/prework.rs +++ b/yazi-scheduler/src/prework/prework.rs @@ -8,9 +8,10 @@ use tokio::sync::mpsc; use tokio_util::sync::CancellationToken; use tracing::error; use yazi_config::Priority; -use yazi_fs::{FilesOp, provider}; +use yazi_fs::FilesOp; use yazi_plugin::isolate; use yazi_shared::{event::CmdCow, url::UrlBuf}; +use yazi_vfs::provider; use super::{PreworkInFetch, PreworkInLoad, PreworkInSize}; use crate::{HIGH, NORMAL, TaskIn, TaskOp, TaskOps, prework::{PreworkOutFetch, PreworkOutLoad, PreworkOutSize}}; diff --git a/yazi-scheduler/src/process/shell.rs b/yazi-scheduler/src/process/shell.rs index 15493872..fd33f8c6 100644 --- a/yazi-scheduler/src/process/shell.rs +++ b/yazi-scheduler/src/process/shell.rs @@ -2,7 +2,7 @@ use std::{borrow::Cow, ffi::OsString, process::Stdio}; use anyhow::{Result, bail}; use tokio::process::{Child, Command}; -use yazi_fs::provider; +use yazi_fs::FsUrl; use yazi_shared::url::UrlBuf; pub(crate) struct ShellOpt { @@ -30,7 +30,7 @@ pub(crate) async fn shell(opt: ShellOpt) -> Result { tokio::task::spawn_blocking(move || { let cwd: Cow<_> = if let Some(path) = opt.cwd.as_path() { path.into() - } else if let Some(cache) = provider::cache(&opt.cwd) { + } else if let Some(cache) = opt.cwd.cache() { std::fs::create_dir_all(&cache).ok(); cache.into() } else { diff --git a/yazi-scheduler/src/scheduler.rs b/yazi-scheduler/src/scheduler.rs index 55dc0f26..73862bfa 100644 --- a/yazi-scheduler/src/scheduler.rs +++ b/yazi-scheduler/src/scheduler.rs @@ -6,10 +6,10 @@ use parking_lot::Mutex; use tokio::{select, sync::{mpsc::{self, UnboundedReceiver}, oneshot}, task::JoinHandle}; use yazi_config::{YAZI, plugin::{Fetcher, Preloader}}; use yazi_dds::Pump; -use yazi_fs::{must_be_dir, path::unique_name, provider, remove_dir_clean}; use yazi_parser::{app::PluginOpt, tasks::ProcessExecOpt}; use yazi_proxy::TasksProxy; use yazi_shared::{Id, Throttle, url::UrlBuf}; +use yazi_vfs::{must_be_dir, provider, unique_name}; use super::{Ongoing, TaskOp}; use crate::{HIGH, LOW, NORMAL, TaskIn, TaskOps, TaskOut, file::{File, FileInDelete, FileInHardlink, FileInLink, FileInPaste, FileInTrash, FileOutDelete, FileOutHardlink, FileOutPaste, FileProgDelete, FileProgHardlink, FileProgLink, FileProgPaste, FileProgTrash}, plugin::{Plugin, PluginInEntry, PluginProgEntry}, prework::{Prework, PreworkInFetch, PreworkInLoad, PreworkInSize, PreworkProgFetch, PreworkProgLoad, PreworkProgSize}, process::{Process, ProcessInBg, ProcessInBlock, ProcessInOrphan, ProcessOutBg, ProcessOutBlock, ProcessOutOrphan, ProcessProgBg, ProcessProgBlock, ProcessProgOrphan}}; @@ -89,7 +89,7 @@ impl Scheduler { move |canceled| async move { if !canceled { - remove_dir_clean(&from).await; + provider::remove_dir_clean(&from).await.ok(); Pump::push_move(from, to); } ops.out(id, FileOutPaste::Clean); diff --git a/yazi-shared/Cargo.toml b/yazi-shared/Cargo.toml index 23c448e5..1d9243bb 100644 --- a/yazi-shared/Cargo.toml +++ b/yazi-shared/Cargo.toml @@ -18,7 +18,7 @@ crossterm = { workspace = true } foldhash = { workspace = true } futures = { workspace = true } hashbrown = { workspace = true } -memchr = "2.7.5" +memchr = "2.7.6" ordered-float = { workspace = true } parking_lot = { workspace = true } percent-encoding = "2.3.2" @@ -30,7 +30,7 @@ libc = { workspace = true } uzers = { workspace = true } [target.'cfg(windows)'.dependencies] -windows-sys = { version = "0.61.0", features = [ "Win32_UI_Shell" ] } +windows-sys = { version = "0.61.1", features = [ "Win32_UI_Shell" ] } [target.'cfg(target_os = "macos")'.dependencies] crossterm = { workspace = true, features = [ "use-dev-tty", "libc" ] } diff --git a/yazi-term/Cargo.toml b/yazi-term/Cargo.toml index 99ab5237..a9261b2b 100644 --- a/yazi-term/Cargo.toml +++ b/yazi-term/Cargo.toml @@ -26,4 +26,4 @@ libc = { workspace = true } crossterm = { workspace = true, features = [ "use-dev-tty", "libc" ] } [target.'cfg(windows)'.dependencies] -windows-sys = { version = "0.61.0", features = [ "Win32_Globalization", "Win32_Storage_FileSystem", "Win32_System_IO", "Win32_System_Console", "Win32_System_Threading", "Win32_Security" ] } +windows-sys = { version = "0.61.1", features = [ "Win32_Globalization", "Win32_Storage_FileSystem", "Win32_System_IO", "Win32_System_Console", "Win32_System_Threading", "Win32_Security" ] } diff --git a/yazi-vfs/Cargo.toml b/yazi-vfs/Cargo.toml index 909b45e7..194b7d37 100644 --- a/yazi-vfs/Cargo.toml +++ b/yazi-vfs/Cargo.toml @@ -9,15 +9,22 @@ homepage = "https://yazi-rs.github.io" repository = "https://github.com/sxyazi/yazi" [dependencies] +yazi-fs = { path = "../yazi-fs", version = "25.9.15" } yazi-macro = { path = "../yazi-macro", version = "25.9.15" } +yazi-sftp = { path = "../yazi-sftp", version = "0.1.0" } yazi-shared = { path = "../yazi-shared", version = "25.9.15" } # External dependencies -dirs = { workspace = true } -hashbrown = { workspace = true } -serde = { workspace = true } -tokio = { workspace = true } -toml = { workspace = true } +anyhow = { workspace = true } +deadpool = { version = "0.12.3", default-features = false, features = [ "managed", "rt_tokio_1" ] } +futures = { workspace = true } +hashbrown = { workspace = true } +parking_lot = { workspace = true } +russh = { workspace = true } +serde = { workspace = true } +tokio = { workspace = true } +toml = { workspace = true } +tracing = { workspace = true } [target."cfg(unix)".dependencies] uzers = { workspace = true } diff --git a/yazi-vfs/src/cha.rs b/yazi-vfs/src/cha.rs new file mode 100644 index 00000000..69312f2a --- /dev/null +++ b/yazi-vfs/src/cha.rs @@ -0,0 +1,37 @@ +use std::io; + +use yazi_fs::cha::{Cha, ChaKind}; +use yazi_shared::url::Url; + +use crate::provider; + +pub trait VfsCha: Sized { + fn from_url<'a>(url: impl Into>) -> impl Future>; + + fn from_follow<'a, U>(url: U, cha: Self) -> impl Future + where + U: Into>; +} + +impl VfsCha for Cha { + #[inline] + async fn from_url<'a>(url: impl Into>) -> io::Result { + let url = url.into(); + Ok(Self::from_follow(url, provider::symlink_metadata(url).await?).await) + } + + async fn from_follow<'a, U>(url: U, mut cha: Self) -> Self + where + U: Into>, + { + let url: Url = url.into(); + let mut retain = cha.kind & (ChaKind::HIDDEN | ChaKind::SYSTEM); + + if cha.is_link() { + retain |= ChaKind::FOLLOW; + cha = provider::metadata(url).await.unwrap_or(cha); + } + + cha.attach(retain) + } +} diff --git a/yazi-vfs/src/config/vfs.rs b/yazi-vfs/src/config/vfs.rs index 2530574c..715a34a0 100644 --- a/yazi-vfs/src/config/vfs.rs +++ b/yazi-vfs/src/config/vfs.rs @@ -3,10 +3,10 @@ use std::io; use hashbrown::HashMap; use serde::{Deserialize, Serialize}; use tokio::sync::OnceCell; +use yazi_fs::Xdg; use yazi_macro::ok_or_not_found; use super::Provider; -use crate::local::Xdg; #[derive(Deserialize, Serialize)] pub struct Vfs { diff --git a/yazi-vfs/src/file.rs b/yazi-vfs/src/file.rs new file mode 100644 index 00000000..ab022361 --- /dev/null +++ b/yazi-vfs/src/file.rs @@ -0,0 +1,29 @@ +use anyhow::Result; +use yazi_fs::{File, cha::Cha}; +use yazi_shared::url::{UrlBuf, UrlCow}; + +use crate::{VfsCha, provider}; + +pub trait VfsFile: Sized { + fn new<'a>(url: impl Into>) -> impl Future>; + + fn from_follow(url: UrlBuf, cha: Cha) -> impl Future; +} + +impl VfsFile for File { + #[inline] + async fn new<'a>(url: impl Into>) -> Result { + let url = url.into(); + let cha = provider::symlink_metadata(&url).await?; + Ok(Self::from_follow(url.into_owned(), cha).await) + } + + #[inline] + async fn from_follow(url: UrlBuf, cha: Cha) -> Self { + let link_to = if cha.is_link() { provider::read_link(&url).await.ok() } else { None }; + + let cha = Cha::from_follow(&url, cha).await; + + Self { url, cha, link_to } + } +} diff --git a/yazi-vfs/src/files.rs b/yazi-vfs/src/files.rs new file mode 100644 index 00000000..f8c82469 --- /dev/null +++ b/yazi-vfs/src/files.rs @@ -0,0 +1,79 @@ +use std::io; + +use tokio::{select, sync::mpsc::{self, UnboundedReceiver}}; +use yazi_fs::{File, Files, FilesOp, cha::Cha, mounts::PARTITIONS, provider::{DirReader, FileHolder}}; +use yazi_shared::url::UrlBuf; + +use crate::{VfsCha, VfsFile, VfsFilesOp, provider::{self, DirEntry}}; + +pub trait VfsFiles { + fn from_dir(dir: &UrlBuf) -> impl Future>>; + + fn from_dir_bulk(dir: &UrlBuf) -> impl Future>>; + + fn assert_stale(dir: &UrlBuf, cha: Cha) -> impl Future>; +} + +impl VfsFiles for Files { + async fn from_dir(dir: &UrlBuf) -> std::io::Result> { + let mut it = provider::read_dir(dir).await?; + let (tx, rx) = mpsc::unbounded_channel(); + + tokio::spawn(async move { + while let Ok(Some(ent)) = it.next().await { + select! { + _ = tx.closed() => break, + result = ent.metadata() => { + let url = ent.url(); + _ = tx.send(match result { + Ok(cha) => File::from_follow(url, cha).await, + Err(_) => File::from_dummy(url, ent.file_type().await.ok()) + }); + } + } + } + }); + Ok(rx) + } + + async fn from_dir_bulk(dir: &UrlBuf) -> std::io::Result> { + let mut it = provider::read_dir(dir).await?; + let mut entries = Vec::with_capacity(5000); + while let Ok(Some(entry)) = it.next().await { + entries.push(entry); + } + + let (first, rest) = entries.split_at(entries.len() / 3); + let (second, third) = rest.split_at(entries.len() / 3); + async fn go(entries: &[DirEntry]) -> Vec { + let mut files = Vec::with_capacity(entries.len()); + for ent in entries { + let url = ent.url(); + files.push(match ent.metadata().await { + Ok(cha) => File::from_follow(url, cha).await, + Err(_) => File::from_dummy(url, ent.file_type().await.ok()), + }); + } + files + } + + Ok( + futures::future::join_all([go(first), go(second), go(third)]) + .await + .into_iter() + .flatten() + .collect(), + ) + } + + async fn assert_stale(dir: &UrlBuf, cha: Cha) -> Option { + use std::io::ErrorKind; + match Cha::from_url(dir).await { + Ok(c) if !c.is_dir() => FilesOp::issue_error(dir, ErrorKind::NotADirectory).await, + Ok(c) if c.hits(cha) && PARTITIONS.read().heuristic(cha) => {} + Ok(c) => return Some(c), + Err(e) => FilesOp::issue_error(dir, e.kind()).await, + } + None + } +} diff --git a/yazi-vfs/src/fns.rs b/yazi-vfs/src/fns.rs new file mode 100644 index 00000000..6fc1d8ad --- /dev/null +++ b/yazi-vfs/src/fns.rs @@ -0,0 +1,122 @@ +use std::{ffi::OsString, io}; + +use tokio::{select, sync::{mpsc, oneshot}}; +use yazi_fs::cha::Cha; +use yazi_shared::url::{Url, UrlBuf}; + +use crate::provider; + +#[inline] +pub async fn maybe_exists<'a>(url: impl Into>) -> bool { + match provider::symlink_metadata(url).await { + Ok(_) => true, + Err(e) => e.kind() != io::ErrorKind::NotFound, + } +} + +#[inline] +pub async fn must_be_dir<'a>(url: impl Into>) -> bool { + provider::metadata(url).await.is_ok_and(|m| m.is_dir()) +} + +pub async fn unique_name(u: UrlBuf, append: F) -> io::Result +where + F: Future, +{ + match provider::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 url: UrlBuf, append: bool) -> io::Result { + let Some(stem) = url.stem().map(|s| s.to_owned()) else { + return Err(io::Error::new(io::ErrorKind::InvalidInput, "empty file stem")); + }; + + let dot_ext = url.ext().map_or_else(OsString::new, |e| { + let mut s = OsString::with_capacity(e.len() + 1); + s.push("."); + s.push(e); + s + }); + + let mut i = 1u64; + let mut name = OsString::with_capacity(stem.len() + dot_ext.len() + 5); + loop { + name.clear(); + name.push(&stem); + + if append { + name.push(&dot_ext); + name.push(format!("_{i}")); + } else { + name.push(format!("_{i}")); + name.push(&dot_ext); + } + + url.set_name(&name); + match provider::symlink_metadata(&url).await { + Ok(_) => i += 1, + Err(e) if e.kind() == io::ErrorKind::NotFound => break, + Err(e) => return Err(e), + } + } + + Ok(url) +} + +pub fn copy_with_progress( + from: &UrlBuf, + to: &UrlBuf, + cha: Cha, +) -> mpsc::Receiver> { + let (prog_tx, prog_rx) = mpsc::channel(1); + let (done_tx, mut done_rx) = oneshot::channel(); + + tokio::spawn({ + let (from, to) = (from.clone(), to.clone()); + async move { + done_tx.send(provider::copy(&from, &to, cha).await).ok(); + } + }); + + tokio::spawn({ + let (prog_tx, to) = (prog_tx.clone(), to.clone()); + async move { + let mut last = 0; + let mut done = None; + loop { + select! { + res = &mut done_rx => done = Some(res.unwrap()), + _ = prog_tx.closed() => break, + _ = tokio::time::sleep(std::time::Duration::from_secs(3)) => {}, + } + + match done { + Some(Ok(len)) => { + if len > last { + prog_tx.send(Ok(len - last)).await.ok(); + } + prog_tx.send(Ok(0)).await.ok(); + break; + } + Some(Err(e)) => { + prog_tx.send(Err(e)).await.ok(); + break; + } + None => {} + } + + let len = provider::symlink_metadata(&to).await.map(|m| m.len).unwrap_or(0); + if len > last { + prog_tx.send(Ok(len - last)).await.ok(); + last = len; + } + } + } + }); + + prog_rx +} diff --git a/yazi-vfs/src/lib.rs b/yazi-vfs/src/lib.rs index b899e780..498f440f 100644 --- a/yazi-vfs/src/lib.rs +++ b/yazi-vfs/src/lib.rs @@ -1 +1,5 @@ -yazi_macro::mod_pub!(config local); +yazi_macro::mod_pub!(config provider); + +yazi_macro::mod_flat!(cha file files fns op); + +pub fn init() { provider::init(); } diff --git a/yazi-vfs/src/local/mod.rs b/yazi-vfs/src/local/mod.rs deleted file mode 100644 index f680801c..00000000 --- a/yazi-vfs/src/local/mod.rs +++ /dev/null @@ -1 +0,0 @@ -yazi_macro::mod_flat!(xdg); diff --git a/yazi-vfs/src/op.rs b/yazi-vfs/src/op.rs new file mode 100644 index 00000000..f8469abd --- /dev/null +++ b/yazi-vfs/src/op.rs @@ -0,0 +1,23 @@ +use std::io; + +use yazi_fs::FilesOp; +use yazi_shared::url::UrlBuf; + +use crate::maybe_exists; + +pub trait VfsFilesOp { + fn issue_error(cwd: &UrlBuf, kind: io::ErrorKind) -> impl Future; +} + +impl VfsFilesOp for FilesOp { + async fn issue_error(cwd: &UrlBuf, kind: std::io::ErrorKind) { + use std::io::ErrorKind; + if kind != ErrorKind::NotFound { + Self::IOErr(cwd.clone(), kind).emit(); + } else if maybe_exists(cwd).await { + Self::IOErr(cwd.clone(), kind).emit(); + } else if let Some((p, n)) = cwd.pair() { + Self::Deleting(p.into(), [n.into()].into()).emit(); + } + } +} diff --git a/yazi-fs/src/provider/calculator.rs b/yazi-vfs/src/provider/calculator.rs similarity index 91% rename from yazi-fs/src/provider/calculator.rs rename to yazi-vfs/src/provider/calculator.rs index bd7adbd4..d5397aad 100644 --- a/yazi-fs/src/provider/calculator.rs +++ b/yazi-vfs/src/provider/calculator.rs @@ -1,8 +1,9 @@ use std::{collections::VecDeque, io, time::{Duration, Instant}}; +use yazi_fs::provider::{DirReader, FileHolder}; use yazi_shared::{Either, url::{Url, UrlBuf}}; -use crate::provider::{self, DirReader, FileHolder, ReadDir}; +use super::ReadDir; pub enum SizeCalculator { File(Option), @@ -15,7 +16,7 @@ impl SizeCalculator { U: Into>, { let url: Url = url.into(); - let cha = provider::symlink_metadata(url).await?; + let cha = super::symlink_metadata(url).await?; Ok(if cha.is_dir() { Self::Dir(VecDeque::from([Either::Left(url.to_owned())])) } else { @@ -59,7 +60,7 @@ impl SizeCalculator { let front = buf.front_mut()?; if let Either::Left(p) = front { - *front = match provider::read_dir(p).await { + *front = match super::read_dir(p).await { Ok(it) => Either::Right(it), Err(_) => pop_and_continue!(), }; diff --git a/yazi-fs/src/provider/dir_entry.rs b/yazi-vfs/src/provider/dir_entry.rs similarity index 88% rename from yazi-fs/src/provider/dir_entry.rs rename to yazi-vfs/src/provider/dir_entry.rs index 169c8c42..574fa1e4 100644 --- a/yazi-fs/src/provider/dir_entry.rs +++ b/yazi-vfs/src/provider/dir_entry.rs @@ -1,12 +1,11 @@ use std::{borrow::Cow, ffi::OsStr, io, sync::Arc}; +use yazi_fs::{cha::{Cha, ChaType}, provider::FileHolder}; use yazi_shared::url::UrlBuf; -use crate::{cha::{Cha, ChaType}, provider::FileHolder}; - pub enum DirEntry { - Regular(super::local::DirEntry), - Search((Arc, super::local::DirEntry)), + Regular(yazi_fs::provider::local::DirEntry), + Search((Arc, yazi_fs::provider::local::DirEntry)), Sftp((Arc, super::sftp::DirEntry)), } diff --git a/yazi-fs/src/provider/gate.rs b/yazi-vfs/src/provider/gate.rs similarity index 85% rename from yazi-fs/src/provider/gate.rs rename to yazi-vfs/src/provider/gate.rs index efe979d5..ec2c2a1a 100644 --- a/yazi-fs/src/provider/gate.rs +++ b/yazi-vfs/src/provider/gate.rs @@ -1,16 +1,15 @@ use std::{io, path::Path}; +use yazi_fs::{cha::Cha, provider::FileBuilder}; use yazi_shared::scheme::SchemeRef; -use crate::{cha::Cha, provider::FileBuilder}; - pub enum Gate { - Local(super::local::Gate), + Local(yazi_fs::provider::local::Gate), Sftp(super::sftp::Gate), } -impl From for Gate { - fn from(value: super::local::Gate) -> Self { Self::Local(value) } +impl From for Gate { + fn from(value: yazi_fs::provider::local::Gate) -> Self { Self::Local(value) } } impl From for Gate { @@ -54,7 +53,9 @@ impl FileBuilder for Gate { async fn new(scheme: SchemeRef<'_>) -> io::Result { Ok(match scheme { - SchemeRef::Regular | SchemeRef::Search(_) => super::local::Gate::new(scheme).await?.into(), + SchemeRef::Regular | SchemeRef::Search(_) => { + yazi_fs::provider::local::Gate::new(scheme).await?.into() + } SchemeRef::Archive(_) => { Err(io::Error::new(io::ErrorKind::Unsupported, "Unsupported filesystem: archive"))? } diff --git a/yazi-vfs/src/provider/mod.rs b/yazi-vfs/src/provider/mod.rs new file mode 100644 index 00000000..913efe06 --- /dev/null +++ b/yazi-vfs/src/provider/mod.rs @@ -0,0 +1,5 @@ +yazi_macro::mod_pub!(sftp); + +yazi_macro::mod_flat!(calculator dir_entry gate provider providers read_dir rw_file); + +pub(super) fn init() { sftp::init(); } diff --git a/yazi-fs/src/provider/provider.rs b/yazi-vfs/src/provider/provider.rs similarity index 89% rename from yazi-fs/src/provider/provider.rs rename to yazi-vfs/src/provider/provider.rs index 58d6e1b6..036f8556 100644 --- a/yazi-fs/src/provider/provider.rs +++ b/yazi-vfs/src/provider/provider.rs @@ -1,11 +1,10 @@ use std::{io, path::{Path, PathBuf}}; use tokio::io::{BufReader, BufWriter}; -use twox_hash::XxHash3_128; +use yazi_fs::{cha::Cha, provider::{Provider, local::Local}}; use yazi_shared::{scheme::SchemeRef, url::{Url, UrlBuf, UrlCow}}; -use yazi_vfs::local::Xdg; -use crate::{cha::Cha, provider::{Provider, Providers, ReadDir, RwFile, local::{self, Local}}}; +use super::{Providers, ReadDir, RwFile}; pub async fn absolute<'a, U>(url: U) -> io::Result> where @@ -15,33 +14,13 @@ where Providers::new(url).await?.absolute(url).await } -pub fn cache<'a, U>(url: U) -> Option -where - U: Into>, -{ - let url: Url = url.into(); - match url.scheme { - SchemeRef::Regular | SchemeRef::Search(_) => None, - SchemeRef::Archive(name) => Some( - Xdg::cache_dir() - .join(format!("archive-{}", yazi_shared::url::Encode::domain(name))) - .join(format!("{:x}", XxHash3_128::oneshot(url.loc.bytes()))), - ), - SchemeRef::Sftp(name) => Some( - Xdg::cache_dir() - .join(format!("sftp-{}", yazi_shared::url::Encode::domain(name))) - .join(format!("{:x}", XxHash3_128::oneshot(url.loc.bytes()))), - ), - } -} - pub async fn calculate<'a, U>(url: U) -> io::Result where U: Into>, { let url: Url = url.into(); if let Some(path) = url.as_path() { - local::SizeCalculator::total(path).await + yazi_fs::provider::local::SizeCalculator::total(path).await } else { super::SizeCalculator::total(url).await } @@ -149,7 +128,7 @@ where V: Into>, { if let (Some(a), Some(b)) = (a.into().as_path(), b.into().as_path()) { - local::identical(a, b).await + yazi_fs::provider::local::identical(a, b).await } else { Err(io::Error::new(io::ErrorKind::Unsupported, "Unsupported filesystem")) } @@ -203,6 +182,14 @@ where Providers::new(url).await?.remove_dir_all(url.loc).await } +pub async fn remove_dir_clean<'a, U>(url: U) -> io::Result<()> +where + U: Into>, +{ + let url: Url = url.into(); + Ok(Providers::new(url).await?.remove_dir_clean(url.loc).await) +} + pub async fn remove_file<'a, U>(url: U) -> io::Result<()> where U: Into>, diff --git a/yazi-fs/src/provider/providers.rs b/yazi-vfs/src/provider/providers.rs similarity index 98% rename from yazi-fs/src/provider/providers.rs rename to yazi-vfs/src/provider/providers.rs index d81ad6a8..213b5177 100644 --- a/yazi-fs/src/provider/providers.rs +++ b/yazi-vfs/src/provider/providers.rs @@ -1,10 +1,9 @@ use std::{io, path::{Path, PathBuf}, sync::Arc}; +use yazi_fs::{cha::Cha, provider::{Provider, local::Local}}; use yazi_shared::{scheme::SchemeRef, url::{Url, UrlCow}}; -use yazi_vfs::config::{ProviderSftp, Vfs}; -use super::local::Local; -use crate::{cha::Cha, provider::Provider}; +use crate::config::{ProviderSftp, Vfs}; pub(super) struct Providers<'a>(Inner<'a>); diff --git a/yazi-fs/src/provider/read_dir.rs b/yazi-vfs/src/provider/read_dir.rs similarity index 80% rename from yazi-fs/src/provider/read_dir.rs rename to yazi-vfs/src/provider/read_dir.rs index 3e6a1f50..303144a4 100644 --- a/yazi-fs/src/provider/read_dir.rs +++ b/yazi-vfs/src/provider/read_dir.rs @@ -1,12 +1,11 @@ use std::{io, sync::Arc}; +use yazi_fs::provider::DirReader; use yazi_shared::url::UrlBuf; -use crate::provider::DirReader; - pub enum ReadDir { - Regular(super::local::ReadDir), - Search((Arc, super::local::ReadDir)), + Regular(yazi_fs::provider::local::ReadDir), + Search((Arc, yazi_fs::provider::local::ReadDir)), Sftp((Arc, super::sftp::ReadDir)), } diff --git a/yazi-fs/src/provider/rw_file.rs b/yazi-vfs/src/provider/rw_file.rs similarity index 96% rename from yazi-fs/src/provider/rw_file.rs rename to yazi-vfs/src/provider/rw_file.rs index 43476c45..a4a0174b 100644 --- a/yazi-fs/src/provider/rw_file.rs +++ b/yazi-vfs/src/provider/rw_file.rs @@ -1,8 +1,7 @@ use std::{io, pin::Pin}; use tokio::io::{AsyncRead, AsyncWrite}; - -use crate::cha::Cha; +use yazi_fs::cha::Cha; pub enum RwFile { Tokio(tokio::fs::File), @@ -30,7 +29,7 @@ impl RwFile { .await?; } Self::Sftp(f) => { - f.fsetstat(&cha.into()).await?; + f.fsetstat(&super::sftp::Cha(cha).into()).await?; } } diff --git a/yazi-fs/src/provider/sftp/gate.rs b/yazi-vfs/src/provider/sftp/gate.rs similarity index 91% rename from yazi-fs/src/provider/sftp/gate.rs rename to yazi-vfs/src/provider/sftp/gate.rs index 1cb5282b..a82931e2 100644 --- a/yazi-fs/src/provider/sftp/gate.rs +++ b/yazi-vfs/src/provider/sftp/gate.rs @@ -1,10 +1,10 @@ use std::{io, path::Path}; +use yazi_fs::{cha::Cha, provider::FileBuilder}; use yazi_sftp::fs::{Attrs, Flags}; use yazi_shared::scheme::SchemeRef; -use yazi_vfs::config::{ProviderSftp, Vfs}; -use crate::{cha::Cha, provider::FileBuilder}; +use crate::config::{ProviderSftp, Vfs}; pub struct Gate { sftp: super::Sftp, @@ -84,7 +84,7 @@ impl FileBuilder for Gate { flags |= Flags::WRITE; } - let attrs = self.cha.map_or(Attrs::default(), Attrs::from); + let attrs = self.cha.map(super::Cha).map_or(Attrs::default(), Attrs::from); Ok(self.sftp.op().await?.open(&path, flags, &attrs).await?) } diff --git a/yazi-fs/src/provider/sftp/metadata.rs b/yazi-vfs/src/provider/sftp/metadata.rs similarity index 66% rename from yazi-fs/src/provider/sftp/metadata.rs rename to yazi-vfs/src/provider/sftp/metadata.rs index d2b31b58..8b3baaf8 100644 --- a/yazi-fs/src/provider/sftp/metadata.rs +++ b/yazi-vfs/src/provider/sftp/metadata.rs @@ -1,13 +1,29 @@ use std::{ffi::OsStr, io, time::{Duration, UNIX_EPOCH}}; -use crate::cha::{Cha, ChaKind, ChaMode}; +use yazi_fs::cha::ChaKind; + +pub(crate) struct Cha(pub(crate) yazi_fs::cha::Cha); + +impl From for yazi_sftp::fs::Attrs { + fn from(cha: Cha) -> Self { + Self { + size: Some(cha.0.len), + uid: Some(cha.0.uid), + gid: Some(cha.0.gid), + perm: Some(cha.0.mode.bits() as u32), + atime: cha.0.atime_dur().ok().map(|d| d.as_secs() as u32), + mtime: cha.0.mtime_dur().ok().map(|d| d.as_secs() as u32), + extended: Default::default(), + } + } +} impl TryFrom<&yazi_sftp::fs::DirEntry> for Cha { type Error = io::Error; fn try_from(ent: &yazi_sftp::fs::DirEntry) -> Result { let mut cha = Self::try_from((ent.name().as_ref(), ent.attrs()))?; - cha.nlink = ent.nlink().unwrap_or_default(); + cha.0.nlink = ent.nlink().unwrap_or_default(); Ok(cha) } } @@ -19,9 +35,9 @@ impl TryFrom<(&OsStr, &yazi_sftp::fs::Attrs)> for Cha { let kind = if name.as_encoded_bytes().starts_with(b".") { ChaKind::HIDDEN } else { ChaKind::empty() }; - Ok(Cha { + Ok(Cha(yazi_fs::cha::Cha { kind, - mode: attrs.try_into()?, + mode: ChaMode::try_from(attrs)?.0, len: attrs.size.unwrap_or(0), atime: attrs.atime.and_then(|t| UNIX_EPOCH.checked_add(Duration::from_secs(t as u64))), btime: None, @@ -31,29 +47,19 @@ impl TryFrom<(&OsStr, &yazi_sftp::fs::Attrs)> for Cha { uid: attrs.uid.unwrap_or(0), gid: attrs.gid.unwrap_or(0), nlink: 0, - }) + })) } } +// --- ChaMode +pub(super) struct ChaMode(pub(super) yazi_fs::cha::ChaMode); + impl TryFrom<&yazi_sftp::fs::Attrs> for ChaMode { type Error = io::Error; fn try_from(attrs: &yazi_sftp::fs::Attrs) -> Result { - ChaMode::try_from(attrs.perm.unwrap_or_default() as u16) + yazi_fs::cha::ChaMode::try_from(attrs.perm.unwrap_or_default() as u16) + .map(Self) .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) } } - -impl From for yazi_sftp::fs::Attrs { - fn from(cha: Cha) -> Self { - Self { - size: Some(cha.len), - uid: Some(cha.uid), - gid: Some(cha.gid), - perm: Some(cha.mode.bits() as u32), - atime: cha.atime_dur().ok().map(|d| d.as_secs() as u32), - mtime: cha.mtime_dur().ok().map(|d| d.as_secs() as u32), - extended: Default::default(), - } - } -} diff --git a/yazi-fs/src/provider/sftp/mod.rs b/yazi-vfs/src/provider/sftp/mod.rs similarity index 86% rename from yazi-fs/src/provider/sftp/mod.rs rename to yazi-vfs/src/provider/sftp/mod.rs index a94405a8..20e52f28 100644 --- a/yazi-fs/src/provider/sftp/mod.rs +++ b/yazi-vfs/src/provider/sftp/mod.rs @@ -3,7 +3,7 @@ yazi_macro::mod_flat!(gate metadata read_dir sftp); pub(super) static CONN: yazi_shared::RoCell< parking_lot::Mutex< hashbrown::HashMap< - &'static yazi_vfs::config::ProviderSftp, + &'static crate::config::ProviderSftp, &'static deadpool::managed::Pool, >, >, diff --git a/yazi-fs/src/provider/sftp/read_dir.rs b/yazi-vfs/src/provider/sftp/read_dir.rs similarity index 62% rename from yazi-fs/src/provider/sftp/read_dir.rs rename to yazi-vfs/src/provider/sftp/read_dir.rs index f628e391..8be0a8b4 100644 --- a/yazi-fs/src/provider/sftp/read_dir.rs +++ b/yazi-vfs/src/provider/sftp/read_dir.rs @@ -1,6 +1,8 @@ use std::{borrow::Cow, ffi::OsStr, io, path::PathBuf}; -use crate::{cha::{Cha, ChaMode, ChaType}, provider::{DirReader, FileHolder}}; +use yazi_fs::provider::{DirReader, FileHolder}; + +use super::{Cha, ChaMode}; pub struct ReadDir(pub(super) yazi_sftp::fs::ReadDir); @@ -20,9 +22,9 @@ impl FileHolder for DirEntry { fn name(&self) -> Cow<'_, OsStr> { self.0.name() } - async fn metadata(&self) -> io::Result { Cha::try_from(&self.0) } + async fn metadata(&self) -> io::Result { Ok(Cha::try_from(&self.0)?.0) } - async fn file_type(&self) -> io::Result { - ChaMode::try_from(self.0.attrs()).map(Into::into) + async fn file_type(&self) -> io::Result { + Ok(ChaMode::try_from(self.0.attrs())?.0.into()) } } diff --git a/yazi-fs/src/provider/sftp/sftp.rs b/yazi-vfs/src/provider/sftp/sftp.rs similarity index 94% rename from yazi-fs/src/provider/sftp/sftp.rs rename to yazi-vfs/src/provider/sftp/sftp.rs index 9cca50ff..21c77beb 100644 --- a/yazi-fs/src/provider/sftp/sftp.rs +++ b/yazi-vfs/src/provider/sftp/sftp.rs @@ -2,11 +2,12 @@ use std::{io, path::{Path, PathBuf}, sync::Arc}; use russh::keys::PrivateKeyWithHashAlg; use tokio::io::{BufReader, BufWriter}; +use yazi_fs::provider::{DirReader, FileBuilder, FileHolder, Provider, local::Local}; use yazi_sftp::fs::{Attrs, Flags}; use yazi_shared::{scheme::SchemeRef, url::{Url, UrlBuf, UrlCow}}; -use yazi_vfs::config::ProviderSftp; -use crate::{cha::Cha, provider::{DirReader, FileBuilder, FileHolder, Provider, local::Local}}; +use super::Cha; +use crate::config::ProviderSftp; #[derive(Clone, Copy)] pub struct Sftp { @@ -76,12 +77,12 @@ impl Provider for Sftp { similar.map(|n| parent.join(n)).ok_or_else(|| io::Error::from(io::ErrorKind::NotFound)) } - async fn copy(&self, from: P, to: Q, cha: Cha) -> io::Result + async fn copy(&self, from: P, to: Q, cha: yazi_fs::cha::Cha) -> io::Result where P: AsRef, Q: AsRef, { - let attrs = Attrs::from(cha); + let attrs = Attrs::from(Cha(cha)); let op = self.op().await?; let from = op.open(&from, Flags::READ, &Attrs::default()).await?; @@ -115,13 +116,13 @@ impl Provider for Sftp { Ok(self.op().await?.hardlink(&original, &link).await?) } - async fn metadata

(&self, path: P) -> io::Result + async fn metadata

(&self, path: P) -> io::Result where P: AsRef, { let path = path.as_ref(); let attrs = self.op().await?.stat(path).await?; - (path.file_name().unwrap_or_default(), &attrs).try_into() + Ok(Cha::try_from((path.file_name().unwrap_or_default(), &attrs))?.0) } async fn read_dir

(&self, path: P) -> io::Result @@ -169,13 +170,13 @@ impl Provider for Sftp { Ok(self.op().await?.symlink(&original, &link).await?) } - async fn symlink_metadata

(&self, path: P) -> io::Result + async fn symlink_metadata

(&self, path: P) -> io::Result where P: AsRef, { let path = path.as_ref(); let attrs = self.op().await?.lstat(path).await?; - (path.file_name().unwrap_or_default(), &attrs).try_into() + Ok(Cha::try_from((path.file_name().unwrap_or_default(), &attrs))?.0) } async fn trash

(&self, _path: P) -> io::Result<()> diff --git a/yazi-watcher/Cargo.toml b/yazi-watcher/Cargo.toml index 1c700a94..6c04486b 100644 --- a/yazi-watcher/Cargo.toml +++ b/yazi-watcher/Cargo.toml @@ -14,6 +14,7 @@ yazi-dds = { path = "../yazi-dds", version = "25.9.15" } yazi-fs = { path = "../yazi-fs", version = "25.9.15" } yazi-macro = { path = "../yazi-macro", version = "25.9.15" } yazi-shared = { path = "../yazi-shared", version = "25.9.15" } +yazi-vfs = { path = "../yazi-vfs", version = "25.9.15" } # Logging tracing = { workspace = true } diff --git a/yazi-watcher/src/watcher.rs b/yazi-watcher/src/watcher.rs index 66891c66..33ca4142 100644 --- a/yazi-watcher/src/watcher.rs +++ b/yazi-watcher/src/watcher.rs @@ -5,6 +5,7 @@ use tokio::{pin, sync::{mpsc::{self, UnboundedReceiver}, watch}}; use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream}; use yazi_fs::{File, FilesOp, provider::local}; use yazi_shared::url::UrlBuf; +use yazi_vfs::VfsFile; use crate::{LINKED, Linked, WATCHED, WATCHER, backend::Backend};