//! For tests: writing to documents the way the node does, one change at a time, so //! that tests don't depend on how the engine groups writes into changes. use super::{Document, Transaction}; /// The writes `f` makes to `doc`, as one change; what `f` returns. pub(crate) fn change(doc: &mut Document, f: impl FnOnce(&mut Transaction<'_>) -> T) -> T { let mut transaction = doc.transaction(); let out = f(&mut transaction); transaction.commit(); out } /// The writes `f` makes to `doc`, as one change if it succeeds, dropped if not; what /// `f` returns. Committed at time 0: the same writes make the same change. pub(crate) fn try_change( doc: &mut Document, f: impl FnOnce(&mut Transaction<'_>) -> Result, ) -> Result { let mut transaction = doc.transaction(); let out = f(&mut transaction); match &out { Ok(_) => { transaction.commit_with(automerge::transaction::CommitOptions::default().with_time(0)); } Err(_) => { transaction.rollback(); } } out } /// `raw` (an uncompressed change chunk, as `Change::raw_bytes` gives one) with an author /// footer appended to its extra bytes, its checksum made right again: what no honest /// node sends for a change that isn't its actor's first (`changes::misplaced_author`). pub(crate) fn with_author_footer(raw: &[u8], author: &[u8]) -> Vec { use sha2::{Digest, Sha256}; fn leb(mut n: usize, out: &mut Vec) { loop { let byte = (n & 0x7f) as u8; n >>= 7; if n == 0 { out.push(byte); return; } out.push(byte | 0x80); } } assert_eq!(raw[8], 1, "an uncompressed change chunk"); let (mut length, mut read, mut shift) = (0usize, 0, 0); loop { let byte = raw[9 + read]; length |= usize::from(byte & 0x7f) << shift; read += 1; if byte & 0x80 == 0 { break; } shift += 7; } let mut body = raw[9 + read..9 + read + length].to_vec(); body.push(1); leb(author.len(), &mut body); body.extend_from_slice(author); let mut header = Vec::new(); leb(body.len(), &mut header); let mut summed = vec![1u8]; summed.extend(&header); summed.extend(&body); let sum = Sha256::digest(&summed); let mut out = raw[..4].to_vec(); out.extend(&sum[..4]); out.push(1); out.extend(header); out.extend(body); out }