fixed macro and made take response type attr optionally
This commit is contained in:
53
src/lib.rs
53
src/lib.rs
@ -3,17 +3,37 @@ use quote::quote;
|
||||
use syn::{parse_macro_input, Lit, ItemEnum, DeriveInput, Fields, Data};
|
||||
use quote::format_ident;
|
||||
|
||||
#[proc_macro_derive(HttpRequest, attributes(http_get))]
|
||||
#[proc_macro_derive(HttpRequest, attributes(http_get, http_response))]
|
||||
pub fn derive_http_get_request(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
let query_name = &input.ident;
|
||||
let query_name_str = query_name.to_string();
|
||||
|
||||
// Derive response enum name by replacing "Q" suffix with "R"
|
||||
let response_name_str = if query_name_str.ends_with("Q") {
|
||||
query_name_str.trim_end_matches("Q").to_string() + "R"
|
||||
// Parse optional #[http_response = "..."] attribute via parse_nested_meta
|
||||
let mut response_name_opt: Option<String> = None;
|
||||
for attr in &input.attrs {
|
||||
if attr.path().is_ident("http_response") {
|
||||
attr.parse_nested_meta(|meta| {
|
||||
if meta.path.is_ident("http_response") {
|
||||
let lit: Lit = meta.value()?.parse()?;
|
||||
if let Lit::Str(litstr) = lit {
|
||||
response_name_opt = Some(litstr.value());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}).unwrap_or_else(|e| panic!("Error parsing http_response attribute: {}", e));
|
||||
}
|
||||
}
|
||||
|
||||
// Determine response type name
|
||||
let response_name_str = if let Some(custom_resp) = response_name_opt {
|
||||
custom_resp
|
||||
} else if query_name_str == "Q" {
|
||||
"R".to_string()
|
||||
} else if query_name_str.ends_with('Q') {
|
||||
format!("{}R", &query_name_str[..query_name_str.len() - 1])
|
||||
} else {
|
||||
panic!("HttpRequest derive macro expects the struct name to end with 'Q'");
|
||||
panic!("HttpRequest derive macro expects the type name to be 'Q' or end with 'Q', or specify #[http_response = \"...\"] to override");
|
||||
};
|
||||
let response_name = format_ident!("{}", response_name_str);
|
||||
|
||||
@ -23,19 +43,19 @@ pub fn derive_http_get_request(input: TokenStream) -> TokenStream {
|
||||
if attr.path().is_ident("http_get") {
|
||||
attr.parse_nested_meta(|meta| {
|
||||
if meta.path.is_ident("url") {
|
||||
let value: Lit = meta.value()?.parse()?;
|
||||
if let Lit::Str(litstr) = value {
|
||||
let lit: Lit = meta.value()?.parse()?;
|
||||
if let Lit::Str(litstr) = lit {
|
||||
base_url = Some(litstr.value());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}).unwrap();
|
||||
}).unwrap_or_else(|e| panic!("Error parsing http_get attribute: {}", e));
|
||||
}
|
||||
}
|
||||
let base_url = base_url.expect("Missing #[http_get(url = \"...\")] attribute");
|
||||
let base_url_lit = syn::LitStr::new(&base_url, proc_macro2::Span::call_site());
|
||||
|
||||
// Collect query parameters from fields named "lnk_p_*"
|
||||
// Collect query parameters from fields named "lnk_p_*" (only for structs)
|
||||
let query_param_code = if let Data::Struct(data_struct) = &input.data {
|
||||
if let Fields::Named(fields_named) = &data_struct.fields {
|
||||
fields_named.named.iter().filter_map(|field| {
|
||||
@ -57,7 +77,6 @@ pub fn derive_http_get_request(input: TokenStream) -> TokenStream {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
// Generate the impl
|
||||
let expanded = quote! {
|
||||
#[async_trait::async_trait]
|
||||
impl Queryable for #query_name {
|
||||
@ -75,10 +94,15 @@ pub fn derive_http_get_request(input: TokenStream) -> TokenStream {
|
||||
|
||||
let mut url = #base_url_lit.to_string();
|
||||
if !query_params.is_empty() {
|
||||
let query_string: String = query_params.iter()
|
||||
.map(|(k, v)| format!("{}={}", k, encode(v)))
|
||||
.collect::<Vec<_>>()
|
||||
.join("&");
|
||||
let mut query_string = String::new();
|
||||
let mut first = true;
|
||||
for (k, v) in &query_params {
|
||||
if !first {
|
||||
query_string.push('&');
|
||||
}
|
||||
first = false;
|
||||
query_string.push_str(&format!("{}={}", k, encode(v)));
|
||||
}
|
||||
url.push('?');
|
||||
url.push_str(&query_string);
|
||||
}
|
||||
@ -95,7 +119,6 @@ pub fn derive_http_get_request(input: TokenStream) -> TokenStream {
|
||||
let response = request.send().await?;
|
||||
let bytes = response.body().await?;
|
||||
|
||||
// Deserialize into associated R type
|
||||
let parsed: Self::R = serde_json::from_slice(&bytes)?;
|
||||
Ok(parsed)
|
||||
}
|
||||
|
Reference in New Issue
Block a user