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}