mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01: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",
|
||||
"regex",
|
||||
"serde",
|
||||
"tokio",
|
||||
"toml 0.9.7",
|
||||
"tracing",
|
||||
"yazi-codegen",
|
||||
|
|
@ -5035,6 +5036,7 @@ dependencies = [
|
|||
"toml 0.9.7",
|
||||
"tracing",
|
||||
"uzers",
|
||||
"yazi-config",
|
||||
"yazi-fs",
|
||||
"yazi-macro",
|
||||
"yazi-sftp",
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ impl UserData for Folder {
|
|||
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||
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, 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));
|
||||
|
||||
fields.add_field_method_get("offset", |_, me| Ok(me.offset));
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ impl Refresh {
|
|||
|
||||
match Files::from_dir_bulk(&cwd).await {
|
||||
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 {
|
||||
Io(std::io::Error),
|
||||
IoKind(std::io::ErrorKind),
|
||||
Fs(yazi_fs::error::Error),
|
||||
Serde(serde_json::Error),
|
||||
Custom(SStr),
|
||||
}
|
||||
|
|
@ -24,7 +24,7 @@ impl Error {
|
|||
pub fn into_string(self) -> SStr {
|
||||
match self {
|
||||
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::Custom(s) => s,
|
||||
}
|
||||
|
|
@ -35,7 +35,7 @@ impl Display for Error {
|
|||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
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::Custom(s) => write!(f, "{s}"),
|
||||
}
|
||||
|
|
@ -56,6 +56,7 @@ impl UserData for Error {
|
|||
fields.add_field_method_get("code", |_, me| {
|
||||
Ok(match me {
|
||||
Self::Io(e) => e.raw_os_error(),
|
||||
Self::Fs(e) => e.raw_os_error(),
|
||||
_ => None,
|
||||
})
|
||||
});
|
||||
|
|
@ -64,7 +65,7 @@ impl UserData for Error {
|
|||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_meta_method(MetaMethod::ToString, |lua, 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()),
|
||||
})
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@ impl UserData for FolderStage {
|
|||
methods.add_meta_method(MetaMethod::Call, |lua, me, ()| {
|
||||
use yazi_fs::FolderStage::*;
|
||||
|
||||
match me.0 {
|
||||
match &me.0 {
|
||||
Loading => false.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 }
|
||||
regex = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
toml = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#![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);
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,8 @@ impl Preview {
|
|||
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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ impl<T: Into<UrlBuf>> From<T> for Folder {
|
|||
|
||||
impl Folder {
|
||||
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 {
|
||||
FilesOp::Full(_, _, cha) => {
|
||||
(self.cha, self.stage) = (cha, FolderStage::Loaded);
|
||||
|
|
@ -57,8 +57,8 @@ impl Folder {
|
|||
FilesOp::Done(_, cha, ticket) if ticket == self.files.ticket() => {
|
||||
(self.cha, self.stage) = (cha, FolderStage::Loaded);
|
||||
}
|
||||
FilesOp::IOErr(_, kind) => {
|
||||
(self.cha, self.stage) = (Cha::default(), FolderStage::Failed(kind));
|
||||
FilesOp::IOErr(_, ref err) => {
|
||||
(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.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 {
|
||||
let old = self.stage;
|
||||
if !self.update(op) {
|
||||
return false;
|
||||
} else if self.stage != old {
|
||||
err!(Pubsub::pub_after_load(tab, &self.url, self.stage));
|
||||
if self.update(op) {
|
||||
err!(Pubsub::pub_after_load(tab, &self.url, &self.stage));
|
||||
return true;
|
||||
}
|
||||
true
|
||||
false
|
||||
}
|
||||
|
||||
pub fn arrow(&mut self, step: impl Into<Step>) -> bool {
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ impl Preview {
|
|||
|
||||
let rx = match Files::from_dir(&wd).await {
|
||||
Ok(rx) => rx,
|
||||
Err(e) => return FilesOp::issue_error(&wd, e.kind()).await,
|
||||
Err(e) => return FilesOp::issue_error(&wd, e).await,
|
||||
};
|
||||
|
||||
let stream =
|
||||
|
|
|
|||
|
|
@ -11,18 +11,18 @@ use super::Ember;
|
|||
pub struct EmberLoad<'a> {
|
||||
pub tab: Id,
|
||||
pub url: Cow<'a, UrlBuf>,
|
||||
pub stage: FolderStage,
|
||||
pub stage: Cow<'a, FolderStage>,
|
||||
}
|
||||
|
||||
impl<'a> EmberLoad<'a> {
|
||||
pub fn borrowed(tab: Id, url: &'a UrlBuf, stage: FolderStage) -> Ember<'a> {
|
||||
Self { tab, url: url.into(), stage }.into()
|
||||
pub fn borrowed(tab: Id, url: &'a UrlBuf, stage: &'a FolderStage) -> Ember<'a> {
|
||||
Self { tab, url: url.into(), stage: Cow::Borrowed(stage) }.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl EmberLoad<'static> {
|
||||
pub fn owned(tab: Id, url: &UrlBuf, stage: FolderStage) -> Ember<'static> {
|
||||
Self { tab, url: url.clone().into(), stage }.into()
|
||||
pub fn owned(tab: Id, url: &UrlBuf, stage: &FolderStage) -> Ember<'static> {
|
||||
Self { tab, url: url.clone().into(), stage: Cow::Owned(stage.clone()) }.into()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ impl IntoLua for EmberLoad<'_> {
|
|||
.create_table_from([
|
||||
("tab", self.tab.get().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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ impl Pubsub {
|
|||
|
||||
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));
|
||||
|
||||
|
|
|
|||
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)]
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use yazi_macro::relay;
|
|||
use yazi_shared::{Id, Ids, url::{UrlBuf, UrnBuf}};
|
||||
|
||||
use super::File;
|
||||
use crate::cha::Cha;
|
||||
use crate::{cha::Cha, error::Error};
|
||||
|
||||
pub static FILES_TICKET: Ids = Ids::new();
|
||||
|
||||
|
|
@ -15,7 +15,7 @@ pub enum FilesOp {
|
|||
Part(UrlBuf, Vec<File>, Id),
|
||||
Done(UrlBuf, Cha, Id),
|
||||
Size(UrlBuf, HashMap<UrnBuf, u64>),
|
||||
IOErr(UrlBuf, std::io::ErrorKind),
|
||||
IOErr(UrlBuf, Error),
|
||||
|
||||
Creating(UrlBuf, Vec<File>),
|
||||
Deleting(UrlBuf, HashSet<UrnBuf>),
|
||||
|
|
@ -112,7 +112,7 @@ impl FilesOp {
|
|||
Self::Part(_, files, ticket) => Self::Part(w, files!(files), *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::IOErr(_, err) => Self::IOErr(w, *err),
|
||||
Self::IOErr(_, err) => Self::IOErr(w, err.clone()),
|
||||
|
||||
Self::Creating(_, files) => Self::Creating(w, files!(files)),
|
||||
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 {
|
||||
#[default]
|
||||
Loading,
|
||||
Loaded,
|
||||
Failed(std::io::ErrorKind),
|
||||
Failed(Error),
|
||||
}
|
||||
|
||||
impl FolderStage {
|
||||
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")),
|
||||
}
|
||||
}
|
||||
pub fn is_loading(&self) -> bool { *self == Self::Loading }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ homepage = "https://yazi-rs.github.io"
|
|||
repository = "https://github.com/sxyazi/yazi"
|
||||
|
||||
[dependencies]
|
||||
yazi-config = { path = "../yazi-config", version = "25.9.15" }
|
||||
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" }
|
||||
|
|
|
|||
|
|
@ -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.hits(cha) && PARTITIONS.read().heuristic(cha) => {}
|
||||
Ok(c) => return Some(c),
|
||||
Err(e) => FilesOp::issue_error(dir, e.kind()).await,
|
||||
Err(e) => FilesOp::issue_error(dir, e).await,
|
||||
}
|
||||
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);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,19 @@
|
|||
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<Output = ()>;
|
||||
fn issue_error(cwd: &UrlBuf, kind: impl Into<yazi_fs::error::Error>) -> impl Future<Output = ()>;
|
||||
}
|
||||
|
||||
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();
|
||||
async fn issue_error(cwd: &UrlBuf, err: impl Into<yazi_fs::error::Error>) {
|
||||
let err = err.into();
|
||||
if err.kind() != std::io::ErrorKind::NotFound {
|
||||
Self::IOErr(cwd.clone(), err).emit();
|
||||
} 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() {
|
||||
Self::Deleting(p.into(), [n.into()].into()).emit();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
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_shared::{scheme::SchemeRef, url::{Url, UrlCow}};
|
||||
|
||||
use crate::config::{ProviderSftp, Vfs};
|
||||
|
||||
pub(super) struct Providers<'a>(Inner<'a>);
|
||||
|
||||
enum Inner<'a> {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
use std::{io, path::Path};
|
||||
|
||||
use yazi_config::vfs::{ProviderSftp, Vfs};
|
||||
use yazi_fs::{cha::Cha, provider::FileBuilder};
|
||||
use yazi_sftp::fs::{Attrs, Flags};
|
||||
use yazi_shared::scheme::SchemeRef;
|
||||
|
||||
use crate::config::{ProviderSftp, Vfs};
|
||||
|
||||
pub struct Gate {
|
||||
sftp: super::Sftp,
|
||||
|
||||
|
|
|
|||
|
|
@ -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 crate::config::ProviderSftp,
|
||||
&'static yazi_config::vfs::ProviderSftp,
|
||||
&'static deadpool::managed::Pool<Sftp>,
|
||||
>,
|
||||
>,
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@ use std::{io, path::{Path, PathBuf}, sync::Arc};
|
|||
|
||||
use russh::keys::PrivateKeyWithHashAlg;
|
||||
use tokio::io::{BufReader, BufWriter};
|
||||
use yazi_config::vfs::ProviderSftp;
|
||||
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 super::Cha;
|
||||
use crate::config::ProviderSftp;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Sftp {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue