automerge/rust/automerge-c
Alex Good de16adbcc5 Explicity create empty changes
Transactions with no ops in them are generally undesirable. They take up
space in the change log but do nothing else. They are not useless
though, it may occasionally be necessary to create an empty change in
order to list all the current heads of the document as dependents of the
empty change.

The current API makes no distinction between empty changes and non-empty
changes. If the user calls `Transaction::commit` a change is created
regardless of whether there are ops to commit. To provide a more useful
API modify `commit` so that if there is a no-op transaction then no
changes are created, but provide explicit methods to create an empty
change via `Transaction::empty_change`, `Automerge::empty_change` and
`Autocommit::empty_change`. Also make these APIs available in Javascript
and C.
2022-12-02 12:12:54 +00:00
..
cmake Move rust workspace into ./rust 2022-10-16 19:55:51 +01:00
examples Updated the quickstart example to work with 2022-11-27 23:52:47 -08:00
img Move rust workspace into ./rust 2022-10-16 19:55:51 +01:00
src Explicity create empty changes 2022-12-02 12:12:54 +00:00
test Explicity create empty changes 2022-12-02 12:12:54 +00:00
.gitignore Move rust workspace into ./rust 2022-10-16 19:55:51 +01:00
build.rs Move rust workspace into ./rust 2022-10-16 19:55:51 +01:00
Cargo.toml Move rust workspace into ./rust 2022-10-16 19:55:51 +01:00
cbindgen.toml Move rust workspace into ./rust 2022-10-16 19:55:51 +01:00
CMakeLists.txt Hard-coded automerge-c's initial independent 2022-11-28 00:08:33 -08:00
README.md Move rust workspace into ./rust 2022-10-16 19:55:51 +01:00

Methods we need to support

Basic management

  1. AMcreate()
  2. AMclone(doc)
  3. AMfree(doc)
  4. AMconfig(doc, key, val) // set actor
  5. actor = get_actor(doc)

Transactions

  1. AMpendingOps(doc)
  2. AMcommit(doc, message, time)
  3. AMrollback(doc)

Write

  1. AMset{Map|List}(doc, obj, prop, value)
  2. AMinsert(doc, obj, index, value)
  3. AMpush(doc, obj, value)
  4. AMdel{Map|List}(doc, obj, prop)
  5. AMinc{Map|List}(doc, obj, prop, value)
  6. AMspliceText(doc, obj, start, num_del, text)

Read (the heads argument is optional and can be on an at variant)

  1. AMkeys(doc, obj, heads)
  2. AMlength(doc, obj, heads)
  3. AMlistRange(doc, obj, heads)
  4. AMmapRange(doc, obj, heads)
  5. AMvalues(doc, obj, heads)
  6. AMtext(doc, obj, heads)

Sync

  1. AMgenerateSyncMessage(doc, state)
  2. AMreceiveSyncMessage(doc, state, message)
  3. AMinitSyncState()

Save / Load

  1. AMload(data)
  2. AMloadIncremental(doc, data)
  3. AMsave(doc)
  4. AMsaveIncremental(doc)

Low Level Access

  1. AMapplyChanges(doc, changes)
  2. AMgetChanges(doc, deps)
  3. AMgetChangesAdded(doc1, doc2)
  4. AMgetHeads(doc)
  5. AMgetLastLocalChange(doc)
  6. AMgetMissingDeps(doc, heads)

Encode/Decode

  1. AMencodeChange(change)
  2. AMdecodeChange(change)
  3. AMencodeSyncMessage(change)
  4. AMdecodeSyncMessage(change)
  5. AMencodeSyncState(change)
  6. AMdecodeSyncState(change)

Open Question - Memory management

Most of these calls return one or more items of arbitrary length. Doing memory management in C is tricky. This is my proposed solution...

  // returns 1 or zero opids
  n = automerge_set(doc, "_root", "hello", datatype, value);
  if (n) {
    automerge_pop(doc, &obj, len);
  }

  // returns n values
  n = automerge_values(doc, "_root", "hello");
  for (i = 0; i<n ;i ++) {
    automerge_pop_value(doc, &value, &datatype, len);
  }

There would be one pop method per object type. Users allocs and frees the buffers. Multiple return values would result in multiple pops. Too small buffers would error and allow retry.

Formats

Actors - We could do (bytes,len) or a hex encoded string?.
ObjIds - We could do flat bytes of the ExId struct but lets do human readable strings for now - the struct would be faster but opque Heads - Might as well make it a flat buffer (n, hash, hash, ...) Changes - Put them all in a flat concatenated buffer Encode/Decode - to json strings?