1use super::ast::{DataFormat, HttpMethod, WebCTESpec};
4use super::lexer::Token;
5
6pub struct WebCteParser<'a> {
7 _tokens: &'a mut dyn Iterator<Item = Token>,
8 _current_token: Token,
9}
10
11impl<'a> WebCteParser<'a> {
12 pub fn new(tokens: &'a mut dyn Iterator<Item = Token>, current_token: Token) -> Self {
13 Self {
14 _tokens: tokens,
15 _current_token: current_token,
16 }
17 }
18
19 pub fn parse(parser: &mut crate::sql::recursive_parser::Parser) -> Result<WebCTESpec, String> {
22 if let Token::Identifier(id) = &parser.current_token {
24 if id.to_uppercase() != "URL" {
25 return Err("Expected URL keyword in WEB CTE".to_string());
26 }
27 } else {
28 return Err("Expected URL keyword in WEB CTE".to_string());
29 }
30 parser.advance();
31
32 let url = match &parser.current_token {
34 Token::StringLiteral(url) => url.clone(),
35 _ => return Err("Expected URL string after URL keyword".to_string()),
36 };
37 parser.advance();
38
39 let mut format = None;
41 let mut headers = Vec::new();
42 let mut cache_seconds = None;
43 let mut method = None;
44 let mut body = None;
45 let mut json_path = None;
46 let mut form_files = Vec::new();
47 let mut form_fields = Vec::new();
48
49 while !matches!(parser.current_token, Token::RightParen)
51 && !matches!(parser.current_token, Token::Eof)
52 {
53 if let Token::Identifier(id) = &parser.current_token {
54 match id.to_uppercase().as_str() {
55 "FORMAT" => {
56 parser.advance();
57 format = Some(Self::parse_data_format(parser)?);
58 }
59 "CACHE" => {
60 parser.advance();
61 cache_seconds = Some(Self::parse_cache_duration(parser)?);
62 }
63 "HEADERS" => {
64 parser.advance();
65 headers = Self::parse_headers(parser)?;
66 }
67 "METHOD" => {
68 parser.advance();
69 method = Some(Self::parse_http_method(parser)?);
70 }
71 "BODY" => {
72 parser.advance();
73 body = Some(Self::parse_body(parser)?);
74 }
75 "JSON_PATH" => {
76 parser.advance();
77 json_path = Some(Self::parse_json_path(parser)?);
78 }
79 "FORM_FILE" => {
80 parser.advance();
81 let (field_name, file_path) = Self::parse_form_file(parser)?;
82 form_files.push((field_name, file_path));
83 }
84 "FORM_FIELD" => {
85 parser.advance();
86 let (field_name, value) = Self::parse_form_field(parser)?;
87 form_fields.push((field_name, value));
88 }
89 _ => {
90 return Err(format!(
91 "Unexpected keyword '{}' in WEB CTE specification",
92 id
93 ));
94 }
95 }
96 } else {
97 break;
98 }
99 }
100
101 Ok(WebCTESpec {
102 url,
103 format,
104 headers,
105 cache_seconds,
106 method,
107 body,
108 json_path,
109 form_files,
110 form_fields,
111 template_vars: Vec::new(), })
113 }
114
115 fn parse_data_format(
116 parser: &mut crate::sql::recursive_parser::Parser,
117 ) -> Result<DataFormat, String> {
118 if let Token::Identifier(id) = &parser.current_token {
119 let format = match id.to_uppercase().as_str() {
120 "CSV" => DataFormat::CSV,
121 "JSON" => DataFormat::JSON,
122 "AUTO" => DataFormat::Auto,
123 _ => return Err(format!("Unknown data format: {}", id)),
124 };
125 parser.advance();
126 Ok(format)
127 } else {
128 Err("Expected data format (CSV, JSON, or AUTO)".to_string())
129 }
130 }
131
132 fn parse_cache_duration(
133 parser: &mut crate::sql::recursive_parser::Parser,
134 ) -> Result<u64, String> {
135 match &parser.current_token {
136 Token::NumberLiteral(n) => {
137 let duration = n
138 .parse::<u64>()
139 .map_err(|_| format!("Invalid cache duration: {}", n))?;
140 parser.advance();
141 Ok(duration)
142 }
143 _ => Err("Expected number for cache duration".to_string()),
144 }
145 }
146
147 fn parse_http_method(
148 parser: &mut crate::sql::recursive_parser::Parser,
149 ) -> Result<HttpMethod, String> {
150 if let Token::Identifier(id) = &parser.current_token {
151 let method = match id.to_uppercase().as_str() {
152 "GET" => HttpMethod::GET,
153 "POST" => HttpMethod::POST,
154 "PUT" => HttpMethod::PUT,
155 "DELETE" => HttpMethod::DELETE,
156 "PATCH" => HttpMethod::PATCH,
157 _ => return Err(format!("Unknown HTTP method: {}", id)),
158 };
159 parser.advance();
160 Ok(method)
161 } else {
162 Err("Expected HTTP method (GET, POST, PUT, DELETE, PATCH)".to_string())
163 }
164 }
165
166 fn parse_body(parser: &mut crate::sql::recursive_parser::Parser) -> Result<String, String> {
167 match &parser.current_token {
168 Token::StringLiteral(body) | Token::JsonBlock(body) => {
169 let body = body.clone();
170 parser.advance();
171 Ok(body)
172 }
173 _ => Err("Expected string literal or $JSON$ block for BODY clause".to_string()),
174 }
175 }
176
177 fn parse_json_path(
178 parser: &mut crate::sql::recursive_parser::Parser,
179 ) -> Result<String, String> {
180 match &parser.current_token {
181 Token::StringLiteral(path) => {
182 let path = path.clone();
183 parser.advance();
184 Ok(path)
185 }
186 _ => Err("Expected string literal for JSON_PATH clause".to_string()),
187 }
188 }
189
190 fn parse_form_file(
191 parser: &mut crate::sql::recursive_parser::Parser,
192 ) -> Result<(String, String), String> {
193 let field_name = match &parser.current_token {
195 Token::StringLiteral(name) => name.clone(),
196 _ => return Err("Expected field name string after FORM_FILE".to_string()),
197 };
198 parser.advance();
199
200 let file_path = match &parser.current_token {
202 Token::StringLiteral(path) => path.clone(),
203 _ => return Err("Expected file path string after field name".to_string()),
204 };
205 parser.advance();
206
207 Ok((field_name, file_path))
208 }
209
210 fn parse_form_field(
211 parser: &mut crate::sql::recursive_parser::Parser,
212 ) -> Result<(String, String), String> {
213 let field_name = match &parser.current_token {
215 Token::StringLiteral(name) | Token::JsonBlock(name) => name.clone(),
216 _ => return Err("Expected field name string after FORM_FIELD".to_string()),
217 };
218 parser.advance();
219
220 let value = match &parser.current_token {
222 Token::StringLiteral(val) | Token::JsonBlock(val) => val.clone(),
223 _ => {
224 return Err(
225 "Expected field value string or $JSON$ block after field name".to_string(),
226 )
227 }
228 };
229 parser.advance();
230
231 Ok((field_name, value))
232 }
233
234 fn parse_headers(
235 parser: &mut crate::sql::recursive_parser::Parser,
236 ) -> Result<Vec<(String, String)>, String> {
237 parser.consume(Token::LeftParen)?;
238 let mut headers = Vec::new();
239
240 loop {
241 let key = match &parser.current_token {
243 Token::Identifier(id) => id.clone(),
244 Token::StringLiteral(s) => s.clone(),
245 _ => return Err("Expected header name".to_string()),
246 };
247 parser.advance();
248
249 if !matches!(parser.current_token, Token::Colon) {
251 if matches!(parser.current_token, Token::Equal) {
253 parser.advance();
254 } else {
255 return Err("Expected ':' or '=' after header name".to_string());
256 }
257 } else {
258 parser.advance(); }
260
261 let value = match &parser.current_token {
263 Token::StringLiteral(s) => s.clone(),
264 _ => return Err("Expected header value as string".to_string()),
265 };
266 parser.advance();
267
268 headers.push((key, value));
269
270 if matches!(parser.current_token, Token::Comma) {
272 parser.advance();
273 } else if matches!(parser.current_token, Token::RightParen) {
274 parser.advance();
275 break;
276 } else {
277 return Err("Expected ',' or ')' after header value".to_string());
278 }
279 }
280
281 Ok(headers)
282 }
283}