Skip to main content

nautilus_codegen/
type_helpers.rs

1//! Type mapping helpers for code generation.
2
3use nautilus_schema::ir::DefaultValue;
4use nautilus_schema::ir::{FieldIr, ResolvedFieldType, ScalarType};
5
6/// Convert a `ScalarType` to its Rust type string.
7pub(crate) fn scalar_to_rust_type(scalar: &ScalarType) -> String {
8    scalar.rust_type().to_string()
9}
10
11/// Get the Rust type for a field, including Option wrapper if nullable.
12pub fn field_to_rust_type(field: &FieldIr) -> String {
13    let base_type = match &field.field_type {
14        ResolvedFieldType::Scalar(scalar) => scalar_to_rust_type(scalar),
15        ResolvedFieldType::Enum { enum_name } => enum_name.clone(),
16        ResolvedFieldType::CompositeType { type_name } => type_name.clone(),
17        ResolvedFieldType::Relation(rel) => {
18            // Relation fields use the target model type.
19            // Single non-array relations are wrapped in `Option<Box<T>>` to
20            // avoid infinite-size struct issues when a model references itself
21            // or when two models reference each other. Array relations use
22            // `Vec<T>` and have no recursive-size concern.
23            if field.is_array {
24                format!("Vec<{}>", rel.target_model)
25            } else {
26                format!("Option<Box<{}>>", rel.target_model)
27            }
28        }
29    };
30
31    // Check array first, then Option
32    // Arrays and relations are handled separately
33    if field.is_array && !matches!(field.field_type, ResolvedFieldType::Relation(_)) {
34        format!("Vec<{}>", base_type)
35    } else if !field.is_required && !matches!(field.field_type, ResolvedFieldType::Relation(_)) {
36        format!("Option<{}>", base_type)
37    } else {
38        base_type
39    }
40}
41
42/// Check if a field should be auto-generated (excluded from create builders).
43pub fn is_auto_generated(field: &FieldIr) -> bool {
44    if field.computed.is_some() {
45        return true;
46    }
47    if let Some(default) = &field.default_value {
48        matches!(
49            default,
50            DefaultValue::Function(func) if func.name == "autoincrement" || func.name == "uuid"
51        )
52    } else {
53        false
54    }
55}