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#[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(¶m.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}