|
|
|
@ -35,73 +35,51 @@ use {
|
|
|
|
|
std::collections::HashMap,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
dict kinds: one is from a generic parse while the other one is the stored metadata
|
|
|
|
|
- MetaDict -> only non-null values
|
|
|
|
|
- DictGeneric -> null allowed
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/// A metadata dictionary
|
|
|
|
|
pub type MetaDict = HashMap<Box<str>, MetaDictEntry>;
|
|
|
|
|
/// A generic dictionary built from scratch from syntactical elements
|
|
|
|
|
pub type DictGeneric = HashMap<Box<str>, DictEntryGeneric>;
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
|
|
|
#[cfg_attr(test, derive(Clone))]
|
|
|
|
|
/// A generic dict entry: either a literal or a recursive dictionary
|
|
|
|
|
pub enum DictEntryGeneric {
|
|
|
|
|
/// A literal
|
|
|
|
|
Lit(Datacell),
|
|
|
|
|
Data(Datacell),
|
|
|
|
|
/// A map
|
|
|
|
|
Map(DictGeneric),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
|
|
|
#[cfg_attr(test, derive(Clone))]
|
|
|
|
|
/// A metadata dictionary
|
|
|
|
|
pub enum MetaDictEntry {
|
|
|
|
|
Data(Datacell),
|
|
|
|
|
Map(MetaDict),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
patchsets
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Default)]
|
|
|
|
|
struct MetaDictPatch(HashMap<Box<str>, Option<MetaDictPatchEntry>>);
|
|
|
|
|
struct DictGenericPatch(HashMap<Box<str>, Option<DictGenericPatchEntry>>);
|
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
|
|
|
enum MetaDictPatchEntry {
|
|
|
|
|
enum DictGenericPatchEntry {
|
|
|
|
|
Data(Datacell),
|
|
|
|
|
Map(MetaDictPatch),
|
|
|
|
|
Map(DictGenericPatch),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Recursively flatten a [`DictGeneric`] into a [`MetaDict`]
|
|
|
|
|
pub fn rflatten_metadata(new: DictGeneric) -> MetaDict {
|
|
|
|
|
let mut empty = MetaDict::new();
|
|
|
|
|
_rflatten_metadata(new, &mut empty);
|
|
|
|
|
empty
|
|
|
|
|
/// Accepts a dict with possible null values, and removes those null values
|
|
|
|
|
pub fn rflatten_metadata(mut new: DictGeneric) -> DictGeneric {
|
|
|
|
|
_rflatten_metadata(&mut new);
|
|
|
|
|
new
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn _rflatten_metadata(new: DictGeneric, empty: &mut MetaDict) {
|
|
|
|
|
for (key, val) in new {
|
|
|
|
|
match val {
|
|
|
|
|
DictEntryGeneric::Lit(l) if l.is_init() => {
|
|
|
|
|
empty.insert(key, MetaDictEntry::Data(l));
|
|
|
|
|
}
|
|
|
|
|
DictEntryGeneric::Lit(_) => {}
|
|
|
|
|
DictEntryGeneric::Map(m) => {
|
|
|
|
|
let mut rnew = MetaDict::new();
|
|
|
|
|
_rflatten_metadata(m, &mut rnew);
|
|
|
|
|
empty.insert(key, MetaDictEntry::Map(rnew));
|
|
|
|
|
}
|
|
|
|
|
fn _rflatten_metadata(new: &mut DictGeneric) {
|
|
|
|
|
new.retain(|_, v| match v {
|
|
|
|
|
DictEntryGeneric::Data(d) => d.is_init(),
|
|
|
|
|
DictEntryGeneric::Map(m) => {
|
|
|
|
|
_rflatten_metadata(m);
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Recursively merge a [`DictGeneric`] into a [`MetaDict`] with the use of an intermediary
|
|
|
|
|
/// Recursively merge a [`DictGeneric`] into a [`DictGeneric`] with the use of an intermediary
|
|
|
|
|
/// patchset to avoid inconsistent states
|
|
|
|
|
pub fn rmerge_metadata(current: &mut MetaDict, new: DictGeneric) -> bool {
|
|
|
|
|
let mut patch = MetaDictPatch::default();
|
|
|
|
|
pub fn rmerge_metadata(current: &mut DictGeneric, new: DictGeneric) -> bool {
|
|
|
|
|
let mut patch = DictGenericPatch::default();
|
|
|
|
|
let current_ref = current as &_;
|
|
|
|
|
let r = rmerge_metadata_prepare_patch(current_ref, new, &mut patch);
|
|
|
|
|
if r {
|
|
|
|
@ -110,15 +88,15 @@ pub fn rmerge_metadata(current: &mut MetaDict, new: DictGeneric) -> bool {
|
|
|
|
|
r
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn merge_data_with_patch(current: &mut MetaDict, patch: MetaDictPatch) {
|
|
|
|
|
fn merge_data_with_patch(current: &mut DictGeneric, patch: DictGenericPatch) {
|
|
|
|
|
for (key, patch) in patch.0 {
|
|
|
|
|
match patch {
|
|
|
|
|
Some(MetaDictPatchEntry::Data(d)) => {
|
|
|
|
|
current.st_upsert(key, MetaDictEntry::Data(d));
|
|
|
|
|
Some(DictGenericPatchEntry::Data(d)) => {
|
|
|
|
|
current.st_upsert(key, DictEntryGeneric::Data(d));
|
|
|
|
|
}
|
|
|
|
|
Some(MetaDictPatchEntry::Map(m)) => match current.get_mut(&key) {
|
|
|
|
|
Some(DictGenericPatchEntry::Map(m)) => match current.get_mut(&key) {
|
|
|
|
|
Some(current_recursive) => match current_recursive {
|
|
|
|
|
MetaDictEntry::Map(current_m) => {
|
|
|
|
|
DictEntryGeneric::Map(current_m) => {
|
|
|
|
|
merge_data_with_patch(current_m, m);
|
|
|
|
|
}
|
|
|
|
|
_ => {
|
|
|
|
@ -127,7 +105,7 @@ fn merge_data_with_patch(current: &mut MetaDict, patch: MetaDictPatch) {
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
None => {
|
|
|
|
|
let mut new = MetaDict::new();
|
|
|
|
|
let mut new = DictGeneric::new();
|
|
|
|
|
merge_data_with_patch(&mut new, m);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
@ -139,9 +117,9 @@ fn merge_data_with_patch(current: &mut MetaDict, patch: MetaDictPatch) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn rmerge_metadata_prepare_patch(
|
|
|
|
|
current: &MetaDict,
|
|
|
|
|
current: &DictGeneric,
|
|
|
|
|
new: DictGeneric,
|
|
|
|
|
patch: &mut MetaDictPatch,
|
|
|
|
|
patch: &mut DictGenericPatch,
|
|
|
|
|
) -> bool {
|
|
|
|
|
let mut new = new.into_iter();
|
|
|
|
|
let mut okay = true;
|
|
|
|
@ -149,25 +127,25 @@ fn rmerge_metadata_prepare_patch(
|
|
|
|
|
let (key, new_entry) = new.next().unwrap();
|
|
|
|
|
match (current.get(&key), new_entry) {
|
|
|
|
|
// non-null -> non-null: merge flatten update
|
|
|
|
|
(Some(MetaDictEntry::Data(this_data)), DictEntryGeneric::Lit(new_data))
|
|
|
|
|
(Some(DictEntryGeneric::Data(this_data)), DictEntryGeneric::Data(new_data))
|
|
|
|
|
if new_data.is_init() =>
|
|
|
|
|
{
|
|
|
|
|
if this_data.kind() == new_data.kind() {
|
|
|
|
|
patch
|
|
|
|
|
.0
|
|
|
|
|
.insert(key, Some(MetaDictPatchEntry::Data(new_data)));
|
|
|
|
|
.insert(key, Some(DictGenericPatchEntry::Data(new_data)));
|
|
|
|
|
} else {
|
|
|
|
|
okay = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
(Some(MetaDictEntry::Data(_)), DictEntryGeneric::Map(_)) => {
|
|
|
|
|
(Some(DictEntryGeneric::Data(_)), DictEntryGeneric::Map(_)) => {
|
|
|
|
|
okay = false;
|
|
|
|
|
}
|
|
|
|
|
(
|
|
|
|
|
Some(MetaDictEntry::Map(this_recursive_data)),
|
|
|
|
|
Some(DictEntryGeneric::Map(this_recursive_data)),
|
|
|
|
|
DictEntryGeneric::Map(new_recursive_data),
|
|
|
|
|
) => {
|
|
|
|
|
let mut this_patch = MetaDictPatch::default();
|
|
|
|
|
let mut this_patch = DictGenericPatch::default();
|
|
|
|
|
let _okay = rmerge_metadata_prepare_patch(
|
|
|
|
|
this_recursive_data,
|
|
|
|
|
new_recursive_data,
|
|
|
|
@ -175,26 +153,26 @@ fn rmerge_metadata_prepare_patch(
|
|
|
|
|
);
|
|
|
|
|
patch
|
|
|
|
|
.0
|
|
|
|
|
.insert(key, Some(MetaDictPatchEntry::Map(this_patch)));
|
|
|
|
|
.insert(key, Some(DictGenericPatchEntry::Map(this_patch)));
|
|
|
|
|
okay &= _okay;
|
|
|
|
|
}
|
|
|
|
|
// null -> non-null: flatten insert
|
|
|
|
|
(None, DictEntryGeneric::Lit(l)) if l.is_init() => {
|
|
|
|
|
let _ = patch.0.insert(key, Some(MetaDictPatchEntry::Data(l)));
|
|
|
|
|
(None, DictEntryGeneric::Data(l)) if l.is_init() => {
|
|
|
|
|
let _ = patch.0.insert(key, Some(DictGenericPatchEntry::Data(l)));
|
|
|
|
|
}
|
|
|
|
|
(None, DictEntryGeneric::Map(m)) => {
|
|
|
|
|
let mut this_patch = MetaDictPatch::default();
|
|
|
|
|
let mut this_patch = DictGenericPatch::default();
|
|
|
|
|
okay &= rmerge_metadata_prepare_patch(&into_dict!(), m, &mut this_patch);
|
|
|
|
|
let _ = patch
|
|
|
|
|
.0
|
|
|
|
|
.insert(key, Some(MetaDictPatchEntry::Map(this_patch)));
|
|
|
|
|
.insert(key, Some(DictGenericPatchEntry::Map(this_patch)));
|
|
|
|
|
}
|
|
|
|
|
// non-null -> null: remove
|
|
|
|
|
(Some(_), DictEntryGeneric::Lit(l)) => {
|
|
|
|
|
(Some(_), DictEntryGeneric::Data(l)) => {
|
|
|
|
|
debug_assert!(l.is_null());
|
|
|
|
|
patch.0.insert(key, None);
|
|
|
|
|
}
|
|
|
|
|
(None, DictEntryGeneric::Lit(l)) => {
|
|
|
|
|
(None, DictEntryGeneric::Data(l)) => {
|
|
|
|
|
debug_assert!(l.is_null());
|
|
|
|
|
// ignore
|
|
|
|
|
}
|
|
|
|
@ -209,26 +187,19 @@ fn rmerge_metadata_prepare_patch(
|
|
|
|
|
|
|
|
|
|
impl<'a> From<LitIR<'a>> for DictEntryGeneric {
|
|
|
|
|
fn from(l: LitIR<'a>) -> Self {
|
|
|
|
|
Self::Lit(Datacell::from(l))
|
|
|
|
|
Self::Data(Datacell::from(l))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> From<Lit<'a>> for DictEntryGeneric {
|
|
|
|
|
fn from(value: Lit<'a>) -> Self {
|
|
|
|
|
Self::Lit(Datacell::from(value))
|
|
|
|
|
Self::Data(Datacell::from(value))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
direct_from! {
|
|
|
|
|
DictEntryGeneric => {
|
|
|
|
|
Datacell as Lit,
|
|
|
|
|
DictGeneric as Map,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
direct_from! {
|
|
|
|
|
MetaDictEntry => {
|
|
|
|
|
Datacell as Data,
|
|
|
|
|
MetaDict as Map,
|
|
|
|
|
DictGeneric as Map,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|