use std::any::TypeId;
use std::collections::HashMap;
use hamelin_lib::func::def::ParameterBinding;
use crate::defs::register;
use crate::reverse_eval::domain::Constraint;
use crate::value::Value;
pub type EvalFn = Box<dyn Fn(ParameterBinding<Value>) -> anyhow::Result<Value> + Send + Sync>;
pub type ReverseFn = Box<
dyn Fn(&Constraint, ParameterBinding<Option<Value>>) -> anyhow::Result<Constraint>
+ Send
+ Sync,
>;
pub struct EvalRegistry {
eval_impls: HashMap<TypeId, EvalFn>,
reverse_impls: HashMap<TypeId, ReverseFn>,
}
impl std::fmt::Debug for EvalRegistry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EvalRegistry")
.field(
"eval_impls",
&format!("{} functions", self.eval_impls.len()),
)
.field(
"reverse_impls",
&format!("{} functions", self.reverse_impls.len()),
)
.finish()
}
}
impl EvalRegistry {
pub fn new() -> Self {
Self {
eval_impls: HashMap::new(),
reverse_impls: HashMap::new(),
}
}
pub fn register_eval<F: 'static>(
&mut self,
f: impl Fn(ParameterBinding<Value>) -> anyhow::Result<Value> + Send + Sync + 'static,
) {
self.eval_impls.insert(TypeId::of::<F>(), Box::new(f));
}
pub fn register_reverse<F: 'static>(
&mut self,
f: impl Fn(&Constraint, ParameterBinding<Option<Value>>) -> anyhow::Result<Constraint>
+ Send
+ Sync
+ 'static,
) {
self.reverse_impls.insert(TypeId::of::<F>(), Box::new(f));
}
pub fn eval(
&self,
type_id: TypeId,
bindings: ParameterBinding<Value>,
) -> Option<anyhow::Result<Value>> {
self.eval_impls.get(&type_id).map(|f| f(bindings))
}
pub fn reverse(
&self,
type_id: TypeId,
constraint: &Constraint,
bindings: ParameterBinding<Option<Value>>,
) -> Option<anyhow::Result<Constraint>> {
self.reverse_impls
.get(&type_id)
.map(|f| f(constraint, bindings))
}
}
impl Default for EvalRegistry {
fn default() -> Self {
let mut registry = Self::new();
register(&mut registry);
registry
}
}
#[macro_export]
macro_rules! null_propagate {
($value:expr) => {
match $value {
$crate::value::Value::Null => return Ok($crate::value::Value::Null),
v => v,
}
};
}
#[macro_export]
macro_rules! null_propagate_reverse {
($value:expr, $output_constraint:expr) => {
match $value {
$crate::value::Value::Null => {
return Ok(match $output_constraint {
$crate::reverse_eval::domain::Constraint::Equals(
$crate::value::Value::Null,
) => $crate::reverse_eval::domain::Constraint::Universal,
_ => $crate::reverse_eval::domain::Constraint::Empty,
});
}
v => v,
}
};
}