mirror of
https://github.com/phfroidmont/scalive.git
synced 2025-12-25 13:36:59 +01:00
Intermediate diff representation
This commit is contained in:
parent
13c15cc289
commit
844a1f7953
5 changed files with 147 additions and 74 deletions
91
core/src/scalive/DiffEngine.scala
Normal file
91
core/src/scalive/DiffEngine.scala
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
package scalive
|
||||
|
||||
import zio.json.ast.*
|
||||
|
||||
enum Diff:
|
||||
case Mod(
|
||||
static: Seq[String] = Seq.empty,
|
||||
dynamic: Seq[Diff.Dynamic] = Seq.empty
|
||||
)
|
||||
case Split(
|
||||
static: Seq[String] = Seq.empty,
|
||||
dynamic: Seq[Diff.Dynamic] = Seq.empty
|
||||
)
|
||||
case Static(value: String)
|
||||
case Dynamic(index: Int, diff: Diff)
|
||||
case Deleted
|
||||
|
||||
object DiffEngine:
|
||||
|
||||
def buildInitJson(lv: RenderedLiveView[?]): Json =
|
||||
JsonAstBuilder
|
||||
.diffToJson(
|
||||
buildDiffValue(lv.static, lv.dynamic, includeUnchanged = true)
|
||||
)
|
||||
|
||||
def buildDiffJson(lv: RenderedLiveView[?]): Json =
|
||||
Json
|
||||
.Obj(
|
||||
"diff" -> JsonAstBuilder.diffToJson(
|
||||
buildDiffValue(static = Seq.empty, lv.dynamic)
|
||||
)
|
||||
)
|
||||
|
||||
private def buildDiffValue(
|
||||
static: Seq[String],
|
||||
dynamic: Seq[RenderedMod[?]],
|
||||
includeUnchanged: Boolean = false
|
||||
): Diff.Mod =
|
||||
Diff.Mod(
|
||||
static = static,
|
||||
dynamic = dynamic.zipWithIndex
|
||||
.filter(includeUnchanged || _._1.wasUpdated)
|
||||
.map {
|
||||
case (v: RenderedMod.Dynamic[?, ?], i) =>
|
||||
Diff.Dynamic(i, Diff.Static(v.currentValue.toString))
|
||||
case (v: RenderedMod.When[?], i) =>
|
||||
if v.displayed then
|
||||
if includeUnchanged || v.cond.wasUpdated then
|
||||
Diff.Dynamic(
|
||||
i,
|
||||
buildDiffValue(
|
||||
v.nested.static,
|
||||
v.nested.dynamic,
|
||||
includeUnchanged = true
|
||||
)
|
||||
)
|
||||
else
|
||||
Diff.Dynamic(
|
||||
i,
|
||||
buildDiffValue(
|
||||
static = Seq.empty,
|
||||
v.nested.dynamic,
|
||||
includeUnchanged
|
||||
)
|
||||
)
|
||||
else Diff.Dynamic(i, Diff.Deleted)
|
||||
case (v: RenderedMod.Split[?, ?], i) =>
|
||||
Diff.Dynamic(
|
||||
i,
|
||||
Diff.Split(
|
||||
static = if includeUnchanged then v.static else Seq.empty,
|
||||
dynamic = v.dynamic.toList.zipWithIndex
|
||||
.filter(includeUnchanged || _._1.exists(_.wasUpdated))
|
||||
.map[Diff.Dynamic]((mods, i) =>
|
||||
Diff.Dynamic(
|
||||
i,
|
||||
buildDiffValue(
|
||||
static = Seq.empty,
|
||||
dynamic = mods,
|
||||
includeUnchanged
|
||||
)
|
||||
)
|
||||
)
|
||||
.appendedAll(
|
||||
v.removedIndexes
|
||||
.map(i => Diff.Dynamic(i, Diff.Deleted))
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
|
@ -1,52 +1,36 @@
|
|||
package scalive
|
||||
|
||||
import zio.Chunk
|
||||
import zio.json.ast.*
|
||||
|
||||
object JsonAstBuilder:
|
||||
def buildInit(static: Seq[String], dynamic: Seq[RenderedMod[?]]): Json =
|
||||
Json
|
||||
.Obj("s" -> Json.Arr(static.map(Json.Str(_))*))
|
||||
.merge(buildDiffValue(dynamic, includeUnchanged = true))
|
||||
|
||||
def buildDiff(dynamic: Seq[RenderedMod[?]]): Json =
|
||||
Json.Obj("diff" -> buildDiffValue(dynamic))
|
||||
|
||||
private def buildDiffValue(
|
||||
dynamic: Seq[RenderedMod[?]],
|
||||
includeUnchanged: Boolean = false
|
||||
): Json =
|
||||
Json.Obj(
|
||||
dynamic.zipWithIndex.filter(includeUnchanged || _._1.wasUpdated).map {
|
||||
case (v: RenderedMod.Dynamic[?, ?], i) =>
|
||||
i.toString -> Json.Str(v.currentValue.toString)
|
||||
case (v: RenderedMod.When[?], i) =>
|
||||
if v.displayed then
|
||||
if includeUnchanged || v.cond.wasUpdated then
|
||||
i.toString -> v.nested.buildInitJson
|
||||
else
|
||||
i.toString -> buildDiffValue(v.nested.dynamic, includeUnchanged)
|
||||
else i.toString -> Json.Bool(false)
|
||||
case (v: RenderedMod.Split[?, ?], i) =>
|
||||
i.toString ->
|
||||
Json
|
||||
.Obj(
|
||||
(Option
|
||||
.when(includeUnchanged)(
|
||||
"s" -> Json.Arr(v.static.map(Json.Str(_))*)
|
||||
def diffToJson(diff: Diff): Json =
|
||||
diff match
|
||||
case Diff.Mod(static, dynamic) =>
|
||||
Json.Obj(
|
||||
Option
|
||||
.when(static.nonEmpty)("s" -> Json.Arr(static.map(Json.Str(_))*))
|
||||
.to(Chunk)
|
||||
.appendedAll(
|
||||
dynamic.map(d => d.index.toString -> diffToJson(d.diff))
|
||||
)
|
||||
)
|
||||
case Diff.Split(static, dynamic) =>
|
||||
Json.Obj(
|
||||
Option
|
||||
.when(static.nonEmpty)("s" -> Json.Arr(static.map(Json.Str(_))*))
|
||||
.to(Chunk)
|
||||
.appendedAll(
|
||||
Option.when(dynamic.nonEmpty)(
|
||||
"d" ->
|
||||
Json.Obj(
|
||||
dynamic.map(d => d.index.toString -> diffToJson(d.diff))*
|
||||
)
|
||||
.toList ++
|
||||
List(
|
||||
"d" ->
|
||||
Json.Obj(
|
||||
(v.dynamic.toList.zipWithIndex
|
||||
.filter(includeUnchanged || _._1.exists(_.wasUpdated))
|
||||
.map((mods, i) =>
|
||||
i.toString -> buildDiffValue(mods, includeUnchanged)
|
||||
) ++
|
||||
v.removedIndexes
|
||||
.map(i => i.toString -> Json.Bool(false)))*
|
||||
)
|
||||
))*
|
||||
)
|
||||
}*
|
||||
)
|
||||
)
|
||||
)
|
||||
case Diff.Static(value) => Json.Str(value)
|
||||
case Diff.Dynamic(index, diff) =>
|
||||
Json.Obj(index.toString -> diffToJson(diff))
|
||||
case Diff.Deleted => Json.Bool(false)
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
package scalive
|
||||
|
||||
import scala.collection.mutable.ListBuffer
|
||||
import scala.collection.immutable.ArraySeq
|
||||
import zio.json.ast.Json
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import scalive.LiveViewRenderer.buildStatic
|
||||
import scalive.LiveViewRenderer.buildDynamic
|
||||
import scalive.LiveViewRenderer.buildStatic
|
||||
|
||||
import scala.annotation.nowarn
|
||||
import scala.collection.immutable.ArraySeq
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import scala.collection.mutable.ListBuffer
|
||||
|
||||
class RenderedLiveView[Model] private[scalive] (
|
||||
val static: ArraySeq[String],
|
||||
|
|
@ -15,8 +15,6 @@ class RenderedLiveView[Model] private[scalive] (
|
|||
def update(model: Model): Unit =
|
||||
dynamic.foreach(_.update(model))
|
||||
def wasUpdated: Boolean = dynamic.exists(_.wasUpdated)
|
||||
def buildInitJson: Json = JsonAstBuilder.buildInit(static, dynamic)
|
||||
def buildDiffJson: Json = JsonAstBuilder.buildDiff(dynamic)
|
||||
|
||||
sealed trait RenderedMod[Model]:
|
||||
def update(model: Model): Unit
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ def main =
|
|||
)
|
||||
)
|
||||
)
|
||||
println(r.buildInitJson.toJsonPretty)
|
||||
println(DiffEngine.buildInitJson(r).toJsonPretty)
|
||||
println("Edit first and last")
|
||||
r.update(
|
||||
MyModel(
|
||||
|
|
@ -26,7 +26,7 @@ def main =
|
|||
)
|
||||
)
|
||||
)
|
||||
println(r.buildDiffJson.toJsonPretty)
|
||||
println(DiffEngine.buildDiffJson(r).toJsonPretty)
|
||||
println("Add one")
|
||||
r.update(
|
||||
MyModel(
|
||||
|
|
@ -38,7 +38,7 @@ def main =
|
|||
)
|
||||
)
|
||||
)
|
||||
println(r.buildDiffJson.toJsonPretty)
|
||||
println(DiffEngine.buildDiffJson(r).toJsonPretty)
|
||||
println("Remove first")
|
||||
r.update(
|
||||
MyModel(
|
||||
|
|
@ -49,12 +49,12 @@ def main =
|
|||
)
|
||||
)
|
||||
)
|
||||
println(r.buildDiffJson.toJsonPretty)
|
||||
println(DiffEngine.buildDiffJson(r).toJsonPretty)
|
||||
println("Remove all")
|
||||
r.update(
|
||||
MyModel(List.empty)
|
||||
)
|
||||
println(r.buildDiffJson.toJsonPretty)
|
||||
println(DiffEngine.buildDiffJson(r).toJsonPretty)
|
||||
|
||||
final case class MyModel(elems: List[NestedModel])
|
||||
final case class NestedModel(name: String, age: Int)
|
||||
|
|
|
|||
|
|
@ -32,14 +32,14 @@ object LiveViewSpec extends TestSuite:
|
|||
)
|
||||
test("init") {
|
||||
assertEqualsJson(
|
||||
lv.buildInitJson,
|
||||
DiffEngine.buildInitJson(lv),
|
||||
Json.Obj(
|
||||
"s" -> Json.Arr(Json.Str("<div>Static string</div>"))
|
||||
)
|
||||
)
|
||||
}
|
||||
test("diff") {
|
||||
assertEqualsJson(lv.buildDiffJson, emptyDiff)
|
||||
assertEqualsJson(DiffEngine.buildDiffJson(lv), emptyDiff)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -54,7 +54,7 @@ object LiveViewSpec extends TestSuite:
|
|||
)
|
||||
test("init") {
|
||||
assertEqualsJson(
|
||||
lv.buildInitJson,
|
||||
DiffEngine.buildInitJson(lv),
|
||||
Json
|
||||
.Obj(
|
||||
"s" -> Json.Arr(Json.Str("<div>"), Json.Str("</div>")),
|
||||
|
|
@ -63,12 +63,12 @@ object LiveViewSpec extends TestSuite:
|
|||
)
|
||||
}
|
||||
test("diff no update") {
|
||||
assertEqualsJson(lv.buildDiffJson, emptyDiff)
|
||||
assertEqualsJson(DiffEngine.buildDiffJson(lv), emptyDiff)
|
||||
}
|
||||
test("diff with update") {
|
||||
lv.update(TestModel(title = "title updated"))
|
||||
assertEqualsJson(
|
||||
lv.buildDiffJson,
|
||||
DiffEngine.buildDiffJson(lv),
|
||||
Json.Obj(
|
||||
"diff" -> Json.Obj("0" -> Json.Str("title updated"))
|
||||
)
|
||||
|
|
@ -77,7 +77,7 @@ object LiveViewSpec extends TestSuite:
|
|||
test("diff with update and no change") {
|
||||
lv.update(TestModel(title = "title updated"))
|
||||
lv.update(TestModel(title = "title updated"))
|
||||
assertEqualsJson(lv.buildDiffJson, emptyDiff)
|
||||
assertEqualsJson(DiffEngine.buildDiffJson(lv), emptyDiff)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -96,7 +96,7 @@ object LiveViewSpec extends TestSuite:
|
|||
)
|
||||
test("init") {
|
||||
assertEqualsJson(
|
||||
lv.buildInitJson,
|
||||
DiffEngine.buildInitJson(lv),
|
||||
Json
|
||||
.Obj(
|
||||
"s" -> Json.Arr(Json.Str("<div>"), Json.Str("</div>")),
|
||||
|
|
@ -105,16 +105,16 @@ object LiveViewSpec extends TestSuite:
|
|||
)
|
||||
}
|
||||
test("diff no update") {
|
||||
assertEqualsJson(lv.buildDiffJson, emptyDiff)
|
||||
assertEqualsJson(DiffEngine.buildDiffJson(lv), emptyDiff)
|
||||
}
|
||||
test("diff with unrelated update") {
|
||||
lv.update(TestModel(title = "title updated"))
|
||||
assertEqualsJson(lv.buildDiffJson, emptyDiff)
|
||||
assertEqualsJson(DiffEngine.buildDiffJson(lv), emptyDiff)
|
||||
}
|
||||
test("diff when true") {
|
||||
lv.update(TestModel(bool = true))
|
||||
assertEqualsJson(
|
||||
lv.buildDiffJson,
|
||||
DiffEngine.buildDiffJson(lv),
|
||||
Json.Obj(
|
||||
"diff" -> Json.Obj(
|
||||
"0" ->
|
||||
|
|
@ -132,7 +132,7 @@ object LiveViewSpec extends TestSuite:
|
|||
lv.update(TestModel(bool = true))
|
||||
lv.update(TestModel(bool = true, nestedTitle = "nested title updated"))
|
||||
assertEqualsJson(
|
||||
lv.buildDiffJson,
|
||||
DiffEngine.buildDiffJson(lv),
|
||||
Json.Obj(
|
||||
"diff" -> Json.Obj(
|
||||
"0" ->
|
||||
|
|
@ -176,7 +176,7 @@ object LiveViewSpec extends TestSuite:
|
|||
)
|
||||
test("init") {
|
||||
assertEqualsJson(
|
||||
lv.buildInitJson,
|
||||
DiffEngine.buildInitJson(lv),
|
||||
Json
|
||||
.Obj(
|
||||
"s" -> Json.Arr(Json.Str("<div><ul>"), Json.Str("</ul></div>")),
|
||||
|
|
@ -205,11 +205,11 @@ object LiveViewSpec extends TestSuite:
|
|||
)
|
||||
}
|
||||
test("diff no update") {
|
||||
assertEqualsJson(lv.buildDiffJson, emptyDiff)
|
||||
assertEqualsJson(DiffEngine.buildDiffJson(lv), emptyDiff)
|
||||
}
|
||||
test("diff with unrelated update") {
|
||||
lv.update(initModel.copy(title = "title updated"))
|
||||
assertEqualsJson(lv.buildDiffJson, emptyDiff)
|
||||
assertEqualsJson(DiffEngine.buildDiffJson(lv), emptyDiff)
|
||||
}
|
||||
test("diff with item changed") {
|
||||
lv.update(
|
||||
|
|
@ -218,7 +218,7 @@ object LiveViewSpec extends TestSuite:
|
|||
)
|
||||
)
|
||||
assertEqualsJson(
|
||||
lv.buildDiffJson,
|
||||
DiffEngine.buildDiffJson(lv),
|
||||
Json.Obj(
|
||||
"diff" -> Json.Obj(
|
||||
"0" ->
|
||||
|
|
@ -239,7 +239,7 @@ object LiveViewSpec extends TestSuite:
|
|||
initModel.copy(items = initModel.items.appended(NestedModel("d", 35)))
|
||||
)
|
||||
assertEqualsJson(
|
||||
lv.buildDiffJson,
|
||||
DiffEngine.buildDiffJson(lv),
|
||||
Json.Obj(
|
||||
"diff" -> Json.Obj(
|
||||
"0" ->
|
||||
|
|
@ -261,7 +261,7 @@ object LiveViewSpec extends TestSuite:
|
|||
initModel.copy(items = initModel.items.tail)
|
||||
)
|
||||
assertEqualsJson(
|
||||
lv.buildDiffJson,
|
||||
DiffEngine.buildDiffJson(lv),
|
||||
Json.Obj(
|
||||
"diff" -> Json.Obj(
|
||||
"0" ->
|
||||
|
|
@ -286,7 +286,7 @@ object LiveViewSpec extends TestSuite:
|
|||
test("diff all removed") {
|
||||
lv.update(initModel.copy(items = List.empty))
|
||||
assertEqualsJson(
|
||||
lv.buildDiffJson,
|
||||
DiffEngine.buildDiffJson(lv),
|
||||
Json.Obj(
|
||||
"diff" -> Json.Obj(
|
||||
"0" ->
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue