rh_codegen/generators/
import_manager.rs1use crate::generators::naming_manager::NamingManager;
7use crate::generators::type_registry::TypeRegistry;
8use crate::rust_types::{RustStruct, RustType};
9use std::collections::HashSet;
10
11pub struct ImportManager;
13
14impl ImportManager {
15 pub fn collect_custom_types_from_struct(
17 rust_struct: &RustStruct,
18 imports: &mut HashSet<String>,
19 structs_in_file: &HashSet<String>,
20 ) {
21 if let Some(base_def) = &rust_struct.base_definition {
23 let base_type = base_def.split('/').next_back().unwrap_or(base_def);
24 if !Self::is_primitive_type(base_type)
26 && base_type != rust_struct.name
27 && !structs_in_file.contains(base_type)
28 {
29 let proper_base_type = if base_type
32 .chars()
33 .all(|c| c.is_lowercase() || c.is_numeric())
34 {
35 crate::naming::Naming::capitalize_first(base_type)
37 } else {
38 base_type.to_string()
40 };
41 let import_path = Self::get_import_path_for_type(&proper_base_type);
42 imports.insert(import_path);
43 }
44 }
45
46 for field in &rust_struct.fields {
48 Self::collect_custom_types_from_type(
49 &field.field_type,
50 imports,
51 &rust_struct.name,
52 structs_in_file,
53 );
54 }
55 }
56
57 pub fn collect_custom_types_from_trait(
59 rust_trait: &crate::rust_types::RustTrait,
60 imports: &mut HashSet<String>,
61 ) {
62 for super_trait in &rust_trait.super_traits {
64 if !Self::is_primitive_type(super_trait) {
65 let import_path = if Self::is_fhir_trait_type(super_trait) {
68 let base_name = if super_trait.ends_with("Accessors") {
71 super_trait.strip_suffix("Accessors").unwrap()
72 } else if super_trait.ends_with("Mutators") {
73 super_trait.strip_suffix("Mutators").unwrap()
74 } else if super_trait.ends_with("Existence") {
75 super_trait.strip_suffix("Existence").unwrap()
76 } else {
77 super_trait
78 };
79
80 format!(
81 "crate::traits::{}::{}",
82 crate::naming::Naming::to_snake_case(base_name),
83 super_trait
84 )
85 } else if Self::is_fhir_resource_type(super_trait) {
86 format!(
87 "crate::traits::{}::{}",
88 crate::naming::Naming::to_snake_case(super_trait),
89 super_trait
90 )
91 } else {
92 Self::get_import_path_for_type(super_trait)
93 };
94 imports.insert(import_path);
95 }
96 }
97
98 for method in &rust_trait.methods {
100 if let Some(return_type) = &method.return_type {
102 Self::collect_custom_types_from_type(
103 return_type,
104 imports,
105 &rust_trait.name,
106 &HashSet::new(), );
108 }
109
110 for param in &method.params {
112 Self::collect_custom_types_from_type(
113 ¶m.param_type,
114 imports,
115 &rust_trait.name,
116 &HashSet::new(),
117 );
118 }
119 }
120 }
121
122 pub fn collect_custom_types_from_type(
124 rust_type: &RustType,
125 imports: &mut HashSet<String>,
126 current_struct_name: &str,
127 structs_in_file: &HashSet<String>,
128 ) {
129 match rust_type {
130 RustType::Custom(type_name) => {
131 if Self::is_fhir_primitive_type(type_name) {
133 let import_path = Self::get_fhir_primitive_import_path(type_name);
134 imports.insert(import_path);
135 }
136 else if !Self::is_primitive_type(type_name)
138 && type_name != current_struct_name
139 && !structs_in_file.contains(type_name)
140 {
141 if TypeRegistry::get_classification(type_name).is_some() {
143 let import_path = Self::get_import_path_for_type(type_name);
145 imports.insert(import_path);
146 }
147 }
150 }
151 RustType::Option(inner) => {
152 Self::collect_custom_types_from_type(
153 inner,
154 imports,
155 current_struct_name,
156 structs_in_file,
157 );
158 }
159 RustType::Vec(inner) => {
160 Self::collect_custom_types_from_type(
161 inner,
162 imports,
163 current_struct_name,
164 structs_in_file,
165 );
166 }
167 RustType::Box(inner) => {
168 Self::collect_custom_types_from_type(
169 inner,
170 imports,
171 current_struct_name,
172 structs_in_file,
173 );
174 }
175 RustType::Slice(inner) => {
176 Self::collect_custom_types_from_type(
177 inner,
178 imports,
179 current_struct_name,
180 structs_in_file,
181 );
182 }
183 RustType::Reference(name) => {
184 if Self::is_fhir_primitive_type(name) {
186 let import_path = Self::get_fhir_primitive_import_path(name);
187 imports.insert(import_path);
188 }
189 else if !Self::is_primitive_type(name)
191 && name != current_struct_name
192 && !structs_in_file.contains(name)
193 {
194 if TypeRegistry::get_classification(name).is_some() {
196 let import_path = Self::get_import_path_for_type(name);
197 imports.insert(import_path);
198 }
199 }
202 }
203 RustType::String | RustType::Integer | RustType::Boolean | RustType::Float => {}
205 }
206 }
207
208 pub fn get_import_path_for_type(type_name: &str) -> String {
210 TypeRegistry::get_import_path_for_type(type_name)
212 }
213
214 pub fn is_fhir_resource_type(type_name: &str) -> bool {
216 NamingManager::is_fhir_resource(type_name)
217 }
218
219 pub fn is_fhir_trait_type(type_name: &str) -> bool {
221 type_name.ends_with("Accessors")
222 || type_name.ends_with("Mutators")
223 || type_name.ends_with("Existence")
224 }
225
226 pub fn is_fhir_datatype(type_name: &str) -> bool {
228 NamingManager::is_fhir_datatype(type_name)
229 }
230
231 pub fn is_fhir_primitive_type(type_name: &str) -> bool {
233 matches!(
235 type_name,
236 "StringType"
237 | "BooleanType"
238 | "IntegerType"
239 | "DecimalType"
240 | "UriType"
241 | "UrlType"
242 | "CanonicalType"
243 | "OidType"
244 | "UuidType"
245 | "InstantType"
246 | "DateType"
247 | "DateTimeType"
248 | "TimeType"
249 | "CodeType"
250 | "IdType"
251 | "MarkdownType"
252 | "Base64BinaryType"
253 | "UnsignedIntType"
254 | "PositiveIntType"
255 | "XhtmlType"
256 | "String"
258 | "Boolean"
259 | "Integer"
260 | "Decimal"
261 | "Uri"
262 | "Url"
263 | "Canonical"
264 | "Oid"
265 | "Uuid"
266 | "Instant"
267 | "Date"
268 | "DateTime"
269 | "Time"
270 | "Code"
271 | "Id"
272 | "Markdown"
273 | "Base64Binary"
274 | "UnsignedInt"
275 | "PositiveInt"
276 | "Xhtml"
277 )
278 }
279
280 pub fn get_fhir_primitive_import_path(type_name: &str) -> String {
282 let module_name = match type_name {
283 "StringType" => "string",
284 "BooleanType" => "boolean",
285 "IntegerType" => "integer",
286 "DecimalType" => "decimal",
287 "UriType" => "uri",
288 "UrlType" => "url",
289 "CanonicalType" => "canonical",
290 "OidType" => "oid",
291 "UuidType" => "uuid",
292 "InstantType" => "instant",
293 "DateType" => "date",
294 "DateTimeType" => "date_time",
295 "TimeType" => "time",
296 "CodeType" => "code",
297 "IdType" => "id",
298 "MarkdownType" => "markdown",
299 "Base64BinaryType" => "base64binary",
300 "UnsignedIntType" => "unsigned_int",
301 "PositiveIntType" => "positive_int",
302 "XhtmlType" => "xhtml",
303 _ => "unknown",
304 };
305 format!("crate::primitives::{module_name}::{type_name}")
306 }
307
308 pub fn is_generated_trait(type_name: &str) -> bool {
310 let lower_name = type_name.to_lowercase();
312 lower_name.ends_with("trait")
313 || lower_name.ends_with("accessors")
314 || lower_name.ends_with("mutators")
315 || lower_name.ends_with("helpers")
316 || matches!(
317 lower_name.as_str(),
318 "resourcetrait"
319 | "domainresourcetrait"
320 | "backboneelementtrait"
321 | "elementtrait"
322 | "metadataresourcetrait"
323 | "resourceaccessors"
324 | "domainresourceaccessors"
325 | "backboneelementaccessors"
326 | "elementaccessors"
327 | "metadataresourceaccessors"
328 )
329 }
330
331 pub fn is_primitive_type(type_name: &str) -> bool {
333 matches!(
334 type_name,
335 "String"
336 | "&str"
337 | "str"
338 | "i32"
339 | "u32"
340 | "i64"
341 | "u64"
342 | "f32"
343 | "f64"
344 | "bool"
345 | "usize"
346 | "isize"
347 | "Self" )
349 }
350}
351
352#[cfg(test)]
353mod tests {
354 use super::*;
355
356 #[test]
357 fn test_import_classification() {
358 assert!(ImportManager::is_fhir_resource_type("DomainResource"));
360 assert!(ImportManager::is_fhir_resource_type("Patient"));
361 assert!(ImportManager::is_fhir_resource_type("ActivityDefinition"));
362 assert!(!ImportManager::is_fhir_resource_type("Identifier"));
363
364 assert!(ImportManager::is_fhir_datatype("Identifier"));
366 assert!(ImportManager::is_fhir_datatype("CodeableConcept"));
367 assert!(ImportManager::is_fhir_datatype("Reference"));
368 assert!(!ImportManager::is_fhir_datatype("DomainResource"));
369
370 assert_eq!(
372 ImportManager::get_import_path_for_type("DomainResource"),
373 "crate::resources::domain_resource::DomainResource"
374 );
375 assert_eq!(
376 ImportManager::get_import_path_for_type("Identifier"),
377 "crate::datatypes::identifier::Identifier"
378 );
379 assert_eq!(
380 ImportManager::get_import_path_for_type("PublicationStatus"),
381 "crate::bindings::publication_status::PublicationStatus"
382 );
383 }
384
385 #[test]
386 fn test_primitive_type_detection() {
387 assert!(ImportManager::is_primitive_type("String"));
388 assert!(ImportManager::is_primitive_type("i32"));
389 assert!(ImportManager::is_primitive_type("bool"));
390 assert!(!ImportManager::is_primitive_type("Patient"));
391 assert!(!ImportManager::is_primitive_type("Identifier"));
392 }
393
394 #[test]
395 fn test_nested_structure_detection() {
396 assert_eq!(
398 ImportManager::get_import_path_for_type("EvidenceVariableCharacteristic"),
399 "crate::resources::evidence_variable::EvidenceVariableCharacteristic"
400 );
401 assert_eq!(
402 ImportManager::get_import_path_for_type("MeasureReportGroup"),
403 "crate::resources::measure_report::MeasureReportGroup"
404 );
405 assert_eq!(
406 ImportManager::get_import_path_for_type("AccountCoverage"),
407 "crate::resources::account::AccountCoverage"
408 );
409 assert_eq!(
410 ImportManager::get_import_path_for_type("AccountGuarantor"),
411 "crate::resources::account::AccountGuarantor"
412 );
413 assert_eq!(
414 ImportManager::get_import_path_for_type("BundleEntry"),
415 "crate::resources::bundle::BundleEntry"
416 );
417 assert_eq!(
418 ImportManager::get_import_path_for_type("ImplementationGuideGlobal"),
419 "crate::resources::implementation_guide::ImplementationGuideGlobal"
420 );
421
422 assert_eq!(
424 ImportManager::get_import_path_for_type("ConditionStage"),
425 "crate::resources::condition::ConditionStage"
426 );
427
428 assert_eq!(
430 ImportManager::get_import_path_for_type("Patient"),
431 "crate::resources::patient::Patient"
432 );
433 assert_eq!(
434 ImportManager::get_import_path_for_type("Identifier"),
435 "crate::datatypes::identifier::Identifier"
436 );
437 }
438}