glib_macros/
value_delegate_derive.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use quote::quote;
4use syn::{parse::Parse, Token};
5
6use crate::utils::crate_ident_new;
7
8#[derive(Default, Debug, Clone)]
9enum DeriveMode {
10    From,
11    #[default]
12    Private,
13}
14
15pub struct ValueDelegateInput {
16    delegated_ty: syn::Path,
17    ident: syn::Ident,
18    mode: DeriveMode,
19    nullable: bool,
20}
21
22enum Arg {
23    FromPath(syn::Path),
24    Nullable,
25}
26
27impl Parse for Arg {
28    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
29        let argname: syn::Ident = input.parse()?;
30        if argname == "nullable" {
31            Ok(Arg::Nullable)
32        } else if argname == "from" {
33            let _eq: Token![=] = input.parse()?;
34            Ok(Arg::FromPath(input.parse()?))
35        } else {
36            Err(syn::Error::new(
37                input.span(),
38                "expected `nullable` or `from`",
39            ))
40        }
41    }
42}
43
44#[derive(Default)]
45struct Args {
46    nullable: bool,
47    from_path: Option<syn::Path>,
48}
49
50impl Parse for Args {
51    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
52        let args = syn::punctuated::Punctuated::<Arg, Token![,]>::parse_terminated(input)?;
53        let mut this = Args::default();
54        for a in args {
55            match a {
56                Arg::FromPath(p) => this.from_path = Some(p),
57                Arg::Nullable => this.nullable = true,
58            }
59        }
60        Ok(this)
61    }
62}
63
64impl Parse for ValueDelegateInput {
65    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
66        let derive_input: syn::DeriveInput = input.parse()?;
67        let args: Option<Args> = if let Some(attr) = derive_input
68            .attrs
69            .iter()
70            .find(|x| x.path().is_ident("value_delegate"))
71        {
72            let args: Args = attr.parse_args()?;
73            Some(args)
74        } else {
75            None
76        };
77
78        let (delegated_ty, mode) =
79            if let Some(path) = args.as_ref().and_then(|a| a.from_path.as_ref()) {
80                (Some(path.clone()), DeriveMode::From)
81            } else {
82                let path = match derive_input.data {
83                    syn::Data::Struct(s) => match s.fields {
84                        syn::Fields::Unnamed(fields) if fields.unnamed.iter().count() == 1 => {
85                            fields.unnamed.into_iter().next().and_then(|x| match x.ty {
86                                syn::Type::Path(p) => Some(p.path),
87                                _ => None,
88                            })
89                        }
90                        _ => None,
91                    },
92                    _ => None,
93                };
94                (path, DeriveMode::Private)
95            };
96        let delegated_ty = delegated_ty.ok_or_else(|| {
97            syn::Error::new(
98                derive_input.ident.span(),
99                "Unless `derive(ValueDelegate)` is used over a newtype with 1 field, \
100                the delegated type must be specified using \
101                #[value_delegate(from = chosen_type)]",
102            )
103        })?;
104
105        Ok(ValueDelegateInput {
106            delegated_ty,
107            ident: derive_input.ident,
108            mode,
109            nullable: args.map(|a| a.nullable).unwrap_or(false),
110        })
111    }
112}
113
114pub fn impl_value_delegate(input: ValueDelegateInput) -> syn::Result<proc_macro::TokenStream> {
115    let ValueDelegateInput {
116        delegated_ty,
117        ident,
118        mode,
119        nullable,
120        ..
121    } = &input;
122    let crate_ident = crate_ident_new();
123
124    // this must be called in a context where `this` is defined.
125    let delegate_value = match mode {
126        DeriveMode::From => {
127            quote!(<#delegated_ty as std::convert::From<_>>::from(this))
128        }
129        DeriveMode::Private => quote!(this.0),
130    };
131
132    let to_value_optional = nullable.then(|| {
133        quote! {
134            impl #crate_ident::value::ToValueOptional for #ident {
135                fn to_value_optional(s: ::core::option::Option<&Self>) -> #crate_ident::value::Value {
136                    if let ::core::option::Option::Some(this) = s {
137                        #crate_ident::value::ToValue::to_value(&::core::option::Option::Some(&#delegate_value))
138                    } else {
139                        #crate_ident::value::ToValueOptional::to_value_optional(::core::option::Option::None::<&#delegated_ty>)
140                    }
141                }
142            }
143        }
144    });
145
146    let from_value = match mode {
147        DeriveMode::From => {
148            quote!(#ident::from(<#delegated_ty as #crate_ident::value::FromValue<'a>>::from_value(value)))
149        }
150        DeriveMode::Private => {
151            quote!(#ident(<#delegated_ty as #crate_ident::value::FromValue<'a>>::from_value(value)))
152        }
153    };
154
155    let res = quote! {
156        impl #crate_ident::prelude::StaticType for #ident {
157            fn static_type() -> glib::types::Type {
158                <#delegated_ty as #crate_ident::prelude::StaticType>::static_type()
159            }
160        }
161
162        impl #crate_ident::value::ToValue for #ident {
163            fn to_value(&self) -> #crate_ident::value::Value {
164                let this = self;
165                #crate_ident::value::ToValue::to_value(&#delegate_value)
166            }
167            fn value_type(&self) -> #crate_ident::types::Type {
168                let this = self;
169                #crate_ident::value::ToValue::value_type(&#delegate_value)
170            }
171        }
172
173        impl From<#ident> for #crate_ident::value::Value {
174            fn from(this: #ident) -> Self {
175                #crate_ident::value::Value::from(#delegate_value)
176            }
177        }
178
179        #to_value_optional
180
181        unsafe impl<'a> #crate_ident::value::FromValue<'a> for #ident {
182            type Checker = <#delegated_ty as #crate_ident::value::FromValue<'a>>::Checker;
183
184            unsafe fn from_value(value: &'a #crate_ident::value::Value) -> Self {
185                #from_value
186            }
187        }
188
189        impl #crate_ident::HasParamSpec for #ident {
190            type ParamSpec = <#delegated_ty as #crate_ident::HasParamSpec>::ParamSpec;
191            type SetValue = Self;
192            type BuilderFn = <#delegated_ty as #crate_ident::HasParamSpec>::BuilderFn;
193
194            fn param_spec_builder() -> Self::BuilderFn {
195                <#delegated_ty as #crate_ident::prelude::HasParamSpec>::param_spec_builder()
196            }
197        }
198    };
199    Ok(res.into())
200}