#![warn(missing_docs)]
use std::hash::Hash;
use std::ops::Index;
use std::{error, fmt};
#[cfg(not(any(feature = "indexmap", feature = "indexmap-serde")))]
use std::collections::{HashMap, HashSet};
#[cfg(any(feature = "indexmap", feature = "indexmap-serde"))]
use indexmap::{IndexMap, IndexSet};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(not(any(feature = "indexmap", feature = "indexmap-serde")))]
type Map<K, V> = HashMap<K, V>;
#[cfg(not(any(feature = "indexmap", feature = "indexmap-serde")))]
type Set<T> = HashSet<T>;
#[cfg(any(feature = "indexmap", feature = "indexmap-serde"))]
type Map<K, V> = IndexMap<K, V>;
#[cfg(any(feature = "indexmap", feature = "indexmap-serde"))]
type Set<T> = IndexSet<T>;
#[derive(Clone, Copy, fmt::Debug, PartialEq)]
pub struct CycleError;
impl fmt::Display for CycleError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
impl error::Error for CycleError {}
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum SortResults<U> {
Full(Vec<U>),
Partial(Vec<U>),
}
impl<U> SortResults<U> {
fn new(nodes: Vec<U>, node_depends_len: usize) -> SortResults<U> {
if node_depends_len == nodes.len() {
SortResults::Full(nodes)
} else {
SortResults::Partial(nodes)
}
}
#[inline]
pub fn cycle_detected(&self) -> bool {
match self {
SortResults::Full(_) => false,
SortResults::Partial(_) => true,
}
}
}
#[derive(Clone, Default)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct TopoSort<T>
where
T: Eq + Hash,
{
node_depends: Map<T, Set<T>>,
}
impl<T> TopoSort<T>
where
T: Eq + Hash,
{
#[inline]
pub fn new() -> Self {
TopoSort {
node_depends: Map::new(),
}
}
#[inline]
pub fn from_map(nodes: Map<T, Set<T>>) -> Self {
TopoSort {
node_depends: nodes,
}
}
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
TopoSort {
node_depends: Map::with_capacity(capacity),
}
}
pub fn insert_from_slice(&mut self, node: T, slice: &[T])
where
T: Clone,
{
self.insert_from_set(node, Set::from_iter(slice.to_vec()));
}
#[inline]
pub fn insert_from_set(&mut self, node: T, depends: Set<T>) {
self.node_depends.insert(node, depends);
}
#[inline]
pub fn insert<I: IntoIterator<Item = T>>(&mut self, node: T, i: I) {
self.insert_from_set(node, i.into_iter().collect());
}
#[inline]
pub fn nodes(&self) -> TopoSortNodeIter<'_, T> {
TopoSortNodeIter::new(&self.node_depends)
}
#[inline]
pub fn into_nodes(self) -> IntoTopoSortNodeIter<T> {
IntoTopoSortNodeIter::new(self.node_depends)
}
#[inline]
pub fn iter(&self) -> TopoSortIter<'_, T> {
TopoSortIter::new(&self.node_depends)
}
pub fn cycle_detected(&self) -> bool {
self.iter().any(|result| result.is_err())
}
pub fn to_vec(&self) -> SortResults<(&T, &Set<T>)> {
SortResults::new(self.iter().flatten().collect(), self.node_depends.len())
}
pub fn into_vec(self) -> SortResults<(T, Set<T>)> {
let len = self.node_depends.len();
let nodes: Vec<_> = self.into_iter().flatten().collect();
SortResults::new(nodes, len)
}
pub fn to_owned_vec(&self) -> SortResults<(T, Set<T>)>
where
T: Clone,
{
SortResults::new(
self.iter()
.flatten()
.map(|(node, depends)| (node.clone(), depends.clone()))
.collect(),
self.node_depends.len(),
)
}
pub fn to_vec_nodes(&self) -> SortResults<&T> {
SortResults::new(self.nodes().flatten().collect(), self.node_depends.len())
}
pub fn into_vec_nodes(self) -> SortResults<T> {
let len = self.node_depends.len();
let nodes: Vec<_> = self.into_nodes().flatten().collect();
SortResults::new(nodes, len)
}
pub fn to_owned_vec_nodes(&self) -> SortResults<T>
where
T: Clone,
{
SortResults::new(
self.nodes().flatten().map(|node| node.clone()).collect(),
self.node_depends.len(),
)
}
#[inline]
pub fn try_vec(&self) -> Result<Vec<(&T, &Set<T>)>, CycleError> {
self.iter().collect()
}
#[inline]
pub fn try_into_vec(self) -> Result<Vec<(T, Set<T>)>, CycleError> {
self.into_iter().collect()
}
pub fn try_owned_vec(&self) -> Result<Vec<(T, Set<T>)>, CycleError>
where
T: Clone,
{
self.iter()
.map(|result| result.map(|(node, depends)| (node.clone(), depends.clone())))
.collect()
}
#[inline]
pub fn try_vec_nodes(&self) -> Result<Vec<&T>, CycleError> {
self.nodes().collect()
}
#[inline]
pub fn try_into_vec_nodes(self) -> Result<Vec<T>, CycleError> {
self.into_nodes().collect()
}
pub fn try_owned_vec_nodes(&self) -> Result<Vec<T>, CycleError>
where
T: Clone,
{
self.nodes()
.map(|result| result.map(|node| node.clone()))
.collect()
}
pub fn into_inner(self) -> Map<T, Set<T>> {
self.node_depends
}
#[inline]
pub fn is_empty(&self) -> bool {
self.node_depends.is_empty()
}
#[inline]
pub fn len(&self) -> usize {
self.node_depends.len()
}
#[inline]
pub fn get(&self, node: &T) -> Option<&Set<T>> {
self.node_depends.get(node)
}
}
impl<T> Index<&T> for TopoSort<T>
where
T: Eq + Hash,
{
type Output = Set<T>;
#[inline]
fn index(&self, index: &T) -> &Self::Output {
self.node_depends.index(index)
}
}
impl<T> IntoIterator for TopoSort<T>
where
T: Eq + Hash,
{
type Item = Result<(T, Set<T>), CycleError>;
type IntoIter = IntoTopoSortIter<T>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
IntoTopoSortIter::new(self.node_depends)
}
}
impl<'d, T> IntoIterator for &'d TopoSort<T>
where
T: Eq + Hash,
{
type Item = Result<(&'d T, &'d Set<T>), CycleError>;
type IntoIter = TopoSortIter<'d, T>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
type Nodes<T> = Map<*const T, (Set<*const T>, u32)>;
struct InnerIter<T> {
nodes: Nodes<T>,
no_edges: Vec<*const T>,
}
impl<T> InnerIter<T>
where
T: Eq + Hash,
{
fn new(node_depends: &Map<T, Set<T>>) -> Self {
let nodes = Self::make_nodes(node_depends);
let no_edges = Self::make_no_edges(&nodes);
InnerIter { nodes, no_edges }
}
fn make_nodes(node_depends: &Map<T, Set<T>>) -> Nodes<T> {
let mut nodes: Nodes<T> = Map::with_capacity(node_depends.len());
let new_entry_fn = || (Set::new(), 0);
let lookup: Map<_, _> = node_depends.keys().map(|key| (key, key)).collect();
for (dependent, dependencies) in node_depends {
nodes.entry(dependent).or_insert_with(new_entry_fn);
for dependency in dependencies {
if dependent != dependency {
if let Some(&dependency) = lookup.get(dependency) {
let dependent_entry = nodes
.get_mut(&(dependent as *const T))
.expect("dependent not found in `nodes`");
dependent_entry.1 += 1;
let dependency_entry = nodes.entry(dependency).or_insert_with(new_entry_fn);
dependency_entry.0.insert(dependent);
}
}
}
}
nodes
}
fn make_no_edges(nodes: &Nodes<T>) -> Vec<*const T> {
nodes
.iter()
.filter(|(_, (_, edges))| *edges == 0)
.map(|(&node, _)| node)
.collect()
}
fn next(&mut self) -> Option<Result<*const T, CycleError>> {
match self.no_edges.pop() {
Some(node) => {
let (dependents, _) = &self
.nodes
.remove(&node)
.expect("node not in `nodes` on remove");
for &dependent in dependents {
let (_, edges) = self
.nodes
.get_mut(&dependent)
.expect("dependent not found in `nodes`");
*edges -= 1;
if *edges == 0 {
self.no_edges.push(dependent);
}
}
Some(Ok(node))
}
None if self.nodes.is_empty() => None,
None => {
self.nodes.clear();
Some(Err(CycleError))
}
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.nodes.len();
(len, Some(len))
}
}
unsafe impl<T> Send for InnerIter<T> {}
pub struct IntoTopoSortIter<T> {
inner: InnerIter<T>,
node_depends: Map<T, Set<T>>,
}
impl<T> IntoTopoSortIter<T>
where
T: Eq + Hash,
{
#[inline]
fn new(node_depends: Map<T, Set<T>>) -> Self {
IntoTopoSortIter {
inner: InnerIter::new(&node_depends),
node_depends,
}
}
}
impl<T> Iterator for IntoTopoSortIter<T>
where
T: Eq + Hash,
{
type Item = Result<(T, Set<T>), CycleError>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|result| {
result.map(|node| unsafe {
self.node_depends
.remove_entry(&*node)
.expect("node not in `node_depends` on remove")
})
})
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
pub struct IntoTopoSortNodeIter<T>(IntoTopoSortIter<T>);
impl<'d, T> IntoTopoSortNodeIter<T>
where
T: Eq + Hash,
{
#[inline]
fn new(node_depends: Map<T, Set<T>>) -> Self {
IntoTopoSortNodeIter(IntoTopoSortIter::new(node_depends))
}
}
impl<T> Iterator for IntoTopoSortNodeIter<T>
where
T: Eq + Hash,
{
type Item = Result<T, CycleError>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|result| result.map(|(node, _)| node))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
pub struct TopoSortIter<'d, T> {
inner: InnerIter<T>,
node_depends: &'d Map<T, Set<T>>,
}
impl<'d, T> TopoSortIter<'d, T>
where
T: Eq + Hash,
{
#[inline]
fn new(node_depends: &'d Map<T, Set<T>>) -> Self {
TopoSortIter {
inner: InnerIter::new(node_depends),
node_depends,
}
}
}
impl<'d, T> Iterator for TopoSortIter<'d, T>
where
T: Eq + Hash,
{
type Item = Result<(&'d T, &'d Set<T>), CycleError>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|result| {
result.map(|node| {
unsafe { (&*node, &self.node_depends[&*node]) }
})
})
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
pub struct TopoSortNodeIter<'d, T>(TopoSortIter<'d, T>);
impl<'d, T> TopoSortNodeIter<'d, T>
where
T: Eq + Hash,
{
#[inline]
fn new(node_depends: &'d Map<T, Set<T>>) -> Self {
TopoSortNodeIter(TopoSortIter::new(node_depends))
}
}
impl<'d, T> Iterator for TopoSortNodeIter<'d, T>
where
T: Eq + Hash,
{
type Item = Result<&'d T, CycleError>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|result| result.map(|(node, _)| node))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
#[cfg(test)]
mod tests {
use crate::{CycleError, Map, Set, SortResults, TopoSort};
#[test]
fn test_termination() {
let mut topo_sort = TopoSort::with_capacity(4);
topo_sort.insert(1, vec![2]);
topo_sort.insert(2, vec![1]); topo_sort.insert(3, vec![4]);
topo_sort.insert(4, vec![]);
let v: Vec<Result<_, _>> = topo_sort.nodes().collect();
assert_eq!(vec![Ok(&4), Ok(&3), Err(CycleError)], v);
}
#[test]
fn test_direct_cycle() {
let mut topo_sort = TopoSort::with_capacity(2);
topo_sort.insert(1, vec![2]);
assert!(!topo_sort.cycle_detected());
assert!(!topo_sort.to_owned_vec_nodes().cycle_detected());
assert!(!topo_sort.try_vec_nodes().is_err());
topo_sort.insert(2, vec![1]); assert!(topo_sort.cycle_detected());
assert!(topo_sort.to_owned_vec_nodes().cycle_detected());
assert!(topo_sort.try_into_vec_nodes().is_err());
}
#[test]
fn test_indirect_cycle() {
let mut topo_sort = TopoSort::with_capacity(3);
topo_sort.insert(1, vec![2, 3]);
topo_sort.insert(2, vec![3]);
assert!(!topo_sort.cycle_detected());
assert!(!topo_sort.to_owned_vec_nodes().cycle_detected());
assert!(!topo_sort.try_vec_nodes().is_err());
topo_sort.insert(3, vec![1]); assert!(topo_sort.cycle_detected());
assert!(topo_sort.to_owned_vec_nodes().cycle_detected());
assert!(topo_sort.try_into_vec_nodes().is_err());
}
#[test]
fn test_typical() {
let mut topo_sort = TopoSort::with_capacity(5);
topo_sort.insert("C", vec!["A", "B"]);
topo_sort.insert("E", vec!["B", "C"]);
topo_sort.insert("A", vec![]);
topo_sort.insert("D", vec!["A", "C", "E"]);
topo_sort.insert("B", vec!["A"]);
match topo_sort.into_vec_nodes() {
SortResults::Full(nodes) => assert_eq!(vec!["A", "B", "C", "E", "D"], nodes),
SortResults::Partial(_) => panic!("unexpected cycle!"),
}
}
#[test]
fn test_empty() {
let topo_sort: TopoSort<u32> = TopoSort::new();
assert_eq!(
Vec::new() as Vec<(u32, Set<u32>)>,
topo_sort.try_owned_vec().unwrap()
);
}
#[test]
fn test_with_no_depends() {
let mut topo_sort = TopoSort::with_capacity(1);
topo_sort.insert("C", vec![]);
assert_eq!(vec!["C"], topo_sort.try_owned_vec_nodes().unwrap());
}
#[test]
fn test_with_excess_depends() {
let mut topo_sort = TopoSort::with_capacity(5);
topo_sort.insert("C", vec!["F", "A", "B", "F"]); topo_sort.insert("E", vec!["C", "B", "C"]); topo_sort.insert("A", vec!["A", "G"]); topo_sort.insert("D", vec!["A", "C", "E"]);
topo_sort.insert("B", vec!["B", "A"]);
assert_eq!(
vec!["A", "B", "C", "E", "D"],
topo_sort.try_owned_vec_nodes().unwrap()
);
}
#[test]
fn test_iter() {
let mut topo_sort = TopoSort::with_capacity(5);
topo_sort.insert("C", vec!["A", "B"]);
topo_sort.insert("E", vec!["B", "C"]);
topo_sort.insert("A", vec![]);
topo_sort.insert("D", vec!["A", "C", "E"]);
topo_sort.insert("B", vec!["A"]);
let mut nodes = Vec::with_capacity(5);
for node in &topo_sort {
match node {
Ok(node) => nodes.push(node),
Err(_) => panic!("Unexpected cycle!"),
}
}
assert_eq!(
vec![
(&"A", &Set::new()),
(&"B", &Set::from_iter(vec!["A"])),
(&"C", &Set::from_iter(vec!["A", "B"])),
(&"E", &Set::from_iter(vec!["B", "C"])),
(&"D", &Set::from_iter(vec!["A", "C", "E"])),
],
nodes
);
}
#[test]
fn test_into_iter() {
let mut topo_sort = TopoSort::with_capacity(5);
topo_sort.insert("C", vec!["A", "B"]);
topo_sort.insert("E", vec!["B", "C"]);
topo_sort.insert("A", vec![]);
topo_sort.insert("D", vec!["A", "C", "E"]);
topo_sort.insert("B", vec!["A"]);
let mut nodes = Vec::with_capacity(5);
for node in topo_sort {
match node {
Ok(node) => nodes.push(node),
Err(_) => panic!("Unexpected cycle!"),
}
}
assert_eq!(
vec![
("A", Set::new()),
("B", Set::from_iter(vec!["A"])),
("C", Set::from_iter(vec!["A", "B"])),
("E", Set::from_iter(vec!["B", "C"])),
("D", Set::from_iter(vec!["A", "C", "E"])),
],
nodes
);
}
#[test]
fn test_misc() {
let mut topo_sort = TopoSort::new();
assert!(topo_sort.is_empty());
topo_sort.insert_from_slice("A", &["B"]);
assert_eq!(1, topo_sort.len());
let set = Set::from_iter(vec!["B", "D"]);
topo_sort.insert_from_set("C", set.clone());
assert_eq!(set, *topo_sort.get(&"C").unwrap());
assert_eq!(set, topo_sort[&"C"]);
assert_eq!(None, topo_sort.get(&"D"));
let mut map = Map::with_capacity(2);
map.insert("A", Set::from_iter(vec!["B"]));
map.insert("C", set.clone());
assert_eq!(map, topo_sort.into_inner())
}
}