1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | |
3 | use crate::utils::crate_ident_new; |
4 | use proc_macro::TokenStream; |
5 | use proc_macro2::TokenStream as TokenStream2; |
6 | use quote::format_ident; |
7 | use quote::{quote, quote_spanned}; |
8 | use std::collections::HashMap; |
9 | use syn::ext::IdentExt; |
10 | use syn::parenthesized; |
11 | use syn::parse::Parse; |
12 | use syn::punctuated::Punctuated; |
13 | use syn::spanned::Spanned; |
14 | use syn::Token; |
15 | use syn::{parse_quote_spanned, LitStr}; |
16 | |
17 | pub struct PropsMacroInput { |
18 | wrapper_ty: syn::Path, |
19 | ext_trait: Option<Option<syn::Ident>>, |
20 | ident: syn::Ident, |
21 | props: Vec<PropDesc>, |
22 | } |
23 | |
24 | pub struct PropertiesAttrs { |
25 | wrapper_ty: syn::Path, |
26 | // None => no ext trait, |
27 | // Some(None) => derive the ext trait from the wrapper type, |
28 | // Some(Some(ident)) => use the given ext trait Ident |
29 | ext_trait: Option<Option<syn::Ident>>, |
30 | } |
31 | |
32 | impl Parse for PropertiesAttrs { |
33 | fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { |
34 | let mut wrapper_ty = None; |
35 | let mut ext_trait = None; |
36 | |
37 | while !input.is_empty() { |
38 | let ident = input.parse::<syn::Ident>()?; |
39 | if ident == "wrapper_type" { |
40 | let _eq = input.parse::<Token![=]>()?; |
41 | wrapper_ty = Some(input.parse::<syn::Path>()?); |
42 | } else if ident == "ext_trait" { |
43 | if input.peek(Token![=]) { |
44 | let _eq = input.parse::<Token![=]>()?; |
45 | let ident = input.parse::<syn::Ident>()?; |
46 | ext_trait = Some(Some(ident)); |
47 | } else { |
48 | ext_trait = Some(None); |
49 | } |
50 | } |
51 | if input.peek(Token![,]) { |
52 | input.parse::<Token![,]>()?; |
53 | } |
54 | } |
55 | |
56 | Ok(Self { |
57 | wrapper_ty: wrapper_ty.ok_or_else(|| { |
58 | syn::Error::new(input.span(), "missing #[properties(wrapper_type = ...)]" ) |
59 | })?, |
60 | ext_trait, |
61 | }) |
62 | } |
63 | } |
64 | |
65 | impl Parse for PropsMacroInput { |
66 | fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { |
67 | let derive_input: syn::DeriveInput = input.parse()?; |
68 | let attrs = derive_input |
69 | .attrs |
70 | .iter() |
71 | .find(|x| x.path().is_ident("properties" )) |
72 | .ok_or_else(|| { |
73 | syn::Error::new( |
74 | derive_input.span(), |
75 | "missing #[properties(wrapper_type = ...)]" , |
76 | ) |
77 | })?; |
78 | let attrs: PropertiesAttrs = attrs.parse_args()?; |
79 | let props: Vec<_> = match derive_input.data { |
80 | syn::Data::Struct(struct_data) => parse_fields(struct_data.fields)?, |
81 | _ => { |
82 | return Err(syn::Error::new( |
83 | derive_input.span(), |
84 | "Properties can only be derived on structs" , |
85 | )) |
86 | } |
87 | }; |
88 | Ok(Self { |
89 | wrapper_ty: attrs.wrapper_ty, |
90 | ext_trait: attrs.ext_trait, |
91 | ident: derive_input.ident, |
92 | props, |
93 | }) |
94 | } |
95 | } |
96 | |
97 | enum MaybeCustomFn { |
98 | Custom(Box<syn::Expr>), |
99 | Default, |
100 | } |
101 | |
102 | impl std::convert::From<Option<syn::Expr>> for MaybeCustomFn { |
103 | fn from(item: Option<syn::Expr>) -> Self { |
104 | match item { |
105 | Some(expr: Expr) => Self::Custom(Box::new(expr)), |
106 | None => Self::Default, |
107 | } |
108 | } |
109 | } |
110 | |
111 | enum PropAttr { |
112 | // builder(required_params).parameter(value) |
113 | // becomes |
114 | // Builder(Punctuated(required_params), Optionals(TokenStream)) |
115 | Builder(Punctuated<syn::Expr, Token![,]>, TokenStream2), |
116 | |
117 | // ident |
118 | Nullable, |
119 | |
120 | // ident [= expr] |
121 | Get(Option<syn::Expr>), |
122 | Set(Option<syn::Expr>), |
123 | |
124 | // ident = expr |
125 | OverrideClass(syn::Type), |
126 | OverrideInterface(syn::Type), |
127 | |
128 | // ident = expr |
129 | Type(syn::Type), |
130 | |
131 | // This will get translated from `ident = value` to `.ident(value)` |
132 | // and will get appended after the `builder(...)` call. |
133 | // ident [= expr] |
134 | BuilderField((syn::Ident, Option<syn::Expr>)), |
135 | |
136 | // ident = ident |
137 | Member(syn::Ident), |
138 | |
139 | // ident = "literal" |
140 | Name(syn::LitStr), |
141 | } |
142 | |
143 | impl Parse for PropAttr { |
144 | fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { |
145 | let name = input.call(syn::Ident::parse_any)?; |
146 | let name_str = name.to_string(); |
147 | |
148 | let res = if input.peek(Token![=]) { |
149 | let _assign_token: Token![=] = input.parse()?; |
150 | // name = expr | type | ident |
151 | match &*name_str { |
152 | "name" => PropAttr::Name(input.parse()?), |
153 | "get" => PropAttr::Get(Some(input.parse()?)), |
154 | "set" => PropAttr::Set(Some(input.parse()?)), |
155 | "override_class" => PropAttr::OverrideClass(input.parse()?), |
156 | "override_interface" => PropAttr::OverrideInterface(input.parse()?), |
157 | "type" => PropAttr::Type(input.parse()?), |
158 | "member" => PropAttr::Member(input.parse()?), |
159 | // Special case "default = ..." and map it to .default_value(...) |
160 | "default" => PropAttr::BuilderField(( |
161 | syn::Ident::new("default_value" , name.span()), |
162 | Some(input.parse()?), |
163 | )), |
164 | _ => PropAttr::BuilderField((name, Some(input.parse()?))), |
165 | } |
166 | } else if input.peek(syn::token::Paren) { |
167 | match &*name_str { |
168 | "builder" => { |
169 | let content; |
170 | parenthesized!(content in input); |
171 | let required = content.parse_terminated(syn::Expr::parse, Token![,])?; |
172 | let rest: TokenStream2 = input.parse()?; |
173 | PropAttr::Builder(required, rest) |
174 | } |
175 | _ => { |
176 | return Err(syn::Error::new( |
177 | name.span(), |
178 | format!("Unsupported attribute list {name_str}(...)" ), |
179 | )) |
180 | } |
181 | } |
182 | } else { |
183 | // attributes with only the identifier name |
184 | match &*name_str { |
185 | "nullable" => PropAttr::Nullable, |
186 | "get" => PropAttr::Get(None), |
187 | "set" => PropAttr::Set(None), |
188 | "readwrite" | "read_only" | "write_only" => { |
189 | return Err(syn::Error::new( |
190 | name.span(), |
191 | format!( |
192 | " {name} is a flag managed by the Properties macro. \ |
193 | Use `get` and `set` to manage read and write access to a property" , |
194 | ), |
195 | )) |
196 | } |
197 | _ => PropAttr::BuilderField((name, None)), |
198 | } |
199 | }; |
200 | Ok(res) |
201 | } |
202 | } |
203 | |
204 | #[derive (Default)] |
205 | struct ReceivedAttrs { |
206 | nullable: bool, |
207 | get: Option<MaybeCustomFn>, |
208 | set: Option<MaybeCustomFn>, |
209 | override_class: Option<syn::Type>, |
210 | override_interface: Option<syn::Type>, |
211 | ty: Option<syn::Type>, |
212 | member: Option<syn::Ident>, |
213 | name: Option<syn::LitStr>, |
214 | builder: Option<(Punctuated<syn::Expr, Token![,]>, TokenStream2)>, |
215 | builder_fields: HashMap<syn::Ident, Option<syn::Expr>>, |
216 | } |
217 | |
218 | impl Parse for ReceivedAttrs { |
219 | fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { |
220 | let attrs: Punctuated = syn::punctuated::Punctuated::<PropAttr, Token![,]>::parse_terminated(input)?; |
221 | let this: ReceivedAttrs = attrs.into_iter().fold(Self::default(), |mut this: ReceivedAttrs, attr: PropAttr| { |
222 | this.set_from_attr(attr); |
223 | this |
224 | }); |
225 | |
226 | Ok(this) |
227 | } |
228 | } |
229 | |
230 | impl ReceivedAttrs { |
231 | fn set_from_attr(&mut self, attr: PropAttr) { |
232 | match attr { |
233 | PropAttr::Nullable => self.nullable = true, |
234 | PropAttr::Get(some_fn: Option) => self.get = Some(some_fn.into()), |
235 | PropAttr::Set(some_fn: Option) => self.set = Some(some_fn.into()), |
236 | PropAttr::Name(lit: LitStr) => self.name = Some(lit), |
237 | PropAttr::OverrideClass(ty: Type) => self.override_class = Some(ty), |
238 | PropAttr::OverrideInterface(ty: Type) => self.override_interface = Some(ty), |
239 | PropAttr::Type(ty: Type) => self.ty = Some(ty), |
240 | PropAttr::Member(member: Ident) => self.member = Some(member), |
241 | PropAttr::Builder(required_params: Punctuated, optionals: TokenStream) => { |
242 | self.builder = Some((required_params, optionals)) |
243 | } |
244 | PropAttr::BuilderField((ident: Ident, expr: Option)) => { |
245 | self.builder_fields.insert(k:ident, v:expr); |
246 | } |
247 | } |
248 | } |
249 | } |
250 | |
251 | // It's a cleaned up version of `ReceivedAttrs` where some missing attributes get a default, |
252 | // generated value. |
253 | struct PropDesc { |
254 | attrs_span: proc_macro2::Span, |
255 | field_ident: syn::Ident, |
256 | ty: syn::Type, |
257 | name: syn::LitStr, |
258 | override_class: Option<syn::Type>, |
259 | override_interface: Option<syn::Type>, |
260 | nullable: bool, |
261 | get: Option<MaybeCustomFn>, |
262 | set: Option<MaybeCustomFn>, |
263 | member: Option<syn::Ident>, |
264 | builder: Option<(Punctuated<syn::Expr, Token![,]>, TokenStream2)>, |
265 | builder_fields: HashMap<syn::Ident, Option<syn::Expr>>, |
266 | is_construct_only: bool, |
267 | } |
268 | |
269 | impl PropDesc { |
270 | fn new( |
271 | attrs_span: proc_macro2::Span, |
272 | field_ident: syn::Ident, |
273 | field_ty: syn::Type, |
274 | attrs: ReceivedAttrs, |
275 | ) -> syn::Result<Self> { |
276 | let ReceivedAttrs { |
277 | nullable, |
278 | get, |
279 | mut set, |
280 | override_class, |
281 | override_interface, |
282 | ty, |
283 | member, |
284 | name, |
285 | builder, |
286 | builder_fields, |
287 | } = attrs; |
288 | |
289 | let is_construct_only = builder_fields.iter().any(|(k, _)| *k == "construct_only" ); |
290 | if is_construct_only && set.is_none() { |
291 | // Insert a default internal setter automatically |
292 | set = Some(MaybeCustomFn::Default); |
293 | } |
294 | |
295 | if get.is_none() && set.is_none() { |
296 | return Err(syn::Error::new( |
297 | attrs_span, |
298 | "No `get` or `set` specified: at least one is required." .to_string(), |
299 | )); |
300 | } |
301 | |
302 | if override_class.is_some() && override_interface.is_some() { |
303 | return Err(syn::Error::new( |
304 | attrs_span, |
305 | "Both `override_class` and `override_interface` specified." .to_string(), |
306 | )); |
307 | } |
308 | |
309 | // Fill needed, but missing, attributes with calculated default values |
310 | let name = name.unwrap_or_else(|| { |
311 | syn::LitStr::new( |
312 | &field_ident.to_string().trim_matches('_' ).replace('_' , "-" ), |
313 | field_ident.span(), |
314 | ) |
315 | }); |
316 | let ty = ty.unwrap_or_else(|| field_ty.clone()); |
317 | |
318 | // Now that everything is set and safe, return the final property description |
319 | Ok(Self { |
320 | attrs_span, |
321 | field_ident, |
322 | ty, |
323 | name, |
324 | override_class, |
325 | override_interface, |
326 | nullable, |
327 | get, |
328 | set, |
329 | member, |
330 | builder, |
331 | builder_fields, |
332 | is_construct_only, |
333 | }) |
334 | } |
335 | fn is_overriding(&self) -> bool { |
336 | self.override_class.is_some() || self.override_interface.is_some() |
337 | } |
338 | } |
339 | |
340 | fn expand_param_spec(prop: &PropDesc) -> TokenStream2 { |
341 | let crate_ident = crate_ident_new(); |
342 | let PropDesc { |
343 | ty, name, builder, .. |
344 | } = prop; |
345 | let stripped_name = strip_raw_prefix_from_name(name); |
346 | |
347 | match (&prop.override_class, &prop.override_interface) { |
348 | (Some(c), None) => { |
349 | return quote!(#crate_ident::ParamSpecOverride::for_class::<#c>(#stripped_name)) |
350 | } |
351 | (None, Some(i)) => { |
352 | return quote!(#crate_ident::ParamSpecOverride::for_interface::<#i>(#stripped_name)) |
353 | } |
354 | (Some(_), Some(_)) => { |
355 | unreachable!("Both `override_class` and `override_interface` specified" ) |
356 | } |
357 | (None, None) => (), |
358 | }; |
359 | |
360 | let rw_flags = match (&prop.get, &prop.set) { |
361 | (Some(_), Some(_)) => quote!(.readwrite()), |
362 | (Some(_), None) => quote!(.read_only()), |
363 | (None, Some(_)) => quote!(.write_only()), |
364 | (None, None) => unreachable!("No `get` or `set` specified" ), |
365 | }; |
366 | |
367 | let builder_call = builder |
368 | .as_ref() |
369 | .cloned() |
370 | .map(|(mut required_params, chained_methods)| { |
371 | let name_expr = syn::ExprLit { |
372 | attrs: vec![], |
373 | lit: syn::Lit::Str(stripped_name.to_owned()), |
374 | }; |
375 | required_params.insert(0, name_expr.into()); |
376 | let required_params = required_params.iter(); |
377 | |
378 | quote!((#(#required_params,)*)#chained_methods) |
379 | }) |
380 | .unwrap_or(quote!((#stripped_name))); |
381 | |
382 | let builder_fields = prop.builder_fields.iter().map(|(k, v)| quote!(.#k(#v))); |
383 | |
384 | let span = prop.attrs_span; |
385 | quote_spanned! {span=> |
386 | <<#ty as #crate_ident::property::Property>::Value as #crate_ident::prelude::HasParamSpec> |
387 | ::param_spec_builder() #builder_call |
388 | #rw_flags |
389 | #(#builder_fields)* |
390 | .build() |
391 | } |
392 | } |
393 | |
394 | fn expand_properties_fn(props: &[PropDesc]) -> TokenStream2 { |
395 | let n_props: usize = props.len(); |
396 | let crate_ident: TokenStream = crate_ident_new(); |
397 | let param_specs: impl Iterator = props.iter().map(expand_param_spec); |
398 | quote!( |
399 | fn derived_properties() -> &'static [#crate_ident::ParamSpec] { |
400 | use #crate_ident::prelude::ParamSpecBuilderExt; |
401 | static PROPERTIES: ::std::sync::OnceLock<[#crate_ident::ParamSpec; #n_props]> = ::std::sync::OnceLock::new(); |
402 | PROPERTIES.get_or_init(|| [ |
403 | #(#param_specs,)* |
404 | ]) |
405 | } |
406 | ) |
407 | } |
408 | |
409 | fn expand_property_fn(props: &[PropDesc]) -> TokenStream2 { |
410 | let crate_ident = crate_ident_new(); |
411 | let match_branch_get = props.iter().flat_map(|p| { |
412 | let PropDesc { |
413 | name, |
414 | field_ident, |
415 | member, |
416 | get, |
417 | ty, |
418 | .. |
419 | } = p; |
420 | |
421 | let enum_ident = name_to_enum_ident(name.value()); |
422 | let span = p.attrs_span; |
423 | get.as_ref().map(|get| { |
424 | let body = match (member, get) { |
425 | (_, MaybeCustomFn::Custom(expr)) => quote!( |
426 | DerivedPropertiesEnum::#enum_ident => { |
427 | let value: <#ty as #crate_ident::property::Property>::Value = (#expr)(&self); |
428 | ::std::convert::From::from(value) |
429 | } |
430 | ), |
431 | (None, MaybeCustomFn::Default) => quote!( |
432 | DerivedPropertiesEnum::#enum_ident => |
433 | #crate_ident::property::PropertyGet::get(&self.#field_ident, |v| ::std::convert::From::from(v)) |
434 | |
435 | ), |
436 | (Some(member), MaybeCustomFn::Default) => quote!( |
437 | DerivedPropertiesEnum::#enum_ident => |
438 | #crate_ident::property::PropertyGet::get(&self.#field_ident, |v| ::std::convert::From::from(&v.#member)) |
439 | |
440 | ), |
441 | }; |
442 | quote_spanned!(span=> #body) |
443 | }) |
444 | }); |
445 | quote!( |
446 | fn derived_property( |
447 | &self, |
448 | id: usize, |
449 | pspec: &#crate_ident::ParamSpec |
450 | ) -> #crate_ident::Value { |
451 | let prop: DerivedPropertiesEnum = std::convert::TryFrom::try_from(id-1) |
452 | .unwrap_or_else(|_| panic!("property not defined {}" , pspec.name())); |
453 | match prop { |
454 | #(#match_branch_get,)* |
455 | _ => panic!("missing getter for property {}" , pspec.name()), |
456 | } |
457 | } |
458 | ) |
459 | } |
460 | |
461 | fn expand_set_property_fn(props: &[PropDesc]) -> TokenStream2 { |
462 | let crate_ident = crate_ident_new(); |
463 | let match_branch_set = props.iter().flat_map(|p| { |
464 | let PropDesc { |
465 | name, |
466 | field_ident, |
467 | member, |
468 | set, |
469 | ty, |
470 | .. |
471 | } = p; |
472 | let stripped_name = strip_raw_prefix_from_name(name); |
473 | let crate_ident = crate_ident_new(); |
474 | let enum_ident = name_to_enum_ident(name.value()); |
475 | let span = p.attrs_span; |
476 | let expect = quote!(.unwrap_or_else( |
477 | |err| panic!( |
478 | "Invalid conversion from `glib::value::Value` to `{}` inside setter for property `{}`: {:?}" , |
479 | ::std::any::type_name::<<#ty as #crate_ident::property::Property>::Value>(), #stripped_name, err |
480 | ) |
481 | )); |
482 | set.as_ref().map(|set| { |
483 | let body = match (member, set) { |
484 | (_, MaybeCustomFn::Custom(expr)) => quote!( |
485 | DerivedPropertiesEnum::#enum_ident => { |
486 | (#expr)(&self, #crate_ident::Value::get(value)#expect); |
487 | } |
488 | ), |
489 | (None, MaybeCustomFn::Default) => quote!( |
490 | DerivedPropertiesEnum::#enum_ident => { |
491 | #crate_ident::property::PropertySet::set( |
492 | &self.#field_ident, |
493 | #crate_ident::Value::get(value)#expect |
494 | ); |
495 | } |
496 | ), |
497 | (Some(member), MaybeCustomFn::Default) => quote!( |
498 | DerivedPropertiesEnum::#enum_ident => { |
499 | #crate_ident::property::PropertySetNested::set_nested( |
500 | &self.#field_ident, |
501 | move |v| v.#member = #crate_ident::Value::get(value)#expect |
502 | ); |
503 | } |
504 | ), |
505 | }; |
506 | quote_spanned!(span=> #body) |
507 | }) |
508 | }); |
509 | quote!( |
510 | #[allow(unreachable_code)] |
511 | fn derived_set_property(&self, |
512 | id: usize, |
513 | value: &#crate_ident::Value, |
514 | pspec: &#crate_ident::ParamSpec |
515 | ){ |
516 | let prop: DerivedPropertiesEnum = std::convert::TryFrom::try_from(id-1) |
517 | .unwrap_or_else(|_| panic!("property not defined {}" , pspec.name())); |
518 | match prop { |
519 | #(#match_branch_set,)* |
520 | _ => panic!("missing setter for property {}" , pspec.name()), |
521 | } |
522 | } |
523 | ) |
524 | } |
525 | |
526 | fn parse_fields(fields: syn::Fields) -> syn::Result<Vec<PropDesc>> { |
527 | fieldsimpl Iterator- >
|
528 | .into_iter() |
529 | .flat_map(|field: Field| { |
530 | let syn::Field { |
531 | ident: Option, attrs: Vec, ty: Type, .. |
532 | } = field; |
533 | attrsimpl Iterator |
534 | .into_iter() |
535 | .filter(|a: &Attribute| a.path().is_ident("property" )) |
536 | .map(move |prop_attrs: Attribute| { |
537 | let span: Span = prop_attrs.span(); |
538 | PropDesc::new( |
539 | span, |
540 | field_ident:ident.as_ref().unwrap().clone(), |
541 | ty.clone(), |
542 | attrs:prop_attrs.parse_args()?, |
543 | ) |
544 | }) |
545 | }) |
546 | .collect::<syn::Result<_>>() |
547 | } |
548 | |
549 | /// Converts a glib property name to a correct rust ident |
550 | fn name_to_ident(name: &syn::LitStr) -> syn::Ident { |
551 | format_ident!(" {}" , name.value().replace('-' , "_" )) |
552 | } |
553 | |
554 | /// Strips out raw identifier prefix (`r#`) from literal string items |
555 | fn strip_raw_prefix_from_name(name: &LitStr) -> LitStr { |
556 | LitStr::new( |
557 | value:name.value().strip_prefix("r#" ).unwrap_or(&name.value()), |
558 | name.span(), |
559 | ) |
560 | } |
561 | |
562 | fn expand_impl_getset_properties(props: &[PropDesc]) -> Vec<syn::ImplItemFn> { |
563 | let crate_ident = crate_ident_new(); |
564 | let defs = props.iter().filter(|p| !p.is_overriding()).map(|p| { |
565 | let name = &p.name; |
566 | let stripped_name = strip_raw_prefix_from_name(name); |
567 | let ident = name_to_ident(name); |
568 | let ty = &p.ty; |
569 | |
570 | let getter = p.get.is_some().then(|| { |
571 | let span = p.attrs_span; |
572 | parse_quote_spanned!(span=> |
573 | #[must_use] |
574 | #[allow(dead_code)] |
575 | pub fn #ident(&self) -> <#ty as #crate_ident::property::Property>::Value { |
576 | self.property::<<#ty as #crate_ident::property::Property>::Value>(#stripped_name) |
577 | } |
578 | ) |
579 | }); |
580 | |
581 | let setter = (p.set.is_some() && !p.is_construct_only).then(|| { |
582 | let ident = format_ident!("set_ {}" , ident); |
583 | let target_ty = quote!(<<#ty as #crate_ident::property::Property>::Value as #crate_ident::prelude::HasParamSpec>::SetValue); |
584 | let set_ty = if p.nullable { |
585 | quote!(::core::option::Option<impl std::borrow::Borrow<#target_ty>>) |
586 | } else { |
587 | quote!(impl std::borrow::Borrow<#target_ty>) |
588 | }; |
589 | let upcasted_borrowed_value = if p.nullable { |
590 | quote!( |
591 | value.as_ref().map(|v| std::borrow::Borrow::borrow(v)) |
592 | ) |
593 | } else { |
594 | quote!( |
595 | std::borrow::Borrow::borrow(&value) |
596 | ) |
597 | }; |
598 | let span = p.attrs_span; |
599 | parse_quote_spanned!(span=> |
600 | #[allow(dead_code)] |
601 | pub fn #ident<'a>(&self, value: #set_ty) { |
602 | self.set_property_from_value(#stripped_name, &::std::convert::From::from(#upcasted_borrowed_value)) |
603 | } |
604 | ) |
605 | }); |
606 | [getter, setter] |
607 | }); |
608 | defs.flatten() // flattens [] |
609 | .flatten() // removes None |
610 | .collect::<Vec<_>>() |
611 | } |
612 | |
613 | fn expand_impl_connect_prop_notify(props: &[PropDesc]) -> Vec<syn::ImplItemFn> { |
614 | let crate_ident: TokenStream = crate_ident_new(); |
615 | let connection_fns: impl Iterator = props.iter().filter(|p: &&PropDesc| !p.is_overriding()).map(|p: &PropDesc| -> syn::ImplItemFn { |
616 | let name: &LitStr = &p.name; |
617 | let stripped_name: LitStr = strip_raw_prefix_from_name(name); |
618 | let fn_ident: Ident = format_ident!("connect_ {}_notify" , name_to_ident(name)); |
619 | let span: Span = p.attrs_span; |
620 | parse_quote_spanned!(span=> |
621 | #[allow(dead_code)] |
622 | pub fn #fn_ident<F: Fn(&Self) + 'static>(&self, f: F) -> #crate_ident::SignalHandlerId { |
623 | self.connect_notify_local(::core::option::Option::Some(#stripped_name), move |this, _| { |
624 | f(this) |
625 | }) |
626 | } |
627 | ) |
628 | }); |
629 | connection_fns.collect::<Vec<_>>() |
630 | } |
631 | |
632 | fn expand_impl_notify_prop(wrapper_type: &syn::Path, props: &[PropDesc]) -> Vec<syn::ImplItemFn> { |
633 | let crate_ident: TokenStream = crate_ident_new(); |
634 | let emit_fns: impl Iterator = props.iter().filter(|p: &&PropDesc| !p.is_overriding()).map(|p: &PropDesc| -> syn::ImplItemFn { |
635 | let name: LitStr = strip_raw_prefix_from_name(&p.name); |
636 | let fn_ident: Ident = format_ident!("notify_ {}" , name_to_ident(&name)); |
637 | let span: Span = p.attrs_span; |
638 | let enum_ident: Ident = name_to_enum_ident(name.value()); |
639 | parse_quote_spanned!(span=> |
640 | #[allow(dead_code)] |
641 | pub fn #fn_ident(&self) { |
642 | self.notify_by_pspec( |
643 | &<<#wrapper_type as #crate_ident::object::ObjectSubclassIs>::Subclass |
644 | as #crate_ident::subclass::object::DerivedObjectProperties>::derived_properties() |
645 | [DerivedPropertiesEnum::#enum_ident as usize] |
646 | ); |
647 | } |
648 | ) |
649 | }); |
650 | emit_fns.collect::<Vec<_>>() |
651 | } |
652 | |
653 | fn name_to_enum_ident(name: String) -> syn::Ident { |
654 | let mut name: String = name.strip_prefix("r#" ).unwrap_or(&name).to_owned(); |
655 | let mut slice: &mut str = name.as_mut_str(); |
656 | while let Some(i: usize) = slice.find('-' ) { |
657 | let (head: &mut str, tail: &mut str) = slice.split_at_mut(mid:i); |
658 | if let Some(c: &mut str) = head.get_mut(0..1) { |
659 | c.make_ascii_uppercase(); |
660 | } |
661 | slice = &mut tail[1..]; |
662 | } |
663 | if let Some(c: &mut str) = slice.get_mut(0..1) { |
664 | c.make_ascii_uppercase(); |
665 | } |
666 | let enum_member: String = name.split('-' ).collect(); |
667 | format_ident!(" {}" , enum_member) |
668 | } |
669 | |
670 | fn expand_properties_enum(props: &[PropDesc]) -> TokenStream2 { |
671 | if props.is_empty() { |
672 | quote! { |
673 | #[derive(Debug, Copy, Clone)] |
674 | enum DerivedPropertiesEnum {} |
675 | impl std::convert::TryFrom<usize> for DerivedPropertiesEnum { |
676 | type Error = usize; |
677 | |
678 | fn try_from(item: usize) -> ::core::result::Result<Self, <Self as std::convert::TryFrom<usize>>::Error> { |
679 | ::core::result::Result::Err(item) |
680 | } |
681 | } |
682 | } |
683 | } else { |
684 | let properties: Vec<syn::Ident> = props |
685 | .iter() |
686 | .map(|p| { |
687 | let name: String = p.name.value(); |
688 | |
689 | name_to_enum_ident(name) |
690 | }) |
691 | .collect(); |
692 | let props = properties.iter(); |
693 | let indices = 0..properties.len(); |
694 | quote! { |
695 | #[repr(usize)] |
696 | #[derive(Debug, Copy, Clone)] |
697 | enum DerivedPropertiesEnum { |
698 | #(#props,)* |
699 | } |
700 | impl std::convert::TryFrom<usize> for DerivedPropertiesEnum { |
701 | type Error = usize; |
702 | |
703 | fn try_from(item: usize) -> ::core::result::Result<Self, <Self as std::convert::TryFrom<usize>>::Error> { |
704 | match item { |
705 | #(#indices => ::core::result::Result::Ok(Self::#properties),)* |
706 | _ => ::core::result::Result::Err(item) |
707 | } |
708 | } |
709 | } |
710 | } |
711 | } |
712 | } |
713 | |
714 | pub fn impl_derive_props(input: PropsMacroInput) -> TokenStream { |
715 | let struct_ident = &input.ident; |
716 | let crate_ident = crate_ident_new(); |
717 | let wrapper_type = input.wrapper_ty; |
718 | let fn_properties = expand_properties_fn(&input.props); |
719 | let fn_property = expand_property_fn(&input.props); |
720 | let fn_set_property = expand_set_property_fn(&input.props); |
721 | let getset_properties = expand_impl_getset_properties(&input.props); |
722 | let connect_prop_notify = expand_impl_connect_prop_notify(&input.props); |
723 | let notify_prop = expand_impl_notify_prop(&wrapper_type, &input.props); |
724 | let properties_enum = expand_properties_enum(&input.props); |
725 | |
726 | let rust_interface = if let Some(ext_trait) = input.ext_trait { |
727 | let trait_ident = if let Some(ext_trait) = ext_trait { |
728 | ext_trait |
729 | } else { |
730 | format_ident!( |
731 | " {}PropertiesExt" , |
732 | wrapper_type.segments.last().unwrap().ident |
733 | ) |
734 | }; |
735 | let fns_without_visibility_modifier = getset_properties |
736 | .into_iter() |
737 | .chain(connect_prop_notify) |
738 | .chain(notify_prop) |
739 | .map(|mut item| { |
740 | item.vis = syn::Visibility::Inherited; |
741 | item |
742 | }); |
743 | quote! { |
744 | pub trait #trait_ident: #crate_ident::prelude::IsA<#wrapper_type> { |
745 | #(#fns_without_visibility_modifier)* |
746 | } |
747 | impl<T: #crate_ident::prelude::IsA<#wrapper_type>> #trait_ident for T {} |
748 | } |
749 | } else { |
750 | quote! { |
751 | #[allow(dead_code)] |
752 | impl #wrapper_type { |
753 | #(#getset_properties)* |
754 | #(#connect_prop_notify)* |
755 | #(#notify_prop)* |
756 | } |
757 | } |
758 | }; |
759 | |
760 | let expanded = quote! { |
761 | #properties_enum |
762 | |
763 | impl #crate_ident::subclass::object::DerivedObjectProperties for #struct_ident { |
764 | #fn_properties |
765 | #fn_property |
766 | #fn_set_property |
767 | } |
768 | |
769 | #rust_interface |
770 | }; |
771 | proc_macro::TokenStream::from(expanded) |
772 | } |
773 | |