autosurgeon 0.12.0

A library for working with data in automerge documents
Documentation
use std::ops::RangeBounds;

use automerge::{self as am, AutomergeError, ObjId, Value};

/// An abstraction over the different ways of reading an automerge document
pub trait ReadDoc {
    type Parents<'a>: Iterator<Item = am::Parent>
    where
        Self: 'a;
    fn get_heads(&self) -> Vec<automerge::ChangeHash>;

    fn get<P: Into<am::Prop>>(
        &self,
        obj: &ObjId,
        prop: P,
    ) -> Result<Option<(Value<'_>, ObjId)>, AutomergeError>;

    fn object_type<O: AsRef<ObjId>>(&self, obj: O) -> Option<am::ObjType>;
    fn map_range<'a, O, R>(&'a self, obj: O, range: R) -> am::iter::MapRange<'a>
    where
        R: RangeBounds<String> + 'a,
        O: AsRef<ObjId>,
        R: RangeBounds<String>;

    fn list_range<O: AsRef<ObjId>, R: RangeBounds<usize>>(
        &self,
        obj: O,
        range: R,
    ) -> am::iter::ListRange<'_>;

    fn length<O: AsRef<ObjId>>(&self, obj: O) -> usize;

    fn text<O: AsRef<ObjId>>(&self, obj: O) -> Result<String, AutomergeError>;
    fn parents<O: AsRef<ObjId>>(&self, obj: O) -> Result<Self::Parents<'_>, AutomergeError>;
    fn is_empty(&self) -> bool;
}

/// An abstraction over the read + write operations we need from an automerge document
pub trait Doc: ReadDoc {
    fn put<O: AsRef<ObjId>, P: Into<am::Prop>, V: Into<am::ScalarValue>>(
        &mut self,
        obj: O,
        prop: P,
        value: V,
    ) -> Result<(), AutomergeError>;

    fn put_object<O: AsRef<am::ObjId>, P: Into<am::Prop>>(
        &mut self,
        obj: O,
        prop: P,
        value: am::ObjType,
    ) -> Result<ObjId, AutomergeError>;

    fn insert<O: AsRef<ObjId>, V: Into<am::ScalarValue>>(
        &mut self,
        obj: O,
        index: usize,
        value: V,
    ) -> Result<(), AutomergeError>;

    fn insert_object<O: AsRef<ObjId>>(
        &mut self,
        obj: O,
        index: usize,
        value: am::ObjType,
    ) -> Result<ObjId, AutomergeError>;

    fn increment<O: AsRef<ObjId>, P: Into<am::Prop>>(
        &mut self,
        obj: O,
        prop: P,
        value: i64,
    ) -> Result<(), AutomergeError>;

    fn delete<O: AsRef<ObjId>, P: Into<am::Prop>>(
        &mut self,
        obj: O,
        prop: P,
    ) -> Result<(), AutomergeError>;
    fn splice_text<O: AsRef<ObjId>>(
        &mut self,
        obj: O,
        pos: usize,
        del: isize,
        text: &str,
    ) -> Result<(), AutomergeError>;

    fn update_text<O: AsRef<ObjId>>(&mut self, obj: O, text: &str) -> Result<(), AutomergeError>;

    fn batch_create<O: AsRef<ObjId>, P: Into<am::Prop>>(
        &mut self,
        obj: O,
        prop: P,
        value: am::hydrate::Value,
        insert: bool,
    ) -> Result<(), AutomergeError>;

    fn init_root_from_hydrate(&mut self, value: &am::hydrate::Map) -> Result<(), AutomergeError>;
}

impl ReadDoc for am::AutoCommit {
    type Parents<'a> = am::Parents<'a>;
    fn get_heads(&self) -> Vec<am::ChangeHash> {
        am::transaction::Transactable::base_heads(self)
    }

    fn get<P: Into<am::Prop>>(
        &self,
        obj: &ObjId,
        prop: P,
    ) -> Result<Option<(Value<'_>, ObjId)>, AutomergeError> {
        am::ReadDoc::get(self, obj, prop)
    }

