automorph 0.2.0

Derive macros for bidirectional Automerge-Rust struct synchronization
Documentation
//! Implementations for byte types.
//!
//! Note: `Vec<u8>` uses the generic `Vec<T>` implementation from collections.rs,
//! which stores bytes as a List. For binary storage, use `Box<[u8]>` which
//! stores as Automerge's bytes scalar. Fixed-size `[u8; N]` arrays use the
//! generic array implementation from arrays.rs.

use automerge::{ChangeHash, ObjId, Prop, ReadDoc, Value, transaction::Transactable};

use crate::{Automorph, Error, PrimitiveChanged, Result, ScalarCursor};

impl Automorph for Box<[u8]> {
    type Changes = PrimitiveChanged;
    type Cursor = ScalarCursor;

    fn save<D: Transactable + ReadDoc>(
        &self,
        doc: &mut D,
        obj: impl AsRef<ObjId>,
        prop: impl Into<Prop>,
    ) -> Result<()> {
        let vec: Vec<u8> = self.to_vec();
        vec.save(doc, obj, prop)
    }

    fn load<D: ReadDoc>(doc: &D, obj: impl AsRef<ObjId>, prop: impl Into<Prop>) -> Result<Self> {
        let prop: Prop = prop.into();
        let obj = obj.as_ref();
        match doc.get(obj, prop)? {
            Some((Value::Scalar(x), _)) => x
                .to_bytes()
                .map(|b| b.to_vec().into_boxed_slice())
                .ok_or_else(|| Error::type_mismatch("Box<[u8]>", Some(format!("{:?}", x)))),
            Some((v, _)) => Err(Error::type_mismatch("Box<[u8]>", Some(format!("{:?}", v)))),
            None => Err(Error::missing_value()),
        }
    }

    fn load_at<D: ReadDoc>(
        doc: &D,
        obj: impl AsRef<ObjId>,
        prop: impl Into<Prop>,
        heads: &[ChangeHash],
    ) -> Result<Self> {
        let prop: Prop = prop.into();
        let obj = obj.as_ref();
        match doc.get_at(obj, prop, heads)? {
            Some((Value::Scalar(x), _)) => x
                .to_bytes()
                .map(|b| b.to_vec().into_boxed_slice())
                .ok_or_else(|| Error::type_mismatch("Box<[u8]>", Some(format!("{:?}", x)))),
            Some((v, _)) => Err(Error::type_mismatch("Box<[u8]>", Some(format!("{:?}", v)))),
            None => Err(Error::missing_value()),
        }
    }

    fn diff<D: ReadDoc>(
        &self,
        doc: &D,
        obj: impl AsRef<ObjId>,
        prop: impl Into<Prop>,
    ) -> Result<Self::Changes> {
        let prop: Prop = prop.into();
        let obj = obj.as_ref();
        match doc.get(obj, prop)? {
            Some((Value::Scalar(x), _)) => {
                let doc_bytes = x
                    .to_bytes()
                    .ok_or_else(|| Error::type_mismatch("Box<[u8]>", Some(format!("{:?}", x))))?;
                Ok(PrimitiveChanged::new(self.as_ref() != doc_bytes))
            }
            Some((v, _)) => Err(Error::type_mismatch("Box<[u8]>", Some(format!("{:?}", v)))),
            None => Err(Error::missing_value()),
        }
    }

    fn diff_at<D: ReadDoc>(
        &self,
        doc: &D,
        obj: impl AsRef<ObjId>,
        prop: impl Into<Prop>,
        heads: &[ChangeHash],
    ) -> Result<Self::Changes> {
        let prop: Prop = prop.into();
        let obj = obj.as_ref();
        match doc.get_at(obj, prop, heads)? {
            Some((Value::Scalar(x), _)) => {
                let doc_bytes = x
                    .to_bytes()
                    .ok_or_else(|| Error::type_mismatch("Box<[u8]>", Some(format!("{:?}", x))))?;
                Ok(PrimitiveChanged::new(self.as_ref() != doc_bytes))
            }
            Some((v, _)) => Err(Error::type_mismatch("Box<[u8]>", Some(format!("{:?}", v)))),
            None => Err(Error::missing_value()),
        }
    }

    fn update<D: ReadDoc>(
        &mut self,
        doc: &D,
        obj: impl AsRef<ObjId>,
        prop: impl Into<Prop>,
    ) -> Result<Self::Changes> {
        let prop: Prop = prop.into();
        let obj = obj.as_ref();
        match doc.get(obj, prop)? {
            Some((Value::Scalar(x), _)) => {
                let new_bytes = x
                    .to_bytes()
                    .ok_or_else(|| Error::type_mismatch("Box<[u8]>", Some(format!("{:?}", x))))?;
                let changed = self.as_ref() != new_bytes;
                if changed {
                    *self = new_bytes.to_vec().into_boxed_slice();
                }
                Ok(PrimitiveChanged::new(changed))
            }
            Some((v, _)) => Err(Error::type_mismatch("Box<[u8]>", Some(format!("{:?}", v)))),
            None => Err(Error::missing_value()),
        }
    }

    fn update_at<D: ReadDoc>(
        &mut self,
        doc: &D,
        obj: impl AsRef<ObjId>,
        prop: impl Into<Prop>,
        heads: &[ChangeHash],
    ) -> Result<Self::Changes> {
        let prop: Prop = prop.into();
        let obj = obj.as_ref();
        match doc.get_at(obj, prop, heads)? {
            Some((Value::Scalar(x), _)) => {
                let new_bytes = x
                    .to_bytes()
                    .ok_or_else(|| Error::type_mismatch("Box<[u8]>", Some(format!("{:?}", x))))?;
                let changed = self.as_ref() != new_bytes;
                if changed {
                    *self = new_bytes.to_vec().into_boxed_slice();
                }
                Ok(PrimitiveChanged::new(changed))
            }
            Some((v, _)) => Err(Error::type_mismatch("Box<[u8]>", Some(format!("{:?}", v)))),
            None => Err(Error::missing_value()),
        }
    }
}