1use std::time::Duration;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub enum Hello {
5 Ok,
6 BackOff(Duration),
7 Unknown,
8}
9
10impl Hello {
11 pub fn is_ok(&self) -> bool {
12 matches!(self, Self::Ok)
13 }
14
15 pub fn backoff_duration(&self) -> Option<Duration> {
16 if let Self::BackOff(d) = self {
17 Some(*d)
18 } else {
19 None
20 }
21 }
22}
23
24impl Hello {
25 pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
26 match bytes.first()? {
27 0 => Some(Self::Ok),
28 1 => {
29 let d = u64::from_be_bytes(bytes.get(1..9)?.try_into().ok()?);
30 Some(Self::BackOff(Duration::from_secs(d)))
31 },
32 _ => Some(Self::Unknown),
33 }
34 }
35
36 pub fn to_bytes(&self) -> HelloBytes {
37 match self {
38 Self::Ok => HelloBytes::Ok([0]),
39 Self::BackOff(d) => {
40 let mut b = [1; 9];
41 b[1..].copy_from_slice(&d.as_secs().to_be_bytes());
42 HelloBytes::BackOff(b)
43 },
44 Self::Unknown => unreachable!("nothing constructs Hello::Unknown"),
45 }
46 }
47}
48
49pub(crate) enum HelloBytes {
50 Ok([u8; 1]),
51 BackOff([u8; 9]),
52}
53
54impl AsRef<[u8]> for HelloBytes {
55 fn as_ref(&self) -> &[u8] {
56 match self {
57 Self::Ok(a) => &a[..],
58 Self::BackOff(a) => &a[..],
59 }
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use std::time::Duration;
66
67 use quickcheck::{Arbitrary, Gen, quickcheck};
68
69 use super::Hello;
70
71 impl Arbitrary for Hello {
72 fn arbitrary(g: &mut Gen) -> Self {
73 match bool::arbitrary(g) {
74 true => Self::Ok,
75 false => Self::BackOff(Duration::from_secs(u64::arbitrary(g))),
76 }
77 }
78 }
79
80 quickcheck! {
81 fn prop_to_bytes_from_bytes_id(h1: Hello) -> bool {
82 let b = h1.to_bytes();
83 let h2 = Hello::from_bytes(b.as_ref());
84 Some(h1) == h2
85 }
86 }
87}