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
63impl<C: Command> Command for &C {
64    fn priority(&self) -> Priority {
65        (*self).priority()
66    }
67
68    fn id(&self) -> CmdId {
69        (*self).id()
70    }
71
72    fn parent(&self) -> Prior<Address> {
73        (*self).parent()
74    }
75
76    fn policy(&self) -> Option<&[u8]> {
77        (*self).policy()
78    }
79
80    fn bytes(&self) -> &[u8] {
81        (*self).bytes()
82    }
83}
84
85pub trait CommandExt: Command {
86    /// Return this command's max cut.
87    ///
88    /// Max cut is the maximum distance to the init command.
89    fn max_cut(&self) -> Result<MaxCut, Bug>;
90
91    /// Return this command's address.
92    fn address(&self) -> Result<Address, Bug>;
93}
94
95impl<C: Command> CommandExt for C {
96    fn max_cut(&self) -> Result<MaxCut, Bug> {
97        match self.parent() {
98            Prior::None => Ok(MaxCut::new(0)),
99            Prior::Single(l) => Ok(l.max_cut.checked_add(1).assume("must not overflow")?),
100            Prior::Merge(l, r) => Ok(l
101                .max_cut
102                .max(r.max_cut)
103                .checked_add(1)
104                .assume("must not overflow")?),
105        }
106    }
107
108    fn address(&self) -> Result<Address, Bug> {
109        Ok(Address {
110            id: self.id(),
111            max_cut: self.max_cut()?,
112        })
113    }
114}
115
116/// An address contains all of the information needed to find a command in
117/// another graph.
118///
119/// The command id identifies the command you're searching for and the
120/// max_cut allows that command to be found efficiently.
121#[derive(
122    Copy,
123    Clone,
124    Debug,
125    PartialEq,
126    Eq,
127    PartialOrd,
128    Ord,
129    serde::Serialize,
130    serde::Deserialize,
131    rkyv::Archive,
132    rkyv::Serialize,
133    rkyv::Deserialize,
134    rkyv::Portable,
135    rkyv::bytecheck::CheckBytes,
136)]
137#[rkyv(as = Self)]
138#[bytecheck(crate = rkyv::bytecheck)]
139#[repr(C)]
140pub struct Address {
141    pub id: CmdId,
142    pub max_cut: MaxCut,
143}
144
145#[cfg(test)]
146mod test {
147    use super::*;
148
149    #[test]
150    fn priority_ordering() {
151        assert!(Priority::Merge < Priority::Basic(0));
152        assert!(Priority::Basic(0) < Priority::Basic(1));
153        assert!(Priority::Basic(1) < Priority::Basic(u32::MAX));
154        assert!(Priority::Basic(u32::MAX) < Priority::Finalize);
155        assert!(Priority::Finalize < Priority::Init);
156    }
157}