mod schema_data;
use std::collections::HashSet;
use handlebars::Handlebars;
use indexmap::IndexMap;
use schemars::schema::{RootSchema, Schema};
use serde::{Deserialize, Serialize};
use serde_json::json;
use crate::{
CodegenResult, SchemaDefinitions, case::Case, format::format_ts, schema_type::SchemaType,
typegen::schema_data::ObjectSchemaData, utils::assign_type_names,
};
static TYPES_TEMPLATE: &str = include_str!("./types.handlebars");
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TypegenResult {
pub types_generated: usize,
pub type_signature: String,
pub all_optional: bool,
pub types: String,
}
pub fn generate_types(root_schema: RootSchema, type_name: &str) -> CodegenResult<TypegenResult> {
let mut defs: SchemaDefinitions = IndexMap::new();
for (ref_key, s) in root_schema.definitions {
let type_name = Case::Pascal.sanitize(format!("{type_name} {ref_key}"));
defs.insert(ref_key, assign_type_names(s, &type_name));
}
let schema = assign_type_names(
Schema::Object(root_schema.schema),
&Case::Pascal.sanitize(type_name),
);
let to_generate = ObjectSchemaData::collect(&schema, &defs)?;
let types = Handlebars::new()
.render_template(TYPES_TEMPLATE, &json!({"objects": to_generate}))
.unwrap();
Ok(TypegenResult {
types: format_ts(&types),
types_generated: to_generate.len(),
type_signature: SchemaType::from(&schema).type_signature(true, &defs)?,
all_optional: is_all_optional(&schema, &defs)?,
})
}
fn is_all_optional(schema: &Schema, defs: &SchemaDefinitions) -> CodegenResult<bool> {
let mut schema_type = SchemaType::from(schema);
let mut visited = HashSet::new();
loop {
if let SchemaType::Reference(ref_st) = &schema_type {
let is_new = visited.insert(ref_st.ref_key.clone());
if is_new {
let followed = ref_st.follow(defs)?;
schema_type = SchemaType::from(followed)
} else {
break;
}
} else {
break;
}
}
match schema_type {
SchemaType::Map(_) => Ok(true),
SchemaType::Object(obj_st) => Ok(obj_st.obj.required.is_empty()),
_ => Ok(false),
}
}