Skip to main content

nodedb_types/protocol/
request_fields.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! RequestFields — tagged union of operation-specific field sets.
4//!
5//! # Wire format (MsgPack)
6//!
7//! Encoded as a 2-element array: `[discriminant: u8, payload_map]`.
8//!
9//! ```text
10//! [discriminant: u8]  [payload: TextFields map]
11//! ```
12//!
13//! Discriminant table:
14//! ```text
15//! 0x01 = Text
16//! ```
17//!
18//! Unknown discriminants are rejected with a typed error.
19
20use serde::{Deserialize, Serialize};
21
22use super::text_fields::TextFields;
23
24// ─── Discriminant constants ────────────────────────────────────────
25
26/// Discriminant for `RequestFields::Text`.
27const DISC_TEXT: u8 = 0x01;
28
29// ─── RequestFields ─────────────────────────────────────────────────
30
31/// Operation-specific request fields.
32///
33/// Each variant carries only the fields needed for that operation.
34/// Unknown discriminants are rejected with a typed error.
35#[derive(Debug, Clone, Serialize, Deserialize)]
36#[serde(tag = "kind", rename_all = "snake_case")]
37#[non_exhaustive]
38pub enum RequestFields {
39    /// Auth, SQL, DDL, Explain, Set, Show, Reset, Begin, Commit, Rollback,
40    /// CopyFrom, Ping — all use a subset of these text fields.
41    Text(TextFields),
42}
43
44// ─── ToMessagePack ─────────────────────────────────────────────────
45
46impl zerompk::ToMessagePack for RequestFields {
47    fn write<W: zerompk::Write>(&self, writer: &mut W) -> zerompk::Result<()> {
48        writer.write_array_len(2)?;
49        match self {
50            RequestFields::Text(fields) => {
51                writer.write_u8(DISC_TEXT)?;
52                fields.write(writer)?;
53            }
54        }
55        Ok(())
56    }
57}
58
59// ─── FromMessagePack ───────────────────────────────────────────────
60
61impl<'a> zerompk::FromMessagePack<'a> for RequestFields {
62    fn read<R: zerompk::Read<'a>>(reader: &mut R) -> zerompk::Result<Self> {
63        let len = reader.read_array_len()?;
64        if len != 2 {
65            return Err(zerompk::Error::ArrayLengthMismatch {
66                expected: 2,
67                actual: len,
68            });
69        }
70        let discriminant = reader.read_u8()?;
71        match discriminant {
72            DISC_TEXT => {
73                let fields = TextFields::read(reader)?;
74                Ok(RequestFields::Text(fields))
75            }
76            other => Err(zerompk::Error::InvalidMarker(other)),
77        }
78    }
79}
80
81// ─── Tests ─────────────────────────────────────────────────────────
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86
87    /// Text discriminant roundtrip.
88    #[test]
89    fn requestfields_text_discriminant_roundtrip() {
90        let rf = RequestFields::Text(TextFields {
91            sql: Some("SELECT 42".into()),
92            top_k: Some(5),
93            ..Default::default()
94        });
95        let bytes = zerompk::to_msgpack_vec(&rf).expect("encode");
96        // Verify outer array header: fixarray(2) = 0x92
97        assert_eq!(bytes[0], 0x92, "expected fixarray(2)");
98        // Verify discriminant byte
99        assert_eq!(bytes[1], DISC_TEXT, "expected Text discriminant");
100
101        let decoded: RequestFields = zerompk::from_msgpack(&bytes).expect("decode");
102        match decoded {
103            RequestFields::Text(tf) => {
104                assert_eq!(tf.sql.as_deref(), Some("SELECT 42"));
105                assert_eq!(tf.top_k, Some(5));
106            }
107        }
108    }
109
110    /// Unknown discriminant is rejected with a typed error.
111    #[test]
112    fn requestfields_unknown_discriminant_rejected() {
113        // Build: fixarray(2), discriminant=0xFF, fixmap(0) (empty TextFields)
114        let buf = vec![
115            0x92u8, // fixarray(2)
116            0xFF,   // unknown discriminant
117            0x80,   // fixmap(0) — empty map
118        ];
119        let result: zerompk::Result<RequestFields> = zerompk::from_msgpack(&buf);
120        assert!(result.is_err(), "expected error for unknown discriminant");
121        // Must be an UnknownVariant error
122        match result.unwrap_err() {
123            zerompk::Error::InvalidMarker(v) => assert_eq!(v, 0xFF),
124            e => panic!(
125                "expected InvalidMarker for unknown discriminant, got: {:?}",
126                e
127            ),
128        }
129    }
130}