scratch_test_model_proc/
lib.rs1#![doc = include_str!("../../../README.md")]
2mod parse_impls;
4mod valid_attributes;
5
6use proc_macro2::TokenStream as TokenStream2;
7use valid_attributes::{BlockLevelAttribute, my_attributes};
8
9use proc_macro::TokenStream;
10use quote::quote;
11use syn::parse_macro_input;
12
13struct BlockKindEnumSpec {
14 vis: syn::Visibility,
15 name: syn::Ident,
16 variants: Vec<BlockVariantSpec>,
17 #[allow(unused)]
18 default_location: Option<ParameterLocation>,
19}
20struct BlockVariantSpec {
21 opcode: String,
23 name: syn::Ident,
24 parameters: Vec<BlockParameterSpec>,
25 parse_via: Option<syn::Ident>,
26}
27
28enum VariantLevelAttributes {
29 Opcode(syn::LitStr),
30 ParseVia(syn::Ident),
31}
32enum FieldLevelAttributes {
33 JsonName(syn::LitStr),
34 Location(ParameterLocation),
35 }
37
38struct BlockParameterSpec {
39 json_name: String,
41 location: ParameterLocation,
42 #[allow(unused)]
44 vis: syn::Visibility,
45 name: syn::Ident,
46 #[allow(unused)]
47 ty: syn::Type,
48}
49
50#[derive(Debug, Clone, Copy)]
51enum ParameterLocation {
52 Inputs,
53 Fields,
54}
55
56impl quote::ToTokens for BlockKindEnumSpec {
57 fn to_tokens(&self, tokens: &mut TokenStream2) {
58 let unit_name = syn::Ident::new(
59 &format!("{}Unit", self.name),
60 proc_macro2::Span::call_site(),
61 );
62 let name = &self.name;
63 let variants = self
64 .variants
65 .iter()
66 .map(|v| v.name.clone())
67 .collect::<Vec<_>>();
68 let opcodes: Vec<_> = self.variants.iter().map(|v| v.opcode.clone()).collect();
69 let opcode_comments = opcodes.iter().map(|o| format!("`{o}` is the json-side opcode"));
70 let opcode_names = opcodes.iter();
71 let vis = &self.vis;
72
73
74 tokens.extend(quote! {
76 #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
77 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
78 #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
79 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
80 #vis enum #unit_name {
81 #(
82 #[doc = #opcode_comments]
83 #[cfg_attr(feature = "serde", serde(rename = #opcode_names))]
84 #variants
85 ,)*
86 }
87 impl crate::_exports::AsOpcodeUnit for #name {
88 type OpcodeUnit = #unit_name;
89
90 fn opcode(&self) -> Self::OpcodeUnit {
91 match self {
92 #( Self::#variants{..} => Self::OpcodeUnit::#variants, )*
93 }
94 }
95 }
96 impl #unit_name {
97 pub const fn k_opcode_name(&self) -> &'static str {
101 match self {
102 #( Self::#variants => #opcodes, )*
103 }
104 }
105 }
106 impl crate::_exports::AsOpcodeName for #unit_name {
107 fn opcode_name(&self) -> &'static str {
108 match self {
109 #( Self::#variants => #opcodes, )*
110 }
111 }
112 }
113
114 impl std::fmt::Display for #unit_name {
115 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
116 f.write_str(crate::_exports::AsOpcodeName::opcode_name(&self))
117 }
118 }
119 });
120
121 let variants = self.variants.iter().map(|variant| {
122 if let Some(parse_via_func) = variant.parse_via.as_ref() {
123 quote! {
124 #parse_via_func(all_target_blocks, opcode, inputs, fields, mutation)
125 }
126 } else {
127
128 let attribute_defs = variant.parameters.iter().map(|p| {
129 let ident = &p.name;
130 let key = &p.json_name;
131 match p.location {
132 ParameterLocation::Inputs => quote! {
133 let #ident = crate::_exports::helper_attr_access(all_target_blocks, inputs, #key)?;
134 },
135 ParameterLocation::Fields => quote! {
136 let #ident = crate::_exports::helper_attr_access(all_target_blocks, fields, #key)?;
137 },
138 }
139 });
140 let attribute_names = variant.parameters.iter().map(|p| &p.name);
141
142 let var_name = &variant.name;
143
144 quote! {
145 #(#attribute_defs;)*
146 Ok(Self::#var_name { #(#attribute_names,)* })
147 }}
148 });
149
150 tokens.extend(quote!( impl crate::_exports::ParseJsonBlock for #name {
151 fn parse_json_block<'a, 'b>(
152 all_target_blocks: crate::_exports::JsonBlocks<'a>,
153 opcode: &str,
154 inputs: &'b crate::_exports::BlockProperties<'a>,
155 fields: &'b crate::_exports::BlockProperties<'a>,
156 mutation: Option<&'b crate::_exports::BlockProperties<'a>>
157 ) -> Result<Self, crate::_exports::BlockKindError<'a>>
158 where
159 Self: Sized {
160 match opcode {
161 #(#opcodes => {#variants})*
162 opcode => Err(crate::_exports::BlockKindError::UnknownOpcode(opcode.into()))
163 }
164 }
165 } ));
166
167 let attribute_types = self.variants.iter()
170 .flat_map(|variant| variant.parameters.iter().map(|p| &p.ty)).map(|ty| {
171 quote! { #ty: crate::_exports::DoForAttrs<'a, S> }
172 });
173
174 let variants = self.variants.iter().map(|variant| {
175 let params = variant.parameters.iter().map(|param| {
176 let name = ¶m.name;
177 quote! { crate::_exports::DoForAttrs::<'a, S>::do_for_attrs(
178 #name,
179 inputs_that_are_passed_to_all_attributes,
180 outputs_received_by_all_attributes,
181 )?;
182 }
183 });
184 let names = variant.parameters.iter().map(|param| ¶m.name);
185 let var_name = &variant.name;
186 quote! {
187 Self::#var_name{ #(#names),* } => { #(#params)* }
188 }
189 });
190
191 tokens.extend(quote!(
192 impl<'a, S: crate::_exports::DoForAttrsStrategy<'a>> crate::_exports::DoForAttrs<'a, S> for #name
193 where
194 #(#attribute_types),*
195 {
196 fn do_for_attrs(
197 &'a self,
198 inputs_that_are_passed_to_all_attributes: &S::Inputs,
199 outputs_received_by_all_attributes: &mut S::Outputs
200 ) -> Result<(), S::Error> {
201 match self {
202 #(#variants)*
203 }
204 Ok(())
205 }
206 }
207 ));
208 }
209}
210
211#[proc_macro_derive(Block, attributes(block))]
212pub fn derive_block(input: TokenStream) -> TokenStream {
213 let data = parse_macro_input!(input as BlockKindEnumSpec);
214
215 quote! {
216 #data
217 }
218 .into()
219}