Part 2: Use value_and_span apparatus in tx-parser/.

I break an abstraction boundary by returning a value column
`edn::ValueAndSpan` rather than just an `edn::Value`.  That is, the
transaction processor shouldn't care where the `edn::Value` it is
processing arose -- even we care to track that information we should
bake it into the `Entity` type.  We do this because we need to
dynamically parse the value column to support nested maps, and parsing
requires a full `edn::ValueAndSpan`.  Alternately, we could cheat and
fake the spans when parsing nested maps, but that's potentially
expensive.
This commit is contained in:
Nick Alexander 2017-03-28 14:38:31 -07:00
parent f4e2a0471f
commit e947a32c59
7 changed files with 214 additions and 260 deletions

View file

@ -273,6 +273,6 @@ pub fn bootstrap_entities() -> Vec<Entity> {
// Failure here is a coding error (since the inputs are fixed), not a runtime error. // Failure here is a coding error (since the inputs are fixed), not a runtime error.
// TODO: represent these bootstrap data errors rather than just panicing. // TODO: represent these bootstrap data errors rather than just panicing.
let bootstrap_entities: Vec<Entity> = mentat_tx_parser::Tx::parse(&[bootstrap_assertions][..]).unwrap(); let bootstrap_entities: Vec<Entity> = mentat_tx_parser::Tx::parse(bootstrap_assertions.with_spans()).unwrap();
return bootstrap_entities; return bootstrap_entities;
} }

View file

