Skip to main content

haste_artifacts/
search_parameters.rs

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