Skip to main content

buffa_reflect/
field.rs

1//! [`FieldDescriptor`], [`Kind`], and [`Cardinality`].
2
3use buffa_descriptor::generated::descriptor::FieldDescriptorProto;
4
5use crate::{
6    enumeration::EnumDescriptor,
7    message::MessageDescriptor,
8    oneof::OneofDescriptor,
9    pool::{DescriptorPool, KindRef, MessageIndex},
10};
11
12/// Handle to one field in a [`MessageDescriptor`].
13#[derive(Clone, Debug)]
14pub struct FieldDescriptor {
15    pub(crate) pool: DescriptorPool,
16    pub(crate) message: MessageIndex,
17    pub(crate) index: u32,
18}
19
20impl FieldDescriptor {
21    fn entry(&self) -> &crate::pool::FieldEntry {
22        &self.pool.inner.messages[self.message as usize].fields[self.index as usize]
23    }
24
25    /// Proto field name.
26    #[must_use]
27    pub fn name(&self) -> &str {
28        &self.entry().name
29    }
30
31    /// `<message.full_name>.<name>`, no leading dot.
32    #[must_use]
33    pub fn full_name(&self) -> &str {
34        &self.entry().full_name
35    }
36
37    /// JSON field name (lower-camelCase by default; honors a user-supplied
38    /// `json_name` option when present).
39    #[must_use]
40    pub fn json_name(&self) -> &str {
41        &self.entry().json_name
42    }
43
44    /// Tag number on the wire.
45    #[must_use]
46    pub fn number(&self) -> u32 {
47        self.entry().number
48    }
49
50    /// Resolved field [`Kind`].
51    #[must_use]
52    pub fn kind(&self) -> Kind {
53        match self.entry().kind {
54            KindRef::Double => Kind::Double,
55            KindRef::Float => Kind::Float,
56            KindRef::Int32 => Kind::Int32,
57            KindRef::Int64 => Kind::Int64,
58            KindRef::Uint32 => Kind::Uint32,
59            KindRef::Uint64 => Kind::Uint64,
60            KindRef::Sint32 => Kind::Sint32,
61            KindRef::Sint64 => Kind::Sint64,
62            KindRef::Fixed32 => Kind::Fixed32,
63            KindRef::Fixed64 => Kind::Fixed64,
64            KindRef::Sfixed32 => Kind::Sfixed32,
65            KindRef::Sfixed64 => Kind::Sfixed64,
66            KindRef::Bool => Kind::Bool,
67            KindRef::String => Kind::String,
68            KindRef::Bytes => Kind::Bytes,
69            KindRef::Message(idx) => Kind::Message(MessageDescriptor {
70                pool: self.pool.clone(),
71                index: idx,
72            }),
73            KindRef::Enum(idx) => Kind::Enum(EnumDescriptor {
74                pool: self.pool.clone(),
75                index: idx,
76            }),
77        }
78    }
79
80    /// `Optional`, `Required`, or `Repeated`.
81    #[must_use]
82    pub fn cardinality(&self) -> Cardinality {
83        self.entry().cardinality
84    }
85
86    /// True iff the wire format tracks presence for this field (proto2
87    /// scalars, message-typed fields, oneof members, proto3 `optional`).
88    #[must_use]
89    pub fn supports_presence(&self) -> bool {
90        self.entry().supports_presence
91    }
92
93    /// True iff the field uses packed encoding for repeated scalars.
94    #[must_use]
95    pub fn is_packed(&self) -> bool {
96        self.entry().is_packed
97    }
98
99    /// True iff this field's [`Kind`] admits packed encoding (scalar or
100    /// enum) — independent of whether `is_packed()` is currently set.
101    #[must_use]
102    pub fn is_packable(&self) -> bool {
103        matches!(
104            self.entry().kind,
105            KindRef::Double
106                | KindRef::Float
107                | KindRef::Int32
108                | KindRef::Int64
109                | KindRef::Uint32
110                | KindRef::Uint64
111                | KindRef::Sint32
112                | KindRef::Sint64
113                | KindRef::Fixed32
114                | KindRef::Fixed64
115                | KindRef::Sfixed32
116                | KindRef::Sfixed64
117                | KindRef::Bool
118                | KindRef::Enum(_)
119        )
120    }
121
122    /// True iff this is a `repeated` (non-map) field.
123    ///
124    /// Note that `map<K, V>` fields are technically also repeated on
125    /// the wire, but their value model is [`crate::Value::Map`], not
126    /// [`crate::Value::List`] — this helper returns `false` for maps.
127    #[must_use]
128    pub fn is_list(&self) -> bool {
129        matches!(self.entry().cardinality, Cardinality::Repeated) && !self.is_map()
130    }
131
132    /// Pre-parsed `[default = ...]` value, if the field carried one.
133    ///
134    /// Available for any scalar / enum / string / bytes field whose
135    /// proto declared an explicit default. `None` for proto3 fields,
136    /// for repeated fields, and for fields without an explicit default.
137    #[cfg(feature = "dynamic")]
138    #[must_use]
139    pub fn parsed_default_value(&self) -> Option<crate::dynamic::Value> {
140        self.entry().parsed_default.clone()
141    }
142
143    /// True iff this field models a `map<K, V>`.
144    ///
145    /// A field is a map iff its kind is a message that carries the
146    /// `map_entry = true` option.
147    #[must_use]
148    pub fn is_map(&self) -> bool {
149        match self.entry().kind {
150            KindRef::Message(idx) => self.pool.inner.messages[idx as usize].is_map_entry,
151            _ => false,
152        }
153    }
154
155    /// Containing oneof, when this field is part of one.
156    #[must_use]
157    pub fn containing_oneof(&self) -> Option<OneofDescriptor> {
158        self.entry().oneof_index.map(|oi| OneofDescriptor {
159            pool: self.pool.clone(),
160            message: self.message,
161            index: oi,
162        })
163    }
164
165    /// Owning message.
166    #[must_use]
167    pub fn parent_message(&self) -> MessageDescriptor {
168        MessageDescriptor {
169            pool: self.pool.clone(),
170            index: self.message,
171        }
172    }
173
174    /// Raw [`FieldDescriptorProto`] for advanced use.
175    #[must_use]
176    pub fn descriptor_proto(&self) -> &FieldDescriptorProto {
177        let msg_entry = &self.pool.inner.messages[self.message as usize];
178        let file = &self.pool.inner.files[msg_entry.file as usize];
179        let msg_proto =
180            crate::pool_build::resolve_message_proto(&file.proto, &msg_entry.proto_path);
181        &msg_proto.field[self.entry().proto_field_index as usize]
182    }
183}
184
185impl PartialEq for FieldDescriptor {
186    fn eq(&self, other: &Self) -> bool {
187        std::sync::Arc::ptr_eq(&self.pool.inner, &other.pool.inner)
188            && self.message == other.message
189            && self.index == other.index
190    }
191}
192
193impl Eq for FieldDescriptor {}
194
195/// Resolved scalar / aggregate type for a field.
196///
197/// Message and enum kinds carry the resolved descriptor handle, not just
198/// the type name.
199#[derive(Clone, Debug)]
200#[non_exhaustive]
201pub enum Kind {
202    /// `double`
203    Double,
204    /// `float`
205    Float,
206    /// `int32`
207    Int32,
208    /// `int64`
209    Int64,
210    /// `uint32`
211    Uint32,
212    /// `uint64`
213    Uint64,
214    /// `sint32`
215    Sint32,
216    /// `sint64`
217    Sint64,
218    /// `fixed32`
219    Fixed32,
220    /// `fixed64`
221    Fixed64,
222    /// `sfixed32`
223    Sfixed32,
224    /// `sfixed64`
225    Sfixed64,
226    /// `bool`
227    Bool,
228    /// `string`
229    String,
230    /// `bytes`
231    Bytes,
232    /// Sub-message reference.
233    Message(MessageDescriptor),
234    /// Enum reference.
235    Enum(EnumDescriptor),
236}
237
238/// Field cardinality (proto label).
239#[derive(Copy, Clone, Debug, PartialEq, Eq)]
240#[non_exhaustive]
241pub enum Cardinality {
242    /// `optional` (the proto3 default and proto2 explicit `optional`).
243    Optional,
244    /// `required` — proto2 only.
245    Required,
246    /// `repeated`.
247    Repeated,
248}