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