@ -1125,8 +1125,8 @@ mod tests {
fn transact<I>(&mut self, transaction: I) -> Result<TxReport> where I: Borrow<str> { fn transact<I>(&mut self, transaction: I) -> Result<TxReport> where I: Borrow<str> {
// Failure to parse the transaction is a coding error, so we unwrap. // Failure to parse the transaction is a coding error, so we unwrap.
let assertions = edn::parse::value(transaction.borrow()).expect(format!("to be able to parse {} into EDN", transaction.borrow()).as_str()).without_spans(); let assertions = edn::parse::value(transaction.borrow()).expect(format!("to be able to parse {} into EDN", transaction.borrow()).as_str());
let entities: Vec<_> = mentat_tx_parser::Tx::parse(&[assertions.clone()][..]).expect(format!("to be able to parse {} into entities", assertions).as_str()); let entities: Vec<_> = mentat_tx_parser::Tx::parse(assertions.clone()).expect(format!("to be able to parse {} into entities", assertions).as_str());
let details = { let details = {
// The block scopes the borrow of self.sqlite. // The block scopes the borrow of self.sqlite.

View file

@ -254,13 +254,13 @@ impl<'conn, 'a> Tx<'conn, 'a> {
let v = match v { let v = match v {
entmod::AtomOrLookupRefOrVectorOrMapNotation::Atom(v) => { entmod::AtomOrLookupRefOrVectorOrMapNotation::Atom(v) => {
if attribute.value_type == ValueType::Ref && v.is_text() { if attribute.value_type == ValueType::Ref && v.inner.is_text() {
Either::Right(LookupRefOrTempId::TempId(temp_ids.intern(v.as_text().cloned().map(TempId::External).unwrap()))) Either::Right(LookupRefOrTempId::TempId(temp_ids.intern(v.inner.as_text().cloned().map(TempId::External).unwrap())))
} else { } else {
// Here is where we do schema-aware typechecking: we either assert that // Here is where we do schema-aware typechecking: we either assert that
// the given value is in the attribute's value set, or (in limited // the given value is in the attribute's value set, or (in limited
// cases) coerce the value into the attribute's value set. // cases) coerce the value into the attribute's value set.
let typed_value: TypedValue = self.schema.to_typed_value(&v, &attribute)?; let typed_value: TypedValue = self.schema.to_typed_value(&v.without_spans(), &attribute)?;
Either::Left(typed_value) Either::Left(typed_value)
} }
}, },

View file

@ -10,7 +10,8 @@
#![allow(dead_code)] #![allow(dead_code)]
use mentat_parser_utils::ValueParseError; use combine;
use mentat_parser_utils::value_and_span::Stream;
error_chain! { error_chain! {
types { types {
@ -18,9 +19,9 @@ error_chain! {
} }
errors { errors {
ParseError(value_parse_error: ValueParseError) { ParseError(parse_error: combine::ParseError<Stream>) {
description("error parsing edn values") description("error parsing edn values")
display("error parsing edn values:\n{}", value_parse_error) display("error parsing edn values:\n{}", parse_error)
} }
DbIdError { DbIdError {

View file

@ -10,8 +10,6 @@
#![allow(dead_code)] #![allow(dead_code)]
use std::iter::once;
extern crate combine; extern crate combine;
#[macro_use] #[macro_use]
extern crate error_chain; extern crate error_chain;
@ -22,13 +20,18 @@ extern crate mentat_tx;
#[macro_use] #[macro_use]
extern crate mentat_parser_utils; extern crate mentat_parser_utils;
use combine::{eof, many, parser, satisfy_map, token, Parser, ParseResult, Stream}; use combine::{
use combine::combinator::{Expected, FnParser}; eof,
many,
parser,
satisfy_map,
Parser,
ParseResult,
};
use edn::symbols::{ use edn::symbols::{
NamespacedKeyword, NamespacedKeyword,
PlainSymbol, PlainSymbol,
}; };
use edn::types::Value;
use mentat_tx::entities::{ use mentat_tx::entities::{
AtomOrLookupRefOrVectorOrMapNotation, AtomOrLookupRefOrVectorOrMapNotation,
Entid, Entid,
@ -39,97 +42,71 @@ use mentat_tx::entities::{
OpType, OpType,
TempId, TempId,
}; };
use mentat_parser_utils::{ResultParser, ValueParseError}; use mentat_parser_utils::{ResultParser};
use mentat_parser_utils::value_and_span::{
Item,
OfParsing,
integer,
list,
map,
namespaced_keyword,
value,
vector,
};
pub mod errors; pub mod errors;
pub use errors::*; pub use errors::*;
pub struct Tx<I>(::std::marker::PhantomData<fn(I) -> I>); pub struct Tx;
type TxParser<O, I> = Expected<FnParser<I, fn(I) -> ParseResult<O, I>>>; def_parser!(Tx, entid, Entid, {
integer()
fn fn_parser<O, I>(f: fn(I) -> ParseResult<O, I>, err: &'static str) -> TxParser<O, I>
where I: Stream<Item = Value>
{
parser(f).expected(err)
}
def_value_satisfy_parser_fn!(Tx, integer, i64, Value::as_integer);
fn value_to_namespaced_keyword(val: &Value) -> Option<NamespacedKeyword> {
val.as_namespaced_keyword().cloned()
}
def_value_satisfy_parser_fn!(Tx, keyword, NamespacedKeyword, value_to_namespaced_keyword);
def_parser_fn!(Tx, entid, Value, Entid, input, {
Tx::<I>::integer()
.map(|x| Entid::Entid(x)) .map(|x| Entid::Entid(x))
.or(Tx::<I>::keyword().map(|x| Entid::Ident(x))) .or(namespaced_keyword().map(|x| Entid::Ident(x)))
.parse_lazy(input)
.into()
}); });
fn value_to_lookup_ref(val: &Value) -> Option<LookupRef> { def_parser!(Tx, lookup_ref, LookupRef, {
val.as_list().and_then(|list| { list()
let vs: Vec<Value> = list.into_iter().cloned().collect(); .of(value(edn::Value::PlainSymbol(PlainSymbol::new("lookup-ref")))
let mut p = token(Value::PlainSymbol(PlainSymbol::new("lookup-ref"))) .with((Tx::entid(),
.with((Tx::<&[Value]>::entid(), Tx::atom()))
Tx::<&[Value]>::atom())) .map(|(a, v)| LookupRef { a: a, v: v.without_spans() }))
.skip(eof())
.map(|(a, v)| LookupRef { a: a, v: v });
let r: ParseResult<LookupRef, _> = p.parse_lazy(&vs[..]).into();
r.ok().map(|x| x.0)
})
}
def_value_satisfy_parser_fn!(Tx, lookup_ref, LookupRef, value_to_lookup_ref);
def_parser_fn!(Tx, entid_or_lookup_ref_or_temp_id, Value, EntidOrLookupRefOrTempId, input, {
Tx::<I>::entid().map(EntidOrLookupRefOrTempId::Entid)
.or(Tx::<I>::lookup_ref().map(EntidOrLookupRefOrTempId::LookupRef))
.or(Tx::<I>::temp_id().map(EntidOrLookupRefOrTempId::TempId))
.parse_lazy(input)
.into()
}); });
def_parser_fn!(Tx, temp_id, Value, TempId, input, { def_parser!(Tx, entid_or_lookup_ref_or_temp_id, EntidOrLookupRefOrTempId, {
satisfy_map(|x: Value| x.into_text().map(TempId::External)) Tx::entid().map(EntidOrLookupRefOrTempId::Entid)
.parse_stream(input) .or(Tx::lookup_ref().map(EntidOrLookupRefOrTempId::LookupRef))
.or(Tx::temp_id().map(EntidOrLookupRefOrTempId::TempId))
}); });
def_parser_fn!(Tx, atom, Value, Value, input, { def_parser!(Tx, temp_id, TempId, {
satisfy_map(|x: Value| x.into_atom()) satisfy_map(|x: edn::ValueAndSpan| x.into_text().map(TempId::External))
.parse_stream(input)
}); });
fn value_to_nested_vector(val: &Value) -> Option<Vec<AtomOrLookupRefOrVectorOrMapNotation>> { def_parser!(Tx, atom, edn::ValueAndSpan, {
val.as_vector().and_then(|vs| { satisfy_map(|x: edn::ValueAndSpan| x.into_atom().map(|v| v))
let mut p = many(Tx::<&[Value]>::atom_or_lookup_ref_or_vector()).skip(eof());
let r: ParseResult<Vec<AtomOrLookupRefOrVectorOrMapNotation>, _> = p.parse_lazy(&vs[..]).into();
r.map(|x| x.0).ok()
})
}
def_value_satisfy_parser_fn!(Tx, nested_vector, Vec<AtomOrLookupRefOrVectorOrMapNotation>, value_to_nested_vector);
def_parser_fn!(Tx, atom_or_lookup_ref_or_vector, Value, AtomOrLookupRefOrVectorOrMapNotation, input, {
Tx::<I>::lookup_ref().map(AtomOrLookupRefOrVectorOrMapNotation::LookupRef)
.or(Tx::<I>::nested_vector().map(AtomOrLookupRefOrVectorOrMapNotation::Vector))
.or(Tx::<I>::map_notation().map(AtomOrLookupRefOrVectorOrMapNotation::MapNotation))
.or(Tx::<I>::atom().map(AtomOrLookupRefOrVectorOrMapNotation::Atom))
.parse_lazy(input)
.into()
}); });
fn value_to_add_or_retract(val: &Value) -> Option<Entity> { def_parser!(Tx, nested_vector, Vec<AtomOrLookupRefOrVectorOrMapNotation>, {
val.as_vector().and_then(|vs| { vector().of(many(Tx::atom_or_lookup_ref_or_vector()))
let add = token(Value::NamespacedKeyword(NamespacedKeyword::new("db", "add"))) });
def_parser!(Tx, atom_or_lookup_ref_or_vector, AtomOrLookupRefOrVectorOrMapNotation, {
Tx::lookup_ref().map(AtomOrLookupRefOrVectorOrMapNotation::LookupRef)
.or(Tx::nested_vector().map(AtomOrLookupRefOrVectorOrMapNotation::Vector))
.or(Tx::map_notation().map(AtomOrLookupRefOrVectorOrMapNotation::MapNotation))
.or(Tx::atom().map(AtomOrLookupRefOrVectorOrMapNotation::Atom))
});
def_parser!(Tx, add_or_retract, Entity, {
let add = value(edn::Value::NamespacedKeyword(NamespacedKeyword::new("db", "add")))
.map(|_| OpType::Add); .map(|_| OpType::Add);
let retract = token(Value::NamespacedKeyword(NamespacedKeyword::new("db", "retract"))) let retract = value(edn::Value::NamespacedKeyword(NamespacedKeyword::new("db", "retract")))
.map(|_| OpType::Retract); .map(|_| OpType::Retract);
let mut p = (add.or(retract), let p = (add.or(retract),
Tx::<&[Value]>::entid_or_lookup_ref_or_temp_id(), Tx::entid_or_lookup_ref_or_temp_id(),
Tx::<&[Value]>::entid(), Tx::entid(),
Tx::<&[Value]>::atom_or_lookup_ref_or_vector()) Tx::atom_or_lookup_ref_or_vector())
.skip(eof())
.map(|(op, e, a, v)| { .map(|(op, e, a, v)| {
Entity::AddOrRetract { Entity::AddOrRetract {
op: op, op: op,
@ -138,66 +115,41 @@ fn value_to_add_or_retract(val: &Value) -> Option<Entity> {
v: v, v: v,
} }
}); });
let r: ParseResult<Entity, _> = p.parse_lazy(&vs[..]).into();
r.map(|x| x.0).ok()
})
}
def_value_satisfy_parser_fn!(Tx, add_or_retract, Entity, value_to_add_or_retract);
fn value_to_map_notation(val: &Value) -> Option<MapNotation> { vector().of(p)
val.as_map().cloned().and_then(|map| { });
// Parsing pairs is tricky; parsing sequences is easy.
let avseq: Vec<Value> = map.into_iter().flat_map(|(a, v)| once(a).chain(once(v))).collect();
let av = (Tx::<&[Value]>::entid(), def_parser!(Tx, map_notation, MapNotation, {
Tx::<&[Value]>::atom_or_lookup_ref_or_vector()) map()
.map(|(a, v)| -> (Entid, AtomOrLookupRefOrVectorOrMapNotation) { (a, v) }); .of(many((Tx::entid(), Tx::atom_or_lookup_ref_or_vector())))
let mut p = many(av)
.skip(eof())
.map(|avs: Vec<(Entid, AtomOrLookupRefOrVectorOrMapNotation)>| -> MapNotation { .map(|avs: Vec<(Entid, AtomOrLookupRefOrVectorOrMapNotation)>| -> MapNotation {
avs.into_iter().collect() avs.into_iter().collect()
});
let r: ParseResult<MapNotation, _> = p.parse_lazy(&avseq[..]).into();
r.map(|x| x.0).ok()
}) })
}
def_value_satisfy_parser_fn!(Tx, map_notation, MapNotation, value_to_map_notation);
def_parser_fn!(Tx, entity, Value, Entity, input, {
let mut p = Tx::<I>::add_or_retract()
.or(Tx::<I>::map_notation().map(Entity::MapNotation));
p.parse_stream(input)
}); });
fn value_to_entities(val: &Value) -> Option<Vec<Entity>> { def_parser!(Tx, entity, Entity, {
val.as_vector().and_then(|vs| { Tx::add_or_retract()
let mut p = many(Tx::<&[Value]>::entity()) .or(Tx::map_notation().map(Entity::MapNotation))
.skip(eof()); });
let r: ParseResult<Vec<Entity>, _> = p.parse_lazy(&vs[..]).into();
r.ok().map(|x| x.0)
})
}
def_value_satisfy_parser_fn!(Tx, entities, Vec<Entity>, value_to_entities); def_parser!(Tx, entities, Vec<Entity>, {
vector().of(many(Tx::entity()))
});
impl<'a> Tx<&'a [edn::Value]> { impl Tx {
pub fn parse(input: &'a [edn::Value]) -> std::result::Result<Vec<Entity>, errors::Error> { pub fn parse(input: edn::ValueAndSpan) -> std::result::Result<Vec<Entity>, errors::Error> {
Tx::<_>::entities() Tx::entities()
.skip(eof()) .skip(eof())
.parse(input) .parse(input.into_atom_stream())
.map(|x| x.0) .map(|x| x.0)
.map_err::<ValueParseError, _>(|e| e.translate_position(input).into())
.map_err(|e| Error::from_kind(ErrorKind::ParseError(e))) .map_err(|e| Error::from_kind(ErrorKind::ParseError(e)))
} }
}
impl<'a> Tx<&'a [edn::Value]> { fn parse_entid_or_lookup_ref_or_temp_id(input: edn::ValueAndSpan) -> std::result::Result<EntidOrLookupRefOrTempId, errors::Error> {
pub fn parse_entid_or_lookup_ref_or_temp_id(input: &'a [edn::Value]) -> std::result::Result<EntidOrLookupRefOrTempId, errors::Error> { Tx::entid_or_lookup_ref_or_temp_id()
Tx::<_>::entid_or_lookup_ref_or_temp_id()
.skip(eof()) .skip(eof())
.parse(input) .parse(input.into_atom_stream())
.map(|x| x.0) .map(|x| x.0)
.map_err::<ValueParseError, _>(|e| e.translate_position(input).into())
.map_err(|e| Error::from_kind(ErrorKind::ParseError(e))) .map_err(|e| Error::from_kind(ErrorKind::ParseError(e)))
} }
} }
@ -213,7 +165,7 @@ pub fn remove_db_id(map: &mut MapNotation) -> std::result::Result<Option<EntidOr
let db_id: Option<EntidOrLookupRefOrTempId> = if let Some(id) = map.remove(&db_id_key) { let db_id: Option<EntidOrLookupRefOrTempId> = if let Some(id) = map.remove(&db_id_key) {
match id { match id {
AtomOrLookupRefOrVectorOrMapNotation::Atom(v) => { AtomOrLookupRefOrVectorOrMapNotation::Atom(v) => {
let db_id = Tx::<_>::parse_entid_or_lookup_ref_or_temp_id(&[v][..]) let db_id = Tx::parse_entid_or_lookup_ref_or_temp_id(v)
.chain_err(|| Error::from(ErrorKind::DbIdError))?; .chain_err(|| Error::from(ErrorKind::DbIdError))?;
Some(db_id) Some(db_id)
}, },
@ -236,7 +188,12 @@ mod tests {
use std::collections::LinkedList; use std::collections::LinkedList;
use combine::Parser; use combine::Parser;
use edn::symbols::NamespacedKeyword; use edn::symbols::NamespacedKeyword;
use edn::types::Value; use edn::{
Span,
SpannedValue,
Value,
ValueAndSpan,
};
use mentat_tx::entities::{ use mentat_tx::entities::{
Entid, Entid,
EntidOrLookupRefOrTempId, EntidOrLookupRefOrTempId,
@ -253,108 +210,107 @@ mod tests {
#[test] #[test]
fn test_add() { fn test_add() {
let input = [Value::Vector(vec![kw("db", "add"), let input = Value::Vector(vec![kw("db", "add"),
kw("test", "entid"), kw("test", "entid"),
kw("test", "a"), kw("test", "a"),
Value::Text("v".into())])]; Value::Text("v".into())]).with_spans();
let mut parser = Tx::entity(); let mut parser = Tx::entity();
let result = parser.parse(&input[..]); let result = parser.parse(input.into_atom_stream()).map(|x| x.0);
assert_eq!(result, assert_eq!(result,
Ok((Entity::AddOrRetract { Ok(Entity::AddOrRetract {
op: OpType::Add, op: OpType::Add,
e: EntidOrLookupRefOrTempId::Entid(Entid::Ident(NamespacedKeyword::new("test", e: EntidOrLookupRefOrTempId::Entid(Entid::Ident(NamespacedKeyword::new("test",
"entid"))), "entid"))),
a: Entid::Ident(NamespacedKeyword::new("test", "a")), a: Entid::Ident(NamespacedKeyword::new("test", "a")),
v: AtomOrLookupRefOrVectorOrMapNotation::Atom(Value::Text("v".into())), v: AtomOrLookupRefOrVectorOrMapNotation::Atom(ValueAndSpan::new(SpannedValue::Text("v".into()), Span(29, 32))),
}, }));
&[][..])));
} }
#[test] // #[test]
fn test_retract() { // fn test_retract() {
let input = [Value::Vector(vec![kw("db", "retract"), // let input = [Value::Vector(vec![kw("db", "retract"),
Value::Integer(101), // Value::Integer(101),
kw("test", "a"), // kw("test", "a"),
Value::Text("v".into())])]; // Value::Text("v".into())])];
let mut parser = Tx::entity(); // let mut parser = Tx::entity();
let result = parser.parse(&input[..]); // let result = parser.parse(&input[..]);
assert_eq!(result, // assert_eq!(result,
Ok((Entity::AddOrRetract { // Ok((Entity::AddOrRetract {
op: OpType::Retract, // op: OpType::Retract,
e: EntidOrLookupRefOrTempId::Entid(Entid::Entid(101)), // e: EntidOrLookupRefOrTempId::Entid(Entid::Entid(101)),
a: Entid::Ident(NamespacedKeyword::new("test", "a")), // a: Entid::Ident(NamespacedKeyword::new("test", "a")),
v: AtomOrLookupRefOrVectorOrMapNotation::Atom(Value::Text("v".into())), // v: AtomOrLookupRefOrVectorOrMapNotation::Atom(Value::Text("v".into())),
}, // },
&[][..]))); // &[][..])));
} // }
#[test] // #[test]
fn test_lookup_ref() { // fn test_lookup_ref() {
let mut list = LinkedList::new(); // let mut list = LinkedList::new();
list.push_back(Value::PlainSymbol(PlainSymbol::new("lookup-ref"))); // list.push_back(Value::PlainSymbol(PlainSymbol::new("lookup-ref")));
list.push_back(kw("test", "a1")); // list.push_back(kw("test", "a1"));
list.push_back(Value::Text("v1".into())); // list.push_back(Value::Text("v1".into()));
let input = [Value::Vector(vec![kw("db", "add"), // let input = [Value::Vector(vec![kw("db", "add"),
Value::List(list), // Value::List(list),
kw("test", "a"), // kw("test", "a"),
Value::Text("v".into())])]; // Value::Text("v".into())])];
let mut parser = Tx::entity(); // let mut parser = Tx::entity();
let result = parser.parse(&input[..]); // let result = parser.parse(&input[..]);
assert_eq!(result, // assert_eq!(result,
Ok((Entity::AddOrRetract { // Ok((Entity::AddOrRetract {
op: OpType::Add, // op: OpType::Add,
e: EntidOrLookupRefOrTempId::LookupRef(LookupRef { // e: EntidOrLookupRefOrTempId::LookupRef(LookupRef {
a: Entid::Ident(NamespacedKeyword::new("test", "a1")), // a: Entid::Ident(NamespacedKeyword::new("test", "a1")),
v: Value::Text("v1".into()), // v: Value::Text("v1".into()),
}), // }),
a: Entid::Ident(NamespacedKeyword::new("test", "a")), // a: Entid::Ident(NamespacedKeyword::new("test", "a")),
v: AtomOrLookupRefOrVectorOrMapNotation::Atom(Value::Text("v".into())), // v: AtomOrLookupRefOrVectorOrMapNotation::Atom(Value::Text("v".into())),
}, // },
&[][..]))); // &[][..])));
} // }
#[test] // #[test]
fn test_nested_vector() { // fn test_nested_vector() {
let mut list = LinkedList::new(); // let mut list = LinkedList::new();
list.push_back(Value::PlainSymbol(PlainSymbol::new("lookup-ref"))); // list.push_back(Value::PlainSymbol(PlainSymbol::new("lookup-ref")));
list.push_back(kw("test", "a1")); // list.push_back(kw("test", "a1"));
list.push_back(Value::Text("v1".into())); // list.push_back(Value::Text("v1".into()));
let input = [Value::Vector(vec![kw("db", "add"), // let input = [Value::Vector(vec![kw("db", "add"),
Value::List(list), // Value::List(list),
kw("test", "a"), // kw("test", "a"),
Value::Vector(vec![Value::Text("v1".into()), Value::Text("v2".into())])])]; // Value::Vector(vec![Value::Text("v1".into()), Value::Text("v2".into())])])];
let mut parser = Tx::entity(); // let mut parser = Tx::entity();
let result = parser.parse(&input[..]); // let result = parser.parse(&input[..]);
assert_eq!(result, // assert_eq!(result,
Ok((Entity::AddOrRetract { // Ok((Entity::AddOrRetract {
op: OpType::Add, // op: OpType::Add,
e: EntidOrLookupRefOrTempId::LookupRef(LookupRef { // e: EntidOrLookupRefOrTempId::LookupRef(LookupRef {
a: Entid::Ident(NamespacedKeyword::new("test", "a1")), // a: Entid::Ident(NamespacedKeyword::new("test", "a1")),
v: Value::Text("v1".into()), // v: Value::Text("v1".into()),
}), // }),
a: Entid::Ident(NamespacedKeyword::new("test", "a")), // a: Entid::Ident(NamespacedKeyword::new("test", "a")),
v: AtomOrLookupRefOrVectorOrMapNotation::Vector(vec![AtomOrLookupRefOrVectorOrMapNotation::Atom(Value::Text("v1".into())), // v: AtomOrLookupRefOrVectorOrMapNotation::Vector(vec![AtomOrLookupRefOrVectorOrMapNotation::Atom(Value::Text("v1".into())),
AtomOrLookupRefOrVectorOrMapNotation::Atom(Value::Text("v2".into()))]), // AtomOrLookupRefOrVectorOrMapNotation::Atom(Value::Text("v2".into()))]),
}, // },
&[][..]))); // &[][..])));
} // }
#[test] // #[test]
fn test_map_notation() { // fn test_map_notation() {
let mut expected: MapNotation = BTreeMap::default(); // let mut expected: MapNotation = BTreeMap::default();
expected.insert(Entid::Ident(NamespacedKeyword::new("db", "id")), AtomOrLookupRefOrVectorOrMapNotation::Atom(Value::Text("t".to_string()))); // expected.insert(Entid::Ident(NamespacedKeyword::new("db", "id")), AtomOrLookupRefOrVectorOrMapNotation::Atom(Value::Text("t".to_string())));
expected.insert(Entid::Ident(NamespacedKeyword::new("db", "ident")), AtomOrLookupRefOrVectorOrMapNotation::Atom(kw("test", "attribute"))); // expected.insert(Entid::Ident(NamespacedKeyword::new("db", "ident")), AtomOrLookupRefOrVectorOrMapNotation::Atom(kw("test", "attribute")));
let mut map: BTreeMap<Value, Value> = BTreeMap::default(); // let mut map: BTreeMap<Value, Value> = BTreeMap::default();
map.insert(kw("db", "id"), Value::Text("t".to_string())); // map.insert(kw("db", "id"), Value::Text("t".to_string()));
map.insert(kw("db", "ident"), kw("test", "attribute")); // map.insert(kw("db", "ident"), kw("test", "attribute"));
let input = [Value::Map(map.clone())]; // let input = [Value::Map(map.clone())];
let mut parser = Tx::entity(); // let mut parser = Tx::entity();
let result = parser.parse(&input[..]); // let result = parser.parse(&input[..]);
assert_eq!(result, // assert_eq!(result,
Ok((Entity::MapNotation(expected), // Ok((Entity::MapNotation(expected),
&[][..]))); // &[][..])));
} // }
} }

View file

@ -15,7 +15,6 @@ extern crate mentat_tx_parser;
use edn::parse; use edn::parse;
use edn::symbols::NamespacedKeyword; use edn::symbols::NamespacedKeyword;
use edn::types::Value;
use mentat_tx::entities::{ use mentat_tx::entities::{
AtomOrLookupRefOrVectorOrMapNotation, AtomOrLookupRefOrVectorOrMapNotation,
Entid, Entid,
@ -33,29 +32,28 @@ fn test_entities() {
[:db/add "tempid" :test/a "v"] [:db/add "tempid" :test/a "v"]
[:db/retract 102 :test/b "w"]]"#; [:db/retract 102 :test/b "w"]]"#;
let edn = parse::value(input).unwrap().without_spans(); let edn = parse::value(input).expect("to parse test input");
let input = [edn];
let result = Tx::parse(&input[..]); let result = Tx::parse(edn);
assert_eq!(result.unwrap(), assert_eq!(result.unwrap(),
vec![ vec![
Entity::AddOrRetract { Entity::AddOrRetract {
op: OpType::Add, op: OpType::Add,
e: EntidOrLookupRefOrTempId::Entid(Entid::Entid(101)), e: EntidOrLookupRefOrTempId::Entid(Entid::Entid(101)),
a: Entid::Ident(NamespacedKeyword::new("test", "a")), a: Entid::Ident(NamespacedKeyword::new("test", "a")),
v: AtomOrLookupRefOrVectorOrMapNotation::Atom(Value::Text("v".into())), v: AtomOrLookupRefOrVectorOrMapNotation::Atom(edn::ValueAndSpan::new(edn::SpannedValue::Text("v".into()), edn::Span(23, 26))),
}, },
Entity::AddOrRetract { Entity::AddOrRetract {
op: OpType::Add, op: OpType::Add,
e: EntidOrLookupRefOrTempId::TempId(TempId::External("tempid".into())), e: EntidOrLookupRefOrTempId::TempId(TempId::External("tempid".into())),
a: Entid::Ident(NamespacedKeyword::new("test", "a")), a: Entid::Ident(NamespacedKeyword::new("test", "a")),
v: AtomOrLookupRefOrVectorOrMapNotation::Atom(Value::Text("v".into())), v: AtomOrLookupRefOrVectorOrMapNotation::Atom(edn::ValueAndSpan::new(edn::SpannedValue::Text("v".into()), edn::Span(55, 58))),
}, },
Entity::AddOrRetract { Entity::AddOrRetract {
op: OpType::Retract, op: OpType::Retract,
e: EntidOrLookupRefOrTempId::Entid(Entid::Entid(102)), e: EntidOrLookupRefOrTempId::Entid(Entid::Entid(102)),
a: Entid::Ident(NamespacedKeyword::new("test", "b")), a: Entid::Ident(NamespacedKeyword::new("test", "b")),
v: AtomOrLookupRefOrVectorOrMapNotation::Atom(Value::Text("w".into())), v: AtomOrLookupRefOrVectorOrMapNotation::Atom(edn::ValueAndSpan::new(edn::SpannedValue::Text("w".into()), edn::Span(86, 89))),
}, },
]); ]);
} }

View file

@ -15,7 +15,6 @@ extern crate edn;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fmt; use std::fmt;
use self::edn::types::Value;
use self::edn::symbols::NamespacedKeyword; use self::edn::symbols::NamespacedKeyword;
/// A tempid, either an external tempid given in a transaction (usually as an `edn::Value::Text`), /// A tempid, either an external tempid given in a transaction (usually as an `edn::Value::Text`),
@ -62,14 +61,14 @@ pub struct LookupRef {
pub a: Entid, pub a: Entid,
// In theory we could allow nested lookup-refs. In practice this would require us to process // In theory we could allow nested lookup-refs. In practice this would require us to process
// lookup-refs in multiple phases, like how we resolve tempids, which isn't worth the effort. // lookup-refs in multiple phases, like how we resolve tempids, which isn't worth the effort.
pub v: Value, // An atom. pub v: edn::Value, // An atom.
} }
pub type MapNotation = BTreeMap<Entid, AtomOrLookupRefOrVectorOrMapNotation>; pub type MapNotation = BTreeMap<Entid, AtomOrLookupRefOrVectorOrMapNotation>;
#[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)] #[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
pub enum AtomOrLookupRefOrVectorOrMapNotation { pub enum AtomOrLookupRefOrVectorOrMapNotation {
Atom(Value), Atom(edn::ValueAndSpan),
LookupRef(LookupRef), LookupRef(LookupRef),
Vector(Vec<AtomOrLookupRefOrVectorOrMapNotation>), Vector(Vec<AtomOrLookupRefOrVectorOrMapNotation>),
MapNotation(MapNotation), MapNotation(MapNotation),