#![cfg_attr(
not(feature = "std"),
doc = "[`std::time::SystemTime`]: https://doc.rust-lang.org/std/time/struct.SystemTime.html"
)]
use alloc::string::ToString;
use alloc::sync::Arc;
use core::any::{Any, TypeId};
use core::fmt;
use crate::event::Atom;
mod bigint;
mod bridges;
mod datetime;
mod decimal;
mod duration;
pub(crate) mod known;
mod number;
pub(crate) mod raw;
mod uuid;
pub use self::bigint::BigInt;
pub use self::datetime::{Date, Datetime, Offset, Time, Timestamp};
pub use self::decimal::Decimal;
pub use self::duration::Duration;
pub use self::number::Number;
pub use self::raw::{Raw, RawFormat, RawFormatId, RawFormatInfo, RawInput, TextRawFormat};
pub use self::uuid::Uuid;
pub trait Extension: Any + fmt::Debug + Clone + PartialEq + Send + Sync {
fn name(&self) -> &str;
fn fallback(&self) -> Atom<'_>;
}
pub trait BorrowedExtension: 'static {
type Value<'a>: fmt::Debug + PartialEq + Send + Sync + 'a;
fn name<'v>(value: &'v Self::Value<'_>) -> &'v str;
fn fallback<'v>(value: &'v Self::Value<'_>) -> Atom<'v>;
fn to_static(value: &Self::Value<'_>) -> Self::Value<'static>;
fn shorten<'s, 'l: 's>(value: &'s Self::Value<'l>) -> &'s Self::Value<'s>;
}
impl<T: Extension> BorrowedExtension for T {
type Value<'a> = T;
fn name(value: &T) -> &str {
Extension::name(value)
}
fn fallback<'v>(value: &'v T) -> Atom<'v> {
Extension::fallback(value)
}
fn to_static(value: &T) -> T {
value.clone()
}
fn shorten<'s, 'l: 's>(value: &'s T) -> &'s T {
value
}
}
trait ErasedExtension: fmt::Debug + Send + Sync {
fn key(&self) -> TypeId;
fn name(&self) -> &str;
fn fallback(&self) -> Atom<'_>;
fn to_static(&self) -> Arc<dyn ErasedExtension>;
fn value_ptr(&self, key: TypeId) -> *const ();
fn dyn_eq(&self, other: &dyn ErasedExtension) -> bool;
}
#[repr(transparent)]
struct Holder<'x, K: BorrowedExtension>(K::Value<'x>);
impl<'x, K: BorrowedExtension> fmt::Debug for Holder<'x, K> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
impl<'x, K: BorrowedExtension> ErasedExtension for Holder<'x, K> {
fn key(&self) -> TypeId {
TypeId::of::<K>()
}
fn name(&self) -> &str {
K::name(&self.0)
}
fn fallback(&self) -> Atom<'_> {
K::fallback(&self.0)
}
fn to_static(&self) -> Arc<dyn ErasedExtension> {
Arc::new(Holder::<'static, K>(K::to_static(&self.0)))
}
#[inline]
fn value_ptr(&self, key: TypeId) -> *const () {
if key == TypeId::of::<K>() {
K::shorten(&self.0) as *const K::Value<'_> as *const ()
} else {
core::ptr::null()
}
}
fn dyn_eq(&self, other: &dyn ErasedExtension) -> bool {
let other = other.value_ptr(TypeId::of::<K>());
!other.is_null() && K::shorten(&self.0) == unsafe { &*(other as *const K::Value<'_>) }
}
}
pub struct ExtValue<'a>(Repr<'a>);
enum Repr<'a> {
Borrowed(&'a (dyn ErasedExtension + 'a)),
Owned(Arc<dyn ErasedExtension + 'a>),
}
impl<'a> ExtValue<'a> {
pub fn borrowed<T: Extension>(value: &'a T) -> ExtValue<'a> {
ExtValue::borrowed_value::<T>(value)
}
pub fn owned<T: Extension>(value: T) -> ExtValue<'a> {
ExtValue::owned_value::<T>(value)
}
pub fn borrowed_value<K: BorrowedExtension>(value: &'a K::Value<'a>) -> ExtValue<'a> {
let holder = unsafe { &*(value as *const K::Value<'a> as *const Holder<'a, K>) };
ExtValue(Repr::Borrowed(holder))
}
pub fn owned_value<K: BorrowedExtension>(value: K::Value<'a>) -> ExtValue<'a> {
ExtValue(Repr::Owned(Arc::new(Holder::<'a, K>(value))))
}
fn get(&self) -> &(dyn ErasedExtension + 'a) {
match self.0 {
Repr::Borrowed(value) => value,
Repr::Owned(ref value) => &**value,
}
}
pub fn name(&self) -> &str {
self.get().name()
}
pub fn fallback(&self) -> Atom<'_> {
self.get().fallback()
}
pub fn is<K: BorrowedExtension>(&self) -> bool {
self.get().key() == TypeId::of::<K>()
}
pub fn downcast_ref<T: Extension>(&self) -> Option<&T> {
self.downcast_value_ref::<T>()
}
#[inline]
pub fn downcast_value_ref<K: BorrowedExtension>(&self) -> Option<&K::Value<'_>> {
let ptr = self.get().value_ptr(TypeId::of::<K>());
(!ptr.is_null()).then(|| unsafe { &*(ptr as *const K::Value<'_>) })
}
#[inline]
pub(crate) unsafe fn downcast_value_ref_covariant<K: BorrowedExtension>(
&self,
) -> Option<&K::Value<'a>> {
let ptr = self.get().value_ptr(TypeId::of::<K>());
(!ptr.is_null()).then(|| unsafe { &*(ptr as *const K::Value<'a>) })
}
pub fn as_borrowed(&self) -> ExtValue<'_> {
ExtValue(Repr::Borrowed(self.get()))
}
pub fn to_static(&self) -> ExtValue<'static> {
ExtValue(Repr::Owned(self.get().to_static()))
}
}
impl<'a> Clone for ExtValue<'a> {
fn clone(&self) -> Self {
match self.0 {
Repr::Borrowed(value) => ExtValue(Repr::Borrowed(value)),
Repr::Owned(ref value) => ExtValue(Repr::Owned(value.clone())),
}
}
}
impl<'a> PartialEq for ExtValue<'a> {
fn eq(&self, other: &Self) -> bool {
self.get().dyn_eq(other.get())
}
}
impl<'a> fmt::Debug for ExtValue<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self.get(), f)
}
}
impl Extension for u128 {
fn name(&self) -> &str {
"u128"
}
fn fallback(&self) -> Atom<'_> {
match u64::try_from(*self) {
Ok(value) => Atom::U64(value),
Err(_) => Atom::Str(self.to_string().into()),
}
}
}
impl Extension for i128 {
fn name(&self) -> &str {
"i128"
}
fn fallback(&self) -> Atom<'_> {
if let Ok(value) = i64::try_from(*self) {
Atom::I64(value)
} else if let Ok(value) = u64::try_from(*self) {
Atom::U64(value)
} else {
Atom::Str(self.to_string().into())
}
}
}
#[test]
fn test_auto_traits() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<ExtValue<'static>>();
assert_send_sync::<Atom<'static>>();
assert_send_sync::<crate::Event<'static>>();
assert_send_sync::<crate::Error>();
}
#[test]
fn test_ext_value() {
let value = 42u128;
let ext = ExtValue::borrowed(&value);
assert!(ext.is::<u128>());
assert!(!ext.is::<i128>());
assert_eq!(ext.downcast_ref::<u128>(), Some(&42));
assert_eq!(ext.fallback(), Atom::U64(42));
assert_eq!(ext.to_static(), ExtValue::owned(42u128));
assert_ne!(ext, ExtValue::owned(42i128));
assert_eq!(format!("{:?}", ext), "42");
let big = u128::MAX;
assert_eq!(
ExtValue::borrowed(&big).fallback(),
Atom::Str(u128::MAX.to_string().into())
);
assert_eq!(ExtValue::owned(-1i128).fallback(), Atom::I64(-1));
assert_eq!(
ExtValue::owned(u64::MAX as i128).fallback(),
Atom::U64(u64::MAX)
);
}