Let's say I have a case class X(id: Int, name: String, age: Int)
and some function (in my case, withUniqueGeneratedKeys
in doobie) that returns X
. If I have already defined X
I am good.
But in my case the core data structure is something like:
case class XData(name: String, age: Int)
case class MaterializedX(id: Int, element: XData)
And of course I could write a line like case class X(id: Int, name: String, age: Int)
to create X
but it would be duplication of logic - whenever something about XData
changes, I'd have to change the code for X
as well. Intuitively, it feels like there should be a way to derive X
from XData
and MaterializedX
. This transformation might mean some code, but it would save me lots of future work because I have many item types beyond X
.
How could this be done? Happy to hear other approaches.
I am using Scala 3.1.2 in case this matters. Thank you!
Edit: Changed title per helpful comment, to make this question easier to understand.
CodePudding user response:
I think you should be more clear about the question, I mean what title says is almost completely different from your question description (and what you might be actually looking for). Anyway, in my assumptions, what you need is probably a custom Read[MaterializedX]
and Write[MaterializedX]
, as follows:
implicit val pointMaterializedX: Read[MaterializedX] =
Read[(Int, String, Int)]
.map {
case (id, name, age) => MaterializedX(id, XData(name, age))
}
implicit val pointWrite: Write[MaterializedX] =
Write[(Int, String, Int)]
.contramap { materializedX =>
(materializedX.id, materializedX.element.name, materializedX.element.age)
}
CodePudding user response:
This works out of the box with doobie. Nested case classes get flattened into one row. The following code compiles without defining any custom decoders.
case class XData(name: String, age: Int)
case class MaterializedX(id: Int, element: XData)
implicitly[doobie.Read[MaterializedX]]
implicitly[doobie.Write[MaterializedX]]