autosurgeon 0.1.0

A library for working with data in automerge documents
Documentation
use automerge::{ScalarValue, Value};
use std::borrow::Cow;

use super::{LoadKey, Reconcile, Reconciler};
use crate::ReadDoc;

impl Reconcile for String {
    type Key<'a> = Cow<'a, str>;
    fn reconcile<R: Reconciler>(&self, mut reconciler: R) -> Result<(), R::Error> {
        reconciler.str(self)
    }
    fn key(&self) -> LoadKey<Self::Key<'_>> {
        LoadKey::Found(Cow::Borrowed(self))
    }
    fn hydrate_key<'a, D: ReadDoc>(
        doc: &D,
        obj: &automerge::ObjId,
        prop: crate::Prop<'_>,
    ) -> Result<LoadKey<Self::Key<'a>>, crate::ReconcileError> {
        Ok(match doc.get(obj, &prop)? {
            Some((Value::Scalar(s), _)) => {
                if let ScalarValue::Str(s) = s.as_ref() {
                    LoadKey::Found(Cow::Owned(s.to_string()))
                } else {
                    LoadKey::KeyNotFound
                }
            }
            _ => LoadKey::KeyNotFound,
        })
    }
}

impl Reconcile for str {
    type Key<'a> = Cow<'a, str>;
    fn reconcile<R: Reconciler>(&self, mut reconciler: R) -> Result<(), R::Error> {
        reconciler.str(self)
    }
    fn key(&self) -> LoadKey<Self::Key<'_>> {
        LoadKey::Found(Cow::Borrowed(self))
    }
    fn hydrate_key<'a, D: ReadDoc>(
        doc: &D,
        obj: &automerge::ObjId,
        prop: crate::Prop<'_>,
    ) -> Result<LoadKey<Self::Key<'a>>, crate::ReconcileError> {
        Ok(match doc.get(obj, &prop)? {
            Some((Value::Scalar(s), _)) => {
                if let ScalarValue::Str(s) = s.as_ref() {
                    LoadKey::Found(Cow::Owned(s.to_string()))
                } else {
                    LoadKey::KeyNotFound
                }
            }
            _ => LoadKey::KeyNotFound,
        })
    }
}

impl<'a, T: Reconcile + ?Sized> Reconcile for &'a T {
    type Key<'b> = T::Key<'b>;
    fn reconcile<R: Reconciler>(&self, reconciler: R) -> Result<(), R::Error> {
        (*self).reconcile(reconciler)
    }

    fn hydrate_key<'b, D: ReadDoc>(
        doc: &D,
        obj: &automerge::ObjId,
        prop: crate::prop::Prop<'_>,
    ) -> Result<LoadKey<Self::Key<'b>>, crate::ReconcileError> {
        T::hydrate_key(doc, obj, prop)
    }

    fn key(&self) -> LoadKey<Self::Key<'_>> {
        T::key(self)
    }
}

impl Reconcile for f64 {
    type Key<'a> = f64;
    fn reconcile<R: Reconciler>(&self, mut reconciler: R) -> Result<(), R::Error> {
        reconciler.f64(*self)
    }
    fn key(&self) -> LoadKey<Self::Key<'_>> {
        LoadKey::Found(*self)
    }
    fn hydrate_key<'a, D: ReadDoc>(
        doc: &D,
        obj: &automerge::ObjId,
        prop: crate::Prop<'_>,
    ) -> Result<LoadKey<Self::Key<'a>>, crate::ReconcileError> {
        Ok(match doc.get(obj, &prop)? {
            Some((Value::Scalar(s), _)) => {
                if let ScalarValue::F64(f) = s.as_ref() {
                    LoadKey::Found(*f)
                } else {
                    LoadKey::KeyNotFound
                }
            }
            _ => LoadKey::KeyNotFound,
        })
    }
}

impl Reconcile for f32 {
    type Key<'a> = f32;
    fn reconcile<R: Reconciler>(&self, mut reconciler: R) -> Result<(), R::Error> {
        reconciler.f64(*self as f64)
    }
    fn key(&self) -> LoadKey<Self::Key<'_>> {
        LoadKey::Found(*self)
    }
    fn hydrate_key<'a, D: ReadDoc>(
        doc: &D,
        obj: &automerge::ObjId,
        prop: crate::Prop<'_>,
    ) -> Result<LoadKey<Self::Key<'a>>, crate::ReconcileError> {
        Ok(match doc.get(obj, &prop)? {
            Some((Value::Scalar(s), _)) => {
                if let ScalarValue::F64(f) = s.as_ref() {
                    LoadKey::Found(*f as f32)
                } else {
                    LoadKey::KeyNotFound
                }
            }
            _ => LoadKey::KeyNotFound,
        })
    }
}

