use crate::{
endpoint::SftpEndpoint,
error::Error,
list::{TreeItem, TreeItemKind, TreeItems, TreeList, strip_relative_path},
};
use chrono::DateTime;
use ssh_transfer::{SftpEntry, SftpEntryKind};
use std::{convert::TryFrom, time::SystemTime};
const PATH_SEPARATOR: char = '/';
#[async_trait::async_trait]
impl TreeList for SftpEndpoint {
async fn list(&self, root_path: &str, prefix: Option<&str>) -> Result<TreeItems, Error> {
let absolute_path = Self::get_absolute_path(root_path, prefix, PATH_SEPARATOR);
let connection = self.connection();
let list_result = connection
.lock()
.unwrap()
.list_over_sftp(&absolute_path)
.map(|entries| {
entries
.iter()
.filter_map(|entry| {
let item = TreeItem::try_from((root_path, entry));
if let Err(error) = &item {
log::warn!("Cannot access entry in {root_path}: {error:?}");
}
item.ok()
})
.collect()
});
let items = list_result.unwrap_or_else(|error| {
log::warn!("Cannot access {root_path} content: {error:?}");
TreeItems::default()
});
Ok(items)
}
}
impl TryFrom<(&str, &SftpEntry)> for TreeItem {
type Error = Error;
fn try_from((root_path, sftp_entry): (&str, &SftpEntry)) -> Result<Self, Self::Error> {
let path = strip_relative_path(root_path, sftp_entry.path(), PATH_SEPARATOR);
let kind = TreeItemKind::try_from(sftp_entry.kind())?;
let size = sftp_entry
.size()
.ok_or_else(|| Error::Other("Could not extract entry size".to_string()))?;
let last_update = sftp_entry
.last_modification_time()
.ok_or_else(|| Error::Other("Could not extract entry last modification time".to_string()))
.map(|timestamp| DateTime::from_timestamp(timestamp as i64, 0))?
.ok_or_else(|| Error::Other("Invalid last modification time".to_string()))
.map(SystemTime::from)?;
Ok(Self::new(
kind,
&path,
root_path,
size,
last_update,
None,
PATH_SEPARATOR,
))
}
}
impl TryFrom<&SftpEntryKind> for TreeItemKind {
type Error = Error;
fn try_from(entry_kind: &SftpEntryKind) -> Result<Self, Self::Error> {
match entry_kind {
SftpEntryKind::File => Ok(TreeItemKind::File),
SftpEntryKind::Directory => Ok(TreeItemKind::Folder),
SftpEntryKind::SymLink => Ok(TreeItemKind::Link),
SftpEntryKind::Other => Err(Error::Other("Unsupported entry type".to_string())),
}
}
}