Skip to content

Commit 63a0476

Browse files
authored
Rework Update JSON encoders and decoders (#3261)
This is an attempt to clarify the various `Encoder` and `Decoder` instances that make up `Encoder[Update]` and `Decoder[Update]` that are used by the `PullRequestRepository` for writing/reading updates to/from disk. I had a hard time understanding what was going on because * nearly all of the instances are implicit (for derivation) but only the instances for `Update` are used by other parts of the program * semi-derivation was mixed with manual modifications * decoders for compatibility with old caches depend on derivation and are defined ad-hoc In this change I tried to bring more clarity to these instances by * giving compatibility decoders their own name * forgoing semi-derivation and define all instances manually. IMHO this makes it easier to evolve them if there are compatibility constraints because of old caches * making every instance except `Encoder[Update]` and `Decoder[Update]` private and non-implicit * only testing them via the public `Encoder[Update]` and `Decoder[Update]` instances This might be a controversial change and I ackknowledge that one gains more understanding by doing this rework. I'm therefore biased that these changes actually clarify anything. I'm fine with letting this age for a while before merging to see if I still feel this is an improvement then.
1 parent 16dbb26 commit 63a0476

File tree

3 files changed

+343
-235
lines changed

3 files changed

+343
-235
lines changed

modules/core/src/main/scala/org/scalasteward/core/data/Update.scala

Lines changed: 83 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@ package org.scalasteward.core.data
1818

1919
import cats.Order
2020
import cats.implicits.*
21-
import io.circe.generic.semiauto.*
22-
import io.circe.syntax.*
23-
import io.circe.{Decoder, Encoder, HCursor, Json}
21+
import io.circe.{Decoder, Encoder}
2422
import org.scalasteward.core.repoconfig.PullRequestGroup
2523
import org.scalasteward.core.util
2624
import org.scalasteward.core.util.Nel
@@ -198,66 +196,98 @@ object Update {
198196
}
199197
}
200198

201-
// TODO: Derive all instances of `Encoder`/`Decoder` here using `deriveCodec`
202-
// Partially manually implemented so we don't fail reading old caches (those
203-
// still using `Single`/`Group`)
199+
implicit val SingleOrder: Order[Single] =
200+
Order.by((u: Single) => (u.crossDependencies, u.newerVersions))
204201

205-
implicit val ForArtifactIdEncoder: Encoder[ForArtifactId] = {
206-
val derived = deriveEncoder[ForArtifactId]
202+
// Encoder and Decoder instances
207203

208-
derived.mapJson(json => Json.obj("ForArtifactId" -> json))
209-
}
204+
// ForArtifactId
210205

211-
implicit val ForArtifactIdDecoder: Decoder[ForArtifactId] = {
212-
val derived = deriveDecoder[ForArtifactId]
213-
derived
214-
.prepare(_.downField("ForArtifactId"))
215-
.or(derived.prepare(_.downField("Single")))
216-
}
206+
implicit private val forArtifactIdEncoder: Encoder[ForArtifactId] =
207+
Encoder.forProduct1("ForArtifactId")(identity[ForArtifactId]) {
208+
Encoder.forProduct4("crossDependency", "newerVersions", "newerGroupId", "newerArtifactId") {
209+
s => (s.crossDependency, s.newerVersions, s.newerGroupId, s.newerArtifactId)
210+
}
211+
}
217212

218-
implicit val ForGroupIdEncoder: Encoder[ForGroupId] = {
219-
val derived = deriveEncoder[ForGroupId]
213+
private val unwrappedForArtifactIdDecoder: Decoder[ForArtifactId] =
214+
Decoder.forProduct4("crossDependency", "newerVersions", "newerGroupId", "newerArtifactId") {
215+
(
216+
crossDependency: CrossDependency,
217+
newerVersions: Nel[Version],
218+
newerGroupId: Option[GroupId],
219+
newerArtifactId: Option[String]
220+
) =>
221+
ForArtifactId(crossDependency, newerVersions, newerGroupId, newerArtifactId)
222+
}
220223

221-
derived.mapJson(json => Json.obj("ForGroupId" -> json))
222-
}
224+
private val forArtifactIdDecoderV1: Decoder[ForArtifactId] =
225+
Decoder.forProduct1("Single")(identity[ForArtifactId])(unwrappedForArtifactIdDecoder)
223226

