automorph 0.1.0

Derive macros for bidirectional Automerge-Rust struct synchronization
Documentation
# Automorph

[![Crates.io](https://img.shields.io/crates/v/automorph.svg)](https://crates.io/crates/automorph)
[![Documentation](https://docs.rs/automorph/badge.svg)](https://docs.rs/automorph)
[![License](https://img.shields.io/crates/l/automorph.svg)](LICENSE-APACHE)

**Bidirectional synchronization between Rust types and [Automerge](https://automerge.org/) documents.**

Automorph works like [Serde](https://serde.rs/) - 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.

> All features documented below are validated by automated tests. Test names are noted in parentheses.

## What is Automerge?

[Automerge](https://automerge.org/) 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

*(validated by: `test_derived_struct`)*

Add to your `Cargo.toml`:

```toml
[dependencies]
automorph = "0.1"
automerge = "0.7"
```

Then derive `Automorph` on your types:

```rust
use automorph::{Automorph, Result};
use automerge::{AutoCommit, ROOT};

#[derive(Automorph, Debug, PartialEq, Default, Clone)]
struct Person {
    name: String,
    age: u64,
}

fn main() -> Result<()> {
    // Create an Automerge document
    let mut doc = AutoCommit::new();

    // Save a struct to the document
    let person = Person {
        name: "Alice".to_string(),
        age: 30,
    };
    person.save(&mut doc, &ROOT, "person")?;

    // Load it back
    let restored = Person::load(&doc, &ROOT, "person")?;
    assert_eq!(person, restored);

    // Efficient updates - only changed fields are written
    let mut updated = person.clone();
    updated.age = 31;
    updated.save(&mut doc, &ROOT, "person")?; // Only writes the age change

    Ok(())
}
```

## Features

### Efficient Diff-Based Updates

*(validated by: `test_diff_detects_changes`, `test_diff_no_changes_when_equal`)*

Automorph only writes values that have changed:

```rust
person.age = 31;
person.save(&mut doc, &ROOT, "person")?;
// Only the 'age' field generates an Automerge operation
```

### Version-Aware Operations

*(validated by: `test_derived_version_aware`, `test_diff_versions`)*

Access historical versions with `*_at` methods:

```rust
// Save state
person.save(&mut doc, &ROOT, "person")?;
let checkpoint = doc.get_heads();

// Make changes
person.age = 32;
person.save(&mut doc, &ROOT, "person")?;

// Load from checkpoint (time travel!)
let old_person = Person::load_at(&doc, &ROOT, "person", &checkpoint)?;
assert_eq!(old_person.age, 31);
```

### Change Detection

*(validated by: `test_update_returns_change_report`, `test_hierarchical_change_tracking`, `test_change_report_paths`)*

Detect and inspect changes with `update` and `diff`:

```rust
// Update returns a ChangeReport showing what changed
let changes = person.update(&doc, &ROOT, "person")?;
if changes.any() {
    println!("Changed fields: {:?}", changes.leaf_paths());
}
```

### Comprehensive Type Support

Automorph supports all types that Serde does:

- **Primitives**: `bool`, `i8`-`i128`, `u8`-`u128`, `f32`, `f64`, `char`
- **Strings**: `String`, `&str`, `Box<str>`, `Cow<str>`
- **Collections**: `Vec`, `HashMap`, `BTreeMap`, `HashSet`, `BTreeSet`
- **Options**: `Option<T>`, `Result<T, E>`
- **Tuples**: up to 16 elements
- **Smart pointers**: `Box`, `Rc`, `Arc`, `Cell`, `RefCell`
- **Time**: `Duration`, `SystemTime`
- **Network**: `IpAddr`, `SocketAddr`
- **And more**: ranges, paths, NonZero types...

## Derive Macro Attributes

### Container Attributes

*(validated by: `test_rename_all`, `test_internally_tagged_enum`)*

```rust
#[derive(Automorph)]
#[automorph(rename_all = "camelCase")]  // Rename all fields
struct Config {
    user_name: String,  // Stored as "userName"
}

#[derive(Automorph)]
#[automorph(tag = "type")]  // Internal tagging for enums
enum Message {
    Text { content: String },
    Image { url: String },
}
// Stored as: {"type": "Text", "content": "Hello"}
```

### Field Attributes

*(validated by: `test_field_rename`, `test_skip_field`, `test_default_field`)*

```rust
#[derive(Automorph)]
struct User {
    #[automorph(rename = "id")]
    user_id: u64,

    #[automorph(skip)]           // Don't sync this field
    cache: Option<Vec<u8>>,

    #[automorph(default)]        // Use Default if missing
    score: u32,
}
```

## Comparison with Serde

| Feature | Serde | Automorph |
|---------|-------|-----------|
| Derive macro | `#[derive(Serialize, Deserialize)]` | `#[derive(Automorph)]` |
| Field rename | `#[serde(rename)]` | `#[automorph(rename)]` |
| Skip field | `#[serde(skip)]` | `#[automorph(skip)]` |
| Default value | `#[serde(default)]` | `#[automorph(default)]` |
| Bidirectional | Needs both traits | Single trait |
| Diff-based | No | Yes - only writes changes |
| Change tracking | No | Yes - `diff()` and `update()` |
| CRDT-aware | No | Yes - preserves Automerge semantics |

## Documentation

- **[Getting Started]docs/getting-started.md** - Quick tutorial to get up and running
- **[Cookbook]docs/cookbook.md** - Common patterns and recipes
- **[Architecture]docs/architecture.md** - How Automorph works internally

## Examples & Demo

Learn Automorph through hands-on examples:

- **[Examples]examples/README.md** - Single-file examples covering individual features:
  - `collaborative.rs` - Tracked<T>, versioning, change detection
  - `crdt_collaboration.rs` - Counter and Text CRDT types
  - `persistence.rs` - File storage patterns
  - `sync_tcp.rs` - TCP sync protocol
  - `notes_tutorial.rs` - Complete CLI notes app

- **[Demo Application]demo/README.md** - Full-featured Yew/WASM web application:
  - Collaborative todo list and chat
  - WebSocket sync between browsers
  - Docker Compose deployment
  - Production-ready patterns

**Start with the examples** to learn individual concepts, then study the **demo** for production patterns.

## License

Automorph is dual-licensed under:

- Apache License 2.0 ([LICENSE-APACHE]LICENSE-APACHE)
- MIT License ([LICENSE-MIT]LICENSE-MIT)

You may use this software under either license at your option.

## Related projects

* [Automerge]https://github.com/automerge
* [Autosurgeon]https://github.com/automerge/autosurgeon -- Automorph provides granular change tracking vs. Autosurgeon's serialization/deserialization functionality.

## Contributing

Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.