nyandere 0.1.2

i help with keeping track of purchases. meow
Documentation
//! Live actors, bundled, cuddled and wrapped up into [`State`].
//!
//! # On field privacy
//!
//! The fields are all private.
//! Why?
//! The answer is type safety.
//! Actors can only be created by issuing a [`Create`] command
//! to the runtime.
//! This implies that the runtime doesn't need to take care of
//! returning errors about non-existent
//! [`Entity`]ies, [`Concept`]s or [`Object`]s:
//! if there is one, it has to exist and hence be created at some point.
//!
//! [`Create`]: crate::cmd::create::Create

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,
}

/// Someone who holds money and deliver things.
#[derive(Owned!)]
pub struct Entity {
    pub(super) name: Name,
}

impl Entity {
    #[must_use]
    pub fn name(&self) -> NameRef<'_> {
        &self.name
    }
}

/// Designed idea of [`Object`]s.
#[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),
        }
    }
}

/// Physically holdable something.
///
/// May also be an anonymous instance of a [`Concept`].
#[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()
    }
}

/// **Directed** edge between 2 different [`Entity`]ies.
#[derive(Owned!)]
pub struct Dir {
    source: Entity,
    target: Entity,
}

impl Dir {
    /// Tries to construct a directed edge from `source` to `target`.
    ///
    /// # Errors
    ///
    /// Returns a [`SameError`] iff the two entities are the same.
    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
    }

    /// Returns [`true`]
    /// iff converting to a [`Pair`]
    /// would put `target` as first argument
    /// and `source` as second,
    /// otherwise the other way around.
    #[must_use]
    pub fn would_reorder(&self) -> bool {
        self.source > self.target
    }

    /// Exchanges `source` and `target`.
    pub fn flip(&mut self) {
        mem::swap(&mut self.source, &mut self.target);
    }

    pub fn flipped(mut self) -> Self {
        self.flip();
        self
    }

    /// Returns a [`Pair`]
    /// as well as a boolean on
    /// whether or not the arguments have been swapped.
    #[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()
    }
}

/// **Undirected** edge between 2 different [`Entity`]ies.
#[derive(Owned!)]
pub struct Pair {
    // invariant: a <= b
    pub(super) a: Entity,
    pub(super) b: Entity,
}

impl Pair {
    /// Tries to construct an **undirected** edge between `a` and `b`.
    /// The order does not matter:
    /// the pair `a`, `b` is equivalent to the pair `b`, `a`.
    ///
    /// # Errors
    ///
    /// Returns a [`SameError`] iff the two entities are the same.
    pub fn new(a: Entity, b: Entity) -> Result<Self, error::Same> {
        // might seem needlessly complicated
        // but i want to parametrize this as much as possible
        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()
    }
}

/// Split an amount between 2 parties.
#[derive(Owned!)]
pub struct Split {
    // invariant: at least one of {source,target} is non-zero
    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 })
    }

    /// How many parts is this ratio over?
    #[must_use]
    pub fn denominator(self) -> Natural {
        self.source + self.target
    }

    /// Distribute the money according to this ratio.
    ///
    /// # Rounding
    ///
    /// If the ratio causes the money to be split into fractional cents,
    /// the source part is rounded down to the next smaller chunk and
    /// the target part is rounded up to cover the rest.
    #[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(),
        }
    }
}