use super::value::IssueFilter;
use super::{Issue, IssueCollection};
#[derive(Debug, Clone)]
pub struct IssueView<'a> {
issues: Vec<&'a Issue>,
}
impl<'a> IssueView<'a> {
pub fn from_refs(issues: Vec<&'a Issue>) -> Self {
Self { issues }
}
pub fn from_slice(issues: &'a [Issue]) -> Self {
Self {
issues: issues.iter().collect(),
}
}
pub fn iter(&self) -> std::slice::Iter<'_, &'a Issue> {
self.issues.iter()
}
pub fn len(&self) -> usize {
self.issues.len()
}
pub fn is_empty(&self) -> bool {
self.issues.is_empty()
}
pub fn filter<F>(&self, mut predicate: F) -> IssueView<'a>
where
F: FnMut(&Issue) -> bool,
{
IssueView {
issues: self
.issues
.iter()
.copied()
.filter(|i| predicate(i))
.collect(),
}
}
pub fn matching(&self, filter: &IssueFilter<'_>) -> IssueView<'a> {
self.filter(|i| i.matches(filter))
}
}
impl IssueCollection {
pub fn view(&self) -> IssueView<'_> {
IssueView::from_refs(self.iter().collect())
}
}
impl<'a, 'v> IntoIterator for &'v IssueView<'a> {
type Item = &'v &'a Issue;
type IntoIter = std::slice::Iter<'v, &'a Issue>;
fn into_iter(self) -> Self::IntoIter {
self.issues.iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::domain::model::issue::test_fixtures::{defect, feature, ir};
use crate::domain::model::status::Status;
fn corpus() -> Vec<Issue> {
vec![
feature("A").status("open").build(ir(1)),
defect("B").status("closed").build(ir(2)),
feature("C").status("open").build(ir(3)),
]
}
#[test]
fn from_slice_yields_every_issue() {
let issues = corpus();
let view = IssueView::from_slice(&issues);
assert_eq!(view.len(), 3);
let ids: Vec<_> = view.iter().map(|i| i.id.clone()).collect();
assert_eq!(ids, vec![ir(1), ir(2), ir(3)]);
}
#[test]
fn collection_view_yields_every_issue() {
let collection = IssueCollection::new(corpus());
let view = collection.view();
assert_eq!(view.len(), 3);
}
#[test]
fn filter_narrows_the_view() {
let issues = corpus();
let view = IssueView::from_slice(&issues);
let open = view.filter(|i| i.matches(&IssueFilter::default()));
assert_eq!(open.len(), 3);
let only_a = view.filter(|i| i.id == ir(1));
assert_eq!(only_a.len(), 1);
}
#[test]
fn matching_applies_an_issue_filter() {
let issues = corpus();
let view = IssueView::from_slice(&issues);
let open = Status::new("open").unwrap();
let f = IssueFilter {
status: Some(&open),
..Default::default()
};
let narrowed = view.matching(&f);
let ids: Vec<_> = narrowed.iter().map(|i| i.id.clone()).collect();
assert_eq!(ids, vec![ir(1), ir(3)]);
}
#[test]
fn filtering_is_allocation_light_and_can_chain() {
let issues = corpus();
let view = IssueView::from_slice(&issues);
let chained = view.filter(|i| i.id != ir(2)).filter(|i| i.id != ir(3));
assert_eq!(chained.len(), 1);
assert_eq!(chained.iter().next().unwrap().id, ir(1));
}
#[test]
fn empty_view_reports_zero_length() {
let view = IssueView::from_refs(vec![]);
assert!(view.is_empty());
assert_eq!(view.len(), 0);
}
}