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