use af_core::prelude::*;
const URL: &str = "https://slack.com/api/";
pub struct Client {
authorization: String,
http: reqwest::Client,
}
impl Client {
pub fn new(token: impl AsRef<str>) -> Self {
Self { authorization: format!("Bearer {}", token.as_ref()), http: default() }
}
pub async fn get<Q, O>(&self, method: &str, query: &Q) -> Result<Result<O, ErrorResponse>>
where
Q: Serialize,
O: for<'a> Deserialize<'a>,
{
let req = self
.http
.get(&format!("{}{}?{}", URL, method, serde_qs::to_string(query).unwrap()))
.header("Authorization", &self.authorization);
let mut res: ResponseProps = req.send().await?.json().await?;
let ok = res.remove("ok").and_then(|v| v.as_bool()).unwrap_or_default();
Ok(match ok {
true => Ok(json::from_value(json::Value::Object(res))?),
false => Err(json::from_value(json::Value::Object(res))?),
})
}
pub async fn post<I, O>(&self, method: &str, body: &I) -> Result<Result<O, ErrorResponse>>
where
I: Serialize,
O: for<'a> Deserialize<'a>,
{
let req = self
.http
.post(&format!("{}{}", URL, method))
.header("Authorization", &self.authorization)
.json(body);
let mut res: ResponseProps = req.send().await?.json().await?;
let ok = res.remove("ok").and_then(|v| v.as_bool()).unwrap_or_default();
Ok(match ok {
true => Ok(json::from_value(json::Value::Object(res))?),
false => Err(json::from_value(json::Value::Object(res))?),
})
}
}
#[derive(Debug, Error)]
pub enum Error {
#[error("invalid response body: {0}")]
InvalidResponse(#[from] json::Error),
#[error(transparent)]
RequestFailed(#[from] reqwest::Error),
}
pub type Result<T = (), E = Error> = std::result::Result<T, E>;
pub type ResponseProps = json::Map<String, json::Value>;
#[derive(Debug, Deserialize, Serialize)]
pub struct ErrorResponse {
pub error: String,
#[serde(flatten)]
pub props: ResponseProps,
}
impl Display for ErrorResponse {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let json = match f.alternate() {
true => json::to_string_pretty(self).unwrap(),
false => json::to_string(self).unwrap(),
};
Display::fmt(&json, f)
}
}