use ct_if::IfCheck;
use std::marker::PhantomData;
use std::ops::Add;
use typenum::{U0, U1, U1024, Unsigned};
pub type CTConsOffsetInvalid = U1024;
pub trait CTCons {
type Head: 'static;
type Tail: CTCons + 'static;
}
#[doc(hidden)]
pub trait CTCounter {
type Counter: Unsigned + Add<U1>;
}
pub trait CTAppend<X> {
type Output: CTCons;
}
pub trait CTSized {
type Size: Unsigned;
fn size() -> usize;
}
pub trait CTOffset<Target> {
type Offset: Unsigned + Add<U1>;
fn offset() -> usize;
}
pub trait CTOffsetExt {
fn offset_of<X>() -> usize
where
Self: CTOffset<X>;
}
pub enum CTConsTerm {}
pub struct Cons<Tail, Head>
where
Tail: CTCons,
{
_marker: PhantomData<(Tail, Head)>,
}
ctif_specialize!{
trait_name = CTIfOffset,
conditions = [Unsigned, Add<U1>]
}
impl CTCons for CTConsTerm {
type Head = ();
type Tail = CTConsTerm;
}
impl<X> CTAppend<X> for CTConsTerm
where
X: 'static,
{
type Output = Cons<CTConsTerm, X>;
}
impl CTCounter for CTConsTerm {
type Counter = U0;
}
impl<Target> CTOffset<Target> for CTConsTerm {
type Offset = CTConsOffsetInvalid;
fn offset() -> usize {
Self::Offset::to_usize()
}
}
impl<Tail, Head> CTCons for Cons<Tail, Head>
where
Tail: CTCons + 'static,
Head: 'static,
{
type Head = Head;
type Tail = Tail;
}
impl<X, Tail, Head> CTAppend<X> for Cons<Tail, Head>
where
X: 'static,
Tail: CTCons + 'static,
Head: 'static,
{
type Output = Cons<Cons<Tail, Head>, X>;
}
impl<Tail, Head> CTCounter for Cons<Tail, Head>
where
Tail: CTCounter + CTCons,
<<Tail as CTCounter>::Counter as Add<U1>>::Output: Unsigned + Add<U1>,
{
type Counter = <Tail::Counter as Add<U1>>::Output;
}
impl<Tail, Head> CTSized for Cons<Tail, Head>
where
Tail: CTCons,
Self: CTCounter,
{
type Size = <Self as CTCounter>::Counter;
fn size() -> usize {
Self::Size::to_usize()
}
}
impl<Target, Tail, Head> CTOffset<Target> for Cons<Tail, Head>
where
Tail: CTCons + CTOffset<Target> + CTCounter,
{
type Offset = <IfCheck<Head> as CTIfOffset<Target, Tail::Counter, Tail::Offset>>::Path;
fn offset() -> usize {
Self::Offset::to_usize()
}
}
impl<C> CTOffsetExt for C
where
C: CTCons,
{
fn offset_of<X>() -> usize
where
C: CTOffset<X>,
{
C::offset()
}
}
#[cfg(test)]
mod test {
use super::*;
type Single = Cons<CTConsTerm, i32>;
type Middle = Cons<Cons<Cons<CTConsTerm, i32>, u32>, f32>;
type Big = Cons<Cons<Cons<Cons<Cons<CTConsTerm, i32>, u32>, f32>, i64>, usize>;
#[test]
fn sized_single() {
let size = Single::size();
assert_eq!(size, 1);
}
#[test]
fn sized_big() {
let size = Big::size();
assert_eq!(size, 5);
}
fn offset_for<CTCons, Target>() -> usize
where
CTCons: CTOffset<Target>,
{
CTCons::offset()
}
#[test]
fn offset_first() {
let offset = offset_for::<Single, i32>();
assert_eq!(offset, 0);
}
#[test]
fn offset_middle() {
let offset = offset_for::<Middle, u32>();
assert_eq!(offset, 1);
}
#[test]
fn offset_end() {
let offset = offset_for::<Big, usize>();
assert_eq!(offset, 4);
}
#[test]
fn offset_unknown() {
let offset = offset_for::<Single, f32>();
assert_eq!(offset, CTConsOffsetInvalid::to_usize());
}
}