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}