aws_smithy_runtime_api_macros/
lib.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6/* Automatically managed default lints */
7#![cfg_attr(docsrs, feature(doc_cfg))]
8/* End of automatically managed default lints */
9
10//! Proc macros for `aws-smithy-runtime-api`.
11
12use proc_macro::TokenStream;
13use quote::quote;
14use syn::{parse_macro_input, ImplItem, ItemImpl};
15
16// If you update this list, also update:
17//   - `OverriddenHooks` constants in `aws-smithy-runtime-api/src/client/interceptors.rs`
18//   - Hook methods on the `Intercept` trait in the same file
19const KNOWN_HOOKS: &[&str] = &[
20    "read_before_execution",
21    "modify_before_serialization",
22    "read_before_serialization",
23    "read_after_serialization",
24    "modify_before_retry_loop",
25    "read_before_attempt",
26    "modify_before_signing",
27    "read_before_signing",
28    "read_after_signing",
29    "modify_before_transmit",
30    "read_before_transmit",
31    "read_after_transmit",
32    "modify_before_deserialization",
33    "read_before_deserialization",
34    "read_after_deserialization",
35    "modify_before_attempt_completion",
36    "read_after_attempt",
37    "modify_before_completion",
38    "read_after_execution",
39];
40
41const _: () = assert!(
42    KNOWN_HOOKS.len() <= 32,
43    "OverriddenHooks uses a u32 bitmask; widen to u64 in interceptors.rs if more hooks are needed"
44);
45
46/// Automatically generates an `overridden_hooks()` method on an `impl Intercept` block
47/// based on which hook methods are overridden.
48///
49/// This attribute must be placed on an `impl Intercept for T` block. It inspects
50/// which hook methods are overridden and generates a corresponding
51/// `overridden_hooks()` method that returns the correct `OverriddenHooks` bitmask.
52///
53/// # Example
54/// ```ignore
55/// #[dyn_dispatch_hint]
56/// impl Intercept for MyInterceptor {
57///     fn name(&self) -> &'static str { "MyInterceptor" }
58///     fn modify_before_signing(...) -> ... { ... }
59/// }
60/// // Generates: fn overridden_hooks(&self) -> OverriddenHooks { OverriddenHooks::MODIFY_BEFORE_SIGNING }
61/// ```
62#[proc_macro_attribute]
63pub fn dyn_dispatch_hint(_attr: TokenStream, item: TokenStream) -> TokenStream {
64    let mut impl_block = parse_macro_input!(item as ItemImpl);
65
66    let overridden: Vec<String> = impl_block
67        .items
68        .iter()
69        .filter_map(|item| {
70            if let ImplItem::Fn(method) = item {
71                let name = method.sig.ident.to_string();
72                if KNOWN_HOOKS.contains(&name.as_str()) {
73                    Some(name)
74                } else {
75                    None
76                }
77            } else {
78                None
79            }
80        })
81        .collect();
82
83    let flags: Vec<proc_macro2::TokenStream> = overridden
84        .iter()
85        .map(|name| {
86            let upper = name.to_uppercase();
87            let ident = syn::Ident::new(&upper, proc_macro2::Span::call_site());
88            quote! { ::aws_smithy_runtime_api::client::interceptors::OverriddenHooks::#ident }
89        })
90        .collect();
91
92    let body = if flags.is_empty() {
93        quote! { ::aws_smithy_runtime_api::client::interceptors::OverriddenHooks::none() }
94    } else {
95        quote! { #(#flags)|* }
96    };
97
98    let method: ImplItem = syn::parse_quote! {
99        fn overridden_hooks(&self) -> ::aws_smithy_runtime_api::client::interceptors::OverriddenHooks {
100            #body
101        }
102    };
103    impl_block.items.push(method);
104
105    quote! { #impl_block }.into()
106}