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