aven_protocol/tail/
parent.rs1#[derive(Clone, Copy, Debug, PartialEq, Eq)]
3pub enum ParentAction {
4 Create = 0,
5 Set = 1,
6 ForceResolve = 2,
7}
8
9impl ParentAction {
10 pub fn from_byte(byte: u8) -> Option<Self> {
11 match byte {
12 0 => Some(Self::Create),
13 1 => Some(Self::Set),
14 2 => Some(Self::ForceResolve),
15 _ => None,
16 }
17 }
18}
19
20#[derive(Default)]
22pub struct ParentState {
23 pub version: Option<String>,
24 pub deleted: bool,
25 pub protected: bool,
26}
27impl ParentState {
28 pub fn protect_hint(&mut self, deleted: bool, version: Option<&str>) {
30 self.protected |=
31 self.version.is_none() || self.version.as_deref() != version || self.deleted != deleted;
32 }
33
34 pub fn apply(&mut self, action: ParentAction, id: &str, deleted: bool, version: Option<&str>) {
35 if action == ParentAction::Create {
36 if self.version.is_none() {
37 self.version = version.map(str::to_owned);
38 }
39 } else if self.version.is_none()
40 || (action == ParentAction::Set && version != self.version.as_deref())
41 {
42 self.protected = true;
43 } else {
44 self.deleted = deleted;
45 self.version = Some(id.to_owned());
46 }
47 }
48}
49
50#[cfg(test)]
51mod tests {
52 use super::*;
53
54 fn project(history: &[(&str, u8, bool, Option<&str>)]) -> bool {
58 let mut state = ParentState::default();
59 for (id, action, deleted, version) in history {
60 state.apply(
61 ParentAction::from_byte(*action).unwrap(),
62 id,
63 *deleted,
64 *version,
65 );
66 }
67 state.deleted && state.version.is_some() && !state.protected
68 }
69
70 #[test]
71 fn ordered_deletion_restore_and_custom_seed() {
72 let mut history = vec![
73 ("create", 0, false, Some("seed")),
74 ("delete", 1, true, Some("seed")),
75 ];
76 assert!(project(&history));
77 history.push(("restore", 1, false, Some("delete")));
78 assert!(!project(&history));
79 history.push(("delete2", 1, true, Some("restore")));
80 assert!(project(&history));
81 }
82
83 #[test]
84 fn stale_delete_and_force_resolution_keep_protection() {
85 let mut history = vec![
86 ("create", 0, false, Some("seed")),
87 ("delete", 1, true, Some("seed")),
88 ("restore", 1, false, Some("delete")),
89 ("stale", 1, true, Some("seed")),
90 ];
91 assert!(!project(&history));
92 history.push(("resolve", 2, true, Some("restore")));
93 assert!(!project(&history));
94 }
95
96 #[test]
97 fn equal_value_conflicts_and_missing_creation_are_conservative() {
98 assert!(!project(&[
99 ("create", 0, false, Some("seed")),
100 ("delete", 1, true, Some("seed")),
101 ("other", 1, true, Some("seed")),
102 ]));
103 assert!(!project(&[("delete", 1, true, None)]));
104 assert!(!project(&[]));
105 }
106}