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