architecturekit_macros/
lib.rs1#![deny(
6 ambiguous_negative_literals,
7 clippy::pedantic,
8 missing_debug_implementations,
9 missing_docs,
10 trivial_casts,
11 trivial_numeric_casts,
12 unused_extern_crates,
13 unused_import_braces,
14 unused_qualifications,
15 unused_results,
16 unsafe_code,
17 warnings
18)]
19
20mod attributes;
21mod events;
22mod rename;
23
24use proc_macro::TokenStream;
25use proc_macro2::TokenStream as TokenStream2;
26use quote::quote;
27use syn::{
28 Data, DeriveInput, Error, Fields, GenericParam, LitStr, Result, ext::IdentExt,
29 parse_macro_input, parse_quote, spanned::Spanned,
30};
31
32use crate::attributes::{ContainerAttributes, FieldAttributes};
33
34#[proc_macro_derive(Event, attributes(event))]
37pub fn derive_event(input: TokenStream) -> TokenStream {
38 let input = parse_macro_input!(input as DeriveInput);
39 expand_event(&input)
40 .unwrap_or_else(Error::into_compile_error)
41 .into()
42}
43
44#[proc_macro_derive(Events)]
47pub fn derive_events(input: TokenStream) -> TokenStream {
48 let input = parse_macro_input!(input as DeriveInput);
49 events::expand_events(&input)
50 .unwrap_or_else(Error::into_compile_error)
51 .into()
52}
53
54#[proc_macro_derive(Schema)]
57pub fn derive_schema(input: TokenStream) -> TokenStream {
58 let input = parse_macro_input!(input as DeriveInput);
59 expand_schema(&input)
60 .unwrap_or_else(Error::into_compile_error)
61 .into()
62}
63
64fn expand_event(input: &DeriveInput) -> Result<TokenStream2> {
65 if let Some(param) = input.generics.params.first() {
68 return Err(Error::new(
69 param.span(),
70 "an event can not be generic, since its event type names exactly one shape of data",
71 ));
72 }
73
74 let event_type = event_type_of(input)?;
75 let schema = expand_schema(input)?;
76 let name = &input.ident;
77
78 Ok(quote! {
79 #schema
80
81 impl ::architecturekit::Event for #name {
82 const EVENT_TYPE: &'static str = #event_type;
83 }
84 })
85}
86
87fn event_type_of(input: &DeriveInput) -> Result<LitStr> {
88 let mut event_type: Option<LitStr> = None;
89
90 for attribute in input.attrs.iter().filter(|a| a.path().is_ident("event")) {
91 attribute.parse_nested_meta(|meta| {
92 if !meta.path.is_ident("type") {
93 return Err(meta.error("expected `type = \"...\"`"));
94 }
95 let value: LitStr = meta.value()?.parse()?;
96 if value.value().is_empty() {
97 return Err(Error::new(value.span(), "the event type must not be empty"));
98 }
99 if event_type.replace(value).is_some() {
100 return Err(meta.error("the event type is given more than once"));
101 }
102 Ok(())
103 })?;
104 }
105
106 event_type.ok_or_else(|| {
107 Error::new(
108 input.ident.span(),
109 "an event needs its event type, as in #[event(type = \"io.eventsourcingdb.library.book-acquired\")]",
110 )
111 })
112}
113
114fn expand_schema(input: &DeriveInput) -> Result<TokenStream2> {
115 let container = ContainerAttributes::of(&input.attrs)?;
116 let body = match &input.data {
117 Data::Struct(data) => schema_of_struct(&container, &data.fields, input)?,
118 Data::Enum(data) => {
119 return Err(Error::new(
120 data.enum_token.span,
121 "the schema of an enum can not be derived, since serde writes enums in several shapes; \
122 implement architecturekit::Schema by hand",
123 ));
124 }
125 Data::Union(data) => {
126 return Err(Error::new(
127 data.union_token.span,
128 "the schema of a union can not be derived; implement architecturekit::Schema by hand",
129 ));
130 }
131 };
132
133 let mut generics = input.generics.clone();
134 for param in &mut generics.params {
135 if let GenericParam::Type(param) = param {
136 param.bounds.push(parse_quote!(::architecturekit::Schema));
137 }
138 }
139 let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
140 let name = &input.ident;
141
142 Ok(quote! {
143 impl #impl_generics ::architecturekit::Schema for #name #type_generics #where_clause {
144 fn schema() -> ::architecturekit::__private::Value {
145 ::architecturekit::__private::derive(::std::any::type_name::<Self>(), || #body)
146 }
147 }
148 })
149}
150
151fn schema_of_struct(
152 container: &ContainerAttributes,
153 fields: &Fields,
154 input: &DeriveInput,
155) -> Result<TokenStream2> {
156 container.refuse_what_can_not_be_derived()?;
157
158 match fields {
159 Fields::Named(named) => {
160 let mut written = Vec::new();
161 for field in &named.named {
162 let attributes = FieldAttributes::of(&field.attrs)?;
163 if attributes.is_skipped {
164 continue;
165 }
166 attributes.refuse_what_can_not_be_derived()?;
167 written.push((field, attributes));
168 }
169
170 if container.is_transparent {
171 return match written.as_slice() {
172 [(field, _)] => {
173 let field_type = &field.ty;
174 Ok(quote! { <#field_type as ::architecturekit::Schema>::schema() })
175 }
176 _ => Err(Error::new(
177 input.ident.span(),
178 "a transparent struct needs exactly one field that is not skipped",
179 )),
180 };
181 }
182
183 let entries = written.iter().map(|(field, attributes)| {
184 let name = attributes.rename.clone().unwrap_or_else(|| {
185 let ident = field.ident.as_ref().map(IdentExt::unraw).map(|i| i.to_string());
186 container.rename_all.apply_to_field(&ident.unwrap_or_default())
187 });
188 let field_type = &field.ty;
189 if attributes.is_optional {
190 quote! { (#name, <#field_type as ::architecturekit::Schema>::schema_without_null(), true) }
191 } else {
192 quote! { (#name, <#field_type as ::architecturekit::Schema>::schema(), false) }
193 }
194 });
195
196 Ok(quote! {
197 ::architecturekit::__private::object(::std::any::type_name::<Self>(), ::std::vec![#(#entries),*])
198 })
199 }
200 Fields::Unnamed(unnamed) => match unnamed.unnamed.first() {
201 Some(field) if unnamed.unnamed.len() == 1 => {
202 let field_type = &field.ty;
204 Ok(quote! { <#field_type as ::architecturekit::Schema>::schema() })
205 }
206 _ => Err(Error::new(
207 unnamed.span(),
208 "the schema of a tuple struct with several fields can not be derived, since serde writes it \
209 as an array of mixed values; use named fields, or implement architecturekit::Schema by hand",
210 )),
211 },
212 Fields::Unit => Err(Error::new(
213 input.ident.span(),
214 "the schema of a unit struct can not be derived, since serde writes it as null; \
215 to describe data without fields, use `struct Name {}`",
216 )),
217 }
218}