lorosurgeon 0.2.0

Derive macros for bidirectional serialization between Rust types and Loro CRDT containers
Documentation

lorosurgeon

Crates.io docs.rs CI License: MIT

Derive macros for bidirectional serialization between Rust types and Loro CRDT containers — the Loro equivalent of autosurgeon for Automerge.

#[derive(Hydrate, Reconcile)] generates field-level mapping between Rust types and Loro containers. Only modified fields produce CRDT operations.

Quick Start

use loro::LoroDoc;
use lorosurgeon::{Hydrate, Reconcile, DocSync};

#[derive(Debug, PartialEq, Hydrate, Reconcile)]
#[loro(root = "config")]
struct Config {
    name: String,
    version: i64,
    position: Position,
}

#[derive(Debug, PartialEq, Hydrate, Reconcile)]
struct Position { x: f64, y: f64 }

let doc = LoroDoc::new();
let config = Config {
    name: "hello".into(),
    version: 1,
    position: Position { x: 10.0, y: 20.0 },
};

config.to_doc(&doc).unwrap();  // Rust → Loro
doc.commit();

let loaded = Config::from_doc(&doc).unwrap();  // Loro → Rust
assert_eq!(loaded, config);

Documentation

Full API documentation on docs.rs →

The crate docs include type mapping tables, attribute reference, examples for concurrent editing, custom serialization, flatten, keyed list diffing, and more.

Features

  • StructsLoroMap (fields become keys)
  • EnumsLoroMap with variant discriminator, unit variants as strings
  • Vec<T>LoroList with Myers LCS diffing
  • #[loro(movable)]LoroMovableList with identity-preserving mov()/set()
  • HashMap<String, V>LoroMap with stale-key cleanup
  • #[loro(text)]LoroText with character-level diffing (on String fields)
  • No-op detection — identical values produce zero CRDT operations
  • Concurrent merge — field-level granularity means independent edits compose

Attributes

// Container-level
#[loro(root = "key")]          // DocSync: to_doc() / from_doc()

// Field-level
#[key]                         // Identity key for movable list diffing
#[loro(rename = "name")]       // Different key in Loro
#[loro(json)]                  // serde_json round-trip
#[loro(text)]                  // LoroText with character-level LCS
#[loro(movable)]               // LoroMovableList instead of LoroList
#[loro(default)]               // Default::default() when absent
#[loro(default = "fn")]        // Custom default function
#[loro(flatten)]               // Inline nested struct fields
#[loro(with = "module")]       // Custom hydrate + reconcile
#[loro(hydrate = "fn")]        // Custom hydrate only
#[loro(reconcile = "fn")]      // Custom reconcile only

License

MIT