mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 14:51:03 +00:00
feat: support Unix path backend for the SFTP provider (#3371)
This commit is contained in:
parent
a1fb206a59
commit
329c80cbee
46 changed files with 410 additions and 401 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -5065,6 +5065,7 @@ dependencies = [
|
|||
"russh",
|
||||
"serde",
|
||||
"tokio",
|
||||
"typed-path",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5123,6 +5124,7 @@ dependencies = [
|
|||
"russh",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"typed-path",
|
||||
"yazi-config",
|
||||
"yazi-fs",
|
||||
"yazi-macro",
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ impl File {
|
|||
Url::Archive { loc: Loc::saturated(to.as_os().ok()?, kind), domain }
|
||||
}
|
||||
UrlBuf::Sftp { domain, .. } => {
|
||||
Url::Sftp { loc: Loc::saturated(to.as_os().ok()?, kind), domain }
|
||||
Url::Sftp { loc: Loc::saturated(to.as_unix().ok()?, kind), domain }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,10 @@ fn expand_url_impl<'a>(url: Url<'a>) -> UrlCow<'a> {
|
|||
Url::Regular(_) => UrlBuf::Regular(loc),
|
||||
Url::Search { domain, .. } => UrlBuf::Search { loc, domain: domain.intern() },
|
||||
Url::Archive { domain, .. } => UrlBuf::Archive { loc, domain: domain.intern() },
|
||||
Url::Sftp { domain, .. } => UrlBuf::Sftp { loc, domain: domain.intern() },
|
||||
Url::Sftp { domain, .. } => {
|
||||
todo!();
|
||||
// UrlBuf::Sftp { loc, domain: domain.intern() }
|
||||
}
|
||||
};
|
||||
|
||||
absolute_url(expanded)
|
||||
|
|
|
|||
|
|
@ -17,3 +17,4 @@ parking_lot = { workspace = true }
|
|||
russh = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
typed-path = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -1,114 +0,0 @@
|
|||
use std::{borrow::Cow, ffi::{OsStr, OsString}, ops::Deref, path::{Path, PathBuf}};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::Error;
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Serialize)]
|
||||
pub struct ByteStr<'a>(Cow<'a, [u8]>);
|
||||
|
||||
impl Deref for ByteStr<'_> {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.0 }
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for ByteStr<'a> {
|
||||
fn from(value: &'a str) -> Self { ByteStr(Cow::Borrowed(value.as_bytes())) }
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Self> for ByteStr<'a> {
|
||||
fn from(value: &'a ByteStr) -> Self { ByteStr(Cow::Borrowed(&value.0)) }
|
||||
}
|
||||
|
||||
impl PartialEq<&str> for ByteStr<'_> {
|
||||
fn eq(&self, other: &&str) -> bool { self.0 == other.as_bytes() }
|
||||
}
|
||||
|
||||
impl<'a> ByteStr<'a> {
|
||||
pub fn to_os_str(&self) -> Cow<'_, OsStr> {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
OsStr::from_bytes(&self.0).into()
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
super::wtf::bytes_to_wide(self.0.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_os_string(self) -> OsString {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::ffi::OsStringExt;
|
||||
OsString::from_vec(self.0.into_owned())
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
match super::wtf::bytes_to_wide(self.0.as_ref()) {
|
||||
Cow::Borrowed(_) => unsafe { String::from_utf8_unchecked(self.0.into_owned()) }.into(),
|
||||
Cow::Owned(s) => s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_path(&self) -> Cow<'_, Path> {
|
||||
match self.to_os_str() {
|
||||
Cow::Borrowed(s) => Path::new(s).into(),
|
||||
Cow::Owned(s) => PathBuf::from(s).into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_path(self) -> PathBuf {
|
||||
match self.0 {
|
||||
Cow::Borrowed(_) => self.to_path().into_owned(),
|
||||
Cow::Owned(_) => self.into_os_string().into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn join(&self, other: impl Into<Self>) -> PathBuf {
|
||||
let other = other.into();
|
||||
match self.to_path() {
|
||||
Cow::Borrowed(p) => p.join(other.to_path()),
|
||||
Cow::Owned(mut p) => {
|
||||
p.push(other.to_path());
|
||||
p
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_owned(self) -> ByteStr<'static> { ByteStr(Cow::Owned(self.0.into_owned())) }
|
||||
|
||||
pub(super) unsafe fn from_str_bytes_unchecked(bytes: &'a [u8]) -> Self {
|
||||
Self(Cow::Borrowed(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
// --- Traits
|
||||
pub trait ToByteStr<'a> {
|
||||
fn to_byte_str(self) -> Result<ByteStr<'a>, Error>;
|
||||
}
|
||||
|
||||
impl<'a, T> ToByteStr<'a> for &'a T
|
||||
where
|
||||
T: AsRef<Path> + ?Sized,
|
||||
{
|
||||
fn to_byte_str(self) -> Result<ByteStr<'a>, Error> {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
Ok(ByteStr(Cow::Borrowed(self.as_ref().as_os_str().as_bytes())))
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
super::wtf::wide_to_bytes(self.as_ref().as_os_str())
|
||||
.ok_or(Error::custom("failed to convert wide path to bytes"))
|
||||
.map(ByteStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToByteStr<'a> for &'a ByteStr<'a> {
|
||||
fn to_byte_str(self) -> Result<ByteStr<'a>, Error> { Ok(ByteStr(Cow::Borrowed(&self.0))) }
|
||||
}
|
||||
|
|
@ -1,47 +1,31 @@
|
|||
use std::{borrow::Cow, ffi::OsStr, path::PathBuf, sync::Arc};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{ByteStr, fs::Attrs};
|
||||
use typed_path::UnixPathBuf;
|
||||
|
||||
use crate::fs::Attrs;
|
||||
|
||||
pub struct DirEntry {
|
||||
pub(super) dir: Arc<ByteStr<'static>>,
|
||||
pub(super) name: ByteStr<'static>,
|
||||
pub(super) long_name: ByteStr<'static>,
|
||||
pub(super) dir: Arc<typed_path::UnixPathBuf>,
|
||||
pub(super) name: Vec<u8>,
|
||||
pub(super) long_name: Vec<u8>,
|
||||
pub(super) attrs: Attrs,
|
||||
}
|
||||
|
||||
impl DirEntry {
|
||||
#[must_use]
|
||||
pub fn path(&self) -> PathBuf { self.dir.join(&self.name) }
|
||||
pub fn path(&self) -> UnixPathBuf { self.dir.join(&self.name) }
|
||||
|
||||
#[must_use]
|
||||
pub fn name(&self) -> Cow<'_, OsStr> { self.name.to_os_str() }
|
||||
pub fn name(&self) -> &[u8] { &self.name }
|
||||
|
||||
#[must_use]
|
||||
pub fn long_name(&self) -> Cow<'_, OsStr> { self.long_name.to_os_str() }
|
||||
pub fn long_name(&self) -> &[u8] { &self.long_name }
|
||||
|
||||
pub fn attrs(&self) -> &Attrs { &self.attrs }
|
||||
|
||||
pub fn nlink(&self) -> Option<u64> { str::from_utf8(self.long_name_field(1)?).ok()?.parse().ok() }
|
||||
|
||||
pub fn user(&self) -> Option<Cow<'_, OsStr>> {
|
||||
let b = self.long_name_field(2)?;
|
||||
Some(unsafe {
|
||||
match ByteStr::from_str_bytes_unchecked(b).to_os_str() {
|
||||
Cow::Borrowed(_) => OsStr::from_encoded_bytes_unchecked(b).into(),
|
||||
Cow::Owned(s) => s.into(),
|
||||
}
|
||||
})
|
||||
}
|
||||
pub fn user(&self) -> Option<&[u8]> { self.long_name_field(2) }
|
||||
|
||||
pub fn group(&self) -> Option<Cow<'_, OsStr>> {
|
||||
let b = self.long_name_field(3)?;
|
||||
Some(unsafe {
|
||||
match ByteStr::from_str_bytes_unchecked(b).to_os_str() {
|
||||
Cow::Borrowed(_) => OsStr::from_encoded_bytes_unchecked(b).into(),
|
||||
Cow::Owned(s) => s.into(),
|
||||
}
|
||||
})
|
||||
}
|
||||
pub fn group(&self) -> Option<&[u8]> { self.long_name_field(3) }
|
||||
|
||||
fn long_name_field(&self, n: usize) -> Option<&[u8]> {
|
||||
self.long_name.split(|b| b.is_ascii_whitespace()).filter(|s| !s.is_empty()).nth(n)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
use std::{mem, sync::Arc};
|
||||
|
||||
use crate::{ByteStr, Error, Session, fs::DirEntry, requests, responses};
|
||||
use crate::{Error, Session, SftpPath, fs::DirEntry, requests, responses};
|
||||
|
||||
pub struct ReadDir {
|
||||
session: Arc<Session>,
|
||||
dir: Arc<ByteStr<'static>>,
|
||||
dir: Arc<typed_path::UnixPathBuf>,
|
||||
handle: String,
|
||||
|
||||
name: responses::Name<'static>,
|
||||
|
|
@ -13,7 +13,7 @@ pub struct ReadDir {
|
|||
}
|
||||
|
||||
impl ReadDir {
|
||||
pub(crate) fn new(session: &Arc<Session>, dir: ByteStr, handle: String) -> Self {
|
||||
pub(crate) fn new(session: &Arc<Session>, dir: SftpPath, handle: String) -> Self {
|
||||
Self {
|
||||
session: session.clone(),
|
||||
dir: Arc::new(dir.into_owned()),
|
||||
|
|
@ -33,11 +33,11 @@ impl ReadDir {
|
|||
};
|
||||
|
||||
self.cursor += 1;
|
||||
if item.name != "." && item.name != ".." {
|
||||
if &*item.name != b"." && &*item.name != b".." {
|
||||
return Ok(Some(DirEntry {
|
||||
dir: self.dir.clone(),
|
||||
name: item.name,
|
||||
long_name: item.long_name,
|
||||
name: item.name.into_owned(),
|
||||
long_name: item.long_name.into_owned(),
|
||||
attrs: item.attrs,
|
||||
}));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,23 +4,21 @@ pub mod fs;
|
|||
pub mod requests;
|
||||
pub mod responses;
|
||||
|
||||
mod byte_str;
|
||||
mod de;
|
||||
mod error;
|
||||
mod id;
|
||||
mod macros;
|
||||
mod operator;
|
||||
mod packet;
|
||||
mod path;
|
||||
mod ser;
|
||||
mod session;
|
||||
#[cfg(windows)]
|
||||
mod wtf;
|
||||
|
||||
pub use byte_str::*;
|
||||
pub(crate) use de::*;
|
||||
pub use error::*;
|
||||
pub(crate) use id::*;
|
||||
pub use operator::*;
|
||||
pub use packet::*;
|
||||
pub use path::*;
|
||||
pub(crate) use ser::*;
|
||||
pub use session::*;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
use std::{ops::Deref, path::PathBuf, sync::Arc};
|
||||
use std::{ops::Deref, sync::Arc};
|
||||
|
||||
use russh::{ChannelStream, client::Msg};
|
||||
use tokio::sync::oneshot;
|
||||
use typed_path::UnixPathBuf;
|
||||
|
||||
use crate::{ByteStr, Error, Packet, Session, ToByteStr, fs::{Attrs, File, Flags, ReadDir}, requests, responses};
|
||||
use crate::{AsSftpPath, Error, Packet, Session, SftpPath, fs::{Attrs, File, Flags, ReadDir}, requests, responses};
|
||||
|
||||
pub struct Operator(Arc<Session>);
|
||||
|
||||
|
|
@ -28,9 +29,9 @@ impl Operator {
|
|||
|
||||
pub async fn open<'a, P>(&self, path: P, flags: Flags, attrs: &'a Attrs) -> Result<File, Error>
|
||||
where
|
||||
P: ToByteStr<'a>,
|
||||
P: AsSftpPath<'a>,
|
||||
{
|
||||
let handle: responses::Handle = self.send(requests::Open::new(path, flags, attrs)?).await?;
|
||||
let handle: responses::Handle = self.send(requests::Open::new(path, flags, attrs)).await?;
|
||||
|
||||
Ok(File::new(&self.0, handle.handle))
|
||||
}
|
||||
|
|
@ -59,9 +60,9 @@ impl Operator {
|
|||
|
||||
pub async fn lstat<'a, P>(&self, path: P) -> Result<Attrs, Error>
|
||||
where
|
||||
P: ToByteStr<'a>,
|
||||
P: AsSftpPath<'a>,
|
||||
{
|
||||
let attrs: responses::Attrs = self.send(requests::Lstat::new(path)?).await?;
|
||||
let attrs: responses::Attrs = self.send(requests::Lstat::new(path)).await?;
|
||||
Ok(attrs.attrs)
|
||||
}
|
||||
|
||||
|
|
@ -72,9 +73,9 @@ impl Operator {
|
|||
|
||||
pub async fn setstat<'a, P>(&self, path: P, attrs: Attrs) -> Result<(), Error>
|
||||
where
|
||||
P: ToByteStr<'a>,
|
||||
P: AsSftpPath<'a>,
|
||||
{
|
||||
let status: responses::Status = self.send(requests::SetStat::new(path, attrs)?).await?;
|
||||
let status: responses::Status = self.send(requests::SetStat::new(path, attrs)).await?;
|
||||
status.into()
|
||||
}
|
||||
|
||||
|
|
@ -85,100 +86,100 @@ impl Operator {
|
|||
|
||||
pub async fn read_dir<'a, P>(&'a self, dir: P) -> Result<ReadDir, Error>
|
||||
where
|
||||
P: ToByteStr<'a>,
|
||||
P: AsSftpPath<'a>,
|
||||
{
|
||||
let dir: ByteStr = dir.to_byte_str()?;
|
||||
let handle: responses::Handle = self.send(requests::OpenDir::new(&dir)?).await?;
|
||||
let dir: SftpPath = dir.as_sftp_path();
|
||||
let handle: responses::Handle = self.send(requests::OpenDir::new(&dir)).await?;
|
||||
|
||||
Ok(ReadDir::new(&self.0, dir, handle.handle))
|
||||
}
|
||||
|
||||
pub async fn remove<'a, P>(&self, path: P) -> Result<(), Error>
|
||||
where
|
||||
P: ToByteStr<'a>,
|
||||
P: AsSftpPath<'a>,
|
||||
{
|
||||
let status: responses::Status = self.send(requests::Remove::new(path)?).await?;
|
||||
let status: responses::Status = self.send(requests::Remove::new(path)).await?;
|
||||
status.into()
|
||||
}
|
||||
|
||||
pub async fn mkdir<'a, P>(&self, path: P, attrs: Attrs) -> Result<(), Error>
|
||||
where
|
||||
P: ToByteStr<'a>,
|
||||
P: AsSftpPath<'a>,
|
||||
{
|
||||
let status: responses::Status = self.send(requests::Mkdir::new(path, attrs)?).await?;
|
||||
let status: responses::Status = self.send(requests::Mkdir::new(path, attrs)).await?;
|
||||
status.into()
|
||||
}
|
||||
|
||||
pub async fn rmdir<'a, P>(&self, path: P) -> Result<(), Error>
|
||||
where
|
||||
P: ToByteStr<'a>,
|
||||
P: AsSftpPath<'a>,
|
||||
{
|
||||
let status: responses::Status = self.send(requests::Rmdir::new(path)?).await?;
|
||||
let status: responses::Status = self.send(requests::Rmdir::new(path)).await?;
|
||||
status.into()
|
||||
}
|
||||
|
||||
pub async fn realpath<'a, P>(&self, path: P) -> Result<PathBuf, Error>
|
||||
pub async fn realpath<'a, P>(&self, path: P) -> Result<UnixPathBuf, Error>
|
||||
where
|
||||
P: ToByteStr<'a>,
|
||||
P: AsSftpPath<'a>,
|
||||
{
|
||||
let mut name: responses::Name = self.send(requests::Realpath::new(path)?).await?;
|
||||
let mut name: responses::Name = self.send(requests::Realpath::new(path)).await?;
|
||||
if name.items.is_empty() {
|
||||
Err(Error::custom("realpath returned no names"))
|
||||
} else {
|
||||
Ok(name.items.swap_remove(0).name.into_path())
|
||||
Ok(name.items.swap_remove(0).name.into_owned().into())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn stat<'a, P>(&self, path: P) -> Result<Attrs, Error>
|
||||
where
|
||||
P: ToByteStr<'a>,
|
||||
P: AsSftpPath<'a>,
|
||||
{
|
||||
let attrs: responses::Attrs = self.send(requests::Stat::new(path)?).await?;
|
||||
let attrs: responses::Attrs = self.send(requests::Stat::new(path)).await?;
|
||||
Ok(attrs.attrs)
|
||||
}
|
||||
|
||||
pub async fn rename<'a, F, T>(&self, from: F, to: T) -> Result<(), Error>
|
||||
where
|
||||
F: ToByteStr<'a>,
|
||||
T: ToByteStr<'a>,
|
||||
F: AsSftpPath<'a>,
|
||||
T: AsSftpPath<'a>,
|
||||
{
|
||||
let status: responses::Status = self.send(requests::Rename::new(from, to)?).await?;
|
||||
let status: responses::Status = self.send(requests::Rename::new(from, to)).await?;
|
||||
status.into()
|
||||
}
|
||||
|
||||
pub async fn rename_posix<'a, F, T>(&self, from: F, to: T) -> Result<(), Error>
|
||||
where
|
||||
F: ToByteStr<'a>,
|
||||
T: ToByteStr<'a>,
|
||||
F: AsSftpPath<'a>,
|
||||
T: AsSftpPath<'a>,
|
||||
{
|
||||
if self.extensions.lock().get("posix-rename@openssh.com").is_none_or(|s| s != "1") {
|
||||
return Err(Error::Unsupported);
|
||||
}
|
||||
|
||||
let data = requests::ExtendedRename::new(from, to)?;
|
||||
let data = requests::ExtendedRename::new(from, to);
|
||||
let status: responses::Status =
|
||||
self.send(requests::Extended::new("posix-rename@openssh.com", data)).await?;
|
||||
status.into()
|
||||
}
|
||||
|
||||
pub async fn readlink<'a, P>(&self, path: P) -> Result<PathBuf, Error>
|
||||
pub async fn readlink<'a, P>(&self, path: P) -> Result<UnixPathBuf, Error>
|
||||
where
|
||||
P: ToByteStr<'a>,
|
||||
P: AsSftpPath<'a>,
|
||||
{
|
||||
let mut name: responses::Name = self.send(requests::Readlink::new(path)?).await?;
|
||||
let mut name: responses::Name = self.send(requests::Readlink::new(path)).await?;
|
||||
if name.items.is_empty() {
|
||||
Err(Error::custom("readlink returned no names"))
|
||||
} else {
|
||||
Ok(name.items.swap_remove(0).name.into_path())
|
||||
Ok(name.items.swap_remove(0).name.into_owned().into())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn symlink<'a, L, O>(&self, original: O, link: L) -> Result<(), Error>
|
||||
where
|
||||
O: ToByteStr<'a>,
|
||||
L: ToByteStr<'a>,
|
||||
O: AsSftpPath<'a>,
|
||||
L: AsSftpPath<'a>,
|
||||
{
|
||||
let status: responses::Status = self.send(requests::Symlink::new(original, link)?).await?;
|
||||
let status: responses::Status = self.send(requests::Symlink::new(original, link)).await?;
|
||||
status.into()
|
||||
}
|
||||
|
||||
|
|
@ -193,14 +194,14 @@ impl Operator {
|
|||
|
||||
pub async fn hardlink<'a, O, L>(&self, original: O, link: L) -> Result<(), Error>
|
||||
where
|
||||
O: ToByteStr<'a>,
|
||||
L: ToByteStr<'a>,
|
||||
O: AsSftpPath<'a>,
|
||||
L: AsSftpPath<'a>,
|
||||
{
|
||||
if self.extensions.lock().get("hardlink@openssh.com").is_none_or(|s| s != "1") {
|
||||
return Err(Error::Unsupported);
|
||||
}
|
||||
|
||||
let data = requests::ExtendedHardlink::new(original, link)?;
|
||||
let data = requests::ExtendedHardlink::new(original, link);
|
||||
let status: responses::Status =
|
||||
self.send(requests::Extended::new("hardlink@openssh.com", data)).await?;
|
||||
status.into()
|
||||
|
|
|
|||
83
yazi-sftp/src/path.rs
Normal file
83
yazi-sftp/src/path.rs
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
use std::{borrow::Cow, ops::Deref};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SftpPath<'a> {
|
||||
Borrowed(&'a typed_path::UnixPath),
|
||||
Owned(typed_path::UnixPathBuf),
|
||||
}
|
||||
|
||||
impl Deref for SftpPath<'_> {
|
||||
type Target = typed_path::UnixPath;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
SftpPath::Borrowed(p) => p,
|
||||
SftpPath::Owned(p) => p.as_path(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SftpPath<'_> {
|
||||
fn default() -> Self { SftpPath::Borrowed(typed_path::UnixPath::new("")) }
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Self> for SftpPath<'a> {
|
||||
fn from(value: &'a SftpPath) -> Self { SftpPath::Borrowed(value) }
|
||||
}
|
||||
|
||||
impl<'a> From<Cow<'a, [u8]>> for SftpPath<'a> {
|
||||
fn from(value: Cow<'a, [u8]>) -> Self {
|
||||
match value {
|
||||
Cow::Borrowed(b) => Self::Borrowed(typed_path::UnixPath::new(b)),
|
||||
Cow::Owned(b) => SftpPath::Owned(typed_path::UnixPathBuf::from(b)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for SftpPath<'_> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_bytes(self.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for SftpPath<'_> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let cow = <Cow<'de, [u8]>>::deserialize(deserializer)?;
|
||||
Ok(Self::Owned(cow.into_owned().into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SftpPath<'a> {
|
||||
pub fn len(&self) -> usize { self.as_bytes().len() }
|
||||
|
||||
pub fn into_owned(self) -> typed_path::UnixPathBuf {
|
||||
match self {
|
||||
SftpPath::Borrowed(p) => p.to_owned(),
|
||||
SftpPath::Owned(p) => p,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Traits
|
||||
pub trait AsSftpPath<'a> {
|
||||
fn as_sftp_path(self) -> SftpPath<'a>;
|
||||
}
|
||||
|
||||
impl<'a, T> AsSftpPath<'a> for &'a T
|
||||
where
|
||||
T: ?Sized + AsRef<typed_path::UnixPath>,
|
||||
{
|
||||
fn as_sftp_path(self) -> SftpPath<'a> { SftpPath::Borrowed(self.as_ref()) }
|
||||
}
|
||||
|
||||
impl<'a> AsSftpPath<'a> for &'a SftpPath<'a> {
|
||||
fn as_sftp_path(self) -> SftpPath<'a> { SftpPath::Borrowed(self) }
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ use std::{borrow::Cow, fmt::Debug};
|
|||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{ByteStr, Error, ToByteStr};
|
||||
use crate::{AsSftpPath, SftpPath};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Extended<'a, D> {
|
||||
|
|
@ -30,17 +30,17 @@ pub trait ExtendedData: Debug + Serialize + for<'de> Deserialize<'de> {
|
|||
// --- POSIX Rename
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct ExtendedRename<'a> {
|
||||
pub from: ByteStr<'a>,
|
||||
pub to: ByteStr<'a>,
|
||||
pub from: SftpPath<'a>,
|
||||
pub to: SftpPath<'a>,
|
||||
}
|
||||
|
||||
impl<'a> ExtendedRename<'a> {
|
||||
pub fn new<F, T>(from: F, to: T) -> Result<Self, Error>
|
||||
pub fn new<F, T>(from: F, to: T) -> Self
|
||||
where
|
||||
F: ToByteStr<'a>,
|
||||
T: ToByteStr<'a>,
|
||||
F: AsSftpPath<'a>,
|
||||
T: AsSftpPath<'a>,
|
||||
{
|
||||
Ok(Self { from: from.to_byte_str()?, to: to.to_byte_str()? })
|
||||
Self { from: from.as_sftp_path(), to: to.as_sftp_path() }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -65,17 +65,17 @@ impl ExtendedData for ExtendedFsync<'_> {
|
|||
// --- Hardlink
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct ExtendedHardlink<'a> {
|
||||
pub original: ByteStr<'a>,
|
||||
pub link: ByteStr<'a>,
|
||||
pub original: SftpPath<'a>,
|
||||
pub link: SftpPath<'a>,
|
||||
}
|
||||
|
||||
impl<'a> ExtendedHardlink<'a> {
|
||||
pub fn new<O, L>(original: O, link: L) -> Result<Self, Error>
|
||||
pub fn new<O, L>(original: O, link: L) -> Self
|
||||
where
|
||||
O: ToByteStr<'a>,
|
||||
L: ToByteStr<'a>,
|
||||
O: AsSftpPath<'a>,
|
||||
L: AsSftpPath<'a>,
|
||||
{
|
||||
Ok(Self { original: original.to_byte_str()?, link: link.to_byte_str()? })
|
||||
Self { original: original.as_sftp_path(), link: link.as_sftp_path() }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{ByteStr, Error, ToByteStr};
|
||||
use crate::{AsSftpPath, SftpPath};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Lstat<'a> {
|
||||
pub id: u32,
|
||||
pub path: ByteStr<'a>,
|
||||
pub path: SftpPath<'a>,
|
||||
}
|
||||
|
||||
impl Lstat<'_> {
|
||||
pub fn new<'a, P>(path: P) -> Result<Lstat<'a>, Error>
|
||||
pub fn new<'a, P>(path: P) -> Lstat<'a>
|
||||
where
|
||||
P: ToByteStr<'a>,
|
||||
P: AsSftpPath<'a>,
|
||||
{
|
||||
Ok(Lstat { id: 0, path: path.to_byte_str()? })
|
||||
Lstat { id: 0, path: path.as_sftp_path() }
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize { size_of_val(&self.id) + 4 + self.path.len() }
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{ByteStr, Error, ToByteStr, fs::Attrs};
|
||||
use crate::{AsSftpPath, SftpPath, fs::Attrs};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Mkdir<'a> {
|
||||
pub id: u32,
|
||||
pub path: ByteStr<'a>,
|
||||
pub path: SftpPath<'a>,
|
||||
pub attrs: Attrs,
|
||||
}
|
||||
|
||||
impl<'a> Mkdir<'a> {
|
||||
pub fn new<P>(path: P, attrs: Attrs) -> Result<Self, Error>
|
||||
pub fn new<P>(path: P, attrs: Attrs) -> Self
|
||||
where
|
||||
P: ToByteStr<'a>,
|
||||
P: AsSftpPath<'a>,
|
||||
{
|
||||
Ok(Self { id: 0, path: path.to_byte_str()?, attrs })
|
||||
Self { id: 0, path: path.as_sftp_path(), attrs }
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize { size_of_val(&self.id) + 4 + self.path.len() + self.attrs.len() }
|
||||
|
|
|
|||
|
|
@ -2,22 +2,22 @@ use std::borrow::Cow;
|
|||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{ByteStr, Error, ToByteStr, fs::{Attrs, Flags}};
|
||||
use crate::{AsSftpPath, SftpPath, fs::{Attrs, Flags}};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Open<'a> {
|
||||
pub id: u32,
|
||||
pub path: ByteStr<'a>,
|
||||
pub path: SftpPath<'a>,
|
||||
pub flags: Flags,
|
||||
pub attrs: Cow<'a, Attrs>,
|
||||
}
|
||||
|
||||
impl<'a> Open<'a> {
|
||||
pub fn new<P>(path: P, flags: Flags, attrs: &'a Attrs) -> Result<Self, Error>
|
||||
pub fn new<P>(path: P, flags: Flags, attrs: &'a Attrs) -> Self
|
||||
where
|
||||
P: ToByteStr<'a>,
|
||||
P: AsSftpPath<'a>,
|
||||
{
|
||||
Ok(Self { id: 0, path: path.to_byte_str()?, flags, attrs: Cow::Borrowed(attrs) })
|
||||
Self { id: 0, path: path.as_sftp_path(), flags, attrs: Cow::Borrowed(attrs) }
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{ByteStr, Error, ToByteStr};
|
||||
use crate::{AsSftpPath, SftpPath};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct OpenDir<'a> {
|
||||
pub id: u32,
|
||||
pub path: ByteStr<'a>,
|
||||
pub path: SftpPath<'a>,
|
||||
}
|
||||
|
||||
impl<'a> OpenDir<'a> {
|
||||
pub fn new<P>(path: P) -> Result<Self, Error>
|
||||
pub fn new<P>(path: P) -> Self
|
||||
where
|
||||
P: ToByteStr<'a>,
|
||||
P: AsSftpPath<'a>,
|
||||
{
|
||||
Ok(Self { id: 0, path: path.to_byte_str()? })
|
||||
Self { id: 0, path: path.as_sftp_path() }
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize { size_of_val(&self.id) + 4 + self.path.len() }
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{ByteStr, Error, ToByteStr};
|
||||
use crate::{AsSftpPath, SftpPath};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Readlink<'a> {
|
||||
pub id: u32,
|
||||
pub path: ByteStr<'a>,
|
||||
pub path: SftpPath<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Readlink<'a> {
|
||||
pub fn new<P>(path: P) -> Result<Self, Error>
|
||||
pub fn new<P>(path: P) -> Self
|
||||
where
|
||||
P: ToByteStr<'a>,
|
||||
P: AsSftpPath<'a>,
|
||||
{
|
||||
Ok(Self { id: 0, path: path.to_byte_str()? })
|
||||
Self { id: 0, path: path.as_sftp_path() }
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize { size_of_val(&self.id) + 4 + self.path.len() }
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{ByteStr, Error, ToByteStr};
|
||||
use crate::{AsSftpPath, SftpPath};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Realpath<'a> {
|
||||
pub id: u32,
|
||||
pub path: ByteStr<'a>,
|
||||
pub path: SftpPath<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Realpath<'a> {
|
||||
pub fn new<P>(path: P) -> Result<Self, Error>
|
||||
pub fn new<P>(path: P) -> Self
|
||||
where
|
||||
P: ToByteStr<'a>,
|
||||
P: AsSftpPath<'a>,
|
||||
{
|
||||
Ok(Self { id: 0, path: path.to_byte_str()? })
|
||||
Self { id: 0, path: path.as_sftp_path() }
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize { size_of_val(&self.id) + 4 + self.path.len() }
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{ByteStr, Error, ToByteStr};
|
||||
use crate::{AsSftpPath, SftpPath};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Remove<'a> {
|
||||
pub id: u32,
|
||||
pub path: ByteStr<'a>,
|
||||
pub path: SftpPath<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Remove<'a> {
|
||||
pub fn new<P>(path: P) -> Result<Self, Error>
|
||||
pub fn new<P>(path: P) -> Self
|
||||
where
|
||||
P: ToByteStr<'a>,
|
||||
P: AsSftpPath<'a>,
|
||||
{
|
||||
Ok(Self { id: 0, path: path.to_byte_str()? })
|
||||
Self { id: 0, path: path.as_sftp_path() }
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize { size_of_val(&self.id) + 4 + self.path.len() }
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{ByteStr, Error, ToByteStr};
|
||||
use crate::{AsSftpPath, SftpPath};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Rename<'a> {
|
||||
pub id: u32,
|
||||
pub from: ByteStr<'a>,
|
||||
pub to: ByteStr<'a>,
|
||||
pub from: SftpPath<'a>,
|
||||
pub to: SftpPath<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Rename<'a> {
|
||||
pub fn new<F, T>(from: F, to: T) -> Result<Self, Error>
|
||||
pub fn new<F, T>(from: F, to: T) -> Self
|
||||
where
|
||||
F: ToByteStr<'a>,
|
||||
T: ToByteStr<'a>,
|
||||
F: AsSftpPath<'a>,
|
||||
T: AsSftpPath<'a>,
|
||||
{
|
||||
Ok(Self { id: 0, from: from.to_byte_str()?, to: to.to_byte_str()? })
|
||||
Self { id: 0, from: from.as_sftp_path(), to: to.as_sftp_path() }
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize { size_of_val(&self.id) + 4 + self.from.len() + 4 + self.to.len() }
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{ByteStr, Error, ToByteStr};
|
||||
use crate::{AsSftpPath, SftpPath};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Rmdir<'a> {
|
||||
pub id: u32,
|
||||
pub path: ByteStr<'a>,
|
||||
pub path: SftpPath<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Rmdir<'a> {
|
||||
pub fn new<P>(path: P) -> Result<Self, Error>
|
||||
pub fn new<P>(path: P) -> Self
|
||||
where
|
||||
P: ToByteStr<'a>,
|
||||
P: AsSftpPath<'a>,
|
||||
{
|
||||
Ok(Self { id: 0, path: path.to_byte_str()? })
|
||||
Self { id: 0, path: path.as_sftp_path() }
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize { size_of_val(&self.id) + 4 + self.path.len() }
|
||||
|
|
|
|||
|
|
@ -2,21 +2,21 @@ use std::borrow::Cow;
|
|||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{ByteStr, Error, ToByteStr, fs::Attrs};
|
||||
use crate::{AsSftpPath, SftpPath, fs::Attrs};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct SetStat<'a> {
|
||||
pub id: u32,
|
||||
pub path: ByteStr<'a>,
|
||||
pub path: SftpPath<'a>,
|
||||
pub attrs: Attrs,
|
||||
}
|
||||
|
||||
impl<'a> SetStat<'a> {
|
||||
pub fn new<P>(path: P, attrs: Attrs) -> Result<Self, Error>
|
||||
pub fn new<P>(path: P, attrs: Attrs) -> Self
|
||||
where
|
||||
P: ToByteStr<'a>,
|
||||
P: AsSftpPath<'a>,
|
||||
{
|
||||
Ok(Self { id: 0, path: path.to_byte_str()?, attrs })
|
||||
Self { id: 0, path: path.as_sftp_path(), attrs }
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize { size_of_val(&self.id) + 4 + self.path.len() + self.attrs.len() }
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{ByteStr, Error, ToByteStr};
|
||||
use crate::{AsSftpPath, SftpPath};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Stat<'a> {
|
||||
pub id: u32,
|
||||
pub path: ByteStr<'a>,
|
||||
pub path: SftpPath<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Stat<'a> {
|
||||
pub fn new<P>(path: P) -> Result<Self, Error>
|
||||
pub fn new<P>(path: P) -> Self
|
||||
where
|
||||
P: ToByteStr<'a>,
|
||||
P: AsSftpPath<'a>,
|
||||
{
|
||||
Ok(Self { id: 0, path: path.to_byte_str()? })
|
||||
Self { id: 0, path: path.as_sftp_path() }
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize { size_of_val(&self.id) + 4 + self.path.len() }
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{ByteStr, Error, ToByteStr};
|
||||
use crate::{AsSftpPath, SftpPath};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Symlink<'a> {
|
||||
pub id: u32,
|
||||
pub link: ByteStr<'a>,
|
||||
pub original: ByteStr<'a>,
|
||||
pub link: SftpPath<'a>,
|
||||
pub original: SftpPath<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Symlink<'a> {
|
||||
pub fn new<L, O>(link: L, original: O) -> Result<Self, Error>
|
||||
pub fn new<L, O>(link: L, original: O) -> Self
|
||||
where
|
||||
L: ToByteStr<'a>,
|
||||
O: ToByteStr<'a>,
|
||||
L: AsSftpPath<'a>,
|
||||
O: AsSftpPath<'a>,
|
||||
{
|
||||
Ok(Self { id: 0, link: link.to_byte_str()?, original: original.to_byte_str()? })
|
||||
Self { id: 0, link: link.as_sftp_path(), original: original.as_sftp_path() }
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ impl Serialize for ExtendedData<'_> {
|
|||
S: serde::Serializer,
|
||||
{
|
||||
let mut seq = serializer.serialize_seq(None)?;
|
||||
for b in self.0.as_ref() {
|
||||
for b in &*self.0 {
|
||||
seq.serialize_element(b)?;
|
||||
}
|
||||
seq.end()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{ByteStr, fs::Attrs};
|
||||
use crate::fs::Attrs;
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Serialize)]
|
||||
pub struct Name<'a> {
|
||||
|
|
@ -10,8 +12,8 @@ pub struct Name<'a> {
|
|||
|
||||
#[derive(Debug, Default, Deserialize, Serialize)]
|
||||
pub struct NameItem<'a> {
|
||||
pub name: ByteStr<'a>,
|
||||
pub long_name: ByteStr<'a>,
|
||||
pub name: Cow<'a, [u8]>,
|
||||
pub long_name: Cow<'a, [u8]>,
|
||||
pub attrs: Attrs,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,87 +0,0 @@
|
|||
use std::{borrow::Cow, ffi::{OsStr, OsString}, os::windows::ffi::{OsStrExt, OsStringExt}};
|
||||
|
||||
pub(super) fn bytes_to_wide(mut bytes: &[u8]) -> Cow<'_, OsStr> {
|
||||
let mut wide: Option<Vec<u16>> = None;
|
||||
while !bytes.is_empty() {
|
||||
match (str::from_utf8(bytes), &mut wide) {
|
||||
(Ok(valid), None) => {
|
||||
return OsStr::new(valid).into();
|
||||
}
|
||||
(Ok(valid), Some(wide)) => {
|
||||
for ch in valid.chars() {
|
||||
wide.extend(ch.encode_utf16(&mut [0u16; 2]).iter());
|
||||
}
|
||||
break;
|
||||
}
|
||||
(Err(err), _) => {
|
||||
let wide = wide.get_or_insert_with(|| Vec::with_capacity(bytes.len()));
|
||||
|
||||
let valid = unsafe { str::from_utf8_unchecked(&bytes[..err.valid_up_to()]) };
|
||||
for c in valid.chars() {
|
||||
wide.extend(c.encode_utf16(&mut [0u16; 2]).iter());
|
||||
}
|
||||
bytes = &bytes[valid.len()..];
|
||||
|
||||
let invalid = err.error_len().unwrap_or(bytes.len());
|
||||
for &b in &bytes[..invalid] {
|
||||
wide.push(0xdc00 + b as u16);
|
||||
}
|
||||
bytes = &bytes[invalid..];
|
||||
}
|
||||
}
|
||||
}
|
||||
OsString::from_wide(&wide.unwrap_or_default()).into()
|
||||
}
|
||||
|
||||
pub(super) fn wide_to_bytes(wide: &OsStr) -> Option<Cow<'_, [u8]>> {
|
||||
if let Some(s) = wide.to_str() {
|
||||
return Some(s.as_bytes().into());
|
||||
}
|
||||
|
||||
let mut it = wide.encode_wide();
|
||||
let mut out = Vec::with_capacity(wide.len());
|
||||
|
||||
while let Some(w) = it.next() {
|
||||
if (0xdc00..=0xdcff).contains(&w) {
|
||||
out.push((w - 0xdc00) as u8);
|
||||
} else if (0xd800..=0xdbff).contains(&w) {
|
||||
let x = it.next().filter(|x| (0xdc00..=0xdfff).contains(x))?;
|
||||
let c = char::from_u32(0x10000 + (((w as u32 - 0xd800) << 10) | (x as u32 - 0xdc00)))?;
|
||||
out.extend_from_slice(c.encode_utf8(&mut [0u8; 4]).as_bytes());
|
||||
} else if (0xdc00..=0xdfff).contains(&w) {
|
||||
return None;
|
||||
} else {
|
||||
let c = char::from_u32(w as u32)?;
|
||||
out.extend_from_slice(c.encode_utf8(&mut [0u8; 4]).as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
Some(out.into())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn wtf8_roundtrip() {
|
||||
let b = &[
|
||||
b'\0', // NUL
|
||||
0xff, // 0xFF
|
||||
b'a', b'b', b'c', // abc
|
||||
0xf0, 0x9f, 0x98, 0x80, // 😀
|
||||
0xc3, 0x28, // illegal UTF-8
|
||||
];
|
||||
assert_eq!(&*wide_to_bytes(&bytes_to_wide(b)).unwrap(), b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(windows)]
|
||||
fn low_surrogates_for_non_utf8() {
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
|
||||
let os = bytes_to_wide(b"\xFF");
|
||||
let wide: Vec<u16> = os.encode_wide().collect();
|
||||
assert_eq!(wide, vec![0xdc00 + 0xff]);
|
||||
}
|
||||
}
|
||||
|
|
@ -18,6 +18,12 @@ impl<'p> LocAble<'p> for &'p std::path::Path {
|
|||
type Strand<'a> = &'a OsStr;
|
||||
}
|
||||
|
||||
impl<'p> LocAble<'p> for &'p typed_path::UnixPath {
|
||||
type Components<'a> = typed_path::UnixComponents<'a>;
|
||||
type Owned = typed_path::UnixPathBuf;
|
||||
type Strand<'a> = &'a [u8];
|
||||
}
|
||||
|
||||
// --- LocBufAble
|
||||
pub trait LocBufAble
|
||||
where
|
||||
|
|
@ -36,11 +42,18 @@ impl LocBufAble for std::path::PathBuf {
|
|||
type Strand<'a> = &'a OsStr;
|
||||
}
|
||||
|
||||
impl LocBufAble for typed_path::UnixPathBuf {
|
||||
type Borrowed<'a> = &'a typed_path::UnixPath;
|
||||
type Strand<'a> = &'a [u8];
|
||||
}
|
||||
|
||||
// --- StrandAble
|
||||
pub trait StrandAble<'a>: Copy {}
|
||||
|
||||
impl<'a> StrandAble<'a> for &'a OsStr {}
|
||||
|
||||
impl<'a> StrandAble<'a> for &'a [u8] {}
|
||||
|
||||
// --- LocAbleImpl
|
||||
pub(super) trait LocAbleImpl<'p>: LocAble<'p> {
|
||||
fn as_encoded_bytes(self) -> &'p [u8];
|
||||
|
|
@ -96,6 +109,36 @@ impl<'p> LocAbleImpl<'p> for &'p std::path::Path {
|
|||
fn to_path_buf(self) -> Self::Owned { self.to_path_buf() }
|
||||
}
|
||||
|
||||
impl<'p> LocAbleImpl<'p> for &'p typed_path::UnixPath {
|
||||
fn as_encoded_bytes(self) -> &'p [u8] { self.as_bytes() }
|
||||
|
||||
fn components(self) -> Self::Components<'p> { self.components() }
|
||||
|
||||
fn empty() -> Self { typed_path::UnixPath::new("") }
|
||||
|
||||
fn file_name(self) -> Option<Self::Strand<'p>> { self.file_name() }
|
||||
|
||||
unsafe fn from_encoded_bytes_unchecked(bytes: &'p [u8]) -> Self {
|
||||
typed_path::UnixPath::new(bytes)
|
||||
}
|
||||
|
||||
fn join<'a, T>(self, path: T) -> Self::Owned
|
||||
where
|
||||
T: AsStrandView<'a, Self::Strand<'a>>,
|
||||
{
|
||||
self.join(path.as_strand_view())
|
||||
}
|
||||
|
||||
fn strip_prefix<'a, T>(self, base: T) -> Option<Self>
|
||||
where
|
||||
T: AsStrandView<'a, Self::Strand<'a>>,
|
||||
{
|
||||
self.strip_prefix(base.as_strand_view()).ok()
|
||||
}
|
||||
|
||||
fn to_path_buf(self) -> Self::Owned { self.to_path_buf() }
|
||||
}
|
||||
|
||||
// --- LocBufAbleImpl
|
||||
pub(super) trait LocBufAbleImpl: LocBufAble {
|
||||
fn as_encoded_bytes(&self) -> &[u8] { self.borrow().as_encoded_bytes() }
|
||||
|
|
@ -130,6 +173,21 @@ impl LocBufAbleImpl for std::path::PathBuf {
|
|||
}
|
||||
}
|
||||
|
||||
impl LocBufAbleImpl for typed_path::UnixPathBuf {
|
||||
fn borrow(&self) -> Self::Borrowed<'_> { self.as_path() }
|
||||
|
||||
unsafe fn from_encoded_bytes_unchecked(bytes: Vec<u8>) -> Self { bytes.into() }
|
||||
|
||||
fn into_encoded_bytes(self) -> Vec<u8> { self.into_vec() }
|
||||
|
||||
fn set_file_name<'a, T>(&mut self, name: T)
|
||||
where
|
||||
T: AsStrandView<'a, Self::Strand<'a>>,
|
||||
{
|
||||
self.set_file_name(name.as_strand_view())
|
||||
}
|
||||
}
|
||||
|
||||
// --- StrandAbleImpl
|
||||
pub(super) trait StrandAbleImpl<'a>: StrandAble<'a> {
|
||||
fn as_encoded_bytes(self) -> &'a [u8];
|
||||
|
|
@ -140,3 +198,7 @@ pub(super) trait StrandAbleImpl<'a>: StrandAble<'a> {
|
|||
impl<'a> StrandAbleImpl<'a> for &'a OsStr {
|
||||
fn as_encoded_bytes(self) -> &'a [u8] { self.as_encoded_bytes() }
|
||||
}
|
||||
|
||||
impl<'a> StrandAbleImpl<'a> for &'a [u8] {
|
||||
fn as_encoded_bytes(self) -> &'a [u8] { self }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use std::{cmp, ffi::OsStr, fmt::{self, Debug, Formatter}, hash::{Hash, Hasher}, marker::PhantomData, mem, ops::Deref, path::PathBuf};
|
||||
use std::{cmp, ffi::OsStr, fmt::{self, Debug, Formatter}, hash::{Hash, Hasher}, marker::PhantomData, mem, ops::Deref};
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::{loc::{Loc, LocAble, LocAbleImpl, LocBufAble, LocBufAbleImpl}, path::{AsPath, AsPathView, PathDyn, SetNameError}, scheme::SchemeKind, strand::AsStrandView};
|
||||
|
||||
#[derive(Clone, Default, Eq, PartialEq)]
|
||||
pub struct LocBuf<P = PathBuf> {
|
||||
pub struct LocBuf<P = std::path::PathBuf> {
|
||||
pub(super) inner: P,
|
||||
pub(super) uri: usize,
|
||||
pub(super) urn: usize,
|
||||
|
|
@ -21,7 +21,7 @@ where
|
|||
}
|
||||
|
||||
// FIXME: remove
|
||||
impl AsRef<std::path::Path> for LocBuf<PathBuf> {
|
||||
impl AsRef<std::path::Path> for LocBuf<std::path::PathBuf> {
|
||||
fn as_ref(&self) -> &std::path::Path { self.inner.as_ref() }
|
||||
}
|
||||
|
||||
|
|
@ -93,8 +93,8 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + AsRef<OsStr>> From<&T> for LocBuf<PathBuf> {
|
||||
fn from(value: &T) -> Self { Self::from(PathBuf::from(value)) }
|
||||
impl<T: ?Sized + AsRef<OsStr>> From<&T> for LocBuf<std::path::PathBuf> {
|
||||
fn from(value: &T) -> Self { Self::from(std::path::PathBuf::from(value)) }
|
||||
}
|
||||
|
||||
impl<P> LocBuf<P>
|
||||
|
|
@ -249,13 +249,13 @@ where
|
|||
pub fn has_trail(&self) -> bool { self.as_loc().has_trail() }
|
||||
}
|
||||
|
||||
impl LocBuf<PathBuf> {
|
||||
pub const fn empty() -> Self { Self { inner: PathBuf::new(), uri: 0, urn: 0 } }
|
||||
impl LocBuf<std::path::PathBuf> {
|
||||
pub const fn empty() -> Self { Self { inner: std::path::PathBuf::new(), uri: 0, urn: 0 } }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use super::*;
|
||||
use crate::url::{UrlBuf, UrlLike};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{hash::{Hash, Hasher}, marker::PhantomData, ops::Deref, path::Path};
|
||||
use std::{hash::{Hash, Hasher}, marker::PhantomData, ops::Deref};
|
||||
|
||||
use anyhow::{Result, bail};
|
||||
|
||||
|
|
@ -6,7 +6,7 @@ use super::LocAbleImpl;
|
|||
use crate::{loc::{LocAble, LocBuf, LocBufAble, StrandAbleImpl}, path::{AsPath, AsPathView, PathDyn}, scheme::SchemeKind, strand::AsStrandView};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Loc<'p, P = &'p Path> {
|
||||
pub struct Loc<'p, P = &'p std::path::Path> {
|
||||
pub(super) inner: P,
|
||||
pub(super) uri: usize,
|
||||
pub(super) urn: usize,
|
||||
|
|
|
|||
|
|
@ -15,6 +15,10 @@ impl From<std::path::PathBuf> for PathBufDyn {
|
|||
fn from(value: std::path::PathBuf) -> Self { Self::Os(value) }
|
||||
}
|
||||
|
||||
impl From<typed_path::UnixPathBuf> for PathBufDyn {
|
||||
fn from(value: typed_path::UnixPathBuf) -> Self { Self::Unix(value) }
|
||||
}
|
||||
|
||||
impl From<PathDyn<'_>> for PathBufDyn {
|
||||
fn from(value: PathDyn<'_>) -> Self { value.to_owned() }
|
||||
}
|
||||
|
|
@ -25,6 +29,12 @@ impl TryFrom<PathBufDyn> for std::path::PathBuf {
|
|||
fn try_from(value: PathBufDyn) -> Result<Self, Self::Error> { value.into_os() }
|
||||
}
|
||||
|
||||
impl TryFrom<PathBufDyn> for typed_path::UnixPathBuf {
|
||||
type Error = PathDynError;
|
||||
|
||||
fn try_from(value: PathBufDyn) -> Result<Self, Self::Error> { value.into_unix() }
|
||||
}
|
||||
|
||||
impl PartialEq<PathDyn<'_>> for PathBufDyn {
|
||||
fn eq(&self, other: &PathDyn<'_>) -> bool { self.as_path() == *other }
|
||||
}
|
||||
|
|
@ -76,6 +86,14 @@ impl PathBufDyn {
|
|||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_unix(self) -> Result<typed_path::UnixPathBuf, PathDynError> {
|
||||
Ok(match self {
|
||||
Self::Os(_) => Err(PathDynError::AsUnix)?,
|
||||
Self::Unix(p) => p,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn new(kind: PathKind) -> Self {
|
||||
match kind {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,14 @@ impl AsPath for std::path::PathBuf {
|
|||
fn as_path(&self) -> PathDyn<'_> { PathDyn::Os(self) }
|
||||
}
|
||||
|
||||
impl AsPath for typed_path::UnixPath {
|
||||
fn as_path(&self) -> PathDyn<'_> { PathDyn::Unix(self) }
|
||||
}
|
||||
|
||||
impl AsPath for typed_path::UnixPathBuf {
|
||||
fn as_path(&self) -> PathDyn<'_> { PathDyn::Unix(self) }
|
||||
}
|
||||
|
||||
impl AsPath for PathDyn<'_> {
|
||||
fn as_path(&self) -> PathDyn<'_> { *self }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ impl From<SchemeKind> for PathKind {
|
|||
SchemeKind::Regular => Self::Os,
|
||||
SchemeKind::Search => Self::Os,
|
||||
SchemeKind::Archive => Self::Os,
|
||||
SchemeKind::Sftp => Self::Os, // FIXME
|
||||
SchemeKind::Sftp => Self::Unix,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ use crate::{Utf8BytePredictor, path::{AsPath, Components, Display, EndsWithError
|
|||
pub trait PathLike: AsPath {
|
||||
fn as_os(&self) -> Result<&std::path::Path, PathDynError> { self.as_path().as_os() }
|
||||
|
||||
fn as_unix(&self) -> Result<&typed_path::UnixPath, PathDynError> { self.as_path().as_unix() }
|
||||
|
||||
fn components(&self) -> Components<'_> { self.as_path().components() }
|
||||
|
||||
fn display(&self) -> Display<'_> { self.as_path().display() }
|
||||
|
|
|
|||
|
|
@ -58,6 +58,14 @@ impl<'p> PathDyn<'p> {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_unix(self) -> Result<&'p typed_path::UnixPath, PathDynError> {
|
||||
match self {
|
||||
Self::Os(_) => Err(PathDynError::AsUnix),
|
||||
Self::Unix(p) => Ok(p),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn components(self) -> Components<'p> {
|
||||
match self {
|
||||
Self::Os(p) => Components::Os(p.components()),
|
||||
|
|
|
|||
|
|
@ -10,3 +10,11 @@ impl<'a> AsPathView<'a, &'a std::path::Path> for &'a std::path::Path {
|
|||
impl<'a> AsPathView<'a, &'a std::path::Path> for &'a std::path::PathBuf {
|
||||
fn as_path_view(self) -> &'a std::path::Path { self }
|
||||
}
|
||||
|
||||
impl<'a> AsPathView<'a, &'a typed_path::UnixPath> for &'a typed_path::UnixPath {
|
||||
fn as_path_view(self) -> &'a typed_path::UnixPath { self }
|
||||
}
|
||||
|
||||
impl<'a> AsPathView<'a, &'a typed_path::UnixPath> for &'a typed_path::UnixPathBuf {
|
||||
fn as_path_view(self) -> &'a typed_path::UnixPath { self }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@ impl AsStrand for [u8] {
|
|||
fn as_strand(&self) -> Strand<'_> { Strand::Bytes(self) }
|
||||
}
|
||||
|
||||
impl AsStrand for &[u8] {
|
||||
fn as_strand(&self) -> Strand<'_> { Strand::Bytes(self) }
|
||||
}
|
||||
|
||||
impl AsStrand for str {
|
||||
fn as_strand(&self) -> Strand<'_> { Strand::Utf8(self) }
|
||||
}
|
||||
|
|
@ -43,6 +47,10 @@ impl AsStrand for &std::path::Path {
|
|||
fn as_strand(&self) -> Strand<'_> { Strand::Os(self.as_os_str()) }
|
||||
}
|
||||
|
||||
impl AsStrand for &typed_path::UnixPath {
|
||||
fn as_strand(&self) -> Strand<'_> { Strand::Bytes(self.as_bytes()) }
|
||||
}
|
||||
|
||||
impl AsStrand for crate::path::Components<'_> {
|
||||
fn as_strand(&self) -> Strand<'_> { self.strand() }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,13 @@ impl From<StrandBuf> for StrandCow<'_> {
|
|||
fn from(value: StrandBuf) -> Self { Self::Owned(value) }
|
||||
}
|
||||
|
||||
impl<'a, T> From<&'a T> for StrandCow<'a>
|
||||
where
|
||||
T: ?Sized + AsStrand,
|
||||
{
|
||||
fn from(value: &'a T) -> Self { Self::Borrowed(value.as_strand()) }
|
||||
}
|
||||
|
||||
impl PartialEq<Strand<'_>> for StrandCow<'_> {
|
||||
fn eq(&self, other: &Strand) -> bool { self.as_strand() == *other }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ impl From<SchemeKind> for StrandKind {
|
|||
SchemeKind::Regular => Self::Os,
|
||||
SchemeKind::Search => Self::Os,
|
||||
SchemeKind::Archive => Self::Os,
|
||||
SchemeKind::Sftp => Self::Os, // FIXME
|
||||
SchemeKind::Sftp => Self::Bytes,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,3 +16,15 @@ impl<'a> AsStrandView<'a, &'a OsStr> for &'a std::path::Path {
|
|||
impl<'a> AsStrandView<'a, &'a OsStr> for std::path::Components<'a> {
|
||||
fn as_strand_view(self) -> &'a OsStr { self.as_path().as_os_str() }
|
||||
}
|
||||
|
||||
impl<'a> AsStrandView<'a, &'a [u8]> for &'a [u8] {
|
||||
fn as_strand_view(self) -> &'a [u8] { self }
|
||||
}
|
||||
|
||||
impl<'a> AsStrandView<'a, &'a [u8]> for &'a typed_path::UnixPath {
|
||||
fn as_strand_view(self) -> &'a [u8] { self.as_bytes() }
|
||||
}
|
||||
|
||||
impl<'a> AsStrandView<'a, &'a [u8]> for typed_path::UnixComponents<'a> {
|
||||
fn as_strand_view(self) -> &'a [u8] { self.as_path::<typed_path::UnixEncoding>().as_bytes() }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ pub enum UrlBuf {
|
|||
Regular(LocBuf),
|
||||
Search { loc: LocBuf, domain: Symbol<str> },
|
||||
Archive { loc: LocBuf, domain: Symbol<str> },
|
||||
Sftp { loc: LocBuf, domain: Symbol<str> },
|
||||
Sftp { loc: LocBuf<typed_path::UnixPathBuf>, domain: Symbol<str> },
|
||||
}
|
||||
|
||||
// FIXME: remove
|
||||
|
|
@ -128,7 +128,7 @@ impl UrlBuf {
|
|||
Self::Regular(loc) => loc.try_set_name(name.as_os()?)?,
|
||||
Self::Search { loc, .. } => loc.try_set_name(name.as_os()?)?,
|
||||
Self::Archive { loc, .. } => loc.try_set_name(name.as_os()?)?,
|
||||
Self::Sftp { loc, .. } => loc.try_set_name(name.as_os()?)?,
|
||||
Self::Sftp { loc, .. } => loc.try_set_name(name.encoded_bytes())?,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -141,7 +141,10 @@ impl UrlBuf {
|
|||
Self::Archive { loc, domain } => {
|
||||
Self::Archive { loc: loc.rebase(base), domain: domain.clone() }
|
||||
}
|
||||
Self::Sftp { loc, domain } => Self::Sftp { loc: loc.rebase(base), domain: domain.clone() },
|
||||
Self::Sftp { loc, domain } => {
|
||||
todo!();
|
||||
// Self::Sftp { loc: loc.rebase(base), domain: domain.clone() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ impl<'a> Components<'a> {
|
|||
Url::Archive { loc: Loc::with(path.as_os().unwrap(), uri, urn).unwrap(), domain }
|
||||
}
|
||||
Url::Sftp { domain, .. } => {
|
||||
Url::Sftp { loc: Loc::with(path.as_os().unwrap(), uri, urn).unwrap(), domain }
|
||||
Url::Sftp { loc: Loc::with(path.as_unix().unwrap(), uri, urn).unwrap(), domain }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,12 +10,12 @@ pub enum UrlCow<'a> {
|
|||
Regular(LocBuf),
|
||||
Search { loc: LocBuf, domain: SymbolCow<'a, str> },
|
||||
Archive { loc: LocBuf, domain: SymbolCow<'a, str> },
|
||||
Sftp { loc: LocBuf, domain: SymbolCow<'a, str> },
|
||||
Sftp { loc: LocBuf<typed_path::UnixPathBuf>, domain: SymbolCow<'a, str> },
|
||||
|
||||
RegularRef(Loc<'a>),
|
||||
SearchRef { loc: Loc<'a>, domain: SymbolCow<'a, str> },
|
||||
ArchiveRef { loc: Loc<'a>, domain: SymbolCow<'a, str> },
|
||||
SftpRef { loc: Loc<'a>, domain: SymbolCow<'a, str> },
|
||||
SftpRef { loc: Loc<'a, &'a typed_path::UnixPath>, domain: SymbolCow<'a, str> },
|
||||
}
|
||||
|
||||
// FIXME: remove
|
||||
|
|
@ -148,7 +148,7 @@ impl<'a> TryFrom<(SchemeCow<'a>, PathDyn<'a>)> for UrlCow<'a> {
|
|||
domain: domain.ok_or_else(|| anyhow!("missing domain for archive scheme"))?,
|
||||
},
|
||||
SchemeKind::Sftp => Self::SftpRef {
|
||||
loc: Loc::with(path.as_os()?, uri, urn)?,
|
||||
loc: Loc::with(path.as_unix()?, uri, urn)?,
|
||||
domain: domain.ok_or_else(|| anyhow!("missing domain for sftp scheme"))?,
|
||||
},
|
||||
})
|
||||
|
|
@ -165,15 +165,15 @@ impl<'a> TryFrom<(SchemeCow<'a>, PathBufDyn)> for UrlCow<'a> {
|
|||
Ok(match kind {
|
||||
SchemeKind::Regular => Self::Regular(path.into_os()?.into()),
|
||||
SchemeKind::Search => Self::Search {
|
||||
loc: LocBuf::<PathBuf>::with(path.try_into()?, uri, urn)?,
|
||||
loc: LocBuf::<std::path::PathBuf>::with(path.try_into()?, uri, urn)?,
|
||||
domain: domain.ok_or_else(|| anyhow!("missing domain for search scheme"))?,
|
||||
},
|
||||
SchemeKind::Archive => Self::Archive {
|
||||
loc: LocBuf::<PathBuf>::with(path.try_into()?, uri, urn)?,
|
||||
loc: LocBuf::<std::path::PathBuf>::with(path.try_into()?, uri, urn)?,
|
||||
domain: domain.ok_or_else(|| anyhow!("missing domain for archive scheme"))?,
|
||||
},
|
||||
SchemeKind::Sftp => Self::Sftp {
|
||||
loc: LocBuf::<PathBuf>::with(path.try_into()?, uri, urn)?,
|
||||
loc: LocBuf::<typed_path::UnixPathBuf>::with(path.try_into()?, uri, urn)?,
|
||||
domain: domain.ok_or_else(|| anyhow!("missing domain for sftp scheme"))?,
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ pub enum Url<'a> {
|
|||
Regular(Loc<'a>),
|
||||
Search { loc: Loc<'a>, domain: &'a str },
|
||||
Archive { loc: Loc<'a>, domain: &'a str },
|
||||
Sftp { loc: Loc<'a>, domain: &'a str },
|
||||
Sftp { loc: Loc<'a, &'a typed_path::UnixPath>, domain: &'a str },
|
||||
}
|
||||
|
||||
// --- Eq
|
||||
|
|
@ -253,7 +253,7 @@ impl<'a> Url<'a> {
|
|||
domain: domain.intern(),
|
||||
},
|
||||
Self::Sftp { domain, .. } => {
|
||||
UrlBuf::Sftp { loc: joined.into_os()?.into(), domain: domain.intern() }
|
||||
UrlBuf::Sftp { loc: joined.into_unix()?.into(), domain: domain.intern() }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -282,24 +282,24 @@ impl<'a> Url<'a> {
|
|||
domain: domain.intern(),
|
||||
},
|
||||
Self::Archive { loc, domain } if path.try_starts_with(loc.trail())? => UrlBuf::Archive {
|
||||
loc: LocBuf::<PathBuf>::new(path.into_os()?, loc.base(), loc.trail()),
|
||||
loc: LocBuf::<std::path::PathBuf>::new(path.into_os()?, loc.base(), loc.trail()),
|
||||
domain: domain.intern(),
|
||||
},
|
||||
Self::Sftp { loc, domain } if path.try_starts_with(loc.trail())? => UrlBuf::Sftp {
|
||||
loc: LocBuf::<PathBuf>::new(path.into_os()?, loc.base(), loc.trail()),
|
||||
loc: LocBuf::<typed_path::UnixPathBuf>::new(path.into_unix()?, loc.base(), loc.trail()),
|
||||
domain: domain.intern(),
|
||||
},
|
||||
|
||||
Self::Search { domain, .. } => UrlBuf::Search {
|
||||
loc: LocBuf::<PathBuf>::saturated(path.into_os()?, self.kind()),
|
||||
loc: LocBuf::<std::path::PathBuf>::saturated(path.into_os()?, self.kind()),
|
||||
domain: domain.intern(),
|
||||
},
|
||||
Self::Archive { domain, .. } => UrlBuf::Archive {
|
||||
loc: LocBuf::<PathBuf>::saturated(path.into_os()?, self.kind()),
|
||||
loc: LocBuf::<std::path::PathBuf>::saturated(path.into_os()?, self.kind()),
|
||||
domain: domain.intern(),
|
||||
},
|
||||
Self::Sftp { domain, .. } => UrlBuf::Sftp {
|
||||
loc: LocBuf::<PathBuf>::saturated(path.into_os()?, self.kind()),
|
||||
loc: LocBuf::<typed_path::UnixPathBuf>::saturated(path.into_unix()?, self.kind()),
|
||||
domain: domain.intern(),
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -27,3 +27,4 @@ parking_lot = { workspace = true }
|
|||
russh = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
typed-path = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{ffi::OsStr, io, time::{Duration, UNIX_EPOCH}};
|
||||
use std::{io, time::{Duration, UNIX_EPOCH}};
|
||||
|
||||
use yazi_fs::cha::ChaKind;
|
||||
|
||||
|
|
@ -26,18 +26,17 @@ 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()))?;
|
||||
let mut cha = Self::try_from((ent.name(), ent.attrs()))?;
|
||||
cha.0.nlink = ent.nlink().unwrap_or_default();
|
||||
Ok(cha)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<(&OsStr, &yazi_sftp::fs::Attrs)> for Cha {
|
||||
impl TryFrom<(&[u8], &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() };
|
||||
fn try_from((name, attrs): (&[u8], &yazi_sftp::fs::Attrs)) -> Result<Self, Self::Error> {
|
||||
let kind = if name.starts_with(b".") { ChaKind::HIDDEN } else { ChaKind::empty() };
|
||||
|
||||
Ok(Self(yazi_fs::cha::Cha {
|
||||
kind,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use crate::provider::sftp::Conn;
|
|||
#[derive(Clone)]
|
||||
pub struct Sftp<'a> {
|
||||
url: Url<'a>,
|
||||
path: &'a Path,
|
||||
path: &'a typed_path::UnixPath,
|
||||
|
||||
name: &'static str,
|
||||
config: &'static ProviderSftp,
|
||||
|
|
@ -71,7 +71,7 @@ impl<'a> Provider for Sftp<'a> {
|
|||
where
|
||||
P: AsPath,
|
||||
{
|
||||
let to = to.as_path().as_os()?;
|
||||
let to = to.as_path().as_unix()?;
|
||||
let attrs = Attrs::from(super::Attrs(attrs));
|
||||
|
||||
let op = self.op().await?;
|
||||
|
|
@ -96,7 +96,7 @@ impl<'a> Provider for Sftp<'a> {
|
|||
where
|
||||
P: AsPath,
|
||||
{
|
||||
let to = to.as_path().as_os()?;
|
||||
let to = to.as_path().as_unix()?;
|
||||
|
||||
Ok(self.op().await?.hardlink(self.path, to).await?)
|
||||
}
|
||||
|
|
@ -137,7 +137,7 @@ impl<'a> Provider for Sftp<'a> {
|
|||
where
|
||||
P: AsPath,
|
||||
{
|
||||
let to = to.as_path().as_os()?;
|
||||
let to = to.as_path().as_unix()?;
|
||||
let op = self.op().await?;
|
||||
|
||||
match op.rename_posix(self.path, &to).await {
|
||||
|
|
@ -156,7 +156,7 @@ impl<'a> Provider for Sftp<'a> {
|
|||
P: AsPath,
|
||||
F: AsyncFnOnce() -> io::Result<bool>,
|
||||
{
|
||||
let original = original.as_path().as_os()?;
|
||||
let original = original.as_path().as_unix()?;
|
||||
|
||||
Ok(self.op().await?.symlink(&original, self.path).await?)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue