use std::{array::IntoIter, mem};
use crate::{
Map,
aux::{NotOrd, Owned},
ext::{Balance, Gtin, Money, Natural},
};
use super::{
cmd::{Name, NameRef},
error::{
self, PriceUnspecified, UnknownActor, UnknownConcept, UnknownConceptGtin, UnknownEntity,
UnknownObject,
},
};
#[derive(NotOrd!, Default)]
pub struct State {
pub entities: Map<Name, Entity>,
pub concepts: Map<Name, Concept>,
pub concepts_gtin: Map<Gtin, Concept>,
pub objects: Map<Name, Object>,
pub balances: Map<Pair, Balance>,
}
impl State {
pub fn get_entity(&self, name: NameRef) -> Result<&Entity, UnknownEntity> {
self.entities
.get(name)
.ok_or_else(|| UnknownEntity(name.to_owned()))
}
pub fn get_concept(&self, name: NameRef) -> Result<&Concept, UnknownConcept> {
self.concepts
.get(name)
.ok_or_else(|| UnknownConcept(name.to_owned()))
}
pub fn get_concept_by_gtin(&self, gtin: &Gtin) -> Result<&Concept, UnknownConceptGtin> {
self.concepts_gtin
.get(gtin)
.ok_or(UnknownConceptGtin(*gtin))
}
pub fn get_object(&self, name: NameRef) -> Result<&Object, UnknownObject> {
self.objects
.get(name)
.ok_or_else(|| UnknownObject(name.to_owned()))
}
pub fn get_dir(&self, source: NameRef, target: NameRef) -> Result<Dir, error::Repr> {
let lookup = |side| self.get_entity(side).map_err(UnknownActor::Entity).cloned();
let dir = Dir::new(lookup(source)?, lookup(target)?)?;
Ok(dir)
}
pub fn balance(&self, dir: Dir) -> Balance {
let mut bal = self
.balances
.get(&dir.clone().into())
.cloned()
.unwrap_or(Balance(0.into()));
bal.take_order(dir);
bal
}
}
#[derive(Owned!)]
pub struct Entity {
pub(super) name: Name,
}
impl Entity {
pub fn name(&self) -> NameRef {
&self.name
}
}
#[derive(Owned!)]
pub struct Concept {
pub(super) name: Name,
pub(super) default_price: Option<Money>,
pub(super) gtin: Option<Gtin>,
}
impl Concept {
pub fn name(&self) -> NameRef {
&self.name
}
pub fn default_price(&self) -> Option<&Money> {
self.default_price.as_ref()
}
pub fn gtin(&self) -> Option<Gtin> {
self.gtin
}
}
#[derive(Owned!)]
pub struct Object {
pub(super) name: Option<Name>,
pub(super) parent: Option<Concept>,
}
impl Object {
pub fn name(&self) -> Option<NameRef> {
self.name.as_ref().map(String::as_ref)
}
pub fn parent(&self) -> Option<&Concept> {
self.parent.as_ref()
}
pub fn new(name: Name) -> Self {
Self {
name: Some(name),
parent: None,
}
}
}
#[derive(Owned!)]
pub enum Product {
Concept(Concept),
Object(Object),
}
impl Product {
pub fn default_price(&self) -> Result<&Money, PriceUnspecified> {
let err = || {
Err(PriceUnspecified {
product: self.clone(),
})
};
let (Product::Concept(concept)
| Product::Object(Object {
parent: Some(concept),
..
})) = self
else {
return err();
};
let Some(default_price) = concept.default_price() else {
return err();
};
Ok(default_price)
}
}
#[derive(Owned!)]
pub struct Dir {
pub(super) source: Entity,
pub(super) target: Entity,
}
impl Dir {
pub fn new(source: Entity, target: Entity) -> Result<Self, error::Same> {
if source == target {
return Err(error::Same(source, target));
}
Ok(Self { source, target })
}
pub fn source(&self) -> &Entity {
&self.source
}
pub fn target(&self) -> &Entity {
&self.target
}
pub fn would_reorder(&self) -> bool {
self.source > self.target
}
pub fn flip(&mut self) {
mem::swap(&mut self.source, &mut self.target);
}
}
impl From<Dir> for Pair {
fn from(Dir { source, target }: Dir) -> Self {
Self::new(source, target).unwrap()
}
}
impl From<Dir> for [Entity; 2] {
fn from(Dir { source, target }: Dir) -> Self {
[source, target]
}
}
impl IntoIterator for Dir {
type Item = Entity;
type IntoIter = IntoIter<Entity, 2>;
fn into_iter(self) -> Self::IntoIter {
<[Entity; 2]>::from(self).into_iter()
}
}
#[derive(Owned!)]
pub struct Pair {
pub(super) a: Entity,
pub(super) b: Entity,
}
impl Pair {
pub fn new(a: Entity, b: Entity) -> Result<Self, error::Same> {
let dir = Dir::new(a, b)?;
let reorder = dir.would_reorder();
let [a, b] = dir.into();
let (a, b) = if reorder { (b, a) } else { (a, b) };
Ok(Self { a, b })
}
pub fn a(&self) -> &Entity {
&self.a
}
pub fn b(&self) -> &Entity {
&self.b
}
}
impl From<Pair> for [Entity; 2] {
fn from(Pair { a, b }: Pair) -> Self {
[a, b]
}
}
impl IntoIterator for Pair {
type Item = Entity;
type IntoIter = IntoIter<Entity, 2>;
fn into_iter(self) -> Self::IntoIter {
<[Entity; 2]>::from(self).into_iter()
}
}
#[derive(Owned!)]
pub struct Ratio {
source: Natural,
target: Natural,
}
impl Ratio {
pub fn new(source: Natural, target: Natural) -> Result<Self, error::BothZero> {
if source == Natural::ZERO && target == Natural::ZERO {
return Err(error::BothZero);
}
Ok(Self { source, target })
}
pub fn denominator(self) -> Natural {
self.source + self.target
}
pub fn split(self, full: Money) -> (Money, Money) {
let chunk = full.0.clone() / self.clone().denominator();
let source = self.source * chunk;
let target = full.0 - source.clone();
(Money(source), Money(target))
}
}
impl Default for Ratio {
fn default() -> Self {
Self {
source: 0u8.into(),
target: 1u8.into(),
}
}
}