| 1 | use proc_macro2::{Span, TokenStream}; |
| 2 | use quote::{quote, ToTokens}; |
| 3 | use syn::{ |
| 4 | spanned::Spanned, Attribute, Data, DataEnum, DeriveInput, Error, Expr, Fields, Generics, Ident, |
| 5 | Lifetime, LifetimeParam, |
| 6 | }; |
| 7 | |
| 8 | use crate::utils::*; |
| 9 | |
| 10 | pub enum ValueType { |
| 11 | Value, |
| 12 | OwnedValue, |
| 13 | } |
| 14 | |
| 15 | pub fn expand_derive(ast: DeriveInput, value_type: ValueType) -> Result<TokenStream, Error> { |
| 16 | let zv = zvariant_path(); |
| 17 | |
| 18 | match &ast.data { |
| 19 | Data::Struct(ds) => match &ds.fields { |
| 20 | Fields::Named(_) | Fields::Unnamed(_) => { |
| 21 | let StructAttributes { signature, .. } = StructAttributes::parse(&ast.attrs)?; |
| 22 | let signature = signature.map(|signature| match signature.as_str() { |
| 23 | "dict" => "a{sv}" .to_string(), |
| 24 | _ => signature, |
| 25 | }); |
| 26 | |
| 27 | impl_struct( |
| 28 | value_type, |
| 29 | ast.ident, |
| 30 | ast.generics, |
| 31 | &ds.fields, |
| 32 | signature, |
| 33 | &zv, |
| 34 | ) |
| 35 | } |
| 36 | Fields::Unit => Err(Error::new(ast.span(), "Unit structures not supported" )), |
| 37 | }, |
| 38 | Data::Enum(data) => impl_enum(value_type, ast.ident, ast.generics, ast.attrs, data, &zv), |
| 39 | _ => Err(Error::new( |
| 40 | ast.span(), |
| 41 | "only structs and enums are supported" , |
| 42 | )), |
| 43 | } |
| 44 | } |
| 45 | |
| 46 | fn impl_struct( |
| 47 | value_type: ValueType, |
| 48 | name: Ident, |
| 49 | generics: Generics, |
| 50 | fields: &Fields, |
| 51 | signature: Option<String>, |
| 52 | zv: &TokenStream, |
| 53 | ) -> Result<TokenStream, Error> { |
| 54 | let statc_lifetime = LifetimeParam::new(Lifetime::new("'static" , Span::call_site())); |
| 55 | let ( |
| 56 | value_type, |
| 57 | value_lifetime, |
| 58 | into_value_trait, |
| 59 | into_value_method, |
| 60 | into_value_error_decl, |
| 61 | into_value_ret, |
| 62 | into_value_error_transform, |
| 63 | ) = match value_type { |
| 64 | ValueType::Value => { |
| 65 | let mut lifetimes = generics.lifetimes(); |
| 66 | let value_lifetime = lifetimes |
| 67 | .next() |
| 68 | .cloned() |
| 69 | .unwrap_or_else(|| statc_lifetime.clone()); |
| 70 | if lifetimes.next().is_some() { |
| 71 | return Err(Error::new( |
| 72 | name.span(), |
| 73 | "Type with more than 1 lifetime not supported" , |
| 74 | )); |
| 75 | } |
| 76 | |
| 77 | ( |
| 78 | quote! { #zv::Value<#value_lifetime> }, |
| 79 | value_lifetime, |
| 80 | quote! { From }, |
| 81 | quote! { from }, |
| 82 | quote! {}, |
| 83 | quote! { Self }, |
| 84 | quote! {}, |
| 85 | ) |
| 86 | } |
| 87 | ValueType::OwnedValue => ( |
| 88 | quote! { #zv::OwnedValue }, |
| 89 | statc_lifetime, |
| 90 | quote! { TryFrom }, |
| 91 | quote! { try_from }, |
| 92 | quote! { type Error = #zv::Error; }, |
| 93 | quote! { #zv::Result<Self> }, |
| 94 | quote! { .map_err(::std::convert::Into::into) }, |
| 95 | ), |
| 96 | }; |
| 97 | |
| 98 | let type_params = generics.type_params().cloned().collect::<Vec<_>>(); |
| 99 | let (from_value_where_clause, into_value_where_clause) = if !type_params.is_empty() { |
| 100 | ( |
| 101 | Some(quote! { |
| 102 | where |
| 103 | #( |
| 104 | #type_params: ::std::convert::TryFrom<#zv::Value<#value_lifetime>> + #zv::Type, |
| 105 | <#type_params as ::std::convert::TryFrom<#zv::Value<#value_lifetime>>>::Error: ::std::convert::Into<#zv::Error> |
| 106 | ),* |
| 107 | }), |
| 108 | Some(quote! { |
| 109 | where |
| 110 | #( |
| 111 | #type_params: ::std::convert::Into<#zv::Value<#value_lifetime>> + #zv::Type |
| 112 | ),* |
| 113 | }), |
| 114 | ) |
| 115 | } else { |
| 116 | (None, None) |
| 117 | }; |
| 118 | let (impl_generics, ty_generics, _) = generics.split_for_impl(); |
| 119 | match fields { |
| 120 | Fields::Named(_) => { |
| 121 | let field_names: Vec<_> = fields |
| 122 | .iter() |
| 123 | .map(|field| field.ident.to_token_stream()) |
| 124 | .collect(); |
| 125 | let (from_value_impl, into_value_impl) = match signature { |
| 126 | Some(signature) if signature == "a{sv}" => ( |
| 127 | // User wants the type to be encoded as a dict. |
| 128 | // FIXME: Not the most efficient implementation. |
| 129 | quote! { |
| 130 | let mut fields = <::std::collections::HashMap::<::std::string::String, #zv::Value>>::try_from(value)?; |
| 131 | |
| 132 | ::std::result::Result::Ok(Self { |
| 133 | #( |
| 134 | #field_names: |
| 135 | fields |
| 136 | .remove(stringify!(#field_names)) |
| 137 | .ok_or_else(|| #zv::Error::IncorrectType)? |
| 138 | .downcast()? |
| 139 | ),* |
| 140 | }) |
| 141 | }, |
| 142 | quote! { |
| 143 | let mut fields = ::std::collections::HashMap::new(); |
| 144 | #( |
| 145 | fields.insert(stringify!(#field_names), #zv::Value::from(s.#field_names)); |
| 146 | )* |
| 147 | |
| 148 | <#value_type>::#into_value_method(#zv::Value::from(fields)) |
| 149 | #into_value_error_transform |
| 150 | }, |
| 151 | ), |
| 152 | Some(_) | None => ( |
| 153 | quote! { |
| 154 | let mut fields = #zv::Structure::try_from(value)?.into_fields(); |
| 155 | |
| 156 | ::std::result::Result::Ok(Self { |
| 157 | #( |
| 158 | #field_names: fields.remove(0).downcast()? |
| 159 | ),* |
| 160 | }) |
| 161 | }, |
| 162 | quote! { |
| 163 | <#value_type>::#into_value_method(#zv::StructureBuilder::new() |
| 164 | #( |
| 165 | .add_field(s.#field_names) |
| 166 | )* |
| 167 | .build()) |
| 168 | #into_value_error_transform |
| 169 | }, |
| 170 | ), |
| 171 | }; |
| 172 | Ok(quote! { |
| 173 | impl #impl_generics ::std::convert::TryFrom<#value_type> for #name #ty_generics |
| 174 | #from_value_where_clause |
| 175 | { |
| 176 | type Error = #zv::Error; |
| 177 | |
| 178 | #[inline] |
| 179 | fn try_from(value: #value_type) -> #zv::Result<Self> { |
| 180 | #from_value_impl |
| 181 | } |
| 182 | } |
| 183 | |
| 184 | impl #impl_generics #into_value_trait<#name #ty_generics> for #value_type |
| 185 | #into_value_where_clause |
| 186 | { |
| 187 | #into_value_error_decl |
| 188 | |
| 189 | #[inline] |
| 190 | fn #into_value_method(s: #name #ty_generics) -> #into_value_ret { |
| 191 | #into_value_impl |
| 192 | } |
| 193 | } |
| 194 | }) |
| 195 | } |
| 196 | Fields::Unnamed(_) if fields.iter().next().is_some() => { |
| 197 | // Newtype struct. |
| 198 | Ok(quote! { |
| 199 | impl #impl_generics ::std::convert::TryFrom<#value_type> for #name #ty_generics |
| 200 | #from_value_where_clause |
| 201 | { |
| 202 | type Error = #zv::Error; |
| 203 | |
| 204 | #[inline] |
| 205 | fn try_from(value: #value_type) -> #zv::Result<Self> { |
| 206 | ::std::convert::TryInto::try_into(value).map(Self) |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | impl #impl_generics #into_value_trait<#name #ty_generics> for #value_type |
| 211 | #into_value_where_clause |
| 212 | { |
| 213 | #into_value_error_decl |
| 214 | |
| 215 | #[inline] |
| 216 | fn #into_value_method(s: #name #ty_generics) -> #into_value_ret { |
| 217 | <#value_type>::#into_value_method(s.0) #into_value_error_transform |
| 218 | } |
| 219 | } |
| 220 | }) |
| 221 | } |
| 222 | Fields::Unnamed(_) => panic!("impl_struct must not be called for tuples" ), |
| 223 | Fields::Unit => panic!("impl_struct must not be called for unit structures" ), |
| 224 | } |
| 225 | } |
| 226 | |
| 227 | fn impl_enum( |
| 228 | value_type: ValueType, |
| 229 | name: Ident, |
| 230 | _generics: Generics, |
| 231 | attrs: Vec<Attribute>, |
| 232 | data: &DataEnum, |
| 233 | zv: &TokenStream, |
| 234 | ) -> Result<TokenStream, Error> { |
| 235 | let repr: TokenStream = match attrs.iter().find(|attr| attr.path().is_ident("repr" )) { |
| 236 | Some(repr_attr) => repr_attr.parse_args()?, |
| 237 | None => quote! { u32 }, |
| 238 | }; |
| 239 | |
| 240 | let mut variant_names = vec![]; |
| 241 | let mut variant_values = vec![]; |
| 242 | for variant in &data.variants { |
| 243 | // Ensure all variants of the enum are unit type |
| 244 | match variant.fields { |
| 245 | Fields::Unit => { |
| 246 | variant_names.push(&variant.ident); |
| 247 | let value = match &variant |
| 248 | .discriminant |
| 249 | .as_ref() |
| 250 | .ok_or_else(|| Error::new(variant.span(), "expected `Name = Value` variants" ))? |
| 251 | .1 |
| 252 | { |
| 253 | Expr::Lit(lit_exp) => &lit_exp.lit, |
| 254 | _ => { |
| 255 | return Err(Error::new( |
| 256 | variant.span(), |
| 257 | "expected `Name = Value` variants" , |
| 258 | )) |
| 259 | } |
| 260 | }; |
| 261 | variant_values.push(value); |
| 262 | } |
| 263 | _ => return Err(Error::new(variant.span(), "must be a unit variant" )), |
| 264 | } |
| 265 | } |
| 266 | |
| 267 | let (value_type, into_value) = match value_type { |
| 268 | ValueType::Value => ( |
| 269 | quote! { #zv::Value<'_> }, |
| 270 | quote! { |
| 271 | impl ::std::convert::From<#name> for #zv::Value<'_> { |
| 272 | #[inline] |
| 273 | fn from(e: #name) -> Self { |
| 274 | let u: #repr = match e { |
| 275 | #( |
| 276 | #name::#variant_names => #variant_values |
| 277 | ),* |
| 278 | }; |
| 279 | |
| 280 | <#zv::Value as ::std::convert::From<_>>::from(u).into() |
| 281 | } |
| 282 | } |
| 283 | }, |
| 284 | ), |
| 285 | ValueType::OwnedValue => ( |
| 286 | quote! { #zv::OwnedValue }, |
| 287 | quote! { |
| 288 | impl ::std::convert::TryFrom<#name> for #zv::OwnedValue { |
| 289 | type Error = #zv::Error; |
| 290 | |
| 291 | #[inline] |
| 292 | fn try_from(e: #name) -> #zv::Result<Self> { |
| 293 | let u: #repr = match e { |
| 294 | #( |
| 295 | #name::#variant_names => #variant_values |
| 296 | ),* |
| 297 | }; |
| 298 | |
| 299 | <#zv::OwnedValue as ::std::convert::TryFrom<_>>::try_from( |
| 300 | <#zv::Value as ::std::convert::From<_>>::from(u) |
| 301 | ) |
| 302 | } |
| 303 | } |
| 304 | }, |
| 305 | ), |
| 306 | }; |
| 307 | |
| 308 | Ok(quote! { |
| 309 | impl ::std::convert::TryFrom<#value_type> for #name { |
| 310 | type Error = #zv::Error; |
| 311 | |
| 312 | #[inline] |
| 313 | fn try_from(value: #value_type) -> #zv::Result<Self> { |
| 314 | let v: #repr = ::std::convert::TryInto::try_into(value)?; |
| 315 | |
| 316 | ::std::result::Result::Ok(match v { |
| 317 | #( |
| 318 | #variant_values => #name::#variant_names |
| 319 | ),*, |
| 320 | _ => return ::std::result::Result::Err(#zv::Error::IncorrectType), |
| 321 | }) |
| 322 | } |
| 323 | } |
| 324 | |
| 325 | #into_value |
| 326 | }) |
| 327 | } |
| 328 | |