fn main() {
#[cfg(all(feature = "schema-gen", not(feature = "test")))]
{
use std::path::Path;
use disposition_input_model::InputDiagram;
let mut schema = schemars::schema_for!(InputDiagram);
theme_attr_inject(&mut schema);
style_alias_casing_fix(&mut schema);
entity_type_casing_fix(&mut schema);
let schema_json = serde_json::to_string_pretty(&schema)
.expect("Failed to serialize `InputDiagram` JSON schema.");
let out_path = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("src")
.join("completion")
.join("input_diagram_schema.json");
std::fs::create_dir_all(out_path.parent().expect("Expected parent directory."))
.expect("Failed to create `completion` directory.");
std::fs::write(&out_path, format!("{schema_json}\n"))
.unwrap_or_else(|e| panic!("Failed to write schema to {}: {e}", out_path.display()));
println!("Wrote schema to {}", out_path.display());
}
#[cfg(any(not(feature = "schema-gen"), feature = "test"))]
eprintln!(
"The `schema_gen` binary must be built with `--features schema-gen` and \
without the `test` feature."
);
}
#[cfg(all(feature = "schema-gen", not(feature = "test")))]
fn theme_attr_inject(schema: &mut schemars::Schema) {
use disposition_input_model::theme::ThemeAttr;
let mut theme_attr_value = schemars::schema_for!(ThemeAttr).to_value();
if let Some(theme_attr_object) = theme_attr_value.as_object_mut() {
theme_attr_object.remove("$schema");
theme_attr_object.remove("$id");
theme_attr_object.remove("title");
}
let defs = schema
.as_object_mut()
.and_then(|root| root.get_mut("$defs"))
.and_then(serde_json::Value::as_object_mut)
.expect("Expected `$defs` in the `InputDiagram` schema.");
defs.insert("ThemeAttr".to_string(), theme_attr_value);
let css_class_partials = defs
.get_mut("CssClassPartials")
.and_then(serde_json::Value::as_object_mut)
.expect("Expected `CssClassPartials` in the `InputDiagram` schema `$defs`.");
css_class_partials.insert(
"propertyNames".to_string(),
serde_json::json!({ "$ref": "#/$defs/ThemeAttr" }),
);
}
#[cfg(all(feature = "schema-gen", not(feature = "test")))]
fn style_alias_casing_fix(schema: &mut schemars::Schema) {
let style_alias_one_of = schema
.as_object_mut()
.and_then(|root| root.get_mut("$defs"))
.and_then(serde_json::Value::as_object_mut)
.and_then(|defs| defs.get_mut("StyleAlias"))
.and_then(serde_json::Value::as_object_mut)
.and_then(|style_alias| style_alias.get_mut("oneOf"))
.and_then(serde_json::Value::as_array_mut)
.expect("Expected `StyleAlias.oneOf` array in the `InputDiagram` schema `$defs`.");
for variant in style_alias_one_of.iter_mut() {
let Some(variant_object) = variant.as_object_mut() else {
continue;
};
match variant_object
.get("const")
.and_then(serde_json::Value::as_str)
{
Some(pascal_case) => {
let snake_case = pascal_case_to_snake_case(pascal_case);
variant_object.insert("const".to_string(), serde_json::Value::String(snake_case));
}
None => {
let description = variant_object
.get("description")
.cloned()
.unwrap_or_else(|| {
serde_json::Value::String("Custom user-defined style alias.".to_string())
});
variant_object.clear();
variant_object.insert(
"type".to_string(),
serde_json::Value::String("string".to_string()),
);
variant_object.insert("description".to_string(), description);
}
}
}
}
#[cfg(all(feature = "schema-gen", not(feature = "test")))]
fn entity_type_casing_fix(schema: &mut schemars::Schema) {
let entity_type_one_of = schema
.as_object_mut()
.and_then(|root| root.get_mut("$defs"))
.and_then(serde_json::Value::as_object_mut)
.and_then(|defs| defs.get_mut("EntityType"))
.and_then(serde_json::Value::as_object_mut)
.and_then(|entity_type| entity_type.get_mut("oneOf"))
.and_then(serde_json::Value::as_array_mut)
.expect("Expected `EntityType.oneOf` array in the `InputDiagram` schema `$defs`.");
for variant in entity_type_one_of.iter_mut() {
let Some(variant_object) = variant.as_object_mut() else {
continue;
};
match variant_object
.get("const")
.and_then(serde_json::Value::as_str)
{
Some(pascal_case) => {
let snake_case = pascal_case_to_snake_case(pascal_case);
let serialized = if pascal_case == "ContainerInbuilt" {
snake_case
} else {
format!("type_{snake_case}")
};
variant_object.insert("const".to_string(), serde_json::Value::String(serialized));
}
None => {
let description = variant_object
.get("description")
.cloned()
.unwrap_or_else(|| {
serde_json::Value::String("Custom user-defined type.".to_string())
});
variant_object.clear();
variant_object.insert(
"type".to_string(),
serde_json::Value::String("string".to_string()),
);
variant_object.insert("description".to_string(), description);
}
}
}
}
#[cfg(all(feature = "schema-gen", not(feature = "test")))]
fn pascal_case_to_snake_case(pascal_case: &str) -> String {
let mut snake_case = String::with_capacity(pascal_case.len() + 4);
let mut previous_is_alphabetic = false;
for (char_index, character) in pascal_case.chars().enumerate() {
let needs_separator = char_index > 0
&& (character.is_ascii_uppercase()
|| (character.is_ascii_digit() && previous_is_alphabetic));
if needs_separator {
snake_case.push('_');
}
snake_case.push(character.to_ascii_lowercase());
previous_is_alphabetic = character.is_ascii_alphabetic();
}
snake_case
}