market 0.30.1

Infrastructure for producers and consumers
//! Defines interfaces used by [`Agent`]s to act upon a market.
//!
//! An [`Agent`] can be either a [`Producer`] that stores goods into the market or a [`Consumer`] that retrieves goods from the market. While an [`Agent`] is acting upon a market, it is immutable.

#![allow(clippy::type_repetition_in_bounds)]
// False positives when dealing with multiple traits.
// Add unstable feature to document when items are supported.
#![cfg_attr(feature = "unstable-doc-cfg", feature(doc_cfg))]
#![no_std]

extern crate alloc;
#[cfg(feature = "std")]
extern crate std;

mod error;

pub use error::{
    Blame, Blockage, ConsumptionFlaws, EmptyStock, Failure, FailureConversionError, Fault,
    FaultConversionError, Flawless, Flaws, FullStock, ProductionFlaws, Recall,
    RecallConversionError, TryBlame,
};

use {
    core::{convert::TryFrom, fmt::Display},
    fehler::{throw, throws},
};

/// Characterizes an agent that interacts with a market.
// Agent does not define Flaws type because an Agent that implements both Producer and Consumer (such as a queue) may have different Flaws for each trait.
pub trait Agent: Display {
    /// Specifies the good that is stored in the market.
    type Good;
}

/// Characterizes an agent that stores goods into a market.
pub trait Producer: Agent {
    /// Specifies the [`Flaws`] thrown when a production fails.
    type Flaws: Flaws;

    /// Returns the [`Recall`] thrown by `self` when `fault` is caught while producing `good`.
    fn recall(
        &self,
        fault: Fault<Self::Flaws>,
        good: Self::Good,
    ) -> Recall<Self::Flaws, Self::Good> {
        Recall::new(Failure::new(&self, fault), good)
    }

    /// Stores `good` into the market without blocking.
    ///
    /// # Errors
    ///
    /// If `produce` fails to store `good` into the market, it shall throw a [`Recall`] containing the [`Fault`] and `good`.
    fn produce(&self, good: Self::Good) -> Result<(), Recall<Self::Flaws, Self::Good>>;

    /// Stores each good from the [`Iterator`] `goods` into the market without blocking.
    ///
    /// # Errors
    ///
    /// If the production of a good fails, shall throw a [`Recall`] and `goods` shall contain all goods whose production was not attempted.
    #[throws(Recall<Self::Flaws, Self::Good>)]
    fn produce_all<I>(&self, goods: &mut I)
    where
        // Required for Producer to be object safe: See https://doc.rust-lang.org/reference/items/traits.html#object-safety.
        Self: Sized,
        I: Iterator<Item = Self::Good>,
    {
        for good in goods {
            self.produce(good)?;
        }
    }

    /// Retrieves each good from the [`Consumer`] `consumer` and stores it into the market without blocking.
    ///
    /// # Errors
    ///
    /// If the consumption or production of a good fails, except in the case where consumption fails due to an insufficiency after at least one successful consumption, `produce_goods` shall throw a [`Blockage`] and `consumer` shall contain all goods whose production was not attempted.
    #[throws(Blockage<C::Flaws, Self::Flaws, Self::Good>)]
    fn produce_goods<C>(&self, consumer: &C)
    where
        // Required for Producer to be object safe: See https://doc.rust-lang.org/reference/items/traits.html#object-safety.
        Self: Sized,
        C: Consumer<Good = Self::Good>,
    {
        // Throw any consumer error on the first attempt; after this only throw defects.
        self.produce(consumer.consume()?)?;

        let failure = loop {
            match consumer.consume() {
                Ok(good) => self.produce(good)?,
                Err(failure) => break failure,
            }
        };

        if failure.is_defect() {
            throw!(failure);
        }
    }

    /// Stores `good` into the market, blocking until stock is available.
    ///
    /// # Errors
    ///
    /// If the production fails due to a defect, `force` shall throw a [`Recall`] containing the [`Fault`] and `good`.
    #[throws(Recall<<Self::Flaws as Flaws>::Defect, Self::Good>)]
    fn force(&self, mut good: Self::Good)
    where
        // Indicates that Self::Flaws::Defect implements Flaws with itself as the Defect.
        <Self::Flaws as Flaws>::Defect: Flaws<Defect = <Self::Flaws as Flaws>::Defect>,
        <<Self::Flaws as Flaws>::Defect as Flaws>::Insufficiency:
            TryFrom<<Self::Flaws as Flaws>::Insufficiency>,
    {
        while let Err(recall) = self.produce(good) {
            match recall.try_blame() {
                Ok(defect) => throw!(defect),
                Err(error) => {
                    good = error.into_good();
                }
            }
        }
    }

    /// Stores each good from the [`Iterator`] `goods` into the market, blocking until stock is available.
    ///
    /// # Errors
    ///
    /// If the production of a good fails, `force_all` shall throw a [`Recall`] and `goods` shall contain all goods whose production was not attempted.
    #[throws(Recall<<Self::Flaws as Flaws>::Defect, Self::Good>)]
    fn force_all<I>(&self, goods: &mut I)
    where
        // Required for Producer to be object safe: See https://doc.rust-lang.org/reference/items/traits.html#object-safety.
        Self: Sized,
        I: Iterator<Item = Self::Good>,
        <Self::Flaws as Flaws>::Defect: Flaws<Defect = <Self::Flaws as Flaws>::Defect>,
        <<Self::Flaws as Flaws>::Defect as Flaws>::Insufficiency:
            TryFrom<<Self::Flaws as Flaws>::Insufficiency>,
    {
        for good in goods {
            self.force(good)?;
        }
    }

    /// Retrieves and stores goods from `consumer` into the market, blocking both until stock is sufficient for the respective action.
    ///
    /// # Errors
    ///
    /// If the consumption or production of a good fails due to a defect, `force_all` shall throw a [`Blockage`] and `consumer` shall contain all goods whose production was not attempted.
    #[allow(unreachable_code)] // Issue with fehler (#53) which has been resolved but not released.
    #[throws(Blockage<<C::Flaws as Flaws>::Defect, <Self::Flaws as Flaws>::Defect, Self::Good>)]
    fn force_goods<C>(&self, consumer: &C)
    where
        // Required for Producer to be object safe: See https://doc.rust-lang.org/reference/items/traits.html#object-safety.
        Self: Sized,
        C: Consumer<Good = Self::Good>,
        <C::Flaws as Flaws>::Defect: Flaws<Defect = <C::Flaws as Flaws>::Defect>,
        <<C::Flaws as Flaws>::Defect as Flaws>::Insufficiency:
            TryFrom<<C::Flaws as Flaws>::Insufficiency>,
        <Self::Flaws as Flaws>::Defect: Flaws<Defect = <Self::Flaws as Flaws>::Defect>,
        <<Self::Flaws as Flaws>::Defect as Flaws>::Insufficiency:
            TryFrom<<Self::Flaws as Flaws>::Insufficiency>,
    {
        loop {
            self.force(consumer.demand()?)?;
        }
    }
}

/// Characterizes an agent that retrieves goods from a market.
///
/// The order in which goods are retrieved is defined by the implementer.
pub trait Consumer: Agent {
    /// Specifies the [`Flaws`] thrown when a consumption fails.
    type Flaws: Flaws;

    /// Returns the [`Failure`] thrown by `self` when `fault` is caught.
    fn failure(&self, fault: Fault<Self::Flaws>) -> Failure<Self::Flaws> {
        Failure::new(&self, fault)
    }

    /// Retrieves the next good from the market without blocking.
    ///
    /// # Errors
    ///
    /// If `consume` fails to retrieve `good` from the market, it shall throw the causing [`Failure`].
    #[throws(Failure<Self::Flaws>)]
    fn consume(&self) -> Self::Good;

    /// Retrieves the next good from the market, blocking until one is available.
    ///
    /// # Errors
    ///
    /// If the consumption fails due to a defect, `demand` shall throw the appropriate [`Failure`].
    #[throws(Failure<<Self::Flaws as Flaws>::Defect>)]
    fn demand(&self) -> Self::Good
    where
        // Indicates that Self::Flaws::Defect implements Flaws with itself as the Defect.
        <Self::Flaws as Flaws>::Defect: Flaws<Defect = <Self::Flaws as Flaws>::Defect>,
        <<Self::Flaws as Flaws>::Defect as Flaws>::Insufficiency:
            TryFrom<<Self::Flaws as Flaws>::Insufficiency>,
    {
        loop {
            match self.consume() {
                Ok(good) => {
                    break good;
                }
                Err(failure) => {
                    if let Ok(defect) = failure.try_blame() {
                        throw!(defect);
                    }
                }
            }
        }
    }
}

