Skip to main content

microcad_lang_proc_macros/
lib.rs

1// Copyright © 2026 The µcad authors <info@microcad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4extern crate proc_macro;
5
6use proc_macro::TokenStream;
7use quote::quote;
8use syn::*;
9
10/// Derives the `SrcReferrer` trait for structs.
11///
12/// This macro supports two types of data structures:
13/// 1. **Named Structs**: Automatically implements `src_ref()` by cloning a field
14///    named `src_ref`.
15/// 2. **Unnamed (Tuple) Structs**: Automatically implements `src_ref()` by
16///    delegating to the first element (`self.0`). The first element must
17///    implement the `SrcReferrer` trait.
18///
19/// # Panics
20/// Will fail to compile if applied to Enums, Unions, or Unit structs.
21#[proc_macro_derive(SrcReferrer)]
22pub fn derive_src_referrer(input: TokenStream) -> TokenStream {
23    let input = parse_macro_input!(input as DeriveInput);
24    let name = &input.ident;
25
26    // Only support structs with named and unnamed fields.
27    match &input.data {
28        Data::Struct(ds) => match &ds.fields {
29            // Generate SrcReferrer for a struct with `src_ref` field:
30            // `struct Foo { bar: Integer, src_ref: SrcRef };`.
31            Fields::Named(_) => {
32                quote! {
33                    impl microcad_lang_base::SrcReferrer for #name {
34                        fn src_ref(&self) -> microcad_lang_base::SrcRef  {
35                            self.src_ref.clone()
36                        }
37                    }
38                }
39            }
40            // Generate SrcReferrer for a tuple `struct Bar(Refer<Identifier>);`.
41            Fields::Unnamed(_) => {
42                quote! {
43                    impl microcad_lang_base::SrcReferrer for #name {
44                        fn src_ref(&self) -> microcad_lang_base::SrcRef  {
45                            self.0.src_ref()
46                        }
47                    }
48                }
49            }
50            // Unit structs are not supported.
51            Fields::Unit => {
52                Error::new_spanned(name, "Unit structs are not supported").to_compile_error()
53            }
54        },
55        _ => Error::new_spanned(name, "Unions and enums are not supported").to_compile_error(),
56    }
57    .into()
58}
59
60/// Derives the `Identifiable` trait for named structs.
61///
62/// This macro implements `id_ref()` by returning a reference to an `id` field.
63/// The `id` field must be of type `crate::Identifier`.
64///
65/// # Constraints
66/// - Only works on **Named Structs**.
67/// - Does **not** support Tuple structs, Unit structs, Enums, or Unions.
68#[proc_macro_derive(Identifiable)]
69pub fn derive_id(input: TokenStream) -> TokenStream {
70    let input = parse_macro_input!(input as DeriveInput);
71    let name = input.ident.clone();
72
73    match &input.data {
74        Data::Struct(ds) => match &ds.fields {
75            // Generate `Identifiable` for a struct with `id` field `struct Foo { bar: Integer, id: Identifier };`.
76            Fields::Named(_) => {
77                quote! {
78                    impl crate::Identifiable for #name {
79                        fn id_ref(&self) -> &crate::Identifier  {
80                            &self.id
81                        }
82                    }
83                }
84            }
85            Fields::Unnamed(_) => {
86                Error::new_spanned(name, "Unnamed structs are not supported").to_compile_error()
87            }
88            // Unit structs not supported.
89            Fields::Unit => {
90                Error::new_spanned(name, "Unit structs are not supported").to_compile_error()
91            }
92        },
93        _ => Error::new_spanned(name, "Unions and enums are not supported").to_compile_error(),
94    }
95    .into()
96}