Skip to main content

buffa_reflect/dynamic/
value.rs

1//! [`Value`], [`MapKey`], and [`SetFieldError`] — the runtime value model
2//! that backs [`super::DynamicMessage`].
3
4use std::collections::HashMap;
5
6use buffa::bytes::Bytes;
7
8use crate::{
9    dynamic::message::DynamicMessage,
10    field::{FieldDescriptor, Kind},
11};
12
13/// A protobuf field value carried by [`super::DynamicMessage`].
14///
15/// One variant per scalar wire type, plus `EnumNumber` (open-enum
16/// semantics — any `i32` is acceptable so unknown variants round-trip
17/// losslessly), `Message`, `List`, and `Map`. The variant set mirrors
18/// `prost-reflect`'s `Value` for consumer migration.
19#[derive(Clone, Debug, PartialEq)]
20pub enum Value {
21    /// `bool`.
22    Bool(bool),
23    /// `int32` / `sint32` / `sfixed32`.
24    I32(i32),
25    /// `int64` / `sint64` / `sfixed64`.
26    I64(i64),
27    /// `uint32` / `fixed32`.
28    U32(u32),
29    /// `uint64` / `fixed64`.
30    U64(u64),
31    /// `float`.
32    F32(f32),
33    /// `double`.
34    F64(f64),
35    /// `string` (UTF-8).
36    String(String),
37    /// `bytes`.
38    Bytes(Bytes),
39    /// Enum variant by number.
40    ///
41    /// `i32` rather than a typed variant so forward-compat decoding (an
42    /// unknown enum number) round-trips byte-identically. Matches
43    /// proto3's open-enum semantics.
44    EnumNumber(i32),
45    /// Sub-message.
46    Message(DynamicMessage),
47    /// Repeated value (every element validated against the list's
48    /// declared element [`Kind`]).
49    List(Vec<Value>),
50    /// `map<K, V>` field.
51    Map(HashMap<MapKey, Value>),
52}
53
54/// A protobuf map key.
55///
56/// Variants are limited to those allowed by the proto spec: floats,
57/// bytes, and message/enum keys are forbidden.
58#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
59pub enum MapKey {
60    /// `bool`.
61    Bool(bool),
62    /// `int32` / `sint32` / `sfixed32`.
63    I32(i32),
64    /// `int64` / `sint64` / `sfixed64`.
65    I64(i64),
66    /// `uint32` / `fixed32`.
67    U32(u32),
68    /// `uint64` / `fixed64`.
69    U64(u64),
70    /// `string`.
71    String(String),
72}
73
74/// Reasons [`super::DynamicMessage::try_set_field`] (and friends) can fail.
75#[derive(Clone, Debug, PartialEq, thiserror::Error)]
76pub enum SetFieldError {
77    /// The named field / number does not exist on the descriptor.
78    #[error("field not found")]
79    NotFound,
80    /// The supplied value's runtime shape did not match the field.
81    #[error("invalid value for field `{}`", field.full_name())]
82    InvalidType {
83        /// The field that rejected the assignment.
84        field: FieldDescriptor,
85        /// The offending value (returned for diagnostic purposes).
86        value: Box<Value>,
87    },
88}
89
90impl Value {
91    /// The proto default for the given [`Kind`] (singular form).
92    ///
93    /// For `Message` returns an empty [`super::DynamicMessage`] of the
94    /// declared sub-message type. For `Enum`, the zero variant.
95    #[must_use]
96    pub fn default_value(kind: &Kind) -> Self {
97        match kind {
98            Kind::Bool => Value::Bool(false),
99            Kind::Int32 | Kind::Sint32 | Kind::Sfixed32 => Value::I32(0),
100            Kind::Int64 | Kind::Sint64 | Kind::Sfixed64 => Value::I64(0),
101            Kind::Uint32 | Kind::Fixed32 => Value::U32(0),
102            Kind::Uint64 | Kind::Fixed64 => Value::U64(0),
103            Kind::Float => Value::F32(0.0),
104            Kind::Double => Value::F64(0.0),
105            Kind::String => Value::String(String::new()),
106            Kind::Bytes => Value::Bytes(Bytes::new()),
107            Kind::Enum(_) => Value::EnumNumber(0),
108            Kind::Message(m) => Value::Message(DynamicMessage::new(m.clone())),
109        }
110    }
111
112    /// The proto default for `field` accounting for cardinality (lists
113    /// and maps default to the empty collection).
114    ///
115    /// Reads any pre-parsed `[default = …]` from the descriptor pool.
116    /// When the descriptor declares no explicit default this returns
117    /// the zero value for the field's [`Kind`].
118    #[must_use]
119    pub fn default_value_for_field(field: &FieldDescriptor) -> Self {
120        if field.is_list() {
121            return Value::List(Vec::new());
122        }
123        if field.is_map() {
124            return Value::Map(HashMap::new());
125        }
126        if let Some(value) = field.parsed_default_value() {
127            return value;
128        }
129        Value::default_value(&field.kind())
130    }
131
132    /// True iff `self` equals the proto default for `kind`.
133    ///
134    /// Lists and maps are default iff empty. `Message` is default iff
135    /// it is a fresh, populated-fields-empty instance.
136    #[must_use]
137    pub fn is_default(&self, kind: &Kind) -> bool {
138        match (self, kind) {
139            (Value::Bool(b), Kind::Bool) => !*b,
140            (Value::I32(v), Kind::Int32 | Kind::Sint32 | Kind::Sfixed32) => *v == 0,
141            (Value::I64(v), Kind::Int64 | Kind::Sint64 | Kind::Sfixed64) => *v == 0,
142            (Value::U32(v), Kind::Uint32 | Kind::Fixed32) => *v == 0,
143            (Value::U64(v), Kind::Uint64 | Kind::Fixed64) => *v == 0,
144            (Value::F32(v), Kind::Float) => *v == 0.0,
145            (Value::F64(v), Kind::Double) => *v == 0.0,
146            (Value::String(s), Kind::String) => s.is_empty(),
147            (Value::Bytes(b), Kind::Bytes) => b.is_empty(),
148            (Value::EnumNumber(n), Kind::Enum(_)) => *n == 0,
149            (Value::Message(m), Kind::Message(_)) => m.is_empty(),
150            (Value::List(l), _) => l.is_empty(),
151            (Value::Map(m), _) => m.is_empty(),
152            _ => false,
153        }
154    }
155
156    /// Validate that `self` matches `field`'s declared shape.
157    ///
158    /// Recursive: list elements validate against the element kind, map
159    /// keys/values against their declared kinds, and sub-message values
160    /// must share the field's expected message descriptor (compared
161    /// pool-pointer + index).
162    #[must_use]
163    pub fn is_valid_for_field(&self, field: &FieldDescriptor) -> bool {
164        if field.is_list() {
165            if let Value::List(items) = self {
166                let kind = field.kind();
167                return items.iter().all(|v| value_matches_kind(v, &kind));
168            }
169            return false;
170        }
171        if field.is_map() {
172            if let Value::Map(entries) = self {
173                let (key_kind, value_kind) = match map_entry_kinds(field) {
174                    Some(kinds) => kinds,
175                    None => return false,
176                };
177                return entries.iter().all(|(k, v)| {
178                    map_key_matches_kind(k, &key_kind) && value_matches_kind(v, &value_kind)
179                });
180            }
181            return false;
182        }
183        value_matches_kind(self, &field.kind())
184    }
185
186    // Typed accessors ------------------------------------------------------
187
188    /// Returns the `bool` payload, if any.
189    #[must_use]
190    pub fn as_bool(&self) -> Option<bool> {
191        match self {
192            Value::Bool(v) => Some(*v),
193            _ => None,
194        }
195    }
196    /// Returns the `i32` payload, if any.
197    #[must_use]
198    pub fn as_i32(&self) -> Option<i32> {
199        match self {
200            Value::I32(v) => Some(*v),
201            _ => None,
202        }
203    }
204    /// Returns the `i64` payload, if any.
205    #[must_use]
206    pub fn as_i64(&self) -> Option<i64> {
207        match self {
208            Value::I64(v) => Some(*v),
209            _ => None,
210        }
211    }
212    /// Returns the `u32` payload, if any.
213    #[must_use]
214    pub fn as_u32(&self) -> Option<u32> {
215        match self {
216            Value::U32(v) => Some(*v),
217            _ => None,
218        }
219    }
220    /// Returns the `u64` payload, if any.
221    #[must_use]
222    pub fn as_u64(&self) -> Option<u64> {
223        match self {
224            Value::U64(v) => Some(*v),
225            _ => None,
226        }
227    }
228    /// Returns the `f32` payload, if any.
229    #[must_use]
230    pub fn as_f32(&self) -> Option<f32> {
231        match self {
232            Value::F32(v) => Some(*v),
233            _ => None,
234        }
235    }
236    /// Returns the `f64` payload, if any.
237    #[must_use]
238    pub fn as_f64(&self) -> Option<f64> {
239        match self {
240            Value::F64(v) => Some(*v),
241            _ => None,
242        }
243    }
244    /// Returns the `&str` payload, if any.
245    #[must_use]
246    pub fn as_str(&self) -> Option<&str> {
247        match self {
248            Value::String(v) => Some(v.as_str()),
249            _ => None,
250        }
251    }
252    /// Returns the `&Bytes` payload, if any.
253    #[must_use]
254    pub fn as_bytes(&self) -> Option<&Bytes> {
255        match self {
256            Value::Bytes(v) => Some(v),
257            _ => None,
258        }
259    }
260    /// Returns the enum-number payload, if any.
261    #[must_use]
262    pub fn as_enum_number(&self) -> Option<i32> {
263        match self {
264            Value::EnumNumber(v) => Some(*v),
265            _ => None,
266        }
267    }
268    /// Returns the [`super::DynamicMessage`] payload, if any.
269    #[must_use]
270    pub fn as_message(&self) -> Option<&DynamicMessage> {
271        match self {
272            Value::Message(v) => Some(v),
273            _ => None,
274        }
275    }
276    /// Mutable form of [`Self::as_message`].
277    pub fn as_message_mut(&mut self) -> Option<&mut DynamicMessage> {
278        match self {
279            Value::Message(v) => Some(v),
280            _ => None,
281        }
282    }
283    /// Returns the list payload, if any.
284    #[must_use]
285    pub fn as_list(&self) -> Option<&[Value]> {
286        match self {
287            Value::List(v) => Some(v.as_slice()),
288            _ => None,
289        }
290    }
291    /// Mutable form of [`Self::as_list`].
292    pub fn as_list_mut(&mut self) -> Option<&mut Vec<Value>> {
293        match self {
294            Value::List(v) => Some(v),
295            _ => None,
296        }
297    }
298    /// Returns the map payload, if any.
299    #[must_use]
300    pub fn as_map(&self) -> Option<&HashMap<MapKey, Value>> {
301        match self {
302            Value::Map(v) => Some(v),
303            _ => None,
304        }
305    }
306    /// Mutable form of [`Self::as_map`].
307    pub fn as_map_mut(&mut self) -> Option<&mut HashMap<MapKey, Value>> {
308        match self {
309            Value::Map(v) => Some(v),
310            _ => None,
311        }
312    }
313}
314
315impl MapKey {
316    /// The proto default for the given map-key kind.
317    ///
318    /// # Panics
319    ///
320    /// Panics if `kind` is not a legal map-key shape (floats, bytes,
321    /// messages, and enums are forbidden by the proto spec). The pool
322    /// builder rejects such map declarations, so this only fires for
323    /// hand-constructed misuse.
324    #[must_use]
325    pub fn default_value(kind: &Kind) -> Self {
326        match kind {
327            Kind::Bool => MapKey::Bool(false),
328            Kind::Int32 | Kind::Sint32 | Kind::Sfixed32 => MapKey::I32(0),
329            Kind::Int64 | Kind::Sint64 | Kind::Sfixed64 => MapKey::I64(0),
330            Kind::Uint32 | Kind::Fixed32 => MapKey::U32(0),
331            Kind::Uint64 | Kind::Fixed64 => MapKey::U64(0),
332            Kind::String => MapKey::String(String::new()),
333            other => panic!("invalid map-key kind: {other:?}"),
334        }
335    }
336}
337
338/// Used by both singular validation and list-element / map-value validation.
339pub(crate) fn value_matches_kind(value: &Value, kind: &Kind) -> bool {
340    match (value, kind) {
341        (Value::Bool(_), Kind::Bool) => true,
342        (Value::I32(_), Kind::Int32 | Kind::Sint32 | Kind::Sfixed32) => true,
343        (Value::I64(_), Kind::Int64 | Kind::Sint64 | Kind::Sfixed64) => true,
344        (Value::U32(_), Kind::Uint32 | Kind::Fixed32) => true,
345        (Value::U64(_), Kind::Uint64 | Kind::Fixed64) => true,
346        (Value::F32(_), Kind::Float) => true,
347        (Value::F64(_), Kind::Double) => true,
348        (Value::String(_), Kind::String) => true,
349        (Value::Bytes(_), Kind::Bytes) => true,
350        (Value::EnumNumber(_), Kind::Enum(_)) => true,
351        // Compare by FQN rather than `Arc::ptr_eq` so that pool
352        // mutations (`add_file_descriptor_set` calls `Arc::make_mut`)
353        // don't invalidate previously-constructed sub-messages.
354        (Value::Message(m), Kind::Message(d)) => m.descriptor().full_name() == d.full_name(),
355        _ => false,
356    }
357}
358
359pub(crate) fn map_key_matches_kind(key: &MapKey, kind: &Kind) -> bool {
360    matches!(
361        (key, kind),
362        (MapKey::Bool(_), Kind::Bool)
363            | (MapKey::I32(_), Kind::Int32 | Kind::Sint32 | Kind::Sfixed32)
364            | (MapKey::I64(_), Kind::Int64 | Kind::Sint64 | Kind::Sfixed64)
365            | (MapKey::U32(_), Kind::Uint32 | Kind::Fixed32)
366            | (MapKey::U64(_), Kind::Uint64 | Kind::Fixed64)
367            | (MapKey::String(_), Kind::String)
368    )
369}
370
371/// Resolve the `(key_kind, value_kind)` of a map field's synthetic
372/// entry message.
373pub(crate) fn map_entry_kinds(field: &FieldDescriptor) -> Option<(Kind, Kind)> {
374    let Kind::Message(entry) = field.kind() else {
375        return None;
376    };
377    if !entry.is_map_entry() {
378        return None;
379    }
380    let key = entry.get_field_by_number(1)?.kind();
381    let value = entry.get_field_by_number(2)?.kind();
382    Some((key, value))
383}
384
385/// Convenience constructors for common literal types.
386impl From<bool> for Value {
387    fn from(v: bool) -> Self {
388        Value::Bool(v)
389    }
390}
391impl From<i32> for Value {
392    fn from(v: i32) -> Self {
393        Value::I32(v)
394    }
395}
396impl From<i64> for Value {
397    fn from(v: i64) -> Self {
398        Value::I64(v)
399    }
400}
401impl From<u32> for Value {
402    fn from(v: u32) -> Self {
403        Value::U32(v)
404    }
405}
406impl From<u64> for Value {
407    fn from(v: u64) -> Self {
408        Value::U64(v)
409    }
410}
411impl From<f32> for Value {
412    fn from(v: f32) -> Self {
413        Value::F32(v)
414    }
415}
416impl From<f64> for Value {
417    fn from(v: f64) -> Self {
418        Value::F64(v)
419    }
420}
421impl From<String> for Value {
422    fn from(v: String) -> Self {
423        Value::String(v)
424    }
425}
426impl From<&str> for Value {
427    fn from(v: &str) -> Self {
428        Value::String(v.to_owned())
429    }
430}
431impl From<Bytes> for Value {
432    fn from(v: Bytes) -> Self {
433        Value::Bytes(v)
434    }
435}
436impl From<Vec<u8>> for Value {
437    fn from(v: Vec<u8>) -> Self {
438        Value::Bytes(Bytes::from(v))
439    }
440}
441impl From<DynamicMessage> for Value {
442    fn from(v: DynamicMessage) -> Self {
443        Value::Message(v)
444    }
445}