use automerge::{ChangeHash, ObjId, ObjType, Prop, ReadDoc, Value, transaction::Transactable};
use crate::{Automorph, ChangeReport, Error, PrimitiveChanged, Result, ScalarCursor};
macro_rules! impl_tuple {
($($idx:tt: $T:ident),+) => {
impl<$($T: Automorph),+> Automorph for ($($T,)+) {
type Changes = PrimitiveChanged;
type Cursor = ScalarCursor;
fn save<Doc: Transactable + ReadDoc>(
&self,
doc: &mut Doc,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<()> {
let prop: Prop = prop.into();
let obj = obj.as_ref();
let cur = doc.get(obj, prop.clone())?;
let list_id = match cur {
Some((Value::Object(ObjType::List), id)) => {
let expected_len = impl_tuple!(@count $($T)+);
let list_len = doc.length(&id);
for idx in (expected_len..list_len).rev() {
doc.delete(&id, idx)?;
}
id
}
_ => doc.put_object(obj, prop, ObjType::List)?,
};
$(
self.$idx.save(doc, &list_id, $idx)?;
)+
Ok(())
}
fn load<Doc: ReadDoc>(
doc: &Doc,
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::List), id)) => {
Ok(($(
$T::load(doc, &id, $idx)
.map_err(|e| e.with_index($idx))?,
)+))
}
Some((v, _)) => Err(Error::type_mismatch("tuple (List)", Some(format!("{:?}", v)))),
None => Err(Error::missing_value()),
}
}
fn load_at<Doc: ReadDoc>(
doc: &Doc,
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::List), id)) => {
Ok(($(
$T::load_at(doc, &id, $idx, heads)
.map_err(|e| e.with_index($idx))?,
)+))
}
Some((v, _)) => Err(Error::type_mismatch("tuple (List)", Some(format!("{:?}", v)))),
None => Err(Error::missing_value()),
}
}
fn diff<Doc: ReadDoc>(
&self,
doc: &Doc,
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::List), id)) => {
$(
let changes = self.$idx.diff(doc, &id, $idx)
.map_err(|e| e.with_index($idx))?;
if changes.any() {
return Ok(PrimitiveChanged::new(true));
}
)+
Ok(PrimitiveChanged::new(false))
}
Some((v, _)) => Err(Error::type_mismatch("tuple (List)", Some(format!("{:?}", v)))),
None => Err(Error::missing_value()),
}
}
fn diff_at<Doc: ReadDoc>(
&self,
doc: &Doc,
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::List), id)) => {
$(
let changes = self.$idx.diff_at(doc, &id, $idx, heads)
.map_err(|e| e.with_index($idx))?;
if changes.any() {
return Ok(PrimitiveChanged::new(true));
}
)+
Ok(PrimitiveChanged::new(false))
}
Some((v, _)) => Err(Error::type_mismatch("tuple (List)", Some(format!("{:?}", v)))),
None => Err(Error::missing_value()),
}
}
fn update<Doc: ReadDoc>(
&mut self,
doc: &Doc,
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::List), id)) => {
let mut any_changed = false;
$(
let changes = self.$idx.update(doc, &id, $idx)
.map_err(|e| e.with_index($idx))?;
if changes.any() {
any_changed = true;
}
)+
Ok(PrimitiveChanged::new(any_changed))
}
Some((v, _)) => Err(Error::type_mismatch("tuple (List)", Some(format!("{:?}", v)))),
None => Err(Error::missing_value()),
}
}
fn update_at<Doc: ReadDoc>(
&mut self,
doc: &Doc,
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::List), id)) => {
let mut any_changed = false;
$(
let changes = self.$idx.update_at(doc, &id, $idx, heads)
.map_err(|e| e.with_index($idx))?;
if changes.any() {
any_changed = true;
}
)+
Ok(PrimitiveChanged::new(any_changed))
}
Some((v, _)) => Err(Error::type_mismatch("tuple (List)", Some(format!("{:?}", v)))),
None => Err(Error::missing_value()),
}
}
}
};
(@count $T:ident) => { 1usize };
(@count $T:ident $($rest:ident)+) => { 1usize + impl_tuple!(@count $($rest)+) };
}
impl_tuple!(0: T0);
impl_tuple!(0: T0, 1: T1);
impl_tuple!(0: T0, 1: T1, 2: T2);
impl_tuple!(0: T0, 1: T1, 2: T2, 3: T3);
impl_tuple!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4);
impl_tuple!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5);
impl_tuple!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6);
impl_tuple!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6, 7: T7);
impl_tuple!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6, 7: T7, 8: T8);
impl_tuple!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6, 7: T7, 8: T8, 9: T9);
impl_tuple!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6, 7: T7, 8: T8, 9: T9, 10: T10);
impl_tuple!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6, 7: T7, 8: T8, 9: T9, 10: T10, 11: T11);
impl_tuple!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6, 7: T7, 8: T8, 9: T9, 10: T10, 11: T11, 12: T12);
impl_tuple!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6, 7: T7, 8: T8, 9: T9, 10: T10, 11: T11, 12: T12, 13: T13);
impl_tuple!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6, 7: T7, 8: T8, 9: T9, 10: T10, 11: T11, 12: T12, 13: T13, 14: T14);
impl_tuple!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6, 7: T7, 8: T8, 9: T9, 10: T10, 11: T11, 12: T12, 13: T13, 14: T14, 15: T15);