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)]
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)]
110pub struct FieldType<'a> {
111    kind: FieldKind<'a>,
112    sequence_type: Option<SequenceType<'a>>,
113    annotation: &'a str,
114}
115
116#[cfg_attr(not(feature = "std"), expect(dead_code))]
117#[derive(Debug, Copy, Clone)]
118enum SequenceType<'a> {
119    Optional,
120    Repeated,
121    Map(&'a str),
122}
123
124#[cfg_attr(not(feature = "std"), expect(dead_code))]
125#[derive(Debug, Copy, Clone)]
126enum FieldKind<'a> {
127    Builtin(&'a str),
128    Local(&'a str),
129    External {
130        name: &'a str,
131        import_path: &'static str,
132    },
133    Message(fn() -> MessageDescription<'a>),
134    Tuple(&'a [FieldType<'a>]),
135    KeyValue(&'a [FieldType<'a>; 2]),
136}
137
138impl<'a> FieldType<'a> {
139    /// Returns a repeated version of this field type.
140    ///
141    /// Panics if the field type is already a sequence type.
142    pub const fn repeated(mut self) -> Self {
143        assert!(self.sequence_type.is_none());
144        self.sequence_type = Some(SequenceType::Repeated);
145        self
146    }
147
148    /// Returns a optional version of this field type.
149    ///
150    /// Panics if the field type is already a sequence type.
151    pub const fn optional(mut self) -> Self {
152        assert!(self.sequence_type.is_none());
153        self.sequence_type = Some(SequenceType::Optional);
154        self
155    }
156
157    /// Sets an annotation to show up in the .proto file.
158    pub const fn annotate(mut self, annotation: &'a str) -> Self {
159        self.annotation = annotation;
160        self
161    }
162
163    /// Returns a map type.
164    ///
165    /// If `key` is not a builtin numeric scalar or string type, or if `value`
166    /// is an optional or repeated type, then this will result in a repeated
167    /// tuple instead of a protobuf `map` type. The encodings for these are the
168    /// same, but `.proto` `map` types are constrained to mappings from scalars
169    /// to non-optional/repeated scalars and messages.
170    pub const fn map(kv: &'a [FieldType<'a>; 2]) -> Self {
171        let [key, value] = kv;
172        if !key.is_sequence() && !value.is_sequence() {
173            if let FieldKind::Builtin(ty) = key.kind {
174                if let b"uint32" | b"int32" | b"sint32" | b"uint64" | b"sint64" | b"int64"
175                | b"fixed32" | b"fixed64" | b"sfixed32" | b"sfixed64" | b"bool" | b"string" =
176                    ty.as_bytes()
177                {
178                    return Self {
179                        kind: value.kind,
180                        sequence_type: Some(SequenceType::Map(ty)),
181                        annotation: "",
182                    };
183                }
184            }
185        }
186        Self {
187            kind: FieldKind::KeyValue(kv),
188            sequence_type: Some(SequenceType::Repeated),
189            annotation: "",
190        }
191    }
192
193    /// Returns a field type for a message whose top-level descriptor is
194    /// returned by `f`.
195    ///
196    /// This is abstracted through a function to allow for recursive types.
197    /// Currently Rust does not allow a `const` to refer to a `static`, but it
198    /// does allow a `const` to refer to a function that returns a `&'static`.
199    pub const fn message(f: fn() -> MessageDescription<'a>) -> Self {
200        Self {
201            kind: FieldKind::Message(f),
202            sequence_type: None,
203            annotation: "",
204        }
205    }
206
207    /// Returns a field type for a local message type with `name`.
208    pub const fn local(name: &'a str) -> Self {
209        Self {
210            kind: FieldKind::Local(name),
211            sequence_type: None,
212            annotation: "",
213        }
214    }
215
216    /// Returns a field type for a builtin type, such as `uint32`.
217    pub const fn builtin(name: &'a str) -> Self {
218        Self {
219            kind: FieldKind::Builtin(name),
220            sequence_type: None,
221            annotation: "",
222        }
223    }
224
225    /// Returns a field type for an anonymous tuple.
226    pub const fn tuple(field_types: &'a [Self]) -> Self {
227        // Use well-known types instead of new anonymous ones when possible.
228        match field_types {
229            [] => {
230                return Self::external("google.protobuf.Empty", "google/protobuf/empty.proto");
231            }
232            &[
233                Self {
234                    kind: FieldKind::Builtin(ty),
235                    sequence_type: None,
236                    annotation,
237                },
238            ] if annotation.is_empty() => {
239                let wrapper = match ty.as_bytes() {
240                    b"double" => Some("google.protobuf.DoubleValue"),
241                    b"float" => Some("google.protobuf.FloatValue"),
242                    b"int64" => Some("google.protobuf.Int64Value"),
243                    b"uint64" => Some("google.protobuf.UInt64Value"),
244                    b"int32" => Some("google.protobuf.Int32Value"),
245                    b"uint32" => Some("google.protobuf.UInt32Value"),
246                    b"bool" => Some("google.protobuf.BoolValue"),
247                    b"string" => Some("google.protobuf.StringValue"),
248                    b"bytes" => Some("google.protobuf.BytesValue"),
249                    _ => None,
250                };
251                if let Some(wrapper) = wrapper {
252                    return Self::external(wrapper, "google/protobuf/wrappers.proto");
253                }
254            }
255            _ => {}
256        }
257        Self {
258            kind: FieldKind::Tuple(field_types),
259            sequence_type: None,
260            annotation: "",
261        }
262    }
263
264    /// Returns a field type for an external type with the given fully-qualified
265    /// name and protoc import path.
266    pub const fn external(name: &'a str, import_path: &'static str) -> Self {
267        Self {
268            kind: FieldKind::External { name, import_path },
269            sequence_type: None,
270            annotation: "",
271        }
272    }
273
274    /// Returns true if this is a sequence type (optional or repeated).
275    pub const fn is_sequence(&self) -> bool {
276        self.sequence_type.is_some()
277    }
278
279    /// Returns true if this type can use a packed encoding in a repeated
280    /// context.
281    pub const fn can_pack(&self) -> bool {
282        if self.sequence_type.is_some() {
283            return false;
284        }
285        match self.kind {
286            FieldKind::Builtin(v) => matches!(
287                v.as_bytes(),
288                b"double" | b"float" | b"int64" | b"uint64" | b"int32" | b"uint32" | b"bool"
289            ),
290            _ => false,
291        }
292    }
293}
294
295/// A descriptor for a message field.
296#[cfg_attr(not(feature = "std"), expect(dead_code))]
297#[derive(Debug, Copy, Clone)]
298pub struct FieldDescriptor<'a> {
299    field_type: FieldType<'a>,
300    field_number: u32,
301    comment: &'a str,
302    name: &'a str,
303}
304
305impl<'a> FieldDescriptor<'a> {
306    /// Returns a new descriptor.
307    pub const fn new(
308        comment: &'a str,
309        field_type: FieldType<'a>,
310        name: &'a str,
311        field_number: u32,
312    ) -> Self {
313        Self {
314            field_type,
315            field_number,
316            comment,
317            name,
318        }
319    }
320}
321
322/// A description of a protobuf `oneof`.
323#[cfg_attr(not(feature = "std"), expect(dead_code))]
324#[derive(Debug, Copy, Clone)]
325pub struct OneofDescriptor<'a> {
326    name: &'a str,
327    variants: &'a [FieldDescriptor<'a>],
328}
329
330impl<'a> OneofDescriptor<'a> {
331    /// Returns a new descriptor.
332    pub const fn new(name: &'a str, variants: &'a [FieldDescriptor<'a>]) -> Self {
333        Self { name, variants }
334    }
335}
336
337/// A message descriptor.
338#[derive(Debug, Copy, Clone)]
339#[cfg_attr(not(feature = "std"), expect(dead_code))]
340pub struct MessageDescriptor<'a> {
341    comment: &'a str,
342    name: &'a str,
343    fields: &'a [FieldDescriptor<'a>],
344    oneofs: &'a [OneofDescriptor<'a>],
345    messages: &'a [MessageDescriptor<'a>],
346}
347
348impl<'a> MessageDescriptor<'a> {
349    /// Creates a new message descriptor.
350    pub const fn new(
351        name: &'a str,
352        comment: &'a str,
353        fields: &'a [FieldDescriptor<'a>],
354        oneofs: &'a [OneofDescriptor<'a>],
355        messages: &'a [MessageDescriptor<'a>],
356    ) -> Self {
357        Self {
358            comment,
359            name,
360            fields,
361            oneofs,
362            messages,
363        }
364    }
365}
366
367/// A message descriptor for a message rooted directly in a package (and not
368/// nested in another message type).
369#[derive(Debug, Copy, Clone)]
370pub struct TopLevelDescriptor<'a> {
371    package: &'a str,
372    message: &'a MessageDescriptor<'a>,
373}
374
375impl<'a> TopLevelDescriptor<'a> {
376    /// Returns a new descriptor.
377    pub const fn message(package: &'a str, message: &'a MessageDescriptor<'a>) -> Self {
378        Self { package, message }
379    }
380}