use crate::{error::Error, secret::SftpSecret};
use ssh_transfer::{AuthenticationType, Configuration, Connection, KnownHost};
use std::{
convert::TryFrom,
sync::{Arc, Mutex},
};
#[derive(Clone)]
pub struct SftpEndpoint {
connection: Arc<Mutex<Connection>>,
root_directory: String,
}
unsafe impl Sync for SftpEndpoint {}
impl TryFrom<&SftpSecret> for SftpEndpoint {
type Error = Error;
fn try_from(secret: &SftpSecret) -> Result<Self, Self::Error> {
let connection = Self::connect(secret)?;
let connection = Arc::new(Mutex::new(connection));
Ok(Self {
connection,
root_directory: secret
.root_directory
.clone()
.unwrap_or_else(|| "/".to_string()),
})
}
}
impl SftpEndpoint {
fn connect(secret: &SftpSecret) -> Result<Connection, Error> {
log::debug!(
"Attempting to connect to {}:{}.",
secret.hostname(),
secret.port()
);
let authentication_type = if let Some(password) = secret.password() {
AuthenticationType::Password(password)
} else if let Some(private_key) = secret.private_key() {
AuthenticationType::KeyMemory(private_key)
} else if let Some(private_key_file_path) = &secret.private_key_file_path() {
AuthenticationType::KeyFile(private_key_file_path.into())
} else {
AuthenticationType::Agent
};
let configuration = Configuration::new(secret.hostname())
.with_port(secret.port())
.with_username(secret.username())
.with_authentication(authentication_type);
let mut connection = Connection::new(&configuration)?;
if let Some(known_host) = &secret.known_host() {
let known_host = KnownHost::try_from(known_host.as_str())?;
connection.add_known_host(&known_host)?;
}
connection.start()?;
Ok(connection)
}
pub fn connection(&self) -> Arc<Mutex<Connection>> {
self.connection.clone()
}
pub fn absolute_path(&self, path: &str) -> String {
format!(
"{}/{}",
self.root_directory.trim_end_matches('/'),
path.trim_start_matches('/')
)
}
}