s_mach.datadiff is an open-source data difference engine for Scala. Implementations of the DataDiff type-class are provided which can compute the difference between all data types, Option, Set, Seq, and Map and all tuple classes. An implementation for any case case class (or other type that can be represented as a struct type) can be generated at compile-time by the mkDataDiff macro method. The difference between any two instances is stored as a patch type, which can be user-defined per type. Patches can be applied to instances of the original type to propagate computed changes. All provided DataDiff implementations are 100% strongly-typed and use absolutely no runtime reflection.

s_mach.datadiff: data difference engine

Build Status Test Coverage Codacy Badge Scaladocs: 2.11 2.12

s_mach.datadiff is an open-source data difference engine for Scala. Implementations of the DataDiff type-class are provided which can compute the difference between all data types, Option, Set, Seq, Map and all tuple types. A DataDiff implementation for any case class (or other type that has matching apply/unapply) can be generated at compile-time by the DataDiff.forProductType macro method. The difference between any two instances is stored as a patch type, which can be user-defined per type. Patches can be applied to instances of the original type to propagate computed changes. All provided DataDiff implementations are 100% strongly-typed and use absolutely no runtime reflection.

Include in SBT

  1. Add to build.sbt

    libraryDependencies += "net.s_mach" %% "datadiff" % "1.1.1"
    Note
    s_mach.datadiff is cross compiled for Scala 2.11/JDK6 and 2.12/JDK8

Versioning

s_mach.datadiff uses semantic versioning (http://semver.org/). s_mach.datadiff does not use the package private modifier. Instead, all code files outside of the s_mach.datadiff.impl package form the public interface and are governed by the rules of semantic versioning. Code files inside the s_mach.datadiff.impl package may be used by downstream applications and libraries. However, no guarantees are made as to the stability or interface of code in the s_mach.datadiff.impl package between versions.

Example

Welcome to Scala version 2.11.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_72).
Type in expressions to have them evaluated.
Type :help for more information.

scala> :paste
// Entering paste mode (ctrl-D to finish)

import s_mach.datadiff._

case class Name(
  first: String,
  middle: Option[String],
  last: String
)

case class NamePatch(
  first: StringPatch,
  middle: OptionPatch[String, StringPatch],
  last: StringPatch
)

object Name {
  implicit val dataDiff_Name = DataDiff.forProductType[Name,NamePatch]
}

case class Person(
  name: Name,
  age: Int
)

case class PersonPatch(
  name: NamePatch,
  age: IntPatch
)

object Person {
  implicit val dataDiff_Person = DataDiff.forProductType[Person,PersonPatch]
}

val p1 = Person(Name("Gary",None,"Oldman"),56)
val p2 = Person(Name("Gary",Some("Freakn'"),"Oldman"),57)

// Exiting paste mode, now interpreting.

import s_mach.datadiff._
defined class Name
defined class NamePatch
defined object Name
defined class Person
defined class PersonPatch
defined object Person
p1: Person = Person(Name(Gary,None,Oldman),56)
p2: Person = Person(Name(Gary,Some(Freakn'),Oldman),57)

scala> val patch1 = p1 calcDiff p2
patch1: PersonPatch = PersonPatch(NamePatch(None,SetValue(Freakn'),None),1)

scala> patch1 == patchFor[Person].noChange
res0: Boolean = false

scala> val patch2 = p1 calcDiff p1
patch2: PersonPatch = PersonPatch(NamePatch(None,NoChange,None),0)

scala> patch2 == patchFor[Person].noChange
res1: Boolean = true

scala> val p3 = p1 applyPatch patch1
p3: Person = Person(Name(Gary,Some(Freakn'),Oldman),57)

scala> p3 == p2
res2: Boolean = true

scala> val p4 = p1 applyPatch patch2
p4: Person = Person(Name(Gary,None,Oldman),56)

scala> p4 == p1
res3: Boolean = true