use crate::k::K;
use crate::kbox::KBox;
use crate::symbol::Symbol;
use crate::type_traits::*;
use crate::{date_time_types::*, k_type::KTypeCode};
use std::marker::PhantomData;
use std::mem;
use std::{fmt, ptr::NonNull};
#[repr(transparent)]
pub struct Atom<T> {
k: K,
_p: PhantomData<T>,
}
impl<T: KValue> Atom<T> {
#[inline]
pub fn value(&self) -> T {
unsafe { T::from_k(&self.k) }
}
#[inline]
pub fn set_value(&mut self, val: T) {
unsafe { *T::as_mutable(&mut self.k) = val }
}
}
impl<T> KObject for Atom<T> {
#[inline]
fn k_ptr(&self) -> *const K {
&self.k
}
#[inline]
fn k_ptr_mut(&mut self) -> *mut K {
&mut self.k
}
}
impl<T: KValue> KTyped for Atom<T> {
const K_TYPE: KTypeCode = T::TYPE_CODE.as_atom();
}
impl<T> private::Sealed for Atom<T> {}
impl<T: KValue + fmt::Debug> fmt::Debug for Atom<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Atom({:?})", self.value())
}
}
impl<T: KValue + fmt::Display> fmt::Display for Atom<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.value())
}
}
impl<T: KValue> From<T> for KBox<Atom<T>> {
#[inline]
fn from(val: T) -> KBox<Atom<T>> {
KBox {
k: unsafe { NonNull::new_unchecked(val.into_k() as *mut K as *mut Atom<T>) },
}
}
}
macro_rules! impl_atom_from {
($ty:ident) => {
impl From<Atom<$ty>> for $ty {
#[inline]
fn from(val: Atom<$ty>) -> $ty {
val.value()
}
}
};
}
impl_atom_from!(u8);
impl_atom_from!(i8);
impl_atom_from!(i16);
impl_atom_from!(i32);
impl_atom_from!(i64);
impl_atom_from!(f32);
impl_atom_from!(f64);
impl_atom_from!(bool);
impl_atom_from!(Second);
impl_atom_from!(Minute);
impl_atom_from!(Date);
impl_atom_from!(Month);
impl_atom_from!(Time);
impl_atom_from!(DateTime);
impl_atom_from!(Timestamp);
impl_atom_from!(Timespan);
impl_atom_from!(Symbol);
#[cfg(feature = "uuid")]
use uuid::Uuid;
#[cfg(feature = "uuid")]
impl_atom_from!(Uuid);
impl<T: KValue> KBox<Atom<T>> {
#[inline]
pub fn new_atom(value: T) -> KBox<Atom<T>> {
unsafe { mem::transmute(value.into_k()) }
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::clippy::float_cmp)]
use super::*;
use crate::symbol::symbol;
use crate::{cast, Any};
#[test]
fn value_returns_underlying_value() {
assert_eq!(KBox::new_atom(12u8).value(), 12u8);
assert_eq!(KBox::new_atom(13i16).value(), 13i16);
assert_eq!(KBox::new_atom(14i32).value(), 14i32);
assert_eq!(KBox::new_atom(15i64).value(), 15i64);
assert_eq!(KBox::new_atom(5.3f32).value(), 5.3f32);
assert_eq!(KBox::new_atom(true).value(), true);
assert_eq!(KBox::new_atom(6.4f64).value(), 6.4f64);
assert_eq!(KBox::new_atom(Second::new(5)).value(), Second::new(5));
assert_eq!(KBox::new_atom(Minute::new(6)).value(), Minute::new(6));
assert_eq!(KBox::new_atom(Date::new(2020, 2, 6)).value(), Date::new(2020, 2, 6));
assert_eq!(KBox::new_atom(Month::new(8)).value(), Month::new(8));
assert_eq!(KBox::new_atom(Time::new(9)).value(), Time::new(9));
assert_eq!(KBox::new_atom(DateTime::new(10.0)).value(), DateTime::new(10.0));
assert_eq!(KBox::new_atom(Timestamp::from_raw(11)).value(), Timestamp::from_raw(11));
assert_eq!(
KBox::new_atom(Timespan::from_nanos(12)).value(),
Timespan::from_nanos(12)
);
assert_eq!(KBox::new_atom(symbol("Foo")).value(), symbol("Foo"));
#[cfg(feature = "uuid")]
assert_eq!(
KBox::new_atom(uuid::Uuid::from_bytes([12u8; 16])).value(),
Uuid::from_bytes([12u8; 16])
);
}
#[test]
fn set_value_changes_underlying_value() {
assert_eq!(
{
let mut a = KBox::new_atom(11i8);
a.set_value(12i8);
a.value()
},
12i8
);
assert_eq!(
{
let mut a = KBox::new_atom(12u8);
a.set_value(13u8);
a.value()
},
13u8
);
assert_eq!(
{
let mut a = KBox::new_atom(13i16);
a.set_value(14i16);
a.value()
},
14i16
);
assert_eq!(
{
let mut a = KBox::new_atom(14i32);
a.set_value(15i32);
a.value()
},
15i32
);
assert_eq!(
{
let mut a = KBox::new_atom(15i64);
a.set_value(16i64);
a.value()
},
16i64
);
assert_eq!(
{
let mut a = KBox::new_atom(5.3f32);
a.set_value(4.3);
a.value()
},
4.3f32
);
assert_eq!(
{
let mut a = KBox::new_atom(6.4f64);
a.set_value(4.6f64);
a.value()
},
4.6f64
);
assert_eq!(
{
let mut a = KBox::new_atom(true);
a.set_value(false);
a.value()
},
false
);
assert_eq!(
{
let mut a = KBox::new_atom(Second::new(5));
a.set_value(Second::new(6));
a.value()
},
Second::new(6)
);
assert_eq!(
{
let mut a = KBox::new_atom(Minute::new(6));
a.set_value(Minute::new(7));
a.value()
},
Minute::new(7)
);
assert_eq!(
{
let mut a = KBox::new_atom(Date::new(2020, 2, 6));
a.set_value(Date::new(2020, 2, 7));
a.value()
},
Date::new(2020, 2, 7)
);
assert_eq!(
{
let mut a = KBox::new_atom(Month::new(8));
a.set_value(Month::new(9));
a.value()
},
Month::new(9)
);
assert_eq!(
{
let mut a = KBox::new_atom(Time::new(9));
a.set_value(Time::new(10));
a.value()
},
Time::new(10)
);
assert_eq!(
{
let mut a = KBox::new_atom(DateTime::new(10.0));
a.set_value(DateTime::new(11.0));
a.value()
},
DateTime::new(11.0)
);
assert_eq!(
{
let mut a = KBox::new_atom(Timestamp::from_raw(11));
a.set_value(Timestamp::from_raw(12));
a.value()
},
Timestamp::from_raw(12)
);
assert_eq!(
{
let mut a = KBox::new_atom(Timespan::from_nanos(12));
a.set_value(Timespan::from_nanos(13));
a.value()
},
Timespan::from_nanos(13)
);
assert_eq!(
{
let mut a = KBox::new_atom(symbol("Foo"));
a.set_value(symbol("Bar"));
a.value()
},
symbol("Bar")
);
assert_eq!(
{
let mut a = KBox::new_atom(Uuid::from_u128(13));
a.set_value(Uuid::from_u128(14));
a.value()
},
Uuid::from_u128(14)
);
}
#[test]
fn atoms_round_trip_to_any() {
assert_eq!(cast!(KBox::<Any>::from(KBox::new_atom(12u8)); Atom<u8>).value(), 12u8);
assert_eq!(
cast!(KBox::<Any>::from(KBox::new_atom(13i16)); Atom<i16>).value(),
13i16
);
assert_eq!(
cast!(KBox::<Any>::from(KBox::new_atom(14i32)); Atom<i32>).value(),
14i32
);
assert_eq!(
cast!(KBox::<Any>::from(KBox::new_atom(15i64)); Atom<i64>).value(),
15i64
);
assert_eq!(
cast!(KBox::<Any>::from(KBox::new_atom(5.3f32)); Atom<f32>).value(),
5.3f32
);
assert_eq!(cast!(KBox::<Any>::from(KBox::new_atom(true)); Atom<bool>).value(), true);
assert_eq!(
cast!(KBox::<Any>::from(KBox::new_atom(6.4f64)); Atom<f64>).value(),
6.4f64
);
assert_eq!(
cast!(KBox::<Any>::from(KBox::new_atom(Second::new(5))); Atom<Second>).value(),
Second::new(5)
);
assert_eq!(
cast!(KBox::<Any>::from(KBox::new_atom(Minute::new(6))); Atom<Minute>).value(),
Minute::new(6)
);
assert_eq!(
cast!(KBox::<Any>::from(KBox::new_atom(Date::new(2020, 2, 6))); Atom<Date>).value(),
Date::new(2020, 2, 6)
);
assert_eq!(
cast!(KBox::<Any>::from(KBox::new_atom(Month::new(8))); Atom<Month>).value(),
Month::new(8)
);
assert_eq!(
cast!(KBox::<Any>::from(KBox::new_atom(Time::new(9))); Atom<Time>).value(),
Time::new(9)
);
assert_eq!(
cast!(KBox::<Any>::from(KBox::new_atom(DateTime::new(10.0))); Atom<DateTime>).value(),
DateTime::new(10.0)
);
assert_eq!(
cast!(
KBox::<Any>::from(KBox::new_atom(Timestamp::from_raw(11)));
Atom<Timestamp>
)
.value(),
Timestamp::from_raw(11)
);
assert_eq!(
cast!(
KBox::<Any>::from(KBox::new_atom(Timespan::from_nanos(12)));
Atom<Timespan>
)
.value(),
Timespan::from_nanos(12)
);
assert_eq!(
cast!(KBox::<Any>::from(KBox::new_atom(symbol("Foo"))); Atom<Symbol>).value(),
symbol("Foo")
);
#[cfg(feature = "uuid")]
assert_eq!(
cast!(KBox::<Any>::from(KBox::new_atom(uuid::Uuid::from_bytes([12u8; 16]))); Atom<uuid::Uuid>).value(),
uuid::Uuid::from_bytes([12u8; 16])
);
}
}