diff options
Diffstat (limited to 'components/domobject_derive/lib.rs')
-rw-r--r-- | components/domobject_derive/lib.rs | 59 |
1 files changed, 55 insertions, 4 deletions
diff --git a/components/domobject_derive/lib.rs b/components/domobject_derive/lib.rs index 037ae597e8b..c718b514fc5 100644 --- a/components/domobject_derive/lib.rs +++ b/components/domobject_derive/lib.rs @@ -14,17 +14,27 @@ pub fn expand_token_stream(input: proc_macro::TokenStream) -> proc_macro::TokenS fn expand_string(input: &str) -> String { let type_ = syn::parse_macro_input(input).unwrap(); - let first_field_name = if let syn::Body::Struct(syn::VariantData::Struct(ref fields)) = type_.body { - let first_field = fields.first().expect("#[derive(DomObject)] should not be applied on empty structs"); - first_field.ident.as_ref().unwrap() + let fields = if let syn::Body::Struct(syn::VariantData::Struct(ref fields)) = type_.body { + fields } else { panic!("#[derive(DomObject)] should only be applied on proper structs") }; + let (first_field, fields) = fields + .split_first() + .expect("#[derive(DomObject)] should not be applied on empty structs"); + let first_field_name = first_field.ident.as_ref().unwrap(); + let mut field_types = vec![]; + for field in fields { + if !field_types.contains(&&field.ty) { + field_types.push(&field.ty); + } + } + let name = &type_.ident; let (impl_generics, ty_generics, where_clause) = type_.generics.split_for_impl(); - let tokens = quote! { + let mut items = quote! { impl #impl_generics ::js::conversions::ToJSValConvertible for #name #ty_generics #where_clause { #[allow(unsafe_code)] unsafe fn to_jsval(&self, @@ -49,5 +59,46 @@ fn expand_string(input: &str) -> String { } }; + let mut params = quote::Tokens::new(); + params.append_separated(type_.generics.ty_params.iter().map(|param| ¶m.ident), ", "); + + // For each field in the struct, we implement ShouldNotImplDomObject for a + // pair of all the type parameters of the DomObject and and the field type. + // This allows us to support parameterized DOM objects + // such as IteratorIterable<T>. + items.append_all(field_types.iter().map(|ty| { + quote! { + impl #impl_generics ShouldNotImplDomObject for ((#params), #ty) #where_clause {} + } + })); + + let bound = syn::TyParamBound::Trait( + syn::PolyTraitRef { + bound_lifetimes: vec![], + trait_ref: syn::parse_path("::dom::bindings::reflector::DomObject").unwrap(), + }, + syn::TraitBoundModifier::None + ); + + let mut generics = type_.generics.clone(); + generics.ty_params.push(syn::TyParam { + attrs: vec![], + ident: "__T".into(), + bounds: vec![bound], + default: None, + }); + let (impl_generics, _, where_clause) = generics.split_for_impl(); + + items.append(quote! { + trait ShouldNotImplDomObject {} + impl #impl_generics ShouldNotImplDomObject for ((#params), __T) #where_clause {} + }.as_str()); + + let dummy_const = syn::Ident::new(format!("_IMPL_DOMOBJECT_FOR_{}", name)); + let tokens = quote! { + #[allow(non_upper_case_globals)] + const #dummy_const: () = { #items }; + }; + tokens.to_string() } |