1#![allow(clippy::too_many_arguments)]
3use std::{cmp::Ordering, collections::BTreeSet, ops::ControlFlow};
4
5use git_ext::Oid;
6use radicle_dag::Dag;
7
8pub use crate::change::{Contents, Entry, EntryId, Timestamp};
9
10#[derive(Clone, Debug)]
12pub struct History {
13 graph: Dag<EntryId, Entry>,
14 root: EntryId,
15}
16
17impl PartialEq for History {
18 fn eq(&self, other: &Self) -> bool {
19 self.tips() == other.tips()
20 }
21}
22
23impl Eq for History {}
24
25impl History {
26 pub fn new(root: EntryId, graph: Dag<EntryId, Entry>) -> Self {
28 assert!(
29 graph.contains(&root),
30 "History::new: root must be present in graph"
31 );
32 Self { root, graph }
33 }
34
35 pub fn new_from_root(root: Entry) -> Self {
37 let id = *root.id();
38
39 Self::new(id, Dag::root(id, root))
40 }
41
42 pub fn tips(&self) -> BTreeSet<Oid> {
44 self.graph.tips().map(|(_, entry)| *entry.id()).collect()
45 }
46
47 pub fn traverse<F, A>(&self, init: A, roots: &[EntryId], mut f: F) -> A
54 where
55 F: for<'r> FnMut(A, &'r EntryId, &'r Entry) -> ControlFlow<A, A>,
56 {
57 self.graph.fold(roots, init, |acc, k, v| f(acc, k, v))
58 }
59
60 pub fn sorted<F>(&self, compare: F) -> impl Iterator<Item = &Entry>
62 where
63 F: FnMut(&EntryId, &EntryId) -> Ordering,
64 {
65 self.graph
66 .sorted_by(compare)
67 .into_iter()
68 .filter_map(|k| self.graph.get(&k))
69 .map(|node| &node.value)
70 }
71
72 pub fn extend(&mut self, change: Entry) {
74 let tips = self.tips();
75 let id = *change.id();
76
77 self.graph.node(id, change);
78
79 for tip in tips {
80 self.graph.dependency(id, (*tip).into());
81 }
82 }
83
84 pub fn merge(&mut self, other: Self) {
86 self.graph.merge(other.graph);
87 }
88
89 pub fn len(&self) -> usize {
91 self.graph.len()
92 }
93
94 pub fn is_empty(&self) -> bool {
96 self.graph.is_empty()
97 }
98
99 pub fn graph(&self) -> &Dag<EntryId, Entry> {
101 &self.graph
102 }
103
104 pub fn root(&self) -> &Entry {
106 self.graph
108 .get(&self.root)
109 .expect("History::root: the root entry must be present in the graph")
110 }
111
112 pub fn children_of(&self, id: &EntryId) -> Vec<EntryId> {
114 self.graph
115 .get(id)
116 .map(|n| n.dependents.iter().cloned().collect())
117 .unwrap_or_default()
118 }
119}