animate.rs (4621B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 5 use crate::cg; 6 use darling::util::PathList; 7 use proc_macro2::TokenStream; 8 use quote::TokenStreamExt; 9 use syn::{DeriveInput, WhereClause}; 10 use synstructure::{Structure, VariantInfo}; 11 12 pub fn derive(mut input: DeriveInput) -> TokenStream { 13 let animation_input_attrs = cg::parse_input_attrs::<AnimationInputAttrs>(&input); 14 15 let no_bound = animation_input_attrs.no_bound.unwrap_or_default(); 16 let mut where_clause = input.generics.where_clause.take(); 17 for param in input.generics.type_params() { 18 if !no_bound.iter().any(|name| name.is_ident(¶m.ident)) { 19 cg::add_predicate( 20 &mut where_clause, 21 parse_quote!(#param: crate::values::animated::Animate), 22 ); 23 } 24 } 25 let (mut match_body, needs_catchall_branch) = { 26 let s = Structure::new(&input); 27 let needs_catchall_branch = s.variants().len() > 1; 28 let match_body = s.variants().iter().fold(quote!(), |body, variant| { 29 let arm = derive_variant_arm(variant, &mut where_clause); 30 quote! { #body #arm } 31 }); 32 (match_body, needs_catchall_branch) 33 }; 34 35 input.generics.where_clause = where_clause; 36 37 if needs_catchall_branch { 38 // This ideally shouldn't be needed, but see 39 // https://github.com/rust-lang/rust/issues/68867 40 match_body.append_all(quote! { _ => unsafe { 41 use ::debug_unreachable::debug_unreachable; 42 debug_unreachable!() 43 } }); 44 } 45 46 let name = &input.ident; 47 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); 48 49 quote! { 50 impl #impl_generics crate::values::animated::Animate for #name #ty_generics #where_clause { 51 #[allow(unused_variables, unused_imports)] 52 #[inline] 53 fn animate( 54 &self, 55 other: &Self, 56 procedure: crate::values::animated::Procedure, 57 ) -> Result<Self, ()> { 58 if std::mem::discriminant(self) != std::mem::discriminant(other) { 59 return Err(()); 60 } 61 match (self, other) { 62 #match_body 63 } 64 } 65 } 66 } 67 } 68 69 fn derive_variant_arm( 70 variant: &VariantInfo, 71 where_clause: &mut Option<WhereClause>, 72 ) -> TokenStream { 73 let variant_attrs = cg::parse_variant_attrs_from_ast::<AnimationVariantAttrs>(&variant.ast()); 74 let (this_pattern, this_info) = cg::ref_pattern(&variant, "this"); 75 let (other_pattern, other_info) = cg::ref_pattern(&variant, "other"); 76 77 if variant_attrs.error { 78 return quote! { 79 (&#this_pattern, &#other_pattern) => Err(()), 80 }; 81 } 82 83 let (result_value, result_info) = cg::value(&variant, "result"); 84 let mut computations = quote!(); 85 let iter = result_info.iter().zip(this_info.iter().zip(&other_info)); 86 computations.append_all(iter.map(|(result, (this, other))| { 87 let field_attrs = cg::parse_field_attrs::<AnimationFieldAttrs>(&result.ast()); 88 if field_attrs.field_bound { 89 let ty = &this.ast().ty; 90 cg::add_predicate( 91 where_clause, 92 parse_quote!(#ty: crate::values::animated::Animate), 93 ); 94 } 95 if field_attrs.constant { 96 quote! { 97 if #this != #other { 98 return Err(()); 99 } 100 let #result = std::clone::Clone::clone(#this); 101 } 102 } else { 103 quote! { 104 let #result = 105 crate::values::animated::Animate::animate(#this, #other, procedure)?; 106 } 107 } 108 })); 109 110 quote! { 111 (&#this_pattern, &#other_pattern) => { 112 #computations 113 Ok(#result_value) 114 } 115 } 116 } 117 118 #[derive(Default, FromDeriveInput)] 119 #[darling(attributes(animation), default)] 120 pub struct AnimationInputAttrs { 121 pub no_bound: Option<PathList>, 122 } 123 124 #[derive(Default, FromVariant)] 125 #[darling(attributes(animation), default)] 126 pub struct AnimationVariantAttrs { 127 pub error: bool, 128 // Only here because of structs, where the struct definition acts as a 129 // variant itself. 130 pub no_bound: Option<PathList>, 131 } 132 133 #[derive(Default, FromField)] 134 #[darling(attributes(animation), default)] 135 pub struct AnimationFieldAttrs { 136 pub constant: bool, 137 pub field_bound: bool, 138 }