rh_codegen/generators/
existence_trait_generator.rs1use crate::fhir_types::StructureDefinition;
2use crate::rust_types::{RustTrait, RustTraitMethod, RustType};
3use crate::CodegenResult;
4
5pub struct ExistenceTraitGenerator;
7
8impl Default for ExistenceTraitGenerator {
9 fn default() -> Self {
10 Self::new()
11 }
12}
13
14impl ExistenceTraitGenerator {
15 pub fn new() -> Self {
16 Self
17 }
18
19 pub fn generate_existence_trait(
21 &mut self,
22 structure_def: &StructureDefinition,
23 ) -> CodegenResult<RustTrait> {
24 let struct_name = crate::naming::Naming::struct_name(structure_def);
25 let trait_name = format!("{struct_name}Existence");
26 let base_trait = self.get_base_trait_name(structure_def);
27
28 let mut rust_trait = RustTrait::new(trait_name.clone());
29 rust_trait.doc_comment = Some(self.generate_trait_documentation(structure_def));
30
31 if !base_trait.is_empty() {
33 rust_trait.super_traits.push(base_trait.clone());
34 }
35
36 self.add_existence_methods(structure_def, &mut rust_trait)?;
38
39 Ok(rust_trait)
40 }
41
42 fn add_existence_methods(
44 &self,
45 structure_def: &StructureDefinition,
46 rust_trait: &mut RustTrait,
47 ) -> CodegenResult<()> {
48 if let Some(snapshot) = &structure_def.snapshot {
49 for element in &snapshot.element {
50 if element.path == structure_def.id {
52 continue;
53 }
54
55 if let Some(field_name) =
57 element.path.strip_prefix(&format!("{}.", structure_def.id))
58 {
59 if field_name.contains('.') {
61 continue;
62 }
63
64 self.add_existence_method(element, field_name, rust_trait)?;
65 }
66 }
67 }
68
69 Ok(())
70 }
71
72 fn add_existence_method(
74 &self,
75 element: &crate::fhir_types::ElementDefinition,
76 field_name: &str,
77 rust_trait: &mut RustTrait,
78 ) -> CodegenResult<()> {
79 let rust_field_name = crate::naming::Naming::field_name(field_name);
80 let method_name = format!("has_{rust_field_name}");
81
82 let is_array = element
84 .max
85 .as_ref()
86 .map(|max| max != "1" && max != "0")
87 .unwrap_or(false);
88
89 let doc_comment = if is_array {
91 format!("Returns true if the {rust_field_name} field is not empty.")
92 } else {
93 format!("Returns true if the {rust_field_name} field is present (Some).")
94 };
95
96 let method = RustTraitMethod::new(method_name)
97 .with_return_type(RustType::Boolean)
98 .with_doc(doc_comment);
99
100 rust_trait.add_method(method);
101 Ok(())
102 }
103
104 fn get_base_trait_name(&self, structure_def: &StructureDefinition) -> String {
106 if structure_def.id == "Resource" {
108 return String::new(); }
110
111 if let Some(base_definition) = &structure_def.base_definition {
113 if base_definition.contains("DomainResource") {
114 return "DomainResourceExistence".to_string();
115 }
116 }
117
118 "ResourceExistence".to_string()
120 }
121
122 fn generate_trait_documentation(&self, structure_def: &StructureDefinition) -> String {
124 let mut docs = vec![
125 format!("{} Existence Checks", structure_def.id),
126 "".to_string(),
127 "This trait provides existence check methods for this FHIR resource type.".to_string(),
128 "".to_string(),
129 ];
130
131 if let Some(description) = &structure_def.description {
132 docs.push(description.clone());
133 docs.push("".to_string());
134 }
135
136 docs.extend(vec![
137 "**Source:**".to_string(),
138 format!("- URL: {}", &structure_def.url),
139 format!(
140 "- Version: {}",
141 structure_def
142 .version
143 .as_ref()
144 .unwrap_or(&"Unknown".to_string())
145 ),
146 format!("- Kind: {}", &structure_def.kind),
147 format!("- Type: {}", &structure_def.base_type),
148 ]);
149
150 if let Some(base_definition) = &structure_def.base_definition {
151 docs.push(format!("- Base Definition: {base_definition}"));
152 }
153
154 docs.join("\n")
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 #[test]
163 fn test_existence_trait_generation() {
164 let _generator = ExistenceTraitGenerator::new();
165
166 }
169
170 #[test]
171 fn test_base_trait_selection() {
172 let generator = ExistenceTraitGenerator::new();
173
174 let structure_def = StructureDefinition {
175 resource_type: "StructureDefinition".to_string(),
176 id: "TestResource".to_string(),
177 url: "http://example.com/TestResource".to_string(),
178 version: None,
179 name: "TestResource".to_string(),
180 title: None,
181 status: "draft".to_string(),
182 description: None,
183 purpose: None,
184 kind: "resource".to_string(),
185 is_abstract: false,
186 base_type: "TestResource".to_string(),
187 base_definition: Some(
188 "http://hl7.org/fhir/StructureDefinition/DomainResource".to_string(),
189 ),
190 differential: None,
191 snapshot: None,
192 };
193
194 let base_trait = generator.get_base_trait_name(&structure_def);
195 assert_eq!(base_trait, "DomainResourceExistence");
196 }
197}