pilota_thrift_parser/parser/
function.rs1use 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}