to_computed_value.rs (6171B)
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 proc_macro2::TokenStream; 7 use syn::{DeriveInput, Ident, Path}; 8 use synstructure::{BindStyle, BindingInfo}; 9 10 pub fn derive_to_value( 11 mut input: DeriveInput, 12 trait_path: Path, 13 output_type_name: Ident, 14 bind_style: BindStyle, 15 // Returns whether to apply the field bound for a given item. 16 mut binding_attrs: impl FnMut(&BindingInfo) -> ToValueAttrs, 17 // Returns a token stream of the form: trait_path::from_foo(#binding) 18 mut call_from: impl FnMut(&BindingInfo) -> TokenStream, 19 mut call_to: impl FnMut(&BindingInfo) -> TokenStream, 20 // Returns a tokenstream of the form: 21 // fn from_function_syntax(foobar) -> Baz { 22 // #first_arg 23 // } 24 // 25 // fn to_function_syntax(foobar) -> Baz { 26 // #second_arg 27 // } 28 mut trait_impl: impl FnMut(TokenStream, TokenStream) -> TokenStream, 29 ) -> TokenStream { 30 let name = &input.ident; 31 32 let mut where_clause = input.generics.where_clause.take(); 33 cg::propagate_clauses_to_output_type( 34 &mut where_clause, 35 &input.generics, 36 &trait_path, 37 &output_type_name, 38 ); 39 40 let moves = match bind_style { 41 BindStyle::Move | BindStyle::MoveMut => true, 42 BindStyle::Ref | BindStyle::RefMut => false, 43 }; 44 45 let params = input.generics.type_params().collect::<Vec<_>>(); 46 for param in ¶ms { 47 cg::add_predicate(&mut where_clause, parse_quote!(#param: #trait_path)); 48 } 49 50 let computed_value_type = cg::fmap_trait_output(&input, &trait_path, &output_type_name); 51 52 let mut add_field_bound = |binding: &BindingInfo| { 53 let ty = &binding.ast().ty; 54 55 let output_type = cg::map_type_params( 56 ty, 57 ¶ms, 58 &computed_value_type, 59 &mut |ident| parse_quote!(<#ident as #trait_path>::#output_type_name), 60 ); 61 62 cg::add_predicate( 63 &mut where_clause, 64 parse_quote!( 65 #ty: #trait_path<#output_type_name = #output_type> 66 ), 67 ); 68 }; 69 70 let (to_body, from_body) = if params.is_empty() { 71 let mut s = synstructure::Structure::new(&input); 72 s.variants_mut().iter_mut().for_each(|v| { 73 v.bind_with(|_| bind_style); 74 }); 75 76 for variant in s.variants() { 77 for binding in variant.bindings() { 78 let attrs = binding_attrs(&binding); 79 assert!( 80 !attrs.field_bound, 81 "It is default on a non-generic implementation", 82 ); 83 if !attrs.no_field_bound { 84 // Add field bounds to all bindings except the manually 85 // excluded. This ensures the correctness of the clone() / 86 // move based implementation. 87 add_field_bound(binding); 88 } 89 } 90 } 91 92 let to_body = if moves { 93 quote! { self } 94 } else { 95 quote! { std::clone::Clone::clone(self) } 96 }; 97 98 let from_body = if moves { 99 quote! { from } 100 } else { 101 quote! { std::clone::Clone::clone(from) } 102 }; 103 104 (to_body, from_body) 105 } else { 106 let to_body = cg::fmap_match(&input, bind_style, |binding| { 107 let attrs = binding_attrs(&binding); 108 assert!( 109 !attrs.no_field_bound, 110 "It doesn't make sense on a generic implementation" 111 ); 112 if attrs.field_bound { 113 add_field_bound(&binding); 114 } 115 call_to(&binding) 116 }); 117 118 let from_body = cg::fmap_match(&input, bind_style, |binding| call_from(&binding)); 119 120 let self_ = if moves { 121 quote! { self } 122 } else { 123 quote! { *self } 124 }; 125 let from_ = if moves { 126 quote! { from } 127 } else { 128 quote! { *from } 129 }; 130 131 let to_body = quote! { 132 match #self_ { 133 #to_body 134 } 135 }; 136 137 let from_body = quote! { 138 match #from_ { 139 #from_body 140 } 141 }; 142 143 (to_body, from_body) 144 }; 145 146 input.generics.where_clause = where_clause; 147 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); 148 149 let impl_ = trait_impl(from_body, to_body); 150 151 quote! { 152 impl #impl_generics #trait_path for #name #ty_generics #where_clause { 153 type #output_type_name = #computed_value_type; 154 155 #impl_ 156 } 157 } 158 } 159 160 pub fn derive(input: DeriveInput) -> TokenStream { 161 let trait_impl = |from_body, to_body| { 162 quote! { 163 #[inline] 164 fn from_computed_value(from: &Self::ComputedValue) -> Self { 165 #from_body 166 } 167 168 #[allow(unused_variables)] 169 #[inline] 170 fn to_computed_value(&self, context: &crate::values::computed::Context) -> Self::ComputedValue { 171 #to_body 172 } 173 } 174 }; 175 176 derive_to_value( 177 input, 178 parse_quote!(crate::values::computed::ToComputedValue), 179 parse_quote!(ComputedValue), 180 BindStyle::Ref, 181 |binding| { 182 let attrs = cg::parse_field_attrs::<ComputedValueAttrs>(&binding.ast()); 183 ToValueAttrs { 184 field_bound: attrs.field_bound, 185 no_field_bound: attrs.no_field_bound, 186 } 187 }, 188 |binding| quote!(crate::values::computed::ToComputedValue::from_computed_value(#binding)), 189 |binding| quote!(crate::values::computed::ToComputedValue::to_computed_value(#binding, context)), 190 trait_impl, 191 ) 192 } 193 194 #[derive(Default)] 195 pub struct ToValueAttrs { 196 pub field_bound: bool, 197 pub no_field_bound: bool, 198 } 199 200 #[derive(Default, FromField)] 201 #[darling(attributes(compute), default)] 202 struct ComputedValueAttrs { 203 field_bound: bool, 204 no_field_bound: bool, 205 }