use serde::{Deserialize, Serialize};
use std::fmt;
use std::path::PathBuf;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum PrefValue {
Bool(bool),
Integer(i64),
Float(f64),
String(String),
Null,
}
impl PrefValue {
pub fn from_f64(num: f64) -> Self {
if num.fract() == 0.0 && num >= i64::MIN as f64 && num <= i64::MAX as f64 {
PrefValue::Integer(num as i64)
} else {
PrefValue::Float(num)
}
}
}
impl fmt::Display for PrefValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PrefValue::Bool(b) => write!(f, "{}", b),
PrefValue::Integer(i) => write!(f, "{}", i),
PrefValue::Float(float) => write!(f, "{}", float),
PrefValue::String(s) => write!(f, "\"{}\"", s),
PrefValue::Null => write!(f, "null"),
}
}
}
pub trait PrefValueExt {
fn as_bool(&self) -> Option<bool>;
fn as_i64(&self) -> Option<i64>;
fn as_f64(&self) -> Option<f64>;
fn as_str(&self) -> Option<&str>;
fn is_null(&self) -> bool;
fn is_number(&self) -> bool;
fn type_name(&self) -> &'static str;
fn to_json_value(&self) -> serde_json::Value;
}
impl PrefValueExt for PrefValue {
fn as_bool(&self) -> Option<bool> {
match self {
PrefValue::Bool(b) => Some(*b),
_ => None,
}
}
fn as_i64(&self) -> Option<i64> {
match self {
PrefValue::Integer(i) => Some(*i),
_ => None,
}
}
fn as_f64(&self) -> Option<f64> {
match self {
PrefValue::Integer(i) => Some(*i as f64),
PrefValue::Float(f) => Some(*f),
_ => None,
}
}
fn as_str(&self) -> Option<&str> {
match self {
PrefValue::String(s) => Some(s),
_ => None,
}
}
fn is_null(&self) -> bool {
matches!(self, PrefValue::Null)
}
fn is_number(&self) -> bool {
matches!(self, PrefValue::Integer(_) | PrefValue::Float(_))
}
fn type_name(&self) -> &'static str {
match self {
PrefValue::Bool(_) => "Bool",
PrefValue::Integer(_) => "Integer",
PrefValue::Float(_) => "Float",
PrefValue::String(_) => "String",
PrefValue::Null => "Null",
}
}
fn to_json_value(&self) -> serde_json::Value {
match self {
PrefValue::Bool(b) => serde_json::Value::Bool(*b),
PrefValue::Integer(i) => serde_json::Value::Number(serde_json::Number::from(*i)),
PrefValue::Float(f) => serde_json::Value::Number(
serde_json::Number::from_f64(*f).unwrap_or_else(|| serde_json::Number::from(0)),
),
PrefValue::String(s) => serde_json::Value::String(s.clone()),
PrefValue::Null => serde_json::Value::Null,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum PrefType {
#[serde(rename = "user")]
User,
#[serde(rename = "default")]
Default,
#[serde(rename = "locked")]
Locked,
#[serde(rename = "sticky")]
Sticky,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum PrefSource {
#[serde(rename = "builtin")]
BuiltIn,
#[serde(rename = "global")]
GlobalDefault,
#[serde(rename = "user")]
User,
#[serde(rename = "policy")]
SystemPolicy,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PrefEntry {
pub key: String,
pub value: PrefValue,
pub pref_type: PrefType,
#[serde(skip_serializing_if = "Option::is_none")]
pub explanation: Option<&'static str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub source: Option<PrefSource>,
#[serde(skip_serializing_if = "Option::is_none")]
pub source_file: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub locked: Option<bool>,
}
impl PrefEntry {
pub fn find_by_key<'a>(prefs: &'a [PrefEntry], key: &str) -> Option<&'a PrefEntry> {
prefs.iter().find(|e| e.key == key)
}
}
impl fmt::Display for PrefEntry {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let source_info = if let Some(ref source) = self.source {
match source {
PrefSource::BuiltIn => " [builtin]",
PrefSource::GlobalDefault => " [global]",
PrefSource::User => " [user]",
PrefSource::SystemPolicy => " [policy]",
}
} else {
""
};
write!(
f,
"{}({:?}){} = {}",
self.key, self.pref_type, source_info, self.value
)
}
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct FirefoxInstallation {
pub version: String,
pub path: PathBuf,
pub has_greprefs: bool,
pub has_omni_ja: bool,
}
#[derive(Debug, Clone, Serialize)]
pub struct MergedPreferences {
pub entries: Vec<PrefEntry>,
pub install_path: Option<PathBuf>,
pub profile_path: PathBuf,
pub loaded_sources: Vec<PrefSource>,
pub warnings: Vec<String>,
}