Skip to main content

cdk_ansible_macro/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{Attribute, ImplItem, ItemImpl, ItemStruct, LitStr, parse_macro_input};
4
5mod inventory_derive;
6
7#[proc_macro_derive(AllInventoryVarsGen)]
8pub fn all_inventory_vars_gen_derive(input: TokenStream) -> TokenStream {
9    inventory_derive::vars_gen_derive(input)
10}
11
12// MIT License
13// Copyright (c) 2021-2023 Astral Sh
14// https://github.com/astral-sh/uv/blob/cfd1e670ddb803f4e67d4abd069fad271e1d1c7f/crates/uv-macros/src/lib.rs
15fn get_doc_comment(attrs: &[Attribute]) -> String {
16    attrs
17        .iter()
18        .filter_map(|attr| {
19            if attr.path().is_ident("doc") {
20                if let syn::Meta::NameValue(meta) = &attr.meta {
21                    if let syn::Expr::Lit(expr) = &meta.value {
22                        if let syn::Lit::Str(str) = &expr.lit {
23                            return Some(str.value().trim().to_string());
24                        }
25                    }
26                }
27            }
28            None
29        })
30        .collect::<Vec<_>>()
31        .join("\n")
32}
33
34// MIT License
35// Copyright (c) 2021-2023 Astral Sh
36// https://github.com/astral-sh/uv/blob/cfd1e670ddb803f4e67d4abd069fad271e1d1c7f/crates/uv-macros/src/lib.rs
37fn get_env_var_pattern_from_attr(attrs: &[Attribute]) -> Option<String> {
38    attrs
39        .iter()
40        .find(|attr| attr.path().is_ident("attr_env_var_pattern"))
41        .and_then(|attr| attr.parse_args::<LitStr>().ok())
42        .map(|lit_str| lit_str.value())
43}
44
45// MIT License
46// Copyright (c) 2021-2023 Astral Sh
47// https://github.com/astral-sh/uv/blob/cfd1e670ddb803f4e67d4abd069fad271e1d1c7f/crates/uv-macros/src/lib.rs
48fn is_hidden(attrs: &[Attribute]) -> bool {
49    attrs.iter().any(|attr| attr.path().is_ident("attr_hidden"))
50}
51
52// MIT License
53// Copyright (c) 2021-2023 Astral Sh
54// https://github.com/astral-sh/uv/blob/cfd1e670ddb803f4e67d4abd069fad271e1d1c7f/crates/uv-macros/src/lib.rs
55#[proc_macro_attribute]
56pub fn attr_hidden(_attr: TokenStream, item: TokenStream) -> TokenStream {
57    item
58}
59
60// MIT License
61// Copyright (c) 2021-2023 Astral Sh
62// https://github.com/astral-sh/uv/blob/cfd1e670ddb803f4e67d4abd069fad271e1d1c7f/crates/uv-macros/src/lib.rs
63//
64/// This attribute is used to generate environment variables metadata for [`cdk_ansible_static::EnvVars`].
65#[proc_macro_attribute]
66pub fn attribute_env_vars_metadata(_attr: TokenStream, input: TokenStream) -> TokenStream {
67    let ast = parse_macro_input!(input as ItemImpl);
68
69    let constants: Vec<_> = ast
70        .items
71        .iter()
72        .filter_map(|item| match item {
73            ImplItem::Const(item) if !is_hidden(&item.attrs) => {
74                let name = item.ident.to_string();
75                let doc = get_doc_comment(&item.attrs);
76                Some((name, doc))
77            }
78            ImplItem::Fn(item) if !is_hidden(&item.attrs) => {
79                // Extract the environment variable patterns.
80                match get_env_var_pattern_from_attr(&item.attrs) {
81                    Some(pattern) => {
82                        let doc = get_doc_comment(&item.attrs);
83                        Some((pattern, doc))
84                    }
85                    _ => {
86                        None // Skip if pattern extraction fails.
87                    }
88                }
89            }
90            _ => None,
91        })
92        .collect();
93
94    let struct_name = &ast.self_ty;
95    let pairs = constants.iter().map(|(name, doc)| {
96        quote! {
97            (#name, #doc)
98        }
99    });
100
101    let expanded = quote! {
102        #ast
103
104        impl #struct_name {
105            /// Returns a list of pairs of env var and their documentation defined in this impl block.
106            pub fn metadata<'a>() -> &'a [(&'static str, &'static str)] {
107                &[#(#pairs),*]
108            }
109        }
110    };
111
112    expanded.into()
113}
114
115// https://stackoverflow.com/questions/54177438/how-to-programmatically-get-the-number-of-fields-of-a-struct
116#[proc_macro_derive(FieldCount)]
117pub fn derive_field_count(input: TokenStream) -> TokenStream {
118    let input = parse_macro_input!(input as ItemStruct);
119    let field_count = input.fields.iter().count();
120    let name = &input.ident;
121    let output = quote! {
122      impl #name {
123        pub fn field_count() -> usize {
124          #field_count
125        }
126      }
127    };
128    TokenStream::from(output)
129}