use serde_json::Value;
use crate::registry::{Field, FieldType, Tone, Variant};
pub fn json_of<T: schemars::JsonSchema>() -> Value {
serde_json::to_value(schemars::schema_for!(T)).expect("schema serializes")
}
pub fn kind_doc(schema: &Value) -> String {
schema["description"]
.as_str()
.unwrap_or_default()
.to_string()
}
pub fn doc_of(schema: &Value, name: &str) -> String {
schema["properties"][name]["description"]
.as_str()
.unwrap_or_default()
.to_string()
}
pub fn field(schema: &Value, name: &str, ty: FieldType, role: Option<&str>) -> Field {
Field {
name: name.into(),
doc: doc_of(schema, name),
ty,
role: role.map(str::to_string),
refers_to: None,
}
}
pub fn enum_ty(schema: &Value, name: &str) -> FieldType {
FieldType::Enum(enum_variants(schema, name))
}
pub fn link(allowed: &[&str]) -> FieldType {
FieldType::Link {
allowed: Some(allowed.iter().map(|s| s.to_string()).collect()),
}
}
pub fn any_link() -> FieldType {
FieldType::Link { allowed: None }
}
pub fn opt(ty: FieldType) -> FieldType {
FieldType::Option(Box::new(ty))
}
pub fn list(ty: FieldType) -> FieldType {
FieldType::List(Box::new(ty))
}
pub fn toned(variants: Vec<Variant>, tones: &[(&str, Tone)]) -> Vec<Variant> {
variants
.into_iter()
.map(|mut v| {
let (_, tone) = tones
.iter()
.find(|(n, _)| *n == v.name)
.unwrap_or_else(|| panic!("variant '{}' has no tone mapping", v.name));
v.tone = Some(*tone);
v
})
.collect()
}
pub fn enum_variants(schema: &Value, name: &str) -> Vec<Variant> {
let prop = &schema["properties"][name];
find_variants(prop, schema)
.unwrap_or_else(|| panic!("field '{name}' has no enum variants in its schema"))
}
fn unit(name: &str, doc: &str) -> Variant {
Variant {
name: name.into(),
doc: doc.into(),
fields: Vec::new(),
tone: None,
}
}
fn find_variants(v: &Value, root: &Value) -> Option<Vec<Variant>> {
match v {
Value::Object(map) => {
if let Some(Value::Array(vals)) = map.get("enum") {
return Some(
vals.iter()
.filter_map(|x| x.as_str().map(|n| unit(n, "")))
.collect(),
);
}
if let Some(Value::Array(alts)) = map.get("oneOf") {
let mut variants: Vec<Variant> = Vec::new();
for alt in alts {
if let Some(name) = alt["const"].as_str() {
variants.push(unit(name, alt["description"].as_str().unwrap_or_default()));
} else if let Some(Value::Array(vals)) = alt.get("enum") {
variants
.extend(vals.iter().filter_map(|x| x.as_str().map(|n| unit(n, ""))));
}
}
if !variants.is_empty() {
return Some(variants);
}
}
if let Some(Value::String(r)) = map.get("$ref")
&& let Some(def) = r.strip_prefix("#/$defs/")
{
return find_variants(&root["$defs"][def], root);
}
map.values().find_map(|x| find_variants(x, root))
}
Value::Array(items) => items.iter().find_map(|x| find_variants(x, root)),
_ => None,
}
}