sails_idl_parser/ast/
mod.rs

1use std::collections::HashSet;
2use thiserror::Error;
3
4use crate::{
5    grammar::ProgramParser,
6    lexer::{Lexer, LexicalError},
7};
8
9pub mod visitor;
10
11#[derive(Error, Debug, PartialEq)]
12pub enum ParseError {
13    #[error(transparent)]
14    Lexical(#[from] LexicalError),
15    #[error("duplicate type `{0}`")]
16    DuplicateType(String),
17    #[error("duplicate ctor `{0}`")]
18    DuplicateCtor(String),
19    #[error("duplicate service `{0}`")]
20    DuplicateService(String),
21    #[error("duplicate service method `{method}` in service `{service}`")]
22    DuplicateServiceMethod { method: String, service: String },
23    #[error("duplicate variant `{0}`")]
24    DuplicateEnumVariant(String),
25    #[error("duplicate field `{0}`")]
26    DuplicateStructField(String),
27    #[error("struct has mixed named and unnamed fields")]
28    StructMixedFields,
29    #[error("parse error: `{0}`")]
30    Other(String),
31}
32
33pub fn parse_idl(idl: &str) -> Result<Program, ParseError> {
34    let lexer = Lexer::new(idl);
35    let parser = ProgramParser::new();
36
37    let program = parser.parse(lexer).map_err(|e| match e {
38        lalrpop_util::ParseError::User { error } => error,
39        _ => ParseError::Other(e.to_string()),
40    })?;
41
42    Ok(program)
43}
44
45type ParseResult<T> = Result<T, ParseError>;
46
47/// A structure describing program
48#[derive(Debug, PartialEq, Clone)]
49pub struct Program {
50    ctor: Option<Ctor>,
51    services: Vec<Service>,
52    types: Vec<Type>,
53}
54
55impl Program {
56    pub(crate) fn new(
57        ctor: Option<Ctor>,
58        services: Vec<Service>,
59        types: Vec<Type>,
60    ) -> ParseResult<Self> {
61        let mut seen_types = HashSet::new();
62        for t in &types {
63            if !seen_types.insert(t.name().to_lowercase()) {
64                return Err(ParseError::DuplicateType(t.name().to_string()));
65            }
66        }
67
68        let mut seen_services = HashSet::new();
69        for s in &services {
70            if !seen_services.insert(s.name().to_lowercase()) {
71                return Err(ParseError::DuplicateService(s.name().to_string()));
72            }
73        }
74
75        Ok(Self {
76            ctor,
77            services,
78            types,
79        })
80    }
81
82    pub fn ctor(&self) -> Option<&Ctor> {
83        self.ctor.as_ref()
84    }
85
86    pub fn services(&self) -> &[Service] {
87        &self.services
88    }
89
90    pub fn types(&self) -> &[Type] {
91        &self.types
92    }
93}
94
95/// A structure describing program constructor
96#[derive(Debug, PartialEq, Clone)]
97pub struct Ctor {
98    funcs: Vec<CtorFunc>,
99}
100
101impl Ctor {
102    pub(crate) fn new(funcs: Vec<CtorFunc>) -> ParseResult<Self> {
103        let mut seen = HashSet::new();
104        for f in &funcs {
105            if !seen.insert(f.name().to_lowercase()) {
106                return Err(ParseError::DuplicateCtor(f.name().to_string()));
107            }
108        }
109        Ok(Self { funcs })
110    }
111
112    pub fn funcs(&self) -> &[CtorFunc] {
113        &self.funcs
114    }
115}
116
117/// A structure describing one of program constructor functions
118#[derive(Debug, PartialEq, Clone)]
119pub struct CtorFunc {
120    name: String,
121    params: Vec<FuncParam>,
122    docs: Vec<String>,
123}
124
125impl CtorFunc {
126    pub(crate) fn new(name: String, params: Vec<FuncParam>, docs: Vec<String>) -> Self {
127        Self { name, params, docs }
128    }
129
130    pub fn name(&self) -> &str {
131        &self.name
132    }
133
134    pub fn params(&self) -> &[FuncParam] {
135        &self.params
136    }
137
138    pub fn docs(&self) -> &Vec<String> {
139        &self.docs
140    }
141}
142
143/// A structure describing one of program services
144#[derive(Debug, PartialEq, Clone)]
145pub struct Service {
146    name: String,
147    funcs: Vec<ServiceFunc>,
148    events: Vec<ServiceEvent>,
149}
150
151impl Service {
152    pub(crate) fn new(
153        name: String,
154        funcs: Vec<ServiceFunc>,
155        events: Vec<ServiceEvent>,
156    ) -> ParseResult<Self> {
157        let mut seen = HashSet::new();
158        for f in &funcs {
159            if !seen.insert(f.name().to_lowercase()) {
160                return Err(ParseError::DuplicateServiceMethod {
161                    method: f.name().to_string(),
162                    service: name.to_string(),
163                });
164            }
165        }
166
167        Ok(Self {
168            name,
169            funcs,
170            events,
171        })
172    }
173
174    pub fn name(&self) -> &str {
175        self.name.as_str()
176    }
177
178    pub fn funcs(&self) -> &[ServiceFunc] {
179        &self.funcs
180    }
181
182    pub fn events(&self) -> &[ServiceEvent] {
183        &self.events
184    }
185}
186
187/// A structure describing one of service functions
188#[derive(Debug, PartialEq, Clone)]
189pub struct ServiceFunc {
190    name: String,
191    params: Vec<FuncParam>,
192    output: TypeDecl,
193    is_query: bool,
194    docs: Vec<String>,
195}
196
197impl ServiceFunc {
198    pub(crate) fn new(
199        name: String,
200        params: Vec<FuncParam>,
201        output: TypeDecl,
202        is_query: bool,
203        docs: Vec<String>,
204    ) -> Self {
205        Self {
206            name,
207            params,
208            output,
209            is_query,
210            docs,
211        }
212    }
213
214    pub fn name(&self) -> &str {
215        &self.name
216    }
217
218    pub fn params(&self) -> &[FuncParam] {
219        &self.params
220    }
221
222    pub fn output(&self) -> &TypeDecl {
223        &self.output
224    }
225
226    pub fn is_query(&self) -> bool {
227        self.is_query
228    }
229
230    pub fn docs(&self) -> &Vec<String> {
231        &self.docs
232    }
233}
234
235/// A structure describing one of service events
236pub type ServiceEvent = EnumVariant;
237
238#[derive(Debug, PartialEq, Clone)]
239pub struct FuncParam {
240    name: String,
241    type_decl: TypeDecl,
242}
243
244impl FuncParam {
245    pub(crate) fn new(name: String, type_decl: TypeDecl) -> Self {
246        Self { name, type_decl }
247    }
248
249    pub fn name(&self) -> &str {
250        &self.name
251    }
252
253    pub fn type_decl(&self) -> &TypeDecl {
254        &self.type_decl
255    }
256}
257
258#[derive(Debug, PartialEq, Clone)]
259pub struct Type {
260    name: String,
261    def: TypeDef,
262    docs: Vec<String>,
263}
264
265impl Type {
266    pub(crate) fn new(name: String, def: TypeDef, docs: Vec<String>) -> Self {
267        Self { name, def, docs }
268    }
269
270    pub fn name(&self) -> &str {
271        &self.name
272    }
273
274    pub fn def(&self) -> &TypeDef {
275        &self.def
276    }
277
278    pub fn docs(&self) -> &Vec<String> {
279        &self.docs
280    }
281}
282
283#[derive(Debug, PartialEq, Clone)]
284pub enum TypeDecl {
285    Vector(Box<TypeDecl>),
286    Array {
287        item: Box<TypeDecl>,
288        len: u32,
289    },
290    Map {
291        key: Box<TypeDecl>,
292        value: Box<TypeDecl>,
293    },
294    Optional(Box<TypeDecl>),
295    Result {
296        ok: Box<TypeDecl>,
297        err: Box<TypeDecl>,
298    },
299    Id(TypeId),
300    Def(TypeDef),
301}
302
303#[derive(Debug, PartialEq, Clone)]
304pub enum TypeId {
305    Primitive(PrimitiveType),
306    UserDefined(String),
307}
308
309#[derive(Debug, PartialEq, Clone, Copy)]
310#[repr(u8)]
311pub enum PrimitiveType {
312    Null,
313    Bool,
314    Char,
315    Str,
316    U8,
317    U16,
318    U32,
319    U64,
320    U128,
321    I8,
322    I16,
323    I32,
324    I64,
325    I128,
326    ActorId,
327    CodeId,
328    MessageId,
329    H256,
330    U256,
331    H160,
332    NonZeroU8,
333    NonZeroU16,
334    NonZeroU32,
335    NonZeroU64,
336    NonZeroU128,
337    NonZeroU256,
338}
339
340impl PrimitiveType {
341    pub(crate) fn str_to_enum(str: &str) -> Option<Self> {
342        match str {
343            "bool" => Some(PrimitiveType::Bool),
344            "char" => Some(PrimitiveType::Char),
345            "str" => Some(PrimitiveType::Str),
346            "u8" => Some(PrimitiveType::U8),
347            "u16" => Some(PrimitiveType::U16),
348            "u32" => Some(PrimitiveType::U32),
349            "u64" => Some(PrimitiveType::U64),
350            "u128" => Some(PrimitiveType::U128),
351            "i8" => Some(PrimitiveType::I8),
352            "i16" => Some(PrimitiveType::I16),
353            "i32" => Some(PrimitiveType::I32),
354            "i64" => Some(PrimitiveType::I64),
355            "i128" => Some(PrimitiveType::I128),
356            "h160" => Some(PrimitiveType::H160),
357            "h256" => Some(PrimitiveType::H256),
358            "u256" => Some(PrimitiveType::U256),
359            "nat8" => Some(PrimitiveType::NonZeroU8),
360            "nat16" => Some(PrimitiveType::NonZeroU16),
361            "nat32" => Some(PrimitiveType::NonZeroU32),
362            "nat64" => Some(PrimitiveType::NonZeroU64),
363            "nat128" => Some(PrimitiveType::NonZeroU128),
364            "nat256" => Some(PrimitiveType::NonZeroU256),
365            "actor_id" => Some(PrimitiveType::ActorId),
366            "code_id" => Some(PrimitiveType::CodeId),
367            "message_id" => Some(PrimitiveType::MessageId),
368            _ => None,
369        }
370    }
371}
372
373#[derive(Debug, PartialEq, Clone)]
374pub enum TypeDef {
375    Struct(StructDef),
376    Enum(EnumDef),
377}
378
379#[derive(Debug, PartialEq, Clone)]
380pub struct StructDef {
381    fields: Vec<StructField>,
382}
383
384impl StructDef {
385    pub(crate) fn new(fields: Vec<StructField>) -> ParseResult<Self> {
386        // check if all fields are named or unnamed
387        let all_unnamed = fields.iter().all(|f| f.name().is_none());
388        let all_named = fields.iter().all(|f| f.name().is_some());
389
390        if !all_unnamed && !all_named {
391            return Err(ParseError::StructMixedFields);
392        }
393
394        let mut seen = HashSet::new();
395
396        if all_named {
397            for f in &fields {
398                let name = f.name().unwrap();
399                if !seen.insert(name.to_lowercase()) {
400                    return Err(ParseError::DuplicateStructField(name.to_string()));
401                }
402            }
403        }
404
405        Ok(Self { fields })
406    }
407
408    pub fn fields(&self) -> &[StructField] {
409        &self.fields
410    }
411}
412
413#[derive(Debug, PartialEq, Clone)]
414pub struct StructField {
415    name: Option<String>,
416    type_decl: TypeDecl,
417    docs: Vec<String>,
418}
419
420impl StructField {
421    pub(crate) fn new(name: Option<String>, type_decl: TypeDecl, docs: Vec<String>) -> Self {
422        Self {
423            name,
424            type_decl,
425            docs,
426        }
427    }
428
429    pub fn name(&self) -> Option<&str> {
430        self.name.as_deref()
431    }
432
433    pub fn type_decl(&self) -> &TypeDecl {
434        &self.type_decl
435    }
436
437    pub fn docs(&self) -> &Vec<String> {
438        &self.docs
439    }
440}
441
442#[derive(Debug, PartialEq, Clone)]
443pub struct EnumDef {
444    variants: Vec<EnumVariant>,
445}
446
447impl EnumDef {
448    pub(crate) fn new(variants: Vec<EnumVariant>) -> ParseResult<Self> {
449        let mut seen = HashSet::new();
450        for v in &variants {
451            if !seen.insert(v.name().to_lowercase()) {
452                return Err(ParseError::DuplicateEnumVariant(v.name().to_string()));
453            }
454        }
455        Ok(Self { variants })
456    }
457
458    pub fn variants(&self) -> &[EnumVariant] {
459        &self.variants
460    }
461}
462
463#[derive(Debug, PartialEq, Clone)]
464pub struct EnumVariant {
465    name: String,
466    type_decl: Option<TypeDecl>,
467    docs: Vec<String>,
468}
469
470impl EnumVariant {
471    pub(crate) fn new(name: String, type_decl: Option<TypeDecl>, docs: Vec<String>) -> Self {
472        Self {
473            name,
474            type_decl,
475            docs,
476        }
477    }
478
479    pub fn name(&self) -> &str {
480        &self.name
481    }
482
483    pub fn type_decl(&self) -> Option<&TypeDecl> {
484        self.type_decl.as_ref()
485    }
486
487    pub fn docs(&self) -> &Vec<String> {
488        &self.docs
489    }
490}
491
492#[cfg(test)]
493mod tests {
494    use super::*;
495
496    #[test]
497    fn parser_works() {
498        let program_idl = r"
499          type ThisThatSvcAppTupleStruct = struct {
500            bool,
501          };
502
503          type ThisThatSvcAppDoThatParam = struct {
504            p1: u32,
505            p2: str,
506            p3: ThisThatSvcAppManyVariants,
507          };
508
509          type ThisThatSvcAppManyVariants = enum {
510            One,
511            Two: u32,
512            Three: opt u256,
513            Four: struct { a: u32, b: opt u16 },
514            Five: struct { str, h256 },
515            Six: struct { u32 },
516            Seven: [map (u32, str), 10],
517            Eight: actor_id,
518          };
519
520          constructor {
521            New : (p1: u32);
522          };
523
524          service {
525            DoThis : (p1: u32, p2: str, p3: struct { opt str, u8 }, p4: ThisThatSvcAppTupleStruct) -> struct { str, u32 };
526            DoThat : (param: ThisThatSvcAppDoThatParam) -> result (struct { str, u32 }, struct { str });
527            query This : (v1: vec u16) -> u32;
528            query That : (v1: null) -> result (str, str);
529
530            events {
531                ThisDone;
532                ThatDone: u32;
533                SomethingHappened: struct { str, u32 };
534                SomethingDone: ThisThatSvcAppManyVariants;
535            }
536          };
537        ";
538
539        let program = parse_idl(program_idl).unwrap();
540
541        assert_eq!(program.types().len(), 3);
542        assert_eq!(program.ctor().unwrap().funcs().len(), 1);
543        assert_eq!(program.services().len(), 1);
544        assert_eq!(program.services()[0].funcs().len(), 4);
545        assert_eq!(program.services()[0].events().len(), 4);
546
547        //println!("ast: {:#?}", program);
548    }
549
550    #[test]
551    fn parser_accepts_types_service() {
552        let program_idl = r"
553          type T = enum { One };
554          service {}
555        ";
556
557        let program = parse_idl(program_idl).unwrap();
558
559        assert_eq!(program.types().len(), 1);
560        assert_eq!(program.services().len(), 1);
561        assert_eq!(program.services()[0].funcs().len(), 0);
562    }
563
564    #[test]
565    fn parser_accepts_ctor_service() {
566        let program_idl = r"
567          constructor {};
568          service {}
569        ";
570
571        let program = parse_idl(program_idl).unwrap();
572
573        assert_eq!(program.ctor().unwrap().funcs().len(), 0);
574        assert_eq!(program.services().len(), 1);
575        assert_eq!(program.services()[0].funcs().len(), 0);
576    }
577
578    #[test]
579    fn parser_accepts_multiple_services() {
580        let program_idl = r"
581          service {};
582          service SomeService {};
583        ";
584
585        let program = parse_idl(program_idl).unwrap();
586
587        assert_eq!(program.services().len(), 2);
588        assert_eq!(program.services()[0].name(), "");
589        assert_eq!(program.services()[1].name(), "SomeService");
590    }
591
592    #[test]
593    fn parser_accepts_types_ctor_service() {
594        let program_idl = r"
595          type T = enum { One };
596          constructor {};
597          service {}
598        ";
599
600        let program = parse_idl(program_idl).unwrap();
601
602        assert_eq!(program.types().len(), 1);
603        assert_eq!(program.ctor().unwrap().funcs().len(), 0);
604        assert_eq!(program.services().len(), 1);
605        assert_eq!(program.services()[0].funcs().len(), 0);
606    }
607
608    #[test]
609    fn parser_requires_semicolon_between_types_and_service() {
610        let program_idl = r"
611          type T = enum { One }
612          service {}
613        ";
614
615        let program = parse_idl(program_idl);
616
617        assert!(program.is_err());
618    }
619
620    #[test]
621    fn parser_recognizes_builtin_types_as_primitives() {
622        let program_idl = r"
623            service {
624                DoThis : (p1: actor_id, p2: code_id, p3: message_id, p4: h256, p5: u256, p6: h160) -> null;
625            }
626        ";
627
628        let program = parse_idl(program_idl).unwrap();
629
630        assert_eq!(program.services().len(), 1);
631        program.services()[0]
632            .funcs()
633            .first()
634            .unwrap()
635            .params()
636            .iter()
637            .for_each(|p| match p.type_decl() {
638                TypeDecl::Id(TypeId::Primitive(PrimitiveType::ActorId)) => {
639                    assert_eq!(p.name(), "p1");
640                }
641                TypeDecl::Id(TypeId::Primitive(PrimitiveType::CodeId)) => {
642                    assert_eq!(p.name(), "p2");
643                }
644                TypeDecl::Id(TypeId::Primitive(PrimitiveType::MessageId)) => {
645                    assert_eq!(p.name(), "p3");
646                }
647                TypeDecl::Id(TypeId::Primitive(PrimitiveType::H256)) => {
648                    assert_eq!(p.name(), "p4");
649                }
650                TypeDecl::Id(TypeId::Primitive(PrimitiveType::U256)) => {
651                    assert_eq!(p.name(), "p5");
652                }
653                TypeDecl::Id(TypeId::Primitive(PrimitiveType::H160)) => {
654                    assert_eq!(p.name(), "p6");
655                }
656                _ => panic!("unexpected type"),
657            });
658    }
659
660    #[test]
661    fn parser_rejects_duplicate_names() {
662        let program_idl = r"
663          type A = enum { One };
664          type A = enum { Two };
665          service {};
666        ";
667
668        let err = parse_idl(program_idl).unwrap_err();
669
670        assert_eq!(err, ParseError::DuplicateType("A".to_owned()));
671    }
672
673    #[test]
674    fn parser_rejects_duplicate_unnamed_services() {
675        let program_idl = r"
676          service {};
677          service {};
678        ";
679
680        let err = parse_idl(program_idl).unwrap_err();
681
682        assert_eq!(err, ParseError::DuplicateService("".to_owned()));
683    }
684
685    #[test]
686    fn parser_rejects_duplicate_named_services() {
687        let program_idl = r"
688          service A {};
689          service B {};
690          service A {};
691          service {};
692        ";
693
694        let err = parse_idl(program_idl).unwrap_err();
695
696        assert_eq!(err, ParseError::DuplicateService("A".to_owned()));
697    }
698
699    #[test]
700    fn parser_rejects_duplicate_service_methods() {
701        let program_idl = r"
702          service {
703            DoTHIS : () -> null;
704            DoThis : () -> null;
705          };
706        ";
707
708        let err = parse_idl(program_idl).unwrap_err();
709
710        assert_eq!(
711            err,
712            ParseError::DuplicateServiceMethod {
713                method: "DoThis".to_owned(),
714                service: "".to_owned()
715            }
716        );
717    }
718
719    #[test]
720    fn parser_rejects_duplicate_ctor_funcs() {
721        let program_idl = r"
722          constructor {
723            New : ();
724            new : ();
725          };
726        ";
727
728        let err = parse_idl(program_idl).unwrap_err();
729
730        assert_eq!(err, ParseError::DuplicateCtor("new".to_owned()));
731    }
732
733    #[test]
734    fn parser_rejects_duplicate_enum_variants() {
735        let program_idl = r"
736          type T = enum { One, One };
737        ";
738
739        let err = parse_idl(program_idl).unwrap_err();
740
741        assert_eq!(err, ParseError::DuplicateEnumVariant("One".to_owned()));
742    }
743
744    #[test]
745    fn parser_rejects_duplicate_struct_fields() {
746        let program_idl = r"
747          type T = struct {
748            a: u32,
749            a: u32,
750          };
751        ";
752
753        let err = parse_idl(program_idl).unwrap_err();
754
755        assert_eq!(err, ParseError::DuplicateStructField("a".to_owned()));
756    }
757
758    #[test]
759    fn parser_rejects_mixed_named_unnamed_struct_fields() {
760        let program_idl = r"
761          type T = struct {
762            a: u32,
763            u32,
764          };
765        ";
766
767        let err = parse_idl(program_idl).unwrap_err();
768
769        assert_eq!(err, ParseError::StructMixedFields);
770    }
771
772    #[test]
773    fn parser_accepts_struct_field_reserved_keywords() {
774        const IDL: &str = r#"
775        type MyStruct = struct {
776            query: u8,
777            result: u8,
778        };
779        "#;
780
781        let expected = TypeDef::Struct(
782            StructDef::new(vec![
783                StructField::new(
784                    Some("query".to_owned()),
785                    TypeDecl::Id(TypeId::Primitive(PrimitiveType::U8)),
786                    vec![],
787                ),
788                StructField::new(
789                    Some("result".to_owned()),
790                    TypeDecl::Id(TypeId::Primitive(PrimitiveType::U8)),
791                    vec![],
792                ),
793            ])
794            .unwrap(),
795        );
796
797        // act
798        let program = parse_idl(IDL).unwrap();
799
800        // assert
801        let my_struct = program
802            .types()
803            .iter()
804            .find(|t| t.name() == "MyStruct")
805            .unwrap();
806        assert_eq!(&expected, my_struct.def());
807    }
808
809    #[test]
810    fn parser_accepts_func_param_reserved_keywords() {
811        const IDL: &str = r#"
812            service {
813                /// DoThis comment
814                DoThis : (constructor: u8, service: u8, events: vec u8) -> null;
815            }
816        "#;
817
818        let expected = Service::new(
819            "".to_owned(),
820            vec![ServiceFunc::new(
821                "DoThis".to_owned(),
822                vec![
823                    FuncParam::new(
824                        "constructor".to_owned(),
825                        TypeDecl::Id(TypeId::Primitive(PrimitiveType::U8)),
826                    ),
827                    FuncParam::new(
828                        "service".to_owned(),
829                        TypeDecl::Id(TypeId::Primitive(PrimitiveType::U8)),
830                    ),
831                    FuncParam::new(
832                        "events".to_owned(),
833                        TypeDecl::Vector(Box::new(TypeDecl::Id(TypeId::Primitive(
834                            PrimitiveType::U8,
835                        )))),
836                    ),
837                ],
838                TypeDecl::Id(TypeId::Primitive(PrimitiveType::Null)),
839                false,
840                vec!["DoThis comment".to_owned()],
841            )],
842            vec![],
843        )
844        .unwrap();
845
846        // act
847        let program = parse_idl(IDL).unwrap();
848
849        // assert
850        let my_service = program.services().iter().find(|t| t.name() == "").unwrap();
851        assert_eq!(&expected, my_service);
852    }
853
854    #[test]
855    fn parser_accepts_nonzero_primitives() {
856        const IDL: &str = r#"
857        type MyStruct = struct {
858            /// field `query`
859            query: nat32,
860            data: nat256,
861            /// field `result`
862            /// second line
863            result: nat8
864        };
865        "#;
866
867        let expected = TypeDef::Struct(
868            StructDef::new(vec![
869                StructField::new(
870                    Some("query".to_owned()),
871                    TypeDecl::Id(TypeId::Primitive(PrimitiveType::NonZeroU32)),
872                    vec!["field `query`".into()],
873                ),
874                StructField::new(
875                    Some("data".to_owned()),
876                    TypeDecl::Id(TypeId::Primitive(PrimitiveType::NonZeroU256)),
877                    vec![],
878                ),
879                StructField::new(
880                    Some("result".to_owned()),
881                    TypeDecl::Id(TypeId::Primitive(PrimitiveType::NonZeroU8)),
882                    vec!["field `result`".into(), "second line".into()],
883                ),
884            ])
885            .unwrap(),
886        );
887
888        // act
889        let program = parse_idl(IDL).unwrap();
890
891        // assert
892        let my_struct = program
893            .types()
894            .iter()
895            .find(|t| t.name() == "MyStruct")
896            .unwrap();
897        assert_eq!(&expected, my_struct.def());
898    }
899}