Skip to main content

oxiproto_reflect/native/
dynamic.rs

1//! A runtime-typed protobuf message: [`DynamicMessage`].
2//!
3//! A `DynamicMessage` pairs a [`MessageDescriptor`] with a sparse map from
4//! field number to [`Value`]. Only explicitly-set fields are stored; reading
5//! an unset field yields the field's default value (proto3 semantics).
6//!
7//! Wire encoding/decoding lives in [`super::wire_codec`]; this module owns the
8//! in-memory representation and field access semantics (including oneof
9//! exclusivity and unknown-field preservation).
10
11use std::borrow::Cow;
12use std::collections::BTreeMap;
13use std::collections::HashMap;
14
15use oxiproto_core::wire::UnknownFields;
16
17use super::descriptor::{Cardinality, FieldDescriptor, Kind, MessageDescriptor};
18use super::value::Value;
19
20/// A dynamically-typed protobuf message instance.
21///
22/// Construct one with [`DynamicMessage::new`], populate it with
23/// [`DynamicMessage::set_field`], and serialise with
24/// [`DynamicMessage::encode_to_vec`]. Decode bytes with
25/// [`DynamicMessage::decode`].
26///
27/// Two `DynamicMessage`s compare equal when they share the same descriptor and
28/// carry equal field values and unknown fields.
29#[derive(Clone, Debug, PartialEq)]
30pub struct DynamicMessage {
31    pub(crate) desc: MessageDescriptor,
32    /// Sparse field storage keyed by field number. A `BTreeMap` keeps fields
33    /// ordered by number on encode, which is the conventional (though not
34    /// required) protobuf serialisation order and makes output deterministic.
35    pub(crate) fields: BTreeMap<u32, Value>,
36    /// Unknown fields preserved across a decode → encode round-trip.
37    pub(crate) unknown: UnknownFields,
38}
39
40impl DynamicMessage {
41    /// Create an empty message for the given descriptor.
42    pub fn new(desc: MessageDescriptor) -> Self {
43        Self {
44            desc,
45            fields: BTreeMap::new(),
46            unknown: UnknownFields::new(),
47        }
48    }
49
50    /// The descriptor describing this message's schema.
51    pub fn descriptor(&self) -> MessageDescriptor {
52        self.desc.clone()
53    }
54
55    /// Borrow the preserved unknown fields.
56    pub fn unknown_fields(&self) -> &UnknownFields {
57        &self.unknown
58    }
59
60    /// Mutably borrow the preserved unknown fields.
61    pub fn unknown_fields_mut(&mut self) -> &mut UnknownFields {
62        &mut self.unknown
63    }
64
65    /// Returns `true` if the field is explicitly set to a non-default value.
66    ///
67    /// For proto3 singular scalar fields, a value equal to the type default is
68    /// considered *not* present. For message, repeated, and map fields,
69    /// presence means the entry exists and is non-empty.
70    pub fn has_field(&self, field: &FieldDescriptor) -> bool {
71        match self.fields.get(&field.number()) {
72            None => false,
73            Some(v) => !is_field_value_default(field, v),
74        }
75    }
76
77    /// Get the value of a field, returning the field's default (as an owned
78    /// value) if it is not set.
79    ///
80    /// The returned [`Cow`] borrows the stored value when present and owns a
81    /// freshly-constructed default otherwise.
82    pub fn get_field(&self, field: &FieldDescriptor) -> Cow<'_, Value> {
83        match self.fields.get(&field.number()) {
84            Some(v) => Cow::Borrowed(v),
85            None => Cow::Owned(default_value_for(field)),
86        }
87    }
88
89    /// Set the value of a field.
90    ///
91    /// If the field is a member of a (real, non-synthetic) oneof, all sibling
92    /// arms of that oneof are cleared first, enforcing oneof exclusivity.
93    pub fn set_field(&mut self, field: &FieldDescriptor, value: Value) {
94        if let Some(oneof) = field.containing_oneof() {
95            // Clear every sibling arm (including synthetic proto3-optional
96            // oneofs, which have exactly one member — clearing it is a no-op
97            // for the field being set).
98            for sibling in oneof.fields() {
99                if sibling.number() != field.number() {
100                    self.fields.remove(&sibling.number());
101                }
102            }
103        }
104        self.fields.insert(field.number(), value);
105    }
106
107    /// Clear a field, removing any stored value.
108    pub fn clear_field(&mut self, field: &FieldDescriptor) {
109        self.fields.remove(&field.number());
110    }
111
112    /// Get a field by name. Returns `None` if the name is not a field of this
113    /// message's descriptor.
114    pub fn get_field_by_name(&self, name: &str) -> Option<Cow<'_, Value>> {
115        let field = self.desc.get_field_by_name(name)?;
116        Some(self.get_field(&field))
117    }
118
119    /// Set a field by name. Returns `false` (and does nothing) if the name is
120    /// not a field of this message's descriptor.
121    pub fn set_field_by_name(&mut self, name: &str, value: Value) -> bool {
122        match self.desc.get_field_by_name(name) {
123            Some(field) => {
124                self.set_field(&field, value);
125                true
126            }
127            None => false,
128        }
129    }
130
131    /// Returns the [`FieldDescriptor`] of whichever arm of `oneof` is set, if
132    /// any. `oneof` is identified by name.
133    pub fn which_oneof(&self, oneof_name: &str) -> Option<FieldDescriptor> {
134        let oneof = self.desc.oneofs().find(|o| o.name() == oneof_name)?;
135        let set_field = oneof
136            .fields()
137            .find(|f| self.fields.contains_key(&f.number()));
138        set_field
139    }
140
141    /// Iterate over the explicitly-set fields as `(descriptor, value)` pairs,
142    /// ordered by field number.
143    pub fn iter_fields(&self) -> impl Iterator<Item = (FieldDescriptor, &Value)> + '_ {
144        self.fields
145            .iter()
146            .filter_map(move |(&number, value)| self.desc.get_field(number).map(|f| (f, value)))
147    }
148}
149
150/// Construct the default [`Value`] for a field based on its kind and
151/// cardinality.
152pub(crate) fn default_value_for(field: &FieldDescriptor) -> Value {
153    if field.is_map() {
154        return Value::Map(HashMap::new());
155    }
156    if matches!(field.cardinality(), Cardinality::Repeated) {
157        return Value::List(Vec::new());
158    }
159    default_scalar_value(field.kind())
160}
161
162/// The default [`Value`] for a singular field of the given kind.
163pub(crate) fn default_scalar_value(kind: Kind) -> Value {
164    match kind {
165        Kind::Double => Value::F64(0.0),
166        Kind::Float => Value::F32(0.0),
167        Kind::Int32 | Kind::Sint32 | Kind::Sfixed32 => Value::I32(0),
168        Kind::Int64 | Kind::Sint64 | Kind::Sfixed64 => Value::I64(0),
169        Kind::Uint32 | Kind::Fixed32 => Value::U32(0),
170        Kind::Uint64 | Kind::Fixed64 => Value::U64(0),
171        Kind::Bool => Value::Bool(false),
172        Kind::String => Value::String(String::new()),
173        Kind::Bytes => Value::Bytes(Vec::new()),
174        Kind::Enum(_) => Value::EnumNumber(0),
175        // For message/group kinds the singular default is "absent"; we model
176        // that with an empty list-less placeholder that `is_default` treats as
177        // present-but-empty. Callers normally branch on cardinality before
178        // reaching here, so this is only hit for an unset singular message.
179        Kind::Message(_) | Kind::Group(_) => Value::List(Vec::new()),
180    }
181}
182
183/// Returns `true` if `value` is the default for the given field (used by
184/// `has_field` and proto3 default-omission on encode).
185pub(crate) fn is_field_value_default(field: &FieldDescriptor, value: &Value) -> bool {
186    match field.cardinality() {
187        Cardinality::Repeated => match value {
188            Value::List(l) => l.is_empty(),
189            Value::Map(m) => m.is_empty(),
190            _ => false,
191        },
192        Cardinality::Optional | Cardinality::Required => {
193            // A singular message that is present is never "default".
194            if matches!(field.kind(), Kind::Message(_) | Kind::Group(_)) {
195                return !matches!(value, Value::Message(_));
196            }
197            value.is_default()
198        }
199    }
200}