diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5bcf0f04..1f2012fa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - scala: [2.12.18, 2.13.12, 3.3.0] + scala: [2.13.11, 3.3.3] java: [temurin@17] runs-on: ${{ matrix.os }} steps: diff --git a/.mergify.yml b/.mergify.yml index f95f996c..825656fc 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -11,8 +11,7 @@ pull_request_rules: conditions: - base=master - author=scala-steward - - status-success=Build and Test (ubuntu-latest, 2.12.18, temurin@17) - - status-success=Build and Test (ubuntu-latest, 2.13.12, temurin@17) + - status-success=Build and Test (ubuntu-latest, 2.13.11, temurin@17) - status-success=Build and Test (ubuntu-latest, 3.3.0, temurin@17) actions: merge: diff --git a/README.md b/README.md index 20e147fb..7caea249 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ A library maintained by [Iterators](https://www.iteratorshq.com). * [JsonSchema support](#jsonschema-support) * [Scalacheck support](#scalacheck-support) * [Kebs for IntelliJ](#kebs-for-intellij) +* [Kebs 2.0 migration guide](#kebs-20-migration-guide) ### Why? @@ -171,7 +172,7 @@ class People(tag: Tag) extends Table[Person](tag, "people") { If you prefer to **mix in trait** instead of import (for example you're using a custom driver like `slick-pg`), you can do it as well: ```scala -import pl.iterators.kebs.Kebs +import pl.iterators.kebs.slick.Kebs object MyPostgresProfile extends ExPostgresDriver with PgArraySupport { override val api: API = new API {} trait API extends super.API with ArrayImplicits with Kebs @@ -338,8 +339,8 @@ class People(tag: Tag) extends Table[Person](tag, "people") { ```scala -import pl.iterators.kebs._ -import enums._ +import pl.iterators.kebs.slick._ +import pl.iterators.kebs.slick.enums._ class People(tag: Tag) extends Table[Person](tag, "people") { @@ -361,8 +362,8 @@ If you import `enums.lowercase._` or `enums.uppercase._` then it'll save enum na Of course, enums also work with traits: ```scala -import pl.iterators.kebs.Kebs -import pl.iterators.kebs.enums.KebsEnums +import pl.iterators.kebs.slick.Kebs +import pl.iterators.kebs.slick.enums.KebsEnums object MyPostgresProfile extends ExPostgresDriver { override val api: API = new API {} @@ -389,12 +390,12 @@ import MyPostgresProfile.api._ kebs-doobie works similarly to [kebs-slick](#--kebs-generates-slick-mappers-for-your-case-class-wrappers-kebs-slick). It provides doobie's `Meta` instances for: -* Instances of `CaseClass1Rep` (value classes, tagged types, opaque types) +* Instances of `ValueClassLike` (value classes, tagged types, opaque types) * Instances of `InstanceConverter` * Enumeratum for Scala 2 * Native enums for Scala 3 -To make the magic happen, do `import pl.iterators.kebs._` and `import pl.iterators.kebs.enums._` (or `import pl.iterators.kebs.enums.uppercase._` or `import pl.iterators.kebs.enums.lowercase._`). +To make the magic happen, do `import pl.iterators.doobie.kebs._` and `import pl.iterators.kebs.doobie.enums._` (or `import pl.iterators.kebs.doobie.enums.uppercase._` or `import pl.iterators.kebs.doobie.enums.lowercase._`). #### - kebs eliminates spray-json induced boilerplate (kebs-spray-json) @@ -519,18 +520,6 @@ test("work with nested single field objects") { } ``` -* mix-in `KebsSpray.NonFlat` if you want _flat_ format to become globally turned off for a protocol -```scala -object KebsProtocol extends DefaultJsonProtocol with KebsSpray.NoFlat -``` - -* use `noflat` annotation on selected case-classes (thanks to @dbronecki) -```scala -case class Book(name: String, chapters: List[Chapter]) -@noflat case class Chapter(name: String) -``` - - Often you have to deal with convention to have **`snake-case` fields in JSON**. That's something `kebs-spray-json` can do for you as well @@ -662,10 +651,7 @@ object AfterKebs { } } ``` -If you want to disable flat formats, you can mix-in `KebsCirce.NoFlat`: -```scala -object KebsProtocol extends KebsCirce with KebsCirce.NoFlat -``` + You can also support snake-case fields in JSON: ```scala object KebsProtocol extends KebsCirce with KebsCirce.Snakified @@ -677,8 +663,7 @@ And capitalized: ``` **NOTE for Scala 3 version of kebs-circe**: -1. As of today, there is no support for the @noflat annotation - using it will have no effect. -2. If you're using recursive types - due to [this issue](https://github.com/circe/circe/issues/1980) you'll have to add codecs explicitly in the following way: +If you're using recursive types - due to [this issue](https://github.com/circe/circe/issues/1980) you'll have to add codecs explicitly in the following way: ```scala case class R(a: Int, rs: Seq[R]) derives Decoder, Encoder.AsObject ``` @@ -690,11 +675,6 @@ case class R(a: Int, rs: Seq[R]) derives Decoder, Encoder.AsObject import KebsProtocol.{given, _} ``` - as for NoFlat, it should stay the same: - ```scala - object KebsProtocol extends KebsCirce with KebsCirce.NoFlat - import KebsProtocol._ - ``` #### - kebs generates akka-http / pekko-http Unmarshaller (kebs-akka-http / kebs-pekko-http) It makes it very easy to use 1-element case-classes or `enumeratum` enums/value enums in eg. `parameters` directive: @@ -722,8 +702,8 @@ case class Limit(value: Int) extends AnyVal case class PaginationQuery(sortBy: Column, sortOrder: SortOrder, offset: Offset, limit: Limit) -import pl.iterators.kebs.unmarshallers._ -import enums._ +import pl.iterators.kebs.akkahttp.unmarshallers._ +import pl.iterators.kebs.akkahttp.unmarshallers.enums._ val route = get { parameters('sortBy.as[Column], 'order.as[SortOrder] ? (SortOrder.Desc: SortOrder), 'offset.as[Offset] ? Offset(0), 'limit.as[Limit]) @@ -901,13 +881,13 @@ object Tags { } object PositiveIntTag { - implicit val PositiveIntCaseClass1Rep = new CaseClass1Rep[PositiveInt, Int](PositiveInt.apply(_), identity) + implicit val PositiveIntValueClassLike = new ValueClassLike[PositiveInt, Int](PositiveInt.apply(_), identity) } object IdTag { - implicit def IdCaseClass1Rep[A] = new CaseClass1Rep[Id[A], Int](Id.apply(_), identity) + implicit def IdValueClassLike[A] = new ValueClassLike[Id[A], Int](Id.apply(_), identity) } object NameTag { - implicit val NameCaseClass1Rep = new CaseClass1Rep[Name, String](Name.apply(_), identity) + implicit val NameValueClassLike = new ValueClassLike[Name, String](Name.apply(_), identity) } } ``` @@ -946,7 +926,7 @@ There are some conventions that are assumed during generation. * take a single argument * return Either (this is not enforced though - you'll have a compilation error later) -Also, `CaseClass1Rep` is generated for each tag meaning you will get a lot of `kebs` machinery for free eg. spray formats etc. +Also, `ValueClassLike` is generated for each tag meaning you will get a lot of `kebs` machinery for free eg. spray formats etc. ### Opaque types @@ -954,7 +934,7 @@ As an alternative to tagged types, Scala 3 provides [opaque types](https://docs. The principles of opaque types are similar to tagged type. The basic usage of opaque types requires the same amount of boilerplate as tagged types - e.g. you have to write smart constructors, validations and unwrapping mechanisms all by hand. `kebs-opaque` is meant to help with that by generating a handful of methods and providing a -`CaseClass1Rep` for an easy typclass derivation. +`ValueClassLike` for an easy typclass derivation. ```scala import pl.iterators.kebs.opaque._ @@ -966,10 +946,10 @@ object MyDomain { ``` That's the basic usage. Inside the companion object you will get methods like `from`, `apply`, `unsafe` and extension -method `unwrap` plus an instance of `CaseClass1Rep[ISBN, String]`. A more complete example below. +method `unwrap` plus an instance of `ValueClassLike[ISBN, String]`. A more complete example below. ```scala -import pl.iterators.kebs.macros.CaseClass1Rep +import pl.iterators.kebs.core.macros.ValueClassLike import pl.iterators.kebs.opaque._ object MyDomain { @@ -1000,7 +980,7 @@ trait Showable[A] { def show(a: A): String } given Showable[String] = (a: String) => a -given[S, A](using showable: Showable[S], cc1Rep: CaseClass1Rep[A, S]): Showable[A] = (a: A) => showable.show(cc1Rep.unapply(a)) +given[S, A](using showable: Showable[S], vcLike: ValueClassLike[A, S]): Showable[A] = (a: A) => showable.show(vcLike.unapply(a)) implicitly[Showable[ISBN]].show(ISBN("1234567890")) // "1234567890" ``` @@ -1105,3 +1085,34 @@ The code generated by macros in `kebs-tagged-meta` is not visible to IntelliJ ID plugin that enhances experience with the library by adding support for generated code. You can install it from the IntelliJ Marketplace. In the Settings/Preferences dialog, select "Plugins" and type "Kebs" into search input (see https://www.jetbrains.com/help/idea/managing-plugins.html for detailed instructions). You can also use this web page: https://plugins.jetbrains.com/plugin/16069-kebs. + +### Kebs 2.0 migration guide + +Please be aware that recent changes in the source code might require some changes in your codebase. Follow the guide below to migrate your code to Kebs 2.0: +* If you are using value classes instead of tagged/opaque types, please mix in the `CaseClass1ToValueClass` trait. +* Extend your value-enums with `pl.iterators.kebs.enums.ValueEnumLikeEntry` parameterized with the type of the value. + * Native Scala 3 value-enums: + ```scala + enum ColorButRGB(val value: Int) extends ValueEnumLikeEntry[Int] {slick + case Red extends ColorButRGB(0xFF0000) + case Green extends ColorButRGB(0x00FF00) + case Blue extends ColorButRGB(0x0000FF) + } + ``` + * enumeratum value-enums for Scala 2 and Scala 3: + ```scala + sealed abstract class LibraryItem(val value: Int) extends IntEnumEntry with ValueEnumLikeEntry[Int] + object LibraryItem extends IntEnum[LibraryItem] { + case object Book extends LibraryItem(value = 1) + case object Movie extends LibraryItem(value = 2) + case object Magazine extends LibraryItem(3) + case object CD extends LibraryItem(4) + val values = findValues + } + ``` +* Extend your traits/classes/objects, if inside of one an implicit enum (or value-enum) conversion for `kebs` library's needs should occur, with one of the following traits: + * For Scala 2 and Scala 3 enums from `enumeratum` library: `pl.iterators.kebs.enumeratum.KebsEnumeratum` + * For Scala 2 and Scala 3 value-enums from `enumeratum` library: `pl.iterators.kebs.enumeratum.KebsValueEnumeratum` + * For Scala 3 native value-enums: `pl.iterators.kebs.enums.KebsValueEnum` + * For Scala 2 `scala.Enumeration` enums or Scala 3 native enums: `pl.iterators.kebs.enums.KebsEnum` + diff --git a/akka-http/src/main/scala/pl/iterators/kebs/matchers/KebsMatchers.scala b/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/matchers/KebsMatchers.scala similarity index 68% rename from akka-http/src/main/scala/pl/iterators/kebs/matchers/KebsMatchers.scala rename to akka-http/src/main/scala/pl/iterators/kebs/akkahttp/matchers/KebsMatchers.scala index a7932b4a..9ff8ca38 100644 --- a/akka-http/src/main/scala/pl/iterators/kebs/matchers/KebsMatchers.scala +++ b/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/matchers/KebsMatchers.scala @@ -1,16 +1,14 @@ -package pl.iterators.kebs.matchers +package pl.iterators.kebs.akkahttp.matchers import akka.http.scaladsl.server.{PathMatcher1, PathMatchers} import enumeratum.{Enum, EnumEntry} -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.macros.CaseClass1Rep - -import scala.language.implicitConversions +import pl.iterators.kebs.core.instances.InstanceConverter +import pl.iterators.kebs.core.macros.ValueClassLike trait KebsMatchers extends PathMatchers { implicit class SegmentIsomorphism[U](segment: PathMatcher1[U]) { - def as[T](implicit rep: CaseClass1Rep[T, U]): PathMatcher1[T] = segment.map(rep.apply) + def as[T](implicit rep: ValueClassLike[T, U]): PathMatcher1[T] = segment.map(rep.apply) } implicit class SegmentConversion[Source](segment: PathMatcher1[Source]) { diff --git a/pekko-http/src/main/scala/pl/iterators/kebs/matchers/package.scala b/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/matchers/package.scala similarity index 56% rename from pekko-http/src/main/scala/pl/iterators/kebs/matchers/package.scala rename to akka-http/src/main/scala/pl/iterators/kebs/akkahttp/matchers/package.scala index 610a2da7..34c0e7df 100644 --- a/pekko-http/src/main/scala/pl/iterators/kebs/matchers/package.scala +++ b/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/matchers/package.scala @@ -1,3 +1,3 @@ -package pl.iterators.kebs +package pl.iterators.kebs.akkahttp package object matchers extends KebsMatchers diff --git a/akka-http/src/main/scala/pl/iterators/kebs/unmarshallers/KebsUnmarshallers.scala b/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/unmarshallers/KebsUnmarshallers.scala similarity index 62% rename from akka-http/src/main/scala/pl/iterators/kebs/unmarshallers/KebsUnmarshallers.scala rename to akka-http/src/main/scala/pl/iterators/kebs/akkahttp/unmarshallers/KebsUnmarshallers.scala index 739055ac..5fb0562c 100644 --- a/akka-http/src/main/scala/pl/iterators/kebs/unmarshallers/KebsUnmarshallers.scala +++ b/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/unmarshallers/KebsUnmarshallers.scala @@ -1,14 +1,14 @@ -package pl.iterators.kebs.unmarshallers +package pl.iterators.kebs.akkahttp.unmarshallers import akka.http.scaladsl.unmarshalling.{FromStringUnmarshaller, Unmarshaller} -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.macros.CaseClass1Rep +import pl.iterators.kebs.core.instances.InstanceConverter +import pl.iterators.kebs.core.macros.{CaseClass1ToValueClass, ValueClassLike} -trait KebsUnmarshallers { - implicit def kebsUnmarshaller[A, B](implicit rep: CaseClass1Rep[B, A]): Unmarshaller[A, B] = +trait KebsUnmarshallers extends CaseClass1ToValueClass { + 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: CaseClass1Rep[B, A], + implicit def kebsFromStringUnmarshaller[A, B](implicit rep: ValueClassLike[B, A], fsu: FromStringUnmarshaller[A]): FromStringUnmarshaller[B] = fsu andThen kebsUnmarshaller(rep) diff --git a/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/unmarshallers/enums/KebsEnumUnmarshallers.scala b/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/unmarshallers/enums/KebsEnumUnmarshallers.scala new file mode 100644 index 00000000..c950720e --- /dev/null +++ b/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/unmarshallers/enums/KebsEnumUnmarshallers.scala @@ -0,0 +1,46 @@ +package pl.iterators.kebs.akkahttp.unmarshallers.enums + +import akka.http.scaladsl.unmarshalling.PredefinedFromStringUnmarshallers._ +import akka.http.scaladsl.unmarshalling.{FromStringUnmarshaller, Unmarshaller} +import akka.http.scaladsl.util.FastFuture +import pl.iterators.kebs.core.enums.{EnumLike, ValueEnumLike, ValueEnumLikeEntry} + +trait EnumUnmarshallers { + final def enumUnmarshaller[E](`enum`: EnumLike[E]): FromStringUnmarshaller[E] = Unmarshaller { _ =>name => + `enum`.withNameInsensitiveOption(name) match { + case Some(enumEntry) => FastFuture.successful(enumEntry) + case None => + FastFuture.failed(new IllegalArgumentException(s"""Invalid value '$name'. Expected one of: ${`enum`.getNamesToValuesMap.keysIterator + .mkString(", ")}""")) + } + } + + implicit def kebsEnumUnmarshaller[E](implicit ev: EnumLike[E]): FromStringUnmarshaller[E] = + enumUnmarshaller(ev) +} + +trait ValueEnumUnmarshallers { + final def valueEnumUnmarshaller[V, E <: ValueEnumLikeEntry[V]](`enum`: ValueEnumLike[V, E]): Unmarshaller[V, E] = Unmarshaller { _ =>v => + `enum`.withValueOption(v) match { + case Some(enumEntry) => FastFuture.successful(enumEntry) + case None => + FastFuture.failed(new IllegalArgumentException(s"""Invalid value '$v'. Expected one of: ${`enum`.getValuesToEntriesMap.keysIterator + .mkString(", ")}""")) + } + } + + implicit def kebsValueEnumUnmarshaller[V, E <: ValueEnumLikeEntry[V]](implicit ev: ValueEnumLike[V, E]): Unmarshaller[V, E] = + valueEnumUnmarshaller(ev) + + implicit def kebsIntValueEnumFromStringUnmarshaller[E <: ValueEnumLikeEntry[Int]](implicit ev: ValueEnumLike[Int, E]): FromStringUnmarshaller[E] = + intFromStringUnmarshaller andThen valueEnumUnmarshaller(ev) + implicit def kebsLongValueEnumFromStringUnmarshaller[E <: ValueEnumLikeEntry[Long]](implicit ev: ValueEnumLike[Long, E]): FromStringUnmarshaller[E] = + longFromStringUnmarshaller andThen valueEnumUnmarshaller(ev) + implicit def kebsShortValueEnumFromStringUnmarshaller[E <: ValueEnumLikeEntry[Short]]( + implicit ev: ValueEnumLike[Short, E]): FromStringUnmarshaller[E] = + shortFromStringUnmarshaller andThen valueEnumUnmarshaller(ev) + implicit def kebsByteValueEnumFromStringUnmarshaller[E <: ValueEnumLikeEntry[Byte]](implicit ev: ValueEnumLike[Byte, E]): FromStringUnmarshaller[E] = + byteFromStringUnmarshaller andThen valueEnumUnmarshaller(ev) +} + +trait KebsEnumUnmarshallers extends EnumUnmarshallers with ValueEnumUnmarshallers {} diff --git a/http4s-stir/src/main/scala-2/unmarshallers/enums/package.scala b/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/unmarshallers/enums/package.scala similarity index 51% rename from http4s-stir/src/main/scala-2/unmarshallers/enums/package.scala rename to akka-http/src/main/scala/pl/iterators/kebs/akkahttp/unmarshallers/enums/package.scala index a4fa82c6..fb2d9919 100644 --- a/http4s-stir/src/main/scala-2/unmarshallers/enums/package.scala +++ b/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/unmarshallers/enums/package.scala @@ -1,3 +1,3 @@ -package pl.iterators.kebs.unmarshallers +package pl.iterators.kebs.akkahttp.unmarshallers package object enums extends KebsEnumUnmarshallers diff --git a/http4s-stir/src/main/scala/pl/iterators/kebs/unmarshallers/package.scala b/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/unmarshallers/package.scala similarity index 61% rename from http4s-stir/src/main/scala/pl/iterators/kebs/unmarshallers/package.scala rename to akka-http/src/main/scala/pl/iterators/kebs/akkahttp/unmarshallers/package.scala index 6fd93de3..615d2a88 100644 --- a/http4s-stir/src/main/scala/pl/iterators/kebs/unmarshallers/package.scala +++ b/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/unmarshallers/package.scala @@ -1,3 +1,3 @@ -package pl.iterators.kebs +package pl.iterators.kebs.akkahttp package object unmarshallers extends KebsUnmarshallers diff --git a/akka-http/src/main/scala/pl/iterators/kebs/unmarshallers/enums/KebsEnumUnmarshallers.scala b/akka-http/src/main/scala/pl/iterators/kebs/unmarshallers/enums/KebsEnumUnmarshallers.scala deleted file mode 100644 index 62324d0f..00000000 --- a/akka-http/src/main/scala/pl/iterators/kebs/unmarshallers/enums/KebsEnumUnmarshallers.scala +++ /dev/null @@ -1,48 +0,0 @@ -package pl.iterators.kebs.unmarshallers.enums - -import akka.http.scaladsl.unmarshalling.PredefinedFromStringUnmarshallers._ -import akka.http.scaladsl.unmarshalling.{FromStringUnmarshaller, Unmarshaller} -import akka.http.scaladsl.util.FastFuture -import enumeratum.values._ -import enumeratum.{Enum, EnumEntry} -import pl.iterators.kebs.macros.enums.{EnumOf, ValueEnumOf} - -trait EnumUnmarshallers { - final def enumUnmarshaller[E <: EnumEntry](`enum`: Enum[E]): FromStringUnmarshaller[E] = Unmarshaller { _ =>name => - `enum`.withNameInsensitiveOption(name) match { - case Some(enumEntry) => FastFuture.successful(enumEntry) - case None => - FastFuture.failed(new IllegalArgumentException(s"""Invalid value '$name'. Expected one of: ${`enum`.namesToValuesMap.keysIterator - .mkString(", ")}""")) - } - } - - implicit def kebsEnumUnmarshaller[E <: EnumEntry](implicit ev: EnumOf[E]): FromStringUnmarshaller[E] = - enumUnmarshaller(ev.`enum`) -} - -trait ValueEnumUnmarshallers { - final def valueEnumUnmarshaller[V, E <: ValueEnumEntry[V]](`enum`: ValueEnum[V, E]): Unmarshaller[V, E] = Unmarshaller { _ =>v => - `enum`.withValueOpt(v) match { - case Some(enumEntry) => FastFuture.successful(enumEntry) - case None => - FastFuture.failed(new IllegalArgumentException(s"""Invalid value '$v'. Expected one of: ${`enum`.valuesToEntriesMap.keysIterator - .mkString(", ")}""")) - } - } - - implicit def kebsValueEnumUnmarshaller[V, E <: ValueEnumEntry[V]](implicit ev: ValueEnumOf[V, E]): Unmarshaller[V, E] = - valueEnumUnmarshaller(ev.valueEnum) - - implicit def kebsIntValueEnumFromStringUnmarshaller[E <: IntEnumEntry](implicit ev: ValueEnumOf[Int, E]): FromStringUnmarshaller[E] = - intFromStringUnmarshaller andThen valueEnumUnmarshaller(ev.valueEnum) - implicit def kebsLongValueEnumFromStringUnmarshaller[E <: LongEnumEntry](implicit ev: ValueEnumOf[Long, E]): FromStringUnmarshaller[E] = - longFromStringUnmarshaller andThen valueEnumUnmarshaller(ev.valueEnum) - implicit def kebsShortValueEnumFromStringUnmarshaller[E <: ShortEnumEntry]( - implicit ev: ValueEnumOf[Short, E]): FromStringUnmarshaller[E] = - shortFromStringUnmarshaller andThen valueEnumUnmarshaller(ev.valueEnum) - implicit def kebsByteValueEnumFromStringUnmarshaller[E <: ByteEnumEntry](implicit ev: ValueEnumOf[Byte, E]): FromStringUnmarshaller[E] = - byteFromStringUnmarshaller andThen valueEnumUnmarshaller(ev.valueEnum) -} - -trait KebsEnumUnmarshallers extends EnumUnmarshallers with ValueEnumUnmarshallers {} diff --git a/akka-http/src/test/scala/pl/iterators/kebs/AkkaHttpTagsDomain.scala b/akka-http/src/test/scala/pl/iterators/kebs/akkahttp/domain/AkkaHttpTagsDomain.scala similarity index 92% rename from akka-http/src/test/scala/pl/iterators/kebs/AkkaHttpTagsDomain.scala rename to akka-http/src/test/scala/pl/iterators/kebs/akkahttp/domain/AkkaHttpTagsDomain.scala index f0661a1e..e40789a0 100644 --- a/akka-http/src/test/scala/pl/iterators/kebs/AkkaHttpTagsDomain.scala +++ b/akka-http/src/test/scala/pl/iterators/kebs/akkahttp/domain/AkkaHttpTagsDomain.scala @@ -1,9 +1,10 @@ -package pl.iterators.kebs +package pl.iterators.kebs.akkahttp.domain import enumeratum.values.{IntEnum, IntEnumEntry, StringEnum, StringEnumEntry} import enumeratum.{Enum, EnumEntry} import pl.iterators.kebs.tag.meta.tagged import pl.iterators.kebs.tagged._ +import pl.iterators.kebs.core.enums.ValueEnumLikeEntry import java.net.URI import java.util.UUID @@ -47,7 +48,7 @@ object Domain extends Tags { val values = findValues } - sealed abstract class LibraryItem(val value: Int) extends IntEnumEntry + sealed abstract class LibraryItem(val value: Int) extends IntEnumEntry with ValueEnumLikeEntry[Int] object LibraryItem extends IntEnum[LibraryItem] { case object Book extends LibraryItem(1) @@ -63,7 +64,7 @@ object Domain extends Tags { case class Blue(value: Int) case class Color(red: Red, green: Green, blue: Blue) - sealed abstract class ShirtSize(val value: String) extends StringEnumEntry + sealed abstract class ShirtSize(val value: String) extends StringEnumEntry with ValueEnumLikeEntry[String] object ShirtSize extends StringEnum[ShirtSize] { case object Small extends ShirtSize("S") case object Medium extends ShirtSize("M") diff --git a/akka-http/src/test/scala/pl/iterators/kebs/matchers/AkkaHttpMatchersTests.scala b/akka-http/src/test/scala/pl/iterators/kebs/akkahttp/matchers/AkkaHttpMatchersTests.scala similarity index 81% rename from akka-http/src/test/scala/pl/iterators/kebs/matchers/AkkaHttpMatchersTests.scala rename to akka-http/src/test/scala/pl/iterators/kebs/akkahttp/matchers/AkkaHttpMatchersTests.scala index ee7a207e..69d89753 100644 --- a/akka-http/src/test/scala/pl/iterators/kebs/matchers/AkkaHttpMatchersTests.scala +++ b/akka-http/src/test/scala/pl/iterators/kebs/akkahttp/matchers/AkkaHttpMatchersTests.scala @@ -1,14 +1,15 @@ -package pl.iterators.kebs.matchers +package pl.iterators.kebs.akkahttp.matchers import akka.http.scaladsl.server.Directives 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.Domain._ +import pl.iterators.kebs.akkahttp.domain.Domain.Greeting +import pl.iterators.kebs.akkahttp.domain.Domain._ import pl.iterators.kebs.instances.net.URIString -import pl.iterators.kebs.instances.time.mixins.InstantEpochMilliLong import pl.iterators.kebs.instances.time.{DayOfWeekInt, ZonedDateTimeString} +import pl.iterators.kebs.instances.time.mixins.InstantEpochMilliLong import java.net.URI import java.time.{DayOfWeek, Instant, ZonedDateTime} @@ -24,15 +25,15 @@ class AkkaHttpMatchersTests with InstantEpochMilliLong with URIString { - test("No CaseClass1Rep implicits derived") { - import pl.iterators.kebs.macros.CaseClass1Rep + test("No ValueClassLike implicits derived") { + import pl.iterators.kebs.core.macros.ValueClassLike - "implicitly[CaseClass1Rep[DayOfWeek, Int]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Int, DayOfWeek]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Instant, Long]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Long, Instant]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[URI, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, URI]]" shouldNot typeCheck + "implicitly[ValueClassLike[DayOfWeek, Int]]" shouldNot typeCheck + "implicitly[ValueClassLike[Int, DayOfWeek]]" shouldNot typeCheck + "implicitly[ValueClassLike[Instant, Long]]" shouldNot typeCheck + "implicitly[ValueClassLike[Long, Instant]]" shouldNot typeCheck + "implicitly[ValueClassLike[URI, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, URI]]" shouldNot typeCheck } test("Extract String to ZonedDateTime") { diff --git a/akka-http/src/test/scala/pl/iterators/kebs/unmarshallers/AkkaHttpUnmarshallersTests.scala b/akka-http/src/test/scala/pl/iterators/kebs/akkahttp/unmarshallers/AkkaHttpUnmarshallersTests.scala similarity index 87% rename from akka-http/src/test/scala/pl/iterators/kebs/unmarshallers/AkkaHttpUnmarshallersTests.scala rename to akka-http/src/test/scala/pl/iterators/kebs/akkahttp/unmarshallers/AkkaHttpUnmarshallersTests.scala index e2841e0d..7454719d 100644 --- a/akka-http/src/test/scala/pl/iterators/kebs/unmarshallers/AkkaHttpUnmarshallersTests.scala +++ b/akka-http/src/test/scala/pl/iterators/kebs/akkahttp/unmarshallers/AkkaHttpUnmarshallersTests.scala @@ -1,4 +1,4 @@ -package pl.iterators.kebs.unmarshallers +package pl.iterators.kebs.akkahttp.unmarshallers import akka.http.scaladsl.model.FormData import akka.http.scaladsl.server.{Directives, MalformedQueryParamRejection} @@ -7,10 +7,12 @@ import akka.http.scaladsl.unmarshalling.Unmarshal import org.scalatest.concurrent.ScalaFutures import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.Domain._ +import pl.iterators.kebs.akkahttp.domain.Domain.{Blue, Color, Green, Greeting, I, LibraryItem, P, Red, S, ShirtSize, SortOrder} +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.unmarshallers.enums.KebsEnumUnmarshallers +import pl.iterators.kebs.enumeratum.{KebsEnumeratum, KebsValueEnumeratum} +import pl.iterators.kebs.akkahttp.unmarshallers.enums.KebsEnumUnmarshallers import java.time.{DayOfWeek, YearMonth} @@ -24,17 +26,19 @@ class AkkaHttpUnmarshallersTests with KebsEnumUnmarshallers with URIString with YearMonthString - with DayOfWeekInt { - - test("No CaseClass1Rep implicits derived") { - import pl.iterators.kebs.macros.CaseClass1Rep - - "implicitly[CaseClass1Rep[URI, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, URI]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[YearMonth, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, YearMonth]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[DayOfWeek, Int]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Int, DayOfWeek]]" shouldNot typeCheck + with DayOfWeekInt + with KebsEnumeratum + with KebsValueEnumeratum { + + test("No ValueClassLike implicits derived") { + import pl.iterators.kebs.core.macros.ValueClassLike + + "implicitly[ValueClassLike[URI, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, URI]]" shouldNot typeCheck + "implicitly[ValueClassLike[YearMonth, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, YearMonth]]" shouldNot typeCheck + "implicitly[ValueClassLike[DayOfWeek, Int]]" shouldNot typeCheck + "implicitly[ValueClassLike[Int, DayOfWeek]]" shouldNot typeCheck } test("Unmarshal") { diff --git a/benchmarks/src/main/scala/pl/iterators/kebs_benchmarks/SprayJsonFormatBenchmark.scala b/benchmarks/src/main/scala/pl/iterators/kebs_benchmarks/SprayJsonFormatBenchmark.scala deleted file mode 100644 index 459d85b7..00000000 --- a/benchmarks/src/main/scala/pl/iterators/kebs_benchmarks/SprayJsonFormatBenchmark.scala +++ /dev/null @@ -1,284 +0,0 @@ -package pl.iterators.kebs_benchmarks - -import java.time.format.DateTimeFormatter -import java.time.{LocalDate, LocalTime} -import java.util.concurrent.TimeUnit - -import org.apache.pekko.http.scaladsl.marshallers.sprayjson.SprayJsonSupport -import org.apache.pekko.http.scaladsl.marshalling.ToResponseMarshallable -import org.apache.pekko.http.scaladsl.model.StatusCodes._ -import org.apache.pekko.http.scaladsl.model.{ContentTypes, HttpEntity} -import org.apache.pekko.http.scaladsl.server.Directives._ -import org.apache.pekko.http.scaladsl.testkit.ScalatestRouteTest -import org.openjdk.jmh.annotations._ -import org.scalatest.{FunSpec, Matchers} -import pl.iterators.kebs.json.KebsSpray -import spray.json._ - -import scala.concurrent.{ExecutionContext, Future} -import scala.util.Try - -case class Contact(phoneNumber: String, url: String) -case class LocationShort(city: String, latitude: BigDecimal, longitude: BigDecimal, neighborhood: String, timeZone: String) -case class LocationFull(city: String, - latitude: BigDecimal, - longitude: BigDecimal, - neighborhood: String, - timeZone: String, - address_1: String, - state: String, - postalCode: String) -case class Reservation(deepLink: String, id: Int, seatType: String, timeSlot: String, webLink: String) -case class TravelTime(distance: Double, driving: Int, walking: Int) -case class AvailableReservation(contact: Contact, - deepLink: String, - location: LocationShort, - images: Option[List[String]], - name: String, - priceRangeId: Int, - reservations: List[Reservation], - travelTime: TravelTime, - `type`: String, - webLink: String) - -case class AvailableReservationsResponse(available: List[AvailableReservation]) - -case class Rater(name: String, score: Double, scale: Double, image: String) -case class Venue(location: LocationFull, - name: String, - priceRangeId: Int, - `type`: String, - images: List[String], - about: String, - tagline: String, - rater: Rater) -case class BookedReservation(deepLink: String, seatType: String, timeSlot: String) -case class PaymentDetails(fee: Option[BigDecimal], serviceCharge: Option[BigDecimal], tax: Option[BigDecimal], total: Option[BigDecimal]) -case class Payment(details: Option[PaymentDetails]) - -case class ReservationDetailsResponse(venue: Venue, reservation: BookedReservation, payment: Option[Payment], token: String) - -case class Reservations(reservations: List[RequestedReservation]) -case class RequestedReservationDetails(day: LocalDate, timeSlot: LocalTime) -case class Fee(amount: BigDecimal) -case class Cancellation(fee: Option[Fee]) -case class RequestedReservation(token: String, reservation: RequestedReservationDetails, cancellation: Option[Cancellation]) - -sealed trait DetailsResult -object DetailsResult { - case class Success(details: ReservationDetailsResponse) extends DetailsResult - case object Expired extends DetailsResult - case class Error(message: String) extends DetailsResult -} - -trait Protocol extends DefaultJsonProtocol with SprayJsonSupport { - implicit val localTimeFormat = new JsonFormat[LocalTime] { - override def write(obj: LocalTime): JsValue = JsString(formatter.format(obj)) - - override def read(json: JsValue): LocalTime = { - json match { - case JsString(lTString) => - Try(LocalTime.parse(lTString, formatter)).getOrElse(deserializationError(deserializationErrorMessage)) - case _ => deserializationError(deserializationErrorMessage) - } - } - - private val formatter = DateTimeFormatter.ISO_LOCAL_TIME - private val deserializationErrorMessage = - s"Expected date time in ISO offset date time format ex. ${LocalTime.now().format(formatter)}" - } - - implicit val localDateFormat = new JsonFormat[LocalDate] { - override def write(obj: LocalDate): JsValue = JsString(formatter.format(obj)) - - override def read(json: JsValue): LocalDate = { - json match { - case JsString(lDString) => - Try(LocalDate.parse(lDString, formatter)).getOrElse(deserializationError(deserializationErrorMessage)) - case _ => deserializationError(deserializationErrorMessage) - } - } - - private val formatter = DateTimeFormatter.ISO_LOCAL_DATE - private val deserializationErrorMessage = - s"Expected date time in ISO offset date time format ex. ${LocalDate.now().format(formatter)}" - } -} - -abstract class Service { - def getAvailableReservations: Future[AvailableReservationsResponse] - def getReservationDetails(id: Int): Future[DetailsResult] - def getUserReservations(accessToken: String): Future[Reservations] -} - -object BeforeKebs { - object Protocol extends Protocol { - def jsonFlatFormat[P, T <: Product](construct: P => T)(implicit jw: JsonWriter[P], jr: JsonReader[P]): JsonFormat[T] = - new JsonFormat[T] { - override def read(json: JsValue): T = construct(jr.read(json)) - override def write(obj: T): JsValue = jw.write(obj.productElement(0).asInstanceOf[P]) - } - - implicit val contactFormat = jsonFormat2(Contact.apply) - implicit val locationShortFormat = jsonFormat5(LocationShort.apply) - implicit val locationFullFormat = jsonFormat8(LocationFull.apply) - implicit val reservationFormat = jsonFormat5(Reservation.apply) - implicit val travelTimeFormat = jsonFormat3(TravelTime.apply) - implicit val availableReservationFormat = jsonFormat10(AvailableReservation.apply) - implicit val availableReservationResponseFormat = jsonFormat1(AvailableReservationsResponse.apply) - implicit val raterFormat = jsonFormat4(Rater.apply) - implicit val venueResponseFormat = jsonFormat8(Venue.apply) - implicit val bookedReservationResponseFormat = jsonFormat3(BookedReservation.apply) - implicit val paymentDetailsFormat = jsonFormat4(PaymentDetails.apply) - implicit val paymentFormat = jsonFormat1(Payment.apply) - implicit val reservationDetailsResponseFormat = jsonFormat4(ReservationDetailsResponse.apply) - implicit val feeFormat = jsonFormat1(Fee.apply) - private implicit val cancellationFormat = jsonFormat1(Cancellation.apply) - implicit val requestedReservationDetailsFormat = jsonFormat2(RequestedReservationDetails.apply) - implicit val requestedReservationFormat = jsonFormat3(RequestedReservation.apply) - implicit val reservationsFormat = jsonFormat1(Reservations.apply) - } - - class Router(service: Service)(implicit ec: ExecutionContext) { - import Protocol._ - val getAvailableReservations = (get & pathEndOrSingleSlash) { - complete(service.getAvailableReservations) - } - val getReservationDetails = (get & path(IntNumber)) { id => - complete { - service.getReservationDetails(id).map[ToResponseMarshallable] { - case DetailsResult.Success(res) => OK -> res - case DetailsResult.Expired => NotFound - case DetailsResult.Error(error) => BadRequest -> error - } - } - } - val getUserReservations = (get & parameters('token)) { token => - complete(service.getUserReservations(token)) - } - } -} - -object AfterKebs { - object Protocol extends Protocol with KebsSpray - - class Router(service: Service)(implicit ec: ExecutionContext) { - import Protocol._ - val getAvailableReservations = (get & pathEndOrSingleSlash) { - complete(service.getAvailableReservations) - } - val getReservationDetails = (get & path(IntNumber)) { id => - complete { - service.getReservationDetails(id).map[ToResponseMarshallable] { - case DetailsResult.Success(res) => OK -> res - case DetailsResult.Expired => NotFound - case DetailsResult.Error(error) => BadRequest -> error - } - } - } - val getUserReservations = (get & parameters('token)) { token => - complete(service.getUserReservations(token)) - } - } -} - -object SprayJsonFormatBenchmark { - val fakeService = new Service { - val sampleAvailableReservationsResponse = AvailableReservationsResponse( - List( - AvailableReservation( - Contact("12 270 24 88", ""), - "?", - LocationShort("Czernichów", 49.9915924, 19.6754663, "Czernichów", "CET"), - None, - "RAPIO", - 1, - List( - Reservation("?", 1, "stolik", "20:00-21:00", ""), - Reservation("?", 2, "stolik", "20:00-21:00", ""), - Reservation("?", 3, "stolik", "20:00-21:00", "") - ), - TravelTime(distance = 100, driving = 99, walking = 1), - "pizzera", - "" - ))) - val sampleReservetionDetailsResponse = ReservationDetailsResponse( - Venue( - LocationFull("Czernichów", 49.9915924, 19.6754663, "Czernichów", "CET", "Czernichów 232", "małopolskie", "32-071"), - "RAPIO", - 1, - "pizzeria", - List.empty, - "Pizzeria & Restauracja RAPIO", - "Pizzeria & Restauracja RAPIO", - Rater("?", 100.0, 1.0, "?") - ), - BookedReservation("?", "stolik", "20.00-21:00"), - Some(Payment(Some(PaymentDetails(fee = None, serviceCharge = Some(100), tax = Some(0.08), total = Some(108))))), - token = "abcdefgh" - ) - val sampleReservations = Reservations(List( - RequestedReservation("abcdefgh", RequestedReservationDetails(LocalDate.now(), LocalTime.now()), Some(Cancellation(Some(Fee(10))))))) - - override def getReservationDetails(id: Int) = Future.successful(DetailsResult.Success(sampleReservetionDetailsResponse)) - override def getUserReservations(accessToken: String) = Future.successful(sampleReservations) - override def getAvailableReservations = Future.successful(sampleAvailableReservationsResponse) - } - - import ExecutionContext.Implicits.global - val beforeKebsRouter = new BeforeKebs.Router(fakeService) - val afterKebsRouter = new AfterKebs.Router(fakeService) - - val beforeKebsRoutes = beforeKebsRouter.getUserReservations ~ beforeKebsRouter.getAvailableReservations ~ beforeKebsRouter.getReservationDetails - val afterKebsRoutes = afterKebsRouter.getUserReservations ~ afterKebsRouter.getAvailableReservations ~ afterKebsRouter.getReservationDetails -} - -@State(Scope.Benchmark) -@Warmup(iterations = 10, timeUnit = TimeUnit.MILLISECONDS) -@Measurement(iterations = 100, timeUnit = TimeUnit.MILLISECONDS) -@Fork(1) -class SprayJsonFormatBenchmark extends FunSpec with Matchers with ScalatestRouteTest { - - @Benchmark - @BenchmarkMode(Array(Mode.AverageTime)) - @OutputTimeUnit(TimeUnit.MILLISECONDS) - def sprayJsonCostBeforeKebs1 = Get("/?token=token") ~> SprayJsonFormatBenchmark.beforeKebsRoutes ~> check { - status shouldEqual OK - } - - @Benchmark - @BenchmarkMode(Array(Mode.AverageTime)) - @OutputTimeUnit(TimeUnit.MILLISECONDS) - def sprayJsonCostBeforeKebs2 = Get("/") ~> SprayJsonFormatBenchmark.beforeKebsRoutes ~> check { - status shouldEqual OK - } - - @Benchmark - @BenchmarkMode(Array(Mode.AverageTime)) - @OutputTimeUnit(TimeUnit.MILLISECONDS) - def sprayJsonCostBeforeKebs3 = Get("/1") ~> SprayJsonFormatBenchmark.beforeKebsRoutes ~> check { - status shouldEqual OK - } - - @Benchmark - @BenchmarkMode(Array(Mode.AverageTime)) - @OutputTimeUnit(TimeUnit.MILLISECONDS) - def sprayJsonCostAfterKebs1 = Get("/?token=token") ~> SprayJsonFormatBenchmark.afterKebsRoutes ~> check { - status shouldEqual OK - } - - @Benchmark - @BenchmarkMode(Array(Mode.AverageTime)) - @OutputTimeUnit(TimeUnit.MILLISECONDS) - def sprayJsonCostAfterKebs2 = Get("/") ~> SprayJsonFormatBenchmark.afterKebsRoutes ~> check { - status shouldEqual OK - } - - @Benchmark - @BenchmarkMode(Array(Mode.AverageTime)) - @OutputTimeUnit(TimeUnit.MILLISECONDS) - def sprayJsonCostAfterKebs3 = Get("/1") ~> SprayJsonFormatBenchmark.afterKebsRoutes ~> check { - status shouldEqual OK - } - -} diff --git a/build.sbt b/build.sbt index 8b562241..a8258807 100644 --- a/build.sbt +++ b/build.sbt @@ -1,10 +1,9 @@ import sbt.librarymanagement.ConflictWarning -val scala_2_12 = "2.12.18" -val scala_2_13 = "2.13.12" +val scala_2_13 = "2.13.14" val scala_3 = "3.3.3" val mainScalaVersion = scala_3 -val supportedScalaVersions = Seq(scala_2_12, scala_2_13, scala_3) +val supportedScalaVersions = Seq(scala_2_13, scala_3) ThisBuild / crossScalaVersions := supportedScalaVersions ThisBuild / scalaVersion := mainScalaVersion @@ -16,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", "-unchecked", "-feature", "-encoding", "utf8") + scalacOptions ++= Seq("-deprecation", "-unchecked", "-feature", "-encoding", "utf8") ++ (if (scalaVersion.value.startsWith("3")) Seq("-Xmax-inlines", "64", "-Yretain-trees") else Seq.empty), ) lazy val commonMacroSettings = baseSettings ++ Seq( @@ -28,7 +27,6 @@ lazy val commonMacroSettings = baseSettings ++ Seq( lazy val metaSettings = commonSettings ++ Seq( scalacOptions ++= paradiseFlag(scalaVersion.value), - libraryDependencies ++= paradisePlugin(scalaVersion.value) ) lazy val crossBuildSettings = Seq(crossScalaVersions := supportedScalaVersions) @@ -37,11 +35,10 @@ lazy val publishSettings = Seq( pomIncludeRepository := const(true), licenses := Seq("MIT License" -> url("http://opensource.org/licenses/MIT")), developers := List( - Developer(id = "mrzeznicki", - name = "Marcin Rzeźnicki", - email = "mrzeznicki@iterato.rs", - url = url("https://github.com/marcin-rzeznicki")), - Developer(id = "jborkowski", name = "Jonatan Borkowski", email = "jborkowski@iterato.rs", url = url("https://github.com/jborkowski")), + Developer(id = "luksow", + name = "Łukasz Sowa", + email = "lsowa@iteratorshq.com", + url = url("https://github.com/luksow")), Developer(id = "pkiersznowski", name = "Paweł Kiersznowski", email = "pkiersznowski@iteratorshq.com", @@ -94,27 +91,21 @@ def sv[A](scalaVersion: String, scala2_12Version: => A, scala2_13Version: => A) } def paradiseFlag(scalaVersion: String): Seq[String] = - if (scalaVersion == scala_2_12 || scalaVersion == scala_3) + if (scalaVersion == scala_3) Seq.empty else Seq("-Ymacro-annotations") -def paradisePlugin(scalaVersion: String): Seq[ModuleID] = - if (scalaVersion == scala_2_12) - Seq(compilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full)) - else - Seq.empty - -val scalaTest = Def.setting("org.scalatest" %%% "scalatest" % "3.2.19") -val scalaCheck = Def.setting("org.scalacheck" %%% "scalacheck" % "1.18.0") -val slick = "com.typesafe.slick" %% "slick" % "3.4.1" +val scalaTest = Def.setting("org.scalatest" %%% "scalatest" % "3.2.17") +val scalaCheck = Def.setting("org.scalacheck" %%% "scalacheck" % "1.17.0") +val slick = "com.typesafe.slick" %% "slick" % "3.5.1" val optionalSlick = optional(slick) val playJson = "org.playframework" %% "play-json" % "3.0.4" -val slickPg = "com.github.tminglei" %% "slick-pg" % "0.21.1" -val doobie = "org.tpolecat" %% "doobie-core" % "1.0.0-RC5" -val doobiePg = "org.tpolecat" %% "doobie-postgres" % "1.0.0-RC5" +val slickPg = "com.github.tminglei" %% "slick-pg" % "0.22.2" +val doobie = "org.tpolecat" %% "doobie-core" % "1.0.0-RC4" +val doobiePg = "org.tpolecat" %% "doobie-postgres" % "1.0.0-RC4" val sprayJson = "io.spray" %% "spray-json" % "1.3.6" -val circeV = "0.14.6" +val circeV = "0.14.9" val circe = "io.circe" %% "circe-core" % circeV val circeAuto = "io.circe" %% "circe-generic" % circeV val circeAutoExtras = "io.circe" %% "circe-generic-extras" % "0.14.3" @@ -123,16 +114,17 @@ 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 scalacheckMagnolify = "com.spotify" % "magnolify-scalacheck" % "0.7.3" -val scalacheckDerived = "io.github.martinhh" %% "scalacheck-derived" % "0.5.0" +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.5.16" +val enumeratumPlayJsonVersion = "1.8.1" val enumeratum = "com.beachape" %% "enumeratum" % enumeratumVersion def enumeratumInExamples = { val playJsonSupport = "com.beachape" %% "enumeratum-play-json" % enumeratumPlayJsonVersion - Seq(enumeratum, playJsonSupport.cross(CrossVersion.for3Use2_13)) + Seq(enumeratum, playJsonSupport) } val optionalEnumeratum = optional(enumeratum) @@ -169,10 +161,6 @@ val http4sStirVersion = "0.3" val http4sStir = "pl.iterators" %% "http4s-stir" % http4sStirVersion val http4sStirTestkit = "pl.iterators" %% "http4s-stir-testkit" % http4sStirVersion -def akkaHttpInBenchmarks = akkaHttpInExamples :+ (akkaHttpTestkit).cross(CrossVersion.for3Use2_13) - -def pekkoHttpInBenchmarks = pekkoHttpInExamples :+ pekkoHttpTestkit - lazy val commonSettings = baseSettings ++ Seq( scalacOptions ++= (if (scalaVersion.value.startsWith("3")) @@ -183,20 +171,32 @@ lazy val commonSettings = baseSettings ++ Seq( ) lazy val slickSettings = commonSettings ++ Seq( - libraryDependencies += slick.cross(CrossVersion.for3Use2_13), - libraryDependencies += (slickPg % "test").cross(CrossVersion.for3Use2_13), - libraryDependencies += optionalEnumeratum + libraryDependencies += slick, + libraryDependencies += (slickPg % "test"), + libraryDependencies += (enumeratum % "test") ) lazy val doobieSettings = commonSettings ++ Seq( libraryDependencies += doobie, libraryDependencies += (doobiePg % "test"), - libraryDependencies += optionalEnumeratum, + libraryDependencies += (enumeratum % "test"), ) lazy val coreSettings = commonMacroSettings ++ Seq( - libraryDependencies += (scalaCheck.value % "test"), - libraryDependencies += optionalEnumeratum + libraryDependencies += (scalaCheck.value % "test").cross(CrossVersion.for3Use2_13) +) + +lazy val enumSettings = commonMacroSettings ++ Seq( + libraryDependencies += scalaCheck.value % "test", + libraryDependencies += scalaTest.value, + scalacOptions ++= paradiseFlag(scalaVersion.value) +) + +lazy val enumeratumSettings = commonMacroSettings ++ Seq( + libraryDependencies += scalaCheck.value % "test", + libraryDependencies += scalaTest.value, + libraryDependencies += optionalEnumeratum, + scalacOptions ++= paradiseFlag(scalaVersion.value) ) lazy val sprayJsonMacroSettings = commonMacroSettings ++ Seq( @@ -208,7 +208,7 @@ lazy val sprayJsonSettings = commonSettings ++ Seq( ) lazy val playJsonSettings = commonSettings ++ Seq( - libraryDependencies += playJson.cross(CrossVersion.for3Use2_13) + libraryDependencies += playJson ) lazy val circeSettings = commonSettings ++ Seq( @@ -225,7 +225,6 @@ lazy val akkaHttpSettings = commonSettings ++ Seq( libraryDependencies += (akkaStreamTestkit % "test").cross(CrossVersion.for3Use2_13), libraryDependencies += (akkaHttpTestkit % "test").cross(CrossVersion.for3Use2_13), libraryDependencies += optionalEnumeratum, - libraryDependencies ++= paradisePlugin(scalaVersion.value), scalacOptions ++= paradiseFlag(scalaVersion.value) ) @@ -234,15 +233,12 @@ lazy val pekkoHttpSettings = commonSettings ++ Seq( libraryDependencies += pekkoStream, libraryDependencies += pekkoStreamTestkit % "test", libraryDependencies += pekkoHttpTestkit % "test", - libraryDependencies += optionalEnumeratum, - libraryDependencies ++= paradisePlugin(scalaVersion.value), + libraryDependencies += enumeratum % "test", scalacOptions ++= paradiseFlag(scalaVersion.value) ) lazy val http4sSettings = commonSettings ++ Seq( libraryDependencies += http4s, - libraryDependencies += optionalEnumeratum, - libraryDependencies ++= paradisePlugin(scalaVersion.value), scalacOptions ++= paradiseFlag(scalaVersion.value) ) @@ -250,8 +246,7 @@ lazy val http4sStirSettings = commonSettings ++ Seq( libraryDependencies += http4s, libraryDependencies += http4sStir, libraryDependencies += http4sStirTestkit % "test", - libraryDependencies += optionalEnumeratum, - libraryDependencies ++= paradisePlugin(scalaVersion.value), + libraryDependencies += enumeratum % "test", scalacOptions ++= paradiseFlag(scalaVersion.value) ) @@ -267,28 +262,20 @@ lazy val scalacheckSettings = commonSettings ++ Seq( else Nil)) ++ Seq(libraryDependencies ++= (if(scalaVersion.value.startsWith("2")) Seq(scalacheckMagnolify.cross(CrossVersion.for3Use2_13)) else Nil)) lazy val taggedSettings = commonSettings ++ Seq( - libraryDependencies += optionalSlick.cross(CrossVersion.for3Use2_13), + libraryDependencies += optionalSlick, libraryDependencies += optional(circe) ) lazy val opaqueSettings = commonSettings lazy val examplesSettings = commonSettings ++ Seq( - libraryDependencies += slickPg.cross(CrossVersion.for3Use2_13), + libraryDependencies += slickPg, libraryDependencies += circeParser, libraryDependencies ++= enumeratumInExamples, libraryDependencies ++= pekkoHttpInExamples, - libraryDependencies ++= paradisePlugin(scalaVersion.value), scalacOptions ++= paradiseFlag(scalaVersion.value) ) -lazy val benchmarkSettings = commonSettings ++ Seq( - libraryDependencies += scalaTest.value, - libraryDependencies ++= pekkoHttpInBenchmarks, - libraryDependencies += enumeratum, - libraryDependencies ++= akkaHttpInBenchmarks -) - lazy val taggedMetaSettings = metaSettings ++ Seq( libraryDependencies += optional(sprayJson.cross(CrossVersion.for3Use2_13)), libraryDependencies += optional(circe) @@ -296,41 +283,23 @@ lazy val taggedMetaSettings = metaSettings ++ Seq( lazy val instancesSettings = commonSettings -lazy val macroUtilsSettings = coreSettings ++ Seq( - Compile / scalaSource := baseDirectory.value / ".." / ".." / "core" / "src" / "main" / "scala", - Test / scalaSource := baseDirectory.value / ".." / ".." / "core" / "src" / "test" / "scala" -) - lazy val core = crossProject(JSPlatform, JVMPlatform) .withoutSuffixFor(JVMPlatform) .crossType(CrossType.Pure) .in(file("core")) - .settings(coreSettings: _*) - .settings(publishSettings: _*) + .settings(coreSettings*) + .settings(publishSettings*) .settings( name := "core", description := "Macros and utils supporting Kebs library", moduleName := "kebs-core", ) -lazy val macroUtils = crossProject(JSPlatform, JVMPlatform) - .withoutSuffixFor(JVMPlatform) - .crossType(CrossType.Pure) - .in(file("macro-utils")) - .settings(macroUtilsSettings: _*) - .settings(publishSettings: _*) - .settings( - name := "macro-utils", - description := "Macros and utils supporting Kebs library", - moduleName := "kebs-macro-utils", - ) - lazy val slickSupport = project .in(file("slick")) - .dependsOn(core.jvm, instances % "test -> test") - .settings(slickSettings: _*) - .settings(publishSettings: _*) - .settings(disableScala(List("3"))) + .dependsOn(core.jvm, enumeratumSupport, instances % "test -> test") + .settings(slickSettings*) + .settings(publishSettings*) .settings( name := "slick", description := "Library to eliminate the boilerplate code that comes with the use of Slick", @@ -340,9 +309,9 @@ lazy val slickSupport = project lazy val doobieSupport = project .in(file("doobie")) - .dependsOn(instances, opaque.jvm % "test -> test") - .settings(doobieSettings: _*) - .settings(publishSettings: _*) + .dependsOn(instances, enumeratumSupport, enumSupport, opaque.jvm % "test -> test") + .settings(doobieSettings*) + .settings(publishSettings*) .settings( name := "doobie", description := "Library to eliminate the boilerplate code that comes with the use of Doobie", @@ -353,8 +322,8 @@ lazy val doobieSupport = project lazy val sprayJsonMacros = project .in(file("spray-json-macros")) .dependsOn(core.jvm) - .settings(sprayJsonMacroSettings: _*) - .settings(publishSettings: _*) + .settings(sprayJsonMacroSettings*) + .settings(publishSettings*) .settings(disableScala(List("3"))) .settings( name := "spray-json-macros", @@ -365,9 +334,9 @@ lazy val sprayJsonMacros = project lazy val sprayJsonSupport = project .in(file("spray-json")) - .dependsOn(sprayJsonMacros, instances % "test -> test") - .settings(sprayJsonSettings: _*) - .settings(publishSettings: _*) + .dependsOn(sprayJsonMacros, enumeratumSupport, instances % "test -> test") + .settings(sprayJsonSettings*) + .settings(publishSettings*) .settings(disableScala(List("3"))) .settings( name := "spray-json", @@ -379,9 +348,8 @@ lazy val sprayJsonSupport = project lazy val playJsonSupport = project .in(file("play-json")) .dependsOn(core.jvm, instances % "test -> test") - .settings(playJsonSettings: _*) - .settings(publishSettings: _*) - .settings(disableScala(List("3"))) + .settings(playJsonSettings*) + .settings(publishSettings*) .settings( name := "play-json", description := "Automatic generation of Play json formats for case-classes", @@ -391,10 +359,10 @@ lazy val playJsonSupport = project lazy val circeSupport = project .in(file("circe")) - .dependsOn(core.jvm, instances % "test -> test") - .settings(circeSettings: _*) - .settings(crossBuildSettings: _*) - .settings(publishSettings: _*) + .dependsOn(core.jvm, enumeratumSupport, enumSupport, instances % "test -> test") + .settings(circeSettings*) + .settings(crossBuildSettings*) + .settings(publishSettings*) .settings( name := "circe", description := "Automatic generation of circe formats for case-classes", @@ -403,9 +371,9 @@ lazy val circeSupport = project lazy val akkaHttpSupport = project .in(file("akka-http")) - .dependsOn(core.jvm, instances % "test -> test", tagged.jvm % "test -> test", taggedMeta % "test -> test") - .settings(akkaHttpSettings: _*) - .settings(publishSettings: _*) + .dependsOn(core.jvm, enumeratumSupport, instances % "test -> test", tagged.jvm % "test -> test", taggedMeta % "test -> test") + .settings(akkaHttpSettings*) + .settings(publishSettings*) .settings(disableScala(List("3"))) .settings( name := "akka-http", @@ -416,9 +384,9 @@ lazy val akkaHttpSupport = project lazy val pekkoHttpSupport = project .in(file("pekko-http")) - .dependsOn(core.jvm, instances % "test -> test", tagged.jvm % "test -> test", taggedMeta % "test -> test") - .settings(pekkoHttpSettings: _*) - .settings(publishSettings: _*) + .dependsOn(core.jvm, enumeratumSupport, enumSupport, instances % "test -> test", tagged.jvm % "test -> test", taggedMeta % "test -> test") + .settings(pekkoHttpSettings*) + .settings(publishSettings*) .settings( name := "pekko-http", description := "Automatic generation of pekko-http deserializers for 1-element case classes", @@ -429,8 +397,8 @@ lazy val pekkoHttpSupport = project lazy val http4sSupport = project .in(file("http4s")) .dependsOn(core.jvm, instances, opaque.jvm % "test -> test", tagged.jvm % "test -> test", taggedMeta % "test -> test") - .settings(http4sSettings: _*) - .settings(publishSettings: _*) + .settings(http4sSettings*) + .settings(publishSettings*) .settings( name := "http4s", description := "Automatic generation of http4s deserializers for 1-element case classes, opaque and tagged types", @@ -442,20 +410,20 @@ lazy val http4sSupport = project lazy val http4sStirSupport = project .in(file("http4s-stir")) .dependsOn(core.jvm, instances, opaque.jvm % "test -> test", tagged.jvm % "test -> test", taggedMeta % "test -> test") - .settings(http4sStirSettings: _*) - .settings(publishSettings: _*) + .settings(http4sStirSettings*) + .settings(publishSettings*) .settings( name := "http4s-stir", description := "Automatic generation of http4s-stir deserializers for 1-element case classes, opaque and tagged types", moduleName := "kebs-http4s-stir", crossScalaVersions := supportedScalaVersions - ).settings(disableScala(List("2.12"))) + ) lazy val jsonschemaSupport = project .in(file("jsonschema")) .dependsOn(core.jvm) - .settings(jsonschemaSettings: _*) - .settings(publishSettings: _*) + .settings(jsonschemaSettings*) + .settings(publishSettings*) .settings(disableScala(List("3"))) .settings( name := "jsonschema", @@ -467,8 +435,8 @@ lazy val jsonschemaSupport = project lazy val scalacheckSupport = project .in(file("scalacheck")) .dependsOn(core.jvm, opaque.jvm % "test -> test") - .settings(scalacheckSettings: _*) - .settings(publishSettings: _*) + .settings(scalacheckSettings*) + .settings(publishSettings*) .settings( name := "scalacheck", description := "Automatic generation of scalacheck generators for case classes", @@ -481,8 +449,8 @@ lazy val tagged = crossProject(JSPlatform, JVMPlatform) .crossType(CrossType.Full) .in(file("tagged")) .dependsOn(core) - .settings(taggedSettings: _*) - .settings(publishSettings: _*) + .settings(taggedSettings*) + .settings(publishSettings*) .settings(disableScala(List("3"))) .settings( name := "tagged", @@ -496,15 +464,15 @@ lazy val opaque = crossProject(JSPlatform, JVMPlatform) .crossType(CrossType.Pure) .in(file("opaque")) .dependsOn(core) - .settings(opaqueSettings: _*) - .settings(publishSettings: _*) + .settings(opaqueSettings*) + .settings(publishSettings*) .settings( name := "opaque", description := "Representation of opaque types", moduleName := "kebs-opaque", crossScalaVersions := supportedScalaVersions ) - .settings(disableScala(List("2.12", "2.13"))) + .settings(disableScala(List("2.13"))) lazy val taggedMeta = project .in(file("tagged-meta")) @@ -516,8 +484,8 @@ lazy val taggedMeta = project jsonschemaSupport % "test -> test", scalacheckSupport % "test -> test" ) - .settings(taggedMetaSettings: _*) - .settings(publishSettings: _*) + .settings(taggedMetaSettings*) + .settings(publishSettings*) .settings(disableScala(List("3"))) .settings( name := "tagged-meta", @@ -529,36 +497,46 @@ lazy val taggedMeta = project lazy val examples = project .in(file("examples")) .dependsOn(slickSupport, sprayJsonSupport, playJsonSupport, pekkoHttpSupport, taggedMeta, circeSupport, instances) - .settings(examplesSettings: _*) - .settings(noPublishSettings: _*) + .settings(examplesSettings*) + .settings(noPublishSettings*) .settings(disableScala(List("3"))) .settings( name := "examples", moduleName := "kebs-examples" ) -lazy val benchmarks = project - .in(file("benchmarks")) - .dependsOn(sprayJsonSupport) - .enablePlugins(JmhPlugin) - .settings(benchmarkSettings: _*) - .settings(noPublishSettings: _*) - .settings( - name := "benchmarks", - moduleName := "kebs-benchmarks" - ) - lazy val instances = project .in(file("instances")) .dependsOn(core.jvm) - .settings(instancesSettings: _*) - .settings(publishSettings: _*) + .settings(instancesSettings*) + .settings(publishSettings*) .settings( name := "instances", description := "Standard type mappings", moduleName := "kebs-instances" ) +lazy val enumSupport = project + .in(file("enum")) + .dependsOn(core.jvm) + .settings(enumSettings*) + .settings(publishSettings*) + .settings( + name := "enum", + moduleName := "kebs-enum" + ) + +lazy val enumeratumSupport = project + .in(file("enumeratum")) + .dependsOn(core.jvm) + .settings(enumeratumSettings*) + .settings(publishSettings*) + .settings( + name := "enumeratum", + moduleName := "kebs-enumeratum" + ) + + lazy val kebs = project .in(file(".")) .aggregate( @@ -568,8 +546,6 @@ lazy val kebs = project opaque.js, core.jvm, core.js, - macroUtils.jvm, - macroUtils.js, slickSupport, doobieSupport, sprayJsonMacros, @@ -583,9 +559,11 @@ lazy val kebs = project http4sSupport, http4sStirSupport, taggedMeta, - instances + instances, + enumSupport, + enumeratumSupport ) - .settings(baseSettings: _*) + .settings(baseSettings*) .settings(noPublishSettings) .settings( name := "kebs", diff --git a/circe/src/main/scala-2/KebsEnumFormats.scala b/circe/src/main/scala-2/KebsEnumFormats.scala deleted file mode 100644 index 5f3f9dab..00000000 --- a/circe/src/main/scala-2/KebsEnumFormats.scala +++ /dev/null @@ -1,85 +0,0 @@ -package pl.iterators.kebs.circe - -import enumeratum.values.{ValueEnum, ValueEnumEntry} -import enumeratum.{Enum, EnumEntry} -import io.circe.Decoder.Result -import io.circe._ -import pl.iterators.kebs.macros.enums.{EnumOf, ValueEnumOf} - -trait CirceEnum { - @inline protected final def enumNameDeserializationError[E <: EnumEntry](`enum`: Enum[E], name: String): String = { - val enumNames = `enum`.namesToValuesMap.values.mkString(", ") - s"$name should be one of $enumNames" - } - - @inline protected final def enumValueDeserializationError[E <: EnumEntry](`enum`: Enum[E], value: Json): String = { - val enumNames = `enum`.namesToValuesMap.values.mkString(", ") - s"$value should be a string of value $enumNames" - } - - protected final def enumDecoder[E <: EnumEntry](`enum`: Enum[E], _comap: String => Option[E]): Decoder[E] = - (c: HCursor) => - Decoder.decodeString.emap(str => _comap(str).toRight("")).withErrorMessage(enumValueDeserializationError(`enum`, c.value))(c) - - protected final def enumEncoder[E <: EnumEntry](`enum`: Enum[E], _map: E => String): Encoder[E] = - (obj: E) => Encoder.encodeString(_map(obj)) - - def enumDecoder[E <: EnumEntry](`enum`: Enum[E]): Decoder[E] = - enumDecoder[E](`enum`, `enum`.withNameInsensitiveOption(_)) - def enumEncoder[E <: EnumEntry](`enum`: Enum[E]): Encoder[E] = - enumEncoder[E](`enum`, (e: EnumEntry) => e.entryName) - - def lowercaseEnumDecoder[E <: EnumEntry](`enum`: Enum[E]): Decoder[E] = - enumDecoder[E](`enum`, `enum`.withNameLowercaseOnlyOption(_)) - def lowercaseEnumEncoder[E <: EnumEntry](`enum`: Enum[E]): Encoder[E] = - enumEncoder[E](`enum`, (e: EnumEntry) => e.entryName.toLowerCase) - - def uppercaseEnumDecoder[E <: EnumEntry](`enum`: Enum[E]): Decoder[E] = - enumDecoder[E](`enum`, `enum`.withNameUppercaseOnlyOption(_)) - def uppercaseEnumEncoder[E <: EnumEntry](`enum`: Enum[E]): Encoder[E] = - enumEncoder[E](`enum`, (e: EnumEntry) => e.entryName.toUpperCase()) -} - -trait CirceValueEnum { - @inline protected final def valueEnumDeserializationError[V, E <: ValueEnumEntry[V]](`enum`: ValueEnum[V, E], value: Json): String = { - val enumValues = `enum`.valuesToEntriesMap.keys.mkString(", ") - s"$value is not a member of $enumValues" - } - - def valueEnumDecoder[V, E <: ValueEnumEntry[V]](`enum`: ValueEnum[V, E])(implicit decoder: Decoder[V]): Decoder[E] = - (c: HCursor) => - decoder.emap(obj => `enum`.withValueOpt(obj).toRight("")).withErrorMessage(valueEnumDeserializationError(`enum`, c.value))(c) - - def valueEnumEncoder[V, E <: ValueEnumEntry[V]](`enum`: ValueEnum[V, E])(implicit encoder: Encoder[V]): Encoder[E] = - (obj: E) => encoder(obj.value) -} - -trait KebsEnumFormats extends CirceEnum with CirceValueEnum { - implicit def enumDecoder[E <: EnumEntry](implicit ev: EnumOf[E]): Decoder[E] = enumDecoder(ev.`enum`) - - implicit def enumEncoder[E <: EnumEntry](implicit ev: EnumOf[E]): Encoder[E] = enumEncoder(ev.`enum`) - - implicit def valueEnumDecoder[V, E <: ValueEnumEntry[V]](implicit ev: ValueEnumOf[V, E], decoder: Decoder[V]): Decoder[E] = - valueEnumDecoder(ev.valueEnum) - - implicit def valueEnumEncoder[V, E <: ValueEnumEntry[V]](implicit ev: ValueEnumOf[V, E], encoder: Encoder[V]): Encoder[E] = - valueEnumEncoder(ev.valueEnum) - - trait Uppercase extends CirceEnum { - implicit def enumDecoder[E <: EnumEntry](implicit ev: EnumOf[E]): Decoder[E] = - uppercaseEnumDecoder(ev.`enum`) - - implicit def enumEncoder[E <: EnumEntry](implicit ev: EnumOf[E]): Encoder[E] = - uppercaseEnumEncoder(ev.`enum`) - } - - trait Lowercase extends CirceEnum { - implicit def enumDecoder[E <: EnumEntry](implicit ev: EnumOf[E]): Decoder[E] = - lowercaseEnumDecoder(ev.`enum`) - - implicit def enumEncoder[E <: EnumEntry](implicit ev: EnumOf[E]): Encoder[E] = - lowercaseEnumEncoder(ev.`enum`) - } -} - -object KebsEnumFormats extends KebsEnumFormats diff --git a/circe/src/main/scala-2/pl/iterators/kebs/circe/KebsCirce.scala b/circe/src/main/scala-2/pl/iterators/kebs/circe/KebsCirce.scala index 3d977105..708f0b73 100644 --- a/circe/src/main/scala-2/pl/iterators/kebs/circe/KebsCirce.scala +++ b/circe/src/main/scala-2/pl/iterators/kebs/circe/KebsCirce.scala @@ -2,16 +2,15 @@ package pl.iterators.kebs.circe import io.circe.generic.AutoDerivation import io.circe.{Decoder, Encoder} -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.macros.CaseClass1Rep +import pl.iterators.kebs.core.instances.InstanceConverter +import pl.iterators.kebs.core.macros.{CaseClass1ToValueClass, ValueClassLike} -import scala.language.experimental.macros import scala.util.Try -trait KebsCirce extends AutoDerivation { - implicit def flatDecoder[T, A](implicit rep: CaseClass1Rep[T, A], decoder: Decoder[A]): Decoder[T] = +trait KebsCirce extends AutoDerivation with CaseClass1ToValueClass { + implicit def flatDecoder[T, A](implicit rep: ValueClassLike[T, A], decoder: Decoder[A]): Decoder[T] = decoder.emap(obj => Try(rep.apply(obj)).toEither.left.map(_.getMessage)) - implicit def flatEncoder[T, A](implicit rep: CaseClass1Rep[T, A], encoder: Encoder[A]): Encoder[T] = + implicit def flatEncoder[T, A](implicit rep: ValueClassLike[T, A], encoder: Encoder[A]): Encoder[T] = encoder.contramap(rep.unapply) implicit def instanceConverterEncoder[T, A](implicit rep: InstanceConverter[T, A], encoder: Encoder[A]): Encoder[T] = @@ -22,10 +21,6 @@ trait KebsCirce extends AutoDerivation { } object KebsCirce { - trait NoFlat extends KebsCirce { - implicit def genericNoFlatDecoder[T <: Product]: Decoder[T] = macro KebsCirceMacros.NoflatVariant.materializeDecoder[T] - implicit def genericNoFlatEncoder[T <: Product]: Encoder[T] = macro KebsCirceMacros.NoflatVariant.materializeEncoder[T] - } trait Snakified extends KebsCirce { implicit def genericSnakifiedDecoder[T <: Product]: Decoder[T] = macro KebsCirceMacros.SnakifyVariant.materializeDecoder[T] diff --git a/circe/src/main/scala-2/KebsCirceMacros.scala b/circe/src/main/scala-2/pl/iterators/kebs/circe/KebsCirceMacros.scala similarity index 87% rename from circe/src/main/scala-2/KebsCirceMacros.scala rename to circe/src/main/scala-2/pl/iterators/kebs/circe/KebsCirceMacros.scala index 77776a13..12e1a302 100644 --- a/circe/src/main/scala-2/KebsCirceMacros.scala +++ b/circe/src/main/scala-2/pl/iterators/kebs/circe/KebsCirceMacros.scala @@ -1,10 +1,9 @@ package pl.iterators.kebs.circe -import io.circe.generic.extras.Configuration import io.circe.{Decoder, Encoder} -import pl.iterators.kebs.macros.MacroUtils +import pl.iterators.kebs.core.macros.MacroUtils +import pl.iterators.kebs.core.macros.namingconventions.SnakifyVariant.snakify -import scala.collection.immutable.Seq import scala.reflect.macros.whitebox class KebsCirceMacros(override val c: whitebox.Context) extends MacroUtils { @@ -19,7 +18,7 @@ class KebsCirceMacros(override val c: whitebox.Context) extends MacroUtils { case Nil => q"""_root_.io.circe.Decoder.decodeJsonObject.emap(obj => if(obj.isEmpty) Right(${T.termSymbol}) else Left("Empty JsonObject"))""" case _1 :: Nil => - if (preferFlat && (isLookingFor(decoderOf(T)) && !noflat(T))) + if (preferFlat && (isLookingFor(decoderOf(T)))) c.abort(c.enclosingPosition, "Flat format preferred") else _materializeDecoder(T, List(_1)) @@ -51,7 +50,7 @@ class KebsCirceMacros(override val c: whitebox.Context) extends MacroUtils { case Nil => q"""_root_.io.circe.Encoder.instance[${T.typeSymbol}](_ => _root_.io.circe.Json.fromJsonObject(_root_.io.circe.JsonObject.empty))""" case _1 :: Nil => - if (preferFlat && (isLookingFor(encoderOf(T)) && !noflat(T))) + if (preferFlat && (isLookingFor(encoderOf(T)))) c.abort(c.enclosingPosition, "Flat format preferred") else _materializeEncoder(T, List(_1)) @@ -75,9 +74,7 @@ class KebsCirceMacros(override val c: whitebox.Context) extends MacroUtils { } } - private val noflatType = typeOf[noflat] private def isLookingFor(t: Type) = c.enclosingImplicits.headOption.exists(_.pt.typeSymbol == t.typeSymbol) - private def noflat(t: Type) = t.typeSymbol.annotations.exists(_.tree.tpe =:= noflatType) private val decoderType = typeOf[Decoder[_]] private val encoderType = typeOf[Encoder[_]] private def decoderOf(p: Type) = appliedType(decoderType, p) @@ -94,10 +91,6 @@ class KebsCirceMacros(override val c: whitebox.Context) extends MacroUtils { object KebsCirceMacros { - class NoflatVariant(context: whitebox.Context) extends KebsCirceMacros(context) { - override protected val preferFlat = false - } - class CapitalizedCamelCase(context: whitebox.Context) extends KebsCirceMacros(context) { import c.universe._ @@ -110,7 +103,6 @@ object KebsCirceMacros { class SnakifyVariant(context: whitebox.Context) extends KebsCirceMacros(context) { - import pl.iterators.kebs.macros.namingconventions.SnakifyVariant.snakify import c.universe._ protected override val semiAutoNamingStrategy: Tree = q"implicit lazy val __config: _root_.io.circe.generic.extras.Configuration = _root_.io.circe.generic.extras.Configuration.default.withSnakeCaseMemberNames" diff --git a/circe/src/main/scala-2/pl/iterators/kebs/circe/KebsEnumFormats.scala b/circe/src/main/scala-2/pl/iterators/kebs/circe/KebsEnumFormats.scala new file mode 100644 index 00000000..af3b53d5 --- /dev/null +++ b/circe/src/main/scala-2/pl/iterators/kebs/circe/KebsEnumFormats.scala @@ -0,0 +1,82 @@ +package pl.iterators.kebs.circe + +import io.circe._ +import pl.iterators.kebs.core.enums.{EnumLike, ValueEnumLike, ValueEnumLikeEntry} + +trait CirceEnum { + @inline protected final def enumNameDeserializationError[E](`enum`: EnumLike[E], name: String): String = { + val enumNames = `enum`.getNamesToValuesMap.values.mkString(", ") + s"$name should be one of $enumNames" + } + + @inline protected final def enumValueDeserializationError[E](`enum`: EnumLike[E], value: Json): String = { + val enumNames = `enum`.getNamesToValuesMap.values.mkString(", ") + s"$value should be a string of value $enumNames" + } + + protected final def enumDecoder[E](`enum`: EnumLike[E], _comap: String => Option[E]): Decoder[E] = + (c: HCursor) => + Decoder.decodeString.emap(str => _comap(str).toRight("")).withErrorMessage(enumValueDeserializationError(`enum`, c.value))(c) + + protected final def enumEncoder[E](`enum`: EnumLike[E], _map: E => String): Encoder[E] = + (obj: E) => Encoder.encodeString(_map(obj)) + + def enumDecoder[E](`enum`: EnumLike[E]): Decoder[E] = + enumDecoder[E](`enum`, `enum`.withNameInsensitiveOption(_)) + def enumEncoder[E](`enum`: EnumLike[E]): Encoder[E] = + enumEncoder[E](`enum`, (e: E) => e.toString) + + def lowercaseEnumDecoder[E](`enum`: EnumLike[E]): Decoder[E] = + enumDecoder[E](`enum`, `enum`.withNameLowercaseOnlyOption(_)) + def lowercaseEnumEncoder[E](`enum`: EnumLike[E]): Encoder[E] = + enumEncoder[E](`enum`, (e: E) => e.toString.toLowerCase) + + def uppercaseEnumDecoder[E](`enum`: EnumLike[E]): Decoder[E] = + enumDecoder[E](`enum`, `enum`.withNameUppercaseOnlyOption(_)) + def uppercaseEnumEncoder[E](`enum`: EnumLike[E]): Encoder[E] = + enumEncoder[E](`enum`, (e: E) => e.toString.toUpperCase()) +} + +trait CirceValueEnum { + @inline protected final def valueEnumDeserializationError[V, E <: ValueEnumLikeEntry[V]](`enum`: ValueEnumLike[V, E], value: Json): String = { + val enumValues = `enum`.getValuesToEntriesMap.keys.mkString(", ") + s"$value is not a member of $enumValues" + } + + def valueEnumDecoder[V, E <: ValueEnumLikeEntry[V]](`enum`: ValueEnumLike[V, E])(implicit decoder: Decoder[V]): Decoder[E] = + (c: HCursor) => + decoder.emap(obj => `enum`.withValueOption(obj).toRight("")).withErrorMessage(valueEnumDeserializationError(`enum`, c.value))(c) + + def valueEnumEncoder[V, E <: ValueEnumLikeEntry[V]](`enum`: ValueEnumLike[V, E])(implicit encoder: Encoder[V]): Encoder[E] = + (obj: E) => encoder(obj.value) +} + +trait KebsEnumFormats extends CirceEnum with CirceValueEnum { + implicit def enumDecoderImpl[E](implicit ev: EnumLike[E]): Decoder[E] = enumDecoder(ev) + + implicit def enumEncoderImpl[E](implicit ev: EnumLike[E]): Encoder[E] = enumEncoder(ev) + + implicit def valueEnumDecoderImpl[V, E <: ValueEnumLikeEntry[V]](implicit ev: ValueEnumLike[V, E], decoder: Decoder[V]): Decoder[E] = + valueEnumDecoder(ev) + + implicit def valueEnumEncoderImpl[V, E <: ValueEnumLikeEntry[V]](implicit ev: ValueEnumLike[V, E], encoder: Encoder[V]): Encoder[E] = + valueEnumEncoder(ev) + + trait Uppercase extends CirceEnum { + implicit def enumDecoderImpl[E](implicit ev: EnumLike[E]): Decoder[E] = + uppercaseEnumDecoder(ev) + + implicit def enumEncoderImpl[E](implicit ev: EnumLike[E]): Encoder[E] = + uppercaseEnumEncoder(ev) + } + + trait Lowercase extends CirceEnum { + implicit def enumDecoderImpl[E](implicit ev: EnumLike[E]): Decoder[E] = + lowercaseEnumDecoder(ev) + + implicit def enumEncoderImpl[E](implicit ev: EnumLike[E]): Encoder[E] = + lowercaseEnumEncoder(ev) + } +} + +object KebsEnumFormats extends KebsEnumFormats diff --git a/circe/src/main/scala-3/pl/iterators/kebs/circe/KebsCirce.scala b/circe/src/main/scala-3/pl/iterators/kebs/circe/KebsCirce.scala index a6219081..6b232beb 100644 --- a/circe/src/main/scala-3/pl/iterators/kebs/circe/KebsCirce.scala +++ b/circe/src/main/scala-3/pl/iterators/kebs/circe/KebsCirce.scala @@ -3,21 +3,14 @@ package pl.iterators.kebs.circe import io.circe.{ Decoder, Encoder } import scala.deriving._ import scala.util.Try -import scala.quoted.Quotes -import io.circe.HCursor -import pl.iterators.kebs.macros.CaseClass1Rep -import pl.iterators.kebs.instances.InstanceConverter -import io.circe.generic.AutoDerivation -import scala.quoted.Type import io.circe.derivation.ConfiguredDecoder import io.circe.derivation.Configuration -import io.circe.Derivation -import io.circe.DecoderDerivation -import io.circe.EncoderDerivation import io.circe.derivation.ConfiguredEncoder -import scala.NonEmptyTuple -private[circe] trait KebsAutoDerivation { +import pl.iterators.kebs.core.macros.{CaseClass1ToValueClass, ValueClassLike} +import pl.iterators.kebs.core.instances.InstanceConverter + +private[circe] trait KebsAutoDerivation extends CaseClass1ToValueClass { implicit val configuration: Configuration = Configuration.default @@ -29,11 +22,11 @@ private[circe] trait KebsAutoDerivation { } trait KebsCirce extends KebsAutoDerivation { - inline given[T, A](using rep: CaseClass1Rep[T, A], decoder: Decoder[A]): Decoder[T] = { + inline given[T, A](using rep: ValueClassLike[T, A], decoder: Decoder[A]): Decoder[T] = { decoder.emap(obj => Try(rep.apply(obj)).toEither.left.map(_.getMessage)) } - inline given[T, A](using rep: CaseClass1Rep[T, A], encoder: Encoder[A]): Encoder[T] = + inline given[T, A](using rep: ValueClassLike[T, A], encoder: Encoder[A]): Encoder[T] = encoder.contramap(rep.unapply) inline given[T, A](using rep: InstanceConverter[T, A], encoder: Encoder[A]): Encoder[T] = @@ -44,8 +37,6 @@ trait KebsCirce extends KebsAutoDerivation { } object KebsCirce { - trait NoFlat extends KebsCirce { - } trait Snakified extends KebsCirce { override implicit val configuration: Configuration = Configuration.default.withSnakeCaseMemberNames diff --git a/circe/src/main/scala-3/pl/iterators/kebs/circe/KebsEnumFormats.scala b/circe/src/main/scala-3/pl/iterators/kebs/circe/KebsEnumFormats.scala index e31d8833..21e6a3b5 100644 --- a/circe/src/main/scala-3/pl/iterators/kebs/circe/KebsEnumFormats.scala +++ b/circe/src/main/scala-3/pl/iterators/kebs/circe/KebsEnumFormats.scala @@ -1,87 +1,85 @@ package pl.iterators.kebs.circe -import io.circe.Decoder.Result import io.circe._ -import pl.iterators.kebs.macros.enums.{EnumOf} import scala.reflect.Enum import scala.util.Try -import pl.iterators.kebs.enums.ValueEnum -import pl.iterators.kebs.macros.enums.ValueEnumLike -import pl.iterators.kebs.macros.enums.ValueEnumOf + +import pl.iterators.kebs.core.enums.{EnumLike, ValueEnumLike, ValueEnumLikeEntry} + trait CirceEnum { - @inline protected final def enumNameDeserializationError[E <: Enum](e: EnumOf[E], name: String): String = { - val enumNames = e.`enum`.values.mkString(", ") + @inline protected final def enumNameDeserializationError[E <: Enum](e: EnumLike[E], name: String): String = { + val enumNames = e.values.mkString(", ") s"$name should be one of $enumNames" } - @inline protected final def enumValueDeserializationError[E <: Enum](e: EnumOf[E], value: Json): String = { - val enumNames = e.`enum`.values.mkString(", ") + @inline protected final def enumValueDeserializationError[E <: Enum](e: EnumLike[E], value: Json): String = { + val enumNames = e.values.mkString(", ") s"$value should be a string of value $enumNames" } - protected final def enumDecoder[E <: Enum](e: EnumOf[E], _comap: String => Option[E]): Decoder[E] = + protected final def enumDecoder[E <: Enum](e: EnumLike[E], _comap: String => Option[E]): Decoder[E] = (c: HCursor) => Decoder.decodeString.emap(str => _comap(str).toRight("")) .withErrorMessage(enumValueDeserializationError(e, c.value))(c) - protected final def enumEncoder[E <: Enum](e: EnumOf[E], _map: E => String): Encoder[E] = + protected final def enumEncoder[E <: Enum](e: EnumLike[E], _map: E => String): Encoder[E] = (obj: E) => Encoder.encodeString(_map(obj)) - def enumDecoder[E <: Enum](e: EnumOf[E]): Decoder[E] = - enumDecoder[E](e, s => e.`enum`.values.find(_.toString.equalsIgnoreCase(s))) + def enumDecoder[E <: Enum](e: EnumLike[E]): Decoder[E] = + enumDecoder[E](e, s => e.values.find(_.toString.equalsIgnoreCase(s))) - def enumEncoder[E <: Enum](e: EnumOf[E]): Encoder[E] = + def enumEncoder[E <: Enum](e: EnumLike[E]): Encoder[E] = enumEncoder[E](e, (e: Enum) => e.toString) - def lowercaseEnumDecoder[E <: Enum](e: EnumOf[E]): Decoder[E] = - enumDecoder[E](e, s => e.`enum`.values.find(_.toString.toLowerCase == s)) - def lowercaseEnumEncoder[E <: Enum](e: EnumOf[E]): Encoder[E] = + def lowercaseEnumDecoder[E <: Enum](e: EnumLike[E]): Decoder[E] = + enumDecoder[E](e, s => e.values.find(_.toString.toLowerCase == s)) + def lowercaseEnumEncoder[E <: Enum](e: EnumLike[E]): Encoder[E] = enumEncoder[E](e, (e: Enum) => e.toString.toLowerCase) - def uppercaseEnumDecoder[E <: Enum](e: EnumOf[E]): Decoder[E] = - enumDecoder[E](e, s => e.`enum`.values.find(_.toString().toUpperCase() == s)) - def uppercaseEnumEncoder[E <: Enum](e: EnumOf[E]): Encoder[E] = + def uppercaseEnumDecoder[E <: Enum](e: EnumLike[E]): Decoder[E] = + enumDecoder[E](e, s => e.values.find(_.toString().toUpperCase() == s)) + def uppercaseEnumEncoder[E <: Enum](e: EnumLike[E]): Encoder[E] = enumEncoder[E](e, (e: Enum) => e.toString().toUpperCase()) } trait CirceValueEnum { - @inline protected final def valueEnumDeserializationError[V, E <: ValueEnum[V] with Enum](e: ValueEnumOf[V, E], value: Json): String = { - val enumValues = e.`enum`.values.map(_.value.toString()).mkString(", ") + @inline protected final def valueEnumDeserializationError[V, E <: ValueEnumLikeEntry[V]](e: ValueEnumLike[V, E], value: Json): String = { + val enumValues = e.values.map(_.value.toString()).mkString(", ") s"$value is not a member of $enumValues" } - def valueEnumDecoder[V, E <: ValueEnum[V] with Enum](e: ValueEnumOf[V, E])(implicit decoder: Decoder[V]): Decoder[E] = + def valueEnumDecoder[V, E <: ValueEnumLikeEntry[V]](e: ValueEnumLike[V, E])(implicit decoder: Decoder[V]): Decoder[E] = (c: HCursor) => - decoder.emap(obj => Try(e.`enum`.valueOf(obj)).toOption.toRight("")).withErrorMessage(valueEnumDeserializationError(e, c.value))(c) + decoder.emap(obj => Try(e.valueOf(obj)).toOption.toRight("")).withErrorMessage(valueEnumDeserializationError(e, c.value))(c) - def valueEnumEncoder[V, E <: ValueEnum[V] with Enum](e: ValueEnumOf[V, E])(implicit encoder: Encoder[V]): Encoder[E] = + def valueEnumEncoder[V, E <: ValueEnumLikeEntry[V]](e: ValueEnumLike[V, E])(implicit encoder: Encoder[V]): Encoder[E] = (obj: E) => { encoder(obj.value) } } trait KebsEnumFormats extends CirceEnum with CirceValueEnum { - implicit inline given[E <: Enum](using ev: EnumOf[E]): Decoder[E] = enumDecoder(ev) + implicit inline given[E <: Enum](using ev: EnumLike[E]): Decoder[E] = enumDecoder(ev) - implicit inline given[E <: Enum](using ev: EnumOf[E]): Encoder[E] = enumEncoder(ev) + implicit inline given[E <: Enum](using ev: EnumLike[E]): Encoder[E] = enumEncoder(ev) - implicit inline given[V, E <: ValueEnum[V] with Enum](using ev: ValueEnumOf[V, E], decoder: Decoder[V]): Decoder[E] = + implicit inline given[V, E <: ValueEnumLikeEntry[V]](using ev: ValueEnumLike[V, E], decoder: Decoder[V]): Decoder[E] = valueEnumDecoder(ev) - implicit inline given[V, E <: ValueEnum[V] with Enum](using ev: ValueEnumOf[V, E], encoder: Encoder[V]): Encoder[E] = + implicit inline given[V, E <: ValueEnumLikeEntry[V]](using ev: ValueEnumLike[V, E], encoder: Encoder[V]): Encoder[E] = valueEnumEncoder(ev) trait Uppercase extends CirceEnum { - implicit inline given[E <: Enum](using ev: EnumOf[E]): Decoder[E] = + implicit inline given[E <: Enum](using ev: EnumLike[E]): Decoder[E] = uppercaseEnumDecoder(ev) - implicit inline given[E <: Enum](using ev: EnumOf[E]): Encoder[E] = + implicit inline given[E <: Enum](using ev: EnumLike[E]): Encoder[E] = uppercaseEnumEncoder(ev) } trait Lowercase extends CirceEnum { - implicit inline given[E <: Enum](using ev: EnumOf[E]): Decoder[E] = + implicit inline given[E <: Enum](using ev: EnumLike[E]): Decoder[E] = lowercaseEnumDecoder(ev) - implicit inline given[E <: Enum](using ev: EnumOf[E]): Encoder[E] = + implicit inline given[E <: Enum](using ev: EnumLike[E]): Encoder[E] = lowercaseEnumEncoder(ev) } } diff --git a/circe/src/main/scala/noflat.scala b/circe/src/main/scala/noflat.scala deleted file mode 100644 index 4c0f20e7..00000000 --- a/circe/src/main/scala/noflat.scala +++ /dev/null @@ -1,3 +0,0 @@ -package pl.iterators.kebs.circe - -final class noflat extends scala.annotation.StaticAnnotation diff --git a/circe/src/test/scala-2/CirceFormatNoFlatTests.scala b/circe/src/test/scala-2/CirceFormatNoFlatTests.scala deleted file mode 100644 index bf799f54..00000000 --- a/circe/src/test/scala-2/CirceFormatNoFlatTests.scala +++ /dev/null @@ -1,39 +0,0 @@ -import io.circe.{Decoder, Encoder, Json} -import org.scalatest.funsuite.AnyFunSuite -import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.circe.KebsCirce -import io.circe.parser.parse - -class CirceFormatNoFlatTests extends AnyFunSuite with Matchers { - object KebsProtocol extends KebsCirce with KebsCirce.NoFlat - import KebsProtocol._ - - case class C(i: Int) - - test("No-flat format") { - val decoder = implicitly[Decoder[C]] - val encoder = implicitly[Encoder[C]] - decoder.apply(Json.fromFields(Seq("i" -> Json.fromInt(10))).hcursor) shouldBe Right(C(10)) - encoder.apply(C(10)) shouldBe Json.fromFields(Seq("i" -> Json.fromInt(10))) - } - - case class Book(name: String, chapters: List[Chapter]) - case class Chapter(name: String) - - test("compound") { - val decoder = implicitly[Decoder[Book]] - val json = - """ - | { - | "name": "Functional Programming in Scala", - | "chapters": [{"name":"first"}, {"name":"second"}] - | } - """.stripMargin - val Right(book) = parse(json) - decoder(book.hcursor) shouldBe Right( - Book( - name = "Functional Programming in Scala", - chapters = List(Chapter("first"), Chapter("second")) - )) - } -} diff --git a/circe/src/test/scala-2/CirceEnumDecoderEncoderTests.scala b/circe/src/test/scala-2/pl/iterators/kebs/circe/CirceEnumDecoderEncoderTests.scala similarity index 82% rename from circe/src/test/scala-2/CirceEnumDecoderEncoderTests.scala rename to circe/src/test/scala-2/pl/iterators/kebs/circe/CirceEnumDecoderEncoderTests.scala index 6b970772..35014276 100644 --- a/circe/src/test/scala-2/CirceEnumDecoderEncoderTests.scala +++ b/circe/src/test/scala-2/pl/iterators/kebs/circe/CirceEnumDecoderEncoderTests.scala @@ -1,22 +1,13 @@ -import enumeratum.{Enum, EnumEntry} +package pl.iterators.kebs.circe + import io.circe._ -import org.scalatest.matchers.should.Matchers import org.scalatest.funsuite.AnyFunSuite -import pl.iterators.kebs.circe.KebsEnumFormats - -class CirceEnumDecoderEncoderTests extends AnyFunSuite with Matchers { - sealed trait Greeting extends EnumEntry - - object Greeting extends Enum[Greeting] { - val values = findValues - - case object Hello extends Greeting - case object GoodBye extends Greeting - case object Hi extends Greeting - case object Bye extends Greeting - } +import org.scalatest.matchers.should.Matchers +import pl.iterators.kebs.enumeratum.KebsEnumeratum +import pl.iterators.kebs.circe.model._ +import pl.iterators.kebs.circe.model.Greeting._ - import Greeting._ +class CirceEnumDecoderEncoderTests extends AnyFunSuite with Matchers with KebsEnumeratum { object KebsProtocol extends KebsEnumFormats object KebsProtocolUppercase extends KebsEnumFormats.Uppercase diff --git a/circe/src/test/scala-2/CirceFormatCapitalizedVariantTests.scala b/circe/src/test/scala-2/pl/iterators/kebs/circe/CirceFormatCapitalizedVariantTests.scala similarity index 94% rename from circe/src/test/scala-2/CirceFormatCapitalizedVariantTests.scala rename to circe/src/test/scala-2/pl/iterators/kebs/circe/CirceFormatCapitalizedVariantTests.scala index 1cbb4dc7..ad3b9ec1 100644 --- a/circe/src/test/scala-2/CirceFormatCapitalizedVariantTests.scala +++ b/circe/src/test/scala-2/pl/iterators/kebs/circe/CirceFormatCapitalizedVariantTests.scala @@ -1,19 +1,15 @@ -import io.circe.generic.extras.decoding.ConfiguredDecoder -import io.circe.{Decoder, Encoder, Json, JsonNumber} +package pl.iterators.kebs.circe + +import io.circe.{Decoder, Encoder, Json} import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers import pl.iterators.kebs.circe.KebsCirce +import pl.iterators.kebs.circe.model._ class CirceFormatCapitalizedVariantTests extends AnyFunSuite with Matchers { object KebsProtocol extends KebsCirce with KebsCirce.Capitalized import KebsProtocol._ - case class C(anInteger: Int) - case class D(intField: Int, stringField: String) - case object F - - case class Compound(CField: C, DField: D) - test("Flat format remains unchanged") { val decoder = implicitly[Decoder[C]] val encoder = implicitly[Encoder[C]] diff --git a/circe/src/test/scala-2/CirceFormatSnakifiedVariantTests.scala b/circe/src/test/scala-2/pl/iterators/kebs/circe/CirceFormatSnakifiedVariantTests.scala similarity index 96% rename from circe/src/test/scala-2/CirceFormatSnakifiedVariantTests.scala rename to circe/src/test/scala-2/pl/iterators/kebs/circe/CirceFormatSnakifiedVariantTests.scala index 83a86e85..15a2b7b6 100644 --- a/circe/src/test/scala-2/CirceFormatSnakifiedVariantTests.scala +++ b/circe/src/test/scala-2/pl/iterators/kebs/circe/CirceFormatSnakifiedVariantTests.scala @@ -1,7 +1,10 @@ +package pl.iterators.kebs.circe + import io.circe.{Decoder, Encoder, Json} import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers import pl.iterators.kebs.circe.KebsCirce +import pl.iterators.kebs.circe.model._ import scala.Right @@ -9,12 +12,6 @@ class CirceFormatSnakifiedVariantTests extends AnyFunSuite with Matchers { object KebsProtocol extends KebsCirce with KebsCirce.Snakified import KebsProtocol._ - case class C(anInteger: Int) - case class D(intField: Int, stringField: String) - case object F - - case class Compound(CField: C, DField: D) - test("Flat format remains unchanged") { val decoder = implicitly[Decoder[C]] val encoder = implicitly[Encoder[C]] diff --git a/circe/src/test/scala-2/CirceFormatTests.scala b/circe/src/test/scala-2/pl/iterators/kebs/circe/CirceFormatTests.scala similarity index 90% rename from circe/src/test/scala-2/CirceFormatTests.scala rename to circe/src/test/scala-2/pl/iterators/kebs/circe/CirceFormatTests.scala index 8cbbdcf6..faf75f01 100644 --- a/circe/src/test/scala-2/CirceFormatTests.scala +++ b/circe/src/test/scala-2/pl/iterators/kebs/circe/CirceFormatTests.scala @@ -1,28 +1,15 @@ -import java.time.ZonedDateTime +package pl.iterators.kebs.circe import io.circe.{Decoder, Encoder, Json} import org.scalatest.funsuite.AnyFunSuite import pl.iterators.kebs.circe.KebsCirce import org.scalatest.matchers.should.Matchers +import pl.iterators.kebs.circe.model._ class CirceFormatTests extends AnyFunSuite with Matchers { object KebsProtocol extends KebsCirce import KebsProtocol._ - case class C(i: Int) - case class D(i: Int, s: String) - case class E(noFormat: ZonedDateTime) - case object F - - case class DTO1(c: C, i: Int) - case class DTO2(c: Option[C], i: Int) - case class Compound(c: C, d: D) - - case class Parametrized1[T](field: T) - case class Parametrized2[T0, T1](field1: T0, field2: T1) - - case class R(a: Int, rs: Seq[R]) - test("Flat format") { val decoder = implicitly[Decoder[C]] val encoder = implicitly[Encoder[C]] @@ -47,8 +34,8 @@ class CirceFormatTests extends AnyFunSuite with Matchers { test("Format 1") { val decoder = implicitly[Decoder[D]] val encoder = implicitly[Encoder[D]] - decoder.apply(Json.fromFields(Seq("i" -> Json.fromInt(10), "s" -> Json.fromString("abcdef"))).hcursor) shouldBe Right(D(10, "abcdef")) - encoder.apply(D(10, "abcdef")) shouldBe Json.fromFields(Seq("i" -> Json.fromInt(10), "s" -> Json.fromString("abcdef"))) + decoder.apply(Json.fromFields(Seq("intField" -> Json.fromInt(10), "stringField" -> Json.fromString("abcdef"))).hcursor) shouldBe Right(D(10, "abcdef")) + encoder.apply(D(10, "abcdef")) shouldBe Json.fromFields(Seq("intField" -> Json.fromInt(10), "stringField" -> Json.fromString("abcdef"))) } test("Format - parametrized") { @@ -80,10 +67,10 @@ class CirceFormatTests extends AnyFunSuite with Matchers { decoder.apply( Json - .fromFields(Seq("c" -> Json.fromInt(10), "d" -> Json.fromFields(Seq("i" -> Json.fromInt(100), "s" -> Json.fromString("abb"))))) + .fromFields(Seq("CField" -> Json.fromInt(10), "DField" -> Json.fromFields(Seq("intField" -> Json.fromInt(100), "stringField" -> Json.fromString("abb"))))) .hcursor) shouldBe Right(Compound(C(10), D(100, "abb"))) encoder.apply(Compound(C(5), D(10, "abcd"))) shouldBe Json.fromFields( - Seq("c" -> Json.fromInt(5), "d" -> Json.fromFields(Seq("i" -> Json.fromInt(10), "s" -> Json.fromString("abcd"))))) + Seq("CField" -> Json.fromInt(5), "DField" -> Json.fromFields(Seq("intField" -> Json.fromInt(10), "stringField" -> Json.fromString("abcd"))))) } test("Recursive format") { diff --git a/circe/src/test/scala-2/CirceValueEnumDecoderEncoderTests.scala b/circe/src/test/scala-2/pl/iterators/kebs/circe/CirceValueEnumDecoderEncoderTests.scala similarity index 63% rename from circe/src/test/scala-2/CirceValueEnumDecoderEncoderTests.scala rename to circe/src/test/scala-2/pl/iterators/kebs/circe/CirceValueEnumDecoderEncoderTests.scala index e17eaca8..75037ff2 100644 --- a/circe/src/test/scala-2/CirceValueEnumDecoderEncoderTests.scala +++ b/circe/src/test/scala-2/pl/iterators/kebs/circe/CirceValueEnumDecoderEncoderTests.scala @@ -1,21 +1,13 @@ -import enumeratum.values.{LongEnum, LongEnumEntry} +package pl.iterators.kebs.circe + import io.circe._ import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.circe.KebsEnumFormats -class CirceValueEnumDecoderEncoderTests extends AnyFunSuite with Matchers { - sealed abstract class LongGreeting(val value: Long) extends LongEnumEntry - - object LongGreeting extends LongEnum[LongGreeting] { - val values = findValues - - case object Hello extends LongGreeting(0L) - case object GoodBye extends LongGreeting(1L) - case object Hi extends LongGreeting(2L) - case object Bye extends LongGreeting(3L) - } +import pl.iterators.kebs.enumeratum.KebsValueEnumeratum +import pl.iterators.kebs.circe.model.LongGreeting +import pl.iterators.kebs.circe.model.LongGreeting._ - import LongGreeting._ +class CirceValueEnumDecoderEncoderTests extends AnyFunSuite with Matchers with KebsValueEnumeratum { object KebsProtocol extends KebsEnumFormats diff --git a/circe/src/test/scala-2/instances/TimeInstancesMixinTests.scala b/circe/src/test/scala-2/pl/iterators/kebs/circe/instances/TimeInstancesMixinTests.scala similarity index 82% rename from circe/src/test/scala-2/instances/TimeInstancesMixinTests.scala rename to circe/src/test/scala-2/pl/iterators/kebs/circe/instances/TimeInstancesMixinTests.scala index 537e4d63..b52cf60b 100644 --- a/circe/src/test/scala-2/instances/TimeInstancesMixinTests.scala +++ b/circe/src/test/scala-2/pl/iterators/kebs/circe/instances/TimeInstancesMixinTests.scala @@ -1,4 +1,4 @@ - +package pl.iterators.kebs.circe.instances import io.circe.{Decoder, Encoder, Json} import org.scalatest.funsuite.AnyFunSuite @@ -6,7 +6,8 @@ import org.scalatest.matchers.should.Matchers import pl.iterators.kebs.circe.KebsCirce import pl.iterators.kebs.instances.time.LocalDateTimeString import pl.iterators.kebs.instances.time.mixins.{DurationNanosLong, InstantEpochMilliLong} -import pl.iterators.kebs.instances.{InstanceConverter, TimeInstances} +import pl.iterators.kebs.core.instances.InstanceConverter +import pl.iterators.kebs.instances.TimeInstances import java.time._ import java.time.format.DateTimeFormatter @@ -17,8 +18,8 @@ class TimeInstancesMixinTests extends AnyFunSuite with Matchers { object TimeInstancesProtocol extends KebsCirce with InstantEpochMilliLong import TimeInstancesProtocol._ - "implicitly[CaseClass1Rep[Instant, Long]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Long, Instant]]" shouldNot typeCheck + "implicitly[ValueClassLike[Instant, Long]]" shouldNot typeCheck + "implicitly[ValueClassLike[Long, Instant]]" shouldNot typeCheck val decoder = implicitly[Decoder[Instant]] val encoder = implicitly[Encoder[Instant]] @@ -33,10 +34,10 @@ class TimeInstancesMixinTests extends AnyFunSuite with Matchers { object TimeInstancesProtocol extends KebsCirce with DurationNanosLong with InstantEpochMilliLong import TimeInstancesProtocol._ - "implicitly[CaseClass1Rep[Instant, Long]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Long, Instant]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Duration, Long]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Long, Duration]]" shouldNot typeCheck + "implicitly[ValueClassLike[Instant, Long]]" shouldNot typeCheck + "implicitly[ValueClassLike[Long, Instant]]" shouldNot typeCheck + "implicitly[ValueClassLike[Duration, Long]]" shouldNot typeCheck + "implicitly[ValueClassLike[Long, Duration]]" shouldNot typeCheck val decoder_duration = implicitly[Decoder[Duration]] val encoder_duration = implicitly[Encoder[Duration]] @@ -64,8 +65,8 @@ class TimeInstancesMixinTests extends AnyFunSuite with Matchers { } import TimeInstancesProtocol._ - "implicitly[CaseClass1Rep[LocalDateTime, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, LocalDateTime]]" shouldNot typeCheck + "implicitly[ValueClassLike[LocalDateTime, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, LocalDateTime]]" shouldNot typeCheck val encoder = implicitly[Encoder[LocalDateTime]] val decoder = implicitly[Decoder[LocalDateTime]] @@ -98,8 +99,8 @@ class TimeInstancesMixinTests extends AnyFunSuite with Matchers { } import TimeInstancesProtocol._ - "implicitly[CaseClass1Rep[LocalDateTime, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, LocalDateTime]]" shouldNot typeCheck + "implicitly[ValueClassLike[LocalDateTime, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, LocalDateTime]]" shouldNot typeCheck val encoder = implicitly[Encoder[LocalDateTime]] val decoder = implicitly[Decoder[LocalDateTime]] diff --git a/spray-json/src/test/scala/model/package.scala b/circe/src/test/scala-2/pl/iterators/kebs/circe/model/model.scala similarity index 65% rename from spray-json/src/test/scala/model/package.scala rename to circe/src/test/scala-2/pl/iterators/kebs/circe/model/model.scala index 5907590a..a4606aae 100644 --- a/spray-json/src/test/scala/model/package.scala +++ b/circe/src/test/scala-2/pl/iterators/kebs/circe/model/model.scala @@ -1,5 +1,48 @@ +package pl.iterators.kebs.circe + +import java.time.ZonedDateTime +import enumeratum.values.{LongEnum, LongEnumEntry} +import enumeratum.{Enum, EnumEntry} +import pl.iterators.kebs.core.enums.ValueEnumLikeEntry + package object model { + sealed abstract class LongGreeting(val value: Long) extends LongEnumEntry with ValueEnumLikeEntry[Long] + + object LongGreeting extends LongEnum[LongGreeting] { + val values = findValues + + case object Hello extends LongGreeting(0L) + case object GoodBye extends LongGreeting(1L) + case object Hi extends LongGreeting(2L) + case object Bye extends LongGreeting(3L) + } + + sealed trait Greeting extends EnumEntry + + object Greeting extends Enum[Greeting] { + val values = findValues + + case object Hello extends Greeting + case object GoodBye extends Greeting + case object Hi extends Greeting + case object Bye extends Greeting + } + + case class C(anInteger: Int) + case class D(intField: Int, stringField: String) + case class E(noFormat: ZonedDateTime) + case object F + + case class DTO1(c: C, i: Int) + case class DTO2(c: Option[C], i: Int) + case class Compound(CField: C, DField: D) + + case class Parametrized1[T](field: T) + case class Parametrized2[T0, T1](field1: T0, field2: T1) + + case class R(a: Int, rs: Seq[R]) + case class F1(f1: String) extends AnyVal case class ClassWith23Fields( @@ -27,6 +70,7 @@ package object model { f22: String, f23: Boolean ) + object ClassWith23Fields { val Example = ClassWith23Fields( f1 = F1("f1 value"), @@ -80,6 +124,7 @@ package object model { f22: String, f23: Boolean ) + object ClassWith23FieldsNested { val Example: ClassWith23FieldsNested = ClassWith23FieldsNested( f1 = F1("f1 value"), diff --git a/circe/src/test/scala-3/CirceFormatNoFlatTests.scala b/circe/src/test/scala-3/CirceFormatNoFlatTests.scala deleted file mode 100644 index c2c5d451..00000000 --- a/circe/src/test/scala-3/CirceFormatNoFlatTests.scala +++ /dev/null @@ -1,43 +0,0 @@ -import io.circe.{Decoder, Encoder, Json} -import org.scalatest.funsuite.AnyFunSuite -import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.circe.KebsCirce -import io.circe.parser.parse -import io.circe.derivation.ConfiguredDecoder -import io.circe.derivation.Configuration -import model.ClassWith23Fields - - -class CirceFormatNoFlatTests extends AnyFunSuite with Matchers { - object KebsProtocol extends KebsCirce with KebsCirce.NoFlat - import KebsProtocol._ - -case class C(i: Int) - - - test("No-flat format") { - val decoder = implicitly[Decoder[C]] - val encoder = implicitly[Encoder[C]] - decoder.apply(Json.fromFields(Seq("i" -> Json.fromInt(10))).hcursor) shouldBe Right(C(10)) - encoder.apply(C(10)) shouldBe Json.fromFields(Seq("i" -> Json.fromInt(10))) - } - - case class Book(name: String, chapters: List[Chapter]) - case class Chapter(name: String) - - test("compound") { - val decoder = implicitly[Decoder[Book]] - val json = - """ - | { - | "name": "Functional Programming in Scala", - | "chapters": [{"name":"first"}, {"name":"second"}] - | } - """.stripMargin - parse(json).flatMap(b => decoder(b.hcursor)) shouldBe Right( - Book( - name = "Functional Programming in Scala", - chapters = List(Chapter("first"), Chapter("second")) - )) - } -} diff --git a/circe/src/test/scala-3/CirceEnumDecoderEncoderTests.scala b/circe/src/test/scala-3/pl/iterators/kebs/circe/CirceEnumDecoderEncoderTests.scala similarity index 90% rename from circe/src/test/scala-3/CirceEnumDecoderEncoderTests.scala rename to circe/src/test/scala-3/pl/iterators/kebs/circe/CirceEnumDecoderEncoderTests.scala index d5a67b0d..6c17796a 100644 --- a/circe/src/test/scala-3/CirceEnumDecoderEncoderTests.scala +++ b/circe/src/test/scala-3/pl/iterators/kebs/circe/CirceEnumDecoderEncoderTests.scala @@ -1,22 +1,20 @@ +package pl.iterators.kebs.circe + import io.circe._ import org.scalatest.matchers.should.Matchers import org.scalatest.funsuite.AnyFunSuite -import pl.iterators.kebs.circe.KebsEnumFormats -import scala.reflect.Enum +import pl.iterators.kebs.circe.model.Greeting._ +import pl.iterators.kebs.circe.model.Greeting +import pl.iterators.kebs.enums.KebsEnum -class CirceEnumDecoderEncoderTests extends AnyFunSuite with Matchers { +class CirceEnumDecoderEncoderTests extends AnyFunSuite with Matchers with KebsEnum { - enum Greeting { - case Hello, GoodBye, Hi, Bye - } object KebsProtocol extends KebsEnumFormats object KebsProtocolUppercase extends KebsEnumFormats.Uppercase object KebsProtocolLowercase extends KebsEnumFormats.Lowercase - - import Greeting._ test("enum JsonFormat") { import KebsProtocol._ val decoder = implicitly[Decoder[Greeting]] diff --git a/circe/src/test/scala-3/CirceFormatCapitalizedVariantTests.scala b/circe/src/test/scala-3/pl/iterators/kebs/circe/CirceFormatCapitalizedVariantTests.scala similarity index 93% rename from circe/src/test/scala-3/CirceFormatCapitalizedVariantTests.scala rename to circe/src/test/scala-3/pl/iterators/kebs/circe/CirceFormatCapitalizedVariantTests.scala index a0c4876f..56b2410f 100644 --- a/circe/src/test/scala-3/CirceFormatCapitalizedVariantTests.scala +++ b/circe/src/test/scala-3/pl/iterators/kebs/circe/CirceFormatCapitalizedVariantTests.scala @@ -1,20 +1,15 @@ -import io.circe.{Decoder, Encoder, Json, JsonNumber} +package pl.iterators.kebs.circe + +import io.circe.{Decoder, Encoder, Json} import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.circe.KebsCirce -import io.circe.derivation.Configuration import io.circe.derivation.ConfiguredDecoder +import pl.iterators.kebs.circe.model._ class CirceFormatCapitalizedVariantTests extends AnyFunSuite with Matchers { object KebsProtocol extends KebsCirce with KebsCirce.Capitalized import KebsProtocol.{given, _} - case class C(anInteger: Int) - case class D(intField: Int, stringField: String) - case object F - - case class Compound(CField: C, DField: D) - test("Flat format remains unchanged") { val decoder = implicitly[Decoder[C]] val encoder = implicitly[Encoder[C]] diff --git a/circe/src/test/scala-3/CirceFormatSnakifiedVariantTests.scala b/circe/src/test/scala-3/pl/iterators/kebs/circe/CirceFormatSnakifiedVariantTests.scala similarity index 94% rename from circe/src/test/scala-3/CirceFormatSnakifiedVariantTests.scala rename to circe/src/test/scala-3/pl/iterators/kebs/circe/CirceFormatSnakifiedVariantTests.scala index 7f0aa9ba..b3478e73 100644 --- a/circe/src/test/scala-3/CirceFormatSnakifiedVariantTests.scala +++ b/circe/src/test/scala-3/pl/iterators/kebs/circe/CirceFormatSnakifiedVariantTests.scala @@ -1,23 +1,15 @@ +package pl.iterators.kebs.circe + import io.circe.{Decoder, Encoder, Json} import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.circe.KebsCirce - +import pl.iterators.kebs.circe.model._ import scala.Right -import io.circe.derivation.Configuration - - class CirceFormatSnakifiedVariantTests extends AnyFunSuite with Matchers { object KebsProtocol extends KebsCirce with KebsCirce.Snakified import KebsProtocol.{given, _} - case class C(anInteger: Int) - case class D(intField: Int, stringField: String) - case object F - - case class Compound(CField: C, DField: D) - test("Flat format remains unchanged") { val decoder = implicitly[Decoder[C]] val encoder = implicitly[Encoder[C]] diff --git a/circe/src/test/scala-3/CirceFormatTests.scala b/circe/src/test/scala-3/pl/iterators/kebs/circe/CirceFormatTests.scala similarity index 90% rename from circe/src/test/scala-3/CirceFormatTests.scala rename to circe/src/test/scala-3/pl/iterators/kebs/circe/CirceFormatTests.scala index b5b7a84b..84827e61 100644 --- a/circe/src/test/scala-3/CirceFormatTests.scala +++ b/circe/src/test/scala-3/pl/iterators/kebs/circe/CirceFormatTests.scala @@ -1,29 +1,17 @@ -import java.time.ZonedDateTime +package pl.iterators.kebs.circe import io.circe.{Decoder, Encoder, Json} import org.scalatest.funsuite.AnyFunSuite -import pl.iterators.kebs.circe.KebsCirce import org.scalatest.matchers.should.Matchers import scala.util.Try import io.circe.derivation.ConfiguredDecoder import io.circe.derivation.Configuration +import pl.iterators.kebs.circe.model._ + class CirceFormatTests extends AnyFunSuite with Matchers { object KebsProtocol extends KebsCirce import KebsProtocol.{given, _} - - case class C(i: Int) - case class D(i: Int, s: String) - case class E(noFormat: ZonedDateTime) - case object F - - case class DTO1(c: C, i: Int) - case class DTO2(c: Option[C], i: Int) - case class Compound(c: C, d: D) - - case class Parametrized1[T](field: T) - case class Parametrized2[T0, T1](field1: T0, field2: T1) - // https://github.com/circe/circe/issues/1980 case class R(a: Int, rs: Seq[R]) derives Decoder, Encoder.AsObject @@ -50,8 +38,8 @@ class CirceFormatTests extends AnyFunSuite with Matchers { test("Format 1") { val decoder = implicitly[Decoder[D]] val encoder = implicitly[Encoder[D]] - decoder.apply(Json.fromFields(Seq("i" -> Json.fromInt(10), "s" -> Json.fromString("abcdef"))).hcursor) shouldBe Right(D(10, "abcdef")) - encoder.apply(D(10, "abcdef")) shouldBe Json.fromFields(Seq("i" -> Json.fromInt(10), "s" -> Json.fromString("abcdef"))) + decoder.apply(Json.fromFields(Seq("intField" -> Json.fromInt(10), "stringField" -> Json.fromString("abcdef"))).hcursor) shouldBe Right(D(10, "abcdef")) + encoder.apply(D(10, "abcdef")) shouldBe Json.fromFields(Seq("intField" -> Json.fromInt(10), "stringField" -> Json.fromString("abcdef"))) } test("Format - parametrized") { @@ -83,10 +71,10 @@ class CirceFormatTests extends AnyFunSuite with Matchers { decoder.apply( Json - .fromFields(Seq("c" -> Json.fromInt(10), "d" -> Json.fromFields(Seq("i" -> Json.fromInt(100), "s" -> Json.fromString("abb"))))) + .fromFields(Seq("CField" -> Json.fromInt(10), "DField" -> Json.fromFields(Seq("intField" -> Json.fromInt(100), "stringField" -> Json.fromString("abb"))))) .hcursor) shouldBe Right(Compound(C(10), D(100, "abb"))) encoder.apply(Compound(C(5), D(10, "abcd"))) shouldBe Json.fromFields( - Seq("c" -> Json.fromInt(5), "d" -> Json.fromFields(Seq("i" -> Json.fromInt(10), "s" -> Json.fromString("abcd"))))) + Seq("CField" -> Json.fromInt(5), "DField" -> Json.fromFields(Seq("intField" -> Json.fromInt(10), "stringField" -> Json.fromString("abcd"))))) } test("Recursive format") { diff --git a/circe/src/test/scala-3/CirceValueEnumDecoderEncoderTests.scala b/circe/src/test/scala-3/pl/iterators/kebs/circe/CirceValueEnumDecoderEncoderTests.scala similarity index 69% rename from circe/src/test/scala-3/CirceValueEnumDecoderEncoderTests.scala rename to circe/src/test/scala-3/pl/iterators/kebs/circe/CirceValueEnumDecoderEncoderTests.scala index 53ba8e4d..aa40860b 100644 --- a/circe/src/test/scala-3/CirceValueEnumDecoderEncoderTests.scala +++ b/circe/src/test/scala-3/pl/iterators/kebs/circe/CirceValueEnumDecoderEncoderTests.scala @@ -1,20 +1,15 @@ -import enumeratum.values.{LongEnum, LongEnumEntry} +package pl.iterators.kebs.circe + import io.circe._ import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.circe.KebsEnumFormats -import pl.iterators.kebs.enums.ValueEnum -class CirceValueEnumDecoderEncoderTests extends AnyFunSuite with Matchers { +import pl.iterators.kebs.enums.KebsValueEnum +import pl.iterators.kebs.circe.model.LongGreeting - object KebsProtocol extends KebsEnumFormats +class CirceValueEnumDecoderEncoderTests extends AnyFunSuite with Matchers with KebsValueEnum { - enum LongGreeting(val value: Long) extends ValueEnum[Long] { - case Hello extends LongGreeting(0L) - case GoodBye extends LongGreeting(1L) - case Hi extends LongGreeting(2L) - case Bye extends LongGreeting(3L) - } + object KebsProtocol extends KebsEnumFormats import LongGreeting._ diff --git a/circe/src/test/scala-3/instances/TimeInstancesMixinTests.scala b/circe/src/test/scala-3/pl/iterators/kebs/circe/instances/TimeInstancesMixinTests.scala similarity index 80% rename from circe/src/test/scala-3/instances/TimeInstancesMixinTests.scala rename to circe/src/test/scala-3/pl/iterators/kebs/circe/instances/TimeInstancesMixinTests.scala index d1f165b6..6b3bbf98 100644 --- a/circe/src/test/scala-3/instances/TimeInstancesMixinTests.scala +++ b/circe/src/test/scala-3/pl/iterators/kebs/circe/instances/TimeInstancesMixinTests.scala @@ -1,24 +1,26 @@ - +package pl.iterators.kebs.circe.instances import io.circe.{Decoder, Encoder, Json} import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers import pl.iterators.kebs.circe.KebsCirce -import pl.iterators.kebs.instances.time.LocalDateTimeString -import pl.iterators.kebs.instances.time.mixins.{DurationNanosLong, InstantEpochMilliLong} -import pl.iterators.kebs.instances.{InstanceConverter, TimeInstances} import java.time._ import java.time.format.DateTimeFormatter +import pl.iterators.kebs.core.instances.InstanceConverter +import pl.iterators.kebs.instances.TimeInstances +import pl.iterators.kebs.instances.time.LocalDateTimeString +import pl.iterators.kebs.instances.time.mixins.{InstantEpochMilliLong, DurationNanosLong} + class TimeInstancesMixinTests extends AnyFunSuite with Matchers { test("Instant epoch milli format") { object TimeInstancesProtocol extends KebsCirce with InstantEpochMilliLong import TimeInstancesProtocol.{given, _} - "implicitly[CaseClass1Rep[Instant, Long]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Long, Instant]]" shouldNot typeCheck + "implicitly[ValueClassLike[Instant, Long]]" shouldNot typeCheck + "implicitly[ValueClassLike[Long, Instant]]" shouldNot typeCheck val decoder = implicitly[Decoder[Instant]] val encoder = implicitly[Encoder[Instant]] @@ -33,10 +35,10 @@ class TimeInstancesMixinTests extends AnyFunSuite with Matchers { object TimeInstancesProtocol extends KebsCirce with DurationNanosLong with InstantEpochMilliLong import TimeInstancesProtocol.{given, _} - "implicitly[CaseClass1Rep[Instant, Long]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Long, Instant]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Duration, Long]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Long, Duration]]" shouldNot typeCheck + "implicitly[ValueClassLike[Instant, Long]]" shouldNot typeCheck + "implicitly[ValueClassLike[Long, Instant]]" shouldNot typeCheck + "implicitly[ValueClassLike[Duration, Long]]" shouldNot typeCheck + "implicitly[ValueClassLike[Long, Duration]]" shouldNot typeCheck val decoder_duration = implicitly[Decoder[Duration]] val encoder_duration = implicitly[Encoder[Duration]] @@ -64,8 +66,8 @@ class TimeInstancesMixinTests extends AnyFunSuite with Matchers { } import TimeInstancesProtocol.{given, _} - "implicitly[CaseClass1Rep[LocalDateTime, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, LocalDateTime]]" shouldNot typeCheck + "implicitly[ValueClassLike[LocalDateTime, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, LocalDateTime]]" shouldNot typeCheck val encoder = implicitly[Encoder[LocalDateTime]] val decoder = implicitly[Decoder[LocalDateTime]] @@ -98,8 +100,8 @@ class TimeInstancesMixinTests extends AnyFunSuite with Matchers { } import TimeInstancesProtocol.{given, _} - "implicitly[CaseClass1Rep[LocalDateTime, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, LocalDateTime]]" shouldNot typeCheck + "implicitly[ValueClassLike[LocalDateTime, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, LocalDateTime]]" shouldNot typeCheck val encoder = implicitly[Encoder[LocalDateTime]] val decoder = implicitly[Decoder[LocalDateTime]] diff --git a/circe/src/test/scala-3/model/model.scala b/circe/src/test/scala-3/pl/iterators/kebs/circe/model/model.scala similarity index 75% rename from circe/src/test/scala-3/model/model.scala rename to circe/src/test/scala-3/pl/iterators/kebs/circe/model/model.scala index f2d030b3..e76a099e 100644 --- a/circe/src/test/scala-3/model/model.scala +++ b/circe/src/test/scala-3/pl/iterators/kebs/circe/model/model.scala @@ -1,8 +1,37 @@ +package pl.iterators.kebs.circe + +import java.time.ZonedDateTime +import pl.iterators.kebs.core.enums.ValueEnumLikeEntry -import io.circe.Decoder -import io.circe.Encoder package object model { + case class E(noFormat: ZonedDateTime) + + case class DTO1(c: C, i: Int) + case class DTO2(c: Option[C], i: Int) + + case class Parametrized1[T](field: T) + case class Parametrized2[T0, T1](field1: T0, field2: T1) + + + case class C(anInteger: Int) + case class D(intField: Int, stringField: String) + case object F + + case class Compound(CField: C, DField: D) + + enum Greeting { + case Hello, GoodBye, Hi, Bye + } + + enum LongGreeting(val value: Long) extends ValueEnumLikeEntry[Long] { + case Hello extends LongGreeting(0L) + case GoodBye extends LongGreeting(1L) + case Hi extends LongGreeting(2L) + case Bye extends LongGreeting(3L) + } + + case class F1(f1: String) case class ClassWith23Fields( diff --git a/circe/src/test/scala/instances/NetInstancesTests.scala b/circe/src/test/scala/pl/iterators/kebs/circe/instances/NetInstancesTests.scala similarity index 78% rename from circe/src/test/scala/instances/NetInstancesTests.scala rename to circe/src/test/scala/pl/iterators/kebs/circe/instances/NetInstancesTests.scala index 8a62ed22..a5aa939f 100644 --- a/circe/src/test/scala/instances/NetInstancesTests.scala +++ b/circe/src/test/scala/pl/iterators/kebs/circe/instances/NetInstancesTests.scala @@ -1,4 +1,4 @@ -package instances +package pl.iterators.kebs.circe.instances import io.circe.{Decoder, Encoder, Json} import org.scalatest.funsuite.AnyFunSuite @@ -27,9 +27,9 @@ class NetInstancesTests extends AnyFunSuite with Matchers with KebsCirce with UR decoder(Json.fromString(value).hcursor) shouldBe a [Left[_, _]] } - test("No CaseClass1Rep implicits derived") { + test("No ValueClassLike implicits derived") { - "implicitly[CaseClass1Rep[URI, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, URI]]" shouldNot typeCheck + "implicitly[ValueClassLike[URI, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, URI]]" shouldNot typeCheck } } diff --git a/circe/src/test/scala/instances/TimeInstancesTests.scala b/circe/src/test/scala/pl/iterators/kebs/circe/instances/TimeInstancesTests.scala similarity index 79% rename from circe/src/test/scala/instances/TimeInstancesTests.scala rename to circe/src/test/scala/pl/iterators/kebs/circe/instances/TimeInstancesTests.scala index 53c114b2..62d7cf60 100644 --- a/circe/src/test/scala/instances/TimeInstancesTests.scala +++ b/circe/src/test/scala/pl/iterators/kebs/circe/instances/TimeInstancesTests.scala @@ -1,50 +1,49 @@ - package instances + package pl.iterators.kebs.circe.instances - import io.circe.{Decoder, Encoder, Json} +import io.circe.{Decoder, Encoder, Json} import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers import pl.iterators.kebs.circe.KebsCirce - import pl.iterators.kebs.instances.InstanceConverter.DecodeErrorException import pl.iterators.kebs.instances.TimeInstances import java.time._ class TimeInstancesTests extends AnyFunSuite with Matchers with KebsCirce with TimeInstances { - test("No CaseClass1Rep implicits derived") { - - "implicitly[CaseClass1Rep[DayOfWeek, Int]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Int, DayOfWeek]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Duration, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, Duration]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Instant, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, Instant]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[LocalDate, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, LocalDate]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[LocalDateTime, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, LocalDateTime]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[LocalTime, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, LocalTime]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Month, Int]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Int, Month]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[MonthDay, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, MonthDay]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[OffsetDateTime, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, OffsetDateTime]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[OffsetTime, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, OffsetTime]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Period, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, Period]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Year, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, Year]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[YearMonth, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, YearMonth]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[ZoneId, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, ZoneId]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[ZoneOffset, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, ZoneOffset]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[ZonedDateTime, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, ZonedDateTime]]" shouldNot typeCheck + test("No ValueClassLike implicits derived") { + + "implicitly[ValueClassLike[DayOfWeek, Int]]" shouldNot typeCheck + "implicitly[ValueClassLike[Int, DayOfWeek]]" shouldNot typeCheck + "implicitly[ValueClassLike[Duration, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, Duration]]" shouldNot typeCheck + "implicitly[ValueClassLike[Instant, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, Instant]]" shouldNot typeCheck + "implicitly[ValueClassLike[LocalDate, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, LocalDate]]" shouldNot typeCheck + "implicitly[ValueClassLike[LocalDateTime, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, LocalDateTime]]" shouldNot typeCheck + "implicitly[ValueClassLike[LocalTime, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, LocalTime]]" shouldNot typeCheck + "implicitly[ValueClassLike[Month, Int]]" shouldNot typeCheck + "implicitly[ValueClassLike[Int, Month]]" shouldNot typeCheck + "implicitly[ValueClassLike[MonthDay, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, MonthDay]]" shouldNot typeCheck + "implicitly[ValueClassLike[OffsetDateTime, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, OffsetDateTime]]" shouldNot typeCheck + "implicitly[ValueClassLike[OffsetTime, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, OffsetTime]]" shouldNot typeCheck + "implicitly[ValueClassLike[Period, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, Period]]" shouldNot typeCheck + "implicitly[ValueClassLike[Year, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, Year]]" shouldNot typeCheck + "implicitly[ValueClassLike[YearMonth, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, YearMonth]]" shouldNot typeCheck + "implicitly[ValueClassLike[ZoneId, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, ZoneId]]" shouldNot typeCheck + "implicitly[ValueClassLike[ZoneOffset, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, ZoneOffset]]" shouldNot typeCheck + "implicitly[ValueClassLike[ZonedDateTime, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, ZonedDateTime]]" shouldNot typeCheck } test("DayOfWeek standard format") { diff --git a/circe/src/test/scala/instances/UtilInstancesTests.scala b/circe/src/test/scala/pl/iterators/kebs/circe/instances/UtilInstancesTests.scala similarity index 76% rename from circe/src/test/scala/instances/UtilInstancesTests.scala rename to circe/src/test/scala/pl/iterators/kebs/circe/instances/UtilInstancesTests.scala index 332c62dc..4828a8c5 100644 --- a/circe/src/test/scala/instances/UtilInstancesTests.scala +++ b/circe/src/test/scala/pl/iterators/kebs/circe/instances/UtilInstancesTests.scala @@ -1,24 +1,23 @@ -package instances +package pl.iterators.kebs.circe.instances import io.circe.{Decoder, Encoder, Json} import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers import pl.iterators.kebs.circe.KebsCirce -import pl.iterators.kebs.instances.InstanceConverter.DecodeErrorException import pl.iterators.kebs.instances.UtilInstances import java.util.{Currency, Locale, UUID} class UtilInstancesTests extends AnyFunSuite with Matchers with KebsCirce with UtilInstances { - test("No CaseClass1Rep implicits derived") { + test("No ValueClassLike implicits derived") { - "implicitly[CaseClass1Rep[Currency, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, Currency]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Locale, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, Locale]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[UUID, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, UUID]]" shouldNot typeCheck + "implicitly[ValueClassLike[Currency, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, Currency]]" shouldNot typeCheck + "implicitly[ValueClassLike[Locale, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, Locale]]" shouldNot typeCheck + "implicitly[ValueClassLike[UUID, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, UUID]]" shouldNot typeCheck } test("Currency standard format") { diff --git a/core/src/main/scala-2.12/pl/iterators/kebs/support/FractionalSupport.scala b/core/src/main/scala-2.12/pl/iterators/kebs/core/support/FractionalSupport.scala similarity index 78% rename from core/src/main/scala-2.12/pl/iterators/kebs/support/FractionalSupport.scala rename to core/src/main/scala-2.12/pl/iterators/kebs/core/support/FractionalSupport.scala index 57a73211..ba41c9d4 100644 --- a/core/src/main/scala-2.12/pl/iterators/kebs/support/FractionalSupport.scala +++ b/core/src/main/scala-2.12/pl/iterators/kebs/core/support/FractionalSupport.scala @@ -1,14 +1,12 @@ package pl.iterators.kebs.support -import pl.iterators.kebs.macros.CaseClass1Rep - trait FractionalSupport { - implicit def fractionalFromCaseClass1Rep[A, Rep](implicit cc1Rep: CaseClass1Rep[A, Rep], + implicit def fractionalFromValueClassLike[A, Rep](implicit vcLike: ValueClassLike[A, Rep], fractionalRep: Fractional[Rep], numeric: Numeric[A]): Fractional[A] = new Fractional[A] { - override def div(x: A, y: A): A = cc1Rep.apply(fractionalRep.div(cc1Rep.unapply(x), cc1Rep.unapply(y))) + override def div(x: A, y: A): A = vcLike.apply(fractionalRep.div(vcLike.unapply(x), vcLike.unapply(y))) override def plus(x: A, y: A): A = numeric.plus(x, y) override def minus(x: A, y: A): A = numeric.minus(x, y) override def times(x: A, y: A): A = numeric.times(x, y) diff --git a/core/src/main/scala-2.12/pl/iterators/kebs/support/IntegralSupport.scala b/core/src/main/scala-2.12/pl/iterators/kebs/core/support/IntegralSupport.scala similarity index 71% rename from core/src/main/scala-2.12/pl/iterators/kebs/support/IntegralSupport.scala rename to core/src/main/scala-2.12/pl/iterators/kebs/core/support/IntegralSupport.scala index 82f6683c..8b1a3417 100644 --- a/core/src/main/scala-2.12/pl/iterators/kebs/support/IntegralSupport.scala +++ b/core/src/main/scala-2.12/pl/iterators/kebs/core/support/IntegralSupport.scala @@ -1,15 +1,13 @@ package pl.iterators.kebs.support -import pl.iterators.kebs.macros.CaseClass1Rep - trait IntegralSupport { - implicit def integralFromCaseClass1Rep[A, Rep](implicit cc1Rep: CaseClass1Rep[A, Rep], + implicit def integralFromValueClassLike[A, Rep](implicit vcLike: ValueClassLike[A, Rep], integralRep: Integral[Rep], numeric: Numeric[A]): Integral[A] = new Integral[A] { - override def quot(x: A, y: A): A = cc1Rep.apply(integralRep.quot(cc1Rep.unapply(x), cc1Rep.unapply(y))) - override def rem(x: A, y: A): A = cc1Rep.apply(integralRep.rem(cc1Rep.unapply(x), cc1Rep.unapply(y))) + override def quot(x: A, y: A): A = vcLike.apply(integralRep.quot(vcLike.unapply(x), vcLike.unapply(y))) + override def rem(x: A, y: A): A = vcLike.apply(integralRep.rem(vcLike.unapply(x), vcLike.unapply(y))) override def plus(x: A, y: A): A = numeric.plus(x, y) override def minus(x: A, y: A): A = numeric.minus(x, y) override def times(x: A, y: A): A = numeric.times(x, y) diff --git a/core/src/main/scala-2.12/pl/iterators/kebs/core/support/NumericSupport.scala b/core/src/main/scala-2.12/pl/iterators/kebs/core/support/NumericSupport.scala new file mode 100644 index 00000000..eec0c85d --- /dev/null +++ b/core/src/main/scala-2.12/pl/iterators/kebs/core/support/NumericSupport.scala @@ -0,0 +1,20 @@ +package pl.iterators.kebs.support + +trait NumericSupport { + + implicit def numericFromValueClassLike[A, Rep](implicit vcLike: ValueClassLike[A, Rep], numericRep: Numeric[Rep]): Numeric[A] = { + new Numeric[A] { + override def plus(x: A, y: A): A = vcLike.apply(numericRep.plus(vcLike.unapply(x), vcLike.unapply(y))) + override def minus(x: A, y: A): A = vcLike.apply(numericRep.minus(vcLike.unapply(x), vcLike.unapply(y))) + override def times(x: A, y: A): A = vcLike.apply(numericRep.times(vcLike.unapply(x), vcLike.unapply(y))) + override def negate(x: A): A = vcLike.apply(numericRep.negate(vcLike.unapply(x))) + override def fromInt(x: Int): A = vcLike.apply(numericRep.fromInt(x)) + override def toInt(x: A): Int = numericRep.toInt(vcLike.unapply(x)) + override def toLong(x: A): Long = numericRep.toLong(vcLike.unapply(x)) + override def toFloat(x: A): Float = numericRep.toFloat(vcLike.unapply(x)) + override def toDouble(x: A): Double = numericRep.toDouble(vcLike.unapply(x)) + override def compare(x: A, y: A): Int = numericRep.compare(vcLike.unapply(x), vcLike.unapply(y)) + } + } + +} diff --git a/core/src/main/scala-2.12/pl/iterators/kebs/support/NumericSupport.scala b/core/src/main/scala-2.12/pl/iterators/kebs/support/NumericSupport.scala deleted file mode 100644 index 74e198d7..00000000 --- a/core/src/main/scala-2.12/pl/iterators/kebs/support/NumericSupport.scala +++ /dev/null @@ -1,22 +0,0 @@ -package pl.iterators.kebs.support - -import pl.iterators.kebs.macros.CaseClass1Rep - -trait NumericSupport { - - implicit def numericFromCaseClass1Rep[A, Rep](implicit cc1Rep: CaseClass1Rep[A, Rep], numericRep: Numeric[Rep]): Numeric[A] = { - new Numeric[A] { - override def plus(x: A, y: A): A = cc1Rep.apply(numericRep.plus(cc1Rep.unapply(x), cc1Rep.unapply(y))) - override def minus(x: A, y: A): A = cc1Rep.apply(numericRep.minus(cc1Rep.unapply(x), cc1Rep.unapply(y))) - override def times(x: A, y: A): A = cc1Rep.apply(numericRep.times(cc1Rep.unapply(x), cc1Rep.unapply(y))) - override def negate(x: A): A = cc1Rep.apply(numericRep.negate(cc1Rep.unapply(x))) - override def fromInt(x: Int): A = cc1Rep.apply(numericRep.fromInt(x)) - override def toInt(x: A): Int = numericRep.toInt(cc1Rep.unapply(x)) - override def toLong(x: A): Long = numericRep.toLong(cc1Rep.unapply(x)) - override def toFloat(x: A): Float = numericRep.toFloat(cc1Rep.unapply(x)) - override def toDouble(x: A): Double = numericRep.toDouble(cc1Rep.unapply(x)) - override def compare(x: A, y: A): Int = numericRep.compare(cc1Rep.unapply(x), cc1Rep.unapply(y)) - } - } - -} diff --git a/core/src/main/scala-3/pl/iterators/kebs/support/FractionalSupport.scala b/core/src/main/scala-2.13/pl/iterators/kebs/core/support/FractionalSupport.scala similarity index 65% rename from core/src/main/scala-3/pl/iterators/kebs/support/FractionalSupport.scala rename to core/src/main/scala-2.13/pl/iterators/kebs/core/support/FractionalSupport.scala index 45f64b50..dca58245 100644 --- a/core/src/main/scala-3/pl/iterators/kebs/support/FractionalSupport.scala +++ b/core/src/main/scala-2.13/pl/iterators/kebs/core/support/FractionalSupport.scala @@ -1,14 +1,14 @@ -package pl.iterators.kebs.support +package pl.iterators.kebs.core.support -import pl.iterators.kebs.macros.CaseClass1Rep +import pl.iterators.kebs.core.macros.ValueClassLike trait FractionalSupport { - implicit def fractionalFromCaseClass1Rep[A, Rep](implicit cc1Rep: CaseClass1Rep[A, Rep], - fractionalRep: Fractional[Rep], - numeric: Numeric[A]): Fractional[A] = + implicit def fractionalFromValueClassLike[A, Rep](implicit vcLike: ValueClassLike[A, Rep], + fractionalRep: Fractional[Rep], + numeric: Numeric[A]): Fractional[A] = new Fractional[A] { - override def div(x: A, y: A): A = cc1Rep.apply(fractionalRep.div(cc1Rep.unapply(x), cc1Rep.unapply(y))) + override def div(x: A, y: A): A = vcLike.apply(fractionalRep.div(vcLike.unapply(x), vcLike.unapply(y))) override def plus(x: A, y: A): A = numeric.plus(x, y) override def minus(x: A, y: A): A = numeric.minus(x, y) override def times(x: A, y: A): A = numeric.times(x, y) diff --git a/core/src/main/scala-2.13/pl/iterators/kebs/support/IntegralSupport.scala b/core/src/main/scala-2.13/pl/iterators/kebs/core/support/IntegralSupport.scala similarity index 71% rename from core/src/main/scala-2.13/pl/iterators/kebs/support/IntegralSupport.scala rename to core/src/main/scala-2.13/pl/iterators/kebs/core/support/IntegralSupport.scala index 3fc592bb..a301d026 100644 --- a/core/src/main/scala-2.13/pl/iterators/kebs/support/IntegralSupport.scala +++ b/core/src/main/scala-2.13/pl/iterators/kebs/core/support/IntegralSupport.scala @@ -1,15 +1,15 @@ -package pl.iterators.kebs.support +package pl.iterators.kebs.core.support -import pl.iterators.kebs.macros.CaseClass1Rep +import pl.iterators.kebs.core.macros.ValueClassLike trait IntegralSupport { - implicit def integralFromCaseClass1Rep[A, Rep](implicit cc1Rep: CaseClass1Rep[A, Rep], + implicit def integralFromValueClassLike[A, Rep](implicit vcLike: ValueClassLike[A, Rep], integralRep: Integral[Rep], numeric: Numeric[A]): Integral[A] = new Integral[A] { - override def quot(x: A, y: A): A = cc1Rep.apply(integralRep.quot(cc1Rep.unapply(x), cc1Rep.unapply(y))) - override def rem(x: A, y: A): A = cc1Rep.apply(integralRep.rem(cc1Rep.unapply(x), cc1Rep.unapply(y))) + override def quot(x: A, y: A): A = vcLike.apply(integralRep.quot(vcLike.unapply(x), vcLike.unapply(y))) + override def rem(x: A, y: A): A = vcLike.apply(integralRep.rem(vcLike.unapply(x), vcLike.unapply(y))) override def plus(x: A, y: A): A = numeric.plus(x, y) override def minus(x: A, y: A): A = numeric.minus(x, y) override def times(x: A, y: A): A = numeric.times(x, y) diff --git a/core/src/main/scala-2.13/pl/iterators/kebs/core/support/NumericSupport.scala b/core/src/main/scala-2.13/pl/iterators/kebs/core/support/NumericSupport.scala new file mode 100644 index 00000000..35ce1bab --- /dev/null +++ b/core/src/main/scala-2.13/pl/iterators/kebs/core/support/NumericSupport.scala @@ -0,0 +1,23 @@ +package pl.iterators.kebs.core.support + +import pl.iterators.kebs.core.macros.ValueClassLike + +trait NumericSupport { + + implicit def numericFromValueClassLike[A, Rep](implicit vcLike: ValueClassLike[A, Rep], numericRep: Numeric[Rep]): Numeric[A] = { + new Numeric[A] { + override def plus(x: A, y: A): A = vcLike.apply(numericRep.plus(vcLike.unapply(x), vcLike.unapply(y))) + override def minus(x: A, y: A): A = vcLike.apply(numericRep.minus(vcLike.unapply(x), vcLike.unapply(y))) + override def times(x: A, y: A): A = vcLike.apply(numericRep.times(vcLike.unapply(x), vcLike.unapply(y))) + override def negate(x: A): A = vcLike.apply(numericRep.negate(vcLike.unapply(x))) + override def fromInt(x: Int): A = vcLike.apply(numericRep.fromInt(x)) + override def toInt(x: A): Int = numericRep.toInt(vcLike.unapply(x)) + override def toLong(x: A): Long = numericRep.toLong(vcLike.unapply(x)) + override def toFloat(x: A): Float = numericRep.toFloat(vcLike.unapply(x)) + override def toDouble(x: A): Double = numericRep.toDouble(vcLike.unapply(x)) + override def compare(x: A, y: A): Int = numericRep.compare(vcLike.unapply(x), vcLike.unapply(y)) + override def parseString(str: String): Option[A] = numericRep.parseString(str).map(vcLike.apply) + } + } + +} diff --git a/core/src/main/scala-2.13/pl/iterators/kebs/support/NumericSupport.scala b/core/src/main/scala-2.13/pl/iterators/kebs/support/NumericSupport.scala deleted file mode 100644 index 27d4c234..00000000 --- a/core/src/main/scala-2.13/pl/iterators/kebs/support/NumericSupport.scala +++ /dev/null @@ -1,23 +0,0 @@ -package pl.iterators.kebs.support - -import pl.iterators.kebs.macros.CaseClass1Rep - -trait NumericSupport { - - implicit def numericFromCaseClass1Rep[A, Rep](implicit cc1Rep: CaseClass1Rep[A, Rep], numericRep: Numeric[Rep]): Numeric[A] = { - new Numeric[A] { - override def plus(x: A, y: A): A = cc1Rep.apply(numericRep.plus(cc1Rep.unapply(x), cc1Rep.unapply(y))) - override def minus(x: A, y: A): A = cc1Rep.apply(numericRep.minus(cc1Rep.unapply(x), cc1Rep.unapply(y))) - override def times(x: A, y: A): A = cc1Rep.apply(numericRep.times(cc1Rep.unapply(x), cc1Rep.unapply(y))) - override def negate(x: A): A = cc1Rep.apply(numericRep.negate(cc1Rep.unapply(x))) - override def fromInt(x: Int): A = cc1Rep.apply(numericRep.fromInt(x)) - override def toInt(x: A): Int = numericRep.toInt(cc1Rep.unapply(x)) - override def toLong(x: A): Long = numericRep.toLong(cc1Rep.unapply(x)) - override def toFloat(x: A): Float = numericRep.toFloat(cc1Rep.unapply(x)) - override def toDouble(x: A): Double = numericRep.toDouble(cc1Rep.unapply(x)) - override def compare(x: A, y: A): Int = numericRep.compare(cc1Rep.unapply(x), cc1Rep.unapply(y)) - override def parseString(str: String): Option[A] = numericRep.parseString(str).map(cc1Rep.apply) - } - } - -} diff --git a/core/src/main/scala-2/pl/iterators/kebs/macros/MacroUtils.scala b/core/src/main/scala-2/pl/iterators/kebs/core/macros/MacroUtils.scala similarity index 97% rename from core/src/main/scala-2/pl/iterators/kebs/macros/MacroUtils.scala rename to core/src/main/scala-2/pl/iterators/kebs/core/macros/MacroUtils.scala index 03014095..3189f67f 100644 --- a/core/src/main/scala-2/pl/iterators/kebs/macros/MacroUtils.scala +++ b/core/src/main/scala-2/pl/iterators/kebs/core/macros/MacroUtils.scala @@ -1,4 +1,4 @@ -package pl.iterators.kebs.macros +package pl.iterators.kebs.core.macros import scala.reflect.macros._ diff --git a/core/src/main/scala-2/pl/iterators/kebs/core/macros/ValueClassReps.scala b/core/src/main/scala-2/pl/iterators/kebs/core/macros/ValueClassReps.scala new file mode 100644 index 00000000..b6822885 --- /dev/null +++ b/core/src/main/scala-2/pl/iterators/kebs/core/macros/ValueClassReps.scala @@ -0,0 +1,34 @@ +package pl.iterators.kebs.core.macros + +import scala.language.experimental.macros +import scala.reflect.macros.whitebox + +final class ValueClassLike[VC, F1](val apply: F1 => VC, val unapply: VC => F1) + +trait CaseClass1ToValueClass { + implicit def repFromCaseClass[VC <: Product, F1]: ValueClassLike[VC, F1] = macro ValueClassRepMacros.materializeValueClassRep[VC, F1] +} +object CaseClass1ToValueClass extends CaseClass1ToValueClass + +class ValueClassRepMacros(override val c: whitebox.Context) extends MacroUtils { + import c.universe._ + + def materializeValueClassRep[VC <: Product: c.WeakTypeTag, F1: c.WeakTypeTag]: c.Expr[ValueClassLike[VC, F1]] = { + val ValueClass = weakTypeOf[VC] + assertCaseClass(ValueClass, s"To materialize value class representation, ${ValueClass.typeSymbol} must be a value class") + + ValueClass match { + case Product1(_1) => c.Expr[ValueClassLike[VC, F1]](materializeRep1(ValueClass, _1)) + case _ => c.abort(c.enclosingPosition, "To materialize ValueClassLike, case class must have arity == 1") + } + } + + private def materializeRep1(caseClassType: Type, caseAccessor: MethodSymbol) = { + val f1 = resultType(caseAccessor, caseClassType) + + val unapplyF = q"_.$caseAccessor" + val applyF = apply(caseClassType) + + q"new _root_.pl.iterators.kebs.core.macros.ValueClassLike[$caseClassType, $f1]($applyF, $unapplyF)" + } +} diff --git a/core/src/main/scala-2/pl/iterators/kebs/macros/namingconventions/SnakifyVariant.scala b/core/src/main/scala-2/pl/iterators/kebs/core/macros/namingconventions/SnakifyVariant.scala similarity index 89% rename from core/src/main/scala-2/pl/iterators/kebs/macros/namingconventions/SnakifyVariant.scala rename to core/src/main/scala-2/pl/iterators/kebs/core/macros/namingconventions/SnakifyVariant.scala index 9550b6c2..5c2d0a70 100644 --- a/core/src/main/scala-2/pl/iterators/kebs/macros/namingconventions/SnakifyVariant.scala +++ b/core/src/main/scala-2/pl/iterators/kebs/core/macros/namingconventions/SnakifyVariant.scala @@ -1,4 +1,4 @@ -package pl.iterators.kebs.macros.namingconventions +package pl.iterators.kebs.core.macros.namingconventions object SnakifyVariant { private val PASS_1 = """([A-Z\d]+)([A-Z][a-z])""".r diff --git a/core/src/main/scala-2/pl/iterators/kebs/core/support/EquivSupport.scala b/core/src/main/scala-2/pl/iterators/kebs/core/support/EquivSupport.scala new file mode 100644 index 00000000..aefd4527 --- /dev/null +++ b/core/src/main/scala-2/pl/iterators/kebs/core/support/EquivSupport.scala @@ -0,0 +1,10 @@ +package pl.iterators.kebs.core.support + +import pl.iterators.kebs.core.macros.ValueClassLike + +trait EquivSupport { + + implicit def equivFromValueClassLike[A, Rep](implicit vcLike: ValueClassLike[A, Rep], equivRep: Equiv[Rep]): Equiv[A] = + (x: A, y: A) => equivRep.equiv(vcLike.unapply(x), vcLike.unapply(y)) + +} diff --git a/core/src/main/scala-2/pl/iterators/kebs/core/support/PartialOrderingSupport.scala b/core/src/main/scala-2/pl/iterators/kebs/core/support/PartialOrderingSupport.scala new file mode 100644 index 00000000..51dde933 --- /dev/null +++ b/core/src/main/scala-2/pl/iterators/kebs/core/support/PartialOrderingSupport.scala @@ -0,0 +1,14 @@ +package pl.iterators.kebs.core.support + +import pl.iterators.kebs.core.macros.ValueClassLike + +trait PartialOrderingSupport { + + implicit def partialOrderingFromValueClassLike[A, Rep](implicit vcLike: ValueClassLike[A, Rep], + partialOrderingRep: PartialOrdering[Rep]): PartialOrdering[A] = + new PartialOrdering[A] { + override def tryCompare(x: A, y: A): Option[Int] = partialOrderingRep.tryCompare(vcLike.unapply(x), vcLike.unapply(y)) + override def lteq(x: A, y: A): Boolean = partialOrderingRep.lteq(vcLike.unapply(x), vcLike.unapply(y)) + } + +} diff --git a/core/src/main/scala-3/pl/iterators/kebs/support/package.scala b/core/src/main/scala-2/pl/iterators/kebs/core/support/package.scala similarity index 81% rename from core/src/main/scala-3/pl/iterators/kebs/support/package.scala rename to core/src/main/scala-2/pl/iterators/kebs/core/support/package.scala index 0c41bbdc..e32aaa6f 100644 --- a/core/src/main/scala-3/pl/iterators/kebs/support/package.scala +++ b/core/src/main/scala-2/pl/iterators/kebs/core/support/package.scala @@ -1,3 +1,3 @@ -package pl.iterators.kebs +package pl.iterators.kebs.core package object support extends FractionalSupport with IntegralSupport with NumericSupport with PartialOrderingSupport with EquivSupport diff --git a/core/src/main/scala-2/pl/iterators/kebs/macros/CaseClassReps.scala b/core/src/main/scala-2/pl/iterators/kebs/macros/CaseClassReps.scala deleted file mode 100644 index 1382279b..00000000 --- a/core/src/main/scala-2/pl/iterators/kebs/macros/CaseClassReps.scala +++ /dev/null @@ -1,34 +0,0 @@ -package pl.iterators.kebs.macros - -import scala.language.experimental.macros -import scala.language.higherKinds -import scala.reflect.macros.whitebox - -final class CaseClass1Rep[CC, F1](val apply: F1 => CC, val unapply: CC => F1) - -object CaseClass1Rep { - implicit def repFromCaseClass[CC <: Product, F1]: CaseClass1Rep[CC, F1] = macro CaseClassRepMacros.materializeCaseClass1Rep[CC, F1] -} - -class CaseClassRepMacros(override val c: whitebox.Context) extends MacroUtils { - import c.universe._ - - def materializeCaseClass1Rep[CC <: Product: c.WeakTypeTag, F1: c.WeakTypeTag]: c.Expr[CaseClass1Rep[CC, F1]] = { - val CaseClass = weakTypeOf[CC] - assertCaseClass(CaseClass, s"To materialize case class representation, ${CaseClass.typeSymbol} must be a case class") - - CaseClass match { - case Product1(_1) => c.Expr[CaseClass1Rep[CC, F1]](materializeRep1(CaseClass, _1)) - case _ => c.abort(c.enclosingPosition, "To materialize CaseClass1Rep, case class must have arity == 1") - } - } - - private def materializeRep1(caseClassType: Type, caseAccessor: MethodSymbol) = { - val f1 = resultType(caseAccessor, caseClassType) - - val unapplyF = q"_.$caseAccessor" - val applyF = apply(caseClassType) - - q"new _root_.pl.iterators.kebs.macros.CaseClass1Rep[$caseClassType, $f1]($applyF, $unapplyF)" - } -} diff --git a/core/src/main/scala-2/pl/iterators/kebs/macros/enums/EnumEntryMacros.scala b/core/src/main/scala-2/pl/iterators/kebs/macros/enums/EnumEntryMacros.scala deleted file mode 100644 index 9848362f..00000000 --- a/core/src/main/scala-2/pl/iterators/kebs/macros/enums/EnumEntryMacros.scala +++ /dev/null @@ -1,23 +0,0 @@ -package pl.iterators.kebs.macros.enums - -import enumeratum.{Enum, EnumEntry} - -import scala.language.experimental.macros -import scala.reflect.macros.blackbox - -class EnumOf[E <: EnumEntry](val `enum`: Enum[E]) - -object EnumOf { - implicit def enumOf[E <: EnumEntry]: EnumOf[E] = macro EnumEntryMacros.enumOfImpl[E] -} - -class EnumEntryMacros(override val c: blackbox.Context) extends EnumMacroUtils { - import c.universe._ - - def enumOfImpl[E <: EnumEntry: c.WeakTypeTag]: c.Expr[EnumOf[E]] = { - val EnumEntry = weakTypeOf[E] - assertEnumEntry(EnumEntry, s"${EnumEntry.typeSymbol} must subclass enumeratum.EnumEntry") - - c.Expr[EnumOf[E]](q"new _root_.pl.iterators.kebs.macros.enums.EnumOf(${companion(EnumEntry)})") - } -} diff --git a/core/src/main/scala-2/pl/iterators/kebs/macros/enums/EnumMacroUtils.scala b/core/src/main/scala-2/pl/iterators/kebs/macros/enums/EnumMacroUtils.scala deleted file mode 100644 index 70a2056e..00000000 --- a/core/src/main/scala-2/pl/iterators/kebs/macros/enums/EnumMacroUtils.scala +++ /dev/null @@ -1,17 +0,0 @@ -package pl.iterators.kebs.macros.enums - -import enumeratum.EnumEntry -import enumeratum.values.ValueEnumEntry -import pl.iterators.kebs.macros.MacroUtils - -abstract class EnumMacroUtils extends MacroUtils { - import c.universe._ - - private val EnumEntry = typeOf[EnumEntry] - private val ValueEnumEntry = typeOf[ValueEnumEntry[_]] - - protected def assertEnumEntry(t: Type, msg: => String) = if (!(t <:< EnumEntry)) c.abort(c.enclosingPosition, msg) - protected def assertValueEnumEntry(t: Type, msg: => String) = if (!(t <:< ValueEnumEntry)) c.abort(c.enclosingPosition, msg) - - protected def ValueType(valueEnumEntry: Type) = valueEnumEntry.typeArgs.head -} diff --git a/core/src/main/scala-2/pl/iterators/kebs/macros/enums/ValueEnumEntryMacros.scala b/core/src/main/scala-2/pl/iterators/kebs/macros/enums/ValueEnumEntryMacros.scala deleted file mode 100644 index 4031e75f..00000000 --- a/core/src/main/scala-2/pl/iterators/kebs/macros/enums/ValueEnumEntryMacros.scala +++ /dev/null @@ -1,37 +0,0 @@ -package pl.iterators.kebs.macros.enums - -import enumeratum.values._ - -import scala.language.experimental.macros -import scala.reflect.macros.blackbox - -class ValueEnumOf[ValueType, E <: ValueEnumEntry[ValueType]](val valueEnum: ValueEnum[ValueType, E]) - -object ValueEnumOf { - implicit def intValueEnumOf[E <: IntEnumEntry]: ValueEnumOf[Int, E] = - macro ValueEnumEntryMacros.valueEnumOfImpl[Int, E] - implicit def shortValueEnumOf[E <: ShortEnumEntry]: ValueEnumOf[Short, E] = - macro ValueEnumEntryMacros.valueEnumOfImpl[Short, E] - implicit def longValueEnumOf[E <: LongEnumEntry]: ValueEnumOf[Long, E] = - macro ValueEnumEntryMacros.valueEnumOfImpl[Long, E] - implicit def byteValueEnumOf[E <: ByteEnumEntry]: ValueEnumOf[Byte, E] = - macro ValueEnumEntryMacros.valueEnumOfImpl[Byte, E] - implicit def stringValueEnumOf[E <: StringEnumEntry]: ValueEnumOf[String, E] = - macro ValueEnumEntryMacros.valueEnumOfImpl[String, E] -} - -/** - * this needs to be whitebox because macro needs to deduce `ValueType` type param - */ -class ValueEnumEntryMacros(override val c: blackbox.Context) extends EnumMacroUtils { - import c.universe._ - - def valueEnumOfImpl[ValueType: c.WeakTypeTag, E <: ValueEnumEntry[ValueType]: c.WeakTypeTag]: c.Expr[ValueEnumOf[ValueType, E]] = { - val EnumEntry = weakTypeOf[E] - assertValueEnumEntry(EnumEntry, s"${EnumEntry.typeSymbol} must subclass enumeratum.values.ValueEnumEntry") - - val ValueType = weakTypeOf[ValueType] - c.Expr[ValueEnumOf[ValueType, E]]( - q"new _root_.pl.iterators.kebs.macros.enums.ValueEnumOf[$ValueType, $EnumEntry](${companion(EnumEntry)})") - } -} diff --git a/core/src/main/scala-2/pl/iterators/kebs/support/EquivSupport.scala b/core/src/main/scala-2/pl/iterators/kebs/support/EquivSupport.scala deleted file mode 100644 index ed316fbe..00000000 --- a/core/src/main/scala-2/pl/iterators/kebs/support/EquivSupport.scala +++ /dev/null @@ -1,10 +0,0 @@ -package pl.iterators.kebs.support - -import pl.iterators.kebs.macros.CaseClass1Rep - -trait EquivSupport { - - implicit def equivFromCaseClass1Rep[A, Rep](implicit cc1Rep: CaseClass1Rep[A, Rep], equivRep: Equiv[Rep]): Equiv[A] = - (x: A, y: A) => equivRep.equiv(cc1Rep.unapply(x), cc1Rep.unapply(y)) - -} diff --git a/core/src/main/scala-2/pl/iterators/kebs/support/PartialOrderingSupport.scala b/core/src/main/scala-2/pl/iterators/kebs/support/PartialOrderingSupport.scala deleted file mode 100644 index 20764fee..00000000 --- a/core/src/main/scala-2/pl/iterators/kebs/support/PartialOrderingSupport.scala +++ /dev/null @@ -1,14 +0,0 @@ -package pl.iterators.kebs.support - -import pl.iterators.kebs.macros.CaseClass1Rep - -trait PartialOrderingSupport { - - implicit def partialOrderingFromCaseClass1Rep[A, Rep](implicit cc1Rep: CaseClass1Rep[A, Rep], - partialOrderingRep: PartialOrdering[Rep]): PartialOrdering[A] = - new PartialOrdering[A] { - override def tryCompare(x: A, y: A): Option[Int] = partialOrderingRep.tryCompare(cc1Rep.unapply(x), cc1Rep.unapply(y)) - override def lteq(x: A, y: A): Boolean = partialOrderingRep.lteq(cc1Rep.unapply(x), cc1Rep.unapply(y)) - } - -} diff --git a/core/src/main/scala-3/pl/iterators/kebs/core/macros/ValueClassReps.scala b/core/src/main/scala-3/pl/iterators/kebs/core/macros/ValueClassReps.scala new file mode 100644 index 00000000..51227554 --- /dev/null +++ b/core/src/main/scala-3/pl/iterators/kebs/core/macros/ValueClassReps.scala @@ -0,0 +1,12 @@ +package pl.iterators.kebs.core.macros + +import scala.deriving.Mirror + +final class ValueClassLike[VC, F1](val apply: F1 => VC, val unapply: VC => F1) + +trait CaseClass1ToValueClass { + implicit def repFromCaseClass[T <: Product, F1](using m: Mirror.ProductOf[T], teq: m.MirroredElemTypes =:= F1 *: EmptyTuple.type): ValueClassLike[T, F1] = { + new ValueClassLike[T, F1](f1 => m.fromProduct(Tuple1(f1)), _.productElement(0).asInstanceOf[F1]) + } +} +object CaseClass1ToValueClass extends CaseClass1ToValueClass \ No newline at end of file diff --git a/core/src/main/scala-3/pl/iterators/kebs/core/support/EquivSupport.scala b/core/src/main/scala-3/pl/iterators/kebs/core/support/EquivSupport.scala new file mode 100644 index 00000000..aefd4527 --- /dev/null +++ b/core/src/main/scala-3/pl/iterators/kebs/core/support/EquivSupport.scala @@ -0,0 +1,10 @@ +package pl.iterators.kebs.core.support + +import pl.iterators.kebs.core.macros.ValueClassLike + +trait EquivSupport { + + implicit def equivFromValueClassLike[A, Rep](implicit vcLike: ValueClassLike[A, Rep], equivRep: Equiv[Rep]): Equiv[A] = + (x: A, y: A) => equivRep.equiv(vcLike.unapply(x), vcLike.unapply(y)) + +} diff --git a/core/src/main/scala-2.13/pl/iterators/kebs/support/FractionalSupport.scala b/core/src/main/scala-3/pl/iterators/kebs/core/support/FractionalSupport.scala similarity index 77% rename from core/src/main/scala-2.13/pl/iterators/kebs/support/FractionalSupport.scala rename to core/src/main/scala-3/pl/iterators/kebs/core/support/FractionalSupport.scala index 45f64b50..3e04be80 100644 --- a/core/src/main/scala-2.13/pl/iterators/kebs/support/FractionalSupport.scala +++ b/core/src/main/scala-3/pl/iterators/kebs/core/support/FractionalSupport.scala @@ -1,14 +1,14 @@ -package pl.iterators.kebs.support +package pl.iterators.kebs.core.support -import pl.iterators.kebs.macros.CaseClass1Rep +import pl.iterators.kebs.core.macros.ValueClassLike trait FractionalSupport { - implicit def fractionalFromCaseClass1Rep[A, Rep](implicit cc1Rep: CaseClass1Rep[A, Rep], + implicit def fractionalFromValueClassLike[A, Rep](implicit vcLike: ValueClassLike[A, Rep], fractionalRep: Fractional[Rep], numeric: Numeric[A]): Fractional[A] = new Fractional[A] { - override def div(x: A, y: A): A = cc1Rep.apply(fractionalRep.div(cc1Rep.unapply(x), cc1Rep.unapply(y))) + override def div(x: A, y: A): A = vcLike.apply(fractionalRep.div(vcLike.unapply(x), vcLike.unapply(y))) override def plus(x: A, y: A): A = numeric.plus(x, y) override def minus(x: A, y: A): A = numeric.minus(x, y) override def times(x: A, y: A): A = numeric.times(x, y) diff --git a/core/src/main/scala-3/pl/iterators/kebs/support/IntegralSupport.scala b/core/src/main/scala-3/pl/iterators/kebs/core/support/IntegralSupport.scala similarity index 71% rename from core/src/main/scala-3/pl/iterators/kebs/support/IntegralSupport.scala rename to core/src/main/scala-3/pl/iterators/kebs/core/support/IntegralSupport.scala index 3fc592bb..a301d026 100644 --- a/core/src/main/scala-3/pl/iterators/kebs/support/IntegralSupport.scala +++ b/core/src/main/scala-3/pl/iterators/kebs/core/support/IntegralSupport.scala @@ -1,15 +1,15 @@ -package pl.iterators.kebs.support +package pl.iterators.kebs.core.support -import pl.iterators.kebs.macros.CaseClass1Rep +import pl.iterators.kebs.core.macros.ValueClassLike trait IntegralSupport { - implicit def integralFromCaseClass1Rep[A, Rep](implicit cc1Rep: CaseClass1Rep[A, Rep], + implicit def integralFromValueClassLike[A, Rep](implicit vcLike: ValueClassLike[A, Rep], integralRep: Integral[Rep], numeric: Numeric[A]): Integral[A] = new Integral[A] { - override def quot(x: A, y: A): A = cc1Rep.apply(integralRep.quot(cc1Rep.unapply(x), cc1Rep.unapply(y))) - override def rem(x: A, y: A): A = cc1Rep.apply(integralRep.rem(cc1Rep.unapply(x), cc1Rep.unapply(y))) + override def quot(x: A, y: A): A = vcLike.apply(integralRep.quot(vcLike.unapply(x), vcLike.unapply(y))) + override def rem(x: A, y: A): A = vcLike.apply(integralRep.rem(vcLike.unapply(x), vcLike.unapply(y))) override def plus(x: A, y: A): A = numeric.plus(x, y) override def minus(x: A, y: A): A = numeric.minus(x, y) override def times(x: A, y: A): A = numeric.times(x, y) diff --git a/core/src/main/scala-3/pl/iterators/kebs/core/support/NumericSupport.scala b/core/src/main/scala-3/pl/iterators/kebs/core/support/NumericSupport.scala new file mode 100644 index 00000000..35ce1bab --- /dev/null +++ b/core/src/main/scala-3/pl/iterators/kebs/core/support/NumericSupport.scala @@ -0,0 +1,23 @@ +package pl.iterators.kebs.core.support + +import pl.iterators.kebs.core.macros.ValueClassLike + +trait NumericSupport { + + implicit def numericFromValueClassLike[A, Rep](implicit vcLike: ValueClassLike[A, Rep], numericRep: Numeric[Rep]): Numeric[A] = { + new Numeric[A] { + override def plus(x: A, y: A): A = vcLike.apply(numericRep.plus(vcLike.unapply(x), vcLike.unapply(y))) + override def minus(x: A, y: A): A = vcLike.apply(numericRep.minus(vcLike.unapply(x), vcLike.unapply(y))) + override def times(x: A, y: A): A = vcLike.apply(numericRep.times(vcLike.unapply(x), vcLike.unapply(y))) + override def negate(x: A): A = vcLike.apply(numericRep.negate(vcLike.unapply(x))) + override def fromInt(x: Int): A = vcLike.apply(numericRep.fromInt(x)) + override def toInt(x: A): Int = numericRep.toInt(vcLike.unapply(x)) + override def toLong(x: A): Long = numericRep.toLong(vcLike.unapply(x)) + override def toFloat(x: A): Float = numericRep.toFloat(vcLike.unapply(x)) + override def toDouble(x: A): Double = numericRep.toDouble(vcLike.unapply(x)) + override def compare(x: A, y: A): Int = numericRep.compare(vcLike.unapply(x), vcLike.unapply(y)) + override def parseString(str: String): Option[A] = numericRep.parseString(str).map(vcLike.apply) + } + } + +} diff --git a/core/src/main/scala-3/pl/iterators/kebs/core/support/PartialOrderingSupport.scala b/core/src/main/scala-3/pl/iterators/kebs/core/support/PartialOrderingSupport.scala new file mode 100644 index 00000000..51dde933 --- /dev/null +++ b/core/src/main/scala-3/pl/iterators/kebs/core/support/PartialOrderingSupport.scala @@ -0,0 +1,14 @@ +package pl.iterators.kebs.core.support + +import pl.iterators.kebs.core.macros.ValueClassLike + +trait PartialOrderingSupport { + + implicit def partialOrderingFromValueClassLike[A, Rep](implicit vcLike: ValueClassLike[A, Rep], + partialOrderingRep: PartialOrdering[Rep]): PartialOrdering[A] = + new PartialOrdering[A] { + override def tryCompare(x: A, y: A): Option[Int] = partialOrderingRep.tryCompare(vcLike.unapply(x), vcLike.unapply(y)) + override def lteq(x: A, y: A): Boolean = partialOrderingRep.lteq(vcLike.unapply(x), vcLike.unapply(y)) + } + +} diff --git a/core/src/main/scala-2/pl/iterators/kebs/support/package.scala b/core/src/main/scala-3/pl/iterators/kebs/core/support/package.scala similarity index 81% rename from core/src/main/scala-2/pl/iterators/kebs/support/package.scala rename to core/src/main/scala-3/pl/iterators/kebs/core/support/package.scala index 0c41bbdc..e32aaa6f 100644 --- a/core/src/main/scala-2/pl/iterators/kebs/support/package.scala +++ b/core/src/main/scala-3/pl/iterators/kebs/core/support/package.scala @@ -1,3 +1,3 @@ -package pl.iterators.kebs +package pl.iterators.kebs.core package object support extends FractionalSupport with IntegralSupport with NumericSupport with PartialOrderingSupport with EquivSupport diff --git a/core/src/main/scala-3/pl/iterators/kebs/macros/CaseClassReps.scala b/core/src/main/scala-3/pl/iterators/kebs/macros/CaseClassReps.scala deleted file mode 100644 index acfcde40..00000000 --- a/core/src/main/scala-3/pl/iterators/kebs/macros/CaseClassReps.scala +++ /dev/null @@ -1,11 +0,0 @@ -package pl.iterators.kebs.macros - -import scala.deriving.Mirror - -final class CaseClass1Rep[CC, F1](val apply: F1 => CC, val unapply: CC => F1) - -object CaseClass1Rep { - inline given[T <: Product, F1](using m: Mirror.ProductOf[T], teq: m.MirroredElemTypes =:= F1 *: EmptyTuple.type): CaseClass1Rep[T, F1] = { - new CaseClass1Rep[T, F1](f1 => m.fromProduct(Tuple1(f1)), _.productElement(0).asInstanceOf[F1]) - } -} \ No newline at end of file diff --git a/core/src/main/scala-3/pl/iterators/kebs/macros/enums/EnumEntryMacros.scala b/core/src/main/scala-3/pl/iterators/kebs/macros/enums/EnumEntryMacros.scala deleted file mode 100644 index 536cc8df..00000000 --- a/core/src/main/scala-3/pl/iterators/kebs/macros/enums/EnumEntryMacros.scala +++ /dev/null @@ -1,70 +0,0 @@ -package pl.iterators.kebs.macros.enums - -import pl.iterators.kebs.enums.ValueEnum -import scala.quoted._ -import scala.compiletime.{constValue, erasedValue, error, summonInline} -import scala.deriving.Mirror -import scala.reflect.{ClassTag, Enum} - -trait EnumLike[T] { - def values: Array[T] - def valueOf(name: String): T = values.find(_.toString == name).getOrElse(throw new IllegalArgumentException(s"enum case not found: $name")) - def fromOrdinal(ordinal: Int): T = values.lift(ordinal).getOrElse(throw new NoSuchElementException(ordinal.toString)) -} - -class EnumOf[E](val `enum`: EnumLike[E]) - -inline private def widen[A, B] (a: A): A & B = - inline a match { - case b: B => b - } - -object EnumOf { - inline given [E <: Enum](using m: Mirror.SumOf[E], ct: ClassTag[E]): EnumOf[E] = { - val enumValues = summonCases[m.MirroredElemTypes, E] - EnumOf[E](new EnumLike[E] { - override def values: Array[E] = enumValues.toArray - }) - } - - inline private def summonCases[T <: Tuple, A]: List[A] = - inline erasedValue[T] match { - case _: (h *: t) => - (inline summonInline[Mirror.Of[h]] match { - case m: Mirror.Singleton => - widen[m.MirroredMonoType, A](m.fromProduct(EmptyTuple)) :: summonCases[t, A] - case x => error("Enums cannot include parameterized cases.") - }) - - case _: EmptyTuple => Nil - } -} - -trait ValueEnumLike[ValueType, T <: ValueEnum[ValueType]] { - def values: Array[T] - def valueOf(value: ValueType): T = values.find(_.value == value).getOrElse(throw new IllegalArgumentException(s"enum case not found: $value")) - def fromOrdinal(ordinal: Int): T = values.lift(ordinal).getOrElse(throw new NoSuchElementException(ordinal.toString)) -} - -class ValueEnumOf[V, E <: ValueEnum[V]](val `enum`: ValueEnumLike[V, E]) - -object ValueEnumOf { - inline given [V, E <: ValueEnum[V] with Enum](using m: Mirror.SumOf[E], ct: ClassTag[E]): ValueEnumOf[V, E] = { - val enumValues = summonValueCases[m.MirroredElemTypes, V, E] - ValueEnumOf[V, E](new ValueEnumLike[V, E] { - override def values: Array[E] = enumValues.toArray - }) - } - - inline private def summonValueCases[T <: Tuple, V, A <: ValueEnum[V]]: List[A] = - inline erasedValue[T] match { - case _: (h *: t) => - (inline summonInline[Mirror.Of[h]] match { - case m: Mirror.Singleton => - widen[m.MirroredMonoType, A](m.fromProduct(EmptyTuple)) :: summonValueCases[t, V, A] - case x => error("Enums cannot include parameterized cases.") - }) - - case _: EmptyTuple => Nil - } -} \ No newline at end of file diff --git a/core/src/main/scala-3/pl/iterators/kebs/macros/enums/ValueEnum.scala b/core/src/main/scala-3/pl/iterators/kebs/macros/enums/ValueEnum.scala deleted file mode 100644 index 802cd69d..00000000 --- a/core/src/main/scala-3/pl/iterators/kebs/macros/enums/ValueEnum.scala +++ /dev/null @@ -1,6 +0,0 @@ - -package pl.iterators.kebs.enums - -trait ValueEnum[ValueType] { - def value: ValueType -} \ No newline at end of file diff --git a/core/src/main/scala-3/pl/iterators/kebs/support/EquivSupport.scala b/core/src/main/scala-3/pl/iterators/kebs/support/EquivSupport.scala deleted file mode 100644 index ed316fbe..00000000 --- a/core/src/main/scala-3/pl/iterators/kebs/support/EquivSupport.scala +++ /dev/null @@ -1,10 +0,0 @@ -package pl.iterators.kebs.support - -import pl.iterators.kebs.macros.CaseClass1Rep - -trait EquivSupport { - - implicit def equivFromCaseClass1Rep[A, Rep](implicit cc1Rep: CaseClass1Rep[A, Rep], equivRep: Equiv[Rep]): Equiv[A] = - (x: A, y: A) => equivRep.equiv(cc1Rep.unapply(x), cc1Rep.unapply(y)) - -} diff --git a/core/src/main/scala-3/pl/iterators/kebs/support/NumericSupport.scala b/core/src/main/scala-3/pl/iterators/kebs/support/NumericSupport.scala deleted file mode 100644 index 27d4c234..00000000 --- a/core/src/main/scala-3/pl/iterators/kebs/support/NumericSupport.scala +++ /dev/null @@ -1,23 +0,0 @@ -package pl.iterators.kebs.support - -import pl.iterators.kebs.macros.CaseClass1Rep - -trait NumericSupport { - - implicit def numericFromCaseClass1Rep[A, Rep](implicit cc1Rep: CaseClass1Rep[A, Rep], numericRep: Numeric[Rep]): Numeric[A] = { - new Numeric[A] { - override def plus(x: A, y: A): A = cc1Rep.apply(numericRep.plus(cc1Rep.unapply(x), cc1Rep.unapply(y))) - override def minus(x: A, y: A): A = cc1Rep.apply(numericRep.minus(cc1Rep.unapply(x), cc1Rep.unapply(y))) - override def times(x: A, y: A): A = cc1Rep.apply(numericRep.times(cc1Rep.unapply(x), cc1Rep.unapply(y))) - override def negate(x: A): A = cc1Rep.apply(numericRep.negate(cc1Rep.unapply(x))) - override def fromInt(x: Int): A = cc1Rep.apply(numericRep.fromInt(x)) - override def toInt(x: A): Int = numericRep.toInt(cc1Rep.unapply(x)) - override def toLong(x: A): Long = numericRep.toLong(cc1Rep.unapply(x)) - override def toFloat(x: A): Float = numericRep.toFloat(cc1Rep.unapply(x)) - override def toDouble(x: A): Double = numericRep.toDouble(cc1Rep.unapply(x)) - override def compare(x: A, y: A): Int = numericRep.compare(cc1Rep.unapply(x), cc1Rep.unapply(y)) - override def parseString(str: String): Option[A] = numericRep.parseString(str).map(cc1Rep.apply) - } - } - -} diff --git a/core/src/main/scala-3/pl/iterators/kebs/support/PartialOrderingSupport.scala b/core/src/main/scala-3/pl/iterators/kebs/support/PartialOrderingSupport.scala deleted file mode 100644 index 20764fee..00000000 --- a/core/src/main/scala-3/pl/iterators/kebs/support/PartialOrderingSupport.scala +++ /dev/null @@ -1,14 +0,0 @@ -package pl.iterators.kebs.support - -import pl.iterators.kebs.macros.CaseClass1Rep - -trait PartialOrderingSupport { - - implicit def partialOrderingFromCaseClass1Rep[A, Rep](implicit cc1Rep: CaseClass1Rep[A, Rep], - partialOrderingRep: PartialOrdering[Rep]): PartialOrdering[A] = - new PartialOrdering[A] { - override def tryCompare(x: A, y: A): Option[Int] = partialOrderingRep.tryCompare(cc1Rep.unapply(x), cc1Rep.unapply(y)) - override def lteq(x: A, y: A): Boolean = partialOrderingRep.lteq(cc1Rep.unapply(x), cc1Rep.unapply(y)) - } - -} diff --git a/core/src/main/scala/pl/iterators/kebs/core/enums/EnumLike.scala b/core/src/main/scala/pl/iterators/kebs/core/enums/EnumLike.scala new file mode 100644 index 00000000..1fa08263 --- /dev/null +++ b/core/src/main/scala/pl/iterators/kebs/core/enums/EnumLike.scala @@ -0,0 +1,29 @@ +package pl.iterators.kebs.core.enums + +import scala.collection.immutable + +trait EnumLike[T] { + def values: immutable.Seq[T] + def getNamesToValuesMap: Map[String, T] = EnumLike.namesToValuesMap(this) + def withNameOption(name: String): Option[T] = EnumLike.namesToValuesMap(this).get(name) + def withNameUppercaseOnlyOption(name: String): Option[T] = EnumLike.upperCaseNameValuesToMap(this).get(name) + def withNameInsensitiveOption(name: String): Option[T] = EnumLike.lowerCaseNamesToValuesMap(this).get(name.toLowerCase) + def withNameLowercaseOnlyOption(name: String): Option[T] = EnumLike.lowerCaseNamesToValuesMap(this).get(name) + def withNameUppercaseOnly(name: String): T = withNameUppercaseOnlyOption(name).getOrElse(throw new NoSuchElementException(EnumLike.buildNotFoundMessage(name, this))) + def withNameLowercaseOnly(name: String): T = withNameLowercaseOnlyOption(name).getOrElse(throw new NoSuchElementException(EnumLike.buildNotFoundMessage(name, this))) + def withName(name: String): T = withNameOption(name).getOrElse(throw new NoSuchElementException(EnumLike.buildNotFoundMessage(name, this))) + def valueOf(name: String): T = values.find(_.toString == name).getOrElse(throw new IllegalArgumentException(s"enum case not found: $name")) + def valueOfIgnoreCase(name: String): T = values.find(_.toString.equalsIgnoreCase(name)).getOrElse(throw new IllegalArgumentException(s"enum case not found: $name")) + def withNameIgnoreCase(name: String): T = values.find(_.toString.equalsIgnoreCase((name))).get + def withNameIgnoreCaseOption(name: String): Option[T] = values.find(_.toString.equalsIgnoreCase((name))) + def fromOrdinal(ordinal: Int): T = values.lift(ordinal).getOrElse(throw new NoSuchElementException(ordinal.toString)) + def indexOf(member: T): Int = values.zipWithIndex.toMap.getOrElse(member, -1) +} + +private[core] object EnumLike { + private def namesToValuesMap[T](`enum`: EnumLike[T]): Map[String, T] = `enum`.values.map(v => v.toString -> v).toMap + private def upperCaseNameValuesToMap[T](`enum`: EnumLike[T]): Map[String, T] = namesToValuesMap(`enum`).map { case (k, v) => k.toUpperCase() -> v } + private def lowerCaseNamesToValuesMap[T](`enum`: EnumLike[T]): Map[String, T] = namesToValuesMap(`enum`).map { case (k, v) => k.toLowerCase() -> v } + private def existingEntriesString[T](`enum`: EnumLike[T]): String = `enum`.values.map(_.toString).mkString(", ") + private def buildNotFoundMessage[T](notFoundName: String, `enum`: EnumLike[T]): String = s"$notFoundName is not a member of Enum (${existingEntriesString(`enum`)})" +} diff --git a/core/src/main/scala/pl/iterators/kebs/core/enums/ValueEnumLike.scala b/core/src/main/scala/pl/iterators/kebs/core/enums/ValueEnumLike.scala new file mode 100644 index 00000000..dcabf606 --- /dev/null +++ b/core/src/main/scala/pl/iterators/kebs/core/enums/ValueEnumLike.scala @@ -0,0 +1,25 @@ +package pl.iterators.kebs.core.enums + +import scala.collection.immutable + +trait ValueEnumLikeEntry[ValueType] { + def value: ValueType +} + + +trait ValueEnumLike[ValueType, EntryType <: ValueEnumLikeEntry[ValueType]] { + def values: immutable.Seq[EntryType] + def getValuesToEntriesMap: Map[ValueType, EntryType] = ValueEnumLike.valuesToEntriesMap(this) + def withValue(i: ValueType): EntryType = withValueOption(i).getOrElse(throw new NoSuchElementException(ValueEnumLike.buildNotFoundMessage(this, i))) + def withValueOption(i: ValueType): Option[EntryType] = ValueEnumLike.valuesToEntriesMap(this).get(i) + def valueOf(value: ValueType): EntryType = values.find(entry => value == entry.value).getOrElse(throw new IllegalArgumentException(s"enum case not found: $value")) + def valueOfOption(value: ValueType): Option[EntryType] = values.find(entry => value == entry.value) + def fromOrdinal(ordinal: Int): EntryType = values.lift(ordinal).getOrElse(throw new NoSuchElementException(ordinal.toString)) + def indexOf(member: EntryType): Int = values.zipWithIndex.find { case (entry, _) => member == entry }.map { case (_, index) => index }.getOrElse(-1) +} + +private[core] object ValueEnumLike { + private def valuesToEntriesMap[ValueType, EntryType <: ValueEnumLikeEntry[ValueType]](`enum`: ValueEnumLike[ValueType, EntryType]): Map[ValueType, EntryType] = `enum`.values.map(v => v.value -> v).toMap + private def existingEntriesString[ValueType, EntryType <: ValueEnumLikeEntry[ValueType]](`enum`: ValueEnumLike[ValueType, EntryType]): String = `enum`.values.map(_.value).mkString(", ") + private def buildNotFoundMessage[ValueType, EntryType <: ValueEnumLikeEntry[ValueType]](`enum`: ValueEnumLike[ValueType, EntryType], i: ValueType): String = s"${i.toString} is not a member of ValueEnum (${existingEntriesString(`enum`)})" +} diff --git a/core/src/main/scala/pl/iterators/kebs/instances/InstanceConverter.scala b/core/src/main/scala/pl/iterators/kebs/core/instances/InstanceConverter.scala similarity index 92% rename from core/src/main/scala/pl/iterators/kebs/instances/InstanceConverter.scala rename to core/src/main/scala/pl/iterators/kebs/core/instances/InstanceConverter.scala index cd8e97d8..8c650016 100644 --- a/core/src/main/scala/pl/iterators/kebs/instances/InstanceConverter.scala +++ b/core/src/main/scala/pl/iterators/kebs/core/instances/InstanceConverter.scala @@ -1,4 +1,4 @@ -package pl.iterators.kebs.instances +package pl.iterators.kebs.core.instances import scala.reflect.{ClassTag, classTag} import scala.util.control.NonFatal @@ -8,7 +8,7 @@ trait InstanceConverter[Obj, Val] { def decode(value: Val): Obj } object InstanceConverter { - private def errorMessage(clazz: String, value: String, formatOpt: Option[String] = None): String = { + private def errorMessage(clazz: String, value: String, formatOpt: Option[String]): String = { s"$clazz cannot be parsed from $value".concat(formatOpt.fold("")(format => s" – should be in format $format")) } diff --git a/core/src/test/scala-3/DerivingSpecification.scala b/core/src/test/scala-3/DerivingSpecification.scala deleted file mode 100644 index fdfa3b7b..00000000 --- a/core/src/test/scala-3/DerivingSpecification.scala +++ /dev/null @@ -1,34 +0,0 @@ -import org.scalacheck.Prop.forAll -import org.scalacheck.{Gen, Properties} -import pl.iterators.kebs.macros.CaseClass1Rep -import pl.iterators.kebs.macros.enums.{EnumOf, ValueEnumOf} -import pl.iterators.kebs.enums.ValueEnum - -object DerivingSpecification extends Properties("Deriving") { - case class CC1Ex(whatever: String) - - property("CaseClass1Rep derives properly from 1-element case class") = forAll { (stringValue: String) => - val tc = implicitly[CaseClass1Rep[CC1Ex, String]] - tc.apply(stringValue) == CC1Ex(stringValue) && tc.unapply(CC1Ex(stringValue)) == stringValue - } - - enum Color { - case Red, Green, Blue - } - - property("EnumOf derives properly for an enum") = forAll(Gen.oneOf(Color.values.toList)) { (color: Color) => - val tc = implicitly[EnumOf[Color]] - tc.`enum`.values.contains(color) && tc.`enum`.valueOf(color.toString) == color && tc.`enum`.fromOrdinal(color.ordinal) == color - } - - enum ColorButRGB(val value: Int) extends ValueEnum[Int] { - case Red extends ColorButRGB(0xFF0000) - case Green extends ColorButRGB(0x00FF00) - case Blue extends ColorButRGB(0x0000FF) - } - - property("ValueEnumOf derives properly for an enum") = forAll(Gen.oneOf(ColorButRGB.values.toList)) { (color: ColorButRGB) => - val tc = implicitly[ValueEnumOf[Int, ColorButRGB]] - tc.`enum`.values.contains(color) && tc.`enum`.valueOf(color.value) == color && tc.`enum`.fromOrdinal(color.ordinal) == color - } -} diff --git a/core/src/test/scala/NumbersDomain.scala b/core/src/test/scala/NumbersDomain.scala deleted file mode 100644 index 1237c1d3..00000000 --- a/core/src/test/scala/NumbersDomain.scala +++ /dev/null @@ -1,36 +0,0 @@ -import pl.iterators.kebs.macros.CaseClass1Rep - -object NumbersDomain { - - trait Tag1 - type TaggedBigDecimal = BigDecimal with Tag1 - object TaggedBigDecimal { - def apply(value: BigDecimal): TaggedBigDecimal = value.asInstanceOf[TaggedBigDecimal] - } - object Tag1 { - implicit val TaggedBigDecimalCaseClass1Rep: CaseClass1Rep[TaggedBigDecimal, BigDecimal] = - new CaseClass1Rep[TaggedBigDecimal, BigDecimal](TaggedBigDecimal.apply, identity) - } - - case class BoxedBigDecimal(value: BigDecimal) - object BoxedBigDecimal { - implicit val BoxedBigDecimalCaseClass1Rep: CaseClass1Rep[BoxedBigDecimal, BigDecimal] = - new CaseClass1Rep[BoxedBigDecimal, BigDecimal](BoxedBigDecimal.apply, _.value) - } - - trait Tag2 - type TaggedInt = Int with Tag2 - object TaggedInt { - def apply(value: Int): TaggedInt = value.asInstanceOf[TaggedInt] - } - object Tag2 { - implicit val TaggedIntCaseClass1Rep: CaseClass1Rep[TaggedInt, Int] = - new CaseClass1Rep[TaggedInt, Int](TaggedInt.apply, identity) - } - - case class BoxedInt(value: Int) - object BoxedInt { - implicit val BoxedIntCaseClass1Rep: CaseClass1Rep[BoxedInt, Int] = - new CaseClass1Rep[BoxedInt, Int](BoxedInt.apply, _.value) - } -} diff --git a/core/src/test/scala/StringsDomain.scala b/core/src/test/scala/StringsDomain.scala deleted file mode 100644 index 4d761ee1..00000000 --- a/core/src/test/scala/StringsDomain.scala +++ /dev/null @@ -1,19 +0,0 @@ -import pl.iterators.kebs.macros.CaseClass1Rep - -object StringsDomain { - trait Tag1 - type TaggedString = String with Tag1 - object TaggedString { - def apply(value: String): TaggedString = value.asInstanceOf[TaggedString] - } - object Tag1 { - implicit val TaggedStringCaseClass1Rep: CaseClass1Rep[TaggedString, String] = - new CaseClass1Rep[TaggedString, String](TaggedString.apply, identity) - } - - case class BoxedString(value: String) - object BoxedString { - implicit val BoxedStringCaseClass1Rep: CaseClass1Rep[BoxedString, String] = - new CaseClass1Rep[BoxedString, String](BoxedString.apply, _.value) - } -} diff --git a/core/src/test/scala/pl/iterators/kebs/core/DerivingSpecification.scala b/core/src/test/scala/pl/iterators/kebs/core/DerivingSpecification.scala new file mode 100644 index 00000000..536304d5 --- /dev/null +++ b/core/src/test/scala/pl/iterators/kebs/core/DerivingSpecification.scala @@ -0,0 +1,14 @@ +package pl.iterators.kebs.core + +import org.scalacheck.Prop.forAll +import org.scalacheck.Properties +import pl.iterators.kebs.core.macros.{CaseClass1ToValueClass, ValueClassLike} + +object DerivingSpecification extends Properties("Deriving") with CaseClass1ToValueClass { + case class CC1Ex(whatever: String) + + property("ValueClassLike derives properly from 1-element case class") = forAll { (stringValue: String) => + val tc = implicitly[ValueClassLike[CC1Ex, String]] + tc.apply(stringValue) == CC1Ex(stringValue) && tc.unapply(CC1Ex(stringValue)) == stringValue + } +} diff --git a/core/src/test/scala/EquivSupportSpecification.scala b/core/src/test/scala/pl/iterators/kebs/core/EquivSupportSpecification.scala similarity index 97% rename from core/src/test/scala/EquivSupportSpecification.scala rename to core/src/test/scala/pl/iterators/kebs/core/EquivSupportSpecification.scala index c7cd36d2..f45ef123 100644 --- a/core/src/test/scala/EquivSupportSpecification.scala +++ b/core/src/test/scala/pl/iterators/kebs/core/EquivSupportSpecification.scala @@ -1,10 +1,12 @@ +package pl.iterators.kebs.core + import org.scalacheck.Prop.forAll import org.scalacheck.Properties object EquivSupportSpecification extends Properties("EquivSupport") { import StringsDomain._ - import pl.iterators.kebs.support._ + import support._ private def isScalaJS = System.getProperty("java.vm.name") == "Scala.js" diff --git a/core/src/test/scala/FractionalSupportSpecification.scala b/core/src/test/scala/pl/iterators/kebs/core/FractionalSupportSpecification.scala similarity index 96% rename from core/src/test/scala/FractionalSupportSpecification.scala rename to core/src/test/scala/pl/iterators/kebs/core/FractionalSupportSpecification.scala index 16548dec..4fe2aad8 100644 --- a/core/src/test/scala/FractionalSupportSpecification.scala +++ b/core/src/test/scala/pl/iterators/kebs/core/FractionalSupportSpecification.scala @@ -1,3 +1,5 @@ +package pl.iterators.kebs.core + import org.scalacheck.Prop.forAll import org.scalacheck.{Arbitrary, Properties} @@ -6,7 +8,7 @@ object FractionalSupportSpecification extends Properties("FractionalSupport") { private val nonZeroBigDecimal = Arbitrary.arbitrary[BigDecimal] suchThat (_ != 0) import NumbersDomain._ - import pl.iterators.kebs.support._ + import support._ private def divide[A: Fractional](f1: A, f2: A): Option[A] = { import Fractional.Implicits._ diff --git a/core/src/test/scala/IntegralSupportSpecification.scala b/core/src/test/scala/pl/iterators/kebs/core/IntegralSupportSpecification.scala similarity index 94% rename from core/src/test/scala/IntegralSupportSpecification.scala rename to core/src/test/scala/pl/iterators/kebs/core/IntegralSupportSpecification.scala index 2cd52ba1..b878c9d5 100644 --- a/core/src/test/scala/IntegralSupportSpecification.scala +++ b/core/src/test/scala/pl/iterators/kebs/core/IntegralSupportSpecification.scala @@ -1,3 +1,5 @@ +package pl.iterators.kebs.core + import org.scalacheck.Prop.forAll import org.scalacheck.{Arbitrary, Properties} @@ -6,7 +8,7 @@ object IntegralSupportSpecification extends Properties("IntegralSupport") { private val nonZeroInt = Arbitrary.arbitrary[Int] suchThat (_ != 0) import NumbersDomain._ - import pl.iterators.kebs.support._ + import support._ private def divide[A: Integral](f1: A, f2: A): (A, A) = { import Integral.Implicits._ diff --git a/core/src/test/scala/pl/iterators/kebs/core/NumbersDomain.scala b/core/src/test/scala/pl/iterators/kebs/core/NumbersDomain.scala new file mode 100644 index 00000000..850e5574 --- /dev/null +++ b/core/src/test/scala/pl/iterators/kebs/core/NumbersDomain.scala @@ -0,0 +1,38 @@ +package pl.iterators.kebs.core + +import pl.iterators.kebs.core.macros.ValueClassLike + +object NumbersDomain { + + trait Tag1 + type TaggedBigDecimal = BigDecimal with Tag1 + object TaggedBigDecimal { + def apply(value: BigDecimal): TaggedBigDecimal = value.asInstanceOf[TaggedBigDecimal] + } + object Tag1 { + implicit val taggedBigDecimalValueClassLike: ValueClassLike[TaggedBigDecimal, BigDecimal] = + new ValueClassLike[TaggedBigDecimal, BigDecimal](TaggedBigDecimal.apply, identity) + } + + case class BoxedBigDecimal(value: BigDecimal) + object BoxedBigDecimal { + implicit val boxedBigDecimalValueClassLike: ValueClassLike[BoxedBigDecimal, BigDecimal] = + new ValueClassLike[BoxedBigDecimal, BigDecimal](BoxedBigDecimal.apply, _.value) + } + + trait Tag2 + type TaggedInt = Int with Tag2 + object TaggedInt { + def apply(value: Int): TaggedInt = value.asInstanceOf[TaggedInt] + } + object Tag2 { + implicit val taggedIntValueClassLike: ValueClassLike[TaggedInt, Int] = + new ValueClassLike[TaggedInt, Int](TaggedInt.apply, identity) + } + + case class BoxedInt(value: Int) + object BoxedInt { + implicit val boxedIntValueClassLike: ValueClassLike[BoxedInt, Int] = + new ValueClassLike[BoxedInt, Int](BoxedInt.apply, _.value) + } +} diff --git a/core/src/test/scala/NumericSupportSpecification.scala b/core/src/test/scala/pl/iterators/kebs/core/NumericSupportSpecification.scala similarity index 91% rename from core/src/test/scala/NumericSupportSpecification.scala rename to core/src/test/scala/pl/iterators/kebs/core/NumericSupportSpecification.scala index f9f6c04c..41615bdd 100644 --- a/core/src/test/scala/NumericSupportSpecification.scala +++ b/core/src/test/scala/pl/iterators/kebs/core/NumericSupportSpecification.scala @@ -1,10 +1,12 @@ +package pl.iterators.kebs.core + import org.scalacheck.Prop.forAll import org.scalacheck.Properties object NumericSupportSpecification extends Properties("NumericSupport") { import NumbersDomain._ - import pl.iterators.kebs.support._ + import support._ property("sum of List[TaggedBigDecimal]") = forAll { (bigDecimalList: List[BigDecimal]) => bigDecimalList.map(TaggedBigDecimal(_)).sum == TaggedBigDecimal(bigDecimalList.sum) diff --git a/core/src/test/scala/OrderingSupportSpecification.scala b/core/src/test/scala/pl/iterators/kebs/core/OrderingSupportSpecification.scala similarity index 96% rename from core/src/test/scala/OrderingSupportSpecification.scala rename to core/src/test/scala/pl/iterators/kebs/core/OrderingSupportSpecification.scala index 9fc18f93..55b8667b 100644 --- a/core/src/test/scala/OrderingSupportSpecification.scala +++ b/core/src/test/scala/pl/iterators/kebs/core/OrderingSupportSpecification.scala @@ -1,9 +1,11 @@ +package pl.iterators.kebs.core + import org.scalacheck.Prop.forAll import org.scalacheck.Properties object OrderingSupportSpecification extends Properties("OrderingSupport") { - import pl.iterators.kebs.support._ + import support._ import NumbersDomain._ private def toTagged(list: List[BigDecimal]): List[TaggedBigDecimal] = diff --git a/core/src/test/scala/PartialOrderingSupportSpecification.scala b/core/src/test/scala/pl/iterators/kebs/core/PartialOrderingSupportSpecification.scala similarity index 98% rename from core/src/test/scala/PartialOrderingSupportSpecification.scala rename to core/src/test/scala/pl/iterators/kebs/core/PartialOrderingSupportSpecification.scala index de11172a..4be258cf 100644 --- a/core/src/test/scala/PartialOrderingSupportSpecification.scala +++ b/core/src/test/scala/pl/iterators/kebs/core/PartialOrderingSupportSpecification.scala @@ -1,10 +1,12 @@ +package pl.iterators.kebs.core + import org.scalacheck.Prop.forAll import org.scalacheck.{Gen, Properties} object PartialOrderingSupportSpecification extends Properties("PartialOrderingSupport") { import StringsDomain._ - import pl.iterators.kebs.support._ + import support._ private def maybeCompare[A](e1: A, e2: A)(implicit PO: PartialOrdering[A]): Option[Int] = PO.tryCompare(e1, e2) diff --git a/core/src/test/scala/pl/iterators/kebs/core/StringsDomain.scala b/core/src/test/scala/pl/iterators/kebs/core/StringsDomain.scala new file mode 100644 index 00000000..c1622ac9 --- /dev/null +++ b/core/src/test/scala/pl/iterators/kebs/core/StringsDomain.scala @@ -0,0 +1,21 @@ +package pl.iterators.kebs.core + +import pl.iterators.kebs.core.macros.ValueClassLike + +object StringsDomain { + trait Tag1 + type TaggedString = String with Tag1 + object TaggedString { + def apply(value: String): TaggedString = value.asInstanceOf[TaggedString] + } + object Tag1 { + implicit val taggedStringValueClassLike: ValueClassLike[TaggedString, String] = + new ValueClassLike[TaggedString, String](TaggedString.apply, identity) + } + + case class BoxedString(value: String) + object BoxedString { + implicit val boxedStringValueClassLike: ValueClassLike[BoxedString, String] = + new ValueClassLike[BoxedString, String](BoxedString.apply, _.value) + } +} diff --git a/doobie/src/main/scala-2/pl/iterators/kebs/Kebs.scala b/doobie/src/main/scala-2/pl/iterators/kebs/Kebs.scala deleted file mode 100644 index 5490058d..00000000 --- a/doobie/src/main/scala-2/pl/iterators/kebs/Kebs.scala +++ /dev/null @@ -1,21 +0,0 @@ -package pl.iterators.kebs - -import doobie.Meta -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.macros.CaseClass1Rep - -import scala.reflect.ClassTag - -trait Kebs { - implicit def caseClass1RepMeta[A, M](implicit cc1Rep: CaseClass1Rep[A, M], m: Meta[M]): Meta[A] = m.imap(cc1Rep.apply)(cc1Rep.unapply) - - implicit def caseClass1RepArrayMeta[A, M](implicit cc1Rep: CaseClass1Rep[A, M], m: Meta[Array[M]], cta: ClassTag[A], ctm: ClassTag[M]): Meta[Array[A]] = m.imap(_.map(cc1Rep.apply))(_.map(cc1Rep.unapply)) - - implicit def caseClass1RepOptionArrayMeta[A, M](implicit cc1Rep: CaseClass1Rep[A, M], m: Meta[Array[Option[M]]], cta: ClassTag[A], ctm: ClassTag[M]): Meta[Array[Option[A]]] = m.imap(_.map(_.map(cc1Rep.apply)))(_.map(_.map(cc1Rep.unapply))) - - implicit def instanceConverterMeta[A, M](implicit instanceConverter: InstanceConverter[A, M], m: Meta[M]): Meta[A] = m.imap(instanceConverter.decode)(instanceConverter.encode) - - implicit def instanceConverterArrayMeta[A, M](implicit instanceConverter: InstanceConverter[A, M], m: Meta[Array[M]], cta: ClassTag[A], ctm: ClassTag[M]): Meta[Array[A]] = m.imap(_.map(instanceConverter.decode))(_.map(instanceConverter.encode)) - - implicit def instanceConverterOptionArrayMeta[A, M](implicit instanceConverter: InstanceConverter[A, M], m: Meta[Array[Option[M]]], cta: ClassTag[A], ctm: ClassTag[M]): Meta[Array[Option[A]]] = m.imap(_.map(_.map(instanceConverter.decode)))(_.map(_.map(instanceConverter.encode))) -} \ No newline at end of file diff --git a/doobie/src/main/scala-2/pl/iterators/kebs/doobie/Kebs.scala b/doobie/src/main/scala-2/pl/iterators/kebs/doobie/Kebs.scala new file mode 100644 index 00000000..c8322c7b --- /dev/null +++ b/doobie/src/main/scala-2/pl/iterators/kebs/doobie/Kebs.scala @@ -0,0 +1,21 @@ +package pl.iterators.kebs.doobie + +import doobie.Meta +import pl.iterators.kebs.core.instances.InstanceConverter +import pl.iterators.kebs.core.macros.{CaseClass1ToValueClass, ValueClassLike} + +import scala.reflect.ClassTag + +trait Kebs extends CaseClass1ToValueClass { + implicit def valueClassLikeMeta[A, M](implicit vcLike: ValueClassLike[A, M], m: Meta[M]): Meta[A] = m.imap(vcLike.apply)(vcLike.unapply) + + implicit def valueClassLikeArrayMeta[A, M](implicit vcLike: ValueClassLike[A, M], m: Meta[Array[M]], cta: ClassTag[A], ctm: ClassTag[M]): Meta[Array[A]] = m.imap(_.map(vcLike.apply))(_.map(vcLike.unapply)) + + implicit def valueClassLikeOptionArrayMeta[A, M](implicit vcLike: ValueClassLike[A, M], m: Meta[Array[Option[M]]]): Meta[Array[Option[A]]] = m.imap(_.map(_.map(vcLike.apply)))(_.map(_.map(vcLike.unapply))) + + implicit def instanceConverterMeta[A, M](implicit instanceConverter: InstanceConverter[A, M], m: Meta[M]): Meta[A] = m.imap(instanceConverter.decode)(instanceConverter.encode) + + implicit def instanceConverterArrayMeta[A, M](implicit instanceConverter: InstanceConverter[A, M], m: Meta[Array[M]], cta: ClassTag[A], ctm: ClassTag[M]): Meta[Array[A]] = m.imap(_.map(instanceConverter.decode))(_.map(instanceConverter.encode)) + + implicit def instanceConverterOptionArrayMeta[A, M](implicit instanceConverter: InstanceConverter[A, M], m: Meta[Array[Option[M]]]): Meta[Array[Option[A]]] = m.imap(_.map(_.map(instanceConverter.decode)))(_.map(_.map(instanceConverter.encode))) +} diff --git a/doobie/src/main/scala-2/pl/iterators/kebs/doobie/enums/KebsEnums.scala b/doobie/src/main/scala-2/pl/iterators/kebs/doobie/enums/KebsEnums.scala new file mode 100644 index 00000000..a6c6cb0a --- /dev/null +++ b/doobie/src/main/scala-2/pl/iterators/kebs/doobie/enums/KebsEnums.scala @@ -0,0 +1,26 @@ +package pl.iterators.kebs.doobie.enums + +import doobie.Meta +import pl.iterators.kebs.core.enums.EnumLike + +import scala.reflect.ClassTag + +trait KebsEnums { + implicit def enumMeta[E](implicit e: EnumLike[E], m: Meta[String]): Meta[E] = m.imap(e.withName)(_.toString) + implicit def enumArrayMeta[E](implicit e: EnumLike[E], m: Meta[Array[String]], cte: ClassTag[E]): Meta[Array[E]] = m.imap(_.map(e.withName))(_.map(_.toString)) + implicit def enumOptionArrayMeta[E](implicit e: EnumLike[E], m: Meta[Array[Option[String]]], cte: ClassTag[Option[E]]): Meta[Array[Option[E]]] = m.imap(_.map(_.map(e.withName)))(_.map(_.map(_.toString))) + + trait Uppercase { + implicit def enumUppercaseMeta[E](implicit e: EnumLike[E], m: Meta[String]): Meta[E] = m.imap(e.withNameUppercaseOnly)(_.toString.toUpperCase) + implicit def enumUppercaseArrayMeta[E](implicit e: EnumLike[E], m: Meta[Array[String]], cte: ClassTag[E]): Meta[Array[E]] = m.imap(_.map(e.withNameUppercaseOnly))(_.map(_.toString.toUpperCase)) + implicit def enumUppercaseOptionArrayMeta[E](implicit e: EnumLike[E], m: Meta[Array[Option[String]]]): Meta[Array[Option[E]]] = m.imap(_.map(_.map(e.withNameUppercaseOnly)))(_.map(_.map(_.toString.toUpperCase))) + } + + trait Lowercase { + implicit def enumLowercaseMeta[E](implicit e: EnumLike[E], m: Meta[String]): Meta[E] = m.imap(e.withNameLowercaseOnly)(_.toString.toLowerCase) + implicit def enumLowercaseArrayMeta[E](implicit e: EnumLike[E], m: Meta[Array[String]], cte: ClassTag[E]): Meta[Array[E]] = m.imap(_.map(e.withNameLowercaseOnly))(_.map(_.toString.toLowerCase)) + implicit def enumLowercaseOptionArrayMeta[E](implicit e: EnumLike[E], m: Meta[Array[Option[String]]]): Meta[Array[Option[E]]] = m.imap(_.map(_.map(e.withNameLowercaseOnly)))(_.map(_.map(_.toString.toLowerCase))) + } +} + +object KebsEnums extends KebsEnums \ No newline at end of file diff --git a/doobie/src/main/scala-3/pl/iterators/kebs/enums/package.scala b/doobie/src/main/scala-2/pl/iterators/kebs/doobie/enums/package.scala similarity index 78% rename from doobie/src/main/scala-3/pl/iterators/kebs/enums/package.scala rename to doobie/src/main/scala-2/pl/iterators/kebs/doobie/enums/package.scala index 153bf1f2..e42cde32 100644 --- a/doobie/src/main/scala-3/pl/iterators/kebs/enums/package.scala +++ b/doobie/src/main/scala-2/pl/iterators/kebs/doobie/enums/package.scala @@ -1,4 +1,4 @@ -package pl.iterators.kebs +package pl.iterators.kebs.doobie package object enums extends KebsEnums { object uppercase extends Uppercase diff --git a/doobie/src/main/scala-2/pl/iterators/kebs/doobie/package.scala b/doobie/src/main/scala-2/pl/iterators/kebs/doobie/package.scala new file mode 100644 index 00000000..2022afb7 --- /dev/null +++ b/doobie/src/main/scala-2/pl/iterators/kebs/doobie/package.scala @@ -0,0 +1,3 @@ +package pl.iterators.kebs + +package object doobie extends Kebs diff --git a/doobie/src/main/scala-2/pl/iterators/kebs/enums/KebsEnums.scala b/doobie/src/main/scala-2/pl/iterators/kebs/enums/KebsEnums.scala deleted file mode 100644 index 4f857117..00000000 --- a/doobie/src/main/scala-2/pl/iterators/kebs/enums/KebsEnums.scala +++ /dev/null @@ -1,27 +0,0 @@ -package pl.iterators.kebs.enums - -import doobie.Meta -import enumeratum.EnumEntry -import pl.iterators.kebs.macros.enums.EnumOf - -import scala.reflect.ClassTag - -trait KebsEnums { - implicit def enumMeta[E <: EnumEntry](implicit e: EnumOf[E], m: Meta[String]): Meta[E] = m.imap(e.`enum`.withName)(_.toString) - implicit def enumArrayMeta[E <: EnumEntry](implicit e: EnumOf[E], m: Meta[Array[String]], cte: ClassTag[E]): Meta[Array[E]] = m.imap(_.map(e.`enum`.withName))(_.map(_.toString)) - implicit def enumOptionArrayMeta[E <: EnumEntry](implicit e: EnumOf[E], m: Meta[Array[Option[String]]], cte: ClassTag[Option[E]]): Meta[Array[Option[E]]] = m.imap(_.map(_.map(e.`enum`.withName)))(_.map(_.map(_.toString))) - - trait Uppercase { - implicit def enumUppercaseMeta[E <: EnumEntry](implicit e: EnumOf[E], m: Meta[String]): Meta[E] = m.imap(e.`enum`.withNameUppercaseOnly)(_.toString.toUpperCase) - implicit def enumUppercaseArrayMeta[E <: EnumEntry](implicit e: EnumOf[E], m: Meta[Array[String]], cte: ClassTag[E]): Meta[Array[E]] = m.imap(_.map(e.`enum`.withNameUppercaseOnly))(_.map(_.toString.toUpperCase)) - implicit def enumUppercaseOptionArrayMeta[E <: EnumEntry](implicit e: EnumOf[E], m: Meta[Array[Option[String]]], cte: ClassTag[E]): Meta[Array[Option[E]]] = m.imap(_.map(_.map(e.`enum`.withNameUppercaseOnly)))(_.map(_.map(_.toString.toUpperCase))) - } - - trait Lowercase { - implicit def enumLowercaseMeta[E <: EnumEntry](implicit e: EnumOf[E], m: Meta[String]): Meta[E] = m.imap(e.`enum`.withNameLowercaseOnly)(_.toString.toLowerCase) - implicit def enumLowercaseArrayMeta[E <: EnumEntry](implicit e: EnumOf[E], m: Meta[Array[String]], cte: ClassTag[E]): Meta[Array[E]] = m.imap(_.map(e.`enum`.withNameLowercaseOnly))(_.map(_.toString.toLowerCase)) - implicit def enumLowercaseOptionArrayMeta[E <: EnumEntry](implicit e: EnumOf[E], m: Meta[Array[Option[String]]], cte: ClassTag[E]): Meta[Array[Option[E]]] = m.imap(_.map(_.map(e.`enum`.withNameLowercaseOnly)))(_.map(_.map(_.toString.toLowerCase))) - } -} - -object KebsEnums extends KebsEnums \ No newline at end of file diff --git a/doobie/src/main/scala-2/pl/iterators/kebs/package.scala b/doobie/src/main/scala-2/pl/iterators/kebs/package.scala deleted file mode 100644 index feea7054..00000000 --- a/doobie/src/main/scala-2/pl/iterators/kebs/package.scala +++ /dev/null @@ -1,3 +0,0 @@ -package pl.iterators - -package object kebs extends Kebs diff --git a/doobie/src/main/scala-3/pl/iterators/kebs/Kebs.scala b/doobie/src/main/scala-3/pl/iterators/kebs/Kebs.scala deleted file mode 100644 index ee4bee28..00000000 --- a/doobie/src/main/scala-3/pl/iterators/kebs/Kebs.scala +++ /dev/null @@ -1,24 +0,0 @@ -package pl.iterators.kebs - -import doobie.{Get, Put, Meta} -import pl.iterators.kebs.enums.KebsEnums -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.macros.CaseClass1Rep - -import scala.reflect.ClassTag - -trait Kebs { - inline given[A, M](using cc1Rep: CaseClass1Rep[A, M], m: Meta[M]): Meta[A] = m.imap(cc1Rep.apply)(cc1Rep.unapply) - - inline given[A, M](using cc1Rep: CaseClass1Rep[A, M], m: Meta[Option[M]]): Meta[Option[A]] = m.imap(_.map(cc1Rep.apply))(_.map(cc1Rep.unapply)) - - inline given[A, M](using cc1Rep: CaseClass1Rep[A, M], m: Meta[Array[M]], cta: ClassTag[A], ctm: ClassTag[M]): Meta[Array[A]] = m.imap(_.map(cc1Rep.apply))(_.map(cc1Rep.unapply)) - - inline given[A, M](using cc1Rep: CaseClass1Rep[A, M], m: Meta[Array[Option[M]]], cta: ClassTag[Option[A]]): Meta[Array[Option[A]]] = m.imap(_.map(_.map(cc1Rep.apply)))(_.map(_.map(cc1Rep.unapply))) - - inline given[A, M](using instanceConverter: InstanceConverter[A, M], m: Meta[M]): Meta[A] = m.imap(instanceConverter.decode)(instanceConverter.encode) - - inline given[A, M](using instanceConverter: InstanceConverter[A, M], m: Meta[Array[M]], cta: ClassTag[A], ctm: ClassTag[M]): Meta[Array[A]] = m.imap(_.map(instanceConverter.decode))(_.map(instanceConverter.encode)) - - inline given[A, M](using instanceConverter: InstanceConverter[A, M], m: Meta[Array[Option[M]]], cta: ClassTag[Option[A]]): Meta[Array[Option[A]]] = m.imap(_.map(_.map(instanceConverter.decode)))(_.map(_.map(instanceConverter.encode))) -} \ No newline at end of file diff --git a/doobie/src/main/scala-3/pl/iterators/kebs/doobie/Kebs.scala b/doobie/src/main/scala-3/pl/iterators/kebs/doobie/Kebs.scala new file mode 100644 index 00000000..136dea04 --- /dev/null +++ b/doobie/src/main/scala-3/pl/iterators/kebs/doobie/Kebs.scala @@ -0,0 +1,23 @@ +package pl.iterators.kebs.doobie + +import doobie.Meta +import pl.iterators.kebs.core.instances.InstanceConverter +import pl.iterators.kebs.core.macros.ValueClassLike + +import scala.reflect.ClassTag + +trait Kebs { + inline given[A, M](using vcLike: ValueClassLike[A, M], m: Meta[M]): Meta[A] = m.imap(vcLike.apply)(vcLike.unapply) + + inline given[A, M](using vcLike: ValueClassLike[A, M], m: Meta[Option[M]]): Meta[Option[A]] = m.imap(_.map(vcLike.apply))(_.map(vcLike.unapply)) + + inline given[A, M](using vcLike: ValueClassLike[A, M], m: Meta[Array[M]], cta: ClassTag[A], ctm: ClassTag[M]): Meta[Array[A]] = m.imap(_.map(vcLike.apply))(_.map(vcLike.unapply)) + + inline given[A, M](using vcLike: ValueClassLike[A, M], m: Meta[Array[Option[M]]], cta: ClassTag[Option[A]]): Meta[Array[Option[A]]] = m.imap(_.map(_.map(vcLike.apply)))(_.map(_.map(vcLike.unapply))) + + inline given[A, M](using instanceConverter: InstanceConverter[A, M], m: Meta[M]): Meta[A] = m.imap(instanceConverter.decode)(instanceConverter.encode) + + inline given[A, M](using instanceConverter: InstanceConverter[A, M], m: Meta[Array[M]], cta: ClassTag[A], ctm: ClassTag[M]): Meta[Array[A]] = m.imap(_.map(instanceConverter.decode))(_.map(instanceConverter.encode)) + + inline given[A, M](using instanceConverter: InstanceConverter[A, M], m: Meta[Array[Option[M]]], cta: ClassTag[Option[A]]): Meta[Array[Option[A]]] = m.imap(_.map(_.map(instanceConverter.decode)))(_.map(_.map(instanceConverter.encode))) +} \ No newline at end of file diff --git a/doobie/src/main/scala-3/pl/iterators/kebs/doobie/enums/KebsEnums.scala b/doobie/src/main/scala-3/pl/iterators/kebs/doobie/enums/KebsEnums.scala new file mode 100644 index 00000000..4e7f7c7a --- /dev/null +++ b/doobie/src/main/scala-3/pl/iterators/kebs/doobie/enums/KebsEnums.scala @@ -0,0 +1,24 @@ +package pl.iterators.kebs.doobie.enums + +import doobie.Meta +import scala.reflect.ClassTag + +import pl.iterators.kebs.core.enums.EnumLike + +trait KebsEnums { + inline given enumMeta[E](using e: EnumLike[E]): Meta[E] = Meta.StringMeta.imap(e.valueOf)(_.toString) + inline given enumArrayMeta[E](using e: EnumLike[E], m: Meta[Array[String]], ct: ClassTag[E]): Meta[Array[E]] = m.imap(_.map(e.valueOf))(_.map(_.toString)) + inline given enumOptionArrayMeta[E](using e: EnumLike[E], m: Meta[Array[Option[String]]], ct: ClassTag[Option[E]]): Meta[Array[Option[E]]] = m.imap(_.map(_.map(e.valueOf)))(_.map(_.map(_.toString))) + + trait Uppercase { + inline given enumUppercaseMeta[E](using e: EnumLike[E]): Meta[E] = Meta.StringMeta.imap(s => e.values.find(_.toString.toUpperCase == s).getOrElse(throw new IllegalArgumentException(s"enum case not found: $s")))(_.toString.toUpperCase) + inline given enumUppercaseArrayMeta[E](using e: EnumLike[E], m: Meta[Array[String]], ct: ClassTag[E]): Meta[Array[E]] = m.imap(_.map(s => e.values.find(_.toString.toUpperCase == s).getOrElse(throw new IllegalArgumentException(s"enum case not found: $s"))))(_.map(_.toString.toUpperCase)) + inline given enumUppercaseOptionArrayMeta[E](using e: EnumLike[E], m: Meta[Array[Option[String]]], ct: ClassTag[Option[E]]): Meta[Array[Option[E]]] = m.imap(_.map(_.map(s => e.values.find(_.toString.toUpperCase == s).getOrElse(throw new IllegalArgumentException(s"enum case not found: $s")))))(_.map(_.map(_.toString.toUpperCase))) + } + + trait Lowercase { + inline given enumLowercaseMeta[E](using e: EnumLike[E]): Meta[E] = Meta.StringMeta.imap(s => e.values.find(_.toString.toLowerCase == s).getOrElse(throw new IllegalArgumentException(s"enum case not found: $s")))(_.toString.toLowerCase) + inline given enumLowercaseMeta[E](using e: EnumLike[E], m: Meta[Array[String]], ct: ClassTag[E]): Meta[Array[E]] = m.imap(_.map(s => e.values.find(_.toString.toLowerCase == s).getOrElse(throw new IllegalArgumentException(s"enum case not found: $s"))))(_.map(_.toString.toLowerCase)) + inline given enumLowercaseOptionArrayMeta[E](using e: EnumLike[E], m: Meta[Array[Option[String]]], ct: ClassTag[Option[E]]): Meta[Array[Option[E]]] = m.imap(_.map(_.map(s => e.values.find(_.toString.toLowerCase == s).getOrElse(throw new IllegalArgumentException(s"enum case not found: $s")))))(_.map(_.map(_.toString.toLowerCase))) + } +} \ No newline at end of file diff --git a/doobie/src/main/scala-2/pl/iterators/kebs/enums/package.scala b/doobie/src/main/scala-3/pl/iterators/kebs/doobie/enums/package.scala similarity index 78% rename from doobie/src/main/scala-2/pl/iterators/kebs/enums/package.scala rename to doobie/src/main/scala-3/pl/iterators/kebs/doobie/enums/package.scala index 153bf1f2..e42cde32 100644 --- a/doobie/src/main/scala-2/pl/iterators/kebs/enums/package.scala +++ b/doobie/src/main/scala-3/pl/iterators/kebs/doobie/enums/package.scala @@ -1,4 +1,4 @@ -package pl.iterators.kebs +package pl.iterators.kebs.doobie package object enums extends KebsEnums { object uppercase extends Uppercase diff --git a/doobie/src/main/scala-3/pl/iterators/kebs/doobie/package.scala b/doobie/src/main/scala-3/pl/iterators/kebs/doobie/package.scala new file mode 100644 index 00000000..829c2abd --- /dev/null +++ b/doobie/src/main/scala-3/pl/iterators/kebs/doobie/package.scala @@ -0,0 +1,3 @@ +package pl.iterators.kebs + +package object doobie extends Kebs \ No newline at end of file diff --git a/doobie/src/main/scala-3/pl/iterators/kebs/enums/KebsEnums.scala b/doobie/src/main/scala-3/pl/iterators/kebs/enums/KebsEnums.scala deleted file mode 100644 index 54717fb0..00000000 --- a/doobie/src/main/scala-3/pl/iterators/kebs/enums/KebsEnums.scala +++ /dev/null @@ -1,24 +0,0 @@ -package pl.iterators.kebs.enums - -import doobie.Meta -import pl.iterators.kebs.macros.enums.{EnumOf, EnumLike} -import scala.reflect.ClassTag -import scala.reflect.Enum - -trait KebsEnums { - inline given enumMeta[E <: Enum](using e: EnumOf[E]): Meta[E] = Meta.StringMeta.imap(e.`enum`.valueOf)(_.toString) - inline given enumArrayMeta[E <: Enum](using e: EnumOf[E], m: Meta[Array[String]], ct: ClassTag[E]): Meta[Array[E]] = m.imap(_.map(e.`enum`.valueOf))(_.map(_.toString)) - inline given enumOptionArrayMeta[E <: Enum](using e: EnumOf[E], m: Meta[Array[Option[String]]], ct: ClassTag[Option[E]]): Meta[Array[Option[E]]] = m.imap(_.map(_.map(e.`enum`.valueOf)))(_.map(_.map(_.toString))) - - trait Uppercase { - inline given enumUppercaseMeta[E <: Enum](using e: EnumOf[E]): Meta[E] = Meta.StringMeta.imap(s => e.`enum`.values.find(_.toString.toUpperCase == s).getOrElse(throw new IllegalArgumentException(s"enum case not found: $s")))(_.toString.toUpperCase) - inline given enumUppercaseArrayMeta[E <: Enum](using e: EnumOf[E], m: Meta[Array[String]], ct: ClassTag[E]): Meta[Array[E]] = m.imap(_.map(s => e.`enum`.values.find(_.toString.toUpperCase == s).getOrElse(throw new IllegalArgumentException(s"enum case not found: $s"))))(_.map(_.toString.toUpperCase)) - inline given enumUppercaseOptionArrayMeta[E <: Enum](using e: EnumOf[E], m: Meta[Array[Option[String]]], ct: ClassTag[Option[E]]): Meta[Array[Option[E]]] = m.imap(_.map(_.map(s => e.`enum`.values.find(_.toString.toUpperCase == s).getOrElse(throw new IllegalArgumentException(s"enum case not found: $s")))))(_.map(_.map(_.toString.toUpperCase))) - } - - trait Lowercase { - inline given enumLowercaseMeta[E <: Enum](using e: EnumOf[E]): Meta[E] = Meta.StringMeta.imap(s => e.`enum`.values.find(_.toString.toLowerCase == s).getOrElse(throw new IllegalArgumentException(s"enum case not found: $s")))(_.toString.toLowerCase) - inline given enumLowercaseMeta[E <: Enum](using e: EnumOf[E], m: Meta[Array[String]], ct: ClassTag[E]): Meta[Array[E]] = m.imap(_.map(s => e.`enum`.values.find(_.toString.toLowerCase == s).getOrElse(throw new IllegalArgumentException(s"enum case not found: $s"))))(_.map(_.toString.toLowerCase)) - inline given enumLowercaseOptionArrayMeta[E <: Enum](using e: EnumOf[E], m: Meta[Array[Option[String]]], ct: ClassTag[Option[E]]): Meta[Array[Option[E]]] = m.imap(_.map(_.map(s => e.`enum`.values.find(_.toString.toLowerCase == s).getOrElse(throw new IllegalArgumentException(s"enum case not found: $s")))))(_.map(_.map(_.toString.toLowerCase))) - } -} \ No newline at end of file diff --git a/doobie/src/main/scala-3/pl/iterators/kebs/package.scala b/doobie/src/main/scala-3/pl/iterators/kebs/package.scala deleted file mode 100644 index feea7054..00000000 --- a/doobie/src/main/scala-3/pl/iterators/kebs/package.scala +++ /dev/null @@ -1,3 +0,0 @@ -package pl.iterators - -package object kebs extends Kebs diff --git a/doobie/src/test/scala-2/ComplexTypesTests.scala b/doobie/src/test/scala-2/pl/iterators/kebs/doobie/ComplexTypesTests.scala similarity index 82% rename from doobie/src/test/scala-2/ComplexTypesTests.scala rename to doobie/src/test/scala-2/pl/iterators/kebs/doobie/ComplexTypesTests.scala index 58dc9e7c..12fa9595 100644 --- a/doobie/src/test/scala-2/ComplexTypesTests.scala +++ b/doobie/src/test/scala-2/pl/iterators/kebs/doobie/ComplexTypesTests.scala @@ -1,27 +1,20 @@ -import enumeratum.{Enum, EnumEntry} +package pl.iterators.kebs.doobie + import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers +import pl.iterators.kebs.enumeratum.KebsEnumeratum -import java.util.Currency import doobie._ import doobie.implicits._ import doobie.postgres._ import doobie.postgres.implicits._ -import pl.iterators.kebs.enums._ -import pl.iterators.kebs._ +import pl.iterators.kebs.doobie.enums._ +import pl.iterators.kebs.doobie._ import pl.iterators.kebs.instances.KebsInstances._ +import pl.iterators.kebs.doobie.model._ +import java.util.Currency -class ComplexTypesTests extends AnyFunSuite with Matchers { - case class Name(name: String) - sealed trait EyeColor extends EnumEntry - object EyeColor extends Enum[EyeColor] { - case object Blue extends EyeColor - case object Green extends EyeColor - case object Brown extends EyeColor - case object Other extends EyeColor - def values = findValues - } - case class Person(name: Name, eyeColor: EyeColor, preferredCurrency: Currency, relatives: List[Name], eyeballsInTheJar: Array[EyeColor]) +class ComplexTypesTests extends AnyFunSuite with Matchers with KebsEnumeratum { test("Put & Get exist") { "implicitly[Get[Name]]" should compile diff --git a/doobie/src/test/scala-2/pl/iterators/kebs/doobie/model/model.scala b/doobie/src/test/scala-2/pl/iterators/kebs/doobie/model/model.scala new file mode 100644 index 00000000..f496c018 --- /dev/null +++ b/doobie/src/test/scala-2/pl/iterators/kebs/doobie/model/model.scala @@ -0,0 +1,23 @@ +package pl.iterators.kebs.doobie + +import enumeratum.{Enum, EnumEntry} +import java.util.Currency + +package object model { + + case class Name(name: String) + + sealed trait EyeColor extends EnumEntry + + object EyeColor extends Enum[EyeColor] { + case object Blue extends EyeColor + case object Green extends EyeColor + case object Brown extends EyeColor + case object Other extends EyeColor + + def values = findValues + } + + case class Person(name: Name, eyeColor: EyeColor, preferredCurrency: Currency, relatives: List[Name], eyeballsInTheJar: Array[EyeColor]) + +} \ No newline at end of file diff --git a/doobie/src/test/scala-3/ComplexTypesTests.scala b/doobie/src/test/scala-3/pl/iterators/kebs/doobie/ComplexTypesTests.scala similarity index 87% rename from doobie/src/test/scala-3/ComplexTypesTests.scala rename to doobie/src/test/scala-3/pl/iterators/kebs/doobie/ComplexTypesTests.scala index 74bb74e3..1c99fc9d 100644 --- a/doobie/src/test/scala-3/ComplexTypesTests.scala +++ b/doobie/src/test/scala-3/pl/iterators/kebs/doobie/ComplexTypesTests.scala @@ -1,25 +1,21 @@ -import org.scalatest.funsuite.AnyFunSuite -import org.scalatest.matchers.should.Matchers +package pl.iterators.kebs.doobie -import java.util.Currency -import doobie.{*, given} import doobie.implicits.given import doobie.postgres.given import doobie.postgres.implicits.given -import pl.iterators.kebs.given -import pl.iterators.kebs.enums.given +import doobie.{*, given} +import org.scalatest.funsuite.AnyFunSuite +import org.scalatest.matchers.should.Matchers +import pl.iterators.kebs.doobie.model._ +import pl.iterators.kebs.enums.KebsEnum +import pl.iterators.kebs.doobie.given +import pl.iterators.kebs.doobie.enums.given import pl.iterators.kebs.instances.KebsInstances.given import pl.iterators.kebs.opaque.Opaque -opaque type Name = String -object Name extends Opaque[Name, String] - -enum EyeColor { - case Blue, Green, Brown, Other -} -case class Person(name: Name, eyeColor: EyeColor, preferredCurrency: Currency, relatives: List[Name], eyeballsInTheJar: Array[EyeColor]) +import java.util.Currency -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 diff --git a/doobie/src/test/scala-3/pl/iterators/kebs/doobie/model/model.scala b/doobie/src/test/scala-3/pl/iterators/kebs/doobie/model/model.scala new file mode 100644 index 00000000..ab7a832b --- /dev/null +++ b/doobie/src/test/scala-3/pl/iterators/kebs/doobie/model/model.scala @@ -0,0 +1,16 @@ +package pl.iterators.kebs.doobie + +import pl.iterators.kebs.opaque.Opaque +import java.util.Currency + +package object model { + opaque type Name = String + object Name extends Opaque[Name, String] + + enum EyeColor { + case Blue, Green, Brown, Other + } + + case class Person(name: Name, eyeColor: EyeColor, preferredCurrency: Currency, relatives: List[Name], eyeballsInTheJar: Array[EyeColor]) + +} diff --git a/enum/src/main/scala-2/pl/iterators/kebs/enums/KebsEnum.scala b/enum/src/main/scala-2/pl/iterators/kebs/enums/KebsEnum.scala new file mode 100644 index 00000000..d64949a5 --- /dev/null +++ b/enum/src/main/scala-2/pl/iterators/kebs/enums/KebsEnum.scala @@ -0,0 +1,20 @@ +package pl.iterators.kebs.enums + +import pl.iterators.kebs.core.enums.EnumLike + +import scala.language.experimental.macros +import scala.reflect.macros.blackbox + +trait KebsEnum { + implicit def enumScala[E <: Enumeration#Value]: EnumLike[E] = macro EnumerationEntryMacros.enumOfImpl[E] +} + +class EnumerationEntryMacros(val c: blackbox.Context) { + def enumOfImpl[E <: Enumeration#Value : c.WeakTypeTag]: c.Expr[EnumLike[E]] = { + import c.universe._ + val valueType = implicitly[c.WeakTypeTag[E]].tpe.dealias + val objectStr = valueType.toString.replaceFirst(".Value$", "") + val objectName = c.typecheck(c.parse(s"$objectStr: $objectStr.type")) + c.Expr[EnumLike[E]](q"new _root_.pl.iterators.kebs.core.enums.EnumLike[$valueType] { override def values: immutable.Seq[${valueType}] = ($objectName).values.toSeq }") + } +} diff --git a/enum/src/main/scala-2/pl/iterators/kebs/enums/package.scala b/enum/src/main/scala-2/pl/iterators/kebs/enums/package.scala new file mode 100644 index 00000000..0b044bd4 --- /dev/null +++ b/enum/src/main/scala-2/pl/iterators/kebs/enums/package.scala @@ -0,0 +1,3 @@ +package pl.iterators.kebs + +package object enums extends KebsEnum diff --git a/enum/src/main/scala-3/pl/iterators/kebs/enums/KebsEnum.scala b/enum/src/main/scala-3/pl/iterators/kebs/enums/KebsEnum.scala new file mode 100644 index 00000000..c35655b9 --- /dev/null +++ b/enum/src/main/scala-3/pl/iterators/kebs/enums/KebsEnum.scala @@ -0,0 +1,34 @@ +package pl.iterators.kebs.enums + +import scala.collection.immutable +import scala.compiletime.{constValue, erasedValue, error, summonInline} +import scala.deriving.Mirror +import scala.reflect.{ClassTag, Enum} + +import pl.iterators.kebs.core.enums.EnumLike + +trait KebsEnum { + inline implicit def kebsEnumScala[E <: Enum](using m: Mirror.SumOf[E], ct: ClassTag[E]): EnumLike[E] = { + val enumValues = summonCases[m.MirroredElemTypes, E] + new EnumLike[E] { + override def values: immutable.Seq[E] = enumValues.toSeq + } + } +} + +inline private def widen[A, B](a: A): A & B = + inline a match { + case b: B => b + } + +inline private def summonCases[T <: Tuple, A]: List[A] = + inline erasedValue[T] match { + case _: (h *: t) => + (inline summonInline[Mirror.Of[h]] match { + case m: Mirror.Singleton => + widen[m.MirroredMonoType, A](m.fromProduct(EmptyTuple)) :: summonCases[t, A] + case x => error("Enums cannot include parameterized cases.") + }) + + case _: EmptyTuple => Nil +} \ No newline at end of file diff --git a/enum/src/main/scala-3/pl/iterators/kebs/enums/KebsValueEnum.scala b/enum/src/main/scala-3/pl/iterators/kebs/enums/KebsValueEnum.scala new file mode 100644 index 00000000..9fae0a32 --- /dev/null +++ b/enum/src/main/scala-3/pl/iterators/kebs/enums/KebsValueEnum.scala @@ -0,0 +1,29 @@ +package pl.iterators.kebs.enums + +import scala.collection.immutable +import scala.compiletime.{constValue, erasedValue, error, summonInline} +import scala.deriving.Mirror +import scala.reflect.ClassTag + +import pl.iterators.kebs.core.enums.{ValueEnumLike, ValueEnumLikeEntry} + +trait KebsValueEnum { + inline implicit def valueEnumScala[V, E <: ValueEnumLikeEntry[V]](using classTag: ClassTag[E], m: Mirror.SumOf[E]): ValueEnumLike[V, E] = { + val enumValues = summonValueCases[m.MirroredElemTypes, V, E] + new ValueEnumLike[V, E] { + override def values: immutable.Seq[E] = enumValues.toSeq + } + } +} + +inline private def summonValueCases[T <: Tuple, V, A <: ValueEnumLikeEntry[V]]: List[A] = + inline erasedValue[T] match { + case _: (h *: t) => + (inline summonInline[Mirror.Of[h]] match { + case m: Mirror.Singleton => + widen[m.MirroredMonoType, A](m.fromProduct(EmptyTuple)) :: summonValueCases[t, V, A] + case x => error("Enums cannot include parameterized cases.") + }) + + case _: EmptyTuple => Nil +} diff --git a/enum/src/main/scala-3/pl/iterators/kebs/enums/package.scala b/enum/src/main/scala-3/pl/iterators/kebs/enums/package.scala new file mode 100644 index 00000000..38514833 --- /dev/null +++ b/enum/src/main/scala-3/pl/iterators/kebs/enums/package.scala @@ -0,0 +1,3 @@ +package pl.iterators.kebs + +package object enums extends KebsEnum with KebsValueEnum diff --git a/enum/src/test/scala-2/pl/iterators/kebs/enums/domain/Color.scala b/enum/src/test/scala-2/pl/iterators/kebs/enums/domain/Color.scala new file mode 100644 index 00000000..cee9d48e --- /dev/null +++ b/enum/src/test/scala-2/pl/iterators/kebs/enums/domain/Color.scala @@ -0,0 +1,10 @@ +package pl.iterators.kebs.enums.domain + +object Color extends Enumeration { + type Color = Value + val Red, Green, Blue = Value +} +object ColorDomain { + val colorValues = Color.values.toList + type ColorType = Color.Color +} \ No newline at end of file diff --git a/enum/src/test/scala-2/pl/iterators/kebs/enums/enums.scala b/enum/src/test/scala-2/pl/iterators/kebs/enums/enums.scala new file mode 100644 index 00000000..0b044bd4 --- /dev/null +++ b/enum/src/test/scala-2/pl/iterators/kebs/enums/enums.scala @@ -0,0 +1,3 @@ +package pl.iterators.kebs + +package object enums extends KebsEnum diff --git a/enum/src/test/scala-3/pl/iterators/kebs/enums/ValueEnumTest.scala b/enum/src/test/scala-3/pl/iterators/kebs/enums/ValueEnumTest.scala new file mode 100644 index 00000000..5baf1943 --- /dev/null +++ b/enum/src/test/scala-3/pl/iterators/kebs/enums/ValueEnumTest.scala @@ -0,0 +1,21 @@ +package pl.iterators.kebs.enums + +import org.scalacheck.Prop.forAll +import org.scalacheck.{Gen, Properties} +import scala.reflect.{ClassTag, Enum} + +import pl.iterators.kebs.core.enums.{ValueEnumLike, ValueEnumLikeEntry} + +object DerivingSpecification extends Properties("Deriving") with KebsValueEnum { + + enum ColorButRGB(val value: Int) extends ValueEnumLikeEntry[Int] { + case Red extends ColorButRGB(0xFF0000) + case Green extends ColorButRGB(0x00FF00) + case Blue extends ColorButRGB(0x0000FF) + } + + property("ValueEnumLike derives properly for a value enum") = forAll(Gen.oneOf(ColorButRGB.values.toList)) { (color: ColorButRGB) => + val tc = implicitly[ValueEnumLike[Int, ColorButRGB]] + tc.values.contains(color) && tc.valueOf(color.value) == color && tc.fromOrdinal(color.ordinal) == color + } +} diff --git a/enum/src/test/scala-3/pl/iterators/kebs/enums/domain/Color.scala b/enum/src/test/scala-3/pl/iterators/kebs/enums/domain/Color.scala new file mode 100644 index 00000000..587f38fc --- /dev/null +++ b/enum/src/test/scala-3/pl/iterators/kebs/enums/domain/Color.scala @@ -0,0 +1,9 @@ +package pl.iterators.kebs.enums.domain + +enum Color { + case Red, Green, Blue +} +object ColorDomain { + val colorValues = Color.values.toList + type ColorType = Color +} \ No newline at end of file diff --git a/enum/src/test/scala-3/pl/iterators/kebs/enums/package.scala b/enum/src/test/scala-3/pl/iterators/kebs/enums/package.scala new file mode 100644 index 00000000..3eaf7405 --- /dev/null +++ b/enum/src/test/scala-3/pl/iterators/kebs/enums/package.scala @@ -0,0 +1,3 @@ +package pl.iterators.kebs + +package object enums extends KebsEnum with KebsValueEnum \ No newline at end of file diff --git a/enum/src/test/scala/pl/iterators/kebs/enums/EnumTest.scala b/enum/src/test/scala/pl/iterators/kebs/enums/EnumTest.scala new file mode 100644 index 00000000..6afa92f2 --- /dev/null +++ b/enum/src/test/scala/pl/iterators/kebs/enums/EnumTest.scala @@ -0,0 +1,15 @@ +package pl.iterators.kebs.enums + +import scala.collection.immutable +import org.scalacheck.Prop.forAll +import org.scalacheck.{Gen, Properties} +import pl.iterators.kebs.core.enums.EnumLike +import pl.iterators.kebs.enums.domain.ColorDomain + +object EnumTest extends Properties("Deriving") with KebsEnum { + + property("EnumLike derives properly for an enum") = forAll(Gen.oneOf(ColorDomain.colorValues)) { (color: ColorDomain.ColorType) => + val tc = implicitly[EnumLike[ColorDomain.ColorType]] + tc.values.contains(color) && tc.valueOf(color.toString) == color + } +} diff --git a/enumeratum/src/main/scala-2/pl/iterators/kebs/enumeratum/KebsEnumeratum.scala b/enumeratum/src/main/scala-2/pl/iterators/kebs/enumeratum/KebsEnumeratum.scala new file mode 100644 index 00000000..6fddd67a --- /dev/null +++ b/enumeratum/src/main/scala-2/pl/iterators/kebs/enumeratum/KebsEnumeratum.scala @@ -0,0 +1,26 @@ +package pl.iterators.kebs.enumeratum + +import scala.language.experimental.macros +import scala.reflect.macros.blackbox +import enumeratum.EnumEntry +import pl.iterators.kebs.core.enums.EnumLike +import pl.iterators.kebs.core.macros.MacroUtils + +trait KebsEnumeratum { + implicit def enumeratumScala2[E <: EnumEntry]: EnumLike[E] = macro EnumeratumEntryMacros.enumeratumOfImpl[E] +} + +class EnumeratumEntryMacros(val c: blackbox.Context) extends MacroUtils { + import c.universe._ + + private def assertEnumEntry(t: Type, msg: => String) = if (!(t <:< typeOf[EnumEntry])) c.abort(c.enclosingPosition, msg) + + def enumeratumOfImpl[E <: EnumEntry: c.WeakTypeTag]: c.Expr[EnumLike[E]] = { + val EnumEntry = weakTypeOf[E] + assertEnumEntry(EnumEntry, s"${EnumEntry.typeSymbol} must subclass EnumEntry") + + c.Expr[EnumLike[E]](q"new _root_.pl.iterators.kebs.core.enums.EnumLike[${EnumEntry.typeSymbol}] { override def values: Seq[${EnumEntry.typeSymbol}] = ${companion(EnumEntry)}.values.toSeq }") + } +} + + diff --git a/enumeratum/src/main/scala-2/pl/iterators/kebs/enumeratum/KebsValueEnumeratum.scala b/enumeratum/src/main/scala-2/pl/iterators/kebs/enumeratum/KebsValueEnumeratum.scala new file mode 100644 index 00000000..c1edecd6 --- /dev/null +++ b/enumeratum/src/main/scala-2/pl/iterators/kebs/enumeratum/KebsValueEnumeratum.scala @@ -0,0 +1,34 @@ +package pl.iterators.kebs.enumeratum + +import enumeratum.values._ +import pl.iterators.kebs.core.enums.{ValueEnumLike, ValueEnumLikeEntry} + +import scala.language.experimental.macros +import scala.reflect.macros.blackbox + +trait KebsValueEnumeratum { + implicit def valueIntEnumeratumScala2[E <: IntEnumEntry with ValueEnumLikeEntry[Int]]: ValueEnumLike[Int, E] = macro ValueEnumEntryMacros.valueEnumOfImpl[Int, E] + implicit def valueShortEnumeratumScala2[E <: ShortEnumEntry with ValueEnumLikeEntry[Short]]: ValueEnumLike[Short, E] = macro ValueEnumEntryMacros.valueEnumOfImpl[Short, E] + implicit def valueLongEnumeratumScala2[E <: LongEnumEntry with ValueEnumLikeEntry[Long]]: ValueEnumLike[Long, E] = macro ValueEnumEntryMacros.valueEnumOfImpl[Long, E] + implicit def valueByteEnumeratumScala2[E <: ByteEnumEntry with ValueEnumLikeEntry[Byte]]: ValueEnumLike[Byte, E] = macro ValueEnumEntryMacros.valueEnumOfImpl[Byte, E] + implicit def valueStringEnumeratumScala2[E <: StringEnumEntry with ValueEnumLikeEntry[String]]: ValueEnumLike[String, E] = macro ValueEnumEntryMacros.valueEnumOfImpl[String, E] + implicit def valueEnumeratumScala2[ValueType, E <: ValueEnumEntry[ValueType] with ValueEnumLikeEntry[ValueType]]: ValueEnumLike[ValueType, E] = macro ValueEnumEntryMacros.valueEnumOfImpl[ValueType, E] +} + + +class ValueEnumEntryMacros(val c: blackbox.Context) { + import c.universe._ + + def valueEnumOfImpl[ValueType: c.WeakTypeTag, E <: ValueEnumEntry[ValueType] with ValueEnumLikeEntry[ValueType]: c.WeakTypeTag]: c.Expr[ValueEnumLike[ValueType, E]] = { + val ValueType = weakTypeOf[ValueType] + val EnumEntry = weakTypeOf[E] + + c.Expr[ValueEnumLike[ValueType, E]]( + q""" + new _root_.pl.iterators.kebs.core.enums.ValueEnumLike[${ValueType}, ${EnumEntry}] { + override def values: Seq[$EnumEntry] = ${EnumEntry.typeSymbol.companion}.values.toSeq + } + """ + ) + } +} diff --git a/enumeratum/src/main/scala-2/pl/iterators/kebs/enumeratum/package.scala b/enumeratum/src/main/scala-2/pl/iterators/kebs/enumeratum/package.scala new file mode 100644 index 00000000..5212edef --- /dev/null +++ b/enumeratum/src/main/scala-2/pl/iterators/kebs/enumeratum/package.scala @@ -0,0 +1,3 @@ +package pl.iterators.kebs + +package object enumeratum extends KebsEnumeratum with KebsValueEnumeratum diff --git a/enumeratum/src/main/scala-3/pl/iterators/kebs/enumeratum/KebsEnumeratum.scala b/enumeratum/src/main/scala-3/pl/iterators/kebs/enumeratum/KebsEnumeratum.scala new file mode 100644 index 00000000..d9a85519 --- /dev/null +++ b/enumeratum/src/main/scala-3/pl/iterators/kebs/enumeratum/KebsEnumeratum.scala @@ -0,0 +1,35 @@ +package pl.iterators.kebs.enumeratum + +import enumeratum._ +import scala.collection.immutable +import scala.compiletime.{constValue, erasedValue, error, summonInline} +import scala.deriving.Mirror +import scala.reflect.ClassTag + +import pl.iterators.kebs.core.enums.EnumLike + +trait KebsEnumeratum { + inline given [E <: EnumEntry](using m: Mirror.SumOf[E], ct: ClassTag[E]): EnumLike[E] = { + val enumValues = summonCases[m.MirroredElemTypes, E] + new EnumLike[E] { + override def values: immutable.Seq[E] = enumValues.toSeq + } + } +} + +inline private def widen[A, B] (a: A): A & B = + inline a match { + case b: B => b + } + +inline private def summonCases[T <: Tuple, A]: List[A] = + inline erasedValue[T] match { + case _: (h *: t) => + (inline summonInline[Mirror.Of[h]] match { + case m: Mirror.Singleton => + widen[m.MirroredMonoType, A](m.fromProduct(EmptyTuple)) :: summonCases[t, A] + case x => error("Enums cannot include parameterized cases.") + }) + + case _: EmptyTuple => Nil +} diff --git a/enumeratum/src/main/scala-3/pl/iterators/kebs/enumeratum/KebsValueEnumeratum.scala b/enumeratum/src/main/scala-3/pl/iterators/kebs/enumeratum/KebsValueEnumeratum.scala new file mode 100644 index 00000000..dad18869 --- /dev/null +++ b/enumeratum/src/main/scala-3/pl/iterators/kebs/enumeratum/KebsValueEnumeratum.scala @@ -0,0 +1,35 @@ +package pl.iterators.kebs.enumeratum + +import scala.collection.immutable +import enumeratum.values._ +import scala.compiletime.{constValue, erasedValue, error, summonInline} +import scala.deriving._ +import scala.reflect.ClassTag + +import pl.iterators.kebs.core.enums.{ValueEnumLike, ValueEnumLikeEntry} + +trait KebsValueEnumeratum { + inline given [V, E <: ValueEnumEntry[V] with ValueEnumLikeEntry[V]](using m: Mirror.SumOf[E], ct: ClassTag[E]): ValueEnumLike[V, E] = { + val enumValues = summonValueCases[m.MirroredElemTypes, V, E] + new ValueEnumLike[V, E] { + override def values: immutable.Seq[E] = enumValues + } + } +} + +inline private def widen[A, B] (a: A): A & B = + inline a match { + case b: B => b + } + +inline private def summonValueCases[T <: Tuple, V, A <: ValueEnumEntry[V]]: List[A] = + inline erasedValue[T] match { + case _: (h *: t) => + (inline summonInline[Mirror.Of[h]] match { + case m: Mirror.Singleton => + widen[m.MirroredMonoType, A](m.fromProduct(EmptyTuple)) :: summonValueCases[t, V, A] + case x => error("Enums cannot include parameterized cases.") + }) + + case _: EmptyTuple => Nil +} diff --git a/enumeratum/src/main/scala-3/pl/iterators/kebs/enumeratum/package.scala b/enumeratum/src/main/scala-3/pl/iterators/kebs/enumeratum/package.scala new file mode 100644 index 00000000..5212edef --- /dev/null +++ b/enumeratum/src/main/scala-3/pl/iterators/kebs/enumeratum/package.scala @@ -0,0 +1,3 @@ +package pl.iterators.kebs + +package object enumeratum extends KebsEnumeratum with KebsValueEnumeratum diff --git a/enumeratum/src/test/scala/pl/iterators/kebs/enumeratum/EnumeratumTest.scala b/enumeratum/src/test/scala/pl/iterators/kebs/enumeratum/EnumeratumTest.scala new file mode 100644 index 00000000..24b74b2f --- /dev/null +++ b/enumeratum/src/test/scala/pl/iterators/kebs/enumeratum/EnumeratumTest.scala @@ -0,0 +1,23 @@ +package pl.iterators.kebs.enumeratum + +import enumeratum._ +import org.scalacheck.Prop.forAll +import org.scalacheck.{Gen, Properties} +import pl.iterators.kebs.core.enums.EnumLike + +object EnumeratumTest extends Properties("Deriving") with KebsEnumeratum { + + sealed trait Greeting extends EnumEntry + object Greeting extends Enum[Greeting] { + val values = findValues + case object Hello extends Greeting + case object GoodBye extends Greeting + case object Hi extends Greeting + case object Bye extends Greeting + } + + property("EnumLike derives properly for an enumeratum enum") = forAll(Gen.oneOf(Greeting.values.toList)) { (greeting: Greeting) => + val tc = implicitly[EnumLike[Greeting]] + tc.values.contains(greeting) && tc.valueOf(greeting.toString) == greeting && tc.fromOrdinal(Greeting.values.indexOf(greeting)) == greeting + } +} diff --git a/enumeratum/src/test/scala/pl/iterators/kebs/enumeratum/ValueEnumeratumTest.scala b/enumeratum/src/test/scala/pl/iterators/kebs/enumeratum/ValueEnumeratumTest.scala new file mode 100644 index 00000000..1fb242ea --- /dev/null +++ b/enumeratum/src/test/scala/pl/iterators/kebs/enumeratum/ValueEnumeratumTest.scala @@ -0,0 +1,22 @@ +package pl.iterators.kebs.enumeratum + +import enumeratum.values._ +import org.scalacheck.Prop.forAll +import org.scalacheck.{Gen, Properties} +import pl.iterators.kebs.core.enums.{ValueEnumLike, ValueEnumLikeEntry} + +object ValueEnumTest extends Properties("Deriving") with KebsValueEnumeratum { + sealed abstract class LibraryItem(val value: Int) extends IntEnumEntry with ValueEnumLikeEntry[Int] + object LibraryItem extends IntEnum[LibraryItem] { + case object Book extends LibraryItem(value = 1) + case object Movie extends LibraryItem(value = 2) + case object Magazine extends LibraryItem(3) + case object CD extends LibraryItem(4) + val values = findValues + } + + property("ValueEnumLike derives properly for a value enum") = forAll(Gen.oneOf(LibraryItem.values.toList)) { (libraryItem: LibraryItem) => + val tc = implicitly[ValueEnumLike[Int, LibraryItem]] + tc.values.contains(libraryItem) && tc.valueOf(libraryItem.value) == libraryItem + } +} diff --git a/enumeratum/src/test/scala/pl/iterators/kebs/enumeratum/package.scala b/enumeratum/src/test/scala/pl/iterators/kebs/enumeratum/package.scala new file mode 100644 index 00000000..5212edef --- /dev/null +++ b/enumeratum/src/test/scala/pl/iterators/kebs/enumeratum/package.scala @@ -0,0 +1,3 @@ +package pl.iterators.kebs + +package object enumeratum extends KebsEnumeratum with KebsValueEnumeratum diff --git a/examples/src/main/scala/pl/iterators/kebs_examples/CirceExample.scala b/examples/src/main/scala/pl/iterators/kebs/examples/CirceExample.scala similarity index 99% rename from examples/src/main/scala/pl/iterators/kebs_examples/CirceExample.scala rename to examples/src/main/scala/pl/iterators/kebs/examples/CirceExample.scala index 9840a610..5c04e7a8 100644 --- a/examples/src/main/scala/pl/iterators/kebs_examples/CirceExample.scala +++ b/examples/src/main/scala/pl/iterators/kebs/examples/CirceExample.scala @@ -1,4 +1,4 @@ -package pl.iterators.kebs_examples +package pl.iterators.kebs.examples import java.net.URL import java.util.UUID diff --git a/examples/src/main/scala/pl/iterators/kebs_examples/EnumSprayJsonFormat.scala b/examples/src/main/scala/pl/iterators/kebs/examples/EnumSprayJsonFormat.scala similarity index 99% rename from examples/src/main/scala/pl/iterators/kebs_examples/EnumSprayJsonFormat.scala rename to examples/src/main/scala/pl/iterators/kebs/examples/EnumSprayJsonFormat.scala index ba6812af..8788bea4 100644 --- a/examples/src/main/scala/pl/iterators/kebs_examples/EnumSprayJsonFormat.scala +++ b/examples/src/main/scala/pl/iterators/kebs/examples/EnumSprayJsonFormat.scala @@ -1,4 +1,4 @@ -package pl.iterators.kebs_examples +package pl.iterators.kebs.examples import java.net.URL import java.util.UUID diff --git a/examples/src/main/scala/pl/iterators/kebs_examples/EnumValueColumnType.scala b/examples/src/main/scala/pl/iterators/kebs/examples/EnumValueColumnType.scala similarity index 98% rename from examples/src/main/scala/pl/iterators/kebs_examples/EnumValueColumnType.scala rename to examples/src/main/scala/pl/iterators/kebs/examples/EnumValueColumnType.scala index 96db161d..5d61b36c 100644 --- a/examples/src/main/scala/pl/iterators/kebs_examples/EnumValueColumnType.scala +++ b/examples/src/main/scala/pl/iterators/kebs/examples/EnumValueColumnType.scala @@ -1,4 +1,4 @@ -package pl.iterators.kebs_examples +package pl.iterators.kebs.examples import com.github.tminglei.slickpg.ExPostgresProfile import enumeratum.{Enum, EnumEntry} @@ -64,7 +64,6 @@ object EnumValueColumnType { object AfterKebs { import slick.jdbc.PostgresProfile.api._ import pl.iterators.kebs._ - import enums._ class People(tag: Tag) extends Table[Person](tag, "people") { def userId: Rep[UserId] = column[UserId]("user_id") @@ -98,12 +97,12 @@ object EnumValueColumnType { } object AfterKebsTraitStyle { - import pl.iterators.kebs.Kebs - import pl.iterators.kebs.enums.KebsEnums + + import pl.iterators.kebs.slick.KebsSlickSupport object MyPostgresProfile extends ExPostgresProfile { override val api: APIWithKebsAndEnums = new APIWithKebsAndEnums {} - trait APIWithKebsAndEnums extends super.API with Kebs with KebsEnums.Lowercase + trait APIWithKebsAndEnums extends super.API with KebsSlickSupport with KebsEnums.Lowercase } import MyPostgresProfile.api._ diff --git a/examples/src/main/scala/pl/iterators/kebs_examples/HStoreColumnType.scala b/examples/src/main/scala/pl/iterators/kebs/examples/HStoreColumnType.scala similarity index 95% rename from examples/src/main/scala/pl/iterators/kebs_examples/HStoreColumnType.scala rename to examples/src/main/scala/pl/iterators/kebs/examples/HStoreColumnType.scala index 8f32cf15..0fd39192 100644 --- a/examples/src/main/scala/pl/iterators/kebs_examples/HStoreColumnType.scala +++ b/examples/src/main/scala/pl/iterators/kebs/examples/HStoreColumnType.scala @@ -1,4 +1,4 @@ -package pl.iterators.kebs_examples +package pl.iterators.kebs.examples import com.github.tminglei.slickpg.{ExPostgresProfile, PgHStoreSupport} import org.postgresql.util.HStoreConverter @@ -72,12 +72,12 @@ object HStoreColumnType { } object AfterKebs { - import pl.iterators.kebs.Kebs - import pl.iterators.kebs.instances.time.YearMonthString + import pl.iterators.kebs.circe.instances.time.YearMonthString + import pl.iterators.kebs.slick.KebsSlickSupport object MyPostgresProfile extends ExPostgresProfile with PgHStoreSupport { override val api: APIWithHstore = new APIWithHstore {} - trait APIWithHstore extends super.API with HStoreImplicits with Kebs with YearMonthString + trait APIWithHstore extends super.API with HStoreImplicits with KebsSlickSupport with YearMonthString } import MyPostgresProfile.api._ diff --git a/examples/src/main/scala/pl/iterators/kebs_examples/HStoreExtensionMethods.scala b/examples/src/main/scala/pl/iterators/kebs/examples/HStoreExtensionMethods.scala similarity index 93% rename from examples/src/main/scala/pl/iterators/kebs_examples/HStoreExtensionMethods.scala rename to examples/src/main/scala/pl/iterators/kebs/examples/HStoreExtensionMethods.scala index de17d9c3..a719b833 100644 --- a/examples/src/main/scala/pl/iterators/kebs_examples/HStoreExtensionMethods.scala +++ b/examples/src/main/scala/pl/iterators/kebs/examples/HStoreExtensionMethods.scala @@ -1,4 +1,4 @@ -package pl.iterators.kebs_examples +package pl.iterators.kebs.examples import com.github.tminglei.slickpg.{ExPostgresProfile, PgArraySupport, PgHStoreSupport} import slick.lifted.MappedProjection @@ -48,12 +48,12 @@ object HStoreExtensionMethods { } object AfterKebs { - import pl.iterators.kebs.Kebs - import pl.iterators.kebs.instances.time.YearMonthString + import pl.iterators.kebs.circe.instances.time.YearMonthString + import pl.iterators.kebs.slick.KebsSlickSupport object MyPostgresProfile extends ExPostgresProfile with PgHStoreSupport with PgArraySupport { override val api: APIWithHstore = new APIWithHstore {} - trait APIWithHstore extends super.API with HStoreImplicits with ArrayImplicits with Kebs with YearMonthString + trait APIWithHstore extends super.API with HStoreImplicits with ArrayImplicits with KebsSlickSupport with YearMonthString } import MyPostgresProfile.api._ diff --git a/examples/src/main/scala/pl/iterators/kebs_examples/ListValueCommonType.scala b/examples/src/main/scala/pl/iterators/kebs/examples/ListValueCommonType.scala similarity index 95% rename from examples/src/main/scala/pl/iterators/kebs_examples/ListValueCommonType.scala rename to examples/src/main/scala/pl/iterators/kebs/examples/ListValueCommonType.scala index d13bd917..fb2af4ee 100644 --- a/examples/src/main/scala/pl/iterators/kebs_examples/ListValueCommonType.scala +++ b/examples/src/main/scala/pl/iterators/kebs/examples/ListValueCommonType.scala @@ -1,4 +1,4 @@ -package pl.iterators.kebs_examples +package pl.iterators.kebs.examples import com.github.tminglei.slickpg._ @@ -46,10 +46,11 @@ object ListValueCommonType { } object AfterKebsTraitStyle { - import pl.iterators.kebs.Kebs + + import pl.iterators.kebs.slick.KebsSlickSupport object MyPostgresProfile extends ExPostgresProfile with PgArraySupport { override val api: APIWithArraysAndKebs = new APIWithArraysAndKebs {} - trait APIWithArraysAndKebs extends super.API with ArrayImplicits with Kebs + trait APIWithArraysAndKebs extends super.API with ArrayImplicits with KebsSlickSupport } import MyPostgresProfile.api._ diff --git a/examples/src/main/scala/pl/iterators/kebs_examples/NumericExample.scala b/examples/src/main/scala/pl/iterators/kebs/examples/NumericExample.scala similarity index 87% rename from examples/src/main/scala/pl/iterators/kebs_examples/NumericExample.scala rename to examples/src/main/scala/pl/iterators/kebs/examples/NumericExample.scala index 1f4b6542..31ab0594 100644 --- a/examples/src/main/scala/pl/iterators/kebs_examples/NumericExample.scala +++ b/examples/src/main/scala/pl/iterators/kebs/examples/NumericExample.scala @@ -1,8 +1,7 @@ -package pl.iterators.kebs_examples +package pl.iterators.kebs.examples import pl.iterators.kebs.tag.meta.tagged import pl.iterators.kebs.tagged._ -import pl.iterators.kebs.support._ @tagged trait NumericDomain { trait Tag1 diff --git a/examples/src/main/scala/pl/iterators/kebs_examples/PekkoHttpUnmarshallers.scala b/examples/src/main/scala/pl/iterators/kebs/examples/PekkoHttpUnmarshallers.scala similarity index 97% rename from examples/src/main/scala/pl/iterators/kebs_examples/PekkoHttpUnmarshallers.scala rename to examples/src/main/scala/pl/iterators/kebs/examples/PekkoHttpUnmarshallers.scala index 6090cfe9..77c91b12 100644 --- a/examples/src/main/scala/pl/iterators/kebs_examples/PekkoHttpUnmarshallers.scala +++ b/examples/src/main/scala/pl/iterators/kebs/examples/PekkoHttpUnmarshallers.scala @@ -1,4 +1,4 @@ -package pl.iterators.kebs_examples +package pl.iterators.kebs.examples import org.apache.pekko.http.scaladsl.model.StatusCodes import org.apache.pekko.http.scaladsl.server.Directives._ @@ -66,8 +66,6 @@ object PekkoHttpUnmarshallers { } object AfterKebs { - import pl.iterators.kebs.unmarshallers._ - import enums._ val route = get { parameters(Symbol("sortBy").as[Column], diff --git a/examples/src/main/scala/pl/iterators/kebs_examples/PlayJsonFormat.scala b/examples/src/main/scala/pl/iterators/kebs/examples/PlayJsonFormat.scala similarity index 99% rename from examples/src/main/scala/pl/iterators/kebs_examples/PlayJsonFormat.scala rename to examples/src/main/scala/pl/iterators/kebs/examples/PlayJsonFormat.scala index 05f7b107..112da84c 100644 --- a/examples/src/main/scala/pl/iterators/kebs_examples/PlayJsonFormat.scala +++ b/examples/src/main/scala/pl/iterators/kebs/examples/PlayJsonFormat.scala @@ -1,4 +1,4 @@ -package pl.iterators.kebs_examples +package pl.iterators.kebs.examples import java.net.URL import java.util.UUID diff --git a/examples/src/main/scala/pl/iterators/kebs_examples/SlickTaggedExample.scala b/examples/src/main/scala/pl/iterators/kebs/examples/SlickTaggedExample.scala similarity index 87% rename from examples/src/main/scala/pl/iterators/kebs_examples/SlickTaggedExample.scala rename to examples/src/main/scala/pl/iterators/kebs/examples/SlickTaggedExample.scala index b7fca3a6..0db482d3 100644 --- a/examples/src/main/scala/pl/iterators/kebs_examples/SlickTaggedExample.scala +++ b/examples/src/main/scala/pl/iterators/kebs/examples/SlickTaggedExample.scala @@ -1,11 +1,11 @@ -package pl.iterators.kebs_examples +package pl.iterators.kebs.examples import slick.jdbc.PostgresProfile.api._ import slick.lifted.ProvenShape import pl.iterators.kebs.tagged._ -import pl.iterators.kebs.tagged.slick.SlickSupport +import pl.iterators.kebs.tagged.slick.TaggedSlickSupport -object SlickTaggedExample extends SlickSupport { +object SlickTaggedExample extends TaggedSlickSupport { trait UserIdTag type UserId = Long @@ UserIdTag diff --git a/examples/src/main/scala/pl/iterators/kebs_examples/SprayJsonFormat.scala b/examples/src/main/scala/pl/iterators/kebs/examples/SprayJsonFormat.scala similarity index 96% rename from examples/src/main/scala/pl/iterators/kebs_examples/SprayJsonFormat.scala rename to examples/src/main/scala/pl/iterators/kebs/examples/SprayJsonFormat.scala index 68c29b08..bcb3a472 100644 --- a/examples/src/main/scala/pl/iterators/kebs_examples/SprayJsonFormat.scala +++ b/examples/src/main/scala/pl/iterators/kebs/examples/SprayJsonFormat.scala @@ -1,11 +1,11 @@ -package pl.iterators.kebs_examples +package pl.iterators.kebs.examples import org.apache.pekko.http.scaladsl.marshallers.sprayjson.SprayJsonSupport import org.apache.pekko.http.scaladsl.marshalling.ToResponseMarshallable import org.apache.pekko.http.scaladsl.model.StatusCodes._ import org.apache.pekko.http.scaladsl.server.Directives._ -import pl.iterators.kebs.instances.net.URIString -import pl.iterators.kebs.instances.util.UUIDString +import pl.iterators.kebs.circe.instances.net.URIString +import pl.iterators.kebs.circe.instances.util.UUIDString import pl.iterators.kebs.json.KebsSpray import spray.json._ diff --git a/examples/src/main/scala/pl/iterators/kebs_examples/SprayJsonWithPekkoHttpExample.scala b/examples/src/main/scala/pl/iterators/kebs/examples/SprayJsonWithPekkoHttpExample.scala similarity index 99% rename from examples/src/main/scala/pl/iterators/kebs_examples/SprayJsonWithPekkoHttpExample.scala rename to examples/src/main/scala/pl/iterators/kebs/examples/SprayJsonWithPekkoHttpExample.scala index b7332a3c..752fc959 100644 --- a/examples/src/main/scala/pl/iterators/kebs_examples/SprayJsonWithPekkoHttpExample.scala +++ b/examples/src/main/scala/pl/iterators/kebs/examples/SprayJsonWithPekkoHttpExample.scala @@ -1,4 +1,4 @@ -package pl.iterators.kebs_examples +package pl.iterators.kebs.examples import java.io.IOException import java.time.format.DateTimeFormatter diff --git a/examples/src/main/scala/pl/iterators/kebs_examples/TaggedMeta.scala b/examples/src/main/scala/pl/iterators/kebs/examples/TaggedMeta.scala similarity index 97% rename from examples/src/main/scala/pl/iterators/kebs_examples/TaggedMeta.scala rename to examples/src/main/scala/pl/iterators/kebs/examples/TaggedMeta.scala index e9cecfe3..7c93da12 100644 --- a/examples/src/main/scala/pl/iterators/kebs_examples/TaggedMeta.scala +++ b/examples/src/main/scala/pl/iterators/kebs/examples/TaggedMeta.scala @@ -1,4 +1,4 @@ -package pl.iterators.kebs_examples +package pl.iterators.kebs.examples import java.util.UUID import pl.iterators.kebs.tagged._ diff --git a/examples/src/main/scala/pl/iterators/kebs_examples/ValueColumnType.scala b/examples/src/main/scala/pl/iterators/kebs/examples/ValueColumnType.scala similarity index 99% rename from examples/src/main/scala/pl/iterators/kebs_examples/ValueColumnType.scala rename to examples/src/main/scala/pl/iterators/kebs/examples/ValueColumnType.scala index 88847ecd..f2b79208 100644 --- a/examples/src/main/scala/pl/iterators/kebs_examples/ValueColumnType.scala +++ b/examples/src/main/scala/pl/iterators/kebs/examples/ValueColumnType.scala @@ -1,4 +1,4 @@ -package pl.iterators.kebs_examples +package pl.iterators.kebs.examples import slick.jdbc.PostgresProfile.api._ import slick.lifted.ProvenShape diff --git a/http4s-stir/src/main/scala-2/matchers/KebsMatchers.scala b/http4s-stir/src/main/scala-2/matchers/KebsMatchers.scala deleted file mode 100644 index f17b477c..00000000 --- a/http4s-stir/src/main/scala-2/matchers/KebsMatchers.scala +++ /dev/null @@ -1,26 +0,0 @@ -package pl.iterators.kebs.matchers - -import pl.iterators.stir.server.PathMatcher1 -import enumeratum.{Enum, EnumEntry} -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.macros.CaseClass1Rep - -import scala.language.implicitConversions - -trait KebsMatchers extends pl.iterators.stir.server.PathMatchers { - - implicit class SegmentIsomorphism[U](segment: PathMatcher1[U]) { - def as[T](implicit rep: CaseClass1Rep[T, U]): PathMatcher1[T] = segment.map(rep.apply) - } - - 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) - } - } -} diff --git a/http4s-stir/src/main/scala-2/pl/iterators/kebs/http4sstir/matchers/KebsMatchers.scala b/http4s-stir/src/main/scala-2/pl/iterators/kebs/http4sstir/matchers/KebsMatchers.scala new file mode 100644 index 00000000..5da229b3 --- /dev/null +++ b/http4s-stir/src/main/scala-2/pl/iterators/kebs/http4sstir/matchers/KebsMatchers.scala @@ -0,0 +1,23 @@ +package pl.iterators.kebs.http4sstir.matchers + +import pl.iterators.stir.server.PathMatcher1 +import pl.iterators.kebs.core.instances.InstanceConverter +import pl.iterators.kebs.core.macros.{CaseClass1ToValueClass, ValueClassLike} +import pl.iterators.kebs.core.enums._ + +trait KebsMatchers extends pl.iterators.stir.server.PathMatchers with CaseClass1ToValueClass { + + implicit class SegmentIsomorphism[U](segment: PathMatcher1[U]) { + def as[T](implicit rep: ValueClassLike[T, U]): PathMatcher1[T] = segment.map(rep.apply) + } + + 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](implicit e: EnumLike[T]): PathMatcher1[T] = { + Segment.map(e.withNameIgnoreCase) + } + } +} diff --git a/http4s-stir/src/main/scala-2/pl/iterators/kebs/http4sstir/unmarshallers/enums/KebsEnumUnmarshallers.scala b/http4s-stir/src/main/scala-2/pl/iterators/kebs/http4sstir/unmarshallers/enums/KebsEnumUnmarshallers.scala new file mode 100644 index 00000000..4ec1dcac --- /dev/null +++ b/http4s-stir/src/main/scala-2/pl/iterators/kebs/http4sstir/unmarshallers/enums/KebsEnumUnmarshallers.scala @@ -0,0 +1,46 @@ +package pl.iterators.kebs.http4sstir.enums + +import pl.iterators.stir.unmarshalling.PredefinedFromStringUnmarshallers._ +import pl.iterators.stir.unmarshalling.{FromStringUnmarshaller, Unmarshaller} +import cats.effect.IO +import pl.iterators.kebs.core.enums.{EnumLike, ValueEnumLike, ValueEnumLikeEntry} + +trait EnumUnmarshallers { + final def enumUnmarshaller[E](`enum`: EnumLike[E]): FromStringUnmarshaller[E] = Unmarshaller { name => + `enum`.withNameInsensitiveOption(name) match { + case Some(enumEntry) => IO.pure(enumEntry) + case None => + IO.raiseError(new IllegalArgumentException(s"""Invalid value '$name'. Expected one of: ${`enum`.getNamesToValuesMap.keysIterator + .mkString(", ")}""")) + } + } + + implicit def kebsEnumUnmarshaller[E](implicit ev: EnumLike[E]): FromStringUnmarshaller[E] = + enumUnmarshaller(ev) +} + +trait ValueEnumUnmarshallers { + final def valueEnumUnmarshaller[V, E <: ValueEnumLikeEntry[V]](`enum`: ValueEnumLike[V, E]): Unmarshaller[V, E] = Unmarshaller { v => + `enum`.withValueOption(v) match { + case Some(enumEntry) => IO.pure(enumEntry) + case None => + IO.raiseError(new IllegalArgumentException(s"""Invalid value '$v'. Expected one of: ${`enum`.getValuesToEntriesMap.keysIterator + .mkString(", ")}""")) + } + } + + implicit def kebsValueEnumUnmarshaller[V, E <: ValueEnumLikeEntry[V]](implicit ev: ValueEnumLike[V, E]): Unmarshaller[V, E] = + valueEnumUnmarshaller(ev) + + implicit def kebsIntValueEnumFromStringUnmarshaller[E <: ValueEnumLikeEntry[Int]](implicit ev: ValueEnumLike[Int, E]): FromStringUnmarshaller[E] = + intFromStringUnmarshaller andThen valueEnumUnmarshaller(ev) + implicit def kebsLongValueEnumFromStringUnmarshaller[E <: ValueEnumLikeEntry[Long]](implicit ev: ValueEnumLike[Long, E]): FromStringUnmarshaller[E] = + longFromStringUnmarshaller andThen valueEnumUnmarshaller(ev) + implicit def kebsShortValueEnumFromStringUnmarshaller[E <: ValueEnumLikeEntry[Short]]( + implicit ev: ValueEnumLike[Short, E]): FromStringUnmarshaller[E] = + shortFromStringUnmarshaller andThen valueEnumUnmarshaller(ev) + implicit def kebsByteValueEnumFromStringUnmarshaller[E <: ValueEnumLikeEntry[Byte]](implicit ev: ValueEnumLike[Byte, E]): FromStringUnmarshaller[E] = + byteFromStringUnmarshaller andThen valueEnumUnmarshaller(ev) +} + +trait KebsEnumUnmarshallers extends EnumUnmarshallers with ValueEnumUnmarshallers {} diff --git a/akka-http/src/main/scala/pl/iterators/kebs/unmarshallers/enums/package.scala b/http4s-stir/src/main/scala-2/pl/iterators/kebs/http4sstir/unmarshallers/enums/package.scala similarity index 56% rename from akka-http/src/main/scala/pl/iterators/kebs/unmarshallers/enums/package.scala rename to http4s-stir/src/main/scala-2/pl/iterators/kebs/http4sstir/unmarshallers/enums/package.scala index a4fa82c6..515fbd46 100644 --- a/akka-http/src/main/scala/pl/iterators/kebs/unmarshallers/enums/package.scala +++ b/http4s-stir/src/main/scala-2/pl/iterators/kebs/http4sstir/unmarshallers/enums/package.scala @@ -1,3 +1,3 @@ -package pl.iterators.kebs.unmarshallers +package pl.iterators.kebs.http4sstir package object enums extends KebsEnumUnmarshallers diff --git a/http4s-stir/src/main/scala-2/unmarshallers/enums/KebsEnumUnmarshallers.scala b/http4s-stir/src/main/scala-2/unmarshallers/enums/KebsEnumUnmarshallers.scala deleted file mode 100644 index 7fcb0d91..00000000 --- a/http4s-stir/src/main/scala-2/unmarshallers/enums/KebsEnumUnmarshallers.scala +++ /dev/null @@ -1,48 +0,0 @@ -package pl.iterators.kebs.unmarshallers.enums - -import pl.iterators.stir.unmarshalling.PredefinedFromStringUnmarshallers._ -import pl.iterators.stir.unmarshalling.{FromStringUnmarshaller, Unmarshaller} -import cats.effect.IO -import enumeratum.values._ -import enumeratum.{Enum, EnumEntry} -import pl.iterators.kebs.macros.enums.{EnumOf, ValueEnumOf} - -trait EnumUnmarshallers { - final def enumUnmarshaller[E <: EnumEntry](`enum`: Enum[E]): FromStringUnmarshaller[E] = Unmarshaller { name => - `enum`.withNameInsensitiveOption(name) match { - case Some(enumEntry) => IO.pure(enumEntry) - case None => - IO.raiseError(new IllegalArgumentException(s"""Invalid value '$name'. Expected one of: ${`enum`.namesToValuesMap.keysIterator - .mkString(", ")}""")) - } - } - - implicit def kebsEnumUnmarshaller[E <: EnumEntry](implicit ev: EnumOf[E]): FromStringUnmarshaller[E] = - enumUnmarshaller(ev.`enum`) -} - -trait ValueEnumUnmarshallers { - final def valueEnumUnmarshaller[V, E <: ValueEnumEntry[V]](`enum`: ValueEnum[V, E]): Unmarshaller[V, E] = Unmarshaller { v => - `enum`.withValueOpt(v) match { - case Some(enumEntry) => IO.pure(enumEntry) - case None => - IO.raiseError(new IllegalArgumentException(s"""Invalid value '$v'. Expected one of: ${`enum`.valuesToEntriesMap.keysIterator - .mkString(", ")}""")) - } - } - - implicit def kebsValueEnumUnmarshaller[V, E <: ValueEnumEntry[V]](implicit ev: ValueEnumOf[V, E]): Unmarshaller[V, E] = - valueEnumUnmarshaller(ev.valueEnum) - - implicit def kebsIntValueEnumFromStringUnmarshaller[E <: IntEnumEntry](implicit ev: ValueEnumOf[Int, E]): FromStringUnmarshaller[E] = - intFromStringUnmarshaller andThen valueEnumUnmarshaller(ev.valueEnum) - implicit def kebsLongValueEnumFromStringUnmarshaller[E <: LongEnumEntry](implicit ev: ValueEnumOf[Long, E]): FromStringUnmarshaller[E] = - longFromStringUnmarshaller andThen valueEnumUnmarshaller(ev.valueEnum) - implicit def kebsShortValueEnumFromStringUnmarshaller[E <: ShortEnumEntry]( - implicit ev: ValueEnumOf[Short, E]): FromStringUnmarshaller[E] = - shortFromStringUnmarshaller andThen valueEnumUnmarshaller(ev.valueEnum) - implicit def kebsByteValueEnumFromStringUnmarshaller[E <: ByteEnumEntry](implicit ev: ValueEnumOf[Byte, E]): FromStringUnmarshaller[E] = - byteFromStringUnmarshaller andThen valueEnumUnmarshaller(ev.valueEnum) -} - -trait KebsEnumUnmarshallers extends EnumUnmarshallers with ValueEnumUnmarshallers {} diff --git a/http4s-stir/src/main/scala-3/matchers/KebsMatchers.scala b/http4s-stir/src/main/scala-3/matchers/KebsMatchers.scala deleted file mode 100644 index 57b8d1c2..00000000 --- a/http4s-stir/src/main/scala-3/matchers/KebsMatchers.scala +++ /dev/null @@ -1,26 +0,0 @@ -package pl.iterators.kebs.matchers - -import pl.iterators.stir.server.PathMatcher1 -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.macros.CaseClass1Rep -import pl.iterators.kebs.macros.enums.EnumOf -import scala.reflect.Enum - -import scala.language.implicitConversions - -trait KebsMatchers extends pl.iterators.stir.server.PathMatchers { - - implicit class SegmentIsomorphism[U](segment: PathMatcher1[U]) { - def as[T](implicit rep: CaseClass1Rep[T, U]): PathMatcher1[T] = segment.map(rep.apply) - } - - 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 <: Enum](using e: EnumOf[T]): PathMatcher1[T] = { - Segment.map(s => e.`enum`.values.find(_.toString().toLowerCase() == s.toLowerCase()).getOrElse(throw new IllegalArgumentException(s"""Invalid value '$s'. Expected one of: ${e.`enum`.values.mkString(", ")}"""))) - } - } -} diff --git a/http4s-stir/src/main/scala-3/pl/iterators/kebs/http4sstir/matchers/KebsMatchers.scala b/http4s-stir/src/main/scala-3/pl/iterators/kebs/http4sstir/matchers/KebsMatchers.scala new file mode 100644 index 00000000..643a5866 --- /dev/null +++ b/http4s-stir/src/main/scala-3/pl/iterators/kebs/http4sstir/matchers/KebsMatchers.scala @@ -0,0 +1,26 @@ +package pl.iterators.kebs.http4sstir.matchers + +import pl.iterators.stir.server.PathMatcher1 +import scala.reflect.Enum +import pl.iterators.kebs.core.enums.EnumLike +import pl.iterators.kebs.core.macros.ValueClassLike +import pl.iterators.kebs.core.instances.InstanceConverter + +import scala.language.implicitConversions + +trait KebsMatchers extends pl.iterators.stir.server.PathMatchers { + + implicit class SegmentIsomorphism[U](segment: PathMatcher1[U]) { + def as[T](implicit rep: ValueClassLike[T, U]): PathMatcher1[T] = segment.map(rep.apply) + } + + 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 <: Enum](using e: EnumLike[T]): PathMatcher1[T] = { + Segment.map(s => e.values.find(_.toString().toLowerCase() == s.toLowerCase()).getOrElse(throw new IllegalArgumentException(s"""Invalid value '$s'. Expected one of: ${e.values.mkString(", ")}"""))) + } + } +} diff --git a/http4s-stir/src/main/scala-3/pl/iterators/kebs/http4sstir/unmarshallers/enums/KebsEnumUnmarshallers.scala b/http4s-stir/src/main/scala-3/pl/iterators/kebs/http4sstir/unmarshallers/enums/KebsEnumUnmarshallers.scala new file mode 100644 index 00000000..46ad71f8 --- /dev/null +++ b/http4s-stir/src/main/scala-3/pl/iterators/kebs/http4sstir/unmarshallers/enums/KebsEnumUnmarshallers.scala @@ -0,0 +1,57 @@ +package pl.iterators.kebs.http4sstir.unmarshallers.enums + +import cats.effect.IO +import pl.iterators.stir.unmarshalling.PredefinedFromStringUnmarshallers.* +import pl.iterators.stir.unmarshalling.{FromStringUnmarshaller, Unmarshaller} + +import scala.reflect.{ClassTag, Enum} + +import pl.iterators.kebs.core.enums.{EnumLike, ValueEnumLike, ValueEnumLikeEntry} + +trait EnumUnmarshallers { + final def enumUnmarshaller[E <: Enum](using e: EnumLike[E]): FromStringUnmarshaller[E] = Unmarshaller { name => + e.values.find(_.toString().toLowerCase() == name.toLowerCase()) match { + case Some(enumEntry) => IO.pure(enumEntry) + case None => + IO.raiseError(new IllegalArgumentException(s"""Invalid value '$name'. Expected one of: ${e.values.mkString(", ")}""")) + } + } + + given kebsEnumUnmarshaller[E <: Enum](using e: EnumLike[E]): FromStringUnmarshaller[E] = + enumUnmarshaller +} + +trait ValueEnumUnmarshallers extends EnumUnmarshallers { + final def valueEnumUnmarshaller[V, E <: ValueEnumLikeEntry[V]](using `enum`: ValueEnumLike[V, E], cls: ClassTag[V]): Unmarshaller[V, E] = + Unmarshaller { + v => + `enum`.values.find(e => e.value == v && e.value.getClass == v.getClass) match { + case Some(enumEntry) => + IO.pure(enumEntry) + case _ => + `enum`.values.find(e => e.value == v) match { + case Some(enumEntry) => + IO.raiseError(new IllegalArgumentException(s"""Invalid value '$v'""")) + case None => + IO.raiseError(new IllegalArgumentException(s"""Invalid value '$v'. Expected one of: ${`enum`.values.map(_.value).mkString(", ")}""")) + } + } + } + + given kebsValueEnumUnmarshaller[V, E <: ValueEnumLikeEntry[V]](using `enum`: ValueEnumLike[V, E], cls: ClassTag[V]): Unmarshaller[V, E] = + valueEnumUnmarshaller + + given kebsIntValueEnumFromStringUnmarshaller[E <: ValueEnumLikeEntry[Int]](using ev: ValueEnumLike[Int, E]): FromStringUnmarshaller[E] = + intFromStringUnmarshaller andThen valueEnumUnmarshaller + + given kebsLongValueEnumFromStringUnmarshaller[E <: ValueEnumLikeEntry[Long]](using ev: ValueEnumLike[Long, E]): FromStringUnmarshaller[E] = + longFromStringUnmarshaller andThen valueEnumUnmarshaller + + given kebsShortValueEnumFromStringUnmarshaller[E <: ValueEnumLikeEntry[Short]](using ev: ValueEnumLike[Short, E]): FromStringUnmarshaller[E] = + shortFromStringUnmarshaller andThen valueEnumUnmarshaller + + given kebsByteValueEnumFromStringUnmarshaller[E <: ValueEnumLikeEntry[Byte]](using ev: ValueEnumLike[Byte, E]): FromStringUnmarshaller[E] = + byteFromStringUnmarshaller andThen valueEnumUnmarshaller +} + +trait KebsEnumUnmarshallers extends ValueEnumUnmarshallers {} diff --git a/http4s-stir/src/main/scala-3/unmarshallers/enums/package.scala b/http4s-stir/src/main/scala-3/pl/iterators/kebs/http4sstir/unmarshallers/enums/package.scala similarity index 50% rename from http4s-stir/src/main/scala-3/unmarshallers/enums/package.scala rename to http4s-stir/src/main/scala-3/pl/iterators/kebs/http4sstir/unmarshallers/enums/package.scala index a4fa82c6..7b6c5418 100644 --- a/http4s-stir/src/main/scala-3/unmarshallers/enums/package.scala +++ b/http4s-stir/src/main/scala-3/pl/iterators/kebs/http4sstir/unmarshallers/enums/package.scala @@ -1,3 +1,3 @@ -package pl.iterators.kebs.unmarshallers +package pl.iterators.kebs.http4sstir.unmarshallers package object enums extends KebsEnumUnmarshallers diff --git a/http4s-stir/src/main/scala-3/unmarshallers/enums/KebsEnumUnmarshallers.scala b/http4s-stir/src/main/scala-3/unmarshallers/enums/KebsEnumUnmarshallers.scala deleted file mode 100644 index 54270cd6..00000000 --- a/http4s-stir/src/main/scala-3/unmarshallers/enums/KebsEnumUnmarshallers.scala +++ /dev/null @@ -1,47 +0,0 @@ -package pl.iterators.kebs.unmarshallers.enums - -import pl.iterators.stir.unmarshalling.PredefinedFromStringUnmarshallers._ -import pl.iterators.stir.unmarshalling.{FromStringUnmarshaller, Unmarshaller} -import cats.effect.IO -import pl.iterators.kebs.macros.enums.{EnumOf, ValueEnumOf} -import pl.iterators.kebs.enums.ValueEnum -import scala.reflect.Enum -import scala.reflect.ClassTag - -trait EnumUnmarshallers { - final def enumUnmarshaller[E <: Enum](using e: EnumOf[E]): FromStringUnmarshaller[E] = Unmarshaller { name => - e.`enum`.values.find(_.toString().toLowerCase() == name.toLowerCase()) match { - case Some(enumEntry) => IO.pure(enumEntry) - case None => - IO.raiseError(new IllegalArgumentException(s"""Invalid value '$name'. Expected one of: ${e.`enum`.values.mkString(", ")}""")) - } - } - - given kebsEnumUnmarshaller[E <: Enum](using e: EnumOf[E]): FromStringUnmarshaller[E] = - enumUnmarshaller -} - -trait ValueEnumUnmarshallers extends EnumUnmarshallers { - final def valueEnumUnmarshaller[V, E <: ValueEnum[V] with Enum](using `enum`: ValueEnumOf[V, E], cls: ClassTag[V]): Unmarshaller[V, E] = Unmarshaller { v => - `enum`.`enum`.values.find(e => e.value == v) match { - case Some(enumEntry) => IO.pure(enumEntry) - case None => - IO.raiseError(new IllegalArgumentException(s"""Invalid value '$v'. Expected one of: ${`enum`.`enum`.values.map(_.value).mkString(", ")}""")) - } - } - - given kebsValueEnumUnmarshaller[V, E <: ValueEnum[V] with Enum](using `enum`: ValueEnumOf[V, E], cls: ClassTag[V]): Unmarshaller[V, E] = - valueEnumUnmarshaller - - given kebsIntValueEnumFromStringUnmarshaller[E <: ValueEnum[Int] with Enum](using ev: ValueEnumOf[Int, E]): FromStringUnmarshaller[E] = - intFromStringUnmarshaller andThen valueEnumUnmarshaller - given kebsLongValueEnumFromStringUnmarshaller[E <: ValueEnum[Long] with Enum](using ev: ValueEnumOf[Long, E]): FromStringUnmarshaller[E] = - longFromStringUnmarshaller andThen valueEnumUnmarshaller - given kebsShortValueEnumFromStringUnmarshaller[E <: ValueEnum[Short] with Enum]( - using ev: ValueEnumOf[Short, E]): FromStringUnmarshaller[E] = - shortFromStringUnmarshaller andThen valueEnumUnmarshaller - given kebsByteValueEnumFromStringUnmarshaller[E <: ValueEnum[Byte] with Enum](using ev: ValueEnumOf[Byte, E]): FromStringUnmarshaller[E] = - byteFromStringUnmarshaller andThen valueEnumUnmarshaller -} - -trait KebsEnumUnmarshallers extends ValueEnumUnmarshallers {} diff --git a/akka-http/src/main/scala/pl/iterators/kebs/matchers/package.scala b/http4s-stir/src/main/scala/pl/iterators/kebs/http4sstir/matchers/package.scala similarity index 55% rename from akka-http/src/main/scala/pl/iterators/kebs/matchers/package.scala rename to http4s-stir/src/main/scala/pl/iterators/kebs/http4sstir/matchers/package.scala index 610a2da7..f5c782bf 100644 --- a/akka-http/src/main/scala/pl/iterators/kebs/matchers/package.scala +++ b/http4s-stir/src/main/scala/pl/iterators/kebs/http4sstir/matchers/package.scala @@ -1,3 +1,3 @@ -package pl.iterators.kebs +package pl.iterators.kebs.http4sstir package object matchers extends KebsMatchers diff --git a/http4s-stir/src/main/scala/pl/iterators/kebs/unmarshallers/KebsUnmarshallers.scala b/http4s-stir/src/main/scala/pl/iterators/kebs/http4sstir/unmarshallers/KebsUnmarshallers.scala similarity index 61% rename from http4s-stir/src/main/scala/pl/iterators/kebs/unmarshallers/KebsUnmarshallers.scala rename to http4s-stir/src/main/scala/pl/iterators/kebs/http4sstir/unmarshallers/KebsUnmarshallers.scala index df5f2607..045613f1 100644 --- a/http4s-stir/src/main/scala/pl/iterators/kebs/unmarshallers/KebsUnmarshallers.scala +++ b/http4s-stir/src/main/scala/pl/iterators/kebs/http4sstir/unmarshallers/KebsUnmarshallers.scala @@ -1,12 +1,12 @@ -package pl.iterators.kebs.unmarshallers +package pl.iterators.kebs.http4sstir.unmarshallers +import pl.iterators.kebs.core.instances.InstanceConverter +import pl.iterators.kebs.core.macros.{CaseClass1ToValueClass, ValueClassLike} import pl.iterators.stir.unmarshalling.{FromStringUnmarshaller, Unmarshaller} -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.macros.CaseClass1Rep -trait KebsUnmarshallers extends LowPriorityKebsUnmarshallers { +trait KebsUnmarshallers extends LowPriorityKebsUnmarshallers with CaseClass1ToValueClass { @inline - implicit def kebsFromStringUnmarshaller[A, B](implicit rep: CaseClass1Rep[B, A], + implicit def kebsFromStringUnmarshaller[A, B](implicit rep: ValueClassLike[B, A], fsu: FromStringUnmarshaller[A]): FromStringUnmarshaller[B] = fsu andThen kebsUnmarshaller(rep) @@ -22,6 +22,6 @@ trait LowPriorityKebsUnmarshallers { implicit def kebsInstancesUnmarshaller[A, B](implicit ico: InstanceConverter[B, A]): Unmarshaller[A, B] = Unmarshaller.strict[A, B](ico.decode) - implicit def kebsUnmarshaller[A, B](implicit rep: CaseClass1Rep[B, A]): Unmarshaller[A, B] = + implicit def kebsUnmarshaller[A, B](implicit rep: ValueClassLike[B, A]): Unmarshaller[A, B] = Unmarshaller.strict[A, B](rep.apply) } \ No newline at end of file diff --git a/akka-http/src/main/scala/pl/iterators/kebs/unmarshallers/package.scala b/http4s-stir/src/main/scala/pl/iterators/kebs/http4sstir/unmarshallers/package.scala similarity index 60% rename from akka-http/src/main/scala/pl/iterators/kebs/unmarshallers/package.scala rename to http4s-stir/src/main/scala/pl/iterators/kebs/http4sstir/unmarshallers/package.scala index 6fd93de3..1e711bc3 100644 --- a/akka-http/src/main/scala/pl/iterators/kebs/unmarshallers/package.scala +++ b/http4s-stir/src/main/scala/pl/iterators/kebs/http4sstir/unmarshallers/package.scala @@ -1,3 +1,3 @@ -package pl.iterators.kebs +package pl.iterators.kebs.http4sstir package object unmarshallers extends KebsUnmarshallers diff --git a/http4s-stir/src/test/scala-2/pl/iterators/kebs/Http4sStirTagsDomain.scala b/http4s-stir/src/test/scala-2/pl/iterators/kebs/http4sstir/domain/Http4sStirTagsDomain.scala similarity index 92% rename from http4s-stir/src/test/scala-2/pl/iterators/kebs/Http4sStirTagsDomain.scala rename to http4s-stir/src/test/scala-2/pl/iterators/kebs/http4sstir/domain/Http4sStirTagsDomain.scala index f0661a1e..554df6f0 100644 --- a/http4s-stir/src/test/scala-2/pl/iterators/kebs/Http4sStirTagsDomain.scala +++ b/http4s-stir/src/test/scala-2/pl/iterators/kebs/http4sstir/domain/Http4sStirTagsDomain.scala @@ -1,9 +1,10 @@ -package pl.iterators.kebs +package pl.iterators.kebs.http4sstir.domain import enumeratum.values.{IntEnum, IntEnumEntry, StringEnum, StringEnumEntry} import enumeratum.{Enum, EnumEntry} import pl.iterators.kebs.tag.meta.tagged import pl.iterators.kebs.tagged._ +import pl.iterators.kebs.core.enums.ValueEnumLikeEntry import java.net.URI import java.util.UUID @@ -47,7 +48,7 @@ object Domain extends Tags { val values = findValues } - sealed abstract class LibraryItem(val value: Int) extends IntEnumEntry + sealed abstract class LibraryItem(val value: Int) extends IntEnumEntry with ValueEnumLikeEntry[Int] object LibraryItem extends IntEnum[LibraryItem] { case object Book extends LibraryItem(1) @@ -63,7 +64,7 @@ object Domain extends Tags { case class Blue(value: Int) case class Color(red: Red, green: Green, blue: Blue) - sealed abstract class ShirtSize(val value: String) extends StringEnumEntry + sealed abstract class ShirtSize(val value: String) extends StringEnumEntry with ValueEnumLikeEntry[String] object ShirtSize extends StringEnum[ShirtSize] { case object Small extends ShirtSize("S") case object Medium extends ShirtSize("M") diff --git a/http4s-stir/src/test/scala-2/pl/iterators/kebs/matchers/Http4sStirMatchersTests.scala b/http4s-stir/src/test/scala-2/pl/iterators/kebs/http4sstir/matchers/Http4sStirMatchersTests.scala similarity index 81% rename from http4s-stir/src/test/scala-2/pl/iterators/kebs/matchers/Http4sStirMatchersTests.scala rename to http4s-stir/src/test/scala-2/pl/iterators/kebs/http4sstir/matchers/Http4sStirMatchersTests.scala index 70b85554..5ac52706 100644 --- a/http4s-stir/src/test/scala-2/pl/iterators/kebs/matchers/Http4sStirMatchersTests.scala +++ b/http4s-stir/src/test/scala-2/pl/iterators/kebs/http4sstir/matchers/Http4sStirMatchersTests.scala @@ -1,15 +1,15 @@ -package pl.iterators.kebs.matchers +package pl.iterators.kebs.http4sstir.matchers import pl.iterators.stir.server.Directives import pl.iterators.stir.testkit.ScalatestRouteTest import org.scalatest.concurrent.ScalaFutures import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.Domain._ import pl.iterators.kebs.instances.net.URIString -import pl.iterators.kebs.instances.time.mixins.InstantEpochMilliLong import pl.iterators.kebs.instances.time.{DayOfWeekInt, ZonedDateTimeString} - +import pl.iterators.kebs.instances.time.mixins.InstantEpochMilliLong +import pl.iterators.kebs.http4sstir.domain.Domain._ +import pl.iterators.kebs.enumeratum._ import java.net.URI import java.time.{DayOfWeek, Instant, ZonedDateTime} @@ -22,18 +22,19 @@ class Http4sStirMatchersTests with ZonedDateTimeString with DayOfWeekInt with InstantEpochMilliLong - with URIString { + with URIString + with KebsEnumeratum { implicit def runtime: cats.effect.unsafe.IORuntime = cats.effect.unsafe.IORuntime.global - test("No CaseClass1Rep implicits derived") { - import pl.iterators.kebs.macros.CaseClass1Rep + test("No ValueClassLike implicits derived") { + import pl.iterators.kebs.core.macros.ValueClassLike - "implicitly[CaseClass1Rep[DayOfWeek, Int]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Int, DayOfWeek]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Instant, Long]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Long, Instant]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[URI, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, URI]]" shouldNot typeCheck + "implicitly[ValueClassLike[DayOfWeek, Int]]" shouldNot typeCheck + "implicitly[ValueClassLike[Int, DayOfWeek]]" shouldNot typeCheck + "implicitly[ValueClassLike[Instant, Long]]" shouldNot typeCheck + "implicitly[ValueClassLike[Long, Instant]]" shouldNot typeCheck + "implicitly[ValueClassLike[URI, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, URI]]" shouldNot typeCheck } test("Extract String to ZonedDateTime") { diff --git a/http4s-stir/src/test/scala-2/pl/iterators/kebs/unmarshallers/Http4sStirUnmarshallersTests.scala b/http4s-stir/src/test/scala-2/pl/iterators/kebs/http4sstir/unmarshallers/Http4sStirUnmarshallersTests.scala similarity index 86% rename from http4s-stir/src/test/scala-2/pl/iterators/kebs/unmarshallers/Http4sStirUnmarshallersTests.scala rename to http4s-stir/src/test/scala-2/pl/iterators/kebs/http4sstir/unmarshallers/Http4sStirUnmarshallersTests.scala index 3deb6c04..90d8e07f 100644 --- a/http4s-stir/src/test/scala-2/pl/iterators/kebs/unmarshallers/Http4sStirUnmarshallersTests.scala +++ b/http4s-stir/src/test/scala-2/pl/iterators/kebs/http4sstir/unmarshallers/Http4sStirUnmarshallersTests.scala @@ -1,4 +1,4 @@ -package pl.iterators.kebs.unmarshallers +package pl.iterators.kebs.http4sstir.unmarshallers import org.http4s.UrlForm import pl.iterators.stir.server.{Directives, MalformedQueryParamRejection} @@ -6,10 +6,12 @@ import pl.iterators.stir.testkit.ScalatestRouteTest import org.scalatest.concurrent.ScalaFutures import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.Domain._ 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.http4sstir.domain.Domain._ +import pl.iterators.kebs.enumeratum.{KebsEnumeratum, KebsValueEnumeratum} +import pl.iterators.kebs.http4sstir.enums.KebsEnumUnmarshallers +import pl.iterators.kebs.http4sstir.unmarshallers.KebsUnmarshallers import java.time.{DayOfWeek, YearMonth} @@ -23,18 +25,20 @@ class Http4sStirUnmarshallersTests with KebsEnumUnmarshallers with URIString with YearMonthString - with DayOfWeekInt { + with DayOfWeekInt + with KebsEnumeratum + with KebsValueEnumeratum { implicit def runtime: cats.effect.unsafe.IORuntime = cats.effect.unsafe.IORuntime.global - test("No CaseClass1Rep implicits derived") { - import pl.iterators.kebs.macros.CaseClass1Rep + test("No ValueClassLike implicits derived") { + import pl.iterators.kebs.core.macros.ValueClassLike - "implicitly[CaseClass1Rep[URI, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, URI]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[YearMonth, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, YearMonth]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[DayOfWeek, Int]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Int, DayOfWeek]]" shouldNot typeCheck + "implicitly[ValueClassLike[URI, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, URI]]" shouldNot typeCheck + "implicitly[ValueClassLike[YearMonth, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, YearMonth]]" shouldNot typeCheck + "implicitly[ValueClassLike[DayOfWeek, Int]]" shouldNot typeCheck + "implicitly[ValueClassLike[Int, DayOfWeek]]" shouldNot typeCheck } test("Unmarshalling parameter") { diff --git a/http4s-stir/src/test/scala-3/pl/iterators/kebs/Http4sStirTagsDomain.scala b/http4s-stir/src/test/scala-3/pl/iterators/kebs/http4sstir/domain/Http4sStirTagsDomain.scala similarity index 80% rename from http4s-stir/src/test/scala-3/pl/iterators/kebs/Http4sStirTagsDomain.scala rename to http4s-stir/src/test/scala-3/pl/iterators/kebs/http4sstir/domain/Http4sStirTagsDomain.scala index 62cccb5b..32cf2900 100644 --- a/http4s-stir/src/test/scala-3/pl/iterators/kebs/Http4sStirTagsDomain.scala +++ b/http4s-stir/src/test/scala-3/pl/iterators/kebs/http4sstir/domain/Http4sStirTagsDomain.scala @@ -1,10 +1,11 @@ -package pl.iterators.kebs +package pl.iterators.kebs.http4sstir.domain import pl.iterators.kebs.opaque.Opaque import java.net.URI import java.util.UUID -import pl.iterators.kebs.enums.ValueEnum + +import pl.iterators.kebs.core.enums.ValueEnumLikeEntry object Domain { opaque type TestTaggedUri = URI @@ -24,7 +25,7 @@ object Domain { } - enum LibraryItem(val value: Int) extends ValueEnum[Int] { + enum LibraryItem(val value: Int) extends ValueEnumLikeEntry[Int] { case Book extends LibraryItem(1) case Movie extends LibraryItem(2) case Magazine extends LibraryItem(3) @@ -37,7 +38,7 @@ object Domain { case class Color(red: Red, green: Green, blue: Blue) - enum ShirtSize(val value: String) extends ValueEnum[String] { + enum ShirtSize(val value: String) extends ValueEnumLikeEntry[String] { case Small extends ShirtSize("S") case Medium extends ShirtSize("M") case Large extends ShirtSize("L") diff --git a/http4s-stir/src/test/scala-3/pl/iterators/kebs/matchers/Http4sStirMatchersTests.scala b/http4s-stir/src/test/scala-3/pl/iterators/kebs/http4sstir/matchers/Http4sStirMatchersTests.scala similarity index 76% rename from http4s-stir/src/test/scala-3/pl/iterators/kebs/matchers/Http4sStirMatchersTests.scala rename to http4s-stir/src/test/scala-3/pl/iterators/kebs/http4sstir/matchers/Http4sStirMatchersTests.scala index ad6e3b41..02d07755 100644 --- a/http4s-stir/src/test/scala-3/pl/iterators/kebs/matchers/Http4sStirMatchersTests.scala +++ b/http4s-stir/src/test/scala-3/pl/iterators/kebs/http4sstir/matchers/Http4sStirMatchersTests.scala @@ -1,14 +1,17 @@ -package pl.iterators.kebs.matchers +package pl.iterators.kebs.http4sstir.matchers import pl.iterators.stir.server.Directives import pl.iterators.stir.testkit.ScalatestRouteTest import org.scalatest.concurrent.ScalaFutures import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.Domain._ import pl.iterators.kebs.instances.net.URIString -import pl.iterators.kebs.instances.time.mixins.InstantEpochMilliLong import pl.iterators.kebs.instances.time.{DayOfWeekInt, ZonedDateTimeString} +import pl.iterators.kebs.instances.time.mixins.InstantEpochMilliLong +import pl.iterators.kebs.http4sstir.domain.Domain._ + +import pl.iterators.kebs.enums.{KebsEnum, KebsValueEnum} +import pl.iterators.kebs.instances import java.net.URI import java.time.{DayOfWeek, Instant, ZonedDateTime} @@ -22,18 +25,18 @@ class Http4sStirMatchersTests with ZonedDateTimeString with DayOfWeekInt with InstantEpochMilliLong - with URIString { + with URIString + with KebsEnum + with KebsValueEnum{ implicit def runtime: cats.effect.unsafe.IORuntime = cats.effect.unsafe.IORuntime.global - test("No CaseClass1Rep implicits derived") { - import pl.iterators.kebs.macros.CaseClass1Rep - - "implicitly[CaseClass1Rep[DayOfWeek, Int]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Int, DayOfWeek]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Instant, Long]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Long, Instant]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[URI, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, URI]]" shouldNot typeCheck + test("No ValueClassLike implicits derived") { + "implicitly[ValueClassLike[DayOfWeek, Int]]" shouldNot typeCheck + "implicitly[ValueClassLike[Int, DayOfWeek]]" shouldNot typeCheck + "implicitly[ValueClassLike[Instant, Long]]" shouldNot typeCheck + "implicitly[ValueClassLike[Long, Instant]]" shouldNot typeCheck + "implicitly[ValueClassLike[URI, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, URI]]" shouldNot typeCheck } test("Extract String to ZonedDateTime") { diff --git a/http4s-stir/src/test/scala-3/pl/iterators/kebs/unmarshallers/Http4sStirUnmarshallersTests.scala b/http4s-stir/src/test/scala-3/pl/iterators/kebs/http4sstir/unmarshallers/Http4sStirUnmarshallersTests.scala similarity index 86% rename from http4s-stir/src/test/scala-3/pl/iterators/kebs/unmarshallers/Http4sStirUnmarshallersTests.scala rename to http4s-stir/src/test/scala-3/pl/iterators/kebs/http4sstir/unmarshallers/Http4sStirUnmarshallersTests.scala index a0dabbed..fb46c0b8 100644 --- a/http4s-stir/src/test/scala-3/pl/iterators/kebs/unmarshallers/Http4sStirUnmarshallersTests.scala +++ b/http4s-stir/src/test/scala-3/pl/iterators/kebs/http4sstir/unmarshallers/Http4sStirUnmarshallersTests.scala @@ -1,4 +1,4 @@ -package pl.iterators.kebs.unmarshallers +package pl.iterators.kebs.http4sstir.unmarshallers import org.http4s.UrlForm import pl.iterators.stir.server.{Directives, MalformedQueryParamRejection} @@ -6,10 +6,12 @@ import pl.iterators.stir.testkit.ScalatestRouteTest import org.scalatest.concurrent.ScalaFutures import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.Domain._ +import pl.iterators.kebs.http4sstir.domain.Domain._ 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.core.macros.CaseClass1ToValueClass +import pl.iterators.kebs.enums.{KebsEnum, KebsValueEnum} +import pl.iterators.kebs.http4sstir.unmarshallers.enums.KebsEnumUnmarshallers import java.time.{DayOfWeek, YearMonth} @@ -23,18 +25,20 @@ class Http4sStirUnmarshallersTests with KebsEnumUnmarshallers with URIString with YearMonthString - with DayOfWeekInt { + with DayOfWeekInt + with KebsEnum + with KebsValueEnum + with CaseClass1ToValueClass + { implicit def runtime: cats.effect.unsafe.IORuntime = cats.effect.unsafe.IORuntime.global - test("No CaseClass1Rep implicits derived") { - import pl.iterators.kebs.macros.CaseClass1Rep - - "implicitly[CaseClass1Rep[URI, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, URI]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[YearMonth, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, YearMonth]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[DayOfWeek, Int]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Int, DayOfWeek]]" shouldNot typeCheck + test("No ValueClassLike implicits derived") { + "implicitly[ValueClassLike[URI, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, URI]]" shouldNot typeCheck + "implicitly[ValueClassLike[YearMonth, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, YearMonth]]" shouldNot typeCheck + "implicitly[ValueClassLike[DayOfWeek, Int]]" shouldNot typeCheck + "implicitly[ValueClassLike[Int, DayOfWeek]]" shouldNot typeCheck } test("Unmarshalling parameter") { diff --git a/http4s/src/main/scala-2/pl/iterators/kebs/Http4s.scala b/http4s/src/main/scala-2/pl/iterators/kebs/Http4s.scala deleted file mode 100644 index eac47ae9..00000000 --- a/http4s/src/main/scala-2/pl/iterators/kebs/Http4s.scala +++ /dev/null @@ -1,61 +0,0 @@ -package pl.iterators.kebs - -import enumeratum.EnumEntry - -import scala.util.Try -import pl.iterators.kebs.macros.CaseClass1Rep -import pl.iterators.kebs.macros.enums.EnumOf -import org.http4s._ -import pl.iterators.kebs.instances.InstanceConverter - -import java.util.UUID - -trait Http4s { - protected class PathVar[A](cast: String => Try[A]) { - def unapply(str: String): Option[A] = - if (str.nonEmpty) - cast(str).toOption - else - None - } - - object WrappedString { - def apply[T](implicit rep: CaseClass1Rep[T, String]) = new PathVar[T](str => Try(rep.apply(str))) - } - - object InstanceString { - def apply[T](implicit rep: InstanceConverter[T, String]) = new PathVar[T](str => Try(rep.decode(str))) - } - - object EnumString { - def apply[T <: EnumEntry](implicit e: EnumOf[T]) = new PathVar[T](str => Try(e.`enum`.values.find(_.toString.toUpperCase == str.toUpperCase).getOrElse(throw new IllegalArgumentException(s"enum case not found: $str")))) - } - - object WrappedInt { - def apply[T](implicit rep: CaseClass1Rep[T, Int]) = new PathVar[T](str => Try(rep.apply(str.toInt))) - } - - object InstanceInt { - def apply[T](implicit rep: InstanceConverter[T, Int]) = new PathVar[T](str => Try(rep.decode(str.toInt))) - } - - object WrappedLong { - def apply[T](implicit rep: CaseClass1Rep[T, Long]) = new PathVar[T](str => Try(rep.apply(str.toLong))) - } - - object InstanceLong { - def apply[T](implicit rep: InstanceConverter[T, Long]) = new PathVar[T](str => Try(rep.decode(str.toLong))) - } - - object WrappedUUID { - def apply[T](implicit rep: CaseClass1Rep[T, UUID]) = new PathVar[T](str => Try(rep.apply(UUID.fromString(str)))) - } - - object InstanceUUID { - def apply[T](implicit rep: InstanceConverter[T, UUID]) = new PathVar[T](str => Try(rep.decode(UUID.fromString(str)))) - } - - implicit def cc1RepQueryParamDecoder[T, U](implicit rep: CaseClass1Rep[T, U], qpd: QueryParamDecoder[U]): QueryParamDecoder[T] = qpd.emap(u => Try(rep.apply(u)).toEither.left.map(t => ParseFailure(t.getMessage, t.getMessage))) - implicit def instanceConverterQueryParamDecoder[T, U](implicit rep: InstanceConverter[T, U], qpd: QueryParamDecoder[U]): QueryParamDecoder[T] = qpd.emap(u => Try(rep.decode(u)).toEither.left.map(t => ParseFailure(t.getMessage, t.getMessage))) - implicit def enumQueryParamDecoder[E <: EnumEntry](implicit e: EnumOf[E]): QueryParamDecoder[E] = QueryParamDecoder[String].emap(str => Try(e.`enum`.values.find(_.toString.toUpperCase == str.toUpperCase).getOrElse(throw new IllegalArgumentException(s"enum case not found: $str"))).toEither.left.map(t => ParseFailure(t.getMessage, t.getMessage))) -} diff --git a/http4s/src/main/scala-2/pl/iterators/kebs/http4s/Http4s.scala b/http4s/src/main/scala-2/pl/iterators/kebs/http4s/Http4s.scala new file mode 100644 index 00000000..ad63a772 --- /dev/null +++ b/http4s/src/main/scala-2/pl/iterators/kebs/http4s/Http4s.scala @@ -0,0 +1,59 @@ +package pl.iterators.kebs.http4s + +import org.http4s._ +import pl.iterators.kebs.core.enums.EnumLike +import pl.iterators.kebs.core.instances.InstanceConverter +import pl.iterators.kebs.core.macros.{CaseClass1ToValueClass, ValueClassLike} + +import java.util.UUID +import scala.util.Try + +trait Http4s extends CaseClass1ToValueClass { + protected class PathVar[A](cast: String => Try[A]) { + def unapply(str: String): Option[A] = + if (str.nonEmpty) + cast(str).toOption + else + None + } + + object WrappedString { + def apply[T](implicit rep: ValueClassLike[T, String]) = new PathVar[T](str => Try(rep.apply(str))) + } + + object InstanceString { + def apply[T](implicit rep: InstanceConverter[T, String]) = new PathVar[T](str => Try(rep.decode(str))) + } + + object EnumString { + def apply[T](implicit e: EnumLike[T]) = new PathVar[T](str => Try(e.values.find(_.toString.toUpperCase == str.toUpperCase).getOrElse(throw new IllegalArgumentException(s"enum case not found: $str")))) + } + + object WrappedInt { + def apply[T](implicit rep: ValueClassLike[T, Int]) = new PathVar[T](str => Try(rep.apply(str.toInt))) + } + + object InstanceInt { + def apply[T](implicit rep: InstanceConverter[T, Int]) = new PathVar[T](str => Try(rep.decode(str.toInt))) + } + + object WrappedLong { + def apply[T](implicit rep: ValueClassLike[T, Long]) = new PathVar[T](str => Try(rep.apply(str.toLong))) + } + + object InstanceLong { + def apply[T](implicit rep: InstanceConverter[T, Long]) = new PathVar[T](str => Try(rep.decode(str.toLong))) + } + + object WrappedUUID { + def apply[T](implicit rep: ValueClassLike[T, UUID]) = new PathVar[T](str => Try(rep.apply(UUID.fromString(str)))) + } + + object InstanceUUID { + def apply[T](implicit rep: InstanceConverter[T, UUID]) = new PathVar[T](str => Try(rep.decode(UUID.fromString(str)))) + } + + implicit def vcLikeQueryParamDecoder[T, U](implicit rep: ValueClassLike[T, U], qpd: QueryParamDecoder[U]): QueryParamDecoder[T] = qpd.emap(u => Try(rep.apply(u)).toEither.left.map(t => ParseFailure(t.getMessage, t.getMessage))) + implicit def instanceConverterQueryParamDecoder[T, U](implicit rep: InstanceConverter[T, U], qpd: QueryParamDecoder[U]): QueryParamDecoder[T] = qpd.emap(u => Try(rep.decode(u)).toEither.left.map(t => ParseFailure(t.getMessage, t.getMessage))) + implicit def enumQueryParamDecoder[E](implicit e: EnumLike[E]): QueryParamDecoder[E] = QueryParamDecoder[String].emap(str => Try(e.values.find(_.toString.toUpperCase == str.toUpperCase).getOrElse(throw new IllegalArgumentException(s"enum case not found: $str"))).toEither.left.map(t => ParseFailure(t.getMessage, t.getMessage))) +} diff --git a/http4s/src/main/scala-3/pl/iterators/kebs/http4s/package.scala b/http4s/src/main/scala-3/pl/iterators/kebs/http4s/package.scala index 886045b9..528671c0 100644 --- a/http4s/src/main/scala-3/pl/iterators/kebs/http4s/package.scala +++ b/http4s/src/main/scala-3/pl/iterators/kebs/http4s/package.scala @@ -2,10 +2,10 @@ package pl.iterators.kebs.http4s import scala.util.Try import scala.reflect.Enum -import pl.iterators.kebs.macros.CaseClass1Rep -import pl.iterators.kebs.macros.enums.EnumOf +import pl.iterators.kebs.core.enums.EnumLike +import pl.iterators.kebs.core.macros.ValueClassLike +import pl.iterators.kebs.core.instances.InstanceConverter import org.http4s._ -import pl.iterators.kebs.instances.InstanceConverter import java.util.UUID protected class PathVar[A](cast: String => Try[A]) { @@ -17,7 +17,7 @@ protected class PathVar[A](cast: String => Try[A]) { } object WrappedString { - def apply[T](using rep: CaseClass1Rep[T, String]) = new PathVar[T](str => Try(rep.apply(str))) + def apply[T](using rep: ValueClassLike[T, String]) = new PathVar[T](str => Try(rep.apply(str))) } object InstanceString { @@ -25,11 +25,11 @@ object InstanceString { } object EnumString { - def apply[T <: Enum](using e: EnumOf[T]) = new PathVar[T](str => Try(e.`enum`.values.find(_.toString.toUpperCase == str.toUpperCase).getOrElse(throw new IllegalArgumentException(s"enum case not found: $str")))) + def apply[T <: Enum](using e: EnumLike[T]) = new PathVar[T](str => Try(e.values.find(_.toString.toUpperCase == str.toUpperCase).getOrElse(throw new IllegalArgumentException(s"enum case not found: $str")))) } object WrappedInt { - def apply[T](using rep: CaseClass1Rep[T, Int]) = new PathVar[T](str => Try(rep.apply(str.toInt))) + def apply[T](using rep: ValueClassLike[T, Int]) = new PathVar[T](str => Try(rep.apply(str.toInt))) } object InstanceInt { @@ -37,7 +37,7 @@ object InstanceInt { } object WrappedLong { - def apply[T](using rep: CaseClass1Rep[T, Long]) = new PathVar[T](str => Try(rep.apply(str.toLong))) + def apply[T](using rep: ValueClassLike[T, Long]) = new PathVar[T](str => Try(rep.apply(str.toLong))) } object InstanceLong { @@ -45,13 +45,13 @@ object InstanceLong { } object WrappedUUID { - def apply[T](using rep: CaseClass1Rep[T, UUID]) = new PathVar[T](str => Try(rep.apply(UUID.fromString(str)))) + def apply[T](using rep: ValueClassLike[T, UUID]) = new PathVar[T](str => Try(rep.apply(UUID.fromString(str)))) } object InstanceUUID { def apply[T](using rep: InstanceConverter[T, UUID]) = new PathVar[T](str => Try(rep.decode(UUID.fromString(str)))) } -given[T, U](using rep: CaseClass1Rep[T, U], qpd: QueryParamDecoder[U]): QueryParamDecoder[T] = qpd.emap(u => Try(rep.apply(u)).toEither.left.map(t => ParseFailure(t.getMessage, t.getMessage))) +given[T, U](using rep: ValueClassLike[T, U], qpd: QueryParamDecoder[U]): QueryParamDecoder[T] = qpd.emap(u => Try(rep.apply(u)).toEither.left.map(t => ParseFailure(t.getMessage, t.getMessage))) given[T, U](using rep: InstanceConverter[T, U], qpd: QueryParamDecoder[U]): QueryParamDecoder[T] = qpd.emap(u => Try(rep.decode(u)).toEither.left.map(t => ParseFailure(t.getMessage, t.getMessage))) -given[E <: Enum](using e: EnumOf[E]): QueryParamDecoder[E] = QueryParamDecoder[String].emap(str => Try(e.`enum`.values.find(_.toString.toUpperCase == str.toUpperCase).getOrElse(throw new IllegalArgumentException(s"enum case not found: $str"))).toEither.left.map(t => ParseFailure(t.getMessage, t.getMessage))) +given[E <: Enum](using e: EnumLike[E]): QueryParamDecoder[E] = QueryParamDecoder[String].emap(str => Try(e.values.find(_.toString.toUpperCase == str.toUpperCase).getOrElse(throw new IllegalArgumentException(s"enum case not found: $str"))).toEither.left.map(t => ParseFailure(t.getMessage, t.getMessage))) diff --git a/http4s/src/test/scala-2/pl/iterators/kebs/Http4sDslTests.scala b/http4s/src/test/scala-2/pl/iterators/kebs/http4s/Http4sDslTests.scala similarity index 93% rename from http4s/src/test/scala-2/pl/iterators/kebs/Http4sDslTests.scala rename to http4s/src/test/scala-2/pl/iterators/kebs/http4s/Http4sDslTests.scala index 18b7f51c..39066821 100644 --- a/http4s/src/test/scala-2/pl/iterators/kebs/Http4sDslTests.scala +++ b/http4s/src/test/scala-2/pl/iterators/kebs/http4s/Http4sDslTests.scala @@ -1,4 +1,4 @@ -package pl.iterators.kebs +package pl.iterators.kebs.http4s import cats.effect.IO import cats.effect.unsafe.IORuntime @@ -7,14 +7,14 @@ import org.http4s.dsl.io._ import org.http4s.implicits._ import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers +import pl.iterators.kebs.instances.KebsInstances._ +import pl.iterators.kebs.enumeratum.KebsEnumeratum import java.time.Year import java.util.Currency -import pl.iterators.kebs.instances.KebsInstances._ -import pl.iterators.kebs.http4s._ -class Http4sDslTests extends AnyFunSuite with Matchers { - import Domain._ +class Http4sDslTests extends AnyFunSuite with Matchers with KebsEnumeratum { + import pl.iterators.kebs.http4s.domain.Domain._ implicit val runtime: IORuntime = cats.effect.unsafe.IORuntime.global diff --git a/http4s/src/test/scala-2/pl/iterators/kebs/Domain.scala b/http4s/src/test/scala-2/pl/iterators/kebs/http4s/domain/Domain.scala similarity index 94% rename from http4s/src/test/scala-2/pl/iterators/kebs/Domain.scala rename to http4s/src/test/scala-2/pl/iterators/kebs/http4s/domain/Domain.scala index 61952f89..eb3836b0 100644 --- a/http4s/src/test/scala-2/pl/iterators/kebs/Domain.scala +++ b/http4s/src/test/scala-2/pl/iterators/kebs/http4s/domain/Domain.scala @@ -1,4 +1,4 @@ -package pl.iterators.kebs +package pl.iterators.kebs.http4s.domain import enumeratum.{Enum, EnumEntry} import pl.iterators.kebs.tag.meta.tagged diff --git a/http4s/src/test/scala-3/pl/iterators/kebs/Domain.scala b/http4s/src/test/scala-3/pl/iterators/kebs/http4s/Domain.scala similarity index 91% rename from http4s/src/test/scala-3/pl/iterators/kebs/Domain.scala rename to http4s/src/test/scala-3/pl/iterators/kebs/http4s/Domain.scala index a0941dc6..1aadbfd9 100644 --- a/http4s/src/test/scala-3/pl/iterators/kebs/Domain.scala +++ b/http4s/src/test/scala-3/pl/iterators/kebs/http4s/Domain.scala @@ -1,4 +1,4 @@ -package pl.iterators.kebs +package pl.iterators.kebs.http4s import pl.iterators.kebs.opaque.Opaque diff --git a/http4s/src/test/scala-3/pl/iterators/kebs/Http4sDslTests.scala b/http4s/src/test/scala-3/pl/iterators/kebs/http4s/Http4sDslTests.scala similarity index 93% rename from http4s/src/test/scala-3/pl/iterators/kebs/Http4sDslTests.scala rename to http4s/src/test/scala-3/pl/iterators/kebs/http4s/Http4sDslTests.scala index 82bfa975..05610b53 100644 --- a/http4s/src/test/scala-3/pl/iterators/kebs/Http4sDslTests.scala +++ b/http4s/src/test/scala-3/pl/iterators/kebs/http4s/Http4sDslTests.scala @@ -1,4 +1,4 @@ -package pl.iterators.kebs +package pl.iterators.kebs.http4s import cats.effect.IO import cats.effect.unsafe.IORuntime @@ -11,9 +11,10 @@ import java.time.Year import java.util.Currency import pl.iterators.kebs.instances.KebsInstances._ -import pl.iterators.kebs.http4s.{given, _} +import pl.iterators.kebs.core.macros.CaseClass1ToValueClass +import pl.iterators.kebs.enums.KebsEnum -class Http4sDslTests extends AnyFunSuite with Matchers { +class Http4sDslTests extends AnyFunSuite with Matchers with KebsEnum with CaseClass1ToValueClass { import Domain._ given runtime: IORuntime = cats.effect.unsafe.IORuntime.global diff --git a/instances/src/main/scala/pl/iterators/kebs/instances/TimeInstances.scala b/instances/src/main/scala/pl/iterators/kebs/instances/TimeInstances.scala index f903b8c2..9de7587c 100644 --- a/instances/src/main/scala/pl/iterators/kebs/instances/TimeInstances.scala +++ b/instances/src/main/scala/pl/iterators/kebs/instances/TimeInstances.scala @@ -1,6 +1,23 @@ package pl.iterators.kebs.instances -import pl.iterators.kebs.instances.time.{DurationString, PeriodString, _} +import pl.iterators.kebs.instances.time.{ + DayOfWeekInt, + DurationString, + InstantString, + LocalDateString, + LocalDateTimeString, + LocalTimeString, + MonthDayString, + MonthInt, + OffsetDateTimeString, + OffsetTimeString, + PeriodString, + YearInt, + YearMonthString, + ZoneIdString, + ZoneOffsetString, + ZonedDateTimeString +} trait TimeInstances extends InstantString diff --git a/instances/src/main/scala/pl/iterators/kebs/instances/net/URIString.scala b/instances/src/main/scala/pl/iterators/kebs/instances/net/URIString.scala index 57b2d46a..84a8e067 100644 --- a/instances/src/main/scala/pl/iterators/kebs/instances/net/URIString.scala +++ b/instances/src/main/scala/pl/iterators/kebs/instances/net/URIString.scala @@ -1,7 +1,7 @@ package pl.iterators.kebs.instances.net -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.instances.net.URIString.URIFormat +import URIString.URIFormat +import pl.iterators.kebs.core.instances.InstanceConverter import java.net.URI diff --git a/instances/src/main/scala/pl/iterators/kebs/instances/time/DayOfWeekInt.scala b/instances/src/main/scala/pl/iterators/kebs/instances/time/DayOfWeekInt.scala index 3252eaed..927fced4 100644 --- a/instances/src/main/scala/pl/iterators/kebs/instances/time/DayOfWeekInt.scala +++ b/instances/src/main/scala/pl/iterators/kebs/instances/time/DayOfWeekInt.scala @@ -1,7 +1,7 @@ package pl.iterators.kebs.instances.time -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.instances.time.DayOfWeekInt._ +import DayOfWeekInt._ +import pl.iterators.kebs.core.instances.InstanceConverter import java.time.DayOfWeek diff --git a/instances/src/main/scala/pl/iterators/kebs/instances/time/DurationString.scala b/instances/src/main/scala/pl/iterators/kebs/instances/time/DurationString.scala index 663cf19d..83a04913 100644 --- a/instances/src/main/scala/pl/iterators/kebs/instances/time/DurationString.scala +++ b/instances/src/main/scala/pl/iterators/kebs/instances/time/DurationString.scala @@ -1,7 +1,7 @@ package pl.iterators.kebs.instances.time -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.instances.time.DurationString.DurationFormat +import DurationString.DurationFormat +import pl.iterators.kebs.core.instances.InstanceConverter import java.time.Duration diff --git a/instances/src/main/scala/pl/iterators/kebs/instances/time/InstantString.scala b/instances/src/main/scala/pl/iterators/kebs/instances/time/InstantString.scala index 24a2fda6..01eeb455 100644 --- a/instances/src/main/scala/pl/iterators/kebs/instances/time/InstantString.scala +++ b/instances/src/main/scala/pl/iterators/kebs/instances/time/InstantString.scala @@ -1,7 +1,7 @@ package pl.iterators.kebs.instances.time -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.instances.time.InstantString.InstantFormat +import InstantString.InstantFormat +import pl.iterators.kebs.core.instances.InstanceConverter import java.time.Instant diff --git a/instances/src/main/scala/pl/iterators/kebs/instances/time/LocalDateString.scala b/instances/src/main/scala/pl/iterators/kebs/instances/time/LocalDateString.scala index 5ebbde0c..333a55d1 100644 --- a/instances/src/main/scala/pl/iterators/kebs/instances/time/LocalDateString.scala +++ b/instances/src/main/scala/pl/iterators/kebs/instances/time/LocalDateString.scala @@ -1,7 +1,7 @@ package pl.iterators.kebs.instances.time -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.instances.time.LocalDateString.{LocalDateFormat, formatter} +import LocalDateString.{LocalDateFormat, formatter} +import pl.iterators.kebs.core.instances.InstanceConverter import java.time.LocalDate import java.time.format.DateTimeFormatter diff --git a/instances/src/main/scala/pl/iterators/kebs/instances/time/LocalDateTimeString.scala b/instances/src/main/scala/pl/iterators/kebs/instances/time/LocalDateTimeString.scala index 644dd6d9..3e977fdf 100644 --- a/instances/src/main/scala/pl/iterators/kebs/instances/time/LocalDateTimeString.scala +++ b/instances/src/main/scala/pl/iterators/kebs/instances/time/LocalDateTimeString.scala @@ -1,7 +1,7 @@ package pl.iterators.kebs.instances.time -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.instances.time.LocalDateTimeString.{LocalDateTimeFormat, formatter} +import LocalDateTimeString.{LocalDateTimeFormat, formatter} +import pl.iterators.kebs.core.instances.InstanceConverter import java.time.LocalDateTime import java.time.format.DateTimeFormatter diff --git a/instances/src/main/scala/pl/iterators/kebs/instances/time/LocalTimeString.scala b/instances/src/main/scala/pl/iterators/kebs/instances/time/LocalTimeString.scala index 6a662292..dd5889dc 100644 --- a/instances/src/main/scala/pl/iterators/kebs/instances/time/LocalTimeString.scala +++ b/instances/src/main/scala/pl/iterators/kebs/instances/time/LocalTimeString.scala @@ -1,7 +1,7 @@ package pl.iterators.kebs.instances.time -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.instances.time.LocalTimeString.{LocalTimeFormat, formatter} +import LocalTimeString.{LocalTimeFormat, formatter} +import pl.iterators.kebs.core.instances.InstanceConverter import java.time.LocalTime import java.time.format.DateTimeFormatter diff --git a/instances/src/main/scala/pl/iterators/kebs/instances/time/MonthDayString.scala b/instances/src/main/scala/pl/iterators/kebs/instances/time/MonthDayString.scala index 10983411..5e803679 100644 --- a/instances/src/main/scala/pl/iterators/kebs/instances/time/MonthDayString.scala +++ b/instances/src/main/scala/pl/iterators/kebs/instances/time/MonthDayString.scala @@ -1,7 +1,7 @@ package pl.iterators.kebs.instances.time -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.instances.time.MonthDayString.{MonthDayFormat, formatter} +import MonthDayString.{MonthDayFormat, formatter} +import pl.iterators.kebs.core.instances.InstanceConverter import java.time.MonthDay import java.time.format.DateTimeFormatter diff --git a/instances/src/main/scala/pl/iterators/kebs/instances/time/MonthInt.scala b/instances/src/main/scala/pl/iterators/kebs/instances/time/MonthInt.scala index 76e987ca..20657a5a 100644 --- a/instances/src/main/scala/pl/iterators/kebs/instances/time/MonthInt.scala +++ b/instances/src/main/scala/pl/iterators/kebs/instances/time/MonthInt.scala @@ -1,7 +1,7 @@ package pl.iterators.kebs.instances.time -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.instances.time.MonthInt.MonthFormat +import MonthInt.MonthFormat +import pl.iterators.kebs.core.instances.InstanceConverter import java.time.Month diff --git a/instances/src/main/scala/pl/iterators/kebs/instances/time/OffsetDateTimeString.scala b/instances/src/main/scala/pl/iterators/kebs/instances/time/OffsetDateTimeString.scala index b7ebd002..cbd68f09 100644 --- a/instances/src/main/scala/pl/iterators/kebs/instances/time/OffsetDateTimeString.scala +++ b/instances/src/main/scala/pl/iterators/kebs/instances/time/OffsetDateTimeString.scala @@ -1,7 +1,7 @@ package pl.iterators.kebs.instances.time -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.instances.time.OffsetDateTimeString.{OffsetDateTimeFormat, formatter} +import OffsetDateTimeString.{OffsetDateTimeFormat, formatter} +import pl.iterators.kebs.core.instances.InstanceConverter import java.time.OffsetDateTime import java.time.format.DateTimeFormatter diff --git a/instances/src/main/scala/pl/iterators/kebs/instances/time/OffsetTimeString.scala b/instances/src/main/scala/pl/iterators/kebs/instances/time/OffsetTimeString.scala index 3bf7e637..81a74472 100644 --- a/instances/src/main/scala/pl/iterators/kebs/instances/time/OffsetTimeString.scala +++ b/instances/src/main/scala/pl/iterators/kebs/instances/time/OffsetTimeString.scala @@ -1,7 +1,7 @@ package pl.iterators.kebs.instances.time -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.instances.time.OffsetTimeString.{OffsetTimeFormat, formatter} +import OffsetTimeString.{OffsetTimeFormat, formatter} +import pl.iterators.kebs.core.instances.InstanceConverter import java.time.OffsetTime import java.time.format.DateTimeFormatter diff --git a/instances/src/main/scala/pl/iterators/kebs/instances/time/PeriodString.scala b/instances/src/main/scala/pl/iterators/kebs/instances/time/PeriodString.scala index e6d7fde3..78f5e4bd 100644 --- a/instances/src/main/scala/pl/iterators/kebs/instances/time/PeriodString.scala +++ b/instances/src/main/scala/pl/iterators/kebs/instances/time/PeriodString.scala @@ -1,7 +1,7 @@ package pl.iterators.kebs.instances.time -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.instances.time.PeriodString.PeriodFormat +import PeriodString.PeriodFormat +import pl.iterators.kebs.core.instances.InstanceConverter import java.time.Period diff --git a/instances/src/main/scala/pl/iterators/kebs/instances/time/YearInt.scala b/instances/src/main/scala/pl/iterators/kebs/instances/time/YearInt.scala index 244805e3..52dc6275 100644 --- a/instances/src/main/scala/pl/iterators/kebs/instances/time/YearInt.scala +++ b/instances/src/main/scala/pl/iterators/kebs/instances/time/YearInt.scala @@ -1,7 +1,7 @@ package pl.iterators.kebs.instances.time -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.instances.time.YearInt.YearFormat +import YearInt.YearFormat +import pl.iterators.kebs.core.instances.InstanceConverter import java.time.Year diff --git a/instances/src/main/scala/pl/iterators/kebs/instances/time/YearMonthString.scala b/instances/src/main/scala/pl/iterators/kebs/instances/time/YearMonthString.scala index 7d579759..bb733ef5 100644 --- a/instances/src/main/scala/pl/iterators/kebs/instances/time/YearMonthString.scala +++ b/instances/src/main/scala/pl/iterators/kebs/instances/time/YearMonthString.scala @@ -1,7 +1,7 @@ package pl.iterators.kebs.instances.time -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.instances.time.YearMonthString.{YearMonthFormat, formatter} +import YearMonthString.{YearMonthFormat, formatter} +import pl.iterators.kebs.core.instances.InstanceConverter import java.time.YearMonth import java.time.format.DateTimeFormatter diff --git a/instances/src/main/scala/pl/iterators/kebs/instances/time/ZoneIdString.scala b/instances/src/main/scala/pl/iterators/kebs/instances/time/ZoneIdString.scala index 33461a08..098bde54 100644 --- a/instances/src/main/scala/pl/iterators/kebs/instances/time/ZoneIdString.scala +++ b/instances/src/main/scala/pl/iterators/kebs/instances/time/ZoneIdString.scala @@ -1,7 +1,7 @@ package pl.iterators.kebs.instances.time -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.instances.time.ZoneIdString.ZoneIdFormat +import ZoneIdString.ZoneIdFormat +import pl.iterators.kebs.core.instances.InstanceConverter import java.time.ZoneId diff --git a/instances/src/main/scala/pl/iterators/kebs/instances/time/ZoneOffsetString.scala b/instances/src/main/scala/pl/iterators/kebs/instances/time/ZoneOffsetString.scala index 1a74f1da..c91da8f2 100644 --- a/instances/src/main/scala/pl/iterators/kebs/instances/time/ZoneOffsetString.scala +++ b/instances/src/main/scala/pl/iterators/kebs/instances/time/ZoneOffsetString.scala @@ -1,7 +1,7 @@ package pl.iterators.kebs.instances.time -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.instances.time.ZoneOffsetString.ZoneOffsetFormat +import ZoneOffsetString.ZoneOffsetFormat +import pl.iterators.kebs.core.instances.InstanceConverter import java.time.ZoneOffset diff --git a/instances/src/main/scala/pl/iterators/kebs/instances/time/ZonedDateTimeString.scala b/instances/src/main/scala/pl/iterators/kebs/instances/time/ZonedDateTimeString.scala index 29b944e7..f5fdef37 100644 --- a/instances/src/main/scala/pl/iterators/kebs/instances/time/ZonedDateTimeString.scala +++ b/instances/src/main/scala/pl/iterators/kebs/instances/time/ZonedDateTimeString.scala @@ -1,7 +1,7 @@ package pl.iterators.kebs.instances.time -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.instances.time.ZonedDateTimeString.{ZonedDateTimeFormat, formatter} +import ZonedDateTimeString.{ZonedDateTimeFormat, formatter} +import pl.iterators.kebs.core.instances.InstanceConverter import java.time.ZonedDateTime import java.time.format.DateTimeFormatter diff --git a/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/DurationMillisLong.scala b/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/DurationMillisLong.scala index aad9a113..d7bc7bce 100644 --- a/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/DurationMillisLong.scala +++ b/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/DurationMillisLong.scala @@ -1,7 +1,6 @@ package pl.iterators.kebs.instances.time.mixins -import pl.iterators.kebs.instances.InstanceConverter - +import pl.iterators.kebs.core.instances.InstanceConverter import java.time.Duration trait DurationMillisLong { diff --git a/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/DurationMinutesLong.scala b/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/DurationMinutesLong.scala index 526e6be9..23bbee34 100644 --- a/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/DurationMinutesLong.scala +++ b/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/DurationMinutesLong.scala @@ -1,7 +1,6 @@ package pl.iterators.kebs.instances.time.mixins -import pl.iterators.kebs.instances.InstanceConverter - +import pl.iterators.kebs.core.instances.InstanceConverter import java.time.Duration trait DurationMinutesLong { diff --git a/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/DurationNanosLong.scala b/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/DurationNanosLong.scala index 4a1448f3..47258dc6 100644 --- a/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/DurationNanosLong.scala +++ b/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/DurationNanosLong.scala @@ -1,7 +1,6 @@ package pl.iterators.kebs.instances.time.mixins -import pl.iterators.kebs.instances.InstanceConverter - +import pl.iterators.kebs.core.instances.InstanceConverter import java.time.Duration trait DurationNanosLong { diff --git a/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/DurationSecondsLong.scala b/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/DurationSecondsLong.scala index 2d55f5df..633159e2 100644 --- a/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/DurationSecondsLong.scala +++ b/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/DurationSecondsLong.scala @@ -1,7 +1,6 @@ package pl.iterators.kebs.instances.time.mixins -import pl.iterators.kebs.instances.InstanceConverter - +import pl.iterators.kebs.core.instances.InstanceConverter import java.time.Duration trait DurationSecondsLong { diff --git a/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/InstantEpochMilliLong.scala b/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/InstantEpochMilliLong.scala index 77ade584..a4373b44 100644 --- a/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/InstantEpochMilliLong.scala +++ b/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/InstantEpochMilliLong.scala @@ -1,7 +1,6 @@ package pl.iterators.kebs.instances.time.mixins -import pl.iterators.kebs.instances.InstanceConverter - +import pl.iterators.kebs.core.instances.InstanceConverter import java.time.Instant trait InstantEpochMilliLong { diff --git a/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/InstantEpochSecondLong.scala b/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/InstantEpochSecondLong.scala index f23383a9..481062e1 100644 --- a/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/InstantEpochSecondLong.scala +++ b/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/InstantEpochSecondLong.scala @@ -1,7 +1,6 @@ package pl.iterators.kebs.instances.time.mixins -import pl.iterators.kebs.instances.InstanceConverter - +import pl.iterators.kebs.core.instances.InstanceConverter import java.time.Instant trait InstantEpochSecondLong { diff --git a/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/PeriodDays.scala b/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/PeriodDays.scala index 7395adf0..c9ee454e 100644 --- a/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/PeriodDays.scala +++ b/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/PeriodDays.scala @@ -1,7 +1,6 @@ package pl.iterators.kebs.instances.time.mixins -import pl.iterators.kebs.instances.InstanceConverter - +import pl.iterators.kebs.core.instances.InstanceConverter import java.time.Period trait PeriodDays { diff --git a/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/PeriodMonthsInt.scala b/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/PeriodMonthsInt.scala index 9d042b5a..e73eb913 100644 --- a/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/PeriodMonthsInt.scala +++ b/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/PeriodMonthsInt.scala @@ -1,7 +1,6 @@ package pl.iterators.kebs.instances.time.mixins -import pl.iterators.kebs.instances.InstanceConverter - +import pl.iterators.kebs.core.instances.InstanceConverter import java.time.Period trait PeriodMonthsInt { diff --git a/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/PeriodYearsInt.scala b/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/PeriodYearsInt.scala index 0d290eac..5de95e4b 100644 --- a/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/PeriodYearsInt.scala +++ b/instances/src/main/scala/pl/iterators/kebs/instances/time/mixins/PeriodYearsInt.scala @@ -1,7 +1,6 @@ package pl.iterators.kebs.instances.time.mixins -import pl.iterators.kebs.instances.InstanceConverter - +import pl.iterators.kebs.core.instances.InstanceConverter import java.time.Period trait PeriodYearsInt { diff --git a/instances/src/main/scala/pl/iterators/kebs/instances/util/CurrencyString.scala b/instances/src/main/scala/pl/iterators/kebs/instances/util/CurrencyString.scala index 0ab1c386..30d74581 100644 --- a/instances/src/main/scala/pl/iterators/kebs/instances/util/CurrencyString.scala +++ b/instances/src/main/scala/pl/iterators/kebs/instances/util/CurrencyString.scala @@ -1,7 +1,7 @@ package pl.iterators.kebs.instances.util -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.instances.util.CurrencyString.CurrencyFormat +import CurrencyString.CurrencyFormat +import pl.iterators.kebs.core.instances.InstanceConverter import java.util.Currency diff --git a/instances/src/main/scala/pl/iterators/kebs/instances/util/LocaleString.scala b/instances/src/main/scala/pl/iterators/kebs/instances/util/LocaleString.scala index e8e44c06..fd8dac83 100644 --- a/instances/src/main/scala/pl/iterators/kebs/instances/util/LocaleString.scala +++ b/instances/src/main/scala/pl/iterators/kebs/instances/util/LocaleString.scala @@ -1,7 +1,7 @@ package pl.iterators.kebs.instances.util -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.instances.util.LocaleString.LocaleFormat +import LocaleString.LocaleFormat +import pl.iterators.kebs.core.instances.InstanceConverter import java.util.Locale diff --git a/instances/src/main/scala/pl/iterators/kebs/instances/util/UUIDString.scala b/instances/src/main/scala/pl/iterators/kebs/instances/util/UUIDString.scala index 2eb45a4d..de2f6b8d 100644 --- a/instances/src/main/scala/pl/iterators/kebs/instances/util/UUIDString.scala +++ b/instances/src/main/scala/pl/iterators/kebs/instances/util/UUIDString.scala @@ -1,7 +1,7 @@ package pl.iterators.kebs.instances.util -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.instances.util.UUIDString.UUIDFormat +import UUIDString.UUIDFormat +import pl.iterators.kebs.core.instances.InstanceConverter import java.util.UUID diff --git a/instances/src/test/scala/pl/iterators/kebs/instances/net/NetInstancesTests.scala b/instances/src/test/scala/pl/iterators/kebs/instances/net/NetInstancesTests.scala index 9237d610..db907ab3 100644 --- a/instances/src/test/scala/pl/iterators/kebs/instances/net/NetInstancesTests.scala +++ b/instances/src/test/scala/pl/iterators/kebs/instances/net/NetInstancesTests.scala @@ -2,8 +2,8 @@ package pl.iterators.kebs.instances.net import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.instances.InstanceConverter -import InstanceConverter.DecodeErrorException +import pl.iterators.kebs.core.instances.InstanceConverter +import pl.iterators.kebs.core.instances.InstanceConverter.DecodeErrorException import java.net.URI diff --git a/instances/src/test/scala/pl/iterators/kebs/instances/time/TimeInstancesTests.scala b/instances/src/test/scala/pl/iterators/kebs/instances/time/TimeInstancesTests.scala index 20764d9e..e159e32b 100644 --- a/instances/src/test/scala/pl/iterators/kebs/instances/time/TimeInstancesTests.scala +++ b/instances/src/test/scala/pl/iterators/kebs/instances/time/TimeInstancesTests.scala @@ -2,9 +2,9 @@ package pl.iterators.kebs.instances.time import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.instances.InstanceConverter.DecodeErrorException import pl.iterators.kebs.instances.TimeInstances +import pl.iterators.kebs.core.instances.InstanceConverter +import InstanceConverter.DecodeErrorException import java.time._ diff --git a/instances/src/test/scala/pl/iterators/kebs/instances/util/UtilInstancesTests.scala b/instances/src/test/scala/pl/iterators/kebs/instances/util/UtilInstancesTests.scala index 6c8aa762..398ae505 100644 --- a/instances/src/test/scala/pl/iterators/kebs/instances/util/UtilInstancesTests.scala +++ b/instances/src/test/scala/pl/iterators/kebs/instances/util/UtilInstancesTests.scala @@ -2,9 +2,9 @@ package pl.iterators.kebs.instances.util import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.instances.InstanceConverter.DecodeErrorException import pl.iterators.kebs.instances.UtilInstances +import pl.iterators.kebs.core.instances.InstanceConverter +import InstanceConverter.DecodeErrorException import java.util.{Currency, Locale, UUID} diff --git a/jsonschema/src/main/scala/pl/iterators/kebs/jsonschema/KebsJsonSchema.scala b/jsonschema/src/main/scala/pl/iterators/kebs/jsonschema/KebsJsonSchema.scala index 48bbaefd..8ed23d38 100644 --- a/jsonschema/src/main/scala/pl/iterators/kebs/jsonschema/KebsJsonSchema.scala +++ b/jsonschema/src/main/scala/pl/iterators/kebs/jsonschema/KebsJsonSchema.scala @@ -1,13 +1,15 @@ package pl.iterators.kebs.jsonschema -import pl.iterators.kebs.macros.CaseClass1Rep +import pl.iterators.kebs.core.macros.ValueClassLike + +import scala.annotation.unused trait KebsJsonSchema { import macros.KebsJsonSchemaMacros implicit val jswUnit: JsonSchemaWrapper[Unit] = JsonSchemaWrapper[Unit](null) - implicit def caseClass1RepJsonSchemaPredef[T, A](implicit rep: CaseClass1Rep[T, A], - schema: json.schema.Predef[A]): json.schema.Predef[T] = + implicit def valueClassLikeJsonSchemaPredef[T, A](implicit @unused rep: ValueClassLike[T, A], + schema: json.schema.Predef[A]): json.schema.Predef[T] = schema.asInstanceOf[json.schema.Predef[T]] implicit def genericJsonSchemaWrapper[T]: JsonSchemaWrapper[T] = macro KebsJsonSchemaMacros.materializeSchema[T] } diff --git a/jsonschema/src/main/scala/pl/iterators/kebs/jsonschema/macros/KebsJsonSchemaMacros.scala b/jsonschema/src/main/scala/pl/iterators/kebs/jsonschema/macros/KebsJsonSchemaMacros.scala index 03bf72c3..5753b18b 100644 --- a/jsonschema/src/main/scala/pl/iterators/kebs/jsonschema/macros/KebsJsonSchemaMacros.scala +++ b/jsonschema/src/main/scala/pl/iterators/kebs/jsonschema/macros/KebsJsonSchemaMacros.scala @@ -1,9 +1,7 @@ package pl.iterators.kebs.jsonschema.macros -import pl.iterators.kebs.macros.MacroUtils - import scala.reflect.macros._ -import json._ +import pl.iterators.kebs.core.macros.MacroUtils import pl.iterators.kebs.jsonschema.JsonSchemaWrapper class KebsJsonSchemaMacros(override val c: whitebox.Context) extends MacroUtils { diff --git a/jsonschema/src/test/scala/JsonSchemaTests.scala b/jsonschema/src/test/scala/pl/iterators/kebs/jsonschema/JsonSchemaTests.scala similarity index 63% rename from jsonschema/src/test/scala/JsonSchemaTests.scala rename to jsonschema/src/test/scala/pl/iterators/kebs/jsonschema/JsonSchemaTests.scala index cbe5391f..3b9d5408 100644 --- a/jsonschema/src/test/scala/JsonSchemaTests.scala +++ b/jsonschema/src/test/scala/pl/iterators/kebs/jsonschema/JsonSchemaTests.scala @@ -1,17 +1,10 @@ +package pl.iterators.kebs.jsonschema + import com.github.andyglow.json.JsonFormatter import com.github.andyglow.jsonschema.AsValue import json.schema.Version.Draft07 import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.jsonschema.{KebsJsonSchema, JsonSchemaWrapper} - -case class WrappedInt(int: Int) -case class WrappedIntAnyVal(int: Int) extends AnyVal -case class Sample(someNumber: Int, - someText: String, - arrayOfNumbers: List[Int], - wrappedNumber: WrappedInt, - wrappedNumberAnyVal: WrappedIntAnyVal) class JsonSchemaTests extends AnyFunSuite with Matchers { object KebsProtocol extends KebsJsonSchema diff --git a/jsonschema/src/test/scala/pl/iterators/kebs/jsonschema/Sample.scala b/jsonschema/src/test/scala/pl/iterators/kebs/jsonschema/Sample.scala new file mode 100644 index 00000000..21abb27f --- /dev/null +++ b/jsonschema/src/test/scala/pl/iterators/kebs/jsonschema/Sample.scala @@ -0,0 +1,7 @@ +package pl.iterators.kebs.jsonschema + +case class Sample(someNumber: Int, + someText: String, + arrayOfNumbers: List[Int], + wrappedNumber: WrappedInt, + wrappedNumberAnyVal: WrappedIntAnyVal) diff --git a/jsonschema/src/test/scala/pl/iterators/kebs/jsonschema/WrappedInt.scala b/jsonschema/src/test/scala/pl/iterators/kebs/jsonschema/WrappedInt.scala new file mode 100644 index 00000000..ee23ab2a --- /dev/null +++ b/jsonschema/src/test/scala/pl/iterators/kebs/jsonschema/WrappedInt.scala @@ -0,0 +1,3 @@ +package pl.iterators.kebs.jsonschema + +case class WrappedInt(int: Int) diff --git a/jsonschema/src/test/scala/pl/iterators/kebs/jsonschema/WrappedIntAnyVal.scala b/jsonschema/src/test/scala/pl/iterators/kebs/jsonschema/WrappedIntAnyVal.scala new file mode 100644 index 00000000..9904d422 --- /dev/null +++ b/jsonschema/src/test/scala/pl/iterators/kebs/jsonschema/WrappedIntAnyVal.scala @@ -0,0 +1,3 @@ +package pl.iterators.kebs.jsonschema + +case class WrappedIntAnyVal(int: Int) extends AnyVal diff --git a/opaque/src/main/scala-3/pl/iterators/kebs/opaque/Opaque.scala b/opaque/src/main/scala-3/pl/iterators/kebs/opaque/Opaque.scala index 321f5c31..9aa33080 100644 --- a/opaque/src/main/scala-3/pl/iterators/kebs/opaque/Opaque.scala +++ b/opaque/src/main/scala-3/pl/iterators/kebs/opaque/Opaque.scala @@ -1,6 +1,6 @@ package pl.iterators.kebs.opaque -import pl.iterators.kebs.macros.CaseClass1Rep +import pl.iterators.kebs.core.macros.ValueClassLike trait Opaque[OpaqueType, Unwrapped](using ev: OpaqueType =:= Unwrapped) { /** @@ -40,5 +40,5 @@ trait Opaque[OpaqueType, Unwrapped](using ev: OpaqueType =:= Unwrapped) { def unwrap: Unwrapped = ev.apply(w) } - given cc1Rep: CaseClass1Rep[OpaqueType, Unwrapped] = CaseClass1Rep(apply, _.unwrap) + given vcLike: ValueClassLike[OpaqueType, Unwrapped] = ValueClassLike(apply, _.unwrap) } \ No newline at end of file diff --git a/opaque/src/test/scala-3/OpaqueTest.scala b/opaque/src/test/scala-3/pl/iterators/kebs/opaque/OpaqueTest.scala similarity index 76% rename from opaque/src/test/scala-3/OpaqueTest.scala rename to opaque/src/test/scala-3/pl/iterators/kebs/opaque/OpaqueTest.scala index aea40056..25c55ba4 100644 --- a/opaque/src/test/scala-3/OpaqueTest.scala +++ b/opaque/src/test/scala-3/pl/iterators/kebs/opaque/OpaqueTest.scala @@ -1,7 +1,7 @@ import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers import pl.iterators.kebs.opaque.Opaque -import pl.iterators.kebs.macros.CaseClass1Rep +import pl.iterators.kebs.core.macros.ValueClassLike object OpaqueTestDomain { opaque type TestWrappedInt = Int @@ -24,7 +24,7 @@ object OpaqueTestTypeclass { } given Showable[Int] = (a: Int) => a.toString - given[S, A](using showable: Showable[S], cc1Rep: CaseClass1Rep[A, S]): Showable[A] = (a: A) => showable.show(cc1Rep.unapply(a)) + given[S, A](using showable: Showable[S], vcLike: ValueClassLike[A, S]): Showable[A] = (a: A) => showable.show(vcLike.unapply(a)) } class OpaqueTest extends AnyFunSuite with Matchers { @@ -54,10 +54,10 @@ class OpaqueTest extends AnyFunSuite with Matchers { } test("Basic derivation") { - "implicitly[CaseClass1Rep[ValidatedTestWrappedString, String]]" should compile - implicitly[CaseClass1Rep[ValidatedTestWrappedString, String]].apply("foo") shouldEqual ValidatedTestWrappedString("foo") - implicitly[CaseClass1Rep[ValidatedTestWrappedString, String]].unapply(ValidatedTestWrappedString("foo")) shouldEqual "foo" - an[IllegalArgumentException] should be thrownBy implicitly[CaseClass1Rep[ValidatedTestWrappedString, String]].apply("") + "implicitly[ValueClassLike[ValidatedTestWrappedString, String]]" should compile + implicitly[ValueClassLike[ValidatedTestWrappedString, String]].apply("foo") shouldEqual ValidatedTestWrappedString("foo") + implicitly[ValueClassLike[ValidatedTestWrappedString, String]].unapply(ValidatedTestWrappedString("foo")) shouldEqual "foo" + an[IllegalArgumentException] should be thrownBy implicitly[ValueClassLike[ValidatedTestWrappedString, String]].apply("") } test("Typeclass derivation") { diff --git a/pekko-http/src/main/scala-2/matchers/KebsMatchers.scala b/pekko-http/src/main/scala-2/matchers/KebsMatchers.scala deleted file mode 100644 index dfa33142..00000000 --- a/pekko-http/src/main/scala-2/matchers/KebsMatchers.scala +++ /dev/null @@ -1,26 +0,0 @@ -package pl.iterators.kebs.matchers - -import org.apache.pekko.http.scaladsl.server.{PathMatcher1, PathMatchers} -import enumeratum.{Enum, EnumEntry} -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.macros.CaseClass1Rep - -import scala.language.implicitConversions - -trait KebsMatchers extends PathMatchers { - - implicit class SegmentIsomorphism[U](segment: PathMatcher1[U]) { - def as[T](implicit rep: CaseClass1Rep[T, U]): PathMatcher1[T] = segment.map(rep.apply) - } - - 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) - } - } -} diff --git a/pekko-http/src/main/scala-2/pl/iterators/kebs/pekkohttp/matchers/KebsMatchers.scala b/pekko-http/src/main/scala-2/pl/iterators/kebs/pekkohttp/matchers/KebsMatchers.scala new file mode 100644 index 00000000..cceddf29 --- /dev/null +++ b/pekko-http/src/main/scala-2/pl/iterators/kebs/pekkohttp/matchers/KebsMatchers.scala @@ -0,0 +1,23 @@ +package pl.iterators.kebs.pekkohttp.matchers + +import org.apache.pekko.http.scaladsl.server.{PathMatcher1, PathMatchers} +import pl.iterators.kebs.core.instances.InstanceConverter +import pl.iterators.kebs.core.macros.ValueClassLike +import pl.iterators.kebs.core.enums._ + +trait KebsMatchers extends PathMatchers { + + implicit class SegmentIsomorphism[U](segment: PathMatcher1[U]) { + def as[T](implicit rep: ValueClassLike[T, U]): PathMatcher1[T] = segment.map(rep.apply) + } + + 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](implicit e: EnumLike[T]): PathMatcher1[T] = { + Segment.map(e.withNameIgnoreCase) + } + } +} diff --git a/pekko-http/src/main/scala-2/pl/iterators/kebs/pekkohttp/unmarshallers/enums/KebsEnumUnmarshallers.scala b/pekko-http/src/main/scala-2/pl/iterators/kebs/pekkohttp/unmarshallers/enums/KebsEnumUnmarshallers.scala new file mode 100644 index 00000000..dce95973 --- /dev/null +++ b/pekko-http/src/main/scala-2/pl/iterators/kebs/pekkohttp/unmarshallers/enums/KebsEnumUnmarshallers.scala @@ -0,0 +1,45 @@ +package pl.iterators.kebs.pekkohttp.unmarshallers.enums + +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.core.enums.{EnumLike, ValueEnumLike, ValueEnumLikeEntry} + +trait EnumUnmarshallers { + final def enumUnmarshaller[E](`enum`: EnumLike[E]): FromStringUnmarshaller[E] = Unmarshaller { _ =>name => + `enum`.withNameInsensitiveOption(name) match { + case Some(enumEntry) => FastFuture.successful(enumEntry) + case None => + FastFuture.failed(new IllegalArgumentException(s"""Invalid value '$name'. Expected one of: ${`enum`.getNamesToValuesMap.keysIterator + .mkString(", ")}""")) + } + } + + implicit def kebsEnumUnmarshaller[E](implicit ev: EnumLike[E]): FromStringUnmarshaller[E] = + enumUnmarshaller(ev) +} + +trait ValueEnumUnmarshallers { + final def valueEnumUnmarshaller[V, E <: ValueEnumLikeEntry[V]](`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 None => + FastFuture.failed(new IllegalArgumentException(s"""Invalid value '$v'. Expected one of: ${`enum`.getValuesToEntriesMap.keysIterator + .mkString(", ")}""")) + } + } + + implicit def kebsValueEnumUnmarshaller[V, E <: ValueEnumLikeEntry[V]](implicit ev: ValueEnumLike[V, E]): Unmarshaller[V, E] = + valueEnumUnmarshaller(ev) + + implicit def kebsIntValueEnumFromStringUnmarshaller[E <: ValueEnumLikeEntry[Int]](implicit ev: ValueEnumLike[Int, E]): FromStringUnmarshaller[E] = + intFromStringUnmarshaller andThen valueEnumUnmarshaller(ev) + implicit def kebsLongValueEnumFromStringUnmarshaller[E <: ValueEnumLikeEntry[Long]](implicit ev: ValueEnumLike[Long, E]): FromStringUnmarshaller[E] = + longFromStringUnmarshaller andThen valueEnumUnmarshaller(ev) + implicit def kebsShortValueEnumFromStringUnmarshaller[E <: ValueEnumLikeEntry[Short]](implicit ev: ValueEnumLike[Short, E]): FromStringUnmarshaller[E] = + shortFromStringUnmarshaller andThen valueEnumUnmarshaller(ev) + implicit def kebsByteValueEnumFromStringUnmarshaller[E <: ValueEnumLikeEntry[Byte]](implicit ev: ValueEnumLike[Byte, E]): FromStringUnmarshaller[E] = + byteFromStringUnmarshaller andThen valueEnumUnmarshaller(ev) +} + +trait KebsEnumUnmarshallers extends EnumUnmarshallers with ValueEnumUnmarshallers {} diff --git a/pekko-http/src/main/scala-2/unmarshallers/enums/package.scala b/pekko-http/src/main/scala-2/pl/iterators/kebs/pekkohttp/unmarshallers/enums/package.scala similarity index 50% rename from pekko-http/src/main/scala-2/unmarshallers/enums/package.scala rename to pekko-http/src/main/scala-2/pl/iterators/kebs/pekkohttp/unmarshallers/enums/package.scala index 757c8e7e..3c2b9251 100644 --- a/pekko-http/src/main/scala-2/unmarshallers/enums/package.scala +++ b/pekko-http/src/main/scala-2/pl/iterators/kebs/pekkohttp/unmarshallers/enums/package.scala @@ -1,3 +1,3 @@ -package pl.iterators.kebs.unmarshallers +package pl.iterators.kebs.pekkohttp.unmarshallers package object enums extends KebsEnumUnmarshallers \ No newline at end of file diff --git a/pekko-http/src/main/scala-2/unmarshallers/enums/KebsEnumUnmarshallers.scala b/pekko-http/src/main/scala-2/unmarshallers/enums/KebsEnumUnmarshallers.scala deleted file mode 100644 index ca127d1b..00000000 --- a/pekko-http/src/main/scala-2/unmarshallers/enums/KebsEnumUnmarshallers.scala +++ /dev/null @@ -1,48 +0,0 @@ -package pl.iterators.kebs.unmarshallers.enums - -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 enumeratum.values._ -import enumeratum.{Enum, EnumEntry} -import pl.iterators.kebs.macros.enums.{EnumOf, ValueEnumOf} - -trait EnumUnmarshallers { - final def enumUnmarshaller[E <: EnumEntry](`enum`: Enum[E]): FromStringUnmarshaller[E] = Unmarshaller { _ => name => - `enum`.withNameInsensitiveOption(name) match { - case Some(enumEntry) => FastFuture.successful(enumEntry) - case None => - FastFuture.failed(new IllegalArgumentException(s"""Invalid value '$name'. Expected one of: ${`enum`.namesToValuesMap.keysIterator - .mkString(", ")}""")) - } - } - - implicit def kebsEnumUnmarshaller[E <: EnumEntry](implicit ev: EnumOf[E]): FromStringUnmarshaller[E] = - enumUnmarshaller(ev.`enum`) -} - -trait ValueEnumUnmarshallers { - final def valueEnumUnmarshaller[V, E <: ValueEnumEntry[V]](`enum`: ValueEnum[V, E]): Unmarshaller[V, E] = Unmarshaller { _ =>v => - `enum`.withValueOpt(v) match { - case Some(enumEntry) => FastFuture.successful(enumEntry) - case None => - FastFuture.failed(new IllegalArgumentException(s"""Invalid value '$v'. Expected one of: ${`enum`.valuesToEntriesMap.keysIterator - .mkString(", ")}""")) - } - } - - implicit def kebsValueEnumUnmarshaller[V, E <: ValueEnumEntry[V]](implicit ev: ValueEnumOf[V, E]): Unmarshaller[V, E] = - valueEnumUnmarshaller(ev.valueEnum) - - implicit def kebsIntValueEnumFromStringUnmarshaller[E <: IntEnumEntry](implicit ev: ValueEnumOf[Int, E]): FromStringUnmarshaller[E] = - intFromStringUnmarshaller andThen valueEnumUnmarshaller(ev.valueEnum) - implicit def kebsLongValueEnumFromStringUnmarshaller[E <: LongEnumEntry](implicit ev: ValueEnumOf[Long, E]): FromStringUnmarshaller[E] = - longFromStringUnmarshaller andThen valueEnumUnmarshaller(ev.valueEnum) - implicit def kebsShortValueEnumFromStringUnmarshaller[E <: ShortEnumEntry]( - implicit ev: ValueEnumOf[Short, E]): FromStringUnmarshaller[E] = - shortFromStringUnmarshaller andThen valueEnumUnmarshaller(ev.valueEnum) - implicit def kebsByteValueEnumFromStringUnmarshaller[E <: ByteEnumEntry](implicit ev: ValueEnumOf[Byte, E]): FromStringUnmarshaller[E] = - byteFromStringUnmarshaller andThen valueEnumUnmarshaller(ev.valueEnum) -} - -trait KebsEnumUnmarshallers extends EnumUnmarshallers with ValueEnumUnmarshallers {} diff --git a/pekko-http/src/main/scala-3/matchers/KebsMatchers.scala b/pekko-http/src/main/scala-3/matchers/KebsMatchers.scala deleted file mode 100644 index 9615081c..00000000 --- a/pekko-http/src/main/scala-3/matchers/KebsMatchers.scala +++ /dev/null @@ -1,27 +0,0 @@ -package pl.iterators.kebs.matchers - -import org.apache.pekko.http.scaladsl.server.PathMatcher1 -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.macros.CaseClass1Rep -import pl.iterators.kebs.macros.enums.EnumOf -import org.apache.pekko.stream.Materializer -import scala.reflect.Enum - -import scala.language.implicitConversions - -trait KebsMatchers extends org.apache.pekko.http.scaladsl.server.PathMatchers { - - implicit class SegmentIsomorphism[U](segment: PathMatcher1[U]) { - def as[T](implicit rep: CaseClass1Rep[T, U]): PathMatcher1[T] = segment.map(rep.apply) - } - - 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 <: Enum](using e: EnumOf[T]): PathMatcher1[T] = { - Segment.map(s => e.`enum`.values.find(_.toString().toLowerCase() == s.toLowerCase()).getOrElse(throw new IllegalArgumentException(s"""Invalid value '$s'. Expected one of: ${e.`enum`.values.mkString(", ")}"""))) - } - } -} diff --git a/pekko-http/src/main/scala-3/pl/iterators/kebs/pekkohttp/matchers/KebsMatchers.scala b/pekko-http/src/main/scala-3/pl/iterators/kebs/pekkohttp/matchers/KebsMatchers.scala new file mode 100644 index 00000000..58afb918 --- /dev/null +++ b/pekko-http/src/main/scala-3/pl/iterators/kebs/pekkohttp/matchers/KebsMatchers.scala @@ -0,0 +1,25 @@ +package pl.iterators.kebs.pekkohttp.matchers + +import org.apache.pekko.http.scaladsl.server.PathMatcher1 +import pl.iterators.kebs.core.macros.ValueClassLike +import scala.reflect.Enum + +import pl.iterators.kebs.core.enums.EnumLike +import pl.iterators.kebs.core.instances.InstanceConverter + +trait KebsMatchers extends org.apache.pekko.http.scaladsl.server.PathMatchers { + + implicit class SegmentIsomorphism[U](segment: PathMatcher1[U]) { + def as[T](implicit rep: ValueClassLike[T, U]): PathMatcher1[T] = segment.map(rep.apply) + } + + 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 <: Enum](using e: EnumLike[T]): PathMatcher1[T] = { + Segment.map(s => e.values.find(_.toString().toLowerCase() == s.toLowerCase()).getOrElse(throw new IllegalArgumentException(s"""Invalid value '$s'. Expected one of: ${e.values.mkString(", ")}"""))) + } + } +} diff --git a/pekko-http/src/main/scala-3/pl/iterators/kebs/pekkohttp/unmarshallers/enums/KebsEnumUnmarshallers.scala b/pekko-http/src/main/scala-3/pl/iterators/kebs/pekkohttp/unmarshallers/enums/KebsEnumUnmarshallers.scala new file mode 100644 index 00000000..3febac59 --- /dev/null +++ b/pekko-http/src/main/scala-3/pl/iterators/kebs/pekkohttp/unmarshallers/enums/KebsEnumUnmarshallers.scala @@ -0,0 +1,58 @@ +package pl.iterators.kebs.pekkohttp.unmarshallers.enums + +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 scala.reflect.ClassTag + +import pl.iterators.kebs.core.enums.{EnumLike, ValueEnumLike, ValueEnumLikeEntry} + +trait EnumUnmarshallers { + + final def enumUnmarshaller[E](using e: EnumLike[E]): FromStringUnmarshaller[E] = org.apache.pekko.http.scaladsl.unmarshalling.Unmarshaller { _ => name => + e.values.find(_.toString().toLowerCase() == name.toLowerCase()) match { + case Some(enumEntry) => FastFuture.successful(enumEntry) + case None => + FastFuture.failed(new IllegalArgumentException(s"""Invalid value '$name'. Expected one of: ${e.values.mkString(", ")}""")) + } + } + + given kebsEnumUnmarshaller[E](using e: EnumLike[E]): FromStringUnmarshaller[E] = + enumUnmarshaller +} + +trait ValueEnumUnmarshallers extends EnumUnmarshallers { + final def valueEnumUnmarshaller[V, E <: ValueEnumLikeEntry[V]](using `enum`: ValueEnumLike[V, E], cls: ClassTag[V]): 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(enumEntry) => + 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(", ")}""")) + } + } + } + + given kebsValueEnumUnmarshaller[V, E <: ValueEnumLikeEntry[V]](using `enum`: ValueEnumLike[V, E], cls: ClassTag[V]): Unmarshaller[V, E] = + valueEnumUnmarshaller + + given kebsIntValueEnumFromStringUnmarshaller[E <: ValueEnumLikeEntry[Int]](using ev: ValueEnumLike[Int, E]): FromStringUnmarshaller[E] = + intFromStringUnmarshaller andThen valueEnumUnmarshaller + + given kebsLongValueEnumFromStringUnmarshaller[E <: ValueEnumLikeEntry[Long]](using ev: ValueEnumLike[Long, E]): FromStringUnmarshaller[E] = + longFromStringUnmarshaller andThen valueEnumUnmarshaller + + given kebsShortValueEnumFromStringUnmarshaller[E <: ValueEnumLikeEntry[Short]](using ev: ValueEnumLike[Short, E]): FromStringUnmarshaller[E] = + shortFromStringUnmarshaller andThen valueEnumUnmarshaller + + given kebsByteValueEnumFromStringUnmarshaller[E <: ValueEnumLikeEntry[Byte]](using ev: ValueEnumLike[Byte, E]): FromStringUnmarshaller[E] = + byteFromStringUnmarshaller andThen valueEnumUnmarshaller +} + +trait KebsEnumUnmarshallers extends ValueEnumUnmarshallers {} diff --git a/pekko-http/src/main/scala-3/unmarshallers/enums/package.scala b/pekko-http/src/main/scala-3/pl/iterators/kebs/pekkohttp/unmarshallers/enums/package.scala similarity index 50% rename from pekko-http/src/main/scala-3/unmarshallers/enums/package.scala rename to pekko-http/src/main/scala-3/pl/iterators/kebs/pekkohttp/unmarshallers/enums/package.scala index 757c8e7e..3c2b9251 100644 --- a/pekko-http/src/main/scala-3/unmarshallers/enums/package.scala +++ b/pekko-http/src/main/scala-3/pl/iterators/kebs/pekkohttp/unmarshallers/enums/package.scala @@ -1,3 +1,3 @@ -package pl.iterators.kebs.unmarshallers +package pl.iterators.kebs.pekkohttp.unmarshallers package object enums extends KebsEnumUnmarshallers \ No newline at end of file diff --git a/pekko-http/src/main/scala-3/unmarshallers/enums/KebsEnumUnmarshallers.scala b/pekko-http/src/main/scala-3/unmarshallers/enums/KebsEnumUnmarshallers.scala deleted file mode 100644 index 914c15e1..00000000 --- a/pekko-http/src/main/scala-3/unmarshallers/enums/KebsEnumUnmarshallers.scala +++ /dev/null @@ -1,50 +0,0 @@ -package pl.iterators.kebs.unmarshallers.enums - -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.macros.enums.{EnumOf, ValueEnumOf} -import pl.iterators.kebs.enums.ValueEnum -import scala.reflect.Enum -import scala.reflect.ClassTag - -trait EnumUnmarshallers { - final def enumUnmarshaller[E <: Enum](using e: EnumOf[E]): FromStringUnmarshaller[E] = org.apache.pekko.http.scaladsl.unmarshalling.Unmarshaller { _ => name => - e.`enum`.values.find(_.toString().toLowerCase() == name.toLowerCase()) match { - case Some(enumEntry) => FastFuture.successful(enumEntry) - case None => - FastFuture.failed(new IllegalArgumentException(s"""Invalid value '$name'. Expected one of: ${e.`enum`.values.mkString(", ")}""")) - } - } - - given kebsEnumUnmarshaller[E <: Enum](using e: EnumOf[E]): FromStringUnmarshaller[E] = - enumUnmarshaller -} - -trait ValueEnumUnmarshallers extends EnumUnmarshallers { - final def valueEnumUnmarshaller[V, E <: ValueEnum[V] with Enum](using `enum`: ValueEnumOf[V, E], cls: ClassTag[V]): Unmarshaller[V, E] = Unmarshaller { _ => v => - `enum`.`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`.`enum`.values.map(_.value).mkString(", ")}""")) - } - } - - given kebsValueEnumUnmarshaller[V, E <: ValueEnum[V] with Enum](using `enum`: ValueEnumOf[V, E], cls: ClassTag[V]): Unmarshaller[V, E] = - valueEnumUnmarshaller - - given kebsIntValueEnumFromStringUnmarshaller[E <: ValueEnum[Int] with Enum](using ev: ValueEnumOf[Int, E]): FromStringUnmarshaller[E] = - intFromStringUnmarshaller andThen valueEnumUnmarshaller - - given kebsLongValueEnumFromStringUnmarshaller[E <: ValueEnum[Long] with Enum](using ev: ValueEnumOf[Long, E]): FromStringUnmarshaller[E] = - longFromStringUnmarshaller andThen valueEnumUnmarshaller - - given kebsShortValueEnumFromStringUnmarshaller[E <: ValueEnum[Short] with Enum](using ev: ValueEnumOf[Short, E]): FromStringUnmarshaller[E] = - shortFromStringUnmarshaller andThen valueEnumUnmarshaller - - given kebsByteValueEnumFromStringUnmarshaller[E <: ValueEnum[Byte] with Enum](using ev: ValueEnumOf[Byte, E]): FromStringUnmarshaller[E] = - byteFromStringUnmarshaller andThen valueEnumUnmarshaller -} - -trait KebsEnumUnmarshallers extends ValueEnumUnmarshallers {} diff --git a/http4s-stir/src/main/scala/pl/iterators/kebs/matchers/package.scala b/pekko-http/src/main/scala/pl/iterators/kebs/pekkohttp/matchers/package.scala similarity index 56% rename from http4s-stir/src/main/scala/pl/iterators/kebs/matchers/package.scala rename to pekko-http/src/main/scala/pl/iterators/kebs/pekkohttp/matchers/package.scala index 610a2da7..3af3236b 100644 --- a/http4s-stir/src/main/scala/pl/iterators/kebs/matchers/package.scala +++ b/pekko-http/src/main/scala/pl/iterators/kebs/pekkohttp/matchers/package.scala @@ -1,3 +1,3 @@ -package pl.iterators.kebs +package pl.iterators.kebs.pekkohttp package object matchers extends KebsMatchers diff --git a/pekko-http/src/main/scala/pl/iterators/kebs/unmarshallers/KebsUnmarshallers.scala b/pekko-http/src/main/scala/pl/iterators/kebs/pekkohttp/unmarshallers/KebsUnmarshallers.scala similarity index 62% rename from pekko-http/src/main/scala/pl/iterators/kebs/unmarshallers/KebsUnmarshallers.scala rename to pekko-http/src/main/scala/pl/iterators/kebs/pekkohttp/unmarshallers/KebsUnmarshallers.scala index 696051a8..06270b6e 100644 --- a/pekko-http/src/main/scala/pl/iterators/kebs/unmarshallers/KebsUnmarshallers.scala +++ b/pekko-http/src/main/scala/pl/iterators/kebs/pekkohttp/unmarshallers/KebsUnmarshallers.scala @@ -1,12 +1,12 @@ -package pl.iterators.kebs.unmarshallers +package pl.iterators.kebs.pekkohttp.unmarshallers import org.apache.pekko.http.scaladsl.unmarshalling.{FromStringUnmarshaller, Unmarshaller} -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.macros.CaseClass1Rep +import pl.iterators.kebs.core.instances.InstanceConverter +import pl.iterators.kebs.core.macros.{CaseClass1ToValueClass, ValueClassLike} -trait KebsUnmarshallers extends LowPriorityKebsUnmarshallers { +trait KebsUnmarshallers extends LowPriorityKebsUnmarshallers with CaseClass1ToValueClass { @inline - implicit def kebsFromStringUnmarshaller[A, B](implicit rep: CaseClass1Rep[B, A], + implicit def kebsFromStringUnmarshaller[A, B](implicit rep: ValueClassLike[B, A], fsu: FromStringUnmarshaller[A]): FromStringUnmarshaller[B] = fsu andThen kebsUnmarshaller(rep) @@ -22,6 +22,6 @@ trait LowPriorityKebsUnmarshallers { implicit def kebsInstancesUnmarshaller[A, B](implicit ico: InstanceConverter[B, A]): Unmarshaller[A, B] = Unmarshaller.strict[A, B](ico.decode) - implicit def kebsUnmarshaller[A, B](implicit rep: CaseClass1Rep[B, A]): Unmarshaller[A, B] = + implicit def kebsUnmarshaller[A, B](implicit rep: ValueClassLike[B, A]): Unmarshaller[A, B] = Unmarshaller.strict[A, B](rep.apply) } \ No newline at end of file diff --git a/pekko-http/src/main/scala/pl/iterators/kebs/unmarshallers/package.scala b/pekko-http/src/main/scala/pl/iterators/kebs/pekkohttp/unmarshallers/package.scala similarity index 60% rename from pekko-http/src/main/scala/pl/iterators/kebs/unmarshallers/package.scala rename to pekko-http/src/main/scala/pl/iterators/kebs/pekkohttp/unmarshallers/package.scala index 6fd93de3..473504c9 100644 --- a/pekko-http/src/main/scala/pl/iterators/kebs/unmarshallers/package.scala +++ b/pekko-http/src/main/scala/pl/iterators/kebs/pekkohttp/unmarshallers/package.scala @@ -1,3 +1,3 @@ -package pl.iterators.kebs +package pl.iterators.kebs.pekkohttp package object unmarshallers extends KebsUnmarshallers diff --git a/pekko-http/src/test/scala-2/pl/iterators/kebs/PekkoHttpTagsDomain.scala b/pekko-http/src/test/scala-2/pl/iterators/kebs/pekkohttp/domain/PekkoHttpTagsDomain.scala similarity index 92% rename from pekko-http/src/test/scala-2/pl/iterators/kebs/PekkoHttpTagsDomain.scala rename to pekko-http/src/test/scala-2/pl/iterators/kebs/pekkohttp/domain/PekkoHttpTagsDomain.scala index f0661a1e..39e575f4 100644 --- a/pekko-http/src/test/scala-2/pl/iterators/kebs/PekkoHttpTagsDomain.scala +++ b/pekko-http/src/test/scala-2/pl/iterators/kebs/pekkohttp/domain/PekkoHttpTagsDomain.scala @@ -1,9 +1,10 @@ -package pl.iterators.kebs +package pl.iterators.kebs.pekkohttp.domain import enumeratum.values.{IntEnum, IntEnumEntry, StringEnum, StringEnumEntry} import enumeratum.{Enum, EnumEntry} import pl.iterators.kebs.tag.meta.tagged import pl.iterators.kebs.tagged._ +import pl.iterators.kebs.core.enums.ValueEnumLikeEntry import java.net.URI import java.util.UUID @@ -47,7 +48,7 @@ object Domain extends Tags { val values = findValues } - sealed abstract class LibraryItem(val value: Int) extends IntEnumEntry + sealed abstract class LibraryItem(val value: Int) extends IntEnumEntry with ValueEnumLikeEntry[Int] object LibraryItem extends IntEnum[LibraryItem] { case object Book extends LibraryItem(1) @@ -63,7 +64,7 @@ object Domain extends Tags { case class Blue(value: Int) case class Color(red: Red, green: Green, blue: Blue) - sealed abstract class ShirtSize(val value: String) extends StringEnumEntry + sealed abstract class ShirtSize(val value: String) extends StringEnumEntry with ValueEnumLikeEntry[String] object ShirtSize extends StringEnum[ShirtSize] { case object Small extends ShirtSize("S") case object Medium extends ShirtSize("M") diff --git a/pekko-http/src/test/scala-2/pl/iterators/kebs/matchers/PekkoHttpMatchersTests.scala b/pekko-http/src/test/scala-2/pl/iterators/kebs/pekkohttp/matchers/PekkoHttpMatchersTests.scala similarity index 81% rename from pekko-http/src/test/scala-2/pl/iterators/kebs/matchers/PekkoHttpMatchersTests.scala rename to pekko-http/src/test/scala-2/pl/iterators/kebs/pekkohttp/matchers/PekkoHttpMatchersTests.scala index 97ca9919..2e943ea3 100644 --- a/pekko-http/src/test/scala-2/pl/iterators/kebs/matchers/PekkoHttpMatchersTests.scala +++ b/pekko-http/src/test/scala-2/pl/iterators/kebs/pekkohttp/matchers/PekkoHttpMatchersTests.scala @@ -1,15 +1,15 @@ -package pl.iterators.kebs.matchers +package pl.iterators.kebs.pekkohttp.matchers import org.apache.pekko.http.scaladsl.server.Directives import org.apache.pekko.http.scaladsl.testkit.ScalatestRouteTest import org.scalatest.concurrent.ScalaFutures import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.Domain._ import pl.iterators.kebs.instances.net.URIString -import pl.iterators.kebs.instances.time.mixins.InstantEpochMilliLong import pl.iterators.kebs.instances.time.{DayOfWeekInt, ZonedDateTimeString} - +import pl.iterators.kebs.instances.time.mixins.InstantEpochMilliLong +import pl.iterators.kebs.pekkohttp.domain.Domain._ +import pl.iterators.kebs.enumeratum._ import java.net.URI import java.time.{DayOfWeek, Instant, ZonedDateTime} @@ -22,17 +22,18 @@ class PekkoHttpMatchersTests with ZonedDateTimeString with DayOfWeekInt with InstantEpochMilliLong - with URIString { + with URIString + with KebsEnumeratum { - test("No CaseClass1Rep implicits derived") { - import pl.iterators.kebs.macros.CaseClass1Rep + test("No ValueClassLike implicits derived") { + import pl.iterators.kebs.core.macros.ValueClassLike - "implicitly[CaseClass1Rep[DayOfWeek, Int]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Int, DayOfWeek]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Instant, Long]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Long, Instant]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[URI, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, URI]]" shouldNot typeCheck + "implicitly[ValueClassLike[DayOfWeek, Int]]" shouldNot typeCheck + "implicitly[ValueClassLike[Int, DayOfWeek]]" shouldNot typeCheck + "implicitly[ValueClassLike[Instant, Long]]" shouldNot typeCheck + "implicitly[ValueClassLike[Long, Instant]]" shouldNot typeCheck + "implicitly[ValueClassLike[URI, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, URI]]" shouldNot typeCheck } test("Extract String to ZonedDateTime") { diff --git a/pekko-http/src/test/scala-3/pl/iterators/kebs/unmarshallers/PekkoHttpUnmarshallersTests.scala b/pekko-http/src/test/scala-2/pl/iterators/kebs/pekkohttp/unmarshallers/PekkoHttpUnmarshallersTests.scala similarity index 86% rename from pekko-http/src/test/scala-3/pl/iterators/kebs/unmarshallers/PekkoHttpUnmarshallersTests.scala rename to pekko-http/src/test/scala-2/pl/iterators/kebs/pekkohttp/unmarshallers/PekkoHttpUnmarshallersTests.scala index 3fcf4e49..ce274ca7 100644 --- a/pekko-http/src/test/scala-3/pl/iterators/kebs/unmarshallers/PekkoHttpUnmarshallersTests.scala +++ b/pekko-http/src/test/scala-2/pl/iterators/kebs/pekkohttp/unmarshallers/PekkoHttpUnmarshallersTests.scala @@ -1,4 +1,4 @@ -package pl.iterators.kebs.unmarshallers +package pl.iterators.kebs.pekkohttp.unmarshallers import org.apache.pekko.http.scaladsl.model.FormData import org.apache.pekko.http.scaladsl.server.{Directives, MalformedQueryParamRejection} @@ -7,11 +7,11 @@ import org.apache.pekko.http.scaladsl.unmarshalling.Unmarshal import org.scalatest.concurrent.ScalaFutures import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.Domain._ 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.pekkohttp.domain.Domain._ +import pl.iterators.kebs.enumeratum.{KebsEnumeratum, KebsValueEnumeratum} +import pl.iterators.kebs.pekkohttp.unmarshallers.enums.KebsEnumUnmarshallers import java.time.{DayOfWeek, YearMonth} class PekkoHttpUnmarshallersTests @@ -24,17 +24,19 @@ class PekkoHttpUnmarshallersTests with KebsEnumUnmarshallers with URIString with YearMonthString - with DayOfWeekInt { + with DayOfWeekInt + with KebsEnumeratum + with KebsValueEnumeratum { - test("No CaseClass1Rep implicits derived") { - import pl.iterators.kebs.macros.CaseClass1Rep + test("No ValueClassLike implicits derived") { + import pl.iterators.kebs.core.macros.ValueClassLike - "implicitly[CaseClass1Rep[URI, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, URI]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[YearMonth, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, YearMonth]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[DayOfWeek, Int]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Int, DayOfWeek]]" shouldNot typeCheck + "implicitly[ValueClassLike[URI, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, URI]]" shouldNot typeCheck + "implicitly[ValueClassLike[YearMonth, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, YearMonth]]" shouldNot typeCheck + "implicitly[ValueClassLike[DayOfWeek, Int]]" shouldNot typeCheck + "implicitly[ValueClassLike[Int, DayOfWeek]]" shouldNot typeCheck } test("Unmarshal") { @@ -119,11 +121,13 @@ class PekkoHttpUnmarshallersTests test("Case class extraction") { val route = path("color") { - parameters(Symbol("red").as[Red], Symbol("green").as[Green], Symbol("blue").as[Blue]).as(Color.apply) { color => + parameters(Symbol("red").as[Red], Symbol("green").as[Green], Symbol("blue").as[Blue]).as(Color) { color => complete(color.toString) } } - Get("/color?red=1&green=2&blue=3") ~> route ~> check { responseAs[String] shouldEqual "Color(Red(1),Green(2),Blue(3))" } + Get("/color?red=1&green=2&blue=3") ~> route ~> check { + responseAs[String] shouldEqual "Color(Red(1),Green(2),Blue(3))" + } } test("Unmarshalling instances parameter") { diff --git a/pekko-http/src/test/scala-3/pl/iterators/kebs/PekkoHttpTagsDomain.scala b/pekko-http/src/test/scala-3/pl/iterators/kebs/pekkohttp/domain/PekkoHttpTagsDomain.scala similarity index 80% rename from pekko-http/src/test/scala-3/pl/iterators/kebs/PekkoHttpTagsDomain.scala rename to pekko-http/src/test/scala-3/pl/iterators/kebs/pekkohttp/domain/PekkoHttpTagsDomain.scala index 39701de2..3c274c86 100644 --- a/pekko-http/src/test/scala-3/pl/iterators/kebs/PekkoHttpTagsDomain.scala +++ b/pekko-http/src/test/scala-3/pl/iterators/kebs/pekkohttp/domain/PekkoHttpTagsDomain.scala @@ -1,10 +1,11 @@ -package pl.iterators.kebs +package pl.iterators.kebs.pekkohttp.domain import pl.iterators.kebs.opaque.Opaque import java.net.URI import java.util.UUID -import pl.iterators.kebs.enums.ValueEnum + +import pl.iterators.kebs.core.enums.ValueEnumLikeEntry object Domain { opaque type TestTaggedUri = URI @@ -24,7 +25,7 @@ object Domain { } - enum LibraryItem(val value: Int) extends ValueEnum[Int] { + enum LibraryItem(val value: Int) extends ValueEnumLikeEntry[Int] { case Book extends LibraryItem(1) case Movie extends LibraryItem(2) case Magazine extends LibraryItem(3) @@ -36,7 +37,7 @@ object Domain { case class Blue(value: Int) case class Color(red: Red, green: Green, blue: Blue) - enum ShirtSize(val value: String) extends ValueEnum[String] { + enum ShirtSize(val value: String) extends ValueEnumLikeEntry[String] { case Small extends ShirtSize("S") case Medium extends ShirtSize("M") case Large extends ShirtSize("L") diff --git a/pekko-http/src/test/scala-3/pl/iterators/kebs/matchers/PekkoHttpMatchersTests.scala b/pekko-http/src/test/scala-3/pl/iterators/kebs/pekkohttp/matchers/PekkoHttpMatchersTests.scala similarity index 77% rename from pekko-http/src/test/scala-3/pl/iterators/kebs/matchers/PekkoHttpMatchersTests.scala rename to pekko-http/src/test/scala-3/pl/iterators/kebs/pekkohttp/matchers/PekkoHttpMatchersTests.scala index 3ceb6c95..dbb82c8b 100644 --- a/pekko-http/src/test/scala-3/pl/iterators/kebs/matchers/PekkoHttpMatchersTests.scala +++ b/pekko-http/src/test/scala-3/pl/iterators/kebs/pekkohttp/matchers/PekkoHttpMatchersTests.scala @@ -1,18 +1,20 @@ -package pl.iterators.kebs.matchers +package pl.iterators.kebs.pekkohttp.matchers import org.apache.pekko.http.scaladsl.server.Directives import org.apache.pekko.http.scaladsl.testkit.ScalatestRouteTest import org.scalatest.concurrent.ScalaFutures import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.Domain._ import pl.iterators.kebs.instances.net.URIString -import pl.iterators.kebs.instances.time.mixins.InstantEpochMilliLong import pl.iterators.kebs.instances.time.{DayOfWeekInt, ZonedDateTimeString} +import pl.iterators.kebs.instances.time.mixins.InstantEpochMilliLong +import pl.iterators.kebs.pekkohttp.domain.Domain._ import java.net.URI import java.time.{DayOfWeek, Instant, ZonedDateTime} +import pl.iterators.kebs.enums.KebsEnum + class PekkoHttpMatchersTests extends AnyFunSuite with Matchers @@ -22,17 +24,16 @@ class PekkoHttpMatchersTests with ZonedDateTimeString with DayOfWeekInt with InstantEpochMilliLong - with URIString { - - test("No CaseClass1Rep implicits derived") { - import pl.iterators.kebs.macros.CaseClass1Rep + with URIString + with KebsEnum { - "implicitly[CaseClass1Rep[DayOfWeek, Int]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Int, DayOfWeek]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Instant, Long]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Long, Instant]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[URI, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, URI]]" shouldNot typeCheck + test("No ValueClassLike implicits derived") { + "implicitly[ValueClassLike[DayOfWeek, Int]]" shouldNot typeCheck + "implicitly[ValueClassLike[Int, DayOfWeek]]" shouldNot typeCheck + "implicitly[ValueClassLike[Instant, Long]]" shouldNot typeCheck + "implicitly[ValueClassLike[Long, Instant]]" shouldNot typeCheck + "implicitly[ValueClassLike[URI, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, URI]]" shouldNot typeCheck } test("Extract String to ZonedDateTime") { diff --git a/pekko-http/src/test/scala-2/pl/iterators/kebs/unmarshallers/PekkoHttpUnmarshallersTests.scala b/pekko-http/src/test/scala-3/pl/iterators/kebs/pekkohttp/unmarshallers/PekkoHttpUnmarshallersTests.scala similarity index 82% rename from pekko-http/src/test/scala-2/pl/iterators/kebs/unmarshallers/PekkoHttpUnmarshallersTests.scala rename to pekko-http/src/test/scala-3/pl/iterators/kebs/pekkohttp/unmarshallers/PekkoHttpUnmarshallersTests.scala index e65f8c52..63446943 100644 --- a/pekko-http/src/test/scala-2/pl/iterators/kebs/unmarshallers/PekkoHttpUnmarshallersTests.scala +++ b/pekko-http/src/test/scala-3/pl/iterators/kebs/pekkohttp/unmarshallers/PekkoHttpUnmarshallersTests.scala @@ -1,21 +1,26 @@ -package pl.iterators.kebs.unmarshallers +package pl.iterators.kebs.pekkohttp.unmarshallers +import org.apache.pekko.http.scaladsl.common.ToNameReceptacleEnhancements import org.apache.pekko.http.scaladsl.model.FormData import org.apache.pekko.http.scaladsl.server.{Directives, MalformedQueryParamRejection} import org.apache.pekko.http.scaladsl.testkit.ScalatestRouteTest import org.apache.pekko.http.scaladsl.unmarshalling.Unmarshal +import org.apache.pekko.http.scaladsl.unmarshalling.Unmarshaller import org.scalatest.concurrent.ScalaFutures import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.Domain._ -import pl.iterators.kebs.instances.net.URIString -import pl.iterators.kebs.instances.time.{DayOfWeekInt, YearMonthString} -import pl.iterators.kebs.unmarshallers.enums.KebsEnumUnmarshallers import java.time.{DayOfWeek, YearMonth} +import pl.iterators.kebs.pekkohttp.domain.Domain._ +import pl.iterators.kebs.instances.net.URIString +import pl.iterators.kebs.instances.time.{DayOfWeekInt, YearMonthString} +import pl.iterators.kebs.pekkohttp.unmarshallers.enums.KebsEnumUnmarshallers +import pl.iterators.kebs.core.macros.CaseClass1ToValueClass +import pl.iterators.kebs.enums.{KebsEnum, KebsValueEnum} + class PekkoHttpUnmarshallersTests - extends AnyFunSuite + extends AnyFunSuite with Matchers with ScalatestRouteTest with ScalaFutures @@ -24,17 +29,18 @@ class PekkoHttpUnmarshallersTests with KebsEnumUnmarshallers with URIString with YearMonthString - with DayOfWeekInt { - - test("No CaseClass1Rep implicits derived") { - import pl.iterators.kebs.macros.CaseClass1Rep + with DayOfWeekInt + with KebsEnum + with KebsValueEnum + with CaseClass1ToValueClass { - "implicitly[CaseClass1Rep[URI, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, URI]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[YearMonth, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, YearMonth]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[DayOfWeek, Int]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Int, DayOfWeek]]" shouldNot typeCheck + test("No ValueClassLike implicits derived") { + "implicitly[ValueClassLike[URI, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, URI]]" shouldNot typeCheck + "implicitly[ValueClassLike[YearMonth, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, YearMonth]]" shouldNot typeCheck + "implicitly[ValueClassLike[DayOfWeek, Int]]" shouldNot typeCheck + "implicitly[ValueClassLike[Int, DayOfWeek]]" shouldNot typeCheck } test("Unmarshal") { @@ -56,6 +62,7 @@ class PekkoHttpUnmarshallersTests } test("Unmarshal value enum") { + Unmarshal(1).to[LibraryItem] Unmarshal(3).to[LibraryItem].futureValue shouldBe LibraryItem.Magazine Unmarshal(5).to[LibraryItem].failed.futureValue shouldBe a[IllegalArgumentException] } @@ -99,13 +106,13 @@ class PekkoHttpUnmarshallersTests } Get("/?greeting=blah") ~> testRoute ~> check { rejection shouldEqual MalformedQueryParamRejection("greeting", - "Invalid value 'blah'. Expected one of: Hello, GoodBye, Hi, Bye", - None) + "Invalid value 'blah'. Expected one of: Hello, GoodBye, Hi, Bye", + None) } } test("Unmarshalling value enum parameter") { - val testRoute = parameters(Symbol("libraryItem").as[LibraryItem]) { item => + val testRoute = parameters(ToNameReceptacleEnhancements._symbol2NR(Symbol("libraryItem")).as[LibraryItem]) { item => complete(item.toString) } Get("/?libraryItem=1") ~> testRoute ~> check { @@ -119,7 +126,7 @@ class PekkoHttpUnmarshallersTests test("Case class extraction") { val route = path("color") { - parameters(Symbol("red").as[Red], Symbol("green").as[Green], Symbol("blue").as[Blue]).as(Color) { color => + parameters(Symbol("red").as[Red], Symbol("green").as[Green], Symbol("blue").as[Blue]).as(Color.apply) { color => complete(color.toString) } } diff --git a/play-json/src/main/scala/pl/iterators/kebs/json/KebsPlay.scala b/play-json/src/main/scala/pl/iterators/kebs/json/KebsPlay.scala index b2dcb4fc..5993d3ad 100644 --- a/play-json/src/main/scala/pl/iterators/kebs/json/KebsPlay.scala +++ b/play-json/src/main/scala/pl/iterators/kebs/json/KebsPlay.scala @@ -1,12 +1,12 @@ package pl.iterators.kebs.json -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.macros.CaseClass1Rep +import pl.iterators.kebs.core.instances.InstanceConverter +import pl.iterators.kebs.core.macros.{CaseClass1ToValueClass, ValueClassLike} import play.api.libs.json._ -trait KebsPlay { - implicit def flatReads[T, A](implicit rep: CaseClass1Rep[T, A], reads: Reads[A]): Reads[T] = reads.map(rep.apply) - implicit def flatWrites[T, B](implicit rep: CaseClass1Rep[T, B], writes: Writes[B]): Writes[T] = +trait KebsPlay extends CaseClass1ToValueClass { + implicit def flatReads[T, A](implicit rep: ValueClassLike[T, A], reads: Reads[A]): Reads[T] = reads.map(rep.apply) + implicit def flatWrites[T, B](implicit rep: ValueClassLike[T, B], writes: Writes[B]): Writes[T] = Writes((obj: T) => writes.writes(rep.unapply(obj))) implicit def instanceConverterReads[T, A](implicit rep: InstanceConverter[T, A], reads: Reads[A]): Reads[T] = reads.map(rep.decode) diff --git a/play-json/src/test/scala/PlayJsonFormatTests.scala b/play-json/src/test/scala/pl/iterators/kebs/json/PlayJsonFormatTests.scala similarity index 97% rename from play-json/src/test/scala/PlayJsonFormatTests.scala rename to play-json/src/test/scala/pl/iterators/kebs/json/PlayJsonFormatTests.scala index ad16ef61..956b111a 100644 --- a/play-json/src/test/scala/PlayJsonFormatTests.scala +++ b/play-json/src/test/scala/pl/iterators/kebs/json/PlayJsonFormatTests.scala @@ -1,11 +1,12 @@ -import java.util.UUID +package pl.iterators.kebs.json -import play.api.libs.json._ import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers +import play.api.libs.json._ + +import java.util.UUID class PlayJsonFormatTests extends AnyFunSuite with Matchers { - import pl.iterators.kebs.json._ case class C(i: Int) case class D(s: String) diff --git a/play-json/src/test/scala/instances/NetInstancesTests.scala b/play-json/src/test/scala/pl/iterators/kebs/json/instances/NetInstancesTests.scala similarity index 71% rename from play-json/src/test/scala/instances/NetInstancesTests.scala rename to play-json/src/test/scala/pl/iterators/kebs/json/instances/NetInstancesTests.scala index 85ae5e06..98bb324d 100644 --- a/play-json/src/test/scala/instances/NetInstancesTests.scala +++ b/play-json/src/test/scala/pl/iterators/kebs/json/instances/NetInstancesTests.scala @@ -1,8 +1,8 @@ -package instances +package pl.iterators.kebs.json.instances import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.instances.InstanceConverter.DecodeErrorException +import pl.iterators.kebs.core.instances.InstanceConverter.DecodeErrorException import pl.iterators.kebs.instances.net.URIString import play.api.libs.json.{Format, JsString, JsSuccess} @@ -27,9 +27,9 @@ class NetInstancesTests extends AnyFunSuite with Matchers with URIString { assertThrows[DecodeErrorException](jf.reads(JsString(value))) } - test("No CaseClass1Rep implicits derived") { + test("No ValueClassLike implicits derived") { - "implicitly[CaseClass1Rep[URI, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, URI]]" shouldNot typeCheck + "implicitly[ValueClassLike[URI, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, URI]]" shouldNot typeCheck } } diff --git a/play-json/src/test/scala/instances/TimeInstancesMixinTests.scala b/play-json/src/test/scala/pl/iterators/kebs/json/instances/TimeInstancesMixinTests.scala similarity index 81% rename from play-json/src/test/scala/instances/TimeInstancesMixinTests.scala rename to play-json/src/test/scala/pl/iterators/kebs/json/instances/TimeInstancesMixinTests.scala index ab57e2c3..b30f0d84 100644 --- a/play-json/src/test/scala/instances/TimeInstancesMixinTests.scala +++ b/play-json/src/test/scala/pl/iterators/kebs/json/instances/TimeInstancesMixinTests.scala @@ -1,11 +1,11 @@ -package instances +package pl.iterators.kebs.json.instances import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.instances.InstanceConverter +import pl.iterators.kebs.instances.TimeInstances import pl.iterators.kebs.instances.time.LocalDateTimeString import pl.iterators.kebs.instances.time.mixins.{DurationNanosLong, InstantEpochMilliLong} -import pl.iterators.kebs.instances.TimeInstances +import pl.iterators.kebs.core.instances.InstanceConverter import play.api.libs.json.{Format, JsNumber, JsString, JsSuccess} import java.time._ @@ -18,8 +18,8 @@ class TimeInstancesMixinTests extends AnyFunSuite with Matchers { object TimeInstancesProtocol extends InstantEpochMilliLong import TimeInstancesProtocol._ - "implicitly[CaseClass1Rep[Instant, Long]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Long, Instant]]" shouldNot typeCheck + "implicitly[ValueClassLike[Instant, Long]]" shouldNot typeCheck + "implicitly[ValueClassLike[Long, Instant]]" shouldNot typeCheck val jf = implicitly[Format[Instant]] val value = 123456789 @@ -33,10 +33,10 @@ class TimeInstancesMixinTests extends AnyFunSuite with Matchers { object TimeInstancesProtocol extends DurationNanosLong with InstantEpochMilliLong import TimeInstancesProtocol._ - "implicitly[CaseClass1Rep[Instant, Long]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Long, Instant]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Duration, Long]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Long, Duration]]" shouldNot typeCheck + "implicitly[ValueClassLike[Instant, Long]]" shouldNot typeCheck + "implicitly[ValueClassLike[Long, Instant]]" shouldNot typeCheck + "implicitly[ValueClassLike[Duration, Long]]" shouldNot typeCheck + "implicitly[ValueClassLike[Long, Duration]]" shouldNot typeCheck val jf_duration = implicitly[Format[Duration]] val value_duration = 123456789 @@ -62,8 +62,8 @@ class TimeInstancesMixinTests extends AnyFunSuite with Matchers { } import TimeInstancesProtocol._ - "implicitly[CaseClass1Rep[LocalDateTime, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, LocalDateTime]]" shouldNot typeCheck + "implicitly[ValueClassLike[LocalDateTime, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, LocalDateTime]]" shouldNot typeCheck val jf = implicitly[Format[LocalDateTime]] val value = "2007/12/03 10:30" @@ -95,8 +95,8 @@ class TimeInstancesMixinTests extends AnyFunSuite with Matchers { } import TimeInstancesProtocol._ - "implicitly[CaseClass1Rep[LocalDateTime, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, LocalDateTime]]" shouldNot typeCheck + "implicitly[ValueClassLike[LocalDateTime, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, LocalDateTime]]" shouldNot typeCheck val jf = implicitly[Format[LocalDateTime]] val value = "2007/12/03 10:30" diff --git a/play-json/src/test/scala/instances/TimeInstancesTests.scala b/play-json/src/test/scala/pl/iterators/kebs/json/instances/TimeInstancesTests.scala similarity index 76% rename from play-json/src/test/scala/instances/TimeInstancesTests.scala rename to play-json/src/test/scala/pl/iterators/kebs/json/instances/TimeInstancesTests.scala index 3f74aa00..56257847 100644 --- a/play-json/src/test/scala/instances/TimeInstancesTests.scala +++ b/play-json/src/test/scala/pl/iterators/kebs/json/instances/TimeInstancesTests.scala @@ -1,8 +1,8 @@ -package instances +package pl.iterators.kebs.json.instances import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.instances.InstanceConverter.DecodeErrorException +import pl.iterators.kebs.core.instances.InstanceConverter.DecodeErrorException import pl.iterators.kebs.instances.TimeInstances import play.api.libs.json.{Format, JsNumber, JsString, JsSuccess} @@ -10,40 +10,40 @@ import java.time._ class TimeInstancesTests extends AnyFunSuite with Matchers with TimeInstances { import pl.iterators.kebs.json._ - test("No CaseClass1Rep implicits derived") { - - "implicitly[CaseClass1Rep[DayOfWeek, Int]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Int, DayOfWeek]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Duration, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, Duration]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Instant, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, Instant]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[LocalDate, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, LocalDate]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[LocalDateTime, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, LocalDateTime]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[LocalTime, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, LocalTime]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Month, Int]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Int, Month]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[MonthDay, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, MonthDay]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[OffsetDateTime, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, OffsetDateTime]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[OffsetTime, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, OffsetTime]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Period, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, Period]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Year, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, Year]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[YearMonth, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, YearMonth]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[ZoneId, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, ZoneId]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[ZoneOffset, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, ZoneOffset]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[ZonedDateTime, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, ZonedDateTime]]" shouldNot typeCheck + test("No ValueClassLike implicits derived") { + + "implicitly[ValueClassLike[DayOfWeek, Int]]" shouldNot typeCheck + "implicitly[ValueClassLike[Int, DayOfWeek]]" shouldNot typeCheck + "implicitly[ValueClassLike[Duration, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, Duration]]" shouldNot typeCheck + "implicitly[ValueClassLike[Instant, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, Instant]]" shouldNot typeCheck + "implicitly[ValueClassLike[LocalDate, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, LocalDate]]" shouldNot typeCheck + "implicitly[ValueClassLike[LocalDateTime, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, LocalDateTime]]" shouldNot typeCheck + "implicitly[ValueClassLike[LocalTime, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, LocalTime]]" shouldNot typeCheck + "implicitly[ValueClassLike[Month, Int]]" shouldNot typeCheck + "implicitly[ValueClassLike[Int, Month]]" shouldNot typeCheck + "implicitly[ValueClassLike[MonthDay, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, MonthDay]]" shouldNot typeCheck + "implicitly[ValueClassLike[OffsetDateTime, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, OffsetDateTime]]" shouldNot typeCheck + "implicitly[ValueClassLike[OffsetTime, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, OffsetTime]]" shouldNot typeCheck + "implicitly[ValueClassLike[Period, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, Period]]" shouldNot typeCheck + "implicitly[ValueClassLike[Year, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, Year]]" shouldNot typeCheck + "implicitly[ValueClassLike[YearMonth, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, YearMonth]]" shouldNot typeCheck + "implicitly[ValueClassLike[ZoneId, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, ZoneId]]" shouldNot typeCheck + "implicitly[ValueClassLike[ZoneOffset, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, ZoneOffset]]" shouldNot typeCheck + "implicitly[ValueClassLike[ZonedDateTime, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, ZonedDateTime]]" shouldNot typeCheck } test("DayOfWeek standard format") { diff --git a/play-json/src/test/scala/instances/UtilInstancesTests.scala b/play-json/src/test/scala/pl/iterators/kebs/json/instances/UtilInstancesTests.scala similarity index 72% rename from play-json/src/test/scala/instances/UtilInstancesTests.scala rename to play-json/src/test/scala/pl/iterators/kebs/json/instances/UtilInstancesTests.scala index c59cfa7e..9f9902e3 100644 --- a/play-json/src/test/scala/instances/UtilInstancesTests.scala +++ b/play-json/src/test/scala/pl/iterators/kebs/json/instances/UtilInstancesTests.scala @@ -1,8 +1,8 @@ -package instances +package pl.iterators.kebs.json.instances import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.instances.InstanceConverter.DecodeErrorException +import pl.iterators.kebs.core.instances.InstanceConverter.DecodeErrorException import pl.iterators.kebs.instances.UtilInstances import play.api.libs.json.{Format, JsString, JsSuccess} @@ -10,14 +10,14 @@ import java.util.{Currency, Locale, UUID} class UtilInstancesTests extends AnyFunSuite with Matchers with UtilInstances { import pl.iterators.kebs.json._ - test("No CaseClass1Rep implicits derived") { - - "implicitly[CaseClass1Rep[Currency, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, Currency]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Locale, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, Locale]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[UUID, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, UUID]]" shouldNot typeCheck + test("No ValueClassLike implicits derived") { + + "implicitly[ValueClassLike[Currency, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, Currency]]" shouldNot typeCheck + "implicitly[ValueClassLike[Locale, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, Locale]]" shouldNot typeCheck + "implicitly[ValueClassLike[UUID, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, UUID]]" shouldNot typeCheck } test("Currency standard format") { diff --git a/project/build.properties b/project/build.properties index f2f13471..ad302606 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version = 1.9.9 \ No newline at end of file +sbt.version = 1.10.1 \ No newline at end of file diff --git a/scalacheck/src/main/scala-2/pl/iterators/kebs/scalacheck/CommonArbitrarySupport.scala b/scalacheck/src/main/scala-2/pl/iterators/kebs/scalacheck/CommonArbitrarySupport.scala index 017a1cb3..857f9f17 100644 --- a/scalacheck/src/main/scala-2/pl/iterators/kebs/scalacheck/CommonArbitrarySupport.scala +++ b/scalacheck/src/main/scala-2/pl/iterators/kebs/scalacheck/CommonArbitrarySupport.scala @@ -1,14 +1,13 @@ package pl.iterators.kebs.scalacheck -import enumeratum.{ScalacheckInstances => EnumScalacheckInstances} +import enumeratum.ScalacheckInstances import org.scalacheck.Arbitrary -import pl.iterators.kebs.macros.CaseClass1Rep +import pl.iterators.kebs.core.macros.ValueClassLike -trait CommonArbitrarySupport extends EnumScalacheckInstances with ScalacheckInstancesSupport { - - implicit def caseClass1RepArbitraryPredef[T, A]( - implicit rep: CaseClass1Rep[T, A], - arb: Arbitrary[A] - ): Arbitrary[T] = Arbitrary(arb.arbitrary.map(rep.apply(_))) - -} +trait CommonArbitrarySupport extends ScalacheckInstances with ScalacheckInstancesSupport { + implicit def valueClassLikeArbitraryPredef[T, A]( + implicit rep: ValueClassLike[T, A], + arbitrary: Arbitrary[A] + ): Arbitrary[T] = + Arbitrary(arbitrary.arbitrary.map(rep.apply(_))) +} \ No newline at end of file diff --git a/scalacheck/src/main/scala-2/pl/iterators/kebs/scalacheck/macros/KebsScalacheckGeneratorsMacro.scala b/scalacheck/src/main/scala-2/pl/iterators/kebs/scalacheck/macros/KebsScalacheckGeneratorsMacro.scala index 9b3b123d..8b7ee5e2 100644 --- a/scalacheck/src/main/scala-2/pl/iterators/kebs/scalacheck/macros/KebsScalacheckGeneratorsMacro.scala +++ b/scalacheck/src/main/scala-2/pl/iterators/kebs/scalacheck/macros/KebsScalacheckGeneratorsMacro.scala @@ -1,7 +1,6 @@ package pl.iterators.kebs.scalacheck.macros -import pl.iterators.kebs.macros.MacroUtils -import scala.language.experimental.macros +import pl.iterators.kebs.core.macros.MacroUtils import scala.reflect.macros._ import pl.iterators.kebs.scalacheck._ diff --git a/scalacheck/src/main/scala-3/pl/iterators/kebs/scalacheck/CommonArbitrarySupport.scala b/scalacheck/src/main/scala-3/pl/iterators/kebs/scalacheck/CommonArbitrarySupport.scala index b15c7153..26538d5e 100644 --- a/scalacheck/src/main/scala-3/pl/iterators/kebs/scalacheck/CommonArbitrarySupport.scala +++ b/scalacheck/src/main/scala-3/pl/iterators/kebs/scalacheck/CommonArbitrarySupport.scala @@ -1,20 +1,14 @@ package pl.iterators.kebs.scalacheck -import org.scalacheck.{Arbitrary, Gen} -import pl.iterators.kebs.macros.CaseClass1Rep +import org.scalacheck.Arbitrary +import pl.iterators.kebs.core.macros.ValueClassLike -import java.net.{URI, URL} -import java.time.temporal.ChronoUnit -import java.time._ -import java.util.concurrent.TimeUnit -import scala.reflect.ClassTag -import scala.util.Random -import io.github.martinhh.derived.scalacheck.given -import enumeratum.{ScalacheckInstances => EnumScalacheckInstances} -trait CommonArbitrarySupport extends EnumScalacheckInstances { - implicit def caseClass1RepArbitraryPredef[T, A]( - implicit rep: CaseClass1Rep[T, A], - arbitrary: Arbitrary[A] +import enumeratum.ScalacheckInstances + +trait CommonArbitrarySupport extends ScalacheckInstances { + implicit def ValueClassLikeArbitraryPredef[T, A]( + implicit rep: ValueClassLike[T, A], + arbitrary: Arbitrary[A] ): Arbitrary[T] = Arbitrary(arbitrary.arbitrary.map(rep.apply(_))) } diff --git a/scalacheck/src/main/scala-3/pl/iterators/kebs/scalacheck/KebsScalacheckGenerators.scala b/scalacheck/src/main/scala-3/pl/iterators/kebs/scalacheck/KebsScalacheckGenerators.scala index 135b1cdf..e6567dba 100644 --- a/scalacheck/src/main/scala-3/pl/iterators/kebs/scalacheck/KebsScalacheckGenerators.scala +++ b/scalacheck/src/main/scala-3/pl/iterators/kebs/scalacheck/KebsScalacheckGenerators.scala @@ -1,9 +1,8 @@ package pl.iterators.kebs.scalacheck import pl.iterators.kebs.scalacheck.macros.KebsScalacheckGeneratorsMacro -import scala.quoted.Quotes import scala.deriving.Mirror -import pl.iterators.kebs.scalacheck.AllGenerators + trait KebsScalacheckGenerators { inline implicit final def allGenerators[T](using inline m: Mirror.Of[T]): AllGenerators[T] = KebsScalacheckGeneratorsMacro.materializeGenerators[T] } diff --git a/scalacheck/src/main/scala-3/pl/iterators/kebs/scalacheck/macros/KebsScalacheckGeneratorsMacro.scala b/scalacheck/src/main/scala-3/pl/iterators/kebs/scalacheck/macros/KebsScalacheckGeneratorsMacro.scala index c627aaf8..082477cb 100644 --- a/scalacheck/src/main/scala-3/pl/iterators/kebs/scalacheck/macros/KebsScalacheckGeneratorsMacro.scala +++ b/scalacheck/src/main/scala-3/pl/iterators/kebs/scalacheck/macros/KebsScalacheckGeneratorsMacro.scala @@ -1,7 +1,6 @@ package pl.iterators.kebs.scalacheck.macros import pl.iterators.kebs.scalacheck._ -import scala.quoted.* import org.scalacheck.Arbitrary import scala.deriving.Mirror import io.github.martinhh.derived.scalacheck.deriveArbitrary diff --git a/scalacheck/src/test/scala-2/pl/iterators/kebs/scalacheck/AnyValGeneratorsTests.scala b/scalacheck/src/test/scala-2/pl/iterators/kebs/scalacheck/AnyValGeneratorsTests.scala index c3ff953d..b90f2906 100644 --- a/scalacheck/src/test/scala-2/pl/iterators/kebs/scalacheck/AnyValGeneratorsTests.scala +++ b/scalacheck/src/test/scala-2/pl/iterators/kebs/scalacheck/AnyValGeneratorsTests.scala @@ -1,18 +1,8 @@ +package pl.iterators.kebs.scalacheck import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.scalacheck._ -import java.net.{URI, URL} -import java.time.{Duration, Instant, LocalDate, LocalDateTime, LocalTime, ZonedDateTime} - -case class WrappedInt(int: Int) -case class WrappedIntAnyVal(int: Int) extends AnyVal -case class BasicSample( - someNumber: Int, - someText: String, - wrappedNumber: WrappedInt, - wrappedNumberAnyVal: WrappedIntAnyVal, -) +import pl.iterators.kebs.scalacheck.model._ class AnyValGeneratorsTests extends AnyFunSuite with Matchers { diff --git a/scalacheck/src/test/scala-2/pl/iterators/kebs/scalacheck/model/model.scala b/scalacheck/src/test/scala-2/pl/iterators/kebs/scalacheck/model/model.scala new file mode 100644 index 00000000..83e4dd19 --- /dev/null +++ b/scalacheck/src/test/scala-2/pl/iterators/kebs/scalacheck/model/model.scala @@ -0,0 +1,12 @@ +package pl.iterators.kebs.scalacheck + +package object model { + case class WrappedInt(int: Int) +case class WrappedIntAnyVal(int: Int) extends AnyVal +case class BasicSample( + someNumber: Int, + someText: String, + wrappedNumber: WrappedInt, + wrappedNumberAnyVal: WrappedIntAnyVal, +) +} diff --git a/scalacheck/src/test/scala-3/OpaqueGeneratorsTests.scala b/scalacheck/src/test/scala-3/pl/iterators/kebs/scalacheck/OpaqueGeneratorsTests.scala similarity index 53% rename from scalacheck/src/test/scala-3/OpaqueGeneratorsTests.scala rename to scalacheck/src/test/scala-3/pl/iterators/kebs/scalacheck/OpaqueGeneratorsTests.scala index 34cfb61f..04bac6b5 100644 --- a/scalacheck/src/test/scala-3/OpaqueGeneratorsTests.scala +++ b/scalacheck/src/test/scala-3/pl/iterators/kebs/scalacheck/OpaqueGeneratorsTests.scala @@ -2,24 +2,7 @@ package pl.iterators.kebs.scalacheck import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import java.net.{URI, URL} -import java.time.{Duration, Instant, LocalDate, LocalDateTime, LocalTime, ZonedDateTime} -import pl.iterators.kebs.opaque.Opaque - - -case class WrappedInt(int: Int) - -opaque type OpaqueInt = Int -object OpaqueInt extends Opaque[OpaqueInt, Int] { - override def apply(value: Int) = value -} - -case class BasicSampleWithOpaque( - someNumber: Int, - someText: String, - wrappedNumber: WrappedInt, - opaqueInt: OpaqueInt -) +import pl.iterators.kebs.scalacheck.model._ class OpaqueGeneratorsTests extends AnyFunSuite with Matchers { diff --git a/scalacheck/src/test/scala-3/pl/iterators/kebs/scalacheck/model/model.scala b/scalacheck/src/test/scala-3/pl/iterators/kebs/scalacheck/model/model.scala new file mode 100644 index 00000000..da4cdbea --- /dev/null +++ b/scalacheck/src/test/scala-3/pl/iterators/kebs/scalacheck/model/model.scala @@ -0,0 +1,20 @@ +package pl.iterators.kebs.scalacheck + +import pl.iterators.kebs.opaque.Opaque + +package object model { + case class WrappedInt(int: Int) + + opaque type OpaqueInt = Int + object OpaqueInt extends Opaque[OpaqueInt, Int] { + override def apply(value: Int) = value +} + +case class BasicSampleWithOpaque( + someNumber: Int, + someText: String, + wrappedNumber: WrappedInt, + opaqueInt: OpaqueInt +) + +} diff --git a/slick/src/main/scala/pl/iterators/kebs/Kebs.scala b/slick/src/main/scala/pl/iterators/kebs/Kebs.scala deleted file mode 100644 index fdd135c2..00000000 --- a/slick/src/main/scala/pl/iterators/kebs/Kebs.scala +++ /dev/null @@ -1,174 +0,0 @@ -package pl.iterators.kebs - -import pl.iterators.kebs.hstore.KebsHStoreColumnExtensionMethods -import pl.iterators.kebs.instances.InstanceConverter -import pl.iterators.kebs.macros.CaseClass1Rep -import slick.ast.{BaseTypedType, NumericTypedType} -import slick.jdbc.JdbcType -import slick.lifted._ - -import scala.language.implicitConversions - -trait KebsColumnExtensionMethods { - implicit def stringValueColumnExt[CC](rep: Rep[CC])(implicit ev: CaseClass1Rep[CC, String]): StringColumnExtensionMethods[CC] = - new StringColumnExtensionMethods[CC](rep) - implicit def stringValueOptionColumnExt[CC](rep: Rep[Option[CC]])( - implicit ev: CaseClass1Rep[CC, String]): StringColumnExtensionMethods[Option[CC]] = new StringColumnExtensionMethods[Option[CC]](rep) - implicit def numericValueColumnExt[CC, B](rep: Rep[CC])( - implicit ev1: CaseClass1Rep[CC, B], - ev2: BaseTypedType[B] with NumericTypedType): BaseNumericColumnExtensionMethods[CC] = new BaseNumericColumnExtensionMethods[CC](rep) - implicit def numericValueOptionColumnExt[CC, B](rep: Rep[Option[CC]])( - implicit ev1: CaseClass1Rep[CC, B], - ev2: BaseTypedType[B] with NumericTypedType): OptionNumericColumnExtensionMethods[CC] = - new OptionNumericColumnExtensionMethods[CC](rep) - implicit def booleanValueColumnExt[CC](rep: Rep[CC])(implicit ev: CaseClass1Rep[CC, Boolean]): BooleanColumnExtensionMethods[CC] = - new BooleanColumnExtensionMethods[CC](rep) - implicit def booleanValueOptionColumnExt[CC](rep: Rep[Option[CC]])( - implicit ev: CaseClass1Rep[CC, Boolean]): BooleanColumnExtensionMethods[Option[CC]] = - new BooleanColumnExtensionMethods[Option[CC]](rep) - - implicit def hstoreColumnExt[KEY, VALUE](c: Rep[Map[KEY, VALUE]])( - implicit tm0: JdbcType[KEY], - tm1: JdbcType[VALUE], - tm2: JdbcType[List[KEY]], - tm3: JdbcType[List[VALUE]], - tm4: JdbcType[Map[KEY, VALUE]] - ): KebsHStoreColumnExtensionMethods[KEY, VALUE, Map[KEY, VALUE]] = - new KebsHStoreColumnExtensionMethods[KEY, VALUE, Map[KEY, VALUE]](c) - - @inline implicit def getCCOptionMapper2TT_1[B1, B2: BaseTypedType, BR, CC]( - implicit ev: CaseClass1Rep[CC, B1]): OptionMapper2[B1, B2, BR, CC, B2, BR] = - OptionMapper2.plain.asInstanceOf[OptionMapper2[B1, B2, BR, CC, B2, BR]] - @inline implicit def getCCOptionMapper2TT_2[B1, B2, BR, CC](implicit ev: CaseClass1Rep[CC, B2]): OptionMapper2[CC, CC, BR, CC, B2, BR] = - OptionMapper2.plain.asInstanceOf[OptionMapper2[CC, CC, BR, CC, B2, BR]] - @inline implicit def getCCOptionMapper2TO[B1, B2: BaseTypedType, BR, CC]( - implicit ev: CaseClass1Rep[CC, B1]): OptionMapper2[B1, B2, BR, CC, Option[B2], Option[BR]] = - OptionMapper2.option.asInstanceOf[OptionMapper2[B1, B2, BR, CC, Option[B2], Option[BR]]] - @inline implicit def getCCOptionMapper2OT[B1, B2: BaseTypedType, BR, CC]( - implicit ev: CaseClass1Rep[CC, B1]): OptionMapper2[B1, B2, BR, Option[CC], B2, Option[BR]] = - OptionMapper2.option.asInstanceOf[OptionMapper2[B1, B2, BR, Option[CC], B2, Option[BR]]] - @inline implicit def getCCOptionMapper2OO[B1, B2: BaseTypedType, BR, CC]( - implicit ev: CaseClass1Rep[CC, B1]): OptionMapper2[B1, B2, BR, Option[CC], Option[B2], Option[BR]] = - OptionMapper2.option.asInstanceOf[OptionMapper2[B1, B2, BR, Option[CC], Option[B2], Option[BR]]] -} - -trait Kebs extends KebsColumnExtensionMethods { - implicit def valueColumnType[CC, B](implicit rep1: CaseClass1Rep[CC, B]): Isomorphism[CC, B] = - new Isomorphism[CC, B](rep1.unapply, rep1.apply) - implicit def valueTransitionColumnType[CC, B](implicit ico: InstanceConverter[CC, B]): Isomorphism[CC, B] = - new Isomorphism[CC, B](ico.encode, ico.decode) - implicit def listValueColumnType[CC, B](implicit iso: Isomorphism[CC, B]): Isomorphism[List[CC], List[B]] = - new Isomorphism[List[CC], List[B]](_.map(iso.map), _.map(iso.comap)) - implicit def seqValueColumnType[CC, B](implicit iso: Isomorphism[CC, B]): Isomorphism[Seq[CC], List[B]] = { - new Isomorphism[Seq[CC], List[B]](_.map(iso.map).toList, _.map(iso.comap)) - } - implicit def mapValueColumnType[CC1, CC2, A, B]( - implicit iso1: Isomorphism[CC1, A], - iso2: Isomorphism[CC2, B] - ): Isomorphism[Map[CC1, CC2], Map[A, B]] = - new Isomorphism[Map[CC1, CC2], Map[A, B]]( - _.map { case (cc1, cc2) => (iso1.map(cc1), iso2.map(cc2)) }, - _.map { case (a, b) => (iso1.comap(a), iso2.comap(b)) } - ) - - private class StringMapIsomorphism[A](comap: String => A) - extends Isomorphism[Map[String, A], Map[String, String]]( - _.map { case (str, a) => (str, a.toString) }, - _.map { case (str1, str2) => (str1, comap(str2)) } - ) - implicit final val intMapValueColumnType: Isomorphism[Map[String, Int], Map[String, String]] = - new StringMapIsomorphism[Int](_.toInt) - implicit final val longMapValueColumnType: Isomorphism[Map[String, Long], Map[String, String]] = - new StringMapIsomorphism[Long](_.toLong) - implicit final val boolMapValueColumnType: Isomorphism[Map[String, Boolean], Map[String, String]] = - new StringMapIsomorphism[Boolean](_.toBoolean) - - private class StringValueMapIsomorphism[A](comap: String => A) - extends Isomorphism[Map[A, String], Map[String, String]]( - _.map { case (a, str) => (a.toString, str) }, - _.map { case (str1, str2) => (comap(str1), str2) } - ) - implicit final val intMapValueColumnType1: Isomorphism[Map[Int, String], Map[String, String]] = - new StringValueMapIsomorphism[Int](_.toInt) - implicit final val longMapValueColumnType1: Isomorphism[Map[Long, String], Map[String, String]] = - new StringValueMapIsomorphism[Long](_.toLong) - implicit final val boolMapValueColumnType1: Isomorphism[Map[Boolean, String], Map[String, String]] = - new StringValueMapIsomorphism[Boolean](_.toBoolean) - - implicit def hstoreColumnType[CC1, CC2, A, B]( - implicit iso1: Isomorphism[Map[CC1, CC2], Map[A, B]], - iso2: Isomorphism[Map[A, B], Map[String, String]]): Isomorphism[Map[CC1, CC2], Map[String, String]] = - new Isomorphism[Map[CC1, CC2], Map[String, String]]( - iso1.map andThen iso2.map, - iso2.comap andThen iso1.comap - ) - - def instancesIsoMapVal[Obj, Value, MapVal](comap1: String => Value, comap2: String => MapVal)( - implicit ico: InstanceConverter[Obj, Value]) = - new Isomorphism[Map[Obj, MapVal], Map[String, String]]( - _.map { case (obj, mapval) => (ico.encode(obj).toString, mapval.toString) }, - _.map { case (value, str) => (ico.decode(comap1(value)), comap2(str)) } - ) - def instancesIsoMapKey[Obj, Value, MapKey](comap1: String => Value, comap2: String => MapKey)( - implicit ico: InstanceConverter[Obj, Value]) = - new Isomorphism[Map[MapKey, Obj], Map[String, String]]( - _.map { case (mapkey, obj) => (mapkey.toString, ico.encode(obj).toString) }, - _.map { case (str1, str2) => (comap2(str1), ico.decode(comap1(str2))) } - ) - - implicit def instancesIsoObj2Str[Obj](implicit ico: InstanceConverter[Obj, String]): Isomorphism[Map[Obj, String], Map[String, String]] = - instancesIsoMapVal[Obj, String, String](identity[String], identity[String]) - implicit def instancesIsoObj2Str1[Obj]( - implicit ico: InstanceConverter[Obj, String]): Isomorphism[Map[String, Obj], Map[String, String]] = - instancesIsoMapKey[Obj, String, String](identity[String], identity[String]) - implicit def instancesIsoObj2Str2[Obj](implicit ico: InstanceConverter[Obj, String]): Isomorphism[Map[Obj, Int], Map[String, String]] = - instancesIsoMapVal[Obj, String, Int](identity[String], _.toInt) - implicit def instancesIsoObj2Str3[Obj](implicit ico: InstanceConverter[Obj, String]): Isomorphism[Map[Int, Obj], Map[String, String]] = - instancesIsoMapKey[Obj, String, Int](identity[String], _.toInt) - implicit def instancesIsoObj2Str4[Obj](implicit ico: InstanceConverter[Obj, String]): Isomorphism[Map[Obj, Long], Map[String, String]] = - instancesIsoMapVal[Obj, String, Long](identity[String], _.toLong) - implicit def instancesIsoObj2Str5[Obj](implicit ico: InstanceConverter[Obj, String]): Isomorphism[Map[Long, Obj], Map[String, String]] = - instancesIsoMapKey[Obj, String, Long](identity[String], _.toLong) - implicit def instancesIsoObj2Str6[Obj]( - implicit ico: InstanceConverter[Obj, String]): Isomorphism[Map[Obj, Boolean], Map[String, String]] = - instancesIsoMapVal[Obj, String, Boolean](identity[String], _.toBoolean) - implicit def instancesIsoObj2Str7[Obj]( - implicit ico: InstanceConverter[Obj, String]): Isomorphism[Map[Boolean, Obj], Map[String, String]] = - instancesIsoMapKey[Obj, String, Boolean](identity[String], _.toBoolean) - - implicit def instancesIsoObj2Int[Obj](implicit ico: InstanceConverter[Obj, Int]): Isomorphism[Map[Obj, String], Map[String, String]] = - instancesIsoMapVal[Obj, Int, String](_.toInt, identity[String]) - implicit def instancesIsoObj2Int1[Obj](implicit ico: InstanceConverter[Obj, Int]): Isomorphism[Map[String, Obj], Map[String, String]] = - instancesIsoMapKey[Obj, Int, String](_.toInt, identity[String]) - implicit def instancesIsoObj2Int2[Obj](implicit ico: InstanceConverter[Obj, Int]): Isomorphism[Map[Obj, Int], Map[String, String]] = - instancesIsoMapVal[Obj, Int, Int](_.toInt, _.toInt) - implicit def instancesIsoObj2Int3[Obj](implicit ico: InstanceConverter[Obj, Int]): Isomorphism[Map[Int, Obj], Map[String, String]] = - instancesIsoMapKey[Obj, Int, Int](_.toInt, _.toInt) - implicit def instancesIsoObj2Int4[Obj](implicit ico: InstanceConverter[Obj, Int]): Isomorphism[Map[Obj, Long], Map[String, String]] = - instancesIsoMapVal[Obj, Int, Long](_.toInt, _.toLong) - implicit def instancesIsoObj2Int5[Obj](implicit ico: InstanceConverter[Obj, Int]): Isomorphism[Map[Long, Obj], Map[String, String]] = - instancesIsoMapKey[Obj, Int, Long](_.toInt, _.toLong) - implicit def instancesIsoObj2Int6[Obj](implicit ico: InstanceConverter[Obj, Int]): Isomorphism[Map[Obj, Boolean], Map[String, String]] = - instancesIsoMapVal[Obj, Int, Boolean](_.toInt, _.toBoolean) - implicit def instancesIsoObj2Int7[Obj](implicit ico: InstanceConverter[Obj, Int]): Isomorphism[Map[Boolean, Obj], Map[String, String]] = - instancesIsoMapKey[Obj, Int, Boolean](_.toInt, _.toBoolean) - - implicit def instancesIsoObj2Long[Obj](implicit ico: InstanceConverter[Obj, Long]): Isomorphism[Map[Obj, String], Map[String, String]] = - instancesIsoMapVal[Obj, Long, String](_.toLong, identity[String]) - implicit def instancesIsoObj2Long1[Obj](implicit ico: InstanceConverter[Obj, Long]): Isomorphism[Map[String, Obj], Map[String, String]] = - instancesIsoMapKey[Obj, Long, String](_.toLong, identity[String]) - implicit def instancesIsoObj2Long2[Obj](implicit ico: InstanceConverter[Obj, Long]): Isomorphism[Map[Obj, Int], Map[String, String]] = - instancesIsoMapVal[Obj, Long, Int](_.toLong, _.toInt) - implicit def instancesIsoObj2Long3[Obj](implicit ico: InstanceConverter[Obj, Long]): Isomorphism[Map[Int, Obj], Map[String, String]] = - instancesIsoMapKey[Obj, Long, Int](_.toLong, _.toInt) - implicit def instancesIsoObj2Long4[Obj](implicit ico: InstanceConverter[Obj, Long]): Isomorphism[Map[Obj, Long], Map[String, String]] = - instancesIsoMapVal[Obj, Long, Long](_.toLong, _.toLong) - implicit def instancesIsoObj2Long5[Obj](implicit ico: InstanceConverter[Obj, Long]): Isomorphism[Map[Long, Obj], Map[String, String]] = - instancesIsoMapKey[Obj, Long, Long](_.toLong, _.toLong) - implicit def instancesIsoObj2Long6[Obj]( - implicit ico: InstanceConverter[Obj, Long]): Isomorphism[Map[Obj, Boolean], Map[String, String]] = - instancesIsoMapVal[Obj, Long, Boolean](_.toLong, _.toBoolean) - implicit def instancesIsoObj2Long7[Obj]( - implicit ico: InstanceConverter[Obj, Long]): Isomorphism[Map[Boolean, Obj], Map[String, String]] = - instancesIsoMapKey[Obj, Long, Boolean](_.toLong, _.toBoolean) -} diff --git a/slick/src/main/scala/pl/iterators/kebs/enums/KebsEnums.scala b/slick/src/main/scala/pl/iterators/kebs/enums/KebsEnums.scala deleted file mode 100644 index 8ae7ebe2..00000000 --- a/slick/src/main/scala/pl/iterators/kebs/enums/KebsEnums.scala +++ /dev/null @@ -1,40 +0,0 @@ -package pl.iterators.kebs.enums - -import enumeratum.{Enum, EnumEntry} -import enumeratum.values.{ValueEnum, ValueEnumEntry} -import pl.iterators.kebs.macros.enums.{EnumOf, ValueEnumOf} -import slick.lifted.Isomorphism - -trait SlickEnum { - def enumIsomorphism[E <: EnumEntry](`enum`: Enum[E]): Isomorphism[E, String] = new Isomorphism[E, String](_.entryName, `enum`.withName) - def uppercaseEnumIsomorphism[E <: EnumEntry](`enum`: Enum[E]): Isomorphism[E, String] = - new Isomorphism[E, String](_.entryName.toUpperCase, `enum`.withNameUppercaseOnly) - def lowercaseEnumIsomorphism[E <: EnumEntry](`enum`: Enum[E]): Isomorphism[E, String] = - new Isomorphism[E, String](_.entryName.toLowerCase, `enum`.withNameLowercaseOnly) - - implicit def enumListColumnType[E <: EnumEntry](implicit iso: Isomorphism[E, String]): Isomorphism[List[E], List[String]] = - new Isomorphism[List[E], List[String]](_.map(iso.map), _.map(iso.comap)) - implicit def enumSeqColumnType[E <: EnumEntry](implicit iso: Isomorphism[E, String]): Isomorphism[Seq[E], List[String]] = - new Isomorphism[Seq[E], List[String]](_.map(iso.map).toList, _.map(iso.comap)) -} - -trait SlickValueEnum { - def valueEnumIsomorphism[V, E <: ValueEnumEntry[V]](`enum`: ValueEnum[V, E]): Isomorphism[E, V] = - new Isomorphism[E, V](_.value, `enum`.withValue) -} - -trait KebsEnums extends SlickEnum with SlickValueEnum { - implicit def enumValueColumn[E <: EnumEntry](implicit ev: EnumOf[E]): Isomorphism[E, String] = enumIsomorphism(ev.`enum`) - implicit def valueEnumColumn[V, E <: ValueEnumEntry[V]](implicit ev: ValueEnumOf[V, E]): Isomorphism[E, V] = - valueEnumIsomorphism(ev.valueEnum) - - trait Uppercase extends SlickEnum { - implicit def enumValueColumn[E <: EnumEntry](implicit ev: EnumOf[E]): Isomorphism[E, String] = uppercaseEnumIsomorphism(ev.`enum`) - } - - trait Lowercase extends SlickEnum { - implicit def enumValueColumn[E <: EnumEntry](implicit ev: EnumOf[E]): Isomorphism[E, String] = lowercaseEnumIsomorphism(ev.`enum`) - } -} - -object KebsEnums extends KebsEnums diff --git a/slick/src/main/scala/pl/iterators/kebs/enums/package.scala b/slick/src/main/scala/pl/iterators/kebs/enums/package.scala deleted file mode 100644 index 5252f17c..00000000 --- a/slick/src/main/scala/pl/iterators/kebs/enums/package.scala +++ /dev/null @@ -1,9 +0,0 @@ -package pl.iterators.kebs - -package object enums extends KebsEnums { - - object uppercase extends Uppercase - - object lowercase extends Lowercase - -} diff --git a/slick/src/main/scala/pl/iterators/kebs/package.scala b/slick/src/main/scala/pl/iterators/kebs/package.scala deleted file mode 100644 index feea7054..00000000 --- a/slick/src/main/scala/pl/iterators/kebs/package.scala +++ /dev/null @@ -1,3 +0,0 @@ -package pl.iterators - -package object kebs extends Kebs diff --git a/slick/src/main/scala/pl/iterators/kebs/slick/KebsSlickSupport.scala b/slick/src/main/scala/pl/iterators/kebs/slick/KebsSlickSupport.scala new file mode 100644 index 00000000..7b032ab2 --- /dev/null +++ b/slick/src/main/scala/pl/iterators/kebs/slick/KebsSlickSupport.scala @@ -0,0 +1,216 @@ +package pl.iterators.kebs.slick + +import pl.iterators.kebs.core.enums.{EnumLike, ValueEnumLike, ValueEnumLikeEntry} +import pl.iterators.kebs.core.instances.InstanceConverter +import pl.iterators.kebs.core.macros.ValueClassLike +import pl.iterators.kebs.slick.hstore.KebsHStoreColumnExtensionMethods +import slick.ast.{BaseTypedType, NumericTypedType} +import slick.jdbc.{JdbcProfile, JdbcType} +import slick.lifted._ + +import scala.annotation.unused +import scala.reflect.ClassTag + +trait KebsColumnExtensionMethods { + implicit def stringValueColumnExt[CC](rep: Rep[CC])(implicit @unused ev: ValueClassLike[CC, String]): StringColumnExtensionMethods[CC] = + new StringColumnExtensionMethods[CC](rep) + implicit def stringValueOptionColumnExt[CC](rep: Rep[Option[CC]])( + implicit @unused ev: ValueClassLike[CC, String]): StringColumnExtensionMethods[Option[CC]] = new StringColumnExtensionMethods[Option[CC]](rep) + implicit def numericValueColumnExt[CC, B](rep: Rep[CC])( + implicit @unused ev1: ValueClassLike[CC, B], + @unused ev2: BaseTypedType[B] with NumericTypedType): BaseNumericColumnExtensionMethods[CC] = new BaseNumericColumnExtensionMethods[CC](rep) + implicit def numericValueOptionColumnExt[CC, B](rep: Rep[Option[CC]])( + implicit @unused ev1: ValueClassLike[CC, B], + @unused ev2: BaseTypedType[B] with NumericTypedType): OptionNumericColumnExtensionMethods[CC] = + new OptionNumericColumnExtensionMethods[CC](rep) + implicit def booleanValueColumnExt[CC](rep: Rep[CC])(implicit @unused ev: ValueClassLike[CC, Boolean]): BooleanColumnExtensionMethods[CC] = + new BooleanColumnExtensionMethods[CC](rep) + implicit def booleanValueOptionColumnExt[CC](rep: Rep[Option[CC]])( + implicit @unused ev: ValueClassLike[CC, Boolean]): BooleanColumnExtensionMethods[Option[CC]] = + new BooleanColumnExtensionMethods[Option[CC]](rep) + + implicit def hstoreColumnExt[KEY, VALUE](c: Rep[Map[KEY, VALUE]])( + implicit tm1: JdbcType[VALUE], + tm4: JdbcType[Map[KEY, VALUE]] + ): KebsHStoreColumnExtensionMethods[KEY, VALUE, Map[KEY, VALUE]] = + new KebsHStoreColumnExtensionMethods[KEY, VALUE, Map[KEY, VALUE]](c) + + @inline implicit def getCCOptionMapper2TT_1[B1, B2: BaseTypedType, BR, CC]( + implicit @unused ev: ValueClassLike[CC, B1]): OptionMapper2[B1, B2, BR, CC, B2, BR] = + OptionMapper2.plain.asInstanceOf[OptionMapper2[B1, B2, BR, CC, B2, BR]] + @inline implicit def getCCOptionMapper2TT_2[B1, B2, BR, CC](implicit @unused ev: ValueClassLike[CC, B2]): OptionMapper2[CC, CC, BR, CC, B2, BR] = + OptionMapper2.plain.asInstanceOf[OptionMapper2[CC, CC, BR, CC, B2, BR]] + @inline implicit def getCCOptionMapper2TO[B1, B2: BaseTypedType, BR, CC]( + implicit @unused ev: ValueClassLike[CC, B1]): OptionMapper2[B1, B2, BR, CC, Option[B2], Option[BR]] = + OptionMapper2.option.asInstanceOf[OptionMapper2[B1, B2, BR, CC, Option[B2], Option[BR]]] + @inline implicit def getCCOptionMapper2OT[B1, B2: BaseTypedType, BR, CC]( + implicit @unused ev: ValueClassLike[CC, B1]): OptionMapper2[B1, B2, BR, Option[CC], B2, Option[BR]] = + OptionMapper2.option.asInstanceOf[OptionMapper2[B1, B2, BR, Option[CC], B2, Option[BR]]] + @inline implicit def getCCOptionMapper2OO[B1, B2: BaseTypedType, BR, CC]( + implicit @unused ev: ValueClassLike[CC, B1]): OptionMapper2[B1, B2, BR, Option[CC], Option[B2], Option[BR]] = + OptionMapper2.option.asInstanceOf[OptionMapper2[B1, B2, BR, Option[CC], Option[B2], Option[BR]]] +} + +trait KebsSlickSupport { this: JdbcProfile => + trait ToFromStringForHstore[T] { + def to(value: T): String + def from(value: String): T + } + + trait KebsBasicImplicits extends KebsColumnExtensionMethods { + implicit def hstoreColumnType[A, B]( + implicit k: ToFromStringForHstore[A], + v: ToFromStringForHstore[B], + bct: BaseColumnType[Map[String, String]] + ): BaseColumnType[Map[A, B]] = + MappedColumnType.base[Map[A, B], Map[String, String]]( + _.map { case (a, b) => (k.to(a), v.to(b)) }, + _.map { case (a, b) => (k.from(a), v.from(b)) } + ) + + implicit val intToFromStringForHstore: ToFromStringForHstore[Int] = new ToFromStringForHstore[Int] { + override def to(value: Int): String = value.toString + override def from(value: String): Int = value.toInt + } + + implicit val longToFromStringForHstore: ToFromStringForHstore[Long] = new ToFromStringForHstore[Long] { + override def to(value: Long): String = value.toString + override def from(value: String): Long = value.toLong + } + + implicit val booleanToFromStringForHstore: ToFromStringForHstore[Boolean] = new ToFromStringForHstore[Boolean] { + override def to(value: Boolean): String = value.toString + override def from(value: String): Boolean = value.toBoolean + } + + implicit val stringToFromStringForHstore: ToFromStringForHstore[String] = new ToFromStringForHstore[String] { + override def to(value: String): String = value + override def from(value: String): String = value + } + + implicit val doubleToFromStringForHstore: ToFromStringForHstore[Double] = new ToFromStringForHstore[Double] { + override def to(value: Double): String = value.toString + override def from(value: String): Double = value.toDouble + } + + implicit val floatToFromStringForHstore: ToFromStringForHstore[Float] = new ToFromStringForHstore[Float] { + override def to(value: Float): String = value.toString + override def from(value: String): Float = value.toFloat + } + + implicit val shortToFromStringForHstore: ToFromStringForHstore[Short] = new ToFromStringForHstore[Short] { + override def to(value: Short): String = value.toString + override def from(value: String): Short = value.toShort + } + + implicit val byteToFromStringForHstore: ToFromStringForHstore[Byte] = new ToFromStringForHstore[Byte] { + override def to(value: Byte): String = value.toString + override def from(value: String): Byte = value.toByte + } + + implicit val charToFromStringForHstore: ToFromStringForHstore[Char] = new ToFromStringForHstore[Char] { + override def to(value: Char): String = value.toString + override def from(value: String): Char = value.head + } + + implicit val bigDecimalToFromStringForHstore: ToFromStringForHstore[BigDecimal] = new ToFromStringForHstore[BigDecimal] { + override def to(value: BigDecimal): String = value.toString + override def from(value: String): BigDecimal = BigDecimal(value) + } + + implicit val bigIntToFromStringForHstore: ToFromStringForHstore[BigInt] = new ToFromStringForHstore[BigInt] { + override def to(value: BigInt): String = value.toString + override def from(value: String): BigInt = BigInt(value) + } + } + + trait KebsValueClassLikeImplicits { + implicit def valueClassLikeColumnType[CC, B](implicit rep1: ValueClassLike[CC, B], bct: BaseColumnType[B], cls: ClassTag[CC]): BaseColumnType[CC] = + MappedColumnType.base[CC, B](rep1.unapply, rep1.apply) + + implicit def listValueColumnType[CC, B](implicit rep1: ValueClassLike[CC, B], bct: BaseColumnType[List[B]]): BaseColumnType[List[CC]] = + MappedColumnType.base[List[CC], List[B]](_.map(rep1.unapply), _.map(rep1.apply)) + + implicit def valueClassLikeToFromStringForHstore[CC, B](implicit rep: ValueClassLike[CC, B], toFromStringForHstore: ToFromStringForHstore[B]): ToFromStringForHstore[CC] = + new ToFromStringForHstore[CC] { + override def to(value: CC): String = toFromStringForHstore.to(rep.unapply(value)) + override def from(value: String): CC = rep.apply(toFromStringForHstore.from(value)) + } + } + + trait KebsInstanceConverterImplicits { + implicit def instanceConverterColumnType[CC, B](implicit ico: InstanceConverter[CC, B], bct: BaseColumnType[B], cls: ClassTag[CC]): BaseColumnType[CC] = + MappedColumnType.base[CC, B](ico.encode, ico.decode) + + implicit def listInstanceConverterColumnType[CC, B](implicit ico: InstanceConverter[CC, B], bct: BaseColumnType[List[B]]): BaseColumnType[List[CC]] = + MappedColumnType.base[List[CC], List[B]](_.map(ico.encode), _.map(ico.decode)) + + implicit def instanceConverterToFromStringForHstore[CC, B](implicit ico: InstanceConverter[CC, B], toFromStringForHstore: ToFromStringForHstore[B]): ToFromStringForHstore[CC] = + new ToFromStringForHstore[CC] { + override def to(value: CC): String = toFromStringForHstore.to(ico.encode(value)) + override def from(value: String): CC = ico.decode(toFromStringForHstore.from(value)) + } + } + + protected trait SlickEnum { + def enumColumn[E](`enum`: EnumLike[E])(implicit bct: BaseColumnType[String], cls: ClassTag[E]): BaseColumnType[E] = MappedColumnType.base[E, String](_.toString, `enum`.withName) + + def uppercaseEnumColumn[E](`enum`: EnumLike[E])(implicit bct: BaseColumnType[String], cls: ClassTag[E]): BaseColumnType[E] = + MappedColumnType.base[E, String](_.toString.toUpperCase, `enum`.withNameUppercaseOnly) + + def lowercaseEnumColumn[E](`enum`: EnumLike[E])(implicit bct: BaseColumnType[String], cls: ClassTag[E]): BaseColumnType[E] = + MappedColumnType.base[E, String](_.toString.toLowerCase, `enum`.withNameLowercaseOnly) + } + + protected trait SlickValueEnum { + def valueEnumColumnType[V, E <: ValueEnumLikeEntry[V]](`enum`: ValueEnumLike[V, E])(implicit bct: BaseColumnType[V], cls: ClassTag[E]): BaseColumnType[E] = + MappedColumnType.base[E, V](_.value, `enum`.withValue) + } + + trait EnumImplicits extends SlickValueEnum with SlickEnum { + implicit def enumValueColumn[E](implicit ev: EnumLike[E], bct: BaseColumnType[String], cls: ClassTag[E]): BaseColumnType[E] = enumColumn(ev) + + implicit def valueEnumColumn[V, E <: ValueEnumLikeEntry[V]](implicit ev: ValueEnumLike[V, E], bct: BaseColumnType[V], cls: ClassTag[E]): BaseColumnType[E] = + valueEnumColumnType(ev) + + implicit def enumListColumn[E](implicit ev: EnumLike[E], bct: BaseColumnType[List[String]]): BaseColumnType[List[E]] = { + MappedColumnType.base[List[E], List[String]](_.map(_.toString), _.map(ev.withName)) + } + + implicit def enumToFromStringForHstore[E](implicit ev: EnumLike[E]): ToFromStringForHstore[E] = new ToFromStringForHstore[E] { + override def to(value: E): String = value.toString + override def from(value: String): E = ev.withName(value) + } + + implicit def valueEnumToFromStringForHstore[V, E <: ValueEnumLikeEntry[V]](implicit ev: ValueEnumLike[V, E], tfs: ToFromStringForHstore[V]): ToFromStringForHstore[E] = new ToFromStringForHstore[E] { + override def to(value: E): String = tfs.to(value.value) + override def from(value: String): E = ev.withValue(tfs.from(value)) + } + } + + trait LowercaseEnumImplicits extends SlickValueEnum with SlickEnum { + implicit def enumValueColumn[E](implicit ev: EnumLike[E], bct: BaseColumnType[String], cls: ClassTag[E]): BaseColumnType[E] = lowercaseEnumColumn(ev) + + implicit def enumListColumn[E](implicit ev: EnumLike[E], bct: BaseColumnType[List[String]]): BaseColumnType[List[E]] = { + MappedColumnType.base[List[E], List[String]](_.map(_.toString.toLowerCase), _.map(ev.withNameLowercaseOnly)) + } + + implicit def toFromStringForHstoreEnum[E](implicit ev: EnumLike[E]): ToFromStringForHstore[E] = new ToFromStringForHstore[E] { + override def to(value: E): String = value.toString.toLowerCase + override def from(value: String): E = ev.withNameLowercaseOnly(value) + } + } + + trait UppercaseEnumImplicits extends SlickValueEnum with SlickEnum { + implicit def enumValueColumn[E](implicit ev: EnumLike[E], bct: BaseColumnType[String], cls: ClassTag[E]): BaseColumnType[E] = uppercaseEnumColumn(ev) + + implicit def enumListColumn[E](implicit ev: EnumLike[E], bct: BaseColumnType[List[String]]): BaseColumnType[List[E]] = { + MappedColumnType.base[List[E], List[String]](_.map(_.toString.toUpperCase), _.map(ev.withNameUppercaseOnly)) + } + + implicit def toFromStringForHstoreEnum[E](implicit ev: EnumLike[E]): ToFromStringForHstore[E] = new ToFromStringForHstore[E] { + override def to(value: E): String = value.toString.toUpperCase + override def from(value: String): E = ev.withNameUppercaseOnly(value) + } + } +} diff --git a/slick/src/main/scala/pl/iterators/kebs/hstore/KebsHStoreColumnExtensionMethods.scala b/slick/src/main/scala/pl/iterators/kebs/slick/hstore/KebsHStoreColumnExtensionMethods.scala similarity index 92% rename from slick/src/main/scala/pl/iterators/kebs/hstore/KebsHStoreColumnExtensionMethods.scala rename to slick/src/main/scala/pl/iterators/kebs/slick/hstore/KebsHStoreColumnExtensionMethods.scala index cefc3dcc..a2a29b5c 100644 --- a/slick/src/main/scala/pl/iterators/kebs/hstore/KebsHStoreColumnExtensionMethods.scala +++ b/slick/src/main/scala/pl/iterators/kebs/slick/hstore/KebsHStoreColumnExtensionMethods.scala @@ -1,4 +1,4 @@ -package pl.iterators.kebs.hstore +package pl.iterators.kebs.slick.hstore import slick.ast.Library.{SqlFunction, SqlOperator} import slick.ast.ScalaBaseType._ @@ -7,10 +7,7 @@ import slick.jdbc.JdbcType import slick.lifted.{ExtensionMethods, FunctionSymbolExtensionMethods, Rep} class KebsHStoreColumnExtensionMethods[KEY, VALUE, P1](val c: Rep[P1])( - implicit tm0: JdbcType[KEY], - tm1: JdbcType[VALUE], - tm2: JdbcType[List[KEY]], - tm3: JdbcType[List[VALUE]], + implicit tm1: JdbcType[VALUE], tm4: JdbcType[Map[KEY, VALUE]] ) extends ExtensionMethods[Map[KEY, VALUE], P1] { import FunctionSymbolExtensionMethods._ @@ -31,7 +28,7 @@ class KebsHStoreColumnExtensionMethods[KEY, VALUE, P1](val c: Rep[P1])( val Slice = new SqlFunction("slice") } - def +>[P2, R](k: Rep[P2])(implicit om: o#arg[KEY, P2]#to[VALUE, R]) = { + def +>[P2, R](k: Rep[P2]) = { KebsHStoreLibrary.On.column[Option[VALUE]](n, k.toNode) } def >>[T: JdbcType](k: Rep[KEY]) = { diff --git a/slick/src/test/scala/caseclasses/SlickMappedColumnTypeTests.scala b/slick/src/test/scala-2/pl/iterators/kebs/slick/caseclasses/SlickMappedColumnTypeTests.scala similarity index 81% rename from slick/src/test/scala/caseclasses/SlickMappedColumnTypeTests.scala rename to slick/src/test/scala-2/pl/iterators/kebs/slick/caseclasses/SlickMappedColumnTypeTests.scala index ee1148b9..6d80a72d 100644 --- a/slick/src/test/scala/caseclasses/SlickMappedColumnTypeTests.scala +++ b/slick/src/test/scala-2/pl/iterators/kebs/slick/caseclasses/SlickMappedColumnTypeTests.scala @@ -1,12 +1,19 @@ -package caseclasses +package pl.iterators.kebs.slick.caseclasses import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers +import pl.iterators.kebs.core.macros.CaseClass1ToValueClass +import pl.iterators.kebs.slick.KebsSlickSupport +import slick.jdbc.PostgresProfile class SlickMappedColumnTypeTests extends AnyFunSuite with Matchers { + object MyPostgresProfile extends PostgresProfile with KebsSlickSupport { + override val api: APITagged = new APITagged {} + trait APITagged extends JdbcAPI with KebsBasicImplicits with KebsValueClassLikeImplicits with CaseClass1ToValueClass + } + + import MyPostgresProfile.api._ import slick.lifted.ProvenShape - import slick.jdbc.PostgresProfile.api._ - import pl.iterators.kebs._ case class Id(id: Long) case class Row(id: Id, name: String, num: Long) @@ -37,11 +44,13 @@ class SlickMappedColumnTypeTests extends AnyFunSuite with Matchers { | def name = column[String]("name") | def num = column[Long]("num") | - | override def * : ProvenShape[Row] = (id, name, num) <> (Row.tupled, Row.unapply) + | override def * : ProvenShape[Row] = (id, name, num) <> ((Row.apply _).tupled, Row.unapply) | } """.stripMargin should compile } + // for the tests below, Name.unapply (etc.) works fine, Scala 3 is more picky here + test("Slick mapping - one element wrapper") { """ |class OneElement(tag: Tag) extends Table[Name](tag, "ONE_ELEMENT_TABLE") { @@ -56,7 +65,7 @@ class SlickMappedColumnTypeTests extends AnyFunSuite with Matchers { """ |class Matryoshka(tag: Tag) extends Table[WrappedName](tag, "MATRYOSHKA") { | def name = column[Name]("name") - | + | | override def * : ProvenShape[WrappedName] = name <> (WrappedName.apply, WrappedName.unapply) |} """.stripMargin should compile diff --git a/slick/src/test/scala-3/pl/iterators/kebs/slick/caseclasses/SlickMappedColumnTypeTests.scala b/slick/src/test/scala-3/pl/iterators/kebs/slick/caseclasses/SlickMappedColumnTypeTests.scala new file mode 100644 index 00000000..9fa64b95 --- /dev/null +++ b/slick/src/test/scala-3/pl/iterators/kebs/slick/caseclasses/SlickMappedColumnTypeTests.scala @@ -0,0 +1,96 @@ +package pl.iterators.kebs.slick.caseclasses + +import org.scalatest.funsuite.AnyFunSuite +import org.scalatest.matchers.should.Matchers +import pl.iterators.kebs.core.macros.CaseClass1ToValueClass +import pl.iterators.kebs.slick.KebsSlickSupport +import slick.jdbc.PostgresProfile + +class SlickMappedColumnTypeTests extends AnyFunSuite with Matchers { + object MyPostgresProfile extends PostgresProfile with KebsSlickSupport { + override val api: APITagged = new APITagged {} + trait APITagged extends JdbcAPI with KebsBasicImplicits with KebsValueClassLikeImplicits with CaseClass1ToValueClass + } + + import MyPostgresProfile.api._ + + case class Id(id: Long) + case class Row(id: Id, name: String, num: Long) + case class Name(name: String) + case class WrappedName(name: Name) + + test("MappedColumnType for case classes") { + "implicitly[BaseColumnType[Id]]" should compile + } + + test("Slick mapping") { + """ + |class ATable(tag: Tag) extends Table[(Id, String, Long)](tag, "A_TABLE") { + | def id = column[Id]("id") + | def name = column[String]("name") + | def num = column[Long]("num") + | + | override def * = (id, name, num) + |} + """.stripMargin should compile + } + + test("Slick mapping - mapped projection") { + """ + | + |class ATable(tag: Tag) extends Table[Row](tag, "A_TABLE") { + | def id = column[Id]("id") + | def name = column[String]("name") + | def num = column[Long]("num") + | + | override def * : slick.lifted.ProvenShape[Row] = (id, name, num) <> ((Row.apply _).tupled, Row.unapply) + | } + """.stripMargin should compile + } + + // for some reason, Scala 3 requires this weird notation below... + + test("Slick mapping - one element wrapper") { + """ + |class OneElement(tag: Tag) extends Table[Name](tag, "ONE_ELEMENT_TABLE") { + | def name = column[String]("name") + | + | override def * : slick.lifted.ProvenShape[Name] = name <> (Name.apply, n => Some(n.name)) + | } + """.stripMargin should compile + } + + test("Slick mapping - matryoshka case 1") { + """ + |class Matryoshka(tag: Tag) extends Table[WrappedName](tag, "MATRYOSHKA") { + | def name = column[Name]("name") + | + | override def * : slick.lifted.ProvenShape[WrappedName] = name <> (WrappedName.apply, n => Some(n.name)) + |} + """.stripMargin should compile + } + + test("Slick mapping - matryoshka case 2") { + """ + |class Matryoshka(tag: Tag) extends Table[WrappedName](tag, "MATRYOSHKA") { + | def name = column[Name]("name") + | private def mappedProjection = name <> (WrappedName.apply, n => Some(n.name)) + | + | override def * : slick.lifted.ProvenShape[WrappedName] = mappedProjection + | } + """.stripMargin should compile + } + + test("Wrong slick mapping") { + """ + |class NoMapping(id: Long) + | + |class ATable(tag: Tag) extends Table[(NoMapping, String)](tag, "A_TABLE") { + | def id = column[NoMapping]("id") + | def name = column[String]("name") + | + | override def * = (id, name) + |} + """.stripMargin shouldNot typeCheck + } +} diff --git a/slick/src/test/scala/arrays/ListIsomorphismTest.scala b/slick/src/test/scala/arrays/ListIsomorphismTest.scala deleted file mode 100644 index e386d2a0..00000000 --- a/slick/src/test/scala/arrays/ListIsomorphismTest.scala +++ /dev/null @@ -1,41 +0,0 @@ -package arrays - -import org.scalatest.funsuite.AnyFunSuite -import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.instances.time.YearMonthString -import slick.lifted.Isomorphism - -class ListIsomorphismTest extends AnyFunSuite with Matchers with YearMonthString { - import pl.iterators.kebs._ - - case class C(a: String) - - test("No CaseClass1Rep implicits derived") { - import pl.iterators.kebs.macros.CaseClass1Rep - - "implicitly[CaseClass1Rep[YearMonth, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, YearMonth]]" shouldNot typeCheck - } - - test("Case class isomorphism implies list isomorphism") { - val iso = implicitly[Isomorphism[List[C], List[String]]] - iso.map(List(C("a"), C("b"))) shouldBe List("a", "b") - iso.comap(List("a", "b")) shouldBe List(C("a"), C("b")) - } - - test("Case class isomorphism implies seq to list isomorphism") { - val iso = implicitly[Isomorphism[Seq[C], List[String]]] - iso.map(Seq(C("a"), C("b"))) shouldBe List("a", "b") - iso.comap(List("a", "b")) shouldBe Seq(C("a"), C("b")) - } - - import java.time.YearMonth - - test("List[Obj[String]] <-> List[String]") { - "val iso = implicitly[Isomorphism[List[YearMonth], List[String]]]" should compile - } - - test("Seq[Obj[String]] <-> List[String]") { - "val iso = implicitly[Isomorphism[Seq[YearMonth], List[String]]]" should compile - } -} diff --git a/slick/src/test/scala/caseclasses/CaseClassIsomorphismTests.scala b/slick/src/test/scala/caseclasses/CaseClassIsomorphismTests.scala deleted file mode 100644 index 3936891d..00000000 --- a/slick/src/test/scala/caseclasses/CaseClassIsomorphismTests.scala +++ /dev/null @@ -1,56 +0,0 @@ -package caseclasses - -import org.scalatest.funsuite.AnyFunSuite -import org.scalatest.matchers.should.Matchers - -class CaseClassIsomorphismTests extends AnyFunSuite with Matchers { - import slick.lifted.Isomorphism - import pl.iterators.kebs._ - - case class Simple1(a: Int) - case class Simple2(a: Option[Int]) - - test("Implicit isomorphism for case class of arity 1") { - val iso = implicitly[Isomorphism[Simple1, Int]] - iso.map(Simple1(10)) shouldBe 10 - iso.comap(10) shouldBe Simple1(10) - } - - test("Implicit isomorphism for case class of arity 1 - parametrized return type") { - val iso = implicitly[Isomorphism[Simple2, Option[Int]]] - iso.map(Simple2(Some(10))) shouldBe Some(10) - iso.comap(Some(10)) shouldBe Simple2(Some(10)) - } - - case class TooBig(a: Int, b: Int) - - test("No isomorphism for case classes of arity > 1") { - "implicitly[Isomorphism[TooBig, _]]" shouldNot typeCheck - } - - case object NoIsoForYou - - test("No isomorphism for case classes of arity == 0") { - "implicitly[Isomorphism[NoIsoForYou.type, _]]" shouldNot typeCheck - } - - class JustAClass(val a: Int) - - test("No isomorphism for ordinary classes") { - "implicitly[Isomorphism[JustAClass, Int]]" shouldNot typeCheck - } - - case class Parametrized[P](a: P) - - test("Implicit isomorphism for parametrized case class of arity 1") { - val iso = implicitly[Isomorphism[Parametrized[Int], Int]] - iso.map(Parametrized(10)) shouldBe 10 - iso.comap(10) shouldBe Parametrized(10) - } - - test("Implicit isomorphism for parametrized case class of arity 1 - unrefined type parameter") { - def iso[P]: Isomorphism[Parametrized[P], P] = implicitly[Isomorphism[Parametrized[P], P]] - iso[Int].map(Parametrized(10)) shouldBe 10 - iso[Option[Int]].comap(Some(10)) shouldBe Parametrized(Some(10)) - } -} diff --git a/slick/src/test/scala/enums/EnumIsomorphismTests.scala b/slick/src/test/scala/enums/EnumIsomorphismTests.scala deleted file mode 100644 index 83653a7f..00000000 --- a/slick/src/test/scala/enums/EnumIsomorphismTests.scala +++ /dev/null @@ -1,47 +0,0 @@ -package enums - -import enumeratum.{Enum, EnumEntry} -import org.scalatest.funsuite.AnyFunSuite -import org.scalatest.matchers.should.Matchers -import slick.lifted.Isomorphism - -class EnumIsomorphismTests extends AnyFunSuite with Matchers { - - sealed trait Greeting extends EnumEntry - - object Greeting extends Enum[Greeting] { - case object Hello extends Greeting - case object GoodBye extends Greeting - case object Hi extends Greeting - case object Bye extends Greeting - - val values = findValues - } - - import Greeting._ - - test("Implicit isomorphism for EnumEntry") { - import pl.iterators.kebs.enums._ - - val iso = implicitly[Isomorphism[Greeting, String]] - iso.map(Hello) shouldBe "Hello" - iso.comap("Hello") shouldBe Hello - } - - test("Implicit isomorphism for EnumEntry - lowercase") { - import pl.iterators.kebs.enums.lowercase._ - - val iso = implicitly[Isomorphism[Greeting, String]] - iso.map(GoodBye) shouldBe "goodbye" - iso.comap("goodbye") shouldBe GoodBye - } - - test("Implicit isomorphism for EnumEntry - uppercase") { - import pl.iterators.kebs.enums.uppercase._ - - val iso = implicitly[Isomorphism[Greeting, String]] - iso.map(GoodBye) shouldBe "GOODBYE" - iso.comap("GOODBYE") shouldBe GoodBye - } - -} diff --git a/slick/src/test/scala/enums/ValueEnumIsomorphismTests.scala b/slick/src/test/scala/enums/ValueEnumIsomorphismTests.scala deleted file mode 100644 index c84a9525..00000000 --- a/slick/src/test/scala/enums/ValueEnumIsomorphismTests.scala +++ /dev/null @@ -1,31 +0,0 @@ -package enums - -import enumeratum.values.{IntEnum, IntEnumEntry} -import org.scalatest.funsuite.AnyFunSuite -import org.scalatest.matchers.should.Matchers -import slick.lifted.Isomorphism - -class ValueEnumIsomorphismTests extends AnyFunSuite with Matchers { - - sealed abstract class IntGreeting(val value: Int) extends IntEnumEntry - - object IntGreeting extends IntEnum[IntGreeting] { - case object Hello extends IntGreeting(0) - case object GoodBye extends IntGreeting(1) - case object Hi extends IntGreeting(2) - case object Bye extends IntGreeting(3) - - val values = findValues - } - - import IntGreeting._ - - test("Implicit isomorphism from ValueEnumEntry") { - import pl.iterators.kebs.enums._ - - val iso = implicitly[Isomorphism[IntGreeting, Int]] - iso.map(Bye) shouldBe 3 - iso.comap(3) shouldBe Bye - } - -} diff --git a/slick/src/test/scala/hstore/MapIsomorphismTest.scala b/slick/src/test/scala/hstore/MapIsomorphismTest.scala deleted file mode 100644 index 889cdbb2..00000000 --- a/slick/src/test/scala/hstore/MapIsomorphismTest.scala +++ /dev/null @@ -1,150 +0,0 @@ -package hstore - -import org.scalatest.funsuite.AnyFunSuite -import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.instances.time.mixins.InstantEpochMilliLong -import pl.iterators.kebs.instances.time.{DayOfWeekInt, YearMonthString} -import slick.lifted.Isomorphism - -class MapIsomorphismTest extends AnyFunSuite with Matchers with YearMonthString with DayOfWeekInt with InstantEpochMilliLong { - import pl.iterators.kebs._ - - case class StringValue(value: String) - case class IntValue(value: Int) - - test("No CaseClass1Rep implicits derived") { - import pl.iterators.kebs.macros.CaseClass1Rep - - "implicitly[CaseClass1Rep[YearMonth, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, YearMonth]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[DayOfWeek, Int]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Int, DayOfWeek]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Instant, Long]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Long, Instant]]" shouldNot typeCheck - } - - test("Case classes isomorphisms implies string to int map isomorphism") { - val iso = implicitly[Isomorphism[Map[StringValue, IntValue], Map[String, Int]]] - iso.map(Map(StringValue("a") -> IntValue(0), StringValue("b") -> IntValue(1))) shouldBe Map("a" -> 0, "b" -> 1) - iso.comap(Map("a" -> 0, "b" -> 1)) shouldBe Map(StringValue("a") -> IntValue(0), StringValue("b") -> IntValue(1)) - } - - test("Case classes isomorphisms implies string to string map isomorphism") { - val iso = implicitly[Isomorphism[Map[StringValue, IntValue], Map[String, String]]] - iso.map(Map(StringValue("a") -> IntValue(0), StringValue("b") -> IntValue(1))) shouldBe Map("a" -> "0", "b" -> "1") - iso.comap(Map("a" -> "0", "b" -> "1")) shouldBe Map(StringValue("a") -> IntValue(0), StringValue("b") -> IntValue(1)) - } - - test("Case classes isomorphisms implies int to string map isomorphism") { - val iso = implicitly[Isomorphism[Map[IntValue, StringValue], Map[Int, String]]] - iso.map(Map(IntValue(0) -> StringValue("a"), IntValue(1) -> StringValue("b"))) shouldBe Map(0 -> "a", 1 -> "b") - iso.comap(Map(0 -> "a", 1 -> "b")) shouldBe Map(IntValue(0) -> StringValue("a"), IntValue(1) -> StringValue("b")) - } - - test("Case classes isomorphisms implies string to string map isomorphism 2") { - val iso = implicitly[Isomorphism[Map[IntValue, StringValue], Map[String, String]]] - iso.map(Map(IntValue(0) -> StringValue("a"), IntValue(1) -> StringValue("b"))) shouldBe Map("0" -> "a", "1" -> "b") - iso.comap(Map("0" -> "a", "1" -> "b")) shouldBe Map(IntValue(0) -> StringValue("a"), IntValue(1) -> StringValue("b")) - } - - import java.time.{DayOfWeek, YearMonth, Instant} - - /* InstanceConverter[Obj, String] */ - test("Map[Obj[String], String] <-> Map[String, String]") { - """val iso = implicitly[Isomorphism[Map[YearMonth, String], Map[String, String]]]""".stripMargin should compile - } - - test("Map[String, Obj[String]] <-> Map[String, String]") { - """val iso = implicitly[Isomorphism[Map[String, YearMonth], Map[String, String]]]""".stripMargin should compile - } - - test("Map[Obj[String], Int] <-> Map[String, String]") { - """val iso = implicitly[Isomorphism[Map[YearMonth, Int], Map[String, String]]]""".stripMargin should compile - } - - test("Map[Int, Obj[String]] <-> Map[String, String]") { - """val iso = implicitly[Isomorphism[Map[Int, YearMonth], Map[String, String]]]""".stripMargin should compile - } - - test("Map[Obj[String], Long] <-> Map[String, String]") { - """val iso = implicitly[Isomorphism[Map[YearMonth, Long], Map[String, String]]]""".stripMargin should compile - } - - test("Map[Long, Obj[String]] <-> Map[String, String]") { - """val iso = implicitly[Isomorphism[Map[Long, YearMonth], Map[String, String]]]""".stripMargin should compile - } - - test("Map[Obj[String], Boolean] <-> Map[String, String]") { - """val iso = implicitly[Isomorphism[Map[YearMonth, Boolean], Map[String, String]]]""".stripMargin should compile - } - - test("Map[Boolean, Obj[String]] <-> Map[String, String]") { - """val iso = implicitly[Isomorphism[Map[Boolean, YearMonth], Map[String, String]]]""".stripMargin should compile - } - - /* implicit InstanceConverter[Obj, Int] */ - test("Map[Obj[Int], String] <-> Map[String, String]") { - """val iso = implicitly[Isomorphism[Map[DayOfWeek, String], Map[String, String]]]""".stripMargin should compile - } - - test("Map[String, Obj[Int]] <-> Map[String, String]") { - """val iso = implicitly[Isomorphism[Map[String, DayOfWeek], Map[String, String]]]""".stripMargin should compile - } - - test("Map[Obj[Int], Int] <-> Map[String, String]") { - """val iso = implicitly[Isomorphism[Map[DayOfWeek, Int], Map[String, String]]]""".stripMargin should compile - } - - test("Map[Int, Obj[Int]] <-> Map[String, String]") { - """val iso = implicitly[Isomorphism[Map[Int, DayOfWeek], Map[String, String]]]""".stripMargin should compile - } - - test("Map[Obj[Int], Long] <-> Map[String, String]") { - """val iso = implicitly[Isomorphism[Map[DayOfWeek, Long], Map[String, String]]]""".stripMargin should compile - } - - test("Map[Long, Obj[Int]] <-> Map[String, String]") { - """val iso = implicitly[Isomorphism[Map[Long, DayOfWeek], Map[String, String]]]""".stripMargin should compile - } - - test("Map[Obj[Int], Boolean] <-> Map[String, String]") { - """val iso = implicitly[Isomorphism[Map[DayOfWeek, Boolean], Map[String, String]]]""".stripMargin should compile - } - - test("Map[Boolean, Obj[Int]] <-> Map[String, String]") { - """val iso = implicitly[Isomorphism[Map[Boolean, DayOfWeek], Map[String, String]]]""".stripMargin should compile - } - - /* implicit InstanceConverter[Obj, Long] */ - test("Map[Obj[Long], String] <-> Map[String, String]") { - """val iso = implicitly[Isomorphism[Map[Instant, String], Map[String, String]]]""".stripMargin should compile - } - - test("Map[String, Obj[Long]] <-> Map[String, String]") { - """val iso = implicitly[Isomorphism[Map[String, Instant], Map[String, String]]]""".stripMargin should compile - } - - test("Map[Obj[Long], Int] <-> Map[String, String]") { - """val iso = implicitly[Isomorphism[Map[Instant, Int], Map[String, String]]]""".stripMargin should compile - } - - test("Map[Int, Obj[Long]] <-> Map[String, String]") { - """val iso = implicitly[Isomorphism[Map[Int, Instant], Map[String, String]]]""".stripMargin should compile - } - - test("Map[Obj[Long], Long] <-> Map[String, String]") { - """val iso = implicitly[Isomorphism[Map[Instant, Long], Map[String, String]]]""".stripMargin should compile - } - - test("Map[Long, Obj[Long]] <-> Map[String, String]") { - """val iso = implicitly[Isomorphism[Map[Long, Instant], Map[String, String]]]""".stripMargin should compile - } - - test("Map[Obj[Long], Boolean] <-> Map[String, String]") { - """val iso = implicitly[Isomorphism[Map[Instant, Boolean], Map[String, String]]]""".stripMargin should compile - } - - test("Map[Boolean, Obj[Long]] <-> Map[String, String]") { - """val iso = implicitly[Isomorphism[Map[Boolean, Instant], Map[String, String]]]""".stripMargin should compile - } -} diff --git a/slick/src/test/scala/arrays/SlickPgArrayColumnTypeTests.scala b/slick/src/test/scala/pl/iterators/kebs/slick/arrays/SlickPgArrayColumnTypeTests.scala similarity index 60% rename from slick/src/test/scala/arrays/SlickPgArrayColumnTypeTests.scala rename to slick/src/test/scala/pl/iterators/kebs/slick/arrays/SlickPgArrayColumnTypeTests.scala index 25237b6f..a95d4d8b 100644 --- a/slick/src/test/scala/arrays/SlickPgArrayColumnTypeTests.scala +++ b/slick/src/test/scala/pl/iterators/kebs/slick/arrays/SlickPgArrayColumnTypeTests.scala @@ -1,20 +1,21 @@ -package arrays +package pl.iterators.kebs.slick.arrays import com.github.tminglei.slickpg._ import enumeratum.{Enum, EnumEntry} import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers +import pl.iterators.kebs.core.macros.CaseClass1ToValueClass +import pl.iterators.kebs.enumeratum.KebsEnumeratum -class SlickPgArrayColumnTypeTests extends AnyFunSuite with Matchers { +class SlickPgArrayColumnTypeTests extends AnyFunSuite with Matchers with KebsEnumeratum { case class Institution(value: Long) case class MarketFinancialProduct(value: String) - import pl.iterators.kebs.Kebs - import pl.iterators.kebs.enums.KebsEnums + import pl.iterators.kebs.slick.KebsSlickSupport - object MyPostgresProfile extends ExPostgresProfile with PgArraySupport { + object MyPostgresProfile extends ExPostgresProfile with PgArraySupport with KebsSlickSupport { override val api: APIWithArrays = new APIWithArrays {} - trait APIWithArrays extends super.API with ArrayImplicits with Kebs with KebsEnums + trait APIWithArrays extends ExtPostgresAPI with ArrayImplicits with KebsBasicImplicits with KebsValueClassLikeImplicits with CaseClass1ToValueClass with EnumImplicits } import MyPostgresProfile.api._ @@ -30,18 +31,6 @@ class SlickPgArrayColumnTypeTests extends AnyFunSuite with Matchers { """.stripMargin should compile } - test("Seq column type") { - """ - | class SeqTestTable(tag: Tag) extends Table[(Long, Seq[Institution], Option[List[MarketFinancialProduct]])](tag, "SeqTest") { - | def id = column[Long]("id", O.AutoInc, O.PrimaryKey) - | def institutions = column[Seq[Institution]]("institutions") - | def mktFinancialProducts = column[Option[List[MarketFinancialProduct]]]("mktFinancialProducts") - | - | def * = (id, institutions, mktFinancialProducts) - | } - """.stripMargin should compile - } - sealed trait AnEnum extends EnumEntry object AnEnum extends Enum[AnEnum] { case object Something extends AnEnum @@ -49,25 +38,24 @@ class SlickPgArrayColumnTypeTests extends AnyFunSuite with Matchers { override val values = findValues } - import enums._ - test("Seq column type with enums") { + test("List column type with enums") { """ - | class EnumSeqTestTable(tag: Tag) extends Table[(Long, Seq[AnEnum])](tag, "EnumSeqTest") { + | class EnumSeqTestTable(tag: Tag) extends Table[(Long, List[AnEnum])](tag, "EnumSeqTest") { | def id = column[Long]("id", O.AutoInc, O.PrimaryKey) - | def enums = column[Seq[AnEnum]]("enums") + | def enums = column[List[AnEnum]]("enums") | | def * = (id, enums) | } """.stripMargin should compile } - test("Seq column type with enums and not enums") { + test("List column type with enums and not enums") { """ - | class EnumSeqTestTable(tag: Tag) extends Table[(Long, Seq[Institution], Seq[AnEnum])](tag, "EnumSeqTest") { + | class EnumSeqTestTable(tag: Tag) extends Table[(Long, List[Institution], List[AnEnum])](tag, "EnumSeqTest") { | def id = column[Long]("id", O.AutoInc, O.PrimaryKey) - | def institutions = column[Seq[Institution]]("institutions") - | def enums = column[Seq[AnEnum]]("enums") + | def institutions = column[List[Institution]]("institutions") + | def enums = column[List[AnEnum]]("enums") | | def * = (id, institutions, enums) | } diff --git a/slick/src/test/scala/arrays/SlickPgArrayTests.scala b/slick/src/test/scala/pl/iterators/kebs/slick/arrays/SlickPgArrayTests.scala similarity index 78% rename from slick/src/test/scala/arrays/SlickPgArrayTests.scala rename to slick/src/test/scala/pl/iterators/kebs/slick/arrays/SlickPgArrayTests.scala index 2218d9a2..4cb6c189 100644 --- a/slick/src/test/scala/arrays/SlickPgArrayTests.scala +++ b/slick/src/test/scala/pl/iterators/kebs/slick/arrays/SlickPgArrayTests.scala @@ -1,20 +1,21 @@ -package arrays +package pl.iterators.kebs.slick.arrays import com.github.tminglei.slickpg._ import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers +import pl.iterators.kebs.core.macros.CaseClass1ToValueClass import slick.lifted.ProvenShape import java.time.YearMonth import java.util.UUID class SlickPgArrayTests extends AnyFunSuite with Matchers { - import pl.iterators.kebs.Kebs import pl.iterators.kebs.instances.time.YearMonthString + import pl.iterators.kebs.slick.KebsSlickSupport - trait PostgresDriver extends ExPostgresProfile with PgArraySupport { + trait PostgresDriver extends ExPostgresProfile with PgArraySupport with KebsSlickSupport { override val api: ArrayAPI = new ArrayAPI {} - trait ArrayAPI extends super.API with ArrayImplicits with Kebs with YearMonthString + trait ArrayAPI extends ExtPostgresAPI with ArrayImplicits with KebsValueClassLikeImplicits with CaseClass1ToValueClass with KebsInstanceConverterImplicits with YearMonthString } object PostgresDriver extends PostgresDriver @@ -40,11 +41,9 @@ class SlickPgArrayTests extends AnyFunSuite with Matchers { override def * : ProvenShape[Test] = (id, ccList) <> ((Test.apply _).tupled, Test.unapply) } - test("No CaseClass1Rep implicits derived") { - import pl.iterators.kebs.macros.CaseClass1Rep - - "implicitly[CaseClass1Rep[YearMonth, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, YearMonth]]" shouldNot typeCheck + test("No ValueClassLike implicits derived") { + "implicitly[pl.iterators.kebs.core.macros.ValueClassLike[YearMonth, String]]" shouldNot typeCheck + "implicitly[pl.iterators.kebs.core.macros.ValueClassLike[String, YearMonth]]" shouldNot typeCheck } test("Case class list extension methods") { diff --git a/slick/src/test/scala/caseclasses/SlickPgTests.scala b/slick/src/test/scala/pl/iterators/kebs/slick/caseclasses/SlickPgTests.scala similarity index 88% rename from slick/src/test/scala/caseclasses/SlickPgTests.scala rename to slick/src/test/scala/pl/iterators/kebs/slick/caseclasses/SlickPgTests.scala index ce07571a..b5183db1 100644 --- a/slick/src/test/scala/caseclasses/SlickPgTests.scala +++ b/slick/src/test/scala/pl/iterators/kebs/slick/caseclasses/SlickPgTests.scala @@ -1,22 +1,24 @@ -package caseclasses +package pl.iterators.kebs.slick.caseclasses import com.github.tminglei.slickpg._ import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers +import pl.iterators.kebs.core.macros.CaseClass1ToValueClass import java.util.UUID class SlickPgTests extends AnyFunSuite with Matchers { - import pl.iterators.kebs.Kebs + + import pl.iterators.kebs.slick.KebsSlickSupport import slick.lifted.ProvenShape case class ServiceLineName(name: String) case class Id(id: Int) case class ServiceLine(id: Id, name: ServiceLineName) - trait PostgresDriver extends ExPostgresProfile { - override val api = PostgresApi - object PostgresApi extends API with Kebs + trait PostgresDriver extends ExPostgresProfile with KebsSlickSupport { + override val api: PostgresApi.type = PostgresApi + object PostgresApi extends ExtPostgresAPI with KebsValueClassLikeImplicits with CaseClass1ToValueClass with KebsBasicImplicits } object PostgresDriver extends PostgresDriver @@ -37,7 +39,7 @@ class SlickPgTests extends AnyFunSuite with Matchers { | def id: Rep[Id] = column[Id]("id", O.PrimaryKey) | def name: Rep[ServiceLineName] = column[ServiceLineName]("name") | - | override def * : ProvenShape[ServiceLine] = (id, name) <> (ServiceLine.tupled, ServiceLine.unapply) + | override def * : ProvenShape[ServiceLine] = (id, name) <> ((ServiceLine.apply _).tupled, ServiceLine.unapply) | } """.stripMargin should compile } diff --git a/slick/src/test/scala/enums/SlickMappedEnumColumnTypeTests.scala b/slick/src/test/scala/pl/iterators/kebs/slick/enums/SlickMappedEnumColumnTypeTests.scala similarity index 56% rename from slick/src/test/scala/enums/SlickMappedEnumColumnTypeTests.scala rename to slick/src/test/scala/pl/iterators/kebs/slick/enums/SlickMappedEnumColumnTypeTests.scala index 68e67755..292f94b1 100644 --- a/slick/src/test/scala/enums/SlickMappedEnumColumnTypeTests.scala +++ b/slick/src/test/scala/pl/iterators/kebs/slick/enums/SlickMappedEnumColumnTypeTests.scala @@ -1,12 +1,28 @@ -package enums +package pl.iterators.kebs.slick.enums +import com.github.tminglei.slickpg.ExPostgresProfile import enumeratum.{Enum, EnumEntry} import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers +import pl.iterators.kebs.enumeratum.KebsEnumeratum -class SlickMappedEnumColumnTypeTests extends AnyFunSuite with Matchers { - import slick.jdbc.PostgresProfile.api._ - import pl.iterators.kebs.enums.lowercase._ +class SlickMappedEnumColumnTypeTests extends AnyFunSuite with Matchers with KebsEnumeratum { + import pl.iterators.kebs.slick.KebsSlickSupport + + trait PostgresDriver extends ExPostgresProfile with KebsSlickSupport { + override val api: EnumAPI = new EnumAPI {} + trait EnumAPI extends ExtPostgresAPI with KebsBasicImplicits with KebsValueClassLikeImplicits with EnumImplicits + } + object PostgresDriver extends PostgresDriver + + abstract class BaseTable[T](tag: BaseTable.Tag, tableName: String) extends BaseTable.driver.Table[T](tag, tableName) { + protected val driver: PostgresDriver = BaseTable.driver + } + + object BaseTable { + protected val driver = PostgresDriver + type Tag = driver.api.Tag + } sealed trait WorkerAccountStatus extends EnumEntry object WorkerAccountStatus extends Enum[WorkerAccountStatus] { @@ -16,13 +32,14 @@ class SlickMappedEnumColumnTypeTests extends AnyFunSuite with Matchers { override val values = findValues } - + import PostgresDriver.api._ test("MappedColumnType for enum entries") { "implicitly[BaseColumnType[WorkerAccountStatus]]" should compile } test("Slick mapping") { - class ATable(tag: Tag) extends Table[(Long, String, WorkerAccountStatus)](tag, "A_TABLE") { + class ATable(tag: BaseTable.Tag) extends BaseTable[(Long, String, WorkerAccountStatus)](tag, "A_TABLE") { + import driver.api._ def id = column[Long]("id") def name = column[String]("name") def status = column[WorkerAccountStatus]("status") diff --git a/slick/src/test/scala/enums/SlickMappedValueEnumColumnTypeTests.scala b/slick/src/test/scala/pl/iterators/kebs/slick/enums/SlickMappedValueEnumColumnTypeTests.scala similarity index 67% rename from slick/src/test/scala/enums/SlickMappedValueEnumColumnTypeTests.scala rename to slick/src/test/scala/pl/iterators/kebs/slick/enums/SlickMappedValueEnumColumnTypeTests.scala index 2e3ff351..df16ea38 100644 --- a/slick/src/test/scala/enums/SlickMappedValueEnumColumnTypeTests.scala +++ b/slick/src/test/scala/pl/iterators/kebs/slick/enums/SlickMappedValueEnumColumnTypeTests.scala @@ -1,14 +1,22 @@ -package enums +package pl.iterators.kebs.slick.enums import enumeratum.values.{IntEnum, IntEnumEntry} import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers +import pl.iterators.kebs.core.enums.ValueEnumLikeEntry +import pl.iterators.kebs.enumeratum.KebsValueEnumeratum +import pl.iterators.kebs.slick.KebsSlickSupport +import slick.jdbc.PostgresProfile -class SlickMappedValueEnumColumnTypeTests extends AnyFunSuite with Matchers { - import slick.jdbc.PostgresProfile.api._ - import pl.iterators.kebs.enums._ +class SlickMappedValueEnumColumnTypeTests extends AnyFunSuite with Matchers with KebsValueEnumeratum { + object MyPostgresProfile extends PostgresProfile with KebsSlickSupport { + override val api: APITagged = new APITagged {} + trait APITagged extends JdbcAPI with EnumImplicits + } + + import MyPostgresProfile.api._ - sealed abstract class WorkerAccountStatusInt(val value: Int) extends IntEnumEntry + sealed abstract class WorkerAccountStatusInt(val value: Int) extends IntEnumEntry with ValueEnumLikeEntry[Int] object WorkerAccountStatusInt extends IntEnum[WorkerAccountStatusInt] { case object Unapproved extends WorkerAccountStatusInt(0) case object Active extends WorkerAccountStatusInt(1) @@ -16,7 +24,7 @@ class SlickMappedValueEnumColumnTypeTests extends AnyFunSuite with Matchers { override val values = findValues } - + test("MappedColumnType for value enum entries") { "implicitly[BaseColumnType[WorkerAccountStatusInt]]" should compile } diff --git a/slick/src/test/scala/hstore/SlickPgHstoreColumnTypeTests.scala b/slick/src/test/scala/pl/iterators/kebs/slick/hstore/SlickPgHstoreColumnTypeTests.scala similarity index 92% rename from slick/src/test/scala/hstore/SlickPgHstoreColumnTypeTests.scala rename to slick/src/test/scala/pl/iterators/kebs/slick/hstore/SlickPgHstoreColumnTypeTests.scala index 5f682c87..c191054f 100644 --- a/slick/src/test/scala/hstore/SlickPgHstoreColumnTypeTests.scala +++ b/slick/src/test/scala/pl/iterators/kebs/slick/hstore/SlickPgHstoreColumnTypeTests.scala @@ -1,18 +1,19 @@ -package hstore +package pl.iterators.kebs.slick.hstore import com.github.tminglei.slickpg._ import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers +import pl.iterators.kebs.core.macros.CaseClass1ToValueClass class SlickPgHstoreColumnTypeTests extends AnyFunSuite with Matchers { - import pl.iterators.kebs.Kebs import pl.iterators.kebs.instances.time.{DayOfWeekInt, YearMonthString} import pl.iterators.kebs.instances.time.mixins.InstantEpochMilliLong + import pl.iterators.kebs.slick.KebsSlickSupport import java.time.{DayOfWeek, YearMonth, Instant} - object MyPostgresProfile extends ExPostgresProfile with PgHStoreSupport { + object MyPostgresProfile extends ExPostgresProfile with PgHStoreSupport with KebsSlickSupport { override val api: APIWithHStore = new APIWithHStore {} - trait APIWithHStore extends super.API with HStoreImplicits with Kebs with YearMonthString with DayOfWeekInt with InstantEpochMilliLong + trait APIWithHStore extends ExtPostgresAPI with HStoreImplicits with KebsBasicImplicits with KebsInstanceConverterImplicits with YearMonthString with DayOfWeekInt with InstantEpochMilliLong with KebsValueClassLikeImplicits with CaseClass1ToValueClass } case class CategoryName(name: String) @@ -20,15 +21,15 @@ class SlickPgHstoreColumnTypeTests extends AnyFunSuite with Matchers { import MyPostgresProfile.api._ - test("No CaseClass1Rep implicits derived") { - import pl.iterators.kebs.macros.CaseClass1Rep + test("No ValueClassLike implicits derived") { + import pl.iterators.kebs.core.macros.ValueClassLike - "implicitly[CaseClass1Rep[YearMonth, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, YearMonth]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[DayOfWeek, Int]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Int, DayOfWeek]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Instant, Long]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Long, Instant]]" shouldNot typeCheck + "implicitly[ValueClassLike[YearMonth, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, YearMonth]]" shouldNot typeCheck + "implicitly[ValueClassLike[DayOfWeek, Int]]" shouldNot typeCheck + "implicitly[ValueClassLike[Int, DayOfWeek]]" shouldNot typeCheck + "implicitly[ValueClassLike[Instant, Long]]" shouldNot typeCheck + "implicitly[ValueClassLike[Long, Instant]]" shouldNot typeCheck } test("Value classes to HStore mapping") { @@ -42,7 +43,7 @@ class SlickPgHstoreColumnTypeTests extends AnyFunSuite with Matchers { |""".stripMargin should compile } - /* CaseClass1Rep[Obj, String] */ + /* ValueClassLike[Obj, String] */ test("Map[Obj[String], String] column type") { """ |class HStoreTestTable(tag: Tag) extends Table[(Long, Map[YearMonth, String])](tag, "HStoreTestTable") { @@ -131,7 +132,7 @@ class SlickPgHstoreColumnTypeTests extends AnyFunSuite with Matchers { |""".stripMargin should compile } - /* CaseClass1Rep[Obj, Int] */ + /* ValueClassLike[Obj, Int] */ test("Map[Obj[Int], String] column type") { """ |class HStoreTestTable(tag: Tag) extends Table[(Long, Map[DayOfWeek, String])](tag, "HStoreTestTable") { @@ -220,7 +221,7 @@ class SlickPgHstoreColumnTypeTests extends AnyFunSuite with Matchers { |""".stripMargin should compile } - /* CaseClass1Rep[Obj, Long] */ + /* ValueClassLike[Obj, Long] */ test("Map[Obj[Long], String] column type") { """ |class HStoreTestTable(tag: Tag) extends Table[(Long, Map[Instant, String])](tag, "HStoreTestTable") { diff --git a/slick/src/test/scala/hstore/SlickPgHstoreTests.scala b/slick/src/test/scala/pl/iterators/kebs/slick/hstore/SlickPgHstoreTests.scala similarity index 87% rename from slick/src/test/scala/hstore/SlickPgHstoreTests.scala rename to slick/src/test/scala/pl/iterators/kebs/slick/hstore/SlickPgHstoreTests.scala index 25726f1a..c6dffdff 100644 --- a/slick/src/test/scala/hstore/SlickPgHstoreTests.scala +++ b/slick/src/test/scala/pl/iterators/kebs/slick/hstore/SlickPgHstoreTests.scala @@ -1,20 +1,21 @@ -package hstore +package pl.iterators.kebs.slick.hstore import com.github.tminglei.slickpg._ import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers +import pl.iterators.kebs.core.macros.CaseClass1ToValueClass import slick.lifted.ProvenShape import java.time.YearMonth import java.util.UUID class SlickPgHstoreTests extends AnyFunSuite with Matchers { - import pl.iterators.kebs.Kebs import pl.iterators.kebs.instances.time.YearMonthString + import pl.iterators.kebs.slick.KebsSlickSupport - trait PostgresDriver extends ExPostgresProfile with PgArraySupport with PgHStoreSupport { + trait PostgresDriver extends ExPostgresProfile with PgArraySupport with PgHStoreSupport with KebsSlickSupport { override val api: HstoreAPI = new HstoreAPI {} - trait HstoreAPI extends super.API with ArrayImplicits with HStoreImplicits with Kebs with YearMonthString + trait HstoreAPI extends ExtPostgresAPI with ArrayImplicits with HStoreImplicits with KebsBasicImplicits with YearMonthString with KebsValueClassLikeImplicits with CaseClass1ToValueClass with KebsInstanceConverterImplicits } object PostgresDriver extends PostgresDriver @@ -41,11 +42,11 @@ class SlickPgHstoreTests extends AnyFunSuite with Matchers { override def * : ProvenShape[Test] = (id, hstoreMap) <> ((Test.apply _).tupled, Test.unapply) } - test("No CaseClass1Rep implicits derived") { - import pl.iterators.kebs.macros.CaseClass1Rep + test("No ValueClassLike implicits derived") { + import pl.iterators.kebs.core.macros.ValueClassLike - "implicitly[CaseClass1Rep[YearMonth, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, YearMonth]]" shouldNot typeCheck + "implicitly[ValueClassLike[YearMonth, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, YearMonth]]" shouldNot typeCheck } test("Case class hstore extension methods") { diff --git a/spray-json-macros/src/main/scala/pl/iterators/kebs/json/macros/KebsSprayMacros.scala b/spray-json-macros/src/main/scala/pl/iterators/kebs/json/macros/KebsSprayMacros.scala index cd7f1d6c..97d9be9e 100644 --- a/spray-json-macros/src/main/scala/pl/iterators/kebs/json/macros/KebsSprayMacros.scala +++ b/spray-json-macros/src/main/scala/pl/iterators/kebs/json/macros/KebsSprayMacros.scala @@ -1,16 +1,13 @@ package pl.iterators.kebs.json.macros -import pl.iterators.kebs.json.noflat -import pl.iterators.kebs.macros.MacroUtils +import pl.iterators.kebs.core.macros.MacroUtils import spray.json.{JsonFormat, JsonReader, JsonWriter, NullOptions, RootJsonFormat} -import scala.collection.immutable.Seq import scala.reflect.macros._ class KebsSprayMacros(override val c: whitebox.Context) extends MacroUtils { import c.universe._ - private val noflatType = typeOf[noflat] private val jsonFormat = typeOf[JsonFormat[_]] private val jsonReader = typeOf[JsonReader[_]] private val jsonWriter = typeOf[JsonWriter[_]] @@ -72,12 +69,11 @@ class KebsSprayMacros(override val c: whitebox.Context) extends MacroUtils { assertCaseClass(T, s"To materialize RootJsonFormat, ${T.typeSymbol} must be a case class") def isLookingFor(t: Type) = c.enclosingImplicits.headOption.exists(_.pt.typeSymbol == t.typeSymbol) - def noflat(t: Type) = t.typeSymbol.annotations.exists(_.tree.tpe =:= noflatType) val jsonFormat = caseAccessors(T) match { case Nil => materializeJsonFormat0(T) case (_1 :: Nil) => - if (preferFlat && (isLookingFor(jsonFormatOf(T)) || isLookingFor(jsonWriterOf(T)) || isLookingFor(jsonReaderOf(T))) && !noflat(T)) + if (preferFlat && (isLookingFor(jsonFormatOf(T)) || isLookingFor(jsonWriterOf(T)) || isLookingFor(jsonReaderOf(T)))) c.abort(c.enclosingPosition, "Flat format preferred") else materializeRootJsonFormat(T, List(_1)) case fields => materializeRootJsonFormat(T, fields) @@ -102,11 +98,9 @@ class KebsSprayMacros(override val c: whitebox.Context) extends MacroUtils { } object KebsSprayMacros { - class NoflatVariant(context: whitebox.Context) extends KebsSprayMacros(context) { - override protected val preferFlat = false - } + class SnakifyVariant(context: whitebox.Context) extends KebsSprayMacros(context) { - import pl.iterators.kebs.macros.namingconventions.SnakifyVariant.snakify + import pl.iterators.kebs.core.macros.namingconventions.SnakifyVariant.snakify import c.universe._ override protected def extractJsonFieldNames(fields: List[MethodSymbol]) = super.extractJsonFieldNames(fields).map(snakify) @@ -117,4 +111,5 @@ object KebsSprayMacros { override protected def extractJsonFieldNames(fields: List[MethodSymbol]) = super.extractJsonFieldNames(fields).map(_.capitalize) } + } diff --git a/spray-json-macros/src/main/scala/pl/iterators/kebs/json/noflat.scala b/spray-json-macros/src/main/scala/pl/iterators/kebs/json/noflat.scala deleted file mode 100644 index 29b19763..00000000 --- a/spray-json-macros/src/main/scala/pl/iterators/kebs/json/noflat.scala +++ /dev/null @@ -1,3 +0,0 @@ -package pl.iterators.kebs.json - -final class noflat extends scala.annotation.StaticAnnotation diff --git a/spray-json/src/main/scala/pl/iterators/kebs/json/KebsEnumFormats.scala b/spray-json/src/main/scala/pl/iterators/kebs/json/KebsEnumFormats.scala index 05b7d4ba..0c1b7d7d 100644 --- a/spray-json/src/main/scala/pl/iterators/kebs/json/KebsEnumFormats.scala +++ b/spray-json/src/main/scala/pl/iterators/kebs/json/KebsEnumFormats.scala @@ -1,61 +1,59 @@ package pl.iterators.kebs.json -import enumeratum.values.{ValueEnum, ValueEnumEntry} -import enumeratum.{Enum, EnumEntry} -import pl.iterators.kebs.macros.enums.{EnumOf, ValueEnumOf} +import pl.iterators.kebs.core.enums.{EnumLike, ValueEnumLike, ValueEnumLikeEntry} import spray.json.{JsString, JsValue, JsonFormat} trait SprayJsonEnum { - @inline protected final def enumNameDeserializationError[E <: EnumEntry](`enum`: Enum[E], name: String) = { - val enumNames = `enum`.namesToValuesMap.values.mkString(", ") + @inline protected final def enumNameDeserializationError[E](`enum`: EnumLike[E], name: String) = { + val enumNames = `enum`.getNamesToValuesMap.values.mkString(", ") spray.json.deserializationError(s"$name should be one of $enumNames") } - @inline protected final def enumValueDeserializationError[E <: EnumEntry](`enum`: Enum[E], value: JsValue) = { - val enumNames = `enum`.namesToValuesMap.values.mkString(", ") + @inline protected final def enumValueDeserializationError[E](`enum`: EnumLike[E], value: JsValue) = { + val enumNames = `enum`.getNamesToValuesMap.values.mkString(", ") spray.json.deserializationError(s"$value should be a string of value $enumNames") } - protected final def enumJsonFormat[E <: EnumEntry](`enum`: Enum[E], map: E => String, comap: String => Option[E]) = new JsonFormat[E] { + protected final def enumJsonFormat[E](`enum`: EnumLike[E], map: E => String, comap: String => Option[E]) = new JsonFormat[E] { override def write(obj: E): JsValue = JsString(map(obj)) override def read(json: JsValue): E = json match { case JsString(name) => comap(name).getOrElse(enumNameDeserializationError(`enum`, name)) case _ => enumValueDeserializationError(`enum`, json) } } - def jsonFormat[E <: EnumEntry](`enum`: Enum[E]) = enumJsonFormat[E](`enum`, _.entryName, `enum`.withNameInsensitiveOption(_)) - def lowercaseJsonFormat[E <: EnumEntry](`enum`: Enum[E]) = - enumJsonFormat[E](`enum`, _.entryName.toLowerCase, `enum`.withNameLowercaseOnlyOption(_)) - def uppercaseJsonFormat[E <: EnumEntry](`enum`: Enum[E]) = - enumJsonFormat[E](`enum`, _.entryName.toUpperCase, `enum`.withNameUppercaseOnlyOption(_)) + def jsonFormat[E](`enum`: EnumLike[E]) = enumJsonFormat[E](`enum`, _.toString, `enum`.withNameInsensitiveOption(_)) + def lowercaseJsonFormat[E](`enum`: EnumLike[E]) = + enumJsonFormat[E](`enum`, _.toString.toLowerCase, `enum`.withNameLowercaseOnlyOption(_)) + def uppercaseJsonFormat[E](`enum`: EnumLike[E]) = + enumJsonFormat[E](`enum`, _.toString.toUpperCase, `enum`.withNameUppercaseOnlyOption(_)) } trait SprayJsonValueEnum { - @inline protected final def valueEnumDeserializationError[V, E <: ValueEnumEntry[V]](`enum`: ValueEnum[V, E], value: V) = { - val enumValues = `enum`.valuesToEntriesMap.keys.mkString(", ") + @inline protected final def valueEnumDeserializationError[V, E <: ValueEnumLikeEntry[V]](`enum`: ValueEnumLike[V, E], value: V) = { + val enumValues = `enum`.getValuesToEntriesMap.keys.mkString(", ") spray.json.deserializationError(s"$value is not a member of $enumValues") } - def jsonFormat[V, E <: ValueEnumEntry[V]](`enum`: ValueEnum[V, E])(implicit baseJsonFormat: JsonFormat[V]) = new JsonFormat[E] { + def jsonFormatValue[V, E <: ValueEnumLikeEntry[V]](`enum`: ValueEnumLike[V, E])(implicit baseJsonFormat: JsonFormat[V]) = new JsonFormat[E] { override def write(obj: E): JsValue = baseJsonFormat.write(obj.value) override def read(json: JsValue): E = { val value = baseJsonFormat.read(json) - `enum`.withValueOpt(value).getOrElse(valueEnumDeserializationError(`enum`, value)) + `enum`.withValueOption(value).getOrElse(valueEnumDeserializationError(`enum`, value)) } } } trait KebsEnumFormats extends SprayJsonEnum with SprayJsonValueEnum { - implicit def jsonEnumFormat[E <: EnumEntry](implicit ev: EnumOf[E]): JsonFormat[E] = jsonFormat(ev.`enum`) - implicit def jsonValueEnumFormat[V, E <: ValueEnumEntry[V]](implicit ev: ValueEnumOf[V, E], - baseJsonFormat: JsonFormat[V]): JsonFormat[E] = jsonFormat(ev.valueEnum) + implicit def jsonEnumFormat[E](implicit ev: EnumLike[E]): JsonFormat[E] = jsonFormat(ev) + implicit def jsonValueEnumFormat[V, E <: ValueEnumLikeEntry[V]](implicit ev: ValueEnumLike[V, E], + baseJsonFormat: JsonFormat[V]): JsonFormat[E] = jsonFormatValue(ev) trait Uppercase extends SprayJsonEnum { - implicit def jsonEnumFormat[E <: EnumEntry](implicit ev: EnumOf[E]): JsonFormat[E] = uppercaseJsonFormat(ev.`enum`) + implicit def jsonEnumFormat[E](implicit ev: EnumLike[E]): JsonFormat[E] = uppercaseJsonFormat(ev) } trait Lowercase extends SprayJsonEnum { - implicit def jsonEnumFormat[E <: EnumEntry](implicit ev: EnumOf[E]): JsonFormat[E] = lowercaseJsonFormat(ev.`enum`) + implicit def jsonEnumFormat[E](implicit ev: EnumLike[E]): JsonFormat[E] = lowercaseJsonFormat(ev) } } diff --git a/spray-json/src/main/scala/pl/iterators/kebs/json/KebsSpray.scala b/spray-json/src/main/scala/pl/iterators/kebs/json/KebsSpray.scala index 3917e00d..eb64b581 100644 --- a/spray-json/src/main/scala/pl/iterators/kebs/json/KebsSpray.scala +++ b/spray-json/src/main/scala/pl/iterators/kebs/json/KebsSpray.scala @@ -1,13 +1,13 @@ package pl.iterators.kebs.json -import pl.iterators.kebs.macros.CaseClass1Rep +import pl.iterators.kebs.core.instances.InstanceConverter +import pl.iterators.kebs.core.macros.{CaseClass1ToValueClass, ValueClassLike} import spray.json.{DefaultJsonProtocol, JsValue, JsonFormat, JsonReader, RootJsonFormat} -import pl.iterators.kebs.instances.InstanceConverter -trait KebsSpray { self: DefaultJsonProtocol => +trait KebsSpray extends CaseClass1ToValueClass { self: DefaultJsonProtocol => import macros.KebsSprayMacros implicit def jsonFormatN[T <: Product]: RootJsonFormat[T] = macro KebsSprayMacros.materializeRootFormat[T] - implicit def jsonFlatFormat[T, A](implicit rep: CaseClass1Rep[T, A], baseJsonFormat: JsonFormat[A]): JsonFormat[T] = { + implicit def jsonFlatFormat[T, A](implicit rep: ValueClassLike[T, A], baseJsonFormat: JsonFormat[A]): JsonFormat[T] = { val reader: JsValue => T = json => rep.apply(baseJsonFormat.read(json)) val writer: T => JsValue = obj => baseJsonFormat.write(rep.unapply(obj)) jsonFormat[T](reader, writer) @@ -29,10 +29,6 @@ object KebsSpray { import macros.KebsSprayMacros implicit def snakifiedJsonFormatN[T <: Product]: RootJsonFormat[T] = macro KebsSprayMacros.SnakifyVariant.materializeRootFormat[T] } - trait NoFlat extends KebsSpray { self: DefaultJsonProtocol => - import macros.KebsSprayMacros - implicit def noflat_jsonFormatN[T <: Product]: RootJsonFormat[T] = macro KebsSprayMacros.NoflatVariant.materializeRootFormat[T] - } trait Capitalized extends KebsSpray { self: DefaultJsonProtocol => import macros.KebsSprayMacros implicit def capitalizedJsonFormatN[T <: Product]: RootJsonFormat[T] = diff --git a/spray-json/src/test/scala/SprayJsonFormatNoFlatTests.scala b/spray-json/src/test/scala/SprayJsonFormatNoFlatTests.scala deleted file mode 100644 index 75b18ed8..00000000 --- a/spray-json/src/test/scala/SprayJsonFormatNoFlatTests.scala +++ /dev/null @@ -1,35 +0,0 @@ -import pl.iterators.kebs.json.KebsSpray -import spray.json._ -import org.scalatest.funsuite.AnyFunSuite -import org.scalatest.matchers.should.Matchers - -class SprayJsonFormatNoFlatTests extends AnyFunSuite with Matchers { - object KebsProtocol extends DefaultJsonProtocol with KebsSpray.NoFlat - import KebsProtocol._ - - case class C(i: Int) - - test("No-flat format") { - val jf = implicitly[JsonFormat[C]] - jf.write(C(10)) shouldBe JsObject("i" -> JsNumber(10)) - jf.read(JsObject("i" -> JsNumber(10))) shouldBe C(10) - } - - case class Book(name: String, chapters: List[Chapter]) - case class Chapter(name: String) - - test("compound") { - val json = - """ - | { - | "name": "Functional Programming in Scala", - | "chapters": [{"name":"first"}, {"name":"second"}] - | } - """.stripMargin - - json.parseJson.convertTo[Book] shouldBe Book( - name = "Functional Programming in Scala", - chapters = List(Chapter("first"), Chapter("second")) - ) - } -} diff --git a/spray-json/src/test/scala/SprayEnumJsonFormatTests.scala b/spray-json/src/test/scala/pl/iterators/kebs/json/SprayEnumJsonFormatTests.scala similarity index 95% rename from spray-json/src/test/scala/SprayEnumJsonFormatTests.scala rename to spray-json/src/test/scala/pl/iterators/kebs/json/SprayEnumJsonFormatTests.scala index bc8c5aff..a69bede3 100644 --- a/spray-json/src/test/scala/SprayEnumJsonFormatTests.scala +++ b/spray-json/src/test/scala/pl/iterators/kebs/json/SprayEnumJsonFormatTests.scala @@ -1,10 +1,13 @@ +package pl.iterators.kebs.json + import enumeratum.{Enum, EnumEntry} import pl.iterators.kebs.json.{KebsEnumFormats, KebsSpray} import spray.json._ import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers +import pl.iterators.kebs.enumeratum.KebsEnumeratum -class SprayEnumJsonFormatTests extends AnyFunSuite with Matchers { +class SprayEnumJsonFormatTests extends AnyFunSuite with Matchers with KebsEnumeratum { sealed trait Greeting extends EnumEntry object Greeting extends Enum[Greeting] { diff --git a/spray-json/src/test/scala/SprayJsonFormatCapitalizedVariantTests.scala b/spray-json/src/test/scala/pl/iterators/kebs/json/SprayJsonFormatCapitalizedVariantTests.scala similarity index 92% rename from spray-json/src/test/scala/SprayJsonFormatCapitalizedVariantTests.scala rename to spray-json/src/test/scala/pl/iterators/kebs/json/SprayJsonFormatCapitalizedVariantTests.scala index 2ed9f5ae..982869b3 100644 --- a/spray-json/src/test/scala/SprayJsonFormatCapitalizedVariantTests.scala +++ b/spray-json/src/test/scala/pl/iterators/kebs/json/SprayJsonFormatCapitalizedVariantTests.scala @@ -1,7 +1,8 @@ -import pl.iterators.kebs.json.KebsSpray -import spray.json.{DefaultJsonProtocol, JsArray, JsBoolean, JsNull, JsNumber, JsObject, JsString, JsonFormat, RootJsonFormat} +package pl.iterators.kebs.json + import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers +import spray.json.{DefaultJsonProtocol, JsNumber, JsObject, JsString, JsonFormat, RootJsonFormat} class SprayJsonFormatCapitalizedVariantTests extends AnyFunSuite with Matchers { object KebsProtocol extends DefaultJsonProtocol with KebsSpray.Capitalized diff --git a/spray-json/src/test/scala/SprayJsonFormatSnakifyVariantTests.scala b/spray-json/src/test/scala/pl/iterators/kebs/json/SprayJsonFormatSnakifyVariantTests.scala similarity index 99% rename from spray-json/src/test/scala/SprayJsonFormatSnakifyVariantTests.scala rename to spray-json/src/test/scala/pl/iterators/kebs/json/SprayJsonFormatSnakifyVariantTests.scala index faacd8fb..a943606a 100644 --- a/spray-json/src/test/scala/SprayJsonFormatSnakifyVariantTests.scala +++ b/spray-json/src/test/scala/pl/iterators/kebs/json/SprayJsonFormatSnakifyVariantTests.scala @@ -1,7 +1,8 @@ -import pl.iterators.kebs.json.KebsSpray -import spray.json.{DefaultJsonProtocol, JsArray, JsBoolean, JsNull, JsNumber, JsObject, JsString, JsonFormat, NullOptions, RootJsonFormat} +package pl.iterators.kebs.json + import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers +import spray.json.{DefaultJsonProtocol, JsArray, JsBoolean, JsNull, JsNumber, JsObject, JsString, JsonFormat, NullOptions, RootJsonFormat} class SprayJsonFormatSnakifyVariantTests extends AnyFunSuite with Matchers { object KebsProtocol extends DefaultJsonProtocol with KebsSpray.Snakified @@ -92,8 +93,8 @@ class SprayJsonFormatSnakifyVariantTests extends AnyFunSuite with Matchers { test("Root format snakified with NullOptions - case class with > 22 fields (issue #73)") { object KebsProtocolNullOptions extends DefaultJsonProtocol with KebsSpray.Snakified with NullOptions - import model._ import KebsProtocolNullOptions._ + import model._ val jf = implicitly[JsonFormat[ClassWith23Fields]] val obj = ClassWith23Fields.Example diff --git a/spray-json/src/test/scala/SprayJsonFormatTests.scala b/spray-json/src/test/scala/pl/iterators/kebs/json/SprayJsonFormatTests.scala similarity index 93% rename from spray-json/src/test/scala/SprayJsonFormatTests.scala rename to spray-json/src/test/scala/pl/iterators/kebs/json/SprayJsonFormatTests.scala index 89577cd9..b70df1e7 100644 --- a/spray-json/src/test/scala/SprayJsonFormatTests.scala +++ b/spray-json/src/test/scala/pl/iterators/kebs/json/SprayJsonFormatTests.scala @@ -1,9 +1,10 @@ -import java.util.UUID +package pl.iterators.kebs.json -import pl.iterators.kebs.json.{KebsSpray, noflat} -import spray.json._ import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers +import spray.json._ + +import java.util.UUID class SprayJsonFormatTests extends AnyFunSuite with Matchers { object KebsProtocol extends DefaultJsonProtocol with KebsSpray @@ -138,27 +139,6 @@ class SprayJsonFormatTests extends AnyFunSuite with Matchers { ) } - case class BookNF(name: String, chapters: List[ChapterNF]) - @noflat case class ChapterNF(name: String) - - test("work with nested single field objects - noflat annotation") { - val json = JsObject( - "name" -> JsString("Functional Programming in Scala"), - "chapters" -> JsArray( - JsObject("name" -> JsString("first")), - JsObject("name" -> JsString("second")) - ) - ) - val instance = BookNF( - name = "Functional Programming in Scala", - chapters = List(ChapterNF("first"), ChapterNF("second")) - ) - - val jf = implicitly[RootJsonFormat[BookNF]] - jf.read(json) shouldBe instance - jf.write(instance) shouldBe json - } - test("Root format - case class with > 22 fields (issue #7)") { import model._ @@ -197,8 +177,8 @@ class SprayJsonFormatTests extends AnyFunSuite with Matchers { test("Root format with NullOptions - case class with > 22 fields (issue #73)") { object KebsProtocolNullOptions extends DefaultJsonProtocol with KebsSpray with NullOptions - import model._ import KebsProtocolNullOptions._ + import model._ val jf = implicitly[JsonFormat[ClassWith23Fields]] val obj = ClassWith23Fields.Example diff --git a/spray-json/src/test/scala/SprayValueEnumJsonFormatTests.scala b/spray-json/src/test/scala/pl/iterators/kebs/json/SprayValueEnumJsonFormatTests.scala similarity index 84% rename from spray-json/src/test/scala/SprayValueEnumJsonFormatTests.scala rename to spray-json/src/test/scala/pl/iterators/kebs/json/SprayValueEnumJsonFormatTests.scala index bfc3845c..b341bd4d 100644 --- a/spray-json/src/test/scala/SprayValueEnumJsonFormatTests.scala +++ b/spray-json/src/test/scala/pl/iterators/kebs/json/SprayValueEnumJsonFormatTests.scala @@ -1,11 +1,14 @@ +package pl.iterators.kebs.json + import enumeratum.values.{LongEnum, LongEnumEntry} -import pl.iterators.kebs.json.{KebsEnumFormats, KebsSpray} -import spray.json._ import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers +import pl.iterators.kebs.core.enums.ValueEnumLikeEntry +import pl.iterators.kebs.enumeratum.KebsValueEnumeratum +import spray.json._ -class SprayValueEnumJsonFormatTests extends AnyFunSuite with Matchers { - sealed abstract class LongGreeting(val value: Long) extends LongEnumEntry +class SprayValueEnumJsonFormatTests extends AnyFunSuite with Matchers with KebsValueEnumeratum { + sealed abstract class LongGreeting(val value: Long) extends LongEnumEntry with ValueEnumLikeEntry[Long] object LongGreeting extends LongEnum[LongGreeting] { val values = findValues diff --git a/spray-json/src/test/scala/instances/NetInstancesTests.scala b/spray-json/src/test/scala/pl/iterators/kebs/json/instances/NetInstancesTests.scala similarity index 68% rename from spray-json/src/test/scala/instances/NetInstancesTests.scala rename to spray-json/src/test/scala/pl/iterators/kebs/json/instances/NetInstancesTests.scala index 8e05c60d..5ea7bbaf 100644 --- a/spray-json/src/test/scala/instances/NetInstancesTests.scala +++ b/spray-json/src/test/scala/pl/iterators/kebs/json/instances/NetInstancesTests.scala @@ -1,9 +1,9 @@ -package instances +package pl.iterators.kebs.json.instances import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.instances.InstanceConverter.DecodeErrorException import pl.iterators.kebs.instances.net.URIString +import pl.iterators.kebs.core.instances.InstanceConverter.DecodeErrorException import pl.iterators.kebs.json.KebsSpray import spray.json._ @@ -27,10 +27,10 @@ class NetInstancesTests extends AnyFunSuite with Matchers with DefaultJsonProtoc assertThrows[DecodeErrorException](jf.read(JsString(value))) } - test("No CaseClass1Rep implicits derived") { - import pl.iterators.kebs.macros.CaseClass1Rep + test("No ValueClassLike implicits derived") { + import pl.iterators.kebs.core.macros.ValueClassLike - "implicitly[CaseClass1Rep[URI, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, URI]]" shouldNot typeCheck + "implicitly[ValueClassLike[URI, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, URI]]" shouldNot typeCheck } } diff --git a/spray-json/src/test/scala/instances/TimeInstancesMixinTests.scala b/spray-json/src/test/scala/pl/iterators/kebs/json/instances/TimeInstancesMixinTests.scala similarity index 81% rename from spray-json/src/test/scala/instances/TimeInstancesMixinTests.scala rename to spray-json/src/test/scala/pl/iterators/kebs/json/instances/TimeInstancesMixinTests.scala index 00950f5b..1b612e3b 100644 --- a/spray-json/src/test/scala/instances/TimeInstancesMixinTests.scala +++ b/spray-json/src/test/scala/pl/iterators/kebs/json/instances/TimeInstancesMixinTests.scala @@ -1,13 +1,13 @@ -package instances +package pl.iterators.kebs.json.instances import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.instances.InstanceConverter +import pl.iterators.kebs.instances.TimeInstances import pl.iterators.kebs.instances.time.LocalDateTimeString import pl.iterators.kebs.instances.time.mixins.{DurationNanosLong, InstantEpochMilliLong} -import pl.iterators.kebs.instances.TimeInstances +import pl.iterators.kebs.core.instances.InstanceConverter +import pl.iterators.kebs.core.macros.ValueClassLike import pl.iterators.kebs.json.KebsSpray -import pl.iterators.kebs.macros.CaseClass1Rep import spray.json._ import java.time._ @@ -19,8 +19,8 @@ class TimeInstancesMixinTests extends AnyFunSuite with Matchers { object TimeInstancesProtocol extends DefaultJsonProtocol with KebsSpray with InstantEpochMilliLong import TimeInstancesProtocol._ - "implicitly[CaseClass1Rep[Instant, Long]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Long, Instant]]" shouldNot typeCheck + "implicitly[ValueClassLike[Instant, Long]]" shouldNot typeCheck + "implicitly[ValueClassLike[Long, Instant]]" shouldNot typeCheck val jf = implicitly[JsonFormat[Instant]] val value = 123456789 @@ -34,10 +34,10 @@ class TimeInstancesMixinTests extends AnyFunSuite with Matchers { object TimeInstancesProtocol extends DefaultJsonProtocol with KebsSpray with DurationNanosLong with InstantEpochMilliLong import TimeInstancesProtocol._ - "implicitly[CaseClass1Rep[Instant, Long]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Long, Instant]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Duration, Long]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Long, Duration]]" shouldNot typeCheck + "implicitly[ValueClassLike[Instant, Long]]" shouldNot typeCheck + "implicitly[ValueClassLike[Long, Instant]]" shouldNot typeCheck + "implicitly[ValueClassLike[Duration, Long]]" shouldNot typeCheck + "implicitly[ValueClassLike[Long, Duration]]" shouldNot typeCheck val jf_duration = implicitly[JsonFormat[Duration]] val value_duration = 123456789 @@ -63,8 +63,8 @@ class TimeInstancesMixinTests extends AnyFunSuite with Matchers { } import TimeInstancesProtocol._ - "implicitly[CaseClass1Rep[LocalDateTime, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, LocalDateTime]]" shouldNot typeCheck + "implicitly[ValueClassLike[LocalDateTime, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, LocalDateTime]]" shouldNot typeCheck val jf = implicitly[JsonFormat[LocalDateTime]] val value = "2007/12/03 10:30" @@ -96,8 +96,8 @@ class TimeInstancesMixinTests extends AnyFunSuite with Matchers { } import TimeInstancesProtocol._ - "implicitly[CaseClass1Rep[LocalDateTime, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, LocalDateTime]]" shouldNot typeCheck + "implicitly[ValueClassLike[LocalDateTime, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, LocalDateTime]]" shouldNot typeCheck val jf = implicitly[JsonFormat[LocalDateTime]] val value = "2007/12/03 10:30" diff --git a/spray-json/src/test/scala/instances/TimeInstancesTests.scala b/spray-json/src/test/scala/pl/iterators/kebs/json/instances/TimeInstancesTests.scala similarity index 75% rename from spray-json/src/test/scala/instances/TimeInstancesTests.scala rename to spray-json/src/test/scala/pl/iterators/kebs/json/instances/TimeInstancesTests.scala index 8270c28f..e3df3b05 100644 --- a/spray-json/src/test/scala/instances/TimeInstancesTests.scala +++ b/spray-json/src/test/scala/pl/iterators/kebs/json/instances/TimeInstancesTests.scala @@ -1,8 +1,8 @@ -package instances +package pl.iterators.kebs.json.instances import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.instances.InstanceConverter.DecodeErrorException +import pl.iterators.kebs.core.instances.InstanceConverter.DecodeErrorException import pl.iterators.kebs.instances.TimeInstances import pl.iterators.kebs.json.KebsSpray import spray.json._ @@ -11,41 +11,41 @@ import java.time._ class TimeInstancesTests extends AnyFunSuite with Matchers with DefaultJsonProtocol with KebsSpray with TimeInstances { - test("No CaseClass1Rep implicits derived") { - import pl.iterators.kebs.macros.CaseClass1Rep - - "implicitly[CaseClass1Rep[DayOfWeek, Int]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Int, DayOfWeek]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Duration, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, Duration]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Instant, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, Instant]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[LocalDate, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, LocalDate]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[LocalDateTime, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, LocalDateTime]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[LocalTime, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, LocalTime]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Month, Int]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Int, Month]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[MonthDay, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, MonthDay]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[OffsetDateTime, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, OffsetDateTime]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[OffsetTime, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, OffsetTime]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Period, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, Period]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Year, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, Year]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[YearMonth, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, YearMonth]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[ZoneId, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, ZoneId]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[ZoneOffset, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, ZoneOffset]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[ZonedDateTime, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, ZonedDateTime]]" shouldNot typeCheck + test("No ValueClassLike implicits derived") { + import pl.iterators.kebs.core.macros.ValueClassLike + + "implicitly[ValueClassLike[DayOfWeek, Int]]" shouldNot typeCheck + "implicitly[ValueClassLike[Int, DayOfWeek]]" shouldNot typeCheck + "implicitly[ValueClassLike[Duration, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, Duration]]" shouldNot typeCheck + "implicitly[ValueClassLike[Instant, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, Instant]]" shouldNot typeCheck + "implicitly[ValueClassLike[LocalDate, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, LocalDate]]" shouldNot typeCheck + "implicitly[ValueClassLike[LocalDateTime, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, LocalDateTime]]" shouldNot typeCheck + "implicitly[ValueClassLike[LocalTime, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, LocalTime]]" shouldNot typeCheck + "implicitly[ValueClassLike[Month, Int]]" shouldNot typeCheck + "implicitly[ValueClassLike[Int, Month]]" shouldNot typeCheck + "implicitly[ValueClassLike[MonthDay, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, MonthDay]]" shouldNot typeCheck + "implicitly[ValueClassLike[OffsetDateTime, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, OffsetDateTime]]" shouldNot typeCheck + "implicitly[ValueClassLike[OffsetTime, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, OffsetTime]]" shouldNot typeCheck + "implicitly[ValueClassLike[Period, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, Period]]" shouldNot typeCheck + "implicitly[ValueClassLike[Year, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, Year]]" shouldNot typeCheck + "implicitly[ValueClassLike[YearMonth, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, YearMonth]]" shouldNot typeCheck + "implicitly[ValueClassLike[ZoneId, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, ZoneId]]" shouldNot typeCheck + "implicitly[ValueClassLike[ZoneOffset, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, ZoneOffset]]" shouldNot typeCheck + "implicitly[ValueClassLike[ZonedDateTime, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, ZonedDateTime]]" shouldNot typeCheck } test("DayOfWeek standard format") { diff --git a/spray-json/src/test/scala/instances/UtilInstancesTests.scala b/spray-json/src/test/scala/pl/iterators/kebs/json/instances/UtilInstancesTests.scala similarity index 70% rename from spray-json/src/test/scala/instances/UtilInstancesTests.scala rename to spray-json/src/test/scala/pl/iterators/kebs/json/instances/UtilInstancesTests.scala index 4409d8ee..4a525a44 100644 --- a/spray-json/src/test/scala/instances/UtilInstancesTests.scala +++ b/spray-json/src/test/scala/pl/iterators/kebs/json/instances/UtilInstancesTests.scala @@ -1,8 +1,8 @@ -package instances +package pl.iterators.kebs.json.instances import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.instances.InstanceConverter.DecodeErrorException +import pl.iterators.kebs.core.instances.InstanceConverter.DecodeErrorException import pl.iterators.kebs.instances.UtilInstances import pl.iterators.kebs.json.KebsSpray import spray.json._ @@ -11,15 +11,15 @@ import java.util.{Currency, Locale, UUID} class UtilInstancesTests extends AnyFunSuite with Matchers with DefaultJsonProtocol with KebsSpray with UtilInstances { - test("No CaseClass1Rep implicits derived") { - import pl.iterators.kebs.macros.CaseClass1Rep + test("No ValueClassLike implicits derived") { + import pl.iterators.kebs.core.macros.ValueClassLike - "implicitly[CaseClass1Rep[Currency, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, Currency]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[Locale, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, Locale]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[UUID, String]]" shouldNot typeCheck - "implicitly[CaseClass1Rep[String, UUID]]" shouldNot typeCheck + "implicitly[ValueClassLike[Currency, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, Currency]]" shouldNot typeCheck + "implicitly[ValueClassLike[Locale, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, Locale]]" shouldNot typeCheck + "implicitly[ValueClassLike[UUID, String]]" shouldNot typeCheck + "implicitly[ValueClassLike[String, UUID]]" shouldNot typeCheck } test("Currency standard format") { diff --git a/circe/src/test/scala-2/model/model.scala b/spray-json/src/test/scala/pl/iterators/kebs/json/model/package.scala similarity index 98% rename from circe/src/test/scala-2/model/model.scala rename to spray-json/src/test/scala/pl/iterators/kebs/json/model/package.scala index 945565f5..ffa444df 100644 --- a/circe/src/test/scala-2/model/model.scala +++ b/spray-json/src/test/scala/pl/iterators/kebs/json/model/package.scala @@ -1,3 +1,5 @@ +package pl.iterators.kebs.json + package object model { case class F1(f1: String) extends AnyVal @@ -27,7 +29,6 @@ package object model { f22: String, f23: Boolean ) - object ClassWith23Fields { val Example = ClassWith23Fields( f1 = F1("f1 value"), @@ -81,7 +82,6 @@ package object model { f22: String, f23: Boolean ) - object ClassWith23FieldsNested { val Example: ClassWith23FieldsNested = ClassWith23FieldsNested( f1 = F1("f1 value"), diff --git a/tagged-meta/src/main/scala/pl/iterators/kebs/tag/meta/tagged.scala b/tagged-meta/src/main/scala/pl/iterators/kebs/tag/meta/tagged.scala index 44745d3c..a7c7935d 100644 --- a/tagged-meta/src/main/scala/pl/iterators/kebs/tag/meta/tagged.scala +++ b/tagged-meta/src/main/scala/pl/iterators/kebs/tag/meta/tagged.scala @@ -1,7 +1,5 @@ package pl.iterators.kebs.tag.meta -import scala.language.existentials -import scala.language.experimental.macros import scala.annotation.{StaticAnnotation, compileTimeOnly} import scala.reflect.macros.whitebox @@ -115,15 +113,15 @@ final class macroImpl(val c: whitebox.Context) { q"def apply[..$typeParams](arg: $baseTypeName[..$baseParams]) = $body" } - def generateCaseClass1RepImplicit: Tree = { - val caseClass1RepInstanceTree = - q"new _root_.pl.iterators.kebs.macros.CaseClass1Rep[$selfType, $baseTypeName[..$baseParams]](${name.toTermName}.apply(_), identity)" - val implicitName = TermName(name.decodedName.toString + "CaseClass1Rep") + def generateValueClassLikeImplicit: Tree = { + val valueClassLikeInstanceTree = + q"new _root_.pl.iterators.kebs.core.macros.ValueClassLike[$selfType, $baseTypeName[..$baseParams]](${name.toTermName}.apply(_), identity)" + val implicitName = TermName(name.decodedName.toString + "ValueClassLike") if (typeParams.isEmpty) - q"implicit val $implicitName: _root_.pl.iterators.kebs.macros.CaseClass1Rep[$selfType, $baseTypeName[..$baseParams]] = $caseClass1RepInstanceTree" + q"implicit val $implicitName: _root_.pl.iterators.kebs.core.macros.ValueClassLike[$selfType, $baseTypeName] = $valueClassLikeInstanceTree" else - q"implicit def $implicitName[..$typeParams]: _root_.pl.iterators.kebs.macros.CaseClass1Rep[$selfType, $baseTypeName[..$baseParams]] = $caseClass1RepInstanceTree" + q"implicit def $implicitName[..$typeParams]: _root_.pl.iterators.kebs.core.macros.ValueClassLike[$selfType, $baseTypeName[..$baseParams]] = $valueClassLikeInstanceTree" } private def containsApply(trees: List[Tree]): Boolean = { @@ -153,7 +151,7 @@ final class macroImpl(val c: whitebox.Context) { case class TagTypeRep(tagName: TypeName, maybeCompanion: Option[ModuleDef]) { def generateCompanion(taggedTypes: List[TaggedType]): Tree = { - val implicits = taggedTypes.map(_.generateCaseClass1RepImplicit) + val implicits = taggedTypes.map(_.generateValueClassLikeImplicit) maybeCompanion match { case Some(ModuleDef(mods, companionName, template)) => ModuleDef(mods, companionName, Template(template.parents, template.self, template.body ++ implicits)) diff --git a/tagged-meta/src/test/scala/CirceAnnotationTests.scala b/tagged-meta/src/test/scala/pl/iterators/kebs/tag/meta/CirceAnnotationTests.scala similarity index 98% rename from tagged-meta/src/test/scala/CirceAnnotationTests.scala rename to tagged-meta/src/test/scala/pl/iterators/kebs/tag/meta/CirceAnnotationTests.scala index d6b49283..27ab094f 100644 --- a/tagged-meta/src/test/scala/CirceAnnotationTests.scala +++ b/tagged-meta/src/test/scala/pl/iterators/kebs/tag/meta/CirceAnnotationTests.scala @@ -1,9 +1,10 @@ +package pl.iterators.kebs.tag.meta + import io.circe.{CursorOp, Decoder, DecodingFailure, Json} import org.scalatest.funsuite.AnyFunSuite -import pl.iterators.kebs.tagged._ -import pl.iterators.kebs.tag.meta._ -import pl.iterators.kebs.circe.KebsCirce import org.scalatest.matchers.should.Matchers +import pl.iterators.kebs.circe.KebsCirce +import pl.iterators.kebs.tagged._ @tagged object CirceTestTags { trait NameTag diff --git a/tagged-meta/src/test/scala/JsonSchemaAnnotationTests.scala b/tagged-meta/src/test/scala/pl/iterators/kebs/tag/meta/JsonSchemaAnnotationTests.scala similarity index 92% rename from tagged-meta/src/test/scala/JsonSchemaAnnotationTests.scala rename to tagged-meta/src/test/scala/pl/iterators/kebs/tag/meta/JsonSchemaAnnotationTests.scala index f7a4429b..7f67f9f5 100644 --- a/tagged-meta/src/test/scala/JsonSchemaAnnotationTests.scala +++ b/tagged-meta/src/test/scala/pl/iterators/kebs/tag/meta/JsonSchemaAnnotationTests.scala @@ -1,12 +1,10 @@ -import com.github.andyglow.json.JsonFormatter -import com.github.andyglow.jsonschema.AsValue +package pl.iterators.kebs.tag.meta + import json.Schema -import json.schema.Version.Draft07 -import pl.iterators.kebs.tagged._ -import pl.iterators.kebs.tag.meta._ import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers import pl.iterators.kebs.jsonschema.{JsonSchemaWrapper, KebsJsonSchema} +import pl.iterators.kebs.tagged._ @tagged object JsonSchemaTestTags { trait NameTag diff --git a/tagged-meta/src/test/scala/SprayAnnotationTests.scala b/tagged-meta/src/test/scala/pl/iterators/kebs/tag/meta/SprayAnnotationTests.scala similarity index 98% rename from tagged-meta/src/test/scala/SprayAnnotationTests.scala rename to tagged-meta/src/test/scala/pl/iterators/kebs/tag/meta/SprayAnnotationTests.scala index d4bff174..fceb89ab 100644 --- a/tagged-meta/src/test/scala/SprayAnnotationTests.scala +++ b/tagged-meta/src/test/scala/pl/iterators/kebs/tag/meta/SprayAnnotationTests.scala @@ -1,9 +1,10 @@ -import pl.iterators.kebs.tagged._ -import pl.iterators.kebs.tag.meta._ +package pl.iterators.kebs.tag.meta + import _root_.spray.json._ -import pl.iterators.kebs.json.KebsSpray import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers +import pl.iterators.kebs.json.KebsSpray +import pl.iterators.kebs.tagged._ @tagged object SprayTestTags { trait NameTag diff --git a/tagged-meta/src/test/scala/SprayKebsIssue47Test.scala b/tagged-meta/src/test/scala/pl/iterators/kebs/tag/meta/SprayKebsIssue47Test.scala similarity index 95% rename from tagged-meta/src/test/scala/SprayKebsIssue47Test.scala rename to tagged-meta/src/test/scala/pl/iterators/kebs/tag/meta/SprayKebsIssue47Test.scala index be019b22..f060834b 100644 --- a/tagged-meta/src/test/scala/SprayKebsIssue47Test.scala +++ b/tagged-meta/src/test/scala/pl/iterators/kebs/tag/meta/SprayKebsIssue47Test.scala @@ -1,7 +1,8 @@ +package pl.iterators.kebs.tag.meta + import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers import pl.iterators.kebs.json.KebsSpray -import pl.iterators.kebs.tag.meta.tagged import pl.iterators.kebs.tagged._ import util.Properties.versionNumberString import spray.json.{DefaultJsonProtocol, JsonFormat} diff --git a/tagged-meta/src/test/scala/TaggedAnnotationFromMethodTest.scala b/tagged-meta/src/test/scala/pl/iterators/kebs/tag/meta/TaggedAnnotationFromMethodTest.scala similarity index 94% rename from tagged-meta/src/test/scala/TaggedAnnotationFromMethodTest.scala rename to tagged-meta/src/test/scala/pl/iterators/kebs/tag/meta/TaggedAnnotationFromMethodTest.scala index 9b82ba02..b9367a0b 100644 --- a/tagged-meta/src/test/scala/TaggedAnnotationFromMethodTest.scala +++ b/tagged-meta/src/test/scala/pl/iterators/kebs/tag/meta/TaggedAnnotationFromMethodTest.scala @@ -1,8 +1,9 @@ +package pl.iterators.kebs.tag.meta + import org.scalatest._ -import pl.iterators.kebs.tagged._ -import pl.iterators.kebs.tag.meta.tagged import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers +import pl.iterators.kebs.tagged._ @tagged object DomainTrimmedString { trait NameTag diff --git a/tagged-meta/src/test/scala/TaggedAnnotationTests.scala b/tagged-meta/src/test/scala/pl/iterators/kebs/tag/meta/TaggedAnnotationTests.scala similarity index 98% rename from tagged-meta/src/test/scala/TaggedAnnotationTests.scala rename to tagged-meta/src/test/scala/pl/iterators/kebs/tag/meta/TaggedAnnotationTests.scala index 93034dd6..9b2cdb4c 100644 --- a/tagged-meta/src/test/scala/TaggedAnnotationTests.scala +++ b/tagged-meta/src/test/scala/pl/iterators/kebs/tag/meta/TaggedAnnotationTests.scala @@ -1,8 +1,9 @@ +package pl.iterators.kebs.tag.meta + import org.scalatest._ -import pl.iterators.kebs.tagged._ -import pl.iterators.kebs.tag.meta.tagged import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers +import pl.iterators.kebs.tagged._ @tagged object TestTags { trait NameTag diff --git a/tagged/jvm/src/main/scala/pl/iterators/kebs/tagged/slick/SlickSupport.scala b/tagged/jvm/src/main/scala/pl/iterators/kebs/tagged/slick/SlickSupport.scala deleted file mode 100644 index 79d18d1d..00000000 --- a/tagged/jvm/src/main/scala/pl/iterators/kebs/tagged/slick/SlickSupport.scala +++ /dev/null @@ -1,8 +0,0 @@ -package pl.iterators.kebs.tagged.slick - -import slick.lifted.Isomorphism -import pl.iterators.kebs.tagged._ - -trait SlickSupport { - implicit def taggedColumnType[T, U]: Isomorphism[T @@ U, T] = new Isomorphism[T @@ U, T](identity, _.@@[U]) -} diff --git a/tagged/jvm/src/main/scala/pl/iterators/kebs/tagged/slick/TaggedSlickSupport.scala b/tagged/jvm/src/main/scala/pl/iterators/kebs/tagged/slick/TaggedSlickSupport.scala new file mode 100644 index 00000000..7af03f37 --- /dev/null +++ b/tagged/jvm/src/main/scala/pl/iterators/kebs/tagged/slick/TaggedSlickSupport.scala @@ -0,0 +1,12 @@ +package pl.iterators.kebs.tagged.slick + +import pl.iterators.kebs.tagged._ +import _root_.slick.jdbc.JdbcProfile + +trait TaggedSlickSupport { this: JdbcProfile => + trait TaggedImplicits { + implicit def taggedColumnType[T, U](implicit baseColumnType: BaseColumnType[T]): BaseColumnType[T @@ U] = { + MappedColumnType.base[T @@ U, T](identity, _.@@[U]) + } + } +} diff --git a/tagged/jvm/src/main/scala/pl/iterators/kebs/tagged/slick/package.scala b/tagged/jvm/src/main/scala/pl/iterators/kebs/tagged/slick/package.scala deleted file mode 100644 index 4f297b17..00000000 --- a/tagged/jvm/src/main/scala/pl/iterators/kebs/tagged/slick/package.scala +++ /dev/null @@ -1,3 +0,0 @@ -package pl.iterators.kebs.tagged - -package object slick extends SlickSupport diff --git a/tagged/jvm/src/test/scala/SlickTaggedColumnTypeTests.scala b/tagged/jvm/src/test/scala/SlickTaggedColumnTypeTests.scala index 91170675..ba9242fd 100644 --- a/tagged/jvm/src/test/scala/SlickTaggedColumnTypeTests.scala +++ b/tagged/jvm/src/test/scala/SlickTaggedColumnTypeTests.scala @@ -1,18 +1,24 @@ -import slick.jdbc.PostgresProfile.api._ +import slick.jdbc.PostgresProfile import pl.iterators.kebs.tagged._ -import pl.iterators.kebs.tagged.slick.{SlickSupport} +import pl.iterators.kebs.tagged.slick.TaggedSlickSupport import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -class SlickTaggedColumnTypeTests extends AnyFunSuite with Matchers with SlickSupport { +class SlickTaggedColumnTypeTests extends AnyFunSuite with Matchers { + + object MyPostgresProfile extends PostgresProfile with TaggedSlickSupport { + override val api: APITagged = new APITagged {} + trait APITagged extends JdbcAPI with TaggedImplicits + } + + import MyPostgresProfile.api._ + trait IdTag type Id = Long @@ IdTag trait NameTag type Name = String @@ NameTag - case class Row(id: Id, name: Name, num: Long) - test("MappedColumnType for tagged types") { "implicitly[BaseColumnType[Long @@ IdTag]]" should compile } diff --git a/tagged/jvm/src/test/scala/TaggedTypeIsomorphismTests.scala b/tagged/jvm/src/test/scala/TaggedTypeIsomorphismTests.scala deleted file mode 100644 index 0bf1ce38..00000000 --- a/tagged/jvm/src/test/scala/TaggedTypeIsomorphismTests.scala +++ /dev/null @@ -1,28 +0,0 @@ -import pl.iterators.kebs.tagged.slick.SlickSupport -import slick.lifted.Isomorphism -import org.scalatest.funsuite.AnyFunSuite -import org.scalatest.matchers.should.Matchers - -class TaggedTypeIsomorphismTests extends AnyFunSuite with Matchers with SlickSupport { - import pl.iterators.kebs.tagged._ - - trait Tag1 - - type Simple = Int @@ Tag1 - object Simple { - def apply(i: Int) = i.@@[Tag1] - } - - test("implicit isomorphism between bare type and type with tag") { - val iso = implicitly[Isomorphism[Int @@ Tag1, Int]] - iso.map(Simple(10)) shouldBe 10 - iso.comap(10) shouldBe Simple(10) - } - - test("implicit isomorphism between bare type and type with tag (alias)") { - val iso = implicitly[Isomorphism[Simple, Int]] - iso.map(Simple(10)) shouldBe 10 - iso.comap(10) shouldBe Simple(10) - } - -} diff --git a/tagged/shared/src/main/scala/pl/iterators/kebs/tagged/package.scala b/tagged/shared/src/main/scala/pl/iterators/kebs/tagged/package.scala index 4c83f2d4..ed8b1d49 100644 --- a/tagged/shared/src/main/scala/pl/iterators/kebs/tagged/package.scala +++ b/tagged/shared/src/main/scala/pl/iterators/kebs/tagged/package.scala @@ -1,7 +1,5 @@ package pl.iterators.kebs -import scala.language.higherKinds - package object tagged { /**