use crate::{
runtime::cmd,
syntax::ast::{self, Stmt},
};
use super::{
Runtime,
cmd::{Balance, Command, Deliver, Pay},
error,
model::{Dir, Product, Ratio},
};
impl Runtime {
pub fn repr<T, U>(&self, source: T) -> Result<U, error::Repr>
where
U: Repr<T>,
{
U::repr(source, self)
}
}
pub trait Repr<T>: Sized {
fn repr(source: T, rt: &Runtime) -> Result<Self, error::Repr>;
}
impl<T, U> Repr<T> for U
where
T: Into<U>,
{
fn repr(source: T, _: &Runtime) -> Result<Self, error::Repr> {
Ok(source.into())
}
}
impl Repr<ast::Stmt> for Command {
fn repr(source: ast::Stmt, rt: &Runtime) -> Result<Self, error::Repr> {
use cmd::Command as Cmd;
let cmd = match source {
Stmt::Create(cmd) => Cmd::Create(match cmd.who {
ast::Actor::Entity(entity) => cmd::Create::Entity(entity.into()),
ast::Actor::Concept(concept) => cmd::Create::Concept(concept.into()),
ast::Actor::Object(object) => cmd::Create::Object(rt.repr(object)?),
}),
Stmt::Transfer(cmd) => match cmd {
ast::Transfer::Pay(cmd) => Cmd::Pay(rt.repr(cmd)?),
ast::Transfer::Deliver(cmd) => Cmd::Deliver(rt.repr(cmd)?),
},
Stmt::Analyze(cmd) => match cmd {
ast::Analyze::Balance(cmd) => Cmd::Balance(rt.repr(cmd)?),
},
};
Ok(cmd)
}
}
impl From<ast::Entity> for cmd::Entity {
fn from(ast::Entity { name }: ast::Entity) -> Self {
Self { name: name.into() }
}
}
impl From<ast::Concept> for cmd::Concept {
fn from(
ast::Concept {
name,
default_price,
gtin,
}: ast::Concept,
) -> Self {
Self {
name: name.into(),
default_price,
gtin,
}
}
}
impl Repr<ast::Object> for cmd::Object {
fn repr(source: ast::Object, rt: &Runtime) -> Result<Self, error::Repr> {
Ok(Self {
name: source.name.into(),
parent: source
.parent
.map(|parent| rt.get_concept(parent.as_ref()).cloned())
.transpose()
.map_err(error::UnknownActor::Concept)?,
})
}
}
impl Repr<ast::Pay> for Pay {
fn repr(source: ast::Pay, rt: &Runtime) -> Result<Self, error::Repr> {
Ok(Self {
amount: source.amount,
who: rt.repr(source.who)?,
})
}
}
impl Repr<ast::Deliver> for Deliver {
fn repr(
ast::Deliver {
what,
who,
price,
split,
}: ast::Deliver,
rt: &Runtime,
) -> Result<Self, error::Repr> {
let product: Product = rt.repr(what)?;
let price =
price
.map_or_else(|| product.default_price().cloned(), Ok)?;
Ok(Self {
who: rt.repr(who)?,
price,
split: split
.map(|split| rt.repr(split))
.transpose()?
.unwrap_or_default(),
})
}
}
impl Repr<ast::Balance> for Balance {
fn repr(source: ast::Balance, rt: &Runtime) -> Result<Self, error::Repr> {
Ok(Self {
between: rt.repr(source.between)?,
})
}
}
impl Repr<ast::Dir> for Dir {
fn repr(
ast::Dir {
source: from,
target: to,
}: ast::Dir,
rt: &Runtime,
) -> Result<Self, error::Repr> {
rt.get_dir(from.as_ref(), to.as_ref())
}
}
impl Repr<ast::Ratio> for Ratio {
fn repr(ast::Ratio { left, right }: ast::Ratio, _: &Runtime) -> Result<Self, error::Repr> {
Self::new(left, right).map_err(error::Repr::BothZero)
}
}
impl Repr<ast::Product> for Product {
fn repr(source: ast::Product, rt: &Runtime) -> Result<Self, error::Repr> {
match source {
ast::Product::Id(gtin) => Ok(Product::Concept(
rt.get_concept_by_gtin(>in)
.map_err(error::UnknownActor::ConceptGtin)?
.clone(),
)),
ast::Product::Name(name) => {
let name = name.as_ref();
let product = match (rt.get_object(name), rt.get_concept(name)) {
(Ok(object), _) => Product::Object(object.clone()),
(_, Ok(concept)) => Product::Concept(concept.clone()),
(Err(_), Err(_)) => {
return Err(error::UnknownActor::ProductName(error::UnknownProductName(
name.to_owned(),
)))?;
}
};
Ok(product)
}
}
}
}