From 4a0376e6de259e5f4111ae8ff77d26b172ea9f69 Mon Sep 17 00:00:00 2001 From: Chip Senkbeil Date: Sun, 26 Sep 2021 14:32:54 -0500 Subject: [PATCH] Refactor exposed ssh2 types to wrapper types --- wezterm-ssh/src/lib.rs | 1 - wezterm-ssh/src/session.rs | 44 ++-- wezterm-ssh/src/session/sftp.rs | 65 ++--- wezterm-ssh/src/session/sftp/file.rs | 16 +- wezterm-ssh/src/session/sftp/types.rs | 336 ++++++++++++++++++++++++++ wezterm-ssh/tests/e2e/sftp.rs | 49 ++-- wezterm-ssh/tests/e2e/sftp/file.rs | 4 +- 7 files changed, 418 insertions(+), 97 deletions(-) create mode 100644 wezterm-ssh/src/session/sftp/types.rs diff --git a/wezterm-ssh/src/lib.rs b/wezterm-ssh/src/lib.rs index 718f9c748..74cd900b8 100644 --- a/wezterm-ssh/src/lib.rs +++ b/wezterm-ssh/src/lib.rs @@ -13,4 +13,3 @@ pub use session::*; // NOTE: Re-exported as is exposed in a public API of this crate pub use filedescriptor::FileDescriptor; pub use portable_pty::Child; -pub use ssh2::{FileStat, OpenFlags, OpenType, RenameFlags}; diff --git a/wezterm-ssh/src/session.rs b/wezterm-ssh/src/session.rs index 981edc855..68b7ed6d5 100644 --- a/wezterm-ssh/src/session.rs +++ b/wezterm-ssh/src/session.rs @@ -16,7 +16,10 @@ use std::sync::{Arc, Mutex}; use std::time::Duration; mod sftp; -pub use sftp::{File, Sftp}; +pub use sftp::{ + File, FilePermissions, FileType, Metadata, OpenFileType, OpenOptions, RenameOptions, Sftp, + WriteMode, +}; use sftp::{FileId, FileRequest, SftpRequest}; #[derive(Debug)] @@ -621,11 +624,15 @@ impl SessionInner { sess: &ssh2::Session, open_mode: &sftp::OpenMode, ) -> anyhow::Result<()> { + let flags: ssh2::OpenFlags = open_mode.opts.into(); + let mode = open_mode.opts.mode; + let open_type: ssh2::OpenType = open_mode.opts.ty.into(); + let ssh_file = self.init_sftp(sess)?.open_mode( open_mode.filename.as_path(), - open_mode.flags, - open_mode.mode, - open_mode.open_type, + flags, + mode, + open_type, )?; let (file_id, file) = self.make_file(); @@ -761,12 +768,12 @@ impl SessionInner { ) -> anyhow::Result<()> { let sftp::SetstatFile { file_id, - stat, + metadata, reply, } = setstat_file; if let Some(file) = self.files.get_mut(file_id) { - file.setstat(stat.clone())?; + file.setstat((*metadata).into())?; } reply.try_send(())?; @@ -781,7 +788,7 @@ impl SessionInner { ) -> anyhow::Result<()> { if let Some(file) = self.files.get_mut(&stat_file.file_id) { let stat = file.stat()?; - stat_file.reply.try_send(stat)?; + stat_file.reply.try_send(Metadata::from(stat))?; } Ok(()) @@ -794,8 +801,8 @@ impl SessionInner { readdir_file: &sftp::ReaddirFile, ) -> anyhow::Result<()> { if let Some(file) = self.files.get_mut(&readdir_file.file_id) { - let result = file.readdir()?; - readdir_file.reply.try_send(result)?; + let (path, stat) = file.readdir()?; + readdir_file.reply.try_send((path, Metadata::from(stat)))?; } Ok(()) @@ -819,7 +826,12 @@ impl SessionInner { /// /// See [`Sftp::readdir`] for more information. pub fn readdir(&mut self, sess: &ssh2::Session, readdir: &sftp::Readdir) -> anyhow::Result<()> { - let result = self.init_sftp(sess)?.readdir(readdir.filename.as_path())?; + let result = self + .init_sftp(sess)? + .readdir(readdir.filename.as_path())? + .into_iter() + .map(|(path, stat)| (path, Metadata::from(stat))) + .collect(); readdir.reply.try_send(result)?; Ok(()) @@ -850,8 +862,8 @@ impl SessionInner { /// /// See [`Sftp::stat`] for more information. pub fn stat(&mut self, sess: &ssh2::Session, stat: &sftp::Stat) -> anyhow::Result<()> { - let stat_data = self.init_sftp(sess)?.stat(stat.filename.as_path())?; - stat.reply.try_send(stat_data)?; + let metadata = Metadata::from(self.init_sftp(sess)?.stat(stat.filename.as_path())?); + stat.reply.try_send(metadata)?; Ok(()) } @@ -860,8 +872,8 @@ impl SessionInner { /// /// See [`Sftp::lstat`] for more information. pub fn lstat(&mut self, sess: &ssh2::Session, lstat: &sftp::Lstat) -> anyhow::Result<()> { - let stat_data = self.init_sftp(sess)?.lstat(lstat.filename.as_path())?; - lstat.reply.try_send(stat_data)?; + let metadata = Metadata::from(self.init_sftp(sess)?.lstat(lstat.filename.as_path())?); + lstat.reply.try_send(metadata)?; Ok(()) } @@ -871,7 +883,7 @@ impl SessionInner { /// See [`Sftp::setstat`] for more information. pub fn setstat(&mut self, sess: &ssh2::Session, setstat: &sftp::Setstat) -> anyhow::Result<()> { self.init_sftp(sess)? - .setstat(setstat.filename.as_path(), setstat.stat.clone())?; + .setstat(setstat.filename.as_path(), setstat.metadata.into())?; setstat.reply.try_send(())?; Ok(()) @@ -923,7 +935,7 @@ impl SessionInner { self.init_sftp(sess)?.rename( rename.src.as_path(), rename.dst.as_path(), - rename.flags.as_ref().copied(), + Some(rename.opts.into()), )?; rename.reply.try_send(())?; diff --git a/wezterm-ssh/src/session/sftp.rs b/wezterm-ssh/src/session/sftp.rs index 14bc81f9e..ef27c488a 100644 --- a/wezterm-ssh/src/session/sftp.rs +++ b/wezterm-ssh/src/session/sftp.rs @@ -1,7 +1,6 @@ use super::{SessionRequest, SessionSender}; use smol::channel::{bounded, Sender}; -use ssh2::{FileStat, OpenFlags, OpenType, RenameFlags}; -use std::{fmt, path::PathBuf}; +use std::path::PathBuf; mod file; pub use file::File; @@ -10,6 +9,11 @@ pub(crate) use file::{ StatFile, WriteFile, }; +mod types; +pub use types::{ + FilePermissions, FileType, Metadata, OpenFileType, OpenOptions, RenameOptions, WriteMode, +}; + /// Represents an open sftp channel for performing filesystem operations #[derive(Clone, Debug)] pub struct Sftp { @@ -23,17 +27,14 @@ impl Sftp { pub async fn open_mode( &self, filename: impl Into, - flags: OpenFlags, - mode: i32, - open_type: OpenType, + opts: OpenOptions, ) -> anyhow::Result { let (reply, rx) = bounded(1); + self.tx .send(SessionRequest::Sftp(SftpRequest::OpenMode(OpenMode { filename: filename.into(), - flags, - mode, - open_type, + opts, reply, }))) .await?; @@ -99,7 +100,7 @@ impl Sftp { pub async fn readdir( &self, filename: impl Into, - ) -> anyhow::Result> { + ) -> anyhow::Result> { let (reply, rx) = bounded(1); self.tx .send(SessionRequest::Sftp(SftpRequest::Readdir(Readdir { @@ -145,7 +146,7 @@ impl Sftp { /// Get the metadata for a file, performed by stat(2). /// /// See [`Sftp::stat`] for more information. - pub async fn stat(&self, filename: impl Into) -> anyhow::Result { + pub async fn stat(&self, filename: impl Into) -> anyhow::Result { let (reply, rx) = bounded(1); self.tx .send(SessionRequest::Sftp(SftpRequest::Stat(Stat { @@ -160,7 +161,7 @@ impl Sftp { /// Get the metadata for a file, performed by lstat(2). /// /// See [`Sftp::lstat`] for more information. - pub async fn lstat(&self, filename: impl Into) -> anyhow::Result { + pub async fn lstat(&self, filename: impl Into) -> anyhow::Result { let (reply, rx) = bounded(1); self.tx .send(SessionRequest::Sftp(SftpRequest::Lstat(Lstat { @@ -178,13 +179,13 @@ impl Sftp { pub async fn setstat( &self, filename: impl Into, - stat: FileStat, + metadata: Metadata, ) -> anyhow::Result<()> { let (reply, rx) = bounded(1); self.tx .send(SessionRequest::Sftp(SftpRequest::Setstat(Setstat { filename: filename.into(), - stat, + metadata, reply, }))) .await?; @@ -249,14 +250,14 @@ impl Sftp { &self, src: impl Into, dst: impl Into, - flags: Option, + opts: RenameOptions, ) -> anyhow::Result<()> { let (reply, rx) = bounded(1); self.tx .send(SessionRequest::Sftp(SftpRequest::Rename(Rename { src: src.into(), dst: dst.into(), - flags, + opts, reply, }))) .await?; @@ -302,33 +303,13 @@ pub(crate) enum SftpRequest { File(FileRequest), } +#[derive(Debug)] pub(crate) struct OpenMode { pub filename: PathBuf, - pub flags: OpenFlags, - pub mode: i32, - pub open_type: OpenType, + pub opts: OpenOptions, pub reply: Sender, } -impl fmt::Debug for OpenMode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // NOTE: OpenType does not implement debug, - // so we create a string representation - let open_type_string = match self.open_type { - OpenType::Dir => String::from("OpenType::Dir"), - OpenType::File => String::from("OpenType::File"), - }; - - f.debug_struct("OpenMode") - .field("filename", &self.filename) - .field("flags", &self.flags) - .field("mode", &self.mode) - .field("open_type", &open_type_string) - .field("reply", &self.reply) - .finish() - } -} - #[derive(Debug)] pub(crate) struct Open { pub filename: PathBuf, @@ -350,7 +331,7 @@ pub(crate) struct Opendir { #[derive(Debug)] pub(crate) struct Readdir { pub filename: PathBuf, - pub reply: Sender>, + pub reply: Sender>, } #[derive(Debug)] @@ -369,19 +350,19 @@ pub(crate) struct Rmdir { #[derive(Debug)] pub(crate) struct Stat { pub filename: PathBuf, - pub reply: Sender, + pub reply: Sender, } #[derive(Debug)] pub(crate) struct Lstat { pub filename: PathBuf, - pub reply: Sender, + pub reply: Sender, } #[derive(Debug)] pub(crate) struct Setstat { pub filename: PathBuf, - pub stat: FileStat, + pub metadata: Metadata, pub reply: Sender<()>, } @@ -408,7 +389,7 @@ pub(crate) struct Realpath { pub(crate) struct Rename { pub src: PathBuf, pub dst: PathBuf, - pub flags: Option, + pub opts: RenameOptions, pub reply: Sender<()>, } diff --git a/wezterm-ssh/src/session/sftp/file.rs b/wezterm-ssh/src/session/sftp/file.rs index ff55ca196..fea545610 100644 --- a/wezterm-ssh/src/session/sftp/file.rs +++ b/wezterm-ssh/src/session/sftp/file.rs @@ -1,4 +1,4 @@ -use super::{FileStat, SessionRequest, SessionSender, SftpRequest}; +use super::{Metadata, SessionRequest, SessionSender, SftpRequest}; use smol::{ channel::{bounded, Sender}, future::FutureExt, @@ -70,20 +70,20 @@ pub(crate) struct FlushFile { #[derive(Debug)] pub(crate) struct SetstatFile { pub file_id: FileId, - pub stat: FileStat, + pub metadata: Metadata, pub reply: Sender<()>, } #[derive(Debug)] pub(crate) struct StatFile { pub file_id: FileId, - pub reply: Sender, + pub reply: Sender, } #[derive(Debug)] pub(crate) struct ReaddirFile { pub file_id: FileId, - pub reply: Sender<(PathBuf, FileStat)>, + pub reply: Sender<(PathBuf, Metadata)>, } #[derive(Debug)] @@ -131,7 +131,7 @@ impl File { /// Set the metadata for this handle. /// /// See [`ssh2::File::setstat`] for more information. - pub async fn setstat(&self, stat: FileStat) -> anyhow::Result<()> { + pub async fn setstat(&self, metadata: Metadata) -> anyhow::Result<()> { let (reply, rx) = bounded(1); self.tx .as_ref() @@ -139,7 +139,7 @@ impl File { .send(SessionRequest::Sftp(SftpRequest::File( FileRequest::Setstat(SetstatFile { file_id: self.file_id, - stat, + metadata, reply, }), ))) @@ -151,7 +151,7 @@ impl File { /// Get the metadata for this handle. /// /// See [`ssh2::File::stat`] for more information. - pub async fn stat(&self) -> anyhow::Result { + pub async fn stat(&self) -> anyhow::Result { let (reply, rx) = bounded(1); self.tx .as_ref() @@ -178,7 +178,7 @@ impl File { /// files in this directory. /// /// See [`ssh2::File::readdir`] for more information. - pub async fn readdir(&self) -> anyhow::Result<(PathBuf, FileStat)> { + pub async fn readdir(&self) -> anyhow::Result<(PathBuf, Metadata)> { let (reply, rx) = bounded(1); self.tx .as_ref() diff --git a/wezterm-ssh/src/session/sftp/types.rs b/wezterm-ssh/src/session/sftp/types.rs new file mode 100644 index 000000000..6a75afe7f --- /dev/null +++ b/wezterm-ssh/src/session/sftp/types.rs @@ -0,0 +1,336 @@ +const OCTAL_FT_DIR: u32 = 0o040000; +const OCTAL_FT_FILE: u32 = 0o100000; +const OCTAL_FT_SYMLINK: u32 = 0o120000; +const OCTAL_FT_OTHER: u32 = 0; + +const OCTAL_PERM_OWNER_READ: u32 = 0o400; +const OCTAL_PERM_OWNER_WRITE: u32 = 0o200; +const OCTAL_PERM_OWNER_EXEC: u32 = 0o100; +const OCTAL_PERM_GROUP_READ: u32 = 0o40; +const OCTAL_PERM_GROUP_WRITE: u32 = 0o20; +const OCTAL_PERM_GROUP_EXEC: u32 = 0o10; +const OCTAL_PERM_OTHER_READ: u32 = 0o4; +const OCTAL_PERM_OTHER_WRITE: u32 = 0o2; +const OCTAL_PERM_OTHER_EXEC: u32 = 0o1; + +/// Represents the type associated with a remote file +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub enum FileType { + Dir, + File, + Symlink, + Other, +} + +impl FileType { + pub fn is_dir(self) -> bool { + matches!(self, Self::Dir) + } + + pub fn is_file(self) -> bool { + matches!(self, Self::File) + } + + pub fn is_symlink(self) -> bool { + matches!(self, Self::Symlink) + } + + /// Create from a unix mode bitset + pub fn from_unix_mode(mode: u32) -> Self { + if mode & OCTAL_FT_DIR != 0 { + Self::Dir + } else if mode & OCTAL_FT_FILE != 0 { + Self::File + } else if mode & OCTAL_FT_SYMLINK != 0 { + Self::Symlink + } else { + Self::Other + } + } + + /// Convert to a unix mode bitset + pub fn to_unix_mode(self) -> u32 { + match self { + FileType::Dir => OCTAL_FT_DIR, + FileType::File => OCTAL_FT_FILE, + FileType::Symlink => OCTAL_FT_SYMLINK, + FileType::Other => OCTAL_FT_OTHER, + } + } +} + +/// Represents permissions associated with a remote file +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub struct FilePermissions { + pub owner_read: bool, + pub owner_write: bool, + pub owner_exec: bool, + + pub group_read: bool, + pub group_write: bool, + pub group_exec: bool, + + pub other_read: bool, + pub other_write: bool, + pub other_exec: bool, +} + +impl FilePermissions { + pub fn is_readonly(self) -> bool { + !(self.owner_read || self.group_read || self.other_read) + } + + /// Create from a unix mode bitset + pub fn from_unix_mode(mode: u32) -> Self { + Self { + owner_read: mode | OCTAL_PERM_OWNER_READ != 0, + owner_write: mode | OCTAL_PERM_OWNER_WRITE != 0, + owner_exec: mode | OCTAL_PERM_OWNER_EXEC != 0, + group_read: mode | OCTAL_PERM_GROUP_READ != 0, + group_write: mode | OCTAL_PERM_GROUP_WRITE != 0, + group_exec: mode | OCTAL_PERM_GROUP_EXEC != 0, + other_read: mode | OCTAL_PERM_OTHER_READ != 0, + other_write: mode | OCTAL_PERM_OTHER_WRITE != 0, + other_exec: mode | OCTAL_PERM_OTHER_EXEC != 0, + } + } + + /// Convert to a unix mode bitset + pub fn to_unix_mode(self) -> u32 { + let mut mode: u32 = 0; + + if self.owner_read { + mode |= OCTAL_PERM_OWNER_READ; + } + if self.owner_write { + mode |= OCTAL_PERM_OWNER_WRITE; + } + if self.owner_exec { + mode |= OCTAL_PERM_OWNER_EXEC; + } + + if self.group_read { + mode |= OCTAL_PERM_GROUP_READ; + } + if self.group_write { + mode |= OCTAL_PERM_GROUP_WRITE; + } + if self.group_exec { + mode |= OCTAL_PERM_GROUP_EXEC; + } + + if self.other_read { + mode |= OCTAL_PERM_OTHER_READ; + } + if self.other_write { + mode |= OCTAL_PERM_OTHER_WRITE; + } + if self.other_exec { + mode |= OCTAL_PERM_OTHER_EXEC; + } + + mode + } +} + +/// Represents metadata about a remote file +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub struct Metadata { + /// Type of the remote file + pub ty: FileType, + + /// Permissions associated with the file + pub permissions: Option, + + /// File size, in bytes of the file + pub size: Option, + + /// Owner ID of the file + pub uid: Option, + + /// Owning group of the file + pub gid: Option, + + /// Last access time of the file + pub accessed: Option, + + /// Last modification time of the file + pub modified: Option, +} + +impl Metadata { + /// Returns the size of the file, in bytes (or zero if unknown) + pub fn len(self) -> u64 { + self.size.unwrap_or(0) + } + + pub fn is_dir(self) -> bool { + self.ty.is_dir() + } + + pub fn is_file(self) -> bool { + self.ty.is_file() + } + + pub fn is_symlink(self) -> bool { + self.ty.is_symlink() + } +} + +/// Represents options to provide when opening a file or directory +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub struct OpenOptions { + /// If true, opens a file (or directory) for reading + pub read: bool, + + /// If provided, opens a file for writing or appending + pub write: Option, + + /// Unix mode that is used when creating a new file + pub mode: i32, + + /// Whether opening a file or directory + pub ty: OpenFileType, +} + +/// Represents whether opening a file or directory +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub enum OpenFileType { + Dir, + File, +} + +/// Represents different writing modes for opening a file +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub enum WriteMode { + /// Append data to end of file instead of overwriting it + Append, + + /// Overwrite an existing file when opening to write it + Write, +} + +/// Represents options to provide when renaming a file or directory +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub struct RenameOptions { + /// Overwrite the destination if it exists, otherwise fail + pub overwrite: bool, + + /// Request atomic rename operation + pub atomic: bool, + + /// Request native system calls + pub native: bool, +} + +impl Default for RenameOptions { + /// Default is to enable all options + fn default() -> Self { + Self { + overwrite: true, + atomic: true, + native: true, + } + } +} + +/// Contains libssh2-specific implementations +mod ssh2_impl { + use super::*; + use ::ssh2::{ + FileStat as Ssh2FileStat, FileType as Ssh2FileType, OpenFlags as Ssh2OpenFlags, + OpenType as Ssh2OpenType, RenameFlags as Ssh2RenameFlags, + }; + + impl From for Ssh2OpenType { + fn from(ty: OpenFileType) -> Self { + match ty { + OpenFileType::Dir => Self::Dir, + OpenFileType::File => Self::File, + } + } + } + + impl From for Ssh2RenameFlags { + fn from(opts: RenameOptions) -> Self { + let mut flags = Self::empty(); + + if opts.overwrite { + flags |= Self::OVERWRITE; + } + + if opts.atomic { + flags |= Self::ATOMIC; + } + + if opts.native { + flags |= Self::NATIVE; + } + + flags + } + } + + impl From for Ssh2OpenFlags { + fn from(opts: OpenOptions) -> Self { + let mut flags = Self::empty(); + + if opts.read { + flags |= Self::READ; + } + + match opts.write { + Some(WriteMode::Write) => flags |= Self::WRITE | Self::TRUNCATE, + Some(WriteMode::Append) => flags |= Self::WRITE | Self::APPEND | Self::CREATE, + None => {} + } + + flags + } + } + + impl From for FileType { + fn from(ft: Ssh2FileType) -> Self { + if ft.is_dir() { + Self::Dir + } else if ft.is_file() { + Self::File + } else if ft.is_symlink() { + Self::Symlink + } else { + Self::Other + } + } + } + + impl From for Metadata { + fn from(stat: Ssh2FileStat) -> Self { + Self { + ty: FileType::from(stat.file_type()), + permissions: stat.perm.map(FilePermissions::from_unix_mode), + size: stat.size, + uid: stat.uid, + gid: stat.gid, + accessed: stat.atime, + modified: stat.mtime, + } + } + } + + impl From for Ssh2FileStat { + fn from(metadata: Metadata) -> Self { + let ft = metadata.ty; + + Self { + perm: metadata + .permissions + .map(|p| p.to_unix_mode() | ft.to_unix_mode()), + size: metadata.size, + uid: metadata.uid, + gid: metadata.gid, + atime: metadata.accessed, + mtime: metadata.modified, + } + } + } +} diff --git a/wezterm-ssh/tests/e2e/sftp.rs b/wezterm-ssh/tests/e2e/sftp.rs index 7912c0c0e..06d9e8448 100644 --- a/wezterm-ssh/tests/e2e/sftp.rs +++ b/wezterm-ssh/tests/e2e/sftp.rs @@ -2,9 +2,8 @@ use crate::sshd::session; use assert_fs::{prelude::*, TempDir}; use predicates::prelude::*; use rstest::*; -use ssh2::FileType; use std::path::PathBuf; -use wezterm_ssh::Session; +use wezterm_ssh::{FileType, Session}; // Sftp file tests mod file; @@ -51,7 +50,7 @@ async fn readdir_should_return_list_of_directories_files_and_symlinks(#[future] .await .expect("Failed to read directory") .into_iter() - .map(|(p, s)| (p, file_type_to_str(s.file_type()))) + .map(|(p, s)| (p, file_type_to_str(s.ty))) .collect::>(); contents.sort_unstable_by_key(|(p, _)| p.to_path_buf()); @@ -226,16 +225,16 @@ async fn stat_should_return_metadata_about_the_file_pointed_to_by_a_symlink( let link = temp.child("link"); link.symlink_to_file(file.path()).unwrap(); - let stat = session + let metadata = session .sftp() .stat(link.path()) .await .expect("Failed to stat symlink"); // Verify that file stat makes sense - assert!(stat.is_file(), "Invalid file stat returned"); - assert!(stat.file_type().is_file(), "Invalid file stat returned"); - assert!(!stat.file_type().is_symlink(), "Invalid file stat returned"); + assert!(metadata.is_file(), "Invalid file stat returned"); + assert!(metadata.ty.is_file(), "Invalid file stat returned"); + assert!(!metadata.ty.is_symlink(), "Invalid file stat returned"); } #[rstest] @@ -252,16 +251,16 @@ async fn stat_should_return_metadata_about_the_dir_pointed_to_by_a_symlink( let link = temp.child("link"); link.symlink_to_dir(dir.path()).unwrap(); - let stat = session + let metadata = session .sftp() .stat(link.path()) .await .expect("Failed to stat symlink"); // Verify that file stat makes sense - assert!(stat.is_dir(), "Invalid file stat returned"); - assert!(stat.file_type().is_dir(), "Invalid file stat returned"); - assert!(!stat.file_type().is_symlink(), "Invalid file stat returned"); + assert!(metadata.is_dir(), "Invalid file stat returned"); + assert!(metadata.ty.is_dir(), "Invalid file stat returned"); + assert!(!metadata.ty.is_symlink(), "Invalid file stat returned"); } #[rstest] @@ -325,19 +324,16 @@ async fn lstat_should_return_metadata_about_symlink_pointing_to_a_file(#[future] let link = temp.child("link"); link.symlink_to_file(file.path()).unwrap(); - let lstat = session + let metadata = session .sftp() .lstat(link.path()) .await .expect("Failed to lstat symlink"); // Verify that file lstat makes sense - assert!(!lstat.is_file(), "Invalid file lstat returned"); - assert!(!lstat.file_type().is_file(), "Invalid file lstat returned"); - assert!( - lstat.file_type().is_symlink(), - "Invalid file lstat returned" - ); + assert!(!metadata.is_file(), "Invalid file lstat returned"); + assert!(!metadata.ty.is_file(), "Invalid file lstat returned"); + assert!(metadata.ty.is_symlink(), "Invalid file lstat returned"); } #[rstest] @@ -354,19 +350,16 @@ async fn lstat_should_return_metadata_about_symlink_pointing_to_a_directory( let link = temp.child("link"); link.symlink_to_dir(dir.path()).unwrap(); - let lstat = session + let metadata = session .sftp() .lstat(link.path()) .await .expect("Failed to lstat symlink"); // Verify that file lstat makes sense - assert!(!lstat.is_dir(), "Invalid file lstat returned"); - assert!(!lstat.file_type().is_dir(), "Invalid file lstat returned"); - assert!( - lstat.file_type().is_symlink(), - "Invalid file lstat returned" - ); + assert!(!metadata.is_dir(), "Invalid file lstat returned"); + assert!(!metadata.ty.is_dir(), "Invalid file lstat returned"); + assert!(metadata.ty.is_symlink(), "Invalid file lstat returned"); } #[rstest] @@ -586,7 +579,7 @@ async fn rename_should_support_singular_file(#[future] session: Session) { session .sftp() - .rename(file.path(), dst.path(), None) + .rename(file.path(), dst.path(), Default::default()) .await .expect("Failed to rename file"); @@ -612,7 +605,7 @@ async fn rename_should_support_dirtectory(#[future] session: Session) { session .sftp() - .rename(dir.path(), dst.path(), None) + .rename(dir.path(), dst.path(), Default::default()) .await .expect("Failed to rename directory"); @@ -637,7 +630,7 @@ async fn rename_should_fail_if_source_path_missing(#[future] session: Session) { let result = session .sftp() - .rename(missing.path(), dst.path(), None) + .rename(missing.path(), dst.path(), Default::default()) .await; assert!( result.is_err(), diff --git a/wezterm-ssh/tests/e2e/sftp/file.rs b/wezterm-ssh/tests/e2e/sftp/file.rs index 554db4b9c..77d6f6d8d 100644 --- a/wezterm-ssh/tests/e2e/sftp/file.rs +++ b/wezterm-ssh/tests/e2e/sftp/file.rs @@ -47,8 +47,8 @@ async fn readdir_should_retrieve_next_dir_entry(#[future] session: Session) { // Collect all of the directory contents (. and .. are included) let mut contents = Vec::new(); - while let Ok((path, stat)) = remote_dir.readdir().await { - let ft = stat.file_type(); + while let Ok((path, metadata)) = remote_dir.readdir().await { + let ft = metadata.ty; contents.push(( path, if ft.is_dir() {