Skip to main content

avalanche_types/choices/
status.rs

1//! Status enum that represents the possible statuses of an consensus operation.
2use crate::{errors, packer::Packer};
3use bytes::Bytes;
4use serde::{Deserialize, Serialize};
5
6/// Defines possible status values.
7///
8/// ref. <https://pkg.go.dev/github.com/ava-labs/avalanchego/snow/choices#Status>
9#[derive(
10    Deserialize,
11    Serialize,
12    std::clone::Clone,
13    std::cmp::Eq,
14    std::cmp::Ord,
15    std::cmp::PartialEq,
16    std::cmp::PartialOrd,
17    std::fmt::Debug,
18    std::hash::Hash,
19)]
20pub enum Status {
21    /// The operation is known but has not been decided yet.
22    Processing,
23
24    /// The operation is already rejected and will never be accepted.
25    Rejected,
26
27    /// The operation has been accepted.
28    Accepted,
29
30    /// The status is unknown.
31    Unknown(String),
32}
33
34impl Default for Status {
35    fn default() -> Self {
36        Status::Unknown("default".to_owned())
37    }
38}
39
40impl std::convert::From<&str> for Status {
41    fn from(s: &str) -> Self {
42        match s {
43            "Processing" => Status::Processing,
44            "Rejected" => Status::Rejected,
45            "Accepted" => Status::Accepted,
46            other => Status::Unknown(other.to_owned()),
47        }
48    }
49}
50
51impl std::str::FromStr for Status {
52    type Err = std::convert::Infallible;
53
54    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
55        Ok(Status::from(s))
56    }
57}
58
59/// ref. <https://doc.rust-lang.org/std/string/trait.ToString.html>
60/// ref. <https://doc.rust-lang.org/std/fmt/trait.Display.html>
61/// Use "Self.to_string()" to directly invoke this.
62impl std::fmt::Display for Status {
63    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64        write!(f, "{}", self.as_str())
65    }
66}
67
68impl Status {
69    pub fn as_str(&self) -> &str {
70        match self {
71            Status::Processing => "Processing",
72            Status::Rejected => "Rejected",
73            Status::Accepted => "Accepted",
74            Status::Unknown(s) => s.as_ref(),
75        }
76    }
77
78    /// Returns all the `&str` values of the enum members.
79    pub fn values() -> &'static [&'static str] {
80        &["Processing", "Rejected", "Accepted"]
81    }
82
83    /// Returns "true" if the status has been decided.
84    pub fn decided(&self) -> bool {
85        matches!(self, Status::Rejected | Status::Accepted)
86    }
87
88    /// Returns "true" if the status has been set.
89    pub fn fetched(&self) -> bool {
90        match self {
91            Status::Processing => true,
92            _ => self.decided(),
93        }
94    }
95
96    /// Returns the bytes representation of this status.
97    pub fn bytes(&self) -> errors::Result<Bytes> {
98        let iota = match self {
99            Status::Processing => 1_u32,
100            Status::Rejected => 2_u32,
101            Status::Accepted => 3_u32,
102            Status::Unknown(_) => 0_u32,
103        };
104
105        let packer = Packer::new(4, 4);
106        packer.pack_u32(iota)?;
107        Ok(packer.take_bytes())
108    }
109
110    /// Returns the u32 primitive representation of this status.
111    pub fn to_u32(&self) -> u32 {
112        match self {
113            Status::Processing => 1,
114            Status::Rejected => 2,
115            Status::Accepted => 3,
116            Status::Unknown(_) => 0,
117        }
118    }
119
120    /// Returns the i32 primitive representation of this status.
121    pub fn to_i32(&self) -> i32 {
122        match self {
123            Status::Processing => 1,
124            Status::Rejected => 2,
125            Status::Accepted => 3,
126            Status::Unknown(_) => 0,
127        }
128    }
129
130    /// Returns native endian value from a slice if u8s.
131    pub fn u32_from_slice(bytes: &[u8]) -> u32 {
132        assert!(bytes.len() <= 4);
133        let d: [u8; 4] = bytes.try_into().unwrap();
134        u32::from_ne_bytes(d)
135    }
136}
137
138impl AsRef<str> for Status {
139    fn as_ref(&self) -> &str {
140        self.as_str()
141    }
142}
143
144/// RUST_LOG=debug cargo test --package avalanche-types --lib -- choices::status::test_bytes --exact --show-output
145#[test]
146fn test_bytes() {
147    let sb = Status::Processing.bytes().unwrap().to_vec();
148    assert!(cmp_manager::eq_vectors(&sb, &[0x00, 0x00, 0x00, 0x01]));
149
150    let sb = Status::Rejected.bytes().unwrap().to_vec();
151    assert!(cmp_manager::eq_vectors(&sb, &[0x00, 0x00, 0x00, 0x02]));
152
153    let sb = Status::Accepted.bytes().unwrap().to_vec();
154    assert!(cmp_manager::eq_vectors(&sb, &[0x00, 0x00, 0x00, 0x03]));
155
156    let sb = Status::Unknown("()".to_string()).bytes().unwrap().to_vec();
157    assert!(cmp_manager::eq_vectors(&sb, &[0x00, 0x00, 0x00, 0x00]));
158}
159
160/// RUST_LOG=debug cargo test --package avalanche-types --lib -- choices::status::test_to_u32 --exact --show-output
161#[test]
162fn test_to_u32() {
163    assert_eq!(Status::Unknown("hello".to_string()).to_u32(), 0);
164    assert_eq!(Status::Processing.to_u32(), 1);
165    assert_eq!(Status::Rejected.to_u32(), 2);
166    assert_eq!(Status::Accepted.to_u32(), 3);
167}