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