use crate::*;
pub use bytes::Bytes;
pub use smol_str::SmolStr;
use std::{collections::HashMap, fmt::Debug, hash::BuildHasher};
pub trait Instance<'ty>: TypedObj + Send + Sync {
fn name(&self) -> SmolStr;
fn as_inst(&self) -> &(dyn Instance<'ty> + 'ty);
}
impl<'ty> dyn Instance<'ty> + 'ty {
#[inline]
pub fn downcast_ref<'val, 't, T: Typed<'ty> + 'ty>(&'val self) -> Option<&'val T>
where
'ty: 'val,
{
if self.inst_ty() == T::ty() {
Some(unsafe { &*(self as *const dyn Instance<'ty> as *const T) })
} else {
None
}
}
}
pub trait DowncastExt<'ty> {
fn downcast<T: Typed<'ty> + 'ty>(self) -> Option<Box<T>>;
}
impl<'ty> DowncastExt<'ty> for Box<dyn Instance<'ty> + 'ty> {
fn downcast<T: Typed<'ty> + 'ty>(self) -> Option<Box<T>> {
if self.inst_ty() == T::ty() {
unsafe {
let raw: *mut (dyn Instance<'ty> + 'ty) = Box::into_raw(self);
Some(Box::from_raw(raw as *mut T))
}
} else {
None
}
}
}
pub trait StructInstance<'s>: Instance<'s> {
fn get_value<'a>(&'a self, field: &str) -> Option<CowValue<'a, 's>>
where
's: 'a;
fn update<'a>(
&'a mut self,
update: &'a (dyn StructInstance<'s> + 's),
field_mask: Option<&FieldMask>,
replace_repeated: bool,
) -> Result<(), Error>;
fn values<'a>(&'a self) -> HashMap<SmolStr, CowValue<'a, 's>>;
fn boxed_clone(&self) -> Box<dyn StructInstance<'s> + 's>;
fn into_boxed_instance(self: Box<Self>) -> Box<dyn Instance<'s> + 's>;
}
impl<'s> std::fmt::Debug for dyn StructInstance<'s> + 's {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut builder = f.debug_struct(&self.name());
for (name, val) in self.values() {
builder.field(&name, &val);
}
builder.finish()
}
}
impl<'s> PartialEq for dyn StructInstance<'s> + 's {
fn eq(&self, other: &Self) -> bool {
self.values() == other.values()
}
}
impl<'s> Clone for Box<dyn StructInstance<'s> + 's> {
fn clone(&self) -> Self {
self.boxed_clone()
}
}
pub trait EnumInstance<'s>: Instance<'s> {
fn boxed_clone(&self) -> Box<dyn EnumInstance<'s> + 's>;
fn field<'a>(&'a self) -> EnumField<'a, 's>
where
's: 'a;
fn into_boxed_instance(self: Box<Self>) -> Box<dyn Instance<'s>>;
}
#[derive(PartialEq, Clone, Debug)]
pub enum EnumField<'a, 's> {
Unit(SmolStr),
Tuple {
name: SmolStr,
fields: Vec<CowValue<'a, 's>>,
},
Struct {
name: SmolStr,
fields: HashMap<SmolStr, CowValue<'a, 's>>,
},
}
impl<'s> PartialEq for dyn EnumInstance<'s> + 's {
fn eq(&self, other: &Self) -> bool {
self.field() == other.field()
}
}
impl<'s> std::fmt::Debug for dyn EnumInstance<'s> + 's {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.field() {
EnumField::Unit(name) => f.write_str(name.as_str()),
EnumField::Tuple { name, fields } => {
let mut tuple = f.debug_tuple(name.as_str());
for field in fields {
tuple.field(&field);
}
tuple.finish()
}
EnumField::Struct { name, fields } => {
let mut s = f.debug_struct(&name);
for (name, field) in fields {
s.field(&name, &field);
}
s.finish()
}
}
}
}
impl<'s> Clone for Box<dyn EnumInstance<'s> + 's> {
fn clone(&self) -> Self {
self.boxed_clone()
}
}
pub trait VecInstance<'s>: Instance<'s> + 's {
fn get_value<'a>(&'a self, i: usize) -> Option<Value<'a, 's>>
where
's: 'a;
fn values<'a>(&'a self) -> Vec<CowValue<'a, 's>>
where
's: 'a;
fn boxed_clone(&self) -> Box<dyn VecInstance<'s> + 's>;
fn update<'a>(
&'a mut self,
update: &'a (dyn VecInstance<'s> + 's),
replace_repeated: bool,
) -> Result<(), Error>;
fn is_empty(&self) -> bool;
fn len(&self) -> usize;
fn vec_eq(&self, inst: &(dyn VecInstance<'s> + 's)) -> bool;
fn into_boxed_instance(self: Box<Self>) -> Box<dyn Instance<'s> + 's>;
}
impl<'s> std::fmt::Debug for dyn VecInstance<'s> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_list().entries(self.values().iter()).finish()
}
}
impl<'s> PartialEq for dyn VecInstance<'s> + 's {
fn eq(&self, other: &Self) -> bool {
self.vec_eq(other)
}
}
impl<'s> Clone for Box<dyn VecInstance<'s> + 's> {
fn clone(&self) -> Self {
self.boxed_clone()
}
}
pub trait HashMapInstance<'s>: Instance<'s> + 's {
fn get_value<'a>(&'a self, key: &str) -> Option<Value<'a, 's>>
where
's: 'a;
fn is_empty(&self) -> bool;
fn len(&self) -> usize;
fn boxed_clone(&self) -> Box<dyn HashMapInstance<'s> + 's>;
fn update<'a>(
&'a mut self,
update: &'a (dyn HashMapInstance<'s> + 's),
field_mask: Option<&FieldMask>,
replace_repeated: bool,
) -> Result<(), Error>;
fn values<'a>(&'a self) -> HashMap<String, CowValue<'a, 's>>
where
's: 'a;
fn hashmap_eq(&self, inst: &(dyn HashMapInstance<'s> + 's)) -> bool;
fn contains(&self, sub_inst: &(dyn HashMapInstance<'s> + 's)) -> bool {
sub_inst.values().into_iter().all(|(k, v)| {
self.get_value(&k).map_or(false, |sv| v.as_ref().slow_eq(&sv))
})
}
fn into_boxed_instance(self: Box<Self>) -> Box<dyn Instance<'s> + 's>;
}
impl<'s> std::fmt::Debug for dyn HashMapInstance<'s> + 's {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut builder = f.debug_map();
for (k, v) in self.values() {
builder.entry(&k, &v);
}
builder.finish()
}
}
impl<'s> PartialEq for dyn HashMapInstance<'s> + 's {
fn eq(&self, other: &Self) -> bool {
self.hashmap_eq(other)
}
}
impl<'s> Clone for Box<dyn HashMapInstance<'s> + 's> {
fn clone(&self) -> Self {
self.boxed_clone()
}
}
pub trait OptionInstance<'s>: Instance<'s> {
fn value<'a>(&'a self) -> Option<Value<'a, 's>>
where
's: 'a;
fn boxed_clone(&self) -> Box<dyn OptionInstance<'s> + 's>;
fn into_boxed_instance(self: Box<Self>) -> Box<dyn Instance<'s> + 's>;
}
impl<'s> std::fmt::Debug for dyn OptionInstance<'s> + 's {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut build = f.debug_tuple(&self.name());
if let Some(val) = self.value() {
build.field(&val);
}
build.finish()
}
}
impl<'s, T: Typed<'s> + Clone + 's> Typed<'s> for Option<T> {
fn ty() -> ValueTy {
ValueTy::Option(Box::new(T::ty()))
}
fn as_value<'a>(&'a self) -> Value<'a, 's>
where
's: 'a,
{
Value::from_option(self)
}
}
impl<'s> PartialEq for dyn OptionInstance<'s> + 's {
fn eq(&self, other: &Self) -> bool {
self.value() == other.value()
}
}
impl<'s> Clone for Box<dyn OptionInstance<'s> + 's> {
fn clone(&self) -> Self {
self.boxed_clone()
}
}
impl<'a, T: Typed<'a> + Clone + PartialEq + 'a> Instance<'a> for Vec<T> {
fn name(&self) -> SmolStr {
format!("Vec<{:?}>", T::ty()).into()
}
fn as_inst(&self) -> &(dyn Instance<'a> + 'a) {
self
}
}
impl<'s, T: Typed<'s> + Clone + 's + PartialEq> VecInstance<'s> for Vec<T> {
fn get_value<'a>(&'a self, i: usize) -> Option<Value<'a, 's>>
where
's: 'a,
{
let val = self.get(i)?.as_value();
Some(val)
}
fn values<'a>(&'a self) -> Vec<CowValue<'a, 's>>
where
's: 'a,
{
self.iter().map(|e| CowValue::Ref(e.as_value())).collect()
}
fn boxed_clone(&self) -> Box<dyn VecInstance<'s> + 's> {
Box::new(self.clone())
}
fn update<'a>(
&'a mut self,
update: &'a (dyn VecInstance<'s> + 's),
replace_repeated: bool,
) -> Result<(), Error> {
if let Some(vec) = Value::from_vec(update).borrow::<&Vec<T>>() {
if replace_repeated {
let vec = vec.clone();
let _ = std::mem::replace(self as &mut Vec<T>, vec);
} else {
self.extend_from_slice(&vec[..]);
}
}
Ok(())
}
fn is_empty(&self) -> bool {
Vec::is_empty(self)
}
fn len(&self) -> usize {
Vec::len(self)
}
fn vec_eq(&self, inst: &(dyn VecInstance<'s> + 's)) -> bool {
inst.as_inst().downcast_ref::<Self>() == Some(self)
}
fn into_boxed_instance(self: Box<Self>) -> Box<dyn Instance<'s> + 's> {
self
}
}
impl<'s, T: Typed<'s> + Clone + 's + PartialEq> Typed<'s> for Vec<T> {
fn ty() -> ValueTy {
ValueTy::Vec(Box::new(T::ty()))
}
fn as_value<'a>(&'a self) -> Value<'a, 's>
where
's: 'a,
{
Value::from_vec(self)
}
}
impl<'s, T> Instance<'s> for HashMap<String, T>
where
T: Typed<'s> + Clone + 's + PartialEq,
{
fn name(&self) -> SmolStr {
format!("HashMap<String, {:?}>", T::ty()).into()
}
fn as_inst(&self) -> &(dyn Instance<'s> + 's) {
self
}
}
impl<'s, T> HashMapInstance<'s> for HashMap<String, T>
where
T: Typed<'s> + Clone + 's + PartialEq,
{
fn get_value<'a>(&'a self, key: &str) -> Option<Value<'a, 's>>
where
's: 'a,
{
let val = self.get(key)?.as_value();
Some(val)
}
fn update<'a>(
&'a mut self,
update: &'a (dyn HashMapInstance<'s> + 's),
field_mask: Option<&FieldMask>,
replace_repeated: bool,
) -> Result<(), Error> {
if let Some(map) = Value::from_hashmap(update).borrow::<&HashMap<String, T>>() {
match (replace_repeated, field_mask) {
(true, None) => {
let _ = std::mem::replace(self as &mut HashMap<String, T>, map.clone());
}
(true, Some(mask)) => {
let masked_keys_to_remove: Vec<String> = self
.keys()
.filter(|k| {
let in_mask = mask.child(&SmolStr::new(k.as_str())).is_some();
let in_update = map.contains_key(k.as_str());
in_mask && !in_update
})
.cloned()
.collect();
for key in masked_keys_to_remove {
self.remove(&key);
}
for (key, value) in map.iter() {
if mask.child(&SmolStr::new(key.as_str())).is_some() {
self.insert(key.clone(), value.clone());
}
}
}
(false, None) => {
for (key, value) in map.iter() {
self.insert(key.clone(), value.clone());
}
}
(false, Some(mask)) => {
for (key, value) in map.iter() {
if mask.child(&SmolStr::new(key.as_str())).is_some() {
self.insert(key.clone(), value.clone());
}
}
}
}
}
Ok(())
}
fn values<'a>(&'a self) -> HashMap<String, CowValue<'a, 's>>
where
's: 'a,
{
self.iter()
.map(|(k, v)| {
let key_str = k.clone();
let cow_val = CowValue::Ref(v.as_value());
(key_str, cow_val)
})
.collect()
}
fn boxed_clone(&self) -> Box<dyn HashMapInstance<'s> + 's> {
Box::new(self.clone())
}
fn is_empty(&self) -> bool {
HashMap::is_empty(self)
}
fn len(&self) -> usize {
HashMap::len(self)
}
fn hashmap_eq(&self, inst: &(dyn HashMapInstance<'s> + 's)) -> bool {
inst.as_inst().downcast_ref::<Self>() == Some(self)
}
fn contains(&self, sub_inst: &(dyn HashMapInstance<'s> + 's)) -> bool {
if let Some(other) = sub_inst.as_inst().downcast_ref::<Self>() {
return other.iter().all(|(k, v)| self.get(k) == Some(v));
}
sub_inst.values().into_iter().all(|(field, sub_inst_value)| {
self.get(&field)
.map_or(false, |self_value| sub_inst_value.as_ref().slow_eq(&self_value.as_value()))
})
}
fn into_boxed_instance(self: Box<Self>) -> Box<dyn Instance<'s> + 's> {
self
}
}
impl<'s, T> Typed<'s> for HashMap<String, T>
where
T: Typed<'s> + Clone + 's + PartialEq,
{
fn ty() -> ValueTy {
ValueTy::HashMap(Box::new(T::ty()))
}
fn as_value<'a>(&'a self) -> Value<'a, 's>
where
's: 'a,
{
Value::from_hashmap(self)
}
}
impl<'s> Instance<'s> for String {
fn name(&self) -> SmolStr {
"String".into()
}
fn as_inst(&self) -> &(dyn Instance<'s> + 's) {
self
}
}
impl<'s> Instance<'s> for Bytes {
fn name(&self) -> SmolStr {
"Bytes".into()
}
fn as_inst(&self) -> &(dyn Instance<'s> + 's) {
self
}
}