use super::SchemaRenderer;
use indexmap::IndexMap;
use miette::IntoDiagnostic;
use schematic_types::*;
use std::collections::HashSet;
use std::fs;
use std::path::Path;
#[derive(Debug, Default)]
pub struct SchemaGenerator {
references: HashSet<String>,
schemas: IndexMap<String, SchemaType>,
}
impl SchemaGenerator {
pub fn add<T: Schematic>(&mut self) {
let schema = T::generate_schema();
self.add_schema(&schema);
}
pub fn add_schema(&mut self, schema: &SchemaType) {
let mut schema = schema.to_owned();
match &mut schema {
SchemaType::Array(ArrayType { items_type, .. }) => {
self.add_schema(items_type);
}
SchemaType::Enum(EnumType { variants, .. }) => {
if let Some(variants) = variants.as_ref() {
for field in variants {
self.add_schema(&field.type_of);
}
}
}
SchemaType::Object(ObjectType {
key_type,
value_type,
..
}) => {
self.add_schema(key_type);
self.add_schema(value_type);
}
SchemaType::Struct(StructType { ref mut fields, .. }) => {
fields.sort_by(|a, d| a.name.cmp(&d.name));
for field in fields {
self.add_schema(&field.type_of);
}
}
SchemaType::Tuple(TupleType { items_types, .. }) => {
for item in items_types {
self.add_schema(item);
}
}
SchemaType::Union(UnionType {
variants_types,
variants,
..
}) => {
for variant in variants_types {
self.add_schema(variant);
}
if let Some(variants) = variants.as_ref() {
for field in variants {
self.add_schema(&field.type_of);
}
}
}
_ => {}
};
if let Some(name) = schema.get_name() {
self.references.insert(name.to_owned());
self.schemas.insert(name.to_owned(), schema);
}
}
pub fn generate<P: AsRef<Path>, O, R: SchemaRenderer<O>>(
&self,
output_file: P,
mut renderer: R,
) -> miette::Result<()> {
let output_file = output_file.as_ref();
let mut output = renderer.render(&self.schemas, &self.references)?;
output.push('\n');
if let Some(parent) = output_file.parent() {
fs::create_dir_all(parent).into_diagnostic()?;
}
fs::write(output_file, output).into_diagnostic()?;
Ok(())
}
}