kantan.csv icon indicating copy to clipboard operation
kantan.csv copied to clipboard

Add a "discriminator" RowDecoder

Open nrinaudo opened this issue 8 years ago • 0 comments

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 _)
}

nrinaudo avatar Jan 27 '17 21:01 nrinaudo