use crate::{
endpoint::GcsEndpoint,
error::Error,
list::{TreeItem, TreeItemKind, TreeItems, TreeList, join_path, strip_relative_path},
};
use cloud_storage::{ListRequest, Object};
use futures::StreamExt;
use std::{convert::TryFrom, time::SystemTime};
const PATH_SEPARATOR: char = '/';
#[async_trait::async_trait]
impl TreeList for GcsEndpoint {
async fn list(&self, root_path: &str, prefix: Option<&str>) -> Result<TreeItems, Error> {
let delimiter = Self::get_delimiter(PATH_SEPARATOR);
let request_prefix = Self::get_request_prefix(root_path, prefix);
let root_path = Self::get_root_path(root_path, PATH_SEPARATOR);
let request = ListRequest {
delimiter: Some(delimiter.clone()),
prefix: request_prefix,
..Default::default()
};
let client = self.connection();
let root_path = root_path.as_str();
let items = match client.object().list(self.bucket(), request).await {
Ok(list_stream) => {
let object_lists = list_stream
.filter_map(|object_list| async move {
object_list
.map(|list| {
list
.items
.into_iter()
.filter_map(|object| {
let item = TreeItem::try_from((root_path, &object));
if let Err(error) = &item {
log::warn!("Cannot access entry in {}: {:?}", self.bucket(), error);
}
item.ok()
})
.chain(
list
.prefixes
.into_iter()
.map(|prefix| GcsPrefix { prefix })
.filter_map(|prefix| {
let item = TreeItem::try_from((root_path, &prefix));
if let Err(error) = &item {
log::warn!("Cannot access entry in {}: {:?}", self.bucket(), error);
}
item.ok()
}),
)
.collect::<TreeItems>()
})
.ok()
})
.collect::<Vec<TreeItems>>() .await;
object_lists.into_iter().flatten().collect::<TreeItems>()
}
Err(error) => {
log::warn!("Cannot access {} content: {:?}", self.bucket(), error);
TreeItems::default()
}
};
Ok(items)
}
}
impl GcsEndpoint {
fn get_request_prefix(root_path: &str, prefix: Option<&str>) -> Option<String> {
if (root_path.is_empty() || root_path == PATH_SEPARATOR.to_string()) && prefix.is_none() {
None
} else {
Some(format!(
"{}{}",
Self::get_absolute_prefix(root_path, prefix, PATH_SEPARATOR)
.trim_start_matches(PATH_SEPARATOR),
PATH_SEPARATOR,
))
}
}
}
struct GcsPrefix {
prefix: String,
}
impl TryFrom<(&str, &Object)> for TreeItem {
type Error = Error;
fn try_from((root_path, object): (&str, &Object)) -> Result<Self, Self::Error> {
let path = strip_relative_path(root_path, &object.name, PATH_SEPARATOR);
Ok(Self::new(
TreeItemKind::File,
&path,
root_path,
object.size,
SystemTime::from(object.updated),
object.content_type.clone(),
PATH_SEPARATOR,
))
}
}
impl TryFrom<(&str, &GcsPrefix)> for TreeItem {
type Error = Error;
fn try_from((root_path, prefix): (&str, &GcsPrefix)) -> Result<Self, Self::Error> {
let path = join_path(
root_path,
prefix.prefix.trim_end_matches(PATH_SEPARATOR),
PATH_SEPARATOR,
);
Ok(Self::new(
TreeItemKind::Folder,
&path,
root_path,
0,
SystemTime::UNIX_EPOCH,
None,
'/',
))
}
}
#[test]
pub fn test_get_root_path() {
assert_eq!("/", &GcsEndpoint::get_root_path("", PATH_SEPARATOR));
assert_eq!("/", &GcsEndpoint::get_root_path("/", PATH_SEPARATOR));
assert_eq!("/root", &GcsEndpoint::get_root_path("root", PATH_SEPARATOR));
assert_eq!(
"/root",
&GcsEndpoint::get_root_path("root/", PATH_SEPARATOR)
);
assert_eq!(
"/root",
&GcsEndpoint::get_root_path("/root", PATH_SEPARATOR)
);
assert_eq!(
"/root",
&GcsEndpoint::get_root_path("/root/", PATH_SEPARATOR)
);
assert_eq!(
"/root/path",
&GcsEndpoint::get_root_path("root/path", PATH_SEPARATOR)
);
assert_eq!(
"/root/path",
&GcsEndpoint::get_root_path("root/path/", PATH_SEPARATOR)
);
assert_eq!(
"/root/path",
&GcsEndpoint::get_root_path("/root/path", PATH_SEPARATOR)
);
assert_eq!(
"/root/path",
&GcsEndpoint::get_root_path("/root/path/", PATH_SEPARATOR)
);
}
#[test]
pub fn test_list_request_prefix() {
assert_eq!(None, GcsEndpoint::get_request_prefix("", None));
assert_eq!(None, GcsEndpoint::get_request_prefix("/", None));
assert_eq!(
Some("root/".to_string()),
GcsEndpoint::get_request_prefix("/root", None)
);
assert_eq!(
Some("root/path/".to_string()),
GcsEndpoint::get_request_prefix("/root/path", None)
);
assert_eq!(
Some("prefix/".to_string()),
GcsEndpoint::get_request_prefix("", Some("prefix"))
);
assert_eq!(
Some("prefix/".to_string()),
GcsEndpoint::get_request_prefix("/", Some("prefix"))
);
assert_eq!(
Some("root/prefix/".to_string()),
GcsEndpoint::get_request_prefix("/root", Some("prefix"))
);
assert_eq!(
Some("root/path/prefix/".to_string()),
GcsEndpoint::get_request_prefix("/root/path", Some("prefix"))
);
}