1use pest::{iterators::Pair, Parser};
2use pest_derive::Parser;
3
4#[derive(Parser)]
5#[grammar = "grammar.pest"] pub 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}