Created
March 24, 2017 11:09
-
-
Save markus1189/a3bc26a84d493e4a16e5fc82be60718a to your computer and use it in GitHub Desktop.
Generic csv encoder with shapeless
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// for ammonite | |
// import $ivy.`com.chuusai::shapeless:2.3.2` | |
import shapeless._ | |
import shapeless.labelled._ | |
import shapeless.ops.hlist._ | |
import shapeless.ops.record._ | |
case class IceCream(flavour: String, price: Int) | |
trait CsvEncoder[A] { | |
def encode(value: A): List[String] | |
} | |
object Symboler extends Poly1 { | |
implicit def default[A <: Symbol](implicit witness: Witness.Aux[A]) = | |
at[A](_ => witness.value.name) | |
} | |
object CsvEncoder { | |
// "Summoner" method | |
def apply[A](implicit enc: CsvEncoder[A]): CsvEncoder[A] = enc | |
// "Constructor" method | |
def instance[A](func: A => List[String]): CsvEncoder[A] = new CsvEncoder[A] { | |
def encode(value: A): List[String] = | |
func(value) | |
} | |
def createEncoder[A](func: A => List[String]): CsvEncoder[A] = | |
new CsvEncoder[A] { | |
def encode(value: A): List[String] = func(value) | |
} | |
implicit val stringEncoder: CsvEncoder[String] = createEncoder( | |
str => List(str)) | |
implicit val intEncoder: CsvEncoder[Int] = createEncoder( | |
num => List(num.toString)) | |
implicit val booleanEncoder: CsvEncoder[Boolean] = createEncoder( | |
bool => List(if (bool) "yes" else "no")) | |
implicit val hnilEncoder: CsvEncoder[HNil] = createEncoder(hnil => Nil) | |
implicit def hlistEncoder[H, T <: HList]( | |
implicit hEncoder: CsvEncoder[H], | |
tEncoder: CsvEncoder[T]): CsvEncoder[H :: T] = | |
createEncoder { | |
case h :: t => hEncoder.encode(h) ++ tEncoder.encode(t) | |
} | |
implicit def genericEncoder[A, R](implicit gen: Generic.Aux[A, R], | |
enc: CsvEncoder[R]): CsvEncoder[A] = | |
createEncoder(a => enc.encode(gen.to(a))) | |
} | |
object Main { | |
def main(args: Array[String]): Unit = { | |
println( | |
writeCsv( | |
List(IceCream("strawberry", 130), | |
IceCream("lemon", 140), | |
IceCream("chocolate", 130)))) | |
} | |
def writeCsv[A, H <: HList, I <: HList, J <: HList](values: List[A])( | |
implicit enc: CsvEncoder[A], | |
gen: LabelledGeneric.Aux[A, H], | |
keys: Keys.Aux[H, I], | |
mapper: Mapper.Aux[Symboler.type, I, J], | |
toTraversable: ToTraversable.Aux[J, List, String]): String = { | |
keys().map(Symboler).toList.mkString(",") + "\n" + | |
values.map(value => enc.encode(value).mkString(",")).mkString("\n") | |
} | |
} | |
// for ammonite | |
// Main.main(Array.empty) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment