proc-macro-assertions 0.1.5

Easily create asserts on proc macro inputs
Documentation
use std::{
    any::TypeId,
    collections::{btree_map::IntoValues, BTreeMap},
};

use better_any::{Tid, TidAble, TidExt};

use crate::token_store::TokenStore;

use super::{context::Context, generatable::Generatable, wrapper::GenerationWrapper};

/// A set of [`Generatable`](crate::generatable::Generatable) Values that is guranteed to be
/// unique across the types of the actual values (not the instances!). This is used to implement
/// the first stage of the Generation process. For more information see the module documentation or
/// the [`Generatable`](crate::generatable::Generatable) trait.
#[derive(Default)]
pub struct GeneratableSet<'set> {
    backend: BTreeMap<TypeId, Box<dyn GeneratableBackend<'set>>>,
}

impl<'set> IntoIterator for GeneratableSet<'set> {
    type Item = Box<dyn GeneratableBackend<'set>>;

    type IntoIter = IntoValues<TypeId, Self::Item>;

    fn into_iter(self) -> Self::IntoIter {
        self.backend.into_values()
    }
}

/// The status returned while inserting some item into [`GeneratableSet`].
#[allow(dead_code)]
pub enum InsertStatus {
    /// Returned when the item was freshly inserted
    WasInserted,
    /// Returned when a corresponding value was already present.
    AlreadyPresent,
}

impl<'set> GeneratableSet<'set> {
    /// Creates a new generatable set
    pub fn new() -> Self {
        Self::default()
    }

    /// Get the current value in the set of type T, or insert a new one. Returns a mutable reference to
    /// the inserted value.
    #[allow(dead_code)]
    #[must_use]
    pub fn get_or_insert_with<T>(&mut self, f: impl FnOnce() -> T) -> &mut T
    where
        T: GeneratableBackend<'set>,
    {
        self.backend
            .entry(T::id())
            .or_insert_with(|| Box::new(f()))
            .as_mut()
            .downcast_mut::<T>()
            .unwrap()
    }

    /// Get the current value in the set of type T, or insert the default value. Returns a mutable reference
    /// to the inserted value.
    #[allow(dead_code)]
    #[must_use]
    pub fn get_or_insert_default<T>(&mut self) -> &mut T
    where
        T: GeneratableBackend<'set> + Default,
    {
        self.get_or_insert_with::<T>(Default::default)
    }

    /// Get a value of Type T that is already present in the set.
    #[allow(dead_code)]
    #[must_use]
    pub fn get<T>(&self) -> Option<&T>
    where
        T: GeneratableBackend<'set>,
    {
        self.backend
            .get(&T::id())
            .map(AsRef::as_ref)
            .and_then(TidExt::downcast_ref)
    }

    /// Insert a new value into the set, if not already present. Returns
    /// a [`InsertStatus`] which indicates if the value was already present
    /// or freshly inserted.
    #[allow(dead_code)]
    pub fn insert<T>(&mut self, val: T) -> InsertStatus
    where
        T: GeneratableBackend<'set>,
    {
        if self.contains::<T>() {
            return InsertStatus::AlreadyPresent;
        }

        self.backend.insert(T::id(), Box::new(val));
        InsertStatus::WasInserted
    }

    /// Insert a new value returned by a closure into the set, if not already present.
    /// Returns  a [`InsertStatus`] which indicates if the value was already present
    /// or freshly inserted. The closure is only executed if the value wasn't already present.
    #[allow(dead_code)]
    pub fn insert_with<T>(&mut self, f: impl FnOnce() -> T) -> InsertStatus
    where
        T: GeneratableBackend<'set>,
    {
        if self.contains::<T>() {
            return InsertStatus::AlreadyPresent;
        }
        self.backend.insert(T::id(), Box::new(f()));
        InsertStatus::WasInserted
    }

    /// Checks if a value of type T is present inside the set.
    #[allow(dead_code)]
    #[must_use]
    pub fn contains<T>(&self) -> bool
    where
        T: GeneratableBackend<'set>,
    {
        self.backend.contains_key(&T::id())
    }

    /// Iter over the values inside the set
    pub fn iter(&self) -> impl Iterator<Item = &dyn GeneratableBackend<'set>> {
        self.backend.values().map(AsRef::as_ref)
    }

    /// Iter over the values inside the set, while beeing able to mutate them. As this
    /// does not change the type this is possible with this set.
    #[allow(dead_code)]
    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut dyn GeneratableBackend<'set>> {
        self.backend.values_mut().map(AsMut::as_mut)
    }
}

/// The trait backing the [`GeneratableStore`]. Any type inside the set must implement this trait.
/// As a trait object is returned while iterating over the set, only methods contained here are usable
/// on the returned items.
pub trait GeneratableBackend<'a>: Tid<'a> {
    fn generate_into(&self, context: &mut Context, token_store: &mut TokenStore);
}

impl<'a, T, A> GeneratableBackend<'a> for GenerationWrapper<'a, T, A>
where
    T: Generatable<'a, A> + Ord,
    A: TidAble<'a> + Ord + PartialOrd,
{
    fn generate_into(&self, context: &mut Context, token_store: &mut TokenStore) {
        self.do_generation(context, token_store);
    }
}