aboutsummaryrefslogtreecommitdiffstats
path: root/components/config/pref_util.rs
diff options
context:
space:
mode:
authorPeter Hall <peterjoel@gmail.com>2019-02-14 12:53:59 +0000
committerPeter Hall <peterjoel@gmail.com>2019-03-20 15:01:26 +0000
commit8bfd4dc1e2f9c71ff3d1f9964565c43a6ae02278 (patch)
tree3e59f25c2588711f4185c6fbba16af293d1d3453 /components/config/pref_util.rs
parent34fda66dfa5528f2b10a873e9a67417c6986c712 (diff)
downloadservo-8bfd4dc1e2f9c71ff3d1f9964565c43a6ae02278.tar.gz
servo-8bfd4dc1e2f9c71ff3d1f9964565c43a6ae02278.zip
#8539 Config preferences backend restructure
Diffstat (limited to 'components/config/pref_util.rs')
-rw-r--r--components/config/pref_util.rs282
1 files changed, 282 insertions, 0 deletions
diff --git a/components/config/pref_util.rs b/components/config/pref_util.rs
new file mode 100644
index 00000000000..3083e39b9f5
--- /dev/null
+++ b/components/config/pref_util.rs
@@ -0,0 +1,282 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+use serde_json::Value;
+use std::collections::HashMap;
+use std::fmt;
+use std::str::FromStr;
+use std::sync::{Arc, RwLock};
+
+#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
+pub enum PrefValue {
+ Float(f64),
+ Int(i64),
+ Str(String),
+ Bool(bool),
+ Missing,
+}
+
+impl PrefValue {
+ pub fn as_str(&self) -> Option<&str> {
+ if let PrefValue::Str(val) = self {
+ Some(val)
+ } else {
+ None
+ }
+ }
+
+ pub fn as_i64(&self) -> Option<i64> {
+ if let PrefValue::Int(val) = self {
+ Some(*val)
+ } else {
+ None
+ }
+ }
+
+ pub fn as_f64(&self) -> Option<f64> {
+ if let PrefValue::Float(val) = self {
+ Some(*val)
+ } else {
+ None
+ }
+ }
+
+ pub fn as_bool(&self) -> Option<bool> {
+ if let PrefValue::Bool(val) = self {
+ Some(*val)
+ } else {
+ None
+ }
+ }
+
+ pub fn is_missing(&self) -> bool {
+ match self {
+ PrefValue::Missing => true,
+ _ => false,
+ }
+ }
+
+ pub fn from_json_value(value: &Value) -> Option<Self> {
+ match value {
+ Value::Bool(b) => Some(PrefValue::Bool(*b)),
+ Value::Number(n) if n.is_i64() => Some(PrefValue::Int(n.as_i64().unwrap())),
+ Value::Number(n) if n.is_f64() => Some(PrefValue::Float(n.as_f64().unwrap())),
+ Value::String(s) => Some(PrefValue::Str(s.to_owned())),
+ _ => None,
+ }
+ }
+}
+
+impl FromStr for PrefValue {
+ type Err = PrefError;
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ if s == "false" {
+ Ok(PrefValue::Bool(false))
+ } else if s == "true" {
+ Ok(PrefValue::Bool(true))
+ } else if let Ok(float) = s.parse::<f64>() {
+ Ok(PrefValue::Float(float))
+ } else if let Ok(integer) = s.parse::<i64>() {
+ Ok(PrefValue::Int(integer))
+ } else {
+ Ok(PrefValue::from(s))
+ }
+ }
+}
+
+macro_rules! impl_pref_from {
+ ($($t: ty => $variant: path,)*) => {
+ $(
+ impl From<$t> for PrefValue {
+ fn from(other: $t) -> Self {
+ $variant(other.into())
+ }
+ }
+ )+
+ $(
+ impl From<Option<$t>> for PrefValue {
+ fn from(other: Option<$t>) -> Self {
+ other.map(|val| $variant(val.into())).unwrap_or(PrefValue::Missing)
+ }
+ }
+ )+
+ }
+}
+
+macro_rules! impl_from_pref {
+ ($($variant: path => $t: ty,)*) => {
+ $(
+ impl From<PrefValue> for $t {
+ #[allow(unsafe_code)]
+ fn from(other: PrefValue) -> Self {
+ if let $variant(value) = other {
+ value.into()
+ } else {
+ panic!(
+ format!("Cannot convert {:?} to {:?}",
+ other,
+ unsafe { std::intrinsics::type_name::<$t>() }
+ )
+ );
+ }
+ }
+ }
+ )+
+ $(
+ impl From<PrefValue> for Option<$t> {
+ fn from(other: PrefValue) -> Self {
+ if let PrefValue::Missing = other {
+ None
+ } else {
+ Some(other.into())
+ }
+ }
+ }
+ )+
+ }
+}
+
+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,
+}
+
+#[derive(Debug)]
+pub enum PrefError {
+ NoSuchPref(String),
+ InvalidValue(String),
+ JsonParseErr(serde_json::error::Error),
+}
+
+impl fmt::Display for PrefError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ PrefError::NoSuchPref(s) | PrefError::InvalidValue(s) => f.write_str(&s),
+ PrefError::JsonParseErr(e) => e.fmt(f),
+ }
+ }
+}
+
+impl std::error::Error for PrefError {}
+
+pub struct Accessor<P, V> {
+ pub getter: Box<Fn(&P) -> V + Sync>,
+ pub setter: Box<Fn(&mut P, V) + Sync>,
+}
+
+impl<P, V> Accessor<P, V> {
+ pub fn new<G, S>(getter: G, setter: S) -> Self
+ where
+ G: Fn(&P) -> V + Sync + 'static,
+ S: Fn(&mut P, V) + Sync + 'static,
+ {
+ Accessor {
+ getter: Box::new(getter),
+ setter: Box::new(setter),
+ }
+ }
+}
+
+pub struct Preferences<'m, P> {
+ user_prefs: Arc<RwLock<P>>,
+ default_prefs: P,
+ accessors: &'m HashMap<String, Accessor<P, PrefValue>>,
+}
+
+impl<'m, P: Clone> Preferences<'m, P> {
+ /// Create a new `Preferences` object. The values provided in `default_prefs` are immutable and
+ /// can always be restored using `reset` or `reset_all`.
+ pub fn new(default_prefs: P, accessors: &'m HashMap<String, Accessor<P, PrefValue>>) -> Self {
+ Self {
+ user_prefs: Arc::new(RwLock::new(default_prefs.clone())),
+ default_prefs,
+ accessors,
+ }
+ }
+
+ /// Access to the data structure holding the preference values.
+ pub fn values(&self) -> Arc<RwLock<P>> {
+ Arc::clone(&self.user_prefs)
+ }
+
+ /// Retrieve a preference using its key
+ pub fn get(&self, key: &str) -> PrefValue {
+ if let Some(accessor) = self.accessors.get(key) {
+ let prefs = self.user_prefs.read().unwrap();
+ (accessor.getter)(&prefs)
+ } else {
+ PrefValue::Missing
+ }
+ }
+
+ /// Creates an iterator over all keys and values
+ pub fn iter<'a>(&'a self) -> impl Iterator<Item = (String, PrefValue)> + 'a {
+ let prefs = self.user_prefs.read().unwrap();
+ self.accessors
+ .iter()
+ .map(move |(k, accessor)| (k.clone(), (accessor.getter)(&prefs)))
+ }
+
+ /// Creates an iterator over all keys
+ pub fn keys<'a>(&'a self) -> impl Iterator<Item = &'a str> + 'a {
+ self.accessors.keys().map(String::as_str)
+ }
+
+ fn set_inner<V>(&self, key: &str, mut prefs: &mut P, val: V) -> Result<(), PrefError>
+ where
+ V: Into<PrefValue>,
+ {
+ if let Some(accessor) = self.accessors.get(key) {
+ Ok((accessor.setter)(&mut prefs, val.into()))
+ } else {
+ Err(PrefError::NoSuchPref(String::from(key)))
+ }
+ }
+
+ /// Set a new value for a preference, using its key.
+ pub fn set<V>(&self, key: &str, val: V) -> Result<(), PrefError>
+ where
+ V: Into<PrefValue>,
+ {
+ let mut prefs = self.user_prefs.write().unwrap();
+ self.set_inner(key, &mut prefs, val)
+ }
+
+ pub fn set_all<M>(&self, values: M) -> Result<(), PrefError>
+ where
+ M: IntoIterator<Item = (String, PrefValue)>,
+ {
+ let mut prefs = self.user_prefs.write().unwrap();
+ for (k, v) in values.into_iter() {
+ self.set_inner(&k, &mut prefs, v)?;
+ }
+ Ok(())
+ }
+
+ pub fn reset(&self, key: &str) -> Result<PrefValue, PrefError> {
+ if let Some(accessor) = self.accessors.get(key) {
+ let mut prefs = self.user_prefs.write().unwrap();
+ let old_pref = (accessor.getter)(&prefs);
+ let default_pref = (accessor.getter)(&self.default_prefs);
+ (accessor.setter)(&mut prefs, default_pref);
+ Ok(old_pref)
+ } else {
+ Err(PrefError::NoSuchPref(String::from(key)))
+ }
+ }
+
+ pub fn reset_all(&self) {
+ *self.user_prefs.write().unwrap() = self.default_prefs.clone();
+ }
+}