use std::rc::Rc;
use super::{State, Unify};
use crate::{
core::{
Value,
Value::{Resolved, Var},
},
LVarList,
};
pub type ResolveFn = Box<dyn FnOnce(State) -> Option<State>>;
pub trait Constraint {
fn attempt(&self, state: &State) -> Result<ResolveFn, LVarList>;
}
pub fn resolve_1<A: Unify>(val: &Value<A>, state: &State) -> Result<Rc<A>, LVarList> {
match state.resolve(val) {
Resolved(a) => Ok(a),
Var(unresolved) => Err(LVarList::one(&unresolved)),
}
}
pub fn resolve_2<A: Unify, B: Unify>(
a: &Value<A>,
b: &Value<B>,
state: &State,
) -> Result<(Rc<A>, Rc<B>), LVarList> {
let a = state.resolve(a);
let b = state.resolve(b);
match (a, b) {
(Resolved(a), Resolved(b)) => Ok((a, b)),
(Var(var), _) => Err(LVarList::one(&var)),
(_, Var(var)) => Err(LVarList::one(&var)),
}
}
pub enum OneOfTwo<A: Unify, B: Unify> {
A(Rc<A>, Value<B>),
B(Value<A>, Rc<B>),
}
impl<A: Unify, B: Unify> OneOfTwo<A, B> {
pub fn resolve(a: &Value<A>, b: &Value<B>, state: &State) -> Result<OneOfTwo<A, B>, LVarList> {
let a = state.resolve(a);
let b = state.resolve(b);
match (a, b) {
(Resolved(a), b) => Ok(OneOfTwo::A(a, b)),
(a, Resolved(b)) => Ok(OneOfTwo::B(a, b)),
(Var(a), Var(b)) => Err(LVarList::two(&a, &b)),
}
}
}
pub enum TwoOfThree<A: Unify, B: Unify, C: Unify> {
AB(Rc<A>, Rc<B>, Value<C>),
BC(Value<A>, Rc<B>, Rc<C>),
AC(Rc<A>, Value<B>, Rc<C>),
}
impl<A: Unify, B: Unify, C: Unify> TwoOfThree<A, B, C> {
pub fn resolve(
a: &Value<A>,
b: &Value<B>,
c: &Value<C>,
state: &State,
) -> Result<TwoOfThree<A, B, C>, LVarList> {
let a = state.resolve(a);
let b = state.resolve(b);
let c = state.resolve(c);
match (a, b, c) {
(Resolved(a), Resolved(b), c) => Ok(TwoOfThree::AB(a, b, c)),
(a, Resolved(b), Resolved(c)) => Ok(TwoOfThree::BC(a, b, c)),
(Resolved(a), b, Resolved(c)) => Ok(TwoOfThree::AC(a, b, c)),
(Var(a), Var(b), _) => Err(LVarList::two(&a, &b)),
(Var(a), _, Var(c)) => Err(LVarList::two(&a, &c)),
(_, Var(b), Var(c)) => Err(LVarList::two(&b, &c)),
}
}
}