idl_parser/
lib.rs

1use pest::{iterators::Pair, Parser};
2use pest_derive::Parser;
3
4#[derive(Parser)]
5#[grammar = "grammar.pest"] // relative to src
6pub struct EndpointParser;
7
8type TypeName = String;
9
10impl EndpointParser {
11    pub fn parse_endpoint(input: &str) -> Result<Endpoint, ParseError> {
12        let value = Self::parse(Rule::endpoint, input)
13            .map_err(Box::new)?
14            .next()
15            .unwrap();
16
17        match value.as_rule() {
18            Rule::endpoint => value.try_into(),
19            _ => panic!("unreachable"),
20        }
21    }
22}
23
24#[derive(thiserror::Error, Debug)]
25pub enum ParseError {
26    #[error("Unexpect rule")]
27    UnexpectRule,
28    #[error("Unsupport type")]
29    UnsupportType,
30    #[error("Parse error")]
31    PestError(#[from] Box<pest::error::Error<Rule>>),
32}
33
34impl TryFrom<Pair<'_, Rule>> for Endpoint {
35    type Error = ParseError;
36
37    fn try_from(value: Pair<'_, Rule>) -> Result<Self, Self::Error> {
38        if Rule::endpoint == value.as_rule() {
39            let mut inner = value.into_inner();
40            let method: Method = inner.next().unwrap().try_into()?;
41            let path: Vec<Path> = inner
42                .next()
43                .unwrap()
44                .into_inner()
45                .map(|v| v.try_into())
46                .collect::<Result<Vec<_>, _>>()
47                .unwrap();
48            let query_params: Vec<Variable> = inner
49                .next()
50                .unwrap()
51                .into_inner()
52                .map(|v| v.try_into())
53                .collect::<Result<Vec<_>, _>>()
54                .unwrap();
55            let mut pair = inner.next();
56            let req_type: Option<RequestType> = if let Some(Ok(rq)) = pair.as_ref().map(|v| v.try_into()) {
57                pair = inner.next();
58                Some(rq)
59            } else {
60                None
61            };
62            let res_type: Option<ResponseType> = pair.and_then(|v| v.try_into().ok());
63
64            Ok(Self {
65                method,
66                path,
67                query_params,
68                request_type: req_type.map(|v| v.0),
69                response_type: res_type.map(|v| v.0),
70            })
71        } else {
72            Err(ParseError::UnexpectRule)
73        }
74    }
75}
76
77#[derive(Debug, PartialEq, PartialOrd)]
78pub struct Endpoint {
79    pub method: Method,
80    pub path: Vec<Path>,
81    pub query_params: Vec<Variable>,
82    pub request_type: Option<TypeName>,
83    pub response_type: Option<TypeName>,
84}
85
86#[derive(Debug, PartialEq, PartialOrd)]
87pub enum Method {
88    GET,
89    POST,
90    PUT,
91    DELETE,
92}
93
94#[derive(Debug, PartialEq, PartialOrd)]
95pub enum Path {
96    Segment(String),
97    Variable(String, VariableType),
98}
99
100#[derive(Debug)]
101pub struct QueryParam(String, VariableType);
102
103#[derive(Debug, PartialEq, PartialOrd)]
104pub enum VariableType {
105    String,
106    Short,
107    Int,
108    Long,
109    Float,
110    Double,
111    Bool,
112}
113
114#[derive(Debug)]
115pub struct RequestType(String);
116#[derive(Debug)]
117pub struct ResponseType(String);
118
119impl TryFrom<Pair<'_, Rule>> for VariableType {
120    type Error = ParseError;
121
122    fn try_from(value: Pair<'_, Rule>) -> Result<Self, Self::Error> {
123        if Rule::variable_type == value.as_rule() {
124            match value.as_str().to_lowercase().as_str() {
125                "string" => Ok(Self::String),
126                "short" => Ok(Self::Short),
127                "int" => Ok(Self::Int),
128                "long" => Ok(Self::Long),
129                "float" => Ok(Self::Float),
130                "double" => Ok(Self::Double),
131                "bool" => Ok(Self::Bool),
132                _ => Err(ParseError::UnsupportType),
133            }
134        } else {
135            Err(ParseError::UnexpectRule)
136        }
137    }
138}
139
140#[derive(Debug, PartialEq, PartialOrd)]
141pub struct Variable(String, VariableType);
142
143impl TryFrom<Pair<'_, Rule>> for Variable {
144    type Error = ParseError;
145
146    fn try_from(value: Pair<'_, Rule>) -> Result<Self, Self::Error> {
147        if Rule::variable == value.as_rule() {
148            let mut pairs = value.into_inner();
149            let name = pairs.next().unwrap();
150            let variable_type = pairs.next().unwrap().try_into()?;
151
152            Ok(Self(name.as_str().to_owned(), variable_type))
153        } else {
154            Err(ParseError::UnexpectRule)
155        }
156    }
157}
158
159impl TryFrom<Pair<'_, Rule>> for Method {
160    type Error = ParseError;
161
162    fn try_from(value: Pair<'_, Rule>) -> Result<Self, Self::Error> {
163        if Rule::method == value.as_rule() {
164            let Some(method) = value.into_inner().next() else {panic!("Impossible")};
165            Ok(match method.as_str().to_uppercase().as_str() {
166                "GET" => Self::GET,
167                "POST" => Self::POST,
168                "PUT" => Self::PUT,
169                "DELETE" => Self::DELETE,
170                _ => panic!("Impossible"),
171            })
172        } else {
173            Err(ParseError::UnexpectRule)
174        }
175    }
176}
177
178impl TryFrom<Pair<'_, Rule>> for Path {
179    type Error = ParseError;
180
181    fn try_from(value: Pair<'_, Rule>) -> Result<Self, Self::Error> {
182        if Rule::segment == value.as_rule() {
183            Ok(Path::Segment(
184                value.into_inner().next().unwrap().as_str().to_string(),
185            ))
186        } else if Rule::variable == value.as_rule() {
187            let Variable(name, var_type) = value.try_into()?;
188            Ok(Path::Variable(name, var_type))
189        } else {
190            Err(ParseError::UnexpectRule)
191        }
192    }
193}
194
195macro_rules! impl_string_type {
196    ($type:ident, $rule:expr, $r:ty) => {
197        impl TryFrom<$r> for $type {
198            type Error = ParseError;
199
200            fn try_from(value: $r) -> Result<Self, Self::Error> {
201                if $rule == value.as_rule() {
202                    Ok($type(value.as_str().to_string()))
203                } else {
204                    Err(ParseError::UnexpectRule)
205                }
206            }
207        }
208    };
209}
210
211impl_string_type!(ResponseType, Rule::response_type, Pair<'_, Rule>);
212impl_string_type!(RequestType, Rule::request_type, Pair<'_, Rule>);
213
214impl_string_type!(ResponseType, Rule::response_type, &Pair<'_, Rule>);
215impl_string_type!(RequestType, Rule::request_type, &Pair<'_, Rule>);
216
217#[cfg(test)]
218mod tests {
219    use pest::Parser;
220
221    use super::*;
222    use crate::EndpointParser;
223
224    #[test]
225    fn test_variable() -> anyhow::Result<()> {
226        let mut pairs = EndpointParser::parse(Rule::variable, "Name:string")?;
227        let _: Variable = pairs.next().unwrap().try_into()?;
228        Ok(())
229    }
230
231    #[test]
232    fn test_method() -> anyhow::Result<()> {
233        let mut pairs = EndpointParser::parse(Rule::method, "GET")?;
234        let var: Method = pairs.next().unwrap().try_into()?;
235        assert!(var == Method::GET);
236        Ok(())
237    }
238
239    #[test]
240    fn test_path() -> anyhow::Result<()> {
241        let mut pairs = EndpointParser::parse(Rule::path, "/seg/{var:string}")?;
242
243        for ele in pairs.next().unwrap().into_inner() {
244            let _path: Path = ele.try_into()?;
245        }
246        Ok(())
247    }
248
249    #[test]
250    fn test_query_params() -> anyhow::Result<()> {
251        let mut pairs = EndpointParser::parse(Rule::query_params, "?a:string&b:bool")?;
252        let inner = pairs.next().unwrap().into_inner();
253
254        let _params: Result<Vec<Variable>, _> = inner.into_iter().map(|v| v.try_into()).collect();
255        Ok(())
256    }
257
258    #[test]
259    fn test_sig() -> anyhow::Result<()> {
260        let mut pairs = EndpointParser::parse(Rule::request_type, "fasd")?;
261        let request_type: RequestType = pairs.next().unwrap().try_into()?;
262
263        println!("{:?}", request_type);
264
265        Ok(())
266    }
267
268    #[test]
269    fn test_endpoint() -> anyhow::Result<()> {
270        let endpoint = EndpointParser::parse_endpoint(
271            "GET /register/{id:string}?type:string&order:string RQ -> RS",
272        )?;
273        assert_eq!(
274            Endpoint {
275                method: Method::GET,
276                path: vec![
277                    Path::Segment("register".to_owned()),
278                    Path::Variable("id".to_owned(), VariableType::String)
279                ],
280                query_params: vec![
281                    Variable("type".to_owned(), VariableType::String),
282                    Variable("order".to_owned(), VariableType::String),
283                ],
284                request_type: Some("RQ".to_owned()),
285                response_type: Some("RS".to_owned())
286            },
287            endpoint
288        );
289        Ok(())
290    }
291
292    #[test]
293    fn test_endpoint_without_sig() -> anyhow::Result<()> {
294        let endpoint =
295            EndpointParser::parse_endpoint("GET /register/{id:string}?type:string&order:string ")?;
296        assert_eq!(
297            Endpoint {
298                method: Method::GET,
299                path: vec![
300                    Path::Segment("register".to_owned()),
301                    Path::Variable("id".to_owned(), VariableType::String)
302                ],
303                query_params: vec![
304                    Variable("type".to_owned(), VariableType::String),
305                    Variable("order".to_owned(), VariableType::String),
306                ],
307                request_type: None,
308                response_type: None
309            },
310            endpoint
311        );
312        Ok(())
313    }
314    #[test]
315    fn test_endpoint_without_query_params() -> anyhow::Result<()> {
316        let endpoint =
317            EndpointParser::parse_endpoint("GET /register/{id:string} RQ -> RS")?;
318        assert_eq!(
319            Endpoint {
320                method: Method::GET,
321                path: vec![
322                    Path::Segment("register".to_owned()),
323                    Path::Variable("id".to_owned(), VariableType::String)
324                ],
325                query_params: vec![
326                ],
327                request_type: Some("RQ".to_owned()),
328                response_type: Some("RS".to_owned())
329            },
330            endpoint
331        );
332        Ok(())
333    }
334}