1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
//! # Automorph
//!
//! Bidirectional synchronization between Rust types and
//! [Automerge](https://automerge.org/) documents.
//!
//! Automorph works similarly to [Serde](https://serde.rs/) - you derive a trait on your
//! structs, and the library handles synchronization automatically. Unlike serialization,
//! Automorph performs efficient diff-based updates, only writing changes to the Automerge
//! document.
//!
//! ## What is Automerge?
//!
//! Automerge is a Conflict-free Replicated Data Type (CRDT) library that enables
//! automatic merging of concurrent changes without coordination. It's ideal for:
//!
//! - **Local-first software**: Apps that work offline and sync when connected
//! - **Real-time collaboration**: Multiple users editing the same document
//! - **Version control for data**: Full history with time-travel debugging
//!
//! ## Quick Start
//!
//! ```rust
//! use automorph::{Automorph, Result};
//! use automerge::{AutoCommit, ROOT};
//!
//! fn main() -> Result<()> {
//! // Create an Automerge document
//! let mut doc = AutoCommit::new();
//!
//! // Save primitive types to the document
//! "Alice".to_string().save(&mut doc, &ROOT, "name")?;
//! 30i64.save(&mut doc, &ROOT, "age")?;
//!
//! // Load them back
//! let name = String::load(&doc, &ROOT, "name")?;
//! let age = i64::load(&doc, &ROOT, "age")?;
//! assert_eq!(name, "Alice");
//! assert_eq!(age, 30);
//! Ok(())
//! }
//! ```
//!
//! With custom structs (using derive macro):
//!
//! ```rust
//! use automorph::Automorph;
//! use automerge::{AutoCommit, ROOT};
//!
//! #[derive(Automorph, Debug, PartialEq, Default, Clone)]
//! struct Person {
//! name: String,
//! age: u64,
//! }
//!
//! let mut doc = AutoCommit::new();
//! let person = Person { name: "Alice".to_string(), age: 30 };
//! person.save(&mut doc, &ROOT, "person").unwrap();
//!
//! let restored = Person::load(&doc, &ROOT, "person").unwrap();
//! assert_eq!(person, restored);
//! ```
//!
//! ## Why Sync Instead of Serialize?
//!
//! Traditional serialization writes the entire value every time. Automorph:
//!
//! 1. **Diffs before writing**: Only changed values generate Automerge operations
//! 2. **Preserves history**: Every change is tracked, enabling undo/redo
//! 3. **Enables collaboration**: Changes merge automatically across devices
//! 4. **Supports time-travel**: Read values from any point in history
//!
//! ## Features
//!
//! - **Efficient diff-based sync**: Only changed values generate Automerge operations
//! (validated by: `test_diff_detects_changes`, `test_diff_no_changes_when_equal`)
//! - **Bidirectional**: Sync Rust -> Automerge and Automerge -> Rust
//! (validated by: `test_derived_struct`)
//! - **Hierarchical change tracking**: Know exactly which fields changed during sync
//! (validated by: `test_hierarchical_change_tracking`, `test_change_report_paths`)
//! - **Version-aware**: Read from historical document states with `*_at` methods
//! (validated by: `test_derived_version_aware`)
//! - **Change detection**: Use `Tracked<T>` to detect document modifications
//! (validated by: `test_tracked_load`, `test_tracked_has_structural_changes`, `test_tracked_update`)
//! - **Serde-like attributes**: `#[automorph(rename = "...")]`, `#[automorph(skip)]`, `#[automorph(default)]`
//! (validated by: `test_field_rename`, `test_skip_field`, `test_default_field`)
//! - **Comprehensive type support**: All primitives, collections, enums, and more
//! - **All enum representations**: Externally, internally, adjacently tagged, and untagged
//! (validated by: `test_enum_unit_variant`, `test_internally_tagged_enum`, `test_adjacently_tagged_enum`, `test_untagged_enum`)
//!
//! ## Supported Types
//!
//! | Category | Types |
//! |----------|-------|
//! | Primitives | `bool`, `i8`-`i128`, `u8`-`u128`, `f32`, `f64`, `char`, `()` |
//! | Strings | `String`, `Box<str>`, `Cow<str>`, `Rc<str>`, `Arc<str>` |
//! | Bytes | `Vec<u8>`, `Box<[u8]>`, `[u8; N]` |
//! | Options | `Option<T>`, `Result<T, E>` |
//! | Collections | `Vec<T>`, `VecDeque<T>`, `LinkedList<T>`, `HashMap<K, V>`, `BTreeMap<K, V>`, `HashSet<T>`, `BTreeSet<T>` |
//! | Arrays | `[T; N]` for N=0..32 |
//! | Smart Pointers | `Box<T>`, `Rc<T>`, `Arc<T>`, `Cell<T>`, `RefCell<T>`, `Mutex<T>`, `RwLock<T>` |
//! | Tuples | `(T1,)` through `(T1, ..., T16)` |
//! | Time | `Duration`, `SystemTime` |
//! | Net | `IpAddr`, `Ipv4Addr`, `Ipv6Addr`, `SocketAddr`, `SocketAddrV4`, `SocketAddrV6` |
//! | Paths | `PathBuf`, `OsString` |
//! | Wrappers | `Wrapping<T>`, `Saturating<T>`, `NonZero*` |
//! | Ranges | `Range<T>`, `RangeInclusive<T>`, `RangeTo<T>`, `RangeFrom<T>` |
//! | Marker | `PhantomData<T>` |
//!
//! ## Container Attributes
//!
//! ```rust
//! use automorph::Automorph;
//! use automerge::{AutoCommit, ROOT};
//!
//! #[derive(Automorph, Debug, PartialEq, Default, Clone)]
//! #[automorph(rename_all = "camelCase")]
//! struct MyStruct {
//! user_name: String,
//! email_address: String,
//! }
//!
//! let mut doc = AutoCommit::new();
//! let s = MyStruct {
//! user_name: "Alice".to_string(),
//! email_address: "alice@example.com".to_string(),
//! };
//! s.save(&mut doc, &ROOT, "s").unwrap();
//!
//! let loaded = MyStruct::load(&doc, &ROOT, "s").unwrap();
//! assert_eq!(s, loaded);
//! ```
//!
//! (validated by: `test_rename_all`)
//!
//! ## Field Attributes
//!
//! ```rust
//! use automorph::Automorph;
//! use automerge::{AutoCommit, ROOT};
//!
//! #[derive(Automorph, Debug, PartialEq, Default, Clone)]
//! struct MyStruct {
//! #[automorph(rename = "user_name")]
//! name: String,
//! #[automorph(skip)]
//! cached: u64,
//! #[automorph(default)]
//! optional: String,
//! }
//!
//! let mut doc = AutoCommit::new();
//! let s = MyStruct {
//! name: "Alice".to_string(),
//! cached: 42,
//! optional: "default".to_string(),
//! };
//! s.save(&mut doc, &ROOT, "s").unwrap();
//!
//! let loaded = MyStruct::load(&doc, &ROOT, "s").unwrap();
//! assert_eq!(loaded.name, "Alice");
//! ```
//!
//! (validated by: `test_skip_field`, `test_field_rename`, `test_default_field`)
//!
//! ## Enum Representations
//!
//! Externally tagged (default): `{"Variant": content}`
//! (validated by: `test_enum_unit_variant`, `test_enum_newtype_variant`, `test_enum_struct_variant`)
//!
//! ```rust
//! use automorph::Automorph;
//! use automerge::{AutoCommit, ROOT};
//!
//! #[derive(Automorph, Debug, PartialEq, Clone)]
//! enum MyEnum {
//! A(i32),
//! B { x: i32 },
//! }
//!
//! let mut doc = AutoCommit::new();
//! let val = MyEnum::A(42);
//! val.save(&mut doc, &ROOT, "val").unwrap();
//!
//! let loaded = MyEnum::load(&doc, &ROOT, "val").unwrap();
//! assert_eq!(val, loaded);
//! ```
//!
//! Internally tagged: `{"type": "Variant", ...fields}`
//! (validated by: `test_internally_tagged_enum`, `test_internally_tagged_unit`)
//!
//! ```rust
//! use automorph::Automorph;
//! use automerge::{AutoCommit, ROOT};
//!
//! #[derive(Automorph, Debug, PartialEq, Clone)]
//! #[automorph(tag = "type")]
//! enum TaggedEnum {
//! A { x: i32 },
//! B { y: i32 },
//! }
//!
//! let mut doc = AutoCommit::new();
//! let val = TaggedEnum::A { x: 42 };
//! val.save(&mut doc, &ROOT, "val").unwrap();
//!
//! let loaded = TaggedEnum::load(&doc, &ROOT, "val").unwrap();
//! assert_eq!(val, loaded);
//! ```
//!
//! Adjacently tagged: `{"t": "Variant", "c": content}`
//! (validated by: `test_adjacently_tagged_enum`, `test_adjacently_tagged_int`)
//!
//! ```rust
//! use automorph::Automorph;
//! use automerge::{AutoCommit, ROOT};
//!
//! #[derive(Automorph, Debug, PartialEq, Clone)]
//! #[automorph(tag = "t", content = "c")]
//! enum AdjacentEnum {
//! A(i32),
//! B(String),
//! }
//!
//! let mut doc = AutoCommit::new();
//! let val = AdjacentEnum::A(42);
//! val.save(&mut doc, &ROOT, "val").unwrap();
//!
//! let loaded = AdjacentEnum::load(&doc, &ROOT, "val").unwrap();
//! assert_eq!(val, loaded);
//! ```
//!
//! Untagged: tries each variant in order
//! (validated by: `test_untagged_enum`)
//!
//! ```rust
//! use automorph::Automorph;
//! use automerge::{AutoCommit, ROOT};
//!
//! #[derive(Automorph, Debug, PartialEq, Clone)]
//! #[automorph(untagged)]
//! enum UntaggedEnum {
//! Int(i32),
//! Str(String),
//! }
//!
//! let mut doc = AutoCommit::new();
//! let val = UntaggedEnum::Int(42);
//! val.save(&mut doc, &ROOT, "val").unwrap();
//!
//! let loaded = UntaggedEnum::load(&doc, &ROOT, "val").unwrap();
//! assert_eq!(val, loaded);
//! ```
//!
//! ## Change Tracking with `Tracked<T>`
//!
//! The `Tracked<T>` wrapper caches document state to detect when values have been
//! modified. This is useful for efficiently checking if you need to re-process data.
//!
//! ```rust
//! use automorph::{Tracked, Automorph, ChangeReport};
//! use automerge::{AutoCommit, ROOT};
//!
//! #[derive(Automorph, Default, Clone, Debug)]
//! struct Workspace {
//! name: String,
//! items: Vec<String>,
//! }
//!
//! let mut doc = AutoCommit::new();
//! let workspace = Workspace {
//! name: "test".to_string(),
//! items: vec!["a".to_string()],
//! };
//! workspace.save(&mut doc, &ROOT, "workspace").unwrap();
//!
//! // Load with change tracking (validated by: test_tracked_load)
//! let mut tracked: Tracked<Workspace> = Tracked::load(&doc, &ROOT, "workspace").unwrap();
//!
//! // Modify the document
//! let workspace2 = Workspace {
//! name: "modified".to_string(),
//! items: vec!["a".to_string(), "b".to_string()],
//! };
//! workspace2.save(&mut doc, &ROOT, "workspace").unwrap();
//!
//! // Check if the document has structural changes (validated by: test_tracked_has_structural_changes)
//! if tracked.has_structural_changes(&doc).unwrap() {
//! // Update our local copy and get a change report (validated by: test_tracked_update, test_update_returns_change_report)
//! let changes = tracked.update(&doc).unwrap();
//! if changes.any() {
//! println!("Workspace was modified!");
//! }
//! }
//! ```
//!
//! **Note:** `Tracked<T>` detects changes by comparing Automerge object IDs.
//! It detects when objects are replaced or modified, but for primitive fields
//! within a struct, it tracks changes at the struct level, not individual fields.
//! (validated by: `test_invariant_tracked_has_changes_detects_document_modification`)
//!
//! ## Version-Aware Operations
//!
//! ```rust
//! use automorph::Automorph;
//! use automerge::{AutoCommit, ROOT};
//!
//! #[derive(Automorph, Debug, PartialEq, Default, Clone)]
//! struct Person {
//! name: String,
//! }
//!
//! let mut doc = AutoCommit::new();
//! let mut person = Person { name: "Alice".to_string() };
//! person.save(&mut doc, &ROOT, "person").unwrap();
//!
//! // Save current version
//! let old_heads = doc.get_heads();
//!
//! // Make changes
//! person.name = "Bob".to_string();
//! person.save(&mut doc, &ROOT, "person").unwrap();
//!
//! // Read old value (validated by: test_derived_version_aware)
//! let old_person = Person::load_at(&doc, &ROOT, "person", &old_heads).unwrap();
//! assert_eq!(old_person.name, "Alice");
//!
//! // Diff between versions (validated by: test_diff_versions)
//! let changes = person.diff_at(&doc, &ROOT, "person", &old_heads).unwrap();
//! assert!(changes.name.map_or(false, |n| n.changed));
//! ```
//!
//! ## Learn More
//!
//! - [Automerge Documentation](https://docs.rs/automerge)
//! - [What is Automerge?](https://automerge.org/)
//! - [CRDTs Explained](https://crdt.tech/)
/// Configuration for Automorph behavior.
/// CRDT primitive types that expose Automerge's collaborative semantics.
///
/// This module provides types that leverage Automerge's CRDT capabilities directly:
/// - `Counter` - For concurrent increment/decrement operations
/// - `Text` - For collaborative text editing with character-level merging
///
/// Unlike regular types which use last-writer-wins semantics, these types
/// merge concurrent operations correctly.
///
/// See the module documentation for usage examples.
/// Debug utilities for inspecting Automerge documents.
///
/// This module provides tools for debugging and inspecting document contents.
/// See `dump_json` for quick document inspection.
// Re-export core types from automerge for convenience
pub use automerge;
// Export our primary trait
pub use Automorph;
// Export supporting traits and types for change tracking
pub use Tracked;
pub use ;
pub use ;
pub use ;
// Export OptionCursor and ResultCursor for cursor-based change detection
pub use OptionCursor;
pub use ResultCursor;
// Re-export with modules for custom save/load
pub use chrono_iso8601;
pub use uuid_string;
// Re-export the derive macro with the same name as the trait
// This allows `#[derive(Automorph)]` to work just like serde's `#[derive(Serialize)]`
pub use Automorph;
// Export error types
pub use ;