#![doc = include_str!("../README.md")]
use anyhow::Context as _;
use anyhow::anyhow;
use http::HeaderMap;
use http::{HeaderValue, Method};
use serde::Deserialize;
use std::borrow::Cow;
use std::str::FromStr;
mod auth;
mod common;
mod from_body;
mod into_body;
mod open_object;
mod query_value;
mod serializes;
mod v3;
use from_body::{
BinaryResponseWithMetaWrap, BinaryResponseWrap, BinaryWithMeta, CODE_MESSAGE, FromBody,
IntoResponse, JsonResponseWrap, SelfResponseWrap, ToCodeMessage, XmlResponseWrap,
};
use into_body::{Form, IntoBody, JsonBody, OctetStream, XmlBody};
pub use open_object::{OpenObject, OpenObjectResponse, Value};
use query_value::QueryValue;
use serializes::{FlatSerialize, SimpleSerialize};
pub use v3::AccessKeySecret;
pub use common::deserialize_default_on_null;
#[cfg(feature = "ocr")]
pub mod ocr;
#[cfg(feature = "sms")]
pub mod sms;
#[cfg(feature = "ecs")]
pub mod ecs;
#[cfg(feature = "oss")]
pub mod oss;
#[cfg(feature = "fc")]
pub mod fc;
#[cfg(feature = "rds")]
pub mod rds;
#[cfg(test)]
mod sample;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error(transparent)]
Ali(#[from] CodeMessage),
#[error(transparent)]
JsonSerialize(#[from] serde_json::Error),
#[error(transparent)]
Other(#[from] anyhow::Error),
}
pub type Result<T, E = Error> = std::result::Result<T, E>;
pub(crate) trait ToFormData {
fn to_form_data(&self) -> Vec<(Cow<'static, str>, QueryValue<'_>)>;
}
trait Request: Sized + Send {
const METHOD: Method;
const URL_PATH: &'static str = "/";
const ACTION: &'static str;
type Body: IntoBody + Send;
type ResponseWrap: FromBody + ToCodeMessage + IntoResponse;
fn to_query_params(&self) -> Vec<(Cow<'static, str>, QueryValue<'_>)> {
Vec::new()
}
fn process_endpoint(&self, endpoint: &'static str) -> Cow<'static, str> {
endpoint.into()
}
fn to_headers(&self) -> Vec<(Cow<'static, str>, String)> {
Vec::new()
}
fn resource_path(&self) -> Cow<'static, str> {
"/".into()
}
fn to_body(self) -> Self::Body;
fn get_path_args(&self) -> Box<[(&'static str, String)]> {
Box::new([])
}
fn from_headers(
_resp: &mut Self::ResponseWrap,
_headers: &HeaderMap<HeaderValue>,
) -> Result<()> {
Ok(())
}
}
#[derive(Debug, Deserialize, Default, thiserror::Error)]
#[serde(rename_all = "PascalCase")]
#[error("{code}: {message}")]
pub struct CodeMessage {
#[serde(default)]
pub code: String,
#[serde(default)]
pub message: String,
}
impl CodeMessage {
pub fn check(&self) -> Result<()> {
if &self.code == "OK" || (self.code.is_empty() && self.message.is_empty()) {
Ok(())
} else {
Err(anyhow!("{}: {}", self.code, self.message).into())
}
}
}
impl From<CodeMessage> for Result<()> {
fn from(value: CodeMessage) -> Self {
value.check()
}
}
macro_rules! impl_to_code_message {
($($ty:ty),* $(,)?) => {
$(
impl $crate::ToCodeMessage for $ty {
fn to_code_message(&self) -> &$crate::CodeMessage {
&self.code_message
}
}
)*
};
}
pub(crate) use impl_to_code_message;
macro_rules! impl_default_to_code_message {
($($ty:ty),* $(,)?) => {
$(
impl $crate::ToCodeMessage for $ty {
fn to_code_message(&self) -> &$crate::CodeMessage {
&$crate::CODE_MESSAGE
}
}
)*
};
}
pub(crate) use impl_default_to_code_message;
trait ParseHeaderValue: Sized {
fn parse_header_value(s: &str) -> Result<Self>;
}
impl<T: FromStr> ParseHeaderValue for Option<T>
where
T::Err: std::error::Error + Send + Sync + 'static,
{
fn parse_header_value(s: &str) -> Result<Self> {
let v = s.parse::<T>().context("parse header value")?;
Ok(Some(v))
}
}