mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01:05 +00:00
feat: introduce ChaType for better cross-filesystem consistency (#3175)
This commit is contained in:
parent
ece59974fe
commit
30c0279570
17 changed files with 249 additions and 127 deletions
|
|
@ -33,7 +33,7 @@ impl Actor for Rename {
|
|||
.chars()
|
||||
.rev()
|
||||
.position(|c| c == '.')
|
||||
.filter(|_| !hovered.is_dir())
|
||||
.filter(|_| hovered.is_file())
|
||||
.map(|i| name.chars().count() - i - 1)
|
||||
.filter(|&i| i != 0),
|
||||
_ => None,
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@ impl Cha {
|
|||
let kind = ChaKind::from_bits(t.raw_get("kind").unwrap_or_default())
|
||||
.ok_or_else(|| "Invalid kind".into_lua_err())?;
|
||||
|
||||
let mode =
|
||||
ChaMode::from_bits(t.raw_get("mode")?).ok_or_else(|| "Invalid mode".into_lua_err())?;
|
||||
let mode = ChaMode::try_from(t.raw_get::<u16>("mode")?)?;
|
||||
|
||||
Self(yazi_fs::cha::Cha {
|
||||
kind,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use std::{ffi::OsStr, fs::{FileType, Metadata}, ops::Deref, time::{Duration, SystemTime, UNIX_EPOCH}};
|
||||
use std::{ffi::OsStr, fs::Metadata, ops::Deref, time::{Duration, SystemTime, UNIX_EPOCH}};
|
||||
|
||||
use anyhow::bail;
|
||||
use yazi_macro::{unix_either, win_either};
|
||||
use yazi_shared::url::Url;
|
||||
|
||||
use super::ChaKind;
|
||||
use crate::{cha::ChaMode, provider};
|
||||
use crate::{cha::{ChaMode, ChaType}, provider};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct Cha {
|
||||
|
|
@ -63,7 +63,7 @@ impl Cha {
|
|||
U: Into<Url<'a>>,
|
||||
{
|
||||
let url: Url = url.into();
|
||||
let mut retain = cha.kind & (ChaKind::HIDDEN | ChaKind::SYSTEM | ChaKind::LINK);
|
||||
let mut retain = cha.kind & (ChaKind::HIDDEN | ChaKind::SYSTEM);
|
||||
|
||||
if cha.is_link() {
|
||||
cha = provider::metadata(url).await.unwrap_or(cha);
|
||||
|
|
@ -75,16 +75,12 @@ impl Cha {
|
|||
cha.attach(retain)
|
||||
}
|
||||
|
||||
pub fn from_dummy<'a, U>(_url: U, ft: Option<FileType>) -> Self
|
||||
pub fn from_dummy<'a, U>(_url: U, r#type: Option<ChaType>) -> Self
|
||||
where
|
||||
U: Into<Url<'a>>,
|
||||
{
|
||||
let mode = ft.map(ChaMode::from_bare).unwrap_or_default();
|
||||
|
||||
let mut kind = ChaKind::DUMMY;
|
||||
if mode.is_link() {
|
||||
kind |= ChaKind::LINK;
|
||||
}
|
||||
let mode = r#type.map(ChaMode::from_bare).unwrap_or_default();
|
||||
|
||||
#[cfg(unix)]
|
||||
if _url.into().urn().is_hidden() {
|
||||
|
|
@ -171,7 +167,7 @@ impl Cha {
|
|||
if let Some(atime) = self.atime {
|
||||
Ok(atime.duration_since(UNIX_EPOCH)?)
|
||||
} else {
|
||||
bail!("atime not supported on this platform");
|
||||
bail!("atime not available");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -179,7 +175,7 @@ impl Cha {
|
|||
if let Some(mtime) = self.mtime {
|
||||
Ok(mtime.duration_since(UNIX_EPOCH)?)
|
||||
} else {
|
||||
bail!("mtime not supported on this platform");
|
||||
bail!("mtime not available");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -187,7 +183,7 @@ impl Cha {
|
|||
if let Some(btime) = self.btime {
|
||||
Ok(btime.duration_since(UNIX_EPOCH)?)
|
||||
} else {
|
||||
bail!("btime not supported on this platform");
|
||||
bail!("btime not available");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -195,7 +191,7 @@ impl Cha {
|
|||
if let Some(ctime) = self.ctime {
|
||||
Ok(ctime.duration_since(UNIX_EPOCH)?)
|
||||
} else {
|
||||
bail!("ctime not supported on this platform");
|
||||
bail!("ctime not available");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,11 +7,8 @@ bitflags! {
|
|||
pub struct ChaKind: u8 {
|
||||
const HIDDEN = 0b0000_0001;
|
||||
const SYSTEM = 0b0000_0010;
|
||||
|
||||
const LINK = 0b0000_0100;
|
||||
const ORPHAN = 0b0000_1000;
|
||||
|
||||
const DUMMY = 0b0001_0000;
|
||||
const ORPHAN = 0b0000_0100;
|
||||
const DUMMY = 0b0000_1000;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
yazi_macro::mod_flat!(cha kind mode);
|
||||
yazi_macro::mod_flat!(cha kind mode r#type);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
use std::fs::FileType;
|
||||
use std::ops::Deref;
|
||||
|
||||
use anyhow::{anyhow, bail};
|
||||
use bitflags::bitflags;
|
||||
|
||||
use crate::cha::ChaType;
|
||||
|
||||
bitflags! {
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub struct ChaMode: u16 {
|
||||
|
|
@ -36,10 +39,43 @@ bitflags! {
|
|||
}
|
||||
}
|
||||
|
||||
impl ChaMode {
|
||||
#[inline]
|
||||
pub fn r#type(self) -> Self { self & Self::T_MASK }
|
||||
impl Deref for ChaMode {
|
||||
type Target = ChaType;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match *self & Self::T_MASK {
|
||||
Self::T_FILE => &ChaType::File,
|
||||
Self::T_DIR => &ChaType::Dir,
|
||||
Self::T_LINK => &ChaType::Link,
|
||||
Self::T_BLOCK => &ChaType::Block,
|
||||
Self::T_CHAR => &ChaType::Char,
|
||||
Self::T_SOCK => &ChaType::Sock,
|
||||
Self::T_FIFO => &ChaType::FIFO,
|
||||
_ => &ChaType::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u16> for ChaMode {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: u16) -> Result<Self, Self::Error> {
|
||||
let me = Self::from_bits(value).ok_or_else(|| anyhow!("invalid file mode: {value:04o}"))?;
|
||||
match me & Self::T_MASK {
|
||||
Self::T_FILE
|
||||
| Self::T_DIR
|
||||
| Self::T_LINK
|
||||
| Self::T_BLOCK
|
||||
| Self::T_CHAR
|
||||
| Self::T_SOCK
|
||||
| Self::T_FIFO => Ok(me),
|
||||
_ => bail!("invalid file type: {value:04o}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ChaMode {
|
||||
// Convert a file mode to a string representation
|
||||
#[cfg(unix)]
|
||||
#[allow(clippy::collapsible_else_if)]
|
||||
|
|
@ -47,13 +83,13 @@ impl ChaMode {
|
|||
let mut s = *b"-?????????";
|
||||
|
||||
// File type
|
||||
s[0] = match self.r#type() {
|
||||
Self::T_DIR => b'd',
|
||||
Self::T_LINK => b'l',
|
||||
Self::T_BLOCK => b'b',
|
||||
Self::T_CHAR => b'c',
|
||||
Self::T_SOCK => b's',
|
||||
Self::T_FIFO => b'p',
|
||||
s[0] = match *self {
|
||||
ChaType::Dir => b'd',
|
||||
ChaType::Link => b'l',
|
||||
ChaType::Block => b'b',
|
||||
ChaType::Char => b'c',
|
||||
ChaType::Sock => b's',
|
||||
ChaType::FIFO => b'p',
|
||||
_ => b'-',
|
||||
};
|
||||
if dummy {
|
||||
|
|
@ -90,69 +126,25 @@ impl ChaMode {
|
|||
s
|
||||
}
|
||||
|
||||
pub(super) fn from_bare(ft: FileType) -> Self {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::FileTypeExt;
|
||||
if ft.is_file() {
|
||||
Self::T_FILE
|
||||
} else if ft.is_dir() {
|
||||
Self::T_DIR
|
||||
} else if ft.is_symlink() {
|
||||
Self::T_LINK
|
||||
} else if ft.is_block_device() {
|
||||
Self::T_BLOCK
|
||||
} else if ft.is_char_device() {
|
||||
Self::T_CHAR
|
||||
} else if ft.is_socket() {
|
||||
Self::T_SOCK
|
||||
} else if ft.is_fifo() {
|
||||
Self::T_FIFO
|
||||
} else {
|
||||
Self::empty()
|
||||
}
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
if ft.is_file() {
|
||||
Self::T_FILE
|
||||
} else if ft.is_dir() {
|
||||
Self::T_DIR
|
||||
} else if ft.is_symlink() {
|
||||
Self::T_LINK
|
||||
} else {
|
||||
Self::empty()
|
||||
}
|
||||
pub(super) fn from_bare(r#type: ChaType) -> Self {
|
||||
match r#type {
|
||||
ChaType::File => Self::T_FILE,
|
||||
ChaType::Dir => Self::T_DIR,
|
||||
ChaType::Link => Self::T_LINK,
|
||||
ChaType::Block => Self::T_BLOCK,
|
||||
ChaType::Char => Self::T_CHAR,
|
||||
ChaType::Sock => Self::T_SOCK,
|
||||
ChaType::FIFO => Self::T_FIFO,
|
||||
ChaType::Unknown => Self::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ChaMode {
|
||||
#[inline]
|
||||
pub const fn is_file(self) -> bool { self.contains(Self::T_FILE) }
|
||||
|
||||
#[inline]
|
||||
pub const fn is_dir(self) -> bool { self.contains(Self::T_DIR) }
|
||||
|
||||
#[inline]
|
||||
pub const fn is_link(&self) -> bool { self.contains(Self::T_LINK) }
|
||||
|
||||
#[inline]
|
||||
pub const fn is_block(&self) -> bool { self.contains(Self::T_BLOCK) }
|
||||
|
||||
#[inline]
|
||||
pub const fn is_char(&self) -> bool { self.contains(Self::T_CHAR) }
|
||||
|
||||
#[inline]
|
||||
pub const fn is_sock(&self) -> bool { self.contains(Self::T_SOCK) }
|
||||
|
||||
#[inline]
|
||||
pub const fn is_fifo(&self) -> bool { self.contains(Self::T_FIFO) }
|
||||
|
||||
// TODO: deprecate
|
||||
#[inline]
|
||||
pub const fn is_exec(&self) -> bool { self.contains(Self::U_EXEC) }
|
||||
pub const fn is_exec(self) -> bool { self.contains(Self::U_EXEC) }
|
||||
|
||||
#[inline]
|
||||
pub const fn is_sticky(&self) -> bool { self.contains(Self::S_STICKY) }
|
||||
pub const fn is_sticky(self) -> bool { self.contains(Self::S_STICKY) }
|
||||
}
|
||||
|
|
|
|||
80
yazi-fs/src/cha/type.rs
Normal file
80
yazi-fs/src/cha/type.rs
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
use std::fs::FileType;
|
||||
|
||||
use crate::cha::ChaMode;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum ChaType {
|
||||
File,
|
||||
Dir,
|
||||
Link,
|
||||
Block,
|
||||
Char,
|
||||
Sock,
|
||||
FIFO,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl From<ChaMode> for ChaType {
|
||||
fn from(value: ChaMode) -> Self { *value }
|
||||
}
|
||||
|
||||
impl From<FileType> for ChaType {
|
||||
fn from(value: FileType) -> Self {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::FileTypeExt;
|
||||
if value.is_file() {
|
||||
Self::File
|
||||
} else if value.is_dir() {
|
||||
Self::Dir
|
||||
} else if value.is_symlink() {
|
||||
Self::Link
|
||||
} else if value.is_block_device() {
|
||||
Self::Block
|
||||
} else if value.is_char_device() {
|
||||
Self::Char
|
||||
} else if value.is_socket() {
|
||||
Self::Sock
|
||||
} else if value.is_fifo() {
|
||||
Self::FIFO
|
||||
} else {
|
||||
Self::Unknown
|
||||
}
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
if value.is_file() {
|
||||
Self::File
|
||||
} else if value.is_dir() {
|
||||
Self::Dir
|
||||
} else if value.is_symlink() {
|
||||
Self::Link
|
||||
} else {
|
||||
Self::Unknown
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ChaType {
|
||||
#[inline]
|
||||
pub fn is_file(self) -> bool { self == Self::File }
|
||||
|
||||
#[inline]
|
||||
pub fn is_dir(self) -> bool { self == Self::Dir }
|
||||
|
||||
#[inline]
|
||||
pub fn is_link(self) -> bool { self == Self::Link }
|
||||
|
||||
#[inline]
|
||||
pub fn is_block(self) -> bool { self == Self::Block }
|
||||
|
||||
#[inline]
|
||||
pub fn is_char(self) -> bool { self == Self::Char }
|
||||
|
||||
#[inline]
|
||||
pub fn is_sock(self) -> bool { self == Self::Sock }
|
||||
|
||||
#[inline]
|
||||
pub fn is_fifo(self) -> bool { self == Self::FIFO }
|
||||
}
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
use std::{ffi::OsStr, fs::FileType, hash::{BuildHasher, Hash, Hasher}, ops::Deref, path::{Path, PathBuf}};
|
||||
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 crate::{cha::Cha, provider};
|
||||
use crate::{cha::{Cha, ChaType}, provider};
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct File {
|
||||
|
|
@ -36,9 +36,9 @@ impl File {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_dummy(url: impl Into<UrlBuf>, ft: Option<FileType>) -> Self {
|
||||
pub fn from_dummy(url: impl Into<UrlBuf>, r#type: Option<ChaType>) -> Self {
|
||||
let url = url.into();
|
||||
let cha = Cha::from_dummy(&url, ft);
|
||||
let cha = Cha::from_dummy(&url, r#type);
|
||||
Self { url, cha, link_to: None }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,14 +40,14 @@ impl Files {
|
|||
let (tx, rx) = mpsc::unbounded_channel();
|
||||
|
||||
tokio::spawn(async move {
|
||||
while let Ok(Some(item)) = it.next_entry().await {
|
||||
while let Ok(Some(ent)) = it.next_entry().await {
|
||||
select! {
|
||||
_ = tx.closed() => break,
|
||||
result = item.metadata() => {
|
||||
let url = item.url();
|
||||
result = ent.metadata() => {
|
||||
let url = ent.url();
|
||||
_ = tx.send(match result {
|
||||
Ok(cha) => File::from_follow(url, cha).await,
|
||||
Err(_) => File::from_dummy(url, item.file_type().await.ok())
|
||||
Err(_) => File::from_dummy(url, ent.file_type().await.ok())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -67,11 +67,11 @@ impl Files {
|
|||
let (second, third) = rest.split_at(entries.len() / 3);
|
||||
async fn go(entries: &[DirEntry]) -> Vec<File> {
|
||||
let mut files = Vec::with_capacity(entries.len());
|
||||
for entry in entries {
|
||||
let url = entry.url();
|
||||
files.push(match entry.metadata().await {
|
||||
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, entry.file_type().await.ok()),
|
||||
Err(_) => File::from_dummy(url, ent.file_type().await.ok()),
|
||||
});
|
||||
}
|
||||
files
|
||||
|
|
|
|||
|
|
@ -83,9 +83,9 @@ pub fn copy_with_progress(
|
|||
pub async fn remove_dir_clean(dir: &UrlBuf) {
|
||||
let Ok(mut it) = provider::read_dir(dir).await else { return };
|
||||
|
||||
while let Ok(Some(entry)) = it.next_entry().await {
|
||||
if entry.file_type().await.is_ok_and(|t| t.is_dir()) {
|
||||
let url = entry.url();
|
||||
while let Ok(Some(ent)) = it.next_entry().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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::{borrow::Cow, ffi::OsStr, io};
|
|||
|
||||
use yazi_shared::url::UrlBuf;
|
||||
|
||||
use crate::{cha::Cha, provider::FileHolder};
|
||||
use crate::{cha::{Cha, ChaType}, provider::FileHolder};
|
||||
|
||||
pub enum DirEntry {
|
||||
Local(super::local::DirEntry),
|
||||
|
|
@ -33,7 +33,7 @@ impl DirEntry {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn file_type(&self) -> io::Result<std::fs::FileType> {
|
||||
pub async fn file_type(&self) -> io::Result<ChaType> {
|
||||
match self {
|
||||
Self::Local(local) => local.file_type().await,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::{borrow::Cow, ffi::OsStr, io, path::PathBuf};
|
||||
|
||||
use crate::{cha::Cha, provider::FileHolder};
|
||||
use crate::{cha::{Cha, ChaType}, provider::FileHolder};
|
||||
|
||||
pub struct DirEntry(pub(super) tokio::fs::DirEntry);
|
||||
|
||||
|
|
@ -14,5 +14,5 @@ impl FileHolder for DirEntry {
|
|||
Ok(Cha::new(&name, self.0.metadata().await?))
|
||||
}
|
||||
|
||||
async fn file_type(&self) -> io::Result<std::fs::FileType> { self.0.file_type().await }
|
||||
async fn file_type(&self) -> io::Result<ChaType> { self.0.file_type().await.map(Into::into) }
|
||||
}
|
||||
|
|
|
|||
59
yazi-fs/src/provider/sftp/metadata.rs
Normal file
59
yazi-fs/src/provider/sftp/metadata.rs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
use std::{ffi::OsStr, io, time::{Duration, UNIX_EPOCH}};
|
||||
|
||||
use crate::cha::{Cha, ChaKind, ChaMode};
|
||||
|
||||
impl TryFrom<yazi_sftp::fs::DirEntry<'_>> for Cha {
|
||||
type Error = io::Error;
|
||||
|
||||
fn try_from(ent: yazi_sftp::fs::DirEntry<'_>) -> Result<Self, Self::Error> {
|
||||
let mut cha = Self::try_from((ent.name().as_ref(), ent.attrs()))?;
|
||||
cha.nlink = ent.nlink().unwrap_or_default();
|
||||
Ok(cha)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<(&OsStr, &yazi_sftp::fs::Attrs)> for Cha {
|
||||
type Error = io::Error;
|
||||
|
||||
fn try_from((name, attrs): (&OsStr, &yazi_sftp::fs::Attrs)) -> Result<Self, Self::Error> {
|
||||
let kind =
|
||||
if name.as_encoded_bytes().starts_with(b".") { ChaKind::HIDDEN } else { ChaKind::empty() };
|
||||
|
||||
Ok(Cha {
|
||||
kind,
|
||||
mode: attrs.try_into()?,
|
||||
len: attrs.size.unwrap_or(0),
|
||||
atime: attrs.atime.and_then(|t| UNIX_EPOCH.checked_add(Duration::from_secs(t as u64))),
|
||||
btime: None,
|
||||
ctime: None,
|
||||
mtime: attrs.mtime.and_then(|t| UNIX_EPOCH.checked_add(Duration::from_secs(t as u64))),
|
||||
dev: 0,
|
||||
uid: attrs.uid.unwrap_or(0),
|
||||
gid: attrs.gid.unwrap_or(0),
|
||||
nlink: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&yazi_sftp::fs::Attrs> for ChaMode {
|
||||
type Error = io::Error;
|
||||
|
||||
fn try_from(attrs: &yazi_sftp::fs::Attrs) -> Result<Self, Self::Error> {
|
||||
ChaMode::try_from(attrs.perm.unwrap_or_default() as u16)
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Cha> 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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
yazi_macro::mod_flat!(gate read_dir sftp);
|
||||
yazi_macro::mod_flat!(gate metadata read_dir sftp);
|
||||
|
||||
pub(super) static CONN: yazi_shared::RoCell<deadpool::managed::Pool<Sftp>> =
|
||||
yazi_shared::RoCell::new();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::{borrow::Cow, ffi::OsStr, io, path::PathBuf};
|
||||
|
||||
use crate::{cha::Cha, provider::{DirReader, FileHolder}};
|
||||
use crate::{cha::{Cha, ChaMode, ChaType}, provider::{DirReader, FileHolder}};
|
||||
|
||||
pub struct ReadDir(pub(super) yazi_sftp::fs::ReadDir);
|
||||
|
||||
|
|
@ -20,7 +20,12 @@ impl FileHolder for DirEntry<'_> {
|
|||
|
||||
fn name(&self) -> Cow<'_, OsStr> { self.0.name() }
|
||||
|
||||
async fn metadata(&self) -> io::Result<Cha> { todo!() }
|
||||
async fn metadata(&self) -> io::Result<Cha> {
|
||||
let name = self.name();
|
||||
(name.as_ref(), self.0.attrs()).try_into()
|
||||
}
|
||||
|
||||
async fn file_type(&self) -> io::Result<std::fs::FileType> { todo!() }
|
||||
async fn file_type(&self) -> io::Result<ChaType> {
|
||||
ChaMode::try_from(self.0.attrs()).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,19 +30,9 @@ impl Provider for Sftp {
|
|||
P: AsRef<Path>,
|
||||
Q: AsRef<Path>,
|
||||
{
|
||||
// FIXME: pull this out to a From<Cha> for Attrs impl
|
||||
let attrs = Attrs {
|
||||
size: Some(cha.len),
|
||||
uid: Some(cha.uid),
|
||||
gid: Some(cha.gid),
|
||||
perm: Some(cha.mode.bits() as _),
|
||||
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(),
|
||||
};
|
||||
let attrs = Attrs::from(cha);
|
||||
|
||||
let op = Self::op().await?;
|
||||
|
||||
let mut from = op.open(&from, Flags::READ, Attrs::default()).await?;
|
||||
let mut to = op.open(&to, Flags::WRITE | Flags::CREATE | Flags::TRUNCATE, attrs).await?;
|
||||
|
||||
|
|
@ -68,7 +58,9 @@ impl Provider for Sftp {
|
|||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
todo!()
|
||||
let path = path.as_ref();
|
||||
let attrs = Self::op().await?.stat(path).await?;
|
||||
(path.file_name().unwrap_or_default(), &attrs).try_into()
|
||||
}
|
||||
|
||||
async fn read_dir<P>(path: P) -> io::Result<Self::ReadDir>
|
||||
|
|
@ -120,7 +112,9 @@ impl Provider for Sftp {
|
|||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
todo!()
|
||||
let path = path.as_ref();
|
||||
let attrs = Self::op().await?.lstat(path).await?;
|
||||
(path.file_name().unwrap_or_default(), &attrs).try_into()
|
||||
}
|
||||
|
||||
async fn trash<P>(_path: P) -> io::Result<()>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::{borrow::Cow, ffi::OsStr, io, path::{Path, PathBuf}};
|
|||
use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt};
|
||||
use yazi_macro::ok_or_not_found;
|
||||
|
||||
use crate::cha::Cha;
|
||||
use crate::cha::{Cha, ChaType};
|
||||
|
||||
pub trait Provider {
|
||||
type File: AsyncRead + AsyncWrite + Unpin;
|
||||
|
|
@ -189,7 +189,7 @@ pub trait FileHolder {
|
|||
|
||||
fn metadata(&self) -> impl Future<Output = io::Result<Cha>>;
|
||||
|
||||
fn file_type(&self) -> impl Future<Output = io::Result<std::fs::FileType>>;
|
||||
fn file_type(&self) -> impl Future<Output = io::Result<ChaType>>;
|
||||
}
|
||||
|
||||
// --- FileOpener
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue