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