| 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::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 | } |
| 201 | |