use core::{borrow::Borrow, fmt, ops::ControlFlow};
use crate::{
and::And,
empty::Empty,
str::{Str, ToStr},
value::{FromValue, ToValue, Value},
};
pub trait Props {
fn for_each<'kv, F: FnMut(Str<'kv>, Value<'kv>) -> ControlFlow<()>>(
&'kv self,
for_each: F,
) -> ControlFlow<()>;
fn get<'v, K: ToStr>(&'v self, key: K) -> Option<Value<'v>> {
let key = key.to_str();
let mut value = None;
let _ = self.for_each(|k, v| {
if k == key {
value = Some(v);
ControlFlow::Break(())
} else {
ControlFlow::Continue(())
}
});
value
}
fn pull<'kv, V: FromValue<'kv>, K: ToStr>(&'kv self, key: K) -> Option<V> {
self.get(key).and_then(|v| v.cast())
}
fn and_props<U: Props>(self, other: U) -> And<Self, U>
where
Self: Sized,
{
And::new(self, other)
}
fn filter<F: Fn(Str, Value) -> bool>(self, filter: F) -> Filter<Self, F>
where
Self: Sized,
{
Filter::new(self, filter)
}
fn collect<'kv, C: FromProps<'kv>>(&'kv self) -> C {
C::from_props(self)
}
fn as_map(&self) -> &AsMap<Self>
where
Self: Sized,
{
AsMap::new(self)
}
#[cfg(feature = "alloc")]
fn dedup(&self) -> &Dedup<Self>
where
Self: Sized,
{
Dedup::new(self)
}
fn is_unique(&self) -> bool {
false
}
fn size(&self) -> Option<usize> {
None
}
}
impl<'a, P: Props + ?Sized> Props for &'a P {
fn for_each<'kv, F: FnMut(Str<'kv>, Value<'kv>) -> ControlFlow<()>>(
&'kv self,
for_each: F,
) -> ControlFlow<()> {
(**self).for_each(for_each)
}
fn get<'v, K: ToStr>(&'v self, key: K) -> Option<Value<'v>> {
(**self).get(key)
}
fn pull<'kv, V: FromValue<'kv>, K: ToStr>(&'kv self, key: K) -> Option<V> {
(**self).pull(key)
}
fn is_unique(&self) -> bool {
(**self).is_unique()
}
fn size(&self) -> Option<usize> {
(**self).size()
}
}
impl<P: Props> Props for Option<P> {
fn for_each<'kv, F: FnMut(Str<'kv>, Value<'kv>) -> ControlFlow<()>>(
&'kv self,
for_each: F,
) -> ControlFlow<()> {
match self {
Some(props) => props.for_each(for_each),
None => ControlFlow::Continue(()),
}
}
fn get<'v, K: ToStr>(&'v self, key: K) -> Option<Value<'v>> {
match self {
Some(props) => props.get(key),
None => None,
}
}
fn pull<'kv, V: FromValue<'kv>, K: ToStr>(&'kv self, key: K) -> Option<V> {
match self {
Some(props) => props.pull(key),
None => None,
}
}
fn is_unique(&self) -> bool {
match self {
Some(props) => props.is_unique(),
None => true,
}
}
fn size(&self) -> Option<usize> {
match self {
Some(props) => props.size(),
None => Some(0),
}
}
}
#[cfg(feature = "alloc")]
impl<'a, P: Props + ?Sized + 'a> Props for alloc::boxed::Box<P> {
fn for_each<'kv, F: FnMut(Str<'kv>, Value<'kv>) -> ControlFlow<()>>(
&'kv self,
for_each: F,
) -> ControlFlow<()> {
(**self).for_each(for_each)
}
fn get<'v, K: ToStr>(&'v self, key: K) -> Option<Value<'v>> {
(**self).get(key)
}
fn pull<'kv, V: FromValue<'kv>, K: ToStr>(&'kv self, key: K) -> Option<V> {
(**self).pull(key)
}
fn is_unique(&self) -> bool {
(**self).is_unique()
}
fn size(&self) -> Option<usize> {
(**self).size()
}
}
#[cfg(feature = "alloc")]
impl<'a, P: Props + ?Sized + 'a> Props for alloc::sync::Arc<P> {
fn for_each<'kv, F: FnMut(Str<'kv>, Value<'kv>) -> ControlFlow<()>>(
&'kv self,
for_each: F,
) -> ControlFlow<()> {
(**self).for_each(for_each)
}
fn get<'v, K: ToStr>(&'v self, key: K) -> Option<Value<'v>> {
(**self).get(key)
}
fn pull<'kv, V: FromValue<'kv>, K: ToStr>(&'kv self, key: K) -> Option<V> {
(**self).pull(key)
}
fn is_unique(&self) -> bool {
(**self).is_unique()
}
fn size(&self) -> Option<usize> {
(**self).size()
}
}
impl<K: ToStr, V: ToValue> Props for (K, V) {
fn for_each<'kv, F: FnMut(Str<'kv>, Value<'kv>) -> ControlFlow<()>>(
&'kv self,
mut for_each: F,
) -> ControlFlow<()> {
for_each(self.0.to_str(), self.1.to_value())
}
fn get<'v, G: ToStr>(&'v self, key: G) -> Option<Value<'v>> {
if key.to_str() == self.0.to_str() {
Some(self.1.to_value())
} else {
None
}
}
fn is_unique(&self) -> bool {
true
}
fn size(&self) -> Option<usize> {
Some(1)
}
}
impl<P: Props> Props for [P] {
fn for_each<'kv, F: FnMut(Str<'kv>, Value<'kv>) -> ControlFlow<()>>(
&'kv self,
mut for_each: F,
) -> ControlFlow<()> {
for p in self {
p.for_each(&mut for_each)?;
}
ControlFlow::Continue(())
}
fn get<'v, G: ToStr>(&'v self, key: G) -> Option<Value<'v>> {
let key = key.to_str();
for p in self {
if let Some(value) = p.get(key.by_ref()) {
return Some(value);
}
}
None
}
fn size(&self) -> Option<usize> {
let mut size = 0;
for p in self {
size += p.size()?;
}
Some(size)
}
}
impl<T, const N: usize> Props for [T; N]
where
[T]: Props,
{
fn for_each<'kv, F: FnMut(Str<'kv>, Value<'kv>) -> ControlFlow<()>>(
&'kv self,
for_each: F,
) -> ControlFlow<()> {
Props::for_each(self as &[_], for_each)
}
fn get<'v, K: ToStr>(&'v self, key: K) -> Option<Value<'v>> {
Props::get(self as &[_], key)
}
fn pull<'kv, V: FromValue<'kv>, K: ToStr>(&'kv self, key: K) -> Option<V> {
Props::pull(self as &[_], key)
}
fn is_unique(&self) -> bool {
Props::is_unique(self as &[_])
}
fn size(&self) -> Option<usize> {
Props::size(self as &[_])
}
}
impl Props for Empty {
fn for_each<'kv, F: FnMut(Str<'kv>, Value<'kv>) -> ControlFlow<()>>(
&'kv self,
_: F,
) -> ControlFlow<()> {
ControlFlow::Continue(())
}
fn get<'v, K: ToStr>(&'v self, _: K) -> Option<Value<'v>> {
None
}
fn is_unique(&self) -> bool {
true
}
fn size(&self) -> Option<usize> {
Some(0)
}
}
impl<A: Props, B: Props> Props for And<A, B> {
fn for_each<'kv, F: FnMut(Str<'kv>, Value<'kv>) -> ControlFlow<()>>(
&'kv self,
mut for_each: F,
) -> ControlFlow<()> {
self.left().for_each(&mut for_each)?;
self.right().for_each(for_each)
}
fn get<'v, K: ToStr>(&'v self, key: K) -> Option<Value<'v>> {
let key = key.borrow();
self.left().get(key).or_else(|| self.right().get(key))
}
fn size(&self) -> Option<usize> {
Some(self.left().size()? + self.right().size()?)
}
}
pub struct Filter<P, F> {
props: P,
filter: F,
}
impl<P, F> Filter<P, F> {
fn new(props: P, filter: F) -> Self {
Filter { props, filter }
}
}
impl<P: Props, F: Fn(Str, Value) -> bool> Props for Filter<P, F> {
fn for_each<'kv, T: FnMut(Str<'kv>, Value<'kv>) -> ControlFlow<()>>(
&'kv self,
mut for_each: T,
) -> ControlFlow<()> {
self.props.for_each(|key, value| {
if !(self.filter)(key.by_ref(), value.by_ref()) {
return ControlFlow::Continue(());
}
for_each(key, value)
})
}
fn get<'v, K: ToStr>(&'v self, key: K) -> Option<Value<'v>> {
let key = key.to_str();
self.props
.get(key.by_ref())
.filter(|v| (self.filter)(key, v.by_ref()))
}
fn is_unique(&self) -> bool {
self.props.is_unique()
}
fn size(&self) -> Option<usize> {
None
}
}
pub trait FromProps<'kv> {
fn from_props<P: Props + ?Sized>(props: &'kv P) -> Self;
}
#[cfg(feature = "alloc")]
impl<'kv, 'a, C: FromProps<'kv> + 'a> FromProps<'kv> for alloc::boxed::Box<C> {
fn from_props<P: Props + ?Sized>(props: &'kv P) -> Self {
alloc::boxed::Box::new(C::from_props(props))
}
}
#[cfg(feature = "alloc")]
impl<'kv, 'a, C: FromProps<'kv> + 'a> FromProps<'kv> for alloc::sync::Arc<C> {
fn from_props<P: Props + ?Sized>(props: &'kv P) -> Self {
alloc::sync::Arc::new(C::from_props(props))
}
}
#[cfg(feature = "alloc")]
mod alloc_support {
use super::*;
use crate::value::OwnedValue;
use core::{cell::UnsafeCell, cmp, mem, ptr};
use alloc::{boxed::Box, collections::BTreeMap, sync::Arc, vec::Vec};
pub struct OwnedProps {
buckets: *const [UnsafeCell<OwnedPropsBucket>],
nprops: usize,
owner: OwnedPropsOwner,
head: Option<*const OwnedProp>,
}
unsafe impl Send for OwnedProps {}
unsafe impl Sync for OwnedProps {}
impl Clone for OwnedProps {
fn clone(&self) -> Self {
match self.owner {
OwnedPropsOwner::Box(_) => Self::collect_owned(self),
OwnedPropsOwner::Shared(_) => Self::collect_shared(self),
}
}
}
impl Drop for OwnedProps {
fn drop(&mut self) {
match self.owner {
OwnedPropsOwner::Box(boxed) => {
drop(unsafe { Box::from_raw(boxed) });
}
OwnedPropsOwner::Shared(_) => {
}
}
}
}
struct OwnedPropsBucket {
head: Option<UnsafeCell<OwnedProp>>,
tail: Vec<*mut OwnedProp>,
}
impl Drop for OwnedPropsBucket {
fn drop(&mut self) {
let mut guard = SliceDropGuard::new(&mut self.tail);
if let Some(head) = self.head.take() {
drop(head);
}
unsafe {
guard.drop();
}
}
}
struct OwnedProp {
key: Str<'static>,
value: OwnedValue,
next: Option<*const OwnedProp>,
}
enum OwnedPropsOwner {
Box(*mut [UnsafeCell<OwnedPropsBucket>]),
Shared(Arc<[UnsafeCell<OwnedPropsBucket>]>),
}
impl OwnedProps {
pub fn collect_owned(props: impl Props) -> Self {
let nbuckets = cmp::min(128, props.size().unwrap_or(32));
let buckets = {
let mut buckets = Box::new_uninit_slice(nbuckets);
for elem in buckets.iter_mut() {
elem.write(UnsafeCell::new(OwnedPropsBucket::new()));
}
unsafe { buckets.assume_init() }
};
let mut guard = BoxDropGuard::new(buckets);
let (nprops, head) = unsafe {
Self::collect_internal(
guard.as_ptr(),
nbuckets,
props,
|k| k.to_owned(),
|v| v.to_owned(),
)
};
let buckets_ptr = guard.take();
OwnedProps {
nprops,
buckets: buckets_ptr,
owner: OwnedPropsOwner::Box(buckets_ptr),
head,
}
}
pub fn collect_shared(props: impl Props) -> Self {
let nbuckets = cmp::min(128, props.size().unwrap_or(32));
let buckets = {
let mut buckets = Arc::new_uninit_slice(nbuckets);
for elem in Arc::get_mut(&mut buckets).unwrap().iter_mut() {
elem.write(UnsafeCell::new(OwnedPropsBucket::new()));
}
unsafe { buckets.assume_init() }
};
let buckets_ptr = Arc::as_ptr(&buckets);
let (nprops, head) = unsafe {
Self::collect_internal(
buckets_ptr as *mut _,
nbuckets,
props,
|k| k.to_shared(),
|v| v.to_shared(),
)
};
OwnedProps {
nprops,
buckets: buckets_ptr,
owner: OwnedPropsOwner::Shared(buckets),
head,
}
}
#[inline]
unsafe fn collect_internal(
buckets_ptr: *mut [UnsafeCell<OwnedPropsBucket>],
nbuckets: usize,
props: impl Props,
mk_key: impl Fn(Str) -> Str<'static>,
mk_value: impl Fn(Value) -> OwnedValue,
) -> (usize, Option<*const OwnedProp>) {
let mut nprops = 0;
let mut head = None::<*const OwnedProp>;
let mut tail = None::<*mut OwnedProp>;
let _ = props.for_each(|k, v| {
let bucket_ptr = unsafe {
let bucket_ptr =
(buckets_ptr as *mut UnsafeCell<OwnedPropsBucket>).add(idx(nbuckets, &k));
(&*bucket_ptr).get()
};
if unsafe { &*bucket_ptr }.get(&k).is_some() {
ControlFlow::Continue(())
} else {
let prop = OwnedProp {
key: mk_key(k),
value: mk_value(v),
next: None,
};
unsafe {
OwnedPropsBucket::push(bucket_ptr, &mut head, &mut tail, prop);
}
nprops += 1;
ControlFlow::Continue(())
}
});
(nprops, head)
}
pub fn to_shared(&self) -> Self {
match self.owner {
OwnedPropsOwner::Box(_) => Self::collect_shared(self),
OwnedPropsOwner::Shared(ref owner) => OwnedProps {
nprops: self.nprops,
buckets: self.buckets,
owner: OwnedPropsOwner::Shared(owner.clone()),
head: self.head,
},
}
}
fn for_each<'kv, F: FnMut(&'kv OwnedProp) -> ControlFlow<()>>(
&'kv self,
mut for_each: F,
) -> ControlFlow<()> {
let mut next = self.head;
while let Some(current) = next.take() {
let current = unsafe { &*current };
for_each(¤t)?;
next = current.next;
}
ControlFlow::Continue(())
}
fn get<'v, K: ToStr>(&'v self, key: K) -> Option<&'v OwnedProp> {
let key = key.to_str();
let buckets = unsafe { &*self.buckets };
let bucket = buckets[idx(buckets.len(), &key)].get();
unsafe { &*bucket }.get(&key)
}
}
impl Props for OwnedProps {
fn for_each<'kv, F: FnMut(Str<'kv>, Value<'kv>) -> ControlFlow<()>>(
&'kv self,
mut for_each: F,
) -> ControlFlow<()> {
self.for_each(|prop| for_each(prop.key.by_ref(), prop.value.by_ref()))
}
fn get<'v, K: ToStr>(&'v self, key: K) -> Option<Value<'v>> {
self.get(key).map(|prop| prop.value.by_ref())
}
fn is_unique(&self) -> bool {
true
}
fn size(&self) -> Option<usize> {
Some(self.nprops)
}
}
impl<'kv> FromProps<'kv> for OwnedProps {
fn from_props<P: Props + ?Sized>(props: &'kv P) -> Self {
Self::collect_owned(props)
}
}
impl OwnedPropsBucket {
#[inline]
const fn new() -> OwnedPropsBucket {
OwnedPropsBucket {
head: None,
tail: Vec::new(),
}
}
#[inline]
unsafe fn push(
this: *mut Self,
head: &mut Option<*const OwnedProp>,
tail: &mut Option<*mut OwnedProp>,
prop: OwnedProp,
) {
let head_field = unsafe { &raw mut (*this).head };
let prop_ptr = if unsafe { &*head_field }.is_none() {
unsafe {
*head_field = Some(UnsafeCell::new(prop));
(&*head_field).as_ref().unwrap().get()
}
} else {
let mut guard = BoxDropGuard::new(Box::new(prop));
let prop_ptr = guard.as_ptr();
unsafe {
let tail_field = &raw mut (*this).tail;
(&mut *tail_field).reserve(1);
(&mut *tail_field).push(guard.take());
};
prop_ptr
};
*head = head.or_else(|| Some(prop_ptr as *const _));
if let Some(tail) = tail {
unsafe {
let tail_field = &raw mut (**tail).next;
*tail_field = Some(prop_ptr)
}
}
*tail = Some(prop_ptr);
}
#[inline]
fn get(&self, k: &Str) -> Option<&OwnedProp> {
if let Some(prop) = &self.head {
let prop = unsafe { &*prop.get() };
if prop.key == *k {
return Some(&prop);
}
}
for prop in &self.tail {
let prop = unsafe { &**prop };
if prop.key == *k {
return Some(&prop);
}
}
None
}
}
struct SliceDropGuard<'a> {
idx: usize,
value: &'a mut [*mut OwnedProp],
}
impl<'a> Drop for SliceDropGuard<'a> {
fn drop(&mut self) {
unsafe {
self.drop();
}
}
}
impl<'a> SliceDropGuard<'a> {
#[inline]
fn new(value: &'a mut [*mut OwnedProp]) -> Self {
SliceDropGuard { value, idx: 0 }
}
#[inline]
unsafe fn drop(&mut self) {
while self.idx < self.value.len() {
let prop = self.value[self.idx];
self.idx += 1;
drop(unsafe { Box::from_raw(prop) });
}
}
}
struct BoxDropGuard<T: ?Sized>(Option<ptr::NonNull<T>>);
impl<T: ?Sized> BoxDropGuard<T> {
#[inline]
fn new(value: Box<T>) -> Self {
BoxDropGuard(ptr::NonNull::new(Box::into_raw(value)))
}
#[inline]
fn as_ptr(&self) -> *mut T {
self.0.unwrap().as_ptr()
}
#[inline]
fn take(&mut self) -> *mut T {
mem::take(&mut self.0).unwrap().as_ptr()
}
}
impl<T: ?Sized> Drop for BoxDropGuard<T> {
fn drop(&mut self) {
let Some(ptr) = self.0.take() else {
return;
};
drop(unsafe { Box::from_raw(ptr.as_ptr()) });
}
}
#[inline]
fn idx(buckets: usize, k: &Str) -> usize {
let mut hash = 0xcbf29ce484222325;
for b in k.get().as_bytes() {
hash = hash ^ (*b as u64);
hash = hash.wrapping_mul(0x00000100000001b3);
}
(hash as usize) % buckets
}
#[repr(transparent)]
pub struct Dedup<P: ?Sized>(P);
impl<P: ?Sized> Dedup<P> {
pub(super) fn new<'a>(props: &'a P) -> &'a Dedup<P> {
unsafe { &*(props as *const P as *const Dedup<P>) }
}
}
impl<P: Props + ?Sized> Props for Dedup<P> {
fn for_each<'kv, F: FnMut(Str<'kv>, Value<'kv>) -> ControlFlow<()>>(
&'kv self,
mut for_each: F,
) -> ControlFlow<()> {
enum Filter<'a> {
Inline(Inline<'a, 16>),
Spilled(Spilled<'a>),
}
impl<'a> Filter<'a> {
fn new(size: Option<usize>) -> Self {
match size {
Some(size) if size <= 16 => Filter::Inline(Inline::new()),
_ => Filter::Spilled(Spilled::new()),
}
}
fn insert(&mut self, key: Str<'a>, value: Value<'a>) {
match self {
Filter::Inline(inline) => match inline.insert(key, value) {
Ok(()) => (),
Err((key, value)) => {
let mut spilled = Spilled::spill(inline.take());
spilled.insert(key, value);
*self = Filter::Spilled(spilled);
}
},
Filter::Spilled(spilled) => spilled.insert(key, value),
}
}
fn take<'b>(&'b mut self) -> impl Iterator<Item = (Str<'a>, Value<'a>)> + 'b {
enum Either<A, B> {
A(A),
B(B),
}
impl<T, A: Iterator<Item = T>, B: Iterator<Item = T>> Iterator for Either<A, B> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
match self {
Either::A(a) => a.next(),
Either::B(b) => b.next(),
}
}
}
match self {
Filter::Inline(inline) => Either::A(inline.take()),
Filter::Spilled(spilled) => Either::B(spilled.take()),
}
}
}
struct Inline<'a, const N: usize> {
values: [(Str<'a>, Value<'a>); N],
len: usize,
}
impl<'a, const N: usize> Inline<'a, N> {
fn new() -> Self {
Inline {
values: [const { (Str::new(""), Value::null()) }; N],
len: 0,
}
}
fn insert(
&mut self,
key: Str<'a>,
value: Value<'a>,
) -> Result<(), (Str<'a>, Value<'a>)> {
if self.len == N {
return Err((key, value));
}
for (seen, _) in &self.values[..self.len] {
if *seen == key {
return Ok(());
}
}
self.values[self.len] = (key, value);
self.len += 1;
Ok(())
}
fn take<'b>(&'b mut self) -> impl Iterator<Item = (Str<'a>, Value<'a>)> + 'b {
let len = self.len;
self.len = 0;
(&mut self.values[..len])
.into_iter()
.map(|v| mem::replace(v, (Str::new(""), Value::null())))
}
}
struct Spilled<'a> {
values: BTreeMap<Str<'a>, Value<'a>>,
}
impl<'a> Spilled<'a> {
fn new() -> Self {
Spilled {
values: Default::default(),
}
}
fn spill(seen: impl Iterator<Item = (Str<'a>, Value<'a>)>) -> Self {
Spilled {
values: seen.collect(),
}
}
fn insert(&mut self, key: Str<'a>, value: Value<'a>) {
self.values.entry(key).or_insert(value);
}
fn take<'b>(&'b mut self) -> impl Iterator<Item = (Str<'a>, Value<'a>)> + 'b {
mem::take(&mut self.values).into_iter()
}
}
if self.0.is_unique() {
return self.0.for_each(for_each);
}
let mut filter = Filter::new(self.0.size());
let _ = self.0.for_each(|key, value| {
filter.insert(key, value);
ControlFlow::Continue(())
});
for (key, value) in filter.take() {
for_each(key, value)?;
}
ControlFlow::Continue(())
}
fn get<'v, K: ToStr>(&'v self, key: K) -> Option<Value<'v>> {
self.0.get(key)
}
fn is_unique(&self) -> bool {
true
}
fn size(&self) -> Option<usize> {
self.0.size()
}
}
impl<T: Props> Props for Vec<T> {
fn for_each<'kv, F: FnMut(Str<'kv>, Value<'kv>) -> ControlFlow<()>>(
&'kv self,
for_each: F,
) -> ControlFlow<()> {
Props::for_each(self as &[_], for_each)
}
fn get<'v, K: ToStr>(&'v self, key: K) -> Option<Value<'v>> {
Props::get(self as &[_], key)
}
fn pull<'kv, V: FromValue<'kv>, K: ToStr>(&'kv self, key: K) -> Option<V> {
Props::pull(self as &[_], key)
}
fn is_unique(&self) -> bool {
Props::is_unique(self as &[_])
}
fn size(&self) -> Option<usize> {
Props::size(self as &[_])
}
}
impl<'kv, K, V> FromProps<'kv> for Vec<(K, V)>
where
K: From<Str<'kv>>,
V: From<Value<'kv>>,
{
fn from_props<P: Props + ?Sized>(props: &'kv P) -> Self {
let mut result = Vec::new();
let _ = props.for_each(|k, v| {
result.push((k.into(), v.into()));
ControlFlow::Continue(())
});
result
}
}
impl<K, V> Props for BTreeMap<K, V>
where
K: Ord + ToStr + Borrow<str>,
V: ToValue,
{
fn for_each<'kv, F: FnMut(Str<'kv>, Value<'kv>) -> ControlFlow<()>>(
&'kv self,
mut for_each: F,
) -> ControlFlow<()> {
for (k, v) in self {
for_each(k.to_str(), v.to_value())?;
}
ControlFlow::Continue(())
}
fn get<'v, Q: ToStr>(&'v self, key: Q) -> Option<Value<'v>> {
self.get(key.to_str().as_ref()).map(|v| v.to_value())
}
fn is_unique(&self) -> bool {
true
}
fn size(&self) -> Option<usize> {
Some(self.len())
}
}
impl<'kv, K, V> FromProps<'kv> for BTreeMap<K, V>
where
K: Ord + From<Str<'kv>>,
V: From<Value<'kv>>,
{
fn from_props<P: Props + ?Sized>(props: &'kv P) -> Self {
let mut result = BTreeMap::new();
let _ = props.for_each(|k, v| {
result.entry(k.into()).or_insert_with(|| v.into());
ControlFlow::Continue(())
});
result
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::value::OwnedValue;
#[test]
fn btreemap_props() {
let props = BTreeMap::from_iter([("a", 1), ("b", 2), ("c", 3)]);
assert_eq!(1, Props::get(&props, "a").unwrap().cast::<i32>().unwrap());
assert_eq!(2, Props::get(&props, "b").unwrap().cast::<i32>().unwrap());
assert_eq!(3, Props::get(&props, "c").unwrap().cast::<i32>().unwrap());
assert_eq!(1, Props::pull::<i32, _>(&props, "a").unwrap());
assert_eq!(2, Props::pull::<i32, _>(&props, "b").unwrap());
assert_eq!(3, Props::pull::<i32, _>(&props, "c").unwrap());
assert!(props.is_unique());
}
#[test]
fn btreemap_from_props() {
let props = BTreeMap::<String, OwnedValue>::from_props(&[("a", 1), ("a", 2), ("c", 3)]);
assert_eq!(1, Props::pull::<i32, _>(&props, "a").unwrap());
assert_eq!(3, Props::pull::<i32, _>(&props, "c").unwrap());
}
#[test]
fn vec_from_props() {
let props = Vec::<(String, OwnedValue)>::from_props(&[("a", 1), ("a", 2), ("c", 3)]);
assert_eq!(3, props.len());
assert_eq!(1, Props::pull::<i32, _>(&props, "a").unwrap());
assert_eq!(3, Props::pull::<i32, _>(&props, "c").unwrap());
}
#[test]
fn dedup() {
let props = [
("a", Value::from(1)),
("a", Value::from(2)),
("b", Value::from(1)),
];
let deduped = props.dedup();
let mut ac = 0;
let mut bc = 0;
let _ = deduped.for_each(|k, v| {
match k.get() {
"a" => {
assert_eq!(1, v.cast::<i32>().unwrap());
ac += 1;
}
"b" => {
assert_eq!(1, v.cast::<i32>().unwrap());
bc += 1;
}
_ => (),
}
ControlFlow::Continue(())
});
assert_eq!(1, ac);
assert_eq!(1, bc);
}
#[test]
fn dedup_many() {
let props = [
("aumcgyiuerskg", 1),
("blvkmnfdigmgc", 2),
("cvojdfmcisemc", 3),
("dlkgjhmgkvnrd", 4),
("eiugrlgmvmgvd", 5),
("flfbjhmrimrtw", 6),
("goihudvngusrg", 7),
("hfjehrngviuwn", 8),
("ivojitvnjysns", 9),
("jciughnrhiens", 10),
("kofhfuernytnd", 11),
("lvgjrunfwwner", 12),
("mfjerukfnjhns", 13),
("nmorikjnnehsx", 14),
("oiovjrmunsnex", 15),
("pijdshfenrnfq", 16),
("aumcgyiuerskg", 11),
("blvkmnfdigmgc", 21),
("cvojdfmcisemc", 31),
("dlkgjhmgkvnrd", 41),
("eiugrlgmvmgvd", 51),
("flfbjhmrimrtw", 61),
("goihudvngusrg", 71),
("hfjehrngviuwn", 81),
("ivojitvnjysns", 91),
("jciughnrhiens", 101),
("kofhfuernytnd", 111),
("lvgjrunfwwner", 121),
("mfjerukfnjhns", 131),
("nmorikjnnehsx", 141),
("oiovjrmunsnex", 151),
("pijdshfenrnfq", 161),
];
let deduped = props.dedup();
let mut ac = 0;
let mut bc = 0;
let _ = deduped.for_each(|k, v| {
match k.get() {
"aumcgyiuerskg" => {
assert_eq!(1, v.cast::<i32>().unwrap());
ac += 1;
}
"blvkmnfdigmgc" => {
assert_eq!(2, v.cast::<i32>().unwrap());
bc += 1;
}
_ => (),
}
ControlFlow::Continue(())
});
assert_eq!(1, ac);
assert_eq!(1, bc);
}
struct WrongSize<P> {
props: P,
size: Option<usize>,
}
impl<P: Props> Props for WrongSize<P> {
fn for_each<'kv, F: FnMut(Str<'kv>, Value<'kv>) -> ControlFlow<()>>(
&'kv self,
for_each: F,
) -> ControlFlow<()> {
self.props.for_each(for_each)
}
fn size(&self) -> Option<usize> {
self.size
}
}
#[test]
fn dedup_low_ball_size() {
let props = WrongSize {
props: [
("aumcgyiuerskg", 1),
("blvkmnfdigmgc", 2),
("cvojdfmcisemc", 3),
("dlkgjhmgkvnrd", 4),
("eiugrlgmvmgvd", 5),
("flfbjhmrimrtw", 6),
("goihudvngusrg", 7),
("hfjehrngviuwn", 8),
("ivojitvnjysns", 9),
("jciughnrhiens", 10),
("kofhfuernytnd", 11),
("lvgjrunfwwner", 12),
("mfjerukfnjhns", 13),
("nmorikjnnehsx", 14),
("oiovjrmunsnex", 15),
("pijdshfenrnfq", 16),
("rkjhfngjrfnhf", 17),
],
size: Some(1),
};
let deduped = props.dedup();
let mut count = 0;
let _ = deduped.for_each(|_, _| {
count += 1;
ControlFlow::Continue(())
});
assert_eq!(17, count);
}
#[test]
fn dedup_high_ball_size() {
let props = WrongSize {
props: [("aumcgyiuerskg", 1)],
size: Some(usize::MAX),
};
let deduped = props.dedup();
let mut count = 0;
let _ = deduped.for_each(|_, _| {
count += 1;
ControlFlow::Continue(())
});
assert_eq!(1, count);
}
#[test]
fn owned_props_empty() {
let props = OwnedProps::collect_owned([] as [(Str, Value); 0]);
assert_eq!(Some(0), props.size());
assert!(props.is_unique());
let mut count = 0;
let _ = props.for_each(|_| {
count += 1;
ControlFlow::Continue(())
});
assert_eq!(0, count);
}
#[test]
fn owned_props_collect() {
for (description, case) in [
(
"owned",
OwnedProps::collect_owned([
("b", 2),
("a", 1),
("c", 3),
("b", 12),
("a", 11),
("c", 13),
]),
),
(
"shared",
OwnedProps::collect_shared([
("b", 2),
("a", 1),
("c", 3),
("b", 12),
("a", 11),
("c", 13),
]),
),
(
"owned -> shared",
OwnedProps::collect_owned([
("b", 2),
("a", 1),
("c", 3),
("b", 12),
("a", 11),
("c", 13),
])
.to_shared(),
),
(
"shared -> shared",
OwnedProps::collect_shared([
("b", 2),
("a", 1),
("c", 3),
("b", 12),
("a", 11),
("c", 13),
])
.to_shared(),
),
(
"owned -> clone",
OwnedProps::collect_owned([
("b", 2),
("a", 1),
("c", 3),
("b", 12),
("a", 11),
("c", 13),
])
.clone(),
),
(
"shared -> clone",
OwnedProps::collect_shared([
("b", 2),
("a", 1),
("c", 3),
("b", 12),
("a", 11),
("c", 13),
])
.clone(),
),
] {
assert_eq!(Some(3), case.size());
assert!(case.is_unique());
assert_eq!(Some(1), case.pull::<usize, _>("a"), "{description}");
assert_eq!(Some(2), case.pull::<usize, _>("b"), "{description}");
assert_eq!(Some(3), case.pull::<usize, _>("c"), "{description}");
let mut values = Vec::new();
let _ = case.for_each(|prop| {
values.push((prop.key.get(), prop.value.by_ref().cast::<usize>()));
ControlFlow::Continue(())
});
assert_eq!(
vec![("b", Some(2)), ("a", Some(1)), ("c", Some(3))],
values,
"{description}"
);
}
}
}
}
#[cfg(feature = "alloc")]
pub use alloc_support::*;
#[cfg(feature = "std")]
mod std_support {
use super::*;
use std::{collections::HashMap, hash::Hash};
impl<K, V> Props for HashMap<K, V>
where
K: Eq + Hash + ToStr + Borrow<str>,
V: ToValue,
{
fn for_each<'kv, F: FnMut(Str<'kv>, Value<'kv>) -> ControlFlow<()>>(
&'kv self,
mut for_each: F,
) -> ControlFlow<()> {
for (k, v) in self {
for_each(k.to_str(), v.to_value())?;
}
ControlFlow::Continue(())
}
fn get<'v, Q: ToStr>(&'v self, key: Q) -> Option<Value<'v>> {
self.get(key.to_str().as_ref()).map(|v| v.to_value())
}
fn is_unique(&self) -> bool {
true
}
fn size(&self) -> Option<usize> {
Some(self.len())
}
}
impl<'kv, K, V> FromProps<'kv> for HashMap<K, V>
where
K: Eq + Hash + From<Str<'kv>>,
V: From<Value<'kv>>,
{
fn from_props<P: Props + ?Sized>(props: &'kv P) -> Self {
let mut result = HashMap::new();
let _ = props.for_each(|k, v| {
result.entry(k.into()).or_insert_with(|| v.into());
ControlFlow::Continue(())
});
result
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::value::OwnedValue;
#[test]
fn hashmap_props() {
let props = HashMap::from_iter([("a", 1), ("b", 2), ("c", 3)]);
assert_eq!(1, Props::get(&props, "a").unwrap().cast::<i32>().unwrap());
assert_eq!(2, Props::get(&props, "b").unwrap().cast::<i32>().unwrap());
assert_eq!(3, Props::get(&props, "c").unwrap().cast::<i32>().unwrap());
assert_eq!(1, Props::pull::<i32, _>(&props, "a").unwrap());
assert_eq!(2, Props::pull::<i32, _>(&props, "b").unwrap());
assert_eq!(3, Props::pull::<i32, _>(&props, "c").unwrap());
assert!(props.is_unique());
}
#[test]
fn hashmap_from_props() {
let props = HashMap::<String, OwnedValue>::from_props(&[("a", 1), ("a", 2), ("c", 3)]);
assert_eq!(1, Props::pull::<i32, _>(&props, "a").unwrap());
assert_eq!(3, Props::pull::<i32, _>(&props, "c").unwrap());
}
}
}
#[repr(transparent)]
pub struct AsMap<P: ?Sized>(P);
impl<P: ?Sized> AsMap<P> {
fn new<'a>(props: &'a P) -> &'a AsMap<P> {
unsafe { &*(props as *const P as *const AsMap<P>) }
}
}
impl<P: Props + ?Sized> Props for AsMap<P> {
fn for_each<'kv, F: FnMut(Str<'kv>, Value<'kv>) -> ControlFlow<()>>(
&'kv self,
for_each: F,
) -> ControlFlow<()> {
self.0.for_each(for_each)
}
fn get<'v, K: ToStr>(&'v self, key: K) -> Option<Value<'v>> {
self.0.get(key)
}
fn pull<'kv, V: FromValue<'kv>, K: ToStr>(&'kv self, key: K) -> Option<V> {
self.0.pull(key)
}
fn is_unique(&self) -> bool {
self.0.is_unique()
}
fn size(&self) -> Option<usize> {
self.0.size()
}
}
#[cfg(feature = "sval")]
impl<P: Props + ?Sized> sval::Value for AsMap<P> {
fn stream<'sval, S: sval::Stream<'sval> + ?Sized>(&'sval self, stream: &mut S) -> sval::Result {
stream.map_begin(None)?;
let mut r = Ok(());
let _ = self.for_each(|k, v| {
r = (|| {
stream.map_key_begin()?;
sval_ref::stream_ref(&mut *stream, k)?;
stream.map_key_end()?;
stream.map_value_begin()?;
sval_ref::stream_ref(&mut *stream, v)?;
stream.map_value_end()
})();
if r.is_ok() {
ControlFlow::Continue(())
} else {
ControlFlow::Break(())
}
});
r?;
stream.map_end()
}
}
#[cfg(feature = "serde")]
impl<P: Props + ?Sized> serde::Serialize for AsMap<P> {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeMap as _;
let mut err = None;
let mut map = serializer.serialize_map(None)?;
let _ = self.for_each(|k, v| match map.serialize_entry(&k, &v) {
Ok(()) => ControlFlow::Continue(()),
Err(e) => {
err = Some(e);
ControlFlow::Break(())
}
});
if let Some(e) = err {
return Err(e);
}
map.end()
}
}
impl<P: Props + ?Sized> fmt::Debug for AsMap<P> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl<P: Props + ?Sized> fmt::Display for AsMap<P> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut map = f.debug_map();
let _ = self.for_each(|k, v| {
map.entry(&k, &v);
ControlFlow::Continue(())
});
map.finish()
}
}
mod internal {
use core::ops::ControlFlow;
use crate::{str::Str, value::Value};
pub trait DispatchProps {
fn dispatch_for_each<'kv, 'f>(
&'kv self,
for_each: &'f mut dyn FnMut(Str<'kv>, Value<'kv>) -> ControlFlow<()>,
) -> ControlFlow<()>;
fn dispatch_get(&self, key: Str) -> Option<Value<'_>>;
fn dispatch_is_unique(&self) -> bool;
fn dispatch_size(&self) -> Option<usize>;
}
pub trait SealedProps {
fn erase_props(&self) -> crate::internal::Erased<&dyn DispatchProps>;
}
}
pub trait ErasedProps: internal::SealedProps {}
impl<P: Props> ErasedProps for P {}
impl<P: Props> internal::SealedProps for P {
fn erase_props(&self) -> crate::internal::Erased<&dyn internal::DispatchProps> {
crate::internal::Erased(self)
}
}
impl<P: Props> internal::DispatchProps for P {
fn dispatch_for_each<'kv, 'f>(
&'kv self,
for_each: &'f mut dyn FnMut(Str<'kv>, Value<'kv>) -> ControlFlow<()>,
) -> ControlFlow<()> {
self.for_each(for_each)
}
fn dispatch_get<'v>(&'v self, key: Str) -> Option<Value<'v>> {
self.get(key)
}
fn dispatch_is_unique(&self) -> bool {
self.is_unique()
}
fn dispatch_size(&self) -> Option<usize> {
self.size()
}
}
impl<'a> Props for dyn ErasedProps + 'a {
fn for_each<'kv, F: FnMut(Str<'kv>, Value<'kv>) -> ControlFlow<()>>(
&'kv self,
mut for_each: F,
) -> ControlFlow<()> {
self.erase_props().0.dispatch_for_each(&mut for_each)
}
fn get<'v, K: ToStr>(&'v self, key: K) -> Option<Value<'v>> {
self.erase_props().0.dispatch_get(key.to_str())
}
fn is_unique(&self) -> bool {
self.erase_props().0.dispatch_is_unique()
}
fn size(&self) -> Option<usize> {
self.erase_props().0.dispatch_size()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tuple_props() {
let props = ("a", 1);
assert_eq!(1, props.get("a").unwrap().cast::<i32>().unwrap());
assert_eq!(1, props.pull::<i32, _>("a").unwrap());
assert!(props.is_unique());
}
#[test]
fn array_props() {
let props = [("a", 1), ("b", 2), ("c", 3)];
assert_eq!(1, props.get("a").unwrap().cast::<i32>().unwrap());
assert_eq!(2, props.get("b").unwrap().cast::<i32>().unwrap());
assert_eq!(3, props.get("c").unwrap().cast::<i32>().unwrap());
assert_eq!(1, props.pull::<i32, _>("a").unwrap());
assert_eq!(2, props.pull::<i32, _>("b").unwrap());
assert_eq!(3, props.pull::<i32, _>("c").unwrap());
assert!(!props.is_unique());
}
#[test]
fn option_props() {
for (props, expected) in [(Some(("a", 1)), Some(1)), (None, None)] {
assert_eq!(expected, props.pull::<i32, _>("a"));
}
}
#[test]
fn erased_props() {
let props = ("a", 1);
let props = &props as &dyn ErasedProps;
assert_eq!(1, props.get("a").unwrap().cast::<i32>().unwrap());
assert_eq!(1, props.pull::<i32, _>("a").unwrap());
assert!(props.is_unique());
}
#[test]
fn get() {
let props = [("a", 1), ("a", 2)];
assert_eq!(1, props.get("a").unwrap().cast::<i32>().unwrap());
}
#[test]
fn pull() {
let props = [("a", 1), ("a", 2)];
assert_eq!(1, props.pull::<i32, _>("a").unwrap());
}
#[test]
fn size() {
let props = [("a", 1), ("b", 2)].and_props([("c", 3)]);
assert_eq!(Some(3), props.size());
}
#[test]
fn and_props() {
let a = ("a", 1);
let b = [("b", 2), ("c", 3)];
let props = a.and_props(b);
assert_eq!(1, props.get("a").unwrap().cast::<i32>().unwrap());
assert_eq!(2, props.get("b").unwrap().cast::<i32>().unwrap());
assert_eq!(3, props.get("c").unwrap().cast::<i32>().unwrap());
assert_eq!(1, props.pull::<i32, _>("a").unwrap());
assert_eq!(2, props.pull::<i32, _>("b").unwrap());
assert_eq!(3, props.pull::<i32, _>("c").unwrap());
assert!(!props.is_unique());
}
#[test]
fn as_map() {
let props = [("a", 1), ("b", 2)].as_map();
assert_eq!("{\"a\": 1, \"b\": 2}", props.to_string());
}
#[cfg(feature = "sval")]
#[test]
fn as_map_stream() {
let props = [("a", 1), ("b", 2)].as_map();
sval_test::assert_tokens(
&props,
&[
sval_test::Token::MapBegin(None),
sval_test::Token::MapKeyBegin,
sval_test::Token::TextBegin(Some(1)),
sval_test::Token::TextFragmentComputed("a".to_owned()),
sval_test::Token::TextEnd,
sval_test::Token::MapKeyEnd,
sval_test::Token::MapValueBegin,
sval_test::Token::I64(1),
sval_test::Token::MapValueEnd,
sval_test::Token::MapKeyBegin,
sval_test::Token::TextBegin(Some(1)),
sval_test::Token::TextFragmentComputed("b".to_owned()),
sval_test::Token::TextEnd,
sval_test::Token::MapKeyEnd,
sval_test::Token::MapValueBegin,
sval_test::Token::I64(2),
sval_test::Token::MapValueEnd,
sval_test::Token::MapEnd,
],
);
}
#[cfg(feature = "serde")]
#[test]
fn as_map_serialize() {
let props = [("a", 1), ("b", 2)].as_map();
serde_test::assert_ser_tokens(
&props,
&[
serde_test::Token::Map { len: None },
serde_test::Token::Str("a"),
serde_test::Token::I64(1),
serde_test::Token::Str("b"),
serde_test::Token::I64(2),
serde_test::Token::MapEnd,
],
);
}
#[test]
fn filter() {
let props =
[("a", 1), ("b", 2), ("c", 3)].filter(|k, v| k == "a" || v.cast::<usize>() == Some(3));
assert!(props.get("a").is_some());
assert!(props.get("b").is_none());
assert!(props.get("c").is_some());
}
}