Skip to main content

oxidns_macros/
lib.rs

1// SPDX-FileCopyrightText: 2026 Sven Shi
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4use proc_macro::TokenStream;
5use quote::quote;
6use syn::{Fields, ItemStruct, LitStr, parse_macro_input};
7
8/// Register an empty plugin factory struct with the OxiDNS inventory.
9///
10/// Supported forms:
11///
12/// ```ignore
13/// #[plugin_factory("cache")]
14/// pub struct CacheFactory;
15///
16/// #[plugin_factory("sequence")]
17/// pub struct SequenceFactory {}
18/// ```
19#[proc_macro_attribute]
20pub fn plugin_factory(attr: TokenStream, item: TokenStream) -> TokenStream {
21    let plugin_type = parse_macro_input!(attr as LitStr);
22    let item_struct = parse_macro_input!(item as ItemStruct);
23    let ident = &item_struct.ident;
24
25    let factory_ctor = match &item_struct.fields {
26        Fields::Unit => quote! { #ident },
27        Fields::Named(fields) if fields.named.is_empty() => quote! { #ident {} },
28        _ => {
29            return syn::Error::new_spanned(
30                &item_struct,
31                "#[plugin_factory] only supports unit structs or empty braced structs; use register_plugin_factory! for factories with state or custom constructors",
32            )
33            .to_compile_error()
34            .into();
35        }
36    };
37
38    quote! {
39        #item_struct
40
41        inventory::submit! {
42            crate::plugin::FactoryRegistration {
43                plugin_type: #plugin_type,
44                module_path: module_path!(),
45                constructor: || -> Box<dyn crate::plugin::PluginFactory> {
46                    Box::new(#factory_ctor)
47                },
48            }
49        }
50    }
51    .into()
52}