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