fhirpathrs 0.1.0

Fhirpath implementation in Rust
Documentation
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use chrono::{DateTime, Utc};
use serde_json::{json, Value};

use crate::{
    error::FhirpathError,
    evaluate::{
        data_types::type_info::{TypeDetails, TypeInfo},
        reflection::{get_reflection_type, ReflectionType},
        EvaluateResult,
    },
    models::ModelDetails,
};

pub struct FhirContext<'a> {
    pub model: Option<ModelDetails>,
    pub vars: HashMap<String, Value>,
    pub now: DateTime<Utc>,
    pub trace_function: Option<Arc<Mutex<&'a mut dyn FnMut(String, Value) -> ()>>>,
}

#[derive(Clone, Debug)]
pub struct PathDetails {
    pub path: String,
    pub fhir_type: Option<String>,
    pub extensible: bool
}

#[derive(Clone)]
pub struct ResourceNode<'a, 'b> {
    pub data_root: &'a Value,
    pub data: Value,
    pub context: &'a FhirContext<'b>,
    pub path: Option<String>,
    pub fhir_types: Vec<Option<PathDetails>>,
    pub reflection_types: Vec<Option<ReflectionType>>,
    pub resource_context: Option<ResourceContext>,
}

#[derive(Clone, Debug)]
pub struct ResourceContext {
    pub index: Option<usize>,
    pub total: Option<Value>,
}

impl<'a, 'b> ResourceNode<'a, 'b> {
    pub fn new(
        data_root: &'a Value,
        data: Value,
        context: &'a FhirContext<'b>,
        path: Option<String>,
        fhir_types: Vec<Option<PathDetails>>,
        resource_context: Option<ResourceContext>,
        reflection_types: Vec<Option<ReflectionType>>,
    ) -> Self {
        let node_data = match data {
            Value::Array(array) => json!(array),
            Value::Bool(boolean) => json!([boolean]),
            Value::Number(num) => json!([num]),
            Value::Object(obj) => json!([obj]),
            Value::Null => json!([]),
            Value::String(string) => json!([string]),
        };

        Self {
            data_root,
            data: node_data,
            context,
            path,
            fhir_types,
            resource_context,
            reflection_types,
        }
    }

    pub fn from_node(node: &'a ResourceNode<'a, 'b>, data: Value) -> Self {
        Self::new(
            node.data_root,
            data,
            node.context,
            node.path.clone(),
            node.fhir_types.clone(),
            None,
            node.reflection_types.clone(),
        )
    }

    pub fn is_empty(&self) -> EvaluateResult<bool> {
        match &self.data {
            Value::Array(array) => Ok(array.len() == 0),
            _ => Err(FhirpathError::EvaluateError {
                msg: "Data must be a Value::Array".to_string(),
            }),
        }
    }

    pub fn is_single(&self) -> EvaluateResult<bool> {
        match &self.data {
            Value::Array(array) => Ok(array.len() == 1),
            _ => Err(FhirpathError::EvaluateError {
                msg: "Data must be a Value::Array".to_string(),
            }),
        }
    }

    pub fn get_single(&self) -> EvaluateResult<Value> {
        if !self.is_single()? {
            return Err(FhirpathError::EvaluateError {
                msg: "Expected single value for node".to_string(),
            });
        }

        match &self.data {
            Value::Array(array) => {
                let first = array.first().ok_or_else(|| FhirpathError::EvaluateError {
                    msg: "Expected single value for node".to_string(),
                })?;

                Ok(first.clone())
            }
            _ => Err(FhirpathError::EvaluateError {
                msg: "Data must be a Value::Array".to_string(),
            }),
        }
    }

    pub fn get_single_or_empty(&self) -> EvaluateResult<Option<Value>> {
        match &self.data {
            Value::Array(array) => {
                if array.len() > 1 {
                    return Err(FhirpathError::EvaluateError {
                        msg: "Expected single value for node".to_string(),
                    })
                }
                
                Ok(array.first().cloned())
            },
            _ => Err(FhirpathError::EvaluateError {
                msg: "Data must be a Value::Array".to_string(),
            }),
        }
    }

    pub fn get_array(&self) -> EvaluateResult<&Vec<Value>> {
        match &self.data {
            Value::Array(array) => Ok(array),
            _ => Err(FhirpathError::EvaluateError {
                msg: "Data must be a Value::Array".to_string(),
            }),
        }
    }

    pub fn get_var(&self, var_name: &String) -> Option<Value> {
        self.context.vars.get(var_name).cloned()
    }

    pub fn get_type_info(&self) -> Vec<Option<TypeInfo>> {
        self.fhir_types
            .iter()
            .map(|pd| {
                pd.as_ref().and_then(|path_details| {
                    TypeInfo::try_from(&TypeDetails {
                        fhir_type: path_details.fhir_type.clone(),
                        model: &self.context.model,
                    })
                    .ok()
                })
            })
            .collect()
    }

    pub fn get_reflection_types(&self) -> Vec<Option<ReflectionType>> {
        self.get_array()
            .unwrap_or(&vec![])
            .iter()
            .enumerate()
            .map(|(index, value)| {
                let pd = &self.fhir_types.iter().nth(index).unwrap_or(&None);

                get_reflection_type(*pd, value, &self.context.model)
            })
            .collect()
    }

    pub fn get_index(&self) -> Option<usize> {
        let index = self.resource_context.as_ref().and_then(|rc| rc.index);

        index.clone()
    }

    pub fn get_total(&self) -> Option<Value> {
        self.resource_context
            .as_ref()
            .and_then(|rc| rc.total.clone())
    }
}