impl Reconcile for bool {
    type Key<'a> = bool;
    fn reconcile<R: Reconciler>(&self, mut reconciler: R) -> Result<(), R::Error> {
        reconciler.boolean(*self)
    }
    fn key(&self) -> LoadKey<Self::Key<'_>> {
        LoadKey::Found(*self)
    }
    fn hydrate_key<'a, D: ReadDoc>(
        doc: &D,
        obj: &automerge::ObjId,
        prop: crate::Prop<'_>,
    ) -> Result<LoadKey<Self::Key<'a>>, crate::ReconcileError> {
        Ok(match doc.get(obj, &prop)? {
            Some((Value::Scalar(s), _)) => {
                if let ScalarValue::Boolean(f) = s.as_ref() {
                    LoadKey::Found(*f)
                } else {
                    LoadKey::KeyNotFound
                }
            }
            _ => LoadKey::KeyNotFound,
        })
    }
}

macro_rules! int_impl {
    ($ty:ident, $from:ident, $to: ident) => {
        impl Reconcile for $ty {
            type Key<'a> = $ty;
            fn reconcile<R: Reconciler>(&self, mut reconciler: R) -> Result<(), R::Error> {
                reconciler.$to(*self as $to)
            }
            fn key(&self) -> LoadKey<Self::Key<'_>> {
                LoadKey::Found(*self)
            }
            fn hydrate_key<'a, D: ReadDoc>(
                doc: &D,
                obj: &automerge::ObjId,
                prop: crate::Prop<'_>,
            ) -> Result<LoadKey<Self::Key<'a>>, crate::ReconcileError> {
                Ok(match doc.get(obj, &prop)? {
                    Some((Value::Scalar(s), _)) => {
                        if let ScalarValue::$from(i) = s.as_ref() {
                            if let Ok(v) = $ty::try_from(*i) {
                                LoadKey::Found(v)
                            } else {
                                LoadKey::KeyNotFound
                            }
                        } else {
                            LoadKey::KeyNotFound
                        }
                    }
                    _ => LoadKey::KeyNotFound,
                })
            }
        }
    };
}

int_impl!(u8, Uint, u64);
int_impl!(u16, Uint, u64);
int_impl!(u32, Uint, u64);
int_impl!(u64, Uint, u64);
int_impl!(i8, Int, i64);
int_impl!(i16, Int, i64);
int_impl!(i32, Int, i64);
int_impl!(i64, Int, i64);

impl<T: Reconcile> Reconcile for Box<T> {
    type Key<'a> = T::Key<'a>;
    fn reconcile<R: Reconciler>(&self, reconciler: R) -> Result<(), R::Error> {
        T::reconcile(self, reconciler)
    }
    fn hydrate_key<'a, D: ReadDoc>(
        doc: &D,
        obj: &automerge::ObjId,
        prop: crate::Prop<'_>,
    ) -> Result<LoadKey<Self::Key<'a>>, crate::ReconcileError> {
        T::hydrate_key(doc, obj, prop)
    }
    fn key(&self) -> LoadKey<Self::Key<'_>> {
        T::key(self)
    }
}

impl<T: Reconcile> Reconcile for Option<T> {
    type Key<'a> = T::Key<'a>;
    fn key(&self) -> LoadKey<Self::Key<'_>> {
        self.as_ref()
            .map(|s| T::key(s))
            .unwrap_or(LoadKey::KeyNotFound)
    }
    fn reconcile<R: Reconciler>(&self, mut reconciler: R) -> Result<(), R::Error> {
        match self {
            Some(s) => s.reconcile(reconciler),
            None => reconciler.none(),
        }
    }
    fn hydrate_key<'a, D: ReadDoc>(
        doc: &D,
        obj: &automerge::ObjId,
        prop: crate::Prop<'_>,
    ) -> Result<LoadKey<Self::Key<'a>>, crate::ReconcileError> {
        match doc.get(obj, &prop)? {
            Some((Value::Scalar(s), _)) => match s.as_ref() {
                ScalarValue::Null => Ok(LoadKey::KeyNotFound),
                _ => T::hydrate_key(doc, obj, prop),
            },
            _ => Ok(LoadKey::KeyNotFound),
        }
    }
}