servo_config/
pref_util.rs1use 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}