noema 0.4.0

Noema IOC and DI framework for Rust
Documentation
use std::fmt;
use std::ops::Index;
use std::sync::Arc;

use super::{Collection, DynItem, Item};

/// Frozen list of items for `C`. Built once by [`items!`](crate::items).
pub struct ItemSet<C: Collection> {
    items: Arc<[Arc<DynItem<C>>]>,
}

impl<C: Collection> ItemSet<C> {
    /// Wrap a unit item (`Default`) as a set member. Used by [`items!`](crate::items).
    pub fn member<I>() -> Arc<DynItem<C>>
    where
        I: Item<CollectionType = C> + Default + 'static,
    {
        Arc::new(I::default())
    }

    /// Build a set from an ordered list of members.
    pub fn freeze(items: impl IntoIterator<Item = Arc<DynItem<C>>>) -> Arc<Self> {
        Arc::new(Self {
            items: items.into_iter().collect::<Vec<_>>().into(),
        })
    }

    pub fn collection_name(&self) -> &'static str {
        C::name()
    }

    pub fn collection_describe(&self) -> &'static str {
        C::describe()
    }

    pub fn len(&self) -> usize {
        self.items.len()
    }

    pub fn is_empty(&self) -> bool {
        self.items.is_empty()
    }

    pub fn all(&self) -> &[Arc<DynItem<C>>] {
        &self.items
    }

    pub fn get(&self, index: usize) -> Option<&Arc<DynItem<C>>> {
        self.items.get(index)
    }

    pub fn iter(&self) -> impl Iterator<Item = &Arc<DynItem<C>>> {
        self.items.iter()
    }

    pub fn names(&self) -> impl Iterator<Item = &'static str> + '_ {
        self.items.iter().map(|item| item.name())
    }

    pub fn values(&self) -> impl Iterator<Item = C::Value> + '_ {
        self.items.iter().map(|item| item.value())
    }

    pub fn by_name(&self, name: &str) -> Option<Arc<DynItem<C>>> {
        self.items.iter().find(|item| item.name() == name).cloned()
    }

    pub fn by_value(&self, value: &C::Value) -> Option<Arc<DynItem<C>>> {
        self.items
            .iter()
            .find(|item| item.value() == *value)
            .cloned()
    }

    pub fn contains_name(&self, name: &str) -> bool {
        self.by_name(name).is_some()
    }

    pub fn contains_value(&self, value: &C::Value) -> bool {
        self.by_value(value).is_some()
    }
}

impl<C: Collection> Clone for ItemSet<C> {
    fn clone(&self) -> Self {
        Self {
            items: Arc::clone(&self.items),
        }
    }
}

impl<C: Collection> Index<usize> for ItemSet<C> {
    type Output = Arc<DynItem<C>>;

    fn index(&self, index: usize) -> &Self::Output {
        &self.items[index]
    }
}

impl<'a, C: Collection> IntoIterator for &'a ItemSet<C> {
    type Item = &'a Arc<DynItem<C>>;
    type IntoIter = std::slice::Iter<'a, Arc<DynItem<C>>>;

    fn into_iter(self) -> Self::IntoIter {
        self.items.iter()
    }
}

impl<C: Collection> fmt::Debug for ItemSet<C> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut list = f.debug_list();
        for item in self.items.iter() {
            list.entry(&format_args!("{}={}", item.name(), item.value()));
        }
        list.finish()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::items_collections::{Collection, Item};

    struct Dummy;

    impl Collection for Dummy {
        type Value = i32;
        fn name() -> &'static str {
            "Dummy"
        }
    }

    #[derive(Default)]
    struct One;

    impl Item for One {
        type CollectionType = Dummy;
        fn value(&self) -> i32 {
            1
        }
        fn name(&self) -> &'static str {
            "one"
        }
    }

    #[derive(Default)]
    struct Two;

    impl Item for Two {
        type CollectionType = Dummy;
        fn value(&self) -> i32 {
            2
        }
        fn name(&self) -> &'static str {
            "two"
        }
        fn description(&self) -> &'static str {
            "second"
        }
    }

    fn sample() -> Arc<ItemSet<Dummy>> {
        ItemSet::freeze([ItemSet::<Dummy>::member::<One>(), ItemSet::member::<Two>()])
    }

    #[test]
    fn freeze_preserves_order_and_lookup() {
        let set = sample();
        assert_eq!(set.collection_name(), "Dummy");
        assert_eq!(set.len(), 2);
        assert!(!set.is_empty());
        assert_eq!(set[0].name(), "one");
        assert_eq!(set.get(1).unwrap().value(), 2);
        assert!(set.get(2).is_none());
        assert_eq!(set.by_name("two").unwrap().description(), "second");
        assert_eq!(set.by_value(&1).unwrap().name(), "one");
        assert!(set.contains_name("one"));
        assert!(set.contains_value(&2));
        assert!(!set.contains_name("missing"));
        let names: Vec<_> = set.names().collect();
        assert_eq!(names, ["one", "two"]);
        let values: Vec<_> = set.values().collect();
        assert_eq!(values, [1, 2]);
        assert_eq!(set.iter().count(), 2);
        assert_eq!((&*set).into_iter().count(), 2);
    }

    #[test]
    fn freeze_empty() {
        let set = ItemSet::<Dummy>::freeze([]);
        assert!(set.is_empty());
        assert_eq!(set.len(), 0);
        assert!(set.by_name("one").is_none());
        assert!(set.by_value(&1).is_none());
    }

    #[test]
    fn clone_shares_the_slice() {
        let a = sample();
        let b = (*a).clone();
        assert!(Arc::ptr_eq(&a.items, &b.items));
    }
}