use super::files::{Content, Tree};
use super::record::{Rec, Val};
use super::schema::Order;
use super::schema::field;
pub(crate) fn canonicalize(tree: &mut Tree) {
for (_, content) in tree.files.values_mut() {
content.records_mut().for_each(sort);
if let Content::Registry(entries) = content {
entries.sort_by(|left, right| entry_order(left).cmp(&entry_order(right)));
}
}
}
fn entry_order(entry: &Rec) -> (&str, &str) {
(
entry.text(field::NAME).unwrap_or_default(),
entry.text(field::KEY).unwrap_or_default(),
)
}
fn sort_text(value: &Val) -> &str {
match value {
Val::Scalar { text, .. } => text,
Val::Record(record) => record
.fields
.first()
.and_then(|(_, first)| first.text())
.unwrap_or_default(),
Val::List(_) | Val::Null { .. } => "",
}
}
fn subject_order(subject: &Val) -> (&str, &str) {
match subject {
Val::Record(record) => (
record.text(field::ROLE).unwrap_or_default(),
record.get(field::NODE).map_or("", sort_text),
),
_ => ("", ""),
}
}
fn sort(record: &mut Rec) {
let kind = record.kind;
for (name, value) in &mut record.fields {
value.records_mut().for_each(sort);
let Val::List(items) = value else {
continue;
};
let (_, field) = kind.field(name).expect("a field the schema defines");
match field.order {
Order::Authored => {}
Order::ByKey => items.sort_by(|left, right| sort_text(left).cmp(sort_text(right))),
Order::ByRoleThenTarget => {
items.sort_by(|left, right| subject_order(left).cmp(&subject_order(right)));
}
}
}
}