Skip to main content

rh_codegen/
rust_types.rs

1//! Rust-specific types and structures for code generation
2//!
3//! This module contains data structures that represent generated Rust code elements
4//! such as structs, fields, enums, and their associated metadata.
5
6/// Represents a generated Rust struct
7#[derive(Debug, Clone)]
8pub struct RustStruct {
9    pub name: String,
10    pub doc_comment: Option<String>,
11    pub fields: Vec<RustField>,
12    pub derives: Vec<String>,
13    pub is_public: bool,
14    pub base_definition: Option<String>,
15}
16
17impl RustStruct {
18    pub fn new(name: String) -> Self {
19        Self {
20            name,
21            doc_comment: None,
22            fields: Vec::new(),
23            derives: vec![
24                "Debug".to_string(),
25                "Clone".to_string(),
26                "Deserialize".to_string(),
27                "Serialize".to_string(),
28            ],
29            is_public: true,
30            base_definition: None,
31        }
32    }
33
34    pub fn with_doc(mut self, doc: String) -> Self {
35        self.doc_comment = Some(doc);
36        self
37    }
38
39    pub fn add_field(&mut self, field: RustField) {
40        self.fields.push(field);
41    }
42}
43
44/// Represents a field in a Rust struct
45#[derive(Debug, Clone)]
46pub struct RustField {
47    pub name: String,
48    pub field_type: RustType,
49    pub doc_comment: Option<String>,
50    /// True when the field has 0..1 cardinality (min=0, max=1) → `Option<T>`
51    pub is_optional: bool,
52    /// True when the field has *..* cardinality (max > 1 or max="*") → `Vec<T>`
53    /// When both is_optional and is_repeating are true (0..*), the field is emitted
54    /// as `Vec<T>` with `#[serde(default, skip_serializing_if = "Vec::is_empty")]`
55    /// instead of `Option<Vec<T>>`, because absent ⇔ empty in FHIR JSON.
56    pub is_repeating: bool,
57    pub is_public: bool,
58    pub serde_attributes: Vec<String>,
59    /// If set, this field should be rendered as a macro call instead of a regular field
60    pub macro_call: Option<String>,
61}
62
63impl RustField {
64    pub fn new(name: String, field_type: RustType) -> Self {
65        Self {
66            name,
67            field_type,
68            doc_comment: None,
69            is_optional: false,
70            is_repeating: false,
71            is_public: true,
72            serde_attributes: Vec::new(),
73            macro_call: None,
74        }
75    }
76
77    /// Create a new field that represents a macro call
78    pub fn new_macro_call(macro_call: String) -> Self {
79        // Extract field name from macro call for display purposes
80        let field_name = Self::extract_field_name_from_macro_call(&macro_call);
81
82        Self {
83            name: field_name,
84            field_type: RustType::Custom("MacroCall".to_string()), // Placeholder type
85            doc_comment: None,
86            is_optional: false,
87            is_repeating: false,
88            is_public: true,
89            serde_attributes: Vec::new(),
90            macro_call: Some(macro_call),
91        }
92    }
93
94    /// Extract field name from a macro call string
95    fn extract_field_name_from_macro_call(macro_call: &str) -> String {
96        // Parse macro call like: primitive_string!("field_name", true)
97        if let Some(start) = macro_call.find('(') {
98            if let Some(end) = macro_call.find(',') {
99                let content = &macro_call[start + 1..end];
100                let field_name = content.trim().trim_matches('"');
101                return field_name.to_string();
102            }
103        }
104        "unknown_field".to_string()
105    }
106
107    pub fn optional(mut self) -> Self {
108        self.is_optional = true;
109        self
110    }
111
112    pub fn repeating(mut self) -> Self {
113        self.is_repeating = true;
114        self
115    }
116
117    pub fn with_doc(mut self, doc: String) -> Self {
118        self.doc_comment = Some(doc);
119        self
120    }
121
122    pub fn with_serde_rename(mut self, name: String) -> Self {
123        self.serde_attributes.push(format!("rename = \"{name}\""));
124        self
125    }
126}
127
128/// Represents a Rust type
129#[derive(Debug, Clone)]
130pub enum RustType {
131    String,
132    Integer,
133    Boolean,
134    Float,
135    Option(Box<RustType>),
136    Vec(Box<RustType>),
137    Box(Box<RustType>),
138    Slice(Box<RustType>),
139    Custom(String),
140    Reference(String),
141}
142
143impl RustType {
144    #[allow(clippy::inherent_to_string)]
145    pub fn to_string(&self) -> String {
146        match self {
147            RustType::String => "String".to_string(),
148            RustType::Integer => "i32".to_string(),
149            RustType::Boolean => "bool".to_string(),
150            RustType::Float => "f64".to_string(),
151            RustType::Option(inner) => format!("Option<{}>", inner.to_string()),
152            RustType::Vec(inner) => format!("Vec<{}>", inner.to_string()),
153            RustType::Box(inner) => format!("Box<{}>", inner.to_string()),
154            RustType::Slice(inner) => format!("[{}]", inner.to_string()),
155            RustType::Custom(name) => name.clone(),
156            RustType::Reference(name) => format!("&{name}"),
157        }
158    }
159
160    pub fn wrap_in_option(self) -> Self {
161        match self {
162            RustType::Option(_) => self,
163            _ => RustType::Option(Box::new(self)),
164        }
165    }
166}
167
168/// Represents a generated Rust enum
169#[derive(Debug, Clone)]
170pub struct RustEnum {
171    pub name: String,
172    pub doc_comment: Option<String>,
173    pub variants: Vec<RustEnumVariant>,
174    pub derives: Vec<String>,
175    pub is_public: bool,
176}
177
178impl RustEnum {
179    pub fn new(name: String) -> Self {
180        Self {
181            name,
182            doc_comment: None,
183            variants: Vec::new(),
184            derives: vec!["Debug".to_string(), "Clone".to_string()],
185            is_public: true,
186        }
187    }
188
189    pub fn add_variant(&mut self, variant: RustEnumVariant) {
190        self.variants.push(variant);
191    }
192}
193
194/// Represents a variant in a Rust enum
195#[derive(Debug, Clone)]
196pub struct RustEnumVariant {
197    pub name: String,
198    pub doc_comment: Option<String>,
199    pub data: Option<RustType>,
200    pub serde_rename: Option<String>,
201}
202
203impl RustEnumVariant {
204    pub fn new(name: String) -> Self {
205        Self {
206            name,
207            doc_comment: None,
208            data: None,
209            serde_rename: None,
210        }
211    }
212
213    pub fn with_data(mut self, data: RustType) -> Self {
214        self.data = Some(data);
215        self
216    }
217
218    pub fn with_serde_rename(mut self, rename: String) -> Self {
219        self.serde_rename = Some(rename);
220        self
221    }
222}
223
224/// Represents a Rust type alias
225#[derive(Debug, Clone)]
226pub struct RustTypeAlias {
227    pub name: String,
228    pub target_type: RustType,
229    pub doc_comment: Option<String>,
230    pub is_public: bool,
231}
232
233impl RustTypeAlias {
234    pub fn new(name: String, target_type: RustType) -> Self {
235        Self {
236            name,
237            target_type,
238            doc_comment: None,
239            is_public: true,
240        }
241    }
242
243    pub fn with_doc(mut self, doc: String) -> Self {
244        self.doc_comment = Some(doc);
245        self
246    }
247}
248
249/// Represents a generated code module
250#[derive(Debug, Clone)]
251pub struct RustModule {
252    pub name: String,
253    pub structs: Vec<RustStruct>,
254    pub enums: Vec<RustEnum>,
255    pub imports: Vec<String>,
256    pub doc_comment: Option<String>,
257}
258
259impl RustModule {
260    pub fn new(name: String) -> Self {
261        Self {
262            name,
263            structs: Vec::new(),
264            enums: Vec::new(),
265            imports: vec!["serde::{Deserialize, Serialize}".to_string()],
266            doc_comment: None,
267        }
268    }
269
270    pub fn add_struct(&mut self, rust_struct: RustStruct) {
271        self.structs.push(rust_struct);
272    }
273
274    pub fn add_enum(&mut self, rust_enum: RustEnum) {
275        self.enums.push(rust_enum);
276    }
277
278    pub fn add_import(&mut self, import: String) {
279        if !self.imports.contains(&import) {
280            self.imports.push(import);
281        }
282    }
283}
284
285/// Represents a parameter in a Rust method
286#[derive(Debug, Clone)]
287pub struct RustMethodParam {
288    pub name: String,
289    pub param_type: RustType,
290    pub is_mut: bool,
291    pub is_ref: bool,
292}
293
294impl RustMethodParam {
295    pub fn new(name: String, param_type: RustType) -> Self {
296        Self {
297            name,
298            param_type,
299            is_mut: false,
300            is_ref: false,
301        }
302    }
303
304    pub fn with_mut(mut self) -> Self {
305        self.is_mut = true;
306        self
307    }
308
309    pub fn with_ref(mut self) -> Self {
310        self.is_ref = true;
311        self
312    }
313}
314
315/// Represents a Rust trait definition
316#[derive(Debug, Clone)]
317pub struct RustTrait {
318    pub name: String,
319    pub doc_comment: Option<String>,
320    pub methods: Vec<RustTraitMethod>,
321    pub is_public: bool,
322    pub super_traits: Vec<String>,
323}
324
325impl RustTrait {
326    pub fn new(name: String) -> Self {
327        Self {
328            name,
329            doc_comment: None,
330            methods: Vec::new(),
331            is_public: true,
332            super_traits: Vec::new(),
333        }
334    }
335
336    pub fn with_doc(mut self, doc: String) -> Self {
337        self.doc_comment = Some(doc);
338        self
339    }
340
341    pub fn add_method(&mut self, method: RustTraitMethod) {
342        // Check if a method with this name already exists
343        if !self
344            .methods
345            .iter()
346            .any(|existing| existing.name == method.name)
347        {
348            self.methods.push(method);
349        }
350    }
351
352    pub fn with_super_trait(mut self, super_trait: String) -> Self {
353        self.super_traits.push(super_trait);
354        self
355    }
356}
357
358/// Represents a method declaration in a trait
359#[derive(Debug, Clone)]
360pub struct RustTraitMethod {
361    pub name: String,
362    pub params: Vec<RustMethodParam>,
363    pub return_type: Option<RustType>,
364    pub doc_comment: Option<String>,
365    pub is_default: bool,
366    pub default_body: Option<String>,
367    /// The self parameter type: None (no self), Some("&self"), Some("&mut self"), Some("self")
368    pub self_param: Option<String>,
369}
370
371impl RustTraitMethod {
372    pub fn new(name: String) -> Self {
373        Self {
374            name,
375            params: Vec::new(),
376            return_type: None,
377            doc_comment: None,
378            is_default: false,
379            default_body: None,
380            self_param: Some("&self".to_string()), // Default to &self for backward compatibility
381        }
382    }
383
384    pub fn with_param(mut self, param: RustMethodParam) -> Self {
385        self.params.push(param);
386        self
387    }
388
389    pub fn with_parameter(mut self, name: String, param_type: RustType) -> Self {
390        self.params.push(RustMethodParam::new(name, param_type));
391        self
392    }
393
394    pub fn with_return_type(mut self, return_type: RustType) -> Self {
395        self.return_type = Some(return_type);
396        self
397    }
398
399    pub fn with_doc(mut self, doc: String) -> Self {
400        self.doc_comment = Some(doc);
401        self
402    }
403
404    pub fn with_default_implementation(mut self, body: String) -> Self {
405        self.is_default = true;
406        self.default_body = Some(body);
407        self
408    }
409
410    pub fn with_body(mut self, body: String) -> Self {
411        self.default_body = Some(body);
412        self
413    }
414
415    pub fn with_self_param(mut self, self_param: Option<String>) -> Self {
416        self.self_param = self_param;
417        self
418    }
419}
420
421/// Represents a Rust trait implementation
422#[derive(Debug, Clone)]
423pub struct RustTraitImpl {
424    /// The name of the trait being implemented
425    pub trait_name: String,
426    /// The name of the struct implementing the trait  
427    pub struct_name: String,
428    /// The methods implemented in this trait impl
429    pub methods: Vec<RustTraitImplMethod>,
430    /// Documentation comment for the impl
431    pub doc_comment: Option<String>,
432}
433
434impl RustTraitImpl {
435    pub fn new(trait_name: String, struct_name: String) -> Self {
436        Self {
437            trait_name,
438            struct_name,
439            methods: Vec::new(),
440            doc_comment: None,
441        }
442    }
443
444    pub fn with_doc(mut self, doc: String) -> Self {
445        self.doc_comment = Some(doc);
446        self
447    }
448
449    pub fn add_method(&mut self, method: RustTraitImplMethod) {
450        self.methods.push(method);
451    }
452
453    pub fn with_method(mut self, method: RustTraitImplMethod) -> Self {
454        self.methods.push(method);
455        self
456    }
457
458    /// Returns true if this trait implementation has no methods
459    pub fn is_empty(&self) -> bool {
460        self.methods.is_empty()
461    }
462}
463
464/// Represents a method implementation in a trait impl block
465#[derive(Debug, Clone)]
466pub struct RustTraitImplMethod {
467    /// The name of the method
468    pub name: String,
469    /// The parameters of the method (excluding self)
470    pub params: Vec<RustMethodParam>,
471    /// The return type of the method
472    pub return_type: String,
473    /// The body of the method implementation
474    pub body: String,
475    /// Documentation comment for the method
476    pub doc_comment: Option<String>,
477    /// The self parameter type: None (no self), Some("&self"), Some("&mut self"), Some("self")
478    pub self_param: Option<String>,
479}
480
481impl RustTraitImplMethod {
482    pub fn new(name: String) -> Self {
483        Self {
484            name,
485            params: Vec::new(),
486            return_type: "()".to_string(),
487            body: "todo!()".to_string(),
488            doc_comment: None,
489            self_param: Some("&self".to_string()), // Default to &self for backward compatibility
490        }
491    }
492
493    pub fn with_return_type(mut self, return_type: String) -> Self {
494        self.return_type = return_type;
495        self
496    }
497
498    pub fn with_body(mut self, body: String) -> Self {
499        self.body = body;
500        self
501    }
502
503    pub fn with_doc(mut self, doc: String) -> Self {
504        self.doc_comment = Some(doc);
505        self
506    }
507
508    pub fn add_param(&mut self, param: RustMethodParam) {
509        self.params.push(param);
510    }
511
512    pub fn with_param(mut self, param: RustMethodParam) -> Self {
513        self.params.push(param);
514        self
515    }
516
517    pub fn with_self_param(mut self, self_param: Option<String>) -> Self {
518        self.self_param = self_param;
519        self
520    }
521}