kantan.csv
kantan.csv copied to clipboard
Add a "discriminator" RowDecoder
Following a discussion with @aroberts on gitter:
import kantan.csv._
import kantan.csv.ops._
object Test extends App {
val input = "true,1,2\nfalse,foo,bar"
sealed abstract class Foo extends Product with Serializable
final case class Ints(a: Int, b: Int) extends Foo
final case class Strings(a: String, b: String) extends Foo
implicit val intsRowDecoder: RowDecoder[Ints] = RowDecoder.decoder(1, 2)(Ints.apply _)
implicit val stringsRowDecoder: RowDecoder[Strings] = RowDecoder.decoder(1, 2)(Strings.apply _)
type Decode[A] = Seq[String] => DecodeResult[A]
private def invalidDiscriminator[C](data: C): Decode[Nothing] = RowDecoder.from(_ => DecodeResult.typeError(s"Couldn't decode discriminator: $data")).decode
def discriminatorRowDecoder[C: CellDecoder, A](index: Int)(discriminator: PartialFunction[C, Decode[A]]): RowDecoder[A] =
RowDecoder.from(input => for {
data <- input.lift(index).map(CellDecoder[C].decode).getOrElse(DecodeResult.outOfBounds(index))
discriminated <- discriminator.applyOrElse(data, invalidDiscriminator)(input)
} yield discriminated)
implicit val fooDecoder: RowDecoder[Foo] = discriminatorRowDecoder[Boolean, Foo](0) {
case true => RowDecoder[Ints].decode
case false => RowDecoder[Strings].decode
}
input.readCsv[List, Foo](',', false).foreach(println _)
}