use super::*;
#[must_use]
pub fn config_schema() -> serde_json::Value {
let schema = schemars::generate::SchemaSettings::draft07()
.into_generator()
.into_root_schema_for::<Config>();
let mut value = schema.to_value();
canonicalize_schema(&mut value);
value
}
const SCHEMA_KEYWORD_ORDER: &[&str] = &[
"$id",
"$schema",
"title",
"description",
"default",
"deprecated",
"readOnly",
"writeOnly",
"type",
"format",
"enum",
"const",
"allOf",
"anyOf",
"oneOf",
"not",
"if",
"then",
"else",
"multipleOf",
"maximum",
"exclusiveMaximum",
"minimum",
"exclusiveMinimum",
"maxLength",
"minLength",
"pattern",
"items",
"additionalItems",
"maxItems",
"minItems",
"uniqueItems",
"contains",
"maxProperties",
"minProperties",
"required",
"properties",
"patternProperties",
"additionalProperties",
"propertyNames",
"$ref",
"definitions",
];
const SCHEMA_DEFINITION_MAPS: &[&str] = &["properties", "patternProperties", "definitions"];
fn canonicalize_schema(value: &mut serde_json::Value) {
use serde_json::Value;
match value {
Value::Object(map) => {
if let Some(Value::String(d)) = map.get_mut("description") {
*d = collapse_description(d);
}
if let Some(Value::Array(required)) = map.get_mut("required") {
required.sort_by(|a, b| match (a.as_str(), b.as_str()) {
(Some(x), Some(y)) => x.cmp(y),
_ => std::cmp::Ordering::Equal,
});
}
for (key, child) in map.iter_mut() {
match key.as_str() {
k if SCHEMA_DEFINITION_MAPS.contains(&k) => {
if let Value::Object(entries) = child {
sort_object_by_key(entries);
for sub in entries.values_mut() {
canonicalize_schema(sub);
}
}
}
"default" | "enum" | "const" | "examples" => {}
_ => canonicalize_schema(child),
}
}
reorder_object(map, SCHEMA_KEYWORD_ORDER);
}
Value::Array(items) => {
for item in items {
canonicalize_schema(item);
}
}
_ => {}
}
}
fn reorder_object(map: &mut serde_json::Map<String, serde_json::Value>, order: &[&str]) {
let mut keys: Vec<String> = map.keys().cloned().collect();
keys.sort_by(|a, b| {
let rank = |k: &str| order.iter().position(|o| *o == k).unwrap_or(order.len());
rank(a).cmp(&rank(b)).then_with(|| a.cmp(b))
});
let mut rebuilt = serde_json::Map::with_capacity(map.len());
for k in keys {
if let Some(v) = map.remove(&k) {
rebuilt.insert(k, v);
}
}
*map = rebuilt;
}
fn sort_object_by_key(map: &mut serde_json::Map<String, serde_json::Value>) {
let mut keys: Vec<String> = map.keys().cloned().collect();
keys.sort();
let mut rebuilt = serde_json::Map::with_capacity(map.len());
for k in keys {
if let Some(v) = map.remove(&k) {
rebuilt.insert(k, v);
}
}
*map = rebuilt;
}
fn collapse_description(s: &str) -> String {
s.split("\n\n")
.map(|para| {
para.split('\n')
.map(str::trim)
.collect::<Vec<_>>()
.join(" ")
})
.collect::<Vec<_>>()
.join("\n\n")
}