Move TransactionInner and add get methods to Transaction
This commit is contained in:
parent
7cbd6effb7
commit
ea826b70f4
2 changed files with 386 additions and 322 deletions
automerge/src
|
@ -1,22 +1,9 @@
|
|||
use crate::exid::ExId;
|
||||
use crate::query;
|
||||
use crate::types::{Key, ObjId, OpId};
|
||||
use crate::{change::export_change, types::Op, Automerge, ChangeHash, Prop, Value};
|
||||
use crate::{AutomergeError, OpType};
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
use crate::AutomergeError;
|
||||
use crate::{Automerge, ChangeHash, Prop, Value};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TransactionInner {
|
||||
pub(crate) actor: usize,
|
||||
pub(crate) seq: u64,
|
||||
pub(crate) start_op: u64,
|
||||
pub(crate) time: i64,
|
||||
pub(crate) message: Option<String>,
|
||||
pub(crate) extra_bytes: Vec<u8>,
|
||||
pub(crate) hash: Option<ChangeHash>,
|
||||
pub(crate) deps: Vec<ChangeHash>,
|
||||
pub(crate) operations: Vec<Op>,
|
||||
}
|
||||
mod inner;
|
||||
pub(crate) use inner::TransactionInner;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Transaction<'a> {
|
||||
|
@ -24,311 +11,6 @@ pub struct Transaction<'a> {
|
|||
pub(crate) doc: &'a mut Automerge,
|
||||
}
|
||||
|
||||
impl TransactionInner {
|
||||
pub fn pending_ops(&self) -> usize {
|
||||
self.operations.len()
|
||||
}
|
||||
|
||||
/// Commit the operations performed in this transaction, returning the hashes corresponding to
|
||||
/// the new heads.
|
||||
pub fn commit(
|
||||
mut self,
|
||||
doc: &mut Automerge,
|
||||
message: Option<String>,
|
||||
time: Option<i64>,
|
||||
) -> Vec<ChangeHash> {
|
||||
if message.is_some() {
|
||||
self.message = message;
|
||||
}
|
||||
|
||||
if let Some(t) = time {
|
||||
self.time = t;
|
||||
}
|
||||
|
||||
doc.update_history(export_change(&self, &doc.ops.m.actors, &doc.ops.m.props));
|
||||
|
||||
doc.get_heads()
|
||||
}
|
||||
|
||||
/// Undo the operations added in this transaction, returning the number of cancelled
|
||||
/// operations.
|
||||
pub fn rollback(self, doc: &mut Automerge) -> usize {
|
||||
let num = self.operations.len();
|
||||
// remove in reverse order so sets are removed before makes etc...
|
||||
for op in self.operations.iter().rev() {
|
||||
for pred_id in &op.pred {
|
||||
// FIXME - use query to make this fast
|
||||
if let Some(p) = doc.ops.iter().position(|o| o.id == *pred_id) {
|
||||
doc.ops.replace(op.obj, p, |o| o.remove_succ(op));
|
||||
}
|
||||
}
|
||||
if let Some(pos) = doc.ops.iter().position(|o| o.id == op.id) {
|
||||
doc.ops.remove(op.obj, pos);
|
||||
}
|
||||
}
|
||||
num
|
||||
}
|
||||
|
||||
/// Set the value of property `P` to value `V` in object `obj`.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// The opid of the operation which was created, or None if this operation doesn't change the
|
||||
/// document
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This will return an error if
|
||||
/// - The object does not exist
|
||||
/// - The key is the wrong type for the object
|
||||
/// - The key does not exist in the object
|
||||
pub fn set<P: Into<Prop>, V: Into<Value>>(
|
||||
&mut self,
|
||||
doc: &mut Automerge,
|
||||
obj: &ExId,
|
||||
prop: P,
|
||||
value: V,
|
||||
) -> Result<Option<ExId>, AutomergeError> {
|
||||
let obj = doc.exid_to_obj(obj)?;
|
||||
let value = value.into();
|
||||
if let Some(id) = self.local_op(doc, obj, prop.into(), value.into())? {
|
||||
Ok(Some(doc.id_to_exid(id)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn next_id(&mut self) -> OpId {
|
||||
OpId(self.start_op + self.operations.len() as u64, self.actor)
|
||||
}
|
||||
|
||||
fn insert_local_op(&mut self, doc: &mut Automerge, op: Op, pos: usize, succ_pos: &[usize]) {
|
||||
for succ in succ_pos {
|
||||
doc.ops.replace(op.obj, *succ, |old_op| {
|
||||
old_op.add_succ(&op);
|
||||
});
|
||||
}
|
||||
|
||||
if !op.is_del() {
|
||||
doc.ops.insert(pos, op.clone());
|
||||
}
|
||||
|
||||
self.operations.push(op);
|
||||
}
|
||||
|
||||
pub fn insert<V: Into<Value>>(
|
||||
&mut self,
|
||||
doc: &mut Automerge,
|
||||
obj: &ExId,
|
||||
index: usize,
|
||||
value: V,
|
||||
) -> Result<Option<ExId>, AutomergeError> {
|
||||
let obj = doc.exid_to_obj(obj)?;
|
||||
if let Some(id) = self.do_insert(doc, obj, index, value)? {
|
||||
Ok(Some(doc.id_to_exid(id)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn do_insert<V: Into<Value>>(
|
||||
&mut self,
|
||||
doc: &mut Automerge,
|
||||
obj: ObjId,
|
||||
index: usize,
|
||||
value: V,
|
||||
) -> Result<Option<OpId>, AutomergeError> {
|
||||
let id = self.next_id();
|
||||
|
||||
let query = doc.ops.search(obj, query::InsertNth::new(index));
|
||||
|
||||
let key = query.key()?;
|
||||
let value = value.into();
|
||||
let action = value.into();
|
||||
let is_make = matches!(&action, OpType::Make(_));
|
||||
|
||||
let op = Op {
|
||||
change: doc.history.len(),
|
||||
id,
|
||||
action,
|
||||
obj,
|
||||
key,
|
||||
succ: Default::default(),
|
||||
pred: Default::default(),
|
||||
insert: true,
|
||||
};
|
||||
|
||||
doc.ops.insert(query.pos(), op.clone());
|
||||
self.operations.push(op);
|
||||
|
||||
if is_make {
|
||||
Ok(Some(id))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn local_op(
|
||||
&mut self,
|
||||
doc: &mut Automerge,
|
||||
obj: ObjId,
|
||||
prop: Prop,
|
||||
action: OpType,
|
||||
) -> Result<Option<OpId>, AutomergeError> {
|
||||
match prop {
|
||||
Prop::Map(s) => self.local_map_op(doc, obj, s, action),
|
||||
Prop::Seq(n) => self.local_list_op(doc, obj, n, action),
|
||||
}
|
||||
}
|
||||
|
||||
fn local_map_op(
|
||||
&mut self,
|
||||
doc: &mut Automerge,
|
||||
obj: ObjId,
|
||||
prop: String,
|
||||
action: OpType,
|
||||
) -> Result<Option<OpId>, AutomergeError> {
|
||||
if prop.is_empty() {
|
||||
return Err(AutomergeError::EmptyStringKey);
|
||||
}
|
||||
|
||||
let id = self.next_id();
|
||||
let prop = doc.ops.m.props.cache(prop);
|
||||
let query = doc.ops.search(obj, query::Prop::new(prop));
|
||||
|
||||
if query.ops.len() == 1 && query.ops[0].is_noop(&action) {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let is_make = matches!(&action, OpType::Make(_));
|
||||
|
||||
let pred = query.ops.iter().map(|op| op.id).collect();
|
||||
|
||||
let op = Op {
|
||||
change: doc.history.len(),
|
||||
id,
|
||||
action,
|
||||
obj,
|
||||
key: Key::Map(prop),
|
||||
succ: Default::default(),
|
||||
pred,
|
||||
insert: false,
|
||||
};
|
||||
|
||||
self.insert_local_op(doc, op, query.pos, &query.ops_pos);
|
||||
|
||||
if is_make {
|
||||
Ok(Some(id))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn local_list_op(
|
||||
&mut self,
|
||||
doc: &mut Automerge,
|
||||
obj: ObjId,
|
||||
index: usize,
|
||||
action: OpType,
|
||||
) -> Result<Option<OpId>, AutomergeError> {
|
||||
let query = doc.ops.search(obj, query::Nth::new(index));
|
||||
|
||||
let id = self.next_id();
|
||||
let pred = query.ops.iter().map(|op| op.id).collect();
|
||||
let key = query.key()?;
|
||||
|
||||
if query.ops.len() == 1 && query.ops[0].is_noop(&action) {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let is_make = matches!(&action, OpType::Make(_));
|
||||
|
||||
let op = Op {
|
||||
change: doc.history.len(),
|
||||
id,
|
||||
action,
|
||||
obj,
|
||||
key,
|
||||
succ: Default::default(),
|
||||
pred,
|
||||
insert: false,
|
||||
};
|
||||
|
||||
self.insert_local_op(doc, op, query.pos, &query.ops_pos);
|
||||
|
||||
if is_make {
|
||||
Ok(Some(id))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inc<P: Into<Prop>>(
|
||||
&mut self,
|
||||
doc: &mut Automerge,
|
||||
obj: &ExId,
|
||||
prop: P,
|
||||
value: i64,
|
||||
) -> Result<(), AutomergeError> {
|
||||
let obj = doc.exid_to_obj(obj)?;
|
||||
self.local_op(doc, obj, prop.into(), OpType::Inc(value))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn del<P: Into<Prop>>(
|
||||
&mut self,
|
||||
doc: &mut Automerge,
|
||||
obj: &ExId,
|
||||
prop: P,
|
||||
) -> Result<(), AutomergeError> {
|
||||
let obj = doc.exid_to_obj(obj)?;
|
||||
self.local_op(doc, obj, prop.into(), OpType::Del)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Splice new elements into the given sequence. Returns a vector of the OpIds used to insert
|
||||
/// the new elements
|
||||
pub fn splice(
|
||||
&mut self,
|
||||
doc: &mut Automerge,
|
||||
obj: &ExId,
|
||||
mut pos: usize,
|
||||
del: usize,
|
||||
vals: Vec<Value>,
|
||||
) -> Result<Vec<ExId>, AutomergeError> {
|
||||
let obj = doc.exid_to_obj(obj)?;
|
||||
for _ in 0..del {
|
||||
// del()
|
||||
self.local_op(doc, obj, pos.into(), OpType::Del)?;
|
||||
}
|
||||
let mut results = Vec::new();
|
||||
for v in vals {
|
||||
// insert()
|
||||
let id = self.do_insert(doc, obj, pos, v.clone())?;
|
||||
if let Some(id) = id {
|
||||
results.push(doc.id_to_exid(id));
|
||||
}
|
||||
pos += 1;
|
||||
}
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
pub fn splice_text(
|
||||
&mut self,
|
||||
doc: &mut Automerge,
|
||||
obj: &ExId,
|
||||
pos: usize,
|
||||
del: usize,
|
||||
text: &str,
|
||||
) -> Result<Vec<ExId>, AutomergeError> {
|
||||
let mut vals = vec![];
|
||||
for c in text.to_owned().graphemes(true) {
|
||||
vals.push(c.into());
|
||||
}
|
||||
self.splice(doc, obj, pos, del, vals)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Transaction<'a> {
|
||||
pub fn pending_ops(&self) -> usize {
|
||||
self.inner.pending_ops()
|
||||
|
@ -411,4 +93,62 @@ impl<'a> Transaction<'a> {
|
|||
) -> Result<Vec<ExId>, AutomergeError> {
|
||||
self.inner.splice_text(self.doc, obj, pos, del, text)
|
||||
}
|
||||
|
||||
pub fn keys(&self, obj: &ExId) -> Vec<String> {
|
||||
self.doc.keys(obj)
|
||||
}
|
||||
|
||||
pub fn keys_at(&self, obj: &ExId, heads: &[ChangeHash]) -> Vec<String> {
|
||||
self.doc.keys_at(obj, heads)
|
||||
}
|
||||
|
||||
pub fn length(&self, obj: &ExId) -> usize {
|
||||
self.doc.length(obj)
|
||||
}
|
||||
|
||||
pub fn length_at(&self, obj: &ExId, heads: &[ChangeHash]) -> usize {
|
||||
self.doc.length_at(obj, heads)
|
||||
}
|
||||
|
||||
pub fn text(&self, obj: &ExId) -> Result<String, AutomergeError> {
|
||||
self.doc.text(obj)
|
||||
}
|
||||
|
||||
pub fn text_at(&self, obj: &ExId, heads: &[ChangeHash]) -> Result<String, AutomergeError> {
|
||||
self.doc.text_at(obj, heads)
|
||||
}
|
||||
|
||||
pub fn value<P: Into<Prop>>(
|
||||
&self,
|
||||
obj: &ExId,
|
||||
prop: P,
|
||||
) -> Result<Option<(Value, ExId)>, AutomergeError> {
|
||||
self.doc.value(obj, prop)
|
||||
}
|
||||
|
||||
pub fn value_at<P: Into<Prop>>(
|
||||
&self,
|
||||
obj: &ExId,
|
||||
prop: P,
|
||||
heads: &[ChangeHash],
|
||||
) -> Result<Option<(Value, ExId)>, AutomergeError> {
|
||||
self.doc.value_at(obj, prop, heads)
|
||||
}
|
||||
|
||||
pub fn values<P: Into<Prop>>(
|
||||
&self,
|
||||
obj: &ExId,
|
||||
prop: P,
|
||||
) -> Result<Vec<(Value, ExId)>, AutomergeError> {
|
||||
self.doc.values(obj, prop)
|
||||
}
|
||||
|
||||
pub fn values_at<P: Into<Prop>>(
|
||||
&self,
|
||||
obj: &ExId,
|
||||
prop: P,
|
||||
heads: &[ChangeHash],
|
||||
) -> Result<Vec<(Value, ExId)>, AutomergeError> {
|
||||
self.doc.values_at(obj, prop, heads)
|
||||
}
|
||||
}
|
||||
|
|
324
automerge/src/transaction/inner.rs
Normal file
324
automerge/src/transaction/inner.rs
Normal file
|
@ -0,0 +1,324 @@
|
|||
use crate::exid::ExId;
|
||||
use crate::query;
|
||||
use crate::types::{Key, ObjId, OpId};
|
||||
use crate::{change::export_change, types::Op, Automerge, ChangeHash, Prop, Value};
|
||||
use crate::{AutomergeError, OpType};
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TransactionInner {
|
||||
pub(crate) actor: usize,
|
||||
pub(crate) seq: u64,
|
||||
pub(crate) start_op: u64,
|
||||
pub(crate) time: i64,
|
||||
pub(crate) message: Option<String>,
|
||||
pub(crate) extra_bytes: Vec<u8>,
|
||||
pub(crate) hash: Option<ChangeHash>,
|
||||
pub(crate) deps: Vec<ChangeHash>,
|
||||
pub(crate) operations: Vec<Op>,
|
||||
}
|
||||
|
||||
impl TransactionInner {
|
||||
pub fn pending_ops(&self) -> usize {
|
||||
self.operations.len()
|
||||
}
|
||||
|
||||
/// Commit the operations performed in this transaction, returning the hashes corresponding to
|
||||
/// the new heads.
|
||||
pub fn commit(
|
||||
mut self,
|
||||
doc: &mut Automerge,
|
||||
message: Option<String>,
|
||||
time: Option<i64>,
|
||||
) -> Vec<ChangeHash> {
|
||||
if message.is_some() {
|
||||
self.message = message;
|
||||
}
|
||||
|
||||
if let Some(t) = time {
|
||||
self.time = t;
|
||||
}
|
||||
|
||||
doc.update_history(export_change(&self, &doc.ops.m.actors, &doc.ops.m.props));
|
||||
|
||||
doc.get_heads()
|
||||
}
|
||||
|
||||
/// Undo the operations added in this transaction, returning the number of cancelled
|
||||
/// operations.
|
||||
pub fn rollback(self, doc: &mut Automerge) -> usize {
|
||||
let num = self.operations.len();
|
||||
// remove in reverse order so sets are removed before makes etc...
|
||||
for op in self.operations.iter().rev() {
|
||||
for pred_id in &op.pred {
|
||||
// FIXME - use query to make this fast
|
||||
if let Some(p) = doc.ops.iter().position(|o| o.id == *pred_id) {
|
||||
doc.ops.replace(op.obj, p, |o| o.remove_succ(op));
|
||||
}
|
||||
}
|
||||
if let Some(pos) = doc.ops.iter().position(|o| o.id == op.id) {
|
||||
doc.ops.remove(op.obj, pos);
|
||||
}
|
||||
}
|
||||
num
|
||||
}
|
||||
|
||||
/// Set the value of property `P` to value `V` in object `obj`.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// The opid of the operation which was created, or None if this operation doesn't change the
|
||||
/// document
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This will return an error if
|
||||
/// - The object does not exist
|
||||
/// - The key is the wrong type for the object
|
||||
/// - The key does not exist in the object
|
||||
pub fn set<P: Into<Prop>, V: Into<Value>>(
|
||||
&mut self,
|
||||
doc: &mut Automerge,
|
||||
obj: &ExId,
|
||||
prop: P,
|
||||
value: V,
|
||||
) -> Result<Option<ExId>, AutomergeError> {
|
||||
let obj = doc.exid_to_obj(obj)?;
|
||||
let value = value.into();
|
||||
if let Some(id) = self.local_op(doc, obj, prop.into(), value.into())? {
|
||||
Ok(Some(doc.id_to_exid(id)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn next_id(&mut self) -> OpId {
|
||||
OpId(self.start_op + self.operations.len() as u64, self.actor)
|
||||
}
|
||||
|
||||
fn insert_local_op(&mut self, doc: &mut Automerge, op: Op, pos: usize, succ_pos: &[usize]) {
|
||||
for succ in succ_pos {
|
||||
doc.ops.replace(op.obj, *succ, |old_op| {
|
||||
old_op.add_succ(&op);
|
||||
});
|
||||
}
|
||||
|
||||
if !op.is_del() {
|
||||
doc.ops.insert(pos, op.clone());
|
||||
}
|
||||
|
||||
self.operations.push(op);
|
||||
}
|
||||
|
||||
pub fn insert<V: Into<Value>>(
|
||||
&mut self,
|
||||
doc: &mut Automerge,
|
||||
obj: &ExId,
|
||||
index: usize,
|
||||
value: V,
|
||||
) -> Result<Option<ExId>, AutomergeError> {
|
||||
let obj = doc.exid_to_obj(obj)?;
|
||||
if let Some(id) = self.do_insert(doc, obj, index, value)? {
|
||||
Ok(Some(doc.id_to_exid(id)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn do_insert<V: Into<Value>>(
|
||||
&mut self,
|
||||
doc: &mut Automerge,
|
||||
obj: ObjId,
|
||||
index: usize,
|
||||
value: V,
|
||||
) -> Result<Option<OpId>, AutomergeError> {
|
||||
let id = self.next_id();
|
||||
|
||||
let query = doc.ops.search(obj, query::InsertNth::new(index));
|
||||
|
||||
let key = query.key()?;
|
||||
let value = value.into();
|
||||
let action = value.into();
|
||||
let is_make = matches!(&action, OpType::Make(_));
|
||||
|
||||
let op = Op {
|
||||
change: doc.history.len(),
|
||||
id,
|
||||
action,
|
||||
obj,
|
||||
key,
|
||||
succ: Default::default(),
|
||||
pred: Default::default(),
|
||||
insert: true,
|
||||
};
|
||||
|
||||
doc.ops.insert(query.pos(), op.clone());
|
||||
self.operations.push(op);
|
||||
|
||||
if is_make {
|
||||
Ok(Some(id))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn local_op(
|
||||
&mut self,
|
||||
doc: &mut Automerge,
|
||||
obj: ObjId,
|
||||
prop: Prop,
|
||||
action: OpType,
|
||||
) -> Result<Option<OpId>, AutomergeError> {
|
||||
match prop {
|
||||
Prop::Map(s) => self.local_map_op(doc, obj, s, action),
|
||||
Prop::Seq(n) => self.local_list_op(doc, obj, n, action),
|
||||
}
|
||||
}
|
||||
|
||||
fn local_map_op(
|
||||
&mut self,
|
||||
doc: &mut Automerge,
|
||||
obj: ObjId,
|
||||
prop: String,
|
||||
action: OpType,
|
||||
) -> Result<Option<OpId>, AutomergeError> {
|
||||
if prop.is_empty() {
|
||||
return Err(AutomergeError::EmptyStringKey);
|
||||
}
|
||||
|
||||
let id = self.next_id();
|
||||
let prop = doc.ops.m.props.cache(prop);
|
||||
let query = doc.ops.search(obj, query::Prop::new(prop));
|
||||
|
||||
if query.ops.len() == 1 && query.ops[0].is_noop(&action) {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let is_make = matches!(&action, OpType::Make(_));
|
||||
|
||||
let pred = query.ops.iter().map(|op| op.id).collect();
|
||||
|
||||
let op = Op {
|
||||
change: doc.history.len(),
|
||||
id,
|
||||
action,
|
||||
obj,
|
||||
key: Key::Map(prop),
|
||||
succ: Default::default(),
|
||||
pred,
|
||||
insert: false,
|
||||
};
|
||||
|
||||
self.insert_local_op(doc, op, query.pos, &query.ops_pos);
|
||||
|
||||
if is_make {
|
||||
Ok(Some(id))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn local_list_op(
|
||||
&mut self,
|
||||
doc: &mut Automerge,
|
||||
obj: ObjId,
|
||||
index: usize,
|
||||
action: OpType,
|
||||
) -> Result<Option<OpId>, AutomergeError> {
|
||||
let query = doc.ops.search(obj, query::Nth::new(index));
|
||||
|
||||
let id = self.next_id();
|
||||
let pred = query.ops.iter().map(|op| op.id).collect();
|
||||
let key = query.key()?;
|
||||
|
||||
if query.ops.len() == 1 && query.ops[0].is_noop(&action) {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let is_make = matches!(&action, OpType::Make(_));
|
||||
|
||||
let op = Op {
|
||||
change: doc.history.len(),
|
||||
id,
|
||||
action,
|
||||
obj,
|
||||
key,
|
||||
succ: Default::default(),
|
||||
pred,
|
||||
insert: false,
|
||||
};
|
||||
|
||||
self.insert_local_op(doc, op, query.pos, &query.ops_pos);
|
||||
|
||||
if is_make {
|
||||
Ok(Some(id))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inc<P: Into<Prop>>(
|
||||
&mut self,
|
||||
doc: &mut Automerge,
|
||||
obj: &ExId,
|
||||
prop: P,
|
||||
value: i64,
|
||||
) -> Result<(), AutomergeError> {
|
||||
let obj = doc.exid_to_obj(obj)?;
|
||||
self.local_op(doc, obj, prop.into(), OpType::Inc(value))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn del<P: Into<Prop>>(
|
||||
&mut self,
|
||||
doc: &mut Automerge,
|
||||
obj: &ExId,
|
||||
prop: P,
|
||||
) -> Result<(), AutomergeError> {
|
||||
let obj = doc.exid_to_obj(obj)?;
|
||||
self.local_op(doc, obj, prop.into(), OpType::Del)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Splice new elements into the given sequence. Returns a vector of the OpIds used to insert
|
||||
/// the new elements
|
||||
pub fn splice(
|
||||
&mut self,
|
||||
doc: &mut Automerge,
|
||||
obj: &ExId,
|
||||
mut pos: usize,
|
||||
del: usize,
|
||||
vals: Vec<Value>,
|
||||
) -> Result<Vec<ExId>, AutomergeError> {
|
||||
let obj = doc.exid_to_obj(obj)?;
|
||||
for _ in 0..del {
|
||||
// del()
|
||||
self.local_op(doc, obj, pos.into(), OpType::Del)?;
|
||||
}
|
||||
let mut results = Vec::new();
|
||||
for v in vals {
|
||||
// insert()
|
||||
let id = self.do_insert(doc, obj, pos, v.clone())?;
|
||||
if let Some(id) = id {
|
||||
results.push(doc.id_to_exid(id));
|
||||
}
|
||||
pos += 1;
|
||||
}
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
pub fn splice_text(
|
||||
&mut self,
|
||||
doc: &mut Automerge,
|
||||
obj: &ExId,
|
||||
pos: usize,
|
||||
del: usize,
|
||||
text: &str,
|
||||
) -> Result<Vec<ExId>, AutomergeError> {
|
||||
let mut vals = vec![];
|
||||
for c in text.to_owned().graphemes(true) {
|
||||
vals.push(c.into());
|
||||
}
|
||||
self.splice(doc, obj, pos, del, vals)
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue