mem-query 0.0.1

Relational algebra interface for Rust collections
use crate::{
    record::{Record, FromRecord, Record},
    header::Header,
    relation::{Insert, Relation, RelationImpl},
    transaction::{Transaction,RevertableOp,UndoLog,Accessor,SubUndoLog},
    tylisp::{eval, sexpr, engine::Eval, ops::list::Concat},
    query::fallback::FallbackPlanner,
};

use either::Either;

#[derive(Default)]
struct Annotation<Rec, Rel> {
    rec: Option<Rec>,
    rel: Rel
}

impl<Rec:Record, Rel:RelationImpl> RelationImpl for Annotation<Rec,Rel>
where sexpr!{Concat, @Rec::Cols, @Rel::Cols}: Eval,
      eval!{Concat, @Rec::Cols, @Rel::Cols}: Header,
      sexpr!{Concat, @Rec::Cols, @Rel::FastCols}: Eval,
      eval!{Concat, @Rec::Cols, @Rel::FastCols}: Header,
{
    type Cols = eval!{Concat, @Rec::Cols, @Rel::Cols};
    type FastCols = eval!{Concat, @Rec::Cols, @Rel::FastCols};
    type Planner = FallbackPlanner;
}

// -----------------------
// Iterate over everything
// -----------------------

impl<'a, Rec, Rel> IntoIterator for &'a Annotation<Rec, Rel>
where Rel: Relation<'a>,
{
    type IntoIter = Either<
        std::iter::Empty<Self::Item>,
        AnnotationIter<'a, Rec, (&'a Rec, Rel::QueryRow)>
    >;
    type Item = (&'a Rec, Rel::QueryRow);

    fn into_iter(self)->Self::IntoIter {
        match self.rec {
            None => Either::Left(std::iter::empty()),
            Some(ref note) => Either::Right(
                AnnotationIter(note, self.rel.iter_all())
            ),
        }
    }
}

pub struct AnnotationIter<'a, Note, Inner>(&'a Note, Inner);

impl<'a, N, I:Iterator> Iterator for AnnotationIter<'a, N, I> {
    type Item = (&'a N, I::Item);
    fn next(&mut self)->Option<Self::Item> {
        self.1.next().map(|x| (self.0, x))
    }
}



impl<Rec,Rel:RelationImpl,H:Header> Insert<H> for Annotation<Rec,Rel> 
where Rec: FromRecord<H> + Eq + Record,
      Rec::Remainder: Record,
      Rel: Insert<<Rec::Remainder as Record>::Cols> + RelationImpl,
      Self: RelationImpl
{
    type Remainder = Rel::Remainder;
    type Op = SaveAnnotation<Rec, Rel::Op>;
    fn insert_op<FromRec:Record<Cols=H>>(inp:FromRec) -> (Self::Op, Self::Remainder) {
        let (note, remainder) = Rec::from_rec(inp);
        let (op, remainder) = Rel::insert_op(remainder);
        
        (SaveAnnotation { note, insert_op:op }, remainder)
    }
}

pub struct SaveAnnotation<Note,Op> {
    note: Note,
    insert_op: Op,
}

impl<Note, Op, Subrel> RevertableOp<Annotation<Note,Subrel>> for SaveAnnotation<Note,Op>
where
    Op: RevertableOp<Subrel>,
    Note: Eq,
{
    type Log = (SubUndoLog<InSubrel, (Op::Log, ()), Subrel>,
                (Option<ClearNote>, ()));
    type Err = Either<Either<Op::Err, std::convert::Infallible>,
                      Either<AnnotationMismatch, std::convert::Infallible>>;

    fn apply(self, idx: &mut Annotation<Note,Subrel>)->Result<Self::Log, Self::Err> {
        let SaveAnnotation { note, insert_op } = self;
        Transaction::start(idx)
            .apply(EnsureNote(note))
            .subtransaction(InSubrel, |xact|
                xact.apply(insert_op)
            )
            .into_undo_log()
    }
}

pub struct EnsureNote<Note>(Note);
pub struct InSubrel;
pub struct ClearNote;

#[derive(thiserror::Error, Debug)]
#[error("Insert would override previous annotation.")]
pub struct AnnotationMismatch;

impl<Note,R> RevertableOp<Annotation<Note,R>> for EnsureNote<Note>
where Note: Eq
{
    type Err = AnnotationMismatch;
    type Log = Option<ClearNote>;
    fn apply(self, target: &mut Annotation<Note, R>) -> Result<Self::Log, Self::Err> {
        match target.rec {
            None => { target.rec = Some(self.0); Ok(Some(ClearNote)) }
            Some(ref note) => {
                if note == &self.0 { Ok(None) }
                else { Err(AnnotationMismatch) }
            }
        }
    }
}


impl<N,R> UndoLog<Annotation<N,R>> for ClearNote {
    fn revert(self, target: &mut Annotation<N,R>) {
        target.rec = None;
    }
}

impl<N,R> Accessor<Annotation<N,R>> for InSubrel {
    type Dest = R;
    fn access<F,O>(&self, src: &mut Annotation<N,R>, body:F)->O
        where F: FnOnce(&mut Self::Dest)->O {
        body(&mut src.rel)
    }
}