use std::collections::HashMap;
use std::hash::Hash;
use std::borrow::Borrow;
use std::convert::TryInto;
use std::rc::Rc;
#[derive(Debug, Clone)]
pub enum PropertyValue {
String(Rc<String>),
Bytes(Vec<u8>),
SInt(i32),
UInt(u32),
Float(f64),
}
impl From<String> for PropertyValue {
fn from(v: String) -> Self {
PropertyValue::String(Rc::new(v))
}
}
impl From<Rc<String>> for PropertyValue {
fn from(v: Rc<String>) -> Self {
PropertyValue::String(v)
}
}
impl From<&Rc<String>> for PropertyValue {
fn from(v: &Rc<String>) -> Self {
PropertyValue::String(v.clone())
}
}
impl From<Vec<u8>> for PropertyValue {
fn from(v: Vec<u8>) -> Self {
PropertyValue::Bytes(v)
}
}
impl<'a> TryInto<&'a str> for &'a PropertyValue {
type Error = ();
fn try_into(self) -> Result<&'a str, Self::Error> {
if let PropertyValue::String(s) = self {
Ok(s.as_str())
} else {
Err(())
}
}
}
impl From<i32> for PropertyValue {
fn from(v: i32) -> Self {
PropertyValue::SInt(v)
}
}
impl TryInto<i32> for &PropertyValue {
type Error = ();
fn try_into(self) -> Result<i32, Self::Error> {
if let PropertyValue::SInt(v) = self {
Ok(*v)
} else {
Err(())
}
}
}
impl From<u32> for PropertyValue {
fn from(v: u32) -> Self {
PropertyValue::UInt(v)
}
}
impl From<f64> for PropertyValue {
fn from(v: f64) -> Self {
PropertyValue::Float(v)
}
}
#[derive(Debug, Clone)]
pub struct PropertyStore<K>
where K: Hash + Eq {
content: HashMap<K, PropertyValue>
}
impl<K: Hash + Eq> Default for PropertyStore<K> {
fn default() -> Self {
Self::new()
}
}
impl<K: Hash + Eq> PropertyStore<K> {
pub fn new() -> Self {
PropertyStore {
content: HashMap::new()
}
}
pub fn insert<V: Into<PropertyValue>>(&mut self, key: K, value: V) -> Option<PropertyValue> {
self.content.insert(key, value.into())
}
pub fn get<Q: ?Sized>(&self, key: &Q) -> Option<&PropertyValue>
where K: Borrow<Q>,
Q: Eq + Hash {
self.content.get(key)
}
pub fn contains_key<Q: ?Sized>(&self, key: &Q) -> bool
where K: Borrow<Q>,
Q: Eq + Hash {
self.content.contains_key(key)
}
pub fn get_string<Q: ?Sized>(&self, key: &Q) -> Option<&Rc<String>>
where K: Borrow<Q>,
Q: Eq + Hash {
self.get(key)
.and_then(|v| if let PropertyValue::String(s) = v {
Some(s)
} else {
None
})
}
}
pub trait WithProperties {
type Key: Hash + Eq;
fn with_properties<F, R>(&self, f: F) -> R
where F: FnOnce(Option<&PropertyStore<Self::Key>>) -> R;
fn with_properties_mut<F, R>(&self, f: F) -> R
where F: FnOnce(&mut PropertyStore<Self::Key>) -> R;
fn property<Q: ?Sized>(&self, key: &Q) -> Option<PropertyValue>
where Self::Key: Borrow<Q>,
Q: Eq + Hash {
self.with_properties(|p|
p.and_then(|p| p.get(key).cloned())
)
}
fn property_str<Q: ?Sized>(&self, key: &Q) -> Option<Rc<String>>
where Self::Key: Borrow<Q>,
Q: Eq + Hash {
self.with_properties(|p|
p.and_then(|p| p.get_string(key).cloned())
)
}
fn set_property<V: Into<PropertyValue>>(&self, key: Self::Key, value: V) -> Option<PropertyValue> {
self.with_properties_mut(|p| p.insert(key, value))
}
}