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 get<'a>(&'a self, name: &str) -> Option<&'a ParsedParameter> {
125        self.0.iter().find(|p| match p {
126            ParsedParameter::Resource(param) | ParsedParameter::Result(param) => param.name == name,
127        })
128    }
129}
130
131impl MetaValue for ParsedParameters {
132    fn fields(&self) -> Vec<&'static str> {
133        todo!()
134    }
135
136    fn get_field<'a>(&'a self, field: &str) -> Option<&'a dyn MetaValue> {
137        if let Some(p) = self.get(field) {
138            Some(p)
139        } else {
140            None
141        }
142    }
143
144    fn get_field_mut<'a>(&'a mut self, _field: &str) -> Option<&'a mut dyn MetaValue> {
145        None
146    }
147
148    fn get_index<'a>(&'a self, _index: usize) -> Option<&'a dyn MetaValue> {
149        None
150    }
151
152    fn get_index_mut<'a>(&'a mut self, _index: usize) -> Option<&'a mut dyn MetaValue> {
153        None
154    }
155
156    fn flatten(&self) -> Vec<&dyn MetaValue> {
157        vec![]
158    }
159
160    fn as_any(&self) -> &dyn std::any::Any {
161        self
162    }
163
164    fn typename(&self) -> &'static str {
165        "ParsedParameters"
166    }
167}
168
169impl TryFrom<&str> for ParsedParameters {
170    type Error = ParseError;
171    fn try_from(query_string: &str) -> Result<Self, ParseError> {
172        let mut query_string = query_string;
173        if query_string.is_empty() {
174            return Ok(Self(vec![]));
175        }
176
177        if query_string.starts_with('?') {
178            query_string = &query_string[1..];
179        }
180
181        let query_map = query_string.split('&').fold(
182            Ok(HashMap::new()),
183            |acc: Result<HashMap<String, String>, ParseError>, pair| {
184                let mut map = acc?;
185                let mut split = pair.splitn(2, '=');
186                let key = split
187                    .next()
188                    .ok_or_else(|| ParseError::InvalidParameter(pair.to_string()))?;
189                let value = split
190                    .next()
191                    .ok_or_else(|| ParseError::InvalidParameter(pair.to_string()))?;
192                map.insert(key.to_string(), value.to_string());
193                Ok(map)
194            },
195        )?;
196
197        Self::try_from(&query_map)
198    }
199}
200
201impl TryFrom<&HashMap<String, String>> for ParsedParameters {
202    type Error = ParseError;
203    fn try_from(query_params: &HashMap<String, String>) -> Result<Self, ParseError> {
204        if query_params.is_empty() {
205            return Ok(Self(vec![]));
206        }
207
208        let params = query_params
209            .keys()
210            .map(|param_name| {
211                let value = query_params.get(param_name).unwrap();
212
213                let chain = param_name
214                    .split('.')
215                    .map(|s| s.to_string())
216                    .collect::<Vec<String>>();
217
218                if chain.is_empty() {
219                    return Err(ParseError::InvalidParameter(param_name.to_string()));
220                }
221
222                let name_and_modifier = chain[0].split(':').collect::<Vec<&str>>();
223
224                if name_and_modifier.len() > 2 || name_and_modifier.is_empty() {
225                    return Err(ParseError::InvalidParameter(param_name.to_string()));
226                }
227
228                let name = name_and_modifier[0].to_string();
229
230                let param = Parameter {
231                    name,
232                    modifier: name_and_modifier.get(1).map(|s| s.to_string()),
233                    value: value.split(',').map(|v| v.to_string()).collect(),
234                    chains: if chain.len() > 1 {
235                        Some(chain[1..].to_vec())
236                    } else {
237                        None
238                    },
239                };
240
241                if RESULT_PARAMETERS.contains(&param.name.as_str()) {
242                    Ok(ParsedParameter::Result(param))
243                } else {
244                    Ok(ParsedParameter::Resource(param))
245                }
246            })
247            .collect::<Result<Vec<ParsedParameter>, ParseError>>()?;
248
249        Ok(Self(params))
250    }
251}
252
253#[cfg(test)]
254mod tests {
255    use super::*;
256
257    #[test]
258    fn test_parse_parameters() {
259        let query_string = "?name=John,Doe&_count=10&address.city=NewYork&status:exact=active";
260        let parsed_params = ParsedParameters::try_from(query_string).unwrap();
261
262        assert_eq!(parsed_params.parameters().len(), 4);
263
264        match parsed_params.get("name") {
265            Some(ParsedParameter::Resource(param)) => {
266                assert_eq!(param.name, "name");
267                assert_eq!(param.value, vec!["John", "Doe"]);
268                assert!(param.modifier.is_none());
269                assert!(param.chains.is_none());
270            }
271            _ => panic!("Expected Resource parameter"),
272        }
273
274        match parsed_params.get("_count") {
275            Some(ParsedParameter::Result(param)) => {
276                assert_eq!(param.name, "_count");
277                assert_eq!(param.value, vec!["10"]);
278                assert!(param.modifier.is_none());
279                assert!(param.chains.is_none());
280            }
281            _ => panic!("Expected Result parameter"),
282        }
283
284        match parsed_params.get("address") {
285            Some(ParsedParameter::Resource(param)) => {
286                assert_eq!(param.name, "address");
287                assert_eq!(param.value, vec!["NewYork"]);
288                assert!(param.modifier.is_none());
289                assert_eq!(param.chains, Some(vec!["city".to_string()]));
290            }
291            _ => panic!("Expected Resource parameter"),
292        }
293
294        match parsed_params.get("status") {
295            Some(ParsedParameter::Resource(param)) => {
296                assert_eq!(param.name, "status");
297                assert_eq!(param.value, vec!["active"]);
298                assert_eq!(param.modifier, Some("exact".to_string()));
299                assert!(param.chains.is_none());
300            }
301            _ => panic!("Expected Resource parameter"),
302        }
303    }
304}