use std::fmt;
use std::io;
pub const SFTP_VERSION: u32 = 3;
pub const FXF_READ: u32 = 0x00000001;
pub const FXF_WRITE: u32 = 0x00000002;
pub const FXF_APPEND: u32 = 0x00000004;
pub const FXF_CREAT: u32 = 0x00000008;
pub const FXF_TRUNC: u32 = 0x00000010;
pub const FXF_EXCL: u32 = 0x00000020;
pub const ATTR_SIZE: u32 = 0x00000001;
pub const ATTR_UIDGID: u32 = 0x00000002;
pub const ATTR_PERMISSIONS: u32 = 0x00000004;
pub const ATTR_ACMODTIME: u32 = 0x00000008;
pub const ATTR_EXTENDED: u32 = 0x80000000;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FxpStatus {
Ok,
Eof,
NoSuchFile,
PermissionDenied,
Failure,
BadMessage,
NoConnection,
ConnectionLost,
OpUnsupported,
}
impl FxpStatus {
pub fn code(self) -> u32 {
match self {
FxpStatus::Ok => 0,
FxpStatus::Eof => 1,
FxpStatus::NoSuchFile => 2,
FxpStatus::PermissionDenied => 3,
FxpStatus::Failure => 4,
FxpStatus::BadMessage => 5,
FxpStatus::NoConnection => 6,
FxpStatus::ConnectionLost => 7,
FxpStatus::OpUnsupported => 8,
}
}
pub fn from_code(code: u32) -> Self {
match code {
0 => FxpStatus::Ok,
1 => FxpStatus::Eof,
2 => FxpStatus::NoSuchFile,
3 => FxpStatus::PermissionDenied,
5 => FxpStatus::BadMessage,
6 => FxpStatus::NoConnection,
7 => FxpStatus::ConnectionLost,
8 => FxpStatus::OpUnsupported,
_ => FxpStatus::Failure,
}
}
pub fn from_io(e: &io::Error) -> Self {
use io::ErrorKind::*;
match e.kind() {
NotFound => FxpStatus::NoSuchFile,
PermissionDenied => FxpStatus::PermissionDenied,
AlreadyExists => FxpStatus::Failure,
Unsupported => FxpStatus::OpUnsupported,
_ => FxpStatus::Failure,
}
}
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct Attrs {
pub size: Option<u64>,
pub uid_gid: Option<(u32, u32)>,
pub permissions: Option<u32>,
pub atime_mtime: Option<(u32, u32)>,
pub extended: Vec<(Vec<u8>, Vec<u8>)>,
}
impl Attrs {
pub fn is_dir(&self) -> bool {
self.permissions.is_some_and(|m| (m & 0o170000) == 0o040000)
}
pub fn is_file(&self) -> bool {
self.permissions.is_some_and(|m| (m & 0o170000) == 0o100000)
}
pub fn is_symlink(&self) -> bool {
self.permissions.is_some_and(|m| (m & 0o170000) == 0o120000)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NameEntry {
pub filename: Vec<u8>,
pub longname: Vec<u8>,
pub attrs: Attrs,
}
#[derive(Debug)]
pub enum SftpError {
Io(io::Error),
Format(&'static str),
Protocol(&'static str),
Status {
code: FxpStatus,
message: String,
},
}
impl fmt::Display for SftpError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SftpError::Io(e) => write!(f, "sftp io: {e}"),
SftpError::Format(s) => write!(f, "sftp format: {s}"),
SftpError::Protocol(s) => write!(f, "sftp protocol: {s}"),
SftpError::Status { code, message } => {
if message.is_empty() {
write!(f, "sftp status: {code:?}")
} else {
write!(f, "sftp status: {code:?} ({message})")
}
}
}
}
}
impl std::error::Error for SftpError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
SftpError::Io(e) => Some(e),
_ => None,
}
}
}
impl From<io::Error> for SftpError {
fn from(e: io::Error) -> Self {
SftpError::Io(e)
}
}
impl From<crate::Error> for SftpError {
fn from(e: crate::Error) -> Self {
match e {
crate::Error::Format(s) => SftpError::Format(s),
crate::Error::Io(e) => SftpError::Io(e),
other => SftpError::Protocol(match other {
crate::Error::Protocol(s) => s,
_ => "transport error",
}),
}
}
}
impl SftpError {
pub fn status(code: FxpStatus) -> Self {
SftpError::Status {
code,
message: String::new(),
}
}
pub fn status_msg(code: FxpStatus, message: impl Into<String>) -> Self {
SftpError::Status {
code,
message: message.into(),
}
}
}