aranya_runtime/command.rs
1pub use aranya_crypto::policy::CmdId;
2use buggy::{Bug, BugExt as _};
3use serde::{Deserialize, Serialize};
4
5use crate::Prior;
6
7/// Identify how the client will sort the associated [`Command`].
8// Note: Order of variants affects derived Ord: Merge is least and Init is greatest.
9#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
10pub enum Priority {
11 /// Indicates two branches in the parent graph have been merged at this
12 /// command. A command with this priority must have two parents,
13 /// `Parent::Merge`.
14 Merge,
15 /// Indicates a device-specific action; the runtime uses the internal u32
16 /// for ordering.
17 Basic(u32),
18 /// Indicates all preceding commands are ancestors of this command.
19 Finalize,
20 /// Indicates state is initialized; the associated command is a common
21 /// ancestor to all other commands in the graph. A command with this
22 /// priority must have no parents, `Parent::None`.
23 Init,
24}
25
26/// An action message interpreted by its associated policy to affect state.
27///
28/// A [`Command`] is opaque to the runtime engine. When the engine receives a
29/// message, it is validated and serialized by its policy. The policy
30/// returns a command implementation to update the stored graph. A
31/// policy will also emit effects once a command is verified,
32/// which are sent to the client.
33pub trait Command {
34 /// Return this command's [`Priority`], determining how this event is
35 /// ordered amongst others it does not have a causal relationship with.
36 fn priority(&self) -> Priority;
37
38 /// Uniquely identifies the serialized command.
39 fn id(&self) -> CmdId;
40
41 /// Return this command's parents, or address(s) that immediately
42 /// precede(s) this.
43 fn parent(&self) -> Prior<Address>;
44
45 /// Return this command's associated policy.
46 fn policy(&self) -> Option<&[u8]>;
47
48 /// Return this command's serialized data.
49 fn bytes(&self) -> &[u8];
50
51 /// Return this command's max cut. Max cut is the maximum distance to the init command.
52 fn max_cut(&self) -> Result<usize, Bug> {
53 match self.parent() {
54 Prior::None => Ok(0),
55 Prior::Single(l) => Ok(l.max_cut.checked_add(1).assume("must not overflow")?),
56 Prior::Merge(l, r) => Ok(l
57 .max_cut
58 .max(r.max_cut)
59 .checked_add(1)
60 .assume("must not overflow")?),
61 }
62 }
63
64 /// Return this command's address.
65 fn address(&self) -> Result<Address, Bug> {
66 Ok(Address {
67 id: self.id(),
68 max_cut: self.max_cut()?,
69 })
70 }
71}
72
73impl<C: Command> Command for &C {
74 fn priority(&self) -> Priority {
75 (*self).priority()
76 }
77
78 fn id(&self) -> CmdId {
79 (*self).id()
80 }
81
82 fn parent(&self) -> Prior<Address> {
83 (*self).parent()
84 }
85
86 fn policy(&self) -> Option<&[u8]> {
87 (*self).policy()
88 }
89
90 fn bytes(&self) -> &[u8] {
91 (*self).bytes()
92 }
93
94 fn max_cut(&self) -> Result<usize, Bug> {
95 (*self).max_cut()
96 }
97
98 fn address(&self) -> Result<Address, Bug> {
99 (*self).address()
100 }
101}
102
103#[derive(Debug, Serialize, Deserialize, Clone, Copy, Ord, PartialEq, PartialOrd, Eq, Default)]
104/// An address contains all of the information needed to find a command in
105/// another graph.
106///
107/// The command id identifies the command you're searching for and the
108/// max_cut allows that command to be found efficiently.
109pub struct Address {
110 pub id: CmdId,
111 pub max_cut: usize,
112}
113
114impl Prior<Address> {
115 /// Returns the max cut for the command that is after this prior.
116 pub fn next_max_cut(&self) -> Result<usize, Bug> {
117 Ok(match self {
118 Self::None => 1,
119 Self::Single(l) => l.max_cut.checked_add(1).assume("must not overflow")?,
120 Self::Merge(l, r) => l
121 .max_cut
122 .max(r.max_cut)
123 .checked_add(1)
124 .assume("must not overflow")?,
125 })
126 }
127}
128
129#[cfg(test)]
130mod test {
131 use super::*;
132
133 #[test]
134 fn priority_ordering() {
135 assert!(Priority::Merge < Priority::Basic(0));
136 assert!(Priority::Basic(0) < Priority::Basic(1));
137 assert!(Priority::Basic(1) < Priority::Basic(u32::MAX));
138 assert!(Priority::Basic(u32::MAX) < Priority::Finalize);
139 assert!(Priority::Finalize < Priority::Init);
140 }
141}