1use crate::pristine::*;
2
3pub enum ReorderError<T: ChannelMutTxnT> {
4 Txn(TxnErr<T::GraphError>),
5 TaggedPosition(ApplyTimestamp),
7}
8
9impl<T: ChannelMutTxnT> From<TxnErr<T::GraphError>> for ReorderError<T> {
10 fn from(e: TxnErr<T::GraphError>) -> Self {
11 ReorderError::Txn(e)
12 }
13}
14
15impl<T: ChannelMutTxnT> std::fmt::Debug for ReorderError<T> {
16 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17 match self {
18 ReorderError::Txn(e) => write!(f, "ReorderError::Txn({e:?})"),
19 ReorderError::TaggedPosition(t) => write!(f, "ReorderError::TaggedPosition({t})"),
20 }
21 }
22}
23
24impl<T: ChannelMutTxnT> std::fmt::Display for ReorderError<T> {
25 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26 match self {
27 ReorderError::Txn(e) => write!(f, "Transaction error: {e}"),
28 ReorderError::TaggedPosition(t) => {
29 write!(f, "Cannot reorder: position {t} is tagged")
30 }
31 }
32 }
33}
34
35impl<T: ChannelMutTxnT + 'static> std::error::Error for ReorderError<T> {}
36
37pub fn reorder_pinned<T>(
48 txn: &mut T,
49 channel: &mut T::Channel,
50 new_change: ChangeId,
51 is_pinned: impl Fn(Hash) -> bool,
52) -> Result<(), ReorderError<T>>
53where
54 T: ChannelMutTxnT + DepsTxnT<DepsError = <T as GraphTxnT>::GraphError> + GraphTxnT,
55{
56 let first_pinned = find_first_pinned(txn, channel, &is_pinned)?;
57 let first_pinned = match first_pinned {
58 Some(p) => p,
59 None => return Ok(()),
60 };
61
62 let new_pos = match txn.get_changeset(txn.changes(channel), &new_change)? {
63 Some(t) => u64::from(*t),
64 None => return Ok(()),
65 };
66
67 if new_pos < first_pinned {
68 return Ok(());
69 }
70
71 for dep in txn.iter_dep(&new_change)? {
74 let (dep_id, _) = dep?;
75 if let Some(dep_pos) = txn.get_changeset(txn.changes(channel), dep_id)? {
76 if u64::from(*dep_pos) >= first_pinned {
77 return Ok(());
78 }
79 }
80 }
81
82 for pos in first_pinned..=new_pos {
84 if txn.is_tagged(txn.tags(channel), pos)? {
85 return Err(ReorderError::TaggedPosition(pos));
86 }
87 }
88
89 txn.move_change(channel, new_change, new_pos, first_pinned)?;
90 Ok(())
91}
92
93fn find_first_pinned<T>(
94 txn: &T,
95 channel: &T::Channel,
96 is_pinned: &impl Fn(Hash) -> bool,
97) -> Result<Option<ApplyTimestamp>, TxnErr<T::GraphError>>
98where
99 T: ChannelTxnT + GraphTxnT,
100{
101 let cursor = T::cursor_revchangeset_ref(txn, txn.rev_changes(channel), None)?;
102 for x in cursor {
103 let (t, pair) = x?;
104 if let Some(h) = txn.get_external(&pair.a).optional()? {
105 if is_pinned(h.into()) {
106 return Ok(Some(u64::from(*t)));
107 }
108 }
109 }
110 Ok(None)
111}
112
113pub fn pinned_watermark<T>(
118 txn: &T,
119 channel: &T::Channel,
120 is_pinned: impl Fn(Hash) -> bool,
121) -> Result<Option<ApplyTimestamp>, TxnErr<T::GraphError>>
122where
123 T: ChannelTxnT + GraphTxnT,
124{
125 find_first_pinned(txn, channel, &is_pinned)
126}
127
128pub fn state_includes_pinned<T>(
132 txn: &T,
133 channel: &T::Channel,
134 pos: ApplyTimestamp,
135 is_pinned: impl Fn(Hash) -> bool,
136) -> Result<bool, TxnErr<T::GraphError>>
137where
138 T: ChannelTxnT + GraphTxnT,
139{
140 match find_first_pinned(txn, channel, &is_pinned)? {
141 Some(fp) => Ok(pos >= fp),
142 None => Ok(false),
143 }
144}