mesh_protobuf/protofile/
mod.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Definitions for describing the format protobuf messages. These can be used
5//! to generate `.proto` files that are binary compatible with the associated
6//! Rust types.
7
8mod writer;
9
10#[cfg(feature = "std")]
11pub use writer::DescriptorWriter;
12
13use crate::DefaultEncoding;
14use core::fmt::Display;
15
16/// A trait for a self-describing protobuf message field.
17pub trait DescribeField<T> {
18    /// The type of the field.
19    const FIELD_TYPE: FieldType<'static>;
20    /// The type name of the field in a packed context.
21    const PACKED_TYPE: Option<&'static str> = None;
22}
23
24/// A trait for a self-describing protobuf message.
25///
26/// This can be derived for `T` by deriving [`Protobuf`](crate::Protobuf) and
27/// adding the attribute `#[mesh(package = "my.package.name")]`.
28pub trait DescribeMessage<T> {
29    /// The message description.
30    const DESCRIPTION: MessageDescription<'static>;
31}
32
33/// A description of a message type.
34#[derive(Copy, Clone)]
35pub enum MessageDescription<'a> {
36    /// An internally-defined type, described by the descriptor.
37    Internal(&'a TopLevelDescriptor<'a>),
38    /// An externally-defined type.
39    External {
40        /// The fully-qualified name of the message type.
41        name: &'a str,
42        /// The import path of the `.proto` file.
43        import_path: &'a str,
44    },
45}
46
47/// A type URL, used in [`ProtobufAny`](super::message::ProtobufAny) (which
48/// shares an encoding with `google.protobuf.Any`).
49#[derive(Debug, Copy, Clone, PartialEq, Eq)]
50pub struct TypeUrl<'a> {
51    package: &'a str,
52    name: &'a str,
53}
54
55impl TypeUrl<'_> {
56    fn eq(&self, type_url: &str) -> bool {
57        let type_url = type_url.strip_prefix("https://").unwrap_or(type_url);
58        if let Some((package, name)) = type_url
59            .strip_prefix("type.googleapis.com/")
60            .and_then(|ty| ty.rsplit_once('.'))
61        {
62            self.package == package && self.name == name
63        } else {
64            false
65        }
66    }
67}
68
69impl Display for TypeUrl<'_> {
70    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
71        write!(f, "type.googleapis.com/{}.{}", self.package, self.name)
72    }
73}
74
75impl PartialEq<str> for TypeUrl<'_> {
76    fn eq(&self, other: &str) -> bool {
77        self.eq(other)
78    }
79}
80
81impl PartialEq<TypeUrl<'_>> for str {
82    fn eq(&self, other: &TypeUrl<'_>) -> bool {
83        other.eq(self)
84    }
85}
86
87impl MessageDescription<'_> {
88    /// Returns the type URL to use with `google.protobuf.Any`.
89    pub const fn type_url(&self) -> TypeUrl<'_> {
90        match *self {
91            MessageDescription::Internal(tld) => TypeUrl {
92                package: tld.package,
93                name: tld.message.name,
94            },
95            MessageDescription::External { name, .. } => TypeUrl { package: "", name },
96        }
97    }
98}
99
100/// Returns the top-level message descriptor for a type with a default encoding.
101pub const fn message_description<T: DefaultEncoding>() -> MessageDescription<'static>
102where
103    T::Encoding: DescribeMessage<T>,
104{
105    <T::Encoding as DescribeMessage<T>>::DESCRIPTION
106}
107
108/// The description of a field type.
109#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
110pub struct FieldType<'a> {
111    kind: FieldKind<'a>,
112    sequence_type: Option<SequenceType<'a>>,
113    annotation: &'a str,
114}
115
116#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
117enum SequenceType<'a> {
118    Optional,
119    Repeated,
120    Map(&'a str),
121}
122
123#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
124enum FieldKind<'a> {
125    Builtin(&'a str),
126    Local(&'a str),
127    External {
128        name: &'a str,
129        import_path: &'static str,
130    },
131    Message(fn() -> MessageDescription<'a>),
132    Tuple(&'a [FieldType<'a>]),
133    KeyValue(&'a [FieldType<'a>; 2]),
134}
135
136impl<'a> FieldType<'a> {
137    /// Returns a repeated version of this field type.
138    ///
139    /// Panics if the field type is already a sequence type.
140    pub const fn repeated(mut self) -> Self {
141        assert!(self.sequence_type.is_none());
142        self.sequence_type = Some(SequenceType::Repeated);
143        self
144    }
145
146    /// Returns a optional version of this field type.
147    ///
148    /// Panics if the field type is already a sequence type.
149    pub const fn optional(mut self) -> Self {
150        assert!(self.sequence_type.is_none());
151        self.sequence_type = Some(SequenceType::Optional);
152        self
153    }
154
155    /// Sets an annotation to show up in the .proto file.
156    pub const fn annotate(mut self, annotation: &'a str) -> Self {
157        self.annotation = annotation;
158        self
159    }
160
161    /// Returns a map type.
162    ///
163    /// If `key` is not a builtin numeric scalar or string type, or if `value`
164    /// is an optional or repeated type, then this will result in a repeated
165    /// tuple instead of a protobuf `map` type. The encodings for these are the
166    /// same, but `.proto` `map` types are constrained to mappings from scalars
167    /// to non-optional/repeated scalars and messages.
168    pub const fn map(kv: &'a [FieldType<'a>; 2]) -> Self {
169        let [key, value] = kv;
170        if !key.is_sequence() && !value.is_sequence() {
171            if let FieldKind::Builtin(ty) = key.kind {
172                if let b"uint32" | b"int32" | b"sint32" | b"uint64" | b"sint64" | b"int64"
173                | b"fixed32" | b"fixed64" | b"sfixed32" | b"sfixed64" | b"bool" | b"string" =
174                    ty.as_bytes()
175                {
176                    return Self {
177                        kind: value.kind,
178                        sequence_type: Some(SequenceType::Map(ty)),
179                        annotation: "",
180                    };
181                }
182            }
183        }
184        Self {
185            kind: FieldKind::KeyValue(kv),
186            sequence_type: Some(SequenceType::Repeated),
187            annotation: "",
188        }
189    }
190
191    /// Returns a field type for a message whose top-level descriptor is
192    /// returned by `f`.
193    ///
194    /// This is abstracted through a function to allow for recursive types.
195    /// Currently Rust does not allow a `const` to refer to a `static`, but it
196    /// does allow a `const` to refer to a function that returns a `&'static`.
197    pub const fn message(f: fn() -> MessageDescription<'a>) -> Self {
198        Self {
199            kind: FieldKind::Message(f),
200            sequence_type: None,
201            annotation: "",
202        }
203    }
204
205    /// Returns a field type for a local message type with `name`.
206    pub const fn local(name: &'a str) -> Self {
207        Self {
208            kind: FieldKind::Local(name),
209            sequence_type: None,
210            annotation: "",
211        }
212    }
213
214    /// Returns a field type for a builtin type, such as `uint32`.
215    pub const fn builtin(name: &'a str) -> Self {
216        Self {
217            kind: FieldKind::Builtin(name),
218            sequence_type: None,
219            annotation: "",
220        }
221    }
222
223    /// Returns a field type for an anonymous tuple.
224    pub const fn tuple(field_types: &'a [Self]) -> Self {
225        // Use well-known types instead of new anonymous ones when possible.
226        match field_types {
227            [] => {
228                return Self::external("google.protobuf.Empty", "google/protobuf/empty.proto");
229            }
230            &[
231                Self {
232                    kind: FieldKind::Builtin(ty),
233                    sequence_type: None,
234                    annotation,
235                },
236            ] if annotation.is_empty() => {
237                let wrapper = match ty.as_bytes() {
238                    b"double" => Some("google.protobuf.DoubleValue"),
239                    b"float" => Some("google.protobuf.FloatValue"),
240                    b"int64" => Some("google.protobuf.Int64Value"),
241                    b"uint64" => Some("google.protobuf.UInt64Value"),
242                    b"int32" => Some("google.protobuf.Int32Value"),
243                    b"uint32" => Some("google.protobuf.UInt32Value"),
244                    b"bool" => Some("google.protobuf.BoolValue"),
245                    b"string" => Some("google.protobuf.StringValue"),
246                    b"bytes" => Some("google.protobuf.BytesValue"),
247                    _ => None,
248                };
249                if let Some(wrapper) = wrapper {
250                    return Self::external(wrapper, "google/protobuf/wrappers.proto");
251                }
252            }
253            _ => {}
254        }
255        Self {
256            kind: FieldKind::Tuple(field_types),
257            sequence_type: None,
258            annotation: "",
259        }
260    }
261
262    /// Returns a field type for an external type with the given fully-qualified
263    /// name and protoc import path.
264    pub const fn external(name: &'a str, import_path: &'static str) -> Self {
265        Self {
266            kind: FieldKind::External { name, import_path },
267            sequence_type: None,
268            annotation: "",
269        }
270    }
271
272    /// Returns true if this is a sequence type (optional or repeated).
273    pub const fn is_sequence(&self) -> bool {
274        self.sequence_type.is_some()
275    }
276
277    /// Returns true if this type can use a packed encoding in a repeated
278    /// context.
279    pub const fn can_pack(&self) -> bool {
280        if self.sequence_type.is_some() {
281            return false;
282        }
283        match self.kind {
284            FieldKind::Builtin(v) => matches!(
285                v.as_bytes(),
286                b"double" | b"float" | b"int64" | b"uint64" | b"int32" | b"uint32" | b"bool"
287            ),
288            _ => false,
289        }
290    }
291}
292
293/// A descriptor for a message field.
294#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
295pub struct FieldDescriptor<'a> {
296    field_type: FieldType<'a>,
297    field_number: u32,
298    comment: &'a str,
299    name: &'a str,
300}
301
302impl<'a> FieldDescriptor<'a> {
303    /// Returns a new descriptor.
304    pub const fn new(
305        comment: &'a str,
306        field_type: FieldType<'a>,
307        name: &'a str,
308        field_number: u32,
309    ) -> Self {
310        Self {
311            field_type,
312            field_number,
313            comment,
314            name,
315        }
316    }
317}
318
319/// A description of a protobuf `oneof`.
320#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
321pub struct OneofDescriptor<'a> {
322    name: &'a str,
323    variants: &'a [FieldDescriptor<'a>],
324}
325
326impl<'a> OneofDescriptor<'a> {
327    /// Returns a new descriptor.
328    pub const fn new(name: &'a str, variants: &'a [FieldDescriptor<'a>]) -> Self {
329        Self { name, variants }
330    }
331}
332
333/// A message descriptor.
334#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
335pub struct MessageDescriptor<'a> {
336    comment: &'a str,
337    name: &'a str,
338    fields: &'a [FieldDescriptor<'a>],
339    oneofs: &'a [OneofDescriptor<'a>],
340    messages: &'a [MessageDescriptor<'a>],
341}
342
343impl<'a> MessageDescriptor<'a> {
344    /// Creates a new message descriptor.
345    pub const fn new(
346        name: &'a str,
347        comment: &'a str,
348        fields: &'a [FieldDescriptor<'a>],
349        oneofs: &'a [OneofDescriptor<'a>],
350        messages: &'a [MessageDescriptor<'a>],
351    ) -> Self {
352        Self {
353            comment,
354            name,
355            fields,
356            oneofs,
357            messages,
358        }
359    }
360}
361
362/// A message descriptor for a message rooted directly in a package (and not
363/// nested in another message type).
364#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
365pub struct TopLevelDescriptor<'a> {
366    package: &'a str,
367    message: &'a MessageDescriptor<'a>,
368}
369
370impl<'a> TopLevelDescriptor<'a> {
371    /// Returns a new descriptor.
372    pub const fn message(package: &'a str, message: &'a MessageDescriptor<'a>) -> Self {
373        Self { package, message }
374    }
375}