use core::{borrow, fmt};
use std::{
collections::HashMap,
hash::{self, BuildHasherDefault},
rc::Rc,
};
use super::{FromValue, Named, ToValue};
use crate::Value;
pub type Keys<'a> = std::collections::hash_map::Keys<'a, String, Value>;
pub type Values<'a> = std::collections::hash_map::Values<'a, String, Value>;
#[derive(Default, Clone)]
pub struct Map {
inner: Rc<HashMap<String, Value, BuildHasherDefault<ahash::AHasher>>>,
}
impl Map {
pub fn new() -> Self {
Self {
inner: Rc::new(HashMap::default()),
}
}
pub fn with_capacity(cap: usize) -> Self {
Self {
inner: Rc::new(HashMap::with_capacity_and_hasher(
cap,
BuildHasherDefault::default(),
)),
}
}
pub fn make_mut(&mut self) -> &mut HashMap<String, Value, BuildHasherDefault<ahash::AHasher>> {
Rc::make_mut(&mut self.inner)
}
pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&Value>
where
String: borrow::Borrow<Q>,
Q: hash::Hash + std::cmp::Eq + std::cmp::Ord,
{
self.inner.get(k)
}
pub fn get_mut<Q: ?Sized>(&mut self, k: &Q) -> Option<&mut Value>
where
String: borrow::Borrow<Q>,
Q: hash::Hash + std::cmp::Eq + std::cmp::Ord,
{
self.make_mut().get_mut(k)
}
pub fn get_value<Q: ?Sized, T>(&self, k: &Q) -> Option<T>
where
String: borrow::Borrow<Q>,
Q: hash::Hash + std::cmp::Eq + std::cmp::Ord,
T: FromValue,
{
let v = match self.inner.get(k) {
Some(v) => v.clone(),
None => return None,
};
Some(T::from_value(v).unwrap())
}
pub fn insert(&mut self, k: String, v: Value) {
self.make_mut().insert(k, v);
}
pub fn insert_value<V>(&mut self, k: String, v: V)
where
V: ToValue,
{
self.make_mut().insert(k, v.to_value());
}
pub fn remove<Q: ?Sized>(&mut self, k: &Q) -> Option<Value>
where
String: borrow::Borrow<Q>,
Q: hash::Hash + std::cmp::Eq + std::cmp::Ord,
{
self.make_mut().remove(k)
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub fn keys(&self) -> Keys<'_> {
self.inner.keys()
}
pub fn values(&self) -> Values<'_> {
self.inner.values()
}
pub fn contains_key<Q: ?Sized>(&self, k: &Q) -> bool
where
String: borrow::Borrow<Q>,
Q: hash::Hash + std::cmp::Eq + std::cmp::Ord,
{
self.inner.contains_key(k)
}
}
impl FromValue for Map {
fn from_value(value: Value) -> Result<Self, String> {
match value {
Value::Map(map) => Ok(map),
_ => {
Err("cannot coerce map from value".into())
}
}
}
}
impl ToValue for Map {
fn to_value(self) -> Value {
Value::Map(self)
}
}
impl fmt::Debug for Map {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_map().entries(self.inner.iter()).finish()
}
}
impl fmt::Display for Map {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("{")?;
for (pos, (key, value)) in self.inner.iter().enumerate() {
f.write_fmt(format_args!("{key}: {value}"))?;
if pos != self.inner.len() {
f.write_str(", ")?;
}
}
f.write_str("}")
}
}
impl PartialEq for Map {
fn eq(&self, other: &Self) -> bool {
self.len() == other.len() && self.inner == other.inner
}
}
impl PartialOrd for Map {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.values().partial_cmp(other.values())
}
}
impl Named for Map {
const NAME: &'static str = "Map";
}