Skip to main content

fbs_build/ir/
transform.rs

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// Utility struct to help build and stack namespaces
28#[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        // Has this type been referenced before?
91        // Let's find out and update the status.
92        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        // We consider the table declared even if all its fields haven't been
99        // properly resolved, because tables may be recursive.
100        self.types
101            .insert(ident.clone(), CustomTypeStatus::TableDeclared);
102
103        let mut fields = Vec::new();
104
105        // Make sure all references to custom types are resolved
106        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 it's a union, push the enum type field first
118                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                // Use `Some` explicit or implicit default value or `None`
136                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                // not satisfied yet, so we can't build the fields
149                // we will try after having seen all other types.
150                return Ok(false);
151            }
152        }
153
154        let ident = ident.clone();
155        let root_type = false; // Set as non-root by default
156        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        // Structs go straight to `defined` state; they can't be referred to
184        // otherise, because they may be invalid.
185
186        let mut fields = Vec::new();
187
188        // Make sure all references to custom types are resolved
189        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                // not satisfied yet, so we can't build the fields
212                // we will try after having seen all other types.
213                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        // Make sure all references to custom types are resolved
288        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                // not satisfied yet, so we can't build the fields
299                // we will try after having seen all other types.
300                return Ok(false);
301            }
302        }
303
304        let doc = u.doc.clone();
305
306        // We generate an enum for this union which describes the different variants
307        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        // There's a max of 255 variants in a union in flatbuffers
341        // See https://github.com/google/flatbuffers/issues/4209
342        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                // Request & Response types have not been defined yet, push back to queue
407                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            // We need to prefix the namespace
477            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; // unsatisfied inner type
512                }
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, // Type unsatisfied
531        };
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        // Keep track of the definition order,
540        // mostly to build IR deterministically
541        let mut input: VecDeque<_> = schema
542            .elements
543            .into_iter()
544            .enumerate()
545            .map(|(i, el)| IndexedElement(i, el))
546            .collect();
547
548        // Loop until all types can be defined
549        // or stop if we make no progress
550        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; // everything is satisfied
560            } else {
561                // Didn't we progress?
562                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                    // TODO proper reporting
569                }
570            }
571            input = std::mem::take(&mut unsatisfied);
572        }
573
574        let nodes = context.nodes;
575        let root_types = &context.root_types;
576
577        // mark tables that are root types
578        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        // Add children to namespace
607        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        // Build top-level with namespaces and elements without namespaces
614        let mut nodes = BTreeMap::new();
615        // stack namespaces
616        for ns in namespaces.into_iter() {
617            let mut parent = &mut nodes;
618
619            // Handle intermediate levels
620            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            // Add leaf
629            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        // Are there elements without namespace?
639        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                            // Synthetic union enum type!
1317                            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}