use std::fmt;
use std::ops::Index;
use std::sync::Arc;
use super::{Collection, DynItem, Item};
pub struct ItemSet<C: Collection> {
items: Arc<[Arc<DynItem<C>>]>,
}
impl<C: Collection> ItemSet<C> {
pub fn member<I>() -> Arc<DynItem<C>>
where
I: Item<CollectionType = C> + Default + 'static,
{
Arc::new(I::default())
}
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));
}
}