use std::ops::{Range, RangeFrom, RangeInclusive, RangeTo};
use automerge::{ChangeHash, ObjId, ObjType, Prop, ReadDoc, Value, transaction::Transactable};
use crate::{Automorph, ChangeReport, Error, PrimitiveChanged, Result, ScalarCursor};
impl<T: Automorph> Automorph for Range<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<()> {
let prop: Prop = prop.into();
let obj = obj.as_ref();
let map_id = match doc.get(obj, prop.clone())? {
Some((Value::Object(ObjType::Map), id)) => id,
_ => doc.put_object(obj, prop, ObjType::Map)?,
};
self.start.save(doc, &map_id, "start")?;
self.end.save(doc, &map_id, "end")?;
Ok(())
}
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::Object(ObjType::Map), map_id)) => {
let start = T::load(doc, &map_id, "start")?;
let end = T::load(doc, &map_id, "end")?;
Ok(start..end)
}
Some((v, _)) => Err(Error::type_mismatch(
"Range (Map)",
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::Object(ObjType::Map), map_id)) => {
let start = T::load_at(doc, &map_id, "start", heads)?;
let end = T::load_at(doc, &map_id, "end", heads)?;
Ok(start..end)
}
Some((v, _)) => Err(Error::type_mismatch(
"Range (Map)",
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::Object(ObjType::Map), map_id)) => {
let start_changes = self.start.diff(doc, &map_id, "start")?;
if start_changes.any() {
return Ok(PrimitiveChanged::new(true));
}
let end_changes = self.end.diff(doc, &map_id, "end")?;
Ok(PrimitiveChanged::new(end_changes.any()))
}
Some((v, _)) => Err(Error::type_mismatch(
"Range (Map)",
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::Object(ObjType::Map), map_id)) => {
let start_changes = self.start.diff_at(doc, &map_id, "start", heads)?;
if start_changes.any() {
return Ok(PrimitiveChanged::new(true));
}
let end_changes = self.end.diff_at(doc, &map_id, "end", heads)?;
Ok(PrimitiveChanged::new(end_changes.any()))
}
Some((v, _)) => Err(Error::type_mismatch(
"Range (Map)",
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::Object(ObjType::Map), map_id)) => {
let mut any_changed = false;
let start_changes = self.start.update(doc, &map_id, "start")?;
if start_changes.any() {
any_changed = true;
}
let end_changes = self.end.update(doc, &map_id, "end")?;
if end_changes.any() {
any_changed = true;
}
Ok(PrimitiveChanged::new(any_changed))
}
Some((v, _)) => Err(Error::type_mismatch(
"Range (Map)",
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::Object(ObjType::Map), map_id)) => {
let mut any_changed = false;
let start_changes = self.start.update_at(doc, &map_id, "start", heads)?;
if start_changes.any() {
any_changed = true;
}
let end_changes = self.end.update_at(doc, &map_id, "end", heads)?;
if end_changes.any() {
any_changed = true;
}
Ok(PrimitiveChanged::new(any_changed))
}
Some((v, _)) => Err(Error::type_mismatch(
"Range (Map)",
Some(format!("{:?}", v)),
)),
None => Err(Error::missing_value()),
}
}
}
impl<T: Automorph + Clone + PartialEq> Automorph for RangeInclusive<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<()> {
let prop: Prop = prop.into();
let obj = obj.as_ref();
let map_id = match doc.get(obj, prop.clone())? {
Some((Value::Object(ObjType::Map), id)) => id,
_ => doc.put_object(obj, prop, ObjType::Map)?,
};
self.start().save(doc, &map_id, "start")?;
self.end().save(doc, &map_id, "end")?;
Ok(())
}
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::Object(ObjType::Map), map_id)) => {
let start = T::load(doc, &map_id, "start")?;
let end = T::load(doc, &map_id, "end")?;
Ok(start..=end)
}
Some((v, _)) => Err(Error::type_mismatch(
"RangeInclusive (Map)",
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::Object(ObjType::Map), map_id)) => {
let start = T::load_at(doc, &map_id, "start", heads)?;
let end = T::load_at(doc, &map_id, "end", heads)?;
Ok(start..=end)
}
Some((v, _)) => Err(Error::type_mismatch(
"RangeInclusive (Map)",
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::Object(ObjType::Map), map_id)) => {
let start_changes = self.start().diff(doc, &map_id, "start")?;
if start_changes.any() {
return Ok(PrimitiveChanged::new(true));
}
let end_changes = self.end().diff(doc, &map_id, "end")?;
Ok(PrimitiveChanged::new(end_changes.any()))
}
Some((v, _)) => Err(Error::type_mismatch(
"RangeInclusive (Map)",
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::Object(ObjType::Map), map_id)) => {
let start_changes = self.start().diff_at(doc, &map_id, "start", heads)?;
if start_changes.any() {
return Ok(PrimitiveChanged::new(true));
}
let end_changes = self.end().diff_at(doc, &map_id, "end", heads)?;
Ok(PrimitiveChanged::new(end_changes.any()))
}
Some((v, _)) => Err(Error::type_mismatch(
"RangeInclusive (Map)",
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::Object(ObjType::Map), map_id)) => {
let start = T::load(doc, &map_id, "start")?;
let end = T::load(doc, &map_id, "end")?;
let new_value = start..=end;
let changed = self.start() != new_value.start() || self.end() != new_value.end();
if changed {
*self = new_value;
}
Ok(PrimitiveChanged::new(changed))
}
Some((v, _)) => Err(Error::type_mismatch(
"RangeInclusive (Map)",
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::Object(ObjType::Map), map_id)) => {
let start = T::load_at(doc, &map_id, "start", heads)?;
let end = T::load_at(doc, &map_id, "end", heads)?;
let new_value = start..=end;
let changed = self.start() != new_value.start() || self.end() != new_value.end();
if changed {
*self = new_value;
}
Ok(PrimitiveChanged::new(changed))
}
Some((v, _)) => Err(Error::type_mismatch(
"RangeInclusive (Map)",
Some(format!("{:?}", v)),
)),
None => Err(Error::missing_value()),
}
}
}
impl<T: Automorph> Automorph for RangeTo<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.end.save(doc, obj, prop)
}
fn load<D: ReadDoc>(doc: &D, obj: impl AsRef<ObjId>, prop: impl Into<Prop>) -> Result<Self> {
Ok(..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(..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.end.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.end.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.end.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.end.update_at(doc, obj, prop, heads)?;
Ok(PrimitiveChanged::new(inner_changes.any()))
}
}
impl<T: Automorph> Automorph for RangeFrom<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.start.save(doc, obj, prop)
}
fn load<D: ReadDoc>(doc: &D, obj: impl AsRef<ObjId>, prop: impl Into<Prop>) -> Result<Self> {
Ok(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(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.start.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.start.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.start.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.start.update_at(doc, obj, prop, heads)?;
Ok(PrimitiveChanged::new(inner_changes.any()))
}
}
#[cfg(test)]
mod tests {
use super::*;
use automerge::{AutoCommit, ROOT};
#[test]
fn test_range() {
let mut doc = AutoCommit::new();
let range = 5i64..10;
range.save(&mut doc, &ROOT, "range").unwrap();
let restored = Range::<i64>::load(&doc, &ROOT, "range").unwrap();
assert_eq!(restored, 5..10);
}
#[test]
fn test_range_inclusive() {
let mut doc = AutoCommit::new();
let range = 5i64..=10;
range.save(&mut doc, &ROOT, "range").unwrap();
let restored = RangeInclusive::<i64>::load(&doc, &ROOT, "range").unwrap();
assert_eq!(restored, 5..=10);
}
#[test]
fn test_range_to() {
let mut doc = AutoCommit::new();
let range = ..10i64;
range.save(&mut doc, &ROOT, "range").unwrap();
let restored = RangeTo::<i64>::load(&doc, &ROOT, "range").unwrap();
assert_eq!(restored, ..10);
}
#[test]
fn test_range_from() {
let mut doc = AutoCommit::new();
let range = 5i64..;
range.save(&mut doc, &ROOT, "range").unwrap();
let restored = RangeFrom::<i64>::load(&doc, &ROOT, "range").unwrap();
assert_eq!(restored, 5..);
}
}