1use crate::utils::crate_ident_new;
4use proc_macro::TokenStream;
5use proc_macro2::TokenStream as TokenStream2;
6use quote::format_ident;
7use quote::{quote, quote_spanned};
8use std::collections::HashMap;
9use syn::ext::IdentExt;
10use syn::parenthesized;
11use syn::parse::Parse;
12use syn::punctuated::Punctuated;
13use syn::spanned::Spanned;
14use syn::Token;
15use syn::{parse_quote_spanned, Attribute, LitStr};
16
17pub struct PropsMacroInput {
18 wrapper_ty: syn::Path,
19 ext_trait: Option<Option<syn::Ident>>,
20 ident: syn::Ident,
21 props: Vec<PropDesc>,
22}
23
24pub struct PropertiesAttrs {
25 wrapper_ty: syn::Path,
26 ext_trait: Option<Option<syn::Ident>>,
30}
31
32impl 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
65impl 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
97enum MaybeCustomFn {
98 Custom(Box<syn::Expr>),
99 Default,
100}
101
102impl std::convert::From<Option<syn::Expr>> for MaybeCustomFn {
103 fn from(item: Option<syn::Expr>) -> Self {
104 match item {
105 Some(expr) => Self::Custom(Box::new(expr)),
106 None => Self::Default,
107 }
108 }
109}
110
111enum PropAttr {
112 Builder(Punctuated<syn::Expr, Token![,]>, TokenStream2),
116
117 Nullable,
119
120 Get(Option<syn::Expr>),
122 Set(Option<syn::Expr>),
123
124 OverrideClass(syn::Type),
126 OverrideInterface(syn::Type),
127
128 Type(syn::Type),
130
131 BuilderField((syn::Ident, Option<syn::Expr>)),
135
136 Member(syn::Ident),
138
139 Name(syn::LitStr),
141
142 Default,
144}
145
146impl Parse for PropAttr {
147 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
148 let name = input.call(syn::Ident::parse_any)?;
149 let name_str = name.to_string();
150
151 let res = if input.peek(Token![=]) {
152 let _assign_token: Token![=] = input.parse()?;
153 match &*name_str {
155 "name" => PropAttr::Name(input.parse()?),
156 "get" => PropAttr::Get(Some(input.parse()?)),
157 "set" => PropAttr::Set(Some(input.parse()?)),
158 "override_class" => PropAttr::OverrideClass(input.parse()?),
159 "override_interface" => PropAttr::OverrideInterface(input.parse()?),
160 "type" => PropAttr::Type(input.parse()?),
161 "member" => PropAttr::Member(input.parse()?),
162 "default" => PropAttr::BuilderField((
164 syn::Ident::new("default_value", name.span()),
165 Some(input.parse()?),
166 )),
167 _ => PropAttr::BuilderField((name, Some(input.parse()?))),
168 }
169 } else if input.peek(syn::token::Paren) {
170 match &*name_str {
171 "builder" => {
172 let content;
173 parenthesized!(content in input);
174 let required = content.parse_terminated(syn::Expr::parse, Token![,])?;
175 let rest: TokenStream2 = input.parse()?;
176 PropAttr::Builder(required, rest)
177 }
178 _ => {
179 return Err(syn::Error::new(
180 name.span(),
181 format!("Unsupported attribute list {name_str}(...)"),
182 ))
183 }
184 }
185 } else {
186 match &*name_str {
188 "nullable" => PropAttr::Nullable,
189 "get" => PropAttr::Get(None),
190 "set" => PropAttr::Set(None),
191 "readwrite" | "read_only" | "write_only" => {
192 return Err(syn::Error::new(
193 name.span(),
194 format!(
195 "{name} is a flag managed by the Properties macro. \
196 Use `get` and `set` to manage read and write access to a property",
197 ),
198 ))
199 }
200 "default" => PropAttr::Default,
201 _ => PropAttr::BuilderField((name, None)),
202 }
203 };
204 Ok(res)
205 }
206}
207
208#[derive(Default)]
209struct ReceivedAttrs {
210 nullable: bool,
211 get: Option<MaybeCustomFn>,
212 set: Option<MaybeCustomFn>,
213 override_class: Option<syn::Type>,
214 override_interface: Option<syn::Type>,
215 ty: Option<syn::Type>,
216 member: Option<syn::Ident>,
217 name: Option<syn::LitStr>,
218 builder: Option<(Punctuated<syn::Expr, Token![,]>, TokenStream2)>,
219 builder_fields: HashMap<syn::Ident, Option<syn::Expr>>,
220 use_default: bool,
221}
222
223impl Parse for ReceivedAttrs {
224 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
225 let attrs = syn::punctuated::Punctuated::<PropAttr, Token![,]>::parse_terminated(input)?;
226 let this = attrs.into_iter().fold(Self::default(), |mut this, attr| {
227 this.set_from_attr(attr);
228 this
229 });
230
231 Ok(this)
232 }
233}
234
235impl ReceivedAttrs {
236 fn set_from_attr(&mut self, attr: PropAttr) {
237 match attr {
238 PropAttr::Nullable => self.nullable = true,
239 PropAttr::Get(some_fn) => self.get = Some(some_fn.into()),
240 PropAttr::Set(some_fn) => self.set = Some(some_fn.into()),
241 PropAttr::Name(lit) => self.name = Some(lit),
242 PropAttr::OverrideClass(ty) => self.override_class = Some(ty),
243 PropAttr::OverrideInterface(ty) => self.override_interface = Some(ty),
244 PropAttr::Type(ty) => self.ty = Some(ty),
245 PropAttr::Member(member) => self.member = Some(member),
246 PropAttr::Builder(required_params, optionals) => {
247 self.builder = Some((required_params, optionals))
248 }
249 PropAttr::BuilderField((ident, expr)) => {
250 self.builder_fields.insert(ident, expr);
251 }
252 PropAttr::Default => {
253 self.use_default = true;
254 }
255 }
256 }
257}
258
259struct PropDesc {
262 attrs_span: proc_macro2::Span,
263 field_ident: syn::Ident,
264 ty: syn::Type,
265 name: syn::LitStr,
266 comments: Vec<Attribute>,
267 override_class: Option<syn::Type>,
268 override_interface: Option<syn::Type>,
269 nullable: bool,
270 get: Option<MaybeCustomFn>,
271 set: Option<MaybeCustomFn>,
272 member: Option<syn::Ident>,
273 builder: Option<(Punctuated<syn::Expr, Token![,]>, TokenStream2)>,
274 builder_fields: HashMap<syn::Ident, Option<syn::Expr>>,
275 is_construct_only: bool,
276 use_default: bool,
277}
278
279impl PropDesc {
280 fn new(
281 attrs_span: proc_macro2::Span,
282 field_ident: syn::Ident,
283 field_ty: syn::Type,
284 comments: Vec<Attribute>,
285 attrs: ReceivedAttrs,
286 ) -> syn::Result<Self> {
287 let ReceivedAttrs {
288 nullable,
289 get,
290 mut set,
291 override_class,
292 override_interface,
293 ty,
294 member,
295 name,
296 builder,
297 builder_fields,
298 use_default,
299 } = attrs;
300
301 let is_construct_only = builder_fields.iter().any(|(k, _)| *k == "construct_only");
302 if is_construct_only && set.is_none() {
303 set = Some(MaybeCustomFn::Default);
305 }
306
307 if get.is_none() && set.is_none() {
308 return Err(syn::Error::new(
309 attrs_span,
310 "No `get` or `set` specified: at least one is required.".to_string(),
311 ));
312 }
313
314 if override_class.is_some() && override_interface.is_some() {
315 return Err(syn::Error::new(
316 attrs_span,
317 "Both `override_class` and `override_interface` specified.".to_string(),
318 ));
319 }
320
321 let name = name.unwrap_or_else(|| {
323 syn::LitStr::new(
324 &field_ident.to_string().trim_matches('_').replace('_', "-"),
325 field_ident.span(),
326 )
327 });
328 let ty = ty.unwrap_or_else(|| field_ty.clone());
329
330 Ok(Self {
332 attrs_span,
333 field_ident,
334 ty,
335 name,
336 comments,
337 override_class,
338 override_interface,
339 nullable,
340 get,
341 set,
342 member,
343 builder,
344 builder_fields,
345 is_construct_only,
346 use_default,
347 })
348 }
349 fn is_overriding(&self) -> bool {
350 self.override_class.is_some() || self.override_interface.is_some()
351 }
352}
353
354fn expand_param_spec(prop: &PropDesc) -> TokenStream2 {
355 let crate_ident = crate_ident_new();
356 let PropDesc {
357 ty,
358 name,
359 builder,
360 use_default,
361 ..
362 } = prop;
363 let stripped_name = strip_raw_prefix_from_name(name);
364
365 match (&prop.override_class, &prop.override_interface) {
366 (Some(c), None) => {
367 return quote!(#crate_ident::ParamSpecOverride::for_class::<#c>(#stripped_name))
368 }
369 (None, Some(i)) => {
370 return quote!(#crate_ident::ParamSpecOverride::for_interface::<#i>(#stripped_name))
371 }
372 (Some(_), Some(_)) => {
373 unreachable!("Both `override_class` and `override_interface` specified")
374 }
375 (None, None) => (),
376 };
377
378 let rw_flags = match (&prop.get, &prop.set) {
379 (Some(_), Some(_)) => quote!(.readwrite()),
380 (Some(_), None) => quote!(.read_only()),
381 (None, Some(_)) => quote!(.write_only()),
382 (None, None) => unreachable!("No `get` or `set` specified"),
383 };
384
385 let builder_call = builder
386 .as_ref()
387 .cloned()
388 .map(|(mut required_params, chained_methods)| {
389 let name_expr = syn::ExprLit {
390 attrs: vec![],
391 lit: syn::Lit::Str(stripped_name.to_owned()),
392 };
393 required_params.insert(0, name_expr.into());
394 let required_params = required_params.iter();
395
396 quote!((#(#required_params,)*)#chained_methods)
397 })
398 .unwrap_or(quote!((#stripped_name)));
399
400 let builder_fields = prop.builder_fields.iter().map(|(k, v)| quote!(.#k(#v)));
401
402 let span = prop.attrs_span;
403
404 let (trait_name, fn_name) = if *use_default {
406 (
407 quote!(HasParamSpecDefaulted),
408 quote!(param_spec_builder_defaulted),
409 )
410 } else {
411 (quote!(HasParamSpec), quote!(param_spec_builder))
412 };
413
414 quote_spanned! {span=>
415 <<#ty as #crate_ident::property::Property>::Value as #crate_ident::#trait_name>
416 ::#fn_name() #builder_call
417 #rw_flags
418 #(#builder_fields)*
419 .build()
420 }
421}
422
423fn expand_properties_fn(props: &[PropDesc]) -> TokenStream2 {
424 let n_props = props.len();
425 let crate_ident = crate_ident_new();
426 let param_specs = props.iter().map(expand_param_spec);
427 quote!(
428 fn derived_properties() -> &'static [#crate_ident::ParamSpec] {
429 use #crate_ident::prelude::ParamSpecBuilderExt;
430 static PROPERTIES: ::std::sync::OnceLock<[#crate_ident::ParamSpec; #n_props]> = ::std::sync::OnceLock::new();
431 PROPERTIES.get_or_init(|| [
432 #(#param_specs,)*
433 ])
434 }
435 )
436}
437
438fn expand_property_fn(props: &[PropDesc]) -> TokenStream2 {
439 let crate_ident = crate_ident_new();
440 let match_branch_get = props.iter().flat_map(|p| {
441 let PropDesc {
442 name,
443 field_ident,
444 member,
445 get,
446 ty,
447 ..
448 } = p;
449
450 let enum_ident = name_to_enum_ident(name.value());
451 let span = p.attrs_span;
452 get.as_ref().map(|get| {
453 let body = match (member, get) {
454 (_, MaybeCustomFn::Custom(expr)) => quote!(
455 DerivedPropertiesEnum::#enum_ident => {
456 let value: <#ty as #crate_ident::property::Property>::Value = (#expr)(&self);
457 ::std::convert::From::from(value)
458 }
459 ),
460 (None, MaybeCustomFn::Default) => quote!(
461 DerivedPropertiesEnum::#enum_ident =>
462 #crate_ident::property::PropertyGet::get(&self.#field_ident, |v| ::std::convert::From::from(v))
463
464 ),
465 (Some(member), MaybeCustomFn::Default) => quote!(
466 DerivedPropertiesEnum::#enum_ident =>
467 #crate_ident::property::PropertyGet::get(&self.#field_ident, |v| ::std::convert::From::from(&v.#member))
468
469 ),
470 };
471 quote_spanned!(span=> #body)
472 })
473 });
474 quote!(
475 fn derived_property(
476 &self,
477 id: usize,
478 pspec: &#crate_ident::ParamSpec
479 ) -> #crate_ident::Value {
480 let prop: DerivedPropertiesEnum = std::convert::TryFrom::try_from(id-1)
481 .unwrap_or_else(|_| panic!("property not defined {}", pspec.name()));
482 match prop {
483 #(#match_branch_get,)*
484 _ => panic!("missing getter for property {}", pspec.name()),
485 }
486 }
487 )
488}
489
490fn expand_set_property_fn(props: &[PropDesc]) -> TokenStream2 {
491 let crate_ident = crate_ident_new();
492 let match_branch_set = props.iter().flat_map(|p| {
493 let PropDesc {
494 name,
495 field_ident,
496 member,
497 set,
498 ty,
499 ..
500 } = p;
501 let stripped_name = strip_raw_prefix_from_name(name);
502 let crate_ident = crate_ident_new();
503 let enum_ident = name_to_enum_ident(name.value());
504 let span = p.attrs_span;
505 let expect = quote!(.unwrap_or_else(
506 |err| panic!(
507 "Invalid conversion from `glib::value::Value` to `{}` inside setter for property `{}`: {:?}",
508 ::std::any::type_name::<<#ty as #crate_ident::property::Property>::Value>(), #stripped_name, err
509 )
510 ));
511 set.as_ref().map(|set| {
512 let body = match (member, set) {
513 (_, MaybeCustomFn::Custom(expr)) => quote!(
514 DerivedPropertiesEnum::#enum_ident => {
515 (#expr)(&self, #crate_ident::Value::get(value)#expect);
516 }
517 ),
518 (None, MaybeCustomFn::Default) => quote!(
519 DerivedPropertiesEnum::#enum_ident => {
520 #crate_ident::property::PropertySet::set(
521 &self.#field_ident,
522 #crate_ident::Value::get(value)#expect
523 );
524 }
525 ),
526 (Some(member), MaybeCustomFn::Default) => quote!(
527 DerivedPropertiesEnum::#enum_ident => {
528 #crate_ident::property::PropertySetNested::set_nested(
529 &self.#field_ident,
530 move |v| v.#member = #crate_ident::Value::get(value)#expect
531 );
532 }
533 ),
534 };
535 quote_spanned!(span=> #body)
536 })
537 });
538 quote!(
539 #[allow(unreachable_code)]
540 fn derived_set_property(&self,
541 id: usize,
542 value: &#crate_ident::Value,
543 pspec: &#crate_ident::ParamSpec
544 ){
545 let prop: DerivedPropertiesEnum = std::convert::TryFrom::try_from(id-1)
546 .unwrap_or_else(|_| panic!("property not defined {}", pspec.name()));
547 match prop {
548 #(#match_branch_set,)*
549 _ => panic!("missing setter for property {}", pspec.name()),
550 }
551 }
552 )
553}
554
555fn parse_fields(fields: syn::Fields) -> syn::Result<Vec<PropDesc>> {
556 let mut properties = vec![];
557
558 for field in fields.into_iter() {
559 let syn::Field {
560 ident, attrs, ty, ..
561 } = field;
562 let mut comments: Vec<Attribute> = vec![];
564 for prop_attr in attrs.iter() {
565 if prop_attr.path().is_ident("doc") {
566 comments.push(prop_attr.clone());
567 } else if prop_attr.path().is_ident("property") {
568 let span = prop_attr.span();
569 let existing_comments = comments;
570 comments = vec![];
571 properties.push(PropDesc::new(
572 span,
573 ident.as_ref().unwrap().clone(),
574 ty.clone(),
575 existing_comments,
576 prop_attr.parse_args()?,
577 )?);
578 }
579 }
580 }
581
582 Ok(properties)
583}
584
585fn name_to_ident(name: &syn::LitStr) -> syn::Ident {
587 format_ident!("{}", name.value().replace('-', "_"))
588}
589
590fn strip_raw_prefix_from_name(name: &LitStr) -> LitStr {
592 LitStr::new(
593 name.value().strip_prefix("r#").unwrap_or(&name.value()),
594 name.span(),
595 )
596}
597
598fn arrange_property_comments(comments: &[Attribute]) -> (Vec<&Attribute>, Vec<&Attribute>) {
603 let mut untagged = vec![];
604 let mut getter = vec![];
605 let mut setter = vec![];
606 let mut saw_section = false;
607
608 let mut current_section = &mut untagged;
610 for attr in comments {
611 if let syn::Meta::NameValue(meta) = &attr.meta {
612 if let syn::Expr::Lit(expr) = &meta.value {
613 if let syn::Lit::Str(lit_str) = &expr.lit {
614 match lit_str.value().trim() {
619 "# Getter" => {
620 current_section = &mut getter;
621 saw_section = true;
622 }
623 "# Setter" => {
624 current_section = &mut setter;
625 saw_section = true;
626 }
627 _ => current_section.push(attr),
628 }
629 }
630 }
631 }
632 }
633
634 if !saw_section {
636 return (untagged.clone(), untagged);
637 }
638
639 (getter, setter)
640}
641
642fn expand_impl_getset_properties(props: &[PropDesc]) -> Vec<syn::ImplItemFn> {
643 let crate_ident = crate_ident_new();
644 let defs = props.iter().filter(|p| !p.is_overriding()).map(|p| {
645 let name = &p.name;
646 let stripped_name = strip_raw_prefix_from_name(name);
647 let ident = name_to_ident(name);
648 let ty = &p.ty;
649
650 let (getter_docs, setter_docs) = arrange_property_comments(&p.comments);
651
652 let getter = p.get.is_some().then(|| {
653 let span = p.attrs_span;
654 parse_quote_spanned!(span=>
655 #(#getter_docs)*
656 #[must_use]
657 #[allow(dead_code)]
658 pub fn #ident(&self) -> <#ty as #crate_ident::property::Property>::Value {
659 self.property::<<#ty as #crate_ident::property::Property>::Value>(#stripped_name)
660 }
661 )
662 });
663
664 let setter = (p.set.is_some() && !p.is_construct_only).then(|| {
665 let ident = format_ident!("set_{}", ident);
666 let target_ty = quote!(<<#ty as #crate_ident::property::Property>::Value as #crate_ident::prelude::HasParamSpec>::SetValue);
667 let set_ty = if p.nullable {
668 quote!(::core::option::Option<impl std::borrow::Borrow<#target_ty>>)
669 } else {
670 quote!(impl std::borrow::Borrow<#target_ty>)
671 };
672 let upcasted_borrowed_value = if p.nullable {
673 quote!(
674 value.as_ref().map(|v| std::borrow::Borrow::borrow(v))
675 )
676 } else {
677 quote!(
678 std::borrow::Borrow::borrow(&value)
679 )
680 };
681 let span = p.attrs_span;
682 parse_quote_spanned!(span=>
683 #(#setter_docs)*
684 #[allow(dead_code)]
685 pub fn #ident<'a>(&self, value: #set_ty) {
686 self.set_property_from_value(#stripped_name, &::std::convert::From::from(#upcasted_borrowed_value))
687 }
688 )
689 });
690
691 [getter, setter]
692 });
693 defs.flatten() .flatten() .collect::<Vec<_>>()
696}
697
698fn expand_impl_connect_prop_notify(props: &[PropDesc]) -> Vec<syn::ImplItemFn> {
699 let crate_ident = crate_ident_new();
700 let connection_fns = props.iter().filter(|p| !p.is_overriding()).map(|p| -> syn::ImplItemFn {
701 let name = &p.name;
702 let stripped_name = strip_raw_prefix_from_name(name);
703 let fn_ident = format_ident!("connect_{}_notify", name_to_ident(name));
704 let span = p.attrs_span;
705 let doc = format!("Listen for notifications of a change in the `{}` property", name.value());
706 parse_quote_spanned!(span=>
707 #[doc = #doc]
708 #[allow(dead_code)]
709 pub fn #fn_ident<F: Fn(&Self) + 'static>(&self, f: F) -> #crate_ident::SignalHandlerId {
710 self.connect_notify_local(::core::option::Option::Some(#stripped_name), move |this, _| {
711 f(this)
712 })
713 }
714 )
715 });
716 connection_fns.collect::<Vec<_>>()
717}
718
719fn expand_impl_notify_prop(wrapper_type: &syn::Path, props: &[PropDesc]) -> Vec<syn::ImplItemFn> {
720 let crate_ident = crate_ident_new();
721 let emit_fns = props.iter().filter(|p| !p.is_overriding()).map(|p| -> syn::ImplItemFn {
722 let name = strip_raw_prefix_from_name(&p.name);
723 let fn_ident = format_ident!("notify_{}", name_to_ident(&name));
724 let span = p.attrs_span;
725 let enum_ident = name_to_enum_ident(name.value());
726 let doc = format!("Notify listeners of a change in the `{}` property", name.value());
727 parse_quote_spanned!(span=>
728 #[doc = #doc]
729 #[allow(dead_code)]
730 pub fn #fn_ident(&self) {
731 self.notify_by_pspec(
732 &<<#wrapper_type as #crate_ident::object::ObjectSubclassIs>::Subclass
733 as #crate_ident::subclass::object::DerivedObjectProperties>::derived_properties()
734 [DerivedPropertiesEnum::#enum_ident as usize]
735 );
736 }
737 )
738 });
739 emit_fns.collect::<Vec<_>>()
740}
741
742fn name_to_enum_ident(name: String) -> syn::Ident {
743 let mut name = name.strip_prefix("r#").unwrap_or(&name).to_owned();
744 let mut slice = name.as_mut_str();
745 while let Some(i) = slice.find('-') {
746 let (head, tail) = slice.split_at_mut(i);
747 if let Some(c) = head.get_mut(0..1) {
748 c.make_ascii_uppercase();
749 }
750 slice = &mut tail[1..];
751 }
752 if let Some(c) = slice.get_mut(0..1) {
753 c.make_ascii_uppercase();
754 }
755 let enum_member: String = name.split('-').collect();
756 format_ident!("{}", enum_member)
757}
758
759fn expand_properties_enum(props: &[PropDesc]) -> TokenStream2 {
760 if props.is_empty() {
761 quote! {
762 #[derive(Debug, Copy, Clone)]
763 enum DerivedPropertiesEnum {}
764 impl std::convert::TryFrom<usize> for DerivedPropertiesEnum {
765 type Error = usize;
766
767 fn try_from(item: usize) -> ::core::result::Result<Self, <Self as std::convert::TryFrom<usize>>::Error> {
768 ::core::result::Result::Err(item)
769 }
770 }
771 }
772 } else {
773 let properties: Vec<syn::Ident> = props
774 .iter()
775 .map(|p| {
776 let name: String = p.name.value();
777
778 name_to_enum_ident(name)
779 })
780 .collect();
781 let props = properties.iter();
782 let indices = 0..properties.len();
783 quote! {
784 #[repr(usize)]
785 #[derive(Debug, Copy, Clone)]
786 enum DerivedPropertiesEnum {
787 #(#props,)*
788 }
789 impl std::convert::TryFrom<usize> for DerivedPropertiesEnum {
790 type Error = usize;
791
792 fn try_from(item: usize) -> ::core::result::Result<Self, <Self as std::convert::TryFrom<usize>>::Error> {
793 match item {
794 #(#indices => ::core::result::Result::Ok(Self::#properties),)*
795 _ => ::core::result::Result::Err(item)
796 }
797 }
798 }
799 }
800 }
801}
802
803pub fn impl_derive_props(input: PropsMacroInput) -> TokenStream {
804 let struct_ident = &input.ident;
805 let crate_ident = crate_ident_new();
806 let wrapper_type = input.wrapper_ty;
807 let fn_properties = expand_properties_fn(&input.props);
808 let fn_property = expand_property_fn(&input.props);
809 let fn_set_property = expand_set_property_fn(&input.props);
810 let getset_properties = expand_impl_getset_properties(&input.props);
811 let connect_prop_notify = expand_impl_connect_prop_notify(&input.props);
812 let notify_prop = expand_impl_notify_prop(&wrapper_type, &input.props);
813 let properties_enum = expand_properties_enum(&input.props);
814
815 let rust_interface = if let Some(ext_trait) = input.ext_trait {
816 let trait_ident = if let Some(ext_trait) = ext_trait {
817 ext_trait
818 } else {
819 format_ident!(
820 "{}PropertiesExt",
821 wrapper_type.segments.last().unwrap().ident
822 )
823 };
824 let fns_without_visibility_modifier = getset_properties
825 .into_iter()
826 .chain(connect_prop_notify)
827 .chain(notify_prop)
828 .map(|mut item| {
829 item.vis = syn::Visibility::Inherited;
830 item
831 });
832 quote! {
833 pub trait #trait_ident: #crate_ident::prelude::IsA<#wrapper_type> {
834 #(#fns_without_visibility_modifier)*
835 }
836 impl<T: #crate_ident::prelude::IsA<#wrapper_type>> #trait_ident for T {}
837 }
838 } else {
839 quote! {
840 #[allow(dead_code)]
841 impl #wrapper_type {
842 #(#getset_properties)*
843 #(#connect_prop_notify)*
844 #(#notify_prop)*
845 }
846 }
847 };
848
849 let expanded = quote! {
850 #properties_enum
851
852 impl #crate_ident::subclass::object::DerivedObjectProperties for #struct_ident {
853 #fn_properties
854 #fn_property
855 #fn_set_property
856 }
857
858 #rust_interface
859 };
860 proc_macro::TokenStream::from(expanded)
861}