use crate::prelude::*;
use crate::record::{ ExternalRecord, Record };
use tylisp::{
ops::{Is, list::{Find, Missing, Here, There, SubsetP}},
marker_traits::{Pass,Fail,List}};
#[cfg(feature="const")]
use tylisp::{
ops::set::{Set,},
};
use core::any::{Any, TypeId};
pub trait Header: Sized
+ Clone
+ List
+ 'static
+ for<'a> AsListRefs<'a> {
fn col_opt<C:Col>(&self)->Option<&C>;
fn has_col<C:Col>() -> bool;
fn is_disjoint<H:Header>() -> bool;
#[inline(always)]
fn col_ref<C:Col>(&self)->&C
where Self: HasCol<C> {
match self.col_opt() {
Some(c) => c,
None => unreachable!()
}
}
fn clone_from_rec<R>(rec:&R)->Self where
sexpr!{SubsetP, @Self, @R::Cols}: Pass,
R: Record
{
Self::clone_from_rec_unchecked(rec)
}
fn clone_from_rec_unchecked(_:&impl Record)->Self;
}
impl Header for HNil {
#[inline(always)]
fn col_opt<C:Col>(&self)->Option<&C> { None }
#[inline(always)]
fn has_col<C:Col>()->bool { false }
#[inline(always)]
fn is_disjoint<H:Header>()->bool { true }
fn clone_from_rec_unchecked(_:&impl Record)->Self { Self }
}
#[cfg(not(feature = "const"))]
impl<H:Col, T:Header> Header for HCons<H,T>
where sexpr!{Is, Missing, {Find, @H, @T}}: Pass,
Self: List
{
#[inline(always)]
fn col_opt<C:Col>(&self) -> Option<&C> {
if TypeId::of::<C>() == TypeId::of::<H>() {
match (&self.head as &dyn Any).downcast_ref() {
Some(x) => Some(x),
None => unreachable!()
}
} else {
self.tail.col_opt()
}
}
#[inline(always)]
fn has_col<C:Col>()->bool {
(TypeId::of::<C>() == TypeId::of::<H>()) || T::has_col::<C>()
}
#[inline(always)]
fn is_disjoint<Other:Header>() -> bool {
T::is_disjoint::<Other>() && !Other::has_col::<H>()
}
fn clone_from_rec_unchecked(r:&impl Record)->Self {
HCons { head: r.col_opt::<H>().unwrap().clone(),
tail: T::clone_from_rec_unchecked(r)
}
}
}
#[cfg(feature = "const")]
impl<H:Col, T:Header> Header for HCons<H,T>
where Self: List + Set {
#[inline(always)]
fn col_opt<C:Col>(&self) -> Option<&C> {
if TypeId::of::<C>() == TypeId::of::<H>() {
match (&self.head as &dyn Any).downcast_ref() {
Some(x) => Some(x),
None => unreachable!()
}
} else {
self.tail.col_opt()
}
}
fn has_col<C:Col>()->bool {
Self::contains::<C>()
}
}
pub trait HasCol<C:Col> {
type Index;
}
impl<C:Col, H:Header> HasCol<C> for H
where sexpr!{Is, Missing, {Find, @C, @H}}: Fail,
sexpr!{Find, @C, @H}: tylisp::engine::Eval,
Self: Take<eval!{Find,@C,@H}, Taken=C>
{
type Index = eval!{Find, @C, @H};
}
pub trait Take<Index> {
type Taken;
type Remainder;
fn take(self)->(Self::Taken, Self::Remainder);
}
impl<H,T> Take<Here> for HCons<H,T> {
type Taken = H;
type Remainder = T;
#[inline(always)]
fn take(self)->(H,T) { (self.head, self.tail) }
}
impl<H,T,More> Take<There<More>> for HCons<H,T>
where T:Take<More> {
type Taken = T::Taken;
type Remainder = HCons<H,T::Remainder>;
#[inline(always)]
fn take(self)->(Self::Taken, Self::Remainder) {
let (x, tail) = self.tail.take();
(x, sexpr_val!{self.head; tail})
}
}
pub trait ProjectAnyRefFrom<Other>: for<'a> ProjectRefFrom<'a, Other> {}
impl<T,Other:Header> ProjectAnyRefFrom<Other> for T
where T: for<'a> ProjectRefFrom<'a, Other> {}
pub trait ProjectFrom<Other>: Header + ProjectAnyRefFrom<Other> {
type Remainder: Header;
fn project_from(other:Other)->(Self, Self::Remainder);
}
pub trait ProjectRefFrom<'a, Other>: Header {
type AsRef: Record<Cols=Self>+'a;
fn ref_from(rec: impl ExternalRecord<'a, Cols=Other>)->Self::AsRef;
}
impl<Other: Header> ProjectFrom<Other> for HNil {
type Remainder = Other;
#[inline(always)]
fn project_from(other:Other)->(HNil, Other) { (HNil, other) }
}
impl<'a, Other: Header> ProjectRefFrom<'a, Other> for HNil {
type AsRef = HNil;
#[inline(always)]
fn ref_from(_: impl ExternalRecord<'a, Cols=Other>)->Self::AsRef { HNil }
}
impl<'a, Other: Header+HasCol<H>, H:Col, T:ProjectRefFrom<'a, Other>>
ProjectRefFrom<'a, Other> for HCons<H,T> where Self: Header {
type AsRef = sexpr!{&'a H; T::AsRef};
#[inline(always)]
fn ref_from(rec: impl ExternalRecord<'a, Cols=Other>)->Self::AsRef {
sexpr_val!{ rec.ext_col_ref(); T::ref_from(rec) }
}
}
impl<Other: Header, T:ProjectFrom<Other>, H:Col>
ProjectFrom<Other> for HCons<H,T>
where
T::Remainder: HasCol<H>
+ Take<<T::Remainder as HasCol<H>>::Index, Taken=H>,
Self: Header + for<'a> ProjectRefFrom<'a, Other>,
<T::Remainder as Take<< T::Remainder as HasCol<H> > ::Index >>::Remainder: Header,
{
type Remainder = <T::Remainder as Take<< T::Remainder as HasCol<H> > ::Index >>::Remainder;
#[inline(always)]
fn project_from(other:Other)->(Self, Self::Remainder) {
let (tail, x) = T::project_from(other);
let (head, remainder) = x.take();
(HCons { head, tail }, remainder)
}
}
pub trait AsListRefsStatic: for<'a> AsListRefs<'a> {}
impl<T> AsListRefsStatic for T where T:for<'a> AsListRefs<'a> {}
pub trait AsListRefs<'a> {
type AsRefs: Copy + Clone;
fn ref_from_ext_rec<R>(r:R)-><Self as AsListRefs<'a>>::AsRefs where
Self: Sized,
sexpr!{SubsetP, @Self, @R::Cols}: Pass,
R: ExternalRecord<'a>
{ <Self as AsListRefs<'a>>::ref_from_ext_rec_unchecked(r) }
fn ref_from_ext_rec_unchecked(_:impl ExternalRecord<'a>)->Self::AsRefs;
}
impl<'a> AsListRefs<'a> for HNil {
type AsRefs = HNil;
fn ref_from_ext_rec_unchecked(_:impl ExternalRecord<'a>)->Self::AsRefs { HNil }
}
impl<'a, H:Col, T:AsListRefs<'a>> AsListRefs<'a> for HCons<H,T> {
type AsRefs = HCons<&'a H, T::AsRefs>;
fn ref_from_ext_rec_unchecked(r:impl ExternalRecord<'a>)->Self::AsRefs {
HCons {
head: r.ext_col_opt::<H>().unwrap(),
tail: T::ref_from_ext_rec_unchecked(r)
}
}
}
#[test]
fn test_headers() {
col!{ A: usize }
col!{ B: usize }
col!{ C: usize }
assert_trait!{ HNil: Header };
assert_trait!{ sexpr!{A, B, C}: Header };
assert_trait!{ sexpr!{A, B, C}: HasCol<A> };
assert_trait!{ sexpr!{A, B, C}: HasCol<B> };
assert_trait!{ sexpr!{B, C, A}: Header };
assert_trait!{ sexpr!{A, C}: Header };
assert_trait!{ sexpr!{A, C}: ProjectFrom<sexpr!{B, C, A}>};
let (_ac, _b):(_,sexpr!{B}) = <sexpr!{A,C}>::project_from(sexpr_val!{A(2), B(3), C(4)});
}
#[test]
fn test_as_list_refs() {
col!{ A: usize }
col!{ B: usize }
col!{ C: usize }
let rec = sexpr_val!{A(1), B(2), C(3)};
let _: sexpr!{&A, &B, &C} = <sexpr!{A,B,C}>::ref_from_ext_rec(&rec);
}