1mod attrs;
2mod codegen;
3mod ir;
4mod type_map;
5
6use proc_macro::TokenStream;
7use syn::{Data, DeriveInput, Fields};
8
9#[proc_macro_derive(DeltaPack, attributes(delta_pack))]
10pub fn derive_delta_pack(input: TokenStream) -> TokenStream {
11 let input = syn::parse_macro_input!(input as DeriveInput);
12 match expand(input) {
13 Ok(ts) => ts.into(),
14 Err(e) => e.to_compile_error().into(),
15 }
16}
17
18fn expand(input: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
19 match &input.data {
20 Data::Struct(s) => expand_struct(&input, s),
21 Data::Enum(e) => expand_enum(&input, e),
22 Data::Union(_) => Err(syn::Error::new_spanned(
23 &input.ident,
24 "raw `union` types are not supported",
25 )),
26 }
27}
28
29fn expand_struct(
30 input: &DeriveInput,
31 data: &syn::DataStruct,
32) -> syn::Result<proc_macro2::TokenStream> {
33 let name = &input.ident;
34 let fields = match &data.fields {
35 Fields::Named(named) => &named.named,
36 Fields::Unit => {
37 return Err(syn::Error::new_spanned(
38 name,
39 "unit structs are not supported",
40 ));
41 }
42 Fields::Unnamed(_) => {
43 return Err(syn::Error::new_spanned(
44 name,
45 "tuple structs are not supported; use a struct with named fields",
46 ));
47 }
48 };
49
50 let ctx = type_map::TypeCtx { self_name: name };
51 let mut out_fields = Vec::new();
52 for field in fields {
53 let ident = field.ident.clone().expect("named field");
54 let field_attrs = attrs::parse_field_attrs(&field.attrs)?;
55 let ty = type_map::map_field(&field.ty, &field_attrs, &ctx)?;
56 out_fields.push(codegen::Field { ident, ty });
57 }
58
59 Ok(codegen::emit_struct(name, &out_fields))
60}
61
62fn expand_enum(input: &DeriveInput, data: &syn::DataEnum) -> syn::Result<proc_macro2::TokenStream> {
63 let name = &input.ident;
64
65 if data.variants.is_empty() {
66 return Err(syn::Error::new_spanned(
67 name,
68 "empty enums are not supported",
69 ));
70 }
71
72 let all_unit = data
73 .variants
74 .iter()
75 .all(|v| matches!(v.fields, Fields::Unit));
76 let all_tuple = data.variants.iter().all(|v| match &v.fields {
77 Fields::Unnamed(unnamed) => unnamed.unnamed.len() == 1,
78 _ => false,
79 });
80
81 if all_unit {
82 let variants: Vec<syn::Ident> = data.variants.iter().map(|v| v.ident.clone()).collect();
83 Ok(codegen::emit_c_enum(name, &variants))
84 } else if all_tuple {
85 let mut variants = Vec::with_capacity(data.variants.len());
86 for v in &data.variants {
87 let unnamed = match &v.fields {
88 Fields::Unnamed(u) => u,
89 _ => unreachable!(),
90 };
91 let inner = &unnamed.unnamed[0].ty;
92 let path = match inner {
93 syn::Type::Path(p) if p.qself.is_none() => p.path.clone(),
94 _ => {
95 return Err(syn::Error::new_spanned(
96 inner,
97 "union variant must wrap a single named type (e.g. Variant(SomeStruct))",
98 ));
99 }
100 };
101 variants.push(codegen::Variant {
102 ident: v.ident.clone(),
103 inner_path: path,
104 });
105 }
106 Ok(codegen::emit_union(name, &variants))
107 } else {
108 Err(syn::Error::new_spanned(
109 name,
110 "all variants must be either unit (C-style enum) or single-tuple `Variant(Inner)` (union). Mixed or struct-variants are not supported.",
111 ))
112 }
113}