use crate::{
endpoint::FileEndpoint,
error::Error,
list::{TreeItem, TreeItemKind, TreeItems, TreeList, strip_relative_path},
};
use async_trait::async_trait;
use std::{
convert::TryFrom,
fs::{DirEntry, Metadata, read_dir},
};
const PATH_SEPARATOR: char = std::path::MAIN_SEPARATOR;
#[async_trait]
impl TreeList for FileEndpoint {
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 list_result = read_dir(absolute_path).map(|entries| {
entries
.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, Result<DirEntry, std::io::Error>)> for TreeItem {
type Error = Error;
fn try_from(
(root_path, dir_entry): (&str, Result<DirEntry, std::io::Error>),
) -> Result<Self, Self::Error> {
let entry = dir_entry?;
let metadata = entry.metadata()?;
let path = entry.path();
let path = path
.to_str()
.ok_or_else(|| Error::Other(format!("Cannot get directory entry path: {path:?}")))?;
let root_path = root_path.trim_end_matches(PATH_SEPARATOR);
let relative_path = strip_relative_path(root_path, path, PATH_SEPARATOR);
let size = metadata.len();
let last_update = metadata.modified()?;
let kind = TreeItemKind::try_from(metadata)?;
Ok(Self::new(
kind,
&relative_path,
root_path,
size,
last_update,
None,
PATH_SEPARATOR,
))
}
}
impl TryFrom<Metadata> for TreeItemKind {
type Error = Error;
fn try_from(metadata: Metadata) -> Result<Self, Self::Error> {
if metadata.is_dir() {
Ok(TreeItemKind::Folder)
} else if metadata.is_file() {
Ok(TreeItemKind::File)
} else if metadata.is_symlink() {
Ok(TreeItemKind::Link)
} else {
Err(Error::Other(format!(
"Invalid tree item type: {:?}",
metadata.file_type()
)))
}
}
}