use itertools::Itertools;
use std::fmt::Debug;
use petgraph::algo::toposort;
use petgraph::dot::{Config, Dot};
use petgraph::prelude::StableGraph;
use petgraph::{Directed, Outgoing};
use partiql_value::Value;
use crate::env::basic::MapBindings;
use crate::env::Bindings;
use petgraph::graph::NodeIndex;
use crate::error::{EvalErr, EvaluationError};
use petgraph::visit::EdgeRef;
use crate::eval::evaluable::Evaluable;
pub mod evaluable;
pub mod expr;
#[derive(Debug)]
pub struct EvalPlan(pub StableGraph<Box<dyn Evaluable>, u8, Directed>);
impl Default for EvalPlan {
fn default() -> Self {
Self::new()
}
}
impl EvalPlan {
fn new() -> Self {
EvalPlan(StableGraph::<Box<dyn Evaluable>, u8, Directed>::new())
}
pub fn execute_mut(&mut self, bindings: MapBindings<Value>) -> Result<Evaluated, EvalErr> {
let ctx: Box<dyn EvalContext> = Box::new(BasicContext { bindings });
let sorted_ops = toposort(&self.0, None);
match sorted_ops {
Ok(ops) => {
let plan_graph = &mut self.0;
let mut result = None;
for idx in ops.into_iter() {
let src = plan_graph
.node_weight_mut(idx)
.expect("Error in retrieving node");
result = src.evaluate(&*ctx);
let destinations: Vec<(usize, (u8, NodeIndex))> = plan_graph
.edges_directed(idx, Outgoing)
.map(|e| (*e.weight(), e.target()))
.enumerate()
.collect_vec();
let branches = destinations.len();
for (i, (branch_num, dst_id)) in destinations {
let res = if i == branches - 1 {
result.take()
} else {
result.clone()
}
.expect("Error in retrieving source value");
let dst = plan_graph
.node_weight_mut(dst_id)
.expect("Error in retrieving node");
dst.update_input(res, branch_num);
}
}
let result = result.expect("Error in retrieving eval output");
Ok(Evaluated { result })
}
Err(e) => Err(EvalErr {
errors: vec![EvaluationError::InvalidEvaluationPlan(format!(
"Malformed evaluation plan detected: {e:?}"
))],
}),
}
}
pub fn to_dot_graph(&self) -> String {
format!("{:?}", Dot::with_config(&self.0, &[Config::EdgeNoLabel]))
}
}
pub type EvalResult = Result<Evaluated, EvalErr>;
pub struct Evaluated {
pub result: Value,
}
pub trait EvalContext {
fn bindings(&self) -> &dyn Bindings<Value>;
}
#[derive(Default, Debug)]
pub struct BasicContext {
bindings: MapBindings<Value>,
}
impl BasicContext {
pub fn new(bindings: MapBindings<Value>) -> Self {
BasicContext { bindings }
}
}
impl EvalContext for BasicContext {
fn bindings(&self) -> &dyn Bindings<Value> {
&self.bindings
}
}