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 message:"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: Punctuated = syn::punctuated::Punctuated::<Arg, Token![,]>::parse_terminated(input)?;
53 let mut this: Args = Args::default();
54 for a: Arg in args {
55 match a {
56 Arg::FromPath(p: Path) => 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::types::StaticType for #ident {
157 fn static_type() -> glib::types::Type {
158 <#delegated_ty as #crate_ident::types::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::HasParamSpec>::param_spec_builder()
196 }
197 }
198 };
199 Ok(res.into())
200}
201