Skip to main content

vexil_codegen_rust/
types.rs

1use std::collections::HashSet;
2use vexil_lang::ast::{PrimitiveType, SemanticType, SubByteType};
3use vexil_lang::ir::{ResolvedType, TypeDef, TypeId, TypeRegistry};
4
5/// Convert a ResolvedType to its Rust type string.
6/// `needs_box` contains `(type_id, field_index)` pairs that need Box wrapping.
7/// `current_context` is `Some((type_id, field_index))` when we're in a boxed context.
8pub fn rust_type(
9    ty: &ResolvedType,
10    registry: &TypeRegistry,
11    needs_box: &HashSet<(TypeId, usize)>,
12    context: Option<(TypeId, usize)>,
13) -> String {
14    match ty {
15        ResolvedType::Primitive(p) => primitive_type(p).to_string(),
16        ResolvedType::SubByte(s) => sub_byte_type(s).to_string(),
17        ResolvedType::Semantic(s) => semantic_type(s).to_string(),
18        ResolvedType::Named(id) => {
19            let name = match registry.get(*id) {
20                Some(def) => type_def_name(def),
21                None => "UnresolvedType".to_string(),
22            };
23            if context.is_some_and(|ctx| needs_box.contains(&ctx)) {
24                format!("Box<{name}>")
25            } else {
26                name
27            }
28        }
29        ResolvedType::Optional(inner) => {
30            let inner_str = rust_type(inner, registry, needs_box, context);
31            format!("Option<{inner_str}>")
32        }
33        ResolvedType::Array(inner) => {
34            let inner_str = rust_type(inner, registry, needs_box, None);
35            format!("Vec<{inner_str}>")
36        }
37        ResolvedType::Map(k, v) => {
38            let k_str = rust_type(k, registry, needs_box, None);
39            let v_str = rust_type(v, registry, needs_box, None);
40            format!("BTreeMap<{k_str}, {v_str}>")
41        }
42        ResolvedType::Result(ok, err) => {
43            let ok_str = rust_type(ok, registry, needs_box, context);
44            let err_str = rust_type(err, registry, needs_box, context);
45            format!("Result<{ok_str}, {err_str}>")
46        }
47        _ => "UnknownType".to_string(),
48    }
49}
50
51fn primitive_type(p: &PrimitiveType) -> &'static str {
52    match p {
53        PrimitiveType::Bool => "bool",
54        PrimitiveType::U8 => "u8",
55        PrimitiveType::U16 => "u16",
56        PrimitiveType::U32 => "u32",
57        PrimitiveType::U64 => "u64",
58        PrimitiveType::I8 => "i8",
59        PrimitiveType::I16 => "i16",
60        PrimitiveType::I32 => "i32",
61        PrimitiveType::I64 => "i64",
62        PrimitiveType::F32 => "f32",
63        PrimitiveType::F64 => "f64",
64        PrimitiveType::Void => "()",
65    }
66}
67
68fn sub_byte_type(s: &SubByteType) -> &'static str {
69    if s.signed {
70        "i8"
71    } else {
72        "u8"
73    }
74}
75
76fn semantic_type(s: &SemanticType) -> &'static str {
77    match s {
78        SemanticType::String => "String",
79        SemanticType::Bytes => "Vec<u8>",
80        SemanticType::Rgb => "(u8, u8, u8)",
81        SemanticType::Uuid => "[u8; 16]",
82        SemanticType::Timestamp => "i64",
83        SemanticType::Hash => "[u8; 32]",
84    }
85}
86
87fn type_def_name(def: &TypeDef) -> String {
88    match def {
89        TypeDef::Message(m) => m.name.to_string(),
90        TypeDef::Enum(e) => e.name.to_string(),
91        TypeDef::Flags(f) => f.name.to_string(),
92        TypeDef::Union(u) => u.name.to_string(),
93        TypeDef::Newtype(n) => n.name.to_string(),
94        TypeDef::Config(c) => c.name.to_string(),
95        _ => "UnknownTypeDef".to_string(),
96    }
97}