1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | |
3 | use quote::quote; |
4 | use syn::{parse::Parse, Token}; |
5 | |
6 | use crate::utils::crate_ident_new; |
7 | |
8 | #[derive (Default, Debug, Clone)] |
9 | enum DeriveMode { |
10 | From, |
11 | #[default] |
12 | Private, |
13 | } |
14 | |
15 | pub struct ValueDelegateInput { |
16 | delegated_ty: syn::Path, |
17 | ident: syn::Ident, |
18 | mode: DeriveMode, |
19 | nullable: bool, |
20 | } |
21 | |
22 | enum Arg { |
23 | FromPath(syn::Path), |
24 | Nullable, |
25 | } |
26 | |
27 | impl 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)] |
45 | struct Args { |
46 | nullable: bool, |
47 | from_path: Option<syn::Path>, |
48 | } |
49 | |
50 | impl 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 | |
64 | impl 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 | |
114 | pub 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 | |