power_protobuf_lib/
model.rs

1use std::ops::RangeInclusive;
2use std::path::PathBuf;
3
4use indexmap::IndexMap;
5use proc_macro2::Span;
6use syn::punctuated::Punctuated;
7use syn::spanned::Spanned;
8use syn::Ident;
9use syn::LitBool;
10use syn::LitFloat;
11use syn::LitInt;
12use syn::LitStr;
13use syn::Token;
14use syn_prelude::ToIdent;
15use syn_prelude::ToLitStr;
16
17use crate::dep::Deps;
18
19#[derive(Debug, Clone, Hash, PartialEq, Eq)]
20pub struct ProtobufPath {
21    pub segments: Punctuated<Ident, Token![.]>,
22}
23
24impl ProtobufPath {
25    pub fn from_ident(name: Ident) -> Self {
26        let mut slf = Self {
27            segments: Punctuated::new(),
28        };
29        slf.segments.push(name);
30        slf
31    }
32
33    pub fn new_empty(span: Span) -> Self {
34        let mut segments = Punctuated::new();
35        segments.push_value(("google", span).to_ident());
36        segments.push_punct(Token![.](span));
37        segments.push_value(("protobuf", span).to_ident());
38        segments.push_punct(Token![.](span));
39        segments.push(("Empty", span).to_ident());
40        Self { segments }
41    }
42}
43
44impl ProtobufPath {
45    pub fn local_name(&self) -> &Ident {
46        if let Some(last) = self.segments.last() {
47            return last;
48        } else {
49            unreachable!()
50        }
51    }
52}
53
54/// Protobuf syntax.
55#[derive(Debug, Clone, Copy)]
56pub enum Syntax {
57    /// Protobuf syntax [2](https://developers.google.com/protocol-buffers/docs/proto)
58    Proto2(Span),
59    /// Protobuf syntax [3](https://developers.google.com/protocol-buffers/docs/proto3) (default)
60    Proto3(Option<Span>),
61}
62
63impl Syntax {
64    pub fn version(&self) -> usize {
65        match self {
66            Self::Proto2(_) => 2,
67            Self::Proto3(_) => 3,
68        }
69    }
70}
71
72/// A field rule
73#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
74pub enum Modifier {
75    /// A well-formed message can have zero or one of this field (but not more than one).
76    Optional,
77    /// This field can be repeated any number of times (including zero) in a well-formed message.
78    /// The order of the repeated values will be preserved.
79    Repeated,
80    /// A well-formed message must have exactly one of this field.
81    Required,
82}
83
84/// Protobuf group
85#[derive(Debug, Clone)]
86pub struct Group {
87    /// Group name
88    pub name: Ident,
89    pub fields: Vec<Field>,
90}
91
92/// Protobuf supported field types
93#[derive(Debug, Clone)]
94pub enum FieldType {
95    /// Protobuf int32
96    ///
97    /// # Remarks
98    ///
99    /// Uses variable-length encoding. Inefficient for encoding negative numbers – if
100    /// your field is likely to have negative values, use sint32 instead.
101    Int32(Span),
102    /// Protobuf int64
103    ///
104    /// # Remarks
105    ///
106    /// Uses variable-length encoding. Inefficient for encoding negative numbers – if
107    /// your field is likely to have negative values, use sint64 instead.
108    Int64(Span),
109    /// Protobuf uint32
110    ///
111    /// # Remarks
112    ///
113    /// Uses variable-length encoding.
114    Uint32(Span),
115    /// Protobuf uint64
116    ///
117    /// # Remarks
118    ///
119    /// Uses variable-length encoding.
120    Uint64(Span),
121    /// Protobuf sint32
122    ///
123    /// # Remarks
124    ///
125    /// Uses ZigZag variable-length encoding. Signed int value. These more efficiently
126    /// encode negative numbers than regular int32s.
127    Sint32(Span),
128    /// Protobuf sint64
129    ///
130    /// # Remarks
131    ///
132    /// Uses ZigZag variable-length encoding. Signed int value. These more efficiently
133    /// encode negative numbers than regular int32s.
134    Sint64(Span),
135    /// Protobuf bool
136    Bool(Span),
137    /// Protobuf fixed64
138    ///
139    /// # Remarks
140    ///
141    /// Always eight bytes. More efficient than uint64 if values are often greater than 2^56.
142    Fixed64(Span),
143    /// Protobuf sfixed64
144    ///
145    /// # Remarks
146    ///
147    /// Always eight bytes.
148    Sfixed64(Span),
149    /// Protobuf double
150    Double(Span),
151    /// Protobuf string
152    ///
153    /// # Remarks
154    ///
155    /// A string must always contain UTF-8 encoded or 7-bit ASCII text.
156    String(Span),
157    /// Protobuf bytes
158    ///
159    /// # Remarks
160    ///
161    /// May contain any arbitrary sequence of bytes.
162    Bytes(Span),
163    /// Protobut fixed32
164    ///
165    /// # Remarks
166    ///
167    /// Always four bytes. More efficient than uint32 if values are often greater than 2^28.
168    Fixed32(Span),
169    /// Protobut sfixed32
170    ///
171    /// # Remarks
172    ///
173    /// Always four bytes.
174    Sfixed32(Span),
175    /// Protobut float
176    Float(Span),
177    /// Protobuf message or enum (holds the name)
178    MessageOrEnum(Type),
179    /// Protobut map
180    Map(MapType),
181    /// Protobuf group (deprecated)
182    Group(Group),
183}
184
185#[derive(Debug, Clone)]
186pub struct MapType {
187    pub span: Span,
188    pub key: Box<FieldType>,
189    pub value: Box<FieldType>,
190}
191
192impl FieldType {
193    #[allow(unused)]
194    pub fn span(&self) -> Span {
195        match self {
196            Self::Int32(span) => *span,
197            Self::Int64(span) => *span,
198            Self::Uint32(span) => *span,
199            Self::Uint64(span) => *span,
200            Self::Sint32(span) => *span,
201            Self::Sint64(span) => *span,
202            Self::Bool(span) => *span,
203            Self::Fixed64(span) => *span,
204            Self::Sfixed64(span) => *span,
205            Self::Double(span) => *span,
206            Self::String(span) => *span,
207            Self::Bytes(span) => *span,
208            Self::Fixed32(span) => *span,
209            Self::Sfixed32(span) => *span,
210            Self::Float(span) => *span,
211            Self::MessageOrEnum(t) => t.type_path.span(),
212            Self::Map(map) => map.span,
213            Self::Group(group) => group.name.span(),
214        }
215    }
216    pub fn is_message(&self) -> bool {
217        match self {
218            Self::MessageOrEnum(t) => t.target_is_message,
219            _ => false,
220        }
221    }
222}
223
224#[derive(Debug, Clone)]
225pub struct Type {
226    pub type_path: ProtobufPath,
227    pub target_is_message: bool,
228    pub ty: syn::Type,
229}
230
231#[derive(Debug, Clone)]
232pub enum TagValue {
233    Value(Span, i32),
234    AutoIncr,
235}
236
237impl ToLitStr for TagValue {
238    fn to_lit_str(&self) -> LitStr {
239        if let Self::Value(span, value) = self {
240            LitStr::new(&format!("{}", value), *span)
241        } else {
242            unreachable!()
243        }
244    }
245}
246
247/// A Protobuf Field
248#[derive(Debug, Clone)]
249pub struct Field {
250    /// Field name
251    pub name: Ident,
252    pub field_name: Ident,
253    /// Field `Rule`
254    pub modifier: Option<Modifier>,
255    /// Field type
256    pub typ: FieldType,
257    /// Tag number
258    pub tag: TagValue,
259    /// Non-builtin options
260    pub options: Vec<ProtobufOption>,
261    pub enum_field: bool,
262}
263
264/// A Protobuf field of oneof group
265#[derive(Debug, Clone)]
266pub enum MessageElement {
267    Field(Field),
268    OneOf(OneOf),
269}
270
271#[derive(Debug, Clone, Copy)]
272pub enum NestedTypeIndex {
273    Message(usize),
274    Enum(usize),
275    Oneof(usize),
276}
277
278/// A protobuf message
279#[derive(Debug, Clone)]
280pub struct Message {
281    /// Message name
282    pub name: Ident,
283    pub struct_name: Ident,
284    /// Message fields and oneofs
285    pub fields: Vec<MessageElement>,
286    /// Message reserved numbers
287    pub reserved_nums: Vec<RangeInclusive<i32>>,
288    /// Message reserved names
289    pub reserved_names: Vec<Ident>,
290    /// Nested messages
291    pub messages: Vec<Message>,
292    /// Nested enums
293    pub enums: Vec<Enumeration>,
294    /// Non-builtin options
295    pub options: Vec<ProtobufOption>,
296    /// Extension field numbers
297    pub extension_ranges: Vec<RangeInclusive<i32>>,
298    /// Extensions
299    pub extensions: Vec<Extension>,
300    pub nested_types: Vec<NestedTypeIndex>,
301}
302
303/// A protobuf enumeration field
304#[derive(Debug, Clone)]
305pub struct EnumValue {
306    /// enum value name
307    pub name: Ident,
308    /// variant name from Self::name
309    pub variant_name: Ident,
310    /// proto_name from Self::name
311    pub proto_name: LitStr,
312    /// enum value number
313    pub tag: Option<LitInt>,
314    pub tag_value: i32,
315    /// enum value options
316    pub options: Option<Vec<ProtobufOption>>,
317}
318
319/// A protobuf enumerator
320#[derive(Debug, Clone)]
321pub struct Enumeration {
322    /// enum name
323    pub name: Ident,
324    /// enum values
325    pub values: Vec<EnumValue>,
326    /// enum options
327    pub options: Vec<ProtobufOption>,
328    /// enum reserved numbers
329    pub reserved_nums: Vec<RangeInclusive<i32>>,
330    /// enum reserved names
331    pub reserved_names: Vec<Ident>,
332}
333
334/// A OneOf
335#[derive(Debug, Clone)]
336pub struct OneOf {
337    /// OneOf name
338    pub name: Ident,
339    pub field_name: Ident,
340    pub field_lit: LitStr,
341    pub enum_name: Ident,
342    pub tags: LitStr,
343    /// OneOf fields
344    pub fields: Vec<Field>,
345    /// oneof options
346    pub options: Vec<ProtobufOption>,
347}
348
349#[derive(Debug, Clone)]
350pub struct Extension {
351    /// Extend this type with field
352    pub extendee: ProtobufPath,
353    /// Extension field
354    pub field: Field,
355}
356
357/// Service method
358#[derive(Debug, Clone)]
359pub struct Method {
360    /// Method name
361    pub name: Ident,
362    // snake case name
363    pub method_name: Ident,
364    // generated input message
365    pub input_message: Option<Message>,
366    /// Input type
367    pub input_type: Type,
368    // generated output message
369    pub output_message: Option<Message>,
370    /// Output type
371    pub output_type: Type,
372    /// If this method is client streaming
373    #[allow(dead_code)] // TODO
374    pub client_streaming: Option<Span>,
375    /// If this method is server streaming
376    #[allow(dead_code)] // TODO
377    pub server_streaming: Option<Span>,
378    /// Method options
379    pub options: Punctuated<ProtobufOption, Token![;]>,
380}
381
382/// Service definition
383#[derive(Debug, Clone)]
384pub struct Service {
385    /// Service name
386    pub name: Ident,
387    pub code_name: Ident,
388    pub methods: Vec<Method>,
389    pub options: Vec<ProtobufOption>,
390}
391
392#[derive(Debug, Clone, Hash, PartialEq, Eq)]
393pub struct AnyTypeUrl {
394    pub prefix: ProtobufPath,
395    pub full_type_name: ProtobufPath,
396}
397
398#[derive(Debug, Clone, Hash, PartialEq, Eq)]
399pub enum ProtobufConstantMessageFieldName {
400    Regular(Ident),
401    Extension(ProtobufPath),
402    AnyTypeUrl(AnyTypeUrl),
403}
404
405#[derive(Debug, Clone)]
406pub struct ProtobufConstantMessage {
407    pub fields: IndexMap<ProtobufConstantMessageFieldName, ProtobufConstant>,
408}
409
410#[derive(Debug, Clone)]
411#[allow(unused)]
412pub enum ProtobufConstant {
413    Int(LitInt),
414    Float(LitFloat),
415    Bool(LitBool),
416    Ident(ProtobufPath),
417    String(LitStr),
418    Message(ProtobufConstantMessage),
419}
420
421/// Equivalent of `UninterpretedOption.NamePart`.
422#[derive(Debug, Clone)]
423pub enum ProtobufOptionNamePart {
424    Direct(Ident),
425    Ext(ProtobufPath),
426}
427
428#[derive(Debug, Clone)]
429pub struct ProtobufOptionNameExt(pub Vec<ProtobufOptionNamePart>);
430
431#[derive(Debug, Clone)]
432pub enum ProtobufOptionName {
433    Builtin(Ident),
434    Ext(ProtobufOptionNameExt),
435}
436
437impl ProtobufOptionName {
438    pub fn is_option(&self, opt_name: &str) -> bool {
439        match self {
440            ProtobufOptionName::Builtin(name) => name.eq(opt_name),
441            ProtobufOptionName::Ext(ext) => {
442                let mut compared_index = 0;
443                for (index, n) in opt_name.split('.').enumerate() {
444                    if let Some(seg) = ext.0.get(index) {
445                        match seg {
446                            ProtobufOptionNamePart::Direct(d) => {
447                                if !d.eq(n) {
448                                    return false;
449                                }
450                            }
451                            ProtobufOptionNamePart::Ext(_ext) => return false,
452                        }
453                    } else {
454                        return false;
455                    }
456                    compared_index = index;
457                }
458                ext.0.len() == compared_index + 1
459            }
460        }
461    }
462}
463
464#[derive(Debug, Clone)]
465pub struct ProtobufOption {
466    pub name: ProtobufOptionName,
467    pub value: ProtobufConstant,
468}
469
470pub trait GetOption {
471    fn get_option<'a>(&'a self, name: &str) -> Option<&'a ProtobufOption>;
472    fn get_string_option<'a>(&'a self, name: &str) -> Option<&'a LitStr> {
473        if let Some(ProtobufOption {
474            value: ProtobufConstant::String(value),
475            ..
476        }) = self.get_option(name)
477        {
478            Some(value)
479        } else {
480            None
481        }
482    }
483    fn get_bool_option<'a>(&'a self, name: &str) -> Option<&'a LitBool> {
484        if let Some(ProtobufOption {
485            value: ProtobufConstant::Bool(value),
486            ..
487        }) = self.get_option(name)
488        {
489            Some(value)
490        } else {
491            None
492        }
493    }
494    fn get_int_option<'a>(&'a self, name: &str) -> Option<&'a LitInt> {
495        if let Some(ProtobufOption {
496            value: ProtobufConstant::Int(value),
497            ..
498        }) = self.get_option(name)
499        {
500            Some(value)
501        } else {
502            None
503        }
504    }
505    fn get_float_option<'a>(&'a self, name: &str) -> Option<&'a LitFloat> {
506        if let Some(ProtobufOption {
507            value: ProtobufConstant::Float(value),
508            ..
509        }) = self.get_option(name)
510        {
511            Some(value)
512        } else {
513            None
514        }
515    }
516    fn get_ident_option<'a>(&'a self, name: &str) -> Option<&'a ProtobufPath> {
517        if let Some(ProtobufOption {
518            value: ProtobufConstant::Ident(value),
519            ..
520        }) = self.get_option(name)
521        {
522            Some(value)
523        } else {
524            None
525        }
526    }
527    fn get_object_option<'a>(&'a self, name: &str) -> Option<&'a ProtobufConstantMessage> {
528        if let Some(ProtobufOption {
529            value: ProtobufConstant::Message(value),
530            ..
531        }) = self.get_option(name)
532        {
533            Some(value)
534        } else {
535            None
536        }
537    }
538}
539
540impl GetOption for Vec<ProtobufOption> {
541    fn get_option<'a>(&'a self, name: &str) -> Option<&'a ProtobufOption> {
542        self.iter().find(|opt| opt.name.is_option(name))
543    }
544}
545
546impl<P> GetOption for Punctuated<ProtobufOption, P> {
547    fn get_option<'a>(&'a self, name: &str) -> Option<&'a ProtobufOption> {
548        self.iter().find(|opt| opt.name.is_option(name))
549    }
550}
551
552/// Visibility of import statement
553#[derive(Debug, Clone)]
554pub enum ImportVis {
555    Default,
556    Public(Span),
557    Weak(Span),
558}
559
560impl Default for ImportVis {
561    fn default() -> Self {
562        ImportVis::Default
563    }
564}
565
566/// Import statement
567#[derive(Debug, Clone)]
568pub struct Import {
569    pub import_token: Span,
570    pub path: LitStr,
571    pub builtin: bool,
572    pub vis: ImportVis,
573    pub file_path: Option<FilePath>,
574}
575
576#[derive(Debug, Clone)]
577pub struct FilePath {
578    pub root: bool,
579    pub bin: bool,
580    pub example: bool,
581    pub is_mod: bool,
582    pub path: PathBuf,
583    pub mod_path: syn::Path,
584}
585
586#[derive(Debug, Clone)]
587pub struct Package {
588    pub package: Ident,
589}
590
591#[derive(Debug, Clone)]
592pub enum DeclIndex {
593    Message(usize),
594    Enum(usize),
595    Service(usize),
596}
597
598/// A File descriptor representing a whole .proto file
599#[derive(Debug, Clone)]
600pub struct Protocol {
601    /// Imports
602    pub imports: Vec<Import>,
603    /// Package
604    pub package: Option<Package>,
605    /// Protobuf Syntax
606    pub syntax: Syntax,
607    /// Top level messages
608    pub messages: Vec<Message>,
609    /// Enums
610    pub enums: Vec<Enumeration>,
611    /// Extensions
612    pub extensions: Vec<Extension>,
613    /// Services
614    pub services: Vec<Service>,
615    /// Non-builtin options
616    pub options: Vec<ProtobufOption>,
617    pub decls: Vec<DeclIndex>,
618    pub deps: Deps,
619}