kinetics_parser/params/
endpoint.rs1use crate::environment::{parse_environment, Environment};
2use http::Method;
3use serde::{Deserialize, Serialize};
4use syn::{
5 bracketed,
6 parse::{Parse, ParseStream},
7 punctuated::Punctuated,
8 token, Ident, LitBool, LitStr,
9};
10
11const ALLOWED_METHODS: [Method; 5] = [
12 Method::GET,
13 Method::POST,
14 Method::PUT,
15 Method::DELETE,
16 Method::PATCH,
17];
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct Endpoint {
21 pub name: Option<String>,
22 pub url_path: String,
23 pub environment: Environment,
24 pub is_disabled: Option<bool>,
25 pub methods: Vec<String>,
26}
27
28impl Parse for Endpoint {
29 fn parse(input: ParseStream) -> syn::Result<Self> {
30 let mut name = None;
31 let mut url_path = None;
32 let mut environment = None;
33 let mut is_disabled = None;
34 let mut methods = vec![];
35
36 while !input.is_empty() {
37 let ident_span = input.span();
38 let ident: Ident = input.parse()?;
39 input.parse::<token::Eq>()?;
40
41 match ident.to_string().as_str() {
42 "name" => {
43 if name.is_some() {
44 return Err(syn::Error::new(ident_span, "Duplicate attribute `name`"));
45 }
46 name = Some(input.parse::<LitStr>()?.value());
47 }
48 "url_path" => {
49 if url_path.is_some() {
50 return Err(syn::Error::new(
51 ident_span,
52 "Duplicate attribute `url_path`",
53 ));
54 }
55 url_path = Some(input.parse::<LitStr>()?.value());
56 }
57 "environment" => {
58 if environment.is_some() {
59 return Err(syn::Error::new(
60 ident_span,
61 "Duplicate attribute `environment`",
62 ));
63 }
64 environment = Some(parse_environment(input)?);
65 }
66 "is_disabled" => {
67 if is_disabled.is_some() {
68 return Err(syn::Error::new(
69 ident_span,
70 "Duplicate attribute `is_disabled`",
71 ));
72 }
73 is_disabled = Some(input.parse::<LitBool>()?.value());
74 }
75 "methods" => {
76 if !methods.is_empty() {
77 return Err(syn::Error::new(ident_span, "Duplicate attribute `methods`"));
78 }
79
80 let content;
82 bracketed!(content in input);
83 let parsed = Punctuated::<LitStr, token::Comma>::parse_terminated(&content)?;
84
85 methods = parsed
86 .iter()
87 .map(|item| LitStr::value(item).to_uppercase())
88 .collect();
89
90 for method in methods.iter() {
91 match Method::from_bytes(method.as_bytes()) {
92 Ok(method) => {
93 if !ALLOWED_METHODS.contains(&method) {
94 let allowed = ALLOWED_METHODS
95 .iter()
96 .map(|m| m.as_str())
97 .collect::<Vec<&str>>()
98 .join(", ");
99
100 return Err(syn::Error::new(
101 ident_span,
102 format!(
103 "Unsupported method: {method}. Available: [{allowed}]",
104 ),
105 ));
106 }
107 }
108 Err(err) => {
109 return Err(syn::Error::new(
110 ident_span,
111 format!("Invalid method: {}", err),
112 ))
113 }
114 }
115 }
116 }
117
118 _ => {}
120 }
121
122 if !input.is_empty() {
123 input.parse::<token::Comma>()?;
124 }
125 }
126
127 Ok(Endpoint {
128 name,
129 url_path: url_path
130 .ok_or_else(|| input.error("Missing required attribute `url_path`"))?,
131 environment: environment.unwrap_or_default(),
132 methods,
133 is_disabled,
134 })
135 }
136}