Skip to main content

sails_idl_parser/ast/
mod.rs

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