/// Defines traits of markets for a channel.
///
/// A channel exchanges goods between [`Producer`]s and [`Consumer`]s. If either all [`Consumer`]s or all [`Producer`]s for a channel are dropped, the channel becomes invalid.
pub mod channel {
    use {
        super::{Consumer, ConsumptionFlaws, Flawless, Flaws, Producer, ProductionFlaws},
        core::fmt::{self, Display, Formatter},
    };

    /// The defect thrown when a [`Producer`] attempts to produce to a channel with no [`Consumer`]s.
    #[derive(Clone, Copy, Debug, Default)]
    #[non_exhaustive]
    pub struct WithdrawnDemand;

    impl Display for WithdrawnDemand {
        /// Writes "demand has withdrawn".
        fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
            write!(f, "demand has withdrawn")
        }
    }

    #[cfg(feature = "std")]
    #[cfg_attr(feature = "unstable-doc-cfg", doc(cfg(feature = "std")))]
    impl std::error::Error for WithdrawnDemand {}

    impl Flaws for WithdrawnDemand {
        type Insufficiency = Flawless;
        type Defect = Self;
    }

    /// The defect thrown when a [`Consumer`] attempts to consume from an empty channel with no [`Producer`]s.
    #[derive(Clone, Copy, Debug, Default)]
    #[non_exhaustive]
    pub struct WithdrawnSupply;

    impl Display for WithdrawnSupply {
        /// Writes "supply has withdrawn".
        fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
            write!(f, "supply has withdrawn")
        }
    }

    #[cfg(feature = "std")]
    #[cfg_attr(feature = "unstable-doc-cfg", doc(cfg(feature = "std")))]
    impl std::error::Error for WithdrawnSupply {}

    impl Flaws for WithdrawnSupply {
        type Insufficiency = Flawless;
        type Defect = Self;
    }

    /// Characterizes a channel with infinite capacity.
    pub trait InfiniteChannel<G> {
        /// Specifies the [`Producer`].
        type Producer: Producer<Good = G, Flaws = WithdrawnDemand>;
        /// Specifies the [`Consumer`].
        type Consumer: Consumer<Good = G, Flaws = ConsumptionFlaws<WithdrawnSupply>>;

        /// Creates the [`Producer`] and [`Consumer`] connected to an infinite channel.
        fn establish<S>(name_str: &S) -> (Self::Producer, Self::Consumer)
        where
            S: AsRef<str> + ?Sized;
    }

    /// Characterizes a channel with a limited capacity.
    pub trait FiniteChannel<G> {
        /// Specifies the [`Producer`].
        type Producer: Producer<Good = G, Flaws = ProductionFlaws<WithdrawnDemand>>;
        /// Specifies the [`Consumer`].
        type Consumer: Consumer<Good = G, Flaws = ConsumptionFlaws<WithdrawnSupply>>;

        /// Creates the [`Producer`] and [`Consumer`] connected to a channel with capacity of `size`.
        fn establish<S>(name_str: &S, size: usize) -> (Self::Producer, Self::Consumer)
        where
            S: AsRef<str> + ?Sized;
    }
}

/// Defines traits of markets for a queue.
///
/// A queue is a single item that implements [`Producer`] and [`Consumer`]. As a result, storing and retrieving from a queue cannot cause a defect.
pub mod queue {
    use super::{Consumer, EmptyStock, Flawless, FullStock, Producer};

    /// Characterizes a queue with infinite size.
    pub trait InfiniteQueue<G>:
        Consumer<Good = G, Flaws = EmptyStock> + Producer<Good = G, Flaws = Flawless>
    {
        /// Creates a queue with infinite size.
        fn allocate<S>(name_str: &S) -> Self
        where
            S: AsRef<str> + ?Sized;
    }

    /// Characterizes a queue with a size.
    pub trait FiniteQueue<G>:
        Consumer<Good = G, Flaws = EmptyStock> + Producer<Good = G, Flaws = FullStock>
    {
        /// Creates a queue with finite size.
        fn allocate<S>(name_str: &S, size: usize) -> Self
        where
            S: AsRef<str> + ?Sized;
    }
}