1use nom::{
2 bytes::complete::tag,
3 character::complete::char,
4 combinator::{cut, map},
5 error::context,
6 multi::separated_list0,
7 sequence::{pair, preceded, terminated},
8 IResult,
9};
10
11use crate::common::FilePosition;
12use crate::idl::common::{parse_field_separator, parse_identifier, trailing_comma, ws, ws1, Span};
13use crate::idl::method::{parse_method, Method};
14
15#[cfg(test)]
16use crate::idl::common::assert_parse;
17
18#[derive(Debug, PartialEq)]
19pub struct Service {
20 pub name: String,
21 pub methods: Vec<Method>,
22 pub position: FilePosition,
23}
24
25fn parse_methods(input: Span) -> IResult<Span, Vec<Method>> {
26 context(
27 "methods",
28 preceded(
29 preceded(ws, char('{')),
30 cut(terminated(
31 separated_list0(parse_field_separator, preceded(ws, parse_method)),
32 preceded(trailing_comma, preceded(ws, char('}'))),
33 )),
34 ),
35 )(input)
36}
37
38pub fn parse_service(input: Span) -> IResult<Span, Service> {
39 context(
40 "service",
41 map(
42 preceded(
43 terminated(tag("service"), ws1),
44 cut(pair(parse_identifier, parse_methods)),
45 ),
46 |(name, methods)| Service {
47 name,
48 methods,
49 position: input.into(),
50 },
51 ),
52 )(input)
53}
54
55#[test]
56fn test_parse_service_no_endpoints() {
57 let contents = [
58 "service Pinger {}",
60 "service Pinger{}",
62 "service Pinger{ }",
63 ];
64 for content in contents.iter() {
65 assert_parse(
66 parse_service(Span::new(content)),
67 Service {
68 name: "Pinger".to_string(),
69 methods: vec![],
70 position: FilePosition { line: 1, column: 1 },
71 },
72 )
73 }
74}
75
76#[test]
77fn test_parse_service() {
78 use crate::idl::r#type::{Type, TypeRef};
79 let contents = [
80 "service Pinger { ping: None -> None, get_version: None -> String }",
82 "service Pinger{ping:None->None,get_version:None->String}",
84 "service Pinger {ping:None->None,get_version:None->String}",
85 "service Pinger{ ping:None->None,get_version:None->String}",
86 "service Pinger{ping :None->None,get_version:None->String}",
87 "service Pinger{ping: None->None,get_version:None->String}",
88 "service Pinger{ping:None ->None,get_version:None->String}",
89 "service Pinger{ping:None-> None,get_version:None->String}",
90 "service Pinger{ping:None->None ,get_version:None->String}",
91 "service Pinger{ping:None->None, get_version:None->String}",
92 "service Pinger{ping:None->None,get_version :None->String}",
93 "service Pinger{ping:None->None,get_version: None->String}",
94 "service Pinger{ping:None->None,get_version:None ->String}",
95 "service Pinger{ping:None->None,get_version:None-> String}",
96 "service Pinger{ping:None->None,get_version:None->String }",
97 ];
98 for content in contents.iter() {
99 assert_parse(
100 parse_service(Span::new(content)),
101 Service {
102 name: "Pinger".to_string(),
103 position: FilePosition { line: 1, column: 1 },
104 methods: vec![
105 Method {
106 name: "ping".to_string(),
107 input: None,
108 output: None,
109 },
110 Method {
111 name: "get_version".to_string(),
112 input: None,
113 output: Some(Type::Ref(TypeRef {
114 abs: false,
115 ns: vec![],
116 name: "String".to_string(),
117 generics: vec![],
118 })),
119 },
120 ],
121 },
122 )
123 }
124}