zedmq/codec/
command.rs

1use std::fmt;
2
3use super::Frame;
4
5// -- PropertyIterator<'a>
6
7struct PropertyIterator<'a, I>
8where
9    I: Iterator<Item = (usize, &'a u8)>,
10{
11    inner: &'a Command<'a>,
12    cursor: I,
13}
14
15impl<'a, I> Iterator for PropertyIterator<'a, I>
16where
17    I: Iterator<Item = (usize, &'a u8)>,
18{
19    type Item = (&'a str, &'a str);
20
21    fn next(&mut self) -> Option<Self::Item> {
22        let (name_idx, name_size) = self.cursor.next()?;
23
24        // Jump over the name chunk so that the field is next.
25        for _ in 0..(*name_size) {
26            let _ = self
27                .cursor
28                .next()
29                .expect("Expected to jump over a name chunk byte...");
30        }
31
32        // Extract out the name str.
33        let name_start = name_idx + 1;
34        let name = {
35            let range = name_start..(name_start + *name_size as usize);
36            let slice = &self.inner.frame.bytes[range];
37
38            std::str::from_utf8(slice).unwrap_or("INVALID.UTF-8")
39        };
40
41        // Read four octets into an array and cast it to a u32
42        // It's written like this so that the iterator is advanced simultaneously.
43        let (field_idx, field_size) = {
44            let mut field_size = [0u8; 4];
45            let mut field_idx = 0;
46
47            for idx in 0..4 {
48                let (pos, byte) = self
49                    .cursor
50                    .next()
51                    .map(|(idx, n)| (idx, *n))
52                    .expect("Unexpected EOF");
53                field_idx = pos;
54                field_size[idx] = byte;
55            }
56
57            (field_idx + 1, u32::from_be_bytes(field_size) as usize)
58        };
59
60        // And now slice out the field.
61        let field = {
62            let range = field_idx..(field_idx + field_size as usize);
63            let slice = &self.inner.frame.bytes[range];
64
65            std::str::from_utf8(slice).unwrap_or("INVALID.UTF-8")
66        };
67
68        // Finally jump over the current field chunk.
69        for _ in 0..field_size {
70            let _ = self.cursor.next();
71        }
72
73        Some((name, field))
74    }
75}
76
77// -- Command<'_>
78
79pub struct Command<'a> {
80    pub(crate) frame: Frame<'a>,
81}
82
83impl<'a> Command<'a> {
84    pub fn new(frame: Frame<'a>) -> Self {
85        Self { frame }
86    }
87
88    pub fn name(&self) -> &str {
89        let idx = if self.frame.bytes[0] == 0x4 { 2 } else { 9 };
90        let size = self.frame.bytes[idx];
91        let start = idx + 1;
92        let end = start + (size as usize);
93        let st = std::str::from_utf8(&self.frame.bytes[start..end]).unwrap_or("INVALID.UTF-8");
94        st
95    }
96
97    /// Get an iterator over the NULL properties of this command.
98    ///
99    /// This frame is only sent once after a handshake only if the security
100    /// mechanism is NULL.
101    #[inline]
102    pub fn null_ready_properties(&self) -> Option<impl Iterator<Item = (&str, &str)>> {
103        if self.name() != "READY" {
104            return None;
105        }
106
107        let cursor = self
108            .frame
109            .bytes
110            .iter()
111            .enumerate()
112            // Skip ahead to the command-metadata/properties index
113            // which is calculated based of frame size.
114            .skip(if self.frame.bytes[0] == 0x4 { 3 } else { 10 } + self.name().len());
115
116        let it = PropertyIterator {
117            inner: self,
118            cursor,
119        };
120
121        Some(it)
122    }
123}
124
125impl<'a> fmt::Debug for Command<'a> {
126    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127        f.write_fmt(format_args!(
128            "Command {{ name: {:#?}, properties: {:#?} }}",
129            self.name(),
130            self.null_ready_properties()
131                .map(|it| it.collect::<Vec<_>>())
132                .unwrap_or_default(),
133        ))
134    }
135}