Skip to content

Instantly share code, notes, and snippets.

@bszwej
Last active October 29, 2019 06:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bszwej/851804e165f23ac835468470178a1d4b to your computer and use it in GitHub Desktop.
Save bszwej/851804e165f23ac835468470178a1d4b to your computer and use it in GitHub Desktop.
// Use Ammonite REPL to run this example: https://ammonite.io/
import $ivy.`org.typelevel::cats-core:2.0.0`, cats.syntax.either._, $ivy.`com.chuusai::shapeless:2.3.3`, shapeless._
trait CsvValueDecoder[A] {
def decode(value: String): Either[String, A]
}
object CsvValueDecoder {
def create[A](f: String => Either[String, A]): CsvValueDecoder[A] = (value: String) => f(value)
}
trait CsvDecoder[A] {
def decode(xs: List[String]): Either[String, A]
}
object CsvDecoder {
def create[A](f: List[String] => Either[String, A]): CsvDecoder[A] = (xs: List[String]) => f(xs)
}
implicit val intDecoder: CsvValueDecoder[Int] =
CsvValueDecoder.create[Int](v => Either.catchNonFatal(v.toInt).leftMap(_.getMessage))
implicit val booleanDecoder: CsvValueDecoder[Boolean] =
CsvValueDecoder.create[Boolean](v => Either.catchNonFatal(v.toBoolean).leftMap(_.getMessage))
implicit val stringDecoder: CsvValueDecoder[String] =
CsvValueDecoder.create[String](_.asRight[String])
// test
implicitly[CsvValueDecoder[Int]].decode("100")
implicitly[CsvValueDecoder[Boolean]].decode("true")
implicitly[CsvValueDecoder[String]].decode("aaa")
implicit def hnilCsvDecoder: CsvDecoder[HNil] = CsvDecoder.create {
case Nil => HNil.asRight
case _ => "Nil expected".asLeft
}
implicit def hlistCsvDecoder[H, T <: HList](implicit tCsvValueDecoder: CsvValueDecoder[H], tCsvDecoder: CsvDecoder[T]): CsvDecoder[H :: T] = CsvDecoder.create {
case Nil => Left("Nil unexpected")
case xs =>
for {
head <- tCsvValueDecoder.decode(xs.head)
tail <- tCsvDecoder.decode(xs.tail)
} yield head :: tail
}
// test
implicitly[CsvDecoder[String :: Int :: Boolean :: HNil]].decode(List("a", "22", "true"))
implicit def deriveCsvDecoder[A, R](implicit gen: Generic.Aux[A, R], decoder: CsvDecoder[R]): CsvDecoder[A] =
CsvDecoder.create(xs => decoder.decode(xs).map(gen.from))
// test
case class Building(name: String, year: Int, height: Int)
implicitly[CsvDecoder[Building]].decode(List("building", "2018", "300"))
implicitly[CsvDecoder[Building]].decode(List("error", "error", "error"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment