#[allow(unused_imports)]
use crate::{
AccessKind, AnyRef, ApplyError, FieldPathQuery, FieldPathQueryMut, MapEntryVisitor,
MutationBatch, Object, ObjectMut, ObjectOps, ObjectRef, ObjectRefMut, PatchOperation,
ReflectiveError, TypeInfo, TypedPath, ValueKind, apply_patch,
};
use alloc::borrow::ToOwned;
use alloc::boxed::Box;
use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::vec::Vec;
use core::any::{Any, TypeId};
#[cfg(feature = "std")]
use core::hash::Hash;
#[cfg(feature = "std")]
use std::collections::HashMap;
pub trait FieldPath: Meta {
fn field_path<'a, Q: FieldPathQuery<'a>>(&'a self, query: Q) -> Option<Q::Output>
where
Self: Sized,
{
ObjectRef::new(self).field_path(query)
}
}
impl<T> FieldPath for T where T: Meta + ?Sized {}
pub trait FieldPathMut: MetaMut {
fn field_path_mut<'r, Q: FieldPathQueryMut<'r>>(
&'r mut self,
query: Q,
) -> Result<Q::Output, ReflectiveError>
where
Self: Sized,
{
query.resolve(ObjectRefMut::new(self))
}
}
impl<T> FieldPathMut for T where T: MetaMut + ?Sized {}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MoveItemError {
Unsupported,
IndexOutOfBounds,
RemoveFailed,
InsertFailed,
RestoreFailed,
}
pub(crate) fn as_map_key<K: 'static>(candidate: &K) -> Option<String> {
let any = candidate as &dyn Any;
if let Some(value) = any.downcast_ref::<String>() {
Some(value.clone())
} else {
any
.downcast_ref::<&'static str>()
.map(|value| (*value).to_owned())
}
}
fn is_self_reference(field: ObjectRef<'_>, parent: &dyn Any) -> bool {
let field = field.as_meta().as_any();
Any::type_id(field) == Any::type_id(parent) && core::ptr::addr_eq(field, parent)
}
fn string_map_key<'a>(key: AnyRef<'a>) -> Option<&'a str> {
key
.to_ref::<String>()
.map(String::as_str)
.or_else(|| key.to_ref::<&'static str>().copied())
}
macro_rules! map_key_lookup {
($get:ident, $get_mut:ident, $map:ident $(, $extra:ident : $extra_bound:path)* ; $($key_bound:path),+) => {
pub(crate) fn $get<'a, K, V, $($extra),*>(map: &'a $map<K, V, $($extra),*>, key: &str) -> Option<&'a V>
where
K: $($key_bound +)+ 'static,
V: 'static,
$($extra: $extra_bound + 'static,)*
{
if let Some(map) = (map as &dyn Any).downcast_ref::<$map<String, V, $($extra),*>>() {
return map.get(key);
}
if let Some(map) = (map as &dyn Any).downcast_ref::<$map<&'static str, V, $($extra),*>>() {
return map.get(key);
}
None
}
pub(crate) fn $get_mut<'a, K, V, $($extra),*>(map: &'a mut $map<K, V, $($extra),*>, key: &str) -> Option<&'a mut V>
where
K: $($key_bound +)+ 'static,
V: 'static,
$($extra: $extra_bound + 'static,)*
{
if TypeId::of::<K>() == TypeId::of::<String>() {
let map = (map as &mut dyn Any)
.downcast_mut::<$map<String, V, $($extra),*>>()
.unwrap();
return map.get_mut(key);
}
if TypeId::of::<K>() == TypeId::of::<&'static str>() {
let map = (map as &mut dyn Any)
.downcast_mut::<$map<&'static str, V, $($extra),*>>()
.unwrap();
return map.get_mut(key);
}
None
}
};
}
map_key_lookup!(btree_map_get, btree_map_get_mut, BTreeMap; Ord);
#[cfg(feature = "std")]
map_key_lookup!(hash_map_get, hash_map_get_mut, HashMap, S: core::hash::BuildHasher; Eq, Hash);
pub trait TypedMapAccess<K> {
fn key_typed(&self, key: &K) -> Option<ObjectRef<'_>>;
}
pub trait TypedMapAccessMut<K> {
fn key_typed_mut(&mut self, key: &K) -> Option<ObjectRefMut<'_>>;
}
pub trait Meta: Any {
fn type_info(&self) -> TypeInfo {
TypeInfo::of::<Self>()
}
fn type_name(&self) -> &'static str {
self.type_info().type_name()
}
fn id(&self) -> core::any::TypeId {
self.type_info().id()
}
fn kind(&self) -> ValueKind;
fn access_kind(&self) -> Option<AccessKind> {
None
}
fn option_value(&self) -> Option<ObjectRef<'_>> {
None
}
fn field(&self, _name: &str) -> Option<ObjectRef<'_>> {
None
}
fn field_names(&self) -> &'static [&'static str] {
&[]
}
fn item(&self, _index: usize) -> Option<ObjectRef<'_>> {
None
}
fn len(&self) -> Option<usize> {
None
}
fn is_empty(&self) -> Option<bool> {
self.len().map(|len| len == 0)
}
fn key(&self, _key: &str) -> Option<ObjectRef<'_>> {
None
}
fn keys(&self) -> Option<Vec<String>> {
None
}
fn visit_map_entries(&self, _visitor: &mut MapEntryVisitor<'_>) -> bool {
false
}
fn eq_dyn(&self, other: &dyn Meta) -> bool {
if self.type_info() != other.type_info() {
return false;
}
match self.kind() {
ValueKind::Scalar => false,
ValueKind::Option => match (self.option_value(), other.option_value()) {
(Some(a), Some(b)) => a.eq_dyn(b),
(None, None) => true,
_ => false,
},
ValueKind::Struct => {
if self.field_names() != other.field_names() {
return false;
}
self
.field_names()
.iter()
.all(|&name| match (self.field(name), other.field(name)) {
(Some(a), Some(b)) if is_self_reference(a, self.as_any()) => {
is_self_reference(b, other.as_any())
}
(Some(a), Some(b)) => a.eq_dyn(b),
_ => false,
})
}
ValueKind::Map => {
let (Some(self_len), Some(other_len)) = (self.len(), other.len()) else {
return false;
};
if self_len != other_len {
return false;
}
let mut equal = true;
if !self.visit_map_entries(&mut |key, value| {
let Some(key) = string_map_key(key) else {
equal = false;
return false;
};
equal = other
.key(key)
.is_some_and(|other_value| value.eq_dyn(other_value));
equal
}) {
return false;
}
equal
}
ValueKind::Sequence => {
let (Some(self_len), Some(other_len)) = (self.len(), other.len()) else {
return false;
};
if self_len != other_len {
return false;
}
(0..self_len).all(|index| match (self.item(index), other.item(index)) {
(Some(a), Some(b)) => a.eq_dyn(b),
_ => false,
})
}
}
}
fn into_any(self: Box<Self>) -> Box<dyn Any>;
fn as_any(&self) -> &dyn Any;
}
pub trait MetaMut: Meta {
fn field_mut(&mut self, _name: &str) -> Option<ObjectRefMut<'_>> {
None
}
fn item_mut(&mut self, _index: usize) -> Option<ObjectRefMut<'_>> {
None
}
fn key_mut(&mut self, _key: &str) -> Option<ObjectRefMut<'_>> {
None
}
fn insert_key(&mut self, _key: &str, value: Object) -> Result<ObjectRefMut<'_>, Object> {
Err(value)
}
fn insert_item(&mut self, _index: usize, value: Object) -> Result<ObjectRefMut<'_>, Object> {
Err(value)
}
fn push_item(&mut self, value: Object) -> Result<ObjectRefMut<'_>, Object> {
Err(value)
}
fn remove_key(&mut self, _key: &str) -> Option<Object> {
None
}
fn remove_item(&mut self, _index: usize) -> Option<Object> {
None
}
fn move_item(&mut self, _from: usize, _to: usize) -> Result<(), MoveItemError> {
Err(MoveItemError::Unsupported)
}
fn set(&mut self, value: Object) -> Result<(), Object>;
fn replace(&mut self, value: Object) -> Result<Object, Object>;
fn apply<'p, I>(&mut self, operations: I) -> Result<(), ApplyError<'p>>
where
Self: Sized,
I: IntoIterator<Item = PatchOperation<'p>>,
{
apply_patch(self as &mut dyn MetaMut, operations)
}
#[doc(hidden)]
fn as_meta(&self) -> &dyn Meta;
fn as_any_mut(&mut self) -> &mut dyn Any;
}
impl dyn MetaMut + '_ {
pub fn to_mut<T: 'static>(&mut self) -> Option<&mut T> {
self.as_any_mut().downcast_mut::<T>()
}
pub fn field_path_mut<'r, Q: FieldPathQueryMut<'r>>(
&'r mut self,
query: Q,
) -> Result<Q::Output, ReflectiveError> {
query.resolve(ObjectRefMut::new(self))
}
}
macro_rules! set_body {
() => {
fn replace(&mut self, value: Object) -> Result<Object, Object> {
if !value.is::<Self>() {
return Err(value);
}
let replacement = *value.into_inner().into_any().downcast::<Self>().unwrap();
Ok(Object::new(core::mem::replace(self, replacement)))
}
fn set(&mut self, value: Object) -> Result<(), Object> {
if !value.is::<Self>() {
return Err(value);
}
*self = *value.into_inner().into_any().downcast::<Self>().unwrap();
Ok(())
}
fn as_meta(&self) -> &dyn Meta {
self
}
};
}