Skip to content

Commit

Permalink
feat: support timestamp type
Browse files Browse the repository at this point in the history
  • Loading branch information
tkrs committed Oct 4, 2021
1 parent 1d9bf2e commit a657928
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 37 deletions.
22 changes: 17 additions & 5 deletions modules/core/src/main/scala/mess/Fmt.scala
Expand Up @@ -3,6 +3,7 @@ package mess
import java.math.BigInteger
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets.UTF_8
import java.time.Instant

import mess.internal.ScalaVersionSpecifics._
import org.msgpack.core.MessagePack
Expand Down Expand Up @@ -164,6 +165,13 @@ object Fmt {
}
}

final case class MTimestamp(timestamp: Instant) extends Fmt {
def isNil: Boolean = false

def pack(buffer: MessagePacker): Unit =
buffer.packTimestamp(timestamp)
}

final case class MExtension(typ: Byte, size: Int, value: Array[Byte]) extends Fmt {
def isNil: Boolean = false

Expand Down Expand Up @@ -426,10 +434,13 @@ object Fmt {
val size = buffer.unpackBinaryHeader()
Fmt.MBin(buffer.readPayload(size))
case MF.EXT8 | MF.EXT16 | MF.EXT32 | MF.FIXEXT1 | MF.FIXEXT2 | MF.FIXEXT4 | MF.FIXEXT8 | MF.FIXEXT16 =>
val ext = buffer.unpackExtensionTypeHeader()
val bytes = Array.ofDim[Byte](ext.getLength)
buffer.readPayload(bytes)
Fmt.MExtension(ext.getType, ext.getLength, bytes)
val ext = buffer.unpackExtensionTypeHeader()
if (ext.isTimestampType()) MTimestamp(buffer.unpackTimestamp())
else {
val bytes = Array.ofDim[Byte](ext.getLength)
buffer.readPayload(bytes)
Fmt.MExtension(ext.getType, ext.getLength, bytes)
}
case MF.NEVER_USED =>
sys.error("cannot unpack format: NEVER USED")
}
Expand All @@ -451,6 +462,7 @@ object Fmt {
def fromLsit(value: List[Fmt]): Fmt = MArray(value.toVector)
def fromSeq(value: Seq[Fmt]): Fmt = MArray(value.toVector)
def fromValues(value: Fmt*): Fmt = MArray(value.toVector)
def fromBytes(x: Array[Byte]): Fmt = MBin(x)
def fromBytes(value: Array[Byte]): Fmt = MBin(value)
def fromInstant(value: Instant): Fmt = MTimestamp(value)
def extension(typ: Byte, size: Int, bytes: Array[Byte]): Fmt = MExtension(typ, size, bytes)
}
6 changes: 6 additions & 0 deletions modules/core/src/main/scala/mess/codec/Decoder.scala
@@ -1,6 +1,7 @@
package mess.codec

import java.nio.charset.StandardCharsets.UTF_8
import java.time.Instant

import mess.Fmt
import mess.internal.ScalaVersionSpecifics._
Expand Down Expand Up @@ -102,6 +103,11 @@ private[codec] trait Decoder1 {
case m => Left(TypeMismatchError("String", m))
}

implicit val decodeTimestamp: Decoder[Instant] = {
case Fmt.MTimestamp(a) => Right(a)
case m => Left(TypeMismatchError("Timestamp", m))
}

implicit def decodeSome[A](implicit decodeA: Decoder[A]): Decoder[Some[A]] =
m =>
decodeA(m) match {
Expand Down
3 changes: 3 additions & 0 deletions modules/core/src/main/scala/mess/codec/Encoder.scala
@@ -1,5 +1,7 @@
package mess.codec

import java.time.Instant

import mess.Fmt

import scala.annotation.tailrec
Expand Down Expand Up @@ -49,6 +51,7 @@ private[codec] trait Encoder1 {
implicit final val encodeFloat: Encoder[Float] = Fmt.fromFloat(_)
implicit final val encodeChar: Encoder[Char] = a => Fmt.fromString(a.toString)
implicit final val encodeString: Encoder[String] = Fmt.fromString(_)
implicit final val encodeTimestamp: Encoder[Instant] = Fmt.fromInstant(_)

implicit final def encodeSymbol[K <: Symbol]: Encoder[K] = a => Fmt.fromString(a.name)

Expand Down
38 changes: 6 additions & 32 deletions modules/core/src/test/scala/com/github/tkrs/CodecChecker.scala
Expand Up @@ -5,10 +5,8 @@ import java.time.Instant
import mess._
import mess.codec.Decoder
import mess.codec.Encoder
import mess.codec.TypeMismatchError
import mess.codec.semiauto._
import org.msgpack.core.MessagePack
import org.msgpack.core.MessagePack.Code
import org.scalacheck.Arbitrary
import org.scalacheck.Gen
import org.scalacheck.Prop
Expand Down Expand Up @@ -37,6 +35,11 @@ class CodecChecker extends MsgpackHelper {
implicit val arbFix: Arbitrary[User[List]] = Arbitrary(genFix)
}

implicit val arbInstant: Arbitrary[Instant] = Arbitrary(for {
seconds <- Gen.choose(0L, System.currentTimeMillis() / 1000)
nanos <- Gen.choose(0L, 999000000L)
} yield Instant.ofEpochSecond(seconds, nanos))

def roundTrip[A: Arbitrary: Shrink](implicit encode: Encoder[A], decode: Decoder[A]) =
Prop.forAll { (a: A) =>
val ast = encode(a)
Expand All @@ -59,6 +62,7 @@ class CodecChecker extends MsgpackHelper {
test("Double")(roundTrip[Double])
test("String")(roundTrip[String])
test("BigInt")(roundTrip[BigInt])
test("Timestamp")(roundTrip[Instant])
test("Map[String, Int]")(roundTrip[Map[String, Int]])
test("Map[String, Long]")(roundTrip[Map[String, Long]])
test("Map[String, Float]")(roundTrip[Map[String, Float]])
Expand Down Expand Up @@ -115,36 +119,6 @@ class CodecChecker extends MsgpackHelper {
test("Tuple22")(roundTrip[Tuple22[Int, Int, Long, String, BigInt, Double, Float, Long, Int, Byte, Short, Boolean, Int, String, List[String], Int, Int, Int, Int, Int, Int, Int]])
// format: on

implicit val arbInstant: Arbitrary[Instant] = Arbitrary(for {
seconds <- Gen.choose(0L, System.currentTimeMillis() / 1000)
nanos <- Gen.choose(0L, 999000000L)
} yield Instant.ofEpochSecond(seconds, nanos))

implicit val encodeInstantAsFluentdEventTime: Encoder[Instant] = a => {
val s = a.getEpochSecond
val n = a.getNano.toLong

val f: (Long, Long) => Byte = (v, m) => (v >>> m).toByte
val fs: Long => Byte = f(s, _)
val fn: Long => Byte = f(n, _)

val arr = Array(fs(24L), fs(16L), fs(8L), fs(0L), fn(24L), fn(16L), fn(8L), fn(0L))
Fmt.extension(Code.EXT8, 8, arr)
}

implicit val decodeInstantAsFluentdEventTime: Decoder[Instant] = {
case Fmt.MExtension(Code.EXT8, _, arr) =>
val f: (Int, Long) => Long = (i, j) => (arr(i) & 0xff).toLong << j

val seconds = f(0, 24L) | f(1, 16L) | f(2, 8L) | f(3, 0L)
val nanos = f(4, 24L) | f(5, 16L) | f(6, 8L) | f(7, 0L)
Right(Instant.ofEpochSecond(seconds, nanos))
case a =>
Left(TypeMismatchError("Instant", a))
}

test("Instant(Extension)")(roundTrip[Instant])

case class Hoge(a: Option[Int])

object Hoge {
Expand Down

0 comments on commit a657928

Please sign in to comment.