webwire_cli/idl/
service.rs

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        // normal whitespaces
59        "service Pinger {}",
60        // whitespace variants
61        "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        // normal whitespaces
81        "service Pinger { ping: None -> None, get_version: None -> String }",
82        // whitespace variants
83        "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}