frodobuf_schema/
model.rs

1//! Frodobuf Schema model
2use serde::{Deserialize, Serialize};
3use std::fmt;
4
5/// Separator in hierarchical names, e.g., "package.service"
6pub const IDENT_PATH_DELIMITER: &str = ".";
7/// name for attributes that store source code location (file, col)
8pub const ATTRIBUTE_ID_SOURCE: &str = "_source";
9/// name for attribute that stores a line of documentation
10pub const ATTRIBUTE_ID_DOC: &str = "doc";
11/// name for attribute that was a protobuf "option"
12pub const ATTRIBUTE_ID_OPTION: &str = "option";
13/// name for anonymous/unnamed attribute (value only)
14pub const ATTRIBUTE_UNNAMED: &str = "_";
15
16/// iterator over a set of attributes, for a field, method, sevice, or schema
17pub struct Attributes<'a> {
18    base: std::slice::Iter<'a, Attribute>,
19}
20
21/// Implementation of iterator over attributes
22impl<'a> Iterator for Attributes<'a> {
23    type Item = &'a Attribute;
24    fn next(&mut self) -> Option<&'a Attribute> {
25        self.base.next()
26    }
27}
28
29/// An attribute is a key, plus a set of (name,value) pairs
30/// If value os omitted, the name is stored with a value of 'true'
31///
32/// Examples
33///   @deprecated                         // attrib with key only
34///   @precision(scale = 2, round="down") // eg, how to display with 2 digits after decimal point
35///   @serialize(flatten)                 // shorthand for @serialize(flatten = true)
36///
37/// There is also (currently) a syntax that can take a literal value only
38///   @doc("This is my method")
39///   @max(95)
40///   @min(5)
41/// For these, the name is recorded as ATTRIBUTE_UNNAMED ("_")
42///
43#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
44pub struct Attribute {
45    /// key of the attribute, like a "namespace" for the name=value pairs
46    pub key: Ident,
47    /// set of name=value pairs associated with the key
48    pub values: Vec<(String, Constant)>,
49}
50
51impl Attribute {
52    /// Construct an attribute with a key only `@key`
53    pub fn new_key_only(key: &str) -> Attribute {
54        Attribute {
55            key: Ident::from(key),
56            values: Vec::new(),
57        }
58    }
59
60    /// Construct an attribute with a single value `@key(value)`
61    pub fn new_single_value<C: Into<Constant>>(key: &str, value: C) -> Attribute {
62        Attribute {
63            key: Ident::from(key),
64            values: vec![("_".to_string(), value.into())],
65        }
66    }
67
68    /// Construct an attribute with a single name=value pair `@key(name=value)`
69    pub fn new_single_kv<S: Into<String>, C: Into<Constant>>(
70        key: &str,
71        name: S,
72        value: C,
73    ) -> Attribute {
74        Attribute {
75            key: Ident::from(key),
76            values: vec![(name.into(), value.into())],
77        }
78    }
79
80    /// Iterate through all attributes
81    pub fn iter(&self) -> impl std::iter::Iterator<Item = &(String, Constant)> {
82        self.values.iter()
83    }
84
85    /// Returns the first value for key 'name', or None if not found
86    pub fn get(&self, name: &str) -> Option<&Constant> {
87        self.iter()
88            .find_map(|opt| if opt.0 == name { Some(&opt.1) } else { None })
89    }
90}
91
92/// trait for schema items that have attributes
93pub trait HasAttributes {
94    /// returns an iterator over the item's attributes
95    fn attributes(&'_ self) -> Attributes<'_>;
96
97    /// Returns an attribute by name, or None if it is not found
98    fn get_attribute(&self, key: &str) -> Option<&Attribute> {
99        self.attributes().find(|a| a.key == key)
100    }
101}
102
103/// Identifier
104/// For services, there is a namespace path corresponding to a 'package' hierarchy
105/// For messages, the 'name' is the message name
106/// TODO: for message, should 'namespace' be the service/trait term, or the global service/trait path?
107/// "Global" name is "namespace::name"
108#[derive(Debug, Default, Clone, Eq, PartialEq, Serialize, Deserialize)]
109pub struct Ident {
110    /// optional namespace for identifier, as a list of nested packages or modules.
111    #[serde(skip_serializing_if = "Option::is_none")]
112    pub namespace: Option<String>, // '::' separated namespace path
113    /// The "leaf" or simple name of the identifier
114    pub name: String,
115}
116
117impl Ident {
118    /// Construct an identifier with no namespace
119    pub fn new(s: &str) -> Self {
120        if let Some((ns, id)) = s.rsplit_once(IDENT_PATH_DELIMITER) {
121            Ident {
122                namespace: Some(ns.to_string()),
123                name: id.to_string(),
124            }
125        } else {
126            Ident {
127                namespace: None,
128                name: s.to_string(),
129            }
130        }
131    }
132
133    /// Construct Identifier with simple namespace
134    pub fn from_namespace(namespace: Option<String>, name: String) -> Self {
135        Ident { namespace, name }
136    }
137}
138
139/// Conversion from &String to Ident
140impl From<String> for Ident {
141    fn from(s: String) -> Ident {
142        Ident::new(&s)
143    }
144}
145
146/// Conversion from &str to Ident
147impl From<&str> for Ident {
148    fn from(s: &str) -> Ident {
149        Ident::new(s)
150    }
151}
152
153/// Implement <Ident> == <String> comparisons
154impl PartialEq<String> for Ident {
155    fn eq(&self, other: &String) -> bool {
156        &self.to_string() == other
157    }
158}
159
160/// Implement <Ident> == <String> comparisons
161impl PartialEq<Ident> for String {
162    fn eq(&self, other: &Ident) -> bool {
163        &other.to_string() == self
164    }
165}
166
167/// Implement <Ident> == <String> comparisons
168impl PartialEq<Ident> for &str {
169    fn eq(&self, other: &Ident) -> bool {
170        &other.to_string() == self
171    }
172}
173
174/// Implement <Ident> == <&str> comparisons
175impl PartialEq<str> for Ident {
176    fn eq(&self, other: &str) -> bool {
177        self.to_string().as_str() == other
178    }
179}
180
181/// Implement <Ident> == <String> comparisons
182impl PartialEq<Ident> for str {
183    fn eq(&self, other: &Ident) -> bool {
184        *other == self
185    }
186}
187
188/// Implement <Ident> == <&str> comparisons
189impl PartialEq<&str> for Ident {
190    fn eq(&self, other: &&str) -> bool {
191        self.to_string().as_str() == *other
192    }
193}
194
195/// Range of integer field numbers
196pub type FieldNumberRange = std::ops::RangeInclusive<u32>;
197
198impl fmt::Display for Ident {
199    /// Displays an identifier with its full path
200    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201        write!(f, "{}", &self.display())
202    }
203}
204
205impl Ident {
206    /// Return fully qualified identifier
207    pub fn display(&self) -> String {
208        match self.namespace.as_ref() {
209            Some(ns) => format!("{}{}{}", ns, IDENT_PATH_DELIMITER, &self.name),
210            None => self.name.clone(),
211        }
212    }
213}
214
215//impl std::string::ToString for Ident {
216//    fn to_string(&self) -> String {
217//        self.display()
218//    }
219//}
220
221/// Field type - for object fields and method parameters
222#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
223pub enum FieldType {
224    /// 8-bit signed int
225    Int8,
226
227    /// 32-bit signed int
228    Int32,
229
230    /// 64-bit signed int
231    Int64,
232
233    /// 8-bit unsigned int
234    Uint8,
235
236    /// 32-bit unsigned int
237    Uint32,
238
239    /// 64-bit unsigned int
240    Uint64,
241
242    /// bool
243    Bool,
244
245    /// UTF-8-encoded String (including 7-bit US ASCII, which is a subset of UTF-8)
246    String,
247
248    /// arbitrary sequence of bytes.
249    Bytes,
250
251    /// 32-bit float
252    Float32,
253
254    /// 64-bit float (in syntax, also called 'double')
255    Float64,
256
257    /// RFC-3339-encoded date/time
258    Datetime,
259
260    /// Map (key-type, val-type). Supported key types: string, int, or bytes
261    Map(Box<(FieldType, FieldType)>),
262
263    /// Array/Vec ("repeated" in protobuf)
264    Array(Box<FieldType>),
265
266    /// A custom datatype
267    /// parameter is path
268    ObjectOrEnum(Ident),
269}
270
271impl FieldType {
272    /// Returns true if type is one of the signed or unsigned integer types
273    pub fn is_integer(&self) -> bool {
274        matches!(
275            *self,
276            FieldType::Uint8
277                | FieldType::Int8
278                | FieldType::Uint32
279                | FieldType::Int32
280                | FieldType::Uint64
281                | FieldType::Int64
282        )
283    }
284}
285
286/// A message Field
287#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
288pub struct Field {
289    /// Field name
290    pub name: String,
291    /// whether field is optional (true) or required (false)
292    pub optional: bool,
293    /// Field type
294    pub typ: FieldType,
295    /// Tag number
296    pub number: u32,
297    /// Field attributes
298    pub attributes: Vec<Attribute>,
299}
300
301impl Field {
302    /// returns the default value for a field based on its data type
303    pub fn default_value(&self) -> Option<Constant> {
304        // if a default is declared, return that
305        if let Some(attr) = self.get_attribute("default") {
306            attr.get("value").cloned()
307        } else {
308            None
309        }
310    }
311}
312
313impl HasAttributes for Field {
314    fn attributes<'a>(&self) -> Attributes {
315        let atr = &self.attributes;
316        Attributes {
317            base: atr.iter(), // self.attributes.iter(),
318        }
319    }
320}
321
322/// A Frodobuf message
323#[derive(Debug, Default, Clone, Serialize, Deserialize)]
324pub struct Message {
325    /// Message name
326    pub name: Ident,
327
328    /// Message fields
329    pub fields: Vec<Field>,
330
331    /// Nested messages
332    pub messages: Vec<Message>,
333
334    /// Nested enums
335    pub enums: Vec<Enumeration>,
336
337    /// Attributes
338    pub attributes: Vec<Attribute>,
339    // Extension field numbers
340    //pub extension_ranges: Vec<FieldNumberRange>,
341    // Extensions
342    //pub extensions: Vec<Extension>,
343}
344
345impl Message {
346    /** Find a field by name. */
347    pub fn get_field(&self, name: &str) -> Option<&Field> {
348        self.fields.iter().find(|f| f.name == name)
349    }
350}
351
352impl<'a> HasAttributes for Message {
353    fn attributes(&'_ self) -> Attributes<'_> {
354        Attributes {
355            base: self.attributes.iter(),
356        }
357    }
358}
359
360/// A Frodobuf enumeration value - name (a symbol) and an Int32 value
361#[derive(Debug, Clone, Serialize, Deserialize)]
362pub struct EnumValue {
363    /// enum value name
364    pub name: String,
365    /// enum value number
366    pub number: i32,
367    /// enum value attributes
368    pub attributes: Vec<Attribute>,
369}
370
371/// A Frodobuf enum - not to be confused with a rust enum (which is more like a protobuf oneof)
372#[derive(Debug, Clone, Serialize, Deserialize)]
373pub struct Enumeration {
374    /// enum name
375    pub name: String,
376    /// enum values
377    pub values: Vec<EnumValue>,
378    /// enum attributes
379    pub attributes: Vec<Attribute>,
380}
381
382impl<'a> HasAttributes for Enumeration {
383    fn attributes(&'_ self) -> Attributes<'_> {
384        Attributes {
385            base: self.attributes.iter(),
386        }
387    }
388}
389
390/// Service method
391#[derive(Debug, Clone, Serialize, Deserialize)]
392pub struct Method {
393    /// Method name
394    pub name: String,
395    /// Input type, or None if the function takes no params
396    pub input_type: Option<FieldType>,
397    /// Output type, or None if the function return void
398    pub output_type: Option<FieldType>,
399
400    /*
401    /// If this method is client streaming
402    pub client_streaming: bool,
403    /// If this method is server streaming
404    pub server_streaming: bool,
405     */
406    /// Method attributes
407    pub attributes: Vec<Attribute>,
408}
409
410impl<'a> HasAttributes for Method {
411    fn attributes(&'_ self) -> Attributes<'_> {
412        Attributes {
413            base: self.attributes.iter(),
414        }
415    }
416}
417
418/// Service definition
419#[derive(Debug, Default, Clone, Serialize, Deserialize)]
420pub struct Service {
421    /// Service name
422    pub name: Ident,
423
424    /// methods in this service
425    pub methods: Vec<Method>,
426
427    /// Optional attributes for the service
428    pub attributes: Vec<Attribute>,
429
430    /// Serialized json schema
431    pub schema: Option<String>,
432
433    /// 256-bit hash, base64-encoded
434    pub schema_id: Option<String>,
435}
436
437impl<'a> HasAttributes for Service {
438    fn attributes(&'_ self) -> Attributes<'_> {
439        Attributes {
440            base: self.attributes.iter(),
441        }
442    }
443}
444
445/// constant = fullIdent | ( [ "-" | "+" ] intLit ) | ( [ "-" | "+" ] floatLit ) |
446//                 strLit | boolLit
447#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
448pub enum Constant {
449    /// unsigned 64-bit integer
450    U64(u64),
451    /// signed 64-bit integer
452    I64(i64),
453    /// 64-bit floating point number
454    F64(f64),
455    /// boolean value
456    Bool(bool),
457    /// Identifier - must be previously defined as a constant
458    Ident(Ident),
459    /// literal string, as in `"Hello"`
460    String(String),
461    /// Sequence of raw bytes
462    Bytes(Vec<u8>),
463}
464impl fmt::Display for Constant {
465    /// format constant value for printing
466    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
467        match self {
468            Constant::U64(v) => write!(f, "{}", v),
469            Constant::I64(v) => write!(f, "{}", v),
470            Constant::F64(v) => write!(f, "{}", crate::format::format_float(*v)),
471            Constant::Bool(v) => write!(f, "{}", v),
472            Constant::Ident(v) => write!(f, "{}", v),
473            Constant::String(v) => write!(f, "{}", v),
474            Constant::Bytes(_) => write!(f, "<bytes>"),
475        }
476    }
477}
478
479impl Constant {
480    /// format constant value for printing
481    pub fn format(&self) -> String {
482        match *self {
483            Constant::U64(u) => u.to_string(),
484            Constant::I64(i) => i.to_string(),
485            Constant::F64(f) => crate::format::format_float(f),
486            Constant::Bool(b) => b.to_string(),
487            Constant::Ident(ref i) => format!("{}", i),
488            // TODO: this needs escaping if used in code generation
489            Constant::String(ref s) => s.to_string(),
490            Constant::Bytes(_) => "<bytes>".to_string(),
491        }
492    }
493    /// Returns Some(s) if value is a string constant, otherwise None
494    pub fn as_string(&self) -> Option<&str> {
495        match &self {
496            Constant::String(s) => Some(crate::format::unquote(s.as_str())),
497            _ => None,
498        }
499    }
500}
501
502impl From<String> for Constant {
503    fn from(s: String) -> Constant {
504        Constant::String(s)
505    }
506}
507
508impl From<&str> for Constant {
509    fn from(s: &str) -> Constant {
510        Constant::String(s.to_string())
511    }
512}
513
514impl From<Vec<u8>> for Constant {
515    fn from(v: Vec<u8>) -> Constant {
516        Constant::Bytes(v)
517    }
518}
519
520impl From<u64> for Constant {
521    fn from(val: u64) -> Constant {
522        Constant::U64(val)
523    }
524}
525
526impl From<u32> for Constant {
527    fn from(val: u32) -> Constant {
528        Constant::U64(val as u64)
529    }
530}
531
532impl From<i64> for Constant {
533    fn from(val: i64) -> Constant {
534        Constant::I64(val)
535    }
536}
537
538impl From<i32> for Constant {
539    fn from(val: i32) -> Constant {
540        Constant::I64(val as i64)
541    }
542}
543
544/// A Schema definition read from a file
545#[derive(Debug, Default, Clone, Serialize, Deserialize)]
546pub struct Schema {
547    /// Package
548    pub namespace: Ident, // from "package"
549
550    /// Top level messages
551    pub messages: Vec<Message>,
552
553    /// Enums
554    pub enums: Vec<Enumeration>,
555
556    /// Services
557    pub services: Vec<Service>,
558
559    /// Schema attributes
560    pub attributes: Vec<Attribute>,
561}
562
563impl<'a> HasAttributes for Schema {
564    fn attributes(&'_ self) -> Attributes<'_> {
565        Attributes {
566            base: self.attributes.iter(),
567        }
568    }
569}