mem-query 0.0.1

Relational algebra interface for Rust collections
use {
    crate::{
        record::{
            Record,
            FromRecord,
            FromRecordImpl,
            ExternalRecord,
            FromExternalRecord,
            proj
        },
        header::{Header, HasCol},
        col::Col,
    },
    tylisp::{
        HNil,
        sexpr, calc, calc_ty,
        engine::{Eval, Calc},
        ops::list::{Concat,Union},
    },
};

impl Record for () {
    type Cols = HNil;

    #[inline(always)]
    fn into_cols(self)->Self::Cols { HNil }
    #[inline(always)]
    fn clone_cols(&self)->Self::Cols { HNil }
    #[inline(always)]
    fn col_ref<C:Col>(&self)->&C where Self::Cols: HasCol<C> { unreachable!() }
    #[inline(always)]
    fn col_opt<C:Col>(&self)->Option<&C> { None }
}

impl<'a> ExternalRecord<'a> for () {
    #[inline(always)]
    fn ext_col_ref<C:Col>(&self)->&'a C where Self::Cols: HasCol<C> { unreachable!() }
    #[inline(always)]
    fn ext_col_opt<C:Col>(&self)->Option<&'a C> { None }
}
    

impl FromRecordImpl for () {
    type Cols = HNil;
    fn from_rec_raw(_: impl Record<Cols=Self::Cols>)->Self { () }
}

impl<'a> FromExternalRecord<'a> for () {
    type Cols = HNil;
    fn from_ext_rec_raw(_: impl ExternalRecord<'a, Cols=Self::Cols>)->Self { () }
}

impl<R:Record> Record for (R,) {
    type Cols = R::Cols;

    #[inline(always)]
    fn into_cols(self)->Self::Cols { self.0.into_cols() }
    #[inline(always)]
    fn clone_cols(&self)->Self::Cols { self.0.clone_cols() }
    #[inline(always)]
    fn col_ref<C:Col>(&self)->&C where Self::Cols: HasCol<C> { self.0.col_ref() }
    #[inline(always)]
    fn col_opt<C:Col>(&self)->Option<&C> { self.0.col_opt() }
}

impl<'a,R> ExternalRecord<'a> for (R,)
where R: ExternalRecord<'a> {
    #[inline(always)]
    fn ext_col_ref<C:Col>(&self)->&'a C where Self::Cols: HasCol<C> { self.0.ext_col_ref() }
    #[inline(always)]
    fn ext_col_opt<C:Col>(&self)->Option<&'a C> { self.0.ext_col_opt() }
}

impl<R:FromRecordImpl> FromRecordImpl for (R,) {
    type Cols = R::Cols;
    fn from_rec_raw(r: impl Record<Cols=Self::Cols>)->Self { (R::from_rec_raw(r),) }
}

impl<'a, R:FromExternalRecord<'a>> FromExternalRecord<'a> for (R,) {
    type Cols = R::Cols;
    fn from_ext_rec_raw(r: impl ExternalRecord<'a, Cols=Self::Cols>)->Self {
        (R::from_ext_rec_raw(r),) 
    }
}

impl<A:Record, B:Record> Record for (A,B)
where sexpr!{Concat, @A::Cols = a, @B::Cols = b}: Calc<sexpr!{(), A::Cols, B::Cols}>,
    calc_ty!{Concat, @A::Cols = a, @B::Cols = b}: Header
{
    type Cols = calc_ty!{Concat, @A::Cols = a, @B::Cols = b};

    #[inline(always)]
    fn into_cols(self)->Self::Cols {
        calc!{Concat, @A::Cols = self.0.into_cols(),
                      @B::Cols = self.1.into_cols()}
    }

    #[inline(always)]
    fn clone_cols(&self)->Self::Cols {
        calc!{Concat, @A::Cols = self.0.clone_cols(),
                      @B::Cols = self.1.clone_cols()}
    }

    #[inline(always)]
    fn col_ref<C:Col>(&self)->&C where Self::Cols: HasCol<C> {
        // Safety: A and B have a disjoint set of fields, because
        //         otherwise there would be a duplicate column in
        //         the contatenation of their headers, which would
        //         violate the `Cols: Header` bound
        match (self.0.col_opt(), self.1.col_opt()) {
            (Some(x), None) => x,
            (None, Some(x)) => x,
            _ => unreachable!()
        }
    }

    #[inline(always)]
    fn col_opt<C:Col>(&self)->Option<&C> {
        // Safety: A and B have a disjoint set of fields, because
        //         otherwise there would be a duplicate column in
        //         the contatenation of their headers, which would
        //         violate the `Cols: Header` bound
        match (self.0.col_opt(), self.1.col_opt()) {
            (Some(x), None) => Some(x),
            (None, Some(x)) => Some(x),
            (None, None)    => None,
            _ => unreachable!()
        }
    }
}

