mem-query 0.0.1

Relational algebra interface for Rust collections
use crate::{
    relation::{RelationImpl,Queryable,Relation,QueryOutput},
    header::{Header, ProjectFrom},
    query::{
        QueryPlanImpl,
        request::{QueryRequest, NewRequest},
    },
    record::{Record, proj::Projection},
};

use tylisp::{
    sexpr, defun, defun_nocalc, HNil,
    engine::{Eval},
    ops::{
        Phantom, Ret,
        list::{BuildList,Intersect},
    },
};

use std::marker::PhantomData;

pub struct ProjectedRel<Rel, Cols> {
    pub(crate) source: Rel,
    pub(crate) cols: PhantomData<Cols>,
}

impl<Rel, Cols, FastCols> RelationImpl for ProjectedRel<Rel,Cols>
where
    Rel: RelationImpl,
    Cols: Header,
    Cols: ProjectFrom<Rel::Cols>,
    FastCols: Header,
    sexpr!{Intersect, {Phantom, @Cols}, @Rel::FastCols}: Eval<Result=FastCols>,
{
    type Cols = Cols;
    type FastCols = FastCols;
    type Planner = Planner;
}

impl<'a,Rel,Cols> QueryOutput<'a> for ProjectedRel<Rel,Cols>
where
    Rel: QueryOutput<'a>,
    Cols: Header,
    Cols: ProjectFrom<Rel::Cols>,
    Self: RelationImpl<Cols = Cols>,
{
    type QueryRow = Projection<Rel::QueryRow, Cols>;
}

#[derive(Default,Copy,Clone,Debug)]
pub struct IterAllQuery;
defun!{ IterAllQuery {
    (Src: RelationImpl, Cols: Header) {_:PhantomData<Src>, _:PhantomData<Cols>} =>
        {NewRequest, {BuildList},
                     {Phantom, @HNil}};
}}

impl<'a,R,C> IntoIterator for &'a ProjectedRel<R,C>
where
    ProjectedRel<R,C>: RelationImpl,
    R: Relation<'a>,
    C: ProjectFrom<R::Cols>,
{
    type Item = Projection<R::QueryRow, C>;
    type IntoIter = impl Iterator<Item = Self::Item>;
    fn into_iter(self)->Self::IntoIter {
        self.source.iter_all().map(|r| r.project())
    }
}

pub struct IterAs<'a, Src,Out> {
    pub(crate) source: &'a Src,
    pub(crate) target: PhantomData<Out>
}

impl<'a, Src,Out:'a> IntoIterator for IterAs<'a, Src,Out>
where
    Out: crate::record::FromExternalRecord<'a>,
    Src: Relation<'a>,
    Out::Cols: ProjectFrom<Src::Cols>,
{
    type Item = Out;
    type IntoIter = impl Iterator<Item = Out>;
    fn into_iter(self)->Self::IntoIter {
        self.source.iter_all().map(Out::from_ext_rec)
    }
}

#[derive(Default,Copy,Clone,Debug)]
pub struct Planner;
defun_nocalc!{() Planner {
    ('a, Rel, Req) {_: &'a Rel, _: Req} => {Ret, @ProjectedRelPlan<'a, Rel, Req>};
}}

pub struct ProjectedRelPlan<'a, Rel, Req> {
    proj: &'a Rel,
    req: Req,
}

impl<'a, Rel, Req, Cols> IntoIterator for ProjectedRelPlan<'a, ProjectedRel<Rel,Cols>, Req>
where
    Cols: Header,
    Req: QueryRequest + 'a,
    Rel: Queryable<'a, Req>,
    Cols: ProjectFrom<Rel::Cols>
{
    type Item = Projection<Rel::QueryRow, Cols>;
    type IntoIter = impl Iterator<Item = Self::Item>;
    fn into_iter(self)->Self::IntoIter {
        self.proj.source.query(self.req).map(|r| r.project())
    }
}

impl<'a, Rel, Req> QueryPlanImpl<'a, Rel, Req> for ProjectedRelPlan<'a, Rel, Req>
where
{
    fn prepare(proj: &'a Rel, req: Req)->Self {
        ProjectedRelPlan { proj, req }
    }
}

#[test]
fn test_projected_rel() {
    col!{A: usize}
    col!{B: usize}

    use crate::relation::{Relation,SelfQuery,OpaqueRel};
    use crate::record::Record;
    let source: OpaqueRel<Vec<(A,B)>> = OpaqueRel::new(vec![ (A(3), B(6)), (A(5), B(2)), (A(3), B(7)) ]);

    let result: Vec<B> = source.as_ref().where_eq(A(3)).project::<sexpr!{B}>().iter_all().map(|rec| rec.into_cols().head).collect();
    assert_eq!(vec![B(6), B(7)], result);
    let result: Vec<B> = source.where_eq(A(3)).project::<sexpr!{B}>().iter_all().map(|rec| rec.into_cols().head).collect();
    assert_eq!(vec![B(6), B(7)], result);
}

#[test]
fn test_iter_as() {
    col!{A: usize}
    col!{B: usize}

    use crate::relation::{Relation,OpaqueRel};
    use crate::record::Record;
    let source: OpaqueRel<Vec<(A,B)>> = OpaqueRel::new(vec![ (A(3), B(6)), (A(5), B(2)), (A(3), B(7)) ]);

    let result: Vec<B> = source.as_ref().where_eq(A(3)).iter_as().collect();
    assert_eq!(vec![B(6), B(7)], result);

    let result: Vec<B> = source.where_eq(A(3)).iter_as().collect();
    assert_eq!(vec![B(6), B(7)], result);
}

#[test] fn test_iter_as_example() {
    col!{ProjectId: usize}
    col!{QtyCommitted: usize}

    let rel = vec![
        (ProjectId(1), QtyCommitted(3)),
        (ProjectId(2), QtyCommitted(5)),
        (ProjectId(3), QtyCommitted(7)),
    ];

    let mut total = 0;
    for &QtyCommitted(qty) in rel.iter_as() {
        total += qty;
    }
    assert_eq!(15, total);
}