Skip to main content

haste_fhir_search/memory/
mod.rs

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