extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use syn::{
parse::Parse, parse::ParseStream, parse_macro_input, FnArg, Ident, ItemFn, LitStr, Pat,
PatIdent, Token,
};
struct MacroArgs {
resource: LitStr,
config_param: Option<Ident>,
}
impl Parse for MacroArgs {
fn parse(input: ParseStream) -> syn::Result<Self> {
let resource = input.parse::<LitStr>()?;
let config_param = if input.peek(Token![,]) {
input.parse::<Token![,]>()?;
Some(input.parse::<Ident>()?)
} else {
None
};
Ok(MacroArgs {
resource,
config_param,
})
}
}
#[proc_macro_attribute]
pub fn request_authorization(attr: TokenStream, item: TokenStream) -> TokenStream {
let args = parse_macro_input!(attr as MacroArgs);
let input = parse_macro_input!(item as ItemFn);
let fn_name = &input.sig.ident;
let fn_args = &input.sig.inputs;
let fn_generics = &input.sig.generics;
let fn_output = &input.sig.output;
let fn_body = &input.block;
let fn_vis = &input.vis;
let is_async = input.sig.asyncness.is_some();
let resource = &args.resource;
let has_config_param = args.config_param.is_some();
let config_param = args.config_param;
let has_base_url_param = fn_args.iter().any(|arg| {
if let FnArg::Typed(pat_type) = arg {
if let Pat::Ident(PatIdent { ident, .. }) = &*pat_type.pat {
return ident == "base_url";
}
}
false
});
let _args: Vec<_> = fn_args
.iter()
.filter_map(|arg| {
if let FnArg::Typed(pat_type) = arg {
if let Pat::Ident(PatIdent { ident, .. }) = &*pat_type.pat {
return Some(ident);
}
}
None
})
.collect();
let expanded = if is_async {
if has_config_param {
quote! {
#fn_vis #fn_generics async fn #fn_name(#fn_args) #fn_output {
let config_clone = #config_param.clone();
let client = config_clone.create_client()
.expect("Failed to create Hessra client from configuration");
let resource = #resource.to_string();
let token = client.request_token(resource)
.await
.expect("Failed to request authorization token");
#fn_body
}
}
} else if has_base_url_param {
quote! {
#fn_vis #fn_generics async fn #fn_name(#fn_args) #fn_output {
let config = hessra_sdk::HessraConfig::new(
base_url.clone(),
None, hessra_sdk::Protocol::Http1,
mtls_cert.clone(),
mtls_key.clone(),
server_ca.clone()
);
let client = config.create_client()
.expect("Failed to create Hessra client from parameters");
let resource = #resource.to_string();
let token = client.request_token(resource)
.await
.expect("Failed to request authorization token");
#fn_body
}
}
} else {
quote! {
#fn_vis #fn_generics async fn #fn_name(#fn_args) #fn_output {
let config = hessra_sdk::get_default_config()
.cloned()
.or_else(|| hessra_sdk::try_load_default_config())
.expect("No Hessra configuration found. Set a default configuration or provide parameters.");
let client = config.create_client()
.expect("Failed to create Hessra client from global configuration");
let resource = #resource.to_string();
let token = client.request_token(resource)
.await
.expect("Failed to request authorization token");
#fn_body
}
}
}
} else {
if has_config_param {
quote! {
#fn_vis #fn_generics fn #fn_name(#fn_args) #fn_output {
let rt = tokio::runtime::Runtime::new().expect("Failed to create runtime");
let config_clone = #config_param.clone();
let client = config_clone.create_client()
.expect("Failed to create Hessra client from configuration");
let resource = #resource.to_string();
let token = rt.block_on(client.request_token(resource))
.expect("Failed to request authorization token");
#fn_body
}
}
} else if has_base_url_param {
quote! {
#fn_vis #fn_generics fn #fn_name(#fn_args) #fn_output {
let rt = tokio::runtime::Runtime::new().expect("Failed to create runtime");
let config = hessra_sdk::HessraConfig::new(
base_url.clone(),
None, hessra_sdk::Protocol::Http1,
mtls_cert.clone(),
mtls_key.clone(),
server_ca.clone()
);
let client = config.create_client()
.expect("Failed to create Hessra client from parameters");
let resource = #resource.to_string();
let token = rt.block_on(client.request_token(resource))
.expect("Failed to request authorization token");
#fn_body
}
}
} else {
quote! {
#fn_vis #fn_generics fn #fn_name(#fn_args) #fn_output {
let rt = tokio::runtime::Runtime::new().expect("Failed to create runtime");
let config = hessra_sdk::get_default_config()
.cloned()
.or_else(|| hessra_sdk::try_load_default_config())
.expect("No Hessra configuration found. Set a default configuration or provide parameters.");
let client = config.create_client()
.expect("Failed to create Hessra client from global configuration");
let resource = #resource.to_string();
let token = rt.block_on(client.request_token(resource))
.expect("Failed to request authorization token");
#fn_body
}
}
}
};
TokenStream::from(expanded)
}
#[proc_macro_attribute]
pub fn authorize(attr: TokenStream, item: TokenStream) -> TokenStream {
let args = parse_macro_input!(attr as MacroArgs);
let input = parse_macro_input!(item as ItemFn);
let fn_name = &input.sig.ident;
let fn_args = &input.sig.inputs;
let fn_generics = &input.sig.generics;
let fn_output = &input.sig.output;
let fn_body = &input.block;
let fn_vis = &input.vis;
let is_async = input.sig.asyncness.is_some();
let resource = &args.resource;
let has_config_param = args.config_param.is_some();
let config_param = args.config_param;
let has_base_url_param = fn_args.iter().any(|arg| {
if let FnArg::Typed(pat_type) = arg {
if let Pat::Ident(PatIdent { ident, .. }) = &*pat_type.pat {
return ident == "base_url";
}
}
false
});
let token_param = fn_args.iter().find_map(|arg| {
if let FnArg::Typed(pat_type) = arg {
if let Pat::Ident(PatIdent { ident, .. }) = &*pat_type.pat {
if ident == "token" {
return Some(ident);
}
}
}
None
});
let token_ident = match token_param {
Some(ident) => ident,
None => {
return syn::Error::new_spanned(
&input.sig,
"The function must have a 'token' parameter to use the authorize macro",
)
.to_compile_error()
.into();
}
};
let expanded = if is_async {
if has_config_param {
quote! {
#fn_vis #fn_generics async fn #fn_name(#fn_args) #fn_output {
let config_clone = #config_param.clone();
let client = config_clone.create_client()
.expect("Failed to create Hessra client from configuration");
let resource = #resource.to_string();
let verification_result = client.verify_token(#token_ident.clone(), resource).await;
match verification_result {
Ok(_) => {
#fn_body
},
Err(e) => {
panic!("Authorization failed: {}", e);
}
}
}
}
} else if has_base_url_param {
quote! {
#fn_vis #fn_generics async fn #fn_name(#fn_args) #fn_output {
let config = hessra_sdk::HessraConfig::new(
base_url.clone(),
None, hessra_sdk::Protocol::Http1,
mtls_cert.clone(),
mtls_key.clone(),
server_ca.clone()
);
let client = config.create_client()
.expect("Failed to create Hessra client from parameters");
let resource = #resource.to_string();
let verification_result = client.verify_token(#token_ident.clone(), resource).await;
match verification_result {
Ok(_) => {
#fn_body
},
Err(e) => {
panic!("Authorization failed: {}", e);
}
}
}
}
} else {
quote! {
#fn_vis #fn_generics async fn #fn_name(#fn_args) #fn_output {
let config = hessra_sdk::get_default_config()
.cloned()
.or_else(|| hessra_sdk::try_load_default_config())
.expect("No Hessra configuration found. Set a default configuration or provide parameters.");
let client = config.create_client()
.expect("Failed to create Hessra client from global configuration");
let resource = #resource.to_string();
let verification_result = client.verify_token(#token_ident.clone(), resource).await;
match verification_result {
Ok(_) => {
#fn_body
},
Err(e) => {
panic!("Authorization failed: {}", e);
}
}
}
}
}
} else {
if has_config_param {
quote! {
#fn_vis #fn_generics fn #fn_name(#fn_args) #fn_output {
let rt = tokio::runtime::Runtime::new().expect("Failed to create runtime");
let config_clone = #config_param.clone();
let client = config_clone.create_client()
.expect("Failed to create Hessra client from configuration");
let resource = #resource.to_string();
let verification_result = rt.block_on(client.verify_token(#token_ident.clone(), resource));
match verification_result {
Ok(_) => {
#fn_body
},
Err(e) => {
panic!("Authorization failed: {}", e);
}
}
}
}
} else if has_base_url_param {
quote! {
#fn_vis #fn_generics fn #fn_name(#fn_args) #fn_output {
let rt = tokio::runtime::Runtime::new().expect("Failed to create runtime");
let config = hessra_sdk::HessraConfig::new(
base_url.clone(),
None, hessra_sdk::Protocol::Http1,
mtls_cert.clone(),
mtls_key.clone(),
server_ca.clone()
);
let client = config.create_client()
.expect("Failed to create Hessra client from parameters");
let resource = #resource.to_string();
let verification_result = rt.block_on(client.verify_token(#token_ident.clone(), resource));
match verification_result {
Ok(_) => {
#fn_body
},
Err(e) => {
panic!("Authorization failed: {}", e);
}
}
}
}
} else {
quote! {
#fn_vis #fn_generics fn #fn_name(#fn_args) #fn_output {
let rt = tokio::runtime::Runtime::new().expect("Failed to create runtime");
let config = hessra_sdk::get_default_config()
.cloned()
.or_else(|| hessra_sdk::try_load_default_config())
.expect("No Hessra configuration found. Set a default configuration or provide parameters.");
let client = config.create_client()
.expect("Failed to create Hessra client from global configuration");
let resource = #resource.to_string();
let verification_result = rt.block_on(client.verify_token(#token_ident.clone(), resource));
match verification_result {
Ok(_) => {
#fn_body
},
Err(e) => {
panic!("Authorization failed: {}", e);
}
}
}
}
}
};
TokenStream::from(expanded)
}