Major rewrite. Breaking changes from v4.
- Flat operation model:
Patch[T]is now a plain[]Operationrather than a recursive tree. Operations haveKind,Path(JSON Pointer),Old,New,If, andUnlessfields. - Code generation:
cmd/deep-genproduces*_deep.gofiles with reflection-freePatch,Diff,Equal, andClonemethods — typically 10–15x faster than the reflection fallback. - Reflection fallback: Types without generated code fall through to the v4-based internal engine automatically.
| Function | Description |
|---|---|
Diff[T](a, b T) (Patch[T], error) |
Compare two values; returns error for unsupported types |
Apply[T](*T, Patch[T], ...ApplyOption) error |
Apply a patch; returns *ApplyError with Unwrap() []error |
Equal[T](a, b T) bool |
Deep equality |
Clone[T](v T) T |
Deep copy (formerly Copy) |
Set[T,V](Path[T,V], V) Op |
Typed replace operation constructor |
Add[T,V](Path[T,V], V) Op |
Typed add operation constructor |
Remove[T,V](Path[T,V]) Op |
Typed remove operation constructor |
Move[T,V](from, to Path[T,V]) Op |
Typed move operation constructor |
Copy[T,V](from, to Path[T,V]) Op |
Typed copy operation constructor |
Edit[T](*T) *Builder[T] |
Returns a fluent patch builder |
Merge[T](base, other, resolver) |
Deduplicate ops by path; resolver called on conflicts, otherwise other wins |
Field[T,V](selector) |
Type-safe path from a selector function |
At[T,S,E](Path[T,S], int) Path[T,E] |
Extend a slice-field path to an element by index |
MapKey[T,M,K,V](Path[T,M], K) Path[T,V] |
Extend a map-field path to a value by key |
WithLogger(*slog.Logger) ApplyOption |
Pass a logger to a single Apply call |
ParseJSONPatch[T]([]byte) (Patch[T], error) |
Parse RFC 6902 + deep extensions back into a Patch |
ConflictResolver (interface) |
Implement Resolve(path string, local, remote any) any to customize Merge |
Patch[T] methods:
| Method | Description |
|---|---|
Patch.IsEmpty() bool |
Reports whether the patch has no operations |
Patch.AsStrict() Patch[T] |
Returns a copy with strict Old-value verification enabled |
Patch.WithGuard(*Condition) Patch[T] |
Returns a copy with a global guard condition set |
Patch.Reverse() Patch[T] |
Returns the inverse patch (undo) |
Patch.ToJSONPatch() ([]byte, error) |
Serialize to RFC 6902 JSON Patch with deep extensions |
Patch.String() string |
Human-readable summary of operations |
Public package used directly by generated *_deep.go files. Most callers will not need to import it directly.
Condition— Serializable predicate struct (Op,Path,Value,Sub).Evaluate(root reflect.Value, c *Condition) (bool, error)— Evaluate a condition against a value.CheckType(v any, typeName string) bool— Runtime type name check (used in generated code).ToPredicate() / FromPredicate()— ConvertConditionto/from the JSON Patch wire-format map.Eq,Ne,Gt,Ge,Lt,Le,Exists,In,Matches,Type,And,Or,Not— Condition operator constants.
Conditionstruct withOp,Path,Value,Subfields (serializable predicates).- Patch-level guard set via
Patch.Guardfield orpatch.WithGuard(c). - Per-operation conditions via
Operation.If/Operation.Unless. - Builder helpers:
Eq,Ne,Gt,Ge,Lt,Le,Exists,In,Matches,Type,And,Or,Not. - Per-op conditions attached to
Opvalues viaOp.If/Op.Unless; passed to the builder viaBuilder.With.
CRDT[T]— Concurrency-safe CRDT wrapper. Create withNewCRDT(initial, nodeID). Key methods:Edit(fn),ApplyDelta(delta),Merge(other),Reverse(delta),View(). JSON-serializable.Reverseapplies the inverse of a delta and returns a new undo delta with a fresh HLC timestamp; callingReverseon that delta produces a redo.Delta[T]— A timestamped set of changes produced byCRDT.Edit; send to peers and apply withCRDT.ApplyDelta.LWW[T]— Embeddable Last-Write-Wins register. Update withSet(v, ts); accepts write only iftsis strictly newer.Text([]TextRun) — Convergent collaborative text. Merge concurrent edits withMergeTextRuns(a, b).
github.com/brunoga/deep/v5/crdt/hlc
Clock— Per-node HLC state. Create withNewClock(nodeID). Methods:Now(),Update(remote),Reserve(n).HLC— Timestamp struct (wall time + logical counter + node ID). Total ordering viaCompare/After.
- Import path:
github.com/brunoga/deep/v4→github.com/brunoga/deep/v5 Diffnow returns(Patch[T], error)instead ofPatch[T].Patchis now generic (Patch[T]); patches are not cross-type compatible.Patch.Conditionrenamed toPatch.Guard;WithCondition→WithGuard.- Global
Logger/SetLoggerremoved; passWithLogger(l)as anApplyoption for per-call logging. cond/package removed; conditions live ingithub.com/brunoga/deep/v5/condition.deep-gennow writes output to{type}_deep.goby default instead of stdout.OpAddon slices sets by index rather than inserting; true insertion is not supported for unkeyed slices.Copy[T](v T) Trenamed toClone[T](v T) T;Copyis now the patch-op constructorCopy[T,V](from, to Path[T,V]) Op.Builder.Set/Add/Remove/Move/Copymethods removed; useBuilder.With(deep.Set(...), ...)instead.Builder.If/Unlessmethods removed; attach per-op conditions on theOpvalue before passing toWith.