oag_node_client/emitters/
types.rs1use std::collections::HashSet;
2
3use minijinja::{Environment, context};
4use oag_core::ir::{IrObjectSchema, IrReturnType, IrSchema, IrSpec};
5
6use crate::type_mapper::ir_type_to_ts;
7
8fn escape_jsdoc(value: String) -> String {
10 value.replace("*/", "*\\/")
11}
12
13pub fn emit_types(ir: &IrSpec) -> String {
15 let mut env = Environment::new();
16 env.set_trim_blocks(true);
17 env.add_filter("escape_jsdoc", escape_jsdoc);
18 env.add_template("types.ts.j2", include_str!("../../templates/types.ts.j2"))
19 .expect("template should be valid");
20 let tmpl = env.get_template("types.ts.j2").unwrap();
21
22 let schemas: Vec<_> = ir.schemas.iter().map(schema_to_ctx).collect();
23 let schema_names: HashSet<String> = ir
24 .schemas
25 .iter()
26 .map(|s| match s {
27 IrSchema::Object(o) => o.name.pascal_case.clone(),
28 IrSchema::Enum(e) => e.name.pascal_case.clone(),
29 IrSchema::Alias(a) => a.name.pascal_case.clone(),
30 IrSchema::Union(u) => u.name.pascal_case.clone(),
31 })
32 .collect();
33 let sse_event_types = collect_sse_event_types(ir, &schema_names);
34
35 tmpl.render(context! {
36 schemas => schemas,
37 sse_event_types => sse_event_types,
38 })
39 .expect("render should succeed")
40}
41
42fn schema_to_ctx(schema: &IrSchema) -> minijinja::Value {
43 match schema {
44 IrSchema::Object(obj) => object_to_ctx(obj),
45 IrSchema::Enum(e) => {
46 let variants: Vec<String> = e.variants.iter().map(|v| format!("\"{v}\"")).collect();
47 context! {
48 kind => "enum",
49 name => e.name.pascal_case.clone(),
50 description => e.description.clone(),
51 variants => variants,
52 }
53 }
54 IrSchema::Alias(a) => {
55 context! {
56 kind => "alias",
57 name => a.name.pascal_case.clone(),
58 description => a.description.clone(),
59 target => ir_type_to_ts(&a.target),
60 }
61 }
62 IrSchema::Union(u) => {
63 let variants: Vec<String> = u.variants.iter().map(ir_type_to_ts).collect();
64 context! {
65 kind => "union",
66 name => u.name.pascal_case.clone(),
67 description => u.description.clone(),
68 variants => variants,
69 }
70 }
71 }
72}
73
74fn object_to_ctx(obj: &IrObjectSchema) -> minijinja::Value {
75 let fields: Vec<minijinja::Value> = obj
76 .fields
77 .iter()
78 .map(|f| {
79 context! {
80 name => f.name.camel_case.clone(),
81 original_name => f.original_name.clone(),
82 type => ir_type_to_ts(&f.field_type),
83 required => f.required,
84 description => f.description.clone(),
85 }
86 })
87 .collect();
88
89 let additional = obj.additional_properties.as_ref().map(ir_type_to_ts);
90
91 context! {
92 kind => "object",
93 name => obj.name.pascal_case.clone(),
94 description => obj.description.clone(),
95 fields => fields,
96 additional_properties => additional,
97 }
98}
99
100fn collect_sse_event_types(ir: &IrSpec, schema_names: &HashSet<String>) -> Vec<minijinja::Value> {
101 let mut event_types = Vec::new();
102 let mut seen = HashSet::new();
103 for op in &ir.operations {
104 if let IrReturnType::Sse(sse) = &op.return_type
105 && let Some(ref event_name) = sse.event_type_name
106 {
107 if seen.contains(event_name) || schema_names.contains(event_name) {
108 continue;
109 }
110 let variants: Vec<String> = sse.variants.iter().map(ir_type_to_ts).collect();
111 if !variants.is_empty() {
112 seen.insert(event_name.clone());
113 event_types.push(context! {
114 name => event_name.clone(),
115 variants => variants,
116 });
117 }
118 }
119 }
120 event_types
121}