1use crate::{ast::types as ast, ir::types as ir};
2use anyhow::{anyhow, Result};
3use heck::SnakeCase;
4use itertools::Itertools;
5use std::{
6 collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque},
7 convert::{TryFrom, TryInto},
8};
9
10#[derive(Default, Clone, Debug)]
11pub struct Builder<'a> {
12 current_namespace: Option<ast::Namespace<'a>>,
13 types: HashMap<ir::QualifiedIdent<'a>, CustomTypeStatus<'a>>,
14 nodes: BTreeMap<usize, Vec<ir::Node<'a>>>,
15 root_types: HashSet<ir::QualifiedIdent<'a>>,
16}
17
18#[derive(Clone, Debug)]
19struct IndexedElement<'a>(usize, ast::Element<'a>);
20
21#[derive(Clone, Debug, PartialEq)]
22enum CustomTypeStatus<'a> {
23 TableDeclared,
24 Defined(ir::CustomType<'a>),
25}
26
27#[derive(Clone, Debug, PartialEq)]
29struct NamespaceBuf<'a> {
30 ident: ir::QualifiedIdent<'a>,
31 namespaces: BTreeMap<ir::Ident<'a>, NamespaceBuf<'a>>,
32 nodes: Vec<ir::Node<'a>>,
33}
34
35impl<'a> NamespaceBuf<'a> {
36 fn new(ident: ir::QualifiedIdent<'a>) -> Self {
37 Self {
38 ident,
39 namespaces: BTreeMap::new(),
40 nodes: Vec::new(),
41 }
42 }
43
44 pub fn depth(&self) -> usize {
45 self.ident.parts.len()
46 }
47
48 fn into_namespace(self) -> ir::Namespace<'a> {
49 let ident = self.ident;
50 let nodes = self
51 .namespaces
52 .into_iter()
53 .map(|(_, v)| ir::Node::Namespace(v.into_namespace()))
54 .chain(self.nodes)
55 .collect();
56 ir::Namespace { ident, nodes }
57 }
58
59 fn into_node(self) -> ir::Node<'a> {
60 ir::Node::Namespace(self.into_namespace())
61 }
62}
63
64impl<'a> Builder<'a> {
65 fn new_element(
66 &mut self,
67 &IndexedElement(pos, ref element): &IndexedElement<'a>,
68 ) -> Result<bool> {
69 match element {
70 ast::Element::Namespace(n) => self.new_namespace(n),
71 ast::Element::Table(t) => self.new_table(t, pos),
72 ast::Element::Struct(s) => self.new_struct(s, pos),
73 ast::Element::Enum(e) => self.new_enum(e, pos),
74 ast::Element::Union(u) => self.new_union(u, pos),
75 ast::Element::Rpc(r) => self.new_rpc(r, pos),
76 ast::Element::Root(r) => self.new_root(r, pos),
77 ast::Element::FileExtension(..) => unimplemented!(),
78 ast::Element::FileIdentifier(..) => unimplemented!(),
79 ast::Element::Attribute(..) => unimplemented!(),
80 ast::Element::Object(..) => unimplemented!(),
81 }
82 }
83
84 fn new_namespace(&mut self, ns: &ast::Namespace<'a>) -> Result<bool> {
85 self.current_namespace = Some(ns.clone());
86 Ok(true)
87 }
88
89 fn new_table(&mut self, t: &ast::Table<'a>, pos: usize) -> Result<bool> {
90 let ident = &self.make_fully_qualified_ident(ir::Ident::from(t.id));
93
94 if let Some(CustomTypeStatus::Defined(..)) = self.types.get(ident) {
95 return Err(anyhow!("type already defined: {}", ident));
96 };
97
98 self.types
101 .insert(ident.clone(), CustomTypeStatus::TableDeclared);
102
103 let mut fields = Vec::new();
104
105 for f in &t.fields {
107 let ty = self.try_type(&f.ty);
108 if let Some(ty) = ty {
109 let required = match &f.metadata {
110 Some(m) => match m.values.get(&ast::Ident::from("required")) {
111 Some(None) => true,
112 _ => false,
113 },
114 None => false,
115 };
116
117 if let Some(enum_ty_ident) = ty.make_union_enum_type_companion() {
119 let ty = self
120 .find_custom_type(enum_ty_ident.clone())
121 .expect("Union enum type companion should be defined now");
122 let ident = ir::Ident::from(format!("{}_type", &f.id.raw.to_snake_case()));
123 let default_value = None;
124 let metadata = ir::FieldMetadata::builder().required(required).build();
125 let doc = f.doc.clone();
126 fields.push(ir::Field {
127 ident,
128 ty,
129 default_value,
130 metadata,
131 doc,
132 });
133 }
134 let ident = ir::Ident::from(&f.id);
135 let default_value = f.default_value.clone().or_else(|| (&f.ty).try_into().ok());
137 let metadata = ir::FieldMetadata::builder().required(required).build();
138 let doc = f.doc.clone();
139
140 fields.push(ir::Field {
141 ident,
142 ty,
143 default_value,
144 metadata,
145 doc,
146 });
147 } else {
148 return Ok(false);
151 }
152 }
153
154 let ident = ident.clone();
155 let root_type = false; let doc = t.doc.clone();
157
158 self.types.insert(
159 ident.clone(),
160 CustomTypeStatus::Defined(ir::CustomType::Table),
161 );
162
163 self.nodes.insert(
164 pos,
165 vec![ir::Node::Table(ir::Table {
166 ident,
167 fields,
168 root_type,
169 doc,
170 })],
171 );
172
173 Ok(true)
174 }
175
176 fn new_struct(&mut self, t: &ast::Struct<'a>, pos: usize) -> Result<bool> {
177 let ident = &self.make_fully_qualified_ident(ir::Ident::from(t.id));
178
179 if let Some(CustomTypeStatus::Defined(..)) = self.types.get(ident) {
180 return Err(anyhow!("type already defined: {}", ident));
181 };
182
183 let mut fields = Vec::new();
187
188 for f in &t.fields {
190 let ty = self.try_type(&f.ty);
191 if let Some(ty) = ty {
192 if !ty.is_scalar() {
193 return Err(anyhow!(
194 "struct contains illegal member: {}\nStructs can only contain scalar types, structs and fixed-size arrays", ident)
195 );
196 }
197
198 let ident = ir::Ident::from(&f.id);
199 let default_value = f.default_value.clone();
200 let metadata = ir::FieldMetadata::default();
201 let doc = f.doc.clone();
202
203 fields.push(ir::Field {
204 ident,
205 ty,
206 default_value,
207 metadata,
208 doc,
209 });
210 } else {
211 return Ok(false);
214 }
215 }
216
217 let doc = t.doc.clone();
218
219 self.types.insert(
220 ident.clone(),
221 CustomTypeStatus::Defined(ir::CustomType::Struct {
222 fields: fields.clone(),
223 }),
224 );
225
226 let s = ir::Struct {
227 ident: ident.clone(),
228 fields,
229 doc,
230 };
231
232 self.nodes.insert(pos, vec![ir::Node::Struct(s)]);
233
234 Ok(true)
235 }
236
237 fn new_enum(&mut self, e: &ast::Enum<'a>, pos: usize) -> Result<bool> {
238 let ident = &self.make_fully_qualified_ident(ir::Ident::from(e.id));
239
240 if let Some(CustomTypeStatus::Defined(..)) = self.types.get(ident) {
241 return Err(anyhow!("type already defined: {}", ident));
242 };
243
244 let base_type = ir::EnumBaseType::try_from(&e.base_type)
245 .map_err(|_| anyhow!("enum has bad base type: {}", ident))?;
246 let variants: Vec<_> = e
247 .variants
248 .iter()
249 .map(|v| ir::EnumVariant {
250 ident: ir::Ident::from(v.id),
251 value: v.value,
252 doc: v.doc.clone(),
253 })
254 .collect();
255
256 let doc = e.doc.clone();
257
258 self.types.insert(
259 ident.clone(),
260 CustomTypeStatus::Defined(ir::CustomType::Enum {
261 variants: variants.clone(),
262 base_type,
263 }),
264 );
265
266 let s = ir::Enum {
267 ident: ident.clone(),
268 base_type,
269 variants,
270 doc,
271 };
272
273 self.nodes.insert(pos, vec![ir::Node::Enum(s)]);
274
275 Ok(true)
276 }
277
278 fn new_union(&mut self, u: &ast::Union<'a>, pos: usize) -> Result<bool> {
279 let ident = &self.make_fully_qualified_ident(ir::Ident::from(u.id));
280
281 if let Some(CustomTypeStatus::Defined(..)) = self.types.get(ident) {
282 return Err(anyhow!("type already defined: {}", ident));
283 };
284
285 let mut variants = Vec::new();
286
287 for f in &u.variants {
289 let ty = self.try_type(&ast::Type::Ident(ast::QualifiedIdent::from(vec![f.id])));
290 if let Some(ty) = ty {
291 let id = &f.id;
292 variants.push(ir::UnionVariant {
293 ty,
294 ident: ir::Ident::from(id),
295 doc: f.doc.clone(),
296 });
297 } else {
298 return Ok(false);
301 }
302 }
303
304 let doc = u.doc.clone();
305
306 let e = self.generate_enum_for_union(u)?;
308
309 self.types.insert(
310 ident.clone(),
311 CustomTypeStatus::Defined(ir::CustomType::Union {
312 enum_ident: e.ident.clone(),
313 variants: variants.clone(),
314 }),
315 );
316
317 let s = ir::Union {
318 ident: ident.clone(),
319 enum_ident: e.ident.clone(),
320 variants,
321 doc,
322 };
323
324 self.nodes
325 .insert(pos, vec![ir::Node::Enum(e), ir::Node::Union(s)]);
326
327 Ok(true)
328 }
329
330 fn generate_enum_for_union(&mut self, u: &ast::Union<'a>) -> Result<ir::Enum<'a>> {
331 let enum_ident = format!("{}Type", u.id.raw);
332 let ident = &self.make_fully_qualified_ident_relative(ir::QualifiedIdent::from(vec![
333 ir::Ident::from("fbs_gen"),
334 ir::Ident::from(enum_ident),
335 ]));
336 if let Some(CustomTypeStatus::Defined(..)) = self.types.get(ident) {
337 return Err(anyhow!("type already defined: {}", ident));
338 };
339
340 let base_type = ir::EnumBaseType::UByte;
343 let variants: Vec<_> = std::iter::once(ir::EnumVariant {
344 ident: ir::Ident::from("None"),
345 value: Some(0.into()),
346 doc: vec![].into(),
347 })
348 .chain(u.variants.iter().map(|v| ir::EnumVariant {
349 ident: v.id.into(),
350 value: None,
351 doc: v.doc.clone(),
352 }))
353 .collect();
354
355 let doc = u.doc.clone();
356
357 self.types.insert(
358 ident.clone(),
359 CustomTypeStatus::Defined(ir::CustomType::Enum {
360 variants: variants.clone(),
361 base_type,
362 }),
363 );
364
365 Ok(ir::Enum {
366 ident: ident.clone(),
367 base_type,
368 variants,
369 doc,
370 })
371 }
372
373 fn new_root(&mut self, root: &ast::Root<'a>, _: usize) -> Result<bool> {
374 let ident = self.make_fully_qualified_ident(ir::Ident::from(&root.typename));
375
376 self.new_root_ident(&ident)
377 }
378
379 fn new_root_ident(&mut self, ident: &ir::QualifiedIdent<'a>) -> Result<bool> {
380 if let Some(&CustomTypeStatus::Defined(ref def)) = self.types.get(&ident) {
381 if def == &ir::CustomType::Table {
382 self.root_types.insert(ident.clone());
383 Ok(true)
384 } else {
385 Err(anyhow!("Only tables can be root types: {}", ident))
386 }
387 } else {
388 Ok(false)
389 }
390 }
391
392 fn new_rpc(&mut self, rpc: &ast::Rpc<'a>, pos: usize) -> Result<bool> {
393 let ident = &self.make_fully_qualified_ident(ir::Ident::from(&rpc.id));
394
395 let mut methods = Vec::new();
396
397 for m in &rpc.methods {
398 let ident = ir::Ident::from(&m.id);
399 let snake_ident = ir::Ident::from(m.id.as_ref().to_snake_case());
400
401 let request_type = self.make_fully_qualified_from_ast_qualified_ident(&m.request_type);
402 let response_type =
403 self.make_fully_qualified_from_ast_qualified_ident(&m.response_type);
404
405 if !self.new_root_ident(&request_type)? || !self.new_root_ident(&response_type)? {
406 return Ok(false);
408 }
409 let streaming = match &m.metadata {
410 Some(m) => match m.values.get(&ast::Ident::from("streaming")) {
411 Some(Some(ast::Single::String("client"))) => ir::RpcStreaming::Client,
412 Some(Some(ast::Single::String("server"))) => ir::RpcStreaming::Server,
413 Some(Some(ast::Single::String("bidi"))) => ir::RpcStreaming::Bidi,
414 None => ir::RpcStreaming::None,
415 Some(Some(s)) => return Err(anyhow!("Unknown streaming type: {:?}", s)),
416 Some(None) => {
417 return Err(anyhow!(
418 "Missing streaming type: possible values are `client`, `server` and `bidi`"
419 ))
420 }
421 },
422 None => ir::RpcStreaming::None,
423 };
424 let metadata = ir::RpcMethodMetadata { streaming };
425 methods.push(ir::RpcMethod {
426 ident,
427 snake_ident,
428 request_type,
429 response_type,
430 metadata,
431 doc: m.doc.clone(),
432 });
433 }
434
435 let s = ir::Rpc {
436 ident: ident.clone(),
437 methods,
438 doc: rpc.doc.clone(),
439 };
440
441 self.nodes.insert(pos, vec![ir::Node::Rpc(s)]);
442
443 Ok(true)
444 }
445
446 fn current_namespace(&self) -> Option<&ast::Namespace<'a>> {
447 self.current_namespace.as_ref()
448 }
449
450 fn make_fully_qualified_ident(&self, ident: ir::Ident<'a>) -> ir::QualifiedIdent<'a> {
451 let mut fqi = self
452 .current_namespace()
453 .map(|ns| ir::QualifiedIdent::from(&ns.ident))
454 .unwrap_or_default();
455 fqi.parts.push(ident);
456 fqi
457 }
458
459 fn make_fully_qualified_ident_relative(
460 &self,
461 ident: ir::QualifiedIdent<'a>,
462 ) -> ir::QualifiedIdent<'a> {
463 let mut fqi = self
464 .current_namespace()
465 .map(|ns| ir::QualifiedIdent::from(&ns.ident))
466 .unwrap_or_default();
467 fqi.parts.extend(ident.parts);
468 fqi
469 }
470
471 fn make_fully_qualified_from_ast_qualified_ident(
472 &self,
473 qualified_ident: &ast::QualifiedIdent<'a>,
474 ) -> ir::QualifiedIdent<'a> {
475 if qualified_ident.parts.len() == 1 {
476 self.make_fully_qualified_ident(ir::Ident::from(qualified_ident.parts[0]))
478 } else {
479 ir::QualifiedIdent::from(qualified_ident.clone())
480 }
481 }
482
483 fn try_type(&self, ty: &ast::Type<'a>) -> Option<ir::Type<'a>> {
484 Some(match ty {
485 ast::Type::Bool => ir::Type::Bool,
486 ast::Type::Byte => ir::Type::Byte,
487 ast::Type::UByte => ir::Type::UByte,
488 ast::Type::Short => ir::Type::Short,
489 ast::Type::UShort => ir::Type::UShort,
490 ast::Type::Int => ir::Type::Int,
491 ast::Type::UInt => ir::Type::UInt,
492 ast::Type::Float => ir::Type::Float,
493 ast::Type::Long => ir::Type::Long,
494 ast::Type::ULong => ir::Type::ULong,
495 ast::Type::Double => ir::Type::Double,
496 ast::Type::Int8 => ir::Type::Int8,
497 ast::Type::UInt8 => ir::Type::UInt8,
498 ast::Type::Int16 => ir::Type::Int16,
499 ast::Type::UInt16 => ir::Type::UInt16,
500 ast::Type::Int32 => ir::Type::Int32,
501 ast::Type::UInt32 => ir::Type::UInt32,
502 ast::Type::Int64 => ir::Type::Int64,
503 ast::Type::UInt64 => ir::Type::UInt64,
504 ast::Type::Float32 => ir::Type::Float32,
505 ast::Type::Float64 => ir::Type::Float64,
506 ast::Type::String => ir::Type::String,
507 ast::Type::Array(ty) => {
508 if let Some(inner) = self.try_type(ty) {
509 ir::Type::Array(Box::new(inner))
510 } else {
511 return None; }
513 }
514 ast::Type::Ident(qualified_ident) => {
515 let ident = self.make_fully_qualified_from_ast_qualified_ident(&qualified_ident);
516
517 return self.find_custom_type(ident);
518 }
519 })
520 }
521
522 fn find_custom_type(
523 &self,
524 fully_qualified_ident: ir::QualifiedIdent<'a>,
525 ) -> Option<ir::Type<'a>> {
526 let ident = fully_qualified_ident;
527 let ty = match self.types.get(&ident) {
528 Some(CustomTypeStatus::TableDeclared) => ir::CustomType::Table,
529 Some(CustomTypeStatus::Defined(ty)) => ty.clone(),
530 _ => return None, };
532
533 Some(ir::Type::Custom(ir::CustomTypeRef { ident, ty }))
534 }
535
536 pub fn build(schema: ast::Schema<'a>) -> Result<ir::Root<'a>> {
537 let mut context = Builder::default();
538
539 let mut input: VecDeque<_> = schema
542 .elements
543 .into_iter()
544 .enumerate()
545 .map(|(i, el)| IndexedElement(i, el))
546 .collect();
547
548 loop {
551 let mut unsatisfied = VecDeque::new();
552 let nodes_len = context.nodes.len();
553 while let Some(element) = input.pop_front() {
554 if !context.new_element(&element)? {
555 unsatisfied.push_back(element);
556 }
557 }
558 if unsatisfied.is_empty() {
559 break; } else {
561 if context.nodes.len() == nodes_len {
563 let unsatisfied_elements = unsatisfied
564 .into_iter()
565 .map(|IndexedElement(_, element)| element)
566 .collect::<Vec<_>>();
567 return Err(anyhow!("Unable to type schema: the following definitions referenced items not found: {:?}", unsatisfied_elements));
568 }
570 }
571 input = std::mem::take(&mut unsatisfied);
572 }
573
574 let nodes = context.nodes;
575 let root_types = &context.root_types;
576
577 let nodes: Vec<_> = nodes
579 .into_iter()
580 .map(|(_, v)| v)
581 .flatten()
582 .map(|mut v| {
583 if let ir::Node::Table(ref mut t) = &mut v {
584 if root_types.contains(&t.ident) {
585 t.root_type = true;
586 }
587 };
588 v
589 })
590 .collect();
591
592 let mut elements: HashMap<_, _> = nodes
593 .into_iter()
594 .map(|n| (n.namespace(), n))
595 .into_group_map()
596 .into_iter()
597 .collect();
598
599 let namespaces_idents: BTreeSet<_> = elements.keys().cloned().filter_map(|id| id).collect();
600
601 let mut namespaces: Vec<_> = namespaces_idents
602 .into_iter()
603 .map(NamespaceBuf::new)
604 .collect();
605
606 for ns in namespaces.iter_mut() {
608 if let Some(children) = elements.remove(&Some(ns.ident.clone())) {
609 ns.nodes = children;
610 }
611 }
612
613 let mut nodes = BTreeMap::new();
615 for ns in namespaces.into_iter() {
617 let mut parent = &mut nodes;
618
619 for i in 1..ns.depth() {
621 parent = &mut parent
622 .entry(ns.ident.parts[i - 1].clone())
623 .or_insert_with(|| {
624 NamespaceBuf::new(ir::QualifiedIdent::from(ns.ident.parts[..i].to_vec()))
625 })
626 .namespaces;
627 }
628 if let Some(n) = parent.get_mut(ns.ident.simple()) {
630 n.nodes = ns.nodes;
631 } else {
632 parent.insert(ns.ident.simple().clone(), ns);
633 }
634 }
635
636 let mut nodes: Vec<_> = nodes.into_iter().map(|(_, v)| v.into_node()).collect();
637
638 if let Some(rest) = elements.remove(&None) {
640 nodes.extend(rest);
641 }
642
643 Ok(ir::Root { nodes })
644 }
645}
646
647#[cfg(test)]
648mod tests {
649 use super::*;
650 use crate::ir::types as ir;
651 use pretty_assertions::assert_eq;
652
653 #[test]
654 fn test_table_simple() {
655 let input = "\
656// Not a doc
657table HelloReply {
658 message:string;
659}
660";
661 let (_, schema) = crate::parser::schema_decl(input).unwrap();
662 let actual = Builder::build(schema).unwrap();
663 let expected = ir::Root {
664 nodes: vec![ir::Node::Table(
665 ir::Table::builder()
666 .ident(ir::QualifiedIdent::from("HelloReply"))
667 .fields(vec![ir::Field::builder()
668 .ident(ir::Ident::from("message"))
669 .ty(ir::Type::String)
670 .build()])
671 .build(),
672 )],
673 };
674
675 assert_eq!(actual, expected);
676 }
677
678 #[test]
679 fn test_table_root() {
680 let input = "\
681table HelloReply {
682 /// Doc
683 message:string;
684}
685
686root_type HelloReply;
687";
688 let (_, schema) = crate::parser::schema_decl(input).unwrap();
689 let actual = Builder::build(schema).unwrap();
690 let expected = ir::Root {
691 nodes: vec![ir::Node::Table(
692 ir::Table::builder()
693 .ident(ir::QualifiedIdent::from("HelloReply"))
694 .fields(vec![ir::Field::builder()
695 .ident(ir::Ident::from("message"))
696 .ty(ir::Type::String)
697 .doc(vec![" Doc"].into())
698 .build()])
699 .root_type(true)
700 .build(),
701 )],
702 };
703
704 assert_eq!(actual, expected);
705 }
706
707 #[test]
708 fn test_table_in_namespace() {
709 let input = "\
710namespace foo.bar;
711
712table HelloReply {
713 message:string;
714}
715";
716 let (_, schema) = crate::parser::schema_decl(input).unwrap();
717 let actual = Builder::build(schema).unwrap();
718 let expected = ir::Root {
719 nodes: vec![ir::Node::Namespace(
720 ir::Namespace::builder()
721 .ident(ir::QualifiedIdent::parse_str("foo"))
722 .nodes(vec![ir::Node::Namespace(
723 ir::Namespace::builder()
724 .ident(ir::QualifiedIdent::parse_str("foo.bar"))
725 .nodes(vec![ir::Node::Table(
726 ir::Table::builder()
727 .ident(ir::QualifiedIdent::parse_str("foo.bar.HelloReply"))
728 .fields(vec![ir::Field::builder()
729 .ident(ir::Ident::from("message"))
730 .ty(ir::Type::String)
731 .build()])
732 .build(),
733 )])
734 .build(),
735 )])
736 .build(),
737 )],
738 };
739
740 assert_eq!(actual, expected);
741 }
742
743 #[test]
744 fn namespace_stacking() {
745 let input = "\
746namespace foo.bar;
747
748table HelloReply {
749 message:string;
750}
751
752namespace foo.baz;
753
754table GoodbyeReply {
755 message:string;
756}
757";
758 let (_, schema) = crate::parser::schema_decl(input).unwrap();
759 let actual = Builder::build(schema).unwrap();
760 let expected = ir::Root {
761 nodes: vec![ir::Node::Namespace(
762 ir::Namespace::builder()
763 .ident(ir::QualifiedIdent::parse_str("foo"))
764 .nodes(vec![
765 ir::Node::Namespace(
766 ir::Namespace::builder()
767 .ident(ir::QualifiedIdent::parse_str("foo.bar"))
768 .nodes(vec![ir::Node::Table(
769 ir::Table::builder()
770 .ident(ir::QualifiedIdent::parse_str("foo.bar.HelloReply"))
771 .fields(vec![ir::Field::builder()
772 .ident(ir::Ident::from("message"))
773 .ty(ir::Type::String)
774 .build()])
775 .build(),
776 )])
777 .build(),
778 ),
779 ir::Node::Namespace(
780 ir::Namespace::builder()
781 .ident(ir::QualifiedIdent::parse_str("foo.baz"))
782 .nodes(vec![ir::Node::Table(
783 ir::Table::builder()
784 .ident(ir::QualifiedIdent::parse_str(
785 "foo.baz.GoodbyeReply",
786 ))
787 .fields(vec![ir::Field::builder()
788 .ident(ir::Ident::from("message"))
789 .ty(ir::Type::String)
790 .build()])
791 .build(),
792 )])
793 .build(),
794 ),
795 ])
796 .build(),
797 )],
798 };
799
800 assert_eq!(actual, expected);
801 }
802
803 #[test]
804 fn test_table_simple_dependency() {
805 let input = "\
806table HelloMsg {
807 val: string;
808}
809
810table HelloReply {
811 message: HelloMsg;
812}
813";
814 let (_, schema) = crate::parser::schema_decl(input).unwrap();
815 let actual = Builder::build(schema).unwrap();
816 let expected = ir::Root {
817 nodes: vec![
818 ir::Node::Table(
819 ir::Table::builder()
820 .ident(ir::QualifiedIdent::from("HelloMsg"))
821 .fields(vec![ir::Field::builder()
822 .ident(ir::Ident::from("val"))
823 .ty(ir::Type::String)
824 .build()])
825 .build(),
826 ),
827 ir::Node::Table(
828 ir::Table::builder()
829 .ident(ir::QualifiedIdent::from("HelloReply"))
830 .fields(vec![ir::Field::builder()
831 .ident(ir::Ident::from("message"))
832 .ty(ir::Type::Custom(
833 ir::CustomTypeRef::builder()
834 .ident(ir::QualifiedIdent::from("HelloMsg"))
835 .ty(ir::CustomType::Table)
836 .build(),
837 ))
838 .build()])
839 .build(),
840 ),
841 ],
842 };
843
844 assert_eq!(actual, expected);
845 }
846
847 #[test]
848 fn test_table_required_field() {
849 let input = "\
850table HelloMsg {
851 message: string (required);
852}
853";
854 let (_, schema) = crate::parser::schema_decl(input).unwrap();
855 let actual = Builder::build(schema).unwrap();
856 let expected = ir::Root {
857 nodes: vec![ir::Node::Table(
858 ir::Table::builder()
859 .ident(ir::QualifiedIdent::from("HelloMsg"))
860 .fields(vec![ir::Field::builder()
861 .ident(ir::Ident::from("message"))
862 .ty(ir::Type::String)
863 .metadata(ir::FieldMetadata::builder().required(true).build())
864 .build()])
865 .build(),
866 )],
867 };
868
869 assert_eq!(actual, expected);
870 }
871
872 #[test]
873 fn test_table_depends_on_next() {
874 let input = "\
875table HelloReply {
876 message: HelloMsg;
877}
878
879table HelloMsg {
880 val: string;
881}
882";
883 let (_, schema) = crate::parser::schema_decl(input).unwrap();
884 let actual = Builder::build(schema).unwrap();
885 let expected = ir::Root {
886 nodes: vec![
887 ir::Node::Table(
888 ir::Table::builder()
889 .ident(ir::QualifiedIdent::from("HelloReply"))
890 .fields(vec![ir::Field::builder()
891 .ident(ir::Ident::from("message"))
892 .ty(ir::Type::Custom(
893 ir::CustomTypeRef::builder()
894 .ident(ir::QualifiedIdent::from("HelloMsg"))
895 .ty(ir::CustomType::Table)
896 .build(),
897 ))
898 .build()])
899 .build(),
900 ),
901 ir::Node::Table(
902 ir::Table::builder()
903 .ident(ir::QualifiedIdent::from("HelloMsg"))
904 .fields(vec![ir::Field::builder()
905 .ident(ir::Ident::from("val"))
906 .ty(ir::Type::String)
907 .build()])
908 .build(),
909 ),
910 ],
911 };
912
913 assert_eq!(actual, expected);
914 }
915
916 #[test]
917 fn test_table_depends_on_next_with_root_type_first() {
918 let input = "\
919root_type HelloReply;
920
921table HelloReply {
922 message: HelloMsg;
923}
924
925table HelloMsg {
926 val: string;
927}
928";
929 let (_, schema) = crate::parser::schema_decl(input).unwrap();
930 let actual = Builder::build(schema).unwrap();
931 let expected = ir::Root {
932 nodes: vec![
933 ir::Node::Table(
934 ir::Table::builder()
935 .ident(ir::QualifiedIdent::from("HelloReply"))
936 .fields(vec![ir::Field::builder()
937 .ident(ir::Ident::from("message"))
938 .ty(ir::Type::Custom(
939 ir::CustomTypeRef::builder()
940 .ident(ir::QualifiedIdent::from("HelloMsg"))
941 .ty(ir::CustomType::Table)
942 .build(),
943 ))
944 .build()])
945 .root_type(true)
946 .build(),
947 ),
948 ir::Node::Table(
949 ir::Table::builder()
950 .ident(ir::QualifiedIdent::from("HelloMsg"))
951 .fields(vec![ir::Field::builder()
952 .ident(ir::Ident::from("val"))
953 .ty(ir::Type::String)
954 .build()])
955 .build(),
956 ),
957 ],
958 };
959
960 assert_eq!(actual, expected);
961 }
962
963 #[test]
964 fn test_table_recursive() {
965 let input = "\
966table HelloReply {
967 message: HelloReply;
968}
969";
970 let (_, schema) = crate::parser::schema_decl(input).unwrap();
971 let actual = Builder::build(schema).unwrap();
972 let expected = ir::Root {
973 nodes: vec![ir::Node::Table(
974 ir::Table::builder()
975 .ident(ir::QualifiedIdent::from("HelloReply"))
976 .fields(vec![ir::Field::builder()
977 .ident(ir::Ident::from("message"))
978 .ty(ir::Type::Custom(
979 ir::CustomTypeRef::builder()
980 .ident(ir::QualifiedIdent::from("HelloReply"))
981 .ty(ir::CustomType::Table)
982 .build(),
983 ))
984 .build()])
985 .build(),
986 )],
987 };
988
989 assert_eq!(actual, expected);
990 }
991
992 #[test]
993 fn test_struct_single() {
994 let input = "\
995struct Vector3 {
996 x: float32;
997 y: float32;
998 z: float32;
999}
1000";
1001 let (_, schema) = crate::parser::schema_decl(input).unwrap();
1002 let actual = Builder::build(schema).unwrap();
1003 let expected = ir::Root {
1004 nodes: vec![ir::Node::Struct(
1005 ir::Struct::builder()
1006 .ident(ir::QualifiedIdent::from("Vector3"))
1007 .fields(vec![
1008 ir::Field::builder()
1009 .ident(ir::Ident::from("x"))
1010 .ty(ir::Type::Float32)
1011 .build(),
1012 ir::Field::builder()
1013 .ident(ir::Ident::from("y"))
1014 .ty(ir::Type::Float32)
1015 .build(),
1016 ir::Field::builder()
1017 .ident(ir::Ident::from("z"))
1018 .ty(ir::Type::Float32)
1019 .build(),
1020 ])
1021 .build(),
1022 )],
1023 };
1024
1025 assert_eq!(actual, expected);
1026 }
1027
1028 #[test]
1029 fn test_struct_in_struct() {
1030 let input = "\
1031struct Vector2 {
1032 x: float32;
1033 y: float32;
1034}
1035struct Vector3 {
1036 xy: Vector2;
1037 z: float32;
1038}
1039";
1040 let (_, schema) = crate::parser::schema_decl(input).unwrap();
1041 let actual = Builder::build(schema).unwrap();
1042 let expected = ir::Root {
1043 nodes: vec![
1044 ir::Node::Struct(
1045 ir::Struct::builder()
1046 .ident(ir::QualifiedIdent::from("Vector2"))
1047 .fields(vec![
1048 ir::Field::builder()
1049 .ident(ir::Ident::from("x"))
1050 .ty(ir::Type::Float32)
1051 .build(),
1052 ir::Field::builder()
1053 .ident(ir::Ident::from("y"))
1054 .ty(ir::Type::Float32)
1055 .build(),
1056 ])
1057 .build(),
1058 ),
1059 ir::Node::Struct(
1060 ir::Struct::builder()
1061 .ident(ir::QualifiedIdent::from("Vector3"))
1062 .fields(vec![
1063 ir::Field::builder()
1064 .ident(ir::Ident::from("xy"))
1065 .ty(ir::Type::Custom(
1066 ir::CustomTypeRef::builder()
1067 .ident(ir::QualifiedIdent::from("Vector2"))
1068 .ty(ir::CustomType::Struct {
1069 fields: vec![
1070 ir::Field::builder()
1071 .ident(ir::Ident::from("x"))
1072 .ty(ir::Type::Float32)
1073 .build(),
1074 ir::Field::builder()
1075 .ident(ir::Ident::from("y"))
1076 .ty(ir::Type::Float32)
1077 .build(),
1078 ],
1079 })
1080 .build(),
1081 ))
1082 .build(),
1083 ir::Field::builder()
1084 .ident(ir::Ident::from("z"))
1085 .ty(ir::Type::Float32)
1086 .build(),
1087 ])
1088 .build(),
1089 ),
1090 ],
1091 };
1092
1093 assert_eq!(actual, expected);
1094 }
1095
1096 #[test]
1097 fn test_enum_single() {
1098 let input = "enum MyEnum : ubyte { Foo, Bar }";
1099 let (_, schema) = crate::parser::schema_decl(input).unwrap();
1100 let actual = Builder::build(schema).unwrap();
1101 let expected = ir::Root {
1102 nodes: vec![ir::Node::Enum(
1103 ir::Enum::builder()
1104 .ident(ir::QualifiedIdent::from("MyEnum"))
1105 .base_type(ir::EnumBaseType::UByte)
1106 .variants(vec![
1107 ir::EnumVariant::builder()
1108 .ident(ir::Ident::from("Foo"))
1109 .build(),
1110 ir::EnumVariant::builder()
1111 .ident(ir::Ident::from("Bar"))
1112 .build(),
1113 ])
1114 .build(),
1115 )],
1116 };
1117
1118 assert_eq!(actual, expected);
1119 }
1120
1121 #[test]
1122 fn test_union_single() {
1123 let input = "\
1124 table A {
1125 message: string;
1126 }
1127 table B {
1128 message: [ubyte];
1129 }
1130 union AorB {
1131 A,
1132 B
1133 }
1134 ";
1135 let (_, schema) = crate::parser::schema_decl(input).unwrap();
1136 let actual = Builder::build(schema).unwrap();
1137 let expected = ir::Root {
1138 nodes: vec![
1139 ir::Node::Namespace(
1140 ir::Namespace::builder()
1141 .ident(ir::QualifiedIdent::parse_str("fbs_gen"))
1142 .nodes(vec![ir::Node::Enum(
1143 ir::Enum::builder()
1144 .ident(ir::QualifiedIdent::parse_str("fbs_gen.AorBType"))
1145 .base_type(ir::EnumBaseType::UByte)
1146 .variants(vec![
1147 ir::EnumVariant::builder()
1148 .ident(ir::Ident::from("None"))
1149 .value(Some(0.into()))
1150 .build(),
1151 ir::EnumVariant::builder()
1152 .ident(ir::Ident::from("A"))
1153 .build(),
1154 ir::EnumVariant::builder()
1155 .ident(ir::Ident::from("B"))
1156 .build(),
1157 ])
1158 .build(),
1159 )])
1160 .build(),
1161 ),
1162 ir::Node::Table(
1163 ir::Table::builder()
1164 .ident(ir::QualifiedIdent::from("A"))
1165 .fields(vec![ir::Field::builder()
1166 .ident(ir::Ident::from("message"))
1167 .ty(ir::Type::String)
1168 .build()])
1169 .build(),
1170 ),
1171 ir::Node::Table(
1172 ir::Table::builder()
1173 .ident(ir::QualifiedIdent::from("B"))
1174 .fields(vec![ir::Field::builder()
1175 .ident(ir::Ident::from("message"))
1176 .ty(ir::Type::Array(Box::new(ir::Type::UByte)))
1177 .build()])
1178 .build(),
1179 ),
1180 ir::Node::Union(
1181 ir::Union::builder()
1182 .ident(ir::QualifiedIdent::from("AorB"))
1183 .enum_ident(ir::QualifiedIdent::parse_str("fbs_gen.AorBType"))
1184 .variants(vec![
1185 ir::UnionVariant::builder()
1186 .ident(ir::Ident::from("A"))
1187 .ty(ir::Type::Custom(
1188 ir::CustomTypeRef::builder()
1189 .ident(ir::QualifiedIdent::from("A"))
1190 .ty(ir::CustomType::Table)
1191 .build(),
1192 ))
1193 .build(),
1194 ir::UnionVariant::builder()
1195 .ident(ir::Ident::from("B"))
1196 .ty(ir::Type::Custom(
1197 ir::CustomTypeRef::builder()
1198 .ident(ir::QualifiedIdent::from("B"))
1199 .ty(ir::CustomType::Table)
1200 .build(),
1201 ))
1202 .build(),
1203 ])
1204 .build(),
1205 ),
1206 ],
1207 };
1208
1209 assert_eq!(actual, expected);
1210 }
1211
1212 #[test]
1213 fn test_table_using_union() {
1214 let input = "\
1215 table A {
1216 /// A nice message
1217 message: string;
1218 }
1219 table B {
1220 /// A nicer message
1221 message: [ubyte];
1222 }
1223 union AorB {
1224 /// Multiple
1225 /// lines
1226 /// of comments.
1227 A,
1228 /// Comments
1229 B
1230 }
1231 table Z {
1232 a_or_b: AorB;
1233 }
1234 ";
1235 let (_, schema) = crate::parser::schema_decl(input).unwrap();
1236 let actual = Builder::build(schema).unwrap();
1237 let expected = ir::Root {
1238 nodes: vec![
1239 ir::Node::Namespace(
1240 ir::Namespace::builder()
1241 .ident(ir::QualifiedIdent::parse_str("fbs_gen"))
1242 .nodes(vec![ir::Node::Enum(
1243 ir::Enum::builder()
1244 .ident(ir::QualifiedIdent::parse_str("fbs_gen.AorBType"))
1245 .base_type(ir::EnumBaseType::UByte)
1246 .variants(vec![
1247 ir::EnumVariant::builder()
1248 .ident(ir::Ident::from("None"))
1249 .value(Some(0.into()))
1250 .build(),
1251 ir::EnumVariant::builder()
1252 .ident(ir::Ident::from("A"))
1253 .doc(vec![" Multiple", " lines", " of comments."].into())
1254 .build(),
1255 ir::EnumVariant::builder()
1256 .ident(ir::Ident::from("B"))
1257 .doc(vec![" Comments"].into())
1258 .build(),
1259 ])
1260 .build(),
1261 )])
1262 .build(),
1263 ),
1264 ir::Node::Table(
1265 ir::Table::builder()
1266 .ident(ir::QualifiedIdent::from("A"))
1267 .fields(vec![ir::Field::builder()
1268 .ident(ir::Ident::from("message"))
1269 .ty(ir::Type::String)
1270 .doc(vec![" A nice message"].into())
1271 .build()])
1272 .build(),
1273 ),
1274 ir::Node::Table(
1275 ir::Table::builder()
1276 .ident(ir::QualifiedIdent::from("B"))
1277 .fields(vec![ir::Field::builder()
1278 .ident(ir::Ident::from("message"))
1279 .ty(ir::Type::Array(Box::new(ir::Type::UByte)))
1280 .doc(vec![" A nicer message"].into())
1281 .build()])
1282 .build(),
1283 ),
1284 ir::Node::Union(
1285 ir::Union::builder()
1286 .ident(ir::QualifiedIdent::from("AorB"))
1287 .enum_ident(ir::QualifiedIdent::parse_str("fbs_gen.AorBType"))
1288 .variants(vec![
1289 ir::UnionVariant::builder()
1290 .ident(ir::Ident::from("A"))
1291 .ty(ir::Type::Custom(
1292 ir::CustomTypeRef::builder()
1293 .ident(ir::QualifiedIdent::from("A"))
1294 .ty(ir::CustomType::Table)
1295 .build(),
1296 ))
1297 .doc(vec![" Multiple", " lines", " of comments."].into())
1298 .build(),
1299 ir::UnionVariant::builder()
1300 .ident(ir::Ident::from("B"))
1301 .ty(ir::Type::Custom(
1302 ir::CustomTypeRef::builder()
1303 .ident(ir::QualifiedIdent::from("B"))
1304 .ty(ir::CustomType::Table)
1305 .build(),
1306 ))
1307 .doc(vec![" Comments"].into())
1308 .build(),
1309 ])
1310 .build(),
1311 ),
1312 ir::Node::Table(
1313 ir::Table::builder()
1314 .ident(ir::QualifiedIdent::from("Z"))
1315 .fields(vec![
1316 ir::Field::builder()
1318 .ident(ir::Ident::from("a_or_b_type"))
1319 .ty(ir::Type::Custom(
1320 ir::CustomTypeRef::builder()
1321 .ident(ir::QualifiedIdent::parse_str("fbs_gen.AorBType"))
1322 .ty(ir::CustomType::Enum {
1323 variants: vec![
1324 ir::EnumVariant::builder()
1325 .ident(ir::Ident::from("None"))
1326 .value(Some(0.into()))
1327 .build(),
1328 ir::EnumVariant::builder()
1329 .ident(ir::Ident::from("A"))
1330 .doc(
1331 vec![
1332 " Multiple",
1333 " lines",
1334 " of comments.",
1335 ]
1336 .into(),
1337 )
1338 .build(),
1339 ir::EnumVariant::builder()
1340 .ident(ir::Ident::from("B"))
1341 .doc(vec![" Comments"].into())
1342 .build(),
1343 ],
1344 base_type: ir::EnumBaseType::UByte,
1345 })
1346 .build(),
1347 ))
1348 .build(),
1349 ir::Field::builder()
1350 .ident(ir::Ident::from("a_or_b"))
1351 .ty(ir::Type::Custom(
1352 ir::CustomTypeRef::builder()
1353 .ident(ir::QualifiedIdent::from("AorB"))
1354 .ty(ir::CustomType::Union {
1355 enum_ident: ir::QualifiedIdent::parse_str(
1356 "fbs_gen.AorBType",
1357 ),
1358 variants: vec![
1359 ir::UnionVariant {
1360 ty: ir::Type::Custom(
1361 ir::CustomTypeRef::builder()
1362 .ident(ir::QualifiedIdent::from("A"))
1363 .ty(ir::CustomType::Table)
1364 .build(),
1365 ),
1366 ident: ir::Ident::from("A"),
1367 doc: vec![
1368 " Multiple",
1369 " lines",
1370 " of comments.",
1371 ]
1372 .into(),
1373 },
1374 ir::UnionVariant {
1375 ty: ir::Type::Custom(
1376 ir::CustomTypeRef::builder()
1377 .ident(ir::QualifiedIdent::from("B"))
1378 .ty(ir::CustomType::Table)
1379 .build(),
1380 ),
1381 ident: ir::Ident::from("B"),
1382 doc: vec![" Comments"].into(),
1383 },
1384 ],
1385 })
1386 .build(),
1387 ))
1388 .build(),
1389 ])
1390 .build(),
1391 ),
1392 ],
1393 };
1394
1395 assert_eq!(actual, expected);
1396 }
1397
1398 #[test]
1399 fn test_rpc_method() {
1400 let input = "\
1401namespace foo.bar;
1402table HelloMsg {
1403 val: string;
1404}
1405
1406table HelloReply {
1407 message: HelloMsg;
1408}
1409
1410rpc_service Greeter {
1411 SayHello(foo.bar.HelloMsg) : foo.bar.HelloReply;
1412}
1413
1414";
1415 let (_, schema) = crate::parser::schema_decl(input).unwrap();
1416 let actual = Builder::build(schema).unwrap();
1417 let expected = ir::Root {
1418 nodes: vec![ir::Node::Namespace(
1419 ir::Namespace::builder()
1420 .ident(ir::QualifiedIdent::parse_str("foo"))
1421 .nodes(vec![ir::Node::Namespace(
1422 ir::Namespace::builder()
1423 .ident(ir::QualifiedIdent::parse_str("foo.bar"))
1424 .nodes(vec![
1425 ir::Node::Table(
1426 ir::Table::builder()
1427 .ident(ir::QualifiedIdent::parse_str("foo.bar.HelloMsg"))
1428 .root_type(true)
1429 .fields(vec![ir::Field::builder()
1430 .ident(ir::Ident::from("val"))
1431 .ty(ir::Type::String)
1432 .build()])
1433 .build(),
1434 ),
1435 ir::Node::Table(
1436 ir::Table::builder()
1437 .ident(ir::QualifiedIdent::parse_str("foo.bar.HelloReply"))
1438 .root_type(true)
1439 .fields(vec![ir::Field::builder()
1440 .ident(ir::Ident::from("message"))
1441 .ty(ir::Type::Custom(
1442 ir::CustomTypeRef::builder()
1443 .ident(ir::QualifiedIdent::parse_str(
1444 "foo.bar.HelloMsg",
1445 ))
1446 .ty(ir::CustomType::Table)
1447 .build(),
1448 ))
1449 .build()])
1450 .build(),
1451 ),
1452 ir::Node::Rpc(
1453 ir::Rpc::builder()
1454 .ident(ir::QualifiedIdent::parse_str("foo.bar.Greeter"))
1455 .methods(vec![ir::RpcMethod::builder()
1456 .ident("SayHello".into())
1457 .snake_ident("say_hello".into())
1458 .request_type(ir::QualifiedIdent::parse_str(
1459 "foo.bar.HelloMsg",
1460 ))
1461 .response_type(ir::QualifiedIdent::parse_str(
1462 "foo.bar.HelloReply",
1463 ))
1464 .build()])
1465 .build(),
1466 ),
1467 ])
1468 .build(),
1469 )])
1470 .build(),
1471 )],
1472 };
1473
1474 assert_eq!(actual, expected);
1475 }
1476}