Compare commits
9 commits
Author | SHA1 | Date | |
---|---|---|---|
|
e8061be6bf | ||
|
853d51429e | ||
|
501d8954c7 | ||
|
447595f120 | ||
|
8e83310ea3 | ||
|
149ab50b3e | ||
|
f06a3e3928 | ||
|
3bb4bd79b9 | ||
|
a09de2302b |
35 changed files with 1872 additions and 1942 deletions
|
@ -1,6 +1,6 @@
|
|||
extern crate web_sys;
|
||||
use automerge as am;
|
||||
use automerge::{Change, ChangeHash, Prop, Value};
|
||||
use automerge::{Change, ChangeHash, ObjId, Prop, Value};
|
||||
use js_sys::{Array, Object, Reflect, Uint8Array};
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
|
@ -151,9 +151,9 @@ impl Automerge {
|
|||
pub fn keys(&mut self, obj: JsValue, heads: JsValue) -> Result<Array, JsValue> {
|
||||
let obj = self.import(obj)?;
|
||||
let result = if let Some(heads) = get_heads(heads) {
|
||||
self.0.keys_at(obj, &heads)
|
||||
self.0.keys_at(&obj, &heads)
|
||||
} else {
|
||||
self.0.keys(obj)
|
||||
self.0.keys(&obj)
|
||||
}
|
||||
.iter()
|
||||
.map(|s| JsValue::from_str(s))
|
||||
|
@ -164,9 +164,9 @@ impl Automerge {
|
|||
pub fn text(&mut self, obj: JsValue, heads: JsValue) -> Result<JsValue, JsValue> {
|
||||
let obj = self.import(obj)?;
|
||||
if let Some(heads) = get_heads(heads) {
|
||||
self.0.text_at(obj, &heads)
|
||||
self.0.text_at(&obj, &heads)
|
||||
} else {
|
||||
self.0.text(obj)
|
||||
self.0.text(&obj)
|
||||
}
|
||||
.map_err(to_js_err)
|
||||
.map(|t| t.into())
|
||||
|
@ -185,7 +185,7 @@ impl Automerge {
|
|||
let mut vals = vec![];
|
||||
if let Some(t) = text.as_string() {
|
||||
self.0
|
||||
.splice_text(obj, start, delete_count, &t)
|
||||
.splice_text(&obj, start, delete_count, &t)
|
||||
.map_err(to_js_err)?;
|
||||
} else {
|
||||
if let Ok(array) = text.dyn_into::<Array>() {
|
||||
|
@ -201,7 +201,7 @@ impl Automerge {
|
|||
}
|
||||
}
|
||||
self.0
|
||||
.splice(obj, start, delete_count, vals)
|
||||
.splice(&obj, start, delete_count, vals)
|
||||
.map_err(to_js_err)?;
|
||||
}
|
||||
Ok(())
|
||||
|
@ -223,9 +223,12 @@ impl Automerge {
|
|||
let value = self.import_value(value, datatype)?;
|
||||
let opid = self
|
||||
.0
|
||||
.insert(obj, index as usize, value)
|
||||
.insert(&obj, index as usize, value)
|
||||
.map_err(to_js_err)?;
|
||||
Ok(self.export(opid))
|
||||
match opid {
|
||||
Some(opid) => Ok(self.export(opid)),
|
||||
None => Ok(JsValue::null()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(
|
||||
|
@ -238,7 +241,7 @@ impl Automerge {
|
|||
let obj = self.import(obj)?;
|
||||
let prop = self.import_prop(prop)?;
|
||||
let value = self.import_value(value, datatype)?;
|
||||
let opid = self.0.set(obj, prop, value).map_err(to_js_err)?;
|
||||
let opid = self.0.set(&obj, prop, value).map_err(to_js_err)?;
|
||||
match opid {
|
||||
Some(opid) => Ok(self.export(opid)),
|
||||
None => Ok(JsValue::null()),
|
||||
|
@ -252,7 +255,7 @@ impl Automerge {
|
|||
.as_f64()
|
||||
.ok_or("inc needs a numberic value")
|
||||
.map_err(to_js_err)?;
|
||||
self.0.inc(obj, prop, value as i64).map_err(to_js_err)?;
|
||||
self.0.inc(&obj, prop, value as i64).map_err(to_js_err)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -263,9 +266,9 @@ impl Automerge {
|
|||
let heads = get_heads(heads);
|
||||
if let Ok(prop) = prop {
|
||||
let value = if let Some(h) = heads {
|
||||
self.0.value_at(obj, prop, &h)
|
||||
self.0.value_at(&obj, prop, &h)
|
||||
} else {
|
||||
self.0.value(obj, prop)
|
||||
self.0.value(&obj, prop)
|
||||
}
|
||||
.map_err(to_js_err)?;
|
||||
match value {
|
||||
|
@ -289,9 +292,9 @@ impl Automerge {
|
|||
let prop = to_prop(arg);
|
||||
if let Ok(prop) = prop {
|
||||
let values = if let Some(heads) = get_heads(heads) {
|
||||
self.0.values_at(obj, prop, &heads)
|
||||
self.0.values_at(&obj, prop, &heads)
|
||||
} else {
|
||||
self.0.values(obj, prop)
|
||||
self.0.values(&obj, prop)
|
||||
}
|
||||
.map_err(to_js_err)?;
|
||||
for value in values {
|
||||
|
@ -318,16 +321,16 @@ impl Automerge {
|
|||
pub fn length(&mut self, obj: JsValue, heads: JsValue) -> Result<JsValue, JsValue> {
|
||||
let obj = self.import(obj)?;
|
||||
if let Some(heads) = get_heads(heads) {
|
||||
Ok((self.0.length_at(obj, &heads) as f64).into())
|
||||
Ok((self.0.length_at(&obj, &heads) as f64).into())
|
||||
} else {
|
||||
Ok((self.0.length(obj) as f64).into())
|
||||
Ok((self.0.length(&obj) as f64).into())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn del(&mut self, obj: JsValue, prop: JsValue) -> Result<(), JsValue> {
|
||||
let obj = self.import(obj)?;
|
||||
let prop = to_prop(prop)?;
|
||||
self.0.del(obj, prop).map_err(to_js_err)?;
|
||||
self.0.del(&obj, prop).map_err(to_js_err)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -442,11 +445,11 @@ impl Automerge {
|
|||
}
|
||||
}
|
||||
|
||||
fn export<E: automerge::Exportable>(&self, val: E) -> JsValue {
|
||||
self.0.export(val).into()
|
||||
fn export(&self, val: ObjId) -> JsValue {
|
||||
val.to_string().into()
|
||||
}
|
||||
|
||||
fn import<I: automerge::Importable>(&self, id: JsValue) -> Result<I, JsValue> {
|
||||
fn import(&self, id: JsValue) -> Result<ObjId, JsValue> {
|
||||
let id_str = id
|
||||
.as_string()
|
||||
.ok_or("invalid opid/objid/elemid")
|
||||
|
|
|
@ -36,3 +36,4 @@ pretty_assertions = "1.0.0"
|
|||
proptest = { version = "^1.0.0", default-features = false, features = ["std"] }
|
||||
serde_json = { version = "^1.0.73", features=["float_roundtrip"], default-features=true }
|
||||
maplit = { version = "^1.0" }
|
||||
decorum = "0.3.1"
|
||||
|
|
1338
automerge/src/automerge.rs
Normal file
1338
automerge/src/automerge.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,3 +1,4 @@
|
|||
use crate::automerge::Transaction;
|
||||
use crate::columnar::{
|
||||
ChangeEncoder, ChangeIterator, ColumnEncoder, DepsIterator, DocChange, DocOp, DocOpEncoder,
|
||||
DocOpIterator, OperationIterator, COLUMN_TYPE_DEFLATE,
|
||||
|
@ -5,11 +6,11 @@ use crate::columnar::{
|
|||
use crate::decoding;
|
||||
use crate::decoding::{Decodable, InvalidChangeError};
|
||||
use crate::encoding::{Encodable, DEFLATE_MIN_SIZE};
|
||||
use crate::error::AutomergeError;
|
||||
use crate::indexed_cache::IndexedCache;
|
||||
use crate::legacy as amp;
|
||||
use crate::{
|
||||
ActorId, AutomergeError, ElemId, IndexedCache, Key, ObjId, Op, OpId, OpType, Transaction, HEAD,
|
||||
ROOT,
|
||||
};
|
||||
use crate::types;
|
||||
use crate::types::{ActorId, ElemId, Key, ObjId, Op, OpId, OpType};
|
||||
use core::ops::Range;
|
||||
use flate2::{
|
||||
bufread::{DeflateDecoder, DeflateEncoder},
|
||||
|
@ -417,7 +418,7 @@ fn increment_range_map(ranges: &mut HashMap<u32, Range<usize>>, len: usize) {
|
|||
}
|
||||
|
||||
fn export_objid(id: &ObjId, actors: &IndexedCache<ActorId>) -> amp::ObjectId {
|
||||
if id.0 == ROOT {
|
||||
if id == &ObjId::root() {
|
||||
amp::ObjectId::Root
|
||||
} else {
|
||||
export_opid(&id.0, actors).into()
|
||||
|
@ -425,7 +426,7 @@ fn export_objid(id: &ObjId, actors: &IndexedCache<ActorId>) -> amp::ObjectId {
|
|||
}
|
||||
|
||||
fn export_elemid(id: &ElemId, actors: &IndexedCache<ActorId>) -> amp::ElementId {
|
||||
if id == &HEAD {
|
||||
if id == &types::HEAD {
|
||||
amp::ElementId::Head
|
||||
} else {
|
||||
export_opid(&id.0, actors).into()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::OpId;
|
||||
use crate::types::OpId;
|
||||
use fxhash::FxBuildHasher;
|
||||
use std::cmp;
|
||||
use std::collections::HashMap;
|
||||
|
|
|
@ -11,8 +11,7 @@ use std::{
|
|||
str,
|
||||
};
|
||||
|
||||
use crate::ROOT;
|
||||
use crate::{ActorId, ElemId, Key, ObjId, ObjType, OpId, OpType, ScalarValue};
|
||||
use crate::types::{ActorId, ElemId, Key, ObjId, ObjType, Op, OpId, OpType, ScalarValue};
|
||||
|
||||
use crate::legacy as amp;
|
||||
use amp::SortedVec;
|
||||
|
@ -20,10 +19,10 @@ use flate2::bufread::DeflateDecoder;
|
|||
use smol_str::SmolStr;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::indexed_cache::IndexedCache;
|
||||
use crate::{
|
||||
decoding::{BooleanDecoder, Decodable, Decoder, DeltaDecoder, RleDecoder},
|
||||
encoding::{BooleanEncoder, ColData, DeltaEncoder, Encodable, RleEncoder},
|
||||
IndexedCache, Op,
|
||||
};
|
||||
|
||||
impl Encodable for Action {
|
||||
|
@ -846,7 +845,7 @@ impl ObjEncoder {
|
|||
|
||||
fn append(&mut self, obj: &ObjId, actors: &[usize]) {
|
||||
match obj.0 {
|
||||
ROOT => {
|
||||
OpId(ctr, _) if ctr == 0 => {
|
||||
self.actor.append_null();
|
||||
self.ctr.append_null();
|
||||
}
|
||||
|
@ -951,7 +950,7 @@ impl ChangeEncoder {
|
|||
index_by_hash.insert(hash, index);
|
||||
}
|
||||
self.actor
|
||||
.append_value(actors.lookup(change.actor_id.clone()).unwrap()); //actors.iter().position(|a| a == &change.actor_id).unwrap());
|
||||
.append_value(actors.lookup(&change.actor_id).unwrap()); //actors.iter().position(|a| a == &change.actor_id).unwrap());
|
||||
self.seq.append_value(change.seq);
|
||||
// FIXME iterops.count is crazy slow
|
||||
self.max_op
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::decoding;
|
||||
use crate::types::ScalarValue;
|
||||
use crate::value::DataType;
|
||||
use crate::ScalarValue;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
|
@ -17,6 +17,8 @@ pub enum AutomergeError {
|
|||
InvalidSeq(u64),
|
||||
#[error("index {0} is out of bounds")]
|
||||
InvalidIndex(usize),
|
||||
#[error("generic automerge error")]
|
||||
Fail,
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for AutomergeError {
|
||||
|
|
33
automerge/src/exid.rs
Normal file
33
automerge/src/exid.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
use crate::ActorId;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ExId {
|
||||
Root,
|
||||
Id(u64, ActorId, usize),
|
||||
}
|
||||
|
||||
impl PartialEq for ExId {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(ExId::Root, ExId::Root) => true,
|
||||
(ExId::Id(ctr1, actor1, _), ExId::Id(ctr2, actor2, _))
|
||||
if ctr1 == ctr2 && actor1 == actor2 =>
|
||||
{
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for ExId {}
|
||||
|
||||
impl fmt::Display for ExId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
ExId::Root => write!(f, "_root"),
|
||||
ExId::Id(ctr, actor, _) => write!(f, "{}@{}", ctr, actor),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -31,8 +31,8 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub fn lookup(&self, item: T) -> Option<usize> {
|
||||
self.lookup.get(&item).cloned()
|
||||
pub fn lookup(&self, item: &T) -> Option<usize> {
|
||||
self.lookup.get(item).cloned()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
|
|
|
@ -2,8 +2,8 @@ mod serde_impls;
|
|||
mod utility_impls;
|
||||
use std::iter::FromIterator;
|
||||
|
||||
pub(crate) use crate::types::{ActorId, ChangeHash, ObjType, OpType, ScalarValue};
|
||||
pub(crate) use crate::value::DataType;
|
||||
pub(crate) use crate::{ActorId, ChangeHash, ObjType, OpType, ScalarValue};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use smol_str::SmolStr;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use serde::{de, Deserialize, Deserializer};
|
||||
use smol_str::SmolStr;
|
||||
|
||||
use crate::ScalarValue;
|
||||
use crate::types::ScalarValue;
|
||||
|
||||
impl<'de> Deserialize<'de> for ScalarValue {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
|
|
1295
automerge/src/lib.rs
1295
automerge/src/lib.rs
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,7 @@
|
|||
use crate::indexed_cache::IndexedCache;
|
||||
use crate::op_tree::OpTreeInternal;
|
||||
use crate::query::TreeQuery;
|
||||
use crate::{ActorId, IndexedCache, Key, ObjId, Op, OpId};
|
||||
use crate::types::{ActorId, Key, ObjId, Op, OpId};
|
||||
use fxhash::FxBuildHasher;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::HashMap;
|
||||
|
|
|
@ -6,7 +6,7 @@ use std::{
|
|||
|
||||
pub(crate) use crate::op_set::OpSetMetadata;
|
||||
use crate::query::{Index, QueryResult, TreeQuery};
|
||||
use crate::{Op, OpId};
|
||||
use crate::types::{Op, OpId};
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
@ -628,7 +628,7 @@ struct CounterData {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::legacy as amp;
|
||||
use crate::{Op, OpId};
|
||||
use crate::types::{Op, OpId};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::op_tree::{OpSetMetadata, OpTreeNode};
|
||||
use crate::{Clock, ElemId, Op, OpId, OpType, ScalarValue};
|
||||
use crate::types::{Clock, ElemId, Op, OpId, OpType, ScalarValue};
|
||||
use fxhash::FxBuildHasher;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::error::AutomergeError;
|
||||
use crate::op_tree::OpTreeNode;
|
||||
use crate::query::{QueryResult, TreeQuery, VisWindow};
|
||||
use crate::{AutomergeError, ElemId, Key, Op, HEAD};
|
||||
use crate::types::{ElemId, Key, Op, HEAD};
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::op_tree::OpTreeNode;
|
||||
use crate::query::{QueryResult, TreeQuery, VisWindow};
|
||||
use crate::Key;
|
||||
use crate::types::Key;
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::query::{QueryResult, TreeQuery, VisWindow};
|
||||
use crate::{Clock, Key, Op};
|
||||
use crate::types::{Clock, Key, Op};
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
use crate::op_tree::OpTreeNode;
|
||||
use crate::query::{QueryResult, TreeQuery};
|
||||
use crate::ObjId;
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(crate) struct Len<const B: usize> {
|
||||
obj: ObjId,
|
||||
pub len: usize,
|
||||
}
|
||||
|
||||
impl<const B: usize> Len<B> {
|
||||
pub fn new(obj: ObjId) -> Self {
|
||||
Len { obj, len: 0 }
|
||||
pub fn new() -> Self {
|
||||
Len { len: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::query::{QueryResult, TreeQuery, VisWindow};
|
||||
use crate::{Clock, ElemId, Op};
|
||||
use crate::types::{Clock, ElemId, Op};
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::op_tree::{OpSetMetadata, OpTreeNode};
|
||||
use crate::query::{binary_search_by, is_visible, visible_op, QueryResult, TreeQuery};
|
||||
use crate::{ElemId, ObjId, Op};
|
||||
use crate::types::{ElemId, ObjId, Op};
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::query::{QueryResult, TreeQuery, VisWindow};
|
||||
use crate::{Clock, ElemId, Op};
|
||||
use crate::types::{Clock, ElemId, Op};
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::error::AutomergeError;
|
||||
use crate::op_tree::OpTreeNode;
|
||||
use crate::query::{QueryResult, TreeQuery, VisWindow};
|
||||
use crate::{AutomergeError, ElemId, Key, Op};
|
||||
use crate::types::{ElemId, Key, Op};
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::query::{QueryResult, TreeQuery, VisWindow};
|
||||
use crate::{Clock, ElemId, Op};
|
||||
use crate::types::{Clock, ElemId, Op};
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::op_tree::{OpSetMetadata, OpTreeNode};
|
||||
use crate::query::{binary_search_by, is_visible, visible_op, QueryResult, TreeQuery};
|
||||
use crate::{Key, ObjId, Op};
|
||||
use crate::types::{Key, ObjId, Op};
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::op_tree::{OpSetMetadata, OpTreeNode};
|
||||
use crate::query::{binary_search_by, QueryResult, TreeQuery, VisWindow};
|
||||
use crate::{Clock, Key, Op};
|
||||
use crate::types::{Clock, Key, Op};
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::op_tree::{OpSetMetadata, OpTreeNode};
|
||||
use crate::query::{binary_search_by, QueryResult, TreeQuery};
|
||||
use crate::{Key, Op, HEAD};
|
||||
use crate::types::{Key, Op, HEAD};
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt::Debug;
|
||||
|
||||
|
|
|
@ -6,9 +6,10 @@ use std::{
|
|||
io::Write,
|
||||
};
|
||||
|
||||
use crate::types::Patch;
|
||||
use crate::{
|
||||
decoding, decoding::Decoder, encoding, encoding::Encodable, Automerge, AutomergeError, Change,
|
||||
ChangeHash, Patch,
|
||||
ChangeHash,
|
||||
};
|
||||
|
||||
mod bloom;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use crate::error;
|
||||
use crate::legacy as amp;
|
||||
use crate::ScalarValue;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cmp::Eq;
|
||||
use std::convert::TryFrom;
|
||||
|
@ -9,8 +8,11 @@ use std::fmt;
|
|||
use std::str::FromStr;
|
||||
use tinyvec::{ArrayVec, TinyVec};
|
||||
|
||||
pub(crate) use crate::clock::Clock;
|
||||
pub(crate) use crate::value::{ScalarValue, Value};
|
||||
|
||||
pub(crate) const HEAD: ElemId = ElemId(OpId(0, 0));
|
||||
pub const ROOT: OpId = OpId(0, 0);
|
||||
pub(crate) const ROOT: OpId = OpId(0, 0);
|
||||
|
||||
const ROOT_STR: &str = "_root";
|
||||
const HEAD_STR: &str = "_head";
|
||||
|
@ -161,23 +163,16 @@ pub enum OpType {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Export {
|
||||
pub(crate) enum Export {
|
||||
Id(OpId),
|
||||
Special(String),
|
||||
Prop(usize),
|
||||
}
|
||||
|
||||
pub trait Exportable {
|
||||
pub(crate) trait Exportable {
|
||||
fn export(&self) -> Export;
|
||||
}
|
||||
|
||||
pub trait Importable {
|
||||
fn wrap(id: OpId) -> Self;
|
||||
fn from(s: &str) -> Option<Self>
|
||||
where
|
||||
Self: std::marker::Sized;
|
||||
}
|
||||
|
||||
impl OpId {
|
||||
#[inline]
|
||||
pub fn counter(&self) -> u64 {
|
||||
|
@ -234,45 +229,6 @@ impl Exportable for Key {
|
|||
}
|
||||
}
|
||||
|
||||
impl Importable for ObjId {
|
||||
fn wrap(id: OpId) -> Self {
|
||||
ObjId(id)
|
||||
}
|
||||
fn from(s: &str) -> Option<Self> {
|
||||
if s == ROOT_STR {
|
||||
Some(ROOT.into())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Importable for OpId {
|
||||
fn wrap(id: OpId) -> Self {
|
||||
id
|
||||
}
|
||||
fn from(s: &str) -> Option<Self> {
|
||||
if s == ROOT_STR {
|
||||
Some(ROOT)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Importable for ElemId {
|
||||
fn wrap(id: OpId) -> Self {
|
||||
ElemId(id)
|
||||
}
|
||||
fn from(s: &str) -> Option<Self> {
|
||||
if s == HEAD_STR {
|
||||
Some(HEAD)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OpId> for ObjId {
|
||||
fn from(o: OpId) -> Self {
|
||||
ObjId(o)
|
||||
|
@ -352,11 +308,17 @@ impl Key {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialOrd, Ord, Eq, PartialEq, Copy, Hash, Default)]
|
||||
pub struct OpId(pub u64, pub usize);
|
||||
pub(crate) struct OpId(pub u64, pub usize);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialOrd, Eq, PartialEq, Ord, Hash, Default)]
|
||||
pub(crate) struct ObjId(pub OpId);
|
||||
|
||||
impl ObjId {
|
||||
pub fn root() -> Self {
|
||||
ObjId(OpId(0, 0))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialOrd, Eq, PartialEq, Ord, Hash, Default)]
|
||||
pub(crate) struct ElemId(pub OpId);
|
||||
|
||||
|
@ -374,7 +336,11 @@ pub(crate) struct Op {
|
|||
|
||||
impl Op {
|
||||
pub fn is_del(&self) -> bool {
|
||||
matches!(self.action, OpType::Del)
|
||||
matches!(&self.action, OpType::Del)
|
||||
}
|
||||
|
||||
pub fn is_noop(&self, action: &OpType) -> bool {
|
||||
matches!((&self.action, action), (OpType::Set(n), OpType::Set(m)) if n == m)
|
||||
}
|
||||
|
||||
pub fn overwrites(&self, other: &Op) -> bool {
|
||||
|
@ -389,6 +355,14 @@ impl Op {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn value(&self) -> Value {
|
||||
match &self.action {
|
||||
OpType::Make(obj_type) => Value::Object(*obj_type),
|
||||
OpType::Set(scalar) => Value::Scalar(scalar.clone()),
|
||||
_ => panic!("cant convert op into a value - {:?}", self),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn dump(&self) -> String {
|
||||
match &self.action {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::{error, ObjType, Op, OpId, OpType};
|
||||
use crate::error;
|
||||
use crate::types::{ObjType, Op, OpId, OpType};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use smol_str::SmolStr;
|
||||
use std::convert::TryFrom;
|
||||
|
|
|
@ -24,7 +24,7 @@ pub(crate) struct Node<'a, const B: usize> {
|
|||
|
||||
#[derive(Clone)]
|
||||
pub(crate) enum NodeType<'a, const B: usize> {
|
||||
ObjRoot(crate::ObjId),
|
||||
ObjRoot(crate::types::ObjId),
|
||||
ObjTreeNode(&'a crate::op_tree::OpTreeNode<B>),
|
||||
}
|
||||
|
||||
|
@ -225,7 +225,7 @@ impl OpTableRow {
|
|||
|
||||
impl OpTableRow {
|
||||
fn create(
|
||||
op: &super::Op,
|
||||
op: &super::types::Op,
|
||||
metadata: &crate::op_set::OpSetMetadata,
|
||||
actor_shorthands: &HashMap<usize, String>,
|
||||
) -> Self {
|
||||
|
@ -236,8 +236,8 @@ impl OpTableRow {
|
|||
crate::OpType::Inc(v) => format!("inc {}", v),
|
||||
};
|
||||
let prop = match op.key {
|
||||
crate::Key::Map(k) => metadata.props[k].clone(),
|
||||
crate::Key::Seq(e) => print_opid(&e.0, actor_shorthands),
|
||||
crate::types::Key::Map(k) => metadata.props[k].clone(),
|
||||
crate::types::Key::Seq(e) => print_opid(&e.0, actor_shorthands),
|
||||
};
|
||||
let succ = op
|
||||
.succ
|
||||
|
@ -254,6 +254,6 @@ impl OpTableRow {
|
|||
}
|
||||
}
|
||||
|
||||
fn print_opid(opid: &crate::OpId, actor_shorthands: &HashMap<usize, String>) -> String {
|
||||
fn print_opid(opid: &crate::types::OpId, actor_shorthands: &HashMap<usize, String>) -> String {
|
||||
format!("{}@{}", opid.counter(), actor_shorthands[&opid.actor()])
|
||||
}
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
use std::{collections::HashMap, convert::TryInto, hash::Hash};
|
||||
use std::{
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
convert::TryInto,
|
||||
hash::Hash,
|
||||
};
|
||||
|
||||
use serde::ser::{SerializeMap, SerializeSeq};
|
||||
|
||||
|
@ -42,7 +46,7 @@ pub fn sorted_actors() -> (automerge::ActorId, automerge::ActorId) {
|
|||
/// map!{
|
||||
/// "todos" => {
|
||||
/// todos => list![
|
||||
/// { todo => map!{ title = "water plants" } }
|
||||
/// { map!{ title = "water plants" } }
|
||||
/// ]
|
||||
/// }
|
||||
/// }
|
||||
|
@ -50,9 +54,9 @@ pub fn sorted_actors() -> (automerge::ActorId, automerge::ActorId) {
|
|||
///
|
||||
/// ```
|
||||
///
|
||||
/// This might look more complicated than you were expecting. Why are there OpIds (`todos`, `todo`,
|
||||
/// `title`) in there? Well the `RealizedObject` contains all the changes in the document tagged by
|
||||
/// OpId. This makes it easy to test for conflicts:
|
||||
/// This might look more complicated than you were expecting. Why is the first element in the list
|
||||
/// wrapped in braces? Because every property in an automerge document can have multiple
|
||||
/// conflicting values we must capture all of these.
|
||||
///
|
||||
/// ```rust
|
||||
/// let mut doc1 = automerge::Automerge::new();
|
||||
|
@ -70,33 +74,20 @@ pub fn sorted_actors() -> (automerge::ActorId, automerge::ActorId) {
|
|||
/// }
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// ## Translating OpIds
|
||||
///
|
||||
/// One thing you may have noticed in the example above is the `op2.translate(&doc2)` call. What is
|
||||
/// that doing there? Well, the problem is that automerge OpIDs (in the current API) are specific
|
||||
/// to a document. Using an opid from one document in a different document will not work. Therefore
|
||||
/// this module defines an `OpIdExt` trait with a `translate` method on it. This method takes a
|
||||
/// document and converts the opid into something which knows how to be compared with opids from
|
||||
/// another document by using the document you pass to `translate`. Again, all you really need to
|
||||
/// know is that when constructing a document for comparison you should call `translate(fromdoc)`
|
||||
/// on opids which come from a document other than the one you pass to `assert_doc`.
|
||||
#[macro_export]
|
||||
macro_rules! assert_doc {
|
||||
($doc: expr, $expected: expr) => {{
|
||||
use $crate::helpers::{realize, ExportableOpId};
|
||||
use $crate::helpers::realize;
|
||||
let realized = realize($doc);
|
||||
let to_export: RealizedObject<ExportableOpId<'_>> = $expected.into();
|
||||
let exported = to_export.export($doc);
|
||||
if realized != exported {
|
||||
let expected_obj = $expected.into();
|
||||
if realized != expected_obj {
|
||||
let serde_right = serde_json::to_string_pretty(&realized).unwrap();
|
||||
let serde_left = serde_json::to_string_pretty(&exported).unwrap();
|
||||
let serde_left = serde_json::to_string_pretty(&expected_obj).unwrap();
|
||||
panic!(
|
||||
"documents didn't match\n expected\n{}\n got\n{}",
|
||||
&serde_left, &serde_right
|
||||
);
|
||||
}
|
||||
pretty_assertions::assert_eq!(realized, exported);
|
||||
}};
|
||||
}
|
||||
|
||||
|
@ -105,63 +96,52 @@ macro_rules! assert_doc {
|
|||
#[macro_export]
|
||||
macro_rules! assert_obj {
|
||||
($doc: expr, $obj_id: expr, $prop: expr, $expected: expr) => {{
|
||||
use $crate::helpers::{realize_prop, ExportableOpId};
|
||||
use $crate::helpers::realize_prop;
|
||||
let realized = realize_prop($doc, $obj_id, $prop);
|
||||
let to_export: RealizedObject<ExportableOpId<'_>> = $expected.into();
|
||||
let exported = to_export.export($doc);
|
||||
if realized != exported {
|
||||
let expected_obj = $expected.into();
|
||||
if realized != expected_obj {
|
||||
let serde_right = serde_json::to_string_pretty(&realized).unwrap();
|
||||
let serde_left = serde_json::to_string_pretty(&exported).unwrap();
|
||||
let serde_left = serde_json::to_string_pretty(&expected_obj).unwrap();
|
||||
panic!(
|
||||
"documents didn't match\n expected\n{}\n got\n{}",
|
||||
&serde_left, &serde_right
|
||||
);
|
||||
}
|
||||
pretty_assertions::assert_eq!(realized, exported);
|
||||
}};
|
||||
}
|
||||
|
||||
/// Construct `RealizedObject::Map`. This macro takes a nested set of curl braces. The outer set is
|
||||
/// the keys of the map, the inner set is the opid tagged values:
|
||||
/// the keys of the map, the inner set is the set of values for that key:
|
||||
///
|
||||
/// ```
|
||||
/// map!{
|
||||
/// "key" => {
|
||||
/// opid1 => "value1",
|
||||
/// opid2 => "value2",
|
||||
/// "value1",
|
||||
/// "value2",
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The map above would represent a map with a conflict on the "key" property. The values can be
|
||||
/// anything which implements `Into<RealizedObject<ExportableOpId<'_>>`. Including nested calls to
|
||||
/// `map!` or `list!`.
|
||||
/// anything which implements `Into<RealizedObject>`. Including nested calls to `map!` or `list!`.
|
||||
#[macro_export]
|
||||
macro_rules! map {
|
||||
(@single $($x:tt)*) => (());
|
||||
(@count $($rest:expr),*) => (<[()]>::len(&[$(map!(@single $rest)),*]));
|
||||
|
||||
(@inner { $($opid:expr => $value:expr,)+ }) => { map!(@inner { $($opid => $value),+ }) };
|
||||
(@inner { $($opid:expr => $value:expr),* }) => {
|
||||
(@inner { $($value:expr,)+ }) => { map!(@inner { $($value),+ }) };
|
||||
(@inner { $($value:expr),* }) => {
|
||||
{
|
||||
use std::collections::HashMap;
|
||||
let mut inner: HashMap<ExportableOpId<'_>, RealizedObject<ExportableOpId<'_>>> = HashMap::new();
|
||||
use std::collections::BTreeSet;
|
||||
let mut inner: BTreeSet<RealizedObject> = BTreeSet::new();
|
||||
$(
|
||||
let _ = inner.insert($opid.into(), $value.into());
|
||||
let _ = inner.insert($value.into());
|
||||
)*
|
||||
inner
|
||||
}
|
||||
};
|
||||
//(&inner $map:expr, $opid:expr => $value:expr, $($tail:tt),*) => {
|
||||
//$map.insert($opid.into(), $value.into());
|
||||
//}
|
||||
($($key:expr => $inner:tt,)+) => { map!($($key => $inner),+) };
|
||||
($($key:expr => $inner:tt),*) => {
|
||||
{
|
||||
use std::collections::HashMap;
|
||||
use crate::helpers::ExportableOpId;
|
||||
let _cap = map!(@count $($key),*);
|
||||
let mut _map: HashMap<String, HashMap<ExportableOpId<'_>, RealizedObject<ExportableOpId<'_>>>> = ::std::collections::HashMap::with_capacity(_cap);
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
let mut _map: BTreeMap<String, BTreeSet<RealizedObject>> = ::std::collections::BTreeMap::new();
|
||||
$(
|
||||
let inner = map!(@inner $inner);
|
||||
let _ = _map.insert($key.to_string(), inner);
|
||||
|
@ -171,32 +151,32 @@ macro_rules! map {
|
|||
}
|
||||
}
|
||||
|
||||
/// Construct `RealizedObject::Sequence`. This macro represents a sequence of opid tagged values
|
||||
/// Construct `RealizedObject::Sequence`. This macro represents a sequence of values
|
||||
///
|
||||
/// ```
|
||||
/// list![
|
||||
/// {
|
||||
/// opid1 => "value1",
|
||||
/// opid2 => "value2",
|
||||
/// "value1",
|
||||
/// "value2",
|
||||
/// }
|
||||
/// ]
|
||||
/// ```
|
||||
///
|
||||
/// The list above would represent a list with a conflict on the 0 index. The values can be
|
||||
/// anything which implements `Into<RealizedObject<ExportableOpId<'_>>` including nested calls to
|
||||
/// anything which implements `Into<RealizedObject>` including nested calls to
|
||||
/// `map!` or `list!`.
|
||||
#[macro_export]
|
||||
macro_rules! list {
|
||||
(@single $($x:tt)*) => (());
|
||||
(@count $($rest:tt),*) => (<[()]>::len(&[$(list!(@single $rest)),*]));
|
||||
|
||||
(@inner { $($opid:expr => $value:expr,)+ }) => { list!(@inner { $($opid => $value),+ }) };
|
||||
(@inner { $($opid:expr => $value:expr),* }) => {
|
||||
(@inner { $($value:expr,)+ }) => { list!(@inner { $($value),+ }) };
|
||||
(@inner { $($value:expr),* }) => {
|
||||
{
|
||||
use std::collections::HashMap;
|
||||
let mut inner: HashMap<ExportableOpId<'_>, RealizedObject<ExportableOpId<'_>>> = HashMap::new();
|
||||
use std::collections::BTreeSet;
|
||||
let mut inner: BTreeSet<RealizedObject> = BTreeSet::new();
|
||||
$(
|
||||
let _ = inner.insert($opid.into(), $value.into());
|
||||
let _ = inner.insert($value.into());
|
||||
)*
|
||||
inner
|
||||
}
|
||||
|
@ -204,9 +184,8 @@ macro_rules! list {
|
|||
($($inner:tt,)+) => { list!($($inner),+) };
|
||||
($($inner:tt),*) => {
|
||||
{
|
||||
use crate::helpers::ExportableOpId;
|
||||
let _cap = list!(@count $($inner),*);
|
||||
let mut _list: Vec<HashMap<ExportableOpId<'_>, RealizedObject<ExportableOpId<'_>>>> = Vec::new();
|
||||
let mut _list: Vec<BTreeSet<RealizedObject>> = Vec::new();
|
||||
$(
|
||||
//println!("{}", stringify!($inner));
|
||||
let inner = list!(@inner $inner);
|
||||
|
@ -217,26 +196,6 @@ macro_rules! list {
|
|||
}
|
||||
}
|
||||
|
||||
/// Translate an op ID produced by one document to an op ID which can be understood by
|
||||
/// another
|
||||
///
|
||||
/// The current API of automerge exposes OpIds of the form (u64, usize) where the first component
|
||||
/// is the counter of an actors lamport timestamp and the second component is the index into an
|
||||
/// array of actor IDs stored by the document where the opid was generated. Obviously this is not
|
||||
/// portable between documents as the index of the actor array is unlikely to match between two
|
||||
/// documents. This function translates between the two representations.
|
||||
///
|
||||
/// At some point we will probably change the API to not be document specific but this function
|
||||
/// allows us to write tests first.
|
||||
pub fn translate_obj_id(
|
||||
from: &automerge::Automerge,
|
||||
to: &automerge::Automerge,
|
||||
id: automerge::OpId,
|
||||
) -> automerge::OpId {
|
||||
let exported = from.export(id);
|
||||
to.import(&exported).unwrap()
|
||||
}
|
||||
|
||||
pub fn mk_counter(value: i64) -> automerge::ScalarValue {
|
||||
automerge::ScalarValue::Counter(value)
|
||||
}
|
||||
|
@ -252,14 +211,72 @@ impl std::fmt::Display for ExportedOpId {
|
|||
|
||||
/// A `RealizedObject` is a representation of all the current values in a document - including
|
||||
/// conflicts.
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum RealizedObject<Oid: PartialEq + Eq + Hash> {
|
||||
Map(HashMap<String, HashMap<Oid, RealizedObject<Oid>>>),
|
||||
Sequence(Vec<HashMap<Oid, RealizedObject<Oid>>>),
|
||||
Value(automerge::ScalarValue),
|
||||
#[derive(PartialEq, PartialOrd, Ord, Eq, Hash, Debug)]
|
||||
pub enum RealizedObject {
|
||||
Map(BTreeMap<String, BTreeSet<RealizedObject>>),
|
||||
Sequence(Vec<BTreeSet<RealizedObject>>),
|
||||
Value(OrdScalarValue),
|
||||
}
|
||||
|
||||
impl serde::Serialize for RealizedObject<ExportedOpId> {
|
||||
// A copy of automerge::ScalarValue which uses decorum::Total for floating point values. This makes the type
|
||||
// orderable, which is useful when we want to compare conflicting values of a register in an
|
||||
// automerge document.
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
|
||||
pub enum OrdScalarValue {
|
||||
Bytes(Vec<u8>),
|
||||
Str(smol_str::SmolStr),
|
||||
Int(i64),
|
||||
Uint(u64),
|
||||
F64(decorum::Total<f64>),
|
||||
Counter(i64),
|
||||
Timestamp(i64),
|
||||
Boolean(bool),
|
||||
Null,
|
||||
}
|
||||
|
||||
impl From<automerge::ScalarValue> for OrdScalarValue {
|
||||
fn from(v: automerge::ScalarValue) -> Self {
|
||||
match v {
|
||||
automerge::ScalarValue::Bytes(v) => OrdScalarValue::Bytes(v),
|
||||
automerge::ScalarValue::Str(v) => OrdScalarValue::Str(v),
|
||||
automerge::ScalarValue::Int(v) => OrdScalarValue::Int(v),
|
||||
automerge::ScalarValue::Uint(v) => OrdScalarValue::Uint(v),
|
||||
automerge::ScalarValue::F64(v) => OrdScalarValue::F64(decorum::Total::from(v)),
|
||||
automerge::ScalarValue::Counter(v) => OrdScalarValue::Counter(v),
|
||||
automerge::ScalarValue::Timestamp(v) => OrdScalarValue::Timestamp(v),
|
||||
automerge::ScalarValue::Boolean(v) => OrdScalarValue::Boolean(v),
|
||||
automerge::ScalarValue::Null => OrdScalarValue::Null,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&OrdScalarValue> for automerge::ScalarValue {
|
||||
fn from(v: &OrdScalarValue) -> Self {
|
||||
match v {
|
||||
OrdScalarValue::Bytes(v) => automerge::ScalarValue::Bytes(v.clone()),
|
||||
OrdScalarValue::Str(v) => automerge::ScalarValue::Str(v.clone()),
|
||||
OrdScalarValue::Int(v) => automerge::ScalarValue::Int(*v),
|
||||
OrdScalarValue::Uint(v) => automerge::ScalarValue::Uint(*v),
|
||||
OrdScalarValue::F64(v) => automerge::ScalarValue::F64(v.into_inner()),
|
||||
OrdScalarValue::Counter(v) => automerge::ScalarValue::Counter(*v),
|
||||
OrdScalarValue::Timestamp(v) => automerge::ScalarValue::Timestamp(*v),
|
||||
OrdScalarValue::Boolean(v) => automerge::ScalarValue::Boolean(*v),
|
||||
OrdScalarValue::Null => automerge::ScalarValue::Null,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Serialize for OrdScalarValue {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let s = automerge::ScalarValue::from(self);
|
||||
s.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Serialize for RealizedObject {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
|
@ -267,23 +284,17 @@ impl serde::Serialize for RealizedObject<ExportedOpId> {
|
|||
match self {
|
||||
Self::Map(kvs) => {
|
||||
let mut map_ser = serializer.serialize_map(Some(kvs.len()))?;
|
||||
for (k, kvs) in kvs {
|
||||
let kvs_serded = kvs
|
||||
.iter()
|
||||
.map(|(opid, value)| (opid.to_string(), value))
|
||||
.collect::<HashMap<String, &RealizedObject<ExportedOpId>>>();
|
||||
map_ser.serialize_entry(k, &kvs_serded)?;
|
||||
for (k, vs) in kvs {
|
||||
let vs_serded = vs.iter().collect::<Vec<&RealizedObject>>();
|
||||
map_ser.serialize_entry(k, &vs_serded)?;
|
||||
}
|
||||
map_ser.end()
|
||||
}
|
||||
Self::Sequence(elems) => {
|
||||
let mut list_ser = serializer.serialize_seq(Some(elems.len()))?;
|
||||
for elem in elems {
|
||||
let kvs_serded = elem
|
||||
.iter()
|
||||
.map(|(opid, value)| (opid.to_string(), value))
|
||||
.collect::<HashMap<String, &RealizedObject<ExportedOpId>>>();
|
||||
list_ser.serialize_element(&kvs_serded)?;
|
||||
let vs_serded = elem.iter().collect::<Vec<&RealizedObject>>();
|
||||
list_ser.serialize_element(&vs_serded)?;
|
||||
}
|
||||
list_ser.end()
|
||||
}
|
||||
|
@ -292,30 +303,30 @@ impl serde::Serialize for RealizedObject<ExportedOpId> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn realize(doc: &automerge::Automerge) -> RealizedObject<ExportedOpId> {
|
||||
realize_obj(doc, automerge::ROOT, automerge::ObjType::Map)
|
||||
pub fn realize(doc: &automerge::Automerge) -> RealizedObject {
|
||||
realize_obj(doc, &automerge::ROOT, automerge::ObjType::Map)
|
||||
}
|
||||
|
||||
pub fn realize_prop<P: Into<automerge::Prop>>(
|
||||
doc: &automerge::Automerge,
|
||||
obj_id: automerge::OpId,
|
||||
obj_id: &automerge::ObjId,
|
||||
prop: P,
|
||||
) -> RealizedObject<ExportedOpId> {
|
||||
) -> RealizedObject {
|
||||
let (val, obj_id) = doc.value(obj_id, prop).unwrap().unwrap();
|
||||
match val {
|
||||
automerge::Value::Object(obj_type) => realize_obj(doc, obj_id, obj_type),
|
||||
automerge::Value::Scalar(v) => RealizedObject::Value(v),
|
||||
automerge::Value::Object(obj_type) => realize_obj(doc, &obj_id, obj_type),
|
||||
automerge::Value::Scalar(v) => RealizedObject::Value(OrdScalarValue::from(v)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn realize_obj(
|
||||
doc: &automerge::Automerge,
|
||||
obj_id: automerge::OpId,
|
||||
obj_id: &automerge::ObjId,
|
||||
objtype: automerge::ObjType,
|
||||
) -> RealizedObject<ExportedOpId> {
|
||||
) -> RealizedObject {
|
||||
match objtype {
|
||||
automerge::ObjType::Map | automerge::ObjType::Table => {
|
||||
let mut result = HashMap::new();
|
||||
let mut result = BTreeMap::new();
|
||||
for key in doc.keys(obj_id) {
|
||||
result.insert(key.clone(), realize_values(doc, obj_id, key));
|
||||
}
|
||||
|
@ -334,166 +345,63 @@ pub fn realize_obj(
|
|||
|
||||
fn realize_values<K: Into<automerge::Prop>>(
|
||||
doc: &automerge::Automerge,
|
||||
obj_id: automerge::OpId,
|
||||
obj_id: &automerge::ObjId,
|
||||
key: K,
|
||||
) -> HashMap<ExportedOpId, RealizedObject<ExportedOpId>> {
|
||||
let mut values_by_opid = HashMap::new();
|
||||
for (value, opid) in doc.values(obj_id, key).unwrap() {
|
||||
) -> BTreeSet<RealizedObject> {
|
||||
let mut values = BTreeSet::new();
|
||||
for (value, objid) in doc.values(obj_id, key).unwrap() {
|
||||
let realized = match value {
|
||||
automerge::Value::Object(objtype) => realize_obj(doc, opid, objtype),
|
||||
automerge::Value::Scalar(v) => RealizedObject::Value(v),
|
||||
automerge::Value::Object(objtype) => realize_obj(doc, &objid, objtype),
|
||||
automerge::Value::Scalar(v) => RealizedObject::Value(OrdScalarValue::from(v)),
|
||||
};
|
||||
let exported_opid = ExportedOpId(doc.export(opid));
|
||||
values_by_opid.insert(exported_opid, realized);
|
||||
values.insert(realized);
|
||||
}
|
||||
values_by_opid
|
||||
values
|
||||
}
|
||||
|
||||
impl<'a> RealizedObject<ExportableOpId<'a>> {
|
||||
pub fn export(self, doc: &automerge::Automerge) -> RealizedObject<ExportedOpId> {
|
||||
match self {
|
||||
Self::Map(kvs) => RealizedObject::Map(
|
||||
kvs.into_iter()
|
||||
.map(|(k, v)| {
|
||||
(
|
||||
k,
|
||||
v.into_iter()
|
||||
.map(|(k, v)| (k.export(doc), v.export(doc)))
|
||||
.collect(),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
),
|
||||
Self::Sequence(values) => RealizedObject::Sequence(
|
||||
values
|
||||
.into_iter()
|
||||
.map(|v| {
|
||||
v.into_iter()
|
||||
.map(|(k, v)| (k.export(doc), v.export(doc)))
|
||||
.collect()
|
||||
})
|
||||
.collect(),
|
||||
),
|
||||
Self::Value(v) => RealizedObject::Value(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, O: Into<ExportableOpId<'a>>, I: Into<RealizedObject<ExportableOpId<'a>>>>
|
||||
From<HashMap<&str, HashMap<O, I>>> for RealizedObject<ExportableOpId<'a>>
|
||||
{
|
||||
fn from(values: HashMap<&str, HashMap<O, I>>) -> Self {
|
||||
impl<I: Into<RealizedObject>> From<BTreeMap<&str, BTreeSet<I>>> for RealizedObject {
|
||||
fn from(values: BTreeMap<&str, BTreeSet<I>>) -> Self {
|
||||
let intoed = values
|
||||
.into_iter()
|
||||
.map(|(k, v)| {
|
||||
(
|
||||
k.to_string(),
|
||||
v.into_iter().map(|(k, v)| (k.into(), v.into())).collect(),
|
||||
)
|
||||
})
|
||||
.map(|(k, v)| (k.to_string(), v.into_iter().map(|v| v.into()).collect()))
|
||||
.collect();
|
||||
RealizedObject::Map(intoed)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, O: Into<ExportableOpId<'a>>, I: Into<RealizedObject<ExportableOpId<'a>>>>
|
||||
From<Vec<HashMap<O, I>>> for RealizedObject<ExportableOpId<'a>>
|
||||
{
|
||||
fn from(values: Vec<HashMap<O, I>>) -> Self {
|
||||
impl<I: Into<RealizedObject>> From<Vec<BTreeSet<I>>> for RealizedObject {
|
||||
fn from(values: Vec<BTreeSet<I>>) -> Self {
|
||||
RealizedObject::Sequence(
|
||||
values
|
||||
.into_iter()
|
||||
.map(|v| v.into_iter().map(|(k, v)| (k.into(), v.into())).collect())
|
||||
.map(|v| v.into_iter().map(|v| v.into()).collect())
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for RealizedObject<ExportableOpId<'_>> {
|
||||
impl From<bool> for RealizedObject {
|
||||
fn from(b: bool) -> Self {
|
||||
RealizedObject::Value(b.into())
|
||||
RealizedObject::Value(OrdScalarValue::Boolean(b))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<usize> for RealizedObject<ExportableOpId<'_>> {
|
||||
impl From<usize> for RealizedObject {
|
||||
fn from(u: usize) -> Self {
|
||||
let v = u.try_into().unwrap();
|
||||
RealizedObject::Value(automerge::ScalarValue::Int(v))
|
||||
RealizedObject::Value(OrdScalarValue::Int(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<automerge::ScalarValue> for RealizedObject<ExportableOpId<'_>> {
|
||||
impl From<automerge::ScalarValue> for RealizedObject {
|
||||
fn from(s: automerge::ScalarValue) -> Self {
|
||||
RealizedObject::Value(s)
|
||||
RealizedObject::Value(OrdScalarValue::from(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for RealizedObject<ExportableOpId<'_>> {
|
||||
impl From<&str> for RealizedObject {
|
||||
fn from(s: &str) -> Self {
|
||||
RealizedObject::Value(automerge::ScalarValue::Str(s.into()))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Hash)]
|
||||
pub enum ExportableOpId<'a> {
|
||||
Native(automerge::OpId),
|
||||
Translate(Translate<'a>),
|
||||
}
|
||||
|
||||
impl<'a> ExportableOpId<'a> {
|
||||
fn export(self, doc: &automerge::Automerge) -> ExportedOpId {
|
||||
let oid = match self {
|
||||
Self::Native(oid) => oid,
|
||||
Self::Translate(Translate { from, opid }) => translate_obj_id(from, doc, opid),
|
||||
};
|
||||
ExportedOpId(doc.export(oid))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Translate<'a> {
|
||||
from: &'a automerge::Automerge,
|
||||
opid: automerge::OpId,
|
||||
}
|
||||
|
||||
impl<'a> PartialEq for Translate<'a> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.from.maybe_get_actor().unwrap() == other.from.maybe_get_actor().unwrap()
|
||||
&& self.opid == other.opid
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Eq for Translate<'a> {}
|
||||
|
||||
impl<'a> Hash for Translate<'a> {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.from.maybe_get_actor().unwrap().hash(state);
|
||||
self.opid.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait OpIdExt {
|
||||
fn native(self) -> ExportableOpId<'static>;
|
||||
fn translate(self, doc: &automerge::Automerge) -> ExportableOpId<'_>;
|
||||
}
|
||||
|
||||
impl OpIdExt for automerge::OpId {
|
||||
/// Use this opid directly when exporting
|
||||
fn native(self) -> ExportableOpId<'static> {
|
||||
ExportableOpId::Native(self)
|
||||
}
|
||||
|
||||
/// Translate this OpID from `doc` when exporting
|
||||
fn translate(self, doc: &automerge::Automerge) -> ExportableOpId<'_> {
|
||||
ExportableOpId::Translate(Translate {
|
||||
from: doc,
|
||||
opid: self,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<automerge::OpId> for ExportableOpId<'_> {
|
||||
fn from(oid: automerge::OpId) -> Self {
|
||||
ExportableOpId::Native(oid)
|
||||
RealizedObject::Value(OrdScalarValue::Str(smol_str::SmolStr::from(s)))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -5,9 +5,9 @@ use std::fs;
|
|||
fn replay_trace(commands: Vec<(usize, usize, Vec<Value>)>) -> Automerge {
|
||||
let mut doc = Automerge::new();
|
||||
|
||||
let text = doc.set(ROOT, "text", Value::text()).unwrap().unwrap();
|
||||
let text = doc.set(&ROOT, "text", Value::text()).unwrap().unwrap();
|
||||
for (pos, del, vals) in commands {
|
||||
doc.splice(text, pos, del, vals).unwrap();
|
||||
doc.splice(&text, pos, del, vals).unwrap();
|
||||
}
|
||||
doc.commit(None, None);
|
||||
doc
|
||||
|
|
|
@ -19,12 +19,12 @@ fn main() -> Result<(), AutomergeError> {
|
|||
let mut doc = Automerge::new();
|
||||
|
||||
let now = Instant::now();
|
||||
let text = doc.set(ROOT, "text", Value::text()).unwrap().unwrap();
|
||||
let text = doc.set( &ROOT, "text", Value::text()).unwrap().unwrap();
|
||||
for (i, (pos, del, vals)) in commands.into_iter().enumerate() {
|
||||
if i % 1000 == 0 {
|
||||
println!("Processed {} edits in {} ms", i, now.elapsed().as_millis());
|
||||
}
|
||||
doc.splice(text, pos, del, vals)?;
|
||||
doc.splice(&text, pos, del, vals)?;
|
||||
}
|
||||
let _ = doc.save();
|
||||
println!("Done in {} ms", now.elapsed().as_millis());
|
||||
|
|
Loading…
Reference in a new issue