Skip to main content

oag_fastapi_server/emitters/
models.rs

1use minijinja::{Environment, context};
2use oag_core::ir::{IrObjectSchema, IrSchema, IrSpec};
3
4use crate::type_mapper::{ir_type_to_python, ir_type_to_python_field};
5
6/// Emit `models.py` — Pydantic v2 BaseModel classes from IrSchema.
7pub fn emit_models(ir: &IrSpec) -> String {
8    let mut env = Environment::new();
9    env.add_template("models.py.j2", include_str!("../../templates/models.py.j2"))
10        .expect("template should be valid");
11    let tmpl = env.get_template("models.py.j2").unwrap();
12
13    let schemas: Vec<_> = ir.schemas.iter().map(schema_to_ctx).collect();
14
15    tmpl.render(context! {
16        schemas => schemas,
17    })
18    .expect("render should succeed")
19}
20
21fn schema_to_ctx(schema: &IrSchema) -> minijinja::Value {
22    match schema {
23        IrSchema::Object(obj) => object_to_ctx(obj),
24        IrSchema::Enum(e) => {
25            let variants: Vec<minijinja::Value> = e
26                .variants
27                .iter()
28                .map(|v| {
29                    context! {
30                        name => heck::AsUpperCamelCase(v).to_string(),
31                        value => v.clone(),
32                    }
33                })
34                .collect();
35            context! {
36                kind => "enum",
37                name => e.name.pascal_case.clone(),
38                description => e.description.clone(),
39                variants => variants,
40            }
41        }
42        IrSchema::Alias(a) => {
43            context! {
44                kind => "alias",
45                name => a.name.pascal_case.clone(),
46                description => a.description.clone(),
47                target => ir_type_to_python(&a.target),
48            }
49        }
50        IrSchema::Union(u) => {
51            let variants: Vec<String> = u.variants.iter().map(ir_type_to_python).collect();
52            context! {
53                kind => "union",
54                name => u.name.pascal_case.clone(),
55                description => u.description.clone(),
56                variants => variants,
57            }
58        }
59    }
60}
61
62fn object_to_ctx(obj: &IrObjectSchema) -> minijinja::Value {
63    let fields: Vec<minijinja::Value> = obj
64        .fields
65        .iter()
66        .map(|f| {
67            context! {
68                name => f.name.snake_case.clone(),
69                original_name => f.original_name.clone(),
70                type_str => ir_type_to_python_field(&f.field_type, f.required),
71                required => f.required,
72                description => f.description.clone(),
73                needs_alias => f.name.snake_case != f.original_name,
74            }
75        })
76        .collect();
77
78    let has_additional_properties = obj.additional_properties.is_some();
79
80    context! {
81        kind => "object",
82        name => obj.name.pascal_case.clone(),
83        description => obj.description.clone(),
84        fields => fields,
85        has_additional_properties => has_additional_properties,
86    }
87}