Add patch based metadata merge algorithm

next
Sayan Nandan 2 years ago
parent 997205a327
commit f669cc7b00
No known key found for this signature in database
GPG Key ID: 42EEDF4AE9D96B54

3
.gitignore vendored

@ -1,7 +1,8 @@
/target
/.vscode
*.bin
data
/data/
/server/data
/server/snapshots
snapstore.bin
snapstore.partmap

@ -24,7 +24,13 @@
*
*/
use crate::engine::ql::lex::{Lit, LitIR};
pub mod md_dict;
pub use md_dict::{DictEntryGeneric, DictGeneric, MetaDict};
use {
crate::engine::ql::lex::{Lit, LitIR},
std::mem::{self, Discriminant},
};
/// A [`DataType`] represents the underlying data-type, although this enumeration when used in a collection will always
/// be of one type.
@ -91,6 +97,9 @@ impl HSData {
LitIR::Bool(b) => Self::Boolean(b),
}
}
fn kind(&self) -> Discriminant<Self> {
mem::discriminant(&self)
}
}
impl<'a> From<Lit<'a>> for HSData {

@ -0,0 +1,230 @@
/*
* Created on Thu Feb 09 2023
*
* This file is a part of Skytable
* Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source
* NoSQL database written by Sayan Nandan ("the Author") with the
* vision to provide flexibility in data modelling without compromising
* on performance, queryability or scalability.
*
* Copyright (c) 2023, Sayan Nandan <ohsayan@outlook.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
use {
crate::engine::{
core::data::HSData,
idx::STIndex,
ql::lex::{Lit, LitIR},
},
std::collections::HashMap,
};
/*
dict kinds: one is from a generic parse while the other one is the stored metadata
*/
/// 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>, Option<DictEntryGeneric>>;
#[derive(Debug, PartialEq)]
/// A generic dict entry: either a literal or a recursive dictionary
pub enum DictEntryGeneric {
Lit(HSData),
Map(DictGeneric),
}
#[derive(Debug, PartialEq)]
#[cfg_attr(test, derive(Clone))]
/// A metadata dictionary
pub enum MetaDictEntry {
Data(HSData),
Map(MetaDict),
}
/*
patchsets
*/
#[derive(Debug, PartialEq, Default)]
struct MetaDictPatch(HashMap<Box<str>, Option<MetaDictPatchEntry>>);
#[derive(Debug, PartialEq)]
enum MetaDictPatchEntry {
Data(HSData),
Map(MetaDictPatch),
}
/// 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
}
fn _rflatten_metadata(new: DictGeneric, empty: &mut MetaDict) {
for (key, val) in new {
match val {
Some(v) => match v {
DictEntryGeneric::Lit(l) => {
empty.insert(key, MetaDictEntry::Data(l));
}
DictEntryGeneric::Map(m) => {
let mut rnew = MetaDict::new();
_rflatten_metadata(m, &mut rnew);
empty.insert(key, MetaDictEntry::Map(rnew));
}
},
_ => {}
}
}
}
/// Recursively merge a [`DictGeneric`] into a [`MetaDict`] 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();
let current_ref = current as &_;
let r = rmerge_metadata_prepare_patch(current_ref, new, &mut patch);
if r {
merge_data_with_patch(current, patch);
}
r
}
fn merge_data_with_patch(current: &mut MetaDict, patch: MetaDictPatch) {
for (key, patch) in patch.0 {
match patch {
Some(MetaDictPatchEntry::Data(d)) => {
current.st_upsert(key, MetaDictEntry::Data(d));
}
Some(MetaDictPatchEntry::Map(m)) => match current.get_mut(&key) {
Some(current_recursive) => match current_recursive {
MetaDictEntry::Map(current_m) => {
merge_data_with_patch(current_m, m);
}
_ => {
// can never reach here since the patch is always correct
unreachable!()
}
},
None => {
let mut new = MetaDict::new();
merge_data_with_patch(&mut new, m);
}
},
None => {
let _ = current.remove(&key);
}
}
}
}
fn rmerge_metadata_prepare_patch(
current: &MetaDict,
new: DictGeneric,
patch: &mut MetaDictPatch,
) -> bool {
let mut new = new.into_iter();
let mut okay = true;
while new.len() != 0 && okay {
let (key, new_entry) = new.next().unwrap();
match (current.get(&key), new_entry) {
// non-null -> non-null: merge flatten update
(Some(this_current), Some(new_entry)) => {
okay &= {
match (this_current, new_entry) {
(MetaDictEntry::Data(this_data), DictEntryGeneric::Lit(new_data))
if this_data.kind() == new_data.kind() =>
{
patch
.0
.insert(key, Some(MetaDictPatchEntry::Data(new_data)));
true
}
(
MetaDictEntry::Map(this_recursive_data),
DictEntryGeneric::Map(new_recursive_data),
) => {
let mut this_patch = MetaDictPatch::default();
let okay = rmerge_metadata_prepare_patch(
this_recursive_data,
new_recursive_data,
&mut this_patch,
);
patch
.0
.insert(key, Some(MetaDictPatchEntry::Map(this_patch)));
okay
}
_ => false,
}
};
}
// null -> non-null: flatten insert
(None, Some(new_entry)) => match new_entry {
DictEntryGeneric::Lit(d) => {
let _ = patch.0.insert(key, Some(MetaDictPatchEntry::Data(d)));
}
DictEntryGeneric::Map(m) => {
let mut this_patch = MetaDictPatch::default();
okay &= rmerge_metadata_prepare_patch(&into_dict!(), m, &mut this_patch);
let _ = patch
.0
.insert(key, Some(MetaDictPatchEntry::Map(this_patch)));
}
},
// non-null -> null: remove
(Some(_), None) => {
patch.0.insert(key, None);
}
(None, None) => {
// ignore
}
}
}
okay
}
/*
impls
*/
impl<'a> From<LitIR<'a>> for DictEntryGeneric {
fn from(l: LitIR<'a>) -> Self {
Self::Lit(HSData::from(l))
}
}
impl<'a> From<Lit<'a>> for DictEntryGeneric {
fn from(value: Lit<'a>) -> Self {
Self::Lit(HSData::from(value))
}
}
enum_impls! {
DictEntryGeneric => {
HSData as Lit,
DictGeneric as Map,
}
}
enum_impls! {
MetaDictEntry => {
HSData as Data,
MetaDict as Map,
}
}

