Skip to main content

capnp/
dynamic_value.rs

1//! Dynamically typed values.
2
3use crate::introspect::{self, TypeVariant};
4use crate::schema_capnp::value;
5use crate::Result;
6use crate::{dynamic_list, dynamic_struct};
7
8/// A dynamically-typed read-only value.
9#[derive(Clone, Copy)]
10pub enum Reader<'a> {
11    Void,
12    Bool(bool),
13    Int8(i8),
14    Int16(i16),
15    Int32(i32),
16    Int64(i64),
17    UInt8(u8),
18    UInt16(u16),
19    UInt32(u32),
20    UInt64(u64),
21    Float32(f32),
22    Float64(f64),
23    Enum(Enum),
24    Text(crate::text::Reader<'a>),
25    Data(crate::data::Reader<'a>),
26    Struct(dynamic_struct::Reader<'a>),
27    List(dynamic_list::Reader<'a>),
28    AnyPointer(crate::any_pointer::Reader<'a>),
29    Capability(Capability),
30}
31
32impl<'a> Reader<'a> {
33    pub fn new(value: value::Reader<'a>, ty: introspect::Type) -> Result<Self> {
34        match (value.which()?, ty.which()) {
35            (value::Void(()), _) => Ok(Reader::Void),
36            (value::Bool(b), _) => Ok(Reader::Bool(b)),
37            (value::Int8(x), _) => Ok(Reader::Int8(x)),
38            (value::Int16(x), _) => Ok(Reader::Int16(x)),
39            (value::Int32(x), _) => Ok(Reader::Int32(x)),
40            (value::Int64(x), _) => Ok(Reader::Int64(x)),
41            (value::Uint8(x), _) => Ok(Reader::UInt8(x)),
42            (value::Uint16(x), _) => Ok(Reader::UInt16(x)),
43            (value::Uint32(x), _) => Ok(Reader::UInt32(x)),
44            (value::Uint64(x), _) => Ok(Reader::UInt64(x)),
45            (value::Float32(x), _) => Ok(Reader::Float32(x)),
46            (value::Float64(x), _) => Ok(Reader::Float64(x)),
47            (value::Enum(d), TypeVariant::Enum(e)) => Ok(Reader::Enum(Enum::new(d, e.into()))),
48            (value::Text(t), _) => Ok(Reader::Text(t?)),
49            (value::Data(d), _) => Ok(Reader::Data(d?)),
50            (value::Struct(d), TypeVariant::Struct(schema)) => Ok(Reader::Struct(
51                dynamic_struct::Reader::new(d.reader.get_struct(None)?, schema.into()),
52            )),
53            (value::List(l), TypeVariant::List(element_type)) => {
54                Ok(Reader::List(dynamic_list::Reader::new(
55                    l.reader
56                        .get_list(element_type.expected_element_size(), None)?,
57                    element_type,
58                )))
59            }
60            (value::Interface(()), TypeVariant::Capability) => Ok(Capability.into()),
61            (value::AnyPointer(a), TypeVariant::AnyPointer) => Ok(a.into()),
62            _ => Err(crate::Error::from_kind(crate::ErrorKind::TypeMismatch)),
63        }
64    }
65
66    /// Downcasts the `Reader` into a more specific type. Panics if the
67    /// expected type does not match the value.
68    pub fn downcast<T: DowncastReader<'a>>(self) -> T {
69        T::downcast_reader(self)
70    }
71
72    /// Downcasts the `Reader` into a specific struct type. Panics if the
73    /// expected type does not match the value.
74    ///
75    /// Design note: instead of this method, it would be better to add a blanket impl
76    /// of the `DowncastBuilder` trait that covered every struct type. Unfortunately,
77    /// the current way the `Introspect` and `OwnedStruct` traits are set up does not
78    /// seem to allow this.
79    pub fn downcast_struct<T: crate::traits::OwnedStruct>(self) -> T::Reader<'a> {
80        let sr: dynamic_struct::Reader = self.downcast();
81        sr.downcast::<T>()
82    }
83}
84
85impl<'a> From<()> for Reader<'a> {
86    fn from((): ()) -> Reader<'a> {
87        Reader::Void
88    }
89}
90
91macro_rules! primitive_dynamic_value(
92    ($t:ty, $v:ident) => (
93        impl <'a> From<$t> for Reader<'a> {
94            fn from(x: $t) -> Reader<'a> { Reader::$v(x) }
95        }
96    )
97);
98
99primitive_dynamic_value!(bool, Bool);
100primitive_dynamic_value!(i8, Int8);
101primitive_dynamic_value!(i16, Int16);
102primitive_dynamic_value!(i32, Int32);
103primitive_dynamic_value!(i64, Int64);
104primitive_dynamic_value!(u8, UInt8);
105primitive_dynamic_value!(u16, UInt16);
106primitive_dynamic_value!(u32, UInt32);
107primitive_dynamic_value!(u64, UInt64);
108primitive_dynamic_value!(f32, Float32);
109primitive_dynamic_value!(f64, Float64);
110
111/// Helper trait for the `dynamic_value::Reader::downcast()` method.
112pub trait DowncastReader<'a> {
113    fn downcast_reader(v: Reader<'a>) -> Self;
114}
115
116impl<'a> DowncastReader<'a> for () {
117    fn downcast_reader(value: Reader<'a>) {
118        let Reader::Void = value else {
119            panic!("error downcasting to void")
120        };
121    }
122}
123
124macro_rules! downcast_reader_impl(
125    ($t:ty, $v:ident, $s:expr) => (
126        impl <'a> DowncastReader<'a> for $t {
127            fn downcast_reader(value: Reader<'a>) -> Self {
128                let Reader::$v(x) = value else { panic!("error downcasting to {}", $s) };
129                x
130            }
131        }
132    )
133);
134
135downcast_reader_impl!(bool, Bool, "bool");
136downcast_reader_impl!(i8, Int8, "i8");
137downcast_reader_impl!(i16, Int16, "i16");
138downcast_reader_impl!(i32, Int32, "i32");
139downcast_reader_impl!(i64, Int64, "i64");
140downcast_reader_impl!(u8, UInt8, "u8");
141downcast_reader_impl!(u16, UInt16, "u16");
142downcast_reader_impl!(u32, UInt32, "u32");
143downcast_reader_impl!(u64, UInt64, "u64");
144downcast_reader_impl!(f32, Float32, "f32");
145downcast_reader_impl!(f64, Float64, "f64");
146downcast_reader_impl!(Enum, Enum, "enum");
147downcast_reader_impl!(crate::text::Reader<'a>, Text, "text");
148downcast_reader_impl!(crate::data::Reader<'a>, Data, "data");
149downcast_reader_impl!(dynamic_list::Reader<'a>, List, "list");
150downcast_reader_impl!(dynamic_struct::Reader<'a>, Struct, "struct");
151downcast_reader_impl!(crate::any_pointer::Reader<'a>, AnyPointer, "anypointer");
152
153/// A dynamically-typed value with mutable interior.
154pub enum Builder<'a> {
155    Void,
156    Bool(bool),
157    Int8(i8),
158    Int16(i16),
159    Int32(i32),
160    Int64(i64),
161    UInt8(u8),
162    UInt16(u16),
163    UInt32(u32),
164    UInt64(u64),
165    Float32(f32),
166    Float64(f64),
167    Enum(Enum),
168    Text(crate::text::Builder<'a>),
169    Data(crate::data::Builder<'a>),
170    Struct(dynamic_struct::Builder<'a>),
171    List(dynamic_list::Builder<'a>),
172    AnyPointer(crate::any_pointer::Builder<'a>),
173    Capability(Capability),
174}
175
176impl<'a> Builder<'a> {
177    pub fn reborrow(&mut self) -> Builder<'_> {
178        match self {
179            Builder::Void => Builder::Void,
180            Builder::Bool(b) => Builder::Bool(*b),
181            Builder::Int8(x) => Builder::Int8(*x),
182            Builder::Int16(x) => Builder::Int16(*x),
183            Builder::Int32(x) => Builder::Int32(*x),
184            Builder::Int64(x) => Builder::Int64(*x),
185            Builder::UInt8(x) => Builder::UInt8(*x),
186            Builder::UInt16(x) => Builder::UInt16(*x),
187            Builder::UInt32(x) => Builder::UInt32(*x),
188            Builder::UInt64(x) => Builder::UInt64(*x),
189            Builder::Float32(x) => Builder::Float32(*x),
190            Builder::Float64(x) => Builder::Float64(*x),
191            Builder::Enum(e) => Builder::Enum(*e),
192            Builder::Text(t) => Builder::Text(t.reborrow()),
193            Builder::Data(d) => Builder::Data(d),
194            Builder::Struct(ref mut s) => Builder::Struct(s.reborrow()),
195            Builder::List(ref mut l) => Builder::List(l.reborrow()),
196            Builder::AnyPointer(ref mut a) => Builder::AnyPointer(a.reborrow()),
197            Builder::Capability(c) => Builder::Capability(*c),
198        }
199    }
200
201    pub fn into_reader(self) -> Reader<'a> {
202        match self {
203            Builder::Void => Reader::Void,
204            Builder::Bool(b) => Reader::Bool(b),
205            Builder::Int8(x) => Reader::Int8(x),
206            Builder::Int16(x) => Reader::Int16(x),
207            Builder::Int32(x) => Reader::Int32(x),
208            Builder::Int64(x) => Reader::Int64(x),
209            Builder::UInt8(x) => Reader::UInt8(x),
210            Builder::UInt16(x) => Reader::UInt16(x),
211            Builder::UInt32(x) => Reader::UInt32(x),
212            Builder::UInt64(x) => Reader::UInt64(x),
213            Builder::Float32(x) => Reader::Float32(x),
214            Builder::Float64(x) => Reader::Float64(x),
215            Builder::Enum(e) => Reader::Enum(e),
216            Builder::Text(t) => Reader::Text(t.into_reader()),
217            Builder::Data(d) => Reader::Data(d),
218            Builder::Struct(s) => Reader::Struct(s.into_reader()),
219            Builder::List(l) => Reader::List(l.into_reader()),
220            Builder::AnyPointer(a) => Reader::AnyPointer(a.into_reader()),
221            Builder::Capability(c) => Reader::Capability(c),
222        }
223    }
224
225    /// Downcasts the `Builder` into a more specific type. Panics if the
226    /// expected type does not match the value.
227    pub fn downcast<T: DowncastBuilder<'a>>(self) -> T {
228        T::downcast_builder(self)
229    }
230
231    /// Downcasts the `Builder` into a specific struct type. Panics if the
232    /// expected type does not match the value.
233    ///
234    /// Design note: instead of this method, it would be better to add a blanket impl
235    /// of the `DowncastBuilder` trait that covered every struct type. Unfortunately,
236    /// the current way the `Introspect` and `OwnedStruct` traits are set up does not
237    /// seem to allow this.
238    pub fn downcast_struct<T: crate::traits::OwnedStruct>(self) -> T::Builder<'a> {
239        let sb: dynamic_struct::Builder = self.downcast();
240        sb.downcast::<T>()
241    }
242}
243
244/// The `DowncastReader` implementation every generated `AnyReader` uses.
245///
246/// Panics, like the other downcasts, unless `value` is a group declared
247/// with `T`'s newtype (directly or through aliases), and its offset table
248/// has the length `T` expects. A length mismatch means the code generated
249/// for the newtype and for the schema disagree about the newtype.
250#[doc(hidden)]
251pub fn downcast_any_reader<'a, T: crate::traits::AnyReader<'a>>(value: Reader<'a>) -> T {
252    let Reader::Struct(group) = value else {
253        panic!("downcast to an AnyReader: the value is not a group");
254    };
255    let raw = group.schema.raw.generic;
256    check_any_layout(raw, T::TYPE_ID, T::LEAF_COUNT);
257    T::from_use_site(group.reader, raw.any_offsets, raw.any_discriminant_offset)
258}
259
260/// The `DowncastBuilder` implementation every generated `AnyBuilder` uses;
261/// see [`downcast_any_reader`].
262#[doc(hidden)]
263pub fn downcast_any_builder<'a, T: crate::traits::AnyBuilder<'a>>(value: Builder<'a>) -> T {
264    let Builder::Struct(group) = value else {
265        panic!("downcast to an AnyBuilder: the value is not a group");
266    };
267    let raw = group.schema.raw.generic;
268    check_any_layout(raw, T::TYPE_ID, T::LEAF_COUNT);
269    T::from_use_site(group.builder, raw.any_offsets, raw.any_discriminant_offset)
270}
271
272fn check_any_layout(raw: &crate::introspect::RawStructSchema, type_id: u64, leaf_count: usize) {
273    assert!(
274        raw.newtype_ids.contains(&type_id),
275        "downcast to an AnyReader/AnyBuilder: the group was not declared as type {type_id:#x}"
276    );
277    assert_eq!(
278        raw.any_offsets.len(),
279        leaf_count,
280        "downcast to an AnyReader/AnyBuilder: the use site's offset table doesn't match \
281         type {type_id:#x}; were they generated from different versions of it?"
282    );
283}
284
285/// Helper trait for the `dynamic_value::Builder::downcast()` method.
286pub trait DowncastBuilder<'a> {
287    fn downcast_builder(v: Builder<'a>) -> Self;
288}
289
290impl<'a> DowncastBuilder<'a> for () {
291    fn downcast_builder(value: Builder<'a>) {
292        let Builder::Void = value else {
293            panic!("error downcasting to void")
294        };
295    }
296}
297
298macro_rules! downcast_builder_impl(
299    ($t:ty, $v:ident, $s:expr) => (
300        impl <'a> DowncastBuilder<'a> for $t {
301            fn downcast_builder(value: Builder<'a>) -> Self {
302                let Builder::$v(x) = value else { panic!("error downcasting to {}", $s) };
303                x
304            }
305        }
306    )
307);
308
309downcast_builder_impl!(bool, Bool, "bool");
310downcast_builder_impl!(i8, Int8, "i8");
311downcast_builder_impl!(i16, Int16, "i16");
312downcast_builder_impl!(i32, Int32, "i32");
313downcast_builder_impl!(i64, Int64, "i64");
314downcast_builder_impl!(u8, UInt8, "u8");
315downcast_builder_impl!(u16, UInt16, "u16");
316downcast_builder_impl!(u32, UInt32, "u32");
317downcast_builder_impl!(u64, UInt64, "u64");
318downcast_builder_impl!(f32, Float32, "f32");
319downcast_builder_impl!(f64, Float64, "f64");
320downcast_builder_impl!(Enum, Enum, "enum");
321downcast_builder_impl!(crate::text::Builder<'a>, Text, "text");
322downcast_builder_impl!(crate::data::Builder<'a>, Data, "data");
323downcast_builder_impl!(dynamic_list::Builder<'a>, List, "list");
324downcast_builder_impl!(dynamic_struct::Builder<'a>, Struct, "struct");
325downcast_builder_impl!(crate::any_pointer::Builder<'a>, AnyPointer, "anypointer");
326
327/// A dynamically-typed enum value.
328#[derive(Clone, Copy)]
329pub struct Enum {
330    value: u16,
331    schema: crate::schema::EnumSchema,
332}
333
334impl Enum {
335    pub fn new(value: u16, schema: crate::schema::EnumSchema) -> Self {
336        Self { value, schema }
337    }
338
339    /// Gets the u16 representation of this value.
340    pub fn get_value(&self) -> u16 {
341        self.value
342    }
343
344    /// Gets the schema of this enumerant.
345    pub fn get_enumerant(self) -> crate::Result<Option<crate::schema::Enumerant>> {
346        let enumerants = self.schema.get_enumerants()?;
347        if (self.value) < enumerants.len() {
348            Ok(Some(enumerants.get(self.value)))
349        } else {
350            Ok(None)
351        }
352    }
353}
354
355impl<'a> From<Enum> for Reader<'a> {
356    fn from(e: Enum) -> Reader<'a> {
357        Reader::Enum(e)
358    }
359}
360
361impl<'a> From<Enum> for Builder<'a> {
362    fn from(e: Enum) -> Builder<'a> {
363        Builder::Enum(e)
364    }
365}
366
367/// A dynamic capability. Currently, this is just a stub and does not support calling
368/// of methods.
369#[derive(Clone, Copy)]
370pub struct Capability;
371
372impl<'a> From<Capability> for Reader<'a> {
373    fn from(c: Capability) -> Reader<'a> {
374        Reader::Capability(c)
375    }
376}
377
378impl<'a> From<Capability> for Builder<'a> {
379    fn from(c: Capability) -> Builder<'a> {
380        Builder::Capability(c)
381    }
382}