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;
}
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)
}
}