use std::sync::Arc;
use std::collections::BTreeMap;
use crate::canonical::ir::{BoundCardinality, Bounds, LengthBounds, ObjectLeaf, Schema};
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct ObjectLeaves {
leaves: Vec<ObjectLeaf>,
canonical: bool,
}
impl Default for ObjectLeaves {
fn default() -> Self {
Self {
leaves: Vec::new(),
canonical: true,
}
}
}
impl ObjectLeaves {
pub(crate) fn insert(&mut self, leaf: ObjectLeaf) {
self.leaves.push(leaf);
self.canonical = false;
}
fn canonicalize(&mut self) {
if self.canonical {
return;
}
let was_empty = self.leaves.is_empty();
self.leaves = merge(std::mem::take(&mut self.leaves));
extend_over_bare_windows(&mut self.leaves);
hand_off_empty(&mut self.leaves);
self.leaves = merge(std::mem::take(&mut self.leaves));
absorb_trivially_admitted(&mut self.leaves);
drop_subsumed(&mut self.leaves);
self.canonical = true;
debug_assert_eq!(
self.leaves.is_empty(),
was_empty,
"merging emptied the leaves"
);
}
pub(crate) fn clear(&mut self) {
self.leaves.clear();
self.canonical = true;
}
pub(crate) fn is_empty(&self) -> bool {
self.leaves.is_empty()
}
}
impl IntoIterator for ObjectLeaves {
type Item = ObjectLeaf;
type IntoIter = std::vec::IntoIter<ObjectLeaf>;
fn into_iter(mut self) -> Self::IntoIter {
self.canonicalize();
self.leaves.into_iter()
}
}
struct Facets {
required: Vec<Arc<str>>,
property_names: Option<Schema>,
properties: BTreeMap<Arc<str>, Schema>,
pattern_properties: BTreeMap<Arc<str>, Schema>,
additional: Option<Schema>,
}
fn merge(mut leaves: Vec<ObjectLeaf>) -> Vec<ObjectLeaf> {
if leaves.len() < 2 {
return leaves;
}
leaves.sort_by(|left, right| {
(
&left.required,
&left.property_names,
&left.properties,
&left.pattern_properties,
&left.additional,
)
.cmp(&(
&right.required,
&right.property_names,
&right.properties,
&right.pattern_properties,
&right.additional,
))
});
let mut merged: Vec<ObjectLeaf> = Vec::with_capacity(leaves.len());
let mut windows: Vec<LengthBounds> = Vec::new();
let mut facets: Option<Facets> = None;
for leaf in leaves {
if facets.as_ref().is_none_or(|group| {
group.required != leaf.required
|| group.property_names != leaf.property_names
|| group.properties != leaf.properties
|| group.pattern_properties != leaf.pattern_properties
|| group.additional != leaf.additional
}) {
flush_group(&mut merged, facets.take(), &mut windows);
facets = Some(Facets {
required: leaf.required,
property_names: leaf.property_names,
properties: leaf.properties,
pattern_properties: leaf.pattern_properties,
additional: leaf.additional,
});
}
windows.push(leaf.sizes);
}
flush_group(&mut merged, facets, &mut windows);
merged
}
fn flush_group(
merged: &mut Vec<ObjectLeaf>,
facets: Option<Facets>,
windows: &mut Vec<LengthBounds>,
) {
let Some(Facets {
required,
property_names,
properties,
pattern_properties,
additional,
}) = facets
else {
return;
};
let mut sizes = Bounds::merge_all(std::mem::take(windows));
let last = sizes.pop().expect("a group holds at least one window");
for window in sizes {
merged.push(ObjectLeaf {
sizes: window,
required: required.clone(),
property_names: property_names.clone(),
properties: properties.clone(),
pattern_properties: pattern_properties.clone(),
additional: additional.clone(),
});
}
merged.push(ObjectLeaf {
sizes: last,
required,
property_names,
properties,
pattern_properties,
additional,
});
}
fn extend_over_bare_windows(leaves: &mut [ObjectLeaf]) {
let bare: Vec<LengthBounds> = leaves
.iter()
.filter(|leaf| {
leaf.required.is_empty()
&& leaf.property_names.is_none()
&& leaf.properties.is_empty()
&& leaf.pattern_properties.is_empty()
&& leaf.additional.is_none()
})
.map(|leaf| leaf.sizes.clone())
.collect();
if bare.is_empty() {
return;
}
for leaf in leaves.iter_mut() {
if leaf.required.is_empty()
&& leaf.property_names.is_none()
&& leaf.properties.is_empty()
&& leaf.pattern_properties.is_empty()
&& leaf.additional.is_none()
{
continue;
}
loop {
let mut grown = false;
for window in &bare {
let merged = Bounds::merge_all(vec![leaf.sizes.clone(), window.clone()]);
if let Ok([merged]) = <[_; 1]>::try_from(merged) {
if merged != leaf.sizes {
leaf.sizes = merged;
grown = true;
}
}
}
if !grown {
break;
}
}
}
}
fn hand_off_empty(leaves: &mut [ObjectLeaf]) {
if !leaves.iter().any(|leaf| {
leaf.required.is_empty()
&& leaf
.sizes
.minimum
.as_ref()
.is_none_or(BoundCardinality::is_zero)
}) {
return;
}
let one = BoundCardinality::from(1);
for leaf in leaves.iter_mut() {
if leaf.sizes.minimum.as_ref() == Some(&one) {
leaf.sizes.minimum = None;
}
}
}
fn absorb_trivially_admitted(leaves: &mut Vec<ObjectLeaf>) {
let Some(trivial) = leaves.iter().position(|leaf| {
leaf.sizes
.maximum
.as_ref()
.is_some_and(BoundCardinality::is_zero)
}) else {
return;
};
debug_assert!(
leaves[trivial].property_names.is_none()
&& leaves[trivial].properties.is_empty()
&& leaves[trivial].pattern_properties.is_empty()
&& leaves[trivial].additional.is_none()
&& leaves[trivial].required.is_empty(),
"a leaf admitting only the empty object carries a facet"
);
let window = leaves[trivial].sizes.clone();
let Some((target, widened)) = leaves.iter().enumerate().find_map(|(index, leaf)| {
if (leaf.property_names.is_none()
&& leaf.properties.is_empty()
&& leaf.pattern_properties.is_empty()
&& leaf.additional.is_none())
|| !leaf.required.is_empty()
{
return None;
}
let mut merged = Bounds::merge_all(vec![leaf.sizes.clone(), window.clone()]);
(merged.len() == 1).then(|| (index, merged.pop().expect("a merged window")))
}) else {
return;
};
leaves[target].sizes = widened;
leaves.remove(trivial);
}
fn drop_subsumed(leaves: &mut Vec<ObjectLeaf>) {
if leaves.len() < 2 {
return;
}
let mut keep = vec![true; leaves.len()];
for (index, leaf) in leaves.iter().enumerate() {
for (other_index, other) in leaves.iter().enumerate() {
if index == other_index || !keep[other_index] || !keep[index] {
continue;
}
let looser_keys = other.property_names.is_none() && leaf.property_names.is_some();
let looser_properties = leaf.additional.is_none()
&& other.properties.len() < leaf.properties.len()
&& other
.properties
.iter()
.all(|(key, schema)| leaf.properties.get(key) == Some(schema));
let looser_patterns = other.pattern_properties.len() < leaf.pattern_properties.len()
&& other
.pattern_properties
.iter()
.all(|(pattern, schema)| leaf.pattern_properties.get(pattern) == Some(schema));
let wider = other.effective_sizes().covers(&leaf.effective_sizes())
&& other.additional == leaf.additional
&& other.required.iter().all(|key| leaf.required.contains(key))
&& (looser_keys || other.property_names == leaf.property_names)
&& (looser_properties || other.properties == leaf.properties)
&& (looser_patterns || other.pattern_properties == leaf.pattern_properties);
debug_assert!(
!wider
|| other.required.len() != leaf.required.len()
|| other.property_names != leaf.property_names
|| other.properties != leaf.properties
|| other.pattern_properties != leaf.pattern_properties,
"merging left two leaves carrying the same facets"
);
if wider
&& (other.required.len() < leaf.required.len()
|| looser_keys
|| looser_properties
|| looser_patterns)
{
keep[index] = false;
}
}
}
let mut index = 0;
leaves.retain(|_| {
let keeps = keep[index];
index += 1;
keeps
});
}