wezterm_ssh/sftp/
error.rs

1use std::convert::TryFrom;
2use thiserror::Error;
3
4/// Represents a result whose error is [`SftpError`]
5pub type SftpResult<T> = Result<T, SftpError>;
6
7/// Represents errors associated with sftp operations
8#[derive(Copy, Clone, Debug, Error, Hash, PartialEq, Eq)]
9pub enum SftpError {
10    // Following are available on libssh and libssh2
11    #[error("End-of-file encountered")]
12    Eof = 1,
13    #[error("File doesn't exist")]
14    NoSuchFile = 2,
15    #[error("Permission denied")]
16    PermissionDenied = 3,
17    #[error("Generic failure")]
18    Failure = 4,
19    #[error("Garbage received from server")]
20    BadMessage = 5,
21    #[error("No connection has been set up")]
22    NoConnection = 6,
23    #[error("There was a connection, but we lost it")]
24    ConnectionLost = 7,
25    #[error("Operation not supported by the server")]
26    OpUnsupported = 8,
27    #[error("Invalid file handle")]
28    InvalidHandle = 9,
29    #[error("No such file or directory path exists")]
30    NoSuchPath = 10,
31    #[error("An attempt to create an already existing file or directory has been made")]
32    FileAlreadyExists = 11,
33    #[error("We are trying to write on a write-protected filesystem")]
34    WriteProtect = 12,
35    #[error("No media in remote drive")]
36    NoMedia = 13,
37
38    // Below are libssh2-specific errors
39    #[cfg(feature = "ssh2")]
40    #[error("No space available on filesystem")]
41    NoSpaceOnFilesystem = 14,
42    #[cfg(feature = "ssh2")]
43    #[error("Quota exceeded")]
44    QuotaExceeded = 15,
45    #[cfg(feature = "ssh2")]
46    #[error("Unknown principal")]
47    UnknownPrincipal = 16,
48    #[cfg(feature = "ssh2")]
49    #[error("Filesystem lock conflict")]
50    LockConflict = 17,
51    #[cfg(feature = "ssh2")]
52    #[error("Directory is not empty")]
53    DirNotEmpty = 18,
54    #[cfg(feature = "ssh2")]
55    #[error("Operation attempted against a path that is not a directory")]
56    NotADirectory = 19,
57    #[cfg(feature = "ssh2")]
58    #[error("Filename invalid")]
59    InvalidFilename = 20,
60    #[cfg(feature = "ssh2")]
61    #[error("Symlink loop encountered")]
62    LinkLoop = 21,
63}
64
65impl SftpError {
66    /// Produces an SFTP error from the given code if it matches a known error type
67    pub fn from_error_code(code: i32) -> Option<SftpError> {
68        Self::try_from(code).ok()
69    }
70
71    /// Converts into an error code
72    pub fn to_error_code(self) -> i32 {
73        self as i32
74    }
75}
76
77impl TryFrom<i32> for SftpError {
78    type Error = Result<(), i32>;
79
80    /// Attempt to convert an arbitrary code to an sftp error, returning
81    /// `Ok` if matching an sftp error or `Err` if the code represented a
82    /// success or was unknown
83    fn try_from(code: i32) -> Result<Self, Self::Error> {
84        match code {
85            // 0 means okay in libssh and libssh2, which isn't an error
86            0 => Err(Ok(())),
87
88            1 => Ok(Self::Eof),
89            2 => Ok(Self::NoSuchFile),
90            3 => Ok(Self::PermissionDenied),
91            4 => Ok(Self::Failure),
92            5 => Ok(Self::BadMessage),
93            6 => Ok(Self::NoConnection),
94            7 => Ok(Self::ConnectionLost),
95            8 => Ok(Self::OpUnsupported),
96            9 => Ok(Self::InvalidHandle),
97            10 => Ok(Self::NoSuchPath),
98            11 => Ok(Self::FileAlreadyExists),
99            12 => Ok(Self::WriteProtect),
100            13 => Ok(Self::NoMedia),
101
102            // Errors only available with ssh2
103            #[cfg(feature = "ssh2")]
104            14 => Ok(Self::NoSpaceOnFilesystem),
105            #[cfg(feature = "ssh2")]
106            15 => Ok(Self::QuotaExceeded),
107            #[cfg(feature = "ssh2")]
108            16 => Ok(Self::UnknownPrincipal),
109            #[cfg(feature = "ssh2")]
110            17 => Ok(Self::LockConflict),
111            #[cfg(feature = "ssh2")]
112            18 => Ok(Self::DirNotEmpty),
113            #[cfg(feature = "ssh2")]
114            19 => Ok(Self::NotADirectory),
115            #[cfg(feature = "ssh2")]
116            20 => Ok(Self::InvalidFilename),
117            #[cfg(feature = "ssh2")]
118            21 => Ok(Self::LinkLoop),
119
120            // Unsupported codes get reflected back
121            x => Err(Err(x)),
122        }
123    }
124}
125
126#[cfg(feature = "ssh2")]
127impl TryFrom<ssh2::Error> for SftpError {
128    type Error = ssh2::Error;
129
130    fn try_from(err: ssh2::Error) -> Result<Self, Self::Error> {
131        match err.code() {
132            ssh2::ErrorCode::SFTP(x) => match Self::from_error_code(x) {
133                Some(err) => Ok(err),
134                None => Err(err),
135            },
136            _ => Err(err),
137        }
138    }
139}
140
141#[cfg(feature = "ssh2")]
142impl TryFrom<ssh2::ErrorCode> for SftpError {
143    type Error = ssh2::ErrorCode;
144
145    fn try_from(code: ssh2::ErrorCode) -> Result<Self, Self::Error> {
146        match code {
147            ssh2::ErrorCode::SFTP(x) => match Self::from_error_code(x) {
148                Some(err) => Ok(err),
149                None => Err(code),
150            },
151            x => Err(x),
152        }
153    }
154}