Merge pull request from automerge/bin_change

Refactor Change to Be Binary Encoded
This commit is contained in:
Orion Henry 2020-05-23 08:57:42 -07:00 committed by GitHub
commit 13142048b1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 1407 additions and 1421 deletions

View file

@ -1,10 +1,11 @@
//#![feature(set_stdio)]
use automerge_backend::{AutomergeError, Backend};
use automerge_protocol::{ActorID, ChangeHash, ChangeRequest};
use automerge_backend::{Backend, Change};
use automerge_protocol::{ActorID, ChangeHash, Request};
use js_sys::{Array, Uint8Array};
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::fmt::Display;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
@ -39,54 +40,46 @@ pub struct State {
impl State {
#[wasm_bindgen(js_name = applyChanges)]
pub fn apply_changes(&mut self, changes: Array) -> Result<JsValue, JsValue> {
let ch: Vec<Vec<u8>> = changes
.iter()
.map(|c| c.dyn_into::<Uint8Array>().unwrap().to_vec())
.collect();
let patch = self
.backend
.apply_changes_binary(ch)
.map_err(automerge_error_to_js)?;
let mut ch = Vec::with_capacity(changes.length() as usize);
for c in changes.iter() {
let bytes = c.dyn_into::<Uint8Array>().unwrap().to_vec();
ch.push(Change::from_bytes(bytes).map_err(to_js_err)?);
}
let patch = self.backend.apply_changes(ch).map_err(to_js_err)?;
rust_to_js(&patch)
}
#[wasm_bindgen(js_name = loadChanges)]
pub fn load_changes(&mut self, changes: Array) -> Result<(), JsValue> {
let ch: Vec<Vec<u8>> = changes
.iter()
.map(|c| c.dyn_into::<Uint8Array>().unwrap().to_vec())
.collect();
self.backend
.load_changes_binary(ch)
.map_err(automerge_error_to_js)
let mut ch = Vec::with_capacity(changes.length() as usize);
for c in changes.iter() {
let bytes = c.dyn_into::<Uint8Array>().unwrap().to_vec();
ch.push(Change::from_bytes(bytes).unwrap())
}
self.backend.load_changes(ch).map_err(to_js_err)?;
Ok(())
}
#[wasm_bindgen(js_name = applyLocalChange)]
pub fn apply_local_change(&mut self, change: JsValue) -> Result<JsValue, JsValue> {
let c: ChangeRequest = js_to_rust(change)?;
let patch = self
.backend
.apply_local_change(c)
.map_err(automerge_error_to_js)?;
let c: Request = js_to_rust(change)?;
let patch = self.backend.apply_local_change(c).map_err(to_js_err)?;
rust_to_js(&patch)
}
#[wasm_bindgen(js_name = getPatch)]
pub fn get_patch(&self) -> Result<JsValue, JsValue> {
let patch = self.backend.get_patch().map_err(automerge_error_to_js)?;
let patch = self.backend.get_patch().map_err(to_js_err)?;
rust_to_js(&patch)
}
#[wasm_bindgen(js_name = getChanges)]
pub fn get_changes(&self, have_deps: JsValue) -> Result<Array, JsValue> {
let deps: Vec<ChangeHash> = js_to_rust(have_deps)?;
let changes = self
.backend
.get_changes(&deps)
.map_err(automerge_error_to_js)?;
let changes = self.backend.get_changes(&deps);
let result = Array::new();
for c in changes {
let bytes: Uint8Array = c.as_slice().into();
let bytes: Uint8Array = c.bytes.as_slice().into();
result.push(bytes.as_ref());
}
Ok(result)
@ -98,10 +91,10 @@ impl State {
let changes = self
.backend
.get_changes_for_actor_id(&a)
.map_err(automerge_error_to_js)?;
.map_err(to_js_err)?;
let result = Array::new();
for c in changes {
let bytes: Uint8Array = c.as_slice().into();
let bytes: Uint8Array = c.bytes.as_slice().into();
result.push(bytes.as_ref());
}
Ok(result)
@ -133,7 +126,7 @@ impl State {
#[wasm_bindgen(js_name = save)]
pub fn save(&self) -> Result<JsValue, JsValue> {
let data = self.backend.save().map_err(automerge_error_to_js)?;
let data = self.backend.save().map_err(to_js_err)?;
let js_bytes: Uint8Array = data.as_slice().into();
Ok(js_bytes.into())
}
@ -141,7 +134,7 @@ impl State {
#[wasm_bindgen(js_name = load)]
pub fn load(data: JsValue) -> Result<State, JsValue> {
let data = data.dyn_into::<Uint8Array>().unwrap().to_vec();
let backend = Backend::load(data).map_err(automerge_error_to_js)?;
let backend = Backend::load(data).map_err(to_js_err)?;
Ok(State { backend })
}
@ -153,7 +146,7 @@ impl State {
}
}
fn automerge_error_to_js(err: AutomergeError) -> JsValue {
fn to_js_err<T: Display>(err: T) -> JsValue {
js_sys::Error::new(&std::format!("Automerge error: {}", err)).into()
}

View file

@ -13,11 +13,11 @@ uuid = { version = "^0.5.1", features=["v4"] }
wasm-bindgen = "^0.2"
js-sys = "^0.3"
im-rc = "^14.3.0"
sha2 = "^0.8.1"
hex = "^0.4.2"
rand = { version = "^0.7.3", features=["wasm-bindgen"] }
leb128 = "^0.2.4"
maplit = "^1.0.2"
sha2 = "^0.8.1"
leb128 = "^0.2.4"
automerge-protocol = { path = "../automerge-protocol" }
[dependencies.web-sys]

View file

@ -1,16 +1,16 @@
use crate::actor_map::ActorMap;
use crate::columnar::{bin_to_changes, change_to_change, changes_to_bin, changes_to_one_bin};
use crate::error::AutomergeError;
use crate::op::Operation;
use crate::op_handle::OpHandle;
use crate::op_set::OpSet;
use crate::op_type::OpType;
use crate::ordered_set::{OrdDelta, OrderedSet};
use crate::pending_diff::PendingDiff;
use crate::time;
use crate::undo_operation::UndoOperation;
use automerge_protocol::{
ActorID, Change, ChangeHash, ChangeRequest, ChangeRequestType, Diff, Key, ObjType, ObjectID,
OpID, OpRequest, OpType, Operation, Patch, ReqOpType, RequestKey, Value,
};
use crate::{Change, UnencodedChange};
use automerge_protocol as amp;
use automerge_protocol::{ActorID, Key, ObjType, ObjectID, OpID};
use std::borrow::BorrowMut;
use std::cmp::max;
use std::collections::{HashMap, HashSet};
@ -22,14 +22,12 @@ pub struct Backend {
versions: Vec<Version>,
queue: Vec<Rc<Change>>,
op_set: Rc<OpSet>,
//states: ActorStates,
states: HashMap<ActorID, Vec<Rc<Change>>>,
actors: ActorMap,
obj_alias: HashMap<String, ObjectID>,
undo_pos: usize,
hashes: HashMap<ChangeHash, Rc<Change>>,
history: Vec<ChangeHash>,
//clock: Clock,
hashes: HashMap<amp::ChangeHash, Rc<Change>>,
history: Vec<amp::ChangeHash>,
pub undo_stack: Vec<Vec<UndoOperation>>,
pub redo_stack: Vec<Vec<UndoOperation>>,
}
@ -49,8 +47,7 @@ impl Backend {
queue: Vec::new(),
actors: ActorMap::new(),
obj_alias: HashMap::new(),
states: HashMap::new(), //ActorStates::new(),
//clock: Clock::empty(),
states: HashMap::new(),
history: Vec::new(),
hashes: HashMap::new(),
undo_stack: Vec::new(),
@ -70,7 +67,7 @@ impl Backend {
fn process_request(
&mut self,
request: &ChangeRequest,
request: &amp::Request,
op_set: Rc<OpSet>,
start_op: u64,
) -> Result<Rc<Change>, AutomergeError> {
@ -82,7 +79,7 @@ impl Backend {
let mut elemid_cache: HashMap<ObjectID, Box<dyn OrderedSet<OpID>>> = HashMap::new();
if let Some(ops) = &request.ops {
for rop in ops.iter() {
let id = OpID(start_op + (operations.len() as u64), actor_id.0.clone());
let id = OpID::new(start_op + (operations.len() as u64), &actor_id);
let insert = rop.insert;
let object_id = self.str_to_object(&rop.obj)?;
@ -125,19 +122,19 @@ impl Backend {
let key = resolve_key(rop, &id, elemids2)?;
let pred = op_set.get_pred(&object_id, &key, insert);
let action = match rop.action {
ReqOpType::MakeMap => OpType::Make(ObjType::Map),
ReqOpType::MakeTable => OpType::Make(ObjType::Table),
ReqOpType::MakeList => OpType::Make(ObjType::List),
ReqOpType::MakeText => OpType::Make(ObjType::Text),
ReqOpType::Del => OpType::Del,
ReqOpType::Link => OpType::Link(
amp::OpType::MakeMap => OpType::Make(ObjType::Map),
amp::OpType::MakeTable => OpType::Make(ObjType::Table),
amp::OpType::MakeList => OpType::Make(ObjType::List),
amp::OpType::MakeText => OpType::Make(ObjType::Text),
amp::OpType::Del => OpType::Del,
amp::OpType::Link => OpType::Link(
child.ok_or_else(|| AutomergeError::LinkMissingChild(id.clone()))?,
),
ReqOpType::Inc => OpType::Inc(
amp::OpType::Inc => OpType::Inc(
rop.to_i64()
.ok_or_else(|| AutomergeError::MissingNumberValue(rop.clone()))?,
),
ReqOpType::Set => OpType::Set(rop.primitive_value()),
amp::OpType::Set => OpType::Set(rop.primitive_value()),
};
let op = Operation {
@ -157,26 +154,28 @@ impl Backend {
operations.push(op);
}
}
Ok(change_to_change(Change {
start_op,
message: request.message.clone(),
actor_id: request.actor.clone(),
hash: ChangeHash::zero(),
seq: request.seq,
deps: request.deps.clone().unwrap_or_default(),
time,
operations,
})?)
Ok(Rc::new(
UnencodedChange {
start_op,
message: request.message.clone(),
actor_id: request.actor.clone(),
seq: request.seq,
deps: request.deps.clone().unwrap_or_default(),
time,
operations,
}
.into(),
))
}
fn make_patch(
&self,
diffs: Option<Diff>,
request: Option<&ChangeRequest>,
) -> Result<Patch, AutomergeError> {
diffs: Option<amp::Diff>,
request: Option<&amp::Request>,
) -> Result<amp::Patch, AutomergeError> {
let mut deps: Vec<_> = self.op_set.deps.iter().cloned().collect();
deps.sort_unstable();
Ok(Patch {
Ok(amp::Patch {
version: self.versions.last().map(|v| v.version).unwrap_or(0),
can_undo: self.can_undo(),
can_redo: self.can_redo(),
@ -187,14 +186,14 @@ impl Backend {
.iter()
.map(|(k, v)| (k.to_hex_string(), v.len() as u64))
.collect(),
actor: request.map(|r| r.actor.0.clone()),
actor: request.map(|r| r.actor.to_hex_string()),
seq: request.map(|r| r.seq),
})
}
fn undo(
&mut self,
request: &ChangeRequest,
request: &amp::Request,
start_op: u64,
) -> Result<Rc<Change>, AutomergeError> {
let undo_pos = self.undo_pos;
@ -222,26 +221,26 @@ impl Backend {
})
.collect();
let change = change_to_change(Change {
let change = UnencodedChange {
actor_id: request.actor.clone(),
seq: request.seq,
hash: ChangeHash::zero(),
start_op,
deps: request.deps.clone().unwrap_or_default(),
message: request.message.clone(),
time: time::unix_timestamp(),
operations,
})?;
}
.into();
self.undo_pos -= 1;
self.redo_stack.push(redo_ops);
Ok(change)
Ok(Rc::new(change))
}
fn redo(
&mut self,
request: &ChangeRequest,
request: &amp::Request,
start_op: u64,
) -> Result<Rc<Change>, AutomergeError> {
let mut redo_ops = self.redo_stack.pop().ok_or(AutomergeError::NoRedo)?;
@ -257,28 +256,20 @@ impl Backend {
})
.collect();
let change = change_to_change(Change {
let change = UnencodedChange {
actor_id: request.actor.clone(),
seq: request.seq,
hash: ChangeHash::zero(),
start_op,
deps: request.deps.clone().unwrap_or_default(),
message: request.message.clone(),
time: time::unix_timestamp(),
operations,
})?;
}
.into();
self.undo_pos += 1;
Ok(change)
}
pub fn load_changes_binary(&mut self, data: Vec<Vec<u8>>) -> Result<(), AutomergeError> {
let mut changes = Vec::new();
for d in data {
changes.extend(bin_to_changes(&d)?);
}
self.load_changes(changes)
Ok(Rc::new(change))
}
pub fn load_changes(&mut self, mut changes: Vec<Change>) -> Result<(), AutomergeError> {
@ -287,15 +278,10 @@ impl Backend {
Ok(())
}
pub fn apply_changes_binary(&mut self, data: Vec<Vec<u8>>) -> Result<Patch, AutomergeError> {
let mut changes = Vec::new();
for d in data {
changes.extend(bin_to_changes(&d)?);
}
self.apply_changes(changes)
}
pub fn apply_changes(&mut self, mut changes: Vec<Change>) -> Result<Patch, AutomergeError> {
pub fn apply_changes(
&mut self,
mut changes: Vec<Change>,
) -> Result<amp::Patch, AutomergeError> {
let op_set = Some(self.op_set.clone());
self.versions.iter_mut().for_each(|v| {
@ -320,12 +306,7 @@ impl Backend {
for change in v.queue.drain(0..) {
let mut m = HashMap::new();
Rc::make_mut(op_set)
.apply_ops(
OpHandle::extract(change.clone()),
false,
&mut m,
&self.actors,
)
.apply_ops(OpHandle::extract(change), false, &mut m, &self.actors)
.unwrap();
}
return Ok(op_set.clone());
@ -336,10 +317,10 @@ impl Backend {
fn apply(
&mut self,
mut changes: Vec<Rc<Change>>,
request: Option<&ChangeRequest>,
request: Option<&amp::Request>,
undoable: bool,
incremental: bool,
) -> Result<Patch, AutomergeError> {
) -> Result<amp::Patch, AutomergeError> {
let mut pending_diffs = HashMap::new();
for change in changes.drain(..) {
@ -371,8 +352,8 @@ impl Backend {
pub fn apply_local_change(
&mut self,
mut request: ChangeRequest,
) -> Result<Patch, AutomergeError> {
mut request: amp::Request,
) -> Result<amp::Patch, AutomergeError> {
self.check_for_duplicate(&request)?; // Change has already been applied
let ver = self.get_version(request.version)?;
@ -383,12 +364,12 @@ impl Backend {
let start_op = ver.max_op + 1;
let change = match request.request_type {
ChangeRequestType::Change => self.process_request(&request, ver, start_op)?,
ChangeRequestType::Undo => self.undo(&request, start_op)?,
ChangeRequestType::Redo => self.redo(&request, start_op)?,
amp::RequestType::Change => self.process_request(&request, ver, start_op)?,
amp::RequestType::Undo => self.undo(&request, start_op)?,
amp::RequestType::Redo => self.redo(&request, start_op)?,
};
let undoable = request.request_type == ChangeRequestType::Change && request.undoable;
let undoable = request.request_type == amp::RequestType::Change && request.undoable;
let patch = self.apply(vec![change.clone()], Some(&request), undoable, true)?;
@ -397,7 +378,7 @@ impl Backend {
Ok(patch)
}
fn check_for_duplicate(&self, request: &ChangeRequest) -> Result<(), AutomergeError> {
fn check_for_duplicate(&self, request: &amp::Request) -> Result<(), AutomergeError> {
if self
.states
.get(&request.actor)
@ -407,7 +388,8 @@ impl Backend {
{
return Err(AutomergeError::DuplicateChange(format!(
"Change request has already been applied {}:{}",
request.actor.0, request.seq
request.actor.to_hex_string(),
request.seq
)));
}
Ok(())
@ -449,12 +431,10 @@ impl Backend {
}
self.states
.entry(change.actor_id.clone())
.entry(change.actor_id())
.or_default()
.push(change.clone());
// self.clock.set(&change.actor_id, change.seq);
self.hashes.insert(change.hash, change.clone());
self.history.push(change.hash);
@ -514,7 +494,7 @@ impl Backend {
Ok(())
}
pub fn get_patch(&self) -> Result<Patch, AutomergeError> {
pub fn get_patch(&self) -> Result<amp::Patch, AutomergeError> {
let diffs = self
.op_set
.construct_object(&ObjectID::Root, &self.actors)?;
@ -524,21 +504,15 @@ impl Backend {
pub fn get_changes_for_actor_id(
&self,
actor_id: &ActorID,
) -> Result<Vec<Vec<u8>>, AutomergeError> {
let changes: Vec<&Change> = self
) -> Result<Vec<&Change>, AutomergeError> {
Ok(self
.states
.get(actor_id)
.map(|vec| vec.iter().map(|c| c.as_ref()).collect())
.unwrap_or_default();
changes_to_bin(&changes)
.unwrap_or_default())
}
pub fn get_changes(&self, have_deps: &[ChangeHash]) -> Result<Vec<Vec<u8>>, AutomergeError> {
let changes = self.get_changes_unserialized(have_deps);
changes_to_bin(&changes)
}
pub fn get_changes_unserialized(&self, have_deps: &[ChangeHash]) -> Vec<&Change> {
pub fn get_changes(&self, have_deps: &[amp::ChangeHash]) -> Vec<&Change> {
let mut stack = have_deps.to_owned();
let mut has_seen = HashSet::new();
while let Some(hash) = stack.pop() {
@ -547,9 +521,7 @@ impl Backend {
}
has_seen.insert(hash);
}
self
// .states
.history
self.history
.iter()
.filter(|hash| !has_seen.contains(hash))
.filter_map(|hash| self.hashes.get(hash))
@ -566,24 +538,23 @@ impl Backend {
}
pub fn save(&self) -> Result<Vec<u8>, AutomergeError> {
let changes: Vec<_> = self
let bytes: Vec<&[u8]> = self
.history
.iter()
.filter_map(|hash| self.hashes.get(&hash))
.map(|ch| ch.as_ref())
.map(|r| r.bytes.as_slice())
.collect();
let data = changes_to_one_bin(&changes)?;
Ok(data)
Ok(bytes.concat())
}
pub fn load(data: Vec<u8>) -> Result<Self, AutomergeError> {
let changes = bin_to_changes(&data)?;
let changes = Change::parse(&data)?;
let mut backend = Self::init();
backend.load_changes(changes)?;
Ok(backend)
}
pub fn get_missing_deps(&self) -> Vec<ChangeHash> {
pub fn get_missing_deps(&self) -> Vec<amp::ChangeHash> {
let in_queue: Vec<_> = self.queue.iter().map(|change| &change.hash).collect();
self.queue
.iter()
@ -607,16 +578,16 @@ struct Version {
}
fn resolve_key(
rop: &OpRequest,
rop: &amp::Op,
id: &OpID,
ids: &mut dyn OrderedSet<OpID>,
) -> Result<Key, AutomergeError> {
let key = &rop.key;
let insert = rop.insert;
let del = rop.action == ReqOpType::Del;
let del = rop.action == amp::OpType::Del;
match key {
RequestKey::Str(s) => Ok(Key::Map(s.clone())),
RequestKey::Num(n) => {
amp::RequestKey::Str(s) => Ok(Key::Map(s.clone())),
amp::RequestKey::Num(n) => {
let n: usize = *n as usize;
(if insert {
if n == 0 {
@ -672,8 +643,8 @@ impl OpExt for Operation {
fn merge(&mut self, other: Operation) {
if let OpType::Inc(delta) = other.action {
match self.action {
OpType::Set(Value::Counter(number)) => {
self.action = OpType::Set(Value::Counter(number + delta))
OpType::Set(amp::Value::Counter(number)) => {
self.action = OpType::Set(amp::Value::Counter(number + delta))
}
OpType::Inc(number) => self.action = OpType::Inc(number + delta),
_ => {}

View file

@ -0,0 +1,474 @@
use crate::columnar;
use crate::columnar::{
ColumnEncoder, KeyIterator, ObjIterator, OperationIterator, PredIterator, ValueIterator,
};
use crate::encoding::{Decodable, Encodable};
use crate::error::AutomergeError;
use crate::op::Operation;
use automerge_protocol::{ActorID, ChangeHash};
use core::fmt::Debug;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::collections::HashMap;
use std::convert::TryFrom;
use std::convert::TryInto;
use std::io;
use std::io::Write;
use std::ops::Range;
use std::str;
const HASH_BYTES: usize = 32;
const CHUNK_TYPE: u8 = 1;
#[derive(Deserialize, Serialize, PartialEq, Debug, Clone)]
pub struct UnencodedChange {
#[serde(rename = "ops")]
pub operations: Vec<Operation>,
#[serde(rename = "actor")]
pub actor_id: ActorID,
//pub hash: ChangeHash,
pub seq: u64,
#[serde(rename = "startOp")]
pub start_op: u64,
pub time: i64,
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
pub deps: Vec<ChangeHash>,
}
impl UnencodedChange {
pub fn max_op(&self) -> u64 {
self.start_op + (self.operations.len() as u64) - 1
}
pub fn encode(&self) -> Change {
let mut buf = Vec::new();
let mut hasher = Sha256::new();
let chunk = self.encode_chunk();
hasher.input(&chunk);
buf.extend(&MAGIC_BYTES);
buf.extend(&hasher.result()[0..4]);
buf.extend(&chunk);
// possible optimization here - i can assemble the metadata without having to parse
// the generated object
// ---
// unwrap :: we generated this binchange so there's no chance of bad format
// ---
Change::from_bytes(buf).unwrap()
}
fn encode_chunk(&self) -> Vec<u8> {
let mut chunk = vec![CHUNK_TYPE]; // chunk type is always 1
// unwrap - io errors cant happen when writing to an in memory vec
let data = self.encode_chunk_body().unwrap();
leb128::write::unsigned(&mut chunk, data.len() as u64).unwrap();
chunk.extend(&data);
chunk
}
fn encode_chunk_body(&self) -> io::Result<Vec<u8>> {
let mut buf = Vec::new();
let mut actors = Vec::new();
actors.push(self.actor_id.clone());
self.actor_id.to_bytes().encode(&mut buf)?;
self.seq.encode(&mut buf)?;
self.start_op.encode(&mut buf)?;
self.time.encode(&mut buf)?;
self.message.encode(&mut buf)?;
let ops_buf = ColumnEncoder::encode_ops(&self.operations, &mut actors);
actors[1..].encode(&mut buf)?;
let mut deps = self.deps.clone();
deps.sort_unstable();
deps.len().encode(&mut buf)?;
for hash in deps.iter() {
buf.write_all(&hash.0)?;
}
buf.write_all(&ops_buf)?;
Ok(buf)
}
}
#[derive(PartialEq, Debug, Clone)]
pub struct Change {
pub bytes: Vec<u8>,
pub hash: ChangeHash,
pub seq: u64,
pub start_op: u64,
pub time: i64,
body: Range<usize>,
message: Range<usize>,
actors: Vec<ActorID>,
pub deps: Vec<ChangeHash>,
ops: HashMap<u32, Range<usize>>,
}
impl Change {
pub fn actor_id(&self) -> ActorID {
self.actors[0].clone()
}
pub fn parse(bytes: &[u8]) -> Result<Vec<Change>, AutomergeError> {
let mut changes = Vec::new();
let mut cursor = &bytes[..];
while !cursor.is_empty() {
let (val, len) = read_leb128(&mut &cursor[HEADER_BYTES..])?;
let (data, rest) = cursor.split_at(HEADER_BYTES + val + len);
changes.push(Self::from_bytes(data.to_vec())?);
cursor = rest;
}
Ok(changes)
}
pub fn from_bytes(bytes: Vec<u8>) -> Result<Change, AutomergeError> {
if bytes.len() <= HEADER_BYTES {
return Err(AutomergeError::EncodingError);
}
if bytes[0..4] != MAGIC_BYTES {
return Err(AutomergeError::EncodingError);
}
let (val, len) = read_leb128(&mut &bytes[HEADER_BYTES..])?;
let body = (HEADER_BYTES + len)..(HEADER_BYTES + len + val);
if bytes.len() != body.end {
return Err(AutomergeError::EncodingError);
}
let chunktype = bytes[PREAMBLE_BYTES];
if chunktype == 0 {
return Err(AutomergeError::EncodingError); // Format not implemented
}
if chunktype > 1 {
return Err(AutomergeError::EncodingError);
}
let mut hasher = Sha256::new();
hasher.input(&bytes[PREAMBLE_BYTES..]);
let hash = hasher.result()[..].try_into()?;
let mut cursor = body.clone();
let actor = ActorID::from(&bytes[slice_bytes(&bytes, &mut cursor)?]);
let seq = read_slice(&bytes, &mut cursor)?;
let start_op = read_slice(&bytes, &mut cursor)?;
let time = read_slice(&bytes, &mut cursor)?;
let message = slice_bytes(&bytes, &mut cursor)?;
let num_actors = read_slice(&bytes, &mut cursor)?;
let mut actors = vec![actor];
for _ in 0..num_actors {
actors.push(ActorID::from(&bytes[slice_bytes(&bytes, &mut cursor)?]));
}
let mut deps = Vec::new();
let num_deps = read_slice(&bytes, &mut cursor)?;
for _ in 0..num_deps {
let hash = cursor.start..(cursor.start + HASH_BYTES);
cursor = hash.end..cursor.end;
//let hash = slice_n_bytes(bytes, HASH_BYTES)?;
deps.push(bytes[hash].try_into()?);
}
let mut ops = HashMap::new();
let mut last_id = 0;
while !bytes[cursor.clone()].is_empty() {
let id = read_slice(&bytes, &mut cursor)?;
if id < last_id {
return Err(AutomergeError::EncodingError);
}
last_id = id;
let column = slice_bytes(&bytes, &mut cursor)?;
ops.insert(id, column);
}
Ok(Change {
bytes,
hash,
body,
seq,
start_op,
time,
actors,
message,
deps,
ops,
})
}
pub fn max_op(&self) -> u64 {
// TODO - this could be a lot more efficient
let len = self.iter_ops().count();
self.start_op + (len as u64) - 1
}
fn message(&self) -> Option<String> {
let m = &self.bytes[self.message.clone()];
if m.is_empty() {
None
} else {
str::from_utf8(&m).map(|s| s.to_string()).ok()
}
}
pub fn decode(&self) -> UnencodedChange {
UnencodedChange {
start_op: self.start_op,
seq: self.seq,
time: self.time,
message: self.message(),
actor_id: self.actors[0].clone(),
deps: self.deps.clone(),
operations: self.iter_ops().collect(),
}
}
fn col_iter<'a, T>(&'a self, col_id: u32) -> T
where
T: From<&'a [u8]>,
{
let empty = 0..0;
let range = self.ops.get(&col_id).unwrap_or(&empty);
let buf = &self.bytes[range.clone()];
T::from(&buf)
}
pub fn iter_ops(&self) -> OperationIterator {
OperationIterator {
objs: ObjIterator {
actors: &self.actors,
actor: self.col_iter(columnar::COL_OBJ_ACTOR),
ctr: self.col_iter(columnar::COL_OBJ_CTR),
},
chld: ObjIterator {
actors: &self.actors,
actor: self.col_iter(columnar::COL_CHILD_ACTOR),
ctr: self.col_iter(columnar::COL_CHILD_CTR),
},
keys: KeyIterator {
actors: &self.actors,
actor: self.col_iter(columnar::COL_KEY_ACTOR),
ctr: self.col_iter(columnar::COL_KEY_CTR),
str: self.col_iter(columnar::COL_KEY_STR),
},
value: ValueIterator {
val_len: self.col_iter(columnar::COL_VAL_LEN),
val_raw: self.col_iter(columnar::COL_VAL_RAW),
},
pred: PredIterator {
actors: &self.actors,
pred_num: self.col_iter(columnar::COL_PRED_NUM),
pred_actor: self.col_iter(columnar::COL_PRED_ACTOR),
pred_ctr: self.col_iter(columnar::COL_PRED_CTR),
},
insert: self.col_iter(columnar::COL_INSERT),
action: self.col_iter(columnar::COL_ACTION),
}
}
}
impl From<&UnencodedChange> for Change {
fn from(change: &UnencodedChange) -> Change {
change.encode()
}
}
impl From<UnencodedChange> for Change {
fn from(change: UnencodedChange) -> Change {
change.encode()
}
}
impl From<&Change> for UnencodedChange {
fn from(change: &Change) -> UnencodedChange {
change.decode()
}
}
impl TryFrom<&[u8]> for Change {
type Error = AutomergeError;
fn try_from(bytes: &[u8]) -> Result<Self, AutomergeError> {
Change::from_bytes(bytes.to_vec())
}
}
fn read_leb128(bytes: &mut &[u8]) -> Result<(usize, usize), AutomergeError> {
let mut buf = &bytes[..];
let val = leb128::read::unsigned(&mut buf)? as usize;
let leb128_bytes = bytes.len() - buf.len();
Ok((val, leb128_bytes))
}
fn read_slice<T: Decodable + Debug>(
bytes: &[u8],
cursor: &mut Range<usize>,
) -> Result<T, AutomergeError> {
let view = &bytes[cursor.clone()];
let mut reader = &view[..];
let val = T::decode::<&[u8]>(&mut reader).ok_or(AutomergeError::EncodingError);
let len = view.len() - reader.len();
*cursor = (cursor.start + len)..cursor.end;
val
}
fn slice_bytes(bytes: &[u8], cursor: &mut Range<usize>) -> Result<Range<usize>, AutomergeError> {
let (val, len) = read_leb128(&mut &bytes[cursor.clone()])?;
let start = cursor.start + len;
let end = start + val;
*cursor = end..cursor.end;
Ok(start..end)
}
const MAGIC_BYTES: [u8; 4] = [0x85, 0x6f, 0x4a, 0x83];
const PREAMBLE_BYTES: usize = 8;
const HEADER_BYTES: usize = PREAMBLE_BYTES + 1;
#[cfg(test)]
mod tests {
use super::*;
use crate::op_type::OpType;
use automerge_protocol::{Key, ObjType, ObjectID, OpID, Value};
use std::str::FromStr;
#[test]
fn test_empty_change() {
let change1 = UnencodedChange {
start_op: 1,
seq: 2,
time: 1234,
message: None,
actor_id: ActorID::from_str("deadbeefdeadbeef").unwrap(),
deps: vec![],
operations: vec![],
};
let bin1 = change1.encode();
let change2 = bin1.decode();
let bin2 = change2.encode();
assert_eq!(bin1, bin2);
assert_eq!(change1, change2);
}
#[test]
fn test_complex_change() -> Result<(), AutomergeError> {
let actor1 = ActorID::from_str("deadbeefdeadbeef").unwrap();
let actor2 = ActorID::from_str("feeddefaff").unwrap();
let actor3 = ActorID::from_str("00101010fafafafa").unwrap();
let opid1 = OpID::new(102, &actor1);
let opid2 = OpID::new(391, &actor1);
let opid3 = OpID::new(299, &actor2);
let opid4 = OpID::new(762, &actor3);
let opid5 = OpID::new(100_203, &actor2);
let obj1 = ObjectID::ID(opid1.clone());
let obj2 = ObjectID::Root;
let obj3 = ObjectID::ID(opid4.clone());
let key1 = Key::Map("field1".into());
let key2 = Key::Map("field2".into());
let key3 = Key::Map("field3".into());
let head = Key::head();
let keyseq1 = Key::from(&opid1);
let keyseq2 = Key::from(&opid2);
let insert = false;
let change1 = UnencodedChange {
start_op: 123,
seq: 29291,
time: 12_341_231,
message: Some("This is my message".into()),
actor_id: actor1,
deps: vec![],
operations: vec![
Operation {
action: OpType::Set(Value::F64(10.0)),
key: key1.clone(),
obj: obj1.clone(),
insert,
pred: vec![opid1.clone(), opid2.clone()],
},
Operation {
action: OpType::Set(Value::Counter(-11)),
key: key2.clone(),
obj: obj1.clone(),
insert,
pred: vec![opid1.clone(), opid2.clone()],
},
Operation {
action: OpType::Set(Value::Timestamp(20)),
key: key3,
obj: obj1.clone(),
insert,
pred: vec![opid1.clone(), opid2],
},
Operation {
action: OpType::Set(Value::Str("some value".into())),
key: key2.clone(),
obj: obj2.clone(),
insert,
pred: vec![opid3.clone(), opid4.clone()],
},
Operation {
action: OpType::Make(ObjType::List),
key: key2.clone(),
obj: obj2.clone(),
insert,
pred: vec![opid3.clone(), opid4.clone()],
},
Operation {
action: OpType::Set(Value::Str("val1".into())),
key: head.clone(),
obj: obj3.clone(),
insert: true,
pred: vec![opid3.clone(), opid4.clone()],
},
Operation {
action: OpType::Set(Value::Str("val2".into())),
key: head,
obj: obj3.clone(),
insert: true,
pred: vec![opid4.clone(), opid5.clone()],
},
Operation {
action: OpType::Inc(10),
key: key2,
obj: obj2,
insert,
pred: vec![opid1.clone(), opid5.clone()],
},
Operation {
action: OpType::Link(obj3.clone()),
obj: obj1,
key: key1,
insert,
pred: vec![opid1, opid3],
},
Operation {
action: OpType::Del,
obj: obj3.clone(),
key: keyseq1,
insert: true,
pred: vec![opid4.clone(), opid5.clone()],
},
Operation {
action: OpType::Del,
obj: obj3,
key: keyseq2,
insert: true,
pred: vec![opid4, opid5],
},
],
};
let bin1 = change1.encode();
let change2 = bin1.decode();
let bin2 = change2.encode();
assert_eq!(bin1, bin2);
assert_eq!(change1, change2);
Ok(())
}
}

View file

@ -1,139 +1,13 @@
use crate::encoding::{BooleanDecoder, Decodable, Decoder, DeltaDecoder, RLEDecoder};
use crate::encoding::{BooleanEncoder, ColData, DeltaEncoder, Encodable, RLEEncoder};
use crate::error::AutomergeError;
use automerge_protocol::{
ActorID, Change, ChangeHash, ElementID, Key, ObjType, ObjectID, OpID, OpType, Operation, Value,
};
use crate::op::Operation;
use crate::op_type::OpType;
use automerge_protocol::{ActorID, ElementID, Key, ObjType, ObjectID, OpID, Value};
use core::fmt::Debug;
use sha2::{Digest, Sha256};
use std::collections::HashMap;
use std::convert::TryInto;
use std::io;
use std::io::{Read, Write};
use std::rc::Rc;
use std::str;
const HASH_BYTES: usize = 32;
pub(crate) fn bin_to_changes(bindata: &[u8]) -> Result<Vec<Change>, AutomergeError> {
BinaryContainer::from(&bindata)?
.iter()
.map(|bin| bin.to_change())
.collect()
}
// FIXME - really I need a bin change constructor
pub(crate) fn change_to_change(change: Change) -> Result<Rc<Change>, AutomergeError> {
let bin_change = changes_to_bin(&[&change])?;
let mut changes = bin_to_changes(&bin_change[0])?;
Ok(Rc::new(changes.remove(0)))
}
pub(crate) fn changes_to_bin(changes: &[&Change]) -> Result<Vec<Vec<u8>>, AutomergeError> {
let mut bins = Vec::new();
for c in changes {
let bin = change_to_bin(c)?;
bins.push(bin)
}
Ok(bins)
}
pub fn change_hash(change: Change) -> Result<ChangeHash, AutomergeError> {
change_to_change(change).map(|c| c.hash)
}
pub(crate) fn changes_to_one_bin(changes: &[&Change]) -> Result<Vec<u8>, AutomergeError> {
let mut data = Vec::new();
for c in changes {
let bin = change_to_bin(c)?;
data.extend(bin)
}
Ok(data)
}
fn change_to_bin(change: &Change) -> Result<Vec<u8>, AutomergeError> {
let mut buf = Vec::new();
let mut hasher = Sha256::new();
let chunk = encode_chunk(&change)?;
hasher.input(&chunk);
buf.extend(&MAGIC_BYTES);
buf.extend(&hasher.result()[0..4]);
buf.extend(&chunk);
Ok(buf)
}
fn encode_chunk(change: &Change) -> Result<Vec<u8>, AutomergeError> {
let mut chunk = vec![CHUNK_TYPE]; // chunk type is always 1
let data = encode(change)?;
leb128::write::unsigned(&mut chunk, data.len() as u64)?;
chunk.extend(&data);
Ok(chunk)
}
fn encode<V: Encodable>(val: &V) -> Result<Vec<u8>, AutomergeError> {
let mut actor_ids = Vec::new();
Ok(val.encode_with_actors_to_vec(&mut actor_ids)?)
}
impl Encodable for Change {
fn encode_with_actors<R: Write>(
&self,
buf: &mut R,
actors: &mut Vec<ActorID>,
) -> io::Result<usize> {
let mut len = 0;
actors.push(self.actor_id.clone());
len += self.actor_id.to_bytes().encode(buf)?;
len += self.seq.encode(buf)?;
len += self.start_op.encode(buf)?;
len += self.time.encode(buf)?;
len += self.message.encode(buf)?;
// let deps_buf = self.deps.encode_with_actors_to_vec(actors)?;
let ops_buf = ColumnEncoder::encode_ops(&self.operations, actors);
len += actors[1..].encode(buf)?;
let mut deps = self.deps.clone();
deps.sort_unstable();
len += deps.len().encode(buf)?;
for hash in deps.iter() {
len += buf.write(&hash.0)?;
}
buf.write_all(&ops_buf)?;
len += ops_buf.len();
Ok(len)
}
}
/*
impl Encodable for Clock {
fn encode_with_actors<R: Write>(
&self,
buf: &mut R,
actors: &mut Vec<ActorID>,
) -> io::Result<usize> {
let mut len = 0;
self.0.len().encode(buf)?;
let mut keys: Vec<&ActorID> = self.0.keys().collect();
keys.sort_unstable();
for actor in keys.iter() {
let val = self.get(actor);
len += actor.encode_with_actors(buf, actors)?;
len += val.encode(buf)?;
}
Ok(len)
}
}
*/
use std::str::FromStr;
impl Encodable for Action {
fn encode<R: Write>(&self, buf: &mut R) -> io::Result<usize> {
@ -152,7 +26,8 @@ impl Encodable for [ActorID] {
}
fn map_string(actor: &str, actors: &mut Vec<ActorID>) -> usize {
let a = ActorID(actor.to_string());
// FIXME - this can be fixed if OpID() contains an actorid instead of a String
let a = ActorID::from_str(actor).unwrap(); // this is only called on interal values
map_actor(&a, actors)
}
@ -189,213 +64,40 @@ impl Encodable for &[u8] {
}
}
fn read_slice<T: Decodable + Debug>(buf: &mut &[u8]) -> Result<T, AutomergeError> {
T::decode::<&[u8]>(buf).ok_or(AutomergeError::ChangeBadFormat)
pub struct OperationIterator<'a> {
pub(crate) action: RLEDecoder<'a, Action>,
pub(crate) objs: ObjIterator<'a>,
pub(crate) chld: ObjIterator<'a>,
pub(crate) keys: KeyIterator<'a>,
pub(crate) insert: BooleanDecoder<'a>,
pub(crate) value: ValueIterator<'a>,
pub(crate) pred: PredIterator<'a>,
}
fn slice_bytes<'a>(bytes: &mut &'a [u8]) -> Result<&'a [u8], AutomergeError> {
let mut buf = &bytes[..];
let len = leb128::read::unsigned(&mut buf)? as usize;
let result = &buf[0..len];
*bytes = &buf[len..];
Ok(result)
pub struct ObjIterator<'a> {
//actors: &'a Vec<&'a [u8]>,
pub(crate) actors: &'a Vec<ActorID>,
pub(crate) actor: RLEDecoder<'a, usize>,
pub(crate) ctr: RLEDecoder<'a, u64>,
}
fn slice_n_bytes<'a>(bytes: &mut &'a [u8], n: usize) -> Result<&'a [u8], AutomergeError> {
let result = &bytes[0..n];
*bytes = &bytes[n..];
Ok(result)
pub struct PredIterator<'a> {
pub(crate) actors: &'a Vec<ActorID>,
pub(crate) pred_num: RLEDecoder<'a, usize>,
pub(crate) pred_actor: RLEDecoder<'a, usize>,
pub(crate) pred_ctr: DeltaDecoder<'a>,
}
fn slice_bytes_len(bytes: &[u8]) -> Result<(&[u8], usize), AutomergeError> {
let mut view = &bytes[..];
let len = leb128::read::unsigned(&mut view)? as usize;
let len_bytes = bytes.len() - view.len();
Ok((&view[0..len], len + len_bytes))
pub struct KeyIterator<'a> {
pub(crate) actors: &'a Vec<ActorID>,
pub(crate) actor: RLEDecoder<'a, usize>,
pub(crate) ctr: DeltaDecoder<'a>,
pub(crate) str: RLEDecoder<'a, String>,
}
impl From<leb128::read::Error> for AutomergeError {
fn from(_err: leb128::read::Error) -> Self {
AutomergeError::ChangeBadFormat
}
}
impl From<std::io::Error> for AutomergeError {
fn from(_err: std::io::Error) -> Self {
AutomergeError::EncodeFailed
}
}
#[derive(Debug, Clone)]
struct BinaryContainer<'a> {
magic: &'a [u8],
checksum: &'a [u8],
hash: ChangeHash,
body: &'a [u8],
chunktype: u8,
chunk: BinaryChange<'a>,
len: usize,
}
#[derive(Debug, Clone)]
struct BinaryChange<'a> {
all: &'a [u8],
seq: u64,
start_op: u64,
time: i64,
message: &'a [u8],
actors: Vec<&'a [u8]>,
deps: Vec<&'a [u8]>,
ops: HashMap<u32, &'a [u8]>,
}
impl<'a> BinaryChange<'a> {
fn from(bytes: &'a [u8]) -> Result<BinaryChange<'a>, AutomergeError> {
let bytes = &mut &bytes[..];
let all = &bytes[0..];
let actor = slice_bytes(bytes)?;
let seq = read_slice(bytes)?;
let start_op = read_slice(bytes)?;
let time = read_slice(bytes)?;
let message = slice_bytes(bytes)?;
let num_actors = read_slice(bytes)?;
let mut actors = vec![&actor[..]];
for _ in 0..num_actors {
let actor = slice_bytes(bytes)?;
actors.push(actor);
}
let mut deps = Vec::new();
let num_deps = read_slice(bytes)?;
for _ in 0..num_deps {
let hash = slice_n_bytes(bytes, HASH_BYTES)?;
deps.push(hash);
}
let mut ops = HashMap::new();
let mut last_id = 0;
while !bytes.is_empty() {
let id = read_slice(bytes)?;
if id < last_id {
return Err(AutomergeError::ChangeBadFormat);
}
last_id = id;
let column = slice_bytes(bytes)?;
ops.insert(id, column);
}
Ok(BinaryChange {
all,
seq,
start_op,
time,
actors,
message,
deps,
ops,
})
}
fn gen_deps(&self) -> Vec<ChangeHash> {
// TODO Add error propagation
self.deps.iter().map(|&v| v.try_into().unwrap()).collect()
}
fn message(&self) -> Option<String> {
if self.message.is_empty() {
None
} else {
str::from_utf8(&self.message).map(|s| s.to_string()).ok()
}
}
fn to_change(&self, hash: ChangeHash) -> Result<Change, AutomergeError> {
let change = Change {
start_op: self.start_op,
seq: self.seq,
hash,
time: self.time,
message: self.message(),
actor_id: ActorID::from_bytes(self.actors[0]),
deps: self.gen_deps(),
operations: self.iter_ops().collect(),
};
Ok(change)
}
fn col_iter<T>(&self, col_id: u32) -> T
where
T: From<&'a [u8]>,
{
let empty = &self.all[0..0];
let buf = self.ops.get(&col_id).unwrap_or(&empty);
T::from(&buf)
}
fn iter_ops(&self) -> OperationIterator {
OperationIterator {
objs: ObjIterator {
actors: &self.actors,
actor: self.col_iter(COL_OBJ_ACTOR),
ctr: self.col_iter(COL_OBJ_CTR),
},
chld: ObjIterator {
actors: &self.actors,
actor: self.col_iter(COL_CHILD_ACTOR),
ctr: self.col_iter(COL_CHILD_CTR),
},
keys: KeyIterator {
actors: &self.actors,
actor: self.col_iter(COL_KEY_ACTOR),
ctr: self.col_iter(COL_KEY_CTR),
str: self.col_iter(COL_KEY_STR),
},
value: ValueIterator {
val_len: self.col_iter(COL_VAL_LEN),
val_raw: self.col_iter(COL_VAL_RAW),
},
pred: PredIterator {
actors: &self.actors,
pred_num: self.col_iter(COL_PRED_NUM),
pred_actor: self.col_iter(COL_PRED_ACTOR),
pred_ctr: self.col_iter(COL_PRED_CTR),
},
insert: self.col_iter(COL_INSERT),
action: self.col_iter(COL_ACTION),
}
}
}
struct OperationIterator<'a> {
action: RLEDecoder<'a, Action>,
objs: ObjIterator<'a>,
chld: ObjIterator<'a>,
keys: KeyIterator<'a>,
insert: BooleanDecoder<'a>,
value: ValueIterator<'a>,
pred: PredIterator<'a>,
}
struct ObjIterator<'a> {
actors: &'a Vec<&'a [u8]>,
actor: RLEDecoder<'a, usize>,
ctr: RLEDecoder<'a, u64>,
}
struct PredIterator<'a> {
actors: &'a Vec<&'a [u8]>,
pred_num: RLEDecoder<'a, usize>,
pred_actor: RLEDecoder<'a, usize>,
pred_ctr: DeltaDecoder<'a>,
}
struct KeyIterator<'a> {
actors: &'a Vec<&'a [u8]>,
actor: RLEDecoder<'a, usize>,
ctr: DeltaDecoder<'a>,
str: RLEDecoder<'a, String>,
}
struct ValueIterator<'a> {
val_len: RLEDecoder<'a, usize>,
val_raw: Decoder<'a>,
pub struct ValueIterator<'a> {
pub(crate) val_len: RLEDecoder<'a, usize>,
pub(crate) val_raw: Decoder<'a>,
}
impl<'a> Iterator for PredIterator<'a> {
@ -406,8 +108,7 @@ impl<'a> Iterator for PredIterator<'a> {
for _ in 0..num {
let actor = self.pred_actor.next()??;
let ctr = self.pred_ctr.next()??;
let actor_bytes = self.actors.get(actor)?;
let actor_id = ActorID::from_bytes(actor_bytes);
let actor_id = self.actors.get(actor)?.clone();
let op_id = OpID::new(ctr, &actor_id);
p.push(op_id)
}
@ -503,9 +204,8 @@ impl<'a> Iterator for KeyIterator<'a> {
(None, None, Some(string)) => Some(Key::Map(string)),
(Some(0), Some(0), None) => Some(Key::head()),
(Some(actor), Some(ctr), None) => {
let actor_bytes = self.actors.get(actor)?;
let actor_id = ActorID::from_bytes(actor_bytes);
Some(OpID(ctr, actor_id.0).into())
let actor_id = self.actors.get(actor)?;
Some(OpID::new(ctr, actor_id).into())
}
_ => None,
}
@ -516,8 +216,8 @@ impl<'a> Iterator for ObjIterator<'a> {
type Item = ObjectID;
fn next(&mut self) -> Option<ObjectID> {
if let (Some(actor), Some(ctr)) = (self.actor.next()?, self.ctr.next()?) {
let actor_id = ActorID::from_bytes(self.actors.get(actor)?);
Some(ObjectID::ID(OpID(ctr, actor_id.0)))
let actor_id = self.actors.get(actor)?;
Some(ObjectID::ID(OpID::new(ctr, &actor_id)))
} else {
Some(ObjectID::Root)
}
@ -554,60 +254,6 @@ impl<'a> Iterator for OperationIterator<'a> {
}
}
impl<'a> BinaryContainer<'a> {
fn from(mut bytes: &'a [u8]) -> Result<Vec<BinaryContainer<'a>>, AutomergeError> {
let mut changes = Vec::new();
while !bytes.is_empty() {
let change = Self::parse_single(bytes)?;
bytes = &bytes[change.len..];
changes.push(change);
}
Ok(changes)
}
fn parse_single(bytes: &'a [u8]) -> Result<BinaryContainer<'a>, AutomergeError> {
if bytes.len() < 8 {
return Err(AutomergeError::ChangeBadFormat);
}
let (header, rest) = &bytes.split_at(8);
let (magic, checksum) = &header.split_at(4);
if magic != &MAGIC_BYTES {
return Err(AutomergeError::ChangeBadFormat);
}
let (chunk_data, chunk_len) = slice_bytes_len(&rest[1..])?;
let body = &rest[0..(chunk_len + 1)]; // +1 for chunktype
let chunktype = body[0];
let len = body.len() + header.len();
let mut hasher = Sha256::new();
hasher.input(&body);
let hash = hasher.result()[..]
.try_into()
.map_err(|_| AutomergeError::DecodeFailed)?;
Ok(BinaryContainer {
magic,
checksum,
hash,
chunktype,
body,
chunk: BinaryChange::from(chunk_data)?,
len,
})
}
fn is_valid(&self) -> bool {
&self.hash.0[0..4] == self.checksum
}
fn to_change(&self) -> Result<Change, AutomergeError> {
if !self.is_valid() {
return Err(AutomergeError::InvalidChange);
}
self.chunk.to_change(self.hash)
}
}
struct ValEncoder {
len: RLEEncoder<usize>,
raw: Vec<u8>,
@ -833,7 +479,7 @@ impl ChildEncoder {
}
}
struct ColumnEncoder {
pub(crate) struct ColumnEncoder {
obj: ObjEncoder,
key: KeyEncoder,
insert: BooleanEncoder,
@ -844,7 +490,7 @@ struct ColumnEncoder {
}
impl ColumnEncoder {
fn encode_ops(ops: &[Operation], actors: &mut Vec<ActorID>) -> Vec<u8> {
pub fn encode_ops(ops: &[Operation], actors: &mut Vec<ActorID>) -> Vec<u8> {
let mut e = Self::new();
e.encode(ops, actors);
e.finish()
@ -927,8 +573,6 @@ impl ColumnEncoder {
}
}
const CHUNK_TYPE: u8 = 1;
const VALUE_TYPE_NULL: usize = 0;
const VALUE_TYPE_FALSE: usize = 1;
const VALUE_TYPE_TRUE: usize = 2;
@ -942,18 +586,18 @@ const VALUE_TYPE_TIMESTAMP: usize = 9;
const VALUE_TYPE_MIN_UNKNOWN: usize = 10;
const VALUE_TYPE_MAX_UNKNOWN: usize = 15;
const COLUMN_TYPE_GROUP_CARD: u32 = 0;
const COLUMN_TYPE_ACTOR_ID: u32 = 1;
const COLUMN_TYPE_INT_RLE: u32 = 2;
const COLUMN_TYPE_INT_DELTA: u32 = 3;
const COLUMN_TYPE_BOOLEAN: u32 = 4;
const COLUMN_TYPE_STRING_RLE: u32 = 5;
const COLUMN_TYPE_VALUE_LEN: u32 = 6;
const COLUMN_TYPE_VALUE_RAW: u32 = 7;
pub(crate) const COLUMN_TYPE_GROUP_CARD: u32 = 0;
pub(crate) const COLUMN_TYPE_ACTOR_ID: u32 = 1;
pub(crate) const COLUMN_TYPE_INT_RLE: u32 = 2;
pub(crate) const COLUMN_TYPE_INT_DELTA: u32 = 3;
pub(crate) const COLUMN_TYPE_BOOLEAN: u32 = 4;
pub(crate) const COLUMN_TYPE_STRING_RLE: u32 = 5;
pub(crate) const COLUMN_TYPE_VALUE_LEN: u32 = 6;
pub(crate) const COLUMN_TYPE_VALUE_RAW: u32 = 7;
#[derive(PartialEq, Debug, Clone, Copy)]
#[repr(u32)]
enum Action {
pub(crate) enum Action {
Set,
Del,
Inc,
@ -984,167 +628,22 @@ impl Decodable for Action {
}
}
const COL_OBJ_ACTOR: u32 = COLUMN_TYPE_ACTOR_ID;
const COL_OBJ_CTR: u32 = COLUMN_TYPE_INT_RLE;
const COL_KEY_ACTOR: u32 = 1 << 3 | COLUMN_TYPE_ACTOR_ID;
const COL_KEY_CTR: u32 = 1 << 3 | COLUMN_TYPE_INT_DELTA;
const COL_KEY_STR: u32 = 1 << 3 | COLUMN_TYPE_STRING_RLE;
//const COL_ID_ACTOR : u32 = 2 << 3 | COLUMN_TYPE_ACTOR_ID;
//const COL_ID_CTR : u32 = 2 << 3 | COLUMN_TYPE_INT_DELTA;
const COL_INSERT: u32 = 3 << 3 | COLUMN_TYPE_BOOLEAN;
const COL_ACTION: u32 = 4 << 3 | COLUMN_TYPE_INT_RLE;
const COL_VAL_LEN: u32 = 5 << 3 | COLUMN_TYPE_VALUE_LEN;
const COL_VAL_RAW: u32 = 5 << 3 | COLUMN_TYPE_VALUE_RAW;
const COL_CHILD_ACTOR: u32 = 6 << 3 | COLUMN_TYPE_ACTOR_ID;
const COL_CHILD_CTR: u32 = 6 << 3 | COLUMN_TYPE_INT_DELTA;
const COL_PRED_NUM: u32 = 7 << 3 | COLUMN_TYPE_GROUP_CARD;
const COL_PRED_ACTOR: u32 = 7 << 3 | COLUMN_TYPE_ACTOR_ID;
const COL_PRED_CTR: u32 = 7 << 3 | COLUMN_TYPE_INT_DELTA;
//const COL_SUCC_NUM : u32 = 8 << 3 | COLUMN_TYPE_GROUP_CARD;
//const COL_SUCC_ACTOR : u32 = 8 << 3 | COLUMN_TYPE_ACTOR_ID;
//const COL_SUCC_CTR : u32 = 8 << 3 | COLUMN_TYPE_INT_DELTA;
const MAGIC_BYTES: [u8; 4] = [0x85, 0x6f, 0x4a, 0x83];
#[cfg(test)]
mod tests {
use super::*;
//use std::str::FromStr;
#[test]
fn test_empty_change() {
let mut change1 = Change {
start_op: 1,
seq: 2,
time: 1234,
message: None,
actor_id: ActorID("deadbeefdeadbeef".into()),
deps: vec![],
operations: vec![],
hash: ChangeHash::zero(),
};
let bin1 = change_to_bin(&change1).unwrap();
let changes2 = bin_to_changes(&bin1).unwrap();
let bin2 = change_to_bin(&changes2[0]).unwrap();
change1.hash = change_hash(change1.clone()).unwrap();
assert_eq!(bin1, bin2);
assert_eq!(vec![change1], changes2);
}
#[test]
fn test_complex_change() -> Result<(), AutomergeError> {
let actor1 = ActorID("deadbeefdeadbeef".into());
let actor2 = ActorID("feeddefaff".into());
let actor3 = ActorID("00101010fafafafa".into());
let opid1 = OpID(102, actor1.0.clone());
let opid2 = OpID(391, actor1.0.clone());
let opid3 = OpID(299, actor2.0.clone());
let opid4 = OpID(762, actor3.0);
let opid5 = OpID(100_203, actor2.0);
let obj1 = ObjectID::ID(opid1.clone());
let obj2 = ObjectID::Root;
let obj3 = ObjectID::ID(opid4.clone());
let key1 = Key::Map("field1".into());
let key2 = Key::Map("field2".into());
let key3 = Key::Map("field3".into());
let head = Key::head();
let keyseq1 = Key::from(&opid1);
let keyseq2 = Key::from(&opid2);
let insert = false;
let mut change1 = Change {
hash: ChangeHash::zero(),
start_op: 123,
seq: 29291,
time: 12_341_231,
message: Some("This is my message".into()),
actor_id: actor1,
deps: vec![],
operations: vec![
Operation {
action: OpType::Set(Value::F64(10.0)),
key: key1.clone(),
obj: obj1.clone(),
insert,
pred: vec![opid1.clone(), opid2.clone()],
},
Operation {
action: OpType::Set(Value::Counter(-11)),
key: key2.clone(),
obj: obj1.clone(),
insert,
pred: vec![opid1.clone(), opid2.clone()],
},
Operation {
action: OpType::Set(Value::Timestamp(20)),
key: key3,
obj: obj1.clone(),
insert,
pred: vec![opid1.clone(), opid2],
},
Operation {
action: OpType::Set(Value::Str("some value".into())),
key: key2.clone(),
obj: obj2.clone(),
insert,
pred: vec![opid3.clone(), opid4.clone()],
},
Operation {
action: OpType::Make(ObjType::List),
key: key2.clone(),
obj: obj2.clone(),
insert,
pred: vec![opid3.clone(), opid4.clone()],
},
Operation {
action: OpType::Set(Value::Str("val1".into())),
key: head.clone(),
obj: obj3.clone(),
insert: true,
pred: vec![opid3.clone(), opid4.clone()],
},
Operation {
action: OpType::Set(Value::Str("val2".into())),
key: head,
obj: obj3.clone(),
insert: true,
pred: vec![opid4.clone(), opid5.clone()],
},
Operation {
action: OpType::Inc(10),
key: key2,
obj: obj2,
insert,
pred: vec![opid1.clone(), opid5.clone()],
},
Operation {
action: OpType::Link(obj3.clone()),
obj: obj1,
key: key1,
insert,
pred: vec![opid1, opid3],
},
Operation {
action: OpType::Del,
obj: obj3.clone(),
key: keyseq1,
insert: true,
pred: vec![opid4.clone(), opid5.clone()],
},
Operation {
action: OpType::Del,
obj: obj3,
key: keyseq2,
insert: true,
pred: vec![opid4, opid5],
},
],
};
change1.hash = change_hash(change1.clone()).unwrap();
let bin1 = change_to_bin(&change1)?;
let changes2 = bin_to_changes(&bin1)?;
let bin2 = change_to_bin(&changes2[0])?;
assert_eq!(bin1, bin2);
assert_eq!(vec![change1], changes2);
Ok(())
}
}
pub(crate) const COL_OBJ_ACTOR: u32 = COLUMN_TYPE_ACTOR_ID;
pub(crate) const COL_OBJ_CTR: u32 = COLUMN_TYPE_INT_RLE;
pub(crate) const COL_KEY_ACTOR: u32 = 1 << 3 | COLUMN_TYPE_ACTOR_ID;
pub(crate) const COL_KEY_CTR: u32 = 1 << 3 | COLUMN_TYPE_INT_DELTA;
pub(crate) const COL_KEY_STR: u32 = 1 << 3 | COLUMN_TYPE_STRING_RLE;
//pub(crate) const COL_ID_ACTOR : u32 = 2 << 3 | COLUMN_TYPE_ACTOR_ID;
//pub(crate) const COL_ID_CTR : u32 = 2 << 3 | COLUMN_TYPE_INT_DELTA;
pub(crate) const COL_INSERT: u32 = 3 << 3 | COLUMN_TYPE_BOOLEAN;
pub(crate) const COL_ACTION: u32 = 4 << 3 | COLUMN_TYPE_INT_RLE;
pub(crate) const COL_VAL_LEN: u32 = 5 << 3 | COLUMN_TYPE_VALUE_LEN;
pub(crate) const COL_VAL_RAW: u32 = 5 << 3 | COLUMN_TYPE_VALUE_RAW;
pub(crate) const COL_CHILD_ACTOR: u32 = 6 << 3 | COLUMN_TYPE_ACTOR_ID;
pub(crate) const COL_CHILD_CTR: u32 = 6 << 3 | COLUMN_TYPE_INT_DELTA;
pub(crate) const COL_PRED_NUM: u32 = 7 << 3 | COLUMN_TYPE_GROUP_CARD;
pub(crate) const COL_PRED_ACTOR: u32 = 7 << 3 | COLUMN_TYPE_ACTOR_ID;
pub(crate) const COL_PRED_CTR: u32 = 7 << 3 | COLUMN_TYPE_INT_DELTA;
//pub(crate) const COL_SUCC_NUM : u32 = 8 << 3 | COLUMN_TYPE_GROUP_CARD;
//pub(crate) const COL_SUCC_ACTOR : u32 = 8 << 3 | COLUMN_TYPE_ACTOR_ID;
//pub(crate) const COL_SUCC_CTR : u32 = 8 << 3 | COLUMN_TYPE_INT_DELTA;

View file

@ -1,6 +1,6 @@
use crate::error::AutomergeError;
use crate::op_handle::OpHandle;
use automerge_protocol::OpType;
use crate::op_type::OpType;
use std::ops::Deref;
/// Represents a set of operations which are relevant to either an element ID

View file

@ -7,8 +7,8 @@ use std::io::{Read, Write};
use std::mem;
use std::str;
fn err(s: &str) -> AutomergeError {
AutomergeError::ChangeDecompressError(s.to_string())
fn err(_s: &str) -> AutomergeError {
AutomergeError::EncodingError
}
#[derive(Clone, Debug)]
@ -29,7 +29,7 @@ impl<'a> Decoder<'a> {
pub fn read<T: Decodable + Debug>(&mut self) -> Result<T, AutomergeError> {
let mut new_buf = &self.buf[..];
let val = T::decode::<&[u8]>(&mut new_buf).ok_or(AutomergeError::DecodeFailed)?;
let val = T::decode::<&[u8]>(&mut new_buf).ok_or(AutomergeError::EncodingError)?;
let delta = self.buf.len() - new_buf.len();
if delta == 0 {
Err(err("buffer size didnt change..."))
@ -44,7 +44,7 @@ impl<'a> Decoder<'a> {
pub fn read_bytes(&mut self, index: usize) -> Result<&'a [u8], AutomergeError> {
let buf = &self.buf[..];
if buf.len() < index {
Err(AutomergeError::DecodeFailed)
Err(AutomergeError::EncodingError)
} else {
let head = &buf[0..index];
self.buf = &buf[index..];
@ -526,7 +526,7 @@ impl Decodable for ActorID {
R: Read,
{
let buffer = Vec::decode(bytes)?;
Some(ActorID::from_bytes(&buffer))
Some(buffer.into())
}
}

View file

@ -1,4 +1,4 @@
use automerge_protocol::{ObjectID, OpID, OpRequest};
use automerge_protocol::{ObjectID, Op, OpID};
use std::error::Error;
use std::fmt;
@ -19,18 +19,26 @@ pub enum AutomergeError {
NoUndo,
MissingValue,
GeneralError(String),
MissingNumberValue(OpRequest),
MissingNumberValue(Op),
UnknownVersion(u64),
DuplicateChange(String),
DivergedState(String),
ChangeDecompressError(String),
MapKeyInSeq,
HeadToOpID,
DocFormatUnimplemented,
DivergentChange(String),
EncodeFailed,
DecodeFailed,
InvalidChange,
ChangeBadFormat,
EncodingError,
}
impl From<automerge_protocol::error::InvalidChangeHashSlice> for AutomergeError {
fn from(_: automerge_protocol::error::InvalidChangeHashSlice) -> AutomergeError {
AutomergeError::ChangeBadFormat
}
}
impl fmt::Display for AutomergeError {
@ -51,3 +59,15 @@ impl fmt::Display for InvalidElementID {
}
impl Error for InvalidElementID {}
impl From<leb128::read::Error> for AutomergeError {
fn from(_err: leb128::read::Error) -> Self {
AutomergeError::EncodingError
}
}
impl From<std::io::Error> for AutomergeError {
fn from(_err: std::io::Error) -> Self {
AutomergeError::EncodingError
}
}

View file

@ -1,9 +1,7 @@
extern crate hex;
extern crate im_rc;
extern crate leb128;
extern crate maplit;
extern crate rand;
extern crate sha2;
extern crate uuid;
extern crate web_sys;
@ -16,20 +14,24 @@ macro_rules! log {
mod actor_map;
mod backend;
mod change;
mod columnar;
mod concurrent_operations;
mod encoding;
mod error;
mod object_store;
mod op;
mod op_handle;
mod op_set;
mod op_type;
mod ordered_set;
mod pending_diff;
mod serialize;
mod time;
mod undo_operation;
pub use automerge_protocol::Change;
pub use backend::Backend;
pub use columnar::change_hash;
pub use change::{Change, UnencodedChange};
pub use error::AutomergeError;
pub use op::Operation;
pub use op_type::OpType;

246
automerge-backend/src/op.rs Normal file
View file

@ -0,0 +1,246 @@
// FIXME
use crate::op_type::OpType;
use automerge_protocol as amp;
use serde::ser::SerializeStruct;
use serde::{
de::{Error, MapAccess, Unexpected, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
fn read_field<'de, T, M>(
name: &'static str,
data: &mut Option<T>,
map: &mut M,
) -> Result<(), M::Error>
where
M: MapAccess<'de>,
T: Deserialize<'de>,
{
if data.is_some() {
Err(Error::duplicate_field(name))
} else {
data.replace(map.next_value()?);
Ok(())
}
}
#[derive(PartialEq, Debug, Clone)]
pub struct Operation {
pub action: OpType,
pub obj: amp::ObjectID,
pub key: amp::Key,
pub pred: Vec<amp::OpID>,
pub insert: bool,
}
impl Operation {
pub fn set(
obj: amp::ObjectID,
key: amp::Key,
value: amp::Value,
pred: Vec<amp::OpID>,
) -> Operation {
Operation {
action: OpType::Set(value),
obj,
key,
insert: false,
pred,
}
}
pub fn insert(
obj: amp::ObjectID,
key: amp::Key,
value: amp::Value,
pred: Vec<amp::OpID>,
) -> Operation {
Operation {
action: OpType::Set(value),
obj,
key,
insert: true,
pred,
}
}
pub fn inc(obj: amp::ObjectID, key: amp::Key, value: i64, pred: Vec<amp::OpID>) -> Operation {
Operation {
action: OpType::Inc(value),
obj,
key,
insert: false,
pred,
}
}
pub fn del(obj: amp::ObjectID, key: amp::Key, pred: Vec<amp::OpID>) -> Operation {
Operation {
action: OpType::Del,
obj,
key,
insert: false,
pred,
}
}
pub fn is_make(&self) -> bool {
self.obj_type().is_some()
}
pub fn is_basic_assign(&self) -> bool {
!self.insert
&& match self.action {
OpType::Del | OpType::Set(_) | OpType::Inc(_) | OpType::Link(_) => true,
_ => false,
}
}
pub fn is_inc(&self) -> bool {
match self.action {
OpType::Inc(_) => true,
_ => false,
}
}
pub fn obj_type(&self) -> Option<amp::ObjType> {
match self.action {
OpType::Make(t) => Some(t),
_ => None,
}
}
}
impl Serialize for Operation {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut fields = 4;
if self.insert {
fields += 1
}
match &self.action {
OpType::Set(amp::Value::Timestamp(_)) => fields += 2,
OpType::Set(amp::Value::Counter(_)) => fields += 2,
OpType::Link(_) | OpType::Inc(_) | OpType::Set(_) => fields += 1,
_ => {}
}
let mut op = serializer.serialize_struct("Operation", fields)?;
op.serialize_field("action", &self.action)?;
op.serialize_field("obj", &self.obj)?;
op.serialize_field("key", &self.key)?;
if self.insert {
op.serialize_field("insert", &self.insert)?;
}
match &self.action {
OpType::Link(child) => op.serialize_field("child", &child)?,
OpType::Inc(n) => op.serialize_field("value", &n)?,
OpType::Set(amp::Value::Counter(value)) => {
op.serialize_field("value", &value)?;
op.serialize_field("datatype", &amp::DataType::Counter)?;
}
OpType::Set(amp::Value::Timestamp(value)) => {
op.serialize_field("value", &value)?;
op.serialize_field("datatype", &amp::DataType::Timestamp)?;
}
OpType::Set(value) => op.serialize_field("value", &value)?,
_ => {}
}
op.serialize_field("pred", &self.pred)?;
op.end()
}
}
impl<'de> Deserialize<'de> for Operation {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
const FIELDS: &[&str] = &["ops", "deps", "message", "seq", "actor", "requestType"];
struct OperationVisitor;
impl<'de> Visitor<'de> for OperationVisitor {
type Value = Operation;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("An operation object")
}
fn visit_map<V>(self, mut map: V) -> Result<Operation, V::Error>
where
V: MapAccess<'de>,
{
let mut action: Option<amp::OpType> = None;
let mut obj: Option<amp::ObjectID> = None;
let mut key: Option<amp::Key> = None;
let mut pred: Option<Vec<amp::OpID>> = None;
let mut insert: Option<bool> = None;
let mut datatype: Option<amp::DataType> = None;
let mut value: Option<amp::Value> = None;
let mut child: Option<amp::ObjectID> = None;
while let Some(field) = map.next_key::<String>()? {
match field.as_ref() {
"action" => read_field("action", &mut action, &mut map)?,
"obj" => read_field("obj", &mut obj, &mut map)?,
"key" => read_field("key", &mut key, &mut map)?,
"pred" => read_field("pred", &mut pred, &mut map)?,
"insert" => read_field("insert", &mut insert, &mut map)?,
"datatype" => read_field("datatype", &mut datatype, &mut map)?,
"value" => read_field("value", &mut value, &mut map)?,
"child" => read_field("child", &mut child, &mut map)?,
_ => return Err(Error::unknown_field(&field, FIELDS)),
}
}
let action = action.ok_or_else(|| Error::missing_field("action"))?;
let obj = obj.ok_or_else(|| Error::missing_field("obj"))?;
let key = key.ok_or_else(|| Error::missing_field("key"))?;
let pred = pred.ok_or_else(|| Error::missing_field("pred"))?;
let insert = insert.unwrap_or(false);
let value = amp::Value::from(value, datatype);
let action = match action {
amp::OpType::MakeMap => OpType::Make(amp::ObjType::Map),
amp::OpType::MakeTable => OpType::Make(amp::ObjType::Table),
amp::OpType::MakeList => OpType::Make(amp::ObjType::List),
amp::OpType::MakeText => OpType::Make(amp::ObjType::Text),
amp::OpType::Del => OpType::Del,
amp::OpType::Link => {
OpType::Link(child.ok_or_else(|| Error::missing_field("pred"))?)
}
amp::OpType::Set => OpType::Set(
amp::Value::from(value, datatype)
.ok_or_else(|| Error::missing_field("value"))?,
),
amp::OpType::Inc => match value {
Some(amp::Value::Int(n)) => Ok(OpType::Inc(n)),
Some(amp::Value::Uint(n)) => Ok(OpType::Inc(n as i64)),
Some(amp::Value::F64(n)) => Ok(OpType::Inc(n as i64)),
Some(amp::Value::F32(n)) => Ok(OpType::Inc(n as i64)),
Some(amp::Value::Counter(n)) => Ok(OpType::Inc(n)),
Some(amp::Value::Timestamp(n)) => Ok(OpType::Inc(n)),
Some(amp::Value::Str(s)) => {
Err(Error::invalid_value(Unexpected::Str(&s), &"a number"))
}
Some(amp::Value::Boolean(b)) => {
Err(Error::invalid_value(Unexpected::Bool(b), &"a number"))
}
Some(amp::Value::Null) => {
Err(Error::invalid_value(Unexpected::Other("null"), &"a number"))
}
None => Err(Error::missing_field("value")),
}?,
};
Ok(Operation {
action,
obj,
key,
insert,
pred,
})
}
}
deserializer.deserialize_struct("Operation", &FIELDS, OperationVisitor)
}
}

View file

@ -4,29 +4,34 @@ use std::hash::{Hash, Hasher};
use std::ops::Deref;
use std::rc::Rc;
use crate::op::Operation;
use crate::op_type::OpType;
use crate::undo_operation::UndoOperation;
use automerge_protocol::{Change, Key, ObjectID, OpID, OpType, Operation, Value};
use crate::Change;
use automerge_protocol::{Key, ObjectID, OpID, Value};
#[derive(Clone)]
pub(crate) struct OpHandle {
pub id: OpID,
change: Rc<Change>,
index: usize,
op: Operation,
//change: Rc<Change>,
//index: usize,
delta: i64,
}
impl OpHandle {
pub fn extract(change: Rc<Change>) -> Vec<OpHandle> {
change
.operations
.iter()
.iter_ops()
// .iter()
.enumerate()
.map(|(index, _)| {
let id = OpID(change.start_op + (index as u64), change.actor_id.0.clone());
.map(|(index, op)| {
let id = OpID::new(change.start_op + (index as u64), &change.actor_id());
OpHandle {
id,
change: change.clone(),
index,
op,
//change: change.clone(),
//index,
delta: 0,
}
})
@ -54,7 +59,7 @@ impl OpHandle {
}
pub fn invert(&self, field_key: &Key) -> UndoOperation {
let base_op = &self.change.operations[self.index];
let base_op = &self.op;
let mut action = base_op.action.clone();
let mut key = &base_op.key;
if self.insert {
@ -149,6 +154,6 @@ impl Deref for OpHandle {
type Target = Operation;
fn deref(&self) -> &Self::Target {
&self.change.operations[self.index]
&self.op
}
}

View file

@ -11,11 +11,12 @@ use crate::concurrent_operations::ConcurrentOperations;
use crate::error::AutomergeError;
use crate::object_store::ObjState;
use crate::op_handle::OpHandle;
use crate::op_type::OpType;
use crate::ordered_set::OrderedSet;
use crate::pending_diff::PendingDiff;
use crate::undo_operation::UndoOperation;
use automerge_protocol::{
ChangeHash, Diff, DiffEdit, Key, MapDiff, ObjDiff, ObjType, ObjectID, OpID, OpType, SeqDiff,
ChangeHash, Diff, DiffEdit, Key, MapDiff, ObjDiff, ObjType, ObjectID, OpID, SeqDiff,
};
use core::cmp::max;
use std::collections::HashMap;

View file

@ -1,6 +1,15 @@
use crate::{ObjType, OpType};
use automerge_protocol::{ObjType, ObjectID, Value};
use serde::{Serialize, Serializer};
#[derive(PartialEq, Debug, Clone)]
pub enum OpType {
Make(ObjType),
Del,
Link(ObjectID),
Inc(i64),
Set(Value),
}
impl Serialize for OpType {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where

View file

@ -1,5 +1,6 @@
use crate::op_type::OpType;
use crate::undo_operation::UndoOperation;
use automerge_protocol::{DataType, OpType, Value};
use automerge_protocol::{DataType, Value};
use serde::ser::SerializeStruct;
use serde::{Serialize, Serializer};

View file

@ -1,4 +1,6 @@
use automerge_protocol::{Key, ObjectID, OpID, OpType, Operation};
use crate::op::Operation;
use crate::op_type::OpType;
use automerge_protocol::{Key, ObjectID, OpID};
#[derive(PartialEq, Debug, Clone)]
pub struct UndoOperation {

View file

@ -1,8 +1,9 @@
extern crate automerge_backend;
use automerge_backend::{change_hash, AutomergeError, Backend};
use automerge_backend::{AutomergeError, Backend, UnencodedChange};
use automerge_backend::{OpType, Operation};
use automerge_protocol::{
ActorID, Change, ChangeHash, Diff, DiffEdit, ElementID, Key, MapDiff, ObjDiff, ObjType,
ObjectID, OpType, Operation, Patch, SeqDiff, Value,
ActorID, Diff, DiffEdit, ElementID, Key, MapDiff, ObjDiff, ObjType, ObjectID, Patch, SeqDiff,
Value,
};
use maplit::hashmap;
use std::convert::TryInto;
@ -10,13 +11,12 @@ use std::str::FromStr;
#[test]
fn test_incremental_diffs_in_a_map() {
let mut change = Change {
actor_id: "7b7723afd9e6480397a4d467b7693156".into(),
let change = UnencodedChange {
actor_id: "7b7723afd9e6480397a4d467b7693156".try_into().unwrap(),
seq: 1,
start_op: 1,
time: 0,
message: None,
hash: ChangeHash::zero(),
deps: Vec::new(),
operations: vec![Operation::set(
ObjectID::Root,
@ -24,8 +24,9 @@ fn test_incremental_diffs_in_a_map() {
"magpie".into(),
vec![],
)],
};
change.hash = change_hash(change.clone()).unwrap();
}
.encode();
let mut backend = Backend::init();
let patch = backend.apply_changes(vec![change.clone()]).unwrap();
let expected_patch = Patch {
@ -47,39 +48,37 @@ fn test_incremental_diffs_in_a_map() {
#[test]
fn test_increment_key_in_map() -> Result<(), AutomergeError> {
let mut change1 = Change {
actor_id: "cdee6963c1664645920be8b41a933c2b".into(),
let change1 = UnencodedChange {
actor_id: "cdee6963c1664645920be8b41a933c2b".try_into().unwrap(),
seq: 1,
start_op: 1,
time: 0,
message: None,
deps: Vec::new(),
hash: ChangeHash::zero(),
operations: vec![Operation::set(
ObjectID::Root,
"counter".into(),
Value::Counter(1),
vec![],
)],
};
change1.hash = change_hash(change1.clone()).unwrap();
}
.encode();
let mut change2 = Change {
actor_id: "cdee6963c1664645920be8b41a933c2b".into(),
let change2 = UnencodedChange {
actor_id: "cdee6963c1664645920be8b41a933c2b".try_into().unwrap(),
seq: 2,
start_op: 2,
time: 2,
message: None,
deps: vec![change1.hash],
hash: ChangeHash::zero(),
operations: vec![Operation::inc(
ObjectID::Root,
"counter".into(),
2,
vec!["1@cdee6963c1664645920be8b41a933c2b".try_into().unwrap()],
)],
};
change2.hash = change_hash(change2.clone()).unwrap();
}
.encode();
let expected_patch = Patch {
version: 2,
@ -110,29 +109,27 @@ fn test_increment_key_in_map() -> Result<(), AutomergeError> {
#[test]
fn test_conflict_on_assignment_to_same_map_key() {
let mut change1 = Change {
actor_id: "ac11".into(),
let change1 = UnencodedChange {
actor_id: ActorID::from_str("ac11").unwrap(),
seq: 1,
message: None,
start_op: 1,
time: 0,
deps: Vec::new(),
hash: ChangeHash::zero(),
operations: vec![Operation::set(
ObjectID::Root,
"bird".into(),
"magpie".into(),
vec![],
)],
};
change1.hash = change_hash(change1.clone()).unwrap();
}
.encode();
let mut change2 = Change {
actor_id: "ac22".into(),
let change2 = UnencodedChange {
actor_id: ActorID::from_str("ac22").unwrap(),
start_op: 2,
seq: 1,
message: None,
hash: ChangeHash::zero(),
deps: vec![change1.hash],
time: 0,
operations: vec![Operation::set(
@ -141,8 +138,8 @@ fn test_conflict_on_assignment_to_same_map_key() {
"blackbird".into(),
vec![],
)],
};
change2.hash = change_hash(change2.clone()).unwrap();
}
.encode();
let expected_patch = Patch {
version: 2,
@ -176,15 +173,14 @@ fn test_conflict_on_assignment_to_same_map_key() {
#[test]
fn delete_key_from_map() {
let actor: ActorID = "cd86c07f109348f494af5be30fdc4c71".into();
let mut change1 = Change {
let actor: ActorID = "cd86c07f109348f494af5be30fdc4c71".try_into().unwrap();
let change1 = UnencodedChange {
actor_id: actor.clone(),
seq: 1,
start_op: 1,
time: 0,
message: None,
deps: Vec::new(),
hash: ChangeHash::zero(),
operations: vec![Operation {
action: OpType::Set(Value::Str("magpie".into())),
obj: ObjectID::Root,
@ -192,17 +188,16 @@ fn delete_key_from_map() {
pred: Vec::new(),
insert: false,
}],
};
change1.hash = change_hash(change1.clone()).unwrap();
}
.encode();
let mut change2 = Change {
let change2 = UnencodedChange {
actor_id: actor.clone(),
seq: 2,
start_op: 2,
time: 0,
message: None,
deps: vec![change1.hash],
hash: ChangeHash::zero(),
operations: vec![Operation {
action: OpType::Del,
obj: ObjectID::Root,
@ -210,8 +205,8 @@ fn delete_key_from_map() {
pred: vec!["1@cd86c07f109348f494af5be30fdc4c71".try_into().unwrap()],
insert: false,
}],
};
change2.hash = change_hash(change2.clone()).unwrap();
}
.encode();
let expected_patch = Patch {
actor: None,
@ -239,14 +234,13 @@ fn delete_key_from_map() {
#[test]
fn create_nested_maps() {
let actor: ActorID = "d6226fcd55204b82b396f2473da3e26f".try_into().unwrap();
let mut change = Change {
let change = UnencodedChange {
actor_id: actor.clone(),
seq: 1,
start_op: 1,
time: 0,
deps: Vec::new(),
message: None,
hash: ChangeHash::zero(),
operations: vec![
Operation {
action: OpType::Make(ObjType::Map),
@ -263,8 +257,8 @@ fn create_nested_maps() {
insert: false,
},
],
};
change.hash = change_hash(change.clone()).unwrap();
}
.encode();
let expected_patch: Patch = Patch {
actor: None,
@ -301,9 +295,8 @@ fn create_nested_maps() {
#[test]
fn test_assign_to_nested_keys_in_map() {
let actor: ActorID = "3c39c994039042778f4779a01a59a917".try_into().unwrap();
let mut change1 = Change {
let change1 = UnencodedChange {
actor_id: actor.clone(),
hash: ChangeHash::zero(),
seq: 1,
start_op: 1,
time: 0,
@ -325,17 +318,16 @@ fn test_assign_to_nested_keys_in_map() {
insert: false,
},
],
};
change1.hash = change_hash(change1.clone()).unwrap();
}
.encode();
let mut change2 = Change {
let change2 = UnencodedChange {
actor_id: actor.clone(),
seq: 2,
start_op: 3,
time: 0,
deps: vec![change1.hash],
message: None,
hash: ChangeHash::zero(),
operations: vec![Operation {
action: OpType::Set(Value::F64(15.0)),
obj: "1@3c39c994039042778f4779a01a59a917".try_into().unwrap(),
@ -343,8 +335,8 @@ fn test_assign_to_nested_keys_in_map() {
pred: Vec::new(),
insert: false,
}],
};
change2.hash = change_hash(change2.clone()).unwrap();
}
.encode();
let expected_patch = Patch {
version: 2,
@ -383,14 +375,13 @@ fn test_assign_to_nested_keys_in_map() {
#[test]
fn test_create_lists() {
let mut change = Change {
let change = UnencodedChange {
actor_id: "f82cb62dabe64372ab87466b77792010".try_into().unwrap(),
seq: 1,
start_op: 1,
time: 0,
message: None,
deps: Vec::new(),
hash: ChangeHash::zero(),
operations: vec![
Operation {
action: OpType::Make(ObjType::List),
@ -407,8 +398,8 @@ fn test_create_lists() {
pred: Vec::new(),
},
],
};
change.hash = change_hash(change.clone()).unwrap();
}
.encode();
let expected_patch = Patch {
version: 1,
@ -448,14 +439,13 @@ fn test_create_lists() {
#[test]
fn test_apply_updates_inside_lists() {
let actor: ActorID = "4ee4a0d033b841c4b26d73d70a879547".try_into().unwrap();
let mut change1 = Change {
let change1 = UnencodedChange {
actor_id: actor.clone(),
seq: 1,
start_op: 1,
time: 0,
message: None,
deps: Vec::new(),
hash: ChangeHash::zero(),
operations: vec![
Operation {
action: OpType::Make(ObjType::List),
@ -472,17 +462,16 @@ fn test_apply_updates_inside_lists() {
insert: true,
},
],
};
change1.hash = change_hash(change1.clone()).unwrap();
}
.encode();
let mut change2 = Change {
let change2 = UnencodedChange {
actor_id: actor.clone(),
seq: 2,
start_op: 3,
time: 0,
deps: vec![change1.hash],
message: None,
hash: ChangeHash::zero(),
operations: vec![Operation {
action: OpType::Set(Value::Str("greenfinch".into())),
obj: "1@4ee4a0d033b841c4b26d73d70a879547".try_into().unwrap(),
@ -490,8 +479,8 @@ fn test_apply_updates_inside_lists() {
pred: vec!["2@4ee4a0d033b841c4b26d73d70a879547".try_into().unwrap()],
insert: false,
}],
};
change2.hash = change_hash(change2.clone()).unwrap();
}
.encode();
let expected_patch = Patch {
actor: None,
@ -532,14 +521,13 @@ fn test_apply_updates_inside_lists() {
#[test]
fn test_delete_list_elements() {
let actor: ActorID = "8a3d4716fdca49f4aa5835901f2034c7".try_into().unwrap();
let mut change1 = Change {
let change1 = UnencodedChange {
actor_id: actor.clone(),
seq: 1,
start_op: 1,
time: 0,
message: None,
deps: Vec::new(),
hash: ChangeHash::zero(),
operations: vec![
Operation {
action: OpType::Make(ObjType::List),
@ -556,17 +544,16 @@ fn test_delete_list_elements() {
pred: Vec::new(),
},
],
};
change1.hash = change_hash(change1.clone()).unwrap();
}
.encode();
let mut change2 = Change {
let change2 = UnencodedChange {
actor_id: actor.clone(),
seq: 2,
start_op: 3,
time: 0,
message: None,
deps: vec![change1.hash],
hash: ChangeHash::zero(),
operations: vec![Operation {
action: OpType::Del,
obj: "1@8a3d4716fdca49f4aa5835901f2034c7".try_into().unwrap(),
@ -576,8 +563,8 @@ fn test_delete_list_elements() {
pred: vec!["2@8a3d4716fdca49f4aa5835901f2034c7".try_into().unwrap()],
insert: false,
}],
};
change2.hash = change_hash(change2.clone()).unwrap();
}
.encode();
let expected_patch = Patch {
version: 2,
@ -614,14 +601,13 @@ fn test_delete_list_elements() {
#[test]
fn test_handle_list_element_insertion_and_deletion_in_same_change() {
let actor: ActorID = "ca95bc759404486bbe7b9dd2be779fa8".try_into().unwrap();
let mut change1 = Change {
let change1 = UnencodedChange {
actor_id: actor.clone(),
seq: 1,
start_op: 1,
time: 0,
message: None,
deps: Vec::new(),
hash: ChangeHash::zero(),
operations: vec![Operation {
action: OpType::Make(ObjType::List),
obj: ObjectID::Root,
@ -629,17 +615,16 @@ fn test_handle_list_element_insertion_and_deletion_in_same_change() {
pred: Vec::new(),
insert: false,
}],
};
change1.hash = change_hash(change1.clone()).unwrap();
}
.encode();
let mut change2 = Change {
let change2 = UnencodedChange {
actor_id: actor.clone(),
seq: 2,
start_op: 2,
time: 0,
message: None,
deps: Vec::new(),
hash: ChangeHash::zero(),
operations: vec![
Operation {
action: OpType::Set(Value::Str("chaffinch".into())),
@ -658,8 +643,8 @@ fn test_handle_list_element_insertion_and_deletion_in_same_change() {
insert: false,
},
],
};
change2.hash = change_hash(change2.clone()).unwrap();
}
.encode();
let expected_patch = Patch {
version: 2,
@ -700,13 +685,12 @@ fn test_handle_list_element_insertion_and_deletion_in_same_change() {
fn test_handle_changes_within_conflicted_objects() {
let actor1: ActorID = "9f17517523e54ee888e9cd51dfd7a572".try_into().unwrap();
let actor2: ActorID = "83768a19a13842beb6dde8c68a662fad".try_into().unwrap();
let mut change1 = Change {
let change1 = UnencodedChange {
actor_id: actor1.clone(),
seq: 1,
start_op: 1,
time: 0,
message: None,
hash: ChangeHash::zero(),
deps: Vec::new(),
operations: vec![Operation {
action: OpType::Make(ObjType::List),
@ -715,16 +699,15 @@ fn test_handle_changes_within_conflicted_objects() {
pred: Vec::new(),
insert: false,
}],
};
change1.hash = change_hash(change1.clone()).unwrap();
}
.encode();
let mut change2 = Change {
let change2 = UnencodedChange {
actor_id: actor2.clone(),
seq: 1,
start_op: 1,
time: 0,
message: None,
hash: ChangeHash::zero(),
deps: Vec::new(),
operations: vec![Operation {
action: OpType::Make(ObjType::Map),
@ -733,17 +716,16 @@ fn test_handle_changes_within_conflicted_objects() {
pred: Vec::new(),
insert: false,
}],
};
change2.hash = change_hash(change2.clone()).unwrap();
}
.encode();
let mut change3 = Change {
let change3 = UnencodedChange {
actor_id: actor2.clone(),
seq: 2,
start_op: 2,
time: 0,
message: None,
deps: vec![change2.hash],
hash: ChangeHash::zero(),
operations: vec![Operation {
action: OpType::Set(Value::F64(12.0)),
obj: "1@83768a19a13842beb6dde8c68a662fad".try_into().unwrap(),
@ -751,8 +733,8 @@ fn test_handle_changes_within_conflicted_objects() {
pred: Vec::new(),
insert: false,
}],
};
change3.hash = change_hash(change3.clone()).unwrap();
}
.encode();
let expected_patch = Patch {
version: 3,
@ -798,12 +780,11 @@ fn test_handle_changes_within_conflicted_objects() {
#[test]
fn test_support_date_objects_at_root() {
let actor: ActorID = "955afa3bbcc140b3b4bac8836479d650".try_into().unwrap();
let mut change = Change {
let change = UnencodedChange {
actor_id: actor.clone(),
seq: 1,
start_op: 1,
time: 0,
hash: ChangeHash::zero(),
deps: Vec::new(),
message: None,
operations: vec![Operation {
@ -813,8 +794,8 @@ fn test_support_date_objects_at_root() {
pred: Vec::new(),
insert: false,
}],
};
change.hash = change_hash(change.clone()).unwrap();
}
.encode();
let expected_patch = Patch {
version: 1,
@ -845,13 +826,12 @@ fn test_support_date_objects_at_root() {
#[test]
fn test_support_date_objects_in_a_list() {
let actor: ActorID = "27d467ecb1a640fb9bed448ce7cf6a44".try_into().unwrap();
let mut change = Change {
let change = UnencodedChange {
actor_id: actor.clone(),
seq: 1,
start_op: 1,
time: 0,
deps: Vec::new(),
hash: ChangeHash::zero(),
message: None,
operations: vec![
Operation {
@ -869,8 +849,8 @@ fn test_support_date_objects_in_a_list() {
pred: Vec::new(),
},
],
};
change.hash = change_hash(change.clone()).unwrap();
}
.encode();
let expected_patch = Patch {
version: 1,

View file

@ -1,8 +1,10 @@
extern crate automerge_backend;
use automerge_backend::{change_hash, Backend};
use automerge_backend::{Backend, UnencodedChange};
use automerge_backend::{OpType, Operation};
use automerge_protocol as protocol;
use automerge_protocol::{
ActorID, Change, ChangeHash, ChangeRequest, ChangeRequestType, DataType, Diff, DiffEdit,
ElementID, MapDiff, ObjType, ObjectID, OpRequest, OpType, Operation, Patch, ReqOpType, SeqDiff,
ActorID, ChangeHash, DataType, Diff, DiffEdit, ElementID, MapDiff, ObjType, ObjectID, Op,
Patch, Request, RequestType, SeqDiff,
};
use maplit::hashmap;
use std::convert::TryInto;
@ -11,7 +13,7 @@ use std::{collections::HashSet, str::FromStr};
#[test]
fn test_apply_local_change() {
let actor: ActorID = "eb738e04ef8848ce8b77309b6c7f7e39".try_into().unwrap();
let change_request = ChangeRequest {
let change_request = Request {
actor: actor.clone(),
seq: 1,
version: 0,
@ -19,8 +21,8 @@ fn test_apply_local_change() {
undoable: false,
time: None,
deps: None,
ops: Some(vec![OpRequest {
action: ReqOpType::Set,
ops: Some(vec![Op {
action: protocol::OpType::Set,
value: Some("magpie".into()),
datatype: Some(DataType::Undefined),
key: "bird".into(),
@ -28,20 +30,20 @@ fn test_apply_local_change() {
child: None,
insert: false,
}]),
request_type: ChangeRequestType::Change,
request_type: RequestType::Change,
};
let mut backend = Backend::init();
let patch = backend.apply_local_change(change_request).unwrap();
let mut expected_change = Change {
let changes = backend.get_changes(&[]);
let expected_change = UnencodedChange {
actor_id: actor.clone(),
seq: 1,
start_op: 1,
time: 0,
time: changes[0].time,
message: None,
deps: Vec::new(),
hash: ChangeHash::zero(),
operations: vec![Operation {
action: OpType::Set("magpie".into()),
obj: ObjectID::Root,
@ -49,12 +51,9 @@ fn test_apply_local_change() {
pred: Vec::new(),
insert: false,
}],
};
let changes: Vec<&Change> = backend.get_changes_unserialized(&[]);
let change = changes[0];
expected_change.time = change.time;
expected_change.hash = change_hash(expected_change.clone()).unwrap();
assert_eq!(change, &expected_change);
}
.encode();
assert_eq!(changes[0], &expected_change);
let expected_patch = Patch {
actor: Some(actor.to_string()),
@ -65,7 +64,7 @@ fn test_apply_local_change() {
},
can_undo: false,
can_redo: false,
deps: vec![change.hash],
deps: vec![changes[0].hash],
diffs: Some(Diff::Map(MapDiff {
object_id: ObjectID::Root.to_string(),
obj_type: ObjType::Map,
@ -82,7 +81,7 @@ fn test_apply_local_change() {
#[test]
fn test_error_on_duplicate_requests() {
let actor: ActorID = "37704788917a499cb0206fa8519ac4d9".try_into().unwrap();
let change_request1 = ChangeRequest {
let change_request1 = Request {
actor: actor.clone(),
seq: 1,
version: 0,
@ -90,8 +89,8 @@ fn test_error_on_duplicate_requests() {
undoable: false,
time: None,
deps: None,
ops: Some(vec![OpRequest {
action: ReqOpType::Set,
ops: Some(vec![Op {
action: protocol::OpType::Set,
obj: ObjectID::Root.to_string(),
key: "bird".into(),
child: None,
@ -99,10 +98,10 @@ fn test_error_on_duplicate_requests() {
datatype: Some(DataType::Undefined),
insert: false,
}]),
request_type: ChangeRequestType::Change,
request_type: RequestType::Change,
};
let change_request2 = ChangeRequest {
let change_request2 = Request {
actor,
seq: 2,
version: 0,
@ -110,8 +109,8 @@ fn test_error_on_duplicate_requests() {
undoable: false,
time: None,
deps: None,
ops: Some(vec![OpRequest {
action: ReqOpType::Set,
ops: Some(vec![Op {
action: protocol::OpType::Set,
obj: ObjectID::Root.to_string(),
key: "bird".into(),
value: Some("jay".into()),
@ -119,7 +118,7 @@ fn test_error_on_duplicate_requests() {
insert: false,
datatype: Some(DataType::Undefined),
}]),
request_type: ChangeRequestType::Change,
request_type: RequestType::Change,
};
let mut backend = Backend::init();
backend.apply_local_change(change_request1.clone()).unwrap();
@ -131,7 +130,7 @@ fn test_error_on_duplicate_requests() {
#[test]
fn test_handle_concurrent_frontend_and_backend_changes() {
let actor: ActorID = "cb55260e9d7e457886a4fc73fd949202".try_into().unwrap();
let local1 = ChangeRequest {
let local1 = Request {
actor: actor.clone(),
seq: 1,
version: 0,
@ -139,9 +138,9 @@ fn test_handle_concurrent_frontend_and_backend_changes() {
deps: None,
message: None,
undoable: false,
request_type: ChangeRequestType::Change,
ops: Some(vec![OpRequest {
action: ReqOpType::Set,
request_type: RequestType::Change,
ops: Some(vec![Op {
action: protocol::OpType::Set,
obj: ObjectID::Root.to_string(),
key: "bird".into(),
value: Some("magpie".into()),
@ -151,17 +150,17 @@ fn test_handle_concurrent_frontend_and_backend_changes() {
}]),
};
let local2 = ChangeRequest {
let local2 = Request {
actor: actor.clone(),
seq: 2,
version: 0,
time: None,
deps: None,
message: None,
request_type: ChangeRequestType::Change,
request_type: RequestType::Change,
undoable: false,
ops: Some(vec![OpRequest {
action: ReqOpType::Set,
ops: Some(vec![Op {
action: protocol::OpType::Set,
obj: ObjectID::Root.to_string(),
key: "bird".into(),
value: Some("jay".into()),
@ -171,14 +170,13 @@ fn test_handle_concurrent_frontend_and_backend_changes() {
}]),
};
let remote_actor: ActorID = "6d48a01318644eed90455d2cb68ac657".try_into().unwrap();
let mut remote1 = Change {
let remote1 = UnencodedChange {
actor_id: remote_actor.clone(),
seq: 1,
start_op: 1,
time: 0,
deps: Vec::new(),
message: None,
hash: ChangeHash::zero(),
operations: vec![Operation {
action: OpType::Set("goldfish".into()),
obj: ObjectID::Root,
@ -186,16 +184,15 @@ fn test_handle_concurrent_frontend_and_backend_changes() {
pred: Vec::new(),
insert: false,
}],
};
remote1.hash = change_hash(remote1.clone()).unwrap();
}
.encode();
let mut expected_change1 = Change {
let mut expected_change1 = UnencodedChange {
actor_id: actor.clone(),
seq: 1,
start_op: 1,
time: 0,
message: None,
hash: ChangeHash::zero(),
deps: Vec::new(),
operations: vec![Operation {
action: OpType::Set("magpie".into()),
@ -206,14 +203,13 @@ fn test_handle_concurrent_frontend_and_backend_changes() {
}],
};
let mut expected_change2 = Change {
let mut expected_change2 = UnencodedChange {
actor_id: remote_actor,
seq: 1,
start_op: 1,
time: 0,
message: None,
deps: Vec::new(),
hash: ChangeHash::zero(),
operations: vec![Operation {
action: OpType::Set("goldfish".into()),
key: "fish".into(),
@ -223,14 +219,13 @@ fn test_handle_concurrent_frontend_and_backend_changes() {
}],
};
let mut expected_change3 = Change {
let mut expected_change3 = UnencodedChange {
actor_id: actor,
seq: 2,
start_op: 2,
time: 0,
message: None,
deps: Vec::new(),
hash: ChangeHash::zero(),
operations: vec![Operation {
action: OpType::Set("jay".into()),
obj: ObjectID::Root,
@ -242,42 +237,39 @@ fn test_handle_concurrent_frontend_and_backend_changes() {
let mut backend = Backend::init();
backend.apply_local_change(local1).unwrap();
let backend_after_first = backend.clone();
let changes1 = backend_after_first.get_changes_unserialized(&[]);
let change01: &Change = *changes1.get(0).unwrap();
let changes1 = backend_after_first.get_changes(&[]);
let change01 = changes1.get(0).unwrap();
backend.apply_changes(vec![remote1]).unwrap();
let backend_after_second = backend.clone();
let changes2 = backend_after_second.get_changes_unserialized(&[change01.hash]);
let changes2 = backend_after_second.get_changes(&[change01.hash]);
let change12 = *changes2.get(0).unwrap();
backend.apply_local_change(local2).unwrap();
let changes3 = backend.get_changes_unserialized(&[change01.hash, change12.hash]);
let changes3 = backend.get_changes(&[change01.hash, change12.hash]);
let change23 = changes3.get(0).unwrap();
expected_change1.time = change01.time;
expected_change1.hash = change_hash(expected_change1.clone()).unwrap();
expected_change2.time = change12.time;
expected_change2.hash = change_hash(expected_change2.clone()).unwrap();
expected_change3.time = change23.time;
expected_change3.deps = vec![change01.hash];
expected_change3.hash = change_hash(expected_change3.clone()).unwrap();
assert_eq!(change01, &expected_change1);
assert_eq!(change12, &expected_change2);
assert_eq!(change23, &&expected_change3);
assert_eq!(change01, &&expected_change1.encode());
assert_eq!(change12, &expected_change2.encode());
assert_eq!(change23, &&expected_change3.encode());
}
#[test]
fn test_transform_list_indexes_into_element_ids() {
let actor: ActorID = "8f389df8fecb4ddc989102321af3578e".try_into().unwrap();
let remote_actor: ActorID = "9ba21574dc44411b8ce37bc6037a9687".try_into().unwrap();
let mut remote1 = Change {
let remote1 = UnencodedChange {
actor_id: remote_actor.clone(),
seq: 1,
start_op: 1,
time: 0,
message: None,
deps: Vec::new(),
hash: ChangeHash::zero(),
operations: vec![Operation {
action: OpType::Make(ObjType::List),
key: "birds".into(),
@ -285,17 +277,16 @@ fn test_transform_list_indexes_into_element_ids() {
pred: Vec::new(),
insert: false,
}],
};
remote1.hash = change_hash(remote1.clone()).unwrap();
}
.encode();
let mut remote2 = Change {
let remote2 = UnencodedChange {
actor_id: remote_actor,
seq: 2,
start_op: 2,
time: 0,
message: None,
deps: vec![remote1.hash],
hash: ChangeHash::zero(),
operations: vec![Operation {
action: OpType::Set("magpie".into()),
obj: "1@9ba21574dc44411b8ce37bc6037a9687".try_into().unwrap(),
@ -303,10 +294,10 @@ fn test_transform_list_indexes_into_element_ids() {
insert: true,
pred: Vec::new(),
}],
};
remote2.hash = change_hash(remote2.clone()).unwrap();
}
.encode();
let local1 = ChangeRequest {
let local1 = Request {
actor: actor.clone(),
seq: 1,
version: 1,
@ -314,10 +305,10 @@ fn test_transform_list_indexes_into_element_ids() {
time: None,
deps: None,
undoable: false,
request_type: ChangeRequestType::Change,
ops: Some(vec![OpRequest {
request_type: RequestType::Change,
ops: Some(vec![Op {
obj: "1@9ba21574dc44411b8ce37bc6037a9687".into(),
action: ReqOpType::Set,
action: protocol::OpType::Set,
value: Some("goldfinch".into()),
key: 0.into(),
datatype: Some(DataType::Undefined),
@ -325,7 +316,7 @@ fn test_transform_list_indexes_into_element_ids() {
child: None,
}]),
};
let local2 = ChangeRequest {
let local2 = Request {
actor: actor.clone(),
seq: 2,
version: 1,
@ -333,10 +324,10 @@ fn test_transform_list_indexes_into_element_ids() {
deps: None,
time: None,
undoable: false,
request_type: ChangeRequestType::Change,
ops: Some(vec![OpRequest {
request_type: RequestType::Change,
ops: Some(vec![Op {
obj: "1@9ba21574dc44411b8ce37bc6037a9687".into(),
action: ReqOpType::Set,
action: protocol::OpType::Set,
value: Some("wagtail".into()),
key: 1.into(),
insert: true,
@ -345,7 +336,7 @@ fn test_transform_list_indexes_into_element_ids() {
}]),
};
let local3 = ChangeRequest {
let local3 = Request {
actor: actor.clone(),
seq: 3,
version: 4,
@ -353,20 +344,20 @@ fn test_transform_list_indexes_into_element_ids() {
deps: None,
time: None,
undoable: false,
request_type: ChangeRequestType::Change,
request_type: RequestType::Change,
ops: Some(vec![
OpRequest {
Op {
obj: "1@9ba21574dc44411b8ce37bc6037a9687".into(),
action: ReqOpType::Set,
action: protocol::OpType::Set,
key: 0.into(),
value: Some("Magpie".into()),
insert: false,
child: None,
datatype: Some(DataType::Undefined),
},
OpRequest {
Op {
obj: "1@9ba21574dc44411b8ce37bc6037a9687".into(),
action: ReqOpType::Set,
action: protocol::OpType::Set,
key: 1.into(),
value: Some("Goldfinch".into()),
child: None,
@ -376,14 +367,13 @@ fn test_transform_list_indexes_into_element_ids() {
]),
};
let mut expected_change1 = Change {
let mut expected_change1 = UnencodedChange {
actor_id: actor.clone(),
seq: 1,
start_op: 2,
time: 0,
message: None,
deps: vec![remote1.hash],
hash: ChangeHash::zero(),
operations: vec![Operation {
obj: "1@9ba21574dc44411b8ce37bc6037a9687".try_into().unwrap(),
action: OpType::Set("goldfinch".into()),
@ -392,13 +382,12 @@ fn test_transform_list_indexes_into_element_ids() {
pred: Vec::new(),
}],
};
let mut expected_change2 = Change {
let mut expected_change2 = UnencodedChange {
actor_id: actor.clone(),
seq: 2,
start_op: 3,
time: 0,
message: None,
hash: ChangeHash::zero(),
deps: Vec::new(),
operations: vec![Operation {
obj: "1@9ba21574dc44411b8ce37bc6037a9687".try_into().unwrap(),
@ -410,14 +399,13 @@ fn test_transform_list_indexes_into_element_ids() {
pred: Vec::new(),
}],
};
let mut expected_change3 = Change {
let mut expected_change3 = UnencodedChange {
actor_id: actor,
seq: 3,
start_op: 4,
time: 0,
message: None,
deps: Vec::new(),
hash: ChangeHash::zero(),
operations: vec![
Operation {
obj: "1@9ba21574dc44411b8ce37bc6037a9687".try_into().unwrap(),
@ -444,48 +432,45 @@ fn test_transform_list_indexes_into_element_ids() {
backend.apply_changes(vec![remote1.clone()]).unwrap();
backend.apply_local_change(local1).unwrap();
let backend_after_first = backend.clone();
let changes1 = backend_after_first.get_changes_unserialized(&[remote1.hash]);
let changes1 = backend_after_first.get_changes(&[remote1.hash]);
let change12 = *changes1.get(0).unwrap();
backend.apply_changes(vec![remote2.clone()]).unwrap();
backend.apply_local_change(local2).unwrap();
let backend_after_second = backend.clone();
let changes2 = backend_after_second.get_changes_unserialized(&[remote2.hash, change12.hash]);
let changes2 = backend_after_second.get_changes(&[remote2.hash, change12.hash]);
let change23 = *changes2.get(0).unwrap();
backend.apply_local_change(local3).unwrap();
let changes3 = backend.get_changes_unserialized(&[remote2.hash, change23.hash]);
let change34 = changes3.get(0).unwrap();
let changes3 = backend.get_changes(&[remote2.hash, change23.hash]);
let change34 = changes3.get(0).unwrap().decode();
expected_change1.time = change12.time;
expected_change1.hash = change_hash(expected_change1.clone()).unwrap();
expected_change2.time = change23.time;
expected_change2.deps = vec![change12.hash];
expected_change2.hash = change_hash(expected_change2.clone()).unwrap();
expected_change3.time = change34.time;
expected_change3.deps = vec![remote2.hash, change23.hash];
expected_change3.hash = change_hash(expected_change3.clone()).unwrap();
assert_eq!(change12, &expected_change1);
assert_eq!(change23, &expected_change2);
assert_changes_equal((*change34).clone(), expected_change3);
assert_eq!(change12, &expected_change1.encode());
assert_eq!(change23, &expected_change2.encode());
assert_changes_equal(change34, expected_change3);
}
#[test]
fn test_handle_list_insertion_and_deletion_in_same_change() {
let actor: ActorID = "0723d2a1940744868ffd6b294ada813f".try_into().unwrap();
let local1 = ChangeRequest {
let local1 = Request {
actor: actor.clone(),
seq: 1,
version: 0,
request_type: ChangeRequestType::Change,
request_type: RequestType::Change,
message: None,
time: None,
undoable: false,
deps: None,
ops: Some(vec![OpRequest {
ops: Some(vec![Op {
obj: ObjectID::Root.to_string(),
action: ReqOpType::MakeList,
action: protocol::OpType::MakeList,
key: "birds".into(),
child: None,
datatype: None,
@ -494,28 +479,28 @@ fn test_handle_list_insertion_and_deletion_in_same_change() {
}]),
};
let local2 = ChangeRequest {
let local2 = Request {
actor: actor.clone(),
seq: 2,
version: 0,
request_type: ChangeRequestType::Change,
request_type: RequestType::Change,
message: None,
time: None,
undoable: false,
deps: None,
ops: Some(vec![
OpRequest {
Op {
obj: "1@0723d2a1940744868ffd6b294ada813f".into(),
action: ReqOpType::Set,
action: protocol::OpType::Set,
key: 0.into(),
insert: true,
value: Some("magpie".into()),
child: None,
datatype: Some(DataType::Undefined),
},
OpRequest {
Op {
obj: "1@0723d2a1940744868ffd6b294ada813f".into(),
action: ReqOpType::Del,
action: protocol::OpType::Del,
key: 0.into(),
child: None,
insert: false,
@ -554,14 +539,24 @@ fn test_handle_list_insertion_and_deletion_in_same_change() {
})),
};
let mut expected_change1 = Change {
let mut backend = Backend::init();
backend.apply_local_change(local1).unwrap();
let patch = backend.apply_local_change(local2).unwrap();
expected_patch.deps = patch.deps.clone();
assert_eq!(patch, expected_patch);
let changes = backend.get_changes(&[]);
assert_eq!(changes.len(), 2);
let change1 = changes[0].clone();
let change2 = changes[1].clone();
let expected_change1 = UnencodedChange {
actor_id: actor.clone(),
seq: 1,
start_op: 1,
time: 0,
time: change1.time,
message: None,
deps: Vec::new(),
hash: ChangeHash::zero(),
operations: vec![Operation {
obj: ObjectID::Root,
action: OpType::Make(ObjType::List),
@ -569,17 +564,16 @@ fn test_handle_list_insertion_and_deletion_in_same_change() {
insert: false,
pred: Vec::new(),
}],
};
expected_change1.hash = change_hash(expected_change1.clone()).unwrap();
}
.encode();
let mut expected_change2 = Change {
let expected_change2 = UnencodedChange {
actor_id: actor,
seq: 2,
start_op: 2,
time: 0,
time: change2.time,
message: None,
deps: vec![expected_change1.hash],
hash: ChangeHash::zero(),
deps: vec![change1.hash],
operations: vec![
Operation {
obj: "1@0723d2a1940744868ffd6b294ada813f".try_into().unwrap(),
@ -598,25 +592,8 @@ fn test_handle_list_insertion_and_deletion_in_same_change() {
insert: false,
},
],
};
let mut backend = Backend::init();
backend.apply_local_change(local1).unwrap();
let patch = backend.apply_local_change(local2).unwrap();
expected_patch.deps = patch.deps.clone();
assert_eq!(patch, expected_patch);
let changes = backend.get_changes_unserialized(&[]);
assert_eq!(changes.len(), 2);
let change1: Change = changes[0].clone();
let change2: Change = changes[1].clone();
expected_change1.time = change1.time;
expected_change1.hash = change_hash(expected_change1.clone()).unwrap();
expected_change2.time = change2.time;
expected_change2.deps = vec![expected_change1.hash];
expected_change2.hash = change_hash(expected_change2.clone()).unwrap();
}
.encode();
assert_eq!(change1, expected_change1);
assert_eq!(change2, expected_change2);
@ -624,7 +601,7 @@ fn test_handle_list_insertion_and_deletion_in_same_change() {
/// Asserts that the changes are equal without respect to order of the hashes
/// in the change dependencies
fn assert_changes_equal(mut change1: Change, change2: Change) {
fn assert_changes_equal(mut change1: UnencodedChange, change2: UnencodedChange) {
let change2_clone = change2.clone();
let deps1: HashSet<&ChangeHash> = change1.deps.iter().collect();
let deps2: HashSet<&ChangeHash> = change2.deps.iter().collect();

View file

@ -1,8 +1,8 @@
extern crate automerge_backend;
use automerge_backend::{change_hash, Backend, Change};
use automerge_backend::{Backend, UnencodedChange};
use automerge_backend::{OpType, Operation};
use automerge_protocol::{
ActorID, ChangeHash, Diff, DiffEdit, ElementID, MapDiff, ObjType, ObjectID, OpType, Operation,
Patch, SeqDiff, Value,
ActorID, Diff, DiffEdit, ElementID, MapDiff, ObjType, ObjectID, Patch, SeqDiff, Value,
};
use maplit::hashmap;
use std::convert::TryInto;
@ -10,14 +10,13 @@ use std::convert::TryInto;
#[test]
fn test_include_most_recent_value_for_key() {
let actor: ActorID = "ec28cfbcdb9e4f32ad24b3c776e651b0".try_into().unwrap();
let mut change1 = Change {
let change1 = UnencodedChange {
actor_id: actor.clone(),
seq: 1,
start_op: 1,
time: 0,
deps: Vec::new(),
message: None,
hash: ChangeHash::zero(),
operations: vec![Operation {
action: OpType::Set("magpie".into()),
key: "bird".into(),
@ -25,16 +24,15 @@ fn test_include_most_recent_value_for_key() {
pred: Vec::new(),
insert: false,
}],
};
change1.hash = change_hash(change1.clone()).unwrap();
}
.encode();
let mut change2 = Change {
let change2 = UnencodedChange {
actor_id: actor.clone(),
seq: 2,
start_op: 2,
time: 0,
message: None,
hash: ChangeHash::zero(),
deps: vec![change1.hash],
operations: vec![Operation {
obj: ObjectID::Root,
@ -43,8 +41,8 @@ fn test_include_most_recent_value_for_key() {
pred: vec!["1@ec28cfbcdb9e4f32ad24b3c776e651b0".try_into().unwrap()],
insert: false,
}],
};
change2.hash = change_hash(change2.clone()).unwrap();
}
.encode();
let expected_patch = Patch {
actor: None,
@ -77,14 +75,13 @@ fn test_include_most_recent_value_for_key() {
fn test_includes_conflicting_values_for_key() {
let actor1: ActorID = "111111".try_into().unwrap();
let actor2: ActorID = "222222".try_into().unwrap();
let mut change1 = Change {
let change1 = UnencodedChange {
actor_id: actor1.clone(),
seq: 1,
start_op: 1,
time: 0,
deps: Vec::new(),
message: None,
hash: ChangeHash::zero(),
operations: vec![Operation {
action: OpType::Set("magpie".into()),
obj: ObjectID::Root,
@ -92,17 +89,16 @@ fn test_includes_conflicting_values_for_key() {
pred: Vec::new(),
insert: false,
}],
};
change1.hash = change_hash(change1.clone()).unwrap();
}
.encode();
let mut change2 = Change {
let change2 = UnencodedChange {
actor_id: actor2.clone(),
seq: 1,
start_op: 1,
time: 0,
message: None,
deps: Vec::new(),
hash: ChangeHash::zero(),
operations: vec![Operation {
action: OpType::Set("blackbird".into()),
key: "bird".into(),
@ -110,8 +106,8 @@ fn test_includes_conflicting_values_for_key() {
pred: Vec::new(),
insert: false,
}],
};
change2.hash = change_hash(change2.clone()).unwrap();
}
.encode();
let expected_patch = Patch {
version: 0,
@ -145,13 +141,12 @@ fn test_includes_conflicting_values_for_key() {
#[test]
fn test_handles_counter_increment_at_keys_in_a_map() {
let actor: ActorID = "46c92088e4484ae5945dc63bf606a4a5".try_into().unwrap();
let mut change1 = Change {
let change1 = UnencodedChange {
actor_id: actor.clone(),
seq: 1,
start_op: 1,
time: 0,
message: None,
hash: ChangeHash::zero(),
deps: Vec::new(),
operations: vec![Operation {
action: OpType::Set(Value::Counter(1)),
@ -160,17 +155,16 @@ fn test_handles_counter_increment_at_keys_in_a_map() {
pred: Vec::new(),
insert: false,
}],
};
change1.hash = change_hash(change1.clone()).unwrap();
}
.encode();
let mut change2 = Change {
let change2 = UnencodedChange {
actor_id: actor.clone(),
seq: 2,
start_op: 2,
time: 0,
deps: vec![change1.hash],
message: None,
hash: ChangeHash::zero(),
operations: vec![Operation {
action: OpType::Inc(2),
obj: ObjectID::Root,
@ -178,8 +172,8 @@ fn test_handles_counter_increment_at_keys_in_a_map() {
pred: vec!["1@46c92088e4484ae5945dc63bf606a4a5".try_into().unwrap()],
insert: false,
}],
};
change2.hash = change_hash(change2.clone()).unwrap();
}
.encode();
let expected_patch = Patch {
version: 0,
@ -211,13 +205,12 @@ fn test_handles_counter_increment_at_keys_in_a_map() {
#[test]
fn test_creates_nested_maps() {
let actor: ActorID = "06148f9422cb40579fd02f1975c34a51".try_into().unwrap();
let mut change1 = Change {
let change1 = UnencodedChange {
actor_id: actor.clone(),
seq: 1,
start_op: 1,
time: 0,
message: None,
hash: ChangeHash::zero(),
deps: Vec::new(),
operations: vec![
Operation {
@ -235,17 +228,16 @@ fn test_creates_nested_maps() {
insert: false,
},
],
};
change1.hash = change_hash(change1.clone()).unwrap();
}
.encode();
let mut change2 = Change {
let change2 = UnencodedChange {
actor_id: actor.clone(),
seq: 2,
start_op: 3,
time: 0,
deps: vec![change1.hash],
message: None,
hash: ChangeHash::zero(),
operations: vec![
Operation {
action: OpType::Del,
@ -262,8 +254,8 @@ fn test_creates_nested_maps() {
insert: false,
},
],
};
change2.hash = change_hash(change2.clone()).unwrap();
}
.encode();
let expected_patch = Patch {
version: 0,
@ -303,13 +295,12 @@ fn test_creates_nested_maps() {
#[test]
fn test_create_lists() {
let actor: ActorID = "90bf7df682f747fa82ac604b35010906".try_into().unwrap();
let mut change1 = Change {
let change1 = UnencodedChange {
actor_id: actor.clone(),
seq: 1,
start_op: 1,
time: 0,
message: None,
hash: ChangeHash::zero(),
deps: Vec::new(),
operations: vec![
Operation {
@ -327,8 +318,8 @@ fn test_create_lists() {
pred: Vec::new(),
},
],
};
change1.hash = change_hash(change1.clone()).unwrap();
}
.encode();
let expected_patch = Patch {
version: 0,
@ -369,14 +360,13 @@ fn test_create_lists() {
#[test]
fn test_includes_latests_state_of_list() {
let actor: ActorID = "6caaa2e433de42ae9c3fa65c9ff3f03e".try_into().unwrap();
let mut change1 = Change {
let change1 = UnencodedChange {
actor_id: actor.clone(),
seq: 1,
start_op: 1,
time: 0,
message: None,
deps: Vec::new(),
hash: ChangeHash::zero(),
operations: vec![
Operation {
action: OpType::Make(ObjType::List),
@ -407,8 +397,8 @@ fn test_includes_latests_state_of_list() {
insert: false,
},
],
};
change1.hash = change_hash(change1.clone()).unwrap();
}
.encode();
let expected_patch = Patch {
version: 0,
@ -460,13 +450,12 @@ fn test_includes_latests_state_of_list() {
#[test]
fn test_includes_date_objects_at_root() {
let actor: ActorID = "90f5dd5d4f524e95ad5929e08d1194f1".try_into().unwrap();
let mut change1 = Change {
let change1 = UnencodedChange {
actor_id: actor.clone(),
seq: 1,
start_op: 1,
time: 0,
message: None,
hash: ChangeHash::zero(),
deps: Vec::new(),
operations: vec![Operation {
action: OpType::Set(Value::Timestamp(1_586_541_033_457)),
@ -475,8 +464,8 @@ fn test_includes_date_objects_at_root() {
pred: Vec::new(),
insert: false,
}],
};
change1.hash = change_hash(change1.clone()).unwrap();
}
.encode();
let expected_patch = Patch {
version: 0,
@ -508,14 +497,13 @@ fn test_includes_date_objects_at_root() {
#[test]
fn test_includes_date_objects_in_a_list() {
let actor: ActorID = "08b050f976a249349021a2e63d99c8e8".try_into().unwrap();
let mut change1 = Change {
let change1 = UnencodedChange {
actor_id: actor.clone(),
seq: 1,
start_op: 1,
time: 0,
message: None,
deps: Vec::new(),
hash: ChangeHash::zero(),
operations: vec![
Operation {
action: OpType::Make(ObjType::List),
@ -532,8 +520,8 @@ fn test_includes_date_objects_in_a_list() {
pred: Vec::new(),
},
],
};
change1.hash = change_hash(change1.clone()).unwrap();
}
.encode();
let expected_patch = Patch {
version: 0,

View file

@ -3,8 +3,8 @@ extern crate errno;
extern crate libc;
extern crate serde;
use automerge_backend::AutomergeError;
use automerge_protocol::{ActorID, ChangeRequest};
use automerge_backend::{AutomergeError, Change};
use automerge_protocol::Request;
use errno::{set_errno, Errno};
use serde::ser::Serialize;
use std::convert::TryInto;
@ -60,13 +60,19 @@ impl Backend {
}
fn handle_binary(&mut self, b: Result<Vec<u8>, AutomergeError>) -> isize {
self.handle_binaries(b.map(|b| vec![b]))
if let Ok(bin) = b {
let len = bin.len();
self.binary = vec![bin];
len as isize
} else {
-1
}
}
fn handle_binaries(&mut self, b: Result<Vec<Vec<u8>>, AutomergeError>) -> isize {
fn handle_binaries(&mut self, b: Result<Vec<&Change>, AutomergeError>) -> isize {
if let Ok(bin) = b {
let len = bin[0].len();
self.binary = bin;
let len = bin[0].bytes.len();
self.binary = bin.iter().map(|b| b.bytes.clone()).collect();
self.binary.reverse();
len as isize
} else {
@ -127,9 +133,14 @@ pub unsafe extern "C" fn automerge_apply_local_change(
) -> isize {
let request: &CStr = CStr::from_ptr(request);
let request = request.to_string_lossy();
let request: ChangeRequest = serde_json::from_str(&request).unwrap();
let patch = (*backend).apply_local_change(request);
(*backend).generate_json(patch)
let request: Result<Request, _> = serde_json::from_str(&request);
if let Ok(request) = request {
let patch = (*backend).apply_local_change(request);
(*backend).generate_json(patch)
} else {
// json parse error
-1
}
}
/// # Safety
@ -154,7 +165,12 @@ pub unsafe extern "C" fn automerge_write_change(
#[no_mangle]
pub unsafe extern "C" fn automerge_apply_changes(backend: *mut Backend) -> isize {
if let Some(changes) = (*backend).queue.take() {
let patch = (*backend).apply_changes_binary(changes);
// FIXME
let changes = changes
.iter()
.map(|c| Change::from_bytes(c.to_vec()).unwrap())
.collect();
let patch = (*backend).apply_changes(changes);
(*backend).generate_json(patch)
} else {
-1
@ -174,7 +190,12 @@ pub unsafe extern "C" fn automerge_get_patch(backend: *mut Backend) -> isize {
#[no_mangle]
pub unsafe extern "C" fn automerge_load_changes(backend: *mut Backend) -> isize {
if let Some(changes) = (*backend).queue.take() {
if (*backend).load_changes_binary(changes).is_ok() {
// FIXME
let changes = changes
.iter()
.map(|c| Change::from_bytes(c.to_vec()).unwrap())
.collect();
if (*backend).load_changes(changes).is_ok() {
return 0;
}
}
@ -219,9 +240,13 @@ pub unsafe extern "C" fn automerge_get_changes_for_actor(
) -> isize {
let actor: &CStr = CStr::from_ptr(actor);
let actor = actor.to_string_lossy();
let actor: ActorID = actor.as_ref().into();
let changes = (*backend).get_changes_for_actor_id(&actor);
(*backend).handle_binaries(changes)
if let Ok(actor) = actor.as_ref().try_into() {
let changes = (*backend).get_changes_for_actor_id(&actor);
(*backend).handle_binaries(changes)
} else {
// bad actor error
-1
}
}
/// # Safety
@ -243,7 +268,7 @@ pub unsafe extern "C" fn automerge_get_changes(
)
}
let changes = (*backend).get_changes(&have_deps);
(*backend).handle_binaries(changes)
(*backend).handle_binaries(Ok(changes))
}
/// # Safety

View file

@ -10,6 +10,7 @@ path = "src/main.rs"
[dependencies]
clap = { git = "https://github.com/clap-rs/clap/" }
automerge-protocol = { path = "../automerge-protocol" }
automerge-backend = { path = "../automerge-backend" }
automerge-frontend = { path = "../automerge-frontend" }
serde_json = "^1.0"

View file

@ -13,7 +13,8 @@ fn main() -> io::Result<()> {
input_file.read_to_end(&mut input_data)?;
let mut backend = automerge_backend::Backend::init();
backend.load_changes_binary(vec![input_data]).unwrap();
let changes = automerge_backend::Change::parse(&input_data).unwrap();
backend.load_changes(changes).unwrap();
let patch = backend.get_patch().unwrap();
let mut frontend = automerge_frontend::Frontend::new();
frontend.apply_patch(patch).unwrap();

View file

@ -11,6 +11,7 @@ pub enum AutomergeFrontendError {
PathIsNotCounter,
CannotOverwriteCounter,
MismatchedSequenceNumber,
InvalidActorIDString(String),
}
impl fmt::Display for AutomergeFrontendError {
@ -19,6 +20,12 @@ impl fmt::Display for AutomergeFrontendError {
}
}
impl From<automerge_protocol::error::InvalidActorID> for AutomergeFrontendError {
fn from(e: automerge_protocol::error::InvalidActorID) -> AutomergeFrontendError {
AutomergeFrontendError::InvalidActorIDString(e.0)
}
}
impl Error for AutomergeFrontendError {}
#[derive(Debug, PartialEq)]

View file

@ -1,6 +1,4 @@
use automerge_protocol::{
ActorID, ChangeRequest, ChangeRequestType, ObjectID, OpID, OpRequest, Patch,
};
use automerge_protocol::{ActorID, ObjectID, Op, OpID, Patch, Request, RequestType};
mod change_context;
mod error;
@ -13,6 +11,7 @@ use mutation::PathElement;
pub use mutation::{LocalChange, MutableDocument, Path};
use object::Object;
use std::convert::TryFrom;
use std::str::FromStr;
use std::time;
use std::{collections::HashMap, rc::Rc};
pub use value::{Conflicts, MapType, SequenceType, Value};
@ -74,7 +73,7 @@ impl FrontendState {
if let (Some(patch_actor), Some(patch_seq)) = (&patch.actor, patch.seq) {
// If this is a local change corresponding to our actor then we
// need to match it against in flight requests
if self_actor == &ActorID::from(patch_actor.as_str()) {
if self_actor == &ActorID::from_str(patch_actor.as_str())? {
// Check that if the patch is for our actor ID then it is not
// out of order
if new_in_flight_requests[0] != patch_seq {
@ -139,7 +138,7 @@ impl FrontendState {
self,
change_closure: F,
seq: u64,
) -> Result<(Option<Vec<OpRequest>>, FrontendState, Value), AutomergeFrontendError>
) -> Result<(Option<Vec<Op>>, FrontendState, Value), AutomergeFrontendError>
where
F: FnOnce(&mut dyn MutableDocument) -> Result<(), AutomergeFrontendError>,
{
@ -235,7 +234,7 @@ impl Frontend {
pub fn new_with_initial_state(
initial_state: Value,
) -> Result<(Self, ChangeRequest), InvalidInitialStateError> {
) -> Result<(Self, Request), InvalidInitialStateError> {
match &initial_state {
Value::Map(kvs, MapType::Map) => {
let init_ops = kvs
@ -252,7 +251,7 @@ impl Frontend {
.collect();
let mut front = Frontend::new();
let init_change_request = ChangeRequest {
let init_change_request = Request {
actor: front.actor_id.clone(),
time: system_time(),
seq: 1,
@ -261,7 +260,7 @@ impl Frontend {
undoable: false,
deps: None,
ops: Some(init_ops),
request_type: ChangeRequestType::Change,
request_type: RequestType::Change,
};
// Unwrap here is fine because it should be impossible to
// cause an error applying a local change from a `Value`. If
@ -285,7 +284,7 @@ impl Frontend {
&mut self,
message: Option<String>,
change_closure: F,
) -> Result<Option<ChangeRequest>, AutomergeFrontendError>
) -> Result<Option<Request>, AutomergeFrontendError>
where
F: FnOnce(&mut dyn MutableDocument) -> Result<(), AutomergeFrontendError>,
{
@ -301,7 +300,7 @@ impl Frontend {
}
self.seq += 1;
self.cached_value = new_value;
let change_request = ChangeRequest {
let change_request = Request {
actor: self.actor_id.clone(),
seq: self.seq,
time: system_time(),
@ -310,7 +309,7 @@ impl Frontend {
undoable: true,
deps: None,
ops,
request_type: ChangeRequestType::Change,
request_type: RequestType::Change,
};
Ok(Some(change_request))
}

View file

@ -135,7 +135,7 @@ impl LocalChange {
/// be applied using `ChangeContext::commit`
pub struct MutationTracker<'a, 'b> {
change_context: &'a mut ChangeContext<'b>,
ops: Vec<amp::OpRequest>,
ops: Vec<amp::Op>,
}
impl<'a, 'b> MutationTracker<'a, 'b> {
@ -146,7 +146,7 @@ impl<'a, 'b> MutationTracker<'a, 'b> {
}
}
pub fn ops(&self) -> Option<Vec<amp::OpRequest>> {
pub fn ops(&self) -> Option<Vec<amp::Op>> {
if !self.ops.is_empty() {
Some(self.ops.clone())
} else {
@ -395,8 +395,8 @@ impl<'a, 'b> MutableDocument for MutationTracker<'a, 'b> {
if self.value_for_path(&change.path).is_some() {
// Unwrap is fine as we know the parent object exists from the above
let parent_obj = self.value_for_path(&change.path.parent()).unwrap();
let op = amp::OpRequest {
action: amp::ReqOpType::Del,
let op = amp::Op {
action: amp::OpType::Del,
// This unwrap is fine because we know the parent
// is a container
obj: parent_obj.id().unwrap().to_string(),
@ -446,8 +446,8 @@ impl<'a, 'b> MutableDocument for MutationTracker<'a, 'b> {
};
// Unwrap is fine as we know the parent object exists from the above
let parent_obj = self.value_for_path(&change.path.parent()).unwrap();
let op = amp::OpRequest {
action: amp::ReqOpType::Inc,
let op = amp::Op {
action: amp::OpType::Inc,
// This unwrap is fine because we know the parent
// is a container
obj: parent_obj.id().unwrap().to_string(),

View file

@ -175,15 +175,15 @@ pub(crate) fn value_to_op_requests(
key: PathElement,
v: &Value,
insert: bool,
) -> (Vec<amp::OpRequest>, amp::Diff) {
) -> (Vec<amp::Op>, amp::Diff) {
match v {
Value::Sequence(vs, seq_type) => {
let make_action = match seq_type {
SequenceType::List => amp::ReqOpType::MakeList,
SequenceType::Text => amp::ReqOpType::MakeText,
SequenceType::List => amp::OpType::MakeList,
SequenceType::Text => amp::OpType::MakeText,
};
let list_id = new_object_id();
let make_op = amp::OpRequest {
let make_op = amp::Op {
action: make_action,
obj: parent_object,
key: key.to_request_key(),
@ -192,14 +192,14 @@ pub(crate) fn value_to_op_requests(
datatype: None,
insert,
};
let child_requests_and_diffs: Vec<(Vec<amp::OpRequest>, amp::Diff)> = vs
let child_requests_and_diffs: Vec<(Vec<amp::Op>, amp::Diff)> = vs
.iter()
.enumerate()
.map(|(index, v)| {
value_to_op_requests(list_id.clone(), PathElement::Index(index), v, true)
})
.collect();
let child_requests: Vec<amp::OpRequest> = child_requests_and_diffs
let child_requests: Vec<amp::Op> = child_requests_and_diffs
.iter()
.cloned()
.flat_map(|(o, _)| o)
@ -229,11 +229,11 @@ pub(crate) fn value_to_op_requests(
}
Value::Map(kvs, map_type) => {
let make_action = match map_type {
MapType::Map => amp::ReqOpType::MakeMap,
MapType::Table => amp::ReqOpType::MakeTable,
MapType::Map => amp::OpType::MakeMap,
MapType::Table => amp::OpType::MakeTable,
};
let map_id = new_object_id();
let make_op = amp::OpRequest {
let make_op = amp::Op {
action: make_action,
obj: parent_object,
key: key.to_request_key(),
@ -242,7 +242,7 @@ pub(crate) fn value_to_op_requests(
datatype: None,
insert,
};
let child_requests_and_diffs: HashMap<String, (Vec<amp::OpRequest>, amp::Diff)> = kvs
let child_requests_and_diffs: HashMap<String, (Vec<amp::Op>, amp::Diff)> = kvs
.iter()
.map(|(k, v)| {
(
@ -252,7 +252,7 @@ pub(crate) fn value_to_op_requests(
})
.collect();
let mut result = vec![make_op];
let child_requests: Vec<amp::OpRequest> = child_requests_and_diffs
let child_requests: Vec<amp::Op> = child_requests_and_diffs
.iter()
.flat_map(|(_, (o, _))| o)
.cloned()
@ -274,8 +274,8 @@ pub(crate) fn value_to_op_requests(
(result, amp::Diff::Map(child_diff))
}
Value::Primitive(prim_value) => {
let ops = vec![amp::OpRequest {
action: amp::ReqOpType::Set,
let ops = vec![amp::Op {
action: amp::OpType::Set,
obj: parent_object,
key: key.to_request_key(),
child: None,
@ -294,7 +294,7 @@ fn new_object_id() -> String {
}
pub(crate) fn random_op_id() -> amp::OpID {
amp::OpID(1, amp::ActorID::random().0)
amp::OpID::new(1, &amp::ActorID::random())
}
fn value_to_datatype(value: &amp::Value) -> amp::DataType {

View file

@ -4,7 +4,7 @@ use automerge_protocol as amp;
use maplit::hashmap;
fn random_op_id() -> String {
amp::OpID(1, amp::ActorID::random().0).to_string()
amp::OpID::new(1, &amp::ActorID::random()).to_string()
}
#[test]
@ -54,7 +54,7 @@ fn use_version_and_sequence_number_from_backend() {
.unwrap()
.unwrap();
let expected_change_request = amp::ChangeRequest {
let expected_change_request = amp::Request {
actor: doc.actor_id,
seq: 5,
time: req.time,
@ -62,9 +62,9 @@ fn use_version_and_sequence_number_from_backend() {
message: None,
undoable: true,
deps: None,
request_type: amp::ChangeRequestType::Change,
ops: Some(vec![amp::OpRequest {
action: amp::ReqOpType::Set,
request_type: amp::RequestType::Change,
ops: Some(vec![amp::Op {
action: amp::OpType::Set,
obj: amp::ObjectID::Root.to_string(),
key: "partridges".into(),
insert: false,
@ -508,7 +508,7 @@ fn allow_interleacing_of_patches_and_changes() {
assert_eq!(
req1,
amp::ChangeRequest {
amp::Request {
actor: doc.actor_id.clone(),
seq: 1,
version: 0,
@ -516,9 +516,9 @@ fn allow_interleacing_of_patches_and_changes() {
time: req1.time,
undoable: true,
deps: None,
request_type: amp::ChangeRequestType::Change,
ops: Some(vec![amp::OpRequest {
action: amp::ReqOpType::Set,
request_type: amp::RequestType::Change,
ops: Some(vec![amp::Op {
action: amp::OpType::Set,
obj: amp::ObjectID::Root.to_string(),
key: "number".into(),
value: Some(amp::Value::Int(1)),
@ -531,7 +531,7 @@ fn allow_interleacing_of_patches_and_changes() {
assert_eq!(
req2,
amp::ChangeRequest {
amp::Request {
actor: doc.actor_id.clone(),
seq: 2,
version: 0,
@ -539,9 +539,9 @@ fn allow_interleacing_of_patches_and_changes() {
time: req2.time,
undoable: true,
deps: None,
request_type: amp::ChangeRequestType::Change,
ops: Some(vec![amp::OpRequest {
action: amp::ReqOpType::Set,
request_type: amp::RequestType::Change,
ops: Some(vec![amp::Op {
action: amp::OpType::Set,
obj: amp::ObjectID::Root.to_string(),
key: "number".into(),
value: Some(amp::Value::Int(2)),
@ -569,7 +569,7 @@ fn allow_interleacing_of_patches_and_changes() {
assert_eq!(
req3,
amp::ChangeRequest {
amp::Request {
actor: doc.actor_id,
seq: 3,
version: 1,
@ -577,9 +577,9 @@ fn allow_interleacing_of_patches_and_changes() {
time: req3.time,
undoable: true,
deps: None,
request_type: amp::ChangeRequestType::Change,
ops: Some(vec![amp::OpRequest {
action: amp::ReqOpType::Set,
request_type: amp::RequestType::Change,
ops: Some(vec![amp::Op {
action: amp::OpType::Set,
obj: amp::ObjectID::Root.to_string(),
key: "number".into(),
value: Some(amp::Value::Int(3)),

View file

@ -60,7 +60,7 @@ fn test_set_root_object_properties() {
cr.time = None;
cr
});
let expected_change = amp::ChangeRequest {
let expected_change = amp::Request {
actor: doc.actor_id,
seq: 1,
version: 0,
@ -68,8 +68,8 @@ fn test_set_root_object_properties() {
message: Some("set root object".into()),
undoable: true,
deps: None,
ops: Some(vec![amp::OpRequest {
action: amp::ReqOpType::Set,
ops: Some(vec![amp::Op {
action: amp::OpType::Set,
obj: ROOT_ID.to_string(),
key: amp::RequestKey::Str("bird".to_string()),
child: None,
@ -77,7 +77,7 @@ fn test_set_root_object_properties() {
datatype: Some(amp::DataType::Undefined),
insert: false,
}]),
request_type: amp::ChangeRequestType::Change,
request_type: amp::RequestType::Change,
};
assert_eq!(change_request, Some(expected_change));
}
@ -105,18 +105,18 @@ fn it_should_create_nested_maps() {
.unwrap()
.unwrap();
let birds_id = doc.get_object_id(&Path::root().key("birds")).unwrap();
let expected_change = amp::ChangeRequest {
let expected_change = amp::Request {
actor: doc.actor_id,
seq: 1,
time: change_request.time,
message: None,
version: 0,
undoable: true,
request_type: amp::ChangeRequestType::Change,
request_type: amp::RequestType::Change,
deps: None,
ops: Some(vec![
amp::OpRequest {
action: amp::ReqOpType::MakeMap,
amp::Op {
action: amp::OpType::MakeMap,
obj: amp::ObjectID::Root.to_string(),
key: amp::RequestKey::Str("birds".into()),
child: Some(birds_id.to_string()),
@ -124,8 +124,8 @@ fn it_should_create_nested_maps() {
value: None,
insert: false,
},
amp::OpRequest {
action: amp::ReqOpType::Set,
amp::Op {
action: amp::OpType::Set,
obj: birds_id.to_string(),
key: amp::RequestKey::Str("wrens".into()),
child: None,
@ -183,7 +183,7 @@ fn apply_updates_inside_nested_maps() {
);
let birds_id = doc.get_object_id(&Path::root().key("birds")).unwrap();
let expected_change_request = amp::ChangeRequest {
let expected_change_request = amp::Request {
actor: doc.actor_id,
seq: 2,
version: 0,
@ -191,9 +191,9 @@ fn apply_updates_inside_nested_maps() {
message: None,
undoable: true,
deps: None,
request_type: amp::ChangeRequestType::Change,
ops: Some(vec![amp::OpRequest {
action: amp::ReqOpType::Set,
request_type: amp::RequestType::Change,
ops: Some(vec![amp::Op {
action: amp::OpType::Set,
obj: birds_id.to_string(),
key: "sparrows".into(),
child: None,
@ -242,7 +242,7 @@ fn delete_keys_in_a_map() {
}))
);
let expected_change_request = amp::ChangeRequest {
let expected_change_request = amp::Request {
actor: doc.actor_id,
seq: 2,
version: 0,
@ -250,9 +250,9 @@ fn delete_keys_in_a_map() {
message: None,
undoable: true,
deps: None,
request_type: amp::ChangeRequestType::Change,
ops: Some(vec![amp::OpRequest {
action: amp::ReqOpType::Del,
request_type: amp::RequestType::Change,
ops: Some(vec![amp::Op {
action: amp::OpType::Del,
obj: amp::ObjectID::Root.to_string(),
key: "magpies".into(),
child: None,
@ -299,7 +299,7 @@ fn create_lists() {
let birds_id = doc.get_object_id(&Path::root().key("birds")).unwrap();
let expected_change_request = amp::ChangeRequest {
let expected_change_request = amp::Request {
actor: doc.actor_id,
seq: 1,
version: 0,
@ -307,10 +307,10 @@ fn create_lists() {
message: None,
undoable: true,
deps: None,
request_type: amp::ChangeRequestType::Change,
request_type: amp::RequestType::Change,
ops: Some(vec![
amp::OpRequest {
action: amp::ReqOpType::MakeList,
amp::Op {
action: amp::OpType::MakeList,
key: "birds".into(),
obj: amp::ObjectID::Root.to_string(),
child: Some(birds_id.to_string()),
@ -318,8 +318,8 @@ fn create_lists() {
datatype: None,
insert: false,
},
amp::OpRequest {
action: amp::ReqOpType::Set,
amp::Op {
action: amp::OpType::Set,
obj: birds_id.to_string(),
key: 0.into(),
child: None,
@ -367,7 +367,7 @@ fn apply_updates_inside_lists() {
let birds_id = doc.get_object_id(&Path::root().key("birds")).unwrap();
let expected_change_request = amp::ChangeRequest {
let expected_change_request = amp::Request {
actor: doc.actor_id,
seq: 2,
version: 0,
@ -375,9 +375,9 @@ fn apply_updates_inside_lists() {
message: None,
undoable: true,
deps: None,
request_type: amp::ChangeRequestType::Change,
ops: Some(vec![amp::OpRequest {
action: amp::ReqOpType::Set,
request_type: amp::RequestType::Change,
ops: Some(vec![amp::Op {
action: amp::OpType::Set,
obj: birds_id.to_string(),
key: 0.into(),
child: None,
@ -421,7 +421,7 @@ fn delete_list_elements() {
let birds_id = doc.get_object_id(&Path::root().key("birds")).unwrap();
let expected_change_request = amp::ChangeRequest {
let expected_change_request = amp::Request {
actor: doc.actor_id,
seq: 2,
version: 0,
@ -429,9 +429,9 @@ fn delete_list_elements() {
message: None,
undoable: true,
deps: None,
request_type: amp::ChangeRequestType::Change,
ops: Some(vec![amp::OpRequest {
action: amp::ReqOpType::Del,
request_type: amp::RequestType::Change,
ops: Some(vec![amp::Op {
action: amp::OpType::Del,
obj: birds_id.to_string(),
key: 0.into(),
child: None,
@ -488,7 +488,7 @@ fn handle_counters_inside_maps() {
)
);
let expected_change_request_1 = amp::ChangeRequest {
let expected_change_request_1 = amp::Request {
actor: doc.actor_id.clone(),
seq: 1,
version: 0,
@ -496,9 +496,9 @@ fn handle_counters_inside_maps() {
message: None,
undoable: true,
deps: None,
request_type: amp::ChangeRequestType::Change,
ops: Some(vec![amp::OpRequest {
action: amp::ReqOpType::Set,
request_type: amp::RequestType::Change,
ops: Some(vec![amp::Op {
action: amp::OpType::Set,
obj: amp::ObjectID::Root.to_string(),
key: "wrens".into(),
child: None,
@ -509,7 +509,7 @@ fn handle_counters_inside_maps() {
};
assert_eq!(req1, expected_change_request_1);
let expected_change_request_2 = amp::ChangeRequest {
let expected_change_request_2 = amp::Request {
actor: doc.actor_id,
seq: 2,
version: 0,
@ -517,9 +517,9 @@ fn handle_counters_inside_maps() {
message: None,
undoable: true,
deps: None,
request_type: amp::ChangeRequestType::Change,
ops: Some(vec![amp::OpRequest {
action: amp::ReqOpType::Inc,
request_type: amp::RequestType::Change,
ops: Some(vec![amp::Op {
action: amp::OpType::Inc,
obj: amp::ObjectID::Root.to_string(),
key: "wrens".into(),
child: None,
@ -580,7 +580,7 @@ fn handle_counters_inside_lists() {
let counts_id = doc.get_object_id(&Path::root().key("counts")).unwrap();
let expected_change_request_1 = amp::ChangeRequest {
let expected_change_request_1 = amp::Request {
actor: doc.actor_id.clone(),
seq: 1,
version: 0,
@ -588,10 +588,10 @@ fn handle_counters_inside_lists() {
message: None,
undoable: true,
deps: None,
request_type: amp::ChangeRequestType::Change,
request_type: amp::RequestType::Change,
ops: Some(vec![
amp::OpRequest {
action: amp::ReqOpType::MakeList,
amp::Op {
action: amp::OpType::MakeList,
obj: amp::ObjectID::Root.to_string(),
key: "counts".into(),
child: Some(counts_id.to_string()),
@ -599,8 +599,8 @@ fn handle_counters_inside_lists() {
value: None,
datatype: None,
},
amp::OpRequest {
action: amp::ReqOpType::Set,
amp::Op {
action: amp::OpType::Set,
obj: counts_id.to_string(),
key: 0.into(),
child: None,
@ -612,7 +612,7 @@ fn handle_counters_inside_lists() {
};
assert_eq!(req1, expected_change_request_1);
let expected_change_request_2 = amp::ChangeRequest {
let expected_change_request_2 = amp::Request {
actor: doc.actor_id,
seq: 2,
version: 0,
@ -620,9 +620,9 @@ fn handle_counters_inside_lists() {
message: None,
undoable: true,
deps: None,
request_type: amp::ChangeRequestType::Change,
ops: Some(vec![amp::OpRequest {
action: amp::ReqOpType::Inc,
request_type: amp::RequestType::Change,
ops: Some(vec![amp::Op {
action: amp::OpType::Inc,
obj: counts_id.to_string(),
key: 0.into(),
child: None,

View file

@ -12,6 +12,10 @@ pub struct InvalidObjectID(pub String);
#[error("Invalid element ID: {0}")]
pub struct InvalidElementID(pub String);
#[derive(Error, Debug)]
#[error("Invalid actor ID: {0}")]
pub struct InvalidActorID(pub String);
#[derive(Error, Debug)]
#[error("Invalid change hash slice: {0:?}")]
pub struct InvalidChangeHashSlice(pub Vec<u8>);

View file

@ -1,32 +1,28 @@
mod error;
pub mod error;
mod serde_impls;
mod utility_impls;
use serde::{ser::SerializeMap, Deserialize, Serialize, Serializer};
use std::collections::HashMap;
// TODO make this an opaque tuple struct. I'm waiting on hearing what the
// constraints are on the possible values of ActorIDs
#[derive(Deserialize, Serialize, Eq, PartialEq, Hash, Debug, Clone, PartialOrd, Ord)]
pub struct ActorID(pub String);
#[derive(Eq, PartialEq, Hash, Debug, Clone, PartialOrd, Ord)]
pub struct ActorID(Vec<u8>);
impl ActorID {
pub fn random() -> ActorID {
ActorID(hex::encode(uuid::Uuid::new_v4().as_bytes()))
ActorID(uuid::Uuid::new_v4().as_bytes().to_vec())
}
pub fn to_bytes(&self) -> Vec<u8> {
// FIXME - I should be storing u8 internally - not strings
// i need proper error handling for non-hex strings
hex::decode(&self.0).unwrap()
}
pub fn to_hex_string(&self) -> String {
self.0.clone()
}
pub fn from_bytes(bytes: &[u8]) -> ActorID {
ActorID(hex::encode(bytes))
ActorID(bytes.to_vec())
}
pub fn to_hex_string(&self) -> String {
hex::encode(&self.0)
}
pub fn op_id_at(&self, seq: u64) -> OpID {
@ -48,7 +44,7 @@ pub struct OpID(pub u64, pub String);
impl OpID {
pub fn new(seq: u64, actor: &ActorID) -> OpID {
OpID(seq, actor.0.clone())
OpID(seq, actor.to_hex_string())
}
pub fn counter(&self) -> u64 {
@ -181,7 +177,7 @@ pub enum RequestKey {
#[derive(Deserialize, PartialEq, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub enum ReqOpType {
pub enum OpType {
MakeMap,
MakeTable,
MakeList,
@ -193,8 +189,8 @@ pub enum ReqOpType {
}
#[derive(Deserialize, PartialEq, Debug, Clone)]
pub struct OpRequest {
pub action: ReqOpType,
pub struct Op {
pub action: OpType,
pub obj: String,
pub key: RequestKey,
pub child: Option<String>,
@ -204,7 +200,7 @@ pub struct OpRequest {
pub insert: bool,
}
impl OpRequest {
impl Op {
pub fn primitive_value(&self) -> Value {
match (self.value.as_ref().and_then(|v| v.to_i64()), self.datatype) {
(Some(n), Some(DataType::Counter)) => Value::Counter(n),
@ -215,10 +211,10 @@ impl OpRequest {
pub fn obj_type(&self) -> Option<ObjType> {
match self.action {
ReqOpType::MakeMap => Some(ObjType::Map),
ReqOpType::MakeTable => Some(ObjType::Table),
ReqOpType::MakeList => Some(ObjType::List),
ReqOpType::MakeText => Some(ObjType::Text),
OpType::MakeMap => Some(ObjType::Map),
OpType::MakeTable => Some(ObjType::Table),
OpType::MakeList => Some(ObjType::List),
OpType::MakeText => Some(ObjType::Text),
_ => None,
}
}
@ -228,127 +224,12 @@ impl OpRequest {
}
}
#[derive(PartialEq, Debug, Clone)]
pub enum OpType {
Make(ObjType),
Del,
Link(ObjectID),
Inc(i64),
Set(Value),
}
#[derive(PartialEq, Debug, Clone)]
pub struct Operation {
pub action: OpType,
pub obj: ObjectID,
pub key: Key,
pub pred: Vec<OpID>,
pub insert: bool,
}
impl Operation {
pub fn set(obj: ObjectID, key: Key, value: Value, pred: Vec<OpID>) -> Operation {
Operation {
action: OpType::Set(value),
obj,
key,
insert: false,
pred,
}
}
pub fn insert(obj: ObjectID, key: Key, value: Value, pred: Vec<OpID>) -> Operation {
Operation {
action: OpType::Set(value),
obj,
key,
insert: true,
pred,
}
}
pub fn inc(obj: ObjectID, key: Key, value: i64, pred: Vec<OpID>) -> Operation {
Operation {
action: OpType::Inc(value),
obj,
key,
insert: false,
pred,
}
}
pub fn del(obj: ObjectID, key: Key, pred: Vec<OpID>) -> Operation {
Operation {
action: OpType::Del,
obj,
key,
insert: false,
pred,
}
}
pub fn is_make(&self) -> bool {
self.obj_type().is_some()
}
pub fn is_basic_assign(&self) -> bool {
!self.insert
&& match self.action {
OpType::Del | OpType::Set(_) | OpType::Inc(_) | OpType::Link(_) => true,
_ => false,
}
}
pub fn is_inc(&self) -> bool {
match self.action {
OpType::Inc(_) => true,
_ => false,
}
}
pub fn obj_type(&self) -> Option<ObjType> {
match self.action {
OpType::Make(t) => Some(t),
_ => None,
}
}
}
#[derive(Eq, PartialEq, Debug, Hash, Clone, PartialOrd, Ord, Copy)]
pub struct ChangeHash(pub [u8; 32]);
impl ChangeHash {
/// A ChangeHash which is all zeros
pub fn zero() -> Self {
ChangeHash([0; 32])
}
}
#[derive(Deserialize, Serialize, PartialEq, Debug, Clone)]
pub struct Change {
#[serde(rename = "ops")]
pub operations: Vec<Operation>,
#[serde(rename = "actor")]
pub actor_id: ActorID,
pub hash: ChangeHash,
pub seq: u64,
#[serde(rename = "startOp")]
pub start_op: u64,
pub time: i64,
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
pub deps: Vec<ChangeHash>,
}
impl Change {
pub fn max_op(&self) -> u64 {
self.start_op + (self.operations.len() as u64) - 1
}
}
#[derive(Deserialize, PartialEq, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ChangeRequest {
pub struct Request {
pub actor: ActorID,
pub seq: u64,
pub version: u64,
@ -357,13 +238,13 @@ pub struct ChangeRequest {
pub undoable: bool,
pub time: Option<i64>,
pub deps: Option<Vec<ChangeHash>>,
pub ops: Option<Vec<OpRequest>>,
pub request_type: ChangeRequestType,
pub ops: Option<Vec<Op>>,
pub request_type: RequestType,
}
#[derive(Deserialize, PartialEq, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub enum ChangeRequestType {
pub enum RequestType {
Change,
Undo,
Redo,

View file

@ -0,0 +1,24 @@
use crate::ActorID;
use serde::de;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::str::FromStr;
impl<'de> Deserialize<'de> for ActorID {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
ActorID::from_str(&s)
.map_err(|_| de::Error::invalid_value(de::Unexpected::Str(&s), &"A valid ActorID"))
}
}
impl Serialize for ActorID {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.to_hex_string().as_str())
}
}

View file

@ -3,13 +3,12 @@ use serde::{
Deserialize,
};
mod actor_id;
mod change_hash;
mod diff;
mod element_id;
mod key;
mod object_id;
mod op_type;
mod operation;
mod opid;
mod request_key;
mod value;

View file

@ -1,141 +0,0 @@
use super::read_field;
use crate::{DataType, Key, ObjType, ObjectID, OpID, OpType, Operation, ReqOpType, Value};
use serde::ser::SerializeStruct;
use serde::{
de::{Error, MapAccess, Unexpected, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
impl Serialize for Operation {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut fields = 4;
if self.insert {
fields += 1
}
match &self.action {
OpType::Set(Value::Timestamp(_)) => fields += 2,
OpType::Set(Value::Counter(_)) => fields += 2,
OpType::Link(_) | OpType::Inc(_) | OpType::Set(_) => fields += 1,
_ => {}
}
let mut op = serializer.serialize_struct("Operation", fields)?;
op.serialize_field("action", &self.action)?;
op.serialize_field("obj", &self.obj)?;
op.serialize_field("key", &self.key)?;
if self.insert {
op.serialize_field("insert", &self.insert)?;
}
match &self.action {
OpType::Link(child) => op.serialize_field("child", &child)?,
OpType::Inc(n) => op.serialize_field("value", &n)?,
OpType::Set(Value::Counter(value)) => {
op.serialize_field("value", &value)?;
op.serialize_field("datatype", &DataType::Counter)?;
}
OpType::Set(Value::Timestamp(value)) => {
op.serialize_field("value", &value)?;
op.serialize_field("datatype", &DataType::Timestamp)?;
}
OpType::Set(value) => op.serialize_field("value", &value)?,
_ => {}
}
op.serialize_field("pred", &self.pred)?;
op.end()
}
}
impl<'de> Deserialize<'de> for Operation {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
const FIELDS: &[&str] = &["ops", "deps", "message", "seq", "actor", "requestType"];
struct OperationVisitor;
impl<'de> Visitor<'de> for OperationVisitor {
type Value = Operation;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("An operation object")
}
fn visit_map<V>(self, mut map: V) -> Result<Operation, V::Error>
where
V: MapAccess<'de>,
{
let mut action: Option<ReqOpType> = None;
let mut obj: Option<ObjectID> = None;
let mut key: Option<Key> = None;
let mut pred: Option<Vec<OpID>> = None;
let mut insert: Option<bool> = None;
let mut datatype: Option<DataType> = None;
let mut value: Option<Value> = None;
let mut child: Option<ObjectID> = None;
while let Some(field) = map.next_key::<String>()? {
match field.as_ref() {
"action" => read_field("action", &mut action, &mut map)?,
"obj" => read_field("obj", &mut obj, &mut map)?,
"key" => read_field("key", &mut key, &mut map)?,
"pred" => read_field("pred", &mut pred, &mut map)?,
"insert" => read_field("insert", &mut insert, &mut map)?,
"datatype" => read_field("datatype", &mut datatype, &mut map)?,
"value" => read_field("value", &mut value, &mut map)?,
"child" => read_field("child", &mut child, &mut map)?,
_ => return Err(Error::unknown_field(&field, FIELDS)),
}
}
let action = action.ok_or_else(|| Error::missing_field("action"))?;
let obj = obj.ok_or_else(|| Error::missing_field("obj"))?;
let key = key.ok_or_else(|| Error::missing_field("key"))?;
let pred = pred.ok_or_else(|| Error::missing_field("pred"))?;
let insert = insert.unwrap_or(false);
let value = Value::from(value, datatype);
let action = match action {
ReqOpType::MakeMap => OpType::Make(ObjType::Map),
ReqOpType::MakeTable => OpType::Make(ObjType::Table),
ReqOpType::MakeList => OpType::Make(ObjType::List),
ReqOpType::MakeText => OpType::Make(ObjType::Text),
ReqOpType::Del => OpType::Del,
ReqOpType::Link => {
OpType::Link(child.ok_or_else(|| Error::missing_field("pred"))?)
}
ReqOpType::Set => OpType::Set(
Value::from(value, datatype)
.ok_or_else(|| Error::missing_field("value"))?,
),
ReqOpType::Inc => match value {
Some(Value::Int(n)) => Ok(OpType::Inc(n)),
Some(Value::Uint(n)) => Ok(OpType::Inc(n as i64)),
Some(Value::F64(n)) => Ok(OpType::Inc(n as i64)),
Some(Value::F32(n)) => Ok(OpType::Inc(n as i64)),
Some(Value::Counter(n)) => Ok(OpType::Inc(n)),
Some(Value::Timestamp(n)) => Ok(OpType::Inc(n)),
Some(Value::Str(s)) => {
Err(Error::invalid_value(Unexpected::Str(&s), &"a number"))
}
Some(Value::Boolean(b)) => {
Err(Error::invalid_value(Unexpected::Bool(b), &"a number"))
}
Some(Value::Null) => {
Err(Error::invalid_value(Unexpected::Other("null"), &"a number"))
}
None => Err(Error::missing_field("value")),
}?,
};
Ok(Operation {
action,
obj,
key,
insert,
pred,
})
}
}
deserializer.deserialize_struct("Operation", &FIELDS, OperationVisitor)
}
}

View file

@ -1,22 +1,40 @@
use crate::error::InvalidActorID;
use crate::ActorID;
use std::{convert::Infallible, fmt, str::FromStr};
use std::convert::TryFrom;
use std::{fmt, str::FromStr};
impl From<&str> for ActorID {
fn from(s: &str) -> Self {
ActorID(s.into())
impl TryFrom<&str> for ActorID {
type Error = InvalidActorID;
fn try_from(s: &str) -> Result<Self, Self::Error> {
hex::decode(s)
.map(ActorID)
.map_err(|_| InvalidActorID(s.into()))
}
}
impl From<&[u8]> for ActorID {
fn from(b: &[u8]) -> Self {
ActorID(b.to_vec())
}
}
impl From<Vec<u8>> for ActorID {
fn from(b: Vec<u8>) -> Self {
ActorID(b)
}
}
impl FromStr for ActorID {
type Err = Infallible;
type Err = InvalidActorID;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(ActorID(s.into()))
ActorID::try_from(s)
}
}
impl fmt::Display for ActorID {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
write!(f, "{}", self.to_hex_string())
}
}