Skip to main content

substrate_data/wire/frame/
shaped.rs

1//! A frame read by its [`Shape`] alone, as a [`Value`] — what a tool (an
2//! IDE's message inspector) shows of a message it has no code for, and the
3//! rule a reader in another language follows: what packs itself is read as
4//! packed, everything else as its binary form.
5
6use super::Unpacker;
7use crate::model::value::{Map, Value};
8use crate::schema::{EnumShape, Shape, StructShape};
9
10/// Bits of a 32-bit float.
11const F32_BITS: u32 = 32;
12
13/// The frame `bytes` read as a value of `shape`.
14#[must_use]
15pub fn read_shaped(bytes: &[u8], shape: &Shape) -> Option<Value> {
16    let mut u = Unpacker::open(bytes)?;
17    let v = unpack_shaped(&mut u, shape)?;
18    u.at_end().then_some(v)
19}
20
21/// The next value, of `shape`.
22pub fn unpack_shaped(u: &mut Unpacker<'_>, shape: &Shape) -> Option<Value> {
23    Some(match shape {
24        Shape::Bool => Value::Bool(u.bool()?),
25        Shape::Int { .. } => Value::Int(u.int()?),
26        Shape::Float { bits } if *bits == F32_BITS => Value::Float(f64::from(u.f32()?)),
27        Shape::Float { .. } => Value::Float(u.f64()?),
28        // Plain text, as the model spells it (a leading `@` doubled).
29        Shape::Str => Value::Str(crate::typed::escape_at(u.str()?)),
30        Shape::Atom => Value::Str(crate::typed::escape_at(
31            crate::Atom::from_number(u32::try_from(u.uint()?).ok()?)?.as_str(),
32        )),
33        // A named type packs as what it's stored as.
34        Shape::Named(named) => unpack_shaped(u, &named.stored())?,
35        // A reference of declared kinds packs only its id, as its id's
36        // shape says; one of any kind, its kind and id as text.
37        Shape::Ref { kinds, id } => Value::Ref(unpack_ref(u, kinds, &id())?),
38        Shape::Bytes => Value::Bytes(u.bytes()?.to_vec()),
39        Shape::Optional(inner) => optional(u, &inner())?,
40        Shape::List(item) => list(u, &item())?,
41        Shape::Struct(s) => record(u, s)?,
42        Shape::Enum(e) if e.tag.is_none() => names(u, e)?,
43        _ => u.tagged::<Value>()?,
44    })
45}
46
47/// An optional value: whether it's there, then it.
48fn optional(u: &mut Unpacker<'_>, inner: &Shape) -> Option<Value> {
49    if u.bool()? {
50        unpack_shaped(u, inner)
51    } else {
52        Some(Value::Null)
53    }
54}
55
56/// A reference: of declared `kinds`, only its id (packed as `id` says);
57/// of any kind, its kind and id as text.
58fn unpack_ref(u: &mut Unpacker<'_>, kinds: &[&str], id: &Shape) -> Option<crate::Ref> {
59    let Some(kind) = kinds.first() else {
60        return Some(crate::Ref::new(
61            crate::KindName::new(u.str()?).ok()?,
62            crate::RefKey::new(u.str()?).ok()?,
63        ));
64    };
65    Some(crate::Ref::new(
66        crate::KindName::new(kind).ok()?,
67        crate::RefKey::new(&unpack_id(u, id)?).ok()?,
68    ))
69}
70
71/// A reference's id packed as `shape`, spelled.
72fn unpack_id(u: &mut Unpacker<'_>, shape: &Shape) -> Option<String> {
73    match shape {
74        Shape::Int { .. } => Some(u.int()?.to_string()),
75        Shape::Named(named) => unpack_id(u, &named.stored()),
76        _ => Some(u.str()?.to_owned()),
77    }
78}
79
80/// A list: numbers packed, anything else value by value.
81fn list(u: &mut Unpacker<'_>, item: &Shape) -> Option<Value> {
82    Some(Value::List(match item {
83        Shape::Int { min, .. } if *min < 0 => {
84            u.uints(|v| Some(Value::Int(crate::binary::unzigzag(v))))?
85        }
86        // A 64-bit unsigned list keeps its bits as the model's `Int` does
87        // (`u64::MAX` is `Int(-1)`); a narrower one only holds what fits.
88        Shape::Int { max, .. } if *max == i64::MAX => {
89            u.uints(|v| Some(Value::Int(v.cast_signed())))?
90        }
91        Shape::Int { .. } => u.uints(|v| i64::try_from(v).ok().map(Value::Int))?,
92        Shape::Float { bits } if *bits == F32_BITS => u
93            .f32s()?
94            .into_iter()
95            .map(|f| Value::Float(f64::from(f)))
96            .collect(),
97        Shape::Float { .. } => u.f64s()?.into_iter().map(Value::Float).collect(),
98        _ => {
99            let (n, end) = u.list()?;
100            let items = (0..n)
101                .map(|_| unpack_shaped(u, item))
102                .collect::<Option<_>>()?;
103            u.skip_to(end)?;
104            items
105        }
106    }))
107}
108
109/// A record: its fields in order, as a map by their names; fields the shape
110/// doesn't know skipped, missing ones left out.
111fn record(u: &mut Unpacker<'_>, s: &StructShape) -> Option<Value> {
112    let end = u.begin()?;
113    let mut m = Map::new();
114    for field in &s.fields {
115        if !u.more(end) {
116            break;
117        }
118        m.insert(field.name, unpack_shaped(u, &(field.shape)())?);
119    }
120    u.leave(end)?;
121    Some(Value::Map(m))
122}
123
124/// An enum of names: its variant's name, by index.
125fn names(u: &mut Unpacker<'_>, e: &EnumShape) -> Option<Value> {
126    let i = usize::try_from(u.uint()?).ok()?;
127    Some(Value::Str(e.variants.get(i)?.name.to_owned()))
128}