spond/cli/src/request/get.rs
2026-02-27 03:34:25 +01:00

110 lines
3.6 KiB
Rust

#[macro_export]
macro_rules! get {
// Case 1: no query
(
$name:ident,
( $( $arg:ident : $arg_ty:ty ),* ) => $path:literal -> $out:ty $(,)?
) => {
get!($name(), ( $( $arg : $arg_ty ),* ) => $path -> $out);
};
// Case 2: empty query ()
(
$name:ident (),
( $( $arg:ident : $arg_ty:ty ),* ) => $path:literal -> $out:ty $(,)?
) => {
#[bon::builder]
pub fn $name(
#[builder(finish_fn)] client: &restson::RestClient,
$( #[builder(finish_fn)] $arg: $arg_ty ),*
) -> impl std::future::Future<Output = Result<$out, restson::Error>> + '_ {
#[derive(serde::Deserialize)]
struct RP($out);
impl restson::RestPath<( $( $arg_ty ),* )> for RP {
fn get_path(args: ( $( $arg_ty ),* )) -> Result<String, restson::Error> {
let ( $( $arg ),* ) = args;
Ok(format!($path, $( $arg = $arg ),* ))
}
}
async move {
let result = client.get_with::<_, RP>(( $( $arg ),* ), &[]).await?;
Ok(result.into_inner().0)
}
}
};
// Case 3: query with flags / mixed types
(
$name:ident ( $( $query:tt ),* $(,)? ),
( $( $arg:ident : $arg_ty:ty ),* ) => $path:literal -> $out:ty $(,)?
) => {
#[bon::builder]
pub fn $name(
#[builder(finish_fn)] client: &restson::RestClient,
$( #[builder(finish_fn)] $arg: $arg_ty, )*
$(
get!(@query_field $query)
)*
) -> impl std::future::Future<Output = Result<$out, restson::Error>> + '_ {
#[derive(serde::Serialize)]
#[serde(rename_all = "camelCase")]
struct Query<'a> {
$(
get!(@query_field_struct $query)
)*
}
impl Query<'_> {
fn as_pairs(&self) -> Vec<(&str, String)> {
let mut out = Vec::new();
$(
get!(@push_pair out, self, $query)
)*
out
}
}
let query = Query {
$(
get!(@query_field_init $query)
)*
};
#[derive(serde::Deserialize)]
struct RP($out);
impl restson::RestPath<( $( $arg_ty ),* )> for RP {
fn get_path(args: ( $( $arg_ty ),* )) -> Result<String, restson::Error> {
let ( $( $arg ),* ) = args;
Ok(format!($path, $( $arg = $arg ),* ))
}
}
async move {
let result = client.get_with::<_, RP>(( $( $arg ),* ), &query.as_pairs()).await?;
Ok(result.into_inner().0)
}
}
};
// Query helpers
(@query_field $field:ident) => { $field: Option<bool>, };
(@query_field $field:ident = $ty:ty) => { $field: $ty, };
(@query_field_struct $field:ident) => { $field: Option<bool>, };
(@query_field_struct $field:ident = $ty:ty) => { $field: $ty, };
(@query_field_init $field:ident) => { $field, };
(@query_field_init $field:ident = $ty:ty) => { $field, };
(@push_pair $vec:ident, $self:ident, $field:ident = $ty:ty) => {
$vec.push((stringify!($field), $self.$field.to_string()));
};
(@push_pair $vec:ident, $self:ident, $field:ident) => {
if let Some(v) = &$self.$field {
$vec.push((stringify!($field), v.to_string()));
}
};
}