servo_config/
pref_util.rs1use serde::{Deserialize, Serialize};
6use serde_json::Value;
7
8#[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 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}