Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ ThisBuild / organizationName := "mlopes"

lazy val wen = project
.in(file("."))
.aggregate(core, cats, circe)
.aggregate(core, cats, circe, avro4s)
.settings(name := "Wen Root")
.settings(
publish := {},
Expand Down Expand Up @@ -42,6 +42,16 @@ lazy val circe = project
defaultConfig
)

lazy val avro4s = project
.in(file("modules/avro4s"))
.dependsOn(core % "compile->compile;test->test", cats)
.settings(moduleName := "wen-avro4s", name := "Wen Avro4s", description := "Avro4s instances for Wen")
.settings(
libraryDependencies ++= avro4sDependencies,
libraryDependencies ++= testDependencies,
defaultConfig
)

lazy val defaultConfig = Seq(
scalacOptions := appScalacOptions,
compile in Compile := (compile in Compile).dependsOn(dependencyUpdates).value,
Expand Down
40 changes: 40 additions & 0 deletions modules/avro4s/src/main/scala/wen/avro4s/package.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package wen

import java.time.Instant

import cats.implicits._
import com.sksamuel.avro4s.{Encoder, SchemaFor}
import com.sksamuel.avro4s.Encoder._
import org.apache.avro.Schema
import wen.datetime._
import wen.instances.iso._
import wen.types._

package object avro4s {
implicit object DateTimeSchemaFor extends SchemaFor[DateTime] {
override def schema: Schema = Schema.create(Schema.Type.LONG)
}

implicit val DateTimeEncoder: Encoder[DateTime] = {
LongEncoder.comap[DateTime] { x =>
val dateTimeAsString: String = f"${x.show}.${x.time.millisecond.millisecond.value}%03dZ"
Instant.parse(dateTimeAsString).toEpochMilli
}
}

implicit object TimeSchemaFor extends SchemaFor[Time] {
override def schema: Schema = Schema.create(Schema.Type.INT)
}

implicit val TimeEncoder: Encoder[Time] = {
IntEncoder.comap[Time] { x =>
x match {
case Time(Hour(h), Minute(m), Second(s), Millisecond(ms)) =>
(h.value * 60 * 60 * 1000) +
(m.value * 60 * 1000) +
(s.value * 1000) +
ms.value
}
}
}
}
36 changes: 36 additions & 0 deletions modules/avro4s/src/test/scala/wen/avro4s/Avro4sSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package wen.avro4s

import java.time.{Instant, LocalDateTime, LocalTime, ZoneOffset}

import com.sksamuel.avro4s.{AvroSchema, Encoder, ImmutableRecord}
import org.scalactic.TypeCheckedTripleEquals
import org.scalatest.{Matchers, WordSpec}
import org.scalatestplus.scalacheck.ScalaCheckDrivenPropertyChecks
import wen.datetime.{DateTime, Time}
import wen.test.Generators._

class Avro4sSpec extends WordSpec with Matchers with TypeCheckedTripleEquals with ScalaCheckDrivenPropertyChecks {

"Avro4s Encoders" should {
"encode a Time" in forAll (timeOfDayInMilliseconds) { millisecondsOfDay: Int =>
case class Foo(s: Time)

val schema = AvroSchema[Foo]
val localTime = LocalTime.ofNanoOfDay(millisecondsOfDay * 1000000L)
val time = Time(localTime)

Encoder[Foo].encode(Foo(time), schema) shouldBe ImmutableRecord(schema, Vector(java.lang.Integer.valueOf(millisecondsOfDay)))
}

"encode a DateTime" in forAll (epochLong) { timestamp: Long =>
case class Foo(s: DateTime)

val schema = AvroSchema[Foo]
val localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneOffset.UTC)
val dateTime = DateTime(localDateTime)

Encoder[Foo].encode(Foo(dateTime), schema) shouldBe ImmutableRecord(schema, Vector(java.lang.Long.valueOf(timestamp)))
}
}

}
6 changes: 6 additions & 0 deletions modules/core/src/test/scala/wen/test/Generators.scala
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,10 @@ object Generators {
val yearWithDefaultEpochGen: Gen[Year] =
Gen.posNum[Int].map(i => Year(refineV[NumericYearConstraint].unsafeFrom(i)))

val epochLong: Gen[Long] =
Gen.choose(0, Long.MaxValue)

val timeOfDayInMilliseconds: Gen[Int] =
Gen.choose(0, 84239999)

}
5 changes: 5 additions & 0 deletions project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ object Dependencies {
val refinedVersion = "0.9.5"
val catsVersion = "1.6.0"
val circeVersion = "0.11.1"
val avro4sVersion = "2.0.4"


lazy val testDependencies = Seq(
Expand All @@ -20,6 +21,10 @@ object Dependencies {
"org.typelevel" %% "cats-core" % catsVersion
)

lazy val avro4sDependencies = Seq(
"com.sksamuel.avro4s" %% "avro4s-core" % avro4sVersion
)

lazy val refinedDependencies = Seq(
"eu.timepit" %% "refined" % refinedVersion
)
Expand Down