use std::{array::IntoIter, mem};
use crate::{
Name, NameRef,
aux::{Owned, Stack},
error,
ext::{business::Money, math::Natural, shop::Gtin},
};
#[derive(Owned!)]
pub enum Actor {
Entity(Entity),
Concept(Concept),
Object(Object),
}
#[derive(Stack!)]
pub enum ActorKind {
Entity,
Concept,
Object,
}
#[derive(Owned!)]
pub struct Entity {
pub(super) name: Name,
}
impl Entity {
#[must_use]
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 {
#[must_use]
pub fn name(&self) -> NameRef<'_> {
&self.name
}
#[must_use]
pub fn default_price(&self) -> Option<&Money> {
self.default_price.as_ref()
}
#[must_use]
pub fn gtin(&self) -> Option<Gtin> {
self.gtin
}
pub fn instantiate(self) -> Object {
Object {
name: None,
parent: Some(self),
}
}
}
#[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(Name::as_ref)
}
#[must_use]
pub fn parent(&self) -> Option<&Concept> {
self.parent.as_ref()
}
}
#[derive(Owned!)]
pub struct Dir {
source: Entity,
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 })
}
#[must_use]
pub fn source(&self) -> &Entity {
&self.source
}
#[must_use]
pub fn target(&self) -> &Entity {
&self.target
}
#[must_use]
pub fn would_reorder(&self) -> bool {
self.source > self.target
}
pub fn flip(&mut self) {
mem::swap(&mut self.source, &mut self.target);
}
pub fn flipped(mut self) -> Self {
self.flip();
self
}
#[must_use]
pub fn to_pair(self) -> (Pair, bool) {
let did_reorder = self.would_reorder();
(self.into(), did_reorder)
}
}
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 })
}
#[must_use]
pub fn a(&self) -> &Entity {
&self.a
}
#[must_use]
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 Split {
source: Natural,
target: Natural,
}
impl Split {
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 })
}
#[must_use]
pub fn denominator(self) -> Natural {
self.source + self.target
}
#[must_use]
pub fn apply(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 Split {
fn default() -> Self {
Self {
source: 0u8.into(),
target: 1u8.into(),
}
}
}