use std::fmt::Write;
use crate::schema::model::{CimAttribute, CimSpecification, CimType};
use crate::schema::table::{self, Kind};
fn ordered_ids(spec: &CimSpecification) -> Vec<&String> {
let mut ids: Vec<&String> = spec.types.keys().collect();
ids.sort();
ids
}
fn attr_kind(a: &CimAttribute) -> &'static str {
match table::attr_kind(a) {
Kind::Enum => "AttrKind::Enum",
Kind::Literal => "AttrKind::Literal",
Kind::Association => "AttrKind::Association",
}
}
fn pairs(name: &str, doc: &str, v: &[(&str, &str)]) -> String {
let mut s = format!("/// {doc}\npub static {name}: &[(&str, &str)] = &[\n");
for (a, b) in v {
s.push_str(&format!(" (\"{a}\", \"{b}\"),\n"));
}
s.push_str("];\n");
s
}
fn str_list(items: &[String]) -> String {
let inner: Vec<String> = items.iter().map(|o| format!("\"{o}\"")).collect();
format!("&[{}]", inner.join(", "))
}
pub fn render_classes(spec: &CimSpecification) -> String {
let family = spec.family;
let ids = ordered_ids(spec);
let index_of: std::collections::HashMap<&str, usize> = ids
.iter()
.enumerate()
.map(|(i, id)| (id.as_str(), i))
.collect();
let mut s = String::new();
writeln!(s, "// Generated by cimgen — do not edit by hand.").unwrap();
writeln!(s, "#![allow(clippy::all, dead_code)]").unwrap();
writeln!(s).unwrap();
writeln!(s, "use crate::base::{{AttrDef, AttrKind, ClassDef, EnumDef, EnumValueDef, Schema}};").unwrap();
writeln!(s).unwrap();
writeln!(
s,
"/// Every class of the `{}` family. The index of a row is its class id.",
family.id
)
.unwrap();
writeln!(s, "pub static CLASSES: &[ClassDef] = &[").unwrap();
for id in &ids {
let t: &CimType = &spec.types[*id];
let super_class = index_of
.get(t.super_type.as_str())
.map(|i| format!("Some({i})"))
.unwrap_or_else(|| "None".to_string());
writeln!(s, " ClassDef {{").unwrap();
writeln!(s, " ns: \"{}\",", t.namespace).unwrap();
writeln!(s, " local: \"{}\",", t.id).unwrap();
writeln!(
s,
" qualified: \"{}{}\",",
family.type_prefix, t.id
)
.unwrap();
writeln!(s, " super_class: {super_class},").unwrap();
writeln!(s, " concrete: {},", !t.concrete_in.is_empty()).unwrap();
writeln!(s, " origins: {},", str_list(&t.origins)).unwrap();
writeln!(s, " comment: {:?},", t.comment).unwrap();
let attrs: Vec<&CimAttribute> = t.attributes.iter().collect();
if attrs.is_empty() {
writeln!(s, " attrs: &[],").unwrap();
} else {
writeln!(s, " attrs: &[").unwrap();
for a in attrs {
writeln!(
s,
" AttrDef {{ id: \"{id}\", ns: \"{ns}\", kind: {kind}, range: \"{range}\", is_list: {is_list}, used: {used}, xsd: \"{xsd}\", value_ns: \"{value_ns}\", origins: {origins}, comment: {comment:?} }},",
id = a.id,
ns = a.namespace,
kind = attr_kind(a),
range = table::attr_range(a),
is_list = a.is_list,
used = a.is_association_used,
xsd = table::xsd_type(a),
value_ns = table::value_namespace(spec, a),
origins = str_list(&a.origins),
comment = a.comment,
)
.unwrap();
}
writeln!(s, " ],").unwrap();
}
writeln!(s, " }},").unwrap();
}
writeln!(s, "];").unwrap();
writeln!(s).unwrap();
s.push_str(&pairs("PROFILES", "Profile code → profile URI, sorted by code.", &table::profile_uris(spec)));
writeln!(s).unwrap();
s.push_str(&pairs("NAMESPACES", "XML prefix → namespace IRI, sorted by prefix.", &table::namespaces(spec)));
writeln!(s).unwrap();
writeln!(s, "/// Every enumeration of the `{}` family, sorted by name.", family.id).unwrap();
writeln!(s, "pub static ENUMS: &[EnumDef] = &[").unwrap();
for e in table::enums(spec) {
writeln!(s, " EnumDef {{").unwrap();
writeln!(s, " ns: \"{}\",", e.namespace).unwrap();
writeln!(s, " local: \"{}\",", e.id).unwrap();
writeln!(s, " comment: {:?},", e.comment).unwrap();
writeln!(s, " values: &[").unwrap();
for v in &e.values {
writeln!(s, " EnumValueDef {{ id: \"{}\", comment: {:?} }},", v.id, v.comment).unwrap();
}
writeln!(s, " ],").unwrap();
writeln!(s, " }},").unwrap();
}
writeln!(s, "];").unwrap();
writeln!(s).unwrap();
writeln!(s, "/// The `{}` family's schema.", family.id).unwrap();
writeln!(s, "pub static SCHEMA: Schema = Schema {{ classes: CLASSES, profiles: PROFILES, namespaces: NAMESPACES, enums: ENUMS }};").unwrap();
s
}