impl<'a,A,B> ExternalRecord<'a> for (A,B)
where Self: Record, A:ExternalRecord<'a>, B:ExternalRecord<'a> {
    #[inline(always)]
    fn ext_col_ref<C:Col>(&self)->&'a C where Self::Cols: HasCol<C> {
        match (self.0.ext_col_opt(), self.1.ext_col_opt()) {
            (Some(x), None) => x,
            (None, Some(x)) => x,
            _ => unreachable!()
        }
    }

    #[inline(always)]
    fn ext_col_opt<C:Col>(&self)->Option<&'a C> {
        match (self.0.ext_col_opt(), self.1.ext_col_opt()) {
            (Some(x), None) => Some(x),
            (None, Some(x)) => Some(x),
            _ => None
        }
    }
}

impl<A:FromRecordImpl, B:FromRecordImpl, H:Header> FromRecordImpl for (A,B)
where sexpr!{Concat, @A::Cols, @B::Cols}: Eval<Result = H>,
    A: FromRecord<H, Remainder = B::Cols>,
    B::Cols: Record<Cols = B::Cols>,
{
    type Cols = H;
    fn from_rec_raw(r: impl Record<Cols=Self::Cols>)->Self {
        let (a, remainder) = A::from_rec(r);
        let b = B::from_rec_raw(remainder);
        (a,b)
    }
}

impl<'a,A,B,H> FromExternalRecord<'a> for (A,B)
    where A: FromExternalRecord<'a>,
          B: FromExternalRecord<'a>,
          sexpr!{Union, @A::Cols, @B::Cols}: Eval<Result = H>,
          H: Header,
{
    type Cols = H;
    fn from_ext_rec_raw(r: impl ExternalRecord<'a, Cols=H>)->Self {
        (
            A::from_ext_rec_raw(proj::Projection::new_unchecked(r)),
            B::from_ext_rec_raw(proj::Projection::new_unchecked(r)),
        ) 
    }
}

// TODO: Wrap the following in a macro for longer tuples
impl<A,B,C,HH:Header> Record for (A,B,C)
where
    A: Record,
    B: Record,
    C: Record,
    (A, (B,C)): Record<Cols=HH>
{
    type Cols = HH;
    fn into_cols(self)->HH {
        let (a,b,c) = self;
        (a,(b,c)).into_cols()
    }

    fn col_opt<CC:Col>(&self)->Option<&CC> {
        let &(ref a, ref b, ref c) = self;
        None
            .or(a.col_opt())
            .or(b.col_opt())
            .or(c.col_opt())
    }
}

impl<'a,A,B,C> ExternalRecord<'a> for (A,B,C)
where Self: Record,
    A:ExternalRecord<'a>,
    B:ExternalRecord<'a>,
    C:ExternalRecord<'a>,
{
    fn ext_col_opt<CC:Col>(&self)->Option<&'a CC> {
        let &(ref a, ref b, ref c) = self;
        None
            .or(a.ext_col_opt())
            .or(b.ext_col_opt())
            .or(c.ext_col_opt())
    }
}

impl<A, B, C, H:Header> FromRecordImpl for (A,B,C)
where (A,(B,C)): FromRecordImpl<Cols=H>
{
    type Cols = H;
    fn from_rec_raw(r: impl Record<Cols=Self::Cols>)->Self {
        let (a, (b,c)) = FromRecordImpl::from_rec_raw(r);
        (a,b,c)
    }
}

impl<'a,A,B,C,HH:Header> FromExternalRecord<'a> for (A,B,C)
where (A,(B,C)): FromExternalRecord<'a, Cols=HH>
{
    type Cols = HH;
    fn from_ext_rec_raw(r: impl ExternalRecord<'a, Cols=HH>)->Self {
        let (a, (b,c)) = FromExternalRecord::from_ext_rec_raw(r);
        (a,b,c)
    }
}

#[test]
fn test_tuple_records() {
    col!{pub A: u32};
    col!{pub B: u32};
    col!{pub C: u32};
    col!{pub D: u32};

    assert_trait!{ (): Record<Cols=sexpr!{}> };
    assert_trait!{ (A,): Record<Cols=sexpr!{A}> };
    assert_trait!{ (A, B): Record<Cols=sexpr!{A,B}> };
    assert_trait!{ (A, (B,C)): Record<Cols=sexpr!{A,B,C}> };
    assert_trait!{ (A, B, C): Record<Cols=sexpr!{A,B,C}> };
    assert_trait!{ (std::sync::Arc<(A,D)>, (B,C)): Record<Cols=sexpr!{A,D,B,C}> };
    assert_trait!{ (A, &(B,C)): Record<Cols=sexpr!{A,B,C}> };

    use tylisp::sexpr_val;

    assert_eq!( ( ((A(1), B(2)), C(3) ), sexpr_val!{D(4)}),
                 <((A,B),C) as FromRecord<_>>::from_rec(sexpr_val!{B(2),C(3),D(4),A(1)}) );
}