use std::collections::BTreeMap;
use super::same::same_record;
use crate::codec::schema::{Item, Kind, Order, Shape, field};
use crate::codec::{Content, Rec, Val};
use crate::identity::Key;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub(super) enum Parent {
Top,
Key(Key),
}
pub(super) type Place = (Parent, &'static str);
#[derive(Debug, Clone)]
pub(super) struct Node {
pub(super) own: Rec,
pub(super) place: Place,
}
impl Node {
pub(super) fn changed_from(&self, base: &Self) -> bool {
!same_record(&base.own, &self.own) || base.place != self.place
}
}
#[derive(Debug, Clone, Default)]
pub(super) struct Flat {
pub(super) top: Option<Rec>,
pub(super) nodes: BTreeMap<Key, Node>,
pub(super) lists: BTreeMap<Place, Vec<Key>>,
}
impl Flat {
pub(super) fn of(content: &Content) -> Self {
let mut flat = Self::default();
match content {
Content::Record(record) => {
let top = flat.own(record, Parent::Top);
flat.top = Some(top);
}
Content::Registry(entries) => {
for entry in entries {
flat.node(entry, (Parent::Top, field::ENTRIES));
}
}
Content::Lint(_) => {}
}
flat
}
pub(super) fn place(&self, key: &Key) -> Option<Place> {
self.nodes.get(key).map(|node| node.place)
}
pub(super) fn parent_value(&self, parent: Parent) -> Option<Val> {
match parent {
Parent::Key(key) => Some(Val::scalar(&key)),
Parent::Top => self
.top
.as_ref()
.filter(|top| top.kind.is_keyed())
.and_then(|top| top.get(field::KEY).cloned()),
}
}
fn node(&mut self, record: &Rec, place: Place) {
let key = record
.key()
.expect("a keyed record carries a key the reader checked");
self.lists.entry(place).or_default().push(key);
let own = self.own(record, Parent::Key(key));
self.nodes.insert(key, Node { own, place });
}
fn own(&mut self, record: &Rec, parent: Parent) -> Rec {
let mut own = Rec {
kind: record.kind,
position: record.position,
fields: Vec::with_capacity(record.fields.len()),
conflicts: record.conflicts.clone(),
};
for (name, value) in &record.fields {
if keyed_list(record.kind, name).is_none() {
own.fields.push((name, value.clone()));
continue;
}
for child in value.records() {
self.node(child, (parent, name));
}
}
own
}
}
pub(super) fn keyed_list(kind: Kind, name: &str) -> Option<Kind> {
match kind.field(name)?.1.shape {
Shape::List(Item::Record(item)) if item.is_keyed() => Some(item),
_ => None,
}
}
pub(super) fn order_of(parent: Option<Kind>, name: &str) -> Order {
parent
.and_then(|kind| kind.field(name))
.map_or(Order::ByKey, |(_, field)| field.order)
}
pub(super) fn accepts(parent: Option<Kind>, name: &str, kind: Kind) -> bool {
match parent {
Some(parent) => keyed_list(parent, name) == Some(kind),
None => name == field::ENTRIES,
}
}
pub(super) fn rebuild(
top: Option<Rec>,
nodes: BTreeMap<Key, Rec>,
lists: &BTreeMap<Place, Vec<Key>>,
) -> Content {
let mut build = Build { nodes, lists };
match top {
Some(top) => Content::Record(build.fill(top, Parent::Top)),
None => Content::Registry(build.records(&(Parent::Top, field::ENTRIES))),
}
}
struct Build<'a> {
nodes: BTreeMap<Key, Rec>,
lists: &'a BTreeMap<Place, Vec<Key>>,
}
impl Build<'_> {
fn fill(&mut self, mut record: Rec, parent: Parent) -> Rec {
for each in record.kind.fields() {
if keyed_list(record.kind, each.name).is_none() {
continue;
}
let records = self.records(&(parent, each.name));
if !records.is_empty() {
record.set(
each.name,
Val::List(records.into_iter().map(Val::Record).collect()),
);
}
}
record
}
fn records(&mut self, place: &Place) -> Vec<Rec> {
let lists = self.lists;
let keys = lists.get(place).map_or(&[][..], Vec::as_slice);
keys.iter()
.map(|key| {
let record = self.nodes.remove(key).expect("a merged record in one list");
self.fill(record, Parent::Key(*key))
})
.collect()
}
}