use std::collections;
use std::slice;
use std::sync::Arc;
use condition::Condition;
use error::*;
use iter::LeftJoinable;
use state;
type EnabledMap<C> = collections::BTreeMap<Arc<state::IssueState<C>>, bool>;
fn deps_enabled<C>(state: &state::IssueState<C>, map: &EnabledMap<C>) -> Result<bool>
where C: Condition
{
state
.relations
.iter()
.join_left(map.iter())
.filter_map(|item| match item.0 {
state::StateRelation::Extends => Some(item.1),
state::StateRelation::Overrides => None,
})
.fold(Some(true), |state, val| if let (Some(s), Some(v)) = (state, val) {
Some(s && *v)
} else {
None
})
.ok_or_else(|| Error::from(ErrorKind::DependencyError))
}
pub trait Resolvable<C>
where C: Condition
{
fn issue_state(&self, issue: &C::Issue) -> Result<Option<Arc<state::IssueState<C>>>>;
}
pub struct IssueStateSet<C>
where C: Condition
{
data: Box<[Arc<state::IssueState<C>>]>,
}
impl<C> IssueStateSet<C>
where C: Condition
{
pub fn from_set(mut states: collections::BTreeSet<Arc<state::IssueState<C>>>) -> Result<Self> {
let mut data = Vec::default();
while !states.is_empty() {
let old_len = data.len();
data.extend(states
.iter()
.filter(|state| !state
.relations
.iter()
.join_left(states.iter().map(|item| (item, ())))
.any(|item| item.1.is_some())
)
.map(Clone::clone));
for state in data.split_at(old_len).1 {
states.remove(state);
}
if data.len() == old_len {
return Err(Error::from(ErrorKind::CyclicDependency));
}
}
Ok(Self {data: data.into_boxed_slice()})
}
pub fn iter(&self) -> slice::Iter<Arc<state::IssueState<C>>> {
self.data.iter()
}
}
impl<C> Resolvable<C> for IssueStateSet<C>
where C: Condition
{
fn issue_state(&self, issue: &C::Issue) -> Result<Option<Arc<state::IssueState<C>>>> {
let mut retval = None;
let mut enabled_map = EnabledMap::default();
for state in self.data.iter() {
let enabled = state.conditions_satisfied(issue)
&& deps_enabled(&state, &enabled_map)?;
enabled_map.insert(state.clone(), enabled);
if enabled {
retval = Some(state);
}
}
Ok(retval.map(Clone::clone))
}
}
impl<C> From<state::IssueStateVec<C>> for IssueStateSet<C>
where C: Condition
{
fn from(states: Vec<Arc<state::IssueState<C>>>) -> Self {
Self {data: states.into_boxed_slice()}
}
}
impl<C> Default for IssueStateSet<C>
where C: Condition
{
fn default() -> Self {
Self {data: Default::default()}
}
}
#[cfg(test)]
mod tests {
use super::*;
use test::TestState;
#[test]
fn smoke() {
let state1 : Arc<TestState> = state::IssueState::new("new".to_string()).into();
let state2 : Arc<TestState> = {
let mut tmp = state::IssueState::new("acknowledged".to_string());
tmp.conditions = vec!["acked".into()];
tmp.add_overridden([state1.clone()].into_iter().map(Clone::clone));
tmp
}.into();
let state3 : Arc<TestState> = {
let mut tmp = state::IssueState::new("assigned".to_string());
tmp.conditions = vec!["assigned".into()];
tmp.add_extended([state2.clone()].into_iter().map(Clone::clone));
tmp
}.into();
let state4 : Arc<TestState> = {
let mut tmp = state::IssueState::new("closed".to_string());
tmp.conditions = vec!["closed".into()];
tmp.add_overridden([state3.clone()].into_iter().map(Clone::clone));
tmp
}.into();
let states = IssueStateSet::from_set({
let mut set = collections::BTreeSet::new();
set.insert(state1);
set.insert(state2);
set.insert(state3);
set.insert(state4);
set
}).expect("Failed to create issue state set.");
{
let state = states
.issue_state(&collections::BTreeMap::new())
.expect("Failed to determine state.")
.expect("Wrongly determined no state.");
assert_eq!(state.name(), "new");
}
{
let mut issue = collections::BTreeMap::new();
issue.insert("acked", true);
let state = states
.issue_state(&issue)
.expect("Failed to determine state.")
.expect("Wrongly determined no state.");
assert_eq!(state.name(), "acknowledged");
}
{
let mut issue = collections::BTreeMap::new();
issue.insert("assigned", true);
let state = states
.issue_state(&issue)
.expect("Failed to determine state.")
.expect("Wrongly determined no state.");
assert_eq!(state.name(), "new");
}
{
let mut issue = collections::BTreeMap::new();
issue.insert("acked", true);
issue.insert("assigned", true);
let state = states
.issue_state(&issue)
.expect("Failed to determine state.")
.expect("Wrongly determined no state.");
assert_eq!(state.name(), "assigned");
}
{
let mut issue = collections::BTreeMap::new();
issue.insert("acked", true);
issue.insert("closed", true);
let state = states
.issue_state(&issue)
.expect("Failed to determine state.")
.expect("Wrongly determined no state.");
assert_eq!(state.name(), "closed");
}
}
}