pub enum Error<T, E> {
CyclicDep(Vec<T>),
UserDef(E),
}
impl<T, E> From<E> for Error<T, E> {
fn from(err: E) -> Self {
Error::UserDef(err)
}
}
pub struct DepMap<T: PartialEq> {
list: Vec<Vec<T>>,
result: Vec<T>,
used: usize,
}
impl<T: PartialEq> DepMap<T> {
pub fn new(list: Vec<T>) -> Self {
Self {
used: if list.is_empty() {0} else {1},
list: vec![list],
result: Vec::new(),
}
}
pub fn process<F, I, E>(initial: Vec<T>, mut f: F) -> Result<Vec<T>, Error<T, E>>
where F: FnMut(&T) -> I, I: Iterator<Item = Result<T, E>> {
let mut state = Self::new(initial);
loop {
match state.destroy() {
Ok(res) => break Ok(res),
Err(map) => state = map,
};
state.add(&mut f)?
.map(|deps| deps.len())
.map_or(Ok(()), |len| Err(state.list.iter_mut()
.take(state.used)
.skip(state.used - len)
.map(|list| list.swap_remove(0))
.collect::<Vec<_>>()))
.map_err(Error::CyclicDep)?;
}
}
pub fn is_empty(&self) -> bool {
self.used == 0
}
pub fn destroy(self) -> Result<Vec<T>, Self> {
if self.is_empty() {
Ok(self.result)
} else {
Err(self)
}
}
pub fn add<F, I, E>(&mut self, f: F) -> Result<Option<Vec<&T>>, E>
where F: FnOnce(&T) -> I, I: Iterator<Item = Result<T, E>> {
if self.is_empty() {
return Ok(None);
}
let mut free = self.get_free();
for tgt in (f)(&self.list[self.used - 1][0]) {
let tgt = tgt?;
if self.result.iter().any(|done| done == &tgt) {
continue;
} else if let Some(pos) = self.list[0..self.used].iter()
.map(|list| &list[0]).position(|cur| cur == &tgt) {
free.clear();
self.list.push(free);
return Ok(Some(self.list[pos..self.used].iter().map(|list| &list[0]).collect()))
} else {
free.push(tgt)
}
}
if free.is_empty() {
self.drop_cur();
} else {
let len = self.list.len();
self.list.push(free);
self.list.swap(len, self.used);
self.used += 1;
}
Ok(None)
}
fn get_free(&mut self) -> Vec<T> {
if self.used < self.list.len() {
self.list.pop().unwrap()
} else {
Vec::new()
}
}
fn drop_cur(&mut self) {
while self.used > 0 {
let list = &mut self.list[self.used - 1];
self.result.push(list.swap_remove(0));
let found = loop {
if list.is_empty() {
break false
}
let tgt = &list[0];
if self.result.iter().any(|done| done == tgt) {
list.swap_remove(0);
} else {
break true
}
};
if found {
break
} else {
self.used -= 1;
}
}
}
}