1use serde::{Deserialize, Serialize};
8use serde_json::Value;
9
10use helios_fhir::ParameterValueAccessor;
12
13pub type FhirPathParameters = helios_fhir::VersionIndependentParameters;
18
19#[derive(Debug, Deserialize, Serialize)]
21pub struct Parameter {
22 pub name: String,
24
25 #[serde(rename = "valueString", skip_serializing_if = "Option::is_none")]
27 pub value_string: Option<String>,
28
29 #[serde(rename = "valueBoolean", skip_serializing_if = "Option::is_none")]
31 pub value_boolean: Option<bool>,
32
33 #[serde(skip_serializing_if = "Option::is_none")]
35 pub resource: Option<Value>,
36
37 #[serde(skip_serializing_if = "Option::is_none")]
39 pub part: Option<Vec<ParameterPart>>,
40}
41
42#[derive(Debug, Deserialize, Serialize)]
44pub struct ParameterPart {
45 pub name: String,
47
48 #[serde(rename = "valueString", skip_serializing_if = "Option::is_none")]
50 pub value_string: Option<String>,
51
52 #[serde(flatten)]
54 pub value: Option<Value>,
55}
56
57#[derive(Debug, Default)]
59pub struct ExtractedParameters {
60 pub context: Option<String>,
62
63 pub expression: Option<String>,
65
66 pub validate: bool,
68
69 pub variables: Vec<Variable>,
71
72 pub resource: Option<Value>,
74
75 pub terminology_server: Option<String>,
77}
78
79#[derive(Debug, Clone)]
81pub struct Variable {
82 pub name: String,
84
85 pub value: Value,
87}
88
89#[derive(Debug, Serialize)]
91pub struct ResultPart {
92 pub name: String,
94
95 #[serde(rename = "valueString", skip_serializing_if = "Option::is_none")]
97 pub value_string: Option<String>,
98
99 #[serde(skip_serializing_if = "Option::is_none")]
101 pub part: Option<Vec<ResultValue>>,
102
103 #[serde(skip_serializing_if = "Option::is_none")]
105 pub extension: Option<Vec<Extension>>,
106}
107
108#[derive(Debug, Serialize)]
110pub struct ResultValue {
111 pub name: String,
113
114 #[serde(rename = "valueString", skip_serializing_if = "Option::is_none")]
116 pub value_string: Option<String>,
117
118 #[serde(rename = "valueBoolean", skip_serializing_if = "Option::is_none")]
119 pub value_boolean: Option<bool>,
120
121 #[serde(rename = "valueInteger", skip_serializing_if = "Option::is_none")]
122 pub value_integer: Option<i64>,
123
124 #[serde(rename = "valueDecimal", skip_serializing_if = "Option::is_none")]
125 pub value_decimal: Option<f64>,
126
127 #[serde(rename = "valueDate", skip_serializing_if = "Option::is_none")]
128 pub value_date: Option<String>,
129
130 #[serde(rename = "valueDateTime", skip_serializing_if = "Option::is_none")]
131 pub value_date_time: Option<String>,
132
133 #[serde(rename = "valueTime", skip_serializing_if = "Option::is_none")]
134 pub value_time: Option<String>,
135
136 #[serde(rename = "valueQuantity", skip_serializing_if = "Option::is_none")]
137 pub value_quantity: Option<Value>,
138
139 #[serde(rename = "valueHumanName", skip_serializing_if = "Option::is_none")]
140 pub value_human_name: Option<Value>,
141
142 #[serde(skip_serializing_if = "Option::is_none")]
144 pub extension: Option<Vec<Extension>>,
145}
146
147#[derive(Debug, Serialize)]
149pub struct Extension {
150 pub url: String,
152
153 #[serde(rename = "valueString")]
155 pub value_string: String,
156}
157
158pub fn create_json_extension(value: &Value) -> Extension {
160 Extension {
161 url: "http://fhir.forms-lab.com/StructureDefinition/json-value".to_string(),
162 value_string: serde_json::to_string_pretty(value).unwrap_or_default(),
163 }
164}
165
166pub fn extract_parameters(params: FhirPathParameters) -> Result<ExtractedParameters, String> {
168 let mut extracted = ExtractedParameters::default();
169
170 match params {
172 #[cfg(feature = "R4")]
173 FhirPathParameters::R4(parameters) => {
174 extract_parameters_from_r4(parameters, &mut extracted)?;
175 }
176 #[cfg(feature = "R4B")]
177 FhirPathParameters::R4B(parameters) => {
178 extract_parameters_from_r4b(parameters, &mut extracted)?;
179 }
180 #[cfg(feature = "R5")]
181 FhirPathParameters::R5(parameters) => {
182 extract_parameters_from_r5(parameters, &mut extracted)?;
183 }
184 #[cfg(feature = "R6")]
185 FhirPathParameters::R6(parameters) => {
186 extract_parameters_from_r6(parameters, &mut extracted)?;
187 }
188 #[allow(unreachable_patterns)]
189 _ => {
190 return Err(
191 "FHIR version of Parameters resource is not enabled in this build".to_string(),
192 );
193 }
194 }
195
196 if extracted.expression.is_none() {
197 return Err("Missing required parameter: expression".to_string());
198 }
199
200 if extracted.resource.is_none() {
201 return Err("Missing required parameter: resource".to_string());
202 }
203
204 Ok(extracted)
205}
206
207#[cfg(feature = "R4")]
208fn extract_parameters_from_r4(
209 parameters: helios_fhir::r4::Parameters,
210 extracted: &mut ExtractedParameters,
211) -> Result<(), String> {
212 for param in parameters.parameter.unwrap_or_default() {
213 process_parameter_r4(¶m, extracted)?;
214 }
215 Ok(())
216}
217
218#[cfg(feature = "R4")]
219fn process_parameter_r4(
220 param: &helios_fhir::r4::ParametersParameter,
221 extracted: &mut ExtractedParameters,
222) -> Result<(), String> {
223 let name = param.name.value.as_deref().unwrap_or("");
224
225 match name {
226 "context" => {
227 extracted.context = param
228 .value
229 .as_ref()
230 .and_then(|v| v.as_string())
231 .map(|s| s.to_string());
232 }
233 "expression" => {
234 extracted.expression = param
235 .value
236 .as_ref()
237 .and_then(|v| v.as_string())
238 .map(|s| s.to_string());
239 }
240 "validate" => {
241 extracted.validate = param
242 .value
243 .as_ref()
244 .and_then(|v| v.as_boolean())
245 .unwrap_or(false);
246 }
247 "variables" => {
248 if let Some(parts) = ¶m.part {
249 for part in parts {
250 if let Some(name) = &part.name.value {
251 let value = if let Some(val) = &part.value {
252 parameter_value_to_json_r4(val)
254 } else {
255 Value::Null
256 };
257
258 extracted.variables.push(Variable {
259 name: name.to_string(),
260 value,
261 });
262 }
263 }
264 }
265 }
266 "resource" => {
267 extracted.resource = param
268 .resource
269 .as_ref()
270 .and_then(|r| serde_json::to_value(r).ok());
271 }
272 "terminologyServer" => {
273 extracted.terminology_server = param
274 .value
275 .as_ref()
276 .and_then(|v| v.as_string())
277 .map(|s| s.to_string());
278 }
279 _ => {
280 }
282 }
283
284 Ok(())
285}
286
287#[cfg(feature = "R4")]
288fn parameter_value_to_json_r4(value: &helios_fhir::r4::ParametersParameterValue) -> Value {
289 match value {
292 helios_fhir::r4::ParametersParameterValue::String(s) => s
293 .value
294 .as_ref()
295 .map(|v| Value::String(v.clone()))
296 .unwrap_or(Value::Null),
297 helios_fhir::r4::ParametersParameterValue::Boolean(b) => {
298 b.value.map(Value::Bool).unwrap_or(Value::Null)
299 }
300 helios_fhir::r4::ParametersParameterValue::Integer(i) => i
301 .value
302 .map(|v| Value::Number(serde_json::Number::from(v)))
303 .unwrap_or(Value::Null),
304 helios_fhir::r4::ParametersParameterValue::Decimal(d) => {
305 serde_json::to_value(d).unwrap_or(Value::Null)
306 }
307 _ => {
308 serde_json::to_value(value).unwrap_or(Value::Null)
310 }
311 }
312}
313
314#[cfg(feature = "R4B")]
315fn extract_parameters_from_r4b(
316 parameters: helios_fhir::r4b::Parameters,
317 extracted: &mut ExtractedParameters,
318) -> Result<(), String> {
319 for param in parameters.parameter.unwrap_or_default() {
320 let name = param.name.value.as_deref().unwrap_or("");
321
322 match name {
323 "context" => {
324 extracted.context = param
325 .value
326 .as_ref()
327 .and_then(|v| v.as_string())
328 .map(|s| s.to_string());
329 }
330 "expression" => {
331 extracted.expression = param
332 .value
333 .as_ref()
334 .and_then(|v| v.as_string())
335 .map(|s| s.to_string());
336 }
337 "validate" => {
338 extracted.validate = param
339 .value
340 .as_ref()
341 .and_then(|v| v.as_boolean())
342 .unwrap_or(false);
343 }
344 "variables" => {
345 if let Some(parts) = ¶m.part {
346 for part in parts {
347 if let Some(name) = &part.name.value {
348 let value = if let Some(val) = &part.value {
349 parameter_value_to_json_r4b(val)
350 } else {
351 Value::Null
352 };
353
354 extracted.variables.push(Variable {
355 name: name.to_string(),
356 value,
357 });
358 }
359 }
360 }
361 }
362 "resource" => {
363 extracted.resource = param
364 .resource
365 .as_ref()
366 .and_then(|r| serde_json::to_value(r).ok());
367 }
368 "terminologyServer" => {
369 extracted.terminology_server = param
370 .value
371 .as_ref()
372 .and_then(|v| v.as_string())
373 .map(|s| s.to_string());
374 }
375 _ => {}
376 }
377 }
378 Ok(())
379}
380
381#[cfg(feature = "R4B")]
382fn parameter_value_to_json_r4b(value: &helios_fhir::r4b::ParametersParameterValue) -> Value {
383 match value {
384 helios_fhir::r4b::ParametersParameterValue::String(s) => s
385 .value
386 .as_ref()
387 .map(|v| Value::String(v.clone()))
388 .unwrap_or(Value::Null),
389 helios_fhir::r4b::ParametersParameterValue::Boolean(b) => {
390 b.value.map(Value::Bool).unwrap_or(Value::Null)
391 }
392 helios_fhir::r4b::ParametersParameterValue::Integer(i) => i
393 .value
394 .map(|v| Value::Number(serde_json::Number::from(v)))
395 .unwrap_or(Value::Null),
396 helios_fhir::r4b::ParametersParameterValue::Decimal(d) => {
397 serde_json::to_value(d).unwrap_or(Value::Null)
398 }
399 _ => serde_json::to_value(value).unwrap_or(Value::Null),
400 }
401}
402
403#[cfg(feature = "R5")]
404fn extract_parameters_from_r5(
405 parameters: helios_fhir::r5::Parameters,
406 extracted: &mut ExtractedParameters,
407) -> Result<(), String> {
408 for param in parameters.parameter.unwrap_or_default() {
409 let name = param.name.value.as_deref().unwrap_or("");
410
411 match name {
412 "context" => {
413 extracted.context = param
414 .value
415 .as_ref()
416 .and_then(|v| v.as_string())
417 .map(|s| s.to_string());
418 }
419 "expression" => {
420 extracted.expression = param
421 .value
422 .as_ref()
423 .and_then(|v| v.as_string())
424 .map(|s| s.to_string());
425 }
426 "validate" => {
427 extracted.validate = param
428 .value
429 .as_ref()
430 .and_then(|v| v.as_boolean())
431 .unwrap_or(false);
432 }
433 "variables" => {
434 if let Some(parts) = ¶m.part {
435 for part in parts {
436 if let Some(name) = &part.name.value {
437 let value = if let Some(val) = &part.value {
438 parameter_value_to_json_r5(val)
439 } else {
440 Value::Null
441 };
442
443 extracted.variables.push(Variable {
444 name: name.to_string(),
445 value,
446 });
447 }
448 }
449 }
450 }
451 "resource" => {
452 extracted.resource = param
453 .resource
454 .as_ref()
455 .and_then(|r| serde_json::to_value(r).ok());
456 }
457 "terminologyServer" => {
458 extracted.terminology_server = param
459 .value
460 .as_ref()
461 .and_then(|v| v.as_string())
462 .map(|s| s.to_string());
463 }
464 _ => {}
465 }
466 }
467 Ok(())
468}
469
470#[cfg(feature = "R5")]
471fn parameter_value_to_json_r5(value: &helios_fhir::r5::ParametersParameterValue) -> Value {
472 match value {
473 helios_fhir::r5::ParametersParameterValue::String(s) => s
474 .value
475 .as_ref()
476 .map(|v| Value::String(v.clone()))
477 .unwrap_or(Value::Null),
478 helios_fhir::r5::ParametersParameterValue::Boolean(b) => {
479 b.value.map(Value::Bool).unwrap_or(Value::Null)
480 }
481 helios_fhir::r5::ParametersParameterValue::Integer(i) => i
482 .value
483 .map(|v| Value::Number(serde_json::Number::from(v)))
484 .unwrap_or(Value::Null),
485 helios_fhir::r5::ParametersParameterValue::Decimal(d) => {
486 serde_json::to_value(d).unwrap_or(Value::Null)
487 }
488 _ => serde_json::to_value(value).unwrap_or(Value::Null),
489 }
490}
491
492#[cfg(feature = "R6")]
493fn extract_parameters_from_r6(
494 parameters: helios_fhir::r6::Parameters,
495 extracted: &mut ExtractedParameters,
496) -> Result<(), String> {
497 for param in parameters.parameter.unwrap_or_default() {
498 let name = param.name.value.as_deref().unwrap_or("");
499
500 match name {
501 "context" => {
502 extracted.context = param
503 .value
504 .as_ref()
505 .and_then(|v| v.as_string())
506 .map(|s| s.to_string());
507 }
508 "expression" => {
509 extracted.expression = param
510 .value
511 .as_ref()
512 .and_then(|v| v.as_string())
513 .map(|s| s.to_string());
514 }
515 "validate" => {
516 extracted.validate = param
517 .value
518 .as_ref()
519 .and_then(|v| v.as_boolean())
520 .unwrap_or(false);
521 }
522 "variables" => {
523 if let Some(parts) = ¶m.part {
524 for part in parts {
525 if let Some(name) = &part.name.value {
526 let value = if let Some(val) = &part.value {
527 parameter_value_to_json_r6(val)
528 } else {
529 Value::Null
530 };
531
532 extracted.variables.push(Variable {
533 name: name.to_string(),
534 value,
535 });
536 }
537 }
538 }
539 }
540 "resource" => {
541 extracted.resource = param
542 .resource
543 .as_ref()
544 .and_then(|r| serde_json::to_value(r).ok());
545 }
546 "terminologyServer" => {
547 extracted.terminology_server = param
548 .value
549 .as_ref()
550 .and_then(|v| v.as_string())
551 .map(|s| s.to_string());
552 }
553 _ => {}
554 }
555 }
556 Ok(())
557}
558
559#[cfg(feature = "R6")]
560fn parameter_value_to_json_r6(value: &helios_fhir::r6::ParametersParameterValue) -> Value {
561 match value {
562 helios_fhir::r6::ParametersParameterValue::String(s) => s
563 .value
564 .as_ref()
565 .map(|v| Value::String(v.clone()))
566 .unwrap_or(Value::Null),
567 helios_fhir::r6::ParametersParameterValue::Boolean(b) => {
568 b.value.map(Value::Bool).unwrap_or(Value::Null)
569 }
570 helios_fhir::r6::ParametersParameterValue::Integer(i) => i
571 .value
572 .map(|v| Value::Number(serde_json::Number::from(v)))
573 .unwrap_or(Value::Null),
574 helios_fhir::r6::ParametersParameterValue::Decimal(d) => {
575 serde_json::to_value(d).unwrap_or(Value::Null)
576 }
577 _ => serde_json::to_value(value).unwrap_or(Value::Null),
578 }
579}