pilota_thrift_parser/parser/
struct_.rs

1use chumsky::prelude::*;
2use faststr::FastStr;
3
4use super::super::{
5    descriptor::{Exception, Struct, StructLike, Union},
6    parser::*,
7};
8use crate::{Annotation, Field, Ident};
9
10impl Struct {
11    pub fn get_parser<'a>() -> impl Parser<'a, &'a str, Struct, extra::Err<Rich<'a, char>>> {
12        Components::comment()
13            .repeated()
14            .collect::<Vec<_>>()
15            .then_ignore(Components::blank().or_not())
16            .then_ignore(just("struct"))
17            .then_ignore(Components::blank())
18            .then(StructLike::parse())
19            .then(Components::trailing_comment().or_not())
20            .then_ignore(Components::blank().or_not())
21            .map(|((comments, struct_like), trailing_comments)| {
22                let leading_comments = FastStr::from(format!(
23                    "{}\n\n{}",
24                    comments.join("\n\n"),
25                    struct_like.comments
26                ));
27                Struct {
28                    leading_comments,
29                    struct_like,
30                    trailing_comments: trailing_comments.unwrap_or_default(),
31                }
32            })
33    }
34}
35
36impl Union {
37    pub fn parse<'a>() -> impl Parser<'a, &'a str, Union, extra::Err<Rich<'a, char>>> {
38        Components::comment()
39            .repeated()
40            .collect::<Vec<_>>()
41            .then_ignore(Components::blank().or_not())
42            .then_ignore(just("union"))
43            .then_ignore(Components::blank())
44            .then(StructLike::parse())
45            .then(Components::trailing_comment().or_not())
46            .then_ignore(Components::blank().or_not())
47            .map(|((comments, struct_like), trailing_comments)| Union {
48                leading_comments: FastStr::from(comments.join("\n\n")),
49                struct_like,
50                trailing_comments: trailing_comments.unwrap_or_default(),
51            })
52    }
53}
54
55impl Exception {
56    pub fn parse<'a>() -> impl Parser<'a, &'a str, Exception, extra::Err<Rich<'a, char>>> {
57        Components::comment()
58            .repeated()
59            .collect::<Vec<_>>()
60            .then_ignore(Components::blank().or_not())
61            .then_ignore(just("exception"))
62            .then_ignore(Components::blank())
63            .then(StructLike::parse())
64            .then(Components::trailing_comment().or_not())
65            .then_ignore(Components::blank().or_not())
66            .map(|((comments, struct_like), trailing_comments)| Exception {
67                leading_comments: FastStr::from(comments.join("\n\n")),
68                struct_like,
69                trailing_comments: trailing_comments.unwrap_or_default(),
70            })
71    }
72}
73
74impl StructLike {
75    pub fn parse<'a>() -> impl Parser<'a, &'a str, StructLike, extra::Err<Rich<'a, char>>> {
76        Ident::get_parser()
77            .then(Components::comment().repeated().collect::<Vec<_>>())
78            .then_ignore(Components::blank().or_not())
79            .then_ignore(just("{"))
80            .then(
81                Components::blank()
82                    .ignore_then(Field::get_parser())
83                    .repeated()
84                    .collect::<Vec<_>>(),
85            )
86            .then(Components::comment().repeated().collect::<Vec<_>>())
87            .then_ignore(Components::blank().or_not())
88            .then_ignore(just("}"))
89            .then(Annotation::get_parser().or_not())
90            .then_ignore(Components::list_separator().or_not())
91            .map(
92                |((((name, name_comments), fields), comments), annotations)| StructLike {
93                    name: Ident(name.into()),
94                    fields,
95                    annotations: annotations.unwrap_or_default(),
96                    comments: FastStr::from(format!(
97                        "{}\n\n{}",
98                        name_comments.join("\n\n"),
99                        comments.join("\n\n")
100                    )),
101                },
102            )
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109
110    #[test]
111    fn test_struct() {
112        let str = r#"struct MGetRequest {
113            1: set<i64> Ids (go.tag = "json:\"Ids\" split:\"type=tenant\""),
114            2: optional set<ExtendField> extendFields,
115
116
117            3: optional set<ExtendField>extendFields2,
118        
119            255: base.Base Base,
120        }
121        "#;
122
123        Struct::get_parser().parse(str).unwrap();
124    }
125
126    #[test]
127    fn test_struct3() {
128        let str = r#"struct TestComment {
129            // 1
130        }
131        "#;
132        Struct::get_parser().parse(str).unwrap();
133    }
134
135    #[test]
136    fn test_tag() {
137        let str = r#"
138        struct ImMsgContent {
139            1: string user_id (go.tag = 'json:\"user_id,omitempty\"'),
140            2: string __files (go.tag = 'json:\"__files,omitempty\"'),
141        }"#;
142        Struct::get_parser().parse(str).unwrap();
143    }
144
145    #[test]
146    fn test_ty() {
147        let str = r#"struct Test {
148            1: required string(pilota.annotation="test") Service,      // required service
149            2: required bytet_i.Injection Injection,
150        }"#;
151        Struct::get_parser().parse(str).unwrap();
152    }
153
154    #[test]
155    fn test_struct_comment() {
156        let input = r#"
157        /* comment */ struct Test {
158            /* comment */ 1: /* comment */ required /* comment */ string /* comment */ Service, // comment
159        }"#;
160        let _ = Struct::get_parser().parse(input).unwrap();
161    }
162}