Skip to main content

micromegas_transit/
value.rs

1use anyhow::{Result, bail};
2
3/// A parsed object: a type name and a slice of named members.
4///
5/// All fields borrow from the parse arena (or the source buffer / stream
6/// metadata), so `Object` is `Copy` and carries no `Drop` glue. This is what
7/// lets it be bump-allocated safely.
8#[derive(Debug, Clone, Copy)]
9pub struct Object<'a> {
10    pub type_name: &'a str,
11    pub members: &'a [(&'a str, Value<'a>)],
12}
13
14impl<'a> Object<'a> {
15    pub fn get<T>(&self, member_name: &str) -> Result<T>
16    where
17        T: TransitValue<'a>,
18    {
19        for m in self.members {
20            if m.0 == member_name {
21                return T::get(m.1);
22            }
23        }
24        bail!("member {} not found in {:?}", member_name, self);
25    }
26
27    pub fn get_ref(&self, member_name: &str) -> Result<&'a Value<'a>> {
28        for m in self.members {
29            if m.0 == member_name {
30                return Ok(&m.1);
31            }
32        }
33        bail!("member {} not found", member_name);
34    }
35}
36
37pub trait TransitValue<'a>: Sized {
38    fn get(value: Value<'a>) -> Result<Self>;
39}
40
41impl<'a> TransitValue<'a> for u8 {
42    fn get(value: Value<'a>) -> Result<Self> {
43        if let Value::U8(val) = value {
44            Ok(val)
45        } else {
46            bail!("bad type cast u8 for value {:?}", value);
47        }
48    }
49}
50
51impl<'a> TransitValue<'a> for u32 {
52    fn get(value: Value<'a>) -> Result<Self> {
53        match value {
54            Value::U32(val) => Ok(val),
55            Value::U8(val) => Ok(Self::from(val)),
56            _ => {
57                bail!("bad type cast u32 for value {:?}", value);
58            }
59        }
60    }
61}
62
63impl<'a> TransitValue<'a> for u64 {
64    fn get(value: Value<'a>) -> Result<Self> {
65        match value {
66            Value::I64(val) => Ok(val as Self),
67            Value::U64(val) => Ok(val),
68            _ => {
69                bail!("bad type cast u64 for value {:?}", value)
70            }
71        }
72    }
73}
74
75impl<'a> TransitValue<'a> for i64 {
76    #[allow(clippy::cast_possible_wrap)]
77    fn get(value: Value<'a>) -> Result<Self> {
78        match value {
79            Value::I64(val) => Ok(val),
80            Value::U64(val) => Ok(val as Self),
81            _ => {
82                bail!("bad type cast i64 for value {:?}", value)
83            }
84        }
85    }
86}
87
88impl<'a> TransitValue<'a> for f64 {
89    fn get(value: Value<'a>) -> Result<Self> {
90        if let Value::F64(val) = value {
91            Ok(val)
92        } else {
93            bail!("bad type cast f64 for value {:?}", value);
94        }
95    }
96}
97
98impl<'a> TransitValue<'a> for &'a str {
99    fn get(value: Value<'a>) -> Result<Self> {
100        if let Value::String(val) = value {
101            Ok(val)
102        } else {
103            bail!("bad type cast str for value {:?}", value);
104        }
105    }
106}
107
108impl<'a> TransitValue<'a> for &'a Object<'a> {
109    fn get(value: Value<'a>) -> Result<Self> {
110        if let Value::Object(val) = value {
111            Ok(val)
112        } else {
113            bail!("bad type cast Object for value {:?}", value);
114        }
115    }
116}
117
118impl<'a> TransitValue<'a> for &'a [u8] {
119    fn get(value: Value<'a>) -> Result<Self> {
120        if let Value::Bytes(val) = value {
121            Ok(val)
122        } else {
123            bail!("bad type cast bytes for value {:?}", value);
124        }
125    }
126}
127
128/// A schemaless runtime value parsed from a transit buffer.
129///
130/// Every variant is a primitive or a shared borrow, so `Value` is `Copy` and
131/// `Drop`-free; it can be stored in a bump arena and discarded by resetting the
132/// arena rather than by running destructors.
133#[derive(Debug, Clone, Copy)]
134pub enum Value<'a> {
135    Bytes(&'a [u8]),
136    F64(f64),
137    I64(i64),
138    None,
139    Object(&'a Object<'a>),
140    String(&'a str),
141    U8(u8),
142    U32(u32),
143    U64(u64),
144}
145
146impl<'a> Value<'a> {
147    pub fn as_str(&self) -> Option<&'a str> {
148        if let Value::String(s) = self {
149            Some(*s)
150        } else {
151            None
152        }
153    }
154}
155
156// Compile-time guarantee that the arena-allocated representation stays `Copy`
157// (hence `Drop`-free): bump allocation never runs destructors, so a `Drop` type
158// here would leak.
159const _: fn() = || {
160    fn assert_copy<T: Copy>() {}
161    assert_copy::<Value<'static>>();
162    assert_copy::<Object<'static>>();
163};