use automerge::{
ChangeHash, ObjId, ObjType, Prop, ReadDoc, ScalarValue, Value, transaction::Transactable,
};
use crate::{Automorph, ChangeReport, Error, FieldCursor, OptionChanges, Result, ResultChanges, ScalarCursor};
#[derive(Clone, Debug, Default)]
pub struct OptionCursor<C: FieldCursor> {
pub is_some: bool,
pub obj_id: Option<ObjId>,
pub inner_cursor: C,
}
impl<C: FieldCursor> FieldCursor for OptionCursor<C> {
type Changes = OptionChanges<C::Changes>;
fn diff<D: ReadDoc>(&self, doc: &D, obj: &ObjId) -> crate::Result<Self::Changes> {
let current = doc.get(obj, "")?;
let current_is_some = current.is_some();
match (self.is_some, current_is_some) {
(false, false) => Ok(OptionChanges::Unchanged),
(false, true) => Ok(OptionChanges::BecameSome),
(true, false) => Ok(OptionChanges::BecameNone),
(true, true) => {
if let Some((_, current_id)) = current {
let inner_changes = self.inner_cursor.diff(doc, ¤t_id)?;
if inner_changes.any() {
Ok(OptionChanges::Inner(inner_changes))
} else {
Ok(OptionChanges::Unchanged)
}
} else {
Ok(OptionChanges::BecameNone)
}
}
}
}
fn refresh<D: ReadDoc>(&mut self, doc: &D, obj: &ObjId) -> crate::Result<()> {
match doc.get(obj, "")? {
Some((_, id)) => {
self.is_some = true;
self.obj_id = Some(id.clone());
self.inner_cursor.refresh(doc, &id)?;
}
None => {
self.is_some = false;
self.obj_id = None;
}
}
Ok(())
}
}
impl<C: FieldCursor> OptionCursor<C> {
pub fn diff_at_key<D: ReadDoc>(
&self,
doc: &D,
obj: &ObjId,
key: &str,
) -> crate::Result<OptionChanges<C::Changes>> {
let current = doc.get(obj, key)?;
let current_is_some = current.is_some();
match (self.is_some, current_is_some) {
(false, false) => Ok(OptionChanges::Unchanged),
(false, true) => Ok(OptionChanges::BecameSome),
(true, false) => Ok(OptionChanges::BecameNone),
(true, true) => {
if let Some((_, current_id)) = current {
let inner_changes = self.inner_cursor.diff(doc, ¤t_id)?;
if inner_changes.any() {
Ok(OptionChanges::Inner(inner_changes))
} else {
Ok(OptionChanges::Unchanged)
}
} else {
Ok(OptionChanges::BecameNone)
}
}
}
}
pub fn refresh_at_key<D: ReadDoc>(
&mut self,
doc: &D,
obj: &ObjId,
key: &str,
) -> crate::Result<()> {
match doc.get(obj, key)? {
Some((_, id)) => {
self.is_some = true;
self.obj_id = Some(id.clone());
self.inner_cursor.refresh(doc, &id)?;
}
None => {
self.is_some = false;
self.obj_id = None;
}
}
Ok(())
}
}
#[derive(Clone, Debug, Default)]
pub struct ResultCursor<OkC: FieldCursor, ErrC: FieldCursor> {
pub is_ok: Option<bool>,
pub map_id: Option<ObjId>,
pub ok_cursor: OkC,
pub err_cursor: ErrC,
}
impl<OkC: FieldCursor, ErrC: FieldCursor> FieldCursor for ResultCursor<OkC, ErrC> {
type Changes = ResultChanges<OkC::Changes, ErrC::Changes>;
fn diff<D: ReadDoc>(&self, doc: &D, obj: &ObjId) -> crate::Result<Self::Changes> {
let current = doc.get(obj, "")?;
if let Some((_, map_id)) = current {
let has_ok = doc.get(&map_id, "Ok")?.is_some();
let has_err = doc.get(&map_id, "Err")?.is_some();
match (self.is_ok, has_ok, has_err) {
(None, true, false) => Ok(ResultChanges::BecameOk),
(None, false, true) => Ok(ResultChanges::BecameErr),
(None, _, _) => Ok(ResultChanges::Unchanged),
(Some(true), true, false) => {
if let Some((_, ok_id)) = doc.get(&map_id, "Ok")? {
let inner_changes = self.ok_cursor.diff(doc, &ok_id)?;
if inner_changes.any() {
Ok(ResultChanges::OkInner(inner_changes))
} else {
Ok(ResultChanges::Unchanged)
}
} else {
Ok(ResultChanges::BecameErr)
}
}
(Some(false), false, true) => {
if let Some((_, err_id)) = doc.get(&map_id, "Err")? {
let inner_changes = self.err_cursor.diff(doc, &err_id)?;
if inner_changes.any() {
Ok(ResultChanges::ErrInner(inner_changes))
} else {
Ok(ResultChanges::Unchanged)
}
} else {
Ok(ResultChanges::BecameOk)
}
}
(Some(true), false, true) => Ok(ResultChanges::BecameErr),
(Some(false), true, false) => Ok(ResultChanges::BecameOk),
(_, true, true) => Err(Error::invalid_value(
"Result cannot have both 'Ok' and 'Err' keys",
)),
(_, false, false) => Ok(ResultChanges::Unchanged),
}
} else {
match self.is_ok {
Some(_) => Ok(ResultChanges::Unchanged), None => Ok(ResultChanges::Unchanged),
}
}
}
fn refresh<D: ReadDoc>(&mut self, doc: &D, obj: &ObjId) -> crate::Result<()> {
match doc.get(obj, "")? {
Some((_, map_id)) => {
self.map_id = Some(map_id.clone());
let has_ok = doc.get(&map_id, "Ok")?.is_some();
let has_err = doc.get(&map_id, "Err")?.is_some();
if has_ok {
self.is_ok = Some(true);
if let Some((_, ok_id)) = doc.get(&map_id, "Ok")? {
self.ok_cursor.refresh(doc, &ok_id)?;
}
} else if has_err {
self.is_ok = Some(false);
if let Some((_, err_id)) = doc.get(&map_id, "Err")? {
self.err_cursor.refresh(doc, &err_id)?;
}
}
}
None => {
self.is_ok = None;
self.map_id = None;
}
}
Ok(())
}
}
impl<OkC: FieldCursor, ErrC: FieldCursor> ResultCursor<OkC, ErrC> {
pub fn diff_at_key<D: ReadDoc>(
&self,
doc: &D,
obj: &ObjId,
key: &str,
) -> crate::Result<ResultChanges<OkC::Changes, ErrC::Changes>> {
match doc.get(obj, key)? {
Some((_, map_id)) => {
let has_ok = doc.get(&map_id, "Ok")?.is_some();
let has_err = doc.get(&map_id, "Err")?.is_some();
match (self.is_ok, has_ok, has_err) {
(None, true, false) => Ok(ResultChanges::BecameOk),
(None, false, true) => Ok(ResultChanges::BecameErr),
(None, _, _) => Ok(ResultChanges::Unchanged),
(Some(true), true, false) => {
if let Some((_, ok_id)) = doc.get(&map_id, "Ok")? {
let inner_changes = self.ok_cursor.diff(doc, &ok_id)?;
if inner_changes.any() {
Ok(ResultChanges::OkInner(inner_changes))
} else {
Ok(ResultChanges::Unchanged)
}
} else {
Ok(ResultChanges::BecameErr)
}
}
(Some(false), false, true) => {
if let Some((_, err_id)) = doc.get(&map_id, "Err")? {
let inner_changes = self.err_cursor.diff(doc, &err_id)?;
if inner_changes.any() {
Ok(ResultChanges::ErrInner(inner_changes))
} else {
Ok(ResultChanges::Unchanged)
}
} else {
Ok(ResultChanges::BecameOk)
}
}
(Some(true), false, true) => Ok(ResultChanges::BecameErr),
(Some(false), true, false) => Ok(ResultChanges::BecameOk),
(_, true, true) => Err(Error::invalid_value(
"Result cannot have both 'Ok' and 'Err' keys",
)),
(_, false, false) => Ok(ResultChanges::Unchanged),
}
}
None => {
match self.is_ok {
Some(_) => Ok(ResultChanges::Unchanged),
None => Ok(ResultChanges::Unchanged),
}
}
}
}
pub fn refresh_at_key<D: ReadDoc>(
&mut self,
doc: &D,
obj: &ObjId,
key: &str,
) -> crate::Result<()> {
match doc.get(obj, key)? {
Some((_, map_id)) => {
self.map_id = Some(map_id.clone());
let has_ok = doc.get(&map_id, "Ok")?.is_some();
let has_err = doc.get(&map_id, "Err")?.is_some();
if has_ok {
self.is_ok = Some(true);
if let Some((_, ok_id)) = doc.get(&map_id, "Ok")? {
self.ok_cursor.refresh(doc, &ok_id)?;
}
} else if has_err {
self.is_ok = Some(false);
if let Some((_, err_id)) = doc.get(&map_id, "Err")? {
self.err_cursor.refresh(doc, &err_id)?;
}
}
}
None => {
self.is_ok = None;
self.map_id = None;
}
}
Ok(())
}
}
impl<S: Automorph> Automorph for Option<S> {
type Changes = OptionChanges<S::Changes>;
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 cur = doc.get(obj, prop.clone())?;
match (self, &cur) {
(None, None) => {}
(None, Some((Value::Scalar(v), _))) if v.is_null() => {}
(None, _) => match (&cur, &prop) {
(None, Prop::Seq(s)) => {
doc.insert(obj, *s, ScalarValue::Null)?;
}
_ => {
doc.put(obj, prop, ScalarValue::Null)?;
}
},
(Some(v), _) => {
v.save(doc, obj, prop)?;
}
}
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.clone())? {
None => Ok(None),
Some((Value::Scalar(v), _)) if v.is_null() => Ok(None),
Some(_) => {
let value = S::load(doc, obj, prop)?;
Ok(Some(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.clone(), heads)? {
None => Ok(None),
Some((Value::Scalar(v), _)) if v.is_null() => Ok(None),
Some(_) => {
let value = S::load_at(doc, obj, prop, heads)?;
Ok(Some(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 (self, doc.get(obj, prop.clone())?) {
(None, None) => Ok(OptionChanges::Unchanged),
(None, Some((Value::Scalar(v), _))) if v.is_null() => Ok(OptionChanges::Unchanged),
(None, Some(_)) => Ok(OptionChanges::BecameNone),
(Some(_), None) => Ok(OptionChanges::BecameSome),
(Some(_), Some((Value::Scalar(v), _))) if v.is_null() => Ok(OptionChanges::BecameSome),
(Some(v), Some(_)) => {
let inner_changes = v.diff(doc, obj, prop)?;
Ok(OptionChanges::Inner(inner_changes))
}
}
}
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 (self, doc.get_at(obj, prop.clone(), heads)?) {
(None, None) => Ok(OptionChanges::Unchanged),
(None, Some((Value::Scalar(v), _))) if v.is_null() => Ok(OptionChanges::Unchanged),
(None, Some(_)) => Ok(OptionChanges::BecameNone),
(Some(_), None) => Ok(OptionChanges::BecameSome),
(Some(_), Some((Value::Scalar(v), _))) if v.is_null() => Ok(OptionChanges::BecameSome),
(Some(v), Some(_)) => {
let inner_changes = v.diff_at(doc, obj, prop, heads)?;
Ok(OptionChanges::Inner(inner_changes))
}
}
}
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.clone())? {
None => {
let was_some = self.is_some();
*self = None;
Ok(if was_some {
OptionChanges::BecameNone
} else {
OptionChanges::Unchanged
})
}
Some((Value::Scalar(v), _)) if v.is_null() => {
let was_some = self.is_some();
*self = None;
Ok(if was_some {
OptionChanges::BecameNone
} else {
OptionChanges::Unchanged
})
}
Some(_) => {
match self {
Some(inner) => {
let inner_changes = inner.update(doc, obj, prop)?;
Ok(OptionChanges::Inner(inner_changes))
}
None => {
let value = S::load(doc, obj, prop)?;
*self = Some(value);
Ok(OptionChanges::BecameSome)
}
}
}
}
}
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.clone(), heads)? {
None => {
let was_some = self.is_some();
*self = None;
Ok(if was_some {
OptionChanges::BecameNone
} else {
OptionChanges::Unchanged
})
}
Some((Value::Scalar(v), _)) if v.is_null() => {
let was_some = self.is_some();
*self = None;
Ok(if was_some {
OptionChanges::BecameNone
} else {
OptionChanges::Unchanged
})
}
Some(_) => {
match self {
Some(inner) => {
let inner_changes = inner.update_at(doc, obj, prop, heads)?;
Ok(OptionChanges::Inner(inner_changes))
}
None => {
let value = S::load_at(doc, obj, prop, heads)?;
*self = Some(value);
Ok(OptionChanges::BecameSome)
}
}
}
}
}
}
impl<O: Automorph, E: Automorph> Automorph for std::result::Result<O, E> {
type Changes = ResultChanges<O::Changes, E::Changes>;
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 cur = doc.get(obj, prop.clone())?;
let map_id = match cur {
Some((Value::Object(ObjType::Map), id)) => {
let keys: Vec<String> = doc.keys(&id).collect();
for k in keys {
let should_delete = match self {
Ok(_) => k != "Ok",
Err(_) => k != "Err",
};
if should_delete {
doc.delete(&id, &k)?;
}
}
id
}
_ => doc.put_object(obj, prop, ObjType::Map)?,
};
match self {
Ok(v) => v.save(doc, &map_id, "Ok")?,
Err(e) => e.save(doc, &map_id, "Err")?,
}
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), id)) => {
if doc.get(&id, "Ok")?.is_some() {
let value = O::load(doc, &id, "Ok").map_err(|e| e.with_field("Ok"))?;
Ok(Ok(value))
} else if doc.get(&id, "Err")?.is_some() {
let value = E::load(doc, &id, "Err").map_err(|e| e.with_field("Err"))?;
Ok(Err(value))
} else {
Err(Error::invalid_value("Result must have 'Ok' or 'Err' key"))
}
}
Some((v, _)) => Err(Error::type_mismatch(
"Result (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), id)) => {
if doc.get_at(&id, "Ok", heads)?.is_some() {
let value =
O::load_at(doc, &id, "Ok", heads).map_err(|e| e.with_field("Ok"))?;
Ok(Ok(value))
} else if doc.get_at(&id, "Err", heads)?.is_some() {
let value =
E::load_at(doc, &id, "Err", heads).map_err(|e| e.with_field("Err"))?;
Ok(Err(value))
} else {
Err(Error::invalid_value("Result must have 'Ok' or 'Err' key"))
}
}
Some((v, _)) => Err(Error::type_mismatch(
"Result (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), id)) => {
let has_ok = doc.get(&id, "Ok")?.is_some();
let has_err = doc.get(&id, "Err")?.is_some();
match (self, has_ok, has_err) {
(Ok(v), true, false) => {
let inner_changes = v.diff(doc, &id, "Ok")?;
Ok(ResultChanges::OkInner(inner_changes))
}
(Err(e), false, true) => {
let inner_changes = e.diff(doc, &id, "Err")?;
Ok(ResultChanges::ErrInner(inner_changes))
}
(Ok(_), false, true) => Ok(ResultChanges::BecameOk),
(Err(_), true, false) => Ok(ResultChanges::BecameErr),
(_, true, true) => Err(Error::invalid_value(
"Result cannot have both 'Ok' and 'Err' keys",
)),
(_, false, false) => {
Err(Error::invalid_value("Result must have 'Ok' or 'Err' key"))
}
}
}
Some((v, _)) => Err(Error::type_mismatch(
"Result (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), id)) => {
let has_ok = doc.get_at(&id, "Ok", heads)?.is_some();
let has_err = doc.get_at(&id, "Err", heads)?.is_some();
match (self, has_ok, has_err) {
(Ok(v), true, false) => {
let inner_changes = v.diff_at(doc, &id, "Ok", heads)?;
Ok(ResultChanges::OkInner(inner_changes))
}
(Err(e), false, true) => {
let inner_changes = e.diff_at(doc, &id, "Err", heads)?;
Ok(ResultChanges::ErrInner(inner_changes))
}
(Ok(_), false, true) => Ok(ResultChanges::BecameOk),
(Err(_), true, false) => Ok(ResultChanges::BecameErr),
(_, true, true) => Err(Error::invalid_value(
"Result cannot have both 'Ok' and 'Err' keys",
)),
(_, false, false) => {
Err(Error::invalid_value("Result must have 'Ok' or 'Err' key"))
}
}
}
Some((v, _)) => Err(Error::type_mismatch(
"Result (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), id)) => {
let has_ok = doc.get(&id, "Ok")?.is_some();
let has_err = doc.get(&id, "Err")?.is_some();
match (self, has_ok, has_err) {
(Ok(v), true, false) => {
let inner_changes =
v.update(doc, &id, "Ok").map_err(|e| e.with_field("Ok"))?;
Ok(ResultChanges::OkInner(inner_changes))
}
(Err(e), false, true) => {
let inner_changes = e
.update(doc, &id, "Err")
.map_err(|err| err.with_field("Err"))?;
Ok(ResultChanges::ErrInner(inner_changes))
}
(s @ Err(_), true, false) => {
let value = O::load(doc, &id, "Ok").map_err(|e| e.with_field("Ok"))?;
*s = Ok(value);
Ok(ResultChanges::BecameOk)
}
(s @ Ok(_), false, true) => {
let value = E::load(doc, &id, "Err").map_err(|e| e.with_field("Err"))?;
*s = Err(value);
Ok(ResultChanges::BecameErr)
}
(_, true, true) => Err(Error::invalid_value(
"Result cannot have both 'Ok' and 'Err' keys",
)),
(_, false, false) => {
Err(Error::invalid_value("Result must have 'Ok' or 'Err' key"))
}
}
}
Some((v, _)) => Err(Error::type_mismatch(
"Result (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), id)) => {
let has_ok = doc.get_at(&id, "Ok", heads)?.is_some();
let has_err = doc.get_at(&id, "Err", heads)?.is_some();
match (self, has_ok, has_err) {
(Ok(v), true, false) => {
let inner_changes = v
.update_at(doc, &id, "Ok", heads)
.map_err(|e| e.with_field("Ok"))?;
Ok(ResultChanges::OkInner(inner_changes))
}
(Err(e), false, true) => {
let inner_changes = e
.update_at(doc, &id, "Err", heads)
.map_err(|err| err.with_field("Err"))?;
Ok(ResultChanges::ErrInner(inner_changes))
}
(s @ Err(_), true, false) => {
let value =
O::load_at(doc, &id, "Ok", heads).map_err(|e| e.with_field("Ok"))?;
*s = Ok(value);
Ok(ResultChanges::BecameOk)
}
(s @ Ok(_), false, true) => {
let value =
E::load_at(doc, &id, "Err", heads).map_err(|e| e.with_field("Err"))?;
*s = Err(value);
Ok(ResultChanges::BecameErr)
}
(_, true, true) => Err(Error::invalid_value(
"Result cannot have both 'Ok' and 'Err' keys",
)),
(_, false, false) => {
Err(Error::invalid_value("Result must have 'Ok' or 'Err' key"))
}
}
}
Some((v, _)) => Err(Error::type_mismatch(
"Result (Map)",
Some(format!("{:?}", v)),
)),
None => Err(Error::missing_value()),
}
}
}