Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kebs 2.0 package cleanup #380

Merged
merged 24 commits into from
Aug 7, 2024
Merged
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
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
package pl.iterators.kebs.akkahttp.matchers

import akka.http.scaladsl.server.{PathMatcher1, PathMatchers}
import enumeratum.{Enum, EnumEntry}
import pl.iterators.kebs.core.enums.{EnumLike, ValueEnumLike, ValueEnumLikeEntry}
import pl.iterators.kebs.core.instances.InstanceConverter
import pl.iterators.kebs.core.macros.ValueClassLike

trait KebsMatchers extends PathMatchers {

trait KebsAkkaHttpMatchers extends PathMatchers {
implicit class SegmentIsomorphism[U](segment: PathMatcher1[U]) {
def as[T](implicit rep: ValueClassLike[T, U]): PathMatcher1[T] = segment.map(rep.apply)
def as[T](implicit rep: ValueClassLike[T, U]): PathMatcher1[T] = segment.map(rep.apply)
def asValueEnum[T <: ValueEnumLikeEntry[U]](implicit e: ValueEnumLike[U, T]): PathMatcher1[T] = segment.map(e.withValue)
}

implicit class SegmentConversion[Source](segment: PathMatcher1[Source]) {
def to[Type](implicit ico: InstanceConverter[Type, Source]): PathMatcher1[Type] = segment.map(ico.decode)
}

object EnumSegment {
def as[T <: EnumEntry: Enum]: PathMatcher1[T] = {
val enumCompanion = implicitly[Enum[T]]
Segment.map(enumCompanion.withNameInsensitive)
}
implicit class SegmentEnumIsomorphism[U](segment: PathMatcher1[String]) {
def asEnum[T](implicit e: EnumLike[T]): PathMatcher1[T] = segment.map(e.withNameIgnoreCase)
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package pl.iterators.kebs.akkahttp

package object matchers extends KebsMatchers
package object matchers extends KebsAkkaHttpMatchers
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package pl.iterators.kebs.akkahttp.unmarshallers.enums
package pl.iterators.kebs.akkahttp.unmarshallers

import akka.http.scaladsl.unmarshalling.PredefinedFromStringUnmarshallers._
import akka.http.scaladsl.unmarshalling.{FromStringUnmarshaller, Unmarshaller}
import pl.iterators.kebs.core.instances.InstanceConverter
import pl.iterators.kebs.core.macros.ValueClassLike
import akka.http.scaladsl.util.FastFuture
import akka.http.scaladsl.unmarshalling.PredefinedFromStringUnmarshallers._
import pl.iterators.kebs.core.enums.{EnumLike, ValueEnumLike, ValueEnumLikeEntry}

trait EnumUnmarshallers {
final def enumUnmarshaller[E](`enum`: EnumLike[E]): FromStringUnmarshaller[E] = Unmarshaller { _ => name =>
trait KebsAkkaHttpEnumUnmarshallers {
private final def enumUnmarshaller[E](`enum`: EnumLike[E]): FromStringUnmarshaller[E] = Unmarshaller { _ => name =>
`enum`.withNameInsensitiveOption(name) match {
case Some(enumEntry) => FastFuture.successful(enumEntry)
case None =>
Expand All @@ -21,7 +23,7 @@ trait EnumUnmarshallers {
enumUnmarshaller(ev)
}

trait ValueEnumUnmarshallers {
trait KebsAkkaHttpValueEnumUnmarshallers {
final def valueEnumUnmarshaller[V, E <: ValueEnumLikeEntry[V]](`enum`: ValueEnumLike[V, E]): Unmarshaller[V, E] = Unmarshaller {
_ => v =>
`enum`.withValueOption(v) match {
Expand Down Expand Up @@ -55,4 +57,23 @@ trait ValueEnumUnmarshallers {
byteFromStringUnmarshaller andThen valueEnumUnmarshaller(ev)
}

trait KebsEnumUnmarshallers extends EnumUnmarshallers with ValueEnumUnmarshallers {}
trait KebsAkkaHttpUnmarshallers extends KebsAkkaHttpEnumUnmarshallers with KebsAkkaHttpValueEnumUnmarshallers {
implicit def kebsUnmarshaller[A, B](implicit rep: ValueClassLike[B, A]): Unmarshaller[A, B] =
Unmarshaller.strict[A, B](rep.apply)
@inline
implicit def kebsFromStringUnmarshaller[A, B](implicit
rep: ValueClassLike[B, A],
fsu: FromStringUnmarshaller[A]
): FromStringUnmarshaller[B] =
fsu andThen kebsUnmarshaller(rep)

implicit def kebsInstancesUnmarshaller[A, B](implicit ico: InstanceConverter[B, A]): Unmarshaller[A, B] =
Unmarshaller.strict[A, B](ico.decode)
@inline
implicit def kebsInstancesFromStringUnmarshaller[A, B](implicit
ico: InstanceConverter[B, A],
fsu: FromStringUnmarshaller[A]
): FromStringUnmarshaller[B] =
fsu andThen kebsInstancesUnmarshaller(ico)

}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package pl.iterators.kebs.akkahttp

package object unmarshallers extends KebsUnmarshallers
package object unmarshallers extends KebsAkkaHttpUnmarshallers
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import akka.http.scaladsl.testkit.ScalatestRouteTest
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers
import pl.iterators.kebs.akkahttp.domain.Domain.Greeting
import pl.iterators.kebs.akkahttp.domain.Domain._
import pl.iterators.kebs.core.macros.CaseClass1ToValueClass
import pl.iterators.kebs.enumeratum.{KebsEnumeratum, KebsValueEnumeratum}
import pl.iterators.kebs.instances.net.URIString
import pl.iterators.kebs.instances.time.{DayOfWeekInt, ZonedDateTimeString}
import pl.iterators.kebs.instances.time.mixins.InstantEpochMilliLong
Expand All @@ -23,6 +24,9 @@ class AkkaHttpMatchersTests
with ZonedDateTimeString
with DayOfWeekInt
with InstantEpochMilliLong
with KebsEnumeratum
with KebsValueEnumeratum
with CaseClass1ToValueClass
with URIString {

test("No ValueClassLike implicits derived") {
Expand Down Expand Up @@ -90,14 +94,32 @@ class AkkaHttpMatchersTests
}

test("Extract String as Enum") {
val testRoute = path("test" / EnumSegment.as[Greeting]) { greeting =>
val testRoute = path("test" / Segment.asEnum[Greeting]) { greeting =>
complete(greeting.toString)
}
Get("/test/hello") ~> testRoute ~> check {
responseAs[String] shouldEqual "Hello"
}
}

test("Extract String as value class") {
val testRoute = path("test" / Segment.as[S]) { item =>
complete(item.toString)
}
Get("/test/check") ~> testRoute ~> check {
responseAs[String] shouldEqual "S(check)"
}
}

test("Extract Int as ValueEnum") {
val testRoute = path("test" / IntNumber.asValueEnum[LibraryItem]) { item =>
complete(item.toString)
}
Get("/test/1") ~> testRoute ~> check {
responseAs[String] shouldEqual "Book"
}
}

test("Extract String to URI as tagged URI") {
val testRoute = path("test" / Segment.to[URI].as[TestTaggedUri]) { id =>
complete(id.toString)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import pl.iterators.kebs.akkahttp.domain.Domain._
import pl.iterators.kebs.instances.net.URIString
import pl.iterators.kebs.instances.time.{DayOfWeekInt, YearMonthString}
import pl.iterators.kebs.enumeratum.{KebsEnumeratum, KebsValueEnumeratum}
import pl.iterators.kebs.akkahttp.unmarshallers.enums.KebsEnumUnmarshallers
import pl.iterators.kebs.core.macros.CaseClass1ToValueClass

import java.time.{DayOfWeek, YearMonth}
Expand All @@ -23,8 +22,7 @@ class AkkaHttpUnmarshallersTests
with ScalatestRouteTest
with ScalaFutures
with Directives
with KebsUnmarshallers
with KebsEnumUnmarshallers
with KebsAkkaHttpUnmarshallers
with URIString
with YearMonthString
with DayOfWeekInt
Expand Down
40 changes: 8 additions & 32 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,6 @@ def disableScala(v: List[String]) =
)

def optional(dependency: ModuleID) = dependency % "provided"
def sv[A](scalaVersion: String, scala2_12Version: => A, scala2_13Version: => A) =
CrossVersion.partialVersion(scalaVersion) match {
case Some((2, 13)) => scala2_13Version
case Some((2, 12)) => scala2_12Version
case _ =>
throw new IllegalArgumentException(s"Unsupported Scala version $scalaVersion")
}

def paradiseFlag(scalaVersion: String): Seq[String] =
if (scalaVersion == scala_3)
Expand All @@ -118,11 +111,10 @@ val circeParser = "io.circe" %% "circe-parser" % circeV

val jsonschema = "com.github.andyglow" %% "scala-jsonschema" % "0.7.11"

val scalacheck = "org.scalacheck" %% "scalacheck" % "1.18.0" % "test"
val scalacheck = "org.scalacheck" %% "scalacheck" % "1.18.0"

val scalacheckMagnolify = "com.spotify" % "magnolify-scalacheck" % "0.7.3"
val scalacheckDerived = "io.github.martinhh" %% "scalacheck-derived" % "0.4.2"
val scalacheckEnumeratum = "com.beachape" %% "enumeratum-scalacheck" % "1.7.4"

val enumeratumVersion = "1.7.4"
val enumeratumPlayJsonVersion = "1.8.1"
Expand Down Expand Up @@ -206,16 +198,14 @@ lazy val enumeratumSettings = commonMacroSettings ++ Seq(
scalacOptions ++= paradiseFlag(scalaVersion.value)
)

lazy val sprayJsonMacroSettings = commonMacroSettings ++ Seq(
libraryDependencies += sprayJson.cross(CrossVersion.for3Use2_13)
)

lazy val sprayJsonSettings = commonSettings ++ Seq(
libraryDependencies += sprayJson.cross(CrossVersion.for3Use2_13),
libraryDependencies += optionalEnumeratum
)

lazy val playJsonSettings = commonSettings ++ Seq(
libraryDependencies += playJson
libraryDependencies += playJson,
libraryDependencies += (enumeratum % "test")
)

lazy val circeSettings = commonSettings ++ Seq(
Expand Down Expand Up @@ -264,7 +254,7 @@ lazy val jsonschemaSettings = commonSettings ++ Seq(

lazy val scalacheckSettings = commonSettings ++ Seq(
libraryDependencies += scalacheck,
libraryDependencies += scalacheckEnumeratum
libraryDependencies += (enumeratum % "test"),
) ++ Seq(
libraryDependencies ++= (if (scalaVersion.value.startsWith("3")) Seq(scalacheckDerived)
else Nil)
Expand Down Expand Up @@ -330,22 +320,9 @@ lazy val doobieSupport = project
crossScalaVersions := supportedScalaVersions
)

lazy val sprayJsonMacros = project
.in(file("spray-json-macros"))
.dependsOn(core.jvm)
.settings(sprayJsonMacroSettings *)
.settings(publishSettings *)
.settings(disableScala(List("3")))
.settings(
name := "spray-json-macros",
description := "Automatic generation of Spray json formats for case-classes - macros",
moduleName := "kebs-spray-json-macros",
crossScalaVersions := supportedScalaVersions
)

lazy val sprayJsonSupport = project
.in(file("spray-json"))
.dependsOn(sprayJsonMacros, enumeratumSupport, instances % "test -> test")
.dependsOn(enumeratumSupport, instances % "test -> test")
.settings(sprayJsonSettings *)
.settings(publishSettings *)
.settings(disableScala(List("3")))
Expand All @@ -358,7 +335,7 @@ lazy val sprayJsonSupport = project

lazy val playJsonSupport = project
.in(file("play-json"))
.dependsOn(core.jvm, instances % "test -> test")
.dependsOn(core.jvm, enumeratumSupport, enumSupport, instances % "test -> test")
.settings(playJsonSettings *)
.settings(publishSettings *)
.settings(
Expand Down Expand Up @@ -451,7 +428,7 @@ lazy val jsonschemaSupport = project

lazy val scalacheckSupport = project
.in(file("scalacheck"))
.dependsOn(core.jvm, opaque.jvm % "test -> test")
.dependsOn(core.jvm, enumSupport, opaque.jvm % "test -> test")
.settings(scalacheckSettings *)
.settings(publishSettings *)
.settings(
Expand Down Expand Up @@ -564,7 +541,6 @@ lazy val kebs = project
core.js,
slickSupport,
doobieSupport,
sprayJsonMacros,
sprayJsonSupport,
playJsonSupport,
circeSupport,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,13 @@ trait KebsCirce extends AutoDerivation {

implicit def instanceConverterDecoder[T, A](implicit rep: InstanceConverter[T, A], decoder: Decoder[A]): Decoder[T] =
decoder.emap(obj => Try(rep.decode(obj)).toEither.left.map(_.getMessage))
}

object KebsCirce {

trait Snakified extends KebsCirce {
trait KebsCirceSnakified extends KebsCirce {
implicit def genericSnakifiedDecoder[T <: Product]: Decoder[T] = macro KebsCirceMacros.SnakifyVariant.materializeDecoder[T]
implicit def genericSnakifiedEncoder[T <: Product]: Encoder[T] = macro KebsCirceMacros.SnakifyVariant.materializeEncoder[T]
}

trait Capitalized extends KebsCirce {
trait KebsCirceCapitalized extends KebsCirce {
implicit def genericCapitalizedDecoder[T <: Product]: Decoder[T] = macro KebsCirceMacros.CapitalizedCamelCase.materializeDecoder[T]
implicit def genericCapitalizedEncoder[T <: Product]: Encoder[T] = macro KebsCirceMacros.CapitalizedCamelCase.materializeEncoder[T]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,12 @@ trait KebsCirce extends KebsAutoDerivation {

inline implicit def instanceConverterDecoder[T, A](using rep: InstanceConverter[T, A], decoder: Decoder[A]): Decoder[T] =
decoder.emap(obj => Try(rep.decode(obj)).toEither.left.map(_.getMessage))
}

object KebsCirce {

trait Snakified extends KebsCirce {
trait KebsCirceSnakified extends KebsCirce {
override implicit val configuration: Configuration = Configuration.default.withSnakeCaseMemberNames
}

trait Capitalized extends KebsCirce {
trait KebsCirceCapitalized extends KebsCirce {
override implicit val configuration: Configuration = Configuration.default.withPascalCaseMemberNames
}
}
Loading
Loading