224-
private val oldForGroupIdDecoder: Decoder[ForGroupId] =
225-
(c: HCursor) =>
226-
for {
227-
crossDependencies <- c.downField("crossDependencies").as[Nel[CrossDependency]]
228-
newerVersions <- c.downField("newerVersions").as[Nel[Version]]
229-
forArtifactIds = crossDependencies
230-
.map(crossDependency => ForArtifactId(crossDependency, newerVersions))
231-
} yield ForGroupId(forArtifactIds)
232-
233-
implicit val ForGroupIdDecoder: Decoder[ForGroupId] =
234-
deriveDecoder[ForGroupId]
235-
.prepare(_.downField("ForGroupId"))
236-
.or(oldForGroupIdDecoder.prepare(_.downField("ForGroupId")))
237-
.or(oldForGroupIdDecoder.prepare(_.downField("Group")))
238-
239-
implicit val GroupedEncoder: Encoder[Grouped] = {
240-
val derived = deriveEncoder[Grouped]
241-
242-
derived.mapJson(json => Json.obj("Grouped" -> json))
243-
}
227+
private val forArtifactIdDecoderV2 =
228+
Decoder.forProduct1("ForArtifactId")(identity[ForArtifactId])(unwrappedForArtifactIdDecoder)
244229

245-
implicit val GroupedDecoder: Decoder[Grouped] =
246-
deriveDecoder[Grouped].prepare(_.downField("Grouped"))
230+
implicit private val forArtifactIdDecoder: Decoder[ForArtifactId] =
231+
forArtifactIdDecoderV2.or(forArtifactIdDecoderV1)
247232

248-
implicit val SingleOrder: Order[Single] =
249-
Order.by((u: Single) => (u.crossDependencies, u.newerVersions))
233+
// ForGroupId
234+
235+
private val forGroupIdEncoder: Encoder[ForGroupId] =
236+
Encoder.forProduct1("ForGroupId")(identity[ForGroupId]) {
237+
Encoder.forProduct1("forArtifactIds")(_.forArtifactIds)
238+
}
239+
240+
private val unwrappedForGroupIdDecoderV1: Decoder[ForGroupId] =
241+
Decoder.forProduct2("crossDependencies", "newerVersions") {
242+
(crossDependencies: Nel[CrossDependency], newerVersions: Nel[Version]) =>
243+
val forArtifactIds =
244+
crossDependencies.map(crossDependency => ForArtifactId(crossDependency, newerVersions))
245+
ForGroupId(forArtifactIds)
246+
}
250247

251-
implicit val UpdateEncoder: Encoder[Update] = {
252-
case update: Grouped => update.asJson
253-
case update: ForArtifactId => update.asJson
254-
case update: ForGroupId => update.asJson
248+
private val unwrappedForGroupIdDecoderV3: Decoder[ForGroupId] =
249+
Decoder.forProduct1("forArtifactIds") { (forArtifactIds: Nel[ForArtifactId]) =>
250+
ForGroupId(forArtifactIds)
251+
}
252+
253+
private val forGroupIdDecoderV1: Decoder[ForGroupId] =
254+
Decoder.forProduct1("Group")(identity[ForGroupId])(unwrappedForGroupIdDecoderV1)
255+
256+
private val forGroupIdDecoderV2: Decoder[ForGroupId] =
257+
Decoder.forProduct1("ForGroupId")(identity[ForGroupId])(unwrappedForGroupIdDecoderV1)
258+
259+
private val forGroupIdDecoderV3: Decoder[ForGroupId] =
260+
Decoder.forProduct1("ForGroupId")(identity[ForGroupId])(unwrappedForGroupIdDecoderV3)
261+
262+
private val forGroupIdDecoder: Decoder[ForGroupId] =
263+
forGroupIdDecoderV3.or(forGroupIdDecoderV2).or(forGroupIdDecoderV1)
264+
265+
// Grouped
266+
267+
private val groupedEncoder: Encoder[Grouped] =
268+
Encoder.forProduct1("Grouped")(identity[Grouped]) {
269+
Encoder.forProduct3("name", "title", "updates")(s => (s.name, s.title, s.updates))
270+
}
271+
272+
private val groupedDecoder: Decoder[Grouped] =
273+
Decoder.forProduct1("Grouped")(identity[Grouped]) {
274+
Decoder.forProduct3("name", "title", "updates") {
275+
(name: String, title: Option[String], updates: List[ForArtifactId]) =>
276+
Grouped(name, title, updates)
277+
}
278+
}
279+
280+
// Update
281+
282+
implicit val updateEncoder: Encoder[Update] = {
283+
case update: ForArtifactId => forArtifactIdEncoder.apply(update)
284+
case update: ForGroupId => forGroupIdEncoder.apply(update)
285+
case update: Grouped => groupedEncoder.apply(update)
255286
}
256287

257-
implicit val UpdateDecoder: Decoder[Update] =
258-
ForArtifactIdDecoder
288+
implicit val updateDecoder: Decoder[Update] =
289+
forArtifactIdDecoder
259290
.widen[Update]
260-
.or(ForGroupIdDecoder.widen[Update])
261-
.or(Decoder[Grouped].widen[Update])
262-
291+
.or(forGroupIdDecoder.widen[Update])
292+
.or(groupedDecoder.widen[Update])
263293
}

0 commit comments

Comments
 (0)