Skip to content

Instantly share code, notes, and snippets.

@tkroman
Last active November 1, 2018 00:34
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 tkroman/1139b117b76a74a463a11aef8708fbe1 to your computer and use it in GitHub Desktop.
Save tkroman/1139b117b76a74a463a11aef8708fbe1 to your computer and use it in GitHub Desktop.
cassandra writer typeclass with support for field annotations to customize column names using shapeless
import scala.annotation.StaticAnnotation
import com.datastax.driver.core.BoundStatement
import shapeless._
import shapeless.labelled._
import shapeless.ops.hlist.Zip
case class ColName(v: String) extends StaticAnnotation
sealed trait CRow[A] {
def write(a: A, s: BoundStatement): BoundStatement
}
object CRow {
implicit val hnil: CRow[HNil] = new CRow[HNil] {
override def write(a: HNil, s: BoundStatement): BoundStatement = s
}
type Cell[A, B, C] = (FieldType[A, B], C)
implicit def hcons[FN <: Symbol,
H,
T <: HList,
CN <: Option[ColName]](implicit
fieldName: Witness.Aux[FN],
writeTail: CRow[T],
writeHead: CCell[H]): CRow[Cell[FN, H, CN] :: T] = {
new CRow[Cell[FN, H, CN] :: T] {
override def write(a: Cell[FN, H, CN] :: T,
s: BoundStatement): BoundStatement = {
val (value, colName) = a.head
val columnName: String = colName.map(_.v).getOrElse(fieldName.value.name)
writeHead.write(value, columnName, s)
writeTail.write(a.tail, s)
}
}
}
implicit def mkCrow[A,
GenRow <: HList,
Annots <: HList,
KeyList <: HList,
KeysToAnns <: HList,
GToAnns <: HList](implicit
gen: LabelledGeneric.Aux[A, GenRow],
anns: Annotations.Aux[ColName, A, Annots],
gaZipper: Zip.Aux[GenRow :: Annots :: HNil, GToAnns],
gcrow: Lazy[CRow[GToAnns]]): CRow[A] = {
new CRow[A] {
override def write(a: A, s: BoundStatement): BoundStatement = {
val genericRow: GToAnns = gen.to(a).zip(anns())
gcrow.value.write(genericRow, s)
}
}
}
def apply[A](implicit crow: CRow[A]): CRow[A] = crow
}
sealed trait CCell[A] {
def write(a: A, n: String, s: BoundStatement): BoundStatement
}
object CCell {
implicit val int: CCell[Int] = new CCell[Int] {
override def write(a: Int,
n: String,
s: BoundStatement): BoundStatement = s.setInt(n, a)
}
implicit val text: CCell[String] = new CCell[String] {
override def write(a: String,
n: String,
s: BoundStatement): BoundStatement = s.setString(n, a)
}
implicit def opt[A: CCell]: CCell[Option[A]] = new CCell[Option[A]] {
override def write(a: Option[A],
n: String,
s: BoundStatement): BoundStatement = {
a match {
case Some(x) => implicitly[CCell[A]].write(x, n, s)
case None => s.setToNull(n)
}
}
}
}
object Main {
case class Foo(@ColName("YES") x: Int, y: String, z: Option[Int])
def main(args: Array[String]): Unit = {
val foo = Foo(1, "2", None)
val crow: CRow[Foo] = CRow[Foo]
crow.write(foo, null)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment