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