Skip to main content

rh_codegen/generators/
token_generator.rs

1//! Token generation utilities for Rust code emission
2//!
3//! This module handles the generation of Rust code tokens from the internal
4//! representation, including structs, enums, and modules.
5
6use crate::rust_types::{
7    RustEnum, RustEnumVariant, RustField, RustModule, RustStruct, RustType, RustTypeAlias,
8};
9use proc_macro2::TokenStream;
10use quote::{format_ident, quote};
11
12/// Generates Rust code tokens from internal representations
13#[derive(Debug)]
14pub struct TokenGenerator {
15    /// Whether to include serde derives by default
16    include_serde: bool,
17}
18
19impl TokenGenerator {
20    pub fn new() -> Self {
21        Self {
22            include_serde: true,
23        }
24    }
25
26    pub fn with_serde(mut self, include_serde: bool) -> Self {
27        self.include_serde = include_serde;
28        self
29    }
30
31    /// Generate tokens for a complete module
32    pub fn generate_module(&self, module: &RustModule) -> TokenStream {
33        let mut tokens = TokenStream::new();
34
35        // Add module documentation if present
36        if let Some(doc) = &module.doc_comment {
37            let doc_lines: Vec<_> = doc.lines().collect();
38            for line in doc_lines {
39                tokens.extend(quote! {
40                    #[doc = #line]
41                });
42            }
43        }
44
45        // Add imports
46        for import in &module.imports {
47            let import_tokens: TokenStream = import.parse().expect("Invalid import statement");
48            tokens.extend(quote! {
49                use #import_tokens;
50            });
51        }
52
53        // Add structs
54        for rust_struct in &module.structs {
55            tokens.extend(self.generate_struct(rust_struct));
56        }
57
58        // Add enums
59        for rust_enum in &module.enums {
60            tokens.extend(self.generate_enum(rust_enum));
61        }
62
63        tokens
64    }
65
66    /// Generate tokens for a Rust struct
67    pub fn generate_struct(&self, rust_struct: &RustStruct) -> TokenStream {
68        let name = format_ident!("{}", rust_struct.name);
69
70        // Generate documentation
71        let doc_attrs = if let Some(doc) = &rust_struct.doc_comment {
72            let doc_lines: Vec<_> = doc.lines().collect();
73            let attrs: Vec<TokenStream> = doc_lines
74                .iter()
75                .map(|line| {
76                    let formatted_line = if line.trim().is_empty() {
77                        "".to_string()
78                    } else {
79                        format!(" {line}")
80                    };
81                    quote! { #[doc = #formatted_line] }
82                })
83                .collect();
84            quote! { #(#attrs)* }
85        } else {
86            quote! {}
87        };
88
89        // Generate derive attributes
90        let mut derives = rust_struct.derives.clone();
91        if self.include_serde {
92            if !derives.contains(&"Serialize".to_string()) {
93                derives.push("Serialize".to_string());
94            }
95            if !derives.contains(&"Deserialize".to_string()) {
96                derives.push("Deserialize".to_string());
97            }
98        }
99
100        let derive_idents: Vec<_> = derives.iter().map(|d| format_ident!("{}", d)).collect();
101
102        // Generate fields
103        let mut fields: Vec<TokenStream> = Vec::new();
104
105        // Add base definition as flattened field if present
106        if let Some(base_def) = &rust_struct.base_definition {
107            // Extract the base type name from the URL (e.g., "http://hl7.org/fhir/StructureDefinition/Element" -> "Element")
108            let base_type = base_def.split('/').next_back().unwrap_or(base_def);
109            // Convert base type to a valid Rust identifier to handle cases with hyphens
110            let base_type = crate::naming::Naming::to_rust_identifier(base_type);
111
112            // For base definitions, ensure proper struct name casing but only for
113            // names that are clearly in all lowercase (like "vitalsigns")
114            let proper_base_type = if base_type
115                .chars()
116                .all(|c| c.is_lowercase() || c.is_numeric())
117            {
118                // Convert all-lowercase names to PascalCase (e.g., "vitalsigns" -> "Vitalsigns")
119                crate::naming::Naming::capitalize_first(&base_type)
120            } else {
121                // Keep names that already have proper casing (e.g., "BackboneElement")
122                base_type
123            };
124            let base_field_name = format_ident!("base");
125            let base_type_ident = format_ident!("{}", proper_base_type);
126
127            fields.push(quote! {
128                #[doc = " Base definition inherited from FHIR specification"]
129                #[serde(flatten)]
130                pub #base_field_name: #base_type_ident
131            });
132        }
133
134        // Add regular fields
135        let regular_fields: Vec<_> = rust_struct
136            .fields
137            .iter()
138            .map(|field| self.generate_field(field))
139            .collect();
140        fields.extend(regular_fields);
141
142        // Generate visibility
143        let vis = if rust_struct.is_public {
144            quote! { pub }
145        } else {
146            quote! {}
147        };
148
149        quote! {
150            #doc_attrs
151            #[derive(#(#derive_idents),*)]
152            #vis struct #name {
153                #(#fields),*
154            }
155        }
156    }
157
158    /// Generate tokens for a struct field
159    fn generate_field(&self, field: &RustField) -> TokenStream {
160        // Check if this is a macro call field - emit it directly as a macro call
161        if let Some(macro_call) = &field.macro_call {
162            return self.emit_macro_call(macro_call);
163        }
164
165        let name = format_ident!("{}", field.name);
166        let field_type = self.generate_type(&field.field_type, field.is_optional);
167
168        // Generate documentation
169        let doc_attrs = if let Some(doc) = &field.doc_comment {
170            let doc_lines: Vec<_> = doc.lines().collect();
171            let attrs: Vec<TokenStream> = doc_lines
172                .iter()
173                .map(|line| {
174                    let formatted_line = if line.trim().is_empty() {
175                        "".to_string()
176                    } else {
177                        format!(" {line}")
178                    };
179                    quote! { #[doc = #formatted_line] }
180                })
181                .collect();
182            quote! { #(#attrs)* }
183        } else {
184            quote! {}
185        };
186
187        // Generate serde attributes
188        let serde_attrs: Vec<TokenStream> = field
189            .serde_attributes
190            .iter()
191            .map(|attr| {
192                let attr_tokens: TokenStream = format!("serde({attr})")
193                    .parse()
194                    .expect("Invalid serde attribute");
195                quote! { #[#attr_tokens] }
196            })
197            .collect();
198
199        // Generate visibility
200        let vis = if field.is_public {
201            quote! { pub }
202        } else {
203            quote! {}
204        };
205
206        quote! {
207            #doc_attrs
208            #(#serde_attrs)*
209            #vis #name: #field_type
210        }
211    }
212
213    /// Generate tokens for a Rust enum
214    pub fn generate_enum(&self, rust_enum: &RustEnum) -> TokenStream {
215        let name = format_ident!("{}", rust_enum.name);
216
217        // Generate documentation
218        let doc_attrs = if let Some(doc) = &rust_enum.doc_comment {
219            let doc_lines: Vec<_> = doc.lines().collect();
220            let attrs: Vec<TokenStream> = doc_lines
221                .iter()
222                .map(|line| quote! { #[doc = #line] })
223                .collect();
224            quote! { #(#attrs)* }
225        } else {
226            quote! {}
227        };
228
229        // Generate derive attributes
230        let mut derives = rust_enum.derives.clone();
231        if self.include_serde {
232            if !derives.contains(&"Serialize".to_string()) {
233                derives.push("Serialize".to_string());
234            }
235            if !derives.contains(&"Deserialize".to_string()) {
236                derives.push("Deserialize".to_string());
237            }
238        }
239
240        let derive_idents: Vec<_> = derives.iter().map(|d| format_ident!("{}", d)).collect();
241
242        // Generate variants
243        let variants: Vec<_> = rust_enum
244            .variants
245            .iter()
246            .map(|variant| self.generate_enum_variant(variant))
247            .collect();
248
249        // Generate visibility
250        let vis = if rust_enum.is_public {
251            quote! { pub }
252        } else {
253            quote! {}
254        };
255
256        // Generate Default implementation using the first variant
257        let default_impl = if !rust_enum.variants.is_empty() {
258            let first_variant = &rust_enum.variants[0];
259            let first_variant_name = format_ident!("{}", first_variant.name);
260
261            // Check if the first variant has data (tuple variant)
262            if first_variant.data.is_some() {
263                // For variants with data, we can't easily generate a Default
264                // Skip Default implementation for enums with data variants
265                quote! {}
266            } else {
267                // For unit variants, generate Default implementation
268                quote! {
269                    impl Default for #name {
270                        fn default() -> Self {
271                            Self::#first_variant_name
272                        }
273                    }
274                }
275            }
276        } else {
277            quote! {}
278        };
279
280        quote! {
281            #doc_attrs
282            #[derive(#(#derive_idents),*)]
283            #vis enum #name {
284                #(#variants),*
285            }
286
287            #default_impl
288        }
289    }
290
291    /// Generate tokens for an enum variant
292    fn generate_enum_variant(&self, variant: &RustEnumVariant) -> TokenStream {
293        let name = format_ident!("{}", variant.name);
294
295        // Generate documentation
296        let doc_attrs = if let Some(doc) = &variant.doc_comment {
297            let doc_lines: Vec<_> = doc.lines().collect();
298            let attrs: Vec<TokenStream> = doc_lines
299                .iter()
300                .map(|line| quote! { #[doc = #line] })
301                .collect();
302            quote! { #(#attrs)* }
303        } else {
304            quote! {}
305        };
306
307        // Generate serde rename attribute if needed
308        let serde_attrs = if let Some(rename) = &variant.serde_rename {
309            quote! { #[serde(rename = #rename)] }
310        } else {
311            quote! {}
312        };
313
314        // Generate variant with optional data
315        if let Some(data_type) = &variant.data {
316            let data_tokens = self.generate_type(data_type, false);
317            quote! {
318                #doc_attrs
319                #serde_attrs
320                #name(#data_tokens)
321            }
322        } else {
323            quote! {
324                #doc_attrs
325                #serde_attrs
326                #name
327            }
328        }
329    }
330
331    /// Generate tokens for a Rust type
332    #[allow(clippy::only_used_in_recursion)]
333    fn generate_type(&self, rust_type: &RustType, wrap_optional: bool) -> TokenStream {
334        let base_type = match rust_type {
335            RustType::String => quote! { String },
336            RustType::Integer => quote! { i32 },
337            RustType::Boolean => quote! { bool },
338            RustType::Float => quote! { f64 },
339            RustType::Option(inner) => {
340                let inner_tokens = self.generate_type(inner, false);
341                quote! { Option<#inner_tokens> }
342            }
343            RustType::Vec(inner) => {
344                let inner_tokens = self.generate_type(inner, false);
345                quote! { Vec<#inner_tokens> }
346            }
347            RustType::Box(inner) => {
348                let inner_tokens = self.generate_type(inner, false);
349                quote! { Box<#inner_tokens> }
350            }
351            RustType::Slice(inner) => {
352                let inner_tokens = self.generate_type(inner, false);
353                quote! { &[#inner_tokens] }
354            }
355            RustType::Custom(name) => {
356                // Check if this is a complex type that shouldn't be treated as an identifier
357                if name.contains('&')
358                    || name.contains('<')
359                    || name.contains('>')
360                    || name.contains('[')
361                    || name.contains(']')
362                    || name.contains('\'')
363                {
364                    // Parse as a type expression
365                    let type_tokens: TokenStream = name.parse().expect("Invalid type expression");
366                    quote! { #type_tokens }
367                } else {
368                    let ident = format_ident!("{}", name);
369                    quote! { #ident }
370                }
371            }
372            RustType::Reference(name) => {
373                let ident = format_ident!("{}", name);
374                quote! { &#ident }
375            }
376        };
377
378        if wrap_optional && !matches!(rust_type, RustType::Option(_)) {
379            quote! { Option<#base_type> }
380        } else {
381            base_type
382        }
383    }
384
385    /// Generate tokens for a Rust type alias
386    pub fn generate_type_alias(&self, type_alias: &RustTypeAlias) -> TokenStream {
387        let name = format_ident!("{}", type_alias.name);
388        let target_type = self.generate_type(&type_alias.target_type, false);
389
390        // Generate documentation
391        let doc_attrs = if let Some(doc) = &type_alias.doc_comment {
392            let doc_lines: Vec<_> = doc.lines().collect();
393            let attrs: Vec<TokenStream> = doc_lines
394                .iter()
395                .map(|line| quote! { #[doc = #line] })
396                .collect();
397            quote! { #(#attrs)* }
398        } else {
399            quote! {}
400        };
401
402        // Generate visibility
403        let vis = if type_alias.is_public {
404            quote! { pub }
405        } else {
406            quote! {}
407        };
408
409        quote! {
410            #doc_attrs
411            #vis type #name = #target_type;
412        }
413    }
414
415    /// Generate tokens for a Rust trait
416    pub fn generate_trait(&self, rust_trait: &crate::rust_types::RustTrait) -> TokenStream {
417        let trait_name = format_ident!("{}", rust_trait.name);
418
419        // Generate documentation
420        let doc = if let Some(doc_comment) = &rust_trait.doc_comment {
421            let doc_lines: Vec<_> = doc_comment.lines().collect();
422            let attrs: Vec<TokenStream> = doc_lines
423                .iter()
424                .map(|line| {
425                    let formatted_line = if line.trim().is_empty() {
426                        "".to_string()
427                    } else {
428                        format!(" {line}")
429                    };
430                    quote! { #[doc = #formatted_line] }
431                })
432                .collect();
433            quote! { #(#attrs)* }
434        } else {
435            quote! {}
436        };
437
438        // Generate super traits
439        let super_traits = if !rust_trait.super_traits.is_empty() {
440            let super_trait_idents: Vec<_> = rust_trait
441                .super_traits
442                .iter()
443                .map(|s| format_ident!("{}", s))
444                .collect();
445            quote! { : #(#super_trait_idents)+* }
446        } else {
447            quote! {}
448        };
449
450        // Generate trait methods
451        let methods: Vec<TokenStream> = rust_trait
452            .methods
453            .iter()
454            .map(|method| self.generate_trait_method(method))
455            .collect();
456
457        quote! {
458            #doc
459            pub trait #trait_name #super_traits {
460                #(#methods)*
461            }
462        }
463    }
464
465    /// Generate tokens for a trait method
466    fn generate_trait_method(&self, method: &crate::rust_types::RustTraitMethod) -> TokenStream {
467        let method_name = format_ident!("{}", method.name);
468
469        // Generate documentation
470        let doc = if let Some(doc_comment) = &method.doc_comment {
471            let doc_lines: Vec<_> = doc_comment.lines().collect();
472            let attrs: Vec<TokenStream> = doc_lines
473                .iter()
474                .map(|line| {
475                    let formatted_line = if line.trim().is_empty() {
476                        "".to_string()
477                    } else {
478                        format!(" {line}")
479                    };
480                    quote! { #[doc = #formatted_line] }
481                })
482                .collect();
483            quote! { #(#attrs)* }
484        } else {
485            quote! {}
486        };
487
488        // Generate parameters
489        let params: Vec<TokenStream> = method
490            .params
491            .iter()
492            .map(|param| {
493                let param_name = format_ident!("{}", param.name);
494                let param_type = self.generate_type(&param.param_type, false);
495
496                match (param.is_ref, param.is_mut) {
497                    (true, true) => quote! { #param_name: &mut #param_type },
498                    (true, false) => quote! { #param_name: &#param_type },
499                    (false, _) => quote! { #param_name: #param_type },
500                }
501            })
502            .collect();
503
504        // Add self parameter if specified
505        let all_params = if let Some(self_param_str) = &method.self_param {
506            let self_param: TokenStream = match self_param_str.as_str() {
507                "self" => quote! { self },
508                "&self" => quote! { &self },
509                "&mut self" => quote! { &mut self },
510                "mut self" => quote! { mut self },
511                _ => self_param_str.parse().unwrap_or_else(|_| quote! { &self }),
512            };
513            if params.is_empty() {
514                vec![self_param]
515            } else {
516                let mut all = vec![self_param];
517                all.extend(params);
518                all
519            }
520        } else {
521            // No self parameter (e.g., for associated functions like new())
522            params
523        };
524
525        // Generate return type
526        let return_type = if let Some(ret_type) = &method.return_type {
527            let return_tokens = self.generate_type(ret_type, false);
528            quote! { -> #return_tokens }
529        } else {
530            quote! {}
531        };
532
533        // Generate method body for default implementations
534        if method.is_default {
535            let body = if let Some(body_code) = &method.default_body {
536                let body_tokens: TokenStream = body_code
537                    .parse()
538                    .unwrap_or_else(|_| quote! { unimplemented!() });
539                quote! { { #body_tokens } }
540            } else {
541                quote! { { unimplemented!() } }
542            };
543
544            quote! {
545                #doc
546                fn #method_name(#(#all_params),*) #return_type #body
547            }
548        } else {
549            quote! {
550                #doc
551                fn #method_name(#(#all_params),*) #return_type;
552            }
553        }
554    }
555
556    /// Expand a primitive macro call into actual struct fields
557    #[allow(dead_code)]
558    fn expand_primitive_macro(&self, macro_call: &str) -> TokenStream {
559        // Parse the macro call to extract parameters
560        // Expected format: "primitive_type!(field_name, is_optional)"
561
562        // Simple parsing without regex
563        if let Some(start) = macro_call.find('!') {
564            let macro_name = &macro_call[..start];
565
566            // Extract the content between parentheses
567            if let (Some(paren_start), Some(paren_end)) =
568                (macro_call.find('('), macro_call.rfind(')'))
569            {
570                let content = &macro_call[paren_start + 1..paren_end];
571                let parts: Vec<&str> = content.split(',').map(|s| s.trim()).collect();
572
573                if parts.len() == 2 {
574                    let field_name = parts[0].trim_matches('"');
575                    let is_optional = parts[1] == "true";
576
577                    // Map macro names to Rust types
578                    let rust_type = match macro_name {
579                        "primitive_string" => "String",
580                        "primitive_boolean" => "bool",
581                        "primitive_integer" => "i32",
582                        "primitive_decimal" => "f64",
583                        "primitive_datetime" => "String", // Placeholder
584                        "primitive_date" => "String",
585                        "primitive_time" => "String",
586                        "primitive_uri" => "String",
587                        "primitive_canonical" => "String",
588                        "primitive_base64binary" => "String",
589                        "primitive_instant" => "String",
590                        "primitive_positiveint" => "u32",
591                        "primitive_unsignedint" => "u32",
592                        "primitive_id" => "String",
593                        "primitive_oid" => "String",
594                        "primitive_uuid" => "String",
595                        "primitive_code" => "String",
596                        "primitive_markdown" => "String",
597                        "primitive_url" => "String",
598                        _ => "String", // Default fallback
599                    };
600
601                    let field_ident = format_ident!("{}", field_name);
602                    let companion_field_ident = format_ident!("_{}", field_name);
603                    let type_ident = format_ident!("{}", rust_type);
604                    let companion_rename = format!("_{field_name}");
605
606                    // Generate both the main field and companion field
607                    if is_optional {
608                        quote! {
609                            pub #field_ident: Option<#type_ident>,
610                            #[serde(rename = #companion_rename)]
611                            pub #companion_field_ident: Option<serde_json::Value>
612                        }
613                    } else {
614                        quote! {
615                            pub #field_ident: #type_ident,
616                            #[serde(rename = #companion_rename)]
617                            pub #companion_field_ident: Option<serde_json::Value>
618                        }
619                    }
620                } else {
621                    // Fallback: return empty
622                    quote! {}
623                }
624            } else {
625                // Fallback: return empty
626                quote! {}
627            }
628        } else {
629            // Fallback: return empty
630            quote! {}
631        }
632    }
633
634    /// Emit a macro call directly into the generated code
635    fn emit_macro_call(&self, macro_call: &str) -> TokenStream {
636        // The macro_call string should be parseable directly as a macro invocation
637        // For example: "primitive_string!(\"description\", true)"
638
639        match macro_call.parse::<TokenStream>() {
640            Ok(tokens) => tokens,
641            Err(_) => {
642                // If parsing fails, try to construct it manually
643                eprintln!("Warning: Failed to parse macro call: {macro_call}");
644                quote! { /* Invalid macro call: #macro_call */ }
645            }
646        }
647    }
648
649    /// Generate tokens for a trait implementation block
650    pub fn generate_trait_impl(
651        &self,
652        trait_impl: &crate::rust_types::RustTraitImpl,
653    ) -> TokenStream {
654        let trait_name: TokenStream = trait_impl.trait_name.parse().unwrap_or_else(|_| {
655            eprintln!(
656                "Warning: Failed to parse trait name: {}",
657                trait_impl.trait_name
658            );
659            quote! { InvalidTraitName }
660        });
661
662        let struct_name: TokenStream = trait_impl.struct_name.parse().unwrap_or_else(|_| {
663            eprintln!(
664                "Warning: Failed to parse struct name: {}",
665                trait_impl.struct_name
666            );
667            quote! { InvalidStructName }
668        });
669
670        let methods: Vec<TokenStream> = trait_impl
671            .methods
672            .iter()
673            .map(|method| {
674                let method_name: TokenStream = method.name.parse().unwrap_or_else(|_| {
675                    quote! { invalid_method_name }
676                });
677
678                let return_type: TokenStream = method.return_type.parse().unwrap_or_else(|_| {
679                    quote! { () }
680                });
681
682                let body: TokenStream = method.body.parse().unwrap_or_else(|_| {
683                    quote! { unimplemented!() }
684                });
685
686                // Generate parameters (excluding self which may or may not be present)
687                let params: Vec<TokenStream> = method
688                    .params
689                    .iter()
690                    .map(|param| {
691                        let param_name: TokenStream = param.name.parse().unwrap_or_else(|_| {
692                            quote! { invalid_param }
693                        });
694                        let param_type_str = param.param_type.to_string();
695                        let param_type: TokenStream = param_type_str.parse().unwrap_or_else(|_| {
696                            quote! { () }
697                        });
698                        quote! { #param_name: #param_type }
699                    })
700                    .collect();
701
702                // Build the complete parameter list with self if specified
703                let all_params = if let Some(self_param_str) = &method.self_param {
704                    let self_param: TokenStream = match self_param_str.as_str() {
705                        "self" => quote! { self },
706                        "&self" => quote! { &self },
707                        "&mut self" => quote! { &mut self },
708                        "mut self" => quote! { mut self },
709                        _ => self_param_str.parse().unwrap_or_else(|_| quote! { &self }),
710                    };
711                    if params.is_empty() {
712                        vec![self_param]
713                    } else {
714                        let mut all = vec![self_param];
715                        all.extend(params);
716                        all
717                    }
718                } else {
719                    // No self parameter (e.g., for associated functions like new())
720                    params
721                };
722
723                quote! {
724                    fn #method_name(#(#all_params),*) -> #return_type {
725                        #body
726                    }
727                }
728            })
729            .collect();
730
731        quote! {
732            impl #trait_name for #struct_name {
733                #(#methods)*
734            }
735        }
736    }
737}
738
739impl Default for TokenGenerator {
740    fn default() -> Self {
741        Self::new()
742    }
743}
744
745#[cfg(test)]
746mod tests {
747    use super::*;
748    use crate::rust_types::*;
749
750    #[test]
751    fn test_generate_simple_struct() {
752        let generator = TokenGenerator::new();
753
754        let mut rust_struct = RustStruct::new("TestStruct".to_string());
755        rust_struct.add_field(RustField::new("name".to_string(), RustType::String));
756        rust_struct.add_field(RustField::new("age".to_string(), RustType::Integer).optional());
757
758        let tokens = generator.generate_struct(&rust_struct);
759        let code = tokens.to_string();
760
761        assert!(code.contains("struct TestStruct"));
762        assert!(code.contains("pub name : String"));
763        assert!(code.contains("pub age : Option < i32 >"));
764    }
765
766    #[test]
767    fn test_generate_simple_enum() {
768        let generator = TokenGenerator::new();
769
770        let mut rust_enum = RustEnum::new("TestEnum".to_string());
771        rust_enum.add_variant(RustEnumVariant::new("Variant1".to_string()));
772        rust_enum
773            .add_variant(RustEnumVariant::new("Variant2".to_string()).with_data(RustType::String));
774
775        let tokens = generator.generate_enum(&rust_enum);
776        let code = tokens.to_string();
777
778        // Print the actual generated code for debugging
779        println!("Generated enum code: {code}");
780
781        assert!(code.contains("enum TestEnum"));
782        assert!(code.contains("Variant1"));
783        assert!(code.contains("Variant2"));
784        assert!(code.contains("String"));
785    }
786
787    #[test]
788    fn test_generate_struct_with_macro_calls() {
789        let generator = TokenGenerator::new();
790
791        let mut rust_struct = RustStruct::new("Patient".to_string());
792
793        // Add a regular field
794        rust_struct.add_field(RustField::new("id".to_string(), RustType::String));
795
796        // Add a field with a macro call
797        let macro_field =
798            RustField::new_macro_call("primitive_boolean!(\"active\", true)".to_string());
799        rust_struct.add_field(macro_field);
800
801        let tokens = generator.generate_struct(&rust_struct);
802        let code = tokens.to_string();
803
804        println!("Generated struct with macro code: {code}");
805
806        assert!(code.contains("struct Patient"));
807        assert!(code.contains("pub id : String"));
808        assert!(code.contains("primitive_boolean !"));
809        assert!(code.contains("\"active\""));
810        assert!(code.contains("true"));
811    }
812
813    #[test]
814    fn test_generate_type_alias() {
815        let generator = TokenGenerator::new();
816
817        let type_alias = RustTypeAlias::new("uri".to_string(), RustType::String)
818            .with_doc("FHIR URI primitive type".to_string());
819
820        let tokens = generator.generate_type_alias(&type_alias);
821        let code = tokens.to_string();
822
823        // Print the actual generated code for debugging
824        println!("Generated type alias code: {code}");
825
826        assert!(code.contains("type uri = String"));
827        assert!(code.contains("FHIR URI primitive type"));
828    }
829}