Skip to main content

aranya_runtime/
command.rs

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