pilota_thrift_parser/parser/
function.rs

1use chumsky::prelude::*;
2use faststr::FastStr;
3
4use super::super::{
5    descriptor::{Attribute, Function},
6    parser::*,
7};
8use crate::{Annotation, Field, Type};
9
10impl Function {
11    pub fn get_parser<'a>() -> impl Parser<'a, &'a str, Function, extra::Err<Rich<'a, char>>> {
12        let fields = Field::get_parser()
13            .padded_by(Components::blank().or_not())
14            .repeated()
15            .at_least(1)
16            .collect::<Vec<_>>()
17            .boxed();
18
19        let throws = Components::blank()
20            .or_not()
21            .ignore_then(just("throws"))
22            .ignore_then(Components::blank().or_not())
23            .ignore_then(just("("))
24            .ignore_then(fields.clone())
25            .then_ignore(Components::blank().or_not())
26            .then_ignore(just(")"));
27
28        Components::comment()
29            .repeated()
30            .collect::<Vec<_>>()
31            .then_ignore(Components::blank().or_not())
32            .then(just("oneway").then_ignore(Components::blank()).or_not())
33            .then(Type::get_parser())
34            .then_ignore(Components::blank())
35            .then(Ident::get_parser())
36            .then_ignore(just("(").padded_by(Components::blank().or_not()))
37            .then(fields.clone().or_not())
38            .then_ignore(Components::blank_with_comments().or_not())
39            .then_ignore(just(")"))
40            .then(throws.or_not())
41            .then(Annotation::get_parser().or_not())
42            .then_ignore(Components::list_separator().or_not())
43            .then(Components::trailing_comment().or_not())
44            .then_ignore(Components::blank().or_not())
45            .map(
46                |(
47                    ((((((comments, oneway), r#type), name), arguments), throws), annotations),
48                    trailing_comments,
49                )| {
50                    let ow = oneway.is_some();
51                    let mut args = arguments.unwrap_or_default();
52                    args.iter_mut().for_each(|f| {
53                        if f.attribute == Attribute::Default {
54                            f.attribute = Attribute::Required
55                        }
56                    });
57                    Function {
58                        leading_comments: FastStr::from(comments.join("\n\n")),
59                        name: Ident(name.into()),
60                        oneway: ow,
61                        result_type: r#type,
62                        arguments: args,
63                        throws: throws.unwrap_or_default(),
64                        annotations: annotations.unwrap_or_default(),
65                        trailing_comments: trailing_comments.unwrap_or_default(),
66                    }
67                },
68            )
69    }
70}
71
72#[cfg(test)]
73mod tests {
74
75    use super::*;
76
77    #[test]
78    fn test_func() {
79        let _f = Function::get_parser()
80            .parse(
81                r#"map<i64, shared.ProcessingStatus> processUserData(
82                            1: required list<UserProfile> profiles,
83                            2: optional map<string, string(go.tag='json:"config_value"')> config = {"timeout": "10s", "retries": "3"},
84                            3: i32(some.annotation = "for_i32_type") executionPriority = 1
85                        ) throws (1: ServiceException ex),"#
86            )
87            .unwrap();
88    }
89
90    #[test]
91    fn test_func2() {
92        let _f = Function::get_parser()
93            .parse(
94                r#"oneway void pingServer(
95                            1: required string(go.tag = 'json:"source_service"') source,
96                            2: optional list<map<i64 /* comment */ , set<double>>> nestedDataPoints /* comment */
97                        ) throws (1: ServiceException ex /* comment */) (api.version = "2.5", deprecated = "false")"#,
98            )
99            .unwrap();
100    }
101
102    #[test]
103    fn test_func3() {
104        let _f = Function::get_parser()
105            .parse(r#"Err test_enum_var_type_name_conflict (1: Request req);"#)
106            .unwrap();
107    }
108
109    #[test]
110    fn test_func_comment() {
111        let _f = Function::get_parser()
112            .parse(r#"// comment
113                        void pingServer( /* comment */
114                            1: required /* comment */ string /* comment */ (go.tag = 'json:"source_service"') source /* comment */,
115                            2: optional list<map<i64 /* comment */ , set<double>>> nestedDataPoints /* comment */
116                        ) /* comment */ (api.version = "2.5", deprecated = "false")"#)
117            .unwrap();
118    }
119}