#[cfg(feature = "blocking")]
use crate::builder::RcPointer;
use crate::builder::{ArcPointer, BuilderError, PointerFamily};
use crate::client::ClientArc;
#[cfg(feature = "blocking")]
use crate::client::ClientRc;
use crate::config::BucketBase;
use crate::decode::{InnerItemError, ListError, RefineBucket, RefineBucketList, RefineObjectList};
#[cfg(feature = "blocking")]
use crate::file::blocking::AlignBuilder as BlockingAlignBuilder;
use crate::file::AlignBuilder;
use crate::object::{ExtractListError, Object, ObjectList, Objects, StorageClass};
use crate::types::{
CanonicalizedResource, InvalidBucketName, InvalidEndPoint, Query, QueryKey, QueryValue,
BUCKET_INFO,
};
use crate::{BucketName, EndPoint};
use chrono::{DateTime, NaiveDateTime, Utc};
use http::Method;
use oss_derive::oss_gen_rc;
use std::error::Error;
use std::fmt::{self, Display};
use std::marker::PhantomData;
use std::num::ParseIntError;
#[cfg(feature = "blocking")]
use std::rc::Rc;
use std::sync::Arc;
#[cfg(test)]
mod test;
#[derive(Clone)]
#[non_exhaustive]
pub struct ListBuckets<
PointerSel: PointerFamily = ArcPointer,
Item: RefineBucket<E> = Bucket<PointerSel>,
E: Error + 'static = BucketError,
> {
prefix: String,
marker: String,
max_keys: u16,
is_truncated: bool,
next_marker: String,
id: String,
display_name: String,
buckets: Vec<Item>,
#[allow(dead_code)]
client: PointerSel::PointerType,
ph_err: PhantomData<E>,
}
pub type Buckets<Item = Bucket<ArcPointer>, Error = BucketError> =
ListBuckets<ArcPointer, Item, Error>;
#[cfg(feature = "blocking")]
pub type BucketsBlocking<Item = Bucket<RcPointer>, Error = BucketError> =
ListBuckets<RcPointer, Item, Error>;
impl<T: PointerFamily, Item: RefineBucket<E> + std::fmt::Debug, E: Error> fmt::Debug
for ListBuckets<T, Item, E>
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("ListBuckets")
.field("prefix", &self.prefix)
.field("marker", &self.marker)
.field("max_keys", &self.max_keys)
.field("is_truncated", &self.is_truncated)
.field("next_marker", &self.next_marker)
.field("id", &self.id)
.field("display_name", &self.display_name)
.field("buckets", &self.buckets)
.finish()
}
}
#[oss_gen_rc]
impl<Item: RefineBucket<E>, E: Error> ListBuckets<ArcPointer, Item, E> {
fn from_client(client: Arc<ClientArc>) -> Self {
Self {
client,
..Default::default()
}
}
}
#[oss_gen_rc]
impl<Item: RefineBucket<E>, E: Error> Default for ListBuckets<ArcPointer, Item, E> {
fn default() -> Self {
Self {
prefix: Default::default(),
marker: Default::default(),
max_keys: Default::default(),
is_truncated: Default::default(),
next_marker: Default::default(),
id: Default::default(),
display_name: Default::default(),
buckets: Default::default(),
client: Default::default(),
ph_err: Default::default(),
}
}
}
#[oss_gen_rc]
impl<Item: RefineBucket<E>, E: Error> ListBuckets<ArcPointer, Item, E> {
pub fn prefix_string(&self) -> &String {
&self.prefix
}
pub fn marker_string(&self) -> &String {
&self.marker
}
pub fn next_marker_string(&self) -> &String {
&self.next_marker
}
pub fn info_string(&self) -> (&String, &String) {
(&self.id, &self.display_name)
}
pub fn to_vec(self) -> Vec<Item> {
self.buckets
}
pub fn len(&self) -> usize {
self.buckets.len()
}
pub fn is_empty(&self) -> bool {
self.buckets.is_empty()
}
}
#[derive(Clone)]
#[non_exhaustive]
pub struct Bucket<PointerSel: PointerFamily = ArcPointer> {
pub(crate) base: BucketBase,
creation_date: DateTime<Utc>,
storage_class: StorageClass,
pub(crate) client: PointerSel::PointerType,
}
impl<T: PointerFamily> fmt::Debug for Bucket<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Bucket")
.field("base", &self.base)
.field("creation_date", &self.creation_date)
.field("storage_class", &self.storage_class)
.finish()
}
}
#[oss_gen_rc]
impl Default for Bucket<ArcPointer> {
fn default() -> Self {
Self {
base: BucketBase::default(),
creation_date: DateTime::<Utc>::from_utc(
#[allow(clippy::unwrap_used)]
NaiveDateTime::from_timestamp_opt(0, 0).unwrap(),
Utc,
),
storage_class: StorageClass::default(),
client: Arc::default(),
}
}
}
impl<T: PointerFamily> AsRef<BucketBase> for Bucket<T> {
fn as_ref(&self) -> &BucketBase {
&self.base
}
}
impl<T: PointerFamily> AsRef<BucketName> for Bucket<T> {
fn as_ref(&self) -> &BucketName {
self.base.as_ref()
}
}
impl<T: PointerFamily> AsRef<EndPoint> for Bucket<T> {
fn as_ref(&self) -> &EndPoint {
self.base.as_ref()
}
}
impl<T: PointerFamily> RefineBucket<BucketError> for Bucket<T> {
fn set_name(&mut self, name: &str) -> Result<(), BucketError> {
self.base
.set_name(name.parse::<BucketName>().map_err(|e| BucketError {
source: name.to_string(),
kind: BucketErrorKind::BucketName(e),
})?);
Ok(())
}
fn set_location(&mut self, location: &str) -> Result<(), BucketError> {
self.base
.set_endpoint(location.parse::<EndPoint>().map_err(|e| BucketError {
source: location.to_string(),
kind: BucketErrorKind::EndPoint(e),
})?);
Ok(())
}
fn set_creation_date(&mut self, creation_date: &str) -> Result<(), BucketError> {
self.creation_date = creation_date.parse().map_err(|e| BucketError {
source: creation_date.to_string(),
kind: BucketErrorKind::Chrono(e),
})?;
Ok(())
}
fn set_storage_class(&mut self, storage_class: &str) -> Result<(), BucketError> {
self.storage_class = StorageClass::new(storage_class).ok_or(BucketError {
source: storage_class.to_string(),
kind: BucketErrorKind::InvalidStorageClass,
})?;
Ok(())
}
}
#[derive(Debug)]
#[non_exhaustive]
pub struct BucketError {
source: String,
kind: BucketErrorKind,
}
impl BucketError {
#[cfg(test)]
pub(crate) fn test_new() -> Self {
Self {
source: "foo".to_string(),
kind: BucketErrorKind::InvalidStorageClass,
}
}
}
impl Display for BucketError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "decode bucket xml faild, gived str: {}", self.source)
}
}
impl Error for BucketError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
use BucketErrorKind::*;
match &self.kind {
BucketName(e) => Some(e),
EndPoint(e) => Some(e),
Chrono(e) => Some(e),
InvalidStorageClass => None,
}
}
}
#[derive(Debug)]
#[non_exhaustive]
enum BucketErrorKind {
BucketName(InvalidBucketName),
EndPoint(InvalidEndPoint),
Chrono(chrono::ParseError),
InvalidStorageClass,
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
enum BucketListError {
#[error("covert max_keys failed")]
ParseInt(#[from] ParseIntError),
}
impl ListError for BucketListError {}
impl<T: PointerFamily> Bucket<T> {
pub fn new(
base: BucketBase,
creation_date: DateTime<Utc>,
storage_class: StorageClass,
client: T::PointerType,
) -> Self {
Self {
base,
creation_date,
storage_class,
client,
}
}
pub fn creation_date(&self) -> &DateTime<Utc> {
&self.creation_date
}
pub fn storage_class(&self) -> &StorageClass {
&self.storage_class
}
pub fn base(&self) -> &BucketBase {
&self.base
}
}
#[oss_gen_rc]
impl Bucket<ArcPointer> {
pub(crate) fn client(&self) -> Arc<ClientArc> {
Arc::clone(&self.client)
}
fn from_client(client: Arc<ClientArc>) -> Self {
Self {
client,
..Default::default()
}
}
}
impl Bucket {
#[inline(always)]
pub async fn get_object_list<Q: IntoIterator<Item = (QueryKey, QueryValue)>>(
&self,
query: Q,
) -> Result<ObjectList, ExtractListError> {
self.get_object_list2(Query::from_iter(query)).await
}
pub async fn get_object_list2(&self, query: Query) -> Result<ObjectList, ExtractListError> {
let bucket_arc = Arc::new(self.base.clone());
let mut list = Objects::<Object>::from_bucket(self, query.get_max_keys());
let (bucket_url, resource) = bucket_arc.get_url_resource(&query);
let response = self.builder(Method::GET, bucket_url, resource)?;
let content = response.send_adjust_error().await?;
list.decode(&content.text().await?, || {
Object::from_bucket(bucket_arc.clone())
})?;
list.set_search_query(query);
Ok(list)
}
}
#[cfg(feature = "blocking")]
impl Bucket<RcPointer> {
pub fn get_object_list<Q: IntoIterator<Item = (QueryKey, QueryValue)>>(
&self,
query: Q,
) -> Result<ObjectList<RcPointer>, ExtractListError> {
let query = Query::from_iter(query);
let bucket_arc = Rc::new(self.base.clone());
let mut list = ObjectList::<RcPointer>::from_bucket(&self, query.get_max_keys());
let (bucket_url, resource) = bucket_arc.get_url_resource(&query);
let response = self.builder(Method::GET, bucket_url, resource)?;
let content = response.send_adjust_error()?;
list.decode(&content.text()?, || {
Object::<RcPointer>::from_bucket(bucket_arc.clone())
})?;
list.set_search_query(query);
Ok(list)
}
}
impl<T: PointerFamily, Item: RefineBucket<E>, E: Error + 'static>
RefineBucketList<Item, BucketListError, E> for ListBuckets<T, Item, E>
{
fn set_prefix(&mut self, prefix: &str) -> Result<(), BucketListError> {
self.prefix = prefix.to_owned();
Ok(())
}
fn set_marker(&mut self, marker: &str) -> Result<(), BucketListError> {
self.marker = marker.to_owned();
Ok(())
}
fn set_max_keys(&mut self, max_keys: &str) -> Result<(), BucketListError> {
self.max_keys = max_keys.parse()?;
Ok(())
}
fn set_is_truncated(&mut self, is_truncated: bool) -> Result<(), BucketListError> {
self.is_truncated = is_truncated;
Ok(())
}
fn set_next_marker(&mut self, marker: &str) -> Result<(), BucketListError> {
self.next_marker = marker.to_owned();
Ok(())
}
fn set_id(&mut self, id: &str) -> Result<(), BucketListError> {
self.id = id.to_owned();
Ok(())
}
fn set_display_name(&mut self, display_name: &str) -> Result<(), BucketListError> {
self.display_name = display_name.to_owned();
Ok(())
}
fn set_list(&mut self, list: Vec<Item>) -> Result<(), BucketListError> {
self.buckets = list;
Ok(())
}
}
impl ClientArc {
pub async fn get_bucket_list(self) -> Result<ListBuckets, ExtractListError> {
let client_arc = Arc::new(self);
let init_bucket = || Bucket::<ArcPointer>::from_client(client_arc.clone());
let mut bucket_list = ListBuckets::<ArcPointer>::from_client(client_arc.clone());
client_arc
.base_bucket_list(&mut bucket_list, init_bucket)
.await?;
Ok(bucket_list)
}
pub async fn base_bucket_list<List, Item, F, E, ItemErr>(
&self,
list: &mut List,
init_bucket: F,
) -> Result<(), ExtractListError>
where
List: RefineBucketList<Item, E, ItemErr>,
Item: RefineBucket<ItemErr>,
E: ListError,
ItemErr: Error + 'static,
F: FnMut() -> Item,
{
let response = self
.builder(
Method::GET,
self.get_endpoint_url(),
CanonicalizedResource::default(),
)?
.send_adjust_error()
.await?;
list.decode(&response.text().await?, init_bucket)?;
Ok(())
}
pub async fn get_bucket_info(self) -> Result<Bucket, ExtractItemError> {
let name = self.get_bucket_name().clone();
let arc_client = Arc::new(self);
let mut bucket = Bucket::<ArcPointer>::from_client(arc_client.clone());
arc_client.base_bucket_info(name, &mut bucket).await?;
Ok(bucket)
}
pub async fn base_bucket_info<Bucket, Name: Into<BucketName>, E>(
&self,
name: Name,
bucket: &mut Bucket,
) -> Result<(), ExtractItemError>
where
Bucket: RefineBucket<E>,
E: Error + 'static,
{
let mut bucket_url = BucketBase::new(name.into(), self.get_endpoint().to_owned()).to_url();
let query = Some(BUCKET_INFO);
bucket_url.set_query(query);
let canonicalized = CanonicalizedResource::from_bucket(&self.get_bucket_base(), query);
let response = self.builder(Method::GET, bucket_url, canonicalized)?;
let content = response.send_adjust_error().await?;
bucket.decode(&content.text().await?)?;
Ok(())
}
}
#[derive(Debug)]
#[non_exhaustive]
pub struct ExtractItemError {
pub(crate) kind: ExtractItemErrorKind,
}
#[derive(Debug)]
#[non_exhaustive]
pub(crate) enum ExtractItemErrorKind {
#[doc(hidden)]
Builder(BuilderError),
Reqwest(reqwest::Error),
#[doc(hidden)]
Decode(InnerItemError),
}
impl From<BuilderError> for ExtractItemError {
fn from(value: BuilderError) -> Self {
use ExtractItemErrorKind::*;
Self {
kind: Builder(value),
}
}
}
impl From<reqwest::Error> for ExtractItemError {
fn from(value: reqwest::Error) -> Self {
use ExtractItemErrorKind::*;
Self {
kind: Reqwest(value),
}
}
}
impl From<InnerItemError> for ExtractItemError {
fn from(value: InnerItemError) -> Self {
use ExtractItemErrorKind::*;
Self {
kind: Decode(value),
}
}
}
impl Display for ExtractItemError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use ExtractItemErrorKind::*;
match &self.kind {
Builder(_) => "builder error".fmt(f),
Reqwest(_) => "reqwest error".fmt(f),
Decode(_) => "decode xml failed".fmt(f),
}
}
}
impl Error for ExtractItemError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
use ExtractItemErrorKind::*;
match &self.kind {
Builder(b) => Some(b),
Reqwest(b) => Some(b),
Decode(d) => d.get_source(),
}
}
}
#[cfg(feature = "blocking")]
impl ClientRc {
pub fn get_bucket_list(self) -> Result<ListBuckets<RcPointer>, ExtractListError> {
let client_arc = Rc::new(self);
let init_bucket = || Bucket::<RcPointer>::from_client(client_arc.clone());
let mut bucket_list = ListBuckets::<RcPointer>::from_client(client_arc.clone());
client_arc.base_bucket_list(&mut bucket_list, init_bucket)?;
Ok(bucket_list)
}
#[inline]
pub fn base_bucket_list<List, Item, F, E, ItemErr>(
&self,
list: &mut List,
init_bucket: F,
) -> Result<(), ExtractListError>
where
List: RefineBucketList<Item, E, ItemErr>,
Item: RefineBucket<ItemErr>,
E: ListError,
ItemErr: Error + 'static,
F: FnMut() -> Item,
{
let response = self
.builder(
Method::GET,
self.get_endpoint_url(),
CanonicalizedResource::default(),
)?
.send_adjust_error()?;
list.decode(&response.text()?, init_bucket)?;
Ok(())
}
pub fn get_bucket_info(self) -> Result<Bucket<RcPointer>, ExtractItemError> {
let name = self.get_bucket_name().clone();
let arc_client = Rc::new(self);
let mut bucket = Bucket::<RcPointer>::from_client(arc_client.clone());
arc_client.base_bucket_info(name, &mut bucket)?;
Ok(bucket)
}
#[inline]
pub fn base_bucket_info<Bucket, Name: Into<BucketName>, E>(
&self,
name: Name,
bucket: &mut Bucket,
) -> Result<(), ExtractItemError>
where
Bucket: RefineBucket<E>,
E: Error + 'static,
{
let mut bucket_url = BucketBase::new(name.into(), self.get_endpoint().to_owned()).to_url();
let query = Some(BUCKET_INFO);
bucket_url.set_query(query);
let canonicalized = CanonicalizedResource::from_bucket(&self.get_bucket_base(), query);
let response = self.builder(Method::GET, bucket_url, canonicalized)?;
let content = response.send_adjust_error()?;
bucket.decode(&content.text()?)?;
Ok(())
}
}
impl<T: PointerFamily> PartialEq<Bucket<T>> for Bucket<T> {
#[inline]
fn eq(&self, other: &Bucket<T>) -> bool {
self.base == other.base
&& self.creation_date == other.creation_date
&& self.storage_class == other.storage_class
}
}
impl<T: PointerFamily> PartialEq<DateTime<Utc>> for Bucket<T> {
#[inline]
fn eq(&self, other: &DateTime<Utc>) -> bool {
&self.creation_date == other
}
}
impl<T: PointerFamily> PartialEq<BucketBase> for Bucket<T> {
#[inline]
fn eq(&self, other: &BucketBase) -> bool {
&self.base == other
}
}