Rewild Your Web
web browser dweb
at main 143 lines 4.6 kB view raw
1--- original 2+++ modified 3@@ -4,10 +4,11 @@ 4 5 use proc_macro2::TokenStream; 6 use quote::quote; 7-use syn::{Data, Fields}; 8+use syn::{Data, Fields, Lit, Meta}; 9 use synstructure::decl_derive; 10 11 decl_derive!([ServoPreferences] => servo_preferences_derive); 12+decl_derive!([EmbedderPreferences, attributes(namespace)] => embedder_preferences_derive); 13 14 /// A derive macro that adds string-based getter and setter for each field of this struct 15 /// (enums and other types are not supported). Each field must be able to be convertable 16@@ -65,3 +66,127 @@ 17 } 18 } 19 } 20+ 21+/// A derive macro for embedder preferences that implements the `EmbedderPreferences` trait. 22+/// 23+/// This macro is similar to `ServoPreferences` but generates a trait implementation 24+/// instead of inherent methods, and returns `Option` types instead of panicking. 25+/// 26+/// # Usage 27+/// 28+/// ```rust,ignore 29+/// #[derive(Clone, Debug, EmbedderPreferences)] 30+/// #[namespace = "browserhtml"] 31+/// pub struct BrowserHtmlPreferences { 32+/// pub theme: String, 33+/// } 34+/// ``` 35+fn embedder_preferences_derive(input: synstructure::Structure) -> TokenStream { 36+ let ast = input.ast(); 37+ 38+ // Extract namespace from attribute 39+ let namespace = ast 40+ .attrs 41+ .iter() 42+ .find_map(|attr| { 43+ if attr.path().is_ident("namespace") { 44+ if let Meta::NameValue(meta) = &attr.meta { 45+ if let syn::Expr::Lit(expr_lit) = &meta.value { 46+ if let Lit::Str(lit) = &expr_lit.lit { 47+ return Some(lit.value()); 48+ } 49+ } 50+ } 51+ } 52+ None 53+ }) 54+ .expect("EmbedderPreferences requires #[namespace = \"...\"] attribute"); 55+ 56+ let Data::Struct(ref data) = ast.data else { 57+ panic!("EmbedderPreferences can only be derived for structs"); 58+ }; 59+ let Fields::Named(ref named_fields) = data.fields else { 60+ panic!("EmbedderPreferences requires named fields"); 61+ }; 62+ 63+ // Generate get_value match arms (returns Option<PrefValue>) 64+ let mut get_match_cases = quote!(); 65+ for field in named_fields.named.iter() { 66+ let name = field.ident.as_ref().unwrap(); 67+ get_match_cases.extend(quote!( 68+ stringify!(#name) => Some(self.#name.clone().into()), 69+ )); 70+ } 71+ 72+ // Generate set_value match arms (returns bool) 73+ let mut set_match_cases = quote!(); 74+ for field in named_fields.named.iter() { 75+ let name = field.ident.as_ref().unwrap(); 76+ set_match_cases.extend(quote!( 77+ stringify!(#name) => { 78+ if let Ok(v) = value.try_into() { 79+ self.#name = v; 80+ true 81+ } else { 82+ false 83+ } 84+ }, 85+ )); 86+ } 87+ 88+ // Generate diff comparisons 89+ let mut comparisons = quote!(); 90+ for field in named_fields.named.iter() { 91+ let name = field.ident.as_ref().unwrap(); 92+ comparisons.extend(quote!( 93+ if self.#name != other.#name { 94+ changes.push((stringify!(#name), self.#name.clone().into())); 95+ } 96+ )); 97+ } 98+ 99+ // Generate pref_names list 100+ let pref_names: Vec<_> = named_fields 101+ .named 102+ .iter() 103+ .map(|f| { 104+ let name = f.ident.as_ref().unwrap(); 105+ quote!(stringify!(#name)) 106+ }) 107+ .collect(); 108+ 109+ let structure_name = &ast.ident; 110+ let namespace_str = &namespace; 111+ 112+ quote! { 113+ impl servo_config::embedder_prefs::EmbedderPreferences for #structure_name { 114+ fn namespace() -> &'static str { 115+ #namespace_str 116+ } 117+ 118+ fn get_value(&self, name: &str) -> Option<servo_config::pref_util::PrefValue> { 119+ match name { 120+ #get_match_cases 121+ _ => None, 122+ } 123+ } 124+ 125+ fn set_value(&mut self, name: &str, value: servo_config::pref_util::PrefValue) -> bool { 126+ match name { 127+ #set_match_cases 128+ _ => false, 129+ } 130+ } 131+ 132+ fn diff(&self, other: &Self) -> Vec<(&'static str, servo_config::pref_util::PrefValue)> { 133+ let mut changes = vec![]; 134+ #comparisons 135+ changes 136+ } 137+ 138+ fn pref_names() -> &'static [&'static str] { 139+ &[#(#pref_names),*] 140+ } 141+ } 142+ } 143+}