use std::marker::PhantomData;
use polyoxide_core::{HttpClient, QueryBuilder, Request, RequestError};
use serde::de::DeserializeOwned;
pub(crate) trait Priced: Copy {
type Cost: Copy + Into<polyoxide_core::Cost>;
fn path(self) -> &'static str;
fn cost(self) -> Self::Cost;
}
pub(crate) struct Routed<T, R, E> {
http: HttpClient,
route: R,
query: Vec<(&'static str, String)>,
_marker: PhantomData<fn() -> (T, E)>,
}
impl<T, R, E> Routed<T, R, E> {
pub(crate) fn new(http: &HttpClient, route: R) -> Self {
Self {
http: http.clone(),
route,
query: Vec::new(),
_marker: PhantomData,
}
}
pub(crate) fn query(mut self, key: &'static str, value: impl ToString) -> Self {
let value = value.to_string();
match self.query.iter_mut().find(|(k, _)| *k == key) {
Some(slot) => slot.1 = value,
None => self.query.push((key, value)),
}
self
}
pub(crate) fn route(mut self, route: R) -> Self {
self.route = route;
self
}
}
impl<T, R: Priced, E> Routed<T, R, E> {
pub(crate) fn cost(&self) -> R::Cost {
self.route.cost()
}
}
impl<T: DeserializeOwned, R: Priced, E: RequestError> Routed<T, R, E> {
pub(crate) async fn send(self) -> Result<T, E> {
let mut request =
Request::<T, E>::new(self.http, self.route.path()).with_cost(self.route.cost().into());
for (key, value) in self.query {
request = request.query(key, value);
}
request.send().await
}
}
macro_rules! route_builder {
(
$(#[$doc:meta])* $name:ident => $output:ty;
module = $module:ident, error = $error:ident
) => {
$(#[$doc])*
#[must_use = "a request does nothing until it is sent"]
pub struct $name {
pub(crate) request: $crate::$module::request::Routed<$output>,
}
impl $name {
pub fn cost(&self) -> $crate::$module::weight::Cost {
self.request.cost()
}
pub async fn send(self) -> Result<$output, $crate::$module::$error> {
self.request.send().await
}
}
};
}
pub(crate) use route_builder;