add blobs via #bytes to edn
This commit is contained in:
parent
479fbc4572
commit
1500d4348c
13 changed files with 83 additions and 26 deletions
|
@ -118,10 +118,10 @@ impl ::std::fmt::Display for InputError {
|
||||||
match self {
|
match self {
|
||||||
BadDbId => {
|
BadDbId => {
|
||||||
writeln!(f, ":db/id in map notation must either not be present or be an entid, an ident, or a tempid")
|
writeln!(f, ":db/id in map notation must either not be present or be an entid, an ident, or a tempid")
|
||||||
},
|
}
|
||||||
BadEntityPlace => {
|
BadEntityPlace => {
|
||||||
writeln!(f, "cannot convert value place into entity place")
|
writeln!(f, "cannot convert value place into entity place")
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ use std::iter::Peekable;
|
||||||
use failure::ResultExt;
|
use failure::ResultExt;
|
||||||
|
|
||||||
use rusqlite;
|
use rusqlite;
|
||||||
use rusqlite::{params_from_iter};
|
use rusqlite::params_from_iter;
|
||||||
|
|
||||||
use core_traits::{Binding, Entid, TypedValue};
|
use core_traits::{Binding, Entid, TypedValue};
|
||||||
|
|
||||||
|
@ -1072,7 +1072,9 @@ impl AttributeCaches {
|
||||||
replacing: bool,
|
replacing: bool,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut aev_factory = AevFactory::new();
|
let mut aev_factory = AevFactory::new();
|
||||||
let rows = statement.query_map(params_from_iter(&args), |row| Ok(aev_factory.row_to_aev(row)))?;
|
let rows = statement.query_map(params_from_iter(&args), |row| {
|
||||||
|
Ok(aev_factory.row_to_aev(row))
|
||||||
|
})?;
|
||||||
let aevs = AevRows { rows };
|
let aevs = AevRows { rows };
|
||||||
self.accumulate_into_cache(
|
self.accumulate_into_cache(
|
||||||
None,
|
None,
|
||||||
|
|
|
@ -22,9 +22,9 @@ use itertools;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rusqlite;
|
use rusqlite;
|
||||||
use rusqlite::limits::Limit;
|
use rusqlite::limits::Limit;
|
||||||
|
use rusqlite::params_from_iter;
|
||||||
use rusqlite::types::{ToSql, ToSqlOutput};
|
use rusqlite::types::{ToSql, ToSqlOutput};
|
||||||
use rusqlite::TransactionBehavior;
|
use rusqlite::TransactionBehavior;
|
||||||
use rusqlite::{params_from_iter};
|
|
||||||
|
|
||||||
use crate::bootstrap;
|
use crate::bootstrap;
|
||||||
use crate::{repeat_values, to_namespaced_keyword};
|
use crate::{repeat_values, to_namespaced_keyword};
|
||||||
|
|
|
@ -75,7 +75,7 @@ impl TransactableValue for ValueAndSpan {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Nil | Boolean(_) | Instant(_) | BigInteger(_) | Float(_) | Uuid(_) | PlainSymbol(_)
|
Nil | Boolean(_) | Instant(_) | BigInteger(_) | Float(_) | Uuid(_) | PlainSymbol(_)
|
||||||
| NamespacedSymbol(_) | Vector(_) | Set(_) | Map(_) => {
|
| NamespacedSymbol(_) | Vector(_) | Set(_) | Map(_) | Bytes(_) => {
|
||||||
bail!(DbErrorKind::InputError(errors::InputError::BadEntityPlace))
|
bail!(DbErrorKind::InputError(errors::InputError::BadEntityPlace))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,7 @@ fn move_transactions_to(
|
||||||
new_timeline,
|
new_timeline,
|
||||||
crate::repeat_values(tx_ids.len(), 1)
|
crate::repeat_values(tx_ids.len(), 1)
|
||||||
),
|
),
|
||||||
params_from_iter(tx_ids.iter())
|
params_from_iter(tx_ids.iter()),
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,8 @@ uuid = { version = "~0.8", features = ["v4", "serde"] }
|
||||||
serde = { version = "~1.0", optional = true }
|
serde = { version = "~1.0", optional = true }
|
||||||
serde_derive = { version = "~1.0", optional = true }
|
serde_derive = { version = "~1.0", optional = true }
|
||||||
peg = "~0.7"
|
peg = "~0.7"
|
||||||
|
bytes = "1.0.1"
|
||||||
|
hex = "0.4.3"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serde_test = "~1.0"
|
serde_test = "~1.0"
|
||||||
|
|
|
@ -8,7 +8,9 @@
|
||||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
// specific language governing permissions and limitations under the License.
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
extern crate bytes;
|
||||||
extern crate chrono;
|
extern crate chrono;
|
||||||
|
extern crate hex;
|
||||||
extern crate itertools;
|
extern crate itertools;
|
||||||
extern crate num;
|
extern crate num;
|
||||||
extern crate ordered_float;
|
extern crate ordered_float;
|
||||||
|
@ -38,7 +40,9 @@ pub mod value_rc;
|
||||||
pub use crate::value_rc::{Cloned, FromRc, ValueRc};
|
pub use crate::value_rc::{Cloned, FromRc, ValueRc};
|
||||||
|
|
||||||
// Re-export the types we use.
|
// Re-export the types we use.
|
||||||
|
use bytes::Bytes;
|
||||||
pub use chrono::{DateTime, Utc};
|
pub use chrono::{DateTime, Utc};
|
||||||
|
use hex::decode;
|
||||||
pub use num::BigInt;
|
pub use num::BigInt;
|
||||||
pub use ordered_float::OrderedFloat;
|
pub use ordered_float::OrderedFloat;
|
||||||
pub use uuid::Uuid;
|
pub use uuid::Uuid;
|
||||||
|
@ -172,6 +176,13 @@ peg::parser!(pub grammar parse() for str {
|
||||||
pub rule uuid() -> SpannedValue = "#uuid" whitespace()+ u:uuid_string()
|
pub rule uuid() -> SpannedValue = "#uuid" whitespace()+ u:uuid_string()
|
||||||
{ SpannedValue::Uuid(u) }
|
{ SpannedValue::Uuid(u) }
|
||||||
|
|
||||||
|
rule byte_buffer() -> Bytes =
|
||||||
|
u:$( ['a'..='f' | 'A'..='F' | '0'..='9']* ) {
|
||||||
|
let b = decode(u).expect("this is a valid hex byte string");
|
||||||
|
Bytes::copy_from_slice(&b)
|
||||||
|
}
|
||||||
|
pub rule bytes() -> SpannedValue = "#bytes" whitespace()+ u:byte_buffer()
|
||||||
|
{ SpannedValue::Bytes(u) }
|
||||||
rule namespace_divider() = "."
|
rule namespace_divider() = "."
|
||||||
rule namespace_separator() = "/"
|
rule namespace_separator() = "/"
|
||||||
|
|
||||||
|
@ -219,7 +230,7 @@ peg::parser!(pub grammar parse() for str {
|
||||||
|
|
||||||
// Note: It's important that float comes before integer or the parser assumes that floats are integers and fails to parse.
|
// Note: It's important that float comes before integer or the parser assumes that floats are integers and fails to parse.
|
||||||
pub rule value() -> ValueAndSpan =
|
pub rule value() -> ValueAndSpan =
|
||||||
__ start:position!() v:(nil() / nan() / infinity() / boolean() / number() / inst() / uuid() / text() / keyword() / symbol() / list() / vector() / map() / set()) end:position!() __ {
|
__ start:position!() v:(nil() / nan() / infinity() / boolean() / number() / inst() / uuid() / text() / keyword() / symbol() / list() / vector() / map() / set() / bytes() ) end:position!() __ {
|
||||||
ValueAndSpan {
|
ValueAndSpan {
|
||||||
inner: v,
|
inner: v,
|
||||||
span: Span::new(start, end)
|
span: Span::new(start, end)
|
||||||
|
|
|
@ -58,9 +58,7 @@ impl Value {
|
||||||
let open = open.into();
|
let open = open.into();
|
||||||
let n = open.len() as isize;
|
let n = open.len() as isize;
|
||||||
let i = {
|
let i = {
|
||||||
let this = vs
|
let this = vs.into_iter().map(|v| v.as_doc(allocator));
|
||||||
.into_iter()
|
|
||||||
.map(|v| v.as_doc(allocator));
|
|
||||||
let element = allocator.line();
|
let element = allocator.line();
|
||||||
Itertools::intersperse(this, element)
|
Itertools::intersperse(this, element)
|
||||||
};
|
};
|
||||||
|
|
|
@ -233,7 +233,7 @@ impl FromValue<FnArg> for FnArg {
|
||||||
{
|
{
|
||||||
Some(FnArg::Constant(x.clone().into()))
|
Some(FnArg::Constant(x.clone().into()))
|
||||||
}
|
}
|
||||||
Nil | NamespacedSymbol(_) | Vector(_) | List(_) | Set(_) | Map(_) => None,
|
Nil | NamespacedSymbol(_) | Vector(_) | List(_) | Set(_) | Map(_) | Bytes(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -410,6 +410,7 @@ impl FromValue<PatternValuePlace> for PatternValuePlace {
|
||||||
crate::SpannedValue::List(_) => None,
|
crate::SpannedValue::List(_) => None,
|
||||||
crate::SpannedValue::Set(_) => None,
|
crate::SpannedValue::Set(_) => None,
|
||||||
crate::SpannedValue::Vector(_) => None,
|
crate::SpannedValue::Vector(_) => None,
|
||||||
|
crate::SpannedValue::Bytes(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,8 @@ use uuid::Uuid;
|
||||||
|
|
||||||
use crate::symbols;
|
use crate::symbols;
|
||||||
|
|
||||||
|
use bytes::Bytes;
|
||||||
|
use hex::encode;
|
||||||
/// Value represents one of the allowed values in an EDN string.
|
/// Value represents one of the allowed values in an EDN string.
|
||||||
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
|
@ -52,6 +54,7 @@ pub enum Value {
|
||||||
// See https://internals.rust-lang.org/t/implementing-hash-for-hashset-hashmap/3817/1
|
// See https://internals.rust-lang.org/t/implementing-hash-for-hashset-hashmap/3817/1
|
||||||
Set(BTreeSet<Value>),
|
Set(BTreeSet<Value>),
|
||||||
Map(BTreeMap<Value, Value>),
|
Map(BTreeMap<Value, Value>),
|
||||||
|
Bytes(Bytes),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `SpannedValue` is the parallel to `Value` but used in `ValueAndSpan`.
|
/// `SpannedValue` is the parallel to `Value` but used in `ValueAndSpan`.
|
||||||
|
@ -73,6 +76,7 @@ pub enum SpannedValue {
|
||||||
List(LinkedList<ValueAndSpan>),
|
List(LinkedList<ValueAndSpan>),
|
||||||
Set(BTreeSet<ValueAndSpan>),
|
Set(BTreeSet<ValueAndSpan>),
|
||||||
Map(BTreeMap<ValueAndSpan, ValueAndSpan>),
|
Map(BTreeMap<ValueAndSpan, ValueAndSpan>),
|
||||||
|
Bytes(Bytes),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Span represents the current offset (start, end) into the input string.
|
/// Span represents the current offset (start, end) into the input string.
|
||||||
|
@ -172,6 +176,7 @@ impl From<SpannedValue> for Value {
|
||||||
.map(|(x, y)| (x.without_spans(), y.without_spans()))
|
.map(|(x, y)| (x.without_spans(), y.without_spans()))
|
||||||
.collect(),
|
.collect(),
|
||||||
),
|
),
|
||||||
|
SpannedValue::Bytes(b) => Value::Bytes(b),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -328,6 +333,7 @@ macro_rules! def_common_value_methods {
|
||||||
def_is!(is_list, $t::List(_));
|
def_is!(is_list, $t::List(_));
|
||||||
def_is!(is_set, $t::Set(_));
|
def_is!(is_set, $t::Set(_));
|
||||||
def_is!(is_map, $t::Map(_));
|
def_is!(is_map, $t::Map(_));
|
||||||
|
def_is!(is_bytes, $t::Bytes(_));
|
||||||
|
|
||||||
pub fn is_keyword(&self) -> bool {
|
pub fn is_keyword(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
|
@ -360,6 +366,7 @@ macro_rules! def_common_value_methods {
|
||||||
def_as_ref!(as_uuid, $t::Uuid, Uuid);
|
def_as_ref!(as_uuid, $t::Uuid, Uuid);
|
||||||
def_as_ref!(as_symbol, $t::PlainSymbol, symbols::PlainSymbol);
|
def_as_ref!(as_symbol, $t::PlainSymbol, symbols::PlainSymbol);
|
||||||
def_as_ref!(as_namespaced_symbol, $t::NamespacedSymbol, symbols::NamespacedSymbol);
|
def_as_ref!(as_namespaced_symbol, $t::NamespacedSymbol, symbols::NamespacedSymbol);
|
||||||
|
def_as_ref!(as_bytes, $t::Bytes, Bytes);
|
||||||
|
|
||||||
pub fn as_keyword(&self) -> Option<&symbols::Keyword> {
|
pub fn as_keyword(&self) -> Option<&symbols::Keyword> {
|
||||||
match self {
|
match self {
|
||||||
|
@ -397,6 +404,7 @@ macro_rules! def_common_value_methods {
|
||||||
def_into!(into_uuid, $t::Uuid, Uuid,);
|
def_into!(into_uuid, $t::Uuid, Uuid,);
|
||||||
def_into!(into_symbol, $t::PlainSymbol, symbols::PlainSymbol,);
|
def_into!(into_symbol, $t::PlainSymbol, symbols::PlainSymbol,);
|
||||||
def_into!(into_namespaced_symbol, $t::NamespacedSymbol, symbols::NamespacedSymbol,);
|
def_into!(into_namespaced_symbol, $t::NamespacedSymbol, symbols::NamespacedSymbol,);
|
||||||
|
def_into!(into_bytes, $t::Bytes, Bytes,);
|
||||||
|
|
||||||
pub fn into_keyword(self) -> Option<symbols::Keyword> {
|
pub fn into_keyword(self) -> Option<symbols::Keyword> {
|
||||||
match self {
|
match self {
|
||||||
|
@ -467,6 +475,7 @@ macro_rules! def_common_value_methods {
|
||||||
$t::List(_) => 13,
|
$t::List(_) => 13,
|
||||||
$t::Set(_) => 14,
|
$t::Set(_) => 14,
|
||||||
$t::Map(_) => 15,
|
$t::Map(_) => 15,
|
||||||
|
$t::Bytes(_) => 16,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -487,6 +496,7 @@ macro_rules! def_common_value_methods {
|
||||||
$t::List(_) => true,
|
$t::List(_) => true,
|
||||||
$t::Set(_) => true,
|
$t::Set(_) => true,
|
||||||
$t::Map(_) => true,
|
$t::Map(_) => true,
|
||||||
|
$t::Bytes(_) => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -524,6 +534,7 @@ macro_rules! def_common_value_ord {
|
||||||
(&$t::List(ref a), &$t::List(ref b)) => b.cmp(a),
|
(&$t::List(ref a), &$t::List(ref b)) => b.cmp(a),
|
||||||
(&$t::Set(ref a), &$t::Set(ref b)) => b.cmp(a),
|
(&$t::Set(ref a), &$t::Set(ref b)) => b.cmp(a),
|
||||||
(&$t::Map(ref a), &$t::Map(ref b)) => b.cmp(a),
|
(&$t::Map(ref a), &$t::Map(ref b)) => b.cmp(a),
|
||||||
|
(&$t::Bytes(ref a), &$t::Bytes(ref b)) => b.cmp(a),
|
||||||
_ => $value.precedence().cmp(&$other.precedence()),
|
_ => $value.precedence().cmp(&$other.precedence()),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -590,6 +601,10 @@ macro_rules! def_common_value_display {
|
||||||
}
|
}
|
||||||
write!($f, " }}")
|
write!($f, " }}")
|
||||||
}
|
}
|
||||||
|
$t::Bytes(ref v) => {
|
||||||
|
let s = encode(v);
|
||||||
|
write!($f, "#bytes \"{}\"", s)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,6 +82,7 @@ fn_parse_into_value!(vector);
|
||||||
fn_parse_into_value!(set);
|
fn_parse_into_value!(set);
|
||||||
fn_parse_into_value!(map);
|
fn_parse_into_value!(map);
|
||||||
fn_parse_into_value!(value);
|
fn_parse_into_value!(value);
|
||||||
|
fn_parse_into_value!(bytes);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_nil() {
|
fn test_nil() {
|
||||||
|
@ -316,6 +317,27 @@ fn test_uuid() {
|
||||||
assert_eq!(value.to_pretty(100).unwrap(), s);
|
assert_eq!(value.to_pretty(100).unwrap(), s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bytes() {
|
||||||
|
assert!(parse::bytes("#bytes01 ").is_err()); // No whitespace.
|
||||||
|
assert!(parse::bytes("#bytes _ZZ").is_err()); // No whitespace.
|
||||||
|
assert!(parse::bytes("#bytes 01 ").is_err()); // No whitespace.
|
||||||
|
assert!(parse::bytes("#01 ").is_err()); // No whitespace.
|
||||||
|
|
||||||
|
let expected = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||||
|
let s = format!("{} {}", "#bytes", hex::encode(expected.clone()));
|
||||||
|
let actual: Value = parse::bytes(&s).expect("parse success").into();
|
||||||
|
assert!(actual.is_bytes());
|
||||||
|
assert_eq!(expected, actual.as_bytes().unwrap().to_vec());
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
self::bytes("#bytes 010203050403022a").unwrap(),
|
||||||
|
Value::Bytes(bytes::Bytes::copy_from_slice(&vec!(
|
||||||
|
1, 2, 3, 5, 4, 3, 2, 42
|
||||||
|
)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_inst() {
|
fn test_inst() {
|
||||||
assert!(parse::value("#inst\"2016-01-01T11:00:00.000Z\"").is_err()); // No whitespace.
|
assert!(parse::value("#inst\"2016-01-01T11:00:00.000Z\"").is_err()); // No whitespace.
|
||||||
|
@ -584,6 +606,12 @@ fn test_value() {
|
||||||
value("#inst \"2017-04-28T20:23:05.187Z\"").unwrap(),
|
value("#inst \"2017-04-28T20:23:05.187Z\"").unwrap(),
|
||||||
Instant(Utc.timestamp(1493410985, 187000000))
|
Instant(Utc.timestamp(1493410985, 187000000))
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
value("#bytes 010203050403022a").unwrap(),
|
||||||
|
Bytes(bytes::Bytes::copy_from_slice(&vec!(
|
||||||
|
1, 2, 3, 5, 4, 3, 2, 42
|
||||||
|
)))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use hyper::{body, header, Client, Body, Method, Request, StatusCode};
|
use hyper::{body, header, Body, Client, Method, Request, StatusCode};
|
||||||
use hyper_tls::HttpsConnector;
|
use hyper_tls::HttpsConnector;
|
||||||
// TODO: https://github.com/mozilla/mentat/issues/570
|
// TODO: https://github.com/mozilla/mentat/issues/570
|
||||||
// use serde_cbor;
|
// use serde_cbor;
|
||||||
|
|
|
@ -741,13 +741,14 @@ impl Syncer {
|
||||||
// Since we've "merged" with the remote bootstrap, the "no-op" and
|
// Since we've "merged" with the remote bootstrap, the "no-op" and
|
||||||
// "local fast-forward" cases are reported as merges.
|
// "local fast-forward" cases are reported as merges.
|
||||||
match Syncer::what_do(remote_state, local_state) {
|
match Syncer::what_do(remote_state, local_state) {
|
||||||
SyncAction::NoOp => {
|
SyncAction::NoOp => Ok(SyncReport::Merge(SyncFollowup::None)),
|
||||||
Ok(SyncReport::Merge(SyncFollowup::None))
|
|
||||||
}
|
|
||||||
|
|
||||||
SyncAction::PopulateRemote => {
|
SyncAction::PopulateRemote => {
|
||||||
// This is a programming error.
|
// This is a programming error.
|
||||||
bail!(TolstoyError::UnexpectedState("Remote state can't be empty on first sync against non-empty remote".to_string()))
|
bail!(TolstoyError::UnexpectedState(
|
||||||
|
"Remote state can't be empty on first sync against non-empty remote"
|
||||||
|
.to_string()
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
SyncAction::RemoteFastForward => {
|
SyncAction::RemoteFastForward => {
|
||||||
|
@ -761,12 +762,11 @@ impl Syncer {
|
||||||
|
|
||||||
SyncAction::CombineChanges => {
|
SyncAction::CombineChanges => {
|
||||||
let local_txs = Processor::process(
|
let local_txs = Processor::process(
|
||||||
&ip.transaction, Some(local_metadata.root), LocalTxSet::new())?;
|
&ip.transaction,
|
||||||
Syncer::merge(
|
Some(local_metadata.root),
|
||||||
ip,
|
LocalTxSet::new(),
|
||||||
incoming_txs[1..].to_vec(),
|
)?;
|
||||||
local_txs,
|
Syncer::merge(ip, incoming_txs[1..].to_vec(), local_txs)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue