#[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> + '_ { #[derive(serde::Deserialize)] struct RP($out); impl restson::RestPath<( $( $arg_ty ),* )> for RP { fn get_path(args: ( $( $arg_ty ),* )) -> Result { 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> + '_ { #[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 { 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, }; (@query_field $field:ident = $ty:ty) => { $field: $ty, }; (@query_field_struct $field:ident) => { $field: Option, }; (@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())); } }; }