Skip to main content

haste_fhir_client/
url.rs

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