servo_config_macro/
lib.rs

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
5use proc_macro2::TokenStream;
6use quote::quote;
7use syn::{Data, Fields};
8use synstructure::decl_derive;
9
10decl_derive!([ServoPreferences] => servo_preferences_derive);
11
12/// A derive macro that adds string-based getter and setter for each field of this struct
13/// (enums and other types are not supported). Each field must be able to be convertable
14/// (with `into()`) into a `PrefValue`.
15fn servo_preferences_derive(input: synstructure::Structure) -> TokenStream {
16    let ast = input.ast();
17
18    let Data::Struct(ref data) = ast.data else {
19        unimplemented!();
20    };
21    let Fields::Named(ref named_fields) = data.fields else {
22        unimplemented!()
23    };
24
25    let mut get_match_cases = quote!();
26    for field in named_fields.named.iter() {
27        let name = field.ident.as_ref().unwrap();
28        get_match_cases.extend(quote!(stringify!(#name) => self.#name.clone().into(),))
29    }
30
31    let mut set_match_cases = quote!();
32    for field in named_fields.named.iter() {
33        let name = field.ident.as_ref().unwrap();
34        set_match_cases.extend(quote!(stringify!(#name) => self.#name = value.try_into().unwrap(),))
35    }
36
37    let mut comparisons = quote!();
38    for field in named_fields.named.iter() {
39        let name = field.ident.as_ref().unwrap();
40        comparisons.extend(quote!(if self.#name != other.#name { changes.push((stringify!(#name), self.#name.clone().into(),)) }))
41    }
42
43    let structure_name = &ast.ident;
44    quote! {
45        impl #structure_name {
46            pub fn get_value(&self, name: &str) -> PrefValue {
47                match name {
48                    #get_match_cases
49                    _ => { panic!("Unknown preference: {:?}", name); }
50                }
51            }
52
53            pub fn set_value(&mut self, name: &str, value: PrefValue) {
54                match name {
55                    #set_match_cases
56                    _ => { panic!("Unknown preference: {:?}", name); }
57                }
58            }
59
60            pub fn diff(&self, other: &Self) -> Vec<(&'static str, PrefValue)> {
61                let mut changes = vec![];
62                #comparisons
63                changes
64            }
65        }
66    }
67}