Added
PatchResult.prefixedWith(property),PatchResult.prefixedWith(segment)and
PatchFailure.prefixedWith(segment), which lift failures onto a prefix the way
Change.prefixedWithlifts changes. A hand-writtenPatchercalls the first on each helper's result
to report full paths, as the library's own patchers now do. It uses the second for an element of a
collection it rebuilds itself. Both return the same instance when nothing failed, so a clean
application allocates nothing for them.
Changed
- The tutorial derives its comparison, and applies a diff. It used to describe its model with seven
differ { }values, which is the route the rest of the documentation tells you not to start from.
It now derives one withautoDiffer { }and three exceptions.Money, which deriving refuses, is
where the refusal and its two exits are taught. A new section sends the handler's diff to a read
model, including one that missed an update. No published API changes. patching.mdsays what applying does to a source that has moved on. A change'sbeforeis
never read, so values are overwritten and a removal of something already gone is a no-op. Only a
change reaching beneath something absent is reported. This was always the behaviour, and now it is
documented, with a runtime test per case.
Fixed
-
A patch failure names the full path of the change it declines, however deep. Applying
addresses[id=A3].cityto an instance holding no addressA3used to report
city: no element with this key to patch. That names neither the list nor the element, and it is
ambiguous in any model wherecityoccurs twice. It now reports
addresses[id=A3].city: no element with this key to patch, on the generated, derived andtoPatcher
routes alike.PatchFailure.changecarries that full path too.Recompile to get it everywhere. The runtime restores element keys and indexes at once, but the
property segment is restored by generated code, so a differ generated by an earlier processor reports
[id=A3].cityuntil it is regenerated. Nothing stops compiling either way.A hand-written
Patcherchanges in one of two ways. If it composespatch*helpers, call
prefixedWith("<property>")on each helper's result, or its property segments stay missing. If it
worked around the old paths by prefixing the failures ofpatchKeyedList,patchPositionalListor
patchMapwith the element's key or index itself, remove that prefixing, or the segment appears
twice ([id=A3][id=A3].city). The helpers now return those failures already lifted onto the element.Restoring the property segment costs a clean
applyone check per property. Nothing is allocated,
but applying one change to an 18-property type measured 4–7% slower inkdiff-benchmarks.
Coordinates
implementation("io.github.rcapraro:kdiff-annotations:2.1.0")
implementation("io.github.rcapraro:kdiff-runtime:2.1.0")
ksp("io.github.rcapraro:kdiff-processor:2.1.0")Deriving a differ from a type's shape is a separate artefact, declared only by a consumer
who uses it — it is the one part of kdiff that brings kotlin-reflect:
implementation("io.github.rcapraro:kdiff-reflect:2.1.0")