servo_config/
pref_util.rs
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub enum PrefValue {
Float(f64),
Int(i64),
Str(String),
Bool(bool),
Array(Vec<PrefValue>),
}
impl PrefValue {
pub fn from_booleanish_str(input: &str) -> Self {
match input {
"false" => PrefValue::Bool(false),
"true" => PrefValue::Bool(true),
_ => input
.parse::<i64>()
.map(PrefValue::Int)
.or_else(|_| input.parse::<f64>().map(PrefValue::Float))
.unwrap_or_else(|_| PrefValue::from(input)),
}
}
}
impl TryFrom<&Value> for PrefValue {
type Error = String;
fn try_from(value: &Value) -> Result<Self, Self::Error> {
match value {
Value::Null => Err("Cannot turn null into preference".into()),
Value::Bool(value) => Ok((*value).into()),
Value::Number(number) => number
.as_i64()
.map(Into::into)
.or_else(|| number.as_f64().map(Into::into))
.map(Ok)
.unwrap_or(Err("Could not parse number from JSON".into())),
Value::String(value) => Ok(value.clone().into()),
Value::Array(array) => {
let array = array.iter().map(TryInto::try_into);
if !array.clone().all(|v| v.is_ok()) {
return Err(format!(
"Cannot turn all array avlues into preference: {array:?}"
));
}
Ok(PrefValue::Array(array.map(Result::unwrap).collect()))
},
Value::Object(_) => Err("Cannot turn object into preference".into()),
}
}
}
macro_rules! impl_pref_from {
($($t: ty => $variant: path,)*) => {
$(
impl From<$t> for PrefValue {
fn from(other: $t) -> Self {
$variant(other.into())
}
}
)+
}
}
macro_rules! impl_from_pref {
($($variant: path => $t: ty,)*) => {
$(
impl TryFrom<PrefValue> for $t {
type Error = String;
fn try_from(other: PrefValue) -> Result<Self, Self::Error> {
match other {
$variant(value) => Ok(value.into()),
_ => Err(format!("Cannot convert {other:?} to {}", std::any::type_name::<$t>())),
}
}
}
)+
}
}
impl_pref_from! {
f64 => PrefValue::Float,
i64 => PrefValue::Int,
String => PrefValue::Str,
&str => PrefValue::Str,
bool => PrefValue::Bool,
}
impl_from_pref! {
PrefValue::Float => f64,
PrefValue::Int => i64,
PrefValue::Str => String,
PrefValue::Bool => bool,
}
impl From<[f64; 4]> for PrefValue {
fn from(other: [f64; 4]) -> PrefValue {
PrefValue::Array(IntoIterator::into_iter(other).map(|v| v.into()).collect())
}
}
impl From<PrefValue> for [f64; 4] {
fn from(other: PrefValue) -> [f64; 4] {
match other {
PrefValue::Array(values) if values.len() == 4 => {
let values: Vec<f64> = values
.into_iter()
.map(TryFrom::try_from)
.filter_map(Result::ok)
.collect();
if values.len() == 4 {
[values[0], values[1], values[2], values[3]]
} else {
panic!(
"Cannot convert PrefValue to {:?}",
std::any::type_name::<[f64; 4]>()
)
}
},
_ => panic!(
"Cannot convert {:?} to {:?}",
other,
std::any::type_name::<[f64; 4]>()
),
}
}
}
#[test]
fn test_pref_value_from_str() {
let value = PrefValue::from_booleanish_str("21");
assert_eq!(value, PrefValue::Int(21));
let value = PrefValue::from_booleanish_str("12.5");
assert_eq!(value, PrefValue::Float(12.5));
let value = PrefValue::from_booleanish_str("a string");
assert_eq!(value, PrefValue::Str("a string".into()));
let value = PrefValue::from_booleanish_str("false");
assert_eq!(value, PrefValue::Bool(false));
let value = PrefValue::from_booleanish_str("true");
assert_eq!(value, PrefValue::Bool(true));
}