use crate::common::traits::{
Adder, Comparer, Container, Divider, Indexer, Iterable, Modder, Multiplier, Negator, Sizer,
Subtractor, Zeroer,
};
use crate::common::types::CelStruct;
use crate::common::types::{CelBytes, CelList, CelMap, CelOptional, CelString, Type};
use std::any::Any;
use std::fmt::Debug;
use std::ops::Deref;
pub trait Val: Debug + Send + Sync {
fn get_type(&self) -> &Type;
fn cel_type() -> &'static Type
where
Self: Sized;
fn as_adder<'b, 'v>(&'b self) -> Option<&'b (dyn Adder + 'v)>
where
Self: 'v,
{
None
}
fn as_comparer(&self) -> Option<&dyn Comparer> {
None
}
fn as_container(&self) -> Option<&dyn Container> {
None
}
fn as_divider<'b, 'v>(&'b self) -> Option<&'b (dyn Divider + 'v)>
where
Self: 'v,
{
None
}
fn as_indexer<'b, 'v>(&'b self) -> Option<&'b (dyn Indexer + 'v)>
where
Self: 'v,
{
None
}
fn into_indexer<'v>(self: Box<Self>) -> Option<Box<dyn Indexer + 'v>>
where
Self: 'v,
{
None
}
fn as_iterable<'b, 'v>(&'b self) -> Option<&'b (dyn Iterable + 'v)>
where
Self: 'v,
{
None
}
fn as_modder<'b, 'v>(&'b self) -> Option<&'b (dyn Modder + 'v)>
where
Self: 'v,
{
None
}
fn as_multiplier<'b, 'v>(&'b self) -> Option<&'b (dyn Multiplier + 'v)>
where
Self: 'v,
{
None
}
fn as_negator<'b, 'v>(&'b self) -> Option<&'b (dyn Negator + 'v)>
where
Self: 'v,
{
None
}
fn as_sizer(&self) -> Option<&dyn Sizer> {
None
}
fn as_subtractor<'b, 'v>(&'b self) -> Option<&'b (dyn Subtractor + 'v)>
where
Self: 'v,
{
None
}
fn as_zeroer(&self) -> Option<&dyn Zeroer> {
None
}
fn equals(&self, _other: &dyn Val) -> bool {
false
}
fn clone_as_boxed<'v>(&self) -> Box<dyn Val + 'v>
where
Self: 'v;
fn as_any(&self) -> Option<&dyn Any> {
None
}
#[doc(hidden)]
fn as_builtin<'b, 'v>(&'b self) -> BuiltinRef<'b, 'v>
where
Self: 'v,
{
BuiltinRef::Other
}
#[doc(hidden)]
fn into_builtin<'v>(self: Box<Self>) -> Option<Builtin<'v>>
where
Self: 'v,
{
None
}
}
pub trait StaticVal: Val + 'static {}
pub trait FromVal<'b, 'v>: Sized {
fn from_val(val: &'b (dyn Val + 'v)) -> Option<&'b Self>;
}
impl<'b, 'v, T: StaticVal> FromVal<'b, 'v> for T {
fn from_val(val: &'b (dyn Val + 'v)) -> Option<&'b Self> {
val.as_any()?.downcast_ref::<T>()
}
}
#[doc(hidden)]
#[non_exhaustive]
pub enum BuiltinRef<'b, 'v> {
String(&'b CelString<'v>),
Bytes(&'b CelBytes<'v>),
List(&'b CelList<'v>),
Map(&'b CelMap<'v>),
Optional(&'b CelOptional<'v>),
Struct(&'b CelStruct<'v>),
Other,
}
#[doc(hidden)]
#[non_exhaustive]
pub enum Builtin<'v> {
String(CelString<'v>),
Bytes(CelBytes<'v>),
List(CelList<'v>),
Map(CelMap<'v>),
Optional(CelOptional<'v>),
Struct(CelStruct<'v>),
}
macro_rules! builtin_from_val {
($($ty:ident => $variant:ident),* $(,)?) => {
$(
impl<'b, 'v> FromVal<'b, 'v> for $ty<'v> {
fn from_val(val: &'b (dyn Val + 'v)) -> Option<&'b Self> {
match val.as_builtin() {
BuiltinRef::$variant(v) => Some(v),
_ => None,
}
}
}
)*
};
}
builtin_from_val! {
CelString => String,
CelBytes => Bytes,
CelList => List,
CelMap => Map,
CelOptional => Optional,
}
builtin_from_val! {
CelStruct => Struct,
}
impl<'v> dyn Val + 'v {
pub fn downcast_ref<'b, T: FromVal<'b, 'v>>(&'b self) -> Option<&'b T> {
T::from_val(self)
}
}
impl<'v> Clone for Box<dyn Val + 'v> {
fn clone(&self) -> Self {
(**self).clone_as_boxed()
}
}
impl<'v> PartialEq for dyn Val + 'v {
fn eq(&self, other: &Self) -> bool {
self.equals(other)
}
}
impl<'v> Eq for dyn Val + 'v {}
pub enum CowVal<'b, 'v> {
Borrowed(&'b (dyn Val + 'v)),
Owned(Box<dyn Val + 'v>),
}
impl<'b, 'v> CowVal<'b, 'v> {
pub fn owned<T: Val + 'v>(val: T) -> Self {
CowVal::Owned(Box::new(val))
}
pub fn is_borrowed(&self) -> bool {
matches!(self, CowVal::Borrowed(_))
}
pub fn is_owned(&self) -> bool {
matches!(self, CowVal::Owned(_))
}
pub fn into_owned(self) -> Box<dyn Val + 'v> {
match self {
CowVal::Borrowed(b) => b.clone_as_boxed(),
CowVal::Owned(o) => o,
}
}
}
impl<'b, 'v> Deref for CowVal<'b, 'v> {
type Target = dyn Val + 'v;
fn deref(&self) -> &Self::Target {
match self {
CowVal::Borrowed(b) => *b,
CowVal::Owned(o) => o.as_ref(),
}
}
}
impl<'b, 'v> AsRef<dyn Val + 'v> for CowVal<'b, 'v> {
fn as_ref(&self) -> &(dyn Val + 'v) {
&**self
}
}
impl<'b, 'v> Clone for CowVal<'b, 'v> {
fn clone(&self) -> Self {
match self {
CowVal::Borrowed(b) => CowVal::Borrowed(*b),
CowVal::Owned(o) => CowVal::Owned(o.clone()),
}
}
}
impl<'b, 'v> Debug for CowVal<'b, 'v> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CowVal::Borrowed(b) => f.debug_tuple("Borrowed").field(b).finish(),
CowVal::Owned(o) => f.debug_tuple("Owned").field(o).finish(),
}
}
}
impl<'b, 'v> PartialEq for CowVal<'b, 'v> {
fn eq(&self, other: &Self) -> bool {
self.as_ref().equals(other.as_ref())
}
}
impl<'b, 'v> Eq for CowVal<'b, 'v> {}
impl<'b, 'v> From<Box<dyn Val + 'v>> for CowVal<'b, 'v> {
fn from(val: Box<dyn Val + 'v>) -> Self {
CowVal::Owned(val)
}
}
impl<'b, 'v> From<&'b (dyn Val + 'v)> for CowVal<'b, 'v> {
fn from(val: &'b (dyn Val + 'v)) -> Self {
CowVal::Borrowed(val)
}
}
#[cfg(test)]
mod test {
use crate::common::types;
use crate::common::types::{CelInt, CelString};
use crate::common::value::{CowVal, Val};
fn test(val: &dyn Val) -> bool {
*val.get_type() == types::STRING_TYPE
}
#[test]
fn test_cow() {
let s1 = types::CelString::from("cel");
let s2 = types::CelString::from("cel");
let b: Box<dyn Val> = Box::new(s1);
let cow: CowVal<'_, '_> = CowVal::Owned(b);
let borrowed: CowVal<'_, '_> = CowVal::Borrowed(&s2);
assert!(test(borrowed.as_ref()));
assert!(test(cow.as_ref()));
assert!(test(borrowed.clone().as_ref()));
assert_eq!(cow.downcast_ref::<CelString>().unwrap().inner(), "cel");
let boxed = cow.into_owned();
let s: &CelString = boxed.downcast_ref::<CelString>().unwrap();
assert_eq!(s.inner(), "cel");
assert!(boxed.downcast_ref::<CelInt>().is_none());
}
#[test]
fn borrowed_string_is_downcastable_and_keeps_its_pointer() {
let owned = String::from("cel-rust");
let borrowed: CowVal<'_, '_> = {
let s = CelString::from(owned.as_str());
CowVal::owned(s)
};
let s = borrowed.downcast_ref::<CelString>().unwrap();
assert!(std::ptr::eq(s.inner(), owned.as_str()));
let cloned = borrowed.clone().into_owned();
let c = cloned.downcast_ref::<CelString>().unwrap();
assert!(std::ptr::eq(c.inner(), owned.as_str()));
}
fn _doc_only() {}
}