use std::hash::Hash;
use std::marker::PhantomData;
use std::ops::Deref;
use starlark_derive::Trace;
use starlark_map::small_map::SmallMap;
use crate as starlark;
use crate::typing::Ty;
use crate::values::dict::DictRef;
use crate::values::type_repr::DictType;
use crate::values::type_repr::StarlarkTypeRepr;
use crate::values::UnpackValue;
use crate::values::Value;
#[derive(Debug, Trace)]
pub struct DictOf<'v, K: UnpackValue<'v>, V: UnpackValue<'v>> {
value: Value<'v>,
phantom: PhantomData<(K, V)>,
}
impl<'v, K: UnpackValue<'v>, V: UnpackValue<'v>> DictOf<'v, K, V> {
pub fn collect_entries(&self) -> Vec<(K, V)> {
DictRef::from_value(self.value)
.expect("already validated as a dict")
.iter()
.map(|(k, v)| {
(
K::unpack_value(k).expect("already validated key"),
V::unpack_value(v).expect("already validated value"),
)
})
.collect()
}
#[inline]
pub fn len(&self) -> usize {
DictRef::from_value(self.value)
.expect("already validated as a dict")
.len()
}
}
impl<'v, K: UnpackValue<'v> + Hash + Eq, V: UnpackValue<'v>> DictOf<'v, K, V> {
pub fn to_dict(&self) -> SmallMap<K, V> {
DictRef::from_value(self.value)
.expect("already validated as a dict")
.iter()
.map(|(k, v)| {
(
K::unpack_value(k).expect("already validated key"),
V::unpack_value(v).expect("already validated value"),
)
})
.collect()
}
}
impl<'v, K: UnpackValue<'v>, V: UnpackValue<'v>> StarlarkTypeRepr for DictOf<'v, K, V> {
fn starlark_type_repr() -> Ty {
DictType::<K, V>::starlark_type_repr()
}
}
impl<'v, K: UnpackValue<'v>, V: UnpackValue<'v>> UnpackValue<'v> for DictOf<'v, K, V> {
fn expected() -> String {
format!("dict mapping {} to {}", K::expected(), V::expected())
}
fn unpack_value(value: Value<'v>) -> Option<Self> {
let dict = DictRef::from_value(value)?;
let all_valid = dict
.iter()
.all(|(k, v)| K::unpack_value(k).is_some() && V::unpack_value(v).is_some());
if all_valid {
Some(DictOf {
value,
phantom: PhantomData,
})
} else {
None
}
}
}
impl<'v, K: UnpackValue<'v> + Hash, V: UnpackValue<'v>> Deref for DictOf<'v, K, V> {
type Target = Value<'v>;
fn deref(&self) -> &Self::Target {
&self.value
}
}