baobao_codegen_typescript/
type_mapper.rs

1//! TypeScript type mapper implementation.
2
3#[cfg(test)]
4use baobao_codegen::builder::TypeRef;
5use baobao_codegen::{
6    builder::{PrimitiveType, TypeMapper as CodeIRTypeMapper},
7    language::TypeMapper,
8};
9use baobao_core::{ArgType, ContextFieldType, DatabaseType};
10
11/// TypeScript type mapper implementation.
12pub struct TypeScriptTypeMapper;
13
14/// TypeScript Code IR type mapper implementation.
15///
16/// Maps language-agnostic TypeRef types to TypeScript type syntax.
17#[derive(Debug, Clone, Copy, Default)]
18pub struct TypeScriptCodeTypeMapper;
19
20impl CodeIRTypeMapper for TypeScriptCodeTypeMapper {
21    fn map_primitive(&self, ty: PrimitiveType) -> String {
22        match ty {
23            PrimitiveType::String => "string".to_string(),
24            PrimitiveType::Int | PrimitiveType::UInt | PrimitiveType::Float => "number".to_string(),
25            PrimitiveType::Bool => "boolean".to_string(),
26            PrimitiveType::Path => "string".to_string(),
27            PrimitiveType::Duration => "number".to_string(), // milliseconds
28            PrimitiveType::Char => "string".to_string(),
29            PrimitiveType::Byte => "number".to_string(),
30        }
31    }
32
33    fn map_optional(&self, inner: &str) -> String {
34        format!("{} | undefined", inner)
35    }
36
37    fn map_array(&self, inner: &str) -> String {
38        format!("{}[]", inner)
39    }
40
41    fn map_result(&self, ok: &str, _err: &str) -> String {
42        // TypeScript doesn't have a built-in Result type
43        // Just return the success type (errors are handled differently)
44        ok.to_string()
45    }
46
47    fn map_unit(&self) -> String {
48        "void".to_string()
49    }
50}
51
52impl TypeMapper for TypeScriptTypeMapper {
53    fn language(&self) -> &'static str {
54        "typescript"
55    }
56
57    fn map_arg_type(&self, arg_type: ArgType) -> &'static str {
58        match arg_type {
59            ArgType::String => "string",
60            ArgType::Int => "number",
61            ArgType::Float => "number",
62            ArgType::Bool => "boolean",
63            ArgType::Path => "string",
64        }
65    }
66
67    fn map_optional_arg_type(&self, arg_type: ArgType) -> String {
68        format!("{} | undefined", self.map_arg_type(arg_type))
69    }
70
71    fn map_context_type(&self, field_type: &ContextFieldType) -> &'static str {
72        match field_type {
73            // Bun's native SQLite
74            ContextFieldType::Database(DatabaseType::Sqlite) => "Database",
75            // For Postgres/MySQL, we'll use placeholder types for now
76            ContextFieldType::Database(DatabaseType::Postgres) => "unknown",
77            ContextFieldType::Database(DatabaseType::Mysql) => "unknown",
78            ContextFieldType::Http => "unknown",
79        }
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86
87    #[test]
88    fn test_typescript_arg_types() {
89        let mapper = TypeScriptTypeMapper;
90
91        assert_eq!(mapper.map_arg_type(ArgType::String), "string");
92        assert_eq!(mapper.map_arg_type(ArgType::Int), "number");
93        assert_eq!(mapper.map_arg_type(ArgType::Float), "number");
94        assert_eq!(mapper.map_arg_type(ArgType::Bool), "boolean");
95        assert_eq!(mapper.map_arg_type(ArgType::Path), "string");
96    }
97
98    #[test]
99    fn test_typescript_optional_types() {
100        let mapper = TypeScriptTypeMapper;
101
102        assert_eq!(
103            mapper.map_optional_arg_type(ArgType::String),
104            "string | undefined"
105        );
106        assert_eq!(
107            mapper.map_optional_arg_type(ArgType::Int),
108            "number | undefined"
109        );
110    }
111
112    #[test]
113    fn test_typescript_context_types() {
114        let mapper = TypeScriptTypeMapper;
115
116        assert_eq!(
117            mapper.map_context_type(&ContextFieldType::Database(DatabaseType::Sqlite)),
118            "Database"
119        );
120    }
121
122    #[test]
123    fn test_typescript_code_type_mapper_primitives() {
124        let mapper = TypeScriptCodeTypeMapper;
125
126        assert_eq!(mapper.map_primitive(PrimitiveType::String), "string");
127        assert_eq!(mapper.map_primitive(PrimitiveType::Int), "number");
128        assert_eq!(mapper.map_primitive(PrimitiveType::UInt), "number");
129        assert_eq!(mapper.map_primitive(PrimitiveType::Float), "number");
130        assert_eq!(mapper.map_primitive(PrimitiveType::Bool), "boolean");
131        assert_eq!(mapper.map_primitive(PrimitiveType::Path), "string");
132        assert_eq!(mapper.map_primitive(PrimitiveType::Duration), "number");
133    }
134
135    #[test]
136    fn test_typescript_code_type_mapper_complex() {
137        let mapper = TypeScriptCodeTypeMapper;
138
139        assert_eq!(mapper.map_optional("string"), "string | undefined");
140        assert_eq!(mapper.map_array("number"), "number[]");
141        assert_eq!(mapper.map_result("void", "Error"), "void");
142        assert_eq!(mapper.map_unit(), "void");
143    }
144
145    #[test]
146    fn test_typescript_code_type_mapper_render() {
147        let mapper = TypeScriptCodeTypeMapper;
148
149        // Test rendering complex TypeRef
150        let string_type = TypeRef::string();
151        assert_eq!(mapper.render_type(&string_type), "string");
152
153        let opt_string = TypeRef::optional(TypeRef::string());
154        assert_eq!(mapper.render_type(&opt_string), "string | undefined");
155
156        let arr_int = TypeRef::array(TypeRef::int());
157        assert_eq!(mapper.render_type(&arr_int), "number[]");
158
159        let unit = TypeRef::unit();
160        assert_eq!(mapper.render_type(&unit), "void");
161    }
162}