Skip to content

Commit

Permalink
decoder: implement missing num/array/int decoders
Browse files Browse the repository at this point in the history
  • Loading branch information
shuttie committed Nov 17, 2017
1 parent 08c6038 commit 892d0e0
Show file tree
Hide file tree
Showing 22 changed files with 187 additions and 46 deletions.
4 changes: 2 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ publishTo := Some(
Opts.resolver.sonatypeStaging
)

//addCompilerPlugin("io.tryp" % "splain" % "0.2.7" cross CrossVersion.patch)
addCompilerPlugin("io.tryp" % "splain" % "0.2.7" cross CrossVersion.patch)

//scalacOptions ++= Seq("-P:splain:implicits:true","-P:splain:color:false")
scalacOptions ++= Seq("-P:splain:implicits:true","-P:splain:color:false")

pomExtra := (
<scm>
Expand Down
24 changes: 14 additions & 10 deletions src/main/scala/io/findify/clickhouse/format/Field.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,22 +37,26 @@ object Field {
case class FixedString(raw: String, length: Int) extends ScalarField {
override def value: Json = Json.fromString(raw)
}
case class Int8(raw: Int) extends ScalarField {
sealed trait ByteNumber extends ScalarField
sealed trait IntNumber extends ScalarField
sealed trait LongNumber extends ScalarField
sealed trait RealNumber extends ScalarField
case class Int8(raw: Byte) extends ByteNumber {
override def value: Json = Json.fromInt(raw)
}
case class Int32(raw: Int) extends ScalarField {
case class UInt8(raw: Byte) extends ByteNumber {
override def value: Json = Json.fromInt(raw)
}
case class Int64(raw: Long) extends ScalarField {
override def value: Json = Json.fromLong(raw)
}
case class UInt8(raw: Int) extends ScalarField {
case class Int32(raw: Int) extends IntNumber {
override def value: Json = Json.fromInt(raw)
}
case class UInt32(raw: Int) extends ScalarField {
case class Int64(raw: Long) extends LongNumber {
override def value: Json = Json.fromLong(raw)
}
case class UInt32(raw: Int) extends IntNumber {
override def value: Json = Json.fromInt(raw)
}
case class UInt64(raw: Long) extends ScalarField {
case class UInt64(raw: Long) extends LongNumber {
override def value: Json = Json.fromLong(raw)
}
case class CDate(raw: LocalDate) extends ScalarField {
Expand Down Expand Up @@ -105,10 +109,10 @@ object Field {
def applyScalar[T](tpe: String, value: Json): ScalarField = (tpe, value.asString, value.asArray, value.asNumber) match {
case (fixedStringPattern(length), Some(str), _, _) => FixedString(str, length.toInt)
case ("String", Some(str), _, _) => CString(str)
case ("UInt8", _, _, Some(num)) => UInt8(num.truncateToInt)
case ("UInt8", _, _, Some(num)) => UInt8(num.truncateToByte)
case ("UInt32", _, _, Some(num)) => UInt32(num.truncateToInt)
case ("UInt64", Some(str), _, _) => UInt64(str.toLong)
case ("Int8", _, _, Some(num)) => Int8(num.truncateToInt)
case ("Int8", _, _, Some(num)) => Int8(num.truncateToByte)
case ("Int32", _, _, Some(num)) => Int32(num.truncateToInt)
case ("Int64", _, _, Some(num)) => Int64(num.truncateToLong)
case ("Float32", _, _, Some(num)) => Float32(num.toDouble.floatValue())
Expand Down
17 changes: 17 additions & 0 deletions src/main/scala/io/findify/clickhouse/format/Scalar.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.findify.clickhouse.format

import org.joda.time.{LocalDate, LocalDateTime}

sealed trait Scalar[T]

object Scalar {
implicit def intScalar = new Scalar[Int] {}
implicit def longScalar = new Scalar[Long] {}
implicit def floatScalar = new Scalar[Float] {}
implicit def doubleScalar = new Scalar[Double] {}
implicit def byteScalar = new Scalar[Byte] {}
implicit def booleanScalar = new Scalar[Boolean] {}
implicit def stringScalar = new Scalar[String] {}
implicit def dateScalar = new Scalar[LocalDate] {}
implicit def dateTimeScalar = new Scalar[LocalDateTime] {}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package io.findify.clickhouse.format.decoder
import io.findify.clickhouse.format.Field
import io.findify.clickhouse.format.{Field, Scalar}
import io.findify.clickhouse.format.Field.{CArray, ScalarField}

class ArrayDecoder[T <: AnyVal, F <: ScalarField](implicit dec: Decoder[T, F]) extends Decoder[Seq[T], CArray[F]] {
class ArrayDecoder[T, F <: ScalarField](implicit dec: Decoder[T, F], s: Scalar[T]) extends Decoder[Seq[T], CArray[F]] {
override def decodeValue: PartialFunction[Field, Seq[T]] = {
case CArray(values) => values.map(v => dec.decodeValue.lift(v) match {
case Some(field) => field
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.findify.clickhouse.format.decoder

import io.findify.clickhouse.format.Field
import io.findify.clickhouse.format.Field.{ByteNumber, Int8, UInt8}

class BooleanDecoder extends Decoder[Boolean, ByteNumber] {
override def decodeValue: PartialFunction[Field, Boolean] = {
case Int8(value) => value == 1
case UInt8(value) => value == 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.findify.clickhouse.format.decoder

import io.findify.clickhouse.format.Field
import io.findify.clickhouse.format.Field._

class ByteDecoder extends Decoder[Byte, ByteNumber] {
override def decodeValue: PartialFunction[Field, Byte] = {
case UInt8(i) => i
case Int8(i) => i
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.findify.clickhouse.format.decoder

import io.findify.clickhouse.format.Field
import io.findify.clickhouse.format.Field.CDate
import org.joda.time.LocalDate

class DateDecoder extends Decoder[LocalDate, CDate] {
override def decodeValue: PartialFunction[Field, LocalDate] = {
case CDate(value) => value
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.findify.clickhouse.format.decoder

import io.findify.clickhouse.format.Field
import io.findify.clickhouse.format.Field.CDateTime
import org.joda.time.LocalDateTime

class DateTimeDecoder extends Decoder[LocalDateTime, CDateTime] {
override def decodeValue: PartialFunction[Field, LocalDateTime] = {
case CDateTime(value) => value
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package io.findify.clickhouse.format.decoder

import io.findify.clickhouse.format.Field
import io.findify.clickhouse.format.Field.Float64

class DoubleDecoder extends Decoder[Double, Float64] {
override def decodeValue: PartialFunction[Field, Double] = {
case Float64(i) => i
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package io.findify.clickhouse.format.decoder

import io.findify.clickhouse.format.Field
import io.findify.clickhouse.format.Field.Float32

class FloatDecoder extends Decoder[Float, Float32] {
override def decodeValue: PartialFunction[Field, Float] = {
case Float32(i) => i
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
package io.findify.clickhouse.format.decoder

import io.findify.clickhouse.format.Field
import io.findify.clickhouse.format.Field.{Int32, UInt32}
import io.findify.clickhouse.format.Field.{Int32, IntNumber, UInt32}

object IntDecoder {
class Int32Decoder extends Decoder[Int, Int32] {
override def decodeValue: PartialFunction[Field, Int] = {
case Int32(i) => i
}
class IntDecoder extends Decoder[Int, IntNumber] {
override def decodeValue: PartialFunction[Field, Int] = {
case Int32(i) => i
case UInt32(i) => i
}
class UInt32Decoder extends Decoder[Int, UInt32] {
override def decodeValue: PartialFunction[Field, Int] = {
case UInt32(i) => i
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.findify.clickhouse.format.decoder

import io.findify.clickhouse.format.Field
import io.findify.clickhouse.format.Field.{Int64, LongNumber, UInt64}

class LongDecoder extends Decoder[Long, LongNumber] {
override def decodeValue: PartialFunction[Field, Long] = {
case UInt64(i) => i
case Int64(i) => i
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.findify.clickhouse.format.decoder

import io.findify.clickhouse.format.{Field, Scalar}
import io.findify.clickhouse.format.Field.{Nullable, ScalarField}

class OptionEncoder[T, F <: ScalarField](implicit dec: Decoder[T, F], s: Scalar[T]) extends Decoder[Option[T], Nullable[F]] {
override def decodeValue: PartialFunction[Field, Option[T]] = {
case Nullable(Some(in)) => dec.decodeValue.lift(in)
case Nullable(None) => None
}
}
19 changes: 14 additions & 5 deletions src/main/scala/io/findify/clickhouse/format/decoder/generic.scala
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
package io.findify.clickhouse.format.decoder

import io.findify.clickhouse.format.Field
import io.findify.clickhouse.format.Field.{CArray, Row, ScalarField}
import io.findify.clickhouse.format.decoder.IntDecoder.{Int32Decoder, UInt32Decoder}
import io.findify.clickhouse.format.{Field, Scalar}
import io.findify.clickhouse.format.Field._
import org.joda.time.{LocalDate, LocalDateTime}
import shapeless.{HList, HNil, LabelledProductTypeClass, LabelledProductTypeClassCompanion, Lazy}

object generic {
import Scalar._
implicit val stringDecoder = new StringDecoder()
implicit val int32Decoder = new Int32Decoder()
implicit def arrayDecoder[T <: AnyVal, F <: ScalarField](implicit dec: Decoder[T, F]): Decoder[Seq[T], CArray[F]] = new ArrayDecoder[T,F]()
implicit val intDecoder = new IntDecoder()
implicit val byteDecoder = new ByteDecoder()
implicit val booleanDecoder = new BooleanDecoder()
implicit val dateDecoder = new DateDecoder()
implicit val dateTimeDecoder = new DateTimeDecoder()
implicit val doubleDecoder = new DoubleDecoder()
implicit val floatDecoder = new FloatDecoder()
implicit val longDecoder = new LongDecoder()
implicit def arrayDecoder[T, F <: ScalarField](implicit dec: Decoder[T, F], s: Scalar[T]): Decoder[Seq[T], CArray[F]] = new ArrayDecoder[T,F]()
implicit def optionDecoder[T, F <: ScalarField](implicit dec: Decoder[T,F], s: Scalar[T]): Decoder[Option[T], Nullable[F]] = new OptionEncoder[T, F]()

type RowDecoder[T] = Decoder[T, _ <: Field]
def deriveDecoder[T](implicit dec: Lazy[RowDecoder[T]]) = dec.value
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.findify.clickhouse.format.encoder
import io.findify.clickhouse.format.Field
import io.findify.clickhouse.format.{Field, Scalar}
import io.findify.clickhouse.format.Field.{CArray, ScalarField}

class ArrayEncoder[T <: AnyVal, F <: ScalarField](implicit enc: ScalarEncoder[T,F]) extends ScalarEncoder[Seq[T], CArray[F]] {
class ArrayEncoder[T, F <: ScalarField](implicit enc: ScalarEncoder[T,F], s: Scalar[T]) extends ScalarEncoder[Seq[T], CArray[F]] {
override def encode(value: Seq[T]): CArray[F] = CArray(value.map(enc.encode))
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package io.findify.clickhouse.format.encoder
import io.findify.clickhouse.format.Field
import io.findify.clickhouse.format.{Field, Scalar}
import io.findify.clickhouse.format.Field.{Nullable, ScalarField}

class OptionEncoder[T, F <: ScalarField](implicit enc: ScalarEncoder[T, F]) extends ScalarEncoder[Option[T], Nullable[F]] {
class OptionEncoder[T, F <: ScalarField](implicit enc: ScalarEncoder[T, F], s: Scalar[T]) extends ScalarEncoder[Option[T], Nullable[F]] {
override def encode(value: Option[T]): Nullable[F] = value match {
case None => Nullable(None)
case Some(in) => Nullable(Some(enc.encode(in)))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
package io.findify.clickhouse.format.encoder

import io.findify.clickhouse.format.Field
import io.findify.clickhouse.format.{Field, Scalar}
import io.findify.clickhouse.format.Field._
import org.joda.time.{LocalDate, LocalDateTime}
import shapeless.{HList, HNil, LabelledProductTypeClass, LabelledProductTypeClassCompanion, Lazy}

object generic {

import io.findify.clickhouse.format.Scalar._

implicit val stringEncoder = new StringEncoder()
implicit val intEncoder = new IntEncoder()
implicit val boolEncoder = new BooleanEncoder()
implicit val dateEncoder = new DateEncoder()
implicit val dateTimeEncoder = new DateTimeEncoder()
implicit val floatEncoder = new FloatEncoder()
implicit val longEncoder = new LongEncoder()
implicit def optionEncoder[T, F <: ScalarField](implicit enc: ScalarEncoder[T,F]): Encoder[Option[T], Nullable[F]] = new OptionEncoder()
implicit def arrayEncoder[T <: AnyVal, F <: ScalarField](implicit enc: ScalarEncoder[T,F]): Encoder[Seq[T], CArray[F]] = new ArrayEncoder()
implicit def arrayStringEncoder(implicit enc: ScalarEncoder[String, CString]): Encoder[Seq[String], CArray[CString]] = new ArrayStringEncoder()
implicit def optionEncoder[T, F <: ScalarField](implicit enc: ScalarEncoder[T,F], s: Scalar[T]): Encoder[Option[T], Nullable[F]] = new OptionEncoder[T,F]()
implicit def arrayEncoder[T, F <: ScalarField](implicit enc: ScalarEncoder[T,F], s: Scalar[T]): Encoder[Seq[T], CArray[F]] = new ArrayEncoder[T,F]()
implicit def nestedEncoder[T <: Product, F <: Field](implicit enc: Encoder[T, F]): Encoder[Seq[T], CNested] = new NestedEncoder()

def deriveEncoder[T](implicit enc: Lazy[RowEncoder[T]]) = enc.value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ class ArrayDecoderTest extends FlatSpec with Matchers {
val data = Row(Map("a" -> CString("a"), "b" -> CArray(Seq(Int32(1)))))
val dec = deriveDecoder[Simple]
dec.decode("", data) shouldBe Simple("a", Seq(1))
}

it should "derive decoder for arrays of strings" in {
case class Simple(a: String, b: Seq[String])
val data = Row(Map("a" -> CString("a"), "b" -> CArray(Seq(CString("c")))))
val dec = deriveDecoder[Simple]
dec.decode("", data) shouldBe Simple("a", Seq("c"))
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.findify.clickhouse.format.decoder

import io.findify.clickhouse.format.Field.{CString, Int32, Row}
import org.joda.time.{LocalDate, LocalDateTime}
import org.scalatest.{FlatSpec, Matchers}

class AutoDecoderTest extends FlatSpec with Matchers {
Expand All @@ -12,4 +13,8 @@ class AutoDecoderTest extends FlatSpec with Matchers {
val dec = deriveDecoder[Simple]
dec.decode("", data) shouldBe Simple("a", 1)
}
it should "derive for rich objects" in {
case class RichBitch(a: Byte, b: Boolean, c: Seq[Byte], d: LocalDate, e: LocalDateTime, f: Seq[LocalDate], g: Long, h: Double, i: Float)
val dec = deriveDecoder[RichBitch]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.findify.clickhouse.format.decoder

import org.scalatest.{FlatSpec, Matchers}

class OptionDecoderTest extends FlatSpec with Matchers {
import io.findify.clickhouse.format.decoder.generic._
import io.findify.clickhouse.format.decoder.generic.auto._

it should "decode simple options" in {
case class Hello(a: Option[Int])
val dec = deriveDecoder[Hello]
}

it should "decode options of non-value classes" in {
case class Hello(a: Option[String])
val dec = deriveDecoder[Hello]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import org.scalatest.{FlatSpec, Matchers}

class ArrayEncoderTest extends FlatSpec with Matchers {
import io.findify.clickhouse.format.encoder.generic._
//import io.findify.clickhouse.format.encoder.generic.row._
import io.findify.clickhouse.format.encoder.generic.auto._

/*it should "derive for int arrays" in {
it should "derive for int arrays" in {
case class Hello(a: String, b: Seq[Int])
val enc = deriveEncoder[Hello]
enc.encode("", Hello("foo", Seq(1,2,3))) shouldBe Map("a" -> CString("foo"), "b" -> CArray(Seq(Int32(1), Int32(2), Int32(3))))
Expand All @@ -17,5 +17,5 @@ class ArrayEncoderTest extends FlatSpec with Matchers {
case class Hello(a: String, b: Seq[String])
val enc = deriveEncoder[Hello]
enc.encode("", Hello("foo", Seq("x", "y"))) shouldBe Map("a" -> CString("foo"), "b" -> CArray(Seq(CString("x"), CString("y"))))
}*/
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import io.findify.clickhouse.format.Field.{CString, Int32, Row}
import org.scalatest.{FlatSpec, Matchers}

class AutoEncoderTest extends FlatSpec with Matchers {
/*import io.findify.clickhouse.format.encoder.generic._
import io.findify.clickhouse.format.encoder.generic.row._
import io.findify.clickhouse.format.encoder.generic._
import io.findify.clickhouse.format.encoder.generic.auto._

it should "derive encoder for plain case classes" in {
case class Hello(a: String, b:Int)
val enc = deriveEncoder[Hello, Row]
val enc = deriveEncoder[Hello]
enc.encode("hello", Hello("foo", 1)) shouldBe Map("a" -> CString("foo"), "b" -> Int32(1))
}*/
}
}

0 comments on commit 892d0e0

Please sign in to comment.