use super::model::error::Error as ErrorResponse;
use crate::{
errors::status_to_response,
model::{ object::ListBucketResult, Empty },
oss::{ ObjectMeta, RequestType },
prelude::OSS,
};
use std::collections::HashMap;
use super::errors::OSSError;
use async_trait::async_trait;
use bytes::Bytes;
use reqwest::StatusCode;
#[async_trait]
pub trait ObjectAPI {
async fn list_object<S, H, R>(
&self,
headers: H,
resources: R
)
-> Result<ListBucketResult, OSSError>
where
S: AsRef<str>,
H: Into<Option<HashMap<S, S>>> + Send,
R: Into<Option<HashMap<S, Option<S>>>> + Send;
async fn list_object_v2<S>(
&self,
prefix: Option<S>,
delimiter: Option<S>
) -> Result<ListBucketResult, OSSError>
where S: AsRef<str> + Send;
async fn get_object<S1, S2, H, R>(
&self,
object_name: S1,
headers: H,
resources: R
)
-> Result<Bytes, OSSError>
where
S1: AsRef<str> + Send,
S2: AsRef<str> + Send,
H: Into<Option<HashMap<S2, S2>>> + Send,
R: Into<Option<HashMap<S2, Option<S2>>>> + Send;
async fn put_object<S1, S2, H, R>(
&self,
buf: &[u8],
object_name: S1,
headers: H,
resources: R
)
-> Result<(), OSSError>
where
S1: AsRef<str> + Send,
S2: AsRef<str> + Send,
H: Into<Option<HashMap<S2, S2>>> + Send,
R: Into<Option<HashMap<S2, Option<S2>>>> + Send;
async fn append_object<S1, S2, H, R>(
&self,
buf: &[u8],
object_name: S1,
headers: H,
resources: R
)
-> Result<Option<u64>, OSSError>
where
S1: AsRef<str> + Send,
S2: AsRef<str> + Send,
H: Into<Option<HashMap<S2, S2>>> + Send,
R: Into<Option<HashMap<S2, Option<S2>>>> + Send;
async fn copy_object_from_object<S1, S2, S3, H, R>(
&self,
src: S1,
dest: S2,
headers: H,
resources: R
)
-> Result<(), OSSError>
where
S1: AsRef<str> + Send,
S2: AsRef<str> + Send,
S3: AsRef<str> + Send,
H: Into<Option<HashMap<S3, S3>>> + Send,
R: Into<Option<HashMap<S3, Option<S3>>>> + Send;
async fn delete_object<S>(&self, object_name: S) -> Result<(), OSSError>
where S: AsRef<str> + Send;
async fn head_object<S>(&self, object_name: S) -> Result<ObjectMeta, OSSError>
where S: AsRef<str> + Send;
}
#[async_trait]
impl<'a> ObjectAPI for OSS<'a> {
async fn list_object<S, H, R>(
&self,
headers: H,
resources: R
)
-> Result<ListBucketResult, OSSError>
where
S: AsRef<str>,
H: Into<Option<HashMap<S, S>>> + Send,
R: Into<Option<HashMap<S, Option<S>>>> + Send
{
let (host, headers) = self.build_request(
RequestType::Get,
String::new(),
headers,
resources
)?;
let resp = self.http_client.get(host).headers(headers).send().await?;
let status = resp.status();
let text = resp.text().await?;
status_to_response(status, text)
}
async fn list_object_v2<S>(
&self,
prefix: Option<S>,
delimiter: Option<S>
) -> Result<ListBucketResult, OSSError>
where S: AsRef<str> + Send
{
let mut resources = HashMap::new();
resources.insert("list-type".to_string(), Some("2".to_string()));
resources.insert(
"delimiter".to_string(),
delimiter.map(|s| s.as_ref().to_string())
);
resources.insert(
"prefix".to_string(),
prefix.map(|s| s.as_ref().to_string())
);
resources.insert("max-keys".to_string(), Some("1000".to_string()));
let headers = HashMap::new();
let (host, headers) = self.build_request(
RequestType::Get,
String::new(),
Some(headers),
resources
)?;
let resp = self.http_client.get(host).headers(headers).send().await?;
let status = resp.status();
let text = resp.text().await?;
status_to_response(status, text)
}
async fn get_object<S1, S2, H, R>(
&self,
object_name: S1,
headers: H,
resources: R
)
-> Result<Bytes, OSSError>
where
S1: AsRef<str> + Send,
S2: AsRef<str> + Send,
H: Into<Option<HashMap<S2, S2>>> + Send,
R: Into<Option<HashMap<S2, Option<S2>>>> + Send
{
let (host, headers) = self.build_request(
RequestType::Get,
object_name,
headers,
resources
)?;
let resp = self.http_client.get(&host).headers(headers).send().await?;
let status = resp.status();
if status.is_success() {
Ok(resp.bytes().await?)
} else {
Err(OSSError::Object {
status_code: status,
message: "get object fail".into(),
})
}
}
async fn append_object<S1, S2, H, R>(
&self,
buf: &[u8],
object_name: S1,
headers: H,
resources: R
)
-> Result<Option<u64>, OSSError>
where
S1: AsRef<str> + Send,
S2: AsRef<str> + Send,
H: Into<Option<HashMap<S2, S2>>> + Send,
R: Into<Option<HashMap<S2, Option<S2>>>> + Send
{
let (host, headers) = self.build_request(
RequestType::Post,
object_name,
headers,
resources
)?;
let resp = self.http_client.post(&host).headers(headers).body(buf.to_owned()).send().await?;
let status = resp.status();
let resp_headers = resp.headers();
match status {
| StatusCode::OK
| StatusCode::NO_CONTENT
| StatusCode::CREATED
| StatusCode::ACCEPTED => {
let next_position = if
let Some(next) = resp_headers.get("x-oss-next-append-position")
{
let next = String::from_utf8_lossy(next.as_bytes()).to_string();
match next.parse::<u64>() {
Ok(u) => Some(u),
Err(_) => None,
}
} else {
None
};
Ok(next_position)
}
StatusCode::BAD_REQUEST | StatusCode::FORBIDDEN | StatusCode::CONFLICT => {
let text = resp.text().await?;
let er: ErrorResponse = serde_xml_rs::from_str(&text)?;
Err(OSSError::Object {
status_code: status,
message: er.message,
})
}
_ => Err(OSSError::Unknown),
}
}
async fn put_object<S1, S2, H, R>(
&self,
buf: &[u8],
object_name: S1,
headers: H,
resources: R
)
-> Result<(), OSSError>
where
S1: AsRef<str> + Send,
S2: AsRef<str> + Send,
H: Into<Option<HashMap<S2, S2>>> + Send,
R: Into<Option<HashMap<S2, Option<S2>>>> + Send
{
let (host, headers) = self.build_request(
RequestType::Put,
object_name,
headers,
resources
)?;
let resp = self.http_client.put(&host).headers(headers).body(buf.to_owned()).send().await?;
let status = resp.status();
let text = resp.text().await?;
let _ = status_to_response::<Empty>(status, text)?;
Ok(())
}
async fn copy_object_from_object<S1, S2, S3, H, R>(
&self,
src: S1,
dest: S2,
headers: H,
resources: R
)
-> Result<(), OSSError>
where
S1: AsRef<str> + Send,
S2: AsRef<str> + Send,
S3: AsRef<str> + Send,
H: Into<Option<HashMap<S3, S3>>> + Send,
R: Into<Option<HashMap<S3, Option<S3>>>> + Send
{
let (host, mut headers) = self.build_request(RequestType::Put, dest, headers, resources)?;
headers.insert("x-oss-copy-source", src.as_ref().parse()?);
let resp = self.http_client.put(&host).headers(headers).send().await?;
let status = resp.status();
let text = resp.text().await?;
status_to_response::<Empty>(status, text)?;
Ok(())
}
async fn delete_object<S>(&self, object_name: S) -> Result<(), OSSError>
where S: AsRef<str> + Send
{
let headers = HashMap::<String, String>::new();
let (host, headers) = self.build_request(
RequestType::Delete,
object_name,
Some(headers),
None
)?;
let resp = self.http_client.delete(&host).headers(headers).send().await?;
let status = resp.status();
let text = resp.text().await?;
let _ = status_to_response::<Empty>(status, text)?;
Ok(())
}
async fn head_object<S>(&self, object_name: S) -> Result<ObjectMeta, OSSError>
where S: AsRef<str> + Send
{
let (host, headers) = self.build_request(
RequestType::Head,
object_name,
None::<HashMap<String, String>>,
None
)?;
let resp = self.http_client.head(&host).headers(headers).send().await?;
let status = resp.status();
if resp.status().is_success() {
Ok(ObjectMeta::from_header_map(resp.headers())?)
} else {
Err(OSSError::Object {
status_code: status,
message: "head object error".into(),
})
}
}
}