Skip to main content

rh_codegen/generators/
trait_impl_generator.rs

1//! Trait implementation generation functionality
2//!
3//! This module handles the generation of trait implementations for FHIR resources.
4
5use super::utils::GeneratorUtils;
6use crate::fhir_types::StructureDefinition;
7use crate::naming::Naming;
8use crate::rust_types::{RustTraitImpl, RustTraitImplMethod};
9use crate::CodegenResult;
10
11/// Generator for trait implementations
12pub struct TraitImplGenerator;
13
14#[allow(dead_code)]
15impl TraitImplGenerator {
16    /// Create a new trait implementation generator
17    pub fn new() -> Self {
18        Self
19    }
20
21    /// Extract the base resource type from a FHIR baseDefinition URL
22    /// For example: "http://hl7.org/fhir/StructureDefinition/Group" -> "Group"
23    fn extract_base_resource_type(base_definition: &str) -> Option<String> {
24        // FHIR baseDefinition URLs follow the pattern:
25        // http://hl7.org/fhir/StructureDefinition/{ResourceType}
26        if base_definition.starts_with("http://hl7.org/fhir/StructureDefinition/") {
27            if let Some(last_segment) = base_definition.split('/').next_back() {
28                return Some(last_segment.to_string());
29            }
30        }
31        None
32    }
33
34    /// Check if a baseDefinition indicates this is a core FHIR resource
35    /// Core resources inherit directly from Resource or DomainResource
36    fn is_core_resource(base_definition: &str) -> bool {
37        matches!(
38            base_definition,
39            "http://hl7.org/fhir/StructureDefinition/Resource"
40                | "http://hl7.org/fhir/StructureDefinition/DomainResource"
41        )
42    }
43
44    /// Get the core resource type that a profile ultimately inherits from
45    /// This handles known profile inheritance chains and can be extended with dynamic loading
46    fn resolve_to_core_resource_type(
47        base_resource_type: &str,
48        _base_definition_url: &str,
49    ) -> String {
50        // For now, use hardcoded mapping for common profiles
51        // TODO: Implement dynamic StructureDefinition loading
52        match base_resource_type.to_lowercase().as_str() {
53            // VitalSigns is a profile on Observation
54            "vitalsigns" => "Observation".to_string(),
55            // BodyWeight, BodyHeight, etc. are profiles on VitalSigns -> Observation
56            "bodyweight" | "bodyheight" | "bmi" | "bodytemp" | "heartrate" | "resprate"
57            | "oxygensat" => "Observation".to_string(),
58            // Add other known profile chains here as needed
59            _ => {
60                // If it's already a core resource type, return it
61                if GeneratorUtils::is_fhir_resource_type(base_resource_type) {
62                    base_resource_type.to_string()
63                } else {
64                    // Unknown profile - return the original name as fallback
65                    base_resource_type.to_string()
66                }
67            }
68        }
69    }
70
71    /// Get the appropriate resource type for a structure definition
72    /// For profiles, returns the base resource type; for core resources, returns the struct name
73    fn get_resource_type_for_struct(
74        struct_name: &str,
75        structure_def: &StructureDefinition,
76    ) -> String {
77        // Check if this has a baseDefinition
78        if let Some(base_def) = &structure_def.base_definition {
79            // If it's a core resource (inherits from Resource/DomainResource), use the struct name
80            if Self::is_core_resource(base_def) {
81                return struct_name.to_string();
82            }
83
84            // If it's a profile (inherits from another resource), extract the base resource type
85            if let Some(base_resource_type) = Self::extract_base_resource_type(base_def) {
86                // Resolve the base resource type to a core resource type
87                let core_resource_type =
88                    Self::resolve_to_core_resource_type(&base_resource_type, base_def);
89
90                // Only return the core resource type if it's actually a known FHIR resource type
91                if GeneratorUtils::is_fhir_resource_type(&core_resource_type) {
92                    return core_resource_type;
93                }
94            }
95        }
96
97        // Fallback to the struct name if no baseDefinition or couldn't extract
98        struct_name.to_string()
99    }
100
101    /// Generate trait implementations for a FHIR resource
102    pub fn generate_trait_impls(
103        &self,
104        structure_def: &StructureDefinition,
105    ) -> CodegenResult<Vec<RustTraitImpl>> {
106        let mut trait_impls = Vec::new();
107
108        // Skip non-resource types
109        if structure_def.kind != "resource" {
110            return Ok(trait_impls);
111        }
112
113        let struct_name = Naming::struct_name(structure_def);
114
115        // Generate Resource trait implementations (Accessors, Mutators, Existence)
116        trait_impls.push(self.generate_resource_trait_impl(&struct_name, structure_def));
117        trait_impls.push(self.generate_resource_mutators_trait_impl(&struct_name, structure_def));
118        trait_impls.push(self.generate_resource_existence_trait_impl(&struct_name, structure_def));
119
120        // Generate DomainResource trait implementations for domain resources
121        if let Some(base_def) = &structure_def.base_definition {
122            if base_def.contains("DomainResource") {
123                trait_impls.push(self.generate_domain_resource_trait_impl(&struct_name));
124                trait_impls.push(self.generate_domain_resource_mutators_trait_impl(&struct_name));
125                trait_impls.push(self.generate_domain_resource_existence_trait_impl(&struct_name));
126            }
127        }
128
129        // Generate specific resource trait implementation (e.g., PatientTrait for Patient)
130        // Skip this for Resource itself to avoid conflicting implementations
131        if struct_name != "Resource" {
132            let specific_trait_impl =
133                self.generate_specific_resource_trait_impl(&struct_name, structure_def);
134
135            // Only include specific trait impl if it has methods
136            if !specific_trait_impl.is_empty() {
137                trait_impls.push(specific_trait_impl);
138            }
139
140            // Generate specific mutators trait implementation
141            let specific_mutators_trait_impl =
142                self.generate_specific_resource_mutators_trait_impl(&struct_name, structure_def);
143
144            if !specific_mutators_trait_impl.is_empty() {
145                trait_impls.push(specific_mutators_trait_impl);
146            }
147
148            // Generate specific existence trait implementation
149            let specific_existence_trait_impl =
150                self.generate_specific_resource_existence_trait_impl(&struct_name, structure_def);
151
152            if !specific_existence_trait_impl.is_empty() {
153                trait_impls.push(specific_existence_trait_impl);
154            }
155        }
156
157        Ok(trait_impls)
158    }
159
160    /// Generate Resource trait implementation
161    fn generate_resource_trait_impl(
162        &self,
163        struct_name: &str,
164        structure_def: &StructureDefinition,
165    ) -> RustTraitImpl {
166        let mut trait_impl = RustTraitImpl::new(
167            "crate::traits::resource::ResourceAccessors".to_string(),
168            struct_name.to_string(),
169        );
170
171        // Determine the base access pattern based on the inheritance chain
172        let (base_access, use_trait_methods) =
173            self.get_resource_base_access(struct_name, structure_def);
174
175        // id method
176        let id_method = RustTraitImplMethod::new("id".to_string())
177            .with_return_type("Option<String>".to_string())
178            .with_body(if use_trait_methods {
179                format!("{base_access}.id()")
180            } else {
181                format!("{base_access}.id.clone()")
182            });
183        trait_impl.add_method(id_method);
184
185        // meta method
186        let meta_method = RustTraitImplMethod::new("meta".to_string())
187            .with_return_type("Option<crate::datatypes::meta::Meta>".to_string())
188            .with_body(if use_trait_methods {
189                format!("{base_access}.meta()")
190            } else {
191                format!("{base_access}.meta.clone()")
192            });
193        trait_impl.add_method(meta_method);
194
195        // implicit_rules method
196        let implicit_rules_method = RustTraitImplMethod::new("implicit_rules".to_string())
197            .with_return_type("Option<String>".to_string())
198            .with_body(if use_trait_methods {
199                format!("{base_access}.implicit_rules()")
200            } else {
201                format!("{base_access}.implicit_rules.clone()")
202            });
203        trait_impl.add_method(implicit_rules_method);
204
205        // language method
206        let language_method = RustTraitImplMethod::new("language".to_string())
207            .with_return_type("Option<String>".to_string())
208            .with_body(if use_trait_methods {
209                format!("{base_access}.language()")
210            } else {
211                format!("{base_access}.language.clone()")
212            });
213        trait_impl.add_method(language_method);
214
215        trait_impl
216    }
217
218    /// Get the base access pattern for resource fields
219    fn get_resource_base_access(
220        &self,
221        struct_name: &str,
222        structure_def: &StructureDefinition,
223    ) -> (String, bool) {
224        if struct_name == "Resource" {
225            // For Resource itself, access fields directly
226            ("self".to_string(), false)
227        } else if struct_name == "DomainResource" {
228            // For DomainResource, access Resource fields directly
229            ("self.base".to_string(), false)
230        } else if let Some(base_def) = &structure_def.base_definition {
231            if base_def.contains("DomainResource") {
232                // For core resources that inherit from DomainResource - access fields directly
233                ("self.base.base".to_string(), false)
234            } else if base_def.contains("Resource") && struct_name != "DomainResource" {
235                // For resources that inherit directly from Resource - access fields directly
236                ("self.base".to_string(), false)
237            } else if base_def.starts_with("http://hl7.org/fhir/StructureDefinition/") {
238                // This is a profile of another resource - delegate to trait method
239                ("self.base".to_string(), true)
240            } else {
241                // Default case - treat as core resource
242                ("self.base.base".to_string(), false)
243            }
244        } else {
245            // Default case when no baseDefinition - treat as core resource
246            ("self.base.base".to_string(), false)
247        }
248    }
249
250    /// Generate DomainResource trait implementation
251    fn generate_domain_resource_trait_impl(&self, struct_name: &str) -> RustTraitImpl {
252        let mut trait_impl = RustTraitImpl::new(
253            "crate::traits::domain_resource::DomainResourceAccessors".to_string(),
254            struct_name.to_string(),
255        );
256
257        // text method
258        let text_method = RustTraitImplMethod::new("text".to_string())
259            .with_return_type("Option<crate::datatypes::narrative::Narrative>".to_string())
260            .with_body("self.base.text.clone()".to_string());
261        trait_impl.add_method(text_method);
262
263        // contained method
264        let contained_method = RustTraitImplMethod::new("contained".to_string())
265            .with_return_type("&[crate::resources::resource::Resource]".to_string())
266            .with_body("self.base.contained.as_deref().unwrap_or(&[])".to_string());
267        trait_impl.add_method(contained_method);
268
269        // extension method
270        let extension_method = RustTraitImplMethod::new("extension".to_string())
271            .with_return_type("&[crate::datatypes::extension::Extension]".to_string())
272            .with_body("self.base.extension.as_deref().unwrap_or(&[])".to_string());
273        trait_impl.add_method(extension_method);
274
275        // modifier_extension method
276        let modifier_extension_method = RustTraitImplMethod::new("modifier_extension".to_string())
277            .with_return_type("&[crate::datatypes::extension::Extension]".to_string())
278            .with_body("self.base.modifier_extension.as_deref().unwrap_or(&[])".to_string());
279        trait_impl.add_method(modifier_extension_method);
280
281        trait_impl
282    }
283
284    /// Generate Resource mutators trait implementation
285    fn generate_resource_mutators_trait_impl(
286        &self,
287        struct_name: &str,
288        structure_def: &StructureDefinition,
289    ) -> RustTraitImpl {
290        let mut trait_impl = RustTraitImpl::new(
291            "crate::traits::resource::ResourceMutators".to_string(),
292            struct_name.to_string(),
293        );
294
295        // Determine the base access pattern based on the inheritance chain
296        let (base_access, use_trait_methods) =
297            self.get_resource_base_access(struct_name, structure_def);
298
299        // Transform base_access for use after cloning self into resource
300        // "self" -> "" (direct field access)
301        // "self.base" -> "base"
302        // "self.base.base" -> "base.base"
303        let resource_access = if base_access == "self" {
304            String::new()
305        } else {
306            base_access
307                .strip_prefix("self.")
308                .unwrap_or(&base_access)
309                .to_string()
310        };
311
312        // new method
313        let new_method = RustTraitImplMethod::new("new".to_string())
314            .with_return_type("Self".to_string())
315            .with_body("Self::default()".to_string())
316            .with_self_param(None); // No self parameter for constructor
317        trait_impl.add_method(new_method);
318
319        // For profiles that extend other profiles, delegate through trait methods
320        // For core resources, access fields directly
321        if use_trait_methods {
322            // set_id method - delegate to base's set_id
323            let set_id_method = RustTraitImplMethod::new("set_id".to_string())
324                .with_param(crate::rust_types::RustMethodParam::new(
325                    "value".to_string(),
326                    crate::rust_types::RustType::String,
327                ))
328                .with_return_type("Self".to_string())
329                .with_body("let mut resource = self.clone();\n        resource.base = resource.base.set_id(value);\n        resource".to_string())
330                .with_self_param(Some("self".to_string()));
331            trait_impl.add_method(set_id_method);
332
333            // set_meta method - delegate to base's set_meta
334            let set_meta_method = RustTraitImplMethod::new("set_meta".to_string())
335                .with_param(crate::rust_types::RustMethodParam::new(
336                    "value".to_string(),
337                    crate::rust_types::RustType::Custom("crate::datatypes::meta::Meta".to_string()),
338                ))
339                .with_return_type("Self".to_string())
340                .with_body("let mut resource = self.clone();\n        resource.base = resource.base.set_meta(value);\n        resource".to_string())
341                .with_self_param(Some("self".to_string()));
342            trait_impl.add_method(set_meta_method);
343
344            // set_implicit_rules method - delegate to base's set_implicit_rules
345            let set_implicit_rules_method = RustTraitImplMethod::new("set_implicit_rules".to_string())
346                .with_param(crate::rust_types::RustMethodParam::new(
347                    "value".to_string(),
348                    crate::rust_types::RustType::String,
349                ))
350                .with_return_type("Self".to_string())
351                .with_body("let mut resource = self.clone();\n        resource.base = resource.base.set_implicit_rules(value);\n        resource".to_string())
352                .with_self_param(Some("self".to_string()));
353            trait_impl.add_method(set_implicit_rules_method);
354
355            // set_language method - delegate to base's set_language
356            let set_language_method = RustTraitImplMethod::new("set_language".to_string())
357                .with_param(crate::rust_types::RustMethodParam::new(
358                    "value".to_string(),
359                    crate::rust_types::RustType::String,
360                ))
361                .with_return_type("Self".to_string())
362                .with_body("let mut resource = self.clone();\n        resource.base = resource.base.set_language(value);\n        resource".to_string())
363                .with_self_param(Some("self".to_string()));
364            trait_impl.add_method(set_language_method);
365        } else {
366            // set_id method - direct field access
367            let set_id_method = RustTraitImplMethod::new("set_id".to_string())
368                .with_param(crate::rust_types::RustMethodParam::new(
369                    "value".to_string(),
370                    crate::rust_types::RustType::String,
371                ))
372                .with_return_type("Self".to_string())
373                .with_body(if resource_access.is_empty() {
374                    "let mut resource = self.clone();\n        resource.id = Some(value);\n        resource".to_string()
375                } else {
376                    format!(
377                        "let mut resource = self.clone();\n        resource.{resource_access}.id = Some(value);\n        resource"
378                    )
379                })
380                .with_self_param(Some("self".to_string())); // Take self by value
381            trait_impl.add_method(set_id_method);
382
383            // set_meta method - direct field access
384            let set_meta_method = RustTraitImplMethod::new("set_meta".to_string())
385                .with_param(crate::rust_types::RustMethodParam::new(
386                    "value".to_string(),
387                    crate::rust_types::RustType::Custom("crate::datatypes::meta::Meta".to_string()),
388                ))
389                .with_return_type("Self".to_string())
390                .with_body(if resource_access.is_empty() {
391                    "let mut resource = self.clone();\n        resource.meta = Some(value);\n        resource".to_string()
392                } else {
393                    format!(
394                        "let mut resource = self.clone();\n        resource.{resource_access}.meta = Some(value);\n        resource"
395                    )
396                })
397                .with_self_param(Some("self".to_string()));
398            trait_impl.add_method(set_meta_method);
399
400            // set_implicit_rules method - direct field access
401            let set_implicit_rules_method = RustTraitImplMethod::new("set_implicit_rules".to_string())
402                .with_param(crate::rust_types::RustMethodParam::new(
403                    "value".to_string(),
404                    crate::rust_types::RustType::String,
405                ))
406                .with_return_type("Self".to_string())
407                .with_body(if resource_access.is_empty() {
408                    "let mut resource = self.clone();\n        resource.implicit_rules = Some(value);\n        resource".to_string()
409                } else {
410                    format!(
411                        "let mut resource = self.clone();\n        resource.{resource_access}.implicit_rules = Some(value);\n        resource"
412                    )
413                })
414                .with_self_param(Some("self".to_string()));
415            trait_impl.add_method(set_implicit_rules_method);
416
417            // set_language method - direct field access
418            let set_language_method = RustTraitImplMethod::new("set_language".to_string())
419                .with_param(crate::rust_types::RustMethodParam::new(
420                    "value".to_string(),
421                    crate::rust_types::RustType::String,
422                ))
423                .with_return_type("Self".to_string())
424                .with_body(if resource_access.is_empty() {
425                    "let mut resource = self.clone();\n        resource.language = Some(value);\n        resource".to_string()
426                } else {
427                    format!(
428                        "let mut resource = self.clone();\n        resource.{resource_access}.language = Some(value);\n        resource"
429                    )
430                })
431                .with_self_param(Some("self".to_string()));
432            trait_impl.add_method(set_language_method);
433        }
434
435        trait_impl
436    }
437
438    /// Generate Resource existence trait implementation
439    fn generate_resource_existence_trait_impl(
440        &self,
441        struct_name: &str,
442        structure_def: &StructureDefinition,
443    ) -> RustTraitImpl {
444        let mut trait_impl = RustTraitImpl::new(
445            "crate::traits::resource::ResourceExistence".to_string(),
446            struct_name.to_string(),
447        );
448
449        // Determine the base access pattern based on the inheritance chain
450        let (base_access, use_trait_methods) =
451            self.get_resource_base_access(struct_name, structure_def);
452
453        // has_id method
454        let has_id_method = RustTraitImplMethod::new("has_id".to_string())
455            .with_return_type("bool".to_string())
456            .with_body(if use_trait_methods {
457                format!("{base_access}.has_id()")
458            } else {
459                format!("{base_access}.id.is_some()")
460            });
461        trait_impl.add_method(has_id_method);
462
463        // has_meta method
464        let has_meta_method = RustTraitImplMethod::new("has_meta".to_string())
465            .with_return_type("bool".to_string())
466            .with_body(if use_trait_methods {
467                format!("{base_access}.has_meta()")
468            } else {
469                format!("{base_access}.meta.is_some()")
470            });
471        trait_impl.add_method(has_meta_method);
472
473        // has_implicit_rules method
474        let has_implicit_rules_method = RustTraitImplMethod::new("has_implicit_rules".to_string())
475            .with_return_type("bool".to_string())
476            .with_body(if use_trait_methods {
477                format!("{base_access}.has_implicit_rules()")
478            } else {
479                format!("{base_access}.implicit_rules.is_some()")
480            });
481        trait_impl.add_method(has_implicit_rules_method);
482
483        // has_language method
484        let has_language_method = RustTraitImplMethod::new("has_language".to_string())
485            .with_return_type("bool".to_string())
486            .with_body(if use_trait_methods {
487                format!("{base_access}.has_language()")
488            } else {
489                format!("{base_access}.language.is_some()")
490            });
491        trait_impl.add_method(has_language_method);
492
493        trait_impl
494    }
495
496    /// Generate DomainResource mutators trait implementation
497    fn generate_domain_resource_mutators_trait_impl(&self, struct_name: &str) -> RustTraitImpl {
498        let mut trait_impl = RustTraitImpl::new(
499            "crate::traits::domain_resource::DomainResourceMutators".to_string(),
500            struct_name.to_string(),
501        );
502
503        // new method
504        let new_method = RustTraitImplMethod::new("new".to_string())
505            .with_return_type("Self".to_string())
506            .with_body("Self::default()".to_string())
507            .with_self_param(None); // No self for constructor
508        trait_impl.add_method(new_method);
509
510        // set_text method
511        let set_text_method = RustTraitImplMethod::new("set_text".to_string())
512            .with_param(crate::rust_types::RustMethodParam::new(
513                "value".to_string(),
514                crate::rust_types::RustType::Custom("crate::datatypes::narrative::Narrative".to_string()),
515            ))
516            .with_return_type("Self".to_string())
517            .with_body("let mut resource = self.clone();\n        resource.base.text = Some(value);\n        resource".to_string())
518            .with_self_param(Some("self".to_string()));
519        trait_impl.add_method(set_text_method);
520
521        // set_contained method
522        let set_contained_method = RustTraitImplMethod::new("set_contained".to_string())
523            .with_param(crate::rust_types::RustMethodParam::new(
524                "value".to_string(),
525                crate::rust_types::RustType::Vec(Box::new(crate::rust_types::RustType::Custom("crate::resources::resource::Resource".to_string()))),
526            ))
527            .with_return_type("Self".to_string())
528            .with_body("let mut resource = self.clone();\n        resource.base.contained = Some(value);\n        resource".to_string())
529            .with_self_param(Some("self".to_string()));
530        trait_impl.add_method(set_contained_method);
531
532        // add_contained method
533        let add_contained_method = RustTraitImplMethod::new("add_contained".to_string())
534            .with_param(crate::rust_types::RustMethodParam::new(
535                "item".to_string(),
536                crate::rust_types::RustType::Custom("crate::resources::resource::Resource".to_string()),
537            ))
538            .with_return_type("Self".to_string())
539            .with_body("let mut resource = self.clone();\n        resource.base.contained.get_or_insert_with(Vec::new).push(item);\n        resource".to_string())
540            .with_self_param(Some("self".to_string()));
541        trait_impl.add_method(add_contained_method);
542
543        // set_extension method
544        let set_extension_method = RustTraitImplMethod::new("set_extension".to_string())
545            .with_param(crate::rust_types::RustMethodParam::new(
546                "value".to_string(),
547                crate::rust_types::RustType::Vec(Box::new(crate::rust_types::RustType::Custom("crate::datatypes::extension::Extension".to_string()))),
548            ))
549            .with_return_type("Self".to_string())
550            .with_body("let mut resource = self.clone();\n        resource.base.extension = Some(value);\n        resource".to_string())
551            .with_self_param(Some("self".to_string()));
552        trait_impl.add_method(set_extension_method);
553
554        // add_extension method
555        let add_extension_method = RustTraitImplMethod::new("add_extension".to_string())
556            .with_param(crate::rust_types::RustMethodParam::new(
557                "item".to_string(),
558                crate::rust_types::RustType::Custom("crate::datatypes::extension::Extension".to_string()),
559            ))
560            .with_return_type("Self".to_string())
561            .with_body("let mut resource = self.clone();\n        resource.base.extension.get_or_insert_with(Vec::new).push(item);\n        resource".to_string())
562            .with_self_param(Some("self".to_string()));
563        trait_impl.add_method(add_extension_method);
564
565        // set_modifier_extension method
566        let set_modifier_extension_method = RustTraitImplMethod::new("set_modifier_extension".to_string())
567            .with_param(crate::rust_types::RustMethodParam::new(
568                "value".to_string(),
569                crate::rust_types::RustType::Vec(Box::new(crate::rust_types::RustType::Custom("crate::datatypes::extension::Extension".to_string()))),
570            ))
571            .with_return_type("Self".to_string())
572            .with_body("let mut resource = self.clone();\n        resource.base.modifier_extension = Some(value);\n        resource".to_string())
573            .with_self_param(Some("self".to_string()));
574        trait_impl.add_method(set_modifier_extension_method);
575
576        // add_modifier_extension method
577        let add_modifier_extension_method =
578            RustTraitImplMethod::new("add_modifier_extension".to_string())
579                .with_param(crate::rust_types::RustMethodParam::new(
580                    "item".to_string(),
581                    crate::rust_types::RustType::Custom("crate::datatypes::extension::Extension".to_string()),
582                ))
583                .with_return_type("Self".to_string())
584                .with_body("let mut resource = self.clone();\n        resource.base.modifier_extension.get_or_insert_with(Vec::new).push(item);\n        resource".to_string())
585                .with_self_param(Some("self".to_string()));
586        trait_impl.add_method(add_modifier_extension_method);
587
588        trait_impl
589    }
590
591    /// Generate DomainResource existence trait implementation
592    fn generate_domain_resource_existence_trait_impl(&self, struct_name: &str) -> RustTraitImpl {
593        let mut trait_impl = RustTraitImpl::new(
594            "crate::traits::domain_resource::DomainResourceExistence".to_string(),
595            struct_name.to_string(),
596        );
597
598        // Duplicate methods from ResourceExistence (required by trait inheritance)
599        // has_id method
600        let has_id_method = RustTraitImplMethod::new("has_id".to_string())
601            .with_return_type("bool".to_string())
602            .with_body("self.base.base.id.is_some()".to_string());
603        trait_impl.add_method(has_id_method);
604
605        // has_meta method
606        let has_meta_method = RustTraitImplMethod::new("has_meta".to_string())
607            .with_return_type("bool".to_string())
608            .with_body("self.base.base.meta.is_some()".to_string());
609        trait_impl.add_method(has_meta_method);
610
611        // has_implicit_rules method
612        let has_implicit_rules_method = RustTraitImplMethod::new("has_implicit_rules".to_string())
613            .with_return_type("bool".to_string())
614            .with_body("self.base.base.implicit_rules.is_some()".to_string());
615        trait_impl.add_method(has_implicit_rules_method);
616
617        // has_language method
618        let has_language_method = RustTraitImplMethod::new("has_language".to_string())
619            .with_return_type("bool".to_string())
620            .with_body("self.base.base.language.is_some()".to_string());
621        trait_impl.add_method(has_language_method);
622
623        // has_text method
624        let has_text_method = RustTraitImplMethod::new("has_text".to_string())
625            .with_return_type("bool".to_string())
626            .with_body("self.base.text.is_some()".to_string());
627        trait_impl.add_method(has_text_method);
628
629        // has_contained method
630        let has_contained_method = RustTraitImplMethod::new("has_contained".to_string())
631            .with_return_type("bool".to_string())
632            .with_body("self.base.contained.as_ref().is_some_and(|c| !c.is_empty())".to_string());
633        trait_impl.add_method(has_contained_method);
634
635        // has_extension method
636        let has_extension_method = RustTraitImplMethod::new("has_extension".to_string())
637            .with_return_type("bool".to_string())
638            .with_body("self.base.extension.as_ref().is_some_and(|e| !e.is_empty())".to_string());
639        trait_impl.add_method(has_extension_method);
640
641        // has_modifier_extension method
642        let has_modifier_extension_method =
643            RustTraitImplMethod::new("has_modifier_extension".to_string())
644                .with_return_type("bool".to_string())
645                .with_body(
646                    "self.base.modifier_extension.as_ref().is_some_and(|m| !m.is_empty())"
647                        .to_string(),
648                );
649        trait_impl.add_method(has_modifier_extension_method);
650
651        trait_impl
652    }
653
654    /// Generate specific resource trait implementation
655    fn generate_specific_resource_trait_impl(
656        &self,
657        struct_name: &str,
658        structure_def: &StructureDefinition,
659    ) -> RustTraitImpl {
660        let trait_name = format!(
661            "crate::traits::{}::{}Accessors",
662            crate::naming::Naming::to_snake_case(struct_name),
663            struct_name
664        );
665
666        let mut trait_impl = RustTraitImpl::new(trait_name, struct_name.to_string());
667
668        // Extract element definitions to generate trait methods
669        let elements = if let Some(differential) = &structure_def.differential {
670            &differential.element
671        } else if let Some(snapshot) = &structure_def.snapshot {
672            &snapshot.element
673        } else {
674            return trait_impl; // No elements to process
675        };
676
677        // Generate methods for all direct fields (matching accessor trait generator logic)
678        for element in elements {
679            if self.should_generate_accessor_impl(element, structure_def) {
680                if let Some(method) = self.generate_field_accessor_method(element) {
681                    trait_impl.add_method(method);
682                }
683            }
684        }
685
686        trait_impl
687    }
688
689    /// Generate specific resource mutators trait implementation
690    fn generate_specific_resource_mutators_trait_impl(
691        &self,
692        struct_name: &str,
693        structure_def: &StructureDefinition,
694    ) -> RustTraitImpl {
695        let trait_name = format!(
696            "crate::traits::{}::{}Mutators",
697            crate::naming::Naming::to_snake_case(struct_name),
698            struct_name
699        );
700
701        let mut trait_impl = RustTraitImpl::new(trait_name, struct_name.to_string());
702
703        // new method
704        let new_method = RustTraitImplMethod::new("new".to_string())
705            .with_return_type("Self".to_string())
706            .with_body("Self::default()".to_string())
707            .with_self_param(None); // No self for constructor
708        trait_impl.add_method(new_method);
709
710        // Extract element definitions to generate trait methods
711        let elements = if let Some(differential) = &structure_def.differential {
712            &differential.element
713        } else if let Some(snapshot) = &structure_def.snapshot {
714            &snapshot.element
715        } else {
716            return trait_impl; // No elements to process
717        };
718
719        // Generate mutator methods for all direct fields
720        for element in elements {
721            if self.should_generate_accessor_impl(element, structure_def) {
722                if let Some(methods) = self.generate_field_mutator_methods(element) {
723                    for method in methods {
724                        trait_impl.add_method(method);
725                    }
726                }
727            }
728        }
729
730        trait_impl
731    }
732
733    /// Generate specific resource existence trait implementation
734    fn generate_specific_resource_existence_trait_impl(
735        &self,
736        struct_name: &str,
737        structure_def: &StructureDefinition,
738    ) -> RustTraitImpl {
739        let trait_name = format!(
740            "crate::traits::{}::{}Existence",
741            crate::naming::Naming::to_snake_case(struct_name),
742            struct_name
743        );
744
745        let mut trait_impl = RustTraitImpl::new(trait_name, struct_name.to_string());
746
747        // Check if this is a profile - profiles extend ResourceExistence and shouldn't redefine inherited methods
748        let is_profile = crate::generators::type_registry::TypeRegistry::is_profile(structure_def);
749
750        // Check if this resource extends DomainResource (vs extending Resource directly)
751        let extends_domain_resource = structure_def
752            .base_definition
753            .as_ref()
754            .map(|base| base.ends_with("/DomainResource"))
755            .unwrap_or(false);
756
757        // Check if this resource extends Resource directly (not DomainResource)
758        let extends_resource_directly = structure_def
759            .base_definition
760            .as_ref()
761            .map(|base| base.ends_with("/Resource") && !base.ends_with("/DomainResource"))
762            .unwrap_or(false);
763
764        // Only add inherited methods for non-profiles
765        // Profiles extend ResourceExistence or DomainResourceExistence, so they inherit these methods automatically
766        if !is_profile {
767            // Determine the correct base access path based on inheritance
768            let (id_access, meta_access, implicit_rules_access, language_access) =
769                if extends_resource_directly {
770                    // Binary, Bundle, Parameters extend Resource directly -> self.base.id
771                    (
772                        "self.base.id.is_some()".to_string(),
773                        "self.base.meta.is_some()".to_string(),
774                        "self.base.implicit_rules.is_some()".to_string(),
775                        "self.base.language.is_some()".to_string(),
776                    )
777                } else {
778                    // Most resources extend DomainResource -> self.base.base.id
779                    (
780                        "self.base.base.id.is_some()".to_string(),
781                        "self.base.base.meta.is_some()".to_string(),
782                        "self.base.base.implicit_rules.is_some()".to_string(),
783                        "self.base.base.language.is_some()".to_string(),
784                    )
785                };
786
787            // Add inherited methods from ResourceExistence
788            // has_id method
789            let has_id_method = RustTraitImplMethod::new("has_id".to_string())
790                .with_return_type("bool".to_string())
791                .with_body(id_access);
792            trait_impl.add_method(has_id_method);
793
794            // has_meta method
795            let has_meta_method = RustTraitImplMethod::new("has_meta".to_string())
796                .with_return_type("bool".to_string())
797                .with_body(meta_access);
798            trait_impl.add_method(has_meta_method);
799
800            // has_implicit_rules method
801            let has_implicit_rules_method =
802                RustTraitImplMethod::new("has_implicit_rules".to_string())
803                    .with_return_type("bool".to_string())
804                    .with_body(implicit_rules_access);
805            trait_impl.add_method(has_implicit_rules_method);
806
807            // has_language method
808            let has_language_method = RustTraitImplMethod::new("has_language".to_string())
809                .with_return_type("bool".to_string())
810                .with_body(language_access);
811            trait_impl.add_method(has_language_method);
812
813            // Only add DomainResource-specific methods if this resource extends DomainResource
814            // Resources like Binary, Bundle, Parameters extend Resource directly and don't have these fields
815            if extends_domain_resource {
816                // Add inherited methods from DomainResourceExistence
817                // has_text method
818                let has_text_method = RustTraitImplMethod::new("has_text".to_string())
819                    .with_return_type("bool".to_string())
820                    .with_body("self.base.text.is_some()".to_string());
821                trait_impl.add_method(has_text_method);
822
823                // has_contained method
824                let has_contained_method = RustTraitImplMethod::new("has_contained".to_string())
825                    .with_return_type("bool".to_string())
826                    .with_body(
827                        "self.base.contained.as_ref().is_some_and(|c| !c.is_empty())".to_string(),
828                    );
829                trait_impl.add_method(has_contained_method);
830
831                // has_extension method
832                let has_extension_method = RustTraitImplMethod::new("has_extension".to_string())
833                    .with_return_type("bool".to_string())
834                    .with_body(
835                        "self.base.extension.as_ref().is_some_and(|e| !e.is_empty())".to_string(),
836                    );
837                trait_impl.add_method(has_extension_method);
838
839                // has_modifier_extension method
840                let has_modifier_extension_method =
841                    RustTraitImplMethod::new("has_modifier_extension".to_string())
842                        .with_return_type("bool".to_string())
843                        .with_body(
844                            "self.base.modifier_extension.as_ref().is_some_and(|m| !m.is_empty())"
845                                .to_string(),
846                        );
847                trait_impl.add_method(has_modifier_extension_method);
848            }
849        }
850
851        // Extract element definitions to generate trait methods for specific fields
852        let elements = if let Some(differential) = &structure_def.differential {
853            &differential.element
854        } else if let Some(snapshot) = &structure_def.snapshot {
855            &snapshot.element
856        } else {
857            return trait_impl; // No elements to process
858        };
859
860        // First, collect choice-type fields (those ending with [x])
861        let mut choice_fields = std::collections::HashSet::new();
862        for element in elements {
863            let path_parts: Vec<&str> = element.path.split('.').collect();
864            if path_parts.len() == 2 && path_parts[0] == structure_def.name {
865                let field_name = path_parts[1];
866                if field_name.ends_with("[x]") {
867                    choice_fields.insert(field_name.trim_end_matches("[x]").to_string());
868                }
869            }
870        }
871
872        // Generate existence check methods for choice types
873        for choice_field in &choice_fields {
874            // Find the element with this choice field name + [x]
875            let choice_path = format!("{}.{}[x]", structure_def.name, choice_field);
876            if let Some(choice_element) = elements.iter().find(|e| e.path == choice_path) {
877                if let Some(method) =
878                    self.generate_choice_type_existence_method(choice_field, choice_element)
879                {
880                    trait_impl.add_method(method);
881                }
882            }
883        }
884
885        // Generate existence check methods for all direct non-choice fields
886        for element in elements {
887            let path_parts: Vec<&str> = element.path.split('.').collect();
888            if path_parts.len() == 2 && path_parts[0] == structure_def.name {
889                let field_name = path_parts[1];
890                // Skip choice type fields (they were handled above)
891                if !field_name.ends_with("[x]")
892                    && self.should_generate_accessor_impl(element, structure_def)
893                {
894                    if let Some(method) = self.generate_field_existence_method(element) {
895                        trait_impl.add_method(method);
896                    }
897                }
898            }
899        }
900
901        trait_impl
902    }
903
904    /// Check if we should generate an accessor implementation for this element
905    /// This mirrors the logic in AccessorTraitGenerator::should_generate_accessor
906    fn should_generate_accessor_impl(
907        &self,
908        element: &crate::fhir_types::ElementDefinition,
909        structure_def: &StructureDefinition,
910    ) -> bool {
911        let field_path = &element.path;
912        let base_name = &structure_def.name;
913
914        // The path must start with the base name of the structure.
915        if !field_path.starts_with(base_name) {
916            return false;
917        }
918
919        // We are interested in direct fields of the resource, which have paths like "Patient.active".
920        // Splitting by '.' should result in exactly two parts.
921        let path_parts: Vec<&str> = field_path.split('.').collect();
922        if path_parts.len() != 2 {
923            return false;
924        }
925
926        // The first part must match the base name.
927        if path_parts[0] != base_name {
928            return false;
929        }
930
931        // We don't generate accessors for choice types here, they are handled separately.
932        let field_name = path_parts[1];
933        !field_name.ends_with("[x]")
934    }
935
936    /// Generate a field accessor method for a trait implementation
937    fn generate_field_accessor_method(
938        &self,
939        element: &crate::fhir_types::ElementDefinition,
940    ) -> Option<RustTraitImplMethod> {
941        use crate::config::CodegenConfig;
942        use crate::type_mapper::TypeMapper;
943        use crate::value_sets::ValueSetManager;
944
945        let path_parts: Vec<&str> = element.path.split('.').collect();
946        let field_name = path_parts.last()?.to_string();
947        let rust_field_name = crate::naming::Naming::field_name(&field_name);
948
949        let is_array = element.max.as_deref() == Some("*")
950            || element
951                .max
952                .as_deref()
953                .unwrap_or("1")
954                .parse::<i32>()
955                .unwrap_or(1)
956                > 1;
957
958        // Check if field is optional based on minimum cardinality
959        let is_optional = element.min.unwrap_or(0) == 0;
960
961        // Create TypeMapper for consistent type resolution
962        let config = CodegenConfig::default();
963        let mut value_set_manager = ValueSetManager::new();
964        let mut type_mapper = TypeMapper::new(&config, &mut value_set_manager);
965
966        // Get the FHIR types for this element
967        let fhir_types = element.element_type.as_ref()?;
968
969        // Check if this is a BackboneElement that should use a specific nested type
970        let rust_type = if self.is_backbone_element(fhir_types) {
971            self.get_nested_type_for_backbone_element(element, is_array)
972        } else {
973            // Map FHIR type to Rust type using TypeMapper with binding information
974            type_mapper.map_fhir_type_with_binding(fhir_types, element.binding.as_ref(), is_array)
975        };
976
977        // Generate return type and body based on the mapped type
978        let (return_type, body) = if is_array {
979            // For arrays, return slice references
980            let inner_type = match &rust_type {
981                crate::rust_types::RustType::Vec(inner) => inner.to_string(),
982                crate::rust_types::RustType::Option(inner) => {
983                    if let crate::rust_types::RustType::Vec(vec_inner) = inner.as_ref() {
984                        vec_inner.to_string()
985                    } else {
986                        inner.to_string()
987                    }
988                }
989                _ => rust_type.to_string(),
990            };
991
992            let return_type = format!("&[{inner_type}]");
993            let body = if is_optional {
994                // Optional array: Option<Vec<T>> -> use as_deref()
995                format!("self.{rust_field_name}.as_deref().unwrap_or(&[])")
996            } else {
997                // Required array: Vec<T> -> use as_ref() or direct reference
998                format!("&self.{rust_field_name}")
999            };
1000            (return_type, body)
1001        } else {
1002            // For non-arrays, consider optionality based on cardinality
1003            if is_optional {
1004                // Field is optional (min cardinality is 0), return Option<T>
1005                let inner_type = match &rust_type {
1006                    crate::rust_types::RustType::Option(inner) => inner.to_string(),
1007                    _ => rust_type.to_string(),
1008                };
1009                let return_type = format!("Option<{inner_type}>");
1010
1011                // For Copy types, don't clone - they're automatically copied
1012                let body = if self.is_copy_type(&rust_type) {
1013                    format!("self.{rust_field_name}")
1014                } else {
1015                    format!("self.{rust_field_name}.clone()")
1016                };
1017
1018                (return_type, body)
1019            } else {
1020                // Field is required (min cardinality is 1+), return T directly
1021                let return_type = match &rust_type {
1022                    crate::rust_types::RustType::Option(inner) => inner.to_string(),
1023                    _ => rust_type.to_string(),
1024                };
1025
1026                // For Copy types, don't clone - they're automatically copied
1027                let body = if self.is_copy_type(&rust_type) {
1028                    format!("self.{rust_field_name}")
1029                } else {
1030                    format!("self.{rust_field_name}.clone()")
1031                };
1032
1033                (return_type, body)
1034            }
1035        };
1036
1037        Some(
1038            RustTraitImplMethod::new(rust_field_name)
1039                .with_return_type(return_type)
1040                .with_body(body),
1041        )
1042    }
1043
1044    /// Generate field mutator methods for a trait implementation (returns both set and add for arrays)
1045    fn generate_field_mutator_methods(
1046        &self,
1047        element: &crate::fhir_types::ElementDefinition,
1048    ) -> Option<Vec<RustTraitImplMethod>> {
1049        let path_parts: Vec<&str> = element.path.split('.').collect();
1050        let field_name = path_parts.last()?.to_string();
1051        let rust_field_name = crate::naming::Naming::field_name(&field_name);
1052
1053        let is_array = element.max.as_deref() == Some("*")
1054            || element
1055                .max
1056                .as_deref()
1057                .unwrap_or("1")
1058                .parse::<i32>()
1059                .unwrap_or(1)
1060                > 1;
1061
1062        // Check if field is optional based on minimum cardinality
1063        let is_optional = element.min.unwrap_or(0) == 0;
1064
1065        // Use binding-aware type mapping to get the correct Rust type
1066        let rust_type = self.get_field_rust_type(element, &field_name).ok()?;
1067
1068        let mut methods = Vec::new();
1069
1070        // Generate methods based on array vs single value
1071        if is_array {
1072            // For arrays, generate both set_xxx and add_xxx methods
1073            let inner_type = match &rust_type {
1074                crate::rust_types::RustType::Vec(inner) => inner.to_string(),
1075                crate::rust_types::RustType::Option(inner) => {
1076                    if let crate::rust_types::RustType::Vec(vec_inner) = inner.as_ref() {
1077                        vec_inner.to_string()
1078                    } else {
1079                        inner.to_string()
1080                    }
1081                }
1082                _ => rust_type.to_string(),
1083            };
1084
1085            // set_xxx method
1086            let set_method_name = format!("set_{rust_field_name}");
1087            let set_body = if is_optional {
1088                format!(
1089                    "let mut resource = self.clone();\n        resource.{rust_field_name} = Some(value);\n        resource"
1090                )
1091            } else {
1092                format!(
1093                    "let mut resource = self.clone();\n        resource.{rust_field_name} = value;\n        resource"
1094                )
1095            };
1096
1097            methods.push(
1098                RustTraitImplMethod::new(set_method_name)
1099                    .with_param(crate::rust_types::RustMethodParam::new(
1100                        "value".to_string(),
1101                        crate::rust_types::RustType::Vec(Box::new(
1102                            crate::rust_types::RustType::Custom(inner_type.clone()),
1103                        )),
1104                    ))
1105                    .with_return_type("Self".to_string())
1106                    .with_body(set_body)
1107                    .with_self_param(Some("self".to_string())),
1108            );
1109
1110            // add_xxx method
1111            let add_method_name = format!("add_{rust_field_name}");
1112            let add_body = if is_optional {
1113                format!(
1114                    "let mut resource = self.clone();\n        resource.{rust_field_name}.get_or_insert_with(Vec::new).push(item);\n        resource"
1115                )
1116            } else {
1117                format!(
1118                    "let mut resource = self.clone();\n        resource.{rust_field_name}.push(item);\n        resource"
1119                )
1120            };
1121
1122            methods.push(
1123                RustTraitImplMethod::new(add_method_name)
1124                    .with_param(crate::rust_types::RustMethodParam::new(
1125                        "item".to_string(),
1126                        crate::rust_types::RustType::Custom(inner_type),
1127                    ))
1128                    .with_return_type("Self".to_string())
1129                    .with_body(add_body)
1130                    .with_self_param(Some("self".to_string())),
1131            );
1132        } else {
1133            // For single values, generate set_xxx method only
1134            let method_name = format!("set_{rust_field_name}");
1135            let inner_type = match &rust_type {
1136                crate::rust_types::RustType::Option(inner) => inner.to_string(),
1137                _ => rust_type.to_string(),
1138            };
1139
1140            // Generate the setter body based on field optionality
1141            let body = if is_optional {
1142                // Optional field: wrap value in Some()
1143                format!(
1144                    "let mut resource = self.clone();\n        resource.{rust_field_name} = Some(value);\n        resource"
1145                )
1146            } else {
1147                // Required field: assign value directly without Some()
1148                format!(
1149                    "let mut resource = self.clone();\n        resource.{rust_field_name} = value;\n        resource"
1150                )
1151            };
1152
1153            methods.push(
1154                RustTraitImplMethod::new(method_name)
1155                    .with_param(crate::rust_types::RustMethodParam::new(
1156                        "value".to_string(),
1157                        crate::rust_types::RustType::Custom(inner_type),
1158                    ))
1159                    .with_return_type("Self".to_string())
1160                    .with_body(body)
1161                    .with_self_param(Some("self".to_string())),
1162            );
1163        }
1164
1165        Some(methods)
1166    }
1167
1168    /// Generate a field existence check method for a trait implementation
1169    fn generate_field_existence_method(
1170        &self,
1171        element: &crate::fhir_types::ElementDefinition,
1172    ) -> Option<RustTraitImplMethod> {
1173        let path_parts: Vec<&str> = element.path.split('.').collect();
1174        let field_name = path_parts.last()?.to_string();
1175        let rust_field_name = crate::naming::Naming::field_name(&field_name);
1176
1177        let is_array = element.max.as_deref() == Some("*")
1178            || element
1179                .max
1180                .as_deref()
1181                .unwrap_or("1")
1182                .parse::<i32>()
1183                .unwrap_or(1)
1184                > 1;
1185
1186        // Check if field is optional based on minimum cardinality
1187        let is_optional = element.min.unwrap_or(0) == 0;
1188
1189        let method_name = format!("has_{rust_field_name}");
1190
1191        let body = if is_array {
1192            if is_optional {
1193                // Optional array: Option<Vec<T>> -> check if Some and not empty
1194                format!("self.{rust_field_name}.as_ref().is_some_and(|v| !v.is_empty())")
1195            } else {
1196                // Required array: Vec<T> -> check if not empty
1197                format!("!self.{rust_field_name}.is_empty()")
1198            }
1199        } else if is_optional {
1200            // Optional single value: Option<T> -> check if Some
1201            format!("self.{rust_field_name}.is_some()")
1202        } else {
1203            // Required single value: T -> always true (field always exists)
1204            "true".to_string()
1205        };
1206
1207        Some(
1208            RustTraitImplMethod::new(method_name)
1209                .with_return_type("bool".to_string())
1210                .with_body(body),
1211        )
1212    }
1213
1214    /// Generate existence checker method for choice-type fields.
1215    /// Choice types like subject[x] expand to multiple fields (subject_codeable_concept, subject_reference).
1216    /// The has_subject() method should return true if ANY variant is present.
1217    fn generate_choice_type_existence_method(
1218        &self,
1219        choice_field: &str,
1220        choice_element: &crate::fhir_types::ElementDefinition,
1221    ) -> Option<RustTraitImplMethod> {
1222        // Get the types from the element - choice types have multiple type entries
1223        let types = choice_element.element_type.as_ref()?;
1224
1225        if types.is_empty() {
1226            return None;
1227        }
1228
1229        // Check if the choice type itself is optional or required
1230        let is_optional = choice_element.min.unwrap_or(0) == 0;
1231
1232        // Generate field names for each type variant
1233        // Use the same naming approach as field_generator.rs to ensure consistency
1234        // e.g., for "occurrence" with type "DateTime" -> "occurrence_date_time"
1235        let mut variants = Vec::new();
1236
1237        for type_def in types {
1238            if let Some(type_code) = &type_def.code {
1239                // Use type_suffix to convert type code to snake_case with underscores
1240                // e.g., "DateTime" -> "date_time"
1241                let type_suffix = Naming::type_suffix(type_code);
1242                // Combine base field name with type suffix
1243                // e.g., "occurrence" + "_" + "date_time" -> "occurrence_date_time"
1244                let field_name = format!("{choice_field}_{type_suffix}");
1245                let rust_field_name = Naming::field_name(&field_name);
1246                variants.push(rust_field_name);
1247            }
1248        }
1249
1250        if variants.is_empty() {
1251            return None;
1252        }
1253
1254        let method_name = format!("has_{}", Naming::to_snake_case(choice_field));
1255
1256        // Generate body based on whether the choice type is optional or required
1257        let body = if is_optional {
1258            // Optional choice type: each variant is Option<T> -> check with .is_some()
1259            variants
1260                .iter()
1261                .map(|v| format!("self.{v}.is_some()"))
1262                .collect::<Vec<_>>()
1263                .join(" || ")
1264        } else {
1265            // Required choice type: at least one variant must be present
1266            // For required choice types, variants are not Option<T>, so existence is always true
1267            // But we still need to check which variant is being used
1268            // Since Rust doesn't allow multiple non-Option fields for a choice, this case shouldn't happen
1269            // in well-formed generated code. However, if it does, we return true.
1270            "true".to_string()
1271        };
1272
1273        Some(
1274            RustTraitImplMethod::new(method_name)
1275                .with_return_type("bool".to_string())
1276                .with_body(body),
1277        )
1278    }
1279    /// Get the inner type for slice return type
1280    fn get_inner_type_for_slice(&self, rust_type: &crate::rust_types::RustType) -> String {
1281        match rust_type {
1282            crate::rust_types::RustType::Vec(inner) => inner.to_string(),
1283            crate::rust_types::RustType::Option(inner) => {
1284                if let crate::rust_types::RustType::Vec(vec_inner) = inner.as_ref() {
1285                    vec_inner.to_string()
1286                } else {
1287                    inner.to_string()
1288                }
1289            }
1290            _ => rust_type.to_string(),
1291        }
1292    }
1293
1294    /// Get the type for option return type
1295    fn get_type_for_option(&self, rust_type: &crate::rust_types::RustType) -> String {
1296        match rust_type {
1297            crate::rust_types::RustType::Option(inner) => inner.to_string(),
1298            _ => rust_type.to_string(),
1299        }
1300    }
1301
1302    /// Check if a RustType implements the Copy trait
1303    /// Copy types can be returned by value without cloning
1304    fn is_copy_type(&self, rust_type: &crate::rust_types::RustType) -> bool {
1305        match rust_type {
1306            // Primitive types are Copy
1307            crate::rust_types::RustType::Boolean
1308            | crate::rust_types::RustType::Integer
1309            | crate::rust_types::RustType::Float => true,
1310
1311            // Option<T> is Copy if T is Copy
1312            crate::rust_types::RustType::Option(inner) => self.is_copy_type(inner),
1313
1314            // Check if Custom type is a FHIR primitive type (which are type aliases to Copy types)
1315            crate::rust_types::RustType::Custom(type_name) => {
1316                self.is_copy_primitive_type(type_name)
1317            }
1318
1319            // String, Vec, and other types are not Copy
1320            _ => false,
1321        }
1322    }
1323
1324    /// Check if a custom type name represents a FHIR primitive type that is Copy
1325    /// FHIR primitive types are often type aliases to Copy Rust types
1326    fn is_copy_primitive_type(&self, type_name: &str) -> bool {
1327        matches!(
1328            type_name,
1329            "BooleanType"
1330                | "IntegerType"
1331                | "UnsignedIntType"
1332                | "PositiveIntType"
1333                | "DecimalType"
1334                | "Integer64Type"
1335        )
1336    }
1337
1338    /// Check if a rust type represents an enum
1339    fn is_enum_type(&self, rust_type: &crate::rust_types::RustType) -> bool {
1340        match rust_type {
1341            crate::rust_types::RustType::Custom(type_name) => self.is_enum_type_name(type_name),
1342            _ => false,
1343        }
1344    }
1345
1346    /// Check if a type name represents a FHIR enum type
1347    fn is_enum_type_name(&self, type_name: &str) -> bool {
1348        // Common FHIR enum type patterns
1349        type_name.ends_with("Status")
1350            || type_name.ends_with("Kind")
1351            || type_name.ends_with("Code")
1352            || type_name.ends_with("Codes")
1353            || type_name.ends_with("Priority")
1354            || type_name.ends_with("Intent")
1355            || matches!(
1356                type_name,
1357                "PublicationStatus"
1358                    | "CapabilityStatementKind"
1359                    | "CodeSearchSupport"
1360                    | "FmStatus"
1361                    | "ReportStatusCodes"
1362                    | "ReportResultCodes"
1363                    | "VerificationresultStatus"
1364                    | "TaskStatus"
1365                    | "TaskIntent"
1366                    | "RequestPriority"
1367                    | "SupplydeliveryStatus"
1368                    | "SupplyrequestStatus"
1369            )
1370    }
1371
1372    /// Determine the return type for a field accessor method
1373    fn determine_method_return_type(
1374        &self,
1375        element: &crate::fhir_types::ElementDefinition,
1376    ) -> String {
1377        // Use the same logic as trait generator to ensure consistency
1378
1379        // Determine if this field is optional (min = 0)
1380        let is_optional = element.min.unwrap_or(0) == 0;
1381
1382        // Determine if this field is an array (max = "*" or > 1)
1383        let is_array = element
1384            .max
1385            .as_ref()
1386            .is_some_and(|max| max == "*" || max.parse::<u32>().unwrap_or(1) > 1);
1387
1388        // Get the base type
1389        let base_type = if let Some(element_types) = &element.element_type {
1390            if let Some(first_type) = element_types.first() {
1391                if let Some(code) = &first_type.code {
1392                    match code.as_str() {
1393                        "string" | "code" | "id" | "markdown" | "uri" | "url" | "canonical"
1394                        | "dateTime" | "date" | "time" | "instant" | "base64Binary" | "oid"
1395                        | "uuid" => "String".to_string(),
1396                        "boolean" => "bool".to_string(),
1397                        "integer" | "positiveInt" | "unsignedInt" => "i32".to_string(),
1398                        "decimal" => "f64".to_string(),
1399                        "Reference" => "crate::datatypes::reference::Reference".to_string(),
1400                        "Identifier" => "crate::datatypes::identifier::Identifier".to_string(),
1401                        "CodeableConcept" => {
1402                            "crate::datatypes::codeable_concept::CodeableConcept".to_string()
1403                        }
1404                        "Coding" => "crate::datatypes::coding::Coding".to_string(),
1405                        "Address" => "crate::datatypes::address::Address".to_string(),
1406                        "HumanName" => "crate::datatypes::human_name::HumanName".to_string(),
1407                        "ContactPoint" => {
1408                            "crate::datatypes::contact_point::ContactPoint".to_string()
1409                        }
1410                        "Attachment" => "crate::datatypes::attachment::Attachment".to_string(),
1411                        "Annotation" => "crate::datatypes::annotation::Annotation".to_string(),
1412                        "BackboneElement" => {
1413                            "crate::datatypes::backbone_element::BackboneElement".to_string()
1414                        }
1415                        _ => "String".to_string(), // For enums and other types, return String representation
1416                    }
1417                } else {
1418                    "String".to_string()
1419                }
1420            } else {
1421                "String".to_string()
1422            }
1423        } else {
1424            "String".to_string()
1425        };
1426
1427        // Build the final return type with the same logic as trait generator
1428        if is_array {
1429            if is_optional {
1430                format!("Option<Vec<{base_type}>>")
1431            } else {
1432                format!("Vec<{base_type}>")
1433            }
1434        } else if is_optional {
1435            format!("Option<{base_type}>")
1436        } else {
1437            base_type
1438        }
1439    }
1440
1441    /// Generate the method body for a field accessor
1442    fn generate_method_body(
1443        &self,
1444        field_name: &str,
1445        element: &crate::fhir_types::ElementDefinition,
1446    ) -> String {
1447        // Convert FHIR field name to Rust field name
1448        let rust_field_name = if field_name == "type" {
1449            "type_".to_string()
1450        } else {
1451            crate::naming::Naming::field_name(field_name)
1452        };
1453
1454        let field_access = format!("self.{rust_field_name}");
1455
1456        // Determine optionality and array nature using same logic as return type
1457        let is_optional = element.min.unwrap_or(0) == 0;
1458        let is_array = element
1459            .max
1460            .as_ref()
1461            .is_some_and(|max| max == "*" || max.parse::<u32>().unwrap_or(1) > 1);
1462
1463        if is_array {
1464            // Array field - just clone
1465            format!("{field_access}.clone()")
1466        } else if let Some(type_def) = element
1467            .element_type
1468            .as_ref()
1469            .and_then(|types| types.first())
1470        {
1471            if let Some(code) = &type_def.code {
1472                match code.as_str() {
1473                    "string" | "code" | "id" | "markdown" | "uri" | "url" | "canonical"
1474                    | "dateTime" | "date" | "time" | "instant" | "base64Binary" | "oid"
1475                    | "uuid" => {
1476                        if is_optional {
1477                            format!("{field_access}.as_ref().map(|s| s.to_string())")
1478                        } else {
1479                            format!("{field_access}.to_string()")
1480                        }
1481                    }
1482                    "boolean" => {
1483                        if is_optional {
1484                            format!("{field_access}.map(|b| b.into())")
1485                        } else {
1486                            format!("{field_access}.into()")
1487                        }
1488                    }
1489                    "integer" | "positiveInt" | "unsignedInt" => {
1490                        if is_optional {
1491                            format!("{field_access}.map(|i| i.into())")
1492                        } else {
1493                            format!("{field_access}.into()")
1494                        }
1495                    }
1496                    "decimal" => {
1497                        if is_optional {
1498                            format!("{field_access}.map(|d| d.into())")
1499                        } else {
1500                            format!("{field_access}.into()")
1501                        }
1502                    }
1503                    "CodeableConcept" | "Reference" | "Identifier" | "Coding" | "Address"
1504                    | "HumanName" | "ContactPoint" | "Attachment" | "Annotation"
1505                    | "BackboneElement" => {
1506                        // Complex types - just clone
1507                        format!("{field_access}.clone()")
1508                    }
1509                    _ => {
1510                        // For enums and other types
1511                        if is_optional {
1512                            format!("{field_access}.as_ref().map(|v| format!(\"{{:?}}\", v))")
1513                        } else {
1514                            format!("format!(\"{{:?}}\", {field_access})")
1515                        }
1516                    }
1517                }
1518            } else {
1519                format!("{field_access}.clone()")
1520            }
1521        } else {
1522            format!("{field_access}.clone()")
1523        }
1524    }
1525
1526    /// Check if the element types contain BackboneElement
1527    fn is_backbone_element(&self, element_types: &[crate::fhir_types::ElementType]) -> bool {
1528        element_types
1529            .iter()
1530            .any(|et| et.code.as_deref() == Some("BackboneElement"))
1531    }
1532
1533    /// Get the specific nested type for a BackboneElement field
1534    fn get_nested_type_for_backbone_element(
1535        &self,
1536        element: &crate::fhir_types::ElementDefinition,
1537        is_array: bool,
1538    ) -> crate::rust_types::RustType {
1539        let path_parts: Vec<&str> = element.path.split('.').collect();
1540
1541        if path_parts.len() == 2 {
1542            let resource_name = path_parts[0];
1543            let field_name = path_parts[1];
1544
1545            // Generate the expected nested type name: ResourceFieldName (e.g., AccountCoverage)
1546            let field_name_pascal = crate::naming::Naming::to_pascal_case(field_name);
1547            let nested_type_name = format!("{resource_name}{field_name_pascal}");
1548
1549            let rust_type = crate::rust_types::RustType::Custom(nested_type_name);
1550
1551            if is_array {
1552                crate::rust_types::RustType::Vec(Box::new(rust_type))
1553            } else {
1554                rust_type
1555            }
1556        } else {
1557            // Fallback to BackboneElement if we can't determine the specific type
1558            let rust_type = crate::rust_types::RustType::Custom("BackboneElement".to_string());
1559            if is_array {
1560                crate::rust_types::RustType::Vec(Box::new(rust_type))
1561            } else {
1562                rust_type
1563            }
1564        }
1565    }
1566
1567    /// Get the Rust type for a field element, considering ValueSet bindings.
1568    /// For code fields with required bindings, returns the enum type name.
1569    /// Otherwise, delegates to TypeUtilities for standard type mapping.
1570    fn get_field_rust_type(
1571        &self,
1572        element: &crate::fhir_types::ElementDefinition,
1573        field_name: &str,
1574    ) -> CodegenResult<crate::rust_types::RustType> {
1575        use crate::rust_types::RustType;
1576
1577        let Some(element_type) = element.element_type.as_ref().and_then(|t| t.first()) else {
1578            return Ok(RustType::String);
1579        };
1580
1581        let Some(code) = &element_type.code else {
1582            return Ok(RustType::String);
1583        };
1584
1585        // Check if this is a code type with a required binding - if so, use enum type
1586        if code == "code" {
1587            if let Some(binding) = &element.binding {
1588                if binding.strength == "required" {
1589                    if let Some(value_set_url) = &binding.value_set {
1590                        // Extract enum name from ValueSet URL
1591                        if let Some(enum_name) =
1592                            self.extract_enum_name_from_value_set(value_set_url)
1593                        {
1594                            return Ok(RustType::Custom(enum_name));
1595                        }
1596                    }
1597                }
1598            }
1599        }
1600
1601        // Otherwise, use the standard type mapping
1602        use crate::generators::TypeUtilities;
1603        TypeUtilities::map_fhir_type_to_rust(element_type, field_name, &element.path)
1604    }
1605
1606    /// Extract enum type name from a ValueSet URL
1607    /// E.g., "http://hl7.org/fhir/ValueSet/account-status" -> "AccountStatus"
1608    fn extract_enum_name_from_value_set(&self, url: &str) -> Option<String> {
1609        // Remove version suffix if present (e.g., |4.0.1)
1610        let url_without_version = url.split('|').next().unwrap_or(url);
1611
1612        // Extract the last part after the last /
1613        let value_set_name = url_without_version.split('/').next_back()?;
1614
1615        // Use the same logic as ValueSetManager::generate_enum_name for consistency
1616        // Split on hyphens and capitalize each part to get PascalCase
1617        let name = value_set_name
1618            .split(&['-', '.'][..])
1619            .filter(|part| !part.is_empty())
1620            .map(|part| {
1621                let mut chars = part.chars();
1622                match chars.next() {
1623                    None => String::new(),
1624                    Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
1625                }
1626            })
1627            .collect::<String>();
1628
1629        // Ensure it's a valid Rust identifier
1630        if name.chars().next().unwrap_or('0').is_ascii_digit() {
1631            Some(format!("ValueSet{name}"))
1632        } else {
1633            Some(name)
1634        }
1635    }
1636}
1637
1638impl Default for TraitImplGenerator {
1639    fn default() -> Self {
1640        Self::new()
1641    }
1642}
1643
1644#[cfg(test)]
1645mod tests {
1646    use super::*;
1647    use crate::fhir_types::StructureDefinitionDifferential;
1648
1649    fn create_test_structure_definition(
1650        name: &str,
1651        base_definition: Option<&str>,
1652    ) -> StructureDefinition {
1653        StructureDefinition {
1654            resource_type: "StructureDefinition".to_string(),
1655            id: name.to_lowercase(),
1656            url: format!("http://test.com/{name}"),
1657            version: Some("1.0.0".to_string()),
1658            name: name.to_string(),
1659            title: Some(name.to_string()),
1660            status: "active".to_string(),
1661            description: None,
1662            purpose: None,
1663            kind: "resource".to_string(),
1664            is_abstract: false,
1665            base_type: "Resource".to_string(),
1666            base_definition: base_definition.map(|s| s.to_string()),
1667            differential: None,
1668            snapshot: None,
1669        }
1670    }
1671
1672    #[test]
1673    fn test_resource_type_for_core_resource() {
1674        let patient = create_test_structure_definition(
1675            "Patient",
1676            Some("http://hl7.org/fhir/StructureDefinition/DomainResource"),
1677        );
1678
1679        let result = TraitImplGenerator::get_resource_type_for_struct("Patient", &patient);
1680        assert_eq!(
1681            result, "Patient",
1682            "Core resource should return its own name"
1683        );
1684    }
1685
1686    #[test]
1687    fn test_resource_type_for_group_profile() {
1688        let group_definition = create_test_structure_definition(
1689            "GroupDefinition",
1690            Some("http://hl7.org/fhir/StructureDefinition/Group"),
1691        );
1692
1693        let result =
1694            TraitImplGenerator::get_resource_type_for_struct("GroupDefinition", &group_definition);
1695        assert_eq!(result, "Group", "Group profile should return 'Group'");
1696    }
1697
1698    #[test]
1699    fn test_resource_type_for_observation_profile() {
1700        let vital_signs = create_test_structure_definition(
1701            "VitalSigns",
1702            Some("http://hl7.org/fhir/StructureDefinition/Observation"),
1703        );
1704
1705        let result = TraitImplGenerator::get_resource_type_for_struct("VitalSigns", &vital_signs);
1706        assert_eq!(
1707            result, "Observation",
1708            "Observation profile should return 'Observation'"
1709        );
1710    }
1711
1712    #[test]
1713    fn test_resource_type_for_profile_on_profile() {
1714        let bmi = create_test_structure_definition(
1715            "BMI",
1716            Some("http://hl7.org/fhir/StructureDefinition/vitalsigns"),
1717        );
1718
1719        let result = TraitImplGenerator::get_resource_type_for_struct("BMI", &bmi);
1720        // BMI inherits from vitalsigns, which should resolve to "Observation"
1721        assert_eq!(
1722            result, "Observation",
1723            "BMI profile should resolve to 'Observation' via vitalsigns"
1724        );
1725    }
1726
1727    #[test]
1728    fn test_resource_type_without_base_definition() {
1729        let custom_resource = create_test_structure_definition("CustomResource", None);
1730
1731        let result =
1732            TraitImplGenerator::get_resource_type_for_struct("CustomResource", &custom_resource);
1733        assert_eq!(
1734            result, "CustomResource",
1735            "Resource without baseDefinition should return struct name"
1736        );
1737    }
1738
1739    #[test]
1740    fn test_is_core_resource() {
1741        assert!(TraitImplGenerator::is_core_resource(
1742            "http://hl7.org/fhir/StructureDefinition/Resource"
1743        ));
1744        assert!(TraitImplGenerator::is_core_resource(
1745            "http://hl7.org/fhir/StructureDefinition/DomainResource"
1746        ));
1747        assert!(!TraitImplGenerator::is_core_resource(
1748            "http://hl7.org/fhir/StructureDefinition/Patient"
1749        ));
1750        assert!(!TraitImplGenerator::is_core_resource(
1751            "http://hl7.org/fhir/StructureDefinition/Group"
1752        ));
1753    }
1754
1755    #[test]
1756    fn test_extract_base_resource_type() {
1757        assert_eq!(
1758            TraitImplGenerator::extract_base_resource_type(
1759                "http://hl7.org/fhir/StructureDefinition/Group"
1760            ),
1761            Some("Group".to_string())
1762        );
1763        assert_eq!(
1764            TraitImplGenerator::extract_base_resource_type(
1765                "http://hl7.org/fhir/StructureDefinition/Observation"
1766            ),
1767            Some("Observation".to_string())
1768        );
1769        assert_eq!(
1770            TraitImplGenerator::extract_base_resource_type(
1771                "http://hl7.org/fhir/StructureDefinition/vitalsigns"
1772            ),
1773            Some("vitalsigns".to_string())
1774        );
1775        assert_eq!(
1776            TraitImplGenerator::extract_base_resource_type("invalid-url"),
1777            None
1778        );
1779    }
1780
1781    #[test]
1782    fn test_resolve_to_core_resource_type() {
1783        // Test known profile resolution
1784        assert_eq!(
1785            TraitImplGenerator::resolve_to_core_resource_type(
1786                "vitalsigns",
1787                "http://hl7.org/fhir/StructureDefinition/vitalsigns"
1788            ),
1789            "Observation"
1790        );
1791
1792        // Test core resource types remain unchanged
1793        assert_eq!(
1794            TraitImplGenerator::resolve_to_core_resource_type(
1795                "Patient",
1796                "http://hl7.org/fhir/StructureDefinition/Patient"
1797            ),
1798            "Patient"
1799        );
1800        assert_eq!(
1801            TraitImplGenerator::resolve_to_core_resource_type(
1802                "Group",
1803                "http://hl7.org/fhir/StructureDefinition/Group"
1804            ),
1805            "Group"
1806        );
1807
1808        // Test BMI and other vital sign profiles
1809        assert_eq!(
1810            TraitImplGenerator::resolve_to_core_resource_type(
1811                "bmi",
1812                "http://hl7.org/fhir/StructureDefinition/bmi"
1813            ),
1814            "Observation"
1815        );
1816
1817        // Test unknown profiles fallback to themselves
1818        assert_eq!(
1819            TraitImplGenerator::resolve_to_core_resource_type(
1820                "UnknownProfile",
1821                "http://hl7.org/fhir/StructureDefinition/UnknownProfile"
1822            ),
1823            "UnknownProfile"
1824        );
1825    }
1826
1827    #[test]
1828    fn test_empty_trait_implementations_are_filtered() {
1829        let generator = TraitImplGenerator::new();
1830
1831        // Create a structure definition with no elements (which would result in empty trait impl)
1832        let mut structure_def = create_test_structure_definition("EmptyProfile", None);
1833        structure_def.differential = Some(StructureDefinitionDifferential { element: vec![] });
1834
1835        // Generate trait implementations
1836        let trait_impls = generator.generate_trait_impls(&structure_def).unwrap();
1837
1838        // Should have Resource trait impl but not specific trait impl (since it would be empty)
1839        assert!(
1840            !trait_impls.is_empty(),
1841            "Should have at least Resource trait impl"
1842        );
1843
1844        // Check that there's no empty specific trait implementation
1845        let specific_trait_name = format!(
1846            "crate::traits::{}::{}Accessors",
1847            crate::naming::Naming::to_snake_case("EmptyProfile"),
1848            "EmptyProfile"
1849        );
1850
1851        let has_empty_specific_impl = trait_impls
1852            .iter()
1853            .any(|impl_| impl_.trait_name == specific_trait_name && impl_.is_empty());
1854
1855        assert!(
1856            !has_empty_specific_impl,
1857            "Should not include empty specific trait implementations"
1858        );
1859    }
1860}