use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
use syn::{parse_macro_input, DeriveInput, LitStr, Meta};
#[proc_macro_derive(ServerResponse, attributes(sr))]
pub fn server_response(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let struct_name = &ast.ident;
let response_struct_name = format_ident!("{}Response", struct_name);
let mut flatten: TokenStream = quote! {};
let mut data_name: Ident = format_ident!("data");
ast.attrs.iter().for_each(|a| {
if a.path().is_ident("sr") {
match &a.meta {
Meta::List(meta) => {
meta.parse_nested_meta(|nest| {
if nest.path.is_ident("flatten") {
flatten = quote! {
#[serde(flatten)]
}
} else if nest.path.is_ident("name") {
let value = nest.value()?;
let s: LitStr = value.parse()?;
data_name = format_ident!("{}", s.value());
}
Ok(())
}).unwrap()
},
_ => {}
}
}
});
let new_struct = quote! {
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct #response_struct_name {
#flatten
pub #data_name: #struct_name,
pub errcode: i32,
pub errmsg: Option<String>,
}
};
let method = quote! {
impl #response_struct_name {
pub fn is_ok(&self) -> bool {
self.errcode == 0
}
pub fn #data_name(self) -> Result<#struct_name, rust_wechat_core::ServerError> {
if self.is_ok() {
Ok(self.#data_name)
} else {
let err = rust_wechat_core::ServerError::default().code(self.errcode);
if let Some(msg) = self.errmsg {
Err(err.message(msg))
} else {
Err(err)
}
}
}
pub fn ignore(self) -> Result<(), rust_wechat_core::ServerError> {
if self.is_ok() {
Ok(())
} else {
let err = rust_wechat_core::ServerError::default().code(self.errcode);
if let Some(msg) = self.errmsg {
Err(err.message(msg))
} else {
Err(err)
}
}
}
}
};
let expanded = quote! {
#new_struct
#method
};
proc_macro::TokenStream::from(expanded)
}