Skip to main content

thunder/wire/
value.rs

1//! The 8-variant value model and the `Request`/`Response` frames.
2//!
3//! Externally-tagged encoding (rmp-serde default): unit variants serialize
4//! as a bare string (`"Null"`), payload variants as a single-key map
5//! (`{"Int": 42}`). `Response.result` is a serde `Result`, so a successful
6//! string reply nests two one-key maps: `{"Ok": {"Str": "PONG"}}` — pinned
7//! by the conformance corpus.
8
9use serde::{Deserialize, Serialize};
10
11/// The wire value model (WIRE-002) — byte-compatible with the value
12/// models the family shipped before Thunder, by construction.
13///
14/// `Map` is an insertion-ordered pair list because keys may be any value.
15#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
16pub enum Value {
17    /// SQL NULL / nil.
18    Null,
19    Bool(bool),
20    Int(i64),
21    Float(f64),
22    /// Raw bytes. Emitted as MessagePack **bin** (WIRE-010); the legacy
23    /// int-array form decodes too (WIRE-011) via `serde_bytes`' visitor.
24    Bytes(#[serde(with = "serde_bytes")] Vec<u8>),
25    Str(String),
26    Array(Vec<Value>),
27    Map(Vec<(Value, Value)>),
28}
29
30impl Value {
31    /// Extract the inner string slice.
32    pub fn as_str(&self) -> Option<&str> {
33        match self {
34            Self::Str(s) => Some(s.as_str()),
35            _ => None,
36        }
37    }
38
39    /// Extract bytes (also accepts `Str` as UTF-8 bytes).
40    pub fn as_bytes(&self) -> Option<&[u8]> {
41        match self {
42            Self::Bytes(b) => Some(b.as_slice()),
43            Self::Str(s) => Some(s.as_bytes()),
44            _ => None,
45        }
46    }
47
48    /// Extract an integer.
49    pub fn as_int(&self) -> Option<i64> {
50        match self {
51            Self::Int(i) => Some(*i),
52            _ => None,
53        }
54    }
55
56    /// Extract a float (accepts `Int` widened to `f64`).
57    pub fn as_float(&self) -> Option<f64> {
58        match self {
59            Self::Float(f) => Some(*f),
60            Self::Int(i) => Some(*i as f64),
61            _ => None,
62        }
63    }
64
65    /// Extract a bool.
66    pub fn as_bool(&self) -> Option<bool> {
67        match self {
68            Self::Bool(b) => Some(*b),
69            _ => None,
70        }
71    }
72
73    /// Extract the array items.
74    pub fn as_array(&self) -> Option<&[Value]> {
75        match self {
76            Self::Array(items) => Some(items.as_slice()),
77            _ => None,
78        }
79    }
80
81    /// Extract the map pairs.
82    pub fn as_map(&self) -> Option<&[(Value, Value)]> {
83        match self {
84            Self::Map(pairs) => Some(pairs.as_slice()),
85            _ => None,
86        }
87    }
88
89    /// Look up a string key in a `Map` value.
90    pub fn map_get(&self, key: &str) -> Option<&Value> {
91        self.as_map()?
92            .iter()
93            .find(|(k, _)| k.as_str() == Some(key))
94            .map(|(_, v)| v)
95    }
96
97    /// True for `Value::Null`.
98    pub fn is_null(&self) -> bool {
99        matches!(self, Self::Null)
100    }
101}
102
103impl From<bool> for Value {
104    fn from(b: bool) -> Self {
105        Self::Bool(b)
106    }
107}
108impl From<i64> for Value {
109    fn from(i: i64) -> Self {
110        Self::Int(i)
111    }
112}
113impl From<f64> for Value {
114    fn from(f: f64) -> Self {
115        Self::Float(f)
116    }
117}
118impl From<String> for Value {
119    fn from(s: String) -> Self {
120        Self::Str(s)
121    }
122}
123impl From<&str> for Value {
124    fn from(s: &str) -> Self {
125        Self::Str(s.to_owned())
126    }
127}
128impl From<Vec<u8>> for Value {
129    fn from(b: Vec<u8>) -> Self {
130        Self::Bytes(b)
131    }
132}
133impl From<Vec<Value>> for Value {
134    fn from(items: Vec<Value>) -> Self {
135        Self::Array(items)
136    }
137}
138
139/// One RPC request (WIRE-001). `id` is client-chosen and echoed back;
140/// many requests multiplex over one connection. Serialized as an array
141/// (WIRE-012); map-shaped requests decode too (WIRE-013).
142#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
143pub struct Request {
144    pub id: u32,
145    pub command: String,
146    pub args: Vec<Value>,
147}
148
149/// One RPC response (WIRE-001). `result` is `Ok(value)` or an error
150/// string; v1 carries no structured error object — conventions are
151/// prefix-based and profile-driven (WIRE-040).
152#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
153pub struct Response {
154    pub id: u32,
155    pub result: Result<Value, String>,
156}
157
158impl Response {
159    /// Success response.
160    pub fn ok(id: u32, value: Value) -> Self {
161        Self {
162            id,
163            result: Ok(value),
164        }
165    }
166
167    /// Error response with the verbatim error string.
168    pub fn err(id: u32, message: impl Into<String>) -> Self {
169        Self {
170            id,
171            result: Err(message.into()),
172        }
173    }
174}