serde_nixos_macros/lib.rs
1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{parse_macro_input, DeriveInput};
4
5mod attributes;
6mod nixos_type;
7mod type_mapping;
8
9/// Derive macro for generating NixOS type definitions from Rust structures.
10///
11/// This macro automatically generates methods to output NixOS module definitions
12/// that correspond to your Rust structure, making it easy to keep your Rust
13/// configuration and NixOS configuration in sync.
14///
15/// # Example
16///
17/// ```ignore
18/// use serde::{Serialize, Deserialize};
19/// use serde_nixos::NixosType;
20///
21/// #[derive(Serialize, Deserialize, NixosType)]
22/// struct Config {
23/// #[nixos(description = "The server port")]
24/// port: u16,
25///
26/// #[nixos(default = "localhost")]
27/// host: String,
28///
29/// #[nixos(optional)]
30/// max_connections: Option<u32>,
31/// }
32///
33/// // Generate NixOS type definition
34/// let nixos_module = Config::nixos_type_definition();
35/// ```
36#[proc_macro_derive(NixosType, attributes(nixos))]
37pub fn derive_nixos_type(input: TokenStream) -> TokenStream {
38 let input = parse_macro_input!(input as DeriveInput);
39
40 match nixos_type::expand_nixos_type(&input) {
41 Ok(expanded) => expanded.into(),
42 Err(err) => err.to_compile_error().into(),
43 }
44}
45
46/// Generate the full NixOS type definition (with `let` bindings for
47/// nested types) from a Rust type at compile time.
48///
49/// The type must derive [`NixosType`]. The macro expands to a call to
50/// `T::nixos_type_full_definition()`, which produces a `let ... in typeNameType`
51/// Nix expression containing all direct-child custom types as `let` bindings.
52///
53/// For composing **multiple** types into a single `.nix` file with proper
54/// cross-references, use the runtime [`NixosModuleGenerator`] API instead.
55///
56/// # Example
57///
58/// ```ignore
59/// use serde::{Serialize, Deserialize};
60/// use serde_nixos::{NixosType, nixos_module};
61///
62/// #[derive(Serialize, Deserialize, NixosType)]
63/// struct Inner { value: String }
64///
65/// #[derive(Serialize, Deserialize, NixosType)]
66/// struct Config {
67/// name: String,
68/// inner: Inner,
69/// }
70///
71/// let nix: String = nixos_module!(Config);
72/// assert!(nix.contains("innerType = types.submodule"));
73/// assert!(nix.contains("configType = types.submodule"));
74/// ```
75#[proc_macro]
76pub fn nixos_module(input: TokenStream) -> TokenStream {
77 let type_path = parse_macro_input!(input as syn::Type);
78
79 quote! {
80 <#type_path>::nixos_type_full_definition()
81 }
82 .into()
83}