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}