use derivative::Derivative;
use once_cell::sync::OnceCell;
use std::collections::{HashMap, HashSet};
use std::fmt::Debug;
use std::hash::Hash;
use std::slice::Iter;
pub struct Ruler<M, T> {
deps: Vec<RuleItem<M, T>>,
compiled: OnceCell<(Vec<usize>, Vec<T>)>,
}
impl<M, T> Ruler<M, T> {
pub fn new() -> Self {
Self::default()
}
}
impl<M: Eq + Hash + Copy + Debug, T: Clone> Ruler<M, T> {
pub fn add(&mut self, mark: M, value: T) -> &mut RuleItem<M, T> {
self.compiled = OnceCell::new();
let dep = RuleItem::new(mark, value);
self.deps.push(dep);
self.deps.last_mut().unwrap()
}
pub fn remove(&mut self, mark: M) {
self.deps.retain(|dep| !dep.marks.contains(&mark));
}
pub fn contains(&mut self, mark: M) -> bool {
self.deps.iter().any(|dep| dep.marks.contains(&mark))
}
#[inline]
pub fn iter(&self) -> Iter<T> {
self.compiled.get_or_init(|| self.compile()).1.iter()
}
fn compile(&self) -> (Vec<usize>, Vec<T>) {
let mut idhash = HashMap::<M, Vec<usize>>::new();
let mut deps_graph = vec![HashSet::new(); self.deps.len()];
let mut deps_order = vec![];
let mut beforeall_len = 0;
let mut afterall_len = 0;
let mut result = vec![];
let mut result_idx = vec![];
let mut deps_inserted = vec![false; self.deps.len()];
let mut deps_remaining = self.deps.len();
for (idx, dep) in self.deps.iter().enumerate() {
match dep.prio {
RuleItemPriority::Normal => {
deps_order.insert(deps_order.len() - afterall_len, idx);
}
RuleItemPriority::BeforeAll => {
deps_order.insert(beforeall_len, idx);
beforeall_len += 1;
}
RuleItemPriority::AfterAll => {
deps_order.insert(deps_order.len(), idx);
afterall_len += 1;
}
}
for mark in &dep.marks {
idhash.entry(*mark).or_default().push(idx);
}
}
for idx in deps_order.iter().copied() {
let dep = self.deps.get(idx).unwrap();
for constraint in &dep.cons {
match constraint {
RuleItemConstraint::Before(v) => {
for depidx in idhash.entry(*v).or_default().iter() {
deps_graph.get_mut(*depidx).unwrap().insert(idx);
}
}
RuleItemConstraint::After(v) => {
for depidx in idhash.entry(*v).or_default().iter() {
deps_graph.get_mut(idx).unwrap().insert(*depidx);
}
}
RuleItemConstraint::Require(v) => {
assert!(
idhash.contains_key(v),
"missing dependency: {:?} requires {:?}", dep.marks.first().unwrap(), v
);
}
}
}
}
'outer: while deps_remaining > 0 {
for idx in deps_order.iter().copied() {
let inserted = deps_inserted.get_mut(idx).unwrap();
if *inserted { continue; }
let dlist = deps_graph.get(idx).unwrap();
if dlist.is_empty() {
let dep = self.deps.get(idx).unwrap();
result.push(dep.value.clone());
result_idx.push(idx);
*inserted = true;
deps_remaining -= 1;
for d in deps_graph.iter_mut() {
d.remove(&idx);
}
continue 'outer;
}
}
#[cfg(debug_assertions)] {
for idx in deps_order.iter().copied() {
let mut seen = HashMap::new();
let mut vec = vec![idx];
while let Some(didx) = vec.pop() {
let dlist = deps_graph.get(didx).unwrap();
for x in dlist.iter() {
if seen.contains_key(x) { continue; }
vec.push(*x);
seen.insert(*x, didx);
if *x == idx {
let mut backtrack = vec![];
let mut curr = idx;
while !backtrack.contains(&curr) {
backtrack.push(curr);
curr = *seen.get(&curr).unwrap();
}
backtrack.push(curr);
let path = backtrack.iter()
.rev()
.map(|x| format!("{:?}", self.deps.get(*x).unwrap().marks.first().unwrap()))
.collect::<Vec<String>>()
.join(" < ");
panic!("cyclic dependency: {}", path);
}
}
}
}
}
panic!("cyclic dependency: (use debug mode for more details)");
}
(result_idx, result)
}
}
impl<M: Eq + Hash + Copy + Debug, T: Clone> Debug for Ruler<M, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let vec: Vec<(usize, M)> = self.compiled.get_or_init(|| self.compile()).0
.iter()
.map(|idx| (*idx, *self.deps.get(*idx).unwrap().marks.first().unwrap()))
.collect();
f.debug_struct("Ruler")
.field("deps", &self.deps)
.field("compiled", &vec)
.finish()
}
}
impl<M, T> Default for Ruler<M, T> {
fn default() -> Self {
Self {
deps: Vec::new(),
compiled: OnceCell::new(),
}
}
}
#[derive(Derivative)]
#[derivative(Debug)]
pub struct RuleItem<M, T> {
marks: Vec<M>,
#[derivative(Debug="ignore")]
value: T,
prio: RuleItemPriority,
cons: Vec<RuleItemConstraint<M>>,
}
impl<M, T> RuleItem<M, T> {
fn new(mark: M, value: T) -> Self {
Self {
marks: vec![mark],
value,
prio: RuleItemPriority::Normal,
cons: vec![],
}
}
}
impl<M: Copy, T> RuleItem<M, T> {
pub fn before(&mut self, mark: M) -> &mut Self {
self.cons.push(RuleItemConstraint::Before(mark));
self
}
pub fn after(&mut self, mark: M) -> &mut Self {
self.cons.push(RuleItemConstraint::After(mark));
self
}
pub fn before_all(&mut self) -> &mut Self {
self.prio = RuleItemPriority::BeforeAll;
self
}
pub fn after_all(&mut self) -> &mut Self {
self.prio = RuleItemPriority::AfterAll;
self
}
pub fn alias(&mut self, mark: M) -> &mut Self {
self.marks.push(mark);
self
}
pub fn require(&mut self, mark: M) -> &mut Self {
self.cons.push(RuleItemConstraint::Require(mark));
self
}
}
#[derive(Debug)]
enum RuleItemConstraint<M> {
Before(M),
After(M),
Require(M),
}
#[derive(Debug)]
enum RuleItemPriority {
Normal,
BeforeAll,
AfterAll,
}
#[cfg(test)]
mod tests {
use super::Ruler;
#[test]
#[should_panic(expected=r#"cyclic dependency: "A" < "B" < "C" < "D" < "E" < "F" < "A""#)]
#[cfg(debug_assertions)]
fn cyclic_dependency_debug() {
let mut r = Ruler::new();
r.add("%", ()).after("D");
r.add("A", ()).after("B");
r.add("E", ()).after("F");
r.add("C", ()).after("D");
r.add("B", ()).after("C");
r.add("D", ()).after("E");
r.add("F", ()).after("A");
r.compile();
}
#[test]
#[should_panic(expected=r#"cyclic dependency"#)]
fn cyclic_dependency() {
let mut r = Ruler::new();
r.add("A", ()).after("B");
r.add("B", ()).after("C");
r.add("C", ()).after("A");
r.compile();
}
#[test]
#[should_panic(expected=r#"missing dependency: "C" requires "Z"#)]
fn missing_require() {
let mut r = Ruler::new();
r.add("A", ());
r.add("B", ()).require("A");
r.add("C", ()).require("Z");
r.compile();
}
}