1use derivative::Derivative;
2use haste_fhir_operation_error::derive::OperationOutcomeError;
3use haste_reflect::{MetaValue, derive::Reflect};
4use std::{collections::HashMap, fmt::Display};
5
6static RESULT_PARAMETERS: &[&str] = &[
7 "_count",
8 "_offset",
9 "_total",
10 "_sort",
11 "_include",
12 "_revinclude",
13 "_summary",
14 "_elements",
15 "_contained",
16 "_containedType",
17 "_since",
18];
19
20#[derive(Derivative, Clone, Reflect)]
21#[fhir_type = "BackboneElement"]
22#[derivative(Debug)]
23pub struct Parameter {
24 pub name: String,
25 #[derivative(Debug(format_with = "crate::redact"))]
26 pub value: Vec<String>,
27 pub modifier: Option<String>,
28 pub chains: Option<Vec<String>>,
29}
30
31impl From<(String, Vec<String>)> for Parameter {
32 fn from(tuple: (String, Vec<String>)) -> Self {
33 Parameter {
34 name: tuple.0,
35 value: tuple.1,
36 modifier: None,
37 chains: None,
38 }
39 }
40}
41
42impl From<(String, Vec<String>, String)> for Parameter {
43 fn from(tuple: (String, Vec<String>, String)) -> Self {
44 Parameter {
45 name: tuple.0,
46 value: tuple.1,
47 modifier: Some(tuple.2),
48 chains: None,
49 }
50 }
51}
52
53impl From<(String, Vec<String>, Option<String>, Vec<String>)> for Parameter {
54 fn from(tuple: (String, Vec<String>, Option<String>, Vec<String>)) -> Self {
55 Parameter {
56 name: tuple.0,
57 value: tuple.1,
58 modifier: tuple.2,
59 chains: Some(tuple.3),
60 }
61 }
62}
63
64#[derive(Derivative, Clone)]
67#[derivative(Debug = "transparent")]
68pub enum ParsedParameter {
69 #[derivative(Debug = "transparent")]
70 Result(Parameter),
71 #[derivative(Debug = "transparent")]
72 Resource(Parameter),
73}
74
75impl<T: Into<Parameter>> From<T> for ParsedParameter {
76 fn from(param: T) -> Self {
77 let param = param.into();
78 if RESULT_PARAMETERS.contains(¶m.name.as_str()) {
79 ParsedParameter::Result(param)
80 } else {
81 ParsedParameter::Resource(param)
82 }
83 }
84}
85
86impl ParsedParameter {
87 #[must_use]
88 pub fn name(&self) -> &str {
89 match self {
90 ParsedParameter::Result(p) | ParsedParameter::Resource(p) => &p.name,
91 }
92 }
93}
94
95impl MetaValue for ParsedParameter {
96 fn fields(&self) -> Vec<&'static str> {
97 match self {
98 ParsedParameter::Result(p) | ParsedParameter::Resource(p) => p.fields(),
99 }
100 }
101
102 fn get_field<'a>(&'a self, field: &str) -> Option<&'a dyn MetaValue> {
103 match self {
104 ParsedParameter::Result(p) | ParsedParameter::Resource(p) => p.get_field(field),
105 }
106 }
107
108 fn get_field_mut<'a>(&'a mut self, field: &str) -> Option<&'a mut dyn MetaValue> {
109 match self {
110 ParsedParameter::Result(p) | ParsedParameter::Resource(p) => p.get_field_mut(field),
111 }
112 }
113
114 fn get_index(&self, index: usize) -> Option<&dyn MetaValue> {
115 match self {
116 ParsedParameter::Result(p) | ParsedParameter::Resource(p) => p.get_index(index),
117 }
118 }
119
120 fn get_index_mut(&mut self, index: usize) -> Option<&mut dyn MetaValue> {
121 match self {
122 ParsedParameter::Result(p) | ParsedParameter::Resource(p) => p.get_index_mut(index),
123 }
124 }
125
126 fn flatten(&self) -> Vec<&dyn MetaValue> {
127 match self {
128 ParsedParameter::Result(p) | ParsedParameter::Resource(p) => p.flatten(),
129 }
130 }
131
132 fn as_any(&self) -> &dyn std::any::Any {
133 match self {
134 ParsedParameter::Result(p) | ParsedParameter::Resource(p) => p.as_any(),
135 }
136 }
137
138 fn fhir_type(&self) -> &'static str {
139 match self {
140 ParsedParameter::Result(p) | ParsedParameter::Resource(p) => p.fhir_type(),
141 }
142 }
143
144 fn is_many(&self) -> bool {
145 false
146 }
147}
148
149#[derive(Debug, OperationOutcomeError)]
150pub enum ParseError {
151 #[fatal(
152 code = "invalid",
153 diagnostic = "Error parsing query parameters '{arg0}'"
154 )]
155 InvalidParameter(String),
156}
157
158impl Display for ParseError {
159 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
160 match self {
161 ParseError::InvalidParameter(param) => {
162 write!(f, "Invalid query parameter: {param}")
163 }
164 }
165 }
166}
167
168impl std::error::Error for ParseError {}
169
170#[derive(Derivative, Clone)]
171#[derivative(Debug = "transparent")]
172pub struct ParsedParameters(Vec<ParsedParameter>);
173
174impl<T: Into<ParsedParameter>> From<Vec<T>> for ParsedParameters {
175 fn from(params: Vec<T>) -> Self {
176 let parsed_params = params.into_iter().map(std::convert::Into::into).collect();
177 ParsedParameters(parsed_params)
178 }
179}
180
181impl ParsedParameters {
182 #[must_use]
183 pub fn new(params: Vec<ParsedParameter>) -> Self {
184 Self(params)
185 }
186 #[must_use]
187 pub fn parameters(&self) -> &Vec<ParsedParameter> {
188 &self.0
189 }
190 #[must_use]
191 pub fn owned_parameters(self) -> Vec<ParsedParameter> {
192 self.0
193 }
194 #[must_use]
195 pub fn get(&self, name: &str) -> Option<&ParsedParameter> {
196 self.0.iter().find(|p| match p {
197 ParsedParameter::Resource(param) | ParsedParameter::Result(param) => param.name == name,
198 })
199 }
200}
201
202impl MetaValue for ParsedParameters {
203 fn fields(&self) -> Vec<&'static str> {
204 todo!()
205 }
206
207 fn get_field<'a>(&'a self, field: &str) -> Option<&'a dyn MetaValue> {
208 if let Some(p) = self.get(field) {
209 Some(p)
210 } else {
211 None
212 }
213 }
214
215 fn get_field_mut<'a>(&'a mut self, _field: &str) -> Option<&'a mut dyn MetaValue> {
216 None
217 }
218
219 fn get_index(&self, _index: usize) -> Option<&dyn MetaValue> {
220 None
221 }
222
223 fn get_index_mut(&mut self, _index: usize) -> Option<&mut dyn MetaValue> {
224 None
225 }
226
227 fn flatten(&self) -> Vec<&dyn MetaValue> {
228 vec![]
229 }
230
231 fn as_any(&self) -> &dyn std::any::Any {
232 self
233 }
234
235 fn fhir_type(&self) -> &'static str {
236 "BackboneElement"
237 }
238
239 fn is_many(&self) -> bool {
240 true
241 }
242}
243
244impl TryFrom<&str> for ParsedParameters {
245 type Error = ParseError;
246 fn try_from(query_string: &str) -> Result<Self, ParseError> {
247 let mut query_string = query_string;
248 if query_string.is_empty() {
249 return Ok(Self(vec![]));
250 }
251
252 if query_string.starts_with('?') {
253 query_string = &query_string[1..];
254 }
255
256 let query_map = query_string
257 .split('&')
258 .map(|pair| {
259 pair.split_once('=')
260 .map(|(key, value)| (key.to_string(), value.to_string()))
261 .ok_or_else(|| ParseError::InvalidParameter(pair.to_string()))
262 })
263 .collect::<Result<std::collections::HashMap<String, String>, ParseError>>()?;
264
265 Self::try_from(&query_map)
266 }
267}
268
269impl TryFrom<&HashMap<String, String>> for ParsedParameters {
270 type Error = ParseError;
271 fn try_from(query_params: &HashMap<String, String>) -> Result<Self, ParseError> {
272 if query_params.is_empty() {
273 return Ok(Self(vec![]));
274 }
275
276 let params = query_params
277 .keys()
278 .map(|param_name| {
279 let value = query_params.get(param_name).unwrap();
280
281 let chain = param_name
282 .split('.')
283 .map(std::string::ToString::to_string)
284 .collect::<Vec<String>>();
285
286 if chain.is_empty() {
287 return Err(ParseError::InvalidParameter(param_name.clone()));
288 }
289
290 let name_and_modifier = chain[0].split(':').collect::<Vec<&str>>();
291
292 if name_and_modifier.len() > 2 || name_and_modifier.is_empty() {
293 return Err(ParseError::InvalidParameter(param_name.clone()));
294 }
295
296 let name = name_and_modifier[0].to_string();
297
298 let param = Parameter {
299 name,
300 modifier: name_and_modifier
301 .get(1)
302 .map(std::string::ToString::to_string),
303 value: value
304 .split(',')
305 .map(std::string::ToString::to_string)
306 .collect(),
307 chains: if chain.len() > 1 {
308 Some(chain[1..].to_vec())
309 } else {
310 None
311 },
312 };
313
314 if RESULT_PARAMETERS.contains(¶m.name.as_str()) {
315 Ok(ParsedParameter::Result(param))
316 } else {
317 Ok(ParsedParameter::Resource(param))
318 }
319 })
320 .collect::<Result<Vec<ParsedParameter>, ParseError>>()?;
321
322 Ok(Self(params))
323 }
324}
325
326#[must_use]
327pub fn parse_prefix(v: &str) -> (Option<&str>, &str) {
328 if v.len() < 3 {
329 return (None, v);
330 }
331
332 let sub_str = &v[..2];
333 let remainder = &v[2..];
334
335 match sub_str {
336 "lt" | "le" | "gt" | "ge" | "eq" | "ne" => (Some(sub_str), remainder),
337 _ => (None, v),
338 }
339}
340
341#[cfg(test)]
342mod tests {
343 use super::*;
344
345 #[test]
346 fn test_parse_prefix() {
347 let cases = vec![
348 ("lt5.0", (Some("lt"), "5.0")),
349 ("le10", (Some("le"), "10")),
350 ("gt3.14", (Some("gt"), "3.14")),
351 ("ge2.71", (Some("ge"), "2.71")),
352 ("eq42", (Some("eq"), "42")),
353 ("ne0", (Some("ne"), "0")),
354 ("5.0", (None, "5.0")),
355 ("10", (None, "10")),
356 ];
357
358 for (input, expected) in cases {
359 let result = parse_prefix(input);
360 assert_eq!(result, expected, "Failed for input: {input}");
361 }
362 }
363
364 #[test]
365 fn test_parse_parameters() {
366 let query_string = "?name=John,Doe&_count=10&address.city=NewYork&status:exact=active";
367 let parsed_params = ParsedParameters::try_from(query_string).unwrap();
368
369 assert_eq!(parsed_params.parameters().len(), 4);
370
371 match parsed_params.get("name") {
372 Some(ParsedParameter::Resource(param)) => {
373 assert_eq!(param.name, "name");
374 assert_eq!(param.value, vec!["John", "Doe"]);
375 assert!(param.modifier.is_none());
376 assert!(param.chains.is_none());
377 }
378 _ => panic!("Expected Resource parameter"),
379 }
380
381 match parsed_params.get("_count") {
382 Some(ParsedParameter::Result(param)) => {
383 assert_eq!(param.name, "_count");
384 assert_eq!(param.value, vec!["10"]);
385 assert!(param.modifier.is_none());
386 assert!(param.chains.is_none());
387 }
388 _ => panic!("Expected Result parameter"),
389 }
390
391 match parsed_params.get("address") {
392 Some(ParsedParameter::Resource(param)) => {
393 assert_eq!(param.name, "address");
394 assert_eq!(param.value, vec!["NewYork"]);
395 assert!(param.modifier.is_none());
396 assert_eq!(param.chains, Some(vec!["city".to_string()]));
397 }
398 _ => panic!("Expected Resource parameter"),
399 }
400
401 match parsed_params.get("status") {
402 Some(ParsedParameter::Resource(param)) => {
403 assert_eq!(param.name, "status");
404 assert_eq!(param.value, vec!["active"]);
405 assert_eq!(param.modifier, Some("exact".to_string()));
406 assert!(param.chains.is_none());
407 }
408 _ => panic!("Expected Resource parameter"),
409 }
410 }
411}