pub trait Automorph: Sized {
type Changes: ChangeReport;
type Cursor: FieldCursor;
// Required methods
fn save<D: Transactable + ReadDoc>(
&self,
doc: &mut D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<()>;
fn load_at<D: ReadDoc>(
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self>;
fn diff_at<D: ReadDoc>(
&self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self::Changes>;
fn load<D: ReadDoc>(
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<Self>;
fn update<D: ReadDoc>(
&mut self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<Self::Changes>;
fn update_at<D: ReadDoc>(
&mut self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self::Changes>;
fn diff<D: ReadDoc>(
&self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<Self::Changes>;
// Provided methods
fn diff_versions<D: ReadDoc>(
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
from_heads: &[ChangeHash],
to_heads: &[ChangeHash],
) -> Result<Self::Changes> { ... }
fn tracked<D: ReadDoc>(
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<Tracked<Self>> { ... }
fn tracked_at<D: ReadDoc>(
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Tracked<Self>> { ... }
}Expand description
Primary trait for bidirectional synchronization with Automerge documents.
This trait enables Rust types to synchronize their state with Automerge documents. It provides:
- Core operations: Write to (
save) and read from (load) Automerge documents - In-place updates: Update existing instances via
update - Change tracking: Know which fields changed via
diff - Version-aware operations: Work with historical document states via
*_atmethods - Efficient tracking: O(1) change detection via
Tracked<T>wrapper
§Derive Macro
The easiest way to implement this trait is using the derive macro:
use automorph::Automorph;
#[derive(Automorph, Default, Clone)]
struct Person {
name: String,
age: u64,
}§Associated Types
Changes: Hierarchical change report for deep inspectionCursor: Cached ObjIds for O(1) change detection
§Example
use automorph::{Automorph, Result};
use automerge::{AutoCommit, ROOT};
fn example() -> Result<()> {
let mut doc = AutoCommit::new();
// Write to document
let value = "hello".to_string();
value.save(&mut doc, &ROOT, "greeting")?;
// Read from document
let restored = String::load(&doc, &ROOT, "greeting")?;
assert_eq!(restored, "hello");
// Read from specific version
let heads = doc.get_heads();
let at_version = String::load_at(&doc, &ROOT, "greeting", &heads)?;
Ok(())
}Required Associated Types§
Sourcetype Changes: ChangeReport
type Changes: ChangeReport
Hierarchical change report type for deep change inspection.
For primitive types, this is PrimitiveChanged.
For structs, this is a generated *Changes struct that mirrors the
field hierarchy, enabling inspection like:
changes.name (bool for primitives) or changes.address (Option<AddressChanges>).
Sourcetype Cursor: FieldCursor
type Cursor: FieldCursor
Cursor type for O(1) change detection via cached ObjIds.
Used internally by Tracked<T> to enable efficient change detection
without deserializing the entire document.
Required Methods§
Sourcefn save<D: Transactable + ReadDoc>(
&self,
doc: &mut D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<()>
fn save<D: Transactable + ReadDoc>( &self, doc: &mut D, obj: impl AsRef<ObjId>, prop: impl Into<Prop>, ) -> Result<()>
Synchronizes this value to an Automerge document.
This method writes the current state of self to the specified location
in the Automerge document. It uses efficient diffing to only write values
that have actually changed.
§Arguments
doc- The Automerge document to write toobj- The parent object ID (useROOTfor the document root)prop- The property name or index to write to
§Errors
Returns an error if the Automerge document operation fails.
Sourcefn load_at<D: ReadDoc>(
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self>
fn load_at<D: ReadDoc>( doc: &D, obj: impl AsRef<ObjId>, prop: impl Into<Prop>, heads: &[ChangeHash], ) -> Result<Self>
Creates a new instance from a specific version of an Automerge document.
This is the core loading method. All other loading methods delegate to this.
§Arguments
doc- The Automerge document to read fromobj- The parent object ID (useROOTfor the document root)prop- The property name or index to read fromheads- The change hashes representing the document version
§Errors
Returns an error if the value doesn’t exist or has an incompatible type.
Sourcefn diff_at<D: ReadDoc>(
&self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self::Changes>
fn diff_at<D: ReadDoc>( &self, doc: &D, obj: impl AsRef<ObjId>, prop: impl Into<Prop>, heads: &[ChangeHash], ) -> Result<Self::Changes>
Compares this value against a specific document version.
Returns a ChangeReport describing which fields differ.
§Implementation Notes
For optimal performance, implementations should compare field-by-field
without allocating a new struct. However, for types where this isn’t
practical (like enums where the variant must be determined first),
calling load_at() and comparing is acceptable.
§Arguments
doc- The Automerge document to compare againstobj- The parent object IDprop- The property name or indexheads- The change hashes representing the document version
Sourcefn load<D: ReadDoc>(
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<Self>
fn load<D: ReadDoc>( doc: &D, obj: impl AsRef<ObjId>, prop: impl Into<Prop>, ) -> Result<Self>
Creates a new instance from the current document state.
§Arguments
doc- The Automerge document to read fromobj- The parent object ID (useROOTfor the document root)prop- The property name or index to read from
§Errors
Returns an error if the value doesn’t exist or has an incompatible type.
§Note
This method uses doc.get() to read current state.
For reading historical versions, use load_at() instead.
Sourcefn update<D: ReadDoc>(
&mut self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<Self::Changes>
fn update<D: ReadDoc>( &mut self, doc: &D, obj: impl AsRef<ObjId>, prop: impl Into<Prop>, ) -> Result<Self::Changes>
Updates this value in place from the current document state.
Returns a ChangeReport describing which fields were modified.
§Implementation Notes
For optimal performance, implementations should walk each field individually
and only update fields that changed. However, for types where this isn’t
practical (like enums or primitives), calling load() and replacing
the value is acceptable.
§Arguments
doc- The Automerge document to read fromobj- The parent object IDprop- The property name or index
§Errors
Returns an error if the value doesn’t exist or has an incompatible type.
Sourcefn update_at<D: ReadDoc>(
&mut self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
heads: &[ChangeHash],
) -> Result<Self::Changes>
fn update_at<D: ReadDoc>( &mut self, doc: &D, obj: impl AsRef<ObjId>, prop: impl Into<Prop>, heads: &[ChangeHash], ) -> Result<Self::Changes>
Updates this value in place from a specific document version.
Returns a ChangeReport describing which fields were modified.
§Implementation Notes
For optimal performance, implementations should walk each field individually
and only update fields that changed. However, for types where this isn’t
practical (like enums or primitives), calling load_at() and replacing
the value is acceptable.
§Arguments
doc- The Automerge document to read fromobj- The parent object IDprop- The property name or indexheads- The change hashes representing the document version
§Errors
Returns an error if the value doesn’t exist or has an incompatible type.
Sourcefn diff<D: ReadDoc>(
&self,
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<Self::Changes>
fn diff<D: ReadDoc>( &self, doc: &D, obj: impl AsRef<ObjId>, prop: impl Into<Prop>, ) -> Result<Self::Changes>
Compares this value against the current document state.
Returns a ChangeReport describing which fields differ.
§Implementation Notes
For optimal performance, implementations should compare field-by-field
without allocating a new struct. However, for types where this isn’t
practical (like enums where the variant must be determined first),
calling load() and comparing is acceptable.
Provided Methods§
Sourcefn diff_versions<D: ReadDoc>(
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
from_heads: &[ChangeHash],
to_heads: &[ChangeHash],
) -> Result<Self::Changes>
fn diff_versions<D: ReadDoc>( doc: &D, obj: impl AsRef<ObjId>, prop: impl Into<Prop>, from_heads: &[ChangeHash], to_heads: &[ChangeHash], ) -> Result<Self::Changes>
Compares two document versions to find which fields changed.
This is a static method that doesn’t require an existing Rust value. Useful for understanding what changed between two points in time.
Sourcefn tracked<D: ReadDoc>(
doc: &D,
obj: impl AsRef<ObjId>,
prop: impl Into<Prop>,
) -> Result<Tracked<Self>>
fn tracked<D: ReadDoc>( doc: &D, obj: impl AsRef<ObjId>, prop: impl Into<Prop>, ) -> Result<Tracked<Self>>
Loads a value from the document wrapped in a Tracked<T> for O(1) change detection.
Tracked<T> caches the ObjIds for each field, enabling efficient change detection
without deserializing and comparing values.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.