1use proc_macro::TokenStream;
11use quote::quote;
12use syn::{
13 Attribute, Data, DeriveInput, Expr, Fields, LitStr, Meta, parse_macro_input, spanned::Spanned,
14};
15
16fn doc_of(attrs: &[Attribute]) -> String {
19 attrs
20 .iter()
21 .filter_map(|a| match &a.meta {
22 Meta::NameValue(nv) if nv.path.is_ident("doc") => match &nv.value {
23 Expr::Lit(syn::ExprLit {
24 lit: syn::Lit::Str(s),
25 ..
26 }) => Some(s.value()),
27 _ => None,
28 },
29 _ => None,
30 })
31 .map(|line| line.strip_prefix(' ').unwrap_or(&line).to_string())
32 .collect::<Vec<_>>()
33 .join("\n")
34}
35
36fn snake_case(name: &str) -> String {
38 let mut out = String::new();
39 for (i, c) in name.chars().enumerate() {
40 if c.is_uppercase() {
41 if i > 0 {
42 out.push('_');
43 }
44 out.extend(c.to_lowercase());
45 } else {
46 out.push(c);
47 }
48 }
49 out
50}
51
52#[proc_macro_derive(Operations, attributes(operation))]
55pub fn derive_operations(input: TokenStream) -> TokenStream {
56 let input = parse_macro_input!(input as DeriveInput);
57 let name = &input.ident;
58 let Data::Enum(data) = &input.data else {
59 return syn::Error::new(input.span(), "Operations needs an enum")
60 .to_compile_error()
61 .into();
62 };
63 let mut infos = Vec::new();
64 let mut new_arms = Vec::new();
65 let mut apply_arms = Vec::new();
66 let mut session_arms = Vec::new();
67 let mut form_arms = Vec::new();
68 let mut set_arms = Vec::new();
69 let mut event_arms = Vec::new();
70 let mut kind_arms = Vec::new();
71 let mut label_arms = Vec::new();
72 let mut froms = Vec::new();
73 for variant in &data.variants {
74 let op = &variant.ident;
75 let Some(args) = (match &variant.fields {
76 Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
77 fields.unnamed.first().map(|f| &f.ty)
78 }
79 _ => None,
80 }) else {
81 return syn::Error::new(variant.span(), "every operation is `Name(NameArgs)`")
82 .to_compile_error()
83 .into();
84 };
85 let mut label = op.to_string();
86 for attr in variant
87 .attrs
88 .iter()
89 .filter(|a| a.path().is_ident("operation"))
90 {
91 let parsed = attr.parse_nested_meta(|meta| {
92 if meta.path.is_ident("label") {
93 label = meta.value()?.parse::<LitStr>()?.value();
94 Ok(())
95 } else {
96 Err(meta.error("expected `label = \"...\"`"))
97 }
98 });
99 if let Err(e) = parsed {
100 return e.to_compile_error().into();
101 }
102 }
103 let kind = snake_case(&op.to_string());
104 let doc = doc_of(&variant.attrs);
105 let operation = quote!(::geop_ops::operation::Operation);
106 infos.push(quote! {
107 ::geop_ops::operation::OperationInfo { kind: #kind, label: #label, doc: #doc }
108 });
109 new_arms.push(quote! {
110 #kind => ::std::result::Result::Ok(#name::#op(#operation::new_args(&#op, before))),
111 });
112 apply_arms.push(quote! {
113 #name::#op(args) => #operation::apply(&#op, part, operation_id, args),
114 });
115 let session = quote!(<#op as #operation>::Session);
116 let expect = quote!(.expect("a session made for a step of the same operation"));
117 session_arms.push(quote! {
118 #name::#op(_) => ::std::boxed::Box::new(<#session as ::std::default::Default>::default()),
119 });
120 form_arms.push(quote! {
121 #name::#op(args) => #operation::form(
122 &#op, before, args, session.downcast_ref::<#session>()#expect, selection,
123 ).erase(),
124 });
125 set_arms.push(quote! {
126 #name::#op(args) => #operation::set(
127 &#op, before, args, session.downcast_mut::<#session>()#expect, selection, key,
128 value,
129 ),
130 });
131 event_arms.push(quote! {
132 #name::#op(args) => #operation::event(
133 &#op, before, args, session.downcast_mut::<#session>()#expect, selection, event,
134 ),
135 });
136 kind_arms.push(quote! { #name::#op(_) => #kind, });
137 label_arms.push(quote! { #name::#op(_) => #label, });
138 froms.push(quote! {
139 impl ::std::convert::From<#args> for #name {
140 fn from(args: #args) -> Self {
141 #name::#op(args)
142 }
143 }
144 });
145 }
146 let private = quote!(::geop_ops::__private);
147 quote! {
148 impl ::geop_ops::operation::Operations for #name {
149 fn infos() -> ::std::vec::Vec<::geop_ops::operation::OperationInfo> {
150 ::std::vec![#(#infos),*]
151 }
152
153 fn new_step<S: #private::Scalar>(
154 kind: &str,
155 before: &::geop_ops::Part<S>,
156 ) -> #private::GeopResult<Self> {
157 match kind {
158 #(#new_arms)*
159 other => ::std::result::Result::Err(#private::GeopError::new(
160 ::std::format!("there is no operation {other:?}"),
161 )),
162 }
163 }
164
165 fn apply<S: #private::Scalar>(
166 &self,
167 part: ::geop_ops::Part<S>,
168 operation_id: &str,
169 ) -> #private::GeopResult<::geop_ops::Part<S>> {
170 match self {
171 #(#apply_arms)*
172 }
173 }
174
175 fn new_session(&self) -> ::std::boxed::Box<dyn ::std::any::Any> {
176 match self {
177 #(#session_arms)*
178 }
179 }
180
181 fn form<'a, S: #private::Scalar>(
182 &self,
183 before: &'a ::geop_ops::Part<S>,
184 session: &dyn ::std::any::Any,
185 selection: &[::std::string::String],
186 ) -> ::geop_ops::ui::Form<'a, S> {
187 match self {
188 #(#form_arms)*
189 }
190 }
191
192 fn set<S: #private::Scalar>(
193 &mut self,
194 before: &::geop_ops::Part<S>,
195 session: &mut dyn ::std::any::Any,
196 selection: &mut ::std::vec::Vec<::std::string::String>,
197 key: &str,
198 value: ::geop_ops::ui::Value,
199 ) {
200 match self {
201 #(#set_arms)*
202 }
203 }
204
205 fn event<S: #private::Scalar>(
206 &mut self,
207 before: &::geop_ops::Part<S>,
208 session: &mut dyn ::std::any::Any,
209 selection: &mut ::std::vec::Vec<::std::string::String>,
210 event: &::geop_ops::ui::CanvasEvent<S>,
211 ) {
212 match self {
213 #(#event_arms)*
214 }
215 }
216
217 fn kind(&self) -> &'static str {
218 match self {
219 #(#kind_arms)*
220 }
221 }
222
223 fn label(&self) -> &'static str {
224 match self {
225 #(#label_arms)*
226 }
227 }
228 }
229 #(#froms)*
230 }
231 .into()
232}