haste_artifacts/
search_parameters.rs

1use haste_fhir_model::r4::generated::resources::{Resource, ResourceType, SearchParameter};
2use once_cell::sync::Lazy;
3use rust_embed::Embed;
4use std::{collections::HashMap, sync::Arc};
5
6#[derive(Debug)]
7pub enum ArtifactError {
8    InvalidResource(String),
9}
10
11pub struct SearchParametersIndex {
12    by_url: HashMap<String, Arc<SearchParameter>>,
13    by_resource_type: HashMap<String, HashMap<String, Arc<SearchParameter>>>,
14}
15
16impl Default for SearchParametersIndex {
17    fn default() -> Self {
18        SearchParametersIndex {
19            by_url: HashMap::new(),
20            by_resource_type: HashMap::new(),
21        }
22    }
23}
24
25fn index_parameter(
26    index: &mut SearchParametersIndex,
27    resource: Resource,
28) -> Result<(), ArtifactError> {
29    match resource {
30        Resource::Bundle(bundle) => {
31            let params = bundle
32                .entry
33                .unwrap_or(vec![])
34                .into_iter()
35                .flat_map(|e| e.resource)
36                .filter_map(|resource| match *resource {
37                    Resource::SearchParameter(search_param) => Some(Arc::new(search_param)),
38                    _ => None,
39                });
40
41            for param in params {
42                index
43                    .by_url
44                    .insert(param.id.clone().unwrap(), param.clone());
45                for resource_type in &param.base {
46                    let resource_type: Option<String> = (&**resource_type).into();
47                    if let Some(resource_type) = resource_type {
48                        index
49                            .by_resource_type
50                            .entry(resource_type)
51                            .or_default()
52                            .insert(
53                                param.code.value.as_ref().unwrap().to_string(),
54                                param.clone(),
55                            );
56                    }
57                }
58            }
59
60            Ok(())
61        }
62        Resource::SearchParameter(search_param) => {
63            let param = Arc::new(search_param);
64            index
65                .by_url
66                .insert(param.id.clone().unwrap(), param.clone());
67            for resource_type in &param.base {
68                let resource_type: Option<String> = (&**resource_type).into();
69                if let Some(resource_type) = resource_type.as_ref() {
70                    index
71                        .by_resource_type
72                        .entry(resource_type.to_string())
73                        .or_default()
74                        .insert(
75                            param.code.value.as_ref().unwrap().to_string(),
76                            param.clone(),
77                        );
78                }
79            }
80            Ok(())
81        }
82        _ => Err(ArtifactError::InvalidResource(
83            "Expected a Bundle resource".to_string(),
84        )),
85    }
86}
87
88#[derive(Embed)]
89#[folder = "./artifacts/r4"]
90#[include = "haste_health/search_parameter/*.json"]
91#[include = "hl7/minified/search-parameters.min.json"]
92
93struct EmbededSearchParameterAssets;
94
95static R4_SEARCH_PARAMETERS: Lazy<SearchParametersIndex> = Lazy::new(|| {
96    let mut index = SearchParametersIndex::default();
97
98    for path in EmbededSearchParameterAssets::iter() {
99        let data = EmbededSearchParameterAssets::get(path.as_ref()).unwrap();
100        let bundle = haste_fhir_serialization_json::from_str::<Resource>(
101            std::str::from_utf8(&data.data).unwrap(),
102        )
103        .expect("Failed to parse search parameters JSON");
104        index_parameter(&mut index, bundle).expect("Failed to extract search parameters");
105    }
106    index
107});
108
109pub fn get_all_search_parameters() -> Vec<Arc<SearchParameter>> {
110    R4_SEARCH_PARAMETERS
111        .by_url
112        .values()
113        .cloned()
114        .collect::<Vec<_>>()
115}
116
117pub fn get_search_parameters_for_resource(
118    resource_type: &ResourceType,
119) -> Vec<Arc<SearchParameter>> {
120    let resource_params = R4_SEARCH_PARAMETERS
121        .by_resource_type
122        .get("Resource")
123        .unwrap();
124    let domain_params = R4_SEARCH_PARAMETERS
125        .by_resource_type
126        .get("DomainResource")
127        .unwrap();
128    let mut return_vec = Vec::new();
129    return_vec.extend(resource_params.values().cloned());
130    return_vec.extend(domain_params.values().cloned());
131
132    if let Some(params) = R4_SEARCH_PARAMETERS
133        .by_resource_type
134        .get(resource_type.as_ref())
135    {
136        return_vec.extend(params.values().cloned());
137    }
138
139    return_vec
140}
141
142pub fn get_search_parameter_for_name(
143    resource_type: Option<&ResourceType>,
144    name: &str,
145) -> Option<Arc<SearchParameter>> {
146    resource_type
147        .and_then(|resource_type| {
148            R4_SEARCH_PARAMETERS
149                .by_resource_type
150                .get(resource_type.as_ref())
151        })
152        .and_then(|params| params.get(name))
153        .or_else(|| {
154            R4_SEARCH_PARAMETERS
155                .by_resource_type
156                .get("Resource")
157                .and_then(|params| params.get(name))
158        })
159        .or_else(|| {
160            R4_SEARCH_PARAMETERS
161                .by_resource_type
162                .get("DomainResource")
163                .and_then(|params| params.get(name))
164        })
165        .cloned()
166}