1use crate::rust_types::{
7 RustEnum, RustEnumVariant, RustField, RustModule, RustStruct, RustType, RustTypeAlias,
8};
9use proc_macro2::TokenStream;
10use quote::{format_ident, quote};
11
12#[derive(Debug)]
14pub struct TokenGenerator {
15 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 pub fn generate_module(&self, module: &RustModule) -> TokenStream {
33 let mut tokens = TokenStream::new();
34
35 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 for import in &module.imports {
47 let import_tokens: TokenStream = import
48 .parse()
49 .expect("codegen bug: invalid import statement in module imports");
50 tokens.extend(quote! {
51 use #import_tokens;
52 });
53 }
54
55 for rust_struct in &module.structs {
57 tokens.extend(self.generate_struct(rust_struct));
58 }
59
60 for rust_enum in &module.enums {
62 tokens.extend(self.generate_enum(rust_enum));
63 }
64
65 tokens
66 }
67
68 pub fn generate_struct(&self, rust_struct: &RustStruct) -> TokenStream {
70 let name = format_ident!("{}", rust_struct.name);
71
72 let doc_attrs = if let Some(doc) = &rust_struct.doc_comment {
74 let doc_lines: Vec<_> = doc.lines().collect();
75 let attrs: Vec<TokenStream> = doc_lines
76 .iter()
77 .map(|line| {
78 let formatted_line = if line.trim().is_empty() {
79 "".to_string()
80 } else {
81 format!(" {line}")
82 };
83 quote! { #[doc = #formatted_line] }
84 })
85 .collect();
86 quote! { #(#attrs)* }
87 } else {
88 quote! {}
89 };
90
91 let mut derives = rust_struct.derives.clone();
93 if self.include_serde {
94 if !derives.contains(&"Serialize".to_string()) {
95 derives.push("Serialize".to_string());
96 }
97 if !derives.contains(&"Deserialize".to_string()) {
98 derives.push("Deserialize".to_string());
99 }
100 }
101
102 let derive_idents: Vec<_> = derives.iter().map(|d| format_ident!("{}", d)).collect();
103
104 let mut fields: Vec<TokenStream> = Vec::new();
106
107 if let Some(base_def) = &rust_struct.base_definition {
109 let base_type = base_def.split('/').next_back().unwrap_or(base_def);
111 let base_type = crate::naming::Naming::to_rust_identifier(base_type);
113
114 let proper_base_type = if base_type
117 .chars()
118 .all(|c| c.is_lowercase() || c.is_numeric())
119 {
120 crate::naming::Naming::capitalize_first(&base_type)
122 } else {
123 base_type
125 };
126 let base_field_name = format_ident!("base");
127 let base_type_ident = format_ident!("{}", proper_base_type);
128
129 fields.push(quote! {
130 #[doc = " Base definition inherited from FHIR specification"]
131 #[serde(flatten)]
132 pub #base_field_name: #base_type_ident
133 });
134 }
135
136 let regular_fields: Vec<_> = rust_struct
138 .fields
139 .iter()
140 .map(|field| self.generate_field(field))
141 .collect();
142 fields.extend(regular_fields);
143
144 let vis = if rust_struct.is_public {
146 quote! { pub }
147 } else {
148 quote! {}
149 };
150
151 quote! {
152 #doc_attrs
153 #[derive(#(#derive_idents),*)]
154 #vis struct #name {
155 #(#fields),*
156 }
157 }
158 }
159
160 fn generate_field(&self, field: &RustField) -> TokenStream {
162 if let Some(macro_call) = &field.macro_call {
164 return self.emit_macro_call(macro_call);
165 }
166
167 let name = format_ident!("{}", field.name);
168
169 let field_type = if field.is_repeating {
172 let inner = match &field.field_type {
176 RustType::Vec(inner) => self.generate_type(inner, false),
177 other => self.generate_type(other, false),
178 };
179 quote! { Vec<#inner> }
180 } else {
181 self.generate_type(&field.field_type, field.is_optional)
182 };
183
184 let doc_attrs = if let Some(doc) = &field.doc_comment {
186 let doc_lines: Vec<_> = doc.lines().collect();
187 let attrs: Vec<TokenStream> = doc_lines
188 .iter()
189 .map(|line| {
190 let formatted_line = if line.trim().is_empty() {
191 "".to_string()
192 } else {
193 format!(" {line}")
194 };
195 quote! { #[doc = #formatted_line] }
196 })
197 .collect();
198 quote! { #(#attrs)* }
199 } else {
200 quote! {}
201 };
202
203 let mut serde_attrs: Vec<TokenStream> = field
206 .serde_attributes
207 .iter()
208 .map(|attr| {
209 let attr_tokens: TokenStream = format!("serde({attr})")
210 .parse()
211 .expect("codegen bug: invalid serde attribute");
212 quote! { #[#attr_tokens] }
213 })
214 .collect();
215
216 if field.is_repeating && field.is_optional {
217 let default_attr: TokenStream =
219 "serde(default, skip_serializing_if = \"Vec::is_empty\")"
220 .parse()
221 .expect("codegen bug: invalid serde attribute for repeating field");
222 serde_attrs.push(quote! { #[#default_attr] });
223 }
224
225 let vis = if field.is_public {
227 quote! { pub }
228 } else {
229 quote! {}
230 };
231
232 quote! {
233 #doc_attrs
234 #(#serde_attrs)*
235 #vis #name: #field_type
236 }
237 }
238
239 pub fn generate_enum(&self, rust_enum: &RustEnum) -> TokenStream {
241 let name = format_ident!("{}", rust_enum.name);
242
243 let doc_attrs = if let Some(doc) = &rust_enum.doc_comment {
245 let doc_lines: Vec<_> = doc.lines().collect();
246 let attrs: Vec<TokenStream> = doc_lines
247 .iter()
248 .map(|line| quote! { #[doc = #line] })
249 .collect();
250 quote! { #(#attrs)* }
251 } else {
252 quote! {}
253 };
254
255 let mut derives = rust_enum.derives.clone();
257 if self.include_serde {
258 if !derives.contains(&"Serialize".to_string()) {
259 derives.push("Serialize".to_string());
260 }
261 if !derives.contains(&"Deserialize".to_string()) {
262 derives.push("Deserialize".to_string());
263 }
264 }
265
266 let derive_idents: Vec<_> = derives.iter().map(|d| format_ident!("{}", d)).collect();
267
268 let variants: Vec<_> = rust_enum
270 .variants
271 .iter()
272 .map(|variant| self.generate_enum_variant(variant))
273 .collect();
274
275 let vis = if rust_enum.is_public {
277 quote! { pub }
278 } else {
279 quote! {}
280 };
281
282 let default_impl = if !rust_enum.variants.is_empty() {
284 let first_variant = &rust_enum.variants[0];
285 let first_variant_name = format_ident!("{}", first_variant.name);
286
287 if first_variant.data.is_some() {
289 quote! {}
292 } else {
293 quote! {
295 impl Default for #name {
296 fn default() -> Self {
297 Self::#first_variant_name
298 }
299 }
300 }
301 }
302 } else {
303 quote! {}
304 };
305
306 quote! {
307 #doc_attrs
308 #[derive(#(#derive_idents),*)]
309 #vis enum #name {
310 #(#variants),*
311 }
312
313 #default_impl
314 }
315 }
316
317 fn generate_enum_variant(&self, variant: &RustEnumVariant) -> TokenStream {
319 let name = format_ident!("{}", variant.name);
320
321 let doc_attrs = if let Some(doc) = &variant.doc_comment {
323 let doc_lines: Vec<_> = doc.lines().collect();
324 let attrs: Vec<TokenStream> = doc_lines
325 .iter()
326 .map(|line| quote! { #[doc = #line] })
327 .collect();
328 quote! { #(#attrs)* }
329 } else {
330 quote! {}
331 };
332
333 let serde_attrs = if let Some(rename) = &variant.serde_rename {
335 quote! { #[serde(rename = #rename)] }
336 } else {
337 quote! {}
338 };
339
340 if let Some(data_type) = &variant.data {
342 let data_tokens = self.generate_type(data_type, false);
343 quote! {
344 #doc_attrs
345 #serde_attrs
346 #name(#data_tokens)
347 }
348 } else {
349 quote! {
350 #doc_attrs
351 #serde_attrs
352 #name
353 }
354 }
355 }
356
357 #[allow(clippy::only_used_in_recursion)]
359 fn generate_type(&self, rust_type: &RustType, wrap_optional: bool) -> TokenStream {
360 let base_type = match rust_type {
361 RustType::String => quote! { String },
362 RustType::Integer => quote! { i32 },
363 RustType::Boolean => quote! { bool },
364 RustType::Float => quote! { f64 },
365 RustType::Option(inner) => {
366 let inner_tokens = self.generate_type(inner, false);
367 quote! { Option<#inner_tokens> }
368 }
369 RustType::Vec(inner) => {
370 let inner_tokens = self.generate_type(inner, false);
371 quote! { Vec<#inner_tokens> }
372 }
373 RustType::Box(inner) => {
374 let inner_tokens = self.generate_type(inner, false);
375 quote! { Box<#inner_tokens> }
376 }
377 RustType::Slice(inner) => {
378 let inner_tokens = self.generate_type(inner, false);
379 quote! { &[#inner_tokens] }
380 }
381 RustType::Custom(name) => {
382 if name.contains('&')
384 || name.contains('<')
385 || name.contains('>')
386 || name.contains('[')
387 || name.contains(']')
388 || name.contains('\'')
389 {
390 let type_tokens: TokenStream = name
392 .parse()
393 .expect("codegen bug: invalid type expression in Custom RustType");
394 quote! { #type_tokens }
395 } else {
396 let ident = format_ident!("{}", name);
397 quote! { #ident }
398 }
399 }
400 RustType::Reference(name) => {
401 let ident = format_ident!("{}", name);
402 quote! { &#ident }
403 }
404 };
405
406 if wrap_optional && !matches!(rust_type, RustType::Option(_)) {
407 quote! { Option<#base_type> }
408 } else {
409 base_type
410 }
411 }
412
413 pub fn generate_type_alias(&self, type_alias: &RustTypeAlias) -> TokenStream {
415 let name = format_ident!("{}", type_alias.name);
416 let target_type = self.generate_type(&type_alias.target_type, false);
417
418 let doc_attrs = if let Some(doc) = &type_alias.doc_comment {
420 let doc_lines: Vec<_> = doc.lines().collect();
421 let attrs: Vec<TokenStream> = doc_lines
422 .iter()
423 .map(|line| quote! { #[doc = #line] })
424 .collect();
425 quote! { #(#attrs)* }
426 } else {
427 quote! {}
428 };
429
430 let vis = if type_alias.is_public {
432 quote! { pub }
433 } else {
434 quote! {}
435 };
436
437 quote! {
438 #doc_attrs
439 #vis type #name = #target_type;
440 }
441 }
442
443 pub fn generate_trait(&self, rust_trait: &crate::rust_types::RustTrait) -> TokenStream {
445 let trait_name = format_ident!("{}", rust_trait.name);
446
447 let doc = if let Some(doc_comment) = &rust_trait.doc_comment {
449 let doc_lines: Vec<_> = doc_comment.lines().collect();
450 let attrs: Vec<TokenStream> = doc_lines
451 .iter()
452 .map(|line| {
453 let formatted_line = if line.trim().is_empty() {
454 "".to_string()
455 } else {
456 format!(" {line}")
457 };
458 quote! { #[doc = #formatted_line] }
459 })
460 .collect();
461 quote! { #(#attrs)* }
462 } else {
463 quote! {}
464 };
465
466 let super_traits = if !rust_trait.super_traits.is_empty() {
468 let super_trait_idents: Vec<_> = rust_trait
469 .super_traits
470 .iter()
471 .map(|s| format_ident!("{}", s))
472 .collect();
473 quote! { : #(#super_trait_idents)+* }
474 } else {
475 quote! {}
476 };
477
478 let methods: Vec<TokenStream> = rust_trait
480 .methods
481 .iter()
482 .map(|method| self.generate_trait_method(method))
483 .collect();
484
485 quote! {
486 #doc
487 pub trait #trait_name #super_traits {
488 #(#methods)*
489 }
490 }
491 }
492
493 fn generate_trait_method(&self, method: &crate::rust_types::RustTraitMethod) -> TokenStream {
495 let method_name = format_ident!("{}", method.name);
496
497 let doc = if let Some(doc_comment) = &method.doc_comment {
499 let doc_lines: Vec<_> = doc_comment.lines().collect();
500 let attrs: Vec<TokenStream> = doc_lines
501 .iter()
502 .map(|line| {
503 let formatted_line = if line.trim().is_empty() {
504 "".to_string()
505 } else {
506 format!(" {line}")
507 };
508 quote! { #[doc = #formatted_line] }
509 })
510 .collect();
511 quote! { #(#attrs)* }
512 } else {
513 quote! {}
514 };
515
516 let params: Vec<TokenStream> = method
518 .params
519 .iter()
520 .map(|param| {
521 let param_name = format_ident!("{}", param.name);
522 let param_type = self.generate_type(¶m.param_type, false);
523
524 match (param.is_ref, param.is_mut) {
525 (true, true) => quote! { #param_name: &mut #param_type },
526 (true, false) => quote! { #param_name: &#param_type },
527 (false, _) => quote! { #param_name: #param_type },
528 }
529 })
530 .collect();
531
532 let all_params = if let Some(self_param_str) = &method.self_param {
534 let self_param: TokenStream = match self_param_str.as_str() {
535 "self" => quote! { self },
536 "&self" => quote! { &self },
537 "&mut self" => quote! { &mut self },
538 "mut self" => quote! { mut self },
539 _ => self_param_str.parse().unwrap_or_else(|_| quote! { &self }),
540 };
541 if params.is_empty() {
542 vec![self_param]
543 } else {
544 let mut all = vec![self_param];
545 all.extend(params);
546 all
547 }
548 } else {
549 params
551 };
552
553 let return_type = if let Some(ret_type) = &method.return_type {
555 let return_tokens = self.generate_type(ret_type, false);
556 quote! { -> #return_tokens }
557 } else {
558 quote! {}
559 };
560
561 if method.is_default {
563 let body = if let Some(body_code) = &method.default_body {
564 let body_tokens: TokenStream = body_code
565 .parse()
566 .unwrap_or_else(|_| quote! { unimplemented!() });
567 quote! { { #body_tokens } }
568 } else {
569 quote! { { unimplemented!() } }
570 };
571
572 quote! {
573 #doc
574 fn #method_name(#(#all_params),*) #return_type #body
575 }
576 } else {
577 quote! {
578 #doc
579 fn #method_name(#(#all_params),*) #return_type;
580 }
581 }
582 }
583
584 #[allow(dead_code)]
586 fn expand_primitive_macro(&self, macro_call: &str) -> TokenStream {
587 if let Some(start) = macro_call.find('!') {
592 let macro_name = ¯o_call[..start];
593
594 if let (Some(paren_start), Some(paren_end)) =
596 (macro_call.find('('), macro_call.rfind(')'))
597 {
598 let content = ¯o_call[paren_start + 1..paren_end];
599 let parts: Vec<&str> = content.split(',').map(|s| s.trim()).collect();
600
601 if parts.len() == 2 {
602 let field_name = parts[0].trim_matches('"');
603 let is_optional = parts[1] == "true";
604
605 let rust_type = match macro_name {
607 "primitive_string" => "String",
608 "primitive_boolean" => "bool",
609 "primitive_integer" => "i32",
610 "primitive_decimal" => "f64",
611 "primitive_datetime" => "String", "primitive_date" => "String",
613 "primitive_time" => "String",
614 "primitive_uri" => "String",
615 "primitive_canonical" => "String",
616 "primitive_base64binary" => "String",
617 "primitive_instant" => "String",
618 "primitive_positiveint" => "u32",
619 "primitive_unsignedint" => "u32",
620 "primitive_id" => "String",
621 "primitive_oid" => "String",
622 "primitive_uuid" => "String",
623 "primitive_code" => "String",
624 "primitive_markdown" => "String",
625 "primitive_url" => "String",
626 _ => "String", };
628
629 let field_ident = format_ident!("{}", field_name);
630 let companion_field_ident = format_ident!("_{}", field_name);
631 let type_ident = format_ident!("{}", rust_type);
632 let companion_rename = format!("_{field_name}");
633
634 if is_optional {
636 quote! {
637 pub #field_ident: Option<#type_ident>,
638 #[serde(rename = #companion_rename)]
639 pub #companion_field_ident: Option<serde_json::Value>
640 }
641 } else {
642 quote! {
643 pub #field_ident: #type_ident,
644 #[serde(rename = #companion_rename)]
645 pub #companion_field_ident: Option<serde_json::Value>
646 }
647 }
648 } else {
649 quote! {}
651 }
652 } else {
653 quote! {}
655 }
656 } else {
657 quote! {}
659 }
660 }
661
662 fn emit_macro_call(&self, macro_call: &str) -> TokenStream {
664 match macro_call.parse::<TokenStream>() {
668 Ok(tokens) => tokens,
669 Err(_) => {
670 eprintln!("Warning: Failed to parse macro call: {macro_call}");
672 quote! { }
673 }
674 }
675 }
676
677 pub fn generate_trait_impl(
679 &self,
680 trait_impl: &crate::rust_types::RustTraitImpl,
681 ) -> TokenStream {
682 let trait_name: TokenStream = trait_impl.trait_name.parse().unwrap_or_else(|_| {
683 eprintln!(
684 "Warning: Failed to parse trait name: {}",
685 trait_impl.trait_name
686 );
687 quote! { InvalidTraitName }
688 });
689
690 let struct_name: TokenStream = trait_impl.struct_name.parse().unwrap_or_else(|_| {
691 eprintln!(
692 "Warning: Failed to parse struct name: {}",
693 trait_impl.struct_name
694 );
695 quote! { InvalidStructName }
696 });
697
698 let methods: Vec<TokenStream> = trait_impl
699 .methods
700 .iter()
701 .map(|method| {
702 let method_name: TokenStream = method.name.parse().unwrap_or_else(|_| {
703 quote! { invalid_method_name }
704 });
705
706 let return_type: TokenStream = method.return_type.parse().unwrap_or_else(|_| {
707 quote! { () }
708 });
709
710 let body: TokenStream = method.body.parse().unwrap_or_else(|_| {
711 quote! { unimplemented!() }
712 });
713
714 let params: Vec<TokenStream> = method
716 .params
717 .iter()
718 .map(|param| {
719 let param_name: TokenStream = param.name.parse().unwrap_or_else(|_| {
720 quote! { invalid_param }
721 });
722 let param_type_str = param.param_type.to_string();
723 let param_type: TokenStream = param_type_str.parse().unwrap_or_else(|_| {
724 quote! { () }
725 });
726 quote! { #param_name: #param_type }
727 })
728 .collect();
729
730 let all_params = if let Some(self_param_str) = &method.self_param {
732 let self_param: TokenStream = match self_param_str.as_str() {
733 "self" => quote! { self },
734 "&self" => quote! { &self },
735 "&mut self" => quote! { &mut self },
736 "mut self" => quote! { mut self },
737 _ => self_param_str.parse().unwrap_or_else(|_| quote! { &self }),
738 };
739 if params.is_empty() {
740 vec![self_param]
741 } else {
742 let mut all = vec![self_param];
743 all.extend(params);
744 all
745 }
746 } else {
747 params
749 };
750
751 quote! {
752 fn #method_name(#(#all_params),*) -> #return_type {
753 #body
754 }
755 }
756 })
757 .collect();
758
759 quote! {
760 impl #trait_name for #struct_name {
761 #(#methods)*
762 }
763 }
764 }
765}
766
767impl Default for TokenGenerator {
768 fn default() -> Self {
769 Self::new()
770 }
771}
772
773#[cfg(test)]
774mod tests {
775 use super::*;
776 use crate::rust_types::*;
777
778 #[test]
779 fn test_generate_simple_struct() {
780 let generator = TokenGenerator::new();
781
782 let mut rust_struct = RustStruct::new("TestStruct".to_string());
783 rust_struct.add_field(RustField::new("name".to_string(), RustType::String));
784 rust_struct.add_field(RustField::new("age".to_string(), RustType::Integer).optional());
785
786 let tokens = generator.generate_struct(&rust_struct);
787 let code = tokens.to_string();
788
789 assert!(code.contains("struct TestStruct"));
790 assert!(code.contains("pub name : String"));
791 assert!(code.contains("pub age : Option < i32 >"));
792 }
793
794 #[test]
795 fn test_generate_simple_enum() {
796 let generator = TokenGenerator::new();
797
798 let mut rust_enum = RustEnum::new("TestEnum".to_string());
799 rust_enum.add_variant(RustEnumVariant::new("Variant1".to_string()));
800 rust_enum
801 .add_variant(RustEnumVariant::new("Variant2".to_string()).with_data(RustType::String));
802
803 let tokens = generator.generate_enum(&rust_enum);
804 let code = tokens.to_string();
805
806 println!("Generated enum code: {code}");
808
809 assert!(code.contains("enum TestEnum"));
810 assert!(code.contains("Variant1"));
811 assert!(code.contains("Variant2"));
812 assert!(code.contains("String"));
813 }
814
815 #[test]
816 fn test_generate_struct_with_macro_calls() {
817 let generator = TokenGenerator::new();
818
819 let mut rust_struct = RustStruct::new("Patient".to_string());
820
821 rust_struct.add_field(RustField::new("id".to_string(), RustType::String));
823
824 let macro_field =
826 RustField::new_macro_call("primitive_boolean!(\"active\", true)".to_string());
827 rust_struct.add_field(macro_field);
828
829 let tokens = generator.generate_struct(&rust_struct);
830 let code = tokens.to_string();
831
832 println!("Generated struct with macro code: {code}");
833
834 assert!(code.contains("struct Patient"));
835 assert!(code.contains("pub id : String"));
836 assert!(code.contains("primitive_boolean !"));
837 assert!(code.contains("\"active\""));
838 assert!(code.contains("true"));
839 }
840
841 #[test]
842 fn test_generate_type_alias() {
843 let generator = TokenGenerator::new();
844
845 let type_alias = RustTypeAlias::new("uri".to_string(), RustType::String)
846 .with_doc("FHIR URI primitive type".to_string());
847
848 let tokens = generator.generate_type_alias(&type_alias);
849 let code = tokens.to_string();
850
851 println!("Generated type alias code: {code}");
853
854 assert!(code.contains("type uri = String"));
855 assert!(code.contains("FHIR URI primitive type"));
856 }
857}