@ -24,7 +24,7 @@
*
*/
mod data;
pub(in crate::engine) mod data;
mod model;
mod space;
#[cfg(test)]

@ -27,10 +27,10 @@
// FIXME(@ohsayan): update this!
#[derive(Debug)]
pub struct ModelNS {}
pub struct ModelView {}
#[cfg(test)]
impl PartialEq for ModelNS {
impl PartialEq for ModelView {
fn eq(&self, _: &Self) -> bool {
true
}

@ -26,28 +26,33 @@
use {
crate::engine::{
core::{model::ModelNS, ItemID, RWLIdx},
core::{
data::{md_dict, DictEntryGeneric, MetaDict},
model::ModelView,
ItemID, RWLIdx,
},
error::{DatabaseError, DatabaseResult},
idx::{IndexST, STIndex},
ql::ddl::{crt::CreateSpace, syn::DictEntry},
ql::ddl::crt::CreateSpace,
},
parking_lot::RwLock,
std::sync::Arc,
};
#[derive(Debug)]
pub struct Space {
mns: RWLIdx<ItemID, Arc<ModelNS>>,
mns: RWLIdx<ItemID, Arc<ModelView>>,
meta: SpaceMeta,
}
#[derive(Debug, Default)]
pub struct SpaceMeta {
env: RWLIdx<Box<str>, DictEntry>,
env: RwLock<MetaDict>,
}
impl SpaceMeta {
pub const KEY_ENV: &str = "env";
pub fn with_env(env: IndexST<Box<str>, DictEntry>) -> Self {
pub fn with_env(env: MetaDict) -> Self {
Self {
env: RWLIdx::new(env),
}
@ -56,7 +61,7 @@ impl SpaceMeta {
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))]
pub(super) struct Procedure {
struct Procedure {
space_name: ItemID,
space: Space,
}
@ -70,14 +75,14 @@ impl Procedure {
impl Space {
#[inline(always)]
pub fn new(mns: IndexST<ItemID, Arc<ModelNS>>, meta: SpaceMeta) -> Self {
pub fn new(mns: IndexST<ItemID, Arc<ModelView>>, meta: SpaceMeta) -> Self {
Self {
mns: RWLIdx::new(mns),
meta,
}
}
#[inline]
pub(super) fn validate(
fn validate(
CreateSpace {
space_name,
mut props,
@ -87,7 +92,7 @@ impl Space {
// check env
let env;
match props.remove(SpaceMeta::KEY_ENV) {
Some(Some(DictEntry::Map(m))) if props.is_empty() => env = m,
Some(Some(DictEntryGeneric::Map(m))) if props.is_empty() => env = m,
None | Some(None) if props.is_empty() => env = IndexST::default(),
_ => {
return Err(DatabaseError::DdlCreateSpaceBadProperty);
@ -99,9 +104,7 @@ impl Space {
IndexST::default(),
SpaceMeta::with_env(
// FIXME(@ohsayan): see this is bad. attempt to do it at AST build time
env.into_iter()
.filter_map(|(k, v)| v.map(move |v| (k, v)))
.collect(),
md_dict::rflatten_metadata(env),
),
),
})

@ -24,7 +24,8 @@
*
*/
mod create_space;
mod create;
mod data_tests;
use {super::ItemID, crate::engine::ql::lex::Ident};

@ -0,0 +1,27 @@
/*
* Created on Thu Feb 09 2023
*
* This file is a part of Skytable
* Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source
* NoSQL database written by Sayan Nandan ("the Author") with the
* vision to provide flexibility in data modelling without compromising
* on performance, queryability or scalability.
*
* Copyright (c) 2023, Sayan Nandan <ohsayan@outlook.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
mod space;

@ -0,0 +1,80 @@
/*
* Created on Thu Feb 09 2023
*
* This file is a part of Skytable
* Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source
* NoSQL database written by Sayan Nandan ("the Author") with the
* vision to provide flexibility in data modelling without compromising
* on performance, queryability or scalability.
*
* Copyright (c) 2023, Sayan Nandan <ohsayan@outlook.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
use crate::engine::core::data::{
md_dict::{self, DictEntryGeneric, DictGeneric, MetaDict},
HSData,
};
#[test]
fn t_simple_flatten() {
let generic_dict: DictGeneric = into_dict! {
"a_valid_key" => Some(DictEntryGeneric::Lit(100.into())),
"a_null_key" => None,
};
let expected: MetaDict = into_dict!(
"a_valid_key" => HSData::UnsignedInt(100)
);
let ret = md_dict::rflatten_metadata(generic_dict);
assert_eq!(ret, expected);
}
#[test]
fn t_simple_patch() {
let mut current: MetaDict = into_dict! {
"a" => HSData::UnsignedInt(2),
"b" => HSData::UnsignedInt(3),
"z" => HSData::SignedInt(-100),
};
let new: DictGeneric = into_dict! {
"a" => Some(HSData::UnsignedInt(1).into()),
"b" => Some(HSData::UnsignedInt(2).into()),
"z" => None,
};
let expected: MetaDict = into_dict! {
"a" => HSData::UnsignedInt(1),
"b" => HSData::UnsignedInt(2),
};
assert!(md_dict::rmerge_metadata(&mut current, new));
assert_eq!(current, expected);
}
#[test]
fn t_bad_patch() {
let mut current: MetaDict = into_dict! {
"a" => HSData::UnsignedInt(2),
"b" => HSData::UnsignedInt(3),
"z" => HSData::SignedInt(-100),
};
let backup = current.clone();
let new: DictGeneric = into_dict! {
"a" => Some(HSData::UnsignedInt(1).into()),
"b" => Some(HSData::UnsignedInt(2).into()),
"z" => Some(HSData::String("omg".into()).into()),
};
assert!(!md_dict::rmerge_metadata(&mut current, new));
assert_eq!(current, backup);
}

@ -132,10 +132,9 @@ macro_rules! void {
};
}
#[cfg(test)]
/// Convert all the KV pairs into an iterator and then turn it into an appropriate collection
/// (inferred).
///
///
/// **Warning: This is going to be potentially slow due to the iterator creation**
macro_rules! into_dict {
() => { ::core::default::Default::default() };

@ -25,9 +25,10 @@
*/
use {
super::syn::{self, Dict, DictFoldState, ExpandedField},
super::syn::{self, DictFoldState, ExpandedField},
crate::{
engine::{
core::data::DictGeneric,
error::{LangError, LangResult},
ql::{
ast::{QueryData, State},
@ -42,11 +43,11 @@ use {
/// An alter space query with corresponding data
pub struct AlterSpace<'a> {
space_name: Ident<'a>,
updated_props: Dict,
updated_props: DictGeneric,
}
impl<'a> AlterSpace<'a> {
pub fn new(space_name: Ident<'a>, updated_props: Dict) -> Self {
pub fn new(space_name: Ident<'a>, updated_props: DictGeneric) -> Self {
Self {
space_name,
updated_props,
@ -69,7 +70,7 @@ impl<'a> AlterSpace<'a> {
}
let space_name = unsafe { extract!(space_name, Token::Ident(ref space) => space.clone()) };
let mut d = Dict::new();
let mut d = DictGeneric::new();
syn::rfold_dict(DictFoldState::CB_OR_IDENT, state, &mut d);
if state.okay() {
Ok(AlterSpace {

@ -25,9 +25,10 @@
*/
use {
super::syn::{self, Dict, DictFoldState, Field},
super::syn::{self, DictFoldState, Field},
crate::{
engine::{
core::data::DictGeneric,
error::{LangError, LangResult},
ql::{
ast::{QueryData, State},
@ -44,7 +45,7 @@ pub struct CreateSpace<'a> {
/// the space name
pub space_name: Ident<'a>,
/// properties
pub props: Dict,
pub props: DictGeneric,
}
impl<'a> CreateSpace<'a> {
@ -61,7 +62,7 @@ impl<'a> CreateSpace<'a> {
let has_more_properties = state.cursor_rounded_eq(Token![with]);
state.poison_if_not(has_more_properties | state.exhausted());
state.cursor_ahead_if(has_more_properties); // +WITH
let mut d = Dict::new();
let mut d = DictGeneric::new();
// properties
if has_more_properties && state.okay() {
syn::rfold_dict(DictFoldState::OB, state, &mut d);
@ -85,7 +86,7 @@ pub struct CreateModel<'a> {
/// the fields
fields: Vec<Field<'a>>,
/// properties
props: Dict,
props: DictGeneric,
}
/*
@ -96,7 +97,7 @@ pub struct CreateModel<'a> {
*/
impl<'a> CreateModel<'a> {
pub fn new(model_name: Ident<'a>, fields: Vec<Field<'a>>, props: Dict) -> Self {
pub fn new(model_name: Ident<'a>, fields: Vec<Field<'a>>, props: DictGeneric) -> Self {
Self {
model_name,
fields,
@ -126,7 +127,7 @@ impl<'a> CreateModel<'a> {
}
state.poison_if_not(stop);
// check props
let mut props = Dict::new();
let mut props = DictGeneric::new();
if state.cursor_rounded_eq(Token![with]) {
state.cursor_ahead();
// parse props

@ -44,55 +44,18 @@
Feb. 2, 2023
*/
use {
crate::{
engine::{
core::HSData,
error::{LangError, LangResult},
ql::{
ast::{QueryData, State},
lex::{Ident, Lit, LitIR, Token},
},
use crate::{
engine::{
core::data::DictGeneric,
error::{LangError, LangResult},
ql::{
ast::{QueryData, State},
lex::{Ident, Token},
},
util::{compiler, MaybeInit},
},
std::collections::HashMap,
util::{compiler, MaybeInit},
};
#[derive(Debug, PartialEq)]
/// A dictionary entry type. Either a literal or another dictionary
pub enum DictEntry {
Lit(HSData),
Map(Dict),
}
impl<'a> From<LitIR<'a>> for DictEntry {
fn from(l: LitIR<'a>) -> Self {
Self::Lit(HSData::from(l))
}
}
impl<'a> From<Lit<'a>> for DictEntry {
fn from(value: Lit<'a>) -> Self {
Self::Lit(HSData::from(value))
}
}
impl From<HSData> for DictEntry {
fn from(hsd: HSData) -> Self {
Self::Lit(hsd)
}
}
impl From<Dict> for DictEntry {
fn from(d: Dict) -> Self {
Self::Map(d)
}
}
/// A metadata dictionary
pub type Dict = HashMap<Box<str>, Option<DictEntry>>;
/// This macro constructs states for our machine
///
/// **DO NOT** construct states manually
@ -150,7 +113,7 @@ impl<'a> Breakpoint<'a> for TypeBreakpoint {
fn _rfold_dict<'a, Qd, Bp>(
mut mstate: DictFoldState,
state: &mut State<'a, Qd>,
dict: &mut Dict,
dict: &mut DictGeneric,
) -> bool
where
Qd: QueryData<'a>,
@ -205,7 +168,7 @@ where
}
(Token![open {}], DictFoldState::LIT_OR_OB) => {
// found a nested dict
let mut ndict = Dict::new();
let mut ndict = DictGeneric::new();
_rfold_dict::<Qd, NoBreakpoint>(DictFoldState::CB_OR_IDENT, state, &mut ndict);
unsafe {
state.poison_if_not(
@ -240,7 +203,7 @@ where
pub(super) fn rfold_dict<'a, Qd: QueryData<'a>>(
mstate: DictFoldState,
state: &mut State<'a, Qd>,
dict: &mut Dict,
dict: &mut DictGeneric,
) {
_rfold_dict::<Qd, NoBreakpoint>(mstate, state, dict);
}
@ -248,7 +211,7 @@ pub(super) fn rfold_dict<'a, Qd: QueryData<'a>>(
pub(super) fn rfold_tymeta<'a, Qd: QueryData<'a>>(
mstate: DictFoldState,
state: &mut State<'a, Qd>,
dict: &mut Dict,
dict: &mut DictGeneric,
) -> bool {
_rfold_dict::<Qd, TypeBreakpoint>(mstate, state, dict)
}
@ -257,12 +220,12 @@ pub(super) fn rfold_tymeta<'a, Qd: QueryData<'a>>(
/// A layer contains a type and corresponding metadata
pub struct Layer<'a> {
ty: Ident<'a>,
props: Dict,
props: DictGeneric,
}
impl<'a> Layer<'a> {
//// Create a new layer
pub const fn new(ty: Ident<'a>, props: Dict) -> Self {
pub const fn new(ty: Ident<'a>, props: DictGeneric) -> Self {
Self { ty, props }
}
}
@ -403,11 +366,11 @@ impl<'a> Field<'a> {
pub struct ExpandedField<'a> {
field_name: Ident<'a>,
layers: Vec<Layer<'a>>,
props: Dict,
props: DictGeneric,
}
impl<'a> ExpandedField<'a> {
pub fn new(field_name: Ident<'a>, layers: Vec<Layer<'a>>, props: Dict) -> Self {
pub fn new(field_name: Ident<'a>, layers: Vec<Layer<'a>>, props: DictGeneric) -> Self {
Self {
field_name,
layers,
@ -426,7 +389,7 @@ impl<'a> ExpandedField<'a> {
state.poison_if_not(state.cursor_eq(Token![open {}]));
state.cursor_ahead();
// ignore errors; now attempt a tymeta-like parse
let mut props = Dict::new();
let mut props = DictGeneric::new();
let mut layers = Vec::new();
if rfold_tymeta(DictFoldState::CB_OR_IDENT, state, &mut props) {
// this has layers. fold them; but don't forget the colon
@ -518,8 +481,8 @@ pub use impls::{DictBasic, DictTypeMeta, DictTypeMetaSplit};
mod impls {
use {
super::{
rfold_dict, rfold_layers, rfold_tymeta, Dict, DictFoldState, ExpandedField, Field,
Layer, LayerFoldState,
rfold_dict, rfold_layers, rfold_tymeta, DictFoldState, ExpandedField, Field, Layer,
LayerFoldState, DictGeneric,
},
crate::engine::{
error::LangResult,
@ -554,34 +517,34 @@ mod impls {
}
}
#[derive(sky_macros::Wrapper, Debug)]
pub struct DictBasic(Dict);
pub struct DictBasic(DictGeneric);
impl<'a> ASTNode<'a> for DictBasic {
// important: upstream must verify this
const VERIFY: bool = true;
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> LangResult<Self> {
let mut dict = Dict::new();
let mut dict = DictGeneric::new();
rfold_dict(DictFoldState::OB, state, &mut dict);
Ok(Self(dict))
}
}
#[derive(sky_macros::Wrapper, Debug)]
pub struct DictTypeMetaSplit(Dict);
pub struct DictTypeMetaSplit(DictGeneric);
impl<'a> ASTNode<'a> for DictTypeMetaSplit {
// important: upstream must verify this
const VERIFY: bool = true;
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> LangResult<Self> {
let mut dict = Dict::new();
let mut dict = DictGeneric::new();
rfold_tymeta(DictFoldState::CB_OR_IDENT, state, &mut dict);
Ok(Self(dict))
}
}
#[derive(sky_macros::Wrapper, Debug)]
pub struct DictTypeMeta(Dict);
pub struct DictTypeMeta(DictGeneric);
impl<'a> ASTNode<'a> for DictTypeMeta {
// important: upstream must verify this
const VERIFY: bool = true;
fn _from_state<Qd: QueryData<'a>>(state: &mut State<'a, Qd>) -> LangResult<Self> {
let mut dict = Dict::new();
let mut dict = DictGeneric::new();
rfold_tymeta(DictFoldState::OB, state, &mut dict);
Ok(Self(dict))
}

@ -76,24 +76,24 @@ fn nullable_datatype(v: impl NullableData<HSData>) -> Option<HSData> {
}
pub trait NullableDictEntry {
fn data(self) -> Option<super::ddl::syn::DictEntry>;
fn data(self) -> Option<crate::engine::core::data::DictEntryGeneric>;
}
impl NullableDictEntry for Null {
fn data(self) -> Option<super::ddl::syn::DictEntry> {
fn data(self) -> Option<crate::engine::core::data::DictEntryGeneric> {
None
}
}
impl<'a> NullableDictEntry for super::lex::Lit<'a> {
fn data(self) -> Option<super::ddl::syn::DictEntry> {
Some(super::ddl::syn::DictEntry::from(self.as_ir()))
fn data(self) -> Option<crate::engine::core::data::DictEntryGeneric> {
Some(crate::engine::core::data::DictEntryGeneric::from(self.as_ir()))
}
}
impl NullableDictEntry for super::ddl::syn::Dict {
fn data(self) -> Option<super::ddl::syn::DictEntry> {
Some(super::ddl::syn::DictEntry::Map(self))
impl NullableDictEntry for crate::engine::core::data::DictGeneric {
fn data(self) -> Option<crate::engine::core::data::DictEntryGeneric> {
Some(crate::engine::core::data::DictEntryGeneric::Map(self))
}
}

@ -26,10 +26,9 @@
use {
super::*,
crate::engine::ql::{
ast::parse_ast_node_full,
ddl::syn::{Dict, DictBasic},
lex::Lit,
crate::engine::{
core::data::DictGeneric,
ql::{ast::parse_ast_node_full, ddl::syn::DictBasic, lex::Lit},
},
};
@ -41,7 +40,7 @@ macro_rules! fold_dict {
}
}
fn fold_dict(raw: &[u8]) -> Option<Dict> {
fn fold_dict(raw: &[u8]) -> Option<DictGeneric> {
let lexed = lex_insecure(raw).unwrap();
parse_ast_node_full::<DictBasic>(&lexed)
.map(|v| v.into_inner())

Loading…
Cancel
Save