#![cfg_attr(docsrs, feature(doc_cfg))]
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, ImplItem, ItemImpl};
const KNOWN_HOOKS: &[&str] = &[
"read_before_execution",
"modify_before_serialization",
"read_before_serialization",
"read_after_serialization",
"modify_before_retry_loop",
"read_before_attempt",
"modify_before_signing",
"read_before_signing",
"read_after_signing",
"modify_before_transmit",
"read_before_transmit",
"read_after_transmit",
"modify_before_deserialization",
"read_before_deserialization",
"read_after_deserialization",
"modify_before_attempt_completion",
"read_after_attempt",
"modify_before_completion",
"read_after_execution",
];
const _: () = assert!(
KNOWN_HOOKS.len() <= 32,
"OverriddenHooks uses a u32 bitmask; widen to u64 in interceptors.rs if more hooks are needed"
);
#[proc_macro]
pub fn known_hooks(_input: TokenStream) -> TokenStream {
let names = KNOWN_HOOKS.iter();
quote::quote! { &[#(#names),*] }.into()
}
#[proc_macro_attribute]
pub fn dyn_dispatch_hint(_attr: TokenStream, item: TokenStream) -> TokenStream {
let mut impl_block = parse_macro_input!(item as ItemImpl);
let overridden: Vec<String> = impl_block
.items
.iter()
.filter_map(|item| {
if let ImplItem::Fn(method) = item {
let name = method.sig.ident.to_string();
if KNOWN_HOOKS.contains(&name.as_str()) {
Some(name)
} else {
None
}
} else {
None
}
})
.collect();
let flags: Vec<proc_macro2::TokenStream> = overridden
.iter()
.map(|name| {
let upper = name.to_uppercase();
let ident = syn::Ident::new(&upper, proc_macro2::Span::call_site());
quote! { ::aws_smithy_runtime_api::client::interceptors::OverriddenHooks::#ident }
})
.collect();
let body = if flags.is_empty() {
quote! { ::aws_smithy_runtime_api::client::interceptors::OverriddenHooks::none() }
} else {
quote! { #(#flags)|* }
};
let method: ImplItem = syn::parse_quote! {
fn overridden_hooks(&self) -> ::aws_smithy_runtime_api::client::interceptors::OverriddenHooks {
#body
}
};
impl_block.items.push(method);
quote! { #impl_block }.into()
}