Skip to main content

oag_node_client/emitters/
types.rs

1use minijinja::{Environment, context};
2use oag_core::ir::{IrObjectSchema, IrReturnType, IrSchema, IrSpec};
3
4use crate::type_mapper::ir_type_to_ts;
5
6/// Emit `types.ts` containing all interfaces, enums, aliases, and SSE event union types.
7pub fn emit_types(ir: &IrSpec) -> String {
8    let mut env = Environment::new();
9    env.add_template("types.ts.j2", include_str!("../../templates/types.ts.j2"))
10        .expect("template should be valid");
11    let tmpl = env.get_template("types.ts.j2").unwrap();
12
13    let schemas: Vec<_> = ir.schemas.iter().map(schema_to_ctx).collect();
14    let sse_event_types = collect_sse_event_types(ir);
15
16    tmpl.render(context! {
17        schemas => schemas,
18        sse_event_types => sse_event_types,
19    })
20    .expect("render should succeed")
21}
22
23fn schema_to_ctx(schema: &IrSchema) -> minijinja::Value {
24    match schema {
25        IrSchema::Object(obj) => object_to_ctx(obj),
26        IrSchema::Enum(e) => {
27            let variants: Vec<String> = e.variants.iter().map(|v| format!("\"{v}\"")).collect();
28            context! {
29                kind => "enum",
30                name => e.name.pascal_case.clone(),
31                description => e.description.clone(),
32                variants => variants,
33            }
34        }
35        IrSchema::Alias(a) => {
36            context! {
37                kind => "alias",
38                name => a.name.pascal_case.clone(),
39                description => a.description.clone(),
40                target => ir_type_to_ts(&a.target),
41            }
42        }
43        IrSchema::Union(u) => {
44            let variants: Vec<String> = u.variants.iter().map(ir_type_to_ts).collect();
45            context! {
46                kind => "union",
47                name => u.name.pascal_case.clone(),
48                description => u.description.clone(),
49                variants => variants,
50            }
51        }
52    }
53}
54
55fn object_to_ctx(obj: &IrObjectSchema) -> minijinja::Value {
56    let fields: Vec<minijinja::Value> = obj
57        .fields
58        .iter()
59        .map(|f| {
60            context! {
61                name => f.name.camel_case.clone(),
62                original_name => f.original_name.clone(),
63                type => ir_type_to_ts(&f.field_type),
64                required => f.required,
65                description => f.description.clone(),
66            }
67        })
68        .collect();
69
70    let additional = obj.additional_properties.as_ref().map(ir_type_to_ts);
71
72    context! {
73        kind => "object",
74        name => obj.name.pascal_case.clone(),
75        description => obj.description.clone(),
76        fields => fields,
77        additional_properties => additional,
78    }
79}
80
81fn collect_sse_event_types(ir: &IrSpec) -> Vec<minijinja::Value> {
82    let mut event_types = Vec::new();
83    for op in &ir.operations {
84        if let IrReturnType::Sse(sse) = &op.return_type
85            && let Some(ref event_name) = sse.event_type_name
86        {
87            let variants: Vec<String> = sse.variants.iter().map(ir_type_to_ts).collect();
88            if !variants.is_empty() {
89                event_types.push(context! {
90                    name => event_name.clone(),
91                    variants => variants,
92                });
93            }
94        }
95    }
96    event_types
97}