autosurgeon 0.1.0

A library for working with data in automerge documents
Documentation
use std::borrow::Cow;

use super::{hydrate_prop, Hydrate, HydrateError, Unexpected};
use crate::ReadDoc;

impl Hydrate for String {
    fn hydrate_string(s: &'_ str) -> Result<Self, HydrateError> {
        Ok(s.to_string())
    }
}

impl<T> Hydrate for Vec<T>
where
    T: Hydrate,
{
    fn hydrate_seq<D: ReadDoc>(doc: &D, obj: &automerge::ObjId) -> Result<Self, HydrateError> {
        let mut result = Vec::with_capacity(doc.length(obj));
        for idx in 0..doc.length(obj) {
            let elem = hydrate_prop(doc, obj, idx)?;
            result.push(elem);
        }
        Ok(result)
    }
}

macro_rules! int_impl {
    ($ty:ident, $hydrator: ident, $from_ty:ident) => {
        impl Hydrate for $ty {
            fn $hydrator(u: $from_ty) -> Result<Self, HydrateError> {
                u.try_into().map_err(|_| {
                    HydrateError::Unexpected(Unexpected::Other {
                        expected: stringify!("a ", $ty),
                        found: "an integer which is too large".to_string(),
                    })
                })
            }
        }
    };
}

int_impl!(u16, hydrate_uint, u64);
int_impl!(u32, hydrate_uint, u64);
int_impl!(u64, hydrate_uint, u64);
int_impl!(i8, hydrate_int, i64);
int_impl!(i16, hydrate_int, i64);
int_impl!(i32, hydrate_int, i64);
int_impl!(i64, hydrate_int, i64);

impl Hydrate for Vec<u8> {
    fn hydrate_bytes(bytes: &[u8]) -> Result<Self, HydrateError> {
        Ok(bytes.to_vec())
    }
}

impl Hydrate for bool {
    fn hydrate_bool(b: bool) -> Result<Self, HydrateError> {
        Ok(b)
    }
}

impl Hydrate for f64 {
    fn hydrate_f64(f: f64) -> Result<Self, HydrateError> {
        Ok(f)
    }
}

impl Hydrate for f32 {
    fn hydrate_f64(f: f64) -> Result<Self, HydrateError> {
        Ok(f as f32)
    }
}

impl<T: Hydrate> Hydrate for Option<T> {
    fn hydrate<'a, D: ReadDoc>(
        doc: &D,
        obj: &automerge::ObjId,
        prop: crate::Prop<'a>,
    ) -> Result<Self, HydrateError> {
        use automerge::{ObjType, ScalarValue, Value};
        Ok(match doc.get(obj, &prop)? {
            None => {
                return Err(HydrateError::unexpected(
                    "a ScalarValue::Null",
                    "nothing at all".to_string(),
                ))
            }
            Some((Value::Object(ObjType::Map), id)) => Some(T::hydrate_map(doc, &id)?),
            Some((Value::Object(ObjType::Table), id)) => Some(T::hydrate_map(doc, &id)?),
            Some((Value::Object(ObjType::List), id)) => Some(T::hydrate_seq(doc, &id)?),
            Some((Value::Object(ObjType::Text), id)) => Some(T::hydrate_text(doc, &id)?),
            Some((Value::Scalar(v), _)) => match v.as_ref() {
                ScalarValue::Null => None,
                ScalarValue::Boolean(b) => Some(T::hydrate_bool(*b)?),
                ScalarValue::Bytes(b) => Some(T::hydrate_bytes(b)?),
                ScalarValue::Counter(c) => Some(T::hydrate_counter(c.into())?),
                ScalarValue::F64(f) => Some(T::hydrate_f64(*f)?),
                ScalarValue::Int(i) => Some(T::hydrate_int(*i)?),
                ScalarValue::Uint(u) => Some(T::hydrate_uint(*u)?),
                ScalarValue::Str(s) => Some(T::hydrate_string(s)?),
                ScalarValue::Timestamp(t) => Some(T::hydrate_timestamp(*t)?),
                ScalarValue::Unknown { type_code, bytes } => {
                    Some(T::hydrate_unknown(*type_code, bytes)?)
                }
            },
        })
    }
}

impl<'a, T: Hydrate + Clone> Hydrate for Cow<'a, T> {
    fn hydrate<'b, D: ReadDoc>(
        doc: &D,
        obj: &automerge::ObjId,
        prop: crate::Prop<'b>,
    ) -> Result<Self, HydrateError> {
        Ok(Cow::Owned(T::hydrate(doc, obj, prop)?))
    }
}