Skip to content

Commit

Permalink
Throw an exception when generic types in pekko-http unmarshaller macr…
Browse files Browse the repository at this point in the history
…os are conflicting
  • Loading branch information
agrodowski committed Nov 8, 2023
1 parent 187229c commit 6be55e2
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 20 deletions.
4 changes: 2 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ lazy val baseSettings = Seq(
organizationName := "Iterators",
organizationHomepage := Some(url("https://iterato.rs")),
homepage := Some(url("https://github.com/theiterators/kebs")),
scalacOptions ++= Seq("-deprecation", "-explain", "-unchecked", "-feature", "-encoding", "utf8")
scalacOptions ++= Seq("-deprecation", "-unchecked", "-feature", "-encoding", "utf8")
)

lazy val commonMacroSettings = baseSettings ++ Seq(
Expand Down Expand Up @@ -342,7 +342,7 @@ lazy val slickSupport = project

lazy val doobieSupport = project
.in(file("doobie"))
.dependsOn(instances, enumeratumSupport, opaque.jvm % "test -> test")
.dependsOn(instances, enumeratumSupport, enumSupport, opaque.jvm % "test -> test")
.settings(doobieSettings: _*)
.settings(publishSettings: _*)
.settings(
Expand Down
4 changes: 3 additions & 1 deletion doobie/src/test/scala-3/ComplexTypesTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import pl.iterators.kebs.enums.given
import pl.iterators.kebs.instances.KebsInstances.given
import pl.iterators.kebs.opaque.Opaque

import pl.iterators.kebs.enums.{KebsEnum, KebsValueEnum}

opaque type Name = String
object Name extends Opaque[Name, String]

Expand All @@ -19,7 +21,7 @@ enum EyeColor {
}
case class Person(name: Name, eyeColor: EyeColor, preferredCurrency: Currency, relatives: List[Name], eyeballsInTheJar: Array[EyeColor])

class ComplexTypesTests extends AnyFunSuite with Matchers {
class ComplexTypesTests extends AnyFunSuite with Matchers with KebsEnum {
test("Put & Get exist") {
"implicitly[Get[Name]]" should compile
"implicitly[Put[Name]]" should compile
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ trait EnumUnmarshallers {

trait ValueEnumUnmarshallers {
final def valueEnumUnmarshaller[V, E <: { def value: V }](`enum`: ValueEnumLike[V, E]): Unmarshaller[V, E] = Unmarshaller { _ =>v =>
`enum`.withValueOpt(v) match {
`enum`.values.find(e => e.value == v && e.value.getClass == v.getClass) match {
case Some(enumEntry) => FastFuture.successful(enumEntry)
case None =>
FastFuture.failed(new IllegalArgumentException(s"""Invalid value '$v'. Expected one of: ${`enum`.valuesToEntriesMap.keysIterator
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package pl.iterators.kebs.unmarshallers.enums

import org.apache.pekko.http.scaladsl.unmarshalling.PredefinedFromStringUnmarshallers._
import org.apache.pekko.http.scaladsl.unmarshalling.PredefinedFromStringUnmarshallers.*
import org.apache.pekko.http.scaladsl.unmarshalling.{FromStringUnmarshaller, Unmarshaller}
import org.apache.pekko.http.scaladsl.util.FastFuture
import pl.iterators.kebs.enums.{EnumLike, ValueEnumLike}

import pl.iterators.kebs.enums.{ValueEnumLike, EnumLike}

import scala.reflect.Enum
import scala.reflect.ClassTag
import reflect.Selectable._

import reflect.Selectable.reflectiveSelectable
import io.github.gaeljw.typetrees.TypeTree
import io.github.gaeljw.typetrees.TypeTreeTag

trait EnumUnmarshallers {
final def enumUnmarshaller[E](using e: EnumLike[E]): FromStringUnmarshaller[E] = org.apache.pekko.http.scaladsl.unmarshalling.Unmarshaller { _ => name =>
Expand All @@ -25,27 +25,34 @@ trait EnumUnmarshallers {
}

trait ValueEnumUnmarshallers extends EnumUnmarshallers {
final def valueEnumUnmarshaller[V, E <: {def value: V}](using `enum`: ValueEnumLike[V, E], cls: ClassTag[V]): Unmarshaller[V, E] = Unmarshaller { _ =>
v =>
`enum`.values.find(e => e.value == v) match {
case Some(enumEntry) => FastFuture.successful(enumEntry)
case None =>
FastFuture.failed(new IllegalArgumentException(s"""Invalid value '$v'. Expected one of: ${`enum`.values.map(_.value).mkString(", ")}"""))
}
final def valueEnumUnmarshaller[V, E <: {def value: V}](using `enum`: ValueEnumLike[V, E]): Unmarshaller[V, E] =
Unmarshaller { _ =>
v =>
`enum`.values.find(e => e.value == v && e.value.getClass == v.getClass) match {
case Some(enumEntry) =>
FastFuture.successful(enumEntry)
case _ =>
`enum`.values.find(e => e.value == v) match {
case Some(_) =>
FastFuture.failed(new IllegalArgumentException(s"""Invalid value '$v'"""))
case None =>
FastFuture.failed(new IllegalArgumentException(s"""Invalid value '$v'. Expected one of: ${`enum`.values.map(_.value).mkString(", ")}"""))
}
}
}
}

trait LowPriorityImplicits extends ValueEnumUnmarshallers {
given kebsValueEnumUnmarshaller[V, E <: {def value: V}](using `enum`: ValueEnumLike[V, E], cls: ClassTag[V]): Unmarshaller[V, E] =
given kebsValueEnumUnmarshaller[V, E <: {def value: V}](using `enum`: ValueEnumLike[V, E], cls: ClassTag[V], tp: TypeTree[E]): Unmarshaller[V, E] =
valueEnumUnmarshaller
}

trait HighPriorityImplicits extends LowPriorityImplicits {
given kebsIntValueEnumFromStringUnmarshaller[E <: {def value: Int}](using ev: ValueEnumLike[Int, E]): FromStringUnmarshaller[E] =
intFromStringUnmarshaller andThen valueEnumUnmarshaller

// given kebsLongValueEnumFromStringUnmarshaller[E <: {def value: Long}](using ev: ValueEnumLike[Long, E]): FromStringUnmarshaller[E] =
// longFromStringUnmarshaller andThen valueEnumUnmarshaller
given kebsLongValueEnumFromStringUnmarshaller[E <: {def value: Long}](using ev: ValueEnumLike[Long, E]): FromStringUnmarshaller[E] =
longFromStringUnmarshaller andThen valueEnumUnmarshaller

given kebsShortValueEnumFromStringUnmarshaller[E <: {def value: Short}](using ev: ValueEnumLike[Short, E]): FromStringUnmarshaller[E] =
shortFromStringUnmarshaller andThen valueEnumUnmarshaller
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import pl.iterators.kebs.enums.{KebsEnum, KebsValueEnum}
import pl.iterators.kebs.instances.net.URIString
import pl.iterators.kebs.instances.time.{DayOfWeekInt, YearMonthString}
import pl.iterators.kebs.unmarshallers.enums.KebsEnumUnmarshallers
import pl.iterators.kebs.enums.ValueEnumLike
import org.apache.pekko.http.scaladsl.unmarshalling.{FromStringUnmarshaller, Unmarshaller}

import java.time.{DayOfWeek, YearMonth}

Expand Down Expand Up @@ -70,7 +72,8 @@ class PekkoHttpUnmarshallersTests
}

test("Unmarshalling value enums is type-safe") {
"""Unmarshal(1L).to[LibraryItem]""" shouldNot typeCheck
Unmarshal(1L).to[LibraryItem].failed.futureValue shouldBe a[IllegalArgumentException] // test w nowej wersji, gdzie rzucany jest IllegalArgumentException
// """Unmarshal(1L).to[LibraryItem]""" shouldNot typeCheck // test w poprzedniej wersji, gdzie żądany jest type error
}

test("Unmarshal from string") {
Expand Down

0 comments on commit 6be55e2

Please sign in to comment.