mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41: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()
|
.chars()
|
||||||
.rev()
|
.rev()
|
||||||
.position(|c| c == '.')
|
.position(|c| c == '.')
|
||||||
.filter(|_| !hovered.is_dir())
|
.filter(|_| hovered.is_file())
|
||||||
.map(|i| name.chars().count() - i - 1)
|
.map(|i| name.chars().count() - i - 1)
|
||||||
.filter(|&i| i != 0),
|
.filter(|&i| i != 0),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,7 @@ impl Cha {
|
||||||
let kind = ChaKind::from_bits(t.raw_get("kind").unwrap_or_default())
|
let kind = ChaKind::from_bits(t.raw_get("kind").unwrap_or_default())
|
||||||
.ok_or_else(|| "Invalid kind".into_lua_err())?;
|
.ok_or_else(|| "Invalid kind".into_lua_err())?;
|
||||||
|
|
||||||
let mode =
|
let mode = ChaMode::try_from(t.raw_get::<u16>("mode")?)?;
|
||||||
ChaMode::from_bits(t.raw_get("mode")?).ok_or_else(|| "Invalid mode".into_lua_err())?;
|
|
||||||
|
|
||||||
Self(yazi_fs::cha::Cha {
|
Self(yazi_fs::cha::Cha {
|
||||||
kind,
|
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 anyhow::bail;
|
||||||
use yazi_macro::{unix_either, win_either};
|
use yazi_macro::{unix_either, win_either};
|
||||||
use yazi_shared::url::Url;
|
use yazi_shared::url::Url;
|
||||||
|
|
||||||
use super::ChaKind;
|
use super::ChaKind;
|
||||||
use crate::{cha::ChaMode, provider};
|
use crate::{cha::{ChaMode, ChaType}, provider};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub struct Cha {
|
pub struct Cha {
|
||||||
|
|
@ -63,7 +63,7 @@ impl Cha {
|
||||||
U: Into<Url<'a>>,
|
U: Into<Url<'a>>,
|
||||||
{
|
{
|
||||||
let url: Url = url.into();
|
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() {
|
if cha.is_link() {
|
||||||
cha = provider::metadata(url).await.unwrap_or(cha);
|
cha = provider::metadata(url).await.unwrap_or(cha);
|
||||||
|
|
@ -75,16 +75,12 @@ impl Cha {
|
||||||
cha.attach(retain)
|
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
|
where
|
||||||
U: Into<Url<'a>>,
|
U: Into<Url<'a>>,
|
||||||
{
|
{
|
||||||
let mode = ft.map(ChaMode::from_bare).unwrap_or_default();
|
|
||||||
|
|
||||||
let mut kind = ChaKind::DUMMY;
|
let mut kind = ChaKind::DUMMY;
|
||||||
if mode.is_link() {
|
let mode = r#type.map(ChaMode::from_bare).unwrap_or_default();
|
||||||
kind |= ChaKind::LINK;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
if _url.into().urn().is_hidden() {
|
if _url.into().urn().is_hidden() {
|
||||||
|
|
@ -171,7 +167,7 @@ impl Cha {
|
||||||
if let Some(atime) = self.atime {
|
if let Some(atime) = self.atime {
|
||||||
Ok(atime.duration_since(UNIX_EPOCH)?)
|
Ok(atime.duration_since(UNIX_EPOCH)?)
|
||||||
} else {
|
} else {
|
||||||
bail!("atime not supported on this platform");
|
bail!("atime not available");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -179,7 +175,7 @@ impl Cha {
|
||||||
if let Some(mtime) = self.mtime {
|
if let Some(mtime) = self.mtime {
|
||||||
Ok(mtime.duration_since(UNIX_EPOCH)?)
|
Ok(mtime.duration_since(UNIX_EPOCH)?)
|
||||||
} else {
|
} else {
|
||||||
bail!("mtime not supported on this platform");
|
bail!("mtime not available");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -187,7 +183,7 @@ impl Cha {
|
||||||
if let Some(btime) = self.btime {
|
if let Some(btime) = self.btime {
|
||||||
Ok(btime.duration_since(UNIX_EPOCH)?)
|
Ok(btime.duration_since(UNIX_EPOCH)?)
|
||||||
} else {
|
} else {
|
||||||
bail!("btime not supported on this platform");
|
bail!("btime not available");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -195,7 +191,7 @@ impl Cha {
|
||||||
if let Some(ctime) = self.ctime {
|
if let Some(ctime) = self.ctime {
|
||||||
Ok(ctime.duration_since(UNIX_EPOCH)?)
|
Ok(ctime.duration_since(UNIX_EPOCH)?)
|
||||||
} else {
|
} else {
|
||||||
bail!("ctime not supported on this platform");
|
bail!("ctime not available");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,8 @@ bitflags! {
|
||||||
pub struct ChaKind: u8 {
|
pub struct ChaKind: u8 {
|
||||||
const HIDDEN = 0b0000_0001;
|
const HIDDEN = 0b0000_0001;
|
||||||
const SYSTEM = 0b0000_0010;
|
const SYSTEM = 0b0000_0010;
|
||||||
|
const ORPHAN = 0b0000_0100;
|
||||||
const LINK = 0b0000_0100;
|
const DUMMY = 0b0000_1000;
|
||||||
const ORPHAN = 0b0000_1000;
|
|
||||||
|
|
||||||
const DUMMY = 0b0001_0000;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 bitflags::bitflags;
|
||||||
|
|
||||||
|
use crate::cha::ChaType;
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||||
pub struct ChaMode: u16 {
|
pub struct ChaMode: u16 {
|
||||||
|
|
@ -36,10 +39,43 @@ bitflags! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChaMode {
|
impl Deref for ChaMode {
|
||||||
#[inline]
|
type Target = ChaType;
|
||||||
pub fn r#type(self) -> Self { self & Self::T_MASK }
|
|
||||||
|
|
||||||
|
#[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
|
// Convert a file mode to a string representation
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
#[allow(clippy::collapsible_else_if)]
|
#[allow(clippy::collapsible_else_if)]
|
||||||
|
|
@ -47,13 +83,13 @@ impl ChaMode {
|
||||||
let mut s = *b"-?????????";
|
let mut s = *b"-?????????";
|
||||||
|
|
||||||
// File type
|
// File type
|
||||||
s[0] = match self.r#type() {
|
s[0] = match *self {
|
||||||
Self::T_DIR => b'd',
|
ChaType::Dir => b'd',
|
||||||
Self::T_LINK => b'l',
|
ChaType::Link => b'l',
|
||||||
Self::T_BLOCK => b'b',
|
ChaType::Block => b'b',
|
||||||
Self::T_CHAR => b'c',
|
ChaType::Char => b'c',
|
||||||
Self::T_SOCK => b's',
|
ChaType::Sock => b's',
|
||||||
Self::T_FIFO => b'p',
|
ChaType::FIFO => b'p',
|
||||||
_ => b'-',
|
_ => b'-',
|
||||||
};
|
};
|
||||||
if dummy {
|
if dummy {
|
||||||
|
|
@ -90,69 +126,25 @@ impl ChaMode {
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn from_bare(ft: FileType) -> Self {
|
pub(super) fn from_bare(r#type: ChaType) -> Self {
|
||||||
#[cfg(unix)]
|
match r#type {
|
||||||
{
|
ChaType::File => Self::T_FILE,
|
||||||
use std::os::unix::fs::FileTypeExt;
|
ChaType::Dir => Self::T_DIR,
|
||||||
if ft.is_file() {
|
ChaType::Link => Self::T_LINK,
|
||||||
Self::T_FILE
|
ChaType::Block => Self::T_BLOCK,
|
||||||
} else if ft.is_dir() {
|
ChaType::Char => Self::T_CHAR,
|
||||||
Self::T_DIR
|
ChaType::Sock => Self::T_SOCK,
|
||||||
} else if ft.is_symlink() {
|
ChaType::FIFO => Self::T_FIFO,
|
||||||
Self::T_LINK
|
ChaType::Unknown => Self::empty(),
|
||||||
} 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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChaMode {
|
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
|
// TODO: deprecate
|
||||||
#[inline]
|
#[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]
|
#[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 anyhow::Result;
|
||||||
use yazi_shared::url::{Uri, UrlBuf, UrlCow, Urn};
|
use yazi_shared::url::{Uri, UrlBuf, UrlCow, Urn};
|
||||||
|
|
||||||
use crate::{cha::Cha, provider};
|
use crate::{cha::{Cha, ChaType}, provider};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct File {
|
pub struct File {
|
||||||
|
|
@ -36,9 +36,9 @@ impl File {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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 url = url.into();
|
||||||
let cha = Cha::from_dummy(&url, ft);
|
let cha = Cha::from_dummy(&url, r#type);
|
||||||
Self { url, cha, link_to: None }
|
Self { url, cha, link_to: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,14 +40,14 @@ impl Files {
|
||||||
let (tx, rx) = mpsc::unbounded_channel();
|
let (tx, rx) = mpsc::unbounded_channel();
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
while let Ok(Some(item)) = it.next_entry().await {
|
while let Ok(Some(ent)) = it.next_entry().await {
|
||||||
select! {
|
select! {
|
||||||
_ = tx.closed() => break,
|
_ = tx.closed() => break,
|
||||||
result = item.metadata() => {
|
result = ent.metadata() => {
|
||||||
let url = item.url();
|
let url = ent.url();
|
||||||
_ = tx.send(match result {
|
_ = tx.send(match result {
|
||||||
Ok(cha) => File::from_follow(url, cha).await,
|
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);
|
let (second, third) = rest.split_at(entries.len() / 3);
|
||||||
async fn go(entries: &[DirEntry]) -> Vec<File> {
|
async fn go(entries: &[DirEntry]) -> Vec<File> {
|
||||||
let mut files = Vec::with_capacity(entries.len());
|
let mut files = Vec::with_capacity(entries.len());
|
||||||
for entry in entries {
|
for ent in entries {
|
||||||
let url = entry.url();
|
let url = ent.url();
|
||||||
files.push(match entry.metadata().await {
|
files.push(match ent.metadata().await {
|
||||||
Ok(cha) => File::from_follow(url, cha).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
|
files
|
||||||
|
|
|
||||||
|
|
@ -83,9 +83,9 @@ pub fn copy_with_progress(
|
||||||
pub async fn remove_dir_clean(dir: &UrlBuf) {
|
pub async fn remove_dir_clean(dir: &UrlBuf) {
|
||||||
let Ok(mut it) = provider::read_dir(dir).await else { return };
|
let Ok(mut it) = provider::read_dir(dir).await else { return };
|
||||||
|
|
||||||
while let Ok(Some(entry)) = it.next_entry().await {
|
while let Ok(Some(ent)) = it.next_entry().await {
|
||||||
if entry.file_type().await.is_ok_and(|t| t.is_dir()) {
|
if ent.file_type().await.is_ok_and(|t| t.is_dir()) {
|
||||||
let url = entry.url();
|
let url = ent.url();
|
||||||
Box::pin(remove_dir_clean(&url)).await;
|
Box::pin(remove_dir_clean(&url)).await;
|
||||||
provider::remove_dir(&url).await.ok();
|
provider::remove_dir(&url).await.ok();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use std::{borrow::Cow, ffi::OsStr, io};
|
||||||
|
|
||||||
use yazi_shared::url::UrlBuf;
|
use yazi_shared::url::UrlBuf;
|
||||||
|
|
||||||
use crate::{cha::Cha, provider::FileHolder};
|
use crate::{cha::{Cha, ChaType}, provider::FileHolder};
|
||||||
|
|
||||||
pub enum DirEntry {
|
pub enum DirEntry {
|
||||||
Local(super::local::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 {
|
match self {
|
||||||
Self::Local(local) => local.file_type().await,
|
Self::Local(local) => local.file_type().await,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{borrow::Cow, ffi::OsStr, io, path::PathBuf};
|
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);
|
pub struct DirEntry(pub(super) tokio::fs::DirEntry);
|
||||||
|
|
||||||
|
|
@ -14,5 +14,5 @@ impl FileHolder for DirEntry {
|
||||||
Ok(Cha::new(&name, self.0.metadata().await?))
|
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>> =
|
pub(super) static CONN: yazi_shared::RoCell<deadpool::managed::Pool<Sftp>> =
|
||||||
yazi_shared::RoCell::new();
|
yazi_shared::RoCell::new();
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{borrow::Cow, ffi::OsStr, io, path::PathBuf};
|
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);
|
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() }
|
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>,
|
P: AsRef<Path>,
|
||||||
Q: AsRef<Path>,
|
Q: AsRef<Path>,
|
||||||
{
|
{
|
||||||
// FIXME: pull this out to a From<Cha> for Attrs impl
|
let attrs = Attrs::from(cha);
|
||||||
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 op = Self::op().await?;
|
let op = Self::op().await?;
|
||||||
|
|
||||||
let mut from = op.open(&from, Flags::READ, Attrs::default()).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?;
|
let mut to = op.open(&to, Flags::WRITE | Flags::CREATE | Flags::TRUNCATE, attrs).await?;
|
||||||
|
|
||||||
|
|
@ -68,7 +58,9 @@ impl Provider for Sftp {
|
||||||
where
|
where
|
||||||
P: AsRef<Path>,
|
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>
|
async fn read_dir<P>(path: P) -> io::Result<Self::ReadDir>
|
||||||
|
|
@ -120,7 +112,9 @@ impl Provider for Sftp {
|
||||||
where
|
where
|
||||||
P: AsRef<Path>,
|
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<()>
|
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 tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt};
|
||||||
use yazi_macro::ok_or_not_found;
|
use yazi_macro::ok_or_not_found;
|
||||||
|
|
||||||
use crate::cha::Cha;
|
use crate::cha::{Cha, ChaType};
|
||||||
|
|
||||||
pub trait Provider {
|
pub trait Provider {
|
||||||
type File: AsyncRead + AsyncWrite + Unpin;
|
type File: AsyncRead + AsyncWrite + Unpin;
|
||||||
|
|
@ -189,7 +189,7 @@ pub trait FileHolder {
|
||||||
|
|
||||||
fn metadata(&self) -> impl Future<Output = io::Result<Cha>>;
|
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
|
// --- FileOpener
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue