use crate::change_graph::ChangeGraph;
use crate::op_set2::change::{ActorMapper, BuildChangeMetadata};
use crate::op_set2::OpSet;
use crate::storage::change::{Unverified, Verified};
use crate::storage::{parse, Header};
use crate::types::{ActorId, ChangeHash};
use crate::{AutomergeError, Change};
use std::borrow::Cow;
mod builder;
mod error;
mod meta;
mod storage;
pub use builder::BundleChangeIter;
pub(crate) use builder::{BundleBuilder, BundleChangeIterUnverified, OpIter, OpIterUnverified};
pub(crate) use error::ParseError;
pub(crate) use meta::BundleMetadata;
pub(crate) use storage::BundleStorage;
#[derive(Debug)]
pub struct Bundle {
pub(crate) storage: BundleStorage<'static, Verified>,
}
impl Bundle {
pub(crate) fn for_hashes<I>(
op_set: &OpSet,
change_graph: &ChangeGraph,
hashes: I,
) -> Result<Bundle, AutomergeError>
where
I: IntoIterator<Item = ChangeHash>,
{
let changes = change_graph
.get_bundle_metadata(hashes)
.collect::<Result<_, _>>()?;
Ok(Self::from_meta(op_set, changes))
}
fn from_meta(op_set: &OpSet, changes: Vec<BundleMetadata<'_>>) -> Bundle {
let min = changes
.iter()
.map(|c| c.start_op as usize)
.min()
.unwrap_or(0);
let max = changes.iter().map(|c| c.max_op as usize).max().unwrap_or(0) + 1;
let mapper = ActorMapper::new(&op_set.actors);
let mut collector = BundleBuilder::from_change_meta(changes, mapper);
for op in op_set.iter_ctr_range(min..max) {
let op_id = op.id;
let op_succ = op.succ();
collector.process_op(op);
for id in op_succ {
collector.process_succ(op_id, id);
}
}
collector.finish()
}
pub(crate) fn new_from_unverified(
stored: BundleStorage<'static, Unverified>,
) -> Result<Self, ParseError> {
Ok(Self {
storage: stored.verify()?,
})
}
pub fn actors(&self) -> &[ActorId] {
&self.storage.actors
}
pub fn authors(&self) -> &[Vec<u8>] {
&[]
}
pub fn iter_changes(&self) -> BundleChangeIter<'_> {
self.storage.iter_change_meta()
}
pub fn to_changes(&self) -> Result<Vec<Change>, AutomergeError> {
self.storage
.to_changes()
.map_err(|e| AutomergeError::Unbundle(Box::new(e)))
}
pub fn bytes(&self) -> &[u8] {
&self.storage.bytes
}
pub fn deps(&self) -> &[ChangeHash] {
self.storage.deps()
}
}
#[derive(Clone, Debug)]
pub struct BundleChange<'a> {
pub actor: usize,
pub author: Option<usize>,
pub seq: u64,
pub start_op: u64,
pub max_op: u64,
pub timestamp: i64,
pub message: Option<Cow<'a, str>>,
pub deps: Vec<u64>,
pub extra: Cow<'a, [u8]>,
}
impl<'a> From<BundleChange<'a>> for BuildChangeMetadata<'a> {
fn from(bundle: BundleChange<'a>) -> Self {
BuildChangeMetadata {
actor: bundle.actor,
seq: bundle.seq,
start_op: bundle.start_op,
max_op: bundle.max_op,
timestamp: bundle.timestamp,
message: bundle.message,
deps: bundle.deps,
extra: bundle.extra,
builder: 0,
}
}
}
impl<'a> TryFrom<&'a [u8]> for Bundle {
type Error = InvalidBundle;
fn try_from(bytes: &'a [u8]) -> Result<Self, Self::Error> {
let input = parse::Input::new(bytes);
let (i, header) = Header::parse::<crate::storage::chunk::error::Header>(input)
.map_err(|e| InvalidBundle(format!("invalid header: {}", e)))?;
let (_i, bundle) = BundleStorage::parse_following_header(i, header)
.map_err(|e| InvalidBundle(format!("invalid contents: {}", e)))?;
let verified = bundle
.verify()
.map_err(|e| InvalidBundle(format!("unable to verify ops: {}", e)))?;
Ok(Self {
storage: verified.into_owned(),
})
}
}
#[derive(Debug, thiserror::Error)]
#[error("invalid bundle: {0}")]
pub struct InvalidBundle(String);
#[cfg(test)]
mod test {
use crate::transaction::Transactable;
use crate::{Automerge, ROOT};
#[test]
fn make_bundle() {
let mut doc = Automerge::new();
let mut tx = doc.transaction();
tx.put(&ROOT, "aaa", "aaa").unwrap();
let (Some(h0), _) = tx.commit() else { panic!() };
let mut d2 = doc.fork();
let mut tx = doc.transaction();
tx.put(&ROOT, "bbb", "bbb").unwrap();
let (Some(h1), _) = tx.commit() else { panic!() };
let mut tx = doc.transaction();
tx.put(&ROOT, "ccc", "ccc").unwrap();
let (Some(h2), _) = tx.commit() else { panic!() };
let bundle = doc.bundle([h0, h1, h2]).unwrap();
let changes = bundle.to_changes().unwrap();
assert_eq!(changes.len(), 3);
assert_eq!(changes[0].max_op(), 1);
assert_eq!(changes[0].hash(), h0);
assert_eq!(changes[1].max_op(), 2);
assert_eq!(changes[1].hash(), h1);
assert_eq!(changes[2].max_op(), 3);
assert_eq!(changes[2].hash(), h2);
d2.load_incremental(bundle.bytes()).unwrap();
assert_eq!(doc.save(), d2.save());
let bundle = doc.bundle([h0, h2]).unwrap();
let changes = bundle.to_changes().unwrap();
assert_eq!(changes.len(), 2);
assert_eq!(changes[0].max_op(), 1);
assert_eq!(changes[0].hash(), h0);
assert_eq!(changes[1].max_op(), 3);
assert_eq!(changes[1].hash(), h2);
}
}