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(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 ¶m.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 ¶m.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}