1use std::collections::BTreeMap;
14use std::fmt;
15
16#[derive(Debug, Clone, PartialEq)]
23pub enum Value {
24 Null,
25 Int64(i64),
26 Uint64(u64),
27 Double(f64),
28 Boolean(bool),
29 String(Vec<u8>),
32 Any(Vec<u8>),
34}
35
36impl Value {
37 pub fn as_bytes(&self) -> Option<&[u8]> {
39 match self {
40 Self::String(bytes) | Self::Any(bytes) => Some(bytes),
41 _ => None,
42 }
43 }
44
45 pub fn as_str(&self) -> Option<&str> {
47 std::str::from_utf8(self.as_bytes()?).ok()
48 }
49
50 pub fn as_i64(&self) -> Option<i64> {
51 match self {
52 Self::Int64(value) => Some(*value),
53 _ => None,
54 }
55 }
56
57 pub fn as_u64(&self) -> Option<u64> {
58 match self {
59 Self::Uint64(value) => Some(*value),
60 _ => None,
61 }
62 }
63
64 pub fn as_f64(&self) -> Option<f64> {
65 match self {
66 Self::Double(value) => Some(*value),
67 _ => None,
68 }
69 }
70
71 pub fn as_bool(&self) -> Option<bool> {
72 match self {
73 Self::Boolean(value) => Some(*value),
74 _ => None,
75 }
76 }
77
78 pub fn is_null(&self) -> bool {
79 matches!(self, Self::Null)
80 }
81}
82
83impl From<i64> for Value {
84 fn from(value: i64) -> Self {
85 Self::Int64(value)
86 }
87}
88
89impl From<u64> for Value {
90 fn from(value: u64) -> Self {
91 Self::Uint64(value)
92 }
93}
94
95impl From<f64> for Value {
96 fn from(value: f64) -> Self {
97 Self::Double(value)
98 }
99}
100
101impl From<bool> for Value {
102 fn from(value: bool) -> Self {
103 Self::Boolean(value)
104 }
105}
106
107impl From<&str> for Value {
108 fn from(value: &str) -> Self {
109 Self::String(value.as_bytes().to_vec())
110 }
111}
112
113impl From<String> for Value {
114 fn from(value: String) -> Self {
115 Self::String(value.into_bytes())
116 }
117}
118
119impl From<Vec<u8>> for Value {
120 fn from(value: Vec<u8>) -> Self {
121 Self::String(value)
122 }
123}
124
125impl<T: Into<Value>> From<Option<T>> for Value {
126 fn from(value: Option<T>) -> Self {
127 match value {
128 Some(value) => value.into(),
129 None => Self::Null,
130 }
131 }
132}
133
134#[derive(Debug, Clone, Default, PartialEq)]
140pub struct Row {
141 columns: Vec<(String, Value)>,
142}
143
144impl Row {
145 pub fn new() -> Self {
146 Self::default()
147 }
148
149 #[must_use]
151 pub fn with(mut self, name: impl Into<String>, value: impl Into<Value>) -> Self {
152 self.columns.push((name.into(), value.into()));
153 self
154 }
155
156 pub fn set(&mut self, name: impl Into<String>, value: impl Into<Value>) {
158 self.columns.push((name.into(), value.into()));
159 }
160
161 pub fn get(&self, name: &str) -> Option<&Value> {
163 self.columns
164 .iter()
165 .find(|(column, _)| column == name)
166 .map(|(_, value)| value)
167 }
168
169 pub fn columns(&self) -> &[(String, Value)] {
171 &self.columns
172 }
173
174 pub fn names(&self) -> impl Iterator<Item = &str> {
176 self.columns.iter().map(|(name, _)| name.as_str())
177 }
178
179 pub fn len(&self) -> usize {
180 self.columns.len()
181 }
182
183 pub fn is_empty(&self) -> bool {
184 self.columns.is_empty()
185 }
186
187 pub fn to_map(&self) -> BTreeMap<&str, &Value> {
190 self.columns
191 .iter()
192 .map(|(name, value)| (name.as_str(), value))
193 .collect()
194 }
195}
196
197impl FromIterator<(String, Value)> for Row {
198 fn from_iter<I: IntoIterator<Item = (String, Value)>>(iterator: I) -> Self {
199 Self {
200 columns: iterator.into_iter().collect(),
201 }
202 }
203}
204
205impl fmt::Display for Row {
206 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
207 formatter.write_str("{")?;
208 for (index, (name, value)) in self.columns.iter().enumerate() {
209 if index > 0 {
210 formatter.write_str(", ")?;
211 }
212 match value {
213 Value::String(bytes) | Value::Any(bytes) => match std::str::from_utf8(bytes) {
214 Ok(text) => write!(formatter, "{name}={text:?}")?,
215 Err(_) => write!(formatter, "{name}=<{} bytes>", bytes.len())?,
216 },
217 other => write!(formatter, "{name}={other:?}")?,
218 }
219 }
220 formatter.write_str("}")
221 }
222}
223
224pub type MaybeRow = Option<Row>;
230
231#[cfg(test)]
232mod tests {
233 use super::*;
234
235 #[test]
236 fn a_row_keeps_the_order_its_columns_were_added_in() {
237 let row = Row::new().with("b", 2i64).with("a", 1i64).with("c", 3i64);
239 assert_eq!(row.names().collect::<Vec<_>>(), ["b", "a", "c"]);
240 }
241
242 #[test]
243 fn columns_are_read_by_name() {
244 let row = Row::new().with("key", 42i64).with("value", "hello");
245 assert_eq!(row.get("key"), Some(&Value::Int64(42)));
246 assert_eq!(row.get("value").and_then(Value::as_str), Some("hello"));
247 assert_eq!(row.get("absent"), None);
248 }
249
250 #[test]
251 fn strings_are_bytes_and_need_not_be_utf8() {
252 let row = Row::new().with("raw", vec![0xff, 0xfe]);
253 assert_eq!(row.get("raw").unwrap().as_bytes(), Some(&[0xff, 0xfe][..]));
254 assert_eq!(
255 row.get("raw").unwrap().as_str(),
256 None,
257 "not UTF-8, and that is allowed"
258 );
259 }
260
261 #[test]
262 fn an_option_becomes_null() {
263 let row = Row::new()
264 .with("present", Some(1i64))
265 .with("absent", None::<i64>);
266 assert_eq!(row.get("present"), Some(&Value::Int64(1)));
267 assert!(row.get("absent").unwrap().is_null());
268 }
269
270 #[test]
271 fn display_is_readable_and_does_not_choke_on_binary() {
272 let row = Row::new().with("key", 1i64).with("blob", vec![0xff, 0x00]);
273 assert_eq!(row.to_string(), r#"{key=Int64(1), blob=<2 bytes>}"#);
274 }
275
276 #[test]
277 fn accessors_report_the_wrong_type_as_absent() {
278 let value = Value::Int64(1);
279 assert_eq!(value.as_i64(), Some(1));
280 assert_eq!(value.as_u64(), None);
281 assert_eq!(value.as_str(), None);
282 assert!(!value.is_null());
283 }
284}