mod file_tree;
mod ftp_tree;
mod gcs_tree;
mod one_drive_tree;
mod s3_tree;
mod sftp_tree;
use crate::error::Error;
use async_trait::async_trait;
use futures::{StreamExt, TryStreamExt};
use serde::{Deserialize, Serialize};
use std::time::SystemTime;
pub type TreeItems = Vec<TreeItem>;
#[async_trait]
pub trait TreeList {
async fn list(&self, root_path: &str, prefix: Option<&str>) -> Result<TreeItems, Error>;
async fn list_tree(&self, root_path: &str, prefix: Option<&str>) -> Result<TreeItems, Error> {
let mut items = self.list(root_path, prefix).await?;
let folders = items
.iter()
.filter(|item| item.kind() == TreeItemKind::Folder);
let mut sub_items = async_std::stream::from_iter(folders)
.then(|folder| async move {
self
.list_tree(root_path, Some(folder.relative_path()))
.await
})
.try_collect::<Vec<TreeItems>>()
.await?
.into_iter()
.flatten()
.collect::<TreeItems>();
items.append(&mut sub_items);
Ok(items)
}
fn get_absolute_prefix(root_path: &str, prefix: Option<&str>, separator: char) -> String {
if let Some(prefix) = prefix {
join_path(root_path, prefix, separator)
} else {
root_path
.to_string()
.trim_end_matches(separator)
.to_string()
}
}
fn get_absolute_path(root_path: &str, prefix: Option<&str>, separator: char) -> String {
format!(
"{}{}",
separator,
Self::get_absolute_prefix(root_path, prefix, separator).trim_start_matches(separator)
)
}
fn get_delimiter(separator: char) -> String {
separator.to_string()
}
fn get_root_path(root_path: &str, separator: char) -> String {
format!(
"{}{}",
separator,
root_path
.trim_start_matches(separator)
.trim_end_matches(separator)
)
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct TreeItem {
#[serde(rename = "type")]
kind: TreeItemKind,
path: String,
root_path: String,
size: u64,
last_update: SystemTime,
mime_type: Option<String>,
separator: char,
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum TreeItemKind {
Folder,
File,
Link,
}
impl TreeItem {
pub fn new(
kind: TreeItemKind,
path: &str,
root_path: &str,
size: u64,
last_update: SystemTime,
mime_type: Option<String>,
separator: char,
) -> Self {
Self {
kind,
path: path.to_string(),
root_path: root_path.to_string(),
size,
last_update,
mime_type,
separator,
}
}
pub fn kind(&self) -> TreeItemKind {
self.kind
}
pub fn path(&self) -> String {
join_path(&self.root_path, &self.path, self.separator)
}
pub fn relative_path(&self) -> &str {
&self.path
}
pub fn root_path(&self) -> &str {
&self.root_path
}
pub fn size(&self) -> u64 {
self.size
}
pub fn last_update(&self) -> SystemTime {
self.last_update
}
pub fn mime_type(&self) -> &Option<String> {
&self.mime_type
}
pub fn separator(&self) -> char {
self.separator
}
}
pub fn join_path(root: &str, path: &str, separator: char) -> String {
let root = root.trim_end_matches(separator);
let path = path.trim_start_matches(separator);
format!("{root}{separator}{path}")
}
pub fn strip_relative_path(prefix: &str, path: &str, separator: char) -> String {
if prefix == separator.to_string() {
format!(
"{separator}{}",
path
.trim_start_matches(separator)
.trim_end_matches(separator)
)
} else {
path
.trim_start_matches(separator)
.trim_start_matches(
prefix
.trim_start_matches(separator)
.trim_end_matches(separator),
)
.trim_end_matches(separator)
.to_string()
}
}