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