Skip to main content

oag_node_client/
type_mapper.rs

1use oag_core::ir::IrType;
2
3/// Map an `IrType` to its TypeScript type string representation.
4pub fn ir_type_to_ts(ir_type: &IrType) -> String {
5    match ir_type {
6        IrType::String => "string".to_string(),
7        IrType::StringLiteral(s) => format!("\"{s}\""),
8        IrType::Number => "number".to_string(),
9        IrType::Integer => "number".to_string(),
10        IrType::Boolean => "boolean".to_string(),
11        IrType::Null => "null".to_string(),
12        IrType::DateTime => "string".to_string(),
13        IrType::Binary => "Blob".to_string(),
14        IrType::Any => "unknown".to_string(),
15        IrType::Void => "void".to_string(),
16        IrType::Ref(name) => name.clone(),
17        IrType::Array(inner) => {
18            let inner_ts = ir_type_to_ts(inner);
19            if inner_ts.contains('|') {
20                format!("({inner_ts})[]")
21            } else {
22                format!("{inner_ts}[]")
23            }
24        }
25        IrType::Map(value_type) => {
26            let value_ts = ir_type_to_ts(value_type);
27            format!("Record<string, {value_ts}>")
28        }
29        IrType::Object(fields) => {
30            if fields.is_empty() {
31                return "Record<string, unknown>".to_string();
32            }
33            let field_strs: Vec<String> = fields
34                .iter()
35                .map(|(name, ty, required)| {
36                    let ts_type = ir_type_to_ts(ty);
37                    if *required {
38                        format!("{name}: {ts_type}")
39                    } else {
40                        format!("{name}?: {ts_type}")
41                    }
42                })
43                .collect();
44            format!("{{ {} }}", field_strs.join("; "))
45        }
46        IrType::Union(variants) => {
47            let variant_strs: Vec<String> = variants.iter().map(ir_type_to_ts).collect();
48            variant_strs.join(" | ")
49        }
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56
57    #[test]
58    fn test_primitives() {
59        assert_eq!(ir_type_to_ts(&IrType::String), "string");
60        assert_eq!(ir_type_to_ts(&IrType::Number), "number");
61        assert_eq!(ir_type_to_ts(&IrType::Integer), "number");
62        assert_eq!(ir_type_to_ts(&IrType::Boolean), "boolean");
63        assert_eq!(ir_type_to_ts(&IrType::Null), "null");
64        assert_eq!(ir_type_to_ts(&IrType::Any), "unknown");
65        assert_eq!(ir_type_to_ts(&IrType::Void), "void");
66    }
67
68    #[test]
69    fn test_array() {
70        assert_eq!(
71            ir_type_to_ts(&IrType::Array(Box::new(IrType::String))),
72            "string[]"
73        );
74        assert_eq!(
75            ir_type_to_ts(&IrType::Array(Box::new(IrType::Union(vec![
76                IrType::String,
77                IrType::Number,
78            ])))),
79            "(string | number)[]"
80        );
81    }
82
83    #[test]
84    fn test_map() {
85        assert_eq!(
86            ir_type_to_ts(&IrType::Map(Box::new(IrType::String))),
87            "Record<string, string>"
88        );
89    }
90
91    #[test]
92    fn test_ref() {
93        assert_eq!(ir_type_to_ts(&IrType::Ref("Pet".to_string())), "Pet");
94    }
95
96    #[test]
97    fn test_union() {
98        assert_eq!(
99            ir_type_to_ts(&IrType::Union(vec![IrType::String, IrType::Number])),
100            "string | number"
101        );
102    }
103}