use std::io;
use thiserror::Error;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Error)]
pub enum Error {
#[error("Failed to connect to {host}:{port}: {source}")]
ConnectionFailed {
host: String,
port: u16,
#[source]
source: io::Error,
},
#[error("SSH handshake failed: {0}")]
HandshakeFailed(#[from] ssh2::Error),
#[error("Authentication failed for user '{username}': {reason}")]
AuthenticationFailed { username: String, reason: String },
#[error("Failed to open SSH channel: {0}")]
ChannelFailed(String),
#[error("Command execution failed: {0}")]
ExecutionFailed(String),
#[error("I/O error: {0}")]
Io(#[from] io::Error),
#[error("Invalid configuration: {0}")]
InvalidConfig(String),
#[error("Client is not connected. Call connect() first.")]
NotConnected,
#[error("Private key file not found or unreadable: {path}")]
PrivateKeyNotFound { path: String },
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display() {
let err = Error::NotConnected;
assert_eq!(
err.to_string(),
"Client is not connected. Call connect() first."
);
}
#[test]
fn test_authentication_error() {
let err = Error::AuthenticationFailed {
username: "testuser".to_string(),
reason: "Invalid password".to_string(),
};
assert!(err.to_string().contains("testuser"));
assert!(err.to_string().contains("Invalid password"));
}
#[test]
fn test_connection_error() {
let io_err = io::Error::new(io::ErrorKind::ConnectionRefused, "refused");
let err = Error::ConnectionFailed {
host: "example.com".to_string(),
port: 22,
source: io_err,
};
assert!(err.to_string().contains("example.com"));
assert!(err.to_string().contains("22"));
}
}