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.parse().expect("Invalid import statement");
48 tokens.extend(quote! {
49 use #import_tokens;
50 });
51 }
52
53 for rust_struct in &module.structs {
55 tokens.extend(self.generate_struct(rust_struct));
56 }
57
58 for rust_enum in &module.enums {
60 tokens.extend(self.generate_enum(rust_enum));
61 }
62
63 tokens
64 }
65
66 pub fn generate_struct(&self, rust_struct: &RustStruct) -> TokenStream {
68 let name = format_ident!("{}", rust_struct.name);
69
70 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 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 let mut fields: Vec<TokenStream> = Vec::new();
104
105 if let Some(base_def) = &rust_struct.base_definition {
107 let base_type = base_def.split('/').next_back().unwrap_or(base_def);
109 let base_type = crate::naming::Naming::to_rust_identifier(base_type);
111
112 let proper_base_type = if base_type
115 .chars()
116 .all(|c| c.is_lowercase() || c.is_numeric())
117 {
118 crate::naming::Naming::capitalize_first(&base_type)
120 } else {
121 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 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 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 fn generate_field(&self, field: &RustField) -> TokenStream {
160 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 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 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 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 pub fn generate_enum(&self, rust_enum: &RustEnum) -> TokenStream {
215 let name = format_ident!("{}", rust_enum.name);
216
217 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 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 let variants: Vec<_> = rust_enum
244 .variants
245 .iter()
246 .map(|variant| self.generate_enum_variant(variant))
247 .collect();
248
249 let vis = if rust_enum.is_public {
251 quote! { pub }
252 } else {
253 quote! {}
254 };
255
256 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 if first_variant.data.is_some() {
263 quote! {}
266 } else {
267 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 fn generate_enum_variant(&self, variant: &RustEnumVariant) -> TokenStream {
293 let name = format_ident!("{}", variant.name);
294
295 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 let serde_attrs = if let Some(rename) = &variant.serde_rename {
309 quote! { #[serde(rename = #rename)] }
310 } else {
311 quote! {}
312 };
313
314 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 #[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 if name.contains('&')
358 || name.contains('<')
359 || name.contains('>')
360 || name.contains('[')
361 || name.contains(']')
362 || name.contains('\'')
363 {
364 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 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 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 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 pub fn generate_trait(&self, rust_trait: &crate::rust_types::RustTrait) -> TokenStream {
417 let trait_name = format_ident!("{}", rust_trait.name);
418
419 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 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 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 fn generate_trait_method(&self, method: &crate::rust_types::RustTraitMethod) -> TokenStream {
467 let method_name = format_ident!("{}", method.name);
468
469 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 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(¶m.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 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 params
523 };
524
525 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 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 #[allow(dead_code)]
558 fn expand_primitive_macro(&self, macro_call: &str) -> TokenStream {
559 if let Some(start) = macro_call.find('!') {
564 let macro_name = ¯o_call[..start];
565
566 if let (Some(paren_start), Some(paren_end)) =
568 (macro_call.find('('), macro_call.rfind(')'))
569 {
570 let content = ¯o_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 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", "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", };
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 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 quote! {}
623 }
624 } else {
625 quote! {}
627 }
628 } else {
629 quote! {}
631 }
632 }
633
634 fn emit_macro_call(&self, macro_call: &str) -> TokenStream {
636 match macro_call.parse::<TokenStream>() {
640 Ok(tokens) => tokens,
641 Err(_) => {
642 eprintln!("Warning: Failed to parse macro call: {macro_call}");
644 quote! { }
645 }
646 }
647 }
648
649 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 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 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 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 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 rust_struct.add_field(RustField::new("id".to_string(), RustType::String));
795
796 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 println!("Generated type alias code: {code}");
825
826 assert!(code.contains("type uri = String"));
827 assert!(code.contains("FHIR URI primitive type"));
828 }
829}