use std::num::{
NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize, NonZeroU8,
NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize, Saturating, Wrapping,
};
use automerge::{ChangeHash, ObjId, Prop, ReadDoc, transaction::Transactable};
use crate::{Automorph, ChangeReport, Error, PrimitiveChanged, Result, ScalarCursor};
impl<T: Automorph> Automorph for Wrapping<T> {
type Changes = PrimitiveChanged;
type Cursor = ScalarCursor;
fn save<D: Transactable + ReadDoc>(
&self,
doc: &mut D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<()> {
self.0.save(doc, obj, prop)
}
fn load<D: ReadDoc>(doc: &D, obj: impl AsRef<ObjId>, prop: impl Into<Prop>) -> Result<Self> {
Ok(Wrapping(T::load(doc, obj, prop)?))
}
fn load_at<D: ReadDoc>(
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self> {
Ok(Wrapping(T::load_at(doc, obj, prop, heads)?))
}
fn diff<D: ReadDoc>(
&self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<Self::Changes> {
let inner_changes = self.0.diff(doc, obj, prop)?;
Ok(PrimitiveChanged::new(inner_changes.any()))
}
fn diff_at<D: ReadDoc>(
&self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self::Changes> {
let inner_changes = self.0.diff_at(doc, obj, prop, heads)?;
Ok(PrimitiveChanged::new(inner_changes.any()))
}
fn update<D: ReadDoc>(
&mut self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<Self::Changes> {
let inner_changes = self.0.update(doc, obj, prop)?;
Ok(PrimitiveChanged::new(inner_changes.any()))
}
fn update_at<D: ReadDoc>(
&mut self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self::Changes> {
let inner_changes = self.0.update_at(doc, obj, prop, heads)?;
Ok(PrimitiveChanged::new(inner_changes.any()))
}
}
impl<T: Automorph> Automorph for Saturating<T> {
type Changes = PrimitiveChanged;
type Cursor = ScalarCursor;
fn save<D: Transactable + ReadDoc>(
&self,
doc: &mut D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<()> {
self.0.save(doc, obj, prop)
}
fn load<D: ReadDoc>(doc: &D, obj: impl AsRef<ObjId>, prop: impl Into<Prop>) -> Result<Self> {
Ok(Saturating(T::load(doc, obj, prop)?))
}
fn load_at<D: ReadDoc>(
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self> {
Ok(Saturating(T::load_at(doc, obj, prop, heads)?))
}
fn diff<D: ReadDoc>(
&self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<Self::Changes> {
let inner_changes = self.0.diff(doc, obj, prop)?;
Ok(PrimitiveChanged::new(inner_changes.any()))
}
fn diff_at<D: ReadDoc>(
&self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self::Changes> {
let inner_changes = self.0.diff_at(doc, obj, prop, heads)?;
Ok(PrimitiveChanged::new(inner_changes.any()))
}
fn update<D: ReadDoc>(
&mut self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<Self::Changes> {
let inner_changes = self.0.update(doc, obj, prop)?;
Ok(PrimitiveChanged::new(inner_changes.any()))
}
fn update_at<D: ReadDoc>(
&mut self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self::Changes> {
let inner_changes = self.0.update_at(doc, obj, prop, heads)?;
Ok(PrimitiveChanged::new(inner_changes.any()))
}
}
macro_rules! impl_nonzero {
($nonzero:ty, $inner:ty, $name:expr) => {
impl Automorph for $nonzero {
type Changes = PrimitiveChanged;
type Cursor = ScalarCursor;
fn save<D: Transactable + ReadDoc>(
&self,
doc: &mut D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<()> {
self.get().save(doc, obj, prop)
}
fn load<D: ReadDoc>(
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<Self> {
let val = <$inner>::load(doc, obj, prop)?;
<$nonzero>::new(val)
.ok_or_else(|| Error::invalid_value(concat!($name, " cannot be zero")))
}
fn load_at<D: ReadDoc>(
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self> {
let val = <$inner>::load_at(doc, obj, prop, heads)?;
<$nonzero>::new(val)
.ok_or_else(|| Error::invalid_value(concat!($name, " cannot be zero")))
}
fn diff<D: ReadDoc>(
&self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<Self::Changes> {
let inner_changes = self.get().diff(doc, obj, prop)?;
Ok(PrimitiveChanged::new(inner_changes.any()))
}
fn diff_at<D: ReadDoc>(
&self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self::Changes> {
let inner_changes = self.get().diff_at(doc, obj, prop, heads)?;
Ok(PrimitiveChanged::new(inner_changes.any()))
}
fn update<D: ReadDoc>(
&mut self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<Self::Changes> {
let val = <$inner>::load(doc, obj, prop)?;
let new_val = <$nonzero>::new(val)
.ok_or_else(|| Error::invalid_value(concat!($name, " cannot be zero")))?;
let changed = *self != new_val;
if changed {
*self = new_val;
}
Ok(PrimitiveChanged::new(changed))
}
fn update_at<D: ReadDoc>(
&mut self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self::Changes> {
let val = <$inner>::load_at(doc, obj, prop, heads)?;
let new_val = <$nonzero>::new(val)
.ok_or_else(|| Error::invalid_value(concat!($name, " cannot be zero")))?;
let changed = *self != new_val;
if changed {
*self = new_val;
}
Ok(PrimitiveChanged::new(changed))
}
}
};
}
impl_nonzero!(NonZeroI8, i8, "NonZeroI8");
impl_nonzero!(NonZeroI16, i16, "NonZeroI16");
impl_nonzero!(NonZeroI32, i32, "NonZeroI32");
impl_nonzero!(NonZeroI64, i64, "NonZeroI64");
impl_nonzero!(NonZeroI128, i128, "NonZeroI128");
impl_nonzero!(NonZeroIsize, isize, "NonZeroIsize");
impl_nonzero!(NonZeroU8, u8, "NonZeroU8");
impl_nonzero!(NonZeroU16, u16, "NonZeroU16");
impl_nonzero!(NonZeroU32, u32, "NonZeroU32");
impl_nonzero!(NonZeroU64, u64, "NonZeroU64");
impl_nonzero!(NonZeroU128, u128, "NonZeroU128");
impl_nonzero!(NonZeroUsize, usize, "NonZeroUsize");
#[cfg(test)]
mod tests {
use super::*;
use automerge::{AutoCommit, ROOT};
#[test]
fn test_wrapping() {
let mut doc = AutoCommit::new();
let val = Wrapping(42i64);
val.save(&mut doc, &ROOT, "val").unwrap();
let restored = Wrapping::<i64>::load(&doc, &ROOT, "val").unwrap();
assert_eq!(restored, Wrapping(42));
}
#[test]
fn test_saturating() {
let mut doc = AutoCommit::new();
let val = Saturating(42i64);
val.save(&mut doc, &ROOT, "val").unwrap();
let restored = Saturating::<i64>::load(&doc, &ROOT, "val").unwrap();
assert_eq!(restored, Saturating(42));
}
#[test]
fn test_nonzero() {
let mut doc = AutoCommit::new();
let val = NonZeroU64::new(42).unwrap();
val.save(&mut doc, &ROOT, "val").unwrap();
let restored = NonZeroU64::load(&doc, &ROOT, "val").unwrap();
assert_eq!(restored.get(), 42);
}
#[test]
fn test_nonzero_zero_error() {
let mut doc = AutoCommit::new();
0i64.save(&mut doc, &ROOT, "val").unwrap();
let result = NonZeroI64::load(&doc, &ROOT, "val");
assert!(result.is_err());
}
}