use crate::domain::model::record_ref::IssueRef;
use super::Issue;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct IssueCollection {
issues: Vec<Issue>,
}
impl IssueCollection {
pub fn new(issues: Vec<Issue>) -> Self {
Self { issues }
}
pub fn iter(&self) -> std::slice::Iter<'_, Issue> {
self.issues.iter()
}
pub fn len(&self) -> usize {
self.issues.len()
}
pub fn is_empty(&self) -> bool {
self.issues.is_empty()
}
pub fn get(&self, id: &IssueRef) -> Option<&Issue> {
self.issues.iter().find(|i| &i.id == id)
}
pub fn into_vec(self) -> Vec<Issue> {
self.issues
}
}
impl From<Vec<Issue>> for IssueCollection {
fn from(issues: Vec<Issue>) -> Self {
Self::new(issues)
}
}
impl FromIterator<Issue> for IssueCollection {
fn from_iter<I: IntoIterator<Item = Issue>>(iter: I) -> Self {
Self::new(iter.into_iter().collect())
}
}
impl IntoIterator for IssueCollection {
type Item = Issue;
type IntoIter = std::vec::IntoIter<Issue>;
fn into_iter(self) -> Self::IntoIter {
self.issues.into_iter()
}
}
impl<'a> IntoIterator for &'a IssueCollection {
type Item = &'a Issue;
type IntoIter = std::slice::Iter<'a, Issue>;
fn into_iter(self) -> Self::IntoIter {
self.issues.iter()
}
}
#[cfg(test)]
pub mod strategy {
use super::IssueCollection;
use crate::domain::model::issue::value::strategy::issue;
use proptest::prelude::*;
pub fn issue_collection() -> impl Strategy<Value = IssueCollection> {
proptest::collection::vec(issue(), 0..6).prop_map(IssueCollection::new)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::domain::model::issue::test_fixtures::{feature, ir};
use proptest::prelude::*;
#[test]
fn empty_collection_is_empty() {
let c = IssueCollection::default();
assert_eq!(c.len(), 0);
assert!(c.is_empty());
assert_eq!(c.iter().count(), 0);
}
#[test]
fn iter_yields_each_issue_in_insertion_order() {
let a = feature("A").status("open").build(ir(1));
let b = feature("B").status("open").build(ir(2));
let c = IssueCollection::new(vec![a.clone(), b.clone()]);
let collected: Vec<&_> = c.iter().collect();
assert_eq!(collected, vec![&a, &b]);
}
#[test]
fn get_returns_the_matching_issue() {
let a = feature("A").status("open").build(ir(1));
let b = feature("B").status("open").build(ir(2));
let c = IssueCollection::new(vec![a.clone(), b.clone()]);
assert_eq!(c.get(&a.id), Some(&a));
assert_eq!(c.get(&b.id), Some(&b));
}
#[test]
fn get_returns_none_for_an_unknown_id() {
let a = feature("A").status("open").build(ir(1));
let c = IssueCollection::new(vec![a]);
assert_eq!(c.get(&ir(99)), None);
}
#[test]
fn into_vec_returns_the_underlying_storage() {
let a = feature("A").status("open").build(ir(1));
let c = IssueCollection::new(vec![a.clone()]);
assert_eq!(c.into_vec(), vec![a]);
}
#[test]
fn from_iter_collects_into_a_collection() {
let a = feature("A").status("open").build(ir(1));
let b = feature("B").status("open").build(ir(2));
let c: IssueCollection = vec![a.clone(), b.clone()].into_iter().collect();
assert_eq!(c.len(), 2);
}
proptest! {
#[test]
fn len_matches_iter_count(c in strategy::issue_collection()) {
prop_assert_eq!(c.len(), c.iter().count());
}
#[test]
fn round_trip_through_vec(c in strategy::issue_collection()) {
let v = c.clone().into_vec();
prop_assert_eq!(IssueCollection::new(v), c);
}
}
}