mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
feat: filesystem-level error (#3217)
This commit is contained in:
parent
7b36b7fe14
commit
879ed49996
29 changed files with 265 additions and 81 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -4717,6 +4717,7 @@ dependencies = [
|
||||||
"ratatui",
|
"ratatui",
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
|
"tokio",
|
||||||
"toml 0.9.7",
|
"toml 0.9.7",
|
||||||
"tracing",
|
"tracing",
|
||||||
"yazi-codegen",
|
"yazi-codegen",
|
||||||
|
|
@ -5035,6 +5036,7 @@ dependencies = [
|
||||||
"toml 0.9.7",
|
"toml 0.9.7",
|
||||||
"tracing",
|
"tracing",
|
||||||
"uzers",
|
"uzers",
|
||||||
|
"yazi-config",
|
||||||
"yazi-fs",
|
"yazi-fs",
|
||||||
"yazi-macro",
|
"yazi-macro",
|
||||||
"yazi-sftp",
|
"yazi-sftp",
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ impl UserData for Folder {
|
||||||
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||||
cached_field!(fields, cwd, |_, me| Ok(Url::new(me.url.to_owned())));
|
cached_field!(fields, cwd, |_, me| Ok(Url::new(me.url.to_owned())));
|
||||||
cached_field!(fields, files, |_, me| Files::make(0..me.files.len(), me, &me.tab));
|
cached_field!(fields, files, |_, me| Files::make(0..me.files.len(), me, &me.tab));
|
||||||
cached_field!(fields, stage, |_, me| Ok(FolderStage::new(me.stage)));
|
cached_field!(fields, stage, |_, me| Ok(FolderStage::new(me.stage.clone())));
|
||||||
cached_field!(fields, window, |_, me| Files::make(me.window.clone(), me, &me.tab));
|
cached_field!(fields, window, |_, me| Files::make(me.window.clone(), me, &me.tab));
|
||||||
|
|
||||||
fields.add_field_method_get("offset", |_, me| Ok(me.offset));
|
fields.add_field_method_get("offset", |_, me| Ok(me.offset));
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ impl Refresh {
|
||||||
|
|
||||||
match Files::from_dir_bulk(&cwd).await {
|
match Files::from_dir_bulk(&cwd).await {
|
||||||
Ok(files) => FilesOp::Full(cwd, files, cha).emit(),
|
Ok(files) => FilesOp::Full(cwd, files, cha).emit(),
|
||||||
Err(e) => FilesOp::issue_error(&cwd, e.kind()).await,
|
Err(e) => FilesOp::issue_error(&cwd, e).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ const EXPECTED: &str = "expected a Error";
|
||||||
|
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
Io(std::io::Error),
|
Io(std::io::Error),
|
||||||
IoKind(std::io::ErrorKind),
|
Fs(yazi_fs::error::Error),
|
||||||
Serde(serde_json::Error),
|
Serde(serde_json::Error),
|
||||||
Custom(SStr),
|
Custom(SStr),
|
||||||
}
|
}
|
||||||
|
|
@ -24,7 +24,7 @@ impl Error {
|
||||||
pub fn into_string(self) -> SStr {
|
pub fn into_string(self) -> SStr {
|
||||||
match self {
|
match self {
|
||||||
Self::Io(e) => Cow::Owned(e.to_string()),
|
Self::Io(e) => Cow::Owned(e.to_string()),
|
||||||
Self::IoKind(e) => Cow::Owned(e.to_string()),
|
Self::Fs(e) => Cow::Owned(e.to_string()),
|
||||||
Self::Serde(e) => Cow::Owned(e.to_string()),
|
Self::Serde(e) => Cow::Owned(e.to_string()),
|
||||||
Self::Custom(s) => s,
|
Self::Custom(s) => s,
|
||||||
}
|
}
|
||||||
|
|
@ -35,7 +35,7 @@ impl Display for Error {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Io(e) => write!(f, "{e}"),
|
Self::Io(e) => write!(f, "{e}"),
|
||||||
Self::IoKind(e) => write!(f, "{e}"),
|
Self::Fs(e) => write!(f, "{e}"),
|
||||||
Self::Serde(e) => write!(f, "{e}"),
|
Self::Serde(e) => write!(f, "{e}"),
|
||||||
Self::Custom(s) => write!(f, "{s}"),
|
Self::Custom(s) => write!(f, "{s}"),
|
||||||
}
|
}
|
||||||
|
|
@ -56,6 +56,7 @@ impl UserData for Error {
|
||||||
fields.add_field_method_get("code", |_, me| {
|
fields.add_field_method_get("code", |_, me| {
|
||||||
Ok(match me {
|
Ok(match me {
|
||||||
Self::Io(e) => e.raw_os_error(),
|
Self::Io(e) => e.raw_os_error(),
|
||||||
|
Self::Fs(e) => e.raw_os_error(),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
@ -64,7 +65,7 @@ impl UserData for Error {
|
||||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||||
methods.add_meta_method(MetaMethod::ToString, |lua, me, ()| {
|
methods.add_meta_method(MetaMethod::ToString, |lua, me, ()| {
|
||||||
Ok(match me {
|
Ok(match me {
|
||||||
Self::Io(_) | Self::IoKind(_) | Self::Serde(_) => lua.create_string(me.to_string()),
|
Self::Io(_) | Self::Fs(_) | Self::Serde(_) => lua.create_string(me.to_string()),
|
||||||
Self::Custom(s) => lua.create_string(s.as_ref()),
|
Self::Custom(s) => lua.create_string(s.as_ref()),
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,10 @@ impl UserData for FolderStage {
|
||||||
methods.add_meta_method(MetaMethod::Call, |lua, me, ()| {
|
methods.add_meta_method(MetaMethod::Call, |lua, me, ()| {
|
||||||
use yazi_fs::FolderStage::*;
|
use yazi_fs::FolderStage::*;
|
||||||
|
|
||||||
match me.0 {
|
match &me.0 {
|
||||||
Loading => false.into_lua_multi(lua),
|
Loading => false.into_lua_multi(lua),
|
||||||
Loaded => true.into_lua_multi(lua),
|
Loaded => true.into_lua_multi(lua),
|
||||||
Failed(kind) => (true, crate::Error::IoKind(kind)).into_lua_multi(lua),
|
Failed(e) => (true, crate::Error::Fs(e.clone())).into_lua_multi(lua),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ indexmap = { workspace = true }
|
||||||
ratatui = { workspace = true }
|
ratatui = { workspace = true }
|
||||||
regex = { workspace = true }
|
regex = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
|
tokio = { workspace = true }
|
||||||
toml = { workspace = true }
|
toml = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#![allow(clippy::module_inception)]
|
#![allow(clippy::module_inception)]
|
||||||
|
|
||||||
yazi_macro::mod_pub!(keymap mgr open opener plugin popup preview tasks theme which);
|
yazi_macro::mod_pub!(keymap mgr open opener plugin popup preview tasks theme which vfs);
|
||||||
|
|
||||||
yazi_macro::mod_flat!(color icon layout pattern platform preset priority style yazi);
|
yazi_macro::mod_flat!(color icon layout pattern platform preset priority style yazi);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,8 @@ impl Preview {
|
||||||
bail!("[preview].cache_dir must be a path within local filesystem.");
|
bail!("[preview].cache_dir must be a path within local filesystem.");
|
||||||
};
|
};
|
||||||
|
|
||||||
std::fs::create_dir_all(&self.cache_dir).context("Failed to create cache directory")?;
|
std::fs::create_dir_all(&self.cache_dir)
|
||||||
|
.context(format!("Failed to create cache directory: {}", self.cache_dir.display()))?;
|
||||||
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ impl<T: Into<UrlBuf>> From<T> for Folder {
|
||||||
|
|
||||||
impl Folder {
|
impl Folder {
|
||||||
pub fn update(&mut self, op: FilesOp) -> bool {
|
pub fn update(&mut self, op: FilesOp) -> bool {
|
||||||
let (stage, revision) = (self.stage, self.files.revision);
|
let (stage, revision) = (self.stage.clone(), self.files.revision);
|
||||||
match op {
|
match op {
|
||||||
FilesOp::Full(_, _, cha) => {
|
FilesOp::Full(_, _, cha) => {
|
||||||
(self.cha, self.stage) = (cha, FolderStage::Loaded);
|
(self.cha, self.stage) = (cha, FolderStage::Loaded);
|
||||||
|
|
@ -57,8 +57,8 @@ impl Folder {
|
||||||
FilesOp::Done(_, cha, ticket) if ticket == self.files.ticket() => {
|
FilesOp::Done(_, cha, ticket) if ticket == self.files.ticket() => {
|
||||||
(self.cha, self.stage) = (cha, FolderStage::Loaded);
|
(self.cha, self.stage) = (cha, FolderStage::Loaded);
|
||||||
}
|
}
|
||||||
FilesOp::IOErr(_, kind) => {
|
FilesOp::IOErr(_, ref err) => {
|
||||||
(self.cha, self.stage) = (Cha::default(), FolderStage::Failed(kind));
|
(self.cha, self.stage) = (Cha::default(), FolderStage::Failed(err.clone()));
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
@ -83,17 +83,15 @@ impl Folder {
|
||||||
self.trace = self.trace.take_if(|_| !self.files.is_empty() || self.stage.is_loading());
|
self.trace = self.trace.take_if(|_| !self.files.is_empty() || self.stage.is_loading());
|
||||||
self.repos(None);
|
self.repos(None);
|
||||||
|
|
||||||
(stage, revision) != (self.stage, self.files.revision)
|
(&stage, revision) != (&self.stage, self.files.revision)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_pub(&mut self, tab: Id, op: FilesOp) -> bool {
|
pub fn update_pub(&mut self, tab: Id, op: FilesOp) -> bool {
|
||||||
let old = self.stage;
|
if self.update(op) {
|
||||||
if !self.update(op) {
|
err!(Pubsub::pub_after_load(tab, &self.url, &self.stage));
|
||||||
return false;
|
return true;
|
||||||
} else if self.stage != old {
|
|
||||||
err!(Pubsub::pub_after_load(tab, &self.url, self.stage));
|
|
||||||
}
|
}
|
||||||
true
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn arrow(&mut self, step: impl Into<Step>) -> bool {
|
pub fn arrow(&mut self, step: impl Into<Step>) -> bool {
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ impl Preview {
|
||||||
|
|
||||||
let rx = match Files::from_dir(&wd).await {
|
let rx = match Files::from_dir(&wd).await {
|
||||||
Ok(rx) => rx,
|
Ok(rx) => rx,
|
||||||
Err(e) => return FilesOp::issue_error(&wd, e.kind()).await,
|
Err(e) => return FilesOp::issue_error(&wd, e).await,
|
||||||
};
|
};
|
||||||
|
|
||||||
let stream =
|
let stream =
|
||||||
|
|
|
||||||
|
|
@ -11,18 +11,18 @@ use super::Ember;
|
||||||
pub struct EmberLoad<'a> {
|
pub struct EmberLoad<'a> {
|
||||||
pub tab: Id,
|
pub tab: Id,
|
||||||
pub url: Cow<'a, UrlBuf>,
|
pub url: Cow<'a, UrlBuf>,
|
||||||
pub stage: FolderStage,
|
pub stage: Cow<'a, FolderStage>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> EmberLoad<'a> {
|
impl<'a> EmberLoad<'a> {
|
||||||
pub fn borrowed(tab: Id, url: &'a UrlBuf, stage: FolderStage) -> Ember<'a> {
|
pub fn borrowed(tab: Id, url: &'a UrlBuf, stage: &'a FolderStage) -> Ember<'a> {
|
||||||
Self { tab, url: url.into(), stage }.into()
|
Self { tab, url: url.into(), stage: Cow::Borrowed(stage) }.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EmberLoad<'static> {
|
impl EmberLoad<'static> {
|
||||||
pub fn owned(tab: Id, url: &UrlBuf, stage: FolderStage) -> Ember<'static> {
|
pub fn owned(tab: Id, url: &UrlBuf, stage: &FolderStage) -> Ember<'static> {
|
||||||
Self { tab, url: url.clone().into(), stage }.into()
|
Self { tab, url: url.clone().into(), stage: Cow::Owned(stage.clone()) }.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -36,7 +36,7 @@ impl IntoLua for EmberLoad<'_> {
|
||||||
.create_table_from([
|
.create_table_from([
|
||||||
("tab", self.tab.get().into_lua(lua)?),
|
("tab", self.tab.get().into_lua(lua)?),
|
||||||
("url", yazi_binding::Url::new(self.url).into_lua(lua)?),
|
("url", yazi_binding::Url::new(self.url).into_lua(lua)?),
|
||||||
("stage", yazi_binding::FolderStage::new(self.stage).into_lua(lua)?),
|
("stage", yazi_binding::FolderStage::new(self.stage.into_owned()).into_lua(lua)?),
|
||||||
])?
|
])?
|
||||||
.into_lua(lua)
|
.into_lua(lua)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -149,7 +149,7 @@ impl Pubsub {
|
||||||
|
|
||||||
pub_after!(cd(tab: Id, url: &UrlBuf), (tab, url));
|
pub_after!(cd(tab: Id, url: &UrlBuf), (tab, url));
|
||||||
|
|
||||||
pub_after!(load(tab: Id, url: &UrlBuf, stage: FolderStage), (tab, url, stage));
|
pub_after!(load(tab: Id, url: &UrlBuf, stage: &FolderStage), (tab, url, stage));
|
||||||
|
|
||||||
pub_after!(hover(tab: Id, url: Option<&UrlBuf>), (tab, url));
|
pub_after!(hover(tab: Id, url: Option<&UrlBuf>), (tab, url));
|
||||||
|
|
||||||
|
|
|
||||||
56
yazi-fs/src/error/error.rs
Normal file
56
yazi-fs/src/error/error.rs
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
use std::{fmt, io, sync::Arc};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub enum Error {
|
||||||
|
Kind(io::ErrorKind),
|
||||||
|
Raw(i32),
|
||||||
|
Custom { kind: io::ErrorKind, code: Option<i32>, message: Arc<str> },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<io::Error> for Error {
|
||||||
|
fn from(err: io::Error) -> Self {
|
||||||
|
if err.get_ref().is_some() {
|
||||||
|
Self::Custom {
|
||||||
|
kind: err.kind(),
|
||||||
|
code: err.raw_os_error(),
|
||||||
|
message: err.to_string().into(),
|
||||||
|
}
|
||||||
|
} else if let Some(code) = err.raw_os_error() {
|
||||||
|
Self::Raw(code)
|
||||||
|
} else {
|
||||||
|
Self::Kind(err.kind())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<io::ErrorKind> for Error {
|
||||||
|
fn from(kind: io::ErrorKind) -> Self { Self::Kind(kind) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Kind(kind) => io::Error::from(*kind).fmt(f),
|
||||||
|
Self::Raw(code) => io::Error::from_raw_os_error(*code).fmt(f),
|
||||||
|
Self::Custom { message, .. } => write!(f, "{message}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error {
|
||||||
|
pub fn kind(&self) -> io::ErrorKind {
|
||||||
|
match self {
|
||||||
|
Self::Kind(kind) => *kind,
|
||||||
|
Self::Raw(code) => io::Error::from_raw_os_error(*code).kind(),
|
||||||
|
Self::Custom { kind, .. } => *kind,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn raw_os_error(&self) -> Option<i32> {
|
||||||
|
match self {
|
||||||
|
Self::Kind(_) => None,
|
||||||
|
Self::Raw(code) => Some(*code),
|
||||||
|
Self::Custom { code, .. } => *code,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
yazi-fs/src/error/mod.rs
Normal file
1
yazi-fs/src/error/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
yazi_macro::mod_flat!(error serde);
|
||||||
154
yazi-fs/src/error/serde.rs
Normal file
154
yazi-fs/src/error/serde.rs
Normal file
|
|
@ -0,0 +1,154 @@
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
|
||||||
|
use super::Error;
|
||||||
|
|
||||||
|
fn kind_to_str(kind: io::ErrorKind) -> &'static str {
|
||||||
|
use std::io::ErrorKind as K;
|
||||||
|
match kind {
|
||||||
|
K::NotFound => "not_found",
|
||||||
|
K::PermissionDenied => "permission_denied",
|
||||||
|
K::ConnectionRefused => "connection_refused",
|
||||||
|
K::ConnectionReset => "connection_reset",
|
||||||
|
K::HostUnreachable => "host_unreachable",
|
||||||
|
K::NetworkUnreachable => "network_unreachable",
|
||||||
|
K::ConnectionAborted => "connection_aborted",
|
||||||
|
K::NotConnected => "not_connected",
|
||||||
|
K::AddrInUse => "addr_in_use",
|
||||||
|
K::AddrNotAvailable => "addr_not_available",
|
||||||
|
K::NetworkDown => "network_down",
|
||||||
|
K::BrokenPipe => "broken_pipe",
|
||||||
|
K::AlreadyExists => "already_exists",
|
||||||
|
K::WouldBlock => "would_block",
|
||||||
|
K::NotADirectory => "not_a_directory",
|
||||||
|
K::IsADirectory => "is_a_directory",
|
||||||
|
K::DirectoryNotEmpty => "directory_not_empty",
|
||||||
|
K::ReadOnlyFilesystem => "read_only_filesystem",
|
||||||
|
// K::FilesystemLoop => "filesystem_loop",
|
||||||
|
K::StaleNetworkFileHandle => "stale_network_file_handle",
|
||||||
|
K::InvalidInput => "invalid_input",
|
||||||
|
K::InvalidData => "invalid_data",
|
||||||
|
K::TimedOut => "timed_out",
|
||||||
|
K::WriteZero => "write_zero",
|
||||||
|
K::StorageFull => "storage_full",
|
||||||
|
K::NotSeekable => "not_seekable",
|
||||||
|
K::QuotaExceeded => "quota_exceeded",
|
||||||
|
K::FileTooLarge => "file_too_large",
|
||||||
|
K::ResourceBusy => "resource_busy",
|
||||||
|
K::ExecutableFileBusy => "executable_file_busy",
|
||||||
|
K::Deadlock => "deadlock",
|
||||||
|
K::CrossesDevices => "crosses_devices",
|
||||||
|
K::TooManyLinks => "too_many_links",
|
||||||
|
K::InvalidFilename => "invalid_filename",
|
||||||
|
K::ArgumentListTooLong => "argument_list_too_long",
|
||||||
|
K::Interrupted => "interrupted",
|
||||||
|
K::Unsupported => "unsupported",
|
||||||
|
K::UnexpectedEof => "unexpected_eof",
|
||||||
|
K::OutOfMemory => "out_of_memory",
|
||||||
|
// K::InProgress => "in_progress",
|
||||||
|
K::Other => "other",
|
||||||
|
_ => "other",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn kind_from_str(s: &str) -> io::ErrorKind {
|
||||||
|
use std::io::ErrorKind as K;
|
||||||
|
match s {
|
||||||
|
"not_found" => K::NotFound,
|
||||||
|
"permission_denied" => K::PermissionDenied,
|
||||||
|
"connection_refused" => K::ConnectionRefused,
|
||||||
|
"connection_reset" => K::ConnectionReset,
|
||||||
|
"host_unreachable" => K::HostUnreachable,
|
||||||
|
"network_unreachable" => K::NetworkUnreachable,
|
||||||
|
"connection_aborted" => K::ConnectionAborted,
|
||||||
|
"not_connected" => K::NotConnected,
|
||||||
|
"addr_in_use" => K::AddrInUse,
|
||||||
|
"addr_not_available" => K::AddrNotAvailable,
|
||||||
|
"network_down" => K::NetworkDown,
|
||||||
|
"broken_pipe" => K::BrokenPipe,
|
||||||
|
"already_exists" => K::AlreadyExists,
|
||||||
|
"would_block" => K::WouldBlock,
|
||||||
|
"not_a_directory" => K::NotADirectory,
|
||||||
|
"is_a_directory" => K::IsADirectory,
|
||||||
|
"directory_not_empty" => K::DirectoryNotEmpty,
|
||||||
|
"read_only_filesystem" => K::ReadOnlyFilesystem,
|
||||||
|
// "filesystem_loop" => K::FilesystemLoop,
|
||||||
|
"stale_network_file_handle" => K::StaleNetworkFileHandle,
|
||||||
|
"invalid_input" => K::InvalidInput,
|
||||||
|
"invalid_data" => K::InvalidData,
|
||||||
|
"timed_out" => K::TimedOut,
|
||||||
|
"write_zero" => K::WriteZero,
|
||||||
|
"storage_full" => K::StorageFull,
|
||||||
|
"not_seekable" => K::NotSeekable,
|
||||||
|
"quota_exceeded" => K::QuotaExceeded,
|
||||||
|
"file_too_large" => K::FileTooLarge,
|
||||||
|
"resource_busy" => K::ResourceBusy,
|
||||||
|
"executable_file_busy" => K::ExecutableFileBusy,
|
||||||
|
"deadlock" => K::Deadlock,
|
||||||
|
"crosses_devices" => K::CrossesDevices,
|
||||||
|
"too_many_links" => K::TooManyLinks,
|
||||||
|
"invalid_filename" => K::InvalidFilename,
|
||||||
|
"argument_list_too_long" => K::ArgumentListTooLong,
|
||||||
|
"interrupted" => K::Interrupted,
|
||||||
|
"unsupported" => K::Unsupported,
|
||||||
|
"unexpected_eof" => K::UnexpectedEof,
|
||||||
|
"out_of_memory" => K::OutOfMemory,
|
||||||
|
// "in_progress" => K::InProgress,
|
||||||
|
"other" => K::Other,
|
||||||
|
_ => K::Other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for Error {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(tag = "type", rename_all = "kebab-case")]
|
||||||
|
enum Shadow<'a> {
|
||||||
|
Kind { kind: &'a str },
|
||||||
|
Raw { code: i32 },
|
||||||
|
Dyn { kind: &'a str, code: Option<i32>, message: &'a str },
|
||||||
|
}
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Error::Kind(kind) => Shadow::Kind { kind: kind_to_str(*kind) }.serialize(serializer),
|
||||||
|
Error::Raw(code) => Shadow::Raw { code: *code }.serialize(serializer),
|
||||||
|
Error::Custom { kind, code, message } => {
|
||||||
|
Shadow::Dyn { kind: kind_to_str(*kind), code: *code, message }.serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for Error {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(tag = "type", rename_all = "kebab-case")]
|
||||||
|
enum Shadow {
|
||||||
|
Kind { kind: String },
|
||||||
|
Raw { code: i32 },
|
||||||
|
Dyn { kind: String, code: Option<i32>, message: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
let shadow = Shadow::deserialize(deserializer)?;
|
||||||
|
Ok(match shadow {
|
||||||
|
Shadow::Kind { kind } => Error::Kind(kind_from_str(&kind)),
|
||||||
|
Shadow::Raw { code } => Error::Raw(code),
|
||||||
|
Shadow::Dyn { kind, code, message } => {
|
||||||
|
if !message.is_empty() {
|
||||||
|
Error::Custom { kind: kind_from_str(&kind), code, message: message.into() }
|
||||||
|
} else if let Some(code) = code {
|
||||||
|
Error::Raw(code)
|
||||||
|
} else {
|
||||||
|
Error::Kind(kind_from_str(&kind))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#![allow(clippy::if_same_then_else, clippy::option_map_unit_fn, clippy::unit_arg)]
|
#![allow(clippy::if_same_then_else, clippy::option_map_unit_fn, clippy::unit_arg)]
|
||||||
|
|
||||||
yazi_macro::mod_pub!(cha mounts provider path);
|
yazi_macro::mod_pub!(cha error mounts path provider);
|
||||||
|
|
||||||
yazi_macro::mod_flat!(cwd file files filter fns op sorter sorting stage url xdg);
|
yazi_macro::mod_flat!(cwd file files filter fns op sorter sorting stage url xdg);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use yazi_macro::relay;
|
||||||
use yazi_shared::{Id, Ids, url::{UrlBuf, UrnBuf}};
|
use yazi_shared::{Id, Ids, url::{UrlBuf, UrnBuf}};
|
||||||
|
|
||||||
use super::File;
|
use super::File;
|
||||||
use crate::cha::Cha;
|
use crate::{cha::Cha, error::Error};
|
||||||
|
|
||||||
pub static FILES_TICKET: Ids = Ids::new();
|
pub static FILES_TICKET: Ids = Ids::new();
|
||||||
|
|
||||||
|
|
@ -15,7 +15,7 @@ pub enum FilesOp {
|
||||||
Part(UrlBuf, Vec<File>, Id),
|
Part(UrlBuf, Vec<File>, Id),
|
||||||
Done(UrlBuf, Cha, Id),
|
Done(UrlBuf, Cha, Id),
|
||||||
Size(UrlBuf, HashMap<UrnBuf, u64>),
|
Size(UrlBuf, HashMap<UrnBuf, u64>),
|
||||||
IOErr(UrlBuf, std::io::ErrorKind),
|
IOErr(UrlBuf, Error),
|
||||||
|
|
||||||
Creating(UrlBuf, Vec<File>),
|
Creating(UrlBuf, Vec<File>),
|
||||||
Deleting(UrlBuf, HashSet<UrnBuf>),
|
Deleting(UrlBuf, HashSet<UrnBuf>),
|
||||||
|
|
@ -112,7 +112,7 @@ impl FilesOp {
|
||||||
Self::Part(_, files, ticket) => Self::Part(w, files!(files), *ticket),
|
Self::Part(_, files, ticket) => Self::Part(w, files!(files), *ticket),
|
||||||
Self::Done(_, cha, ticket) => Self::Done(w, *cha, *ticket),
|
Self::Done(_, cha, ticket) => Self::Done(w, *cha, *ticket),
|
||||||
Self::Size(_, map) => Self::Size(w, map.iter().map(|(urn, &s)| (urn.clone(), s)).collect()),
|
Self::Size(_, map) => Self::Size(w, map.iter().map(|(urn, &s)| (urn.clone(), s)).collect()),
|
||||||
Self::IOErr(_, err) => Self::IOErr(w, *err),
|
Self::IOErr(_, err) => Self::IOErr(w, err.clone()),
|
||||||
|
|
||||||
Self::Creating(_, files) => Self::Creating(w, files!(files)),
|
Self::Creating(_, files) => Self::Creating(w, files!(files)),
|
||||||
Self::Deleting(_, urns) => Self::Deleting(w, urns.clone()),
|
Self::Deleting(_, urns) => Self::Deleting(w, urns.clone()),
|
||||||
|
|
|
||||||
|
|
@ -1,42 +1,15 @@
|
||||||
use serde::{Deserialize, Serialize, ser::SerializeMap};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
use crate::error::Error;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
|
||||||
pub enum FolderStage {
|
pub enum FolderStage {
|
||||||
#[default]
|
#[default]
|
||||||
Loading,
|
Loading,
|
||||||
Loaded,
|
Loaded,
|
||||||
Failed(std::io::ErrorKind),
|
Failed(Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FolderStage {
|
impl FolderStage {
|
||||||
pub fn is_loading(self) -> bool { self == Self::Loading }
|
pub fn is_loading(&self) -> bool { *self == Self::Loading }
|
||||||
}
|
|
||||||
|
|
||||||
impl Serialize for FolderStage {
|
|
||||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
|
||||||
let mut map = serializer.serialize_map(Some(2))?;
|
|
||||||
match self {
|
|
||||||
Self::Loading => map.serialize_entry("state", "loading")?,
|
|
||||||
Self::Loaded => map.serialize_entry("state", "loaded")?,
|
|
||||||
Self::Failed(_) => map.serialize_entry("state", "failed")?,
|
|
||||||
}
|
|
||||||
map.end()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for FolderStage {
|
|
||||||
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct Shadow {
|
|
||||||
state: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
let shadow = Shadow::deserialize(deserializer)?;
|
|
||||||
match shadow.state.as_str() {
|
|
||||||
"loading" => Ok(Self::Loading),
|
|
||||||
"loaded" => Ok(Self::Loaded),
|
|
||||||
"failed" => Ok(Self::Failed(std::io::ErrorKind::Other)),
|
|
||||||
_ => Err(serde::de::Error::custom("invalid folder stage")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ homepage = "https://yazi-rs.github.io"
|
||||||
repository = "https://github.com/sxyazi/yazi"
|
repository = "https://github.com/sxyazi/yazi"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
yazi-config = { path = "../yazi-config", version = "25.9.15" }
|
||||||
yazi-fs = { path = "../yazi-fs", version = "25.9.15" }
|
yazi-fs = { path = "../yazi-fs", version = "25.9.15" }
|
||||||
yazi-macro = { path = "../yazi-macro", version = "25.9.15" }
|
yazi-macro = { path = "../yazi-macro", version = "25.9.15" }
|
||||||
yazi-sftp = { path = "../yazi-sftp", version = "0.1.0" }
|
yazi-sftp = { path = "../yazi-sftp", version = "0.1.0" }
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ impl VfsFiles for Files {
|
||||||
Ok(c) if !c.is_dir() => FilesOp::issue_error(dir, ErrorKind::NotADirectory).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) if c.hits(cha) && PARTITIONS.read().heuristic(cha) => {}
|
||||||
Ok(c) => return Some(c),
|
Ok(c) => return Some(c),
|
||||||
Err(e) => FilesOp::issue_error(dir, e.kind()).await,
|
Err(e) => FilesOp::issue_error(dir, e).await,
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
yazi_macro::mod_pub!(config provider);
|
yazi_macro::mod_pub!(provider);
|
||||||
|
|
||||||
yazi_macro::mod_flat!(cha file files fns op);
|
yazi_macro::mod_flat!(cha file files fns op);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,19 @@
|
||||||
use std::io;
|
|
||||||
|
|
||||||
use yazi_fs::FilesOp;
|
use yazi_fs::FilesOp;
|
||||||
use yazi_shared::url::UrlBuf;
|
use yazi_shared::url::UrlBuf;
|
||||||
|
|
||||||
use crate::maybe_exists;
|
use crate::maybe_exists;
|
||||||
|
|
||||||
pub trait VfsFilesOp {
|
pub trait VfsFilesOp {
|
||||||
fn issue_error(cwd: &UrlBuf, kind: io::ErrorKind) -> impl Future<Output = ()>;
|
fn issue_error(cwd: &UrlBuf, kind: impl Into<yazi_fs::error::Error>) -> impl Future<Output = ()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VfsFilesOp for FilesOp {
|
impl VfsFilesOp for FilesOp {
|
||||||
async fn issue_error(cwd: &UrlBuf, kind: std::io::ErrorKind) {
|
async fn issue_error(cwd: &UrlBuf, err: impl Into<yazi_fs::error::Error>) {
|
||||||
use std::io::ErrorKind;
|
let err = err.into();
|
||||||
if kind != ErrorKind::NotFound {
|
if err.kind() != std::io::ErrorKind::NotFound {
|
||||||
Self::IOErr(cwd.clone(), kind).emit();
|
Self::IOErr(cwd.clone(), err).emit();
|
||||||
} else if maybe_exists(cwd).await {
|
} else if maybe_exists(cwd).await {
|
||||||
Self::IOErr(cwd.clone(), kind).emit();
|
Self::IOErr(cwd.clone(), err).emit();
|
||||||
} else if let Some((p, n)) = cwd.pair() {
|
} else if let Some((p, n)) = cwd.pair() {
|
||||||
Self::Deleting(p.into(), [n.into()].into()).emit();
|
Self::Deleting(p.into(), [n.into()].into()).emit();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
use std::{io, path::{Path, PathBuf}, sync::Arc};
|
use std::{io, path::{Path, PathBuf}, sync::Arc};
|
||||||
|
|
||||||
|
use yazi_config::vfs::{ProviderSftp, Vfs};
|
||||||
use yazi_fs::{cha::Cha, provider::{Provider, local::Local}};
|
use yazi_fs::{cha::Cha, provider::{Provider, local::Local}};
|
||||||
use yazi_shared::{scheme::SchemeRef, url::{Url, UrlCow}};
|
use yazi_shared::{scheme::SchemeRef, url::{Url, UrlCow}};
|
||||||
|
|
||||||
use crate::config::{ProviderSftp, Vfs};
|
|
||||||
|
|
||||||
pub(super) struct Providers<'a>(Inner<'a>);
|
pub(super) struct Providers<'a>(Inner<'a>);
|
||||||
|
|
||||||
enum Inner<'a> {
|
enum Inner<'a> {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
use std::{io, path::Path};
|
use std::{io, path::Path};
|
||||||
|
|
||||||
|
use yazi_config::vfs::{ProviderSftp, Vfs};
|
||||||
use yazi_fs::{cha::Cha, provider::FileBuilder};
|
use yazi_fs::{cha::Cha, provider::FileBuilder};
|
||||||
use yazi_sftp::fs::{Attrs, Flags};
|
use yazi_sftp::fs::{Attrs, Flags};
|
||||||
use yazi_shared::scheme::SchemeRef;
|
use yazi_shared::scheme::SchemeRef;
|
||||||
|
|
||||||
use crate::config::{ProviderSftp, Vfs};
|
|
||||||
|
|
||||||
pub struct Gate {
|
pub struct Gate {
|
||||||
sftp: super::Sftp,
|
sftp: super::Sftp,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ yazi_macro::mod_flat!(gate metadata read_dir sftp);
|
||||||
pub(super) static CONN: yazi_shared::RoCell<
|
pub(super) static CONN: yazi_shared::RoCell<
|
||||||
parking_lot::Mutex<
|
parking_lot::Mutex<
|
||||||
hashbrown::HashMap<
|
hashbrown::HashMap<
|
||||||
&'static crate::config::ProviderSftp,
|
&'static yazi_config::vfs::ProviderSftp,
|
||||||
&'static deadpool::managed::Pool<Sftp>,
|
&'static deadpool::managed::Pool<Sftp>,
|
||||||
>,
|
>,
|
||||||
>,
|
>,
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,12 @@ use std::{io, path::{Path, PathBuf}, sync::Arc};
|
||||||
|
|
||||||
use russh::keys::PrivateKeyWithHashAlg;
|
use russh::keys::PrivateKeyWithHashAlg;
|
||||||
use tokio::io::{BufReader, BufWriter};
|
use tokio::io::{BufReader, BufWriter};
|
||||||
|
use yazi_config::vfs::ProviderSftp;
|
||||||
use yazi_fs::provider::{DirReader, FileBuilder, FileHolder, Provider, local::Local};
|
use yazi_fs::provider::{DirReader, FileBuilder, FileHolder, Provider, local::Local};
|
||||||
use yazi_sftp::fs::{Attrs, Flags};
|
use yazi_sftp::fs::{Attrs, Flags};
|
||||||
use yazi_shared::{scheme::SchemeRef, url::{Url, UrlBuf, UrlCow}};
|
use yazi_shared::{scheme::SchemeRef, url::{Url, UrlBuf, UrlCow}};
|
||||||
|
|
||||||
use super::Cha;
|
use super::Cha;
|
||||||
use crate::config::ProviderSftp;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Sftp {
|
pub struct Sftp {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue