use std::collections::HashMap;
#[derive(Debug, Clone)]
pub enum PlistValue {
Boolean(bool),
Integer(i64),
Real(f64),
Data(Vec<u8>),
String(String),
Array(Vec<PlistValue>),
Dict(HashMap<String, PlistValue>),
}
impl PlistValue {
pub fn as_bool(&self) -> Option<bool> {
match self {
PlistValue::Boolean(v) => Some(*v),
_ => None,
}
}
pub fn as_integer(&self) -> Option<i64> {
match self {
PlistValue::Integer(v) => Some(*v),
_ => None,
}
}
pub fn as_real(&self) -> Option<f64> {
match self {
PlistValue::Real(v) => Some(*v),
_ => None,
}
}
pub fn as_data(&self) -> Option<&[u8]> {
match self {
PlistValue::Data(v) => Some(v),
_ => None,
}
}
pub fn as_string(&self) -> Option<&str> {
match self {
PlistValue::String(v) => Some(v),
_ => None,
}
}
pub fn as_array(&self) -> Option<&[PlistValue]> {
match self {
PlistValue::Array(v) => Some(v),
_ => None,
}
}
pub fn as_dict(&self) -> Option<&HashMap<String, PlistValue>> {
match self {
PlistValue::Dict(v) => Some(v),
_ => None,
}
}
pub fn dict_get(&self, key: &str) -> Option<&PlistValue> {
self.as_dict()?.get(key)
}
pub fn array_get(&self, idx: usize) -> Option<&PlistValue> {
self.as_array()?.get(idx)
}
}
fn from_plist_value(val: plist::Value) -> PlistValue {
match val {
plist::Value::Boolean(b) => PlistValue::Boolean(b),
plist::Value::Integer(i) => PlistValue::Integer(i.as_signed().unwrap_or(0)),
plist::Value::Real(r) => PlistValue::Real(r),
plist::Value::Data(d) => PlistValue::Data(d),
plist::Value::String(s) => PlistValue::String(s),
plist::Value::Array(a) => PlistValue::Array(a.into_iter().map(from_plist_value).collect()),
plist::Value::Dictionary(d) => {
let map = d.into_iter().map(|(k, v)| (k, from_plist_value(v))).collect();
PlistValue::Dict(map)
}
plist::Value::Date(_) => PlistValue::String("(date)".to_string()),
plist::Value::Uid(u) => PlistValue::Integer(u.get() as i64),
_ => PlistValue::Data(vec![]),
}
}
fn to_plist_value(val: &PlistValue) -> plist::Value {
match val {
PlistValue::Boolean(b) => plist::Value::Boolean(*b),
PlistValue::Integer(i) => plist::Value::Integer((*i).into()),
PlistValue::Real(r) => plist::Value::Real(*r),
PlistValue::Data(d) => plist::Value::Data(d.clone()),
PlistValue::String(s) => plist::Value::String(s.clone()),
PlistValue::Array(a) => plist::Value::Array(a.iter().map(to_plist_value).collect()),
PlistValue::Dict(d) => {
let dict: plist::Dictionary = d.iter().map(|(k, v)| (k.clone(), to_plist_value(v))).collect();
plist::Value::Dictionary(dict)
}
}
}
pub fn from_bplist(data: &[u8]) -> Option<PlistValue> {
let val = plist::Value::from_reader(std::io::Cursor::new(data)).ok()?;
Some(from_plist_value(val))
}
pub fn to_bplist(value: &PlistValue) -> Option<Vec<u8>> {
let pval = to_plist_value(value);
let mut buf = Vec::new();
pval.to_writer_binary(&mut buf).ok()?;
Some(buf)
}