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_or(super_trait)
72 } else if super_trait.ends_with("Mutators") {
73 super_trait.strip_suffix("Mutators").unwrap_or(super_trait)
74 } else if super_trait.ends_with("Existence") {
75 super_trait.strip_suffix("Existence").unwrap_or(super_trait)
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()
146 || NamingManager::detect_nested_structure_parent(type_name).is_some()
147 {
148 let import_path = Self::get_import_path_for_type(type_name);
149 imports.insert(import_path);
150 }
151 }
152 }
153 RustType::Option(inner) => {
154 Self::collect_custom_types_from_type(
155 inner,
156 imports,
157 current_struct_name,
158 structs_in_file,
159 );
160 }
161 RustType::Vec(inner) => {
162 Self::collect_custom_types_from_type(
163 inner,
164 imports,
165 current_struct_name,
166 structs_in_file,
167 );
168 }
169 RustType::Box(inner) => {
170 Self::collect_custom_types_from_type(
171 inner,
172 imports,
173 current_struct_name,
174 structs_in_file,
175 );
176 }
177 RustType::Slice(inner) => {
178 Self::collect_custom_types_from_type(
179 inner,
180 imports,
181 current_struct_name,
182 structs_in_file,
183 );
184 }
185 RustType::Reference(name) => {
186 if Self::is_fhir_primitive_type(name) {
188 let import_path = Self::get_fhir_primitive_import_path(name);
189 imports.insert(import_path);
190 }
191 else if !Self::is_primitive_type(name)
193 && name != current_struct_name
194 && !structs_in_file.contains(name)
195 && (TypeRegistry::get_classification(name).is_some()
196 || NamingManager::detect_nested_structure_parent(name).is_some())
197 {
198 let import_path = Self::get_import_path_for_type(name);
199 imports.insert(import_path);
200 }
201 }
202 RustType::String | RustType::Integer | RustType::Boolean | RustType::Float => {}
204 }
205 }
206
207 pub fn get_import_path_for_type(type_name: &str) -> String {
209 TypeRegistry::get_import_path_for_type(type_name)
211 }
212
213 pub fn is_fhir_resource_type(type_name: &str) -> bool {
215 NamingManager::is_fhir_resource(type_name)
216 }
217
218 pub fn is_fhir_trait_type(type_name: &str) -> bool {
220 type_name.ends_with("Accessors")
221 || type_name.ends_with("Mutators")
222 || type_name.ends_with("Existence")
223 }
224
225 pub fn is_fhir_datatype(type_name: &str) -> bool {
227 NamingManager::is_fhir_datatype(type_name)
228 }
229
230 pub fn is_fhir_primitive_type(type_name: &str) -> bool {
232 matches!(
234 type_name,
235 "StringType"
236 | "BooleanType"
237 | "IntegerType"
238 | "DecimalType"
239 | "UriType"
240 | "UrlType"
241 | "CanonicalType"
242 | "OidType"
243 | "UuidType"
244 | "InstantType"
245 | "DateType"
246 | "DateTimeType"
247 | "TimeType"
248 | "CodeType"
249 | "IdType"
250 | "MarkdownType"
251 | "Base64BinaryType"
252 | "UnsignedIntType"
253 | "PositiveIntType"
254 | "Integer64Type"
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 "Integer64Type" => "integer64",
303 "XhtmlType" => "xhtml",
304 _ => "unknown",
305 };
306 format!("crate::primitives::{module_name}::{type_name}")
307 }
308
309 pub fn is_generated_trait(type_name: &str) -> bool {
311 let lower_name = type_name.to_lowercase();
313 lower_name.ends_with("trait")
314 || lower_name.ends_with("accessors")
315 || lower_name.ends_with("mutators")
316 || lower_name.ends_with("helpers")
317 || matches!(
318 lower_name.as_str(),
319 "resourcetrait"
320 | "domainresourcetrait"
321 | "backboneelementtrait"
322 | "elementtrait"
323 | "metadataresourcetrait"
324 | "resourceaccessors"
325 | "domainresourceaccessors"
326 | "backboneelementaccessors"
327 | "elementaccessors"
328 | "metadataresourceaccessors"
329 )
330 }
331
332 pub fn is_primitive_type(type_name: &str) -> bool {
334 matches!(
335 type_name,
336 "String"
337 | "&str"
338 | "str"
339 | "i32"
340 | "u32"
341 | "i64"
342 | "u64"
343 | "f32"
344 | "f64"
345 | "bool"
346 | "usize"
347 | "isize"
348 | "Self" )
350 }
351}
352
353#[cfg(test)]
354mod tests {
355 use super::*;
356
357 #[test]
358 fn test_import_classification() {
359 assert!(ImportManager::is_fhir_resource_type("DomainResource"));
361 assert!(ImportManager::is_fhir_resource_type("Patient"));
362 assert!(ImportManager::is_fhir_resource_type("ActivityDefinition"));
363 assert!(!ImportManager::is_fhir_resource_type("Identifier"));
364
365 assert!(ImportManager::is_fhir_datatype("Identifier"));
367 assert!(ImportManager::is_fhir_datatype("CodeableConcept"));
368 assert!(ImportManager::is_fhir_datatype("Reference"));
369 assert!(!ImportManager::is_fhir_datatype("DomainResource"));
370
371 assert_eq!(
373 ImportManager::get_import_path_for_type("DomainResource"),
374 "crate::resources::domain_resource::DomainResource"
375 );
376 assert_eq!(
377 ImportManager::get_import_path_for_type("Identifier"),
378 "crate::datatypes::identifier::Identifier"
379 );
380 assert_eq!(
381 ImportManager::get_import_path_for_type("PublicationStatus"),
382 "crate::bindings::publication_status::PublicationStatus"
383 );
384 }
385
386 #[test]
387 fn test_primitive_type_detection() {
388 assert!(ImportManager::is_primitive_type("String"));
389 assert!(ImportManager::is_primitive_type("i32"));
390 assert!(ImportManager::is_primitive_type("bool"));
391 assert!(!ImportManager::is_primitive_type("Patient"));
392 assert!(!ImportManager::is_primitive_type("Identifier"));
393 }
394
395 #[test]
396 fn test_nested_structure_detection() {
397 assert_eq!(
399 ImportManager::get_import_path_for_type("EvidenceVariableCharacteristic"),
400 "crate::resources::evidence_variable::EvidenceVariableCharacteristic"
401 );
402 assert_eq!(
403 ImportManager::get_import_path_for_type("MeasureReportGroup"),
404 "crate::resources::measure_report::MeasureReportGroup"
405 );
406 assert_eq!(
407 ImportManager::get_import_path_for_type("AccountCoverage"),
408 "crate::resources::account::AccountCoverage"
409 );
410 assert_eq!(
411 ImportManager::get_import_path_for_type("AccountGuarantor"),
412 "crate::resources::account::AccountGuarantor"
413 );
414 assert_eq!(
415 ImportManager::get_import_path_for_type("BundleEntry"),
416 "crate::resources::bundle::BundleEntry"
417 );
418 assert_eq!(
419 ImportManager::get_import_path_for_type("ImplementationGuideGlobal"),
420 "crate::resources::implementation_guide::ImplementationGuideGlobal"
421 );
422
423 assert_eq!(
425 ImportManager::get_import_path_for_type("ConditionStage"),
426 "crate::resources::condition::ConditionStage"
427 );
428
429 assert_eq!(
431 ImportManager::get_import_path_for_type("Patient"),
432 "crate::resources::patient::Patient"
433 );
434 assert_eq!(
435 ImportManager::get_import_path_for_type("Identifier"),
436 "crate::datatypes::identifier::Identifier"
437 );
438 }
439}