    fn object_type<O: AsRef<ObjId>>(&self, obj: O) -> Option<am::ObjType> {
        am::ReadDoc::object_type(self, obj)
            .map(Some)
            .unwrap_or(None)
    }

    fn map_range<'a, O, R>(&'a self, obj: O, range: R) -> am::iter::MapRange<'a>
    where
        R: RangeBounds<String> + 'a,
        O: AsRef<ObjId>,
        R: RangeBounds<String>,
    {
        am::ReadDoc::map_range(self, obj, range)
    }

    fn list_range<O: AsRef<ObjId>, R: RangeBounds<usize>>(
        &self,
        obj: O,
        range: R,
    ) -> am::iter::ListRange<'_> {
        am::ReadDoc::list_range(self, obj, range)
    }

    fn length<O: AsRef<ObjId>>(&self, obj: O) -> usize {
        am::ReadDoc::length(self, obj)
    }

    fn text<O: AsRef<ObjId>>(&self, obj: O) -> Result<String, AutomergeError> {
        am::ReadDoc::text(self, obj)
    }

    fn parents<O: AsRef<ObjId>>(&self, obj: O) -> Result<Self::Parents<'_>, AutomergeError> {
        am::ReadDoc::parents(self, obj)
    }

    fn is_empty(&self) -> bool {
        am::ReadDoc::keys(self, am::ROOT).count() == 0
    }
}

impl ReadDoc for am::transaction::Transaction<'_> {
    type Parents<'b>
        = am::Parents<'b>
    where
        Self: 'b;
    fn get_heads(&self) -> Vec<am::ChangeHash> {
        am::transaction::Transactable::base_heads(self)
    }

    fn get<P: Into<am::Prop>>(
        &self,
        obj: &ObjId,
        prop: P,
    ) -> Result<Option<(Value<'_>, ObjId)>, AutomergeError> {
        am::ReadDoc::get(self, obj, prop)
    }

    fn object_type<O: AsRef<ObjId>>(&self, obj: O) -> Option<am::ObjType> {
        am::ReadDoc::object_type(self, obj)
            .map(Some)
            .unwrap_or(None)
    }

    fn map_range<'b, O: AsRef<ObjId>, R>(&'b self, obj: O, range: R) -> am::iter::MapRange<'b>
    where
        R: RangeBounds<String> + 'b,
    {
        am::ReadDoc::map_range(self, obj, range)
    }

    fn list_range<O: AsRef<ObjId>, R: RangeBounds<usize>>(
        &self,
        obj: O,
        range: R,
    ) -> am::iter::ListRange<'_> {
        am::ReadDoc::list_range(self, obj, range)
    }

    fn length<O: AsRef<ObjId>>(&self, obj: O) -> usize {
        am::ReadDoc::length(self, obj)
    }

    fn text<O: AsRef<ObjId>>(&self, obj: O) -> Result<String, AutomergeError> {
        am::ReadDoc::text(self, obj)
    }

    fn parents<O: AsRef<ObjId>>(&self, obj: O) -> Result<Self::Parents<'_>, AutomergeError> {
        am::ReadDoc::parents(self, obj)
    }

    fn is_empty(&self) -> bool {
        am::ReadDoc::keys(self, am::ROOT).count() == 0
    }
}

impl ReadDoc for am::Automerge {
    type Parents<'a> = am::Parents<'a>;
    fn get_heads(&self) -> Vec<am::ChangeHash> {
        am::Automerge::get_heads(self)
    }

    fn get<P: Into<am::Prop>>(
        &self,
        obj: &ObjId,
        prop: P,
    ) -> Result<Option<(Value<'_>, ObjId)>, AutomergeError> {
        am::ReadDoc::get(self, obj, prop)
    }

    fn object_type<O: AsRef<ObjId>>(&self, obj: O) -> Option<am::ObjType> {
        am::ReadDoc::object_type(self, obj)
            .map(Some)
            .unwrap_or(None)
    }

    fn map_range<'a, O: AsRef<ObjId>, R: RangeBounds<String> + 'a>(
        &'a self,
        obj: O,
        range: R,
    ) -> am::iter::MapRange<'a> {
        am::ReadDoc::map_range(self, obj, range)
    }

    fn list_range<O: AsRef<ObjId>, R: RangeBounds<usize>>(
        &self,
        obj: O,
        range: R,
    ) -> am::iter::ListRange<'_> {
        am::ReadDoc::list_range(self, obj, range)
    }

    fn length<O: AsRef<ObjId>>(&self, obj: O) -> usize {
        am::ReadDoc::length(self, obj)
    }

    fn text<O: AsRef<ObjId>>(&self, obj: O) -> Result<String, AutomergeError> {
        am::ReadDoc::text(self, obj)
    }

    fn parents<O: AsRef<ObjId>>(&self, obj: O) -> Result<Self::Parents<'_>, AutomergeError> {
        am::ReadDoc::parents(self, obj)
    }

    fn is_empty(&self) -> bool {
        am::ReadDoc::keys(self, am::ROOT).count() == 0
    }
}

impl<T: am::transaction::Transactable + ReadDoc> Doc for T {
    fn put<O: AsRef<ObjId>, P: Into<am::Prop>, V: Into<am::ScalarValue>>(
        &mut self,
        obj: O,
        prop: P,
        value: V,
    ) -> Result<(), AutomergeError> {
        am::transaction::Transactable::put(self, obj, prop, value)
    }

    fn put_object<O: AsRef<am::ObjId>, P: Into<am::Prop>>(
        &mut self,
        obj: O,
        prop: P,
        value: am::ObjType,
    ) -> Result<ObjId, AutomergeError> {
        am::transaction::Transactable::put_object(self, obj, prop, value)
    }

    fn insert<O: AsRef<ObjId>, V: Into<am::ScalarValue>>(
        &mut self,
        obj: O,
        index: usize,
        value: V,
    ) -> Result<(), AutomergeError> {
        am::transaction::Transactable::insert(self, obj, index, value)
    }

    fn insert_object<O: AsRef<ObjId>>(
        &mut self,
        obj: O,
        index: usize,
        value: am::ObjType,
    ) -> Result<ObjId, AutomergeError> {
        am::transaction::Transactable::insert_object(self, obj, index, value)
    }

    fn increment<O: AsRef<ObjId>, P: Into<am::Prop>>(
        &mut self,
        obj: O,
        prop: P,
        value: i64,
    ) -> Result<(), AutomergeError> {
        am::transaction::Transactable::increment(self, obj, prop, value)
    }

    fn delete<O: AsRef<ObjId>, P: Into<am::Prop>>(
        &mut self,
        obj: O,
        prop: P,
    ) -> Result<(), AutomergeError> {
        am::transaction::Transactable::delete(self, obj, prop)
    }

    fn splice_text<O: AsRef<ObjId>>(
        &mut self,
        obj: O,
        pos: usize,
        del: isize,
        text: &str,
    ) -> Result<(), AutomergeError> {
        am::transaction::Transactable::splice_text(self, obj, pos, del, text)
    }

    fn update_text<O: AsRef<ObjId>>(&mut self, obj: O, text: &str) -> Result<(), AutomergeError> {
        am::transaction::Transactable::update_text(self, obj.as_ref(), text)
    }

    fn batch_create<O: AsRef<ObjId>, P: Into<am::Prop>>(
        &mut self,
        obj: O,
        prop: P,
        value: am::hydrate::Value,
        insert: bool,
    ) -> Result<(), AutomergeError> {
        let prop = prop.into();
        if let am::hydrate::Value::Scalar(sv) = value {
            if insert {
                if let am::Prop::Seq(idx) = prop {
                    am::transaction::Transactable::insert(self, obj.as_ref(), idx, sv)?;
                    return Ok(());
                }
            }
            am::transaction::Transactable::put(self, obj.as_ref(), prop, sv)?;
            return Ok(());
        }
        am::transaction::Transactable::batch_create_object(
            self,
            obj.as_ref(),
            prop,
            &value,
            insert,
        )?;
        Ok(())
    }

    fn init_root_from_hydrate(&mut self, value: &am::hydrate::Map) -> Result<(), AutomergeError> {
        am::transaction::Transactable::init_root_from_hydrate(self, value)
    }
}