use std::convert::Infallible;
use crate::{
NameRef, error,
ext::{business::Balance, shop::Gtin},
runtime::{
State,
model::{Concept, Dir, Entity, Object, Split},
},
syntax::ast,
};
pub type Always<T> = Result<T, Infallible>;
pub trait Resolve<T, E>: Sized {
fn resolve(self, ctx: &State) -> Result<T, E>;
}
pub trait Emerge<S, E>: Sized {
fn emerge(src: S, ctx: &State) -> Result<Self, E>;
}
impl<S, T, E> Emerge<S, E> for T
where
S: Resolve<T, E>,
{
fn emerge(src: S, ctx: &State) -> Result<Self, E> {
src.resolve(ctx)
}
}
impl Resolve<Entity, error::UnknownEntity> for NameRef<'_> {
fn resolve(self, ctx: &State) -> Result<Entity, error::UnknownEntity> {
ctx.entities
.get(self)
.cloned()
.ok_or_else(|| error::UnknownEntity(self.to_owned()))
}
}
impl Resolve<Concept, error::UnknownConcept> for NameRef<'_> {
fn resolve(self, ctx: &State) -> Result<Concept, error::UnknownConcept> {
ctx.concepts
.get(self)
.cloned()
.ok_or_else(|| error::UnknownConcept(self.to_owned()))
}
}
impl Resolve<Concept, error::UnknownConceptGtin> for Gtin {
fn resolve(self, ctx: &State) -> Result<Concept, error::UnknownConceptGtin> {
ctx.concepts_gtin
.get(&self)
.cloned()
.ok_or(error::UnknownConceptGtin(self))
}
}
impl Resolve<Object, error::UnknownConceptGtin> for Gtin {
fn resolve(self, ctx: &State) -> Result<Object, error::UnknownConceptGtin> {
Concept::emerge(self, ctx).map(Concept::instantiate)
}
}
impl Resolve<Object, error::UnknownObject> for NameRef<'_> {
fn resolve(self, ctx: &State) -> Result<Object, error::UnknownObject> {
ctx.objects
.get(self)
.cloned()
.or_else(|| ctx.concepts.get(self).cloned().map(Concept::instantiate))
.ok_or_else(|| error::UnknownObject(self.to_owned()))
}
}
impl Resolve<Dir, error::Construction> for (NameRef<'_>, NameRef<'_>) {
fn resolve(self, ctx: &State) -> Result<Dir, error::Construction> {
let (source, target) = self;
let lookup = |side: &str| side.resolve(ctx).map_err(error::UnknownActor::Entity);
let dir = Dir::new(lookup(source)?, lookup(target)?)?;
Ok(dir)
}
}
impl Resolve<Balance, Infallible> for Dir {
fn resolve(self, ctx: &State) -> Always<Balance> {
let mut value = ctx
.balances
.get(&self.clone().into())
.cloned()
.unwrap_or_default();
value.align_to(&self);
Ok(Balance::new(self, value))
}
}
impl Resolve<Split, error::BothZero> for ast::Split {
fn resolve(self, _: &State) -> Result<Split, error::BothZero> {
let Self { from, to } = self;
Split::new(from, to)
}
}