Rewild Your Web
web
browser
dweb
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+}