Skip to main content

haste_fhir_client/
url.rs

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/// Represnet both resource parameters IE Patient.name and
18/// result parameters IE _count
19#[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(&param.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}