fhirpathrs 0.1.0

Fhirpath implementation in Rust
Documentation
use serde_json::Value;
use crate::error::FhirpathError;
use crate::evaluate::{Evaluate, EvaluateResult};
use crate::evaluate::nodes::resource_node::{FhirContext, ResourceNode};
use crate::evaluate::utils::get_string;
use crate::parser::expression::Expression;

const DEFAULT_TRACE: fn(String, Value) = |name: String, output_result: Value|  println!("TRACE:[{}] {}", name.clone(), output_result.clone());

fn call_trace(ctx: &FhirContext, name: String, value: Value) -> EvaluateResult<()> {
    match ctx.trace_function {
        Some(ref f) => {
            let mut func = f.lock().map_err(|e| {
                FhirpathError::EvaluateError {
                    msg: format!("Could not obtain lock on trace function: {}", e),
                }
            })?;

            func(name.clone(), value);
        },
        None => DEFAULT_TRACE(name.clone(), value),
    };
    
    Ok(())
}

pub fn trace<'a, 'b>(
    input: &'a ResourceNode<'a, 'b>,
    expressions: &Vec<Box<Expression>>,
) -> EvaluateResult<ResourceNode<'a, 'b>> {
    let name_expr = expressions.first().ok_or(FhirpathError::EvaluateError {
        msg: "Trace expects at least one expression".to_string()
    })?;

    let name = get_string(&name_expr.evaluate(input)?.get_single()?)?;

    let projection_expr = expressions.iter().nth(1);

    let output_result = match projection_expr {
        None => input.data.clone(),
        Some(exp) => exp.evaluate(input)?.data,
    };

    call_trace(input.context, name.clone(), output_result.clone())?;
    
    Ok(ResourceNode::from_node(input, input.data.clone()))
}

#[cfg(test)]
mod test {
    use std::collections::HashMap;
    use std::sync::{Arc, Mutex};
    use serde_json::{json, Value};
    use crate::evaluate::EvaluateOptions;
    use crate::evaluate::test::test::{run_tests, Expected, TestCase};

    #[test]
    fn test_trace_path() {
        let patient = json!({ "resourceType": "Patient", "a": [{ "b": true }] });

        let test_cases: Vec<TestCase> = vec![
            TestCase {
                path: "Patient.a.trace('test', b).b.anyTrue()".to_string(),
                input: patient.clone(),
                expected: Expected::Value(json!([true])),
                options: None,
            },
        ];

        run_tests(test_cases);
    }

    #[test]
    fn test_custom_trace_path() {
        let patient = json!({ "resourceType": "Patient", "a": [{ "b": true }] });

        let mut map: HashMap<String, Value> = HashMap::new();

        {
            let mut trace_function = |name: String, trace_details: Value| {
                map.insert(name, trace_details);
            };

            let test_cases: Vec<TestCase> = vec![
                TestCase {
                    path: "Patient.a.trace('test', b).b.anyTrue()".to_string(),
                    input: patient.clone(),
                    expected: Expected::Value(json!([true])),
                    options: Some(EvaluateOptions {
                        model: None,
                        vars: None,
                        now: None,
                        trace_function: Some(Arc::new(Mutex::new(&mut trace_function))),
                    }),
                },
            ];

            run_tests(test_cases);
        }

        assert_eq!(map.get("test").unwrap().to_owned(),  json!([true]));
    }
}