servo_config/
pref_util.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 serde::{Deserialize, Serialize};
6use serde_json::Value;
7
8/// The types of preference values in Servo.
9#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
10pub enum PrefValue {
11    Float(f64),
12    Int(i64),
13    UInt(u64),
14    Str(String),
15    Bool(bool),
16    Array(Vec<PrefValue>),
17}
18
19impl PrefValue {
20    /// Parse the `input` string as a preference value. Defaults to a `PrefValue::Str` if the input
21    /// cannot be parsed as valid value of one of the other types.
22    pub fn from_booleanish_str(input: &str) -> Self {
23        match input {
24            "false" => PrefValue::Bool(false),
25            "true" => PrefValue::Bool(true),
26            _ => input
27                .parse::<i64>()
28                .map(PrefValue::Int)
29                .or_else(|_| input.parse::<f64>().map(PrefValue::Float))
30                .unwrap_or_else(|_| PrefValue::from(input)),
31        }
32    }
33}
34
35impl TryFrom<&Value> for PrefValue {
36    type Error = String;
37
38    fn try_from(value: &Value) -> Result<Self, Self::Error> {
39        match value {
40            Value::Null => Err("Cannot turn null into preference".into()),
41            Value::Bool(value) => Ok((*value).into()),
42            Value::Number(number) => number
43                .as_i64()
44                .map(Into::into)
45                .or_else(|| number.as_f64().map(Into::into))
46                .map(Ok)
47                .unwrap_or(Err("Could not parse number from JSON".into())),
48            Value::String(value) => Ok(value.clone().into()),
49            Value::Array(array) => {
50                let array = array
51                    .iter()
52                    .map(TryInto::<PrefValue>::try_into)
53                    .collect::<Result<Vec<_>, _>>()?;
54                Ok(PrefValue::Array(array))
55            },
56            Value::Object(_) => Err("Cannot turn object into preference".into()),
57        }
58    }
59}
60
61macro_rules! impl_pref_from {
62    ($($t: ty => $variant: path,)*) => {
63        $(
64            impl From<$t> for PrefValue {
65                fn from(other: $t) -> Self {
66                    $variant(other.into())
67                }
68            }
69        )+
70    }
71}
72
73macro_rules! impl_from_pref {
74    ($($variant: path => $t: ty,)*) => {
75        $(
76            impl TryFrom<PrefValue> for $t {
77                type Error = String;
78                fn try_from(other: PrefValue) -> Result<Self, Self::Error> {
79                    match other {
80                        $variant(value) => Ok(value.into()),
81                        _ => Err(format!("Cannot convert {other:?} to {}", std::any::type_name::<$t>())),
82                    }
83                }
84            }
85        )+
86    }
87}
88
89impl_pref_from! {
90    f64 => PrefValue::Float,
91    i64 => PrefValue::Int,
92    u64 => PrefValue::UInt,
93    String => PrefValue::Str,
94    &str => PrefValue::Str,
95    bool => PrefValue::Bool,
96}
97
98impl_from_pref! {
99    PrefValue::Float => f64,
100    PrefValue::Int => i64,
101    PrefValue::UInt => u64,
102    PrefValue::Str => String,
103    PrefValue::Bool => bool,
104}
105
106impl From<[f64; 4]> for PrefValue {
107    fn from(other: [f64; 4]) -> PrefValue {
108        PrefValue::Array(IntoIterator::into_iter(other).map(|v| v.into()).collect())
109    }
110}
111
112impl From<PrefValue> for [f64; 4] {
113    fn from(other: PrefValue) -> [f64; 4] {
114        match other {
115            PrefValue::Array(values) if values.len() == 4 => {
116                let values: Vec<f64> = values
117                    .into_iter()
118                    .map(TryFrom::try_from)
119                    .filter_map(Result::ok)
120                    .collect();
121                if values.len() == 4 {
122                    [values[0], values[1], values[2], values[3]]
123                } else {
124                    panic!(
125                        "Cannot convert PrefValue to {:?}",
126                        std::any::type_name::<[f64; 4]>()
127                    )
128                }
129            },
130            _ => panic!(
131                "Cannot convert {:?} to {:?}",
132                other,
133                std::any::type_name::<[f64; 4]>()
134            ),
135        }
136    }
137}
138
139#[cfg(test)]
140mod test {
141    use super::*;
142
143    #[test]
144    fn test_pref_value_from_str() {
145        let value = PrefValue::from_booleanish_str("21");
146        assert_eq!(value, PrefValue::Int(21));
147
148        let value = PrefValue::from_booleanish_str("12.5");
149        assert_eq!(value, PrefValue::Float(12.5));
150
151        let value = PrefValue::from_booleanish_str("a string");
152        assert_eq!(value, PrefValue::Str("a string".into()));
153
154        let value = PrefValue::from_booleanish_str("false");
155        assert_eq!(value, PrefValue::Bool(false));
156
157        let value = PrefValue::from_booleanish_str("true");
158        assert_eq!(value, PrefValue::Bool(true));
159    }
160}