From daad478eaea675f6ccf4400197aece84a0a5d0ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Sowa?= Date: Wed, 7 Aug 2024 20:00:30 +0200 Subject: [PATCH] Kebs 2.0 package cleanup (#380) * Renaming packages for spray & play json. * Fixing pekko-http Scala 2/3 division. * Refactoring tests for doobie. * Refactoring circe tests. * Cleaning up http4s package. * Adding previously removed doobie tests. * Refactor of scalacheck package * Mergin spray-json-macros and spray-json. * Fixing akka-http to work with generic EnumLike * Merging pekko-http tests. * Removing bunch of useless classtags * Merging http4s-stir implementation and tests. * Removing legacy `sv` from build.sbt * Cleaning up akka-http, pekko-http and http4s-stir. * Circe cleanup. * Naming fix Enum -> Enums in Circe * Doobie cleanup. * Http4s cleanup * jsonschema cleanup * Cleaning up play-json, adding enums. * Fixing scalacheck * Slick trait visibility fix. * Cleanup spray-json. * Cleanup tagged. --- ...chers.scala => KebsAkkaHttpMatchers.scala} | 15 +- .../kebs/akkahttp/matchers/package.scala | 2 +- ....scala => KebsAkkaHttpUnmarshallers.scala} | 33 ++- .../unmarshallers/KebsUnmarshallers.scala | 26 -- .../unmarshallers/enums/package.scala | 3 - .../kebs/akkahttp/unmarshallers/package.scala | 2 +- .../matchers/AkkaHttpMatchersTests.scala | 26 +- .../AkkaHttpUnmarshallersTests.scala | 4 +- build.sbt | 40 +-- .../pl/iterators/kebs/circe/KebsCirce.scala | 7 +- .../pl/iterators/kebs/circe/KebsCirce.scala | 7 +- .../kebs/circe/KebsEnumFormats.scala | 94 ------- .../kebs/circe/enums/KebsCirceEnums.scala} | 50 ++-- .../iterators/kebs/circe/enums/package.scala | 6 + .../pl/iterators/kebs/circe/package.scala | 6 + .../CirceFormatSnakifiedVariantTests.scala | 124 --------- .../kebs/circe/KebsEnumForTests.scala | 5 + .../kebs/circe/KebsValueEnumForTests.scala | 5 + .../circe/CirceEnumDecoderEncoderTests.scala | 53 ---- .../CirceFormatCapitalizedVariantTests.scala | 123 --------- .../kebs/circe/CirceFormatTests.scala | 212 --------------- .../CirceValueEnumDecoderEncoderTests.scala | 33 --- .../kebs/circe/KebsEnumForTests.scala | 5 + .../kebs/circe/KebsValueEnumForTests.scala | 5 + .../instances/TimeInstancesMixinTests.scala | 115 --------- .../pl/iterators/kebs/circe/model/model.scala | 4 + .../CirceEnumDecoderEncoderTests.scala | 16 +- .../CirceFormatCapitalizedVariantTests.scala | 7 +- .../CirceFormatSnakifiedVariantTests.scala | 10 +- .../circe/formats}/CirceFormatTests.scala | 6 +- .../CirceValueEnumDecoderEncoderTests.scala | 6 +- .../instances/TimeInstancesMixinTests.scala | 4 +- .../iterators/kebs/doobie/enums/package.scala | 8 - .../pl/iterators/kebs/doobie/package.scala | 3 - .../pl/iterators/kebs/doobie/Kebs.scala | 43 ---- .../kebs/doobie/enums/KebsEnums.scala | 53 ---- .../iterators/kebs/doobie/enums/package.scala | 8 - .../pl/iterators/kebs/doobie/package.scala | 3 - .../iterators/kebs/doobie/KebsDoobie.scala} | 2 +- .../kebs/doobie/enums/KebsDoobieEnums.scala} | 27 +- .../iterators/kebs/doobie/enums/package.scala | 7 + .../pl/iterators/kebs/doobie/package.scala | 3 + .../kebs/doobie/KebsEnumsForTests.scala | 5 + .../iterators/kebs/doobie/model/model.scala | 14 + .../kebs/doobie/ComplexTypesTests.scala | 69 ----- .../kebs/doobie/KebsEnumsForTests.scala | 5 + .../iterators/kebs/doobie/model/model.scala | 8 + .../kebs/doobie/ComplexTypesTests.scala | 24 +- .../pl/iterators/kebs/enums/KebsEnum.scala | 4 +- .../iterators/kebs/enums/KebsValueEnum.scala | 4 +- .../iterators/kebs/enums/ValueEnumTest.scala | 1 - .../kebs/enumeratum/KebsEnumeratum.scala | 3 +- .../kebs/enumeratum/KebsValueEnumeratum.scala | 4 +- .../kebs/examples/CirceExample.scala | 2 +- .../kebs/examples/EnumSprayJsonFormat.scala | 2 +- .../kebs/examples/PlayJsonFormat.scala | 4 +- .../kebs/examples/SprayJsonFormat.scala | 2 +- .../SprayJsonWithPekkoHttpExample.scala | 2 +- .../enums/KebsEnumUnmarshallers.scala | 53 ---- .../unmarshallers/enums/package.scala | 3 - .../http4sstir/matchers/KebsMatchers.scala | 30 --- .../enums/KebsEnumUnmarshallers.scala | 69 ----- .../unmarshallers/enums/package.scala | 3 - .../matchers/KebsHttp4sStirMatchers.scala} | 16 +- .../kebs/http4sstir/matchers/package.scala | 2 +- .../KebsHttp4sStirUnmarshallers.scala | 92 +++++++ .../unmarshallers/KebsUnmarshallers.scala | 30 --- .../http4sstir/unmarshallers/package.scala | 2 +- .../kebs/http4sstir/KebsEnumForTests.scala | 5 + .../http4sstir/KebsValueEnumForTests.scala | 5 + .../Http4sStirUnmarshallersTests.scala | 207 --------------- .../kebs/http4sstir/KebsEnumForTests.scala | 5 + .../http4sstir/KebsValueEnumForTests.scala | 5 + .../domain/Http4sStirTagsDomain.scala | 13 + .../matchers/Http4sStirMatchersTests.scala | 86 ------- .../matchers/Http4sStirMatchersTests.scala | 29 ++- .../Http4sStirUnmarshallersTests.scala | 19 +- .../pl/iterators/kebs/http4s/package.scala | 3 - .../pl/iterators/kebs/http4s/package.scala | 75 ------ .../iterators/kebs/http4s/KebsHttp4s.scala} | 2 +- .../pl/iterators/kebs/http4s/package.scala | 3 + .../iterators/kebs/jsonschema/package.scala | 3 + .../pekkohttp/matchers/KebsMatchers.scala | 23 -- .../unmarshallers/enums/package.scala | 3 - .../enums/KebsEnumUnmarshallers.scala | 71 ----- .../unmarshallers/enums/package.scala | 3 - .../matchers/KebsPekkoHttpMatchers.scala} | 22 +- .../kebs/pekkohttp/matchers/package.scala | 2 +- .../KebsPekkoHttpUnmarshallers.scala} | 53 +++- .../unmarshallers/KebsUnmarshallers.scala | 30 --- .../pekkohttp/unmarshallers/package.scala | 2 +- .../kebs/pekkohttp/KebsEnumForTests.scala | 5 + .../pekkohttp/KebsValueEnumForTests.scala | 5 + .../kebs/pekkohttp/KebsEnumForTests.scala | 5 + .../pekkohttp/KebsValueEnumForTests.scala | 5 + .../domain/PekkoHttpTagsDomain.scala | 13 + .../matchers/PekkoHttpMatchersTests.scala | 83 ------ .../PekkoHttpUnmarshallersTests.scala | 243 ------------------ .../matchers/PekkoHttpMatchersTests.scala | 30 ++- .../PekkoHttpUnmarshallersTests.scala | 15 +- .../pl/iterators/kebs/json/package.scala | 3 - .../KebsPlayJson.scala} | 16 +- .../playjson/enums/KebsPlayJsonEnums.scala | 71 +++++ .../iterators/kebs/playjson/enums/enums.scala | 6 + .../pl/iterators/kebs/playjson/package.scala | 3 + .../kebs/json/PlayJsonFormatTests.scala | 52 ---- .../kebs/playjson/PlayJsonFormatTests.scala | 131 ++++++++++ .../instances/NetInstancesTests.scala | 8 +- .../instances/TimeInstancesMixinTests.scala | 4 +- .../instances/TimeInstancesTests.scala | 38 +-- .../instances/UtilInstancesTests.scala | 10 +- .../scalacheck/CommonArbitrarySupport.scala | 14 - .../kebs/scalacheck/Generators.scala | 18 -- .../ScalacheckInstancesSupport.scala | 3 + .../scalacheck/CommonArbitrarySupport.scala | 3 +- .../kebs/scalacheck/Generators.scala | 0 .../kebs/scalacheck/model/model.scala | 30 ++- .../kebs/scalacheck/model/model.scala | 16 +- .../kebs/slick/KebsSlickSupport.scala | 2 +- .../KebsSprayJson.scala} | 12 +- .../enums/KebsSprayJsonEnums.scala} | 29 +-- .../kebs/sprayjson/enums/package.scala | 6 + .../sprayjson}/macros/KebsSprayMacros.scala | 2 +- .../pl/iterators/kebs/sprayjson/package.scala | 8 + .../formats}/SprayEnumJsonFormatTests.scala | 11 +- ...rayJsonFormatCapitalizedVariantTests.scala | 5 +- ...prayJsonFormatSnakifiedVariantTests.scala} | 27 +- .../formats}/SprayJsonFormatTests.scala | 13 +- .../SprayValueEnumJsonFormatTests.scala | 5 +- .../instances/NetInstancesTests.scala | 6 +- .../instances/TimeInstancesMixinTests.scala | 12 +- .../instances/TimeInstancesTests.scala | 6 +- .../instances/UtilInstancesTests.scala | 6 +- .../{json => sprayjson}/model/package.scala | 2 +- .../kebs/tag/meta/SprayAnnotationTests.scala | 4 +- .../kebs/tag/meta/SprayKebsIssue47Test.scala | 4 +- ...rceSupport.scala => KebsTaggedCirce.scala} | 2 +- .../iterators/kebs/tagged/circe/package.scala | 2 +- 138 files changed, 944 insertions(+), 2383 deletions(-) rename akka-http/src/main/scala/pl/iterators/kebs/akkahttp/matchers/{KebsMatchers.scala => KebsAkkaHttpMatchers.scala} (53%) rename akka-http/src/main/scala/pl/iterators/kebs/akkahttp/unmarshallers/{enums/KebsEnumUnmarshallers.scala => KebsAkkaHttpUnmarshallers.scala} (65%) delete mode 100644 akka-http/src/main/scala/pl/iterators/kebs/akkahttp/unmarshallers/KebsUnmarshallers.scala delete mode 100644 akka-http/src/main/scala/pl/iterators/kebs/akkahttp/unmarshallers/enums/package.scala delete mode 100644 circe/src/main/scala-3/pl/iterators/kebs/circe/KebsEnumFormats.scala rename circe/src/main/{scala-2/pl/iterators/kebs/circe/KebsEnumFormats.scala => scala/pl/iterators/kebs/circe/enums/KebsCirceEnums.scala} (92%) create mode 100644 circe/src/main/scala/pl/iterators/kebs/circe/enums/package.scala create mode 100644 circe/src/main/scala/pl/iterators/kebs/circe/package.scala delete mode 100644 circe/src/test/scala-2/pl/iterators/kebs/circe/CirceFormatSnakifiedVariantTests.scala create mode 100644 circe/src/test/scala-2/pl/iterators/kebs/circe/KebsEnumForTests.scala create mode 100644 circe/src/test/scala-2/pl/iterators/kebs/circe/KebsValueEnumForTests.scala delete mode 100644 circe/src/test/scala-3/pl/iterators/kebs/circe/CirceEnumDecoderEncoderTests.scala delete mode 100644 circe/src/test/scala-3/pl/iterators/kebs/circe/CirceFormatCapitalizedVariantTests.scala delete mode 100644 circe/src/test/scala-3/pl/iterators/kebs/circe/CirceFormatTests.scala delete mode 100644 circe/src/test/scala-3/pl/iterators/kebs/circe/CirceValueEnumDecoderEncoderTests.scala create mode 100644 circe/src/test/scala-3/pl/iterators/kebs/circe/KebsEnumForTests.scala create mode 100644 circe/src/test/scala-3/pl/iterators/kebs/circe/KebsValueEnumForTests.scala delete mode 100644 circe/src/test/scala-3/pl/iterators/kebs/circe/instances/TimeInstancesMixinTests.scala rename circe/src/test/{scala-2/pl/iterators/kebs/circe => scala/pl/iterators/kebs/circe/formats}/CirceEnumDecoderEncoderTests.scala (80%) rename circe/src/test/{scala-2/pl/iterators/kebs/circe => scala/pl/iterators/kebs/circe/formats}/CirceFormatCapitalizedVariantTests.scala (95%) rename circe/src/test/{scala-3/pl/iterators/kebs/circe => scala/pl/iterators/kebs/circe/formats}/CirceFormatSnakifiedVariantTests.scala (95%) rename circe/src/test/{scala-2/pl/iterators/kebs/circe => scala/pl/iterators/kebs/circe/formats}/CirceFormatTests.scala (99%) rename circe/src/test/{scala-2/pl/iterators/kebs/circe => scala/pl/iterators/kebs/circe/formats}/CirceValueEnumDecoderEncoderTests.scala (86%) rename circe/src/test/{scala-2 => scala}/pl/iterators/kebs/circe/instances/TimeInstancesMixinTests.scala (100%) delete mode 100644 doobie/src/main/scala-2/pl/iterators/kebs/doobie/enums/package.scala delete mode 100644 doobie/src/main/scala-2/pl/iterators/kebs/doobie/package.scala delete mode 100644 doobie/src/main/scala-3/pl/iterators/kebs/doobie/Kebs.scala delete mode 100644 doobie/src/main/scala-3/pl/iterators/kebs/doobie/enums/KebsEnums.scala delete mode 100644 doobie/src/main/scala-3/pl/iterators/kebs/doobie/enums/package.scala delete mode 100644 doobie/src/main/scala-3/pl/iterators/kebs/doobie/package.scala rename doobie/src/main/{scala-2/pl/iterators/kebs/doobie/Kebs.scala => scala/pl/iterators/kebs/doobie/KebsDoobie.scala} (98%) rename doobie/src/main/{scala-2/pl/iterators/kebs/doobie/enums/KebsEnums.scala => scala/pl/iterators/kebs/doobie/enums/KebsDoobieEnums.scala} (66%) create mode 100644 doobie/src/main/scala/pl/iterators/kebs/doobie/enums/package.scala create mode 100644 doobie/src/main/scala/pl/iterators/kebs/doobie/package.scala create mode 100644 doobie/src/test/scala-2/pl/iterators/kebs/doobie/KebsEnumsForTests.scala delete mode 100644 doobie/src/test/scala-3/pl/iterators/kebs/doobie/ComplexTypesTests.scala create mode 100644 doobie/src/test/scala-3/pl/iterators/kebs/doobie/KebsEnumsForTests.scala rename doobie/src/test/{scala-2 => scala}/pl/iterators/kebs/doobie/ComplexTypesTests.scala (74%) delete mode 100644 http4s-stir/src/main/scala-2/pl/iterators/kebs/http4sstir/unmarshallers/enums/KebsEnumUnmarshallers.scala delete mode 100644 http4s-stir/src/main/scala-2/pl/iterators/kebs/http4sstir/unmarshallers/enums/package.scala delete mode 100644 http4s-stir/src/main/scala-3/pl/iterators/kebs/http4sstir/matchers/KebsMatchers.scala delete mode 100644 http4s-stir/src/main/scala-3/pl/iterators/kebs/http4sstir/unmarshallers/enums/KebsEnumUnmarshallers.scala delete mode 100644 http4s-stir/src/main/scala-3/pl/iterators/kebs/http4sstir/unmarshallers/enums/package.scala rename http4s-stir/src/main/{scala-2/pl/iterators/kebs/http4sstir/matchers/KebsMatchers.scala => scala/pl/iterators/kebs/http4sstir/matchers/KebsHttp4sStirMatchers.scala} (57%) create mode 100644 http4s-stir/src/main/scala/pl/iterators/kebs/http4sstir/unmarshallers/KebsHttp4sStirUnmarshallers.scala delete mode 100644 http4s-stir/src/main/scala/pl/iterators/kebs/http4sstir/unmarshallers/KebsUnmarshallers.scala create mode 100644 http4s-stir/src/test/scala-2/pl/iterators/kebs/http4sstir/KebsEnumForTests.scala create mode 100644 http4s-stir/src/test/scala-2/pl/iterators/kebs/http4sstir/KebsValueEnumForTests.scala delete mode 100644 http4s-stir/src/test/scala-2/pl/iterators/kebs/http4sstir/unmarshallers/Http4sStirUnmarshallersTests.scala create mode 100644 http4s-stir/src/test/scala-3/pl/iterators/kebs/http4sstir/KebsEnumForTests.scala create mode 100644 http4s-stir/src/test/scala-3/pl/iterators/kebs/http4sstir/KebsValueEnumForTests.scala delete mode 100644 http4s-stir/src/test/scala-3/pl/iterators/kebs/http4sstir/matchers/Http4sStirMatchersTests.scala rename http4s-stir/src/test/{scala-2 => scala}/pl/iterators/kebs/http4sstir/matchers/Http4sStirMatchersTests.scala (81%) rename http4s-stir/src/test/{scala-3 => scala}/pl/iterators/kebs/http4sstir/unmarshallers/Http4sStirUnmarshallersTests.scala (94%) delete mode 100644 http4s/src/main/scala-2/pl/iterators/kebs/http4s/package.scala delete mode 100644 http4s/src/main/scala-3/pl/iterators/kebs/http4s/package.scala rename http4s/src/main/{scala-2/pl/iterators/kebs/http4s/Http4s.scala => scala/pl/iterators/kebs/http4s/KebsHttp4s.scala} (99%) create mode 100644 http4s/src/main/scala/pl/iterators/kebs/http4s/package.scala create mode 100644 jsonschema/src/main/scala/pl/iterators/kebs/jsonschema/package.scala delete mode 100644 pekko-http/src/main/scala-2/pl/iterators/kebs/pekkohttp/matchers/KebsMatchers.scala delete mode 100644 pekko-http/src/main/scala-2/pl/iterators/kebs/pekkohttp/unmarshallers/enums/package.scala delete mode 100644 pekko-http/src/main/scala-3/pl/iterators/kebs/pekkohttp/unmarshallers/enums/KebsEnumUnmarshallers.scala delete mode 100644 pekko-http/src/main/scala-3/pl/iterators/kebs/pekkohttp/unmarshallers/enums/package.scala rename pekko-http/src/main/{scala-3/pl/iterators/kebs/pekkohttp/matchers/KebsMatchers.scala => scala/pl/iterators/kebs/pekkohttp/matchers/KebsPekkoHttpMatchers.scala} (51%) rename pekko-http/src/main/{scala-2/pl/iterators/kebs/pekkohttp/unmarshallers/enums/KebsEnumUnmarshallers.scala => scala/pl/iterators/kebs/pekkohttp/unmarshallers/KebsPekkoHttpUnmarshallers.scala} (57%) delete mode 100644 pekko-http/src/main/scala/pl/iterators/kebs/pekkohttp/unmarshallers/KebsUnmarshallers.scala create mode 100644 pekko-http/src/test/scala-2/pl/iterators/kebs/pekkohttp/KebsEnumForTests.scala create mode 100644 pekko-http/src/test/scala-2/pl/iterators/kebs/pekkohttp/KebsValueEnumForTests.scala create mode 100644 pekko-http/src/test/scala-3/pl/iterators/kebs/pekkohttp/KebsEnumForTests.scala create mode 100644 pekko-http/src/test/scala-3/pl/iterators/kebs/pekkohttp/KebsValueEnumForTests.scala delete mode 100644 pekko-http/src/test/scala-3/pl/iterators/kebs/pekkohttp/matchers/PekkoHttpMatchersTests.scala delete mode 100644 pekko-http/src/test/scala-3/pl/iterators/kebs/pekkohttp/unmarshallers/PekkoHttpUnmarshallersTests.scala rename pekko-http/src/test/{scala-2 => scala}/pl/iterators/kebs/pekkohttp/matchers/PekkoHttpMatchersTests.scala (81%) rename pekko-http/src/test/{scala-2 => scala}/pl/iterators/kebs/pekkohttp/unmarshallers/PekkoHttpUnmarshallersTests.scala (94%) delete mode 100644 play-json/src/main/scala/pl/iterators/kebs/json/package.scala rename play-json/src/main/scala/pl/iterators/kebs/{json/KebsPlay.scala => playjson/KebsPlayJson.scala} (58%) create mode 100644 play-json/src/main/scala/pl/iterators/kebs/playjson/enums/KebsPlayJsonEnums.scala create mode 100644 play-json/src/main/scala/pl/iterators/kebs/playjson/enums/enums.scala create mode 100644 play-json/src/main/scala/pl/iterators/kebs/playjson/package.scala delete mode 100644 play-json/src/test/scala/pl/iterators/kebs/json/PlayJsonFormatTests.scala create mode 100644 play-json/src/test/scala/pl/iterators/kebs/playjson/PlayJsonFormatTests.scala rename play-json/src/test/scala/pl/iterators/kebs/{json => playjson}/instances/NetInstancesTests.scala (81%) rename play-json/src/test/scala/pl/iterators/kebs/{json => playjson}/instances/TimeInstancesMixinTests.scala (98%) rename play-json/src/test/scala/pl/iterators/kebs/{json => playjson}/instances/TimeInstancesTests.scala (87%) rename play-json/src/test/scala/pl/iterators/kebs/{json => playjson}/instances/UtilInstancesTests.scala (87%) delete mode 100644 scalacheck/src/main/scala-3/pl/iterators/kebs/scalacheck/CommonArbitrarySupport.scala delete mode 100644 scalacheck/src/main/scala-3/pl/iterators/kebs/scalacheck/Generators.scala create mode 100644 scalacheck/src/main/scala-3/pl/iterators/kebs/scalacheck/ScalacheckInstancesSupport.scala rename scalacheck/src/main/{scala-2 => scala}/pl/iterators/kebs/scalacheck/CommonArbitrarySupport.scala (71%) rename scalacheck/src/main/{scala-2 => scala}/pl/iterators/kebs/scalacheck/Generators.scala (100%) rename spray-json/src/main/scala/pl/iterators/kebs/{json/KebsSpray.scala => sprayjson/KebsSprayJson.scala} (82%) rename spray-json/src/main/scala/pl/iterators/kebs/{json/KebsEnumFormats.scala => sprayjson/enums/KebsSprayJsonEnums.scala} (90%) create mode 100644 spray-json/src/main/scala/pl/iterators/kebs/sprayjson/enums/package.scala rename {spray-json-macros/src/main/scala/pl/iterators/kebs/json => spray-json/src/main/scala/pl/iterators/kebs/sprayjson}/macros/KebsSprayMacros.scala (99%) create mode 100644 spray-json/src/main/scala/pl/iterators/kebs/sprayjson/package.scala rename spray-json/src/test/scala/pl/iterators/kebs/{json => sprayjson/formats}/SprayEnumJsonFormatTests.scala (86%) rename spray-json/src/test/scala/pl/iterators/kebs/{json => sprayjson/formats}/SprayJsonFormatCapitalizedVariantTests.scala (93%) rename spray-json/src/test/scala/pl/iterators/kebs/{json/SprayJsonFormatSnakifyVariantTests.scala => sprayjson/formats/SprayJsonFormatSnakifiedVariantTests.scala} (90%) rename spray-json/src/test/scala/pl/iterators/kebs/{json => sprayjson/formats}/SprayJsonFormatTests.scala (97%) rename spray-json/src/test/scala/pl/iterators/kebs/{json => sprayjson/formats}/SprayValueEnumJsonFormatTests.scala (87%) rename spray-json/src/test/scala/pl/iterators/kebs/{json => sprayjson}/instances/NetInstancesTests.scala (86%) rename spray-json/src/test/scala/pl/iterators/kebs/{json => sprayjson}/instances/TimeInstancesMixinTests.scala (93%) rename spray-json/src/test/scala/pl/iterators/kebs/{json => sprayjson}/instances/TimeInstancesTests.scala (98%) rename spray-json/src/test/scala/pl/iterators/kebs/{json => sprayjson}/instances/UtilInstancesTests.scala (92%) rename spray-json/src/test/scala/pl/iterators/kebs/{json => sprayjson}/model/package.scala (98%) rename tagged/shared/src/main/scala/pl/iterators/kebs/tagged/circe/{CirceSupport.scala => KebsTaggedCirce.scala} (98%) diff --git a/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/matchers/KebsMatchers.scala b/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/matchers/KebsAkkaHttpMatchers.scala similarity index 53% rename from akka-http/src/main/scala/pl/iterators/kebs/akkahttp/matchers/KebsMatchers.scala rename to akka-http/src/main/scala/pl/iterators/kebs/akkahttp/matchers/KebsAkkaHttpMatchers.scala index 9ff8ca38..bd8e7b43 100644 --- a/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/matchers/KebsMatchers.scala +++ b/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/matchers/KebsAkkaHttpMatchers.scala @@ -1,24 +1,21 @@ package pl.iterators.kebs.akkahttp.matchers import akka.http.scaladsl.server.{PathMatcher1, PathMatchers} -import enumeratum.{Enum, EnumEntry} +import pl.iterators.kebs.core.enums.{EnumLike, ValueEnumLike, ValueEnumLikeEntry} import pl.iterators.kebs.core.instances.InstanceConverter import pl.iterators.kebs.core.macros.ValueClassLike -trait KebsMatchers extends PathMatchers { - +trait KebsAkkaHttpMatchers extends PathMatchers { implicit class SegmentIsomorphism[U](segment: PathMatcher1[U]) { - def as[T](implicit rep: ValueClassLike[T, U]): PathMatcher1[T] = segment.map(rep.apply) + def as[T](implicit rep: ValueClassLike[T, U]): PathMatcher1[T] = segment.map(rep.apply) + def asValueEnum[T <: ValueEnumLikeEntry[U]](implicit e: ValueEnumLike[U, T]): PathMatcher1[T] = segment.map(e.withValue) } implicit class SegmentConversion[Source](segment: PathMatcher1[Source]) { def to[Type](implicit ico: InstanceConverter[Type, Source]): PathMatcher1[Type] = segment.map(ico.decode) } - object EnumSegment { - def as[T <: EnumEntry: Enum]: PathMatcher1[T] = { - val enumCompanion = implicitly[Enum[T]] - Segment.map(enumCompanion.withNameInsensitive) - } + implicit class SegmentEnumIsomorphism[U](segment: PathMatcher1[String]) { + def asEnum[T](implicit e: EnumLike[T]): PathMatcher1[T] = segment.map(e.withNameIgnoreCase) } } diff --git a/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/matchers/package.scala b/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/matchers/package.scala index 34c0e7df..17977487 100644 --- a/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/matchers/package.scala +++ b/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/matchers/package.scala @@ -1,3 +1,3 @@ package pl.iterators.kebs.akkahttp -package object matchers extends KebsMatchers +package object matchers extends KebsAkkaHttpMatchers 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/KebsAkkaHttpUnmarshallers.scala similarity index 65% rename from akka-http/src/main/scala/pl/iterators/kebs/akkahttp/unmarshallers/enums/KebsEnumUnmarshallers.scala rename to akka-http/src/main/scala/pl/iterators/kebs/akkahttp/unmarshallers/KebsAkkaHttpUnmarshallers.scala index e32cb1aa..ac4f8b54 100644 --- 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/KebsAkkaHttpUnmarshallers.scala @@ -1,12 +1,14 @@ -package pl.iterators.kebs.akkahttp.unmarshallers.enums +package pl.iterators.kebs.akkahttp.unmarshallers -import akka.http.scaladsl.unmarshalling.PredefinedFromStringUnmarshallers._ import akka.http.scaladsl.unmarshalling.{FromStringUnmarshaller, Unmarshaller} +import pl.iterators.kebs.core.instances.InstanceConverter +import pl.iterators.kebs.core.macros.ValueClassLike import akka.http.scaladsl.util.FastFuture +import akka.http.scaladsl.unmarshalling.PredefinedFromStringUnmarshallers._ import pl.iterators.kebs.core.enums.{EnumLike, ValueEnumLike, ValueEnumLikeEntry} -trait EnumUnmarshallers { - final def enumUnmarshaller[E](`enum`: EnumLike[E]): FromStringUnmarshaller[E] = Unmarshaller { _ => name => +trait KebsAkkaHttpEnumUnmarshallers { + private final def enumUnmarshaller[E](`enum`: EnumLike[E]): FromStringUnmarshaller[E] = Unmarshaller { _ => name => `enum`.withNameInsensitiveOption(name) match { case Some(enumEntry) => FastFuture.successful(enumEntry) case None => @@ -21,7 +23,7 @@ trait EnumUnmarshallers { enumUnmarshaller(ev) } -trait ValueEnumUnmarshallers { +trait KebsAkkaHttpValueEnumUnmarshallers { final def valueEnumUnmarshaller[V, E <: ValueEnumLikeEntry[V]](`enum`: ValueEnumLike[V, E]): Unmarshaller[V, E] = Unmarshaller { _ => v => `enum`.withValueOption(v) match { @@ -55,4 +57,23 @@ trait ValueEnumUnmarshallers { byteFromStringUnmarshaller andThen valueEnumUnmarshaller(ev) } -trait KebsEnumUnmarshallers extends EnumUnmarshallers with ValueEnumUnmarshallers {} +trait KebsAkkaHttpUnmarshallers extends KebsAkkaHttpEnumUnmarshallers with KebsAkkaHttpValueEnumUnmarshallers { + implicit def kebsUnmarshaller[A, B](implicit rep: ValueClassLike[B, A]): Unmarshaller[A, B] = + Unmarshaller.strict[A, B](rep.apply) + @inline + implicit def kebsFromStringUnmarshaller[A, B](implicit + rep: ValueClassLike[B, A], + fsu: FromStringUnmarshaller[A] + ): FromStringUnmarshaller[B] = + fsu andThen kebsUnmarshaller(rep) + + implicit def kebsInstancesUnmarshaller[A, B](implicit ico: InstanceConverter[B, A]): Unmarshaller[A, B] = + Unmarshaller.strict[A, B](ico.decode) + @inline + implicit def kebsInstancesFromStringUnmarshaller[A, B](implicit + ico: InstanceConverter[B, A], + fsu: FromStringUnmarshaller[A] + ): FromStringUnmarshaller[B] = + fsu andThen kebsInstancesUnmarshaller(ico) + +} diff --git a/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/unmarshallers/KebsUnmarshallers.scala b/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/unmarshallers/KebsUnmarshallers.scala deleted file mode 100644 index d57bc749..00000000 --- a/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/unmarshallers/KebsUnmarshallers.scala +++ /dev/null @@ -1,26 +0,0 @@ -package pl.iterators.kebs.akkahttp.unmarshallers - -import akka.http.scaladsl.unmarshalling.{FromStringUnmarshaller, Unmarshaller} -import pl.iterators.kebs.core.instances.InstanceConverter -import pl.iterators.kebs.core.macros.ValueClassLike - -trait KebsUnmarshallers { - implicit def kebsUnmarshaller[A, B](implicit rep: ValueClassLike[B, A]): Unmarshaller[A, B] = - Unmarshaller.strict[A, B](rep.apply) - @inline - implicit def kebsFromStringUnmarshaller[A, B](implicit - rep: ValueClassLike[B, A], - fsu: FromStringUnmarshaller[A] - ): FromStringUnmarshaller[B] = - fsu andThen kebsUnmarshaller(rep) - - implicit def kebsInstancesUnmarshaller[A, B](implicit ico: InstanceConverter[B, A]): Unmarshaller[A, B] = - Unmarshaller.strict[A, B](ico.decode) - @inline - implicit def kebsInstancesFromStringUnmarshaller[A, B](implicit - ico: InstanceConverter[B, A], - fsu: FromStringUnmarshaller[A] - ): FromStringUnmarshaller[B] = - fsu andThen kebsInstancesUnmarshaller(ico) - -} diff --git a/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/unmarshallers/enums/package.scala b/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/unmarshallers/enums/package.scala deleted file mode 100644 index fb2d9919..00000000 --- a/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/unmarshallers/enums/package.scala +++ /dev/null @@ -1,3 +0,0 @@ -package pl.iterators.kebs.akkahttp.unmarshallers - -package object enums extends KebsEnumUnmarshallers diff --git a/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/unmarshallers/package.scala b/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/unmarshallers/package.scala index 615d2a88..799b2146 100644 --- a/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/unmarshallers/package.scala +++ b/akka-http/src/main/scala/pl/iterators/kebs/akkahttp/unmarshallers/package.scala @@ -1,3 +1,3 @@ package pl.iterators.kebs.akkahttp -package object unmarshallers extends KebsUnmarshallers +package object unmarshallers extends KebsAkkaHttpUnmarshallers diff --git a/akka-http/src/test/scala/pl/iterators/kebs/akkahttp/matchers/AkkaHttpMatchersTests.scala b/akka-http/src/test/scala/pl/iterators/kebs/akkahttp/matchers/AkkaHttpMatchersTests.scala index 69d89753..8ae23585 100644 --- a/akka-http/src/test/scala/pl/iterators/kebs/akkahttp/matchers/AkkaHttpMatchersTests.scala +++ b/akka-http/src/test/scala/pl/iterators/kebs/akkahttp/matchers/AkkaHttpMatchersTests.scala @@ -5,8 +5,9 @@ import akka.http.scaladsl.testkit.ScalatestRouteTest import org.scalatest.concurrent.ScalaFutures import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.akkahttp.domain.Domain.Greeting import pl.iterators.kebs.akkahttp.domain.Domain._ +import pl.iterators.kebs.core.macros.CaseClass1ToValueClass +import pl.iterators.kebs.enumeratum.{KebsEnumeratum, KebsValueEnumeratum} import pl.iterators.kebs.instances.net.URIString import pl.iterators.kebs.instances.time.{DayOfWeekInt, ZonedDateTimeString} import pl.iterators.kebs.instances.time.mixins.InstantEpochMilliLong @@ -23,6 +24,9 @@ class AkkaHttpMatchersTests with ZonedDateTimeString with DayOfWeekInt with InstantEpochMilliLong + with KebsEnumeratum + with KebsValueEnumeratum + with CaseClass1ToValueClass with URIString { test("No ValueClassLike implicits derived") { @@ -90,7 +94,7 @@ class AkkaHttpMatchersTests } test("Extract String as Enum") { - val testRoute = path("test" / EnumSegment.as[Greeting]) { greeting => + val testRoute = path("test" / Segment.asEnum[Greeting]) { greeting => complete(greeting.toString) } Get("/test/hello") ~> testRoute ~> check { @@ -98,6 +102,24 @@ class AkkaHttpMatchersTests } } + test("Extract String as value class") { + val testRoute = path("test" / Segment.as[S]) { item => + complete(item.toString) + } + Get("/test/check") ~> testRoute ~> check { + responseAs[String] shouldEqual "S(check)" + } + } + + test("Extract Int as ValueEnum") { + val testRoute = path("test" / IntNumber.asValueEnum[LibraryItem]) { item => + complete(item.toString) + } + Get("/test/1") ~> testRoute ~> check { + responseAs[String] shouldEqual "Book" + } + } + test("Extract String to URI as tagged URI") { val testRoute = path("test" / Segment.to[URI].as[TestTaggedUri]) { id => complete(id.toString) diff --git a/akka-http/src/test/scala/pl/iterators/kebs/akkahttp/unmarshallers/AkkaHttpUnmarshallersTests.scala b/akka-http/src/test/scala/pl/iterators/kebs/akkahttp/unmarshallers/AkkaHttpUnmarshallersTests.scala index 7cf7e2e8..e2797a1d 100644 --- a/akka-http/src/test/scala/pl/iterators/kebs/akkahttp/unmarshallers/AkkaHttpUnmarshallersTests.scala +++ b/akka-http/src/test/scala/pl/iterators/kebs/akkahttp/unmarshallers/AkkaHttpUnmarshallersTests.scala @@ -12,7 +12,6 @@ import pl.iterators.kebs.akkahttp.domain.Domain._ import pl.iterators.kebs.instances.net.URIString import pl.iterators.kebs.instances.time.{DayOfWeekInt, YearMonthString} import pl.iterators.kebs.enumeratum.{KebsEnumeratum, KebsValueEnumeratum} -import pl.iterators.kebs.akkahttp.unmarshallers.enums.KebsEnumUnmarshallers import pl.iterators.kebs.core.macros.CaseClass1ToValueClass import java.time.{DayOfWeek, YearMonth} @@ -23,8 +22,7 @@ class AkkaHttpUnmarshallersTests with ScalatestRouteTest with ScalaFutures with Directives - with KebsUnmarshallers - with KebsEnumUnmarshallers + with KebsAkkaHttpUnmarshallers with URIString with YearMonthString with DayOfWeekInt diff --git a/build.sbt b/build.sbt index 4dd15fad..a8473e97 100644 --- a/build.sbt +++ b/build.sbt @@ -87,13 +87,6 @@ def disableScala(v: List[String]) = ) def optional(dependency: ModuleID) = dependency % "provided" -def sv[A](scalaVersion: String, scala2_12Version: => A, scala2_13Version: => A) = - CrossVersion.partialVersion(scalaVersion) match { - case Some((2, 13)) => scala2_13Version - case Some((2, 12)) => scala2_12Version - case _ => - throw new IllegalArgumentException(s"Unsupported Scala version $scalaVersion") - } def paradiseFlag(scalaVersion: String): Seq[String] = if (scalaVersion == scala_3) @@ -118,11 +111,10 @@ val circeParser = "io.circe" %% "circe-parser" % circeV val jsonschema = "com.github.andyglow" %% "scala-jsonschema" % "0.7.11" -val scalacheck = "org.scalacheck" %% "scalacheck" % "1.18.0" % "test" +val scalacheck = "org.scalacheck" %% "scalacheck" % "1.18.0" val scalacheckMagnolify = "com.spotify" % "magnolify-scalacheck" % "0.7.3" val scalacheckDerived = "io.github.martinhh" %% "scalacheck-derived" % "0.4.2" -val scalacheckEnumeratum = "com.beachape" %% "enumeratum-scalacheck" % "1.7.4" val enumeratumVersion = "1.7.4" val enumeratumPlayJsonVersion = "1.8.1" @@ -206,16 +198,14 @@ lazy val enumeratumSettings = commonMacroSettings ++ Seq( scalacOptions ++= paradiseFlag(scalaVersion.value) ) -lazy val sprayJsonMacroSettings = commonMacroSettings ++ Seq( - libraryDependencies += sprayJson.cross(CrossVersion.for3Use2_13) -) - lazy val sprayJsonSettings = commonSettings ++ Seq( + libraryDependencies += sprayJson.cross(CrossVersion.for3Use2_13), libraryDependencies += optionalEnumeratum ) lazy val playJsonSettings = commonSettings ++ Seq( - libraryDependencies += playJson + libraryDependencies += playJson, + libraryDependencies += (enumeratum % "test") ) lazy val circeSettings = commonSettings ++ Seq( @@ -264,7 +254,7 @@ lazy val jsonschemaSettings = commonSettings ++ Seq( lazy val scalacheckSettings = commonSettings ++ Seq( libraryDependencies += scalacheck, - libraryDependencies += scalacheckEnumeratum + libraryDependencies += (enumeratum % "test"), ) ++ Seq( libraryDependencies ++= (if (scalaVersion.value.startsWith("3")) Seq(scalacheckDerived) else Nil) @@ -330,22 +320,9 @@ lazy val doobieSupport = project crossScalaVersions := supportedScalaVersions ) -lazy val sprayJsonMacros = project - .in(file("spray-json-macros")) - .dependsOn(core.jvm) - .settings(sprayJsonMacroSettings *) - .settings(publishSettings *) - .settings(disableScala(List("3"))) - .settings( - name := "spray-json-macros", - description := "Automatic generation of Spray json formats for case-classes - macros", - moduleName := "kebs-spray-json-macros", - crossScalaVersions := supportedScalaVersions - ) - lazy val sprayJsonSupport = project .in(file("spray-json")) - .dependsOn(sprayJsonMacros, enumeratumSupport, instances % "test -> test") + .dependsOn(enumeratumSupport, instances % "test -> test") .settings(sprayJsonSettings *) .settings(publishSettings *) .settings(disableScala(List("3"))) @@ -358,7 +335,7 @@ lazy val sprayJsonSupport = project lazy val playJsonSupport = project .in(file("play-json")) - .dependsOn(core.jvm, instances % "test -> test") + .dependsOn(core.jvm, enumeratumSupport, enumSupport, instances % "test -> test") .settings(playJsonSettings *) .settings(publishSettings *) .settings( @@ -451,7 +428,7 @@ lazy val jsonschemaSupport = project lazy val scalacheckSupport = project .in(file("scalacheck")) - .dependsOn(core.jvm, opaque.jvm % "test -> test") + .dependsOn(core.jvm, enumSupport, opaque.jvm % "test -> test") .settings(scalacheckSettings *) .settings(publishSettings *) .settings( @@ -564,7 +541,6 @@ lazy val kebs = project core.js, slickSupport, doobieSupport, - sprayJsonMacros, sprayJsonSupport, playJsonSupport, circeSupport, 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 5581e29e..9c7130f3 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 @@ -18,16 +18,13 @@ trait KebsCirce extends AutoDerivation { implicit def instanceConverterDecoder[T, A](implicit rep: InstanceConverter[T, A], decoder: Decoder[A]): Decoder[T] = decoder.emap(obj => Try(rep.decode(obj)).toEither.left.map(_.getMessage)) -} - -object KebsCirce { - trait Snakified extends KebsCirce { + trait KebsCirceSnakified extends KebsCirce { implicit def genericSnakifiedDecoder[T <: Product]: Decoder[T] = macro KebsCirceMacros.SnakifyVariant.materializeDecoder[T] implicit def genericSnakifiedEncoder[T <: Product]: Encoder[T] = macro KebsCirceMacros.SnakifyVariant.materializeEncoder[T] } - trait Capitalized extends KebsCirce { + trait KebsCirceCapitalized extends KebsCirce { implicit def genericCapitalizedDecoder[T <: Product]: Decoder[T] = macro KebsCirceMacros.CapitalizedCamelCase.materializeDecoder[T] implicit def genericCapitalizedEncoder[T <: Product]: Encoder[T] = macro KebsCirceMacros.CapitalizedCamelCase.materializeEncoder[T] } 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 642a41b4..6a0749ed 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 @@ -34,15 +34,12 @@ trait KebsCirce extends KebsAutoDerivation { inline implicit def instanceConverterDecoder[T, A](using rep: InstanceConverter[T, A], decoder: Decoder[A]): Decoder[T] = decoder.emap(obj => Try(rep.decode(obj)).toEither.left.map(_.getMessage)) -} - -object KebsCirce { - trait Snakified extends KebsCirce { + trait KebsCirceSnakified extends KebsCirce { override implicit val configuration: Configuration = Configuration.default.withSnakeCaseMemberNames } - trait Capitalized extends KebsCirce { + trait KebsCirceCapitalized extends KebsCirce { override implicit val configuration: Configuration = Configuration.default.withPascalCaseMemberNames } } 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 deleted file mode 100644 index 42a9e1ca..00000000 --- a/circe/src/main/scala-3/pl/iterators/kebs/circe/KebsEnumFormats.scala +++ /dev/null @@ -1,94 +0,0 @@ -package pl.iterators.kebs.circe - -import io.circe._ -import scala.reflect.Enum -import scala.util.Try - -import pl.iterators.kebs.core.enums.{EnumLike, ValueEnumLike, ValueEnumLikeEntry} - -trait CirceEnum { - @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: 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: 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: EnumLike[E], _map: E => String): Encoder[E] = - (obj: E) => Encoder.encodeString(_map(obj)) - - def enumDecoder[E <: Enum](e: EnumLike[E]): Decoder[E] = - enumDecoder[E](e, s => e.values.find(_.toString.equalsIgnoreCase(s))) - - def enumEncoder[E <: Enum](e: EnumLike[E]): Encoder[E] = - enumEncoder[E](e, (e: Enum) => e.toString) - - 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: 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 <: 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 <: ValueEnumLikeEntry[V]](e: ValueEnumLike[V, E])(implicit decoder: Decoder[V]): Decoder[E] = - (c: HCursor) => - decoder.emap(obj => Try(e.valueOf(obj)).toOption.toRight("")).withErrorMessage(valueEnumDeserializationError(e, c.value))(c) - - 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 { - inline implicit def decoderFromEnumLike[E <: Enum](using ev: EnumLike[E]): Decoder[E] = enumDecoder(ev) - - inline implicit def encoderFromEnumLike[E <: Enum](using ev: EnumLike[E]): Encoder[E] = enumEncoder(ev) - - inline implicit def decoderFromValueEnumLike[V, E <: ValueEnumLikeEntry[V]](using - ev: ValueEnumLike[V, E], - decoder: Decoder[V] - ): Decoder[E] = - valueEnumDecoder(ev) - - inline implicit def encoderFromValueEnumLike[V, E <: ValueEnumLikeEntry[V]](using - ev: ValueEnumLike[V, E], - encoder: Encoder[V] - ): Encoder[E] = - valueEnumEncoder(ev) - - trait Uppercase extends CirceEnum { - inline implicit def decoderFromEnumLike[E <: Enum](using ev: EnumLike[E]): Decoder[E] = - uppercaseEnumDecoder(ev) - - inline implicit def encoderFromEnumLike[E <: Enum](using ev: EnumLike[E]): Encoder[E] = - uppercaseEnumEncoder(ev) - } - - trait Lowercase extends CirceEnum { - inline implicit def decoderFromEnumLike[E <: Enum](using ev: EnumLike[E]): Decoder[E] = - lowercaseEnumDecoder(ev) - - inline implicit def encoderFromEnumLike[E <: Enum](using ev: EnumLike[E]): Encoder[E] = - lowercaseEnumEncoder(ev) - } -} - -object KebsEnumFormats extends KebsEnumFormats diff --git a/circe/src/main/scala-2/pl/iterators/kebs/circe/KebsEnumFormats.scala b/circe/src/main/scala/pl/iterators/kebs/circe/enums/KebsCirceEnums.scala similarity index 92% rename from circe/src/main/scala-2/pl/iterators/kebs/circe/KebsEnumFormats.scala rename to circe/src/main/scala/pl/iterators/kebs/circe/enums/KebsCirceEnums.scala index d064efda..182b09e4 100644 --- a/circe/src/main/scala-2/pl/iterators/kebs/circe/KebsEnumFormats.scala +++ b/circe/src/main/scala/pl/iterators/kebs/circe/enums/KebsCirceEnums.scala @@ -1,9 +1,9 @@ -package pl.iterators.kebs.circe +package pl.iterators.kebs.circe.enums import io.circe._ import pl.iterators.kebs.core.enums.{EnumLike, ValueEnumLike, ValueEnumLikeEntry} -trait CirceEnum { +trait KebsCirceEnums { @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" @@ -35,9 +35,29 @@ trait CirceEnum { enumDecoder[E](`enum`, `enum`.withNameUppercaseOnlyOption(_)) def uppercaseEnumEncoder[E](`enum`: EnumLike[E]): Encoder[E] = enumEncoder[E](`enum`, (e: E) => e.toString.toUpperCase()) + + implicit def enumDecoderImpl[E](implicit ev: EnumLike[E]): Decoder[E] = enumDecoder(ev) + + implicit def enumEncoderImpl[E](implicit ev: EnumLike[E]): Encoder[E] = enumEncoder(ev) + + trait KebsCirceEnumsUppercase { + 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 KebsCirceEnumsLowercase { + implicit def enumDecoderImpl[E](implicit ev: EnumLike[E]): Decoder[E] = + lowercaseEnumDecoder(ev) + + implicit def enumEncoderImpl[E](implicit ev: EnumLike[E]): Encoder[E] = + lowercaseEnumEncoder(ev) + } } -trait CirceValueEnum { +trait KebsCirceValueEnums { @inline protected final def valueEnumDeserializationError[V, E <: ValueEnumLikeEntry[V]]( `enum`: ValueEnumLike[V, E], value: Json @@ -52,34 +72,10 @@ trait CirceValueEnum { 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/pl/iterators/kebs/circe/enums/package.scala b/circe/src/main/scala/pl/iterators/kebs/circe/enums/package.scala new file mode 100644 index 00000000..79c77483 --- /dev/null +++ b/circe/src/main/scala/pl/iterators/kebs/circe/enums/package.scala @@ -0,0 +1,6 @@ +package pl.iterators.kebs.circe + +package object enums extends KebsCirceEnums with KebsCirceValueEnums { + object uppercase extends KebsCirceEnumsUppercase with KebsCirceValueEnums + object lowercase extends KebsCirceEnumsLowercase with KebsCirceValueEnums +} diff --git a/circe/src/main/scala/pl/iterators/kebs/circe/package.scala b/circe/src/main/scala/pl/iterators/kebs/circe/package.scala new file mode 100644 index 00000000..dc499ea0 --- /dev/null +++ b/circe/src/main/scala/pl/iterators/kebs/circe/package.scala @@ -0,0 +1,6 @@ +package pl.iterators.kebs + +package object circe extends KebsCirce { + object snakified extends KebsCirceSnakified + object capitalized extends KebsCirceCapitalized +} diff --git a/circe/src/test/scala-2/pl/iterators/kebs/circe/CirceFormatSnakifiedVariantTests.scala b/circe/src/test/scala-2/pl/iterators/kebs/circe/CirceFormatSnakifiedVariantTests.scala deleted file mode 100644 index 50d20a60..00000000 --- a/circe/src/test/scala-2/pl/iterators/kebs/circe/CirceFormatSnakifiedVariantTests.scala +++ /dev/null @@ -1,124 +0,0 @@ -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 pl.iterators.kebs.core.macros.CaseClass1ToValueClass - -import scala.Right - -class CirceFormatSnakifiedVariantTests extends AnyFunSuite with Matchers { - object KebsProtocol extends KebsCirce with KebsCirce.Snakified with CaseClass1ToValueClass - import KebsProtocol._ - - test("Flat format remains unchanged") { - val decoder = implicitly[Decoder[C]] - val encoder = implicitly[Encoder[C]] - decoder.apply(Json.fromInt(10).hcursor) shouldBe Right(C(10)) - encoder.apply(C(10)) shouldBe Json.fromInt(10) - } - - test("Format 0 remains unchanged") { - val decoder = implicitly[Decoder[F.type]] - val encoder = implicitly[Encoder[F.type]] - decoder.apply(Json.fromFields(Seq.empty[(String, Json)]).hcursor) shouldBe Right(F) - encoder.apply(F) shouldBe Json.fromFields(Seq.empty[(String, Json)]) - } - - test("format 2 snakified") { - val decoder = implicitly[Decoder[D]] - val encoder = implicitly[Encoder[D]] - decoder - .apply(Json.fromFields(Seq("int_field" -> Json.fromInt(10), "string_field" -> Json.fromString("abcd"))).hcursor) shouldBe Right( - D(10, "abcd") - ) - encoder.apply(D(10, "abcd")) shouldBe Json.fromFields(Seq("int_field" -> Json.fromInt(10), "string_field" -> Json.fromString("abcd"))) - } - - test("Format snakified - compound") { - val decoder = implicitly[Decoder[Compound]] - val encoder = implicitly[Encoder[Compound]] - - encoder.apply(Compound(C(5), D(10, "abcd"))) shouldBe Json.fromFields( - Seq( - "c_field" -> Json.fromInt(5), - "d_field" -> Json.fromFields(Seq("int_field" -> Json.fromInt(10), "string_field" -> Json.fromString("abcd"))) - ) - ) - decoder - .apply( - Json - .fromFields( - Seq( - "c_field" -> Json.fromInt(5), - "d_field" -> Json.fromFields(Seq("int_field" -> Json.fromInt(10), "string_field" -> Json.fromString("abcd"))) - ) - ) - .hcursor - ) shouldBe Right(Compound(C(5), D(10, "abcd"))) - } - - test("Format snakified - case class with > 22 fields") { - import model._ - - val decoder = implicitly[Decoder[ClassWith23Fields]] - val encoder = implicitly[Encoder[ClassWith23Fields]] - val obj = ClassWith23Fields( - F1("f1 value"), - 2, - 3L, - None, - Some("f5 value"), - "six", - List("f7 value 1", "f7 value 2"), - "f8 value", - "f9 value", - "f10 value", - "f11 value", - "f12 value", - "f13 value", - "f14 value", - "f15 value", - "f16 value", - "f17 value", - "f18 value", - "f19 value", - "f20 value", - "f21 value", - "f22 value", - f23 = true - ) - val json = Json.fromFields( - Seq( - "f1" -> Json.fromString("f1 value"), - "f2" -> Json.fromInt(2), - "f3" -> Json.fromInt(3), - "f4" -> Json.Null, - "f5" -> Json.fromString("f5 value"), - "field_number_six" -> Json.fromString("six"), - "f7" -> Json.arr(Json.fromString("f7 value 1"), Json.fromString("f7 value 2")), - "f8" -> Json.fromString("f8 value"), - "f9" -> Json.fromString("f9 value"), - "f10" -> Json.fromString("f10 value"), - "f11" -> Json.fromString("f11 value"), - "f12" -> Json.fromString("f12 value"), - "f13" -> Json.fromString("f13 value"), - "f14" -> Json.fromString("f14 value"), - "f15" -> Json.fromString("f15 value"), - "f16" -> Json.fromString("f16 value"), - "f17" -> Json.fromString("f17 value"), - "f18" -> Json.fromString("f18 value"), - "f19" -> Json.fromString("f19 value"), - "f20" -> Json.fromString("f20 value"), - "f21" -> Json.fromString("f21 value"), - "f22" -> Json.fromString("f22 value"), - "f23" -> Json.fromBoolean(true) - ) - ) - - encoder.apply(obj) shouldBe json - decoder.apply(json.hcursor) shouldBe Right(obj) - } -} diff --git a/circe/src/test/scala-2/pl/iterators/kebs/circe/KebsEnumForTests.scala b/circe/src/test/scala-2/pl/iterators/kebs/circe/KebsEnumForTests.scala new file mode 100644 index 00000000..758b9454 --- /dev/null +++ b/circe/src/test/scala-2/pl/iterators/kebs/circe/KebsEnumForTests.scala @@ -0,0 +1,5 @@ +package pl.iterators.kebs.circe + +import pl.iterators.kebs.enumeratum.KebsEnumeratum + +trait KebsEnumForTests extends KebsEnumeratum diff --git a/circe/src/test/scala-2/pl/iterators/kebs/circe/KebsValueEnumForTests.scala b/circe/src/test/scala-2/pl/iterators/kebs/circe/KebsValueEnumForTests.scala new file mode 100644 index 00000000..f0cbaf5c --- /dev/null +++ b/circe/src/test/scala-2/pl/iterators/kebs/circe/KebsValueEnumForTests.scala @@ -0,0 +1,5 @@ +package pl.iterators.kebs.circe + +import pl.iterators.kebs.enumeratum.KebsValueEnumeratum + +trait KebsValueEnumForTests extends KebsValueEnumeratum diff --git a/circe/src/test/scala-3/pl/iterators/kebs/circe/CirceEnumDecoderEncoderTests.scala b/circe/src/test/scala-3/pl/iterators/kebs/circe/CirceEnumDecoderEncoderTests.scala deleted file mode 100644 index 20a81ebc..00000000 --- a/circe/src/test/scala-3/pl/iterators/kebs/circe/CirceEnumDecoderEncoderTests.scala +++ /dev/null @@ -1,53 +0,0 @@ -package pl.iterators.kebs.circe - -import io.circe._ -import org.scalatest.matchers.should.Matchers -import org.scalatest.funsuite.AnyFunSuite -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 with KebsEnum { - - object KebsProtocol extends KebsEnumFormats - object KebsProtocolUppercase extends KebsEnumFormats.Uppercase - object KebsProtocolLowercase extends KebsEnumFormats.Lowercase - - test("enum JsonFormat") { - import KebsProtocol._ - val decoder = implicitly[Decoder[Greeting]] - val encoder = implicitly[Encoder[Greeting]] - decoder(Json.fromString("hElLo").hcursor) shouldBe Right(Hello) - decoder(Json.fromString("goodbye").hcursor) shouldBe Right(GoodBye) - encoder(Hello) shouldBe Json.fromString("Hello") - encoder(GoodBye) shouldBe Json.fromString("GoodBye") - } - - test("enum name deserialization error") { - import KebsProtocol._ - val decoder = implicitly[Decoder[Greeting]] - decoder(Json.fromInt(1).hcursor) shouldBe Left( - DecodingFailure("1 should be a string of value Hello, GoodBye, Hi, Bye", List.empty[CursorOp]) - ) - } - - test("enum JsonFormat - lowercase") { - import KebsProtocol._ - val decoder = implicitly[Decoder[Greeting]] - val encoder = implicitly[Encoder[Greeting]] - decoder(Json.fromString("hello").hcursor) shouldBe Right(Hello) - decoder(Json.fromString("goodbye").hcursor) shouldBe Right(GoodBye) - encoder(Hello) shouldBe Json.fromString("Hello") - encoder(GoodBye) shouldBe Json.fromString("GoodBye") - } - - test("enum JsonFormat - uppercase") { - import KebsProtocol._ - val decoder = implicitly[Decoder[Greeting]] - val encoder = implicitly[Encoder[Greeting]] - decoder(Json.fromString("HELLO").hcursor) shouldBe Right(Hello) - decoder(Json.fromString("GOODBYE").hcursor) shouldBe Right(GoodBye) - encoder(Hello) shouldBe Json.fromString("Hello") - encoder(GoodBye) shouldBe Json.fromString("GoodBye") - } -} diff --git a/circe/src/test/scala-3/pl/iterators/kebs/circe/CirceFormatCapitalizedVariantTests.scala b/circe/src/test/scala-3/pl/iterators/kebs/circe/CirceFormatCapitalizedVariantTests.scala deleted file mode 100644 index 18e06387..00000000 --- a/circe/src/test/scala-3/pl/iterators/kebs/circe/CirceFormatCapitalizedVariantTests.scala +++ /dev/null @@ -1,123 +0,0 @@ -package pl.iterators.kebs.circe - -import io.circe.{Decoder, Encoder, Json} -import org.scalatest.funsuite.AnyFunSuite -import org.scalatest.matchers.should.Matchers -import io.circe.derivation.ConfiguredDecoder -import pl.iterators.kebs.circe.model._ -import pl.iterators.kebs.core.macros.CaseClass1ToValueClass - -class CirceFormatCapitalizedVariantTests extends AnyFunSuite with Matchers { - object KebsProtocol extends KebsCirce with KebsCirce.Capitalized with CaseClass1ToValueClass - import KebsProtocol.* - - test("Flat format remains unchanged") { - val decoder = implicitly[Decoder[C]] - val encoder = implicitly[Encoder[C]] - decoder.apply(Json.fromInt(10).hcursor) shouldBe Right(C(10)) - encoder.apply(C(10)) shouldBe Json.fromInt(10) - } - - test("Format 0 remains unchanged") { - val decoder = implicitly[Decoder[F.type]] - val encoder = implicitly[Encoder[F.type]] - decoder.apply(Json.fromFields(Seq.empty[(String, Json)]).hcursor) shouldBe Right(F) - encoder.apply(F) shouldBe Json.fromFields(Seq.empty[(String, Json)]) - } - - test("Format 2 capitalized") { - val decoder = implicitly[Decoder[D]] - val encoder = implicitly[Encoder[D]] - decoder - .apply(Json.fromFields(Seq("IntField" -> Json.fromInt(5), "StringField" -> Json.fromString("abcd"))).hcursor) shouldBe Right( - D(5, "abcd") - ) - encoder.apply(D(5, "abcd")) shouldBe Json.fromFields(Seq("IntField" -> Json.fromInt(5), "StringField" -> Json.fromString("abcd"))) - } - - test("Format capitalized - compound") { - val decoder = implicitly[Decoder[Compound]] - val encoder = implicitly[Encoder[Compound]] - - encoder.apply(Compound(C(5), D(10, "abcd"))) shouldBe Json.fromFields( - Seq( - "CField" -> Json.fromInt(5), - "DField" -> Json.fromFields(Seq("IntField" -> Json.fromInt(10), "StringField" -> Json.fromString("abcd"))) - ) - ) - decoder - .apply( - Json - .fromFields( - Seq( - "CField" -> Json.fromInt(5), - "DField" -> Json.fromFields(Seq("IntField" -> Json.fromInt(10), "StringField" -> Json.fromString("abcd"))) - ) - ) - .hcursor - ) shouldBe Right(Compound(C(5), D(10, "abcd"))) - } - - test("Format capitalized - case class with > 22 fields") { - import model._ - - val decoder = implicitly[Decoder[ClassWith23Fields]] - val encoder = implicitly[Encoder[ClassWith23Fields]] - - val obj = ClassWith23Fields( - F1("f1 value"), - 2, - 3L, - None, - Some("f5 value"), - "six", - List("f7 value 1", "f7 value 2"), - "f8 value", - "f9 value", - "f10 value", - "f11 value", - "f12 value", - "f13 value", - "f14 value", - "f15 value", - "f16 value", - "f17 value", - "f18 value", - "f19 value", - "f20 value", - "f21 value", - "f22 value", - f23 = true - ) - val json = Json.fromFields( - Seq( - "F1" -> Json.fromString("f1 value"), - "F2" -> Json.fromInt(2), - "F3" -> Json.fromInt(3), - "F4" -> Json.Null, - "F5" -> Json.fromString("f5 value"), - "FieldNumberSix" -> Json.fromString("six"), - "F7" -> Json.arr(Json.fromString("f7 value 1"), Json.fromString("f7 value 2")), - "F8" -> Json.fromString("f8 value"), - "F9" -> Json.fromString("f9 value"), - "F10" -> Json.fromString("f10 value"), - "F11" -> Json.fromString("f11 value"), - "F12" -> Json.fromString("f12 value"), - "F13" -> Json.fromString("f13 value"), - "F14" -> Json.fromString("f14 value"), - "F15" -> Json.fromString("f15 value"), - "F16" -> Json.fromString("f16 value"), - "F17" -> Json.fromString("f17 value"), - "F18" -> Json.fromString("f18 value"), - "F19" -> Json.fromString("f19 value"), - "F20" -> Json.fromString("f20 value"), - "F21" -> Json.fromString("f21 value"), - "F22" -> Json.fromString("f22 value"), - "F23" -> Json.fromBoolean(true) - ) - ) - - encoder.apply(obj) shouldBe json - decoder.apply(json.hcursor) shouldBe Right(obj) - } -} diff --git a/circe/src/test/scala-3/pl/iterators/kebs/circe/CirceFormatTests.scala b/circe/src/test/scala-3/pl/iterators/kebs/circe/CirceFormatTests.scala deleted file mode 100644 index b7281f6a..00000000 --- a/circe/src/test/scala-3/pl/iterators/kebs/circe/CirceFormatTests.scala +++ /dev/null @@ -1,212 +0,0 @@ -package pl.iterators.kebs.circe - -import io.circe.{Decoder, Encoder, Json} -import org.scalatest.funsuite.AnyFunSuite -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._ -import pl.iterators.kebs.core.macros.CaseClass1ToValueClass - -class CirceFormatTests extends AnyFunSuite with Matchers { - object KebsProtocol extends KebsCirce with CaseClass1ToValueClass - import KebsProtocol.* - - // https://github.com/circe/circe/issues/1980 - case class R(a: Int, rs: Seq[R]) derives Decoder, Encoder.AsObject - - test("Flat format") { - val decoder = implicitly[Decoder[C]] - val encoder = implicitly[Encoder[C]] - decoder.apply(Json.fromInt(10).hcursor) shouldBe Right(C(10)) - encoder.apply(C(10)) shouldBe Json.fromInt(10) - } - test("Flat format - parametrized") { - val decoder = implicitly[Decoder[Parametrized1[Double]]] - val encoder = implicitly[Encoder[Parametrized1[Double]]] - decoder.apply(Json.fromDouble(15.0).get.hcursor) shouldBe Right(Parametrized1(15.0)) - encoder.apply(Parametrized1(15.0)) shouldBe Json.fromDouble(15.0).get - } - - test("Format 0") { - val decoder = implicitly[Decoder[F.type]] - val encoder = implicitly[Encoder[F.type]] - decoder.apply(Json.fromFields(Seq.empty[(String, Json)]).hcursor) shouldBe Right(F) - encoder.apply(F) shouldBe Json.fromFields(Seq.empty[(String, Json)]) - } - - test("Format 1") { - val decoder = implicitly[Decoder[D]] - val encoder = implicitly[Encoder[D]] - 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") { - val decoder = implicitly[Decoder[Parametrized2[Int, String]]] - val encoder = implicitly[Encoder[Parametrized2[Int, String]]] - decoder.apply(Json.fromFields(Seq("field1" -> Json.fromInt(5), "field2" -> Json.fromString("abcdef"))).hcursor) shouldBe Right( - Parametrized2(5, "abcdef") - ) - encoder.apply(Parametrized2(5, "abcdef")) shouldBe Json.fromFields( - Seq("field1" -> Json.fromInt(5), "field2" -> Json.fromString("abcdef")) - ) - } - - test("Format - DTO style") { - val decoder = implicitly[Decoder[DTO1]] - val encoder = implicitly[Encoder[DTO1]] - decoder.apply(Json.fromFields(Seq("c" -> Json.fromInt(10), "i" -> Json.fromInt(5))).hcursor) shouldBe Right(DTO1(C(10), 5)) - encoder.apply(DTO1(C(10), 5)) shouldBe Json.fromFields(Seq("c" -> Json.fromInt(10), "i" -> Json.fromInt(5))) - } - - test("Format - DTO style with Option") { - val decoder = implicitly[Decoder[DTO2]] - val encoder = implicitly[Encoder[DTO2]] - decoder.apply(Json.fromFields(Seq("c" -> Json.fromInt(10), "i" -> Json.fromInt(5))).hcursor) shouldBe Right(DTO2(Some(C(10)), 5)) - encoder.apply(DTO2(None, 5)) shouldBe Json.fromFields(Seq("c" -> Json.Null, "i" -> Json.fromInt(5))) - } - - test("Format - compound") { - val decoder = implicitly[Decoder[Compound]] - val encoder = implicitly[Encoder[Compound]] - - decoder.apply( - Json - .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( - "CField" -> Json.fromInt(5), - "DField" -> Json.fromFields(Seq("intField" -> Json.fromInt(10), "stringField" -> Json.fromString("abcd"))) - ) - ) - } - - test("Recursive format") { - val decoder = implicitly[Decoder[R]] - val encoder = implicitly[Encoder[R]] - - decoder.apply( - Json - .fromFields(Seq("a" -> Json.fromInt(1), "rs" -> Json.arr(Json.fromFields(Seq("a" -> Json.fromInt(2), "rs" -> Json.arr()))))) - .hcursor - ) shouldBe Right(R(1, Seq(R(2, Seq.empty[R])))) - encoder.apply(R(1, Seq(R(2, Seq.empty[R])))) shouldBe Json.fromFields( - Seq("a" -> Json.fromInt(1), "rs" -> Json.arr(Json.fromFields(Seq("a" -> Json.fromInt(2), "rs" -> Json.arr())))) - ) - } - - test("Format - case class with > 22 fields") { - import model._ - - val decoder = implicitly[Decoder[ClassWith23Fields]] - val encoder = implicitly[Encoder[ClassWith23Fields]] - val obj = ClassWith23Fields.Example - val json = Json.fromFields( - Seq( - "f1" -> Json.fromString("f1 value"), - "f2" -> Json.fromInt(2), - "f3" -> Json.fromInt(3), - "f4" -> Json.Null, - "f5" -> Json.fromString("f5 value"), - "fieldNumberSix" -> Json.fromString("six"), - "f7" -> Json.arr(Json.fromString("f7 value 1"), Json.fromString("f7 value 2")), - "f8" -> Json.fromString("f8 value"), - "f9" -> Json.fromString("f9 value"), - "f10" -> Json.fromString("f10 value"), - "f11" -> Json.fromString("f11 value"), - "f12" -> Json.fromString("f12 value"), - "f13" -> Json.fromString("f13 value"), - "f14" -> Json.fromString("f14 value"), - "f15" -> Json.fromString("f15 value"), - "f16" -> Json.fromString("f16 value"), - "f17" -> Json.fromString("f17 value"), - "f18" -> Json.fromString("f18 value"), - "f19" -> Json.fromString("f19 value"), - "f20" -> Json.fromString("f20 value"), - "f21" -> Json.fromString("f21 value"), - "f22" -> Json.fromString("f22 value"), - "f23" -> Json.fromBoolean(true) - ) - ) - - encoder.apply(obj) shouldBe json - decoder.apply(json.hcursor) shouldBe Right(obj) - } - - test("Nested case classes with > 22 fields") { - import model._ - - val decoder = implicitly[Decoder[ClassWith23FieldsNested]] - val encoder = implicitly[Encoder[ClassWith23FieldsNested]] - val obj = ClassWith23FieldsNested.Example - val json = Json.fromFields( - Map( - "f1" -> Json.fromString("f1 value"), - "f2" -> Json.fromFields( - Seq( - "f1" -> Json.fromString("f1 value"), - "f2" -> Json.fromInt(2), - "f3" -> Json.fromInt(3), - "f4" -> Json.Null, - "f5" -> Json.fromString("f5 value"), - "fieldNumberSix" -> Json.fromString("six"), - "f7" -> Json.arr(Json.fromString("f7 value 1"), Json.fromString("f7 value 2")), - "f8" -> Json.fromString("f8 value"), - "f9" -> Json.fromString("f9 value"), - "f10" -> Json.fromString("f10 value"), - "f11" -> Json.fromString("f11 value"), - "f12" -> Json.fromString("f12 value"), - "f13" -> Json.fromString("f13 value"), - "f14" -> Json.fromString("f14 value"), - "f15" -> Json.fromString("f15 value"), - "f16" -> Json.fromString("f16 value"), - "f17" -> Json.fromString("f17 value"), - "f18" -> Json.fromString("f18 value"), - "f19" -> Json.fromString("f19 value"), - "f20" -> Json.fromString("f20 value"), - "f21" -> Json.fromString("f21 value"), - "f22" -> Json.fromString("f22 value"), - "f23" -> Json.fromBoolean(true) - ) - ), - "f3" -> Json.fromInt(3), - "f4" -> Json.Null, - "f5" -> Json.fromString("f5 value"), - "fieldNumberSix" -> Json.fromString("six"), - "f7" -> Json.arr(Json.fromString("f7 value 1"), Json.fromString("f7 value 2")), - "f8" -> Json.fromString("f8 value"), - "f9" -> Json.fromString("f9 value"), - "f10" -> Json.fromString("f10 value"), - "f11" -> Json.fromString("f11 value"), - "f12" -> Json.fromString("f12 value"), - "f13" -> Json.fromString("f13 value"), - "f14" -> Json.fromString("f14 value"), - "f15" -> Json.fromString("f15 value"), - "f16" -> Json.fromString("f16 value"), - "f17" -> Json.fromString("f17 value"), - "f18" -> Json.fromString("f18 value"), - "f19" -> Json.fromString("f19 value"), - "f20" -> Json.fromString("f20 value"), - "f21" -> Json.fromString("f21 value"), - "f22" -> Json.fromString("f22 value"), - "f23" -> Json.fromBoolean(true) - ) - ) - - encoder.apply(obj) shouldBe json - decoder.apply(json.hcursor) shouldBe Right(obj) - } -} diff --git a/circe/src/test/scala-3/pl/iterators/kebs/circe/CirceValueEnumDecoderEncoderTests.scala b/circe/src/test/scala-3/pl/iterators/kebs/circe/CirceValueEnumDecoderEncoderTests.scala deleted file mode 100644 index aa40860b..00000000 --- a/circe/src/test/scala-3/pl/iterators/kebs/circe/CirceValueEnumDecoderEncoderTests.scala +++ /dev/null @@ -1,33 +0,0 @@ -package pl.iterators.kebs.circe - -import io.circe._ -import org.scalatest.funsuite.AnyFunSuite -import org.scalatest.matchers.should.Matchers - -import pl.iterators.kebs.enums.KebsValueEnum -import pl.iterators.kebs.circe.model.LongGreeting - -class CirceValueEnumDecoderEncoderTests extends AnyFunSuite with Matchers with KebsValueEnum { - - object KebsProtocol extends KebsEnumFormats - - import LongGreeting._ - - test("value enum JsonFormat") { - import KebsProtocol._ - val decoder = implicitly[Decoder[LongGreeting]] - val encoder = implicitly[Encoder[LongGreeting]] - - decoder(Json.fromLong(0L).hcursor) shouldBe Right(Hello) - decoder(Json.fromLong(1L).hcursor) shouldBe Right(GoodBye) - - encoder(Hello) shouldBe Json.fromLong(0L) - encoder(GoodBye) shouldBe Json.fromLong(1L) - } - - test("value enum deserialization error") { - import KebsProtocol._ - val decoder = implicitly[Decoder[LongGreeting]] - decoder(Json.fromLong(4L).hcursor) shouldBe Left(DecodingFailure("4 is not a member of 0, 1, 2, 3", List.empty[CursorOp])) - } -} diff --git a/circe/src/test/scala-3/pl/iterators/kebs/circe/KebsEnumForTests.scala b/circe/src/test/scala-3/pl/iterators/kebs/circe/KebsEnumForTests.scala new file mode 100644 index 00000000..5f1cfd7a --- /dev/null +++ b/circe/src/test/scala-3/pl/iterators/kebs/circe/KebsEnumForTests.scala @@ -0,0 +1,5 @@ +package pl.iterators.kebs.circe + +import pl.iterators.kebs.enums.KebsEnum + +trait KebsEnumForTests extends KebsEnum diff --git a/circe/src/test/scala-3/pl/iterators/kebs/circe/KebsValueEnumForTests.scala b/circe/src/test/scala-3/pl/iterators/kebs/circe/KebsValueEnumForTests.scala new file mode 100644 index 00000000..2a949598 --- /dev/null +++ b/circe/src/test/scala-3/pl/iterators/kebs/circe/KebsValueEnumForTests.scala @@ -0,0 +1,5 @@ +package pl.iterators.kebs.circe + +import pl.iterators.kebs.enums.KebsValueEnum + +trait KebsValueEnumForTests extends KebsValueEnum diff --git a/circe/src/test/scala-3/pl/iterators/kebs/circe/instances/TimeInstancesMixinTests.scala b/circe/src/test/scala-3/pl/iterators/kebs/circe/instances/TimeInstancesMixinTests.scala deleted file mode 100644 index 9b9d1e26..00000000 --- a/circe/src/test/scala-3/pl/iterators/kebs/circe/instances/TimeInstancesMixinTests.scala +++ /dev/null @@ -1,115 +0,0 @@ -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 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.* - - "implicitly[ValueClassLike[Instant, Long]]" shouldNot typeCheck - "implicitly[ValueClassLike[Long, Instant]]" shouldNot typeCheck - - val decoder = implicitly[Decoder[Instant]] - val encoder = implicitly[Encoder[Instant]] - val value = 123456789 - val obj = Instant.ofEpochMilli(value) - - encoder(obj) shouldBe Json.fromInt(value) - decoder(Json.fromInt(value).hcursor) shouldBe Right(obj) - } - - test("Duration nanos format, Instant epoch milli format") { - object TimeInstancesProtocol extends KebsCirce with DurationNanosLong with InstantEpochMilliLong - import TimeInstancesProtocol.* - - "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]] - val value_duration = 123456789 - val obj_duration = Duration.ofNanos(value_duration) - - val decoder_instant = implicitly[Decoder[Instant]] - val encoder_instant = implicitly[Encoder[Instant]] - val value_instant = 123456789 - val obj_instant = Instant.ofEpochMilli(value_instant) - - encoder_duration(obj_duration) shouldBe Json.fromInt(value_duration) - decoder_duration(Json.fromInt(value_duration).hcursor) shouldBe Right(obj_duration) - - encoder_instant(obj_instant) shouldBe Json.fromInt(value_instant) - decoder_instant(Json.fromInt(value_instant).hcursor) shouldBe Right(obj_instant) - } - - test("LocalDateTime custom format using companion object") { - object TimeInstancesProtocol extends KebsCirce with LocalDateTimeString { - val formatter: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm") - - override implicit val localDateTimeFormatter: InstanceConverter[LocalDateTime, String] = - InstanceConverter.apply[LocalDateTime, String](_.format(formatter), LocalDateTime.parse(_, formatter)) - } - import TimeInstancesProtocol.* - - "implicitly[ValueClassLike[LocalDateTime, String]]" shouldNot typeCheck - "implicitly[ValueClassLike[String, LocalDateTime]]" shouldNot typeCheck - - val encoder = implicitly[Encoder[LocalDateTime]] - val decoder = implicitly[Decoder[LocalDateTime]] - val value = "2007/12/03 10:30" - val obj = LocalDateTime.parse(value, formatter) - - encoder(obj) shouldBe Json.fromString(value) - decoder(Json.fromString(value).hcursor) shouldBe Right(obj) - } - - test("LocalDateTime custom format with error handling") { - object TimeInstancesProtocol extends KebsCirce with TimeInstances { - val pattern = "yyyy/MM/dd HH:mm" - val formatter: DateTimeFormatter = DateTimeFormatter.ofPattern(pattern) - - override implicit val localDateTimeFormatter: InstanceConverter[LocalDateTime, String] = - new InstanceConverter[LocalDateTime, String] { - override def encode(obj: LocalDateTime): String = obj.format(formatter) - override def decode(value: String): LocalDateTime = - try { - LocalDateTime.parse(value, formatter) - } catch { - case e: DateTimeException => - throw new IllegalArgumentException( - s"${classOf[LocalDateTime]} cannot be parsed from $value – should be in format $pattern", - e - ) - case e: Throwable => throw e - } - } - } - import TimeInstancesProtocol.* - - "implicitly[ValueClassLike[LocalDateTime, String]]" shouldNot typeCheck - "implicitly[ValueClassLike[String, LocalDateTime]]" shouldNot typeCheck - - val encoder = implicitly[Encoder[LocalDateTime]] - val decoder = implicitly[Decoder[LocalDateTime]] - val value = "2007/12/03 10:30" - val obj = LocalDateTime.parse(value, formatter) - - encoder(obj) shouldBe Json.fromString(value) - decoder(Json.fromString(value).hcursor) shouldBe Right(obj) - } -} diff --git a/circe/src/test/scala-3/pl/iterators/kebs/circe/model/model.scala b/circe/src/test/scala-3/pl/iterators/kebs/circe/model/model.scala index fda4d707..144908e7 100644 --- a/circe/src/test/scala-3/pl/iterators/kebs/circe/model/model.scala +++ b/circe/src/test/scala-3/pl/iterators/kebs/circe/model/model.scala @@ -1,5 +1,6 @@ package pl.iterators.kebs.circe +import io.circe.{Decoder, Encoder} import java.time.ZonedDateTime import pl.iterators.kebs.core.enums.ValueEnumLikeEntry @@ -19,6 +20,9 @@ package object model { case class Compound(CField: C, DField: D) + // https://github.com/circe/circe/issues/1980 + case class R(a: Int, rs: Seq[R]) derives Decoder, Encoder.AsObject + enum Greeting { case Hello, GoodBye, Hi, Bye } diff --git a/circe/src/test/scala-2/pl/iterators/kebs/circe/CirceEnumDecoderEncoderTests.scala b/circe/src/test/scala/pl/iterators/kebs/circe/formats/CirceEnumDecoderEncoderTests.scala similarity index 80% rename from circe/src/test/scala-2/pl/iterators/kebs/circe/CirceEnumDecoderEncoderTests.scala rename to circe/src/test/scala/pl/iterators/kebs/circe/formats/CirceEnumDecoderEncoderTests.scala index 90f286a5..f0f4bb62 100644 --- a/circe/src/test/scala-2/pl/iterators/kebs/circe/CirceEnumDecoderEncoderTests.scala +++ b/circe/src/test/scala/pl/iterators/kebs/circe/formats/CirceEnumDecoderEncoderTests.scala @@ -1,17 +1,19 @@ -package pl.iterators.kebs.circe +package pl.iterators.kebs.circe.formats import io.circe._ import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.enumeratum.KebsEnumeratum -import pl.iterators.kebs.circe.model._ +import pl.iterators.kebs.circe.KebsEnumForTests +import pl.iterators.kebs.circe.enums.KebsCirceEnums +import pl.iterators.kebs.circe.enums.{KebsCirceEnumsLowercase, KebsCirceEnumsUppercase} import pl.iterators.kebs.circe.model.Greeting._ +import pl.iterators.kebs.circe.model._ -class CirceEnumDecoderEncoderTests extends AnyFunSuite with Matchers with KebsEnumeratum { +class CirceEnumDecoderEncoderTests extends AnyFunSuite with Matchers with KebsEnumForTests { - object KebsProtocol extends KebsEnumFormats - object KebsProtocolUppercase extends KebsEnumFormats.Uppercase - object KebsProtocolLowercase extends KebsEnumFormats.Lowercase + object KebsProtocol extends KebsCirceEnums + object KebsProtocolUppercase extends KebsCirceEnumsUppercase + object KebsProtocolLowercase extends KebsCirceEnumsLowercase test("enum JsonFormat") { import KebsProtocol._ diff --git a/circe/src/test/scala-2/pl/iterators/kebs/circe/CirceFormatCapitalizedVariantTests.scala b/circe/src/test/scala/pl/iterators/kebs/circe/formats/CirceFormatCapitalizedVariantTests.scala similarity index 95% rename from circe/src/test/scala-2/pl/iterators/kebs/circe/CirceFormatCapitalizedVariantTests.scala rename to circe/src/test/scala/pl/iterators/kebs/circe/formats/CirceFormatCapitalizedVariantTests.scala index 74de54a9..37841f65 100644 --- a/circe/src/test/scala-2/pl/iterators/kebs/circe/CirceFormatCapitalizedVariantTests.scala +++ b/circe/src/test/scala/pl/iterators/kebs/circe/formats/CirceFormatCapitalizedVariantTests.scala @@ -1,14 +1,14 @@ -package pl.iterators.kebs.circe +package pl.iterators.kebs.circe.formats 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.KebsCirceCapitalized import pl.iterators.kebs.circe.model._ import pl.iterators.kebs.core.macros.CaseClass1ToValueClass class CirceFormatCapitalizedVariantTests extends AnyFunSuite with Matchers { - object KebsProtocol extends KebsCirce with KebsCirce.Capitalized with CaseClass1ToValueClass + object KebsProtocol extends KebsCirceCapitalized with CaseClass1ToValueClass import KebsProtocol._ test("Flat format remains unchanged") { @@ -59,7 +59,6 @@ class CirceFormatCapitalizedVariantTests extends AnyFunSuite with Matchers { } test("Format capitalized - case class with > 22 fields") { - import model._ val decoder = implicitly[Decoder[ClassWith23Fields]] val encoder = implicitly[Encoder[ClassWith23Fields]] diff --git a/circe/src/test/scala-3/pl/iterators/kebs/circe/CirceFormatSnakifiedVariantTests.scala b/circe/src/test/scala/pl/iterators/kebs/circe/formats/CirceFormatSnakifiedVariantTests.scala similarity index 95% rename from circe/src/test/scala-3/pl/iterators/kebs/circe/CirceFormatSnakifiedVariantTests.scala rename to circe/src/test/scala/pl/iterators/kebs/circe/formats/CirceFormatSnakifiedVariantTests.scala index 55c12fe8..1e534133 100644 --- a/circe/src/test/scala-3/pl/iterators/kebs/circe/CirceFormatSnakifiedVariantTests.scala +++ b/circe/src/test/scala/pl/iterators/kebs/circe/formats/CirceFormatSnakifiedVariantTests.scala @@ -1,15 +1,15 @@ -package pl.iterators.kebs.circe +package pl.iterators.kebs.circe.formats import io.circe.{Decoder, Encoder, Json} import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers +import pl.iterators.kebs.circe.KebsCirceSnakified import pl.iterators.kebs.circe.model._ -import scala.Right import pl.iterators.kebs.core.macros.CaseClass1ToValueClass class CirceFormatSnakifiedVariantTests extends AnyFunSuite with Matchers { - object KebsProtocol extends KebsCirce with KebsCirce.Snakified with CaseClass1ToValueClass - import KebsProtocol.* + object KebsProtocol extends KebsCirceSnakified with CaseClass1ToValueClass + import KebsProtocol._ test("Flat format remains unchanged") { val decoder = implicitly[Decoder[C]] @@ -59,8 +59,6 @@ class CirceFormatSnakifiedVariantTests extends AnyFunSuite with Matchers { } test("Format snakified - case class with > 22 fields") { - import model._ - val decoder = implicitly[Decoder[ClassWith23Fields]] val encoder = implicitly[Encoder[ClassWith23Fields]] val obj = ClassWith23Fields( diff --git a/circe/src/test/scala-2/pl/iterators/kebs/circe/CirceFormatTests.scala b/circe/src/test/scala/pl/iterators/kebs/circe/formats/CirceFormatTests.scala similarity index 99% rename from circe/src/test/scala-2/pl/iterators/kebs/circe/CirceFormatTests.scala rename to circe/src/test/scala/pl/iterators/kebs/circe/formats/CirceFormatTests.scala index 157eb570..ee0ce8a1 100644 --- a/circe/src/test/scala-2/pl/iterators/kebs/circe/CirceFormatTests.scala +++ b/circe/src/test/scala/pl/iterators/kebs/circe/formats/CirceFormatTests.scala @@ -1,9 +1,9 @@ -package pl.iterators.kebs.circe +package pl.iterators.kebs.circe.formats 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.KebsCirce import pl.iterators.kebs.circe.model._ import pl.iterators.kebs.core.macros.CaseClass1ToValueClass @@ -105,7 +105,6 @@ class CirceFormatTests extends AnyFunSuite with Matchers { } test("Format - case class with > 22 fields") { - import model._ val decoder = implicitly[Decoder[ClassWith23Fields]] val encoder = implicitly[Encoder[ClassWith23Fields]] @@ -143,7 +142,6 @@ class CirceFormatTests extends AnyFunSuite with Matchers { } test("Nested case classes with > 22 fields") { - import model._ val decoder = implicitly[Decoder[ClassWith23FieldsNested]] val encoder = implicitly[Encoder[ClassWith23FieldsNested]] diff --git a/circe/src/test/scala-2/pl/iterators/kebs/circe/CirceValueEnumDecoderEncoderTests.scala b/circe/src/test/scala/pl/iterators/kebs/circe/formats/CirceValueEnumDecoderEncoderTests.scala similarity index 86% rename from circe/src/test/scala-2/pl/iterators/kebs/circe/CirceValueEnumDecoderEncoderTests.scala rename to circe/src/test/scala/pl/iterators/kebs/circe/formats/CirceValueEnumDecoderEncoderTests.scala index 0b1fd146..6fb5cff6 100644 --- a/circe/src/test/scala-2/pl/iterators/kebs/circe/CirceValueEnumDecoderEncoderTests.scala +++ b/circe/src/test/scala/pl/iterators/kebs/circe/formats/CirceValueEnumDecoderEncoderTests.scala @@ -3,13 +3,13 @@ package pl.iterators.kebs.circe import io.circe._ import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.enumeratum.KebsValueEnumeratum +import pl.iterators.kebs.circe.enums.KebsCirceValueEnums import pl.iterators.kebs.circe.model.LongGreeting import pl.iterators.kebs.circe.model.LongGreeting._ -class CirceValueEnumDecoderEncoderTests extends AnyFunSuite with Matchers with KebsValueEnumeratum { +class CirceValueEnumDecoderEncoderTests extends AnyFunSuite with Matchers with KebsValueEnumForTests { - object KebsProtocol extends KebsEnumFormats + object KebsProtocol extends KebsCirceValueEnums test("value enum JsonFormat") { import KebsProtocol._ diff --git a/circe/src/test/scala-2/pl/iterators/kebs/circe/instances/TimeInstancesMixinTests.scala b/circe/src/test/scala/pl/iterators/kebs/circe/instances/TimeInstancesMixinTests.scala similarity index 100% rename from circe/src/test/scala-2/pl/iterators/kebs/circe/instances/TimeInstancesMixinTests.scala rename to circe/src/test/scala/pl/iterators/kebs/circe/instances/TimeInstancesMixinTests.scala index dac6f262..210dbb73 100644 --- a/circe/src/test/scala-2/pl/iterators/kebs/circe/instances/TimeInstancesMixinTests.scala +++ b/circe/src/test/scala/pl/iterators/kebs/circe/instances/TimeInstancesMixinTests.scala @@ -4,10 +4,10 @@ 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.core.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 java.time._ import java.time.format.DateTimeFormatter diff --git a/doobie/src/main/scala-2/pl/iterators/kebs/doobie/enums/package.scala b/doobie/src/main/scala-2/pl/iterators/kebs/doobie/enums/package.scala deleted file mode 100644 index f2d6e0c4..00000000 --- a/doobie/src/main/scala-2/pl/iterators/kebs/doobie/enums/package.scala +++ /dev/null @@ -1,8 +0,0 @@ -package pl.iterators.kebs.doobie - -package object enums extends KebsEnums { - object uppercase extends Uppercase - - object lowercase extends Lowercase - -} 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 deleted file mode 100644 index 2022afb7..00000000 --- a/doobie/src/main/scala-2/pl/iterators/kebs/doobie/package.scala +++ /dev/null @@ -1,3 +0,0 @@ -package pl.iterators.kebs - -package object doobie extends Kebs 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 deleted file mode 100644 index 3a8e2d50..00000000 --- a/doobie/src/main/scala-3/pl/iterators/kebs/doobie/Kebs.scala +++ /dev/null @@ -1,43 +0,0 @@ -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 implicit def metaFromValueClassLike[A, M](using vcLike: ValueClassLike[A, M], m: Meta[M]): Meta[A] = - m.imap(vcLike.apply)(vcLike.unapply) - - inline implicit def metaArrayFromValueClassLike[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 implicit def metaArrayOptionFromValueClassLike[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 implicit def metaFromInstanceConverter[A, M](using instanceConverter: InstanceConverter[A, M], m: Meta[M]): Meta[A] = - m.imap(instanceConverter.decode)(instanceConverter.encode) - - inline implicit def metaArrayFromInstanceConverter[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 implicit def metaArrayOptionFromInstanceConverter[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))) -} 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 deleted file mode 100644 index e52336f7..00000000 --- a/doobie/src/main/scala-3/pl/iterators/kebs/doobie/enums/KebsEnums.scala +++ /dev/null @@ -1,53 +0,0 @@ -package pl.iterators.kebs.doobie.enums - -import doobie.Meta -import scala.reflect.ClassTag - -import pl.iterators.kebs.core.enums.EnumLike - -trait KebsEnums { - inline implicit def enumMeta[E](using e: EnumLike[E]): Meta[E] = Meta.StringMeta.imap(e.valueOf)(_.toString) - inline implicit def enumArrayMeta[E](using e: EnumLike[E], m: Meta[Array[String]], ct: ClassTag[E]): Meta[Array[E]] = - m.imap(_.map(e.valueOf))(_.map(_.toString)) - inline implicit def 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 implicit def 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 implicit def 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 implicit def 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 implicit def 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 implicit def 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 implicit def 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))) - } -} diff --git a/doobie/src/main/scala-3/pl/iterators/kebs/doobie/enums/package.scala b/doobie/src/main/scala-3/pl/iterators/kebs/doobie/enums/package.scala deleted file mode 100644 index f2d6e0c4..00000000 --- a/doobie/src/main/scala-3/pl/iterators/kebs/doobie/enums/package.scala +++ /dev/null @@ -1,8 +0,0 @@ -package pl.iterators.kebs.doobie - -package object enums extends KebsEnums { - object uppercase extends Uppercase - - object lowercase extends Lowercase - -} 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 deleted file mode 100644 index 2022afb7..00000000 --- a/doobie/src/main/scala-3/pl/iterators/kebs/doobie/package.scala +++ /dev/null @@ -1,3 +0,0 @@ -package pl.iterators.kebs - -package object doobie extends Kebs diff --git a/doobie/src/main/scala-2/pl/iterators/kebs/doobie/Kebs.scala b/doobie/src/main/scala/pl/iterators/kebs/doobie/KebsDoobie.scala similarity index 98% rename from doobie/src/main/scala-2/pl/iterators/kebs/doobie/Kebs.scala rename to doobie/src/main/scala/pl/iterators/kebs/doobie/KebsDoobie.scala index c126ac22..453858a4 100644 --- a/doobie/src/main/scala-2/pl/iterators/kebs/doobie/Kebs.scala +++ b/doobie/src/main/scala/pl/iterators/kebs/doobie/KebsDoobie.scala @@ -6,7 +6,7 @@ import pl.iterators.kebs.core.macros.ValueClassLike import scala.reflect.ClassTag -trait Kebs { +trait KebsDoobie { 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 diff --git a/doobie/src/main/scala-2/pl/iterators/kebs/doobie/enums/KebsEnums.scala b/doobie/src/main/scala/pl/iterators/kebs/doobie/enums/KebsDoobieEnums.scala similarity index 66% rename from doobie/src/main/scala-2/pl/iterators/kebs/doobie/enums/KebsEnums.scala rename to doobie/src/main/scala/pl/iterators/kebs/doobie/enums/KebsDoobieEnums.scala index ed6d4cb1..7f031bce 100644 --- a/doobie/src/main/scala-2/pl/iterators/kebs/doobie/enums/KebsEnums.scala +++ b/doobie/src/main/scala/pl/iterators/kebs/doobie/enums/KebsDoobieEnums.scala @@ -1,11 +1,11 @@ package pl.iterators.kebs.doobie.enums import doobie.Meta -import pl.iterators.kebs.core.enums.EnumLike +import pl.iterators.kebs.core.enums.{EnumLike, ValueEnumLike, ValueEnumLikeEntry} import scala.reflect.ClassTag -trait KebsEnums { +trait KebsDoobieEnums { 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)) @@ -15,7 +15,7 @@ trait KebsEnums { cte: ClassTag[Option[E]] ): Meta[Array[Option[E]]] = m.imap(_.map(_.map(e.withName)))(_.map(_.map(_.toString))) - trait Uppercase { + trait KebsDoobieEnumsUppercase { 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]] = @@ -24,7 +24,7 @@ trait KebsEnums { m.imap(_.map(_.map(e.withNameUppercaseOnly)))(_.map(_.map(_.toString.toUpperCase))) } - trait Lowercase { + trait KebsDoobieEnumsLowercase { 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]] = @@ -34,4 +34,21 @@ trait KebsEnums { } } -object KebsEnums extends KebsEnums +trait KebsDoobieValueEnums { + implicit def valueEnumMeta[V, E <: ValueEnumLikeEntry[V]](implicit e: ValueEnumLike[V, E], m: Meta[V]): Meta[E] = + m.imap(e.withValue)(_.value) + + implicit def valueEnumArrayMeta[V, E <: ValueEnumLikeEntry[V]](implicit + e: ValueEnumLike[V, E], + m: Meta[Array[V]], + cte: ClassTag[E], + ctv: ClassTag[V] + ): Meta[Array[E]] = m.imap(_.map(e.withValue))(_.map(_.value)) + + implicit def valueEnumOptionArrayMeta[V, E <: ValueEnumLikeEntry[V]](implicit + e: ValueEnumLike[V, E], + m: Meta[Array[Option[V]]], + cte: ClassTag[Option[E]], + ctv: ClassTag[V] + ): Meta[Array[Option[E]]] = m.imap(_.map(_.map(e.withValue)))(_.map(_.map(_.value))) +} diff --git a/doobie/src/main/scala/pl/iterators/kebs/doobie/enums/package.scala b/doobie/src/main/scala/pl/iterators/kebs/doobie/enums/package.scala new file mode 100644 index 00000000..a7249a94 --- /dev/null +++ b/doobie/src/main/scala/pl/iterators/kebs/doobie/enums/package.scala @@ -0,0 +1,7 @@ +package pl.iterators.kebs.doobie + +package object enums extends KebsDoobieEnums with KebsDoobieValueEnums { + object uppercase extends KebsDoobieEnumsUppercase with KebsDoobieValueEnums + + object lowercase extends KebsDoobieEnumsLowercase with KebsDoobieValueEnums +} diff --git a/doobie/src/main/scala/pl/iterators/kebs/doobie/package.scala b/doobie/src/main/scala/pl/iterators/kebs/doobie/package.scala new file mode 100644 index 00000000..92dbb99b --- /dev/null +++ b/doobie/src/main/scala/pl/iterators/kebs/doobie/package.scala @@ -0,0 +1,3 @@ +package pl.iterators.kebs + +package object doobie extends KebsDoobie diff --git a/doobie/src/test/scala-2/pl/iterators/kebs/doobie/KebsEnumsForTests.scala b/doobie/src/test/scala-2/pl/iterators/kebs/doobie/KebsEnumsForTests.scala new file mode 100644 index 00000000..00a0761a --- /dev/null +++ b/doobie/src/test/scala-2/pl/iterators/kebs/doobie/KebsEnumsForTests.scala @@ -0,0 +1,5 @@ +package pl.iterators.kebs.doobie + +import pl.iterators.kebs.enumeratum.{KebsEnumeratum, KebsValueEnumeratum} + +trait KebsEnumsForTests extends KebsEnumeratum with KebsValueEnumeratum 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 index 794bf8b5..4b3824b2 100644 --- 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 @@ -1,6 +1,9 @@ package pl.iterators.kebs.doobie +import enumeratum.values.{IntEnum, IntEnumEntry} import enumeratum.{Enum, EnumEntry} +import pl.iterators.kebs.core.enums.ValueEnumLikeEntry + import java.util.Currency package object model { @@ -18,6 +21,17 @@ package object model { def values = findValues } + sealed abstract class LibraryItem(val value: Int) extends IntEnumEntry with ValueEnumLikeEntry[Int] + + object LibraryItem extends IntEnum[LibraryItem] { + case object Book extends LibraryItem(1) + case object Movie extends LibraryItem(2) + case object Magazine extends LibraryItem(3) + case object CD extends LibraryItem(4) + + val values = findValues + } + case class Person(name: Name, eyeColor: EyeColor, preferredCurrency: Currency, relatives: List[Name], eyeballsInTheJar: Array[EyeColor]) } diff --git a/doobie/src/test/scala-3/pl/iterators/kebs/doobie/ComplexTypesTests.scala b/doobie/src/test/scala-3/pl/iterators/kebs/doobie/ComplexTypesTests.scala deleted file mode 100644 index cc1454ed..00000000 --- a/doobie/src/test/scala-3/pl/iterators/kebs/doobie/ComplexTypesTests.scala +++ /dev/null @@ -1,69 +0,0 @@ -package pl.iterators.kebs.doobie - -import doobie.implicits.* -import doobie.postgres.* -import doobie.postgres.implicits.* -import doobie.* -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.* -import pl.iterators.kebs.doobie.enums.* -import pl.iterators.kebs.instances.KebsInstances.* -import pl.iterators.kebs.opaque.Opaque - -import java.util.Currency - -class ComplexTypesTests extends AnyFunSuite with Matchers with KebsEnum { - test("Put & Get exist") { - "implicitly[Get[Name]]" should compile - "implicitly[Put[Name]]" should compile - "implicitly[Get[List[Name]]]" should compile - "implicitly[Put[List[Name]]]" should compile - "implicitly[Get[Array[Name]]]" should compile - "implicitly[Put[Array[Name]]]" should compile - "implicitly[Get[Vector[Name]]]" should compile - "implicitly[Put[Vector[Name]]]" should compile - "implicitly[Get[List[Option[Name]]]]" should compile - "implicitly[Put[List[Option[Name]]]]" should compile - "implicitly[Get[Array[Option[Name]]]]" should compile - "implicitly[Put[Array[Option[Name]]]]" should compile - "implicitly[Get[Vector[Option[Name]]]]" should compile - "implicitly[Put[Vector[Option[Name]]]]" should compile - - "implicitly[Get[Currency]]" should compile - "implicitly[Put[Currency]]" should compile - "implicitly[Get[List[Currency]]]" should compile - "implicitly[Put[List[Currency]]]" should compile - "implicitly[Get[Array[Currency]]]" should compile - "implicitly[Put[Array[Currency]]]" should compile - "implicitly[Get[Vector[Currency]]]" should compile - "implicitly[Put[Vector[Currency]]]" should compile - "implicitly[Get[List[Option[Currency]]]]" should compile - "implicitly[Put[List[Option[Currency]]]]" should compile - "implicitly[Get[Array[Option[Currency]]]]" should compile - "implicitly[Put[Array[Option[Currency]]]]" should compile - "implicitly[Get[Vector[Option[Currency]]]]" should compile - "implicitly[Put[Vector[Option[Currency]]]]" should compile - - "implicitly[Get[EyeColor]]" should compile - "implicitly[Put[EyeColor]]" should compile - "implicitly[Get[List[EyeColor]]]" should compile - "implicitly[Put[List[EyeColor]]]" should compile - "implicitly[Get[Array[EyeColor]]]" should compile - "implicitly[Put[Array[EyeColor]]]" should compile - "implicitly[Get[Vector[EyeColor]]]" should compile - "implicitly[Put[Vector[EyeColor]]]" should compile - "implicitly[Get[List[Option[EyeColor]]]]" should compile - "implicitly[Put[List[Option[EyeColor]]]]" should compile - "implicitly[Get[Array[Option[EyeColor]]]]" should compile - "implicitly[Put[Array[Option[EyeColor]]]]" should compile - "implicitly[Get[Vector[Option[EyeColor]]]]" should compile - "implicitly[Put[Vector[Option[EyeColor]]]]" should compile - } - - test("Query should compile") { - """sql"SELECT * FROM people".query[Person].unique""" should compile - } -} diff --git a/doobie/src/test/scala-3/pl/iterators/kebs/doobie/KebsEnumsForTests.scala b/doobie/src/test/scala-3/pl/iterators/kebs/doobie/KebsEnumsForTests.scala new file mode 100644 index 00000000..32428c83 --- /dev/null +++ b/doobie/src/test/scala-3/pl/iterators/kebs/doobie/KebsEnumsForTests.scala @@ -0,0 +1,5 @@ +package pl.iterators.kebs.doobie + +import pl.iterators.kebs.enums.{KebsEnum, KebsValueEnum} + +trait KebsEnumsForTests extends KebsEnum with KebsValueEnum 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 index c05fc831..421526b2 100644 --- 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 @@ -2,6 +2,7 @@ package pl.iterators.kebs.doobie import pl.iterators.kebs.opaque.Opaque import java.util.Currency +import pl.iterators.kebs.core.enums.ValueEnumLikeEntry package object model { opaque type Name = String @@ -11,6 +12,13 @@ package object model { case Blue, Green, Brown, Other } + enum LibraryItem(val value: Int) extends ValueEnumLikeEntry[Int] { + case Book extends LibraryItem(1) + case Movie extends LibraryItem(2) + case Magazine extends LibraryItem(3) + case CD extends LibraryItem(4) + } + case class Person(name: Name, eyeColor: EyeColor, preferredCurrency: Currency, relatives: List[Name], eyeballsInTheJar: Array[EyeColor]) } diff --git a/doobie/src/test/scala-2/pl/iterators/kebs/doobie/ComplexTypesTests.scala b/doobie/src/test/scala/pl/iterators/kebs/doobie/ComplexTypesTests.scala similarity index 74% rename from doobie/src/test/scala-2/pl/iterators/kebs/doobie/ComplexTypesTests.scala rename to doobie/src/test/scala/pl/iterators/kebs/doobie/ComplexTypesTests.scala index 4c02e5f7..34452e4a 100644 --- a/doobie/src/test/scala-2/pl/iterators/kebs/doobie/ComplexTypesTests.scala +++ b/doobie/src/test/scala/pl/iterators/kebs/doobie/ComplexTypesTests.scala @@ -2,7 +2,6 @@ package pl.iterators.kebs.doobie import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.enumeratum.KebsEnumeratum import doobie._ import doobie.implicits._ import doobie.postgres._ @@ -15,7 +14,13 @@ import pl.iterators.kebs.doobie.model._ import java.util.Currency -class ComplexTypesTests extends AnyFunSuite with Matchers with KebsEnumeratum with CaseClass1ToValueClass { +class ComplexTypesTests + extends AnyFunSuite + with Matchers + with KebsEnumsForTests + with CaseClass1ToValueClass + with KebsDoobieEnums + with KebsDoobieValueEnums { test("Put & Get exist") { "implicitly[Get[Name]]" should compile @@ -62,6 +67,21 @@ class ComplexTypesTests extends AnyFunSuite with Matchers with KebsEnumeratum wi "implicitly[Put[Array[Option[EyeColor]]]]" should compile "implicitly[Get[Vector[Option[EyeColor]]]]" should compile "implicitly[Put[Vector[Option[EyeColor]]]]" should compile + + "implicitly[Get[LibraryItem]]" should compile + "implicitly[Put[LibraryItem]]" should compile + "implicitly[Get[List[LibraryItem]]]" should compile + "implicitly[Put[List[LibraryItem]]]" should compile + "implicitly[Get[Array[LibraryItem]]]" should compile + "implicitly[Put[Array[LibraryItem]]]" should compile + "implicitly[Get[Vector[LibraryItem]]]" should compile + "implicitly[Put[Vector[LibraryItem]]]" should compile + "implicitly[Get[List[Option[LibraryItem]]]]" should compile + "implicitly[Put[List[Option[LibraryItem]]]]" should compile + "implicitly[Get[Array[Option[LibraryItem]]]]" should compile + "implicitly[Put[Array[Option[LibraryItem]]]]" should compile + "implicitly[Get[Vector[Option[LibraryItem]]]]" should compile + "implicitly[Put[Vector[Option[LibraryItem]]]]" should compile } test("Query should compile") { 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 index f2be9b03..4e66b6ea 100644 --- a/enum/src/main/scala-3/pl/iterators/kebs/enums/KebsEnum.scala +++ b/enum/src/main/scala-3/pl/iterators/kebs/enums/KebsEnum.scala @@ -3,12 +3,12 @@ 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 scala.reflect.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] = { + inline implicit def kebsEnumScala[E <: Enum](using m: Mirror.SumOf[E]): EnumLike[E] = { val enumValues = summonCases[m.MirroredElemTypes, E] new EnumLike[E] { override def values: immutable.Seq[E] = enumValues.toSeq 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 index ccb35e30..56ee7a0f 100644 --- a/enum/src/main/scala-3/pl/iterators/kebs/enums/KebsValueEnum.scala +++ b/enum/src/main/scala-3/pl/iterators/kebs/enums/KebsValueEnum.scala @@ -3,13 +3,11 @@ 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], + inline implicit def kebsValueEnumScala[V, E <: ValueEnumLikeEntry[V]](using m: Mirror.SumOf[E] ): ValueEnumLike[V, E] = { val enumValues = summonValueCases[m.MirroredElemTypes, V, E] 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 index 98f6b742..6721397f 100644 --- a/enum/src/test/scala-3/pl/iterators/kebs/enums/ValueEnumTest.scala +++ b/enum/src/test/scala-3/pl/iterators/kebs/enums/ValueEnumTest.scala @@ -2,7 +2,6 @@ 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} 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 index 98c063c9..a247a3a2 100644 --- a/enumeratum/src/main/scala-3/pl/iterators/kebs/enumeratum/KebsEnumeratum.scala +++ b/enumeratum/src/main/scala-3/pl/iterators/kebs/enumeratum/KebsEnumeratum.scala @@ -4,12 +4,11 @@ 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 implicit def enumLikeFromEnumeratum[E <: EnumEntry](using m: Mirror.SumOf[E], ct: ClassTag[E]): EnumLike[E] = { + inline implicit def enumLikeFromEnumeratum[E <: EnumEntry](using m: Mirror.SumOf[E]): EnumLike[E] = { val enumValues = summonCases[m.MirroredElemTypes, E] new EnumLike[E] { override def values: immutable.Seq[E] = enumValues.toSeq 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 index 5fe0a21a..de6a675b 100644 --- a/enumeratum/src/main/scala-3/pl/iterators/kebs/enumeratum/KebsValueEnumeratum.scala +++ b/enumeratum/src/main/scala-3/pl/iterators/kebs/enumeratum/KebsValueEnumeratum.scala @@ -4,14 +4,12 @@ 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 implicit def valueEnumeratumScala3[V, E <: ValueEnumEntry[V] with ValueEnumLikeEntry[V]](using - m: Mirror.SumOf[E], - ct: ClassTag[E] + m: Mirror.SumOf[E] ): ValueEnumLike[V, E] = { val enumValues = summonValueCases[m.MirroredElemTypes, V, E] new ValueEnumLike[V, E] { diff --git a/examples/src/main/scala/pl/iterators/kebs/examples/CirceExample.scala b/examples/src/main/scala/pl/iterators/kebs/examples/CirceExample.scala index 5c04e7a8..d712c689 100644 --- a/examples/src/main/scala/pl/iterators/kebs/examples/CirceExample.scala +++ b/examples/src/main/scala/pl/iterators/kebs/examples/CirceExample.scala @@ -3,7 +3,7 @@ package pl.iterators.kebs.examples import java.net.URL import java.util.UUID -import org.apache.pekko.http.scaladsl.marshalling.{ToResponseMarshallable, _} +import org.apache.pekko.http.scaladsl.marshalling._ import org.apache.pekko.http.scaladsl.model.MediaTypes.`application/json` import org.apache.pekko.http.scaladsl.model.StatusCodes._ import org.apache.pekko.http.scaladsl.model.{ContentType, ContentTypeRange, HttpEntity, MediaType} diff --git a/examples/src/main/scala/pl/iterators/kebs/examples/EnumSprayJsonFormat.scala b/examples/src/main/scala/pl/iterators/kebs/examples/EnumSprayJsonFormat.scala index 8788bea4..4dc30775 100644 --- a/examples/src/main/scala/pl/iterators/kebs/examples/EnumSprayJsonFormat.scala +++ b/examples/src/main/scala/pl/iterators/kebs/examples/EnumSprayJsonFormat.scala @@ -8,7 +8,7 @@ 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 enumeratum.{Enum, EnumEntry} -import pl.iterators.kebs.json.{KebsEnumFormats, KebsSpray} +import pl.iterators.kebs.playjson.{KebsEnumFormats, KebsSpray} import spray.json.{DefaultJsonProtocol, JsString, JsValue, JsonFormat, JsonReader, JsonWriter, deserializationError} import scala.concurrent.{ExecutionContext, Future} diff --git a/examples/src/main/scala/pl/iterators/kebs/examples/PlayJsonFormat.scala b/examples/src/main/scala/pl/iterators/kebs/examples/PlayJsonFormat.scala index 112da84c..ce910805 100644 --- a/examples/src/main/scala/pl/iterators/kebs/examples/PlayJsonFormat.scala +++ b/examples/src/main/scala/pl/iterators/kebs/examples/PlayJsonFormat.scala @@ -4,7 +4,7 @@ import java.net.URL import java.util.UUID import enumeratum.{Enum, EnumEntry, PlayJsonEnum} -import pl.iterators.kebs.json.KebsPlay +import pl.iterators.kebs.playjson.KebsPlayJson import play.api.libs.json._ import scala.util.Try @@ -37,7 +37,7 @@ object PlayJsonFormat { implicit val thingJsonFormat = Json.format[Thing] } - object AfterKebs extends JsonProtocol with KebsPlay { + object AfterKebs extends JsonProtocol with KebsPlayJson { implicit val errorJsonFormat = Json.format[Error] implicit val locationJsonFormat = Json.format[Location] implicit val createThingRequestJsonFormat = Json.format[ThingCreateRequest] diff --git a/examples/src/main/scala/pl/iterators/kebs/examples/SprayJsonFormat.scala b/examples/src/main/scala/pl/iterators/kebs/examples/SprayJsonFormat.scala index bcb3a472..3d929fd7 100644 --- a/examples/src/main/scala/pl/iterators/kebs/examples/SprayJsonFormat.scala +++ b/examples/src/main/scala/pl/iterators/kebs/examples/SprayJsonFormat.scala @@ -6,7 +6,7 @@ import org.apache.pekko.http.scaladsl.model.StatusCodes._ import org.apache.pekko.http.scaladsl.server.Directives._ import pl.iterators.kebs.circe.instances.net.URIString import pl.iterators.kebs.circe.instances.util.UUIDString -import pl.iterators.kebs.json.KebsSpray +import pl.iterators.kebs.playjson.KebsSpray import spray.json._ import java.net.URI diff --git a/examples/src/main/scala/pl/iterators/kebs/examples/SprayJsonWithPekkoHttpExample.scala b/examples/src/main/scala/pl/iterators/kebs/examples/SprayJsonWithPekkoHttpExample.scala index 752fc959..b7dbcdb5 100644 --- a/examples/src/main/scala/pl/iterators/kebs/examples/SprayJsonWithPekkoHttpExample.scala +++ b/examples/src/main/scala/pl/iterators/kebs/examples/SprayJsonWithPekkoHttpExample.scala @@ -15,7 +15,7 @@ import org.apache.pekko.http.scaladsl.model._ import org.apache.pekko.http.scaladsl.model.headers.RawHeader import org.apache.pekko.http.scaladsl.unmarshalling.Unmarshal import org.apache.pekko.stream.Materializer -import pl.iterators.kebs.json.KebsSpray +import pl.iterators.kebs.playjson.KebsSpray import spray.json._ import scala.concurrent.duration._ 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 deleted file mode 100644 index 6ad20244..00000000 --- a/http4s-stir/src/main/scala-2/pl/iterators/kebs/http4sstir/unmarshallers/enums/KebsEnumUnmarshallers.scala +++ /dev/null @@ -1,53 +0,0 @@ -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/http4s-stir/src/main/scala-2/pl/iterators/kebs/http4sstir/unmarshallers/enums/package.scala b/http4s-stir/src/main/scala-2/pl/iterators/kebs/http4sstir/unmarshallers/enums/package.scala deleted file mode 100644 index 515fbd46..00000000 --- a/http4s-stir/src/main/scala-2/pl/iterators/kebs/http4sstir/unmarshallers/enums/package.scala +++ /dev/null @@ -1,3 +0,0 @@ -package pl.iterators.kebs.http4sstir - -package object enums extends KebsEnumUnmarshallers 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 deleted file mode 100644 index e2c19def..00000000 --- a/http4s-stir/src/main/scala-3/pl/iterators/kebs/http4sstir/matchers/KebsMatchers.scala +++ /dev/null @@ -1,30 +0,0 @@ -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 deleted file mode 100644 index 020d406e..00000000 --- a/http4s-stir/src/main/scala-3/pl/iterators/kebs/http4sstir/unmarshallers/enums/KebsEnumUnmarshallers.scala +++ /dev/null @@ -1,69 +0,0 @@ -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(", ")}""")) - } - } - - implicit def 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(", ")}""") - ) - } - } - } - - implicit def kebsValueEnumUnmarshaller[V, E <: ValueEnumLikeEntry[V]](using - `enum`: ValueEnumLike[V, E], - cls: ClassTag[V] - ): Unmarshaller[V, E] = - valueEnumUnmarshaller - - implicit def kebsIntValueEnumFromStringUnmarshaller[E <: ValueEnumLikeEntry[Int]](using - ev: ValueEnumLike[Int, E] - ): FromStringUnmarshaller[E] = - intFromStringUnmarshaller andThen valueEnumUnmarshaller - - implicit def kebsLongValueEnumFromStringUnmarshaller[E <: ValueEnumLikeEntry[Long]](using - ev: ValueEnumLike[Long, E] - ): FromStringUnmarshaller[E] = - longFromStringUnmarshaller andThen valueEnumUnmarshaller - - implicit def kebsShortValueEnumFromStringUnmarshaller[E <: ValueEnumLikeEntry[Short]](using - ev: ValueEnumLike[Short, E] - ): FromStringUnmarshaller[E] = - shortFromStringUnmarshaller andThen valueEnumUnmarshaller - - implicit def 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/pl/iterators/kebs/http4sstir/unmarshallers/enums/package.scala b/http4s-stir/src/main/scala-3/pl/iterators/kebs/http4sstir/unmarshallers/enums/package.scala deleted file mode 100644 index 7b6c5418..00000000 --- a/http4s-stir/src/main/scala-3/pl/iterators/kebs/http4sstir/unmarshallers/enums/package.scala +++ /dev/null @@ -1,3 +0,0 @@ -package pl.iterators.kebs.http4sstir.unmarshallers - -package object enums extends KebsEnumUnmarshallers diff --git a/http4s-stir/src/main/scala-2/pl/iterators/kebs/http4sstir/matchers/KebsMatchers.scala b/http4s-stir/src/main/scala/pl/iterators/kebs/http4sstir/matchers/KebsHttp4sStirMatchers.scala similarity index 57% rename from http4s-stir/src/main/scala-2/pl/iterators/kebs/http4sstir/matchers/KebsMatchers.scala rename to http4s-stir/src/main/scala/pl/iterators/kebs/http4sstir/matchers/KebsHttp4sStirMatchers.scala index f822696a..2dcf5385 100644 --- a/http4s-stir/src/main/scala-2/pl/iterators/kebs/http4sstir/matchers/KebsMatchers.scala +++ b/http4s-stir/src/main/scala/pl/iterators/kebs/http4sstir/matchers/KebsHttp4sStirMatchers.scala @@ -1,23 +1,21 @@ package pl.iterators.kebs.http4sstir.matchers -import pl.iterators.stir.server.PathMatcher1 +import pl.iterators.kebs.core.enums._ import pl.iterators.kebs.core.instances.InstanceConverter import pl.iterators.kebs.core.macros.ValueClassLike -import pl.iterators.kebs.core.enums._ - -trait KebsMatchers extends pl.iterators.stir.server.PathMatchers { +import pl.iterators.stir.server.PathMatcher1 +trait KebsHttp4sStirMatchers 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) + def as[T](implicit rep: ValueClassLike[T, U]): PathMatcher1[T] = segment.map(rep.apply) + def asValueEnum[T <: ValueEnumLikeEntry[U]](implicit e: ValueEnumLike[U, T]): PathMatcher1[T] = segment.map(e.withValue) } implicit class SegmentConversion[Source](segment: PathMatcher1[Source]) { def to[Type](implicit ico: InstanceConverter[Type, Source]): PathMatcher1[Type] = segment.map(ico.decode) } - object EnumSegment { - def as[T](implicit e: EnumLike[T]): PathMatcher1[T] = { - Segment.map(e.withNameIgnoreCase) - } + implicit class SegmentEnumIsomorphism[U](segment: PathMatcher1[String]) { + def asEnum[T](implicit e: EnumLike[T]): PathMatcher1[T] = segment.map(e.withNameIgnoreCase) } } diff --git a/http4s-stir/src/main/scala/pl/iterators/kebs/http4sstir/matchers/package.scala b/http4s-stir/src/main/scala/pl/iterators/kebs/http4sstir/matchers/package.scala index f5c782bf..46805b58 100644 --- a/http4s-stir/src/main/scala/pl/iterators/kebs/http4sstir/matchers/package.scala +++ b/http4s-stir/src/main/scala/pl/iterators/kebs/http4sstir/matchers/package.scala @@ -1,3 +1,3 @@ package pl.iterators.kebs.http4sstir -package object matchers extends KebsMatchers +package object matchers extends KebsHttp4sStirMatchers diff --git a/http4s-stir/src/main/scala/pl/iterators/kebs/http4sstir/unmarshallers/KebsHttp4sStirUnmarshallers.scala b/http4s-stir/src/main/scala/pl/iterators/kebs/http4sstir/unmarshallers/KebsHttp4sStirUnmarshallers.scala new file mode 100644 index 00000000..1c6a9a54 --- /dev/null +++ b/http4s-stir/src/main/scala/pl/iterators/kebs/http4sstir/unmarshallers/KebsHttp4sStirUnmarshallers.scala @@ -0,0 +1,92 @@ +package pl.iterators.kebs.http4sstir.unmarshallers + +import pl.iterators.kebs.core.instances.InstanceConverter +import pl.iterators.kebs.core.macros.ValueClassLike +import pl.iterators.stir.unmarshalling.{FromStringUnmarshaller, Unmarshaller} +import pl.iterators.stir.unmarshalling.PredefinedFromStringUnmarshallers._ +import cats.effect.IO +import pl.iterators.kebs.core.enums.{EnumLike, ValueEnumLike, ValueEnumLikeEntry} + +trait KebsHttp4sStirEnumUnmarshallers { + private 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) +} + +private[kebs] trait LowerPriorityKebsHttp4sStirValueEnumUnmarshallers { + final def valueEnumUnmarshaller[V, E <: ValueEnumLikeEntry[V]](`enum`: ValueEnumLike[V, E]): Unmarshaller[V, E] = Unmarshaller { v => + `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`.getValuesToEntriesMap.keysIterator + .mkString(", ")}""") + ) + } + } + + implicit def kebsValueEnumFromStringUnmarshaller[E <: ValueEnumLikeEntry[String]](implicit + ev: ValueEnumLike[String, E] + ): FromStringUnmarshaller[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 KebsHttp4sStirValueEnumUnmarshallers extends LowerPriorityKebsHttp4sStirValueEnumUnmarshallers { + implicit def kebsValueEnumUnmarshaller[V, E <: ValueEnumLikeEntry[V]](implicit ev: ValueEnumLike[V, E]): Unmarshaller[V, E] = + valueEnumUnmarshaller(ev) +} + +trait KebsHttp4sStirUnmarshallers + extends LowPriorityKebsUnmarshallers + with KebsHttp4sStirEnumUnmarshallers + with KebsHttp4sStirValueEnumUnmarshallers { + @inline + implicit def kebsFromStringUnmarshaller[A, B](implicit + rep: ValueClassLike[B, A], + fsu: FromStringUnmarshaller[A] + ): FromStringUnmarshaller[B] = + fsu andThen kebsUnmarshaller(rep) + + @inline + implicit def kebsInstancesFromStringUnmarshaller[A, B](implicit + ico: InstanceConverter[B, A], + fsu: FromStringUnmarshaller[A] + ): FromStringUnmarshaller[B] = + fsu andThen kebsInstancesUnmarshaller(ico) + + // this is to make both 2.13.x and 3.x compilers happy + override implicit def kebsValueEnumUnmarshaller[V, E <: ValueEnumLikeEntry[V]](implicit ev: ValueEnumLike[V, E]): Unmarshaller[V, E] = + valueEnumUnmarshaller(ev) +} + +private[kebs] 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: ValueClassLike[B, A]): Unmarshaller[A, B] = + Unmarshaller.strict[A, B](rep.apply) +} diff --git a/http4s-stir/src/main/scala/pl/iterators/kebs/http4sstir/unmarshallers/KebsUnmarshallers.scala b/http4s-stir/src/main/scala/pl/iterators/kebs/http4sstir/unmarshallers/KebsUnmarshallers.scala deleted file mode 100644 index 5aedf715..00000000 --- a/http4s-stir/src/main/scala/pl/iterators/kebs/http4sstir/unmarshallers/KebsUnmarshallers.scala +++ /dev/null @@ -1,30 +0,0 @@ -package pl.iterators.kebs.http4sstir.unmarshallers - -import pl.iterators.kebs.core.instances.InstanceConverter -import pl.iterators.kebs.core.macros.ValueClassLike -import pl.iterators.stir.unmarshalling.{FromStringUnmarshaller, Unmarshaller} - -trait KebsUnmarshallers extends LowPriorityKebsUnmarshallers { - @inline - implicit def kebsFromStringUnmarshaller[A, B](implicit - rep: ValueClassLike[B, A], - fsu: FromStringUnmarshaller[A] - ): FromStringUnmarshaller[B] = - fsu andThen kebsUnmarshaller(rep) - - @inline - implicit def kebsInstancesFromStringUnmarshaller[A, B](implicit - ico: InstanceConverter[B, A], - fsu: FromStringUnmarshaller[A] - ): FromStringUnmarshaller[B] = - fsu andThen kebsInstancesUnmarshaller(ico) - -} - -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: ValueClassLike[B, A]): Unmarshaller[A, B] = - Unmarshaller.strict[A, B](rep.apply) -} diff --git a/http4s-stir/src/main/scala/pl/iterators/kebs/http4sstir/unmarshallers/package.scala b/http4s-stir/src/main/scala/pl/iterators/kebs/http4sstir/unmarshallers/package.scala index 1e711bc3..2561668d 100644 --- a/http4s-stir/src/main/scala/pl/iterators/kebs/http4sstir/unmarshallers/package.scala +++ b/http4s-stir/src/main/scala/pl/iterators/kebs/http4sstir/unmarshallers/package.scala @@ -1,3 +1,3 @@ package pl.iterators.kebs.http4sstir -package object unmarshallers extends KebsUnmarshallers +package object unmarshallers extends KebsHttp4sStirUnmarshallers diff --git a/http4s-stir/src/test/scala-2/pl/iterators/kebs/http4sstir/KebsEnumForTests.scala b/http4s-stir/src/test/scala-2/pl/iterators/kebs/http4sstir/KebsEnumForTests.scala new file mode 100644 index 00000000..cdf8522e --- /dev/null +++ b/http4s-stir/src/test/scala-2/pl/iterators/kebs/http4sstir/KebsEnumForTests.scala @@ -0,0 +1,5 @@ +package pl.iterators.kebs.http4sstir + +import pl.iterators.kebs.enumeratum.KebsEnumeratum + +trait KebsEnumForTests extends KebsEnumeratum diff --git a/http4s-stir/src/test/scala-2/pl/iterators/kebs/http4sstir/KebsValueEnumForTests.scala b/http4s-stir/src/test/scala-2/pl/iterators/kebs/http4sstir/KebsValueEnumForTests.scala new file mode 100644 index 00000000..d9821c31 --- /dev/null +++ b/http4s-stir/src/test/scala-2/pl/iterators/kebs/http4sstir/KebsValueEnumForTests.scala @@ -0,0 +1,5 @@ +package pl.iterators.kebs.http4sstir + +import pl.iterators.kebs.enumeratum.KebsValueEnumeratum + +trait KebsValueEnumForTests extends KebsValueEnumeratum diff --git a/http4s-stir/src/test/scala-2/pl/iterators/kebs/http4sstir/unmarshallers/Http4sStirUnmarshallersTests.scala b/http4s-stir/src/test/scala-2/pl/iterators/kebs/http4sstir/unmarshallers/Http4sStirUnmarshallersTests.scala deleted file mode 100644 index 9d1efa56..00000000 --- a/http4s-stir/src/test/scala-2/pl/iterators/kebs/http4sstir/unmarshallers/Http4sStirUnmarshallersTests.scala +++ /dev/null @@ -1,207 +0,0 @@ -package pl.iterators.kebs.http4sstir.unmarshallers - -import org.http4s.UrlForm -import pl.iterators.stir.server.{Directives, MalformedQueryParamRejection} -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.core.macros.CaseClass1ToValueClass -import pl.iterators.kebs.instances.net.URIString -import pl.iterators.kebs.instances.time.{DayOfWeekInt, YearMonthString} -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} - -class Http4sStirUnmarshallersTests - extends AnyFunSuite - with Matchers - with ScalatestRouteTest - with ScalaFutures - with Directives - with KebsUnmarshallers - with KebsEnumUnmarshallers - with URIString - with YearMonthString - with DayOfWeekInt - with KebsEnumeratum - with KebsValueEnumeratum - with CaseClass1ToValueClass { - implicit def runtime: cats.effect.unsafe.IORuntime = cats.effect.unsafe.IORuntime.global - - 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("Unmarshalling parameter") { - val testRoute = parameters(Symbol("i").as[I]) { i => - complete(i.toString) - } - Get("/?i=42") ~> testRoute ~> check { - responseAs[String] shouldEqual "I(42)" - } - } - - test("Unmarshalling optional parameter") { - val testRoute = parameters(Symbol("i").as[I].?) { i => - complete(i.toString) - } - Get("/?i=42") ~> testRoute ~> check { - responseAs[String] shouldEqual "Some(I(42))" - } - } - - test("Unmarshalling enum parameter") { - val testRoute = parameters(Symbol("greeting").as[Greeting]) { greeting => - complete(greeting.toString) - } - Get("/?greeting=hi") ~> testRoute ~> check { - responseAs[String] shouldEqual "Hi" - } - Get("/?greeting=blah") ~> testRoute ~> check { - rejection shouldEqual MalformedQueryParamRejection( - "greeting", - "Invalid value 'blah'. Expected one of: Hello, GoodBye, Hi, Bye", - None - ) - } - } - - test("Unmarshalling value enum parameter") { - val testRoute = parameters(Symbol("libraryItem").as[LibraryItem]) { item => - complete(item.toString) - } - Get("/?libraryItem=1") ~> testRoute ~> check { - responseAs[String] shouldEqual "Book" - } - Get("/?libraryItem=10") ~> testRoute ~> check { - rejection shouldEqual MalformedQueryParamRejection("libraryItem", "Invalid value '10'. Expected one of: 1, 2, 3, 4", None) - } - } - - test("Case class extraction") { - val route = - path("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))" } - } - - test("Unmarshalling instances parameter") { - val testRoute = path("instances") { - parameters(Symbol("year").as[YearMonth]) { year => - complete(year.toString) - } - } - Get("/instances?year=2021-05") ~> testRoute ~> check { - responseAs[String] shouldEqual "2021-05" - } - } - - test("Unmarshalling string value enum parameter") { - val testRoute = parameters(Symbol("shirtSize").as[ShirtSize]) { shirtSize => - complete(shirtSize.toString) - } - Get("/?shirtSize=M") ~> testRoute ~> check { - responseAs[String] shouldEqual "Medium" - } - Get("/?shirtSize=XL") ~> testRoute ~> check { - rejection shouldEqual MalformedQueryParamRejection("shirtSize", "Invalid value 'XL'. Expected one of: S, M, L", None) - } - } - - test("bug: work with default enum values") { - val route = - path("test_enum") { - parameter(Symbol("sort").as[SortOrder] ? (SortOrder.Desc: SortOrder)) { sort => - complete { - s"Sort was $sort" - } - } - } - - Get("/test_enum?sort=Asc") ~> route ~> check { - responseAs[String] shouldBe "Sort was Asc" - } - Get("/test_enum") ~> route ~> check { - responseAs[String] shouldBe "Sort was Desc" - } - } - - test("Unmarshal form field from String") { - val route = - path("test_form_fields") { - formFields("yearMonth".as[YearMonth]) { yearMonth => - complete(yearMonth.toString) - } - } - - Post("/test_form_fields", UrlForm("yearMonth" -> "2021-05")) ~> route ~> check { - responseAs[String] shouldEqual "2021-05" - } - } - - test("Unmarshal form fields from Int") { - val route = - path("test_form_fields") { - formFields("dayOfWeek".as[DayOfWeek]) { dayOfWeek => - complete(dayOfWeek.getValue.toString) - } - } - - Post("/test_form_fields", UrlForm("dayOfWeek" -> "1")) ~> route ~> check { - responseAs[String] shouldEqual "1" - } - } - - test("Unmarshal tagged parameters from Long") { - val route = - path("test_tagged") { - parameter("tagged".as[Id]) { id => - complete(id.toString) - } - } - - Get("/test_tagged?tagged=123456") ~> route ~> check { - responseAs[String] shouldBe "123456" - } - } - - test("Unmarshal tagged parameters from UUID") { - val route = - path("test_tagged") { - parameter("tagged".as[TestId]) { id => - complete(id.toString) - } - } - - Get("/test_tagged?tagged=ce7a7cf1-8c00-49a9-a963-9fd119dd0642") ~> route ~> check { - responseAs[String] shouldBe "ce7a7cf1-8c00-49a9-a963-9fd119dd0642" - } - } - - test("Unmarshal tagged URI") { - val route = - path("test_tagged") { - parameter("tagged".as[TestTaggedUri]) { id => - complete(id.toString) - } - } - - Get("/test_tagged?tagged=www.test.pl") ~> route ~> check { - responseAs[String] shouldBe "www.test.pl" - } - } -} diff --git a/http4s-stir/src/test/scala-3/pl/iterators/kebs/http4sstir/KebsEnumForTests.scala b/http4s-stir/src/test/scala-3/pl/iterators/kebs/http4sstir/KebsEnumForTests.scala new file mode 100644 index 00000000..662e2d9d --- /dev/null +++ b/http4s-stir/src/test/scala-3/pl/iterators/kebs/http4sstir/KebsEnumForTests.scala @@ -0,0 +1,5 @@ +package pl.iterators.kebs.http4sstir + +import pl.iterators.kebs.enums.KebsEnum + +trait KebsEnumForTests extends KebsEnum diff --git a/http4s-stir/src/test/scala-3/pl/iterators/kebs/http4sstir/KebsValueEnumForTests.scala b/http4s-stir/src/test/scala-3/pl/iterators/kebs/http4sstir/KebsValueEnumForTests.scala new file mode 100644 index 00000000..40594082 --- /dev/null +++ b/http4s-stir/src/test/scala-3/pl/iterators/kebs/http4sstir/KebsValueEnumForTests.scala @@ -0,0 +1,5 @@ +package pl.iterators.kebs.http4sstir + +import pl.iterators.kebs.enums.KebsValueEnum + +trait KebsValueEnumForTests extends KebsValueEnum diff --git a/http4s-stir/src/test/scala-3/pl/iterators/kebs/http4sstir/domain/Http4sStirTagsDomain.scala b/http4s-stir/src/test/scala-3/pl/iterators/kebs/http4sstir/domain/Http4sStirTagsDomain.scala index 8d2d6074..f359468d 100644 --- a/http4s-stir/src/test/scala-3/pl/iterators/kebs/http4sstir/domain/Http4sStirTagsDomain.scala +++ b/http4s-stir/src/test/scala-3/pl/iterators/kebs/http4sstir/domain/Http4sStirTagsDomain.scala @@ -10,10 +10,23 @@ import pl.iterators.kebs.core.enums.ValueEnumLikeEntry object Domain { opaque type TestTaggedUri = URI object TestTaggedUri extends Opaque[TestTaggedUri, URI] + opaque type TestId = UUID object TestId extends Opaque[TestId, UUID] + opaque type Id = Long object Id extends Opaque[Id, Long] + + opaque type TestDouble = Double + object TestDouble extends Opaque[TestDouble, Double] + + opaque type UUIDId = UUID + object UUIDId extends Opaque[UUIDId, UUID] { + def generate[T]: UUIDId = UUID.randomUUID() + def fromString[T](str: String): UUIDId = + UUID.fromString(str) + } + case class I(i: Int) case class S(s: String) case class P[A](a: A) diff --git a/http4s-stir/src/test/scala-3/pl/iterators/kebs/http4sstir/matchers/Http4sStirMatchersTests.scala b/http4s-stir/src/test/scala-3/pl/iterators/kebs/http4sstir/matchers/Http4sStirMatchersTests.scala deleted file mode 100644 index 6c1e5aad..00000000 --- a/http4s-stir/src/test/scala-3/pl/iterators/kebs/http4sstir/matchers/Http4sStirMatchersTests.scala +++ /dev/null @@ -1,86 +0,0 @@ -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.instances.net.URIString -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} - -class Http4sStirMatchersTests - extends AnyFunSuite - with Matchers - with Directives - with ScalatestRouteTest - with ScalaFutures - with ZonedDateTimeString - with DayOfWeekInt - with InstantEpochMilliLong - with URIString - with KebsEnum - with KebsValueEnum { - implicit def runtime: cats.effect.unsafe.IORuntime = cats.effect.unsafe.IORuntime.global - - 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") { - val testRoute = path("test" / Segment.to[ZonedDateTime]) { zdt => - complete(zdt.toString) - } - Get("/test/2011-12-03T10:15:30+01:00") ~> testRoute ~> check { - responseAs[String] shouldEqual "2011-12-03T10:15:30+01:00" - } - } - - test("Extract Int to DayOfWeek") { - val testRoute = path("test" / IntNumber.to[DayOfWeek]) { dayOfWeek => - complete(dayOfWeek.getValue.toString) - } - Get("/test/1") ~> testRoute ~> check { - responseAs[String] shouldEqual "1" - } - } - - test("Extract Long to Instant ") { - val testRoute = path("test" / LongNumber.to[Instant]) { instant => - complete(instant.toEpochMilli.toString) - } - Get("/test/1621258399") ~> testRoute ~> check { - responseAs[String] shouldEqual "1621258399" - } - } - - test("Extract String as Enum") { - val testRoute = path("test" / EnumSegment.as[Greeting]) { greeting => - complete(greeting.toString) - } - Get("/test/hello") ~> testRoute ~> check { - responseAs[String] shouldEqual "Hello" - } - } - - test("Extract String to URI as tagged URI") { - val testRoute = path("test" / Segment.to[URI].as[TestTaggedUri]) { id => - complete(id.toString) - } - Get("/test/ce7a7cf1-8c00-49a9-a963-9fd119dd0642") ~> testRoute ~> check { - responseAs[String] shouldEqual "ce7a7cf1-8c00-49a9-a963-9fd119dd0642" - } - } -} diff --git a/http4s-stir/src/test/scala-2/pl/iterators/kebs/http4sstir/matchers/Http4sStirMatchersTests.scala b/http4s-stir/src/test/scala/pl/iterators/kebs/http4sstir/matchers/Http4sStirMatchersTests.scala similarity index 81% rename from http4s-stir/src/test/scala-2/pl/iterators/kebs/http4sstir/matchers/Http4sStirMatchersTests.scala rename to http4s-stir/src/test/scala/pl/iterators/kebs/http4sstir/matchers/Http4sStirMatchersTests.scala index e172ae39..86debaaf 100644 --- a/http4s-stir/src/test/scala-2/pl/iterators/kebs/http4sstir/matchers/Http4sStirMatchersTests.scala +++ b/http4s-stir/src/test/scala/pl/iterators/kebs/http4sstir/matchers/Http4sStirMatchersTests.scala @@ -5,11 +5,14 @@ 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.circe.KebsEnumForTests +import pl.iterators.kebs.core.macros.CaseClass1ToValueClass +import pl.iterators.kebs.http4sstir.KebsValueEnumForTests import pl.iterators.kebs.instances.net.URIString 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} @@ -23,7 +26,9 @@ class Http4sStirMatchersTests with DayOfWeekInt with InstantEpochMilliLong with URIString - with KebsEnumeratum { + with KebsEnumForTests + with KebsValueEnumForTests + with CaseClass1ToValueClass { implicit def runtime: cats.effect.unsafe.IORuntime = cats.effect.unsafe.IORuntime.global test("No ValueClassLike implicits derived") { @@ -91,7 +96,7 @@ class Http4sStirMatchersTests } test("Extract String as Enum") { - val testRoute = path("test" / EnumSegment.as[Greeting]) { greeting => + val testRoute = path("test" / Segment.asEnum[Greeting]) { greeting => complete(greeting.toString) } Get("/test/hello") ~> testRoute ~> check { @@ -99,6 +104,24 @@ class Http4sStirMatchersTests } } + test("Extract String as value class") { + val testRoute = path("test" / Segment.as[S]) { item => + complete(item.toString) + } + Get("/test/check") ~> testRoute ~> check { + responseAs[String] shouldEqual "S(check)" + } + } + + test("Extract Int as ValueEnum") { + val testRoute = path("test" / IntNumber.asValueEnum[LibraryItem]) { item => + complete(item.toString) + } + Get("/test/1") ~> testRoute ~> check { + responseAs[String] shouldEqual "Book" + } + } + test("Extract String to URI as tagged URI") { val testRoute = path("test" / Segment.to[URI].as[TestTaggedUri]) { id => complete(id.toString) diff --git a/http4s-stir/src/test/scala-3/pl/iterators/kebs/http4sstir/unmarshallers/Http4sStirUnmarshallersTests.scala b/http4s-stir/src/test/scala/pl/iterators/kebs/http4sstir/unmarshallers/Http4sStirUnmarshallersTests.scala similarity index 94% rename from http4s-stir/src/test/scala-3/pl/iterators/kebs/http4sstir/unmarshallers/Http4sStirUnmarshallersTests.scala rename to http4s-stir/src/test/scala/pl/iterators/kebs/http4sstir/unmarshallers/Http4sStirUnmarshallersTests.scala index df3c7da5..a2ed4259 100644 --- a/http4s-stir/src/test/scala-3/pl/iterators/kebs/http4sstir/unmarshallers/Http4sStirUnmarshallersTests.scala +++ b/http4s-stir/src/test/scala/pl/iterators/kebs/http4sstir/unmarshallers/Http4sStirUnmarshallersTests.scala @@ -6,12 +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.http4sstir.domain.Domain._ +import pl.iterators.kebs.circe.{KebsEnumForTests, KebsValueEnumForTests} +import pl.iterators.kebs.core.macros.CaseClass1ToValueClass import pl.iterators.kebs.instances.net.URIString import pl.iterators.kebs.instances.time.{DayOfWeekInt, YearMonthString} -import pl.iterators.kebs.core.macros.CaseClass1ToValueClass -import pl.iterators.kebs.enums.{KebsEnum, KebsValueEnum} -import pl.iterators.kebs.http4sstir.unmarshallers.enums.KebsEnumUnmarshallers +import pl.iterators.kebs.http4sstir.domain.Domain._ +import pl.iterators.kebs.http4sstir.unmarshallers.KebsHttp4sStirUnmarshallers import java.time.{DayOfWeek, YearMonth} @@ -21,17 +21,18 @@ class Http4sStirUnmarshallersTests with ScalatestRouteTest with ScalaFutures with Directives - with KebsUnmarshallers - with KebsEnumUnmarshallers + with KebsHttp4sStirUnmarshallers with URIString with YearMonthString with DayOfWeekInt - with KebsEnum - with KebsValueEnum + with KebsEnumForTests + with KebsValueEnumForTests with CaseClass1ToValueClass { implicit def runtime: cats.effect.unsafe.IORuntime = cats.effect.unsafe.IORuntime.global 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 @@ -89,7 +90,7 @@ class Http4sStirUnmarshallersTests 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.apply _) { color => complete(color.toString) } } diff --git a/http4s/src/main/scala-2/pl/iterators/kebs/http4s/package.scala b/http4s/src/main/scala-2/pl/iterators/kebs/http4s/package.scala deleted file mode 100644 index 906c817b..00000000 --- a/http4s/src/main/scala-2/pl/iterators/kebs/http4s/package.scala +++ /dev/null @@ -1,3 +0,0 @@ -package pl.iterators.kebs - -package object http4s extends Http4s 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 deleted file mode 100644 index 878083b5..00000000 --- a/http4s/src/main/scala-3/pl/iterators/kebs/http4s/package.scala +++ /dev/null @@ -1,75 +0,0 @@ -package pl.iterators.kebs.http4s - -import scala.util.Try -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 org.http4s._ -import java.util.UUID - -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](using rep: ValueClassLike[T, String]) = new PathVar[T](str => Try(rep.apply(str))) -} - -object InstanceString { - def apply[T](using rep: InstanceConverter[T, String]) = new PathVar[T](str => Try(rep.decode(str))) -} - -object EnumString { - 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: ValueClassLike[T, Int]) = new PathVar[T](str => Try(rep.apply(str.toInt))) -} - -object InstanceInt { - def apply[T](using rep: InstanceConverter[T, Int]) = new PathVar[T](str => Try(rep.decode(str.toInt))) -} - -object WrappedLong { - def apply[T](using rep: ValueClassLike[T, Long]) = new PathVar[T](str => Try(rep.apply(str.toLong))) -} - -object InstanceLong { - def apply[T](using rep: InstanceConverter[T, Long]) = new PathVar[T](str => Try(rep.decode(str.toLong))) -} - -object WrappedUUID { - 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)))) -} - -implicit def queryParamDecoderFromValueClassLike[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))) - -implicit def queryParamDecoderFromInstanceConverter[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))) - -implicit def queryParamDecoderFromEnumLike[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/main/scala-2/pl/iterators/kebs/http4s/Http4s.scala b/http4s/src/main/scala/pl/iterators/kebs/http4s/KebsHttp4s.scala similarity index 99% rename from http4s/src/main/scala-2/pl/iterators/kebs/http4s/Http4s.scala rename to http4s/src/main/scala/pl/iterators/kebs/http4s/KebsHttp4s.scala index 67562e92..d69888c4 100644 --- a/http4s/src/main/scala-2/pl/iterators/kebs/http4s/Http4s.scala +++ b/http4s/src/main/scala/pl/iterators/kebs/http4s/KebsHttp4s.scala @@ -8,7 +8,7 @@ import pl.iterators.kebs.core.macros.ValueClassLike import java.util.UUID import scala.util.Try -trait Http4s { +trait KebsHttp4s { protected class PathVar[A](cast: String => Try[A]) { def unapply(str: String): Option[A] = if (str.nonEmpty) diff --git a/http4s/src/main/scala/pl/iterators/kebs/http4s/package.scala b/http4s/src/main/scala/pl/iterators/kebs/http4s/package.scala new file mode 100644 index 00000000..bee0ef16 --- /dev/null +++ b/http4s/src/main/scala/pl/iterators/kebs/http4s/package.scala @@ -0,0 +1,3 @@ +package pl.iterators.kebs + +package object http4s extends KebsHttp4s diff --git a/jsonschema/src/main/scala/pl/iterators/kebs/jsonschema/package.scala b/jsonschema/src/main/scala/pl/iterators/kebs/jsonschema/package.scala new file mode 100644 index 00000000..b05efb13 --- /dev/null +++ b/jsonschema/src/main/scala/pl/iterators/kebs/jsonschema/package.scala @@ -0,0 +1,3 @@ +package pl.iterators.kebs + +package object jsonschema extends KebsJsonSchema with KebsJsonSchemaPredefs 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 deleted file mode 100644 index cceddf29..00000000 --- a/pekko-http/src/main/scala-2/pl/iterators/kebs/pekkohttp/matchers/KebsMatchers.scala +++ /dev/null @@ -1,23 +0,0 @@ -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/package.scala b/pekko-http/src/main/scala-2/pl/iterators/kebs/pekkohttp/unmarshallers/enums/package.scala deleted file mode 100644 index d4002b2d..00000000 --- a/pekko-http/src/main/scala-2/pl/iterators/kebs/pekkohttp/unmarshallers/enums/package.scala +++ /dev/null @@ -1,3 +0,0 @@ -package pl.iterators.kebs.pekkohttp.unmarshallers - -package object enums extends KebsEnumUnmarshallers 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 deleted file mode 100644 index 54825976..00000000 --- a/pekko-http/src/main/scala-3/pl/iterators/kebs/pekkohttp/unmarshallers/enums/KebsEnumUnmarshallers.scala +++ /dev/null @@ -1,71 +0,0 @@ -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(", ")}""")) - } - } - - implicit def 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(", ")}""") - ) - } - } - } - - implicit def kebsValueEnumUnmarshaller[V, E <: ValueEnumLikeEntry[V]](using - `enum`: ValueEnumLike[V, E], - cls: ClassTag[V] - ): Unmarshaller[V, E] = - valueEnumUnmarshaller - - implicit def kebsIntValueEnumFromStringUnmarshaller[E <: ValueEnumLikeEntry[Int]](using - ev: ValueEnumLike[Int, E] - ): FromStringUnmarshaller[E] = - intFromStringUnmarshaller andThen valueEnumUnmarshaller - - implicit def kebsLongValueEnumFromStringUnmarshaller[E <: ValueEnumLikeEntry[Long]](using - ev: ValueEnumLike[Long, E] - ): FromStringUnmarshaller[E] = - longFromStringUnmarshaller andThen valueEnumUnmarshaller - - implicit def kebsShortValueEnumFromStringUnmarshaller[E <: ValueEnumLikeEntry[Short]](using - ev: ValueEnumLike[Short, E] - ): FromStringUnmarshaller[E] = - shortFromStringUnmarshaller andThen valueEnumUnmarshaller - - implicit def 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/pl/iterators/kebs/pekkohttp/unmarshallers/enums/package.scala b/pekko-http/src/main/scala-3/pl/iterators/kebs/pekkohttp/unmarshallers/enums/package.scala deleted file mode 100644 index d4002b2d..00000000 --- a/pekko-http/src/main/scala-3/pl/iterators/kebs/pekkohttp/unmarshallers/enums/package.scala +++ /dev/null @@ -1,3 +0,0 @@ -package pl.iterators.kebs.pekkohttp.unmarshallers - -package object enums extends KebsEnumUnmarshallers diff --git a/pekko-http/src/main/scala-3/pl/iterators/kebs/pekkohttp/matchers/KebsMatchers.scala b/pekko-http/src/main/scala/pl/iterators/kebs/pekkohttp/matchers/KebsPekkoHttpMatchers.scala similarity index 51% rename from pekko-http/src/main/scala-3/pl/iterators/kebs/pekkohttp/matchers/KebsMatchers.scala rename to pekko-http/src/main/scala/pl/iterators/kebs/pekkohttp/matchers/KebsPekkoHttpMatchers.scala index b5fd080e..beeb6128 100644 --- a/pekko-http/src/main/scala-3/pl/iterators/kebs/pekkohttp/matchers/KebsMatchers.scala +++ b/pekko-http/src/main/scala/pl/iterators/kebs/pekkohttp/matchers/KebsPekkoHttpMatchers.scala @@ -1,29 +1,21 @@ 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 +import pl.iterators.kebs.core.macros.ValueClassLike +import pl.iterators.kebs.core.enums._ -trait KebsMatchers extends org.apache.pekko.http.scaladsl.server.PathMatchers { - +trait KebsPekkoHttpMatchers 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) + def as[T](implicit rep: ValueClassLike[T, U]): PathMatcher1[T] = segment.map(rep.apply) + def asValueEnum[T <: ValueEnumLikeEntry[U]](implicit e: ValueEnumLike[U, T]): PathMatcher1[T] = segment.map(e.withValue) } implicit class SegmentConversion[Source](segment: PathMatcher1[Source]) { def to[Type](implicit ico: InstanceConverter[Type, Source]): PathMatcher1[Type] = segment.map(ico.decode) } - object EnumSegment { - def as[T <: 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(", ")}""")) - ) - } + implicit class SegmentEnumIsomorphism[U](segment: PathMatcher1[String]) { + def asEnum[T](implicit e: EnumLike[T]): PathMatcher1[T] = segment.map(e.withNameIgnoreCase) } } diff --git a/pekko-http/src/main/scala/pl/iterators/kebs/pekkohttp/matchers/package.scala b/pekko-http/src/main/scala/pl/iterators/kebs/pekkohttp/matchers/package.scala index 3af3236b..fed00343 100644 --- a/pekko-http/src/main/scala/pl/iterators/kebs/pekkohttp/matchers/package.scala +++ b/pekko-http/src/main/scala/pl/iterators/kebs/pekkohttp/matchers/package.scala @@ -1,3 +1,3 @@ package pl.iterators.kebs.pekkohttp -package object matchers extends KebsMatchers +package object matchers extends KebsPekkoHttpMatchers diff --git a/pekko-http/src/main/scala-2/pl/iterators/kebs/pekkohttp/unmarshallers/enums/KebsEnumUnmarshallers.scala b/pekko-http/src/main/scala/pl/iterators/kebs/pekkohttp/unmarshallers/KebsPekkoHttpUnmarshallers.scala similarity index 57% rename from pekko-http/src/main/scala-2/pl/iterators/kebs/pekkohttp/unmarshallers/enums/KebsEnumUnmarshallers.scala rename to pekko-http/src/main/scala/pl/iterators/kebs/pekkohttp/unmarshallers/KebsPekkoHttpUnmarshallers.scala index 80174671..32941ece 100644 --- a/pekko-http/src/main/scala-2/pl/iterators/kebs/pekkohttp/unmarshallers/enums/KebsEnumUnmarshallers.scala +++ b/pekko-http/src/main/scala/pl/iterators/kebs/pekkohttp/unmarshallers/KebsPekkoHttpUnmarshallers.scala @@ -1,11 +1,13 @@ -package pl.iterators.kebs.pekkohttp.unmarshallers.enums +package pl.iterators.kebs.pekkohttp.unmarshallers -import org.apache.pekko.http.scaladsl.unmarshalling.PredefinedFromStringUnmarshallers._ import org.apache.pekko.http.scaladsl.unmarshalling.{FromStringUnmarshaller, Unmarshaller} +import pl.iterators.kebs.core.instances.InstanceConverter +import pl.iterators.kebs.core.macros.ValueClassLike +import org.apache.pekko.http.scaladsl.unmarshalling.PredefinedFromStringUnmarshallers._ import org.apache.pekko.http.scaladsl.util.FastFuture import pl.iterators.kebs.core.enums.{EnumLike, ValueEnumLike, ValueEnumLikeEntry} -trait EnumUnmarshallers { +trait KebsPekkoHttpEnumUnmarshallers { final def enumUnmarshaller[E](`enum`: EnumLike[E]): FromStringUnmarshaller[E] = Unmarshaller { _ => name => `enum`.withNameInsensitiveOption(name) match { case Some(enumEntry) => FastFuture.successful(enumEntry) @@ -21,10 +23,10 @@ trait EnumUnmarshallers { enumUnmarshaller(ev) } -trait ValueEnumUnmarshallers { +private[kebs] trait LowerPriorityKebsPekkoHttpValueEnumUnmarshallers { 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 { + `enum`.values.find(e => e.value == v) match { case Some(enumEntry) => FastFuture.successful(enumEntry) case None => FastFuture.failed( @@ -34,8 +36,9 @@ trait ValueEnumUnmarshallers { } } - implicit def kebsValueEnumUnmarshaller[V, E <: ValueEnumLikeEntry[V]](implicit ev: ValueEnumLike[V, E]): Unmarshaller[V, E] = - valueEnumUnmarshaller(ev) + implicit def kebsValueEnumFromStringUnmarshaller[E <: ValueEnumLikeEntry[String]](implicit + ev: ValueEnumLike[String, E] + ): FromStringUnmarshaller[E] = valueEnumUnmarshaller(ev) implicit def kebsIntValueEnumFromStringUnmarshaller[E <: ValueEnumLikeEntry[Int]](implicit ev: ValueEnumLike[Int, E] @@ -55,4 +58,38 @@ trait ValueEnumUnmarshallers { byteFromStringUnmarshaller andThen valueEnumUnmarshaller(ev) } -trait KebsEnumUnmarshallers extends EnumUnmarshallers with ValueEnumUnmarshallers {} +trait KebsPekkoHttpValueEnumUnmarshallers extends LowerPriorityKebsPekkoHttpValueEnumUnmarshallers { + implicit def kebsValueEnumUnmarshaller[V, E <: ValueEnumLikeEntry[V]](implicit ev: ValueEnumLike[V, E]): Unmarshaller[V, E] = + valueEnumUnmarshaller(ev) +} + +trait KebsPekkoHttpUnmarshallers + extends LowPriorityKebsPekkoHttpUnmarshallers + with KebsPekkoHttpEnumUnmarshallers + with KebsPekkoHttpValueEnumUnmarshallers { + @inline + implicit def kebsFromStringUnmarshaller[A, B](implicit + rep: ValueClassLike[B, A], + fsu: FromStringUnmarshaller[A] + ): FromStringUnmarshaller[B] = + fsu andThen kebsUnmarshaller(rep) + + @inline + implicit def kebsInstancesFromStringUnmarshaller[A, B](implicit + ico: InstanceConverter[B, A], + fsu: FromStringUnmarshaller[A] + ): FromStringUnmarshaller[B] = + fsu andThen kebsInstancesUnmarshaller(ico) + + // this is to make both 2.13.x and 3.x compilers happy + override implicit def kebsValueEnumUnmarshaller[V, E <: ValueEnumLikeEntry[V]](implicit ev: ValueEnumLike[V, E]): Unmarshaller[V, E] = + valueEnumUnmarshaller(ev) +} + +private[kebs] trait LowPriorityKebsPekkoHttpUnmarshallers { + 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: ValueClassLike[B, A]): Unmarshaller[A, B] = + Unmarshaller.strict[A, B](rep.apply) +} diff --git a/pekko-http/src/main/scala/pl/iterators/kebs/pekkohttp/unmarshallers/KebsUnmarshallers.scala b/pekko-http/src/main/scala/pl/iterators/kebs/pekkohttp/unmarshallers/KebsUnmarshallers.scala deleted file mode 100644 index 44c3b437..00000000 --- a/pekko-http/src/main/scala/pl/iterators/kebs/pekkohttp/unmarshallers/KebsUnmarshallers.scala +++ /dev/null @@ -1,30 +0,0 @@ -package pl.iterators.kebs.pekkohttp.unmarshallers - -import org.apache.pekko.http.scaladsl.unmarshalling.{FromStringUnmarshaller, Unmarshaller} -import pl.iterators.kebs.core.instances.InstanceConverter -import pl.iterators.kebs.core.macros.ValueClassLike - -trait KebsUnmarshallers extends LowPriorityKebsUnmarshallers { - @inline - implicit def kebsFromStringUnmarshaller[A, B](implicit - rep: ValueClassLike[B, A], - fsu: FromStringUnmarshaller[A] - ): FromStringUnmarshaller[B] = - fsu andThen kebsUnmarshaller(rep) - - @inline - implicit def kebsInstancesFromStringUnmarshaller[A, B](implicit - ico: InstanceConverter[B, A], - fsu: FromStringUnmarshaller[A] - ): FromStringUnmarshaller[B] = - fsu andThen kebsInstancesUnmarshaller(ico) - -} - -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: ValueClassLike[B, A]): Unmarshaller[A, B] = - Unmarshaller.strict[A, B](rep.apply) -} diff --git a/pekko-http/src/main/scala/pl/iterators/kebs/pekkohttp/unmarshallers/package.scala b/pekko-http/src/main/scala/pl/iterators/kebs/pekkohttp/unmarshallers/package.scala index 473504c9..7700f57f 100644 --- a/pekko-http/src/main/scala/pl/iterators/kebs/pekkohttp/unmarshallers/package.scala +++ b/pekko-http/src/main/scala/pl/iterators/kebs/pekkohttp/unmarshallers/package.scala @@ -1,3 +1,3 @@ package pl.iterators.kebs.pekkohttp -package object unmarshallers extends KebsUnmarshallers +package object unmarshallers extends KebsPekkoHttpUnmarshallers diff --git a/pekko-http/src/test/scala-2/pl/iterators/kebs/pekkohttp/KebsEnumForTests.scala b/pekko-http/src/test/scala-2/pl/iterators/kebs/pekkohttp/KebsEnumForTests.scala new file mode 100644 index 00000000..6e4b97d1 --- /dev/null +++ b/pekko-http/src/test/scala-2/pl/iterators/kebs/pekkohttp/KebsEnumForTests.scala @@ -0,0 +1,5 @@ +package pl.iterators.kebs.pekkohttp + +import pl.iterators.kebs.enumeratum.KebsEnumeratum + +trait KebsEnumForTests extends KebsEnumeratum diff --git a/pekko-http/src/test/scala-2/pl/iterators/kebs/pekkohttp/KebsValueEnumForTests.scala b/pekko-http/src/test/scala-2/pl/iterators/kebs/pekkohttp/KebsValueEnumForTests.scala new file mode 100644 index 00000000..dda8b799 --- /dev/null +++ b/pekko-http/src/test/scala-2/pl/iterators/kebs/pekkohttp/KebsValueEnumForTests.scala @@ -0,0 +1,5 @@ +package pl.iterators.kebs.pekkohttp + +import pl.iterators.kebs.enumeratum.KebsValueEnumeratum + +trait KebsValueEnumForTests extends KebsValueEnumeratum diff --git a/pekko-http/src/test/scala-3/pl/iterators/kebs/pekkohttp/KebsEnumForTests.scala b/pekko-http/src/test/scala-3/pl/iterators/kebs/pekkohttp/KebsEnumForTests.scala new file mode 100644 index 00000000..6d1f3db7 --- /dev/null +++ b/pekko-http/src/test/scala-3/pl/iterators/kebs/pekkohttp/KebsEnumForTests.scala @@ -0,0 +1,5 @@ +package pl.iterators.kebs.pekkohttp + +import pl.iterators.kebs.enums.KebsEnum + +trait KebsEnumForTests extends KebsEnum diff --git a/pekko-http/src/test/scala-3/pl/iterators/kebs/pekkohttp/KebsValueEnumForTests.scala b/pekko-http/src/test/scala-3/pl/iterators/kebs/pekkohttp/KebsValueEnumForTests.scala new file mode 100644 index 00000000..79f83568 --- /dev/null +++ b/pekko-http/src/test/scala-3/pl/iterators/kebs/pekkohttp/KebsValueEnumForTests.scala @@ -0,0 +1,5 @@ +package pl.iterators.kebs.pekkohttp + +import pl.iterators.kebs.enums.KebsValueEnum + +trait KebsValueEnumForTests extends KebsValueEnum diff --git a/pekko-http/src/test/scala-3/pl/iterators/kebs/pekkohttp/domain/PekkoHttpTagsDomain.scala b/pekko-http/src/test/scala-3/pl/iterators/kebs/pekkohttp/domain/PekkoHttpTagsDomain.scala index 19a13b91..4fcca59e 100644 --- a/pekko-http/src/test/scala-3/pl/iterators/kebs/pekkohttp/domain/PekkoHttpTagsDomain.scala +++ b/pekko-http/src/test/scala-3/pl/iterators/kebs/pekkohttp/domain/PekkoHttpTagsDomain.scala @@ -10,10 +10,23 @@ import pl.iterators.kebs.core.enums.ValueEnumLikeEntry object Domain { opaque type TestTaggedUri = URI object TestTaggedUri extends Opaque[TestTaggedUri, URI] + opaque type TestId = UUID object TestId extends Opaque[TestId, UUID] + opaque type Id = Long object Id extends Opaque[Id, Long] + + opaque type TestDouble = Double + object TestDouble extends Opaque[TestDouble, Double] + + opaque type UUIDId = UUID + object UUIDId extends Opaque[UUIDId, UUID] { + def generate[T]: UUIDId = UUID.randomUUID() + def fromString[T](str: String): UUIDId = + UUID.fromString(str) + } + case class I(i: Int) case class S(s: String) case class P[A](a: A) diff --git a/pekko-http/src/test/scala-3/pl/iterators/kebs/pekkohttp/matchers/PekkoHttpMatchersTests.scala b/pekko-http/src/test/scala-3/pl/iterators/kebs/pekkohttp/matchers/PekkoHttpMatchersTests.scala deleted file mode 100644 index c5485da7..00000000 --- a/pekko-http/src/test/scala-3/pl/iterators/kebs/pekkohttp/matchers/PekkoHttpMatchersTests.scala +++ /dev/null @@ -1,83 +0,0 @@ -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.instances.net.URIString -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 - with Directives - with ScalatestRouteTest - with ScalaFutures - with ZonedDateTimeString - with DayOfWeekInt - with InstantEpochMilliLong - with URIString - with KebsEnum { - - 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") { - val testRoute = path("test" / Segment.to[ZonedDateTime]) { zdt => - complete(zdt.toString) - } - Get("/test/2011-12-03T10:15:30+01:00") ~> testRoute ~> check { - responseAs[String] shouldEqual "2011-12-03T10:15:30+01:00" - } - } - - test("Extract Int to DayOfWeek") { - val testRoute = path("test" / IntNumber.to[DayOfWeek]) { dayOfWeek => - complete(dayOfWeek.getValue.toString) - } - Get("/test/1") ~> testRoute ~> check { - responseAs[String] shouldEqual "1" - } - } - - test("Extract Long to Instant ") { - val testRoute = path("test" / LongNumber.to[Instant]) { instant => - complete(instant.toEpochMilli.toString) - } - Get("/test/1621258399") ~> testRoute ~> check { - responseAs[String] shouldEqual "1621258399" - } - } - - test("Extract String as Enum") { - val testRoute = path("test" / EnumSegment.as[Greeting]) { greeting => - complete(greeting.toString) - } - Get("/test/hello") ~> testRoute ~> check { - responseAs[String] shouldEqual "Hello" - } - } - - test("Extract String to URI as tagged URI") { - val testRoute = path("test" / Segment.to[URI].as[TestTaggedUri]) { id => - complete(id.toString) - } - Get("/test/ce7a7cf1-8c00-49a9-a963-9fd119dd0642") ~> testRoute ~> check { - responseAs[String] shouldEqual "ce7a7cf1-8c00-49a9-a963-9fd119dd0642" - } - } -} diff --git a/pekko-http/src/test/scala-3/pl/iterators/kebs/pekkohttp/unmarshallers/PekkoHttpUnmarshallersTests.scala b/pekko-http/src/test/scala-3/pl/iterators/kebs/pekkohttp/unmarshallers/PekkoHttpUnmarshallersTests.scala deleted file mode 100644 index f7fbf5d8..00000000 --- a/pekko-http/src/test/scala-3/pl/iterators/kebs/pekkohttp/unmarshallers/PekkoHttpUnmarshallersTests.scala +++ /dev/null @@ -1,243 +0,0 @@ -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 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 - with Matchers - with ScalatestRouteTest - with ScalaFutures - with Directives - with KebsUnmarshallers - with KebsEnumUnmarshallers - with URIString - with YearMonthString - with DayOfWeekInt - with KebsEnum - with KebsValueEnum - with CaseClass1ToValueClass { - - 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") { - Unmarshal(42).to[I].futureValue shouldBe I(42) - Unmarshal("42").to[S].futureValue shouldBe S("42") - } - - test("Unmarshal parametrized") { - Unmarshal("42").to[P[String]].futureValue shouldBe P("42") - } - - test("Unmarshal case object") { - """Unmarshal(42).to[O.type]""" shouldNot typeCheck - } - - test("Unmarshal enum") { - Unmarshal("hello").to[Greeting].futureValue shouldBe Greeting.Hello - Unmarshal("blah").to[Greeting].failed.futureValue shouldBe a[IllegalArgumentException] - } - - 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] - } - - test("No unmarshaller for case-classes of arity > 1") { - """Unmarshal("42").to[CantUnmarshall]""" shouldNot typeCheck - } - - test("Unmarshalling value enums is type-safe") { - """Unmarshal(1L).to[LibraryItem]""" shouldNot typeCheck - } - - test("Unmarshal from string") { - Unmarshal("42").to[I].futureValue shouldBe I(42) - } - - test("Unmarshalling parameter") { - val testRoute = parameters(Symbol("i").as[I]) { i => - complete(i.toString) - } - Get("/?i=42") ~> testRoute ~> check { - responseAs[String] shouldEqual "I(42)" - } - } - - test("Unmarshalling optional parameter") { - val testRoute = parameters(Symbol("i").as[I].?) { i => - complete(i.toString) - } - Get("/?i=42") ~> testRoute ~> check { - responseAs[String] shouldEqual "Some(I(42))" - } - } - - test("Unmarshalling enum parameter") { - val testRoute = parameters(Symbol("greeting").as[Greeting]) { greeting => - complete(greeting.toString) - } - Get("/?greeting=hi") ~> testRoute ~> check { - responseAs[String] shouldEqual "Hi" - } - Get("/?greeting=blah") ~> testRoute ~> check { - rejection shouldEqual MalformedQueryParamRejection( - "greeting", - "Invalid value 'blah'. Expected one of: Hello, GoodBye, Hi, Bye", - None - ) - } - } - - test("Unmarshalling value enum parameter") { - val testRoute = parameters(ToNameReceptacleEnhancements._symbol2NR(Symbol("libraryItem")).as[LibraryItem]) { item => - complete(item.toString) - } - Get("/?libraryItem=1") ~> testRoute ~> check { - responseAs[String] shouldEqual "Book" - } - Get("/?libraryItem=10") ~> testRoute ~> check { - rejection shouldEqual MalformedQueryParamRejection("libraryItem", "Invalid value '10'. Expected one of: 1, 2, 3, 4", None) - } - } - - 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 => - complete(color.toString) - } - } - Get("/color?red=1&green=2&blue=3") ~> route ~> check { responseAs[String] shouldEqual "Color(Red(1),Green(2),Blue(3))" } - } - - test("Unmarshalling instances parameter") { - val testRoute = path("instances") { - parameters(Symbol("year").as[YearMonth]) { year => - complete(year.toString) - } - } - Get("/instances?year=2021-05") ~> testRoute ~> check { - responseAs[String] shouldEqual "2021-05" - } - } - - test("Unmarshalling string value enum parameter") { - val testRoute = parameters(Symbol("shirtSize").as[ShirtSize]) { shirtSize => - complete(shirtSize.toString) - } - Get("/?shirtSize=M") ~> testRoute ~> check { - responseAs[String] shouldEqual "Medium" - } - Get("/?shirtSize=XL") ~> testRoute ~> check { - rejection shouldEqual MalformedQueryParamRejection("shirtSize", "Invalid value 'XL'. Expected one of: S, M, L", None) - } - } - - test("bug: work with default enum values") { - val route = - path("test_enum") { - parameter(Symbol("sort").as[SortOrder] ? (SortOrder.Desc: SortOrder)) { sort => - complete { - s"Sort was $sort" - } - } - } - - Get("/test_enum?sort=Asc") ~> route ~> check { - responseAs[String] shouldBe "Sort was Asc" - } - Get("/test_enum") ~> route ~> check { - responseAs[String] shouldBe "Sort was Desc" - } - } - - test("Unmarshal form field from String") { - val route = - path("test_form_fields") { - formFields("yearMonth".as[YearMonth]) { yearMonth => - complete(yearMonth.toString) - } - } - - Post("/test_form_fields", FormData("yearMonth" -> "2021-05")) ~> route ~> check { - responseAs[String] shouldEqual "2021-05" - } - } - - test("Unmarshal form fields from Int") { - val route = - path("test_form_fields") { - formFields("dayOfWeek".as[DayOfWeek]) { dayOfWeek => - complete(dayOfWeek.getValue.toString) - } - } - - Post("/test_form_fields", FormData("dayOfWeek" -> "1")) ~> route ~> check { - responseAs[String] shouldEqual "1" - } - } - - test("Unmarshal tagged parameters from Long") { - val route = - path("test_tagged") { - parameter("tagged".as[Id]) { id => - complete(id.toString) - } - } - - Get("/test_tagged?tagged=123456") ~> route ~> check { - responseAs[String] shouldBe "123456" - } - } - - test("Unmarshal tagged parameters from UUID") { - val route = - path("test_tagged") { - parameter("tagged".as[TestId]) { id => - complete(id.toString) - } - } - - Get("/test_tagged?tagged=ce7a7cf1-8c00-49a9-a963-9fd119dd0642") ~> route ~> check { - responseAs[String] shouldBe "ce7a7cf1-8c00-49a9-a963-9fd119dd0642" - } - } - - test("Unmarshal tagged URI") { - val route = - path("test_tagged") { - parameter("tagged".as[TestTaggedUri]) { id => - complete(id.toString) - } - } - - Get("/test_tagged?tagged=www.test.pl") ~> route ~> check { - responseAs[String] shouldBe "www.test.pl" - } - } -} diff --git a/pekko-http/src/test/scala-2/pl/iterators/kebs/pekkohttp/matchers/PekkoHttpMatchersTests.scala b/pekko-http/src/test/scala/pl/iterators/kebs/pekkohttp/matchers/PekkoHttpMatchersTests.scala similarity index 81% rename from pekko-http/src/test/scala-2/pl/iterators/kebs/pekkohttp/matchers/PekkoHttpMatchersTests.scala rename to pekko-http/src/test/scala/pl/iterators/kebs/pekkohttp/matchers/PekkoHttpMatchersTests.scala index 2e943ea3..e6859edf 100644 --- a/pekko-http/src/test/scala-2/pl/iterators/kebs/pekkohttp/matchers/PekkoHttpMatchersTests.scala +++ b/pekko-http/src/test/scala/pl/iterators/kebs/pekkohttp/matchers/PekkoHttpMatchersTests.scala @@ -5,11 +5,13 @@ 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.core.macros.CaseClass1ToValueClass import pl.iterators.kebs.instances.net.URIString 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 pl.iterators.kebs.pekkohttp.{KebsEnumForTests, KebsValueEnumForTests} + import java.net.URI import java.time.{DayOfWeek, Instant, ZonedDateTime} @@ -23,11 +25,11 @@ class PekkoHttpMatchersTests with DayOfWeekInt with InstantEpochMilliLong with URIString - with KebsEnumeratum { + with CaseClass1ToValueClass + with KebsEnumForTests + with KebsValueEnumForTests { 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[Instant, Long]]" shouldNot typeCheck @@ -90,7 +92,7 @@ class PekkoHttpMatchersTests } test("Extract String as Enum") { - val testRoute = path("test" / EnumSegment.as[Greeting]) { greeting => + val testRoute = path("test" / Segment.asEnum[Greeting]) { greeting => complete(greeting.toString) } Get("/test/hello") ~> testRoute ~> check { @@ -98,6 +100,24 @@ class PekkoHttpMatchersTests } } + test("Extract String as value class") { + val testRoute = path("test" / Segment.as[S]) { item => + complete(item.toString) + } + Get("/test/check") ~> testRoute ~> check { + responseAs[String] shouldEqual "S(check)" + } + } + + test("Extract Int as ValueEnum") { + val testRoute = path("test" / IntNumber.asValueEnum[LibraryItem]) { item => + complete(item.toString) + } + Get("/test/1") ~> testRoute ~> check { + responseAs[String] shouldEqual "Book" + } + } + test("Extract String to URI as tagged URI") { val testRoute = path("test" / Segment.to[URI].as[TestTaggedUri]) { id => complete(id.toString) diff --git a/pekko-http/src/test/scala-2/pl/iterators/kebs/pekkohttp/unmarshallers/PekkoHttpUnmarshallersTests.scala b/pekko-http/src/test/scala/pl/iterators/kebs/pekkohttp/unmarshallers/PekkoHttpUnmarshallersTests.scala similarity index 94% rename from pekko-http/src/test/scala-2/pl/iterators/kebs/pekkohttp/unmarshallers/PekkoHttpUnmarshallersTests.scala rename to pekko-http/src/test/scala/pl/iterators/kebs/pekkohttp/unmarshallers/PekkoHttpUnmarshallersTests.scala index 35842e42..ce7c747d 100644 --- a/pekko-http/src/test/scala-2/pl/iterators/kebs/pekkohttp/unmarshallers/PekkoHttpUnmarshallersTests.scala +++ b/pekko-http/src/test/scala/pl/iterators/kebs/pekkohttp/unmarshallers/PekkoHttpUnmarshallersTests.scala @@ -3,16 +3,16 @@ package pl.iterators.kebs.pekkohttp.unmarshallers 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.{FromStringUnmarshaller, Unmarshal, Unmarshaller} import org.scalatest.concurrent.ScalaFutures import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers +import pl.iterators.kebs.circe.{KebsEnumForTests, KebsValueEnumForTests} +import pl.iterators.kebs.core.enums.ValueEnumLike import pl.iterators.kebs.core.macros.CaseClass1ToValueClass import pl.iterators.kebs.instances.net.URIString import pl.iterators.kebs.instances.time.{DayOfWeekInt, YearMonthString} 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} @@ -22,13 +22,12 @@ class PekkoHttpUnmarshallersTests with ScalatestRouteTest with ScalaFutures with Directives - with KebsUnmarshallers - with KebsEnumUnmarshallers + with KebsPekkoHttpUnmarshallers with URIString with YearMonthString with DayOfWeekInt - with KebsEnumeratum - with KebsValueEnumeratum + with KebsEnumForTests + with KebsValueEnumForTests with CaseClass1ToValueClass { test("No ValueClassLike implicits derived") { @@ -126,7 +125,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/package.scala b/play-json/src/main/scala/pl/iterators/kebs/json/package.scala deleted file mode 100644 index 3db5124f..00000000 --- a/play-json/src/main/scala/pl/iterators/kebs/json/package.scala +++ /dev/null @@ -1,3 +0,0 @@ -package pl.iterators.kebs - -package object json extends KebsPlay diff --git a/play-json/src/main/scala/pl/iterators/kebs/json/KebsPlay.scala b/play-json/src/main/scala/pl/iterators/kebs/playjson/KebsPlayJson.scala similarity index 58% rename from play-json/src/main/scala/pl/iterators/kebs/json/KebsPlay.scala rename to play-json/src/main/scala/pl/iterators/kebs/playjson/KebsPlayJson.scala index 839ffc1a..dfdaba98 100644 --- a/play-json/src/main/scala/pl/iterators/kebs/json/KebsPlay.scala +++ b/play-json/src/main/scala/pl/iterators/kebs/playjson/KebsPlayJson.scala @@ -1,15 +1,23 @@ -package pl.iterators.kebs.json +package pl.iterators.kebs.playjson import pl.iterators.kebs.core.instances.InstanceConverter import pl.iterators.kebs.core.macros.ValueClassLike import play.api.libs.json._ -trait KebsPlay { - implicit def flatReads[T, A](implicit rep: ValueClassLike[T, A], reads: Reads[A]): Reads[T] = reads.map(rep.apply) +import scala.util.Try + +trait KebsPlayJson { + implicit def flatReads[T, A](implicit rep: ValueClassLike[T, A], reads: Reads[A]): Reads[T] = reads.flatMapResult { obj => + Try(rep.apply(obj)).toEither.left.map(e => JsError(e.getMessage)).map(JsSuccess(_)).merge + } 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) + implicit def instanceConverterReads[T, A](implicit rep: InstanceConverter[T, A], reads: Reads[A]): Reads[T] = reads.flatMapResult { + obj => + Try(rep.decode(obj)).toEither.left.map(e => JsError(e.getMessage)).map(JsSuccess(_)).merge + + } implicit def instanceConverterWrites[T, B](implicit rep: InstanceConverter[T, B], writes: Writes[B]): Writes[T] = Writes((obj: T) => writes.writes(rep.encode(obj))) } diff --git a/play-json/src/main/scala/pl/iterators/kebs/playjson/enums/KebsPlayJsonEnums.scala b/play-json/src/main/scala/pl/iterators/kebs/playjson/enums/KebsPlayJsonEnums.scala new file mode 100644 index 00000000..2e87d19e --- /dev/null +++ b/play-json/src/main/scala/pl/iterators/kebs/playjson/enums/KebsPlayJsonEnums.scala @@ -0,0 +1,71 @@ +package pl.iterators.kebs.playjson.enums + +import pl.iterators.kebs.core.enums.{EnumLike, ValueEnumLike, ValueEnumLikeEntry} +import play.api.libs.json.{JsError, JsSuccess, Reads, Writes} + +trait KebsPlayJsonEnums { + protected final def enumDecoder[E](`enum`: EnumLike[E], _comap: String => Option[E]): Reads[E] = + Reads.StringReads.flatMapResult { str => + _comap(str) match { + case Some(e) => JsSuccess(e) + case None => JsError(s"$str should be one of ${`enum`.getNamesToValuesMap.values.mkString(", ")}") + } + } + + protected final def enumEncoder[E](`enum`: EnumLike[E], _map: E => String): Writes[E] = + Writes.StringWrites.contramap(_map) + + def enumDecoder[E](`enum`: EnumLike[E]): Reads[E] = + enumDecoder[E](`enum`, `enum`.withNameInsensitiveOption(_)) + def enumEncoder[E](`enum`: EnumLike[E]): Writes[E] = + enumEncoder[E](`enum`, (e: E) => e.toString) + + def lowercaseEnumDecoder[E](`enum`: EnumLike[E]): Reads[E] = + enumDecoder[E](`enum`, `enum`.withNameLowercaseOnlyOption(_)) + def lowercaseEnumEncoder[E](`enum`: EnumLike[E]): Writes[E] = + enumEncoder[E](`enum`, (e: E) => e.toString.toLowerCase) + + def uppercaseEnumDecoder[E](`enum`: EnumLike[E]): Reads[E] = + enumDecoder[E](`enum`, `enum`.withNameUppercaseOnlyOption(_)) + def uppercaseEnumEncoder[E](`enum`: EnumLike[E]): Writes[E] = + enumEncoder[E](`enum`, (e: E) => e.toString.toUpperCase()) + + implicit def enumDecoderImpl[E](implicit ev: EnumLike[E]): Reads[E] = enumDecoder(ev) + + implicit def enumEncoderImpl[E](implicit ev: EnumLike[E]): Writes[E] = enumEncoder(ev) + + trait KebsCirceEnumsUppercase { + implicit def enumDecoderImpl[E](implicit ev: EnumLike[E]): Reads[E] = + uppercaseEnumDecoder(ev) + + implicit def enumEncoderImpl[E](implicit ev: EnumLike[E]): Writes[E] = + uppercaseEnumEncoder(ev) + } + + trait KebsCirceEnumsLowercase { + implicit def enumDecoderImpl[E](implicit ev: EnumLike[E]): Reads[E] = + lowercaseEnumDecoder(ev) + + implicit def enumEncoderImpl[E](implicit ev: EnumLike[E]): Writes[E] = + lowercaseEnumEncoder(ev) + } +} + +trait KebsPlayJsonValueEnums { + def valueEnumDecoder[V, E <: ValueEnumLikeEntry[V]](`enum`: ValueEnumLike[V, E])(implicit decoder: Reads[V]): Reads[E] = + Reads.of[V].flatMapResult { v => + `enum`.withValueOption(v) match { + case Some(e) => JsSuccess(e) + case None => JsError(s"$v is not a member of ${`enum`.getValuesToEntriesMap.keys.mkString(", ")}") + } + } + + def valueEnumEncoder[V, E <: ValueEnumLikeEntry[V]](`enum`: ValueEnumLike[V, E])(implicit encoder: Writes[V]): Writes[E] = + Writes.of[V].contramap(_.value) + + implicit def valueEnumDecoderImpl[V, E <: ValueEnumLikeEntry[V]](implicit ev: ValueEnumLike[V, E], decoder: Reads[V]): Reads[E] = + valueEnumDecoder(ev) + + implicit def valueEnumEncoderImpl[V, E <: ValueEnumLikeEntry[V]](implicit ev: ValueEnumLike[V, E], encoder: Writes[V]): Writes[E] = + valueEnumEncoder(ev) +} diff --git a/play-json/src/main/scala/pl/iterators/kebs/playjson/enums/enums.scala b/play-json/src/main/scala/pl/iterators/kebs/playjson/enums/enums.scala new file mode 100644 index 00000000..fb5b909e --- /dev/null +++ b/play-json/src/main/scala/pl/iterators/kebs/playjson/enums/enums.scala @@ -0,0 +1,6 @@ +package pl.iterators.kebs.playjson + +package object enums extends KebsPlayJsonEnums with KebsPlayJsonValueEnums { + object uppercase extends KebsCirceEnumsUppercase with KebsPlayJsonValueEnums + object lowercase extends KebsCirceEnumsLowercase with KebsPlayJsonValueEnums +} diff --git a/play-json/src/main/scala/pl/iterators/kebs/playjson/package.scala b/play-json/src/main/scala/pl/iterators/kebs/playjson/package.scala new file mode 100644 index 00000000..03b6a052 --- /dev/null +++ b/play-json/src/main/scala/pl/iterators/kebs/playjson/package.scala @@ -0,0 +1,3 @@ +package pl.iterators.kebs + +package object playjson extends KebsPlayJson diff --git a/play-json/src/test/scala/pl/iterators/kebs/json/PlayJsonFormatTests.scala b/play-json/src/test/scala/pl/iterators/kebs/json/PlayJsonFormatTests.scala deleted file mode 100644 index 069336f1..00000000 --- a/play-json/src/test/scala/pl/iterators/kebs/json/PlayJsonFormatTests.scala +++ /dev/null @@ -1,52 +0,0 @@ -package pl.iterators.kebs.json - -import org.scalatest.funsuite.AnyFunSuite -import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.core.macros.CaseClass1ToValueClass -import play.api.libs.json._ - -import java.util.UUID - -class PlayJsonFormatTests extends AnyFunSuite with Matchers with CaseClass1ToValueClass { - - case class C(i: Int) - case class D(s: String) - case class E(noFormat: UUID) - - case class Parametrized[T](field: T) - - case class DTO(c: C, d: D) - - test("Flat format") { - val jf = implicitly[Format[C]] - jf.writes(C(10)) shouldBe JsNumber(10) - jf.reads(JsNumber(10)) shouldBe JsSuccess(C(10)) - } - - test("Flat format - no implicit JsonFormat") { - "implicitly[JsonFormat[E]]" shouldNot typeCheck - } - - test("Flat format - parametrized") { - val jf = implicitly[Format[Parametrized[Double]]] - jf.writes(Parametrized(15.0)) shouldBe JsNumber(15.0) - jf.reads(JsNumber(15.0)) shouldBe JsSuccess(Parametrized(15.0)) - } - - test("Reads only") { - val jf = implicitly[Reads[C]] - jf.reads(JsNumber(10)) shouldBe JsSuccess(C(10)) - } - - test("Writes only") { - val jf = implicitly[Writes[C]] - jf.writes(C(10)) shouldBe JsNumber(10) - } - - test("with Json.format") { - val jf = Json.format[DTO] - jf.writes(DTO(C(50), D("a"))) shouldBe Json.obj("c" -> JsNumber(50), "d" -> JsString("a")) - jf.reads(Json.obj("c" -> JsNumber(50), "d" -> JsString("a"))) shouldBe JsSuccess(DTO(C(50), D("a"))) - } - -} diff --git a/play-json/src/test/scala/pl/iterators/kebs/playjson/PlayJsonFormatTests.scala b/play-json/src/test/scala/pl/iterators/kebs/playjson/PlayJsonFormatTests.scala new file mode 100644 index 00000000..7f0a439b --- /dev/null +++ b/play-json/src/test/scala/pl/iterators/kebs/playjson/PlayJsonFormatTests.scala @@ -0,0 +1,131 @@ +package pl.iterators.kebs.playjson + +import enumeratum.{Enum, EnumEntry} +import enumeratum.values.{LongEnum, LongEnumEntry} +import org.scalatest.funsuite.AnyFunSuite +import org.scalatest.matchers.should.Matchers +import pl.iterators.kebs.core.enums.ValueEnumLikeEntry +import pl.iterators.kebs.core.macros.CaseClass1ToValueClass +import pl.iterators.kebs.enumeratum.{KebsEnumeratum, KebsValueEnumeratum} +import play.api.libs.json._ + +import java.util.UUID + +class PlayJsonFormatTests extends AnyFunSuite with Matchers with CaseClass1ToValueClass with KebsEnumeratum with KebsValueEnumeratum { + + case class C(i: Int) + case class D(s: String) + case class E(noFormat: UUID) + + case class Parametrized[T](field: T) + + case class DTO(c: C, d: D) + + 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 + } + + test("Flat format") { + val jf = implicitly[Format[C]] + jf.writes(C(10)) shouldBe JsNumber(10) + jf.reads(JsNumber(10)) shouldBe JsSuccess(C(10)) + } + + test("Flat format - no implicit JsonFormat") { + "implicitly[JsonFormat[E]]" shouldNot typeCheck + } + + test("Flat format - parametrized") { + val jf = implicitly[Format[Parametrized[Double]]] + jf.writes(Parametrized(15.0)) shouldBe JsNumber(15.0) + jf.reads(JsNumber(15.0)) shouldBe JsSuccess(Parametrized(15.0)) + } + + test("Reads only") { + val jf = implicitly[Reads[C]] + jf.reads(JsNumber(10)) shouldBe JsSuccess(C(10)) + } + + test("Writes only") { + val jf = implicitly[Writes[C]] + jf.writes(C(10)) shouldBe JsNumber(10) + } + + test("with Json.format") { + val jf = Json.format[DTO] + jf.writes(DTO(C(50), D("a"))) shouldBe Json.obj("c" -> JsNumber(50), "d" -> JsString("a")) + jf.reads(Json.obj("c" -> JsNumber(50), "d" -> JsString("a"))) shouldBe JsSuccess(DTO(C(50), D("a"))) + } + + import enums._ + + test("enum JsonFormat") { + import Greeting._ + val decoder = implicitly[Reads[Greeting]] + val encoder = implicitly[Writes[Greeting]] + decoder.reads(JsString("hElLo")) shouldBe JsSuccess(Hello) + decoder.reads(JsString("goodbye")) shouldBe JsSuccess(GoodBye) + encoder.writes(Hello) shouldBe JsString("Hello") + encoder.writes(GoodBye) shouldBe JsString("GoodBye") + } + + test("enum name deserialization error") { + val decoder = implicitly[Reads[Greeting]] + decoder.reads(JsNumber(BigDecimal(1))) shouldBe a[JsError] + } + + test("enum JsonFormat - lowercase") { + import Greeting._ + val decoder = implicitly[Reads[Greeting]] + val encoder = implicitly[Writes[Greeting]] + decoder.reads(JsString("hello")) shouldBe JsSuccess(Hello) + decoder.reads(JsString("goodbye")) shouldBe JsSuccess(GoodBye) + encoder.writes(Hello) shouldBe JsString("Hello") + encoder.writes(GoodBye) shouldBe JsString("GoodBye") + } + + test("enum JsonFormat - uppercase") { + import Greeting._ + val decoder = implicitly[Reads[Greeting]] + val encoder = implicitly[Writes[Greeting]] + decoder.reads(JsString("HELLO")) shouldBe JsSuccess(Hello) + decoder.reads(JsString("GOODBYE")) shouldBe JsSuccess(GoodBye) + encoder.writes(Hello) shouldBe JsString("Hello") + encoder.writes(GoodBye) shouldBe JsString("GoodBye") + } + + test("value enum JsonFormat") { + import LongGreeting._ + val decoder = implicitly[Reads[LongGreeting]] + val encoder = implicitly[Writes[LongGreeting]] + decoder.reads(JsNumber(0)) shouldBe JsSuccess(Hello) + decoder.reads(JsNumber(1)) shouldBe JsSuccess(GoodBye) + encoder.writes(Hello) shouldBe JsNumber(0) + encoder.writes(GoodBye) shouldBe JsNumber(1) + } + + test("value enum deserialization error") { + import LongGreeting._ + val decoder = implicitly[Reads[LongGreeting]] + decoder.reads(JsNumber(4)) shouldBe a[JsError] + } + +} diff --git a/play-json/src/test/scala/pl/iterators/kebs/json/instances/NetInstancesTests.scala b/play-json/src/test/scala/pl/iterators/kebs/playjson/instances/NetInstancesTests.scala similarity index 81% rename from play-json/src/test/scala/pl/iterators/kebs/json/instances/NetInstancesTests.scala rename to play-json/src/test/scala/pl/iterators/kebs/playjson/instances/NetInstancesTests.scala index 98bb324d..dc1d7bc3 100644 --- a/play-json/src/test/scala/pl/iterators/kebs/json/instances/NetInstancesTests.scala +++ b/play-json/src/test/scala/pl/iterators/kebs/playjson/instances/NetInstancesTests.scala @@ -1,15 +1,15 @@ -package pl.iterators.kebs.json.instances +package pl.iterators.kebs.playjson.instances import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers import pl.iterators.kebs.core.instances.InstanceConverter.DecodeErrorException import pl.iterators.kebs.instances.net.URIString -import play.api.libs.json.{Format, JsString, JsSuccess} +import play.api.libs.json.{Format, JsError, JsString, JsSuccess} import java.net.URI class NetInstancesTests extends AnyFunSuite with Matchers with URIString { - import pl.iterators.kebs.json._ + import pl.iterators.kebs.playjson._ test("URI standard format") { val jf = implicitly[Format[URI]] @@ -24,7 +24,7 @@ class NetInstancesTests extends AnyFunSuite with Matchers with URIString { val jf = implicitly[Format[URI]] val value = "not a URI" - assertThrows[DecodeErrorException](jf.reads(JsString(value))) + jf.reads(JsString(value)) shouldBe a[JsError] } test("No ValueClassLike implicits derived") { diff --git a/play-json/src/test/scala/pl/iterators/kebs/json/instances/TimeInstancesMixinTests.scala b/play-json/src/test/scala/pl/iterators/kebs/playjson/instances/TimeInstancesMixinTests.scala similarity index 98% rename from play-json/src/test/scala/pl/iterators/kebs/json/instances/TimeInstancesMixinTests.scala rename to play-json/src/test/scala/pl/iterators/kebs/playjson/instances/TimeInstancesMixinTests.scala index de858c2e..98a87de9 100644 --- a/play-json/src/test/scala/pl/iterators/kebs/json/instances/TimeInstancesMixinTests.scala +++ b/play-json/src/test/scala/pl/iterators/kebs/playjson/instances/TimeInstancesMixinTests.scala @@ -1,4 +1,4 @@ -package pl.iterators.kebs.json.instances +package pl.iterators.kebs.playjson.instances import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers @@ -12,7 +12,7 @@ import java.time._ import java.time.format.DateTimeFormatter class TimeInstancesMixinTests extends AnyFunSuite with Matchers { - import pl.iterators.kebs.json._ + import pl.iterators.kebs.playjson._ test("Instant epoch milli format") { object TimeInstancesProtocol extends InstantEpochMilliLong diff --git a/play-json/src/test/scala/pl/iterators/kebs/json/instances/TimeInstancesTests.scala b/play-json/src/test/scala/pl/iterators/kebs/playjson/instances/TimeInstancesTests.scala similarity index 87% rename from play-json/src/test/scala/pl/iterators/kebs/json/instances/TimeInstancesTests.scala rename to play-json/src/test/scala/pl/iterators/kebs/playjson/instances/TimeInstancesTests.scala index 56257847..77e2eafa 100644 --- a/play-json/src/test/scala/pl/iterators/kebs/json/instances/TimeInstancesTests.scala +++ b/play-json/src/test/scala/pl/iterators/kebs/playjson/instances/TimeInstancesTests.scala @@ -1,15 +1,15 @@ -package pl.iterators.kebs.json.instances +package pl.iterators.kebs.playjson.instances import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers import pl.iterators.kebs.core.instances.InstanceConverter.DecodeErrorException import pl.iterators.kebs.instances.TimeInstances -import play.api.libs.json.{Format, JsNumber, JsString, JsSuccess} +import play.api.libs.json.{Format, JsError, JsNumber, JsString, JsSuccess} import java.time._ class TimeInstancesTests extends AnyFunSuite with Matchers with TimeInstances { - import pl.iterators.kebs.json._ + import pl.iterators.kebs.playjson._ test("No ValueClassLike implicits derived") { "implicitly[ValueClassLike[DayOfWeek, Int]]" shouldNot typeCheck @@ -59,7 +59,7 @@ class TimeInstancesTests extends AnyFunSuite with Matchers with TimeInstances { val jf = implicitly[Format[DayOfWeek]] val value = 8 - assertThrows[DecodeErrorException](jf.reads(JsNumber(value))) + jf.reads(JsNumber(value)) shouldBe a[JsError] } test("Duration standard format") { @@ -75,7 +75,7 @@ class TimeInstancesTests extends AnyFunSuite with Matchers with TimeInstances { val jf = implicitly[Format[Duration]] val value = "NotADuration" - assertThrows[DecodeErrorException](jf.reads(JsString(value))) + jf.reads(JsString(value)) shouldBe a[JsError] } test("Instant standard format") { @@ -91,7 +91,7 @@ class TimeInstancesTests extends AnyFunSuite with Matchers with TimeInstances { val jf = implicitly[Format[Instant]] val value = "NotAnInstant" - assertThrows[DecodeErrorException](jf.reads(JsString(value))) + jf.reads(JsString(value)) shouldBe a[JsError] } test("LocalDate standard format") { @@ -107,7 +107,7 @@ class TimeInstancesTests extends AnyFunSuite with Matchers with TimeInstances { val jf = implicitly[Format[LocalDate]] val value = "NotALocalDate" - assertThrows[DecodeErrorException](jf.reads(JsString(value))) + jf.reads(JsString(value)) shouldBe a[JsError] } test("LocalDateTime standard format") { @@ -123,7 +123,7 @@ class TimeInstancesTests extends AnyFunSuite with Matchers with TimeInstances { val jf = implicitly[Format[LocalDateTime]] val value = "NotALocalDateTime" - assertThrows[DecodeErrorException](jf.reads(JsString(value))) + jf.reads(JsString(value)) shouldBe a[JsError] } test("LocalTime standard format") { @@ -139,7 +139,7 @@ class TimeInstancesTests extends AnyFunSuite with Matchers with TimeInstances { val jf = implicitly[Format[LocalTime]] val value = "NotALocalTime" - assertThrows[DecodeErrorException](jf.reads(JsString(value))) + jf.reads(JsString(value)) shouldBe a[JsError] } test("Month standard format") { @@ -155,7 +155,7 @@ class TimeInstancesTests extends AnyFunSuite with Matchers with TimeInstances { val jf = implicitly[Format[Month]] val value = 13 - assertThrows[DecodeErrorException](jf.reads(JsNumber(value))) + jf.reads(JsNumber(value)) shouldBe a[JsError] } test("MonthDay standard format") { @@ -171,7 +171,7 @@ class TimeInstancesTests extends AnyFunSuite with Matchers with TimeInstances { val jf = implicitly[Format[MonthDay]] val value = "NotAMonthDay" - assertThrows[DecodeErrorException](jf.reads(JsString(value))) + jf.reads(JsString(value)) shouldBe a[JsError] } test("OffsetDateTime standard format") { @@ -187,7 +187,7 @@ class TimeInstancesTests extends AnyFunSuite with Matchers with TimeInstances { val jf = implicitly[Format[OffsetDateTime]] val value = "NotAnOffsetDateTime" - assertThrows[DecodeErrorException](jf.reads(JsString(value))) + jf.reads(JsString(value)) shouldBe a[JsError] } test("OffsetTime standard format") { @@ -203,7 +203,7 @@ class TimeInstancesTests extends AnyFunSuite with Matchers with TimeInstances { val jf = implicitly[Format[OffsetTime]] val value = "NotAnOffsetTime" - assertThrows[DecodeErrorException](jf.reads(JsString(value))) + jf.reads(JsString(value)) shouldBe a[JsError] } test("Period standard format") { @@ -219,7 +219,7 @@ class TimeInstancesTests extends AnyFunSuite with Matchers with TimeInstances { val jf = implicitly[Format[Period]] val value = "NotAPeriod" - assertThrows[DecodeErrorException](jf.reads(JsString(value))) + jf.reads(JsString(value)) shouldBe a[JsError] } test("Year standard format") { @@ -235,7 +235,7 @@ class TimeInstancesTests extends AnyFunSuite with Matchers with TimeInstances { val jf = implicitly[Format[Year]] val value = Int.MinValue - assertThrows[DecodeErrorException](jf.reads(JsNumber(value))) + jf.reads(JsNumber(value)) shouldBe a[JsError] } test("YearMonth standard format") { @@ -251,7 +251,7 @@ class TimeInstancesTests extends AnyFunSuite with Matchers with TimeInstances { val jf = implicitly[Format[YearMonth]] val value = "NotAYearMonth" - assertThrows[DecodeErrorException](jf.reads(JsString(value))) + jf.reads(JsString(value)) shouldBe a[JsError] } test("ZoneId standard format") { @@ -267,7 +267,7 @@ class TimeInstancesTests extends AnyFunSuite with Matchers with TimeInstances { val jf = implicitly[Format[ZoneId]] val value = "NotAZoneId" - assertThrows[DecodeErrorException](jf.reads(JsString(value))) + jf.reads(JsString(value)) shouldBe a[JsError] } test("ZoneOffset standard format") { @@ -283,7 +283,7 @@ class TimeInstancesTests extends AnyFunSuite with Matchers with TimeInstances { val jf = implicitly[Format[ZoneOffset]] val value = "NotAZoneOffset" - assertThrows[DecodeErrorException](jf.reads(JsString(value))) + jf.reads(JsString(value)) shouldBe a[JsError] } test("ZonedDateTime standard format") { @@ -299,7 +299,7 @@ class TimeInstancesTests extends AnyFunSuite with Matchers with TimeInstances { val jf = implicitly[Format[ZonedDateTime]] val value = "NotAZoneOffset" - assertThrows[DecodeErrorException](jf.reads(JsString(value))) + jf.reads(JsString(value)) shouldBe a[JsError] } } diff --git a/play-json/src/test/scala/pl/iterators/kebs/json/instances/UtilInstancesTests.scala b/play-json/src/test/scala/pl/iterators/kebs/playjson/instances/UtilInstancesTests.scala similarity index 87% rename from play-json/src/test/scala/pl/iterators/kebs/json/instances/UtilInstancesTests.scala rename to play-json/src/test/scala/pl/iterators/kebs/playjson/instances/UtilInstancesTests.scala index 9f9902e3..f7b07cd3 100644 --- a/play-json/src/test/scala/pl/iterators/kebs/json/instances/UtilInstancesTests.scala +++ b/play-json/src/test/scala/pl/iterators/kebs/playjson/instances/UtilInstancesTests.scala @@ -1,15 +1,15 @@ -package pl.iterators.kebs.json.instances +package pl.iterators.kebs.playjson.instances import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers import pl.iterators.kebs.core.instances.InstanceConverter.DecodeErrorException import pl.iterators.kebs.instances.UtilInstances -import play.api.libs.json.{Format, JsString, JsSuccess} +import play.api.libs.json.{Format, JsError, JsString, JsSuccess} import java.util.{Currency, Locale, UUID} class UtilInstancesTests extends AnyFunSuite with Matchers with UtilInstances { - import pl.iterators.kebs.json._ + import pl.iterators.kebs.playjson._ test("No ValueClassLike implicits derived") { "implicitly[ValueClassLike[Currency, String]]" shouldNot typeCheck @@ -33,7 +33,7 @@ class UtilInstancesTests extends AnyFunSuite with Matchers with UtilInstances { val jf = implicitly[Format[Currency]] val value = "not a Currency" - assertThrows[DecodeErrorException](jf.reads(JsString(value))) + jf.reads(JsString(value)) shouldBe a[JsError] } test("Locale standard format") { @@ -58,6 +58,6 @@ class UtilInstancesTests extends AnyFunSuite with Matchers with UtilInstances { val jf = implicitly[Format[UUID]] val value = "not an UUID" - assertThrows[DecodeErrorException](jf.reads(JsString(value))) + jf.reads(JsString(value)) shouldBe a[JsError] } } 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 deleted file mode 100644 index 3ecfd11d..00000000 --- a/scalacheck/src/main/scala-3/pl/iterators/kebs/scalacheck/CommonArbitrarySupport.scala +++ /dev/null @@ -1,14 +0,0 @@ -package pl.iterators.kebs.scalacheck - -import org.scalacheck.Arbitrary -import pl.iterators.kebs.core.macros.ValueClassLike - -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/Generators.scala b/scalacheck/src/main/scala-3/pl/iterators/kebs/scalacheck/Generators.scala deleted file mode 100644 index 79f66bb8..00000000 --- a/scalacheck/src/main/scala-3/pl/iterators/kebs/scalacheck/Generators.scala +++ /dev/null @@ -1,18 +0,0 @@ -package pl.iterators.kebs.scalacheck - -import org.scalacheck.Arbitrary - -trait Generator[T] extends CommonArbitrarySupport { - def ArbT: Arbitrary[T] - - def generate: T = ArbT.arbitrary.sample.get -} - -trait AllGenerators[T] { - - val normal: Generator[T] - - val minimal: Generator[T] - - val maximal: Generator[T] -} diff --git a/scalacheck/src/main/scala-3/pl/iterators/kebs/scalacheck/ScalacheckInstancesSupport.scala b/scalacheck/src/main/scala-3/pl/iterators/kebs/scalacheck/ScalacheckInstancesSupport.scala new file mode 100644 index 00000000..c5ffac60 --- /dev/null +++ b/scalacheck/src/main/scala-3/pl/iterators/kebs/scalacheck/ScalacheckInstancesSupport.scala @@ -0,0 +1,3 @@ +package pl.iterators.kebs.scalacheck + +trait ScalacheckInstancesSupport diff --git a/scalacheck/src/main/scala-2/pl/iterators/kebs/scalacheck/CommonArbitrarySupport.scala b/scalacheck/src/main/scala/pl/iterators/kebs/scalacheck/CommonArbitrarySupport.scala similarity index 71% rename from scalacheck/src/main/scala-2/pl/iterators/kebs/scalacheck/CommonArbitrarySupport.scala rename to scalacheck/src/main/scala/pl/iterators/kebs/scalacheck/CommonArbitrarySupport.scala index db04f491..bc752d5b 100644 --- a/scalacheck/src/main/scala-2/pl/iterators/kebs/scalacheck/CommonArbitrarySupport.scala +++ b/scalacheck/src/main/scala/pl/iterators/kebs/scalacheck/CommonArbitrarySupport.scala @@ -1,10 +1,9 @@ package pl.iterators.kebs.scalacheck -import enumeratum.ScalacheckInstances import org.scalacheck.Arbitrary import pl.iterators.kebs.core.macros.ValueClassLike -trait CommonArbitrarySupport extends ScalacheckInstances with ScalacheckInstancesSupport { +trait CommonArbitrarySupport extends ScalacheckInstancesSupport { implicit def valueClassLikeArbitraryPredef[T, A](implicit rep: ValueClassLike[T, A], arbitrary: Arbitrary[A] diff --git a/scalacheck/src/main/scala-2/pl/iterators/kebs/scalacheck/Generators.scala b/scalacheck/src/main/scala/pl/iterators/kebs/scalacheck/Generators.scala similarity index 100% rename from scalacheck/src/main/scala-2/pl/iterators/kebs/scalacheck/Generators.scala rename to scalacheck/src/main/scala/pl/iterators/kebs/scalacheck/Generators.scala 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 index e4b57853..2f995794 100644 --- 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 @@ -1,12 +1,40 @@ package pl.iterators.kebs.scalacheck +import enumeratum.{Enum, EnumEntry} +import enumeratum.values.{LongEnum, LongEnumEntry} +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 WrappedInt(int: Int) case class WrappedIntAnyVal(int: Int) extends AnyVal case class BasicSample( someNumber: Int, someText: String, wrappedNumber: WrappedInt, - wrappedNumberAnyVal: WrappedIntAnyVal + wrappedNumberAnyVal: WrappedIntAnyVal, + greeting: Greeting, + longGreeting: LongGreeting ) } 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 index 3f397aca..2e754731 100644 --- 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 @@ -1,10 +1,22 @@ package pl.iterators.kebs.scalacheck import pl.iterators.kebs.opaque.Opaque +import pl.iterators.kebs.core.enums.ValueEnumLikeEntry package object model { case class WrappedInt(int: Int) + 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) + } + opaque type OpaqueInt = Int object OpaqueInt extends Opaque[OpaqueInt, Int] { override def apply(value: Int) = value @@ -14,7 +26,9 @@ package object model { someNumber: Int, someText: String, wrappedNumber: WrappedInt, - opaqueInt: OpaqueInt + opaqueInt: OpaqueInt, + greeting: Greeting, + longGreeting: LongGreeting ) } diff --git a/slick/src/main/scala/pl/iterators/kebs/slick/KebsSlickSupport.scala b/slick/src/main/scala/pl/iterators/kebs/slick/KebsSlickSupport.scala index 42adac68..11dfb9f1 100644 --- a/slick/src/main/scala/pl/iterators/kebs/slick/KebsSlickSupport.scala +++ b/slick/src/main/scala/pl/iterators/kebs/slick/KebsSlickSupport.scala @@ -11,7 +11,7 @@ import slick.lifted._ import scala.annotation.unused import scala.reflect.ClassTag -trait KebsColumnExtensionMethods { +private[kebs] 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 diff --git a/spray-json/src/main/scala/pl/iterators/kebs/json/KebsSpray.scala b/spray-json/src/main/scala/pl/iterators/kebs/sprayjson/KebsSprayJson.scala similarity index 82% rename from spray-json/src/main/scala/pl/iterators/kebs/json/KebsSpray.scala rename to spray-json/src/main/scala/pl/iterators/kebs/sprayjson/KebsSprayJson.scala index cc5c28f0..fb5c3b5e 100644 --- a/spray-json/src/main/scala/pl/iterators/kebs/json/KebsSpray.scala +++ b/spray-json/src/main/scala/pl/iterators/kebs/sprayjson/KebsSprayJson.scala @@ -1,10 +1,10 @@ -package pl.iterators.kebs.json +package pl.iterators.kebs.sprayjson import pl.iterators.kebs.core.instances.InstanceConverter import pl.iterators.kebs.core.macros.ValueClassLike import spray.json.{DefaultJsonProtocol, JsValue, JsonFormat, JsonReader, RootJsonFormat} -trait KebsSpray { self: DefaultJsonProtocol => +trait KebsSprayJson { self: DefaultJsonProtocol => import macros.KebsSprayMacros implicit def jsonFormatN[T <: Product]: RootJsonFormat[T] = macro KebsSprayMacros.materializeRootFormat[T] implicit def jsonFlatFormat[T, A](implicit rep: ValueClassLike[T, A], baseJsonFormat: JsonFormat[A]): JsonFormat[T] = { @@ -22,18 +22,16 @@ trait KebsSpray { self: DefaultJsonProtocol => @inline def _kebs_getField[T](value: JsValue, fieldName: String)(implicit reader: JsonReader[T]) = fromField[T](value, fieldName) -} -object KebsSpray { - trait Snakified extends KebsSpray { self: DefaultJsonProtocol => + trait KebsSprayJsonSnakified extends KebsSprayJson { self: DefaultJsonProtocol => import macros.KebsSprayMacros implicit def snakifiedJsonFormatN[T <: Product]: RootJsonFormat[T] = macro KebsSprayMacros.SnakifyVariant.materializeRootFormat[T] } - trait Capitalized extends KebsSpray { self: DefaultJsonProtocol => + trait KebsSprayJsonCapitalized extends KebsSprayJson { self: DefaultJsonProtocol => import macros.KebsSprayMacros // format: off implicit def capitalizedJsonFormatN[T <: Product]: RootJsonFormat[T] = - macro KebsSprayMacros.CapitalizedCamelCase.materializeRootFormat[T] + macro KebsSprayMacros.CapitalizedCamelCase.materializeRootFormat[T] // format: on } } diff --git a/spray-json/src/main/scala/pl/iterators/kebs/json/KebsEnumFormats.scala b/spray-json/src/main/scala/pl/iterators/kebs/sprayjson/enums/KebsSprayJsonEnums.scala similarity index 90% rename from spray-json/src/main/scala/pl/iterators/kebs/json/KebsEnumFormats.scala rename to spray-json/src/main/scala/pl/iterators/kebs/sprayjson/enums/KebsSprayJsonEnums.scala index 66c57fea..81b57e0d 100644 --- a/spray-json/src/main/scala/pl/iterators/kebs/json/KebsEnumFormats.scala +++ b/spray-json/src/main/scala/pl/iterators/kebs/sprayjson/enums/KebsSprayJsonEnums.scala @@ -1,9 +1,9 @@ -package pl.iterators.kebs.json +package pl.iterators.kebs.sprayjson import pl.iterators.kebs.core.enums.{EnumLike, ValueEnumLike, ValueEnumLikeEntry} import spray.json.{JsString, JsValue, JsonFormat} -trait SprayJsonEnum { +trait KebsSprayJsonEnums { @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") @@ -26,9 +26,19 @@ trait SprayJsonEnum { enumJsonFormat[E](`enum`, _.toString.toLowerCase, `enum`.withNameLowercaseOnlyOption(_)) def uppercaseJsonFormat[E](`enum`: EnumLike[E]) = enumJsonFormat[E](`enum`, _.toString.toUpperCase, `enum`.withNameUppercaseOnlyOption(_)) + + implicit def jsonEnumFormat[E](implicit ev: EnumLike[E]): JsonFormat[E] = jsonFormat(ev) + + trait KebsSprayJsonEnumsUppercase { + implicit def jsonEnumFormat[E](implicit ev: EnumLike[E]): JsonFormat[E] = uppercaseJsonFormat(ev) + } + + trait KebsSprayJsonEnumsLowercase { + implicit def jsonEnumFormat[E](implicit ev: EnumLike[E]): JsonFormat[E] = lowercaseJsonFormat(ev) + } } -trait SprayJsonValueEnum { +trait KebsSprayJsonValueEnums { @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") @@ -42,22 +52,9 @@ trait SprayJsonValueEnum { `enum`.withValueOption(value).getOrElse(valueEnumDeserializationError(`enum`, value)) } } -} -trait KebsEnumFormats extends SprayJsonEnum with SprayJsonValueEnum { - 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](implicit ev: EnumLike[E]): JsonFormat[E] = uppercaseJsonFormat(ev) - } - - trait Lowercase extends SprayJsonEnum { - implicit def jsonEnumFormat[E](implicit ev: EnumLike[E]): JsonFormat[E] = lowercaseJsonFormat(ev) - } } - -object KebsEnumFormats extends KebsEnumFormats diff --git a/spray-json/src/main/scala/pl/iterators/kebs/sprayjson/enums/package.scala b/spray-json/src/main/scala/pl/iterators/kebs/sprayjson/enums/package.scala new file mode 100644 index 00000000..85798644 --- /dev/null +++ b/spray-json/src/main/scala/pl/iterators/kebs/sprayjson/enums/package.scala @@ -0,0 +1,6 @@ +package pl.iterators.kebs.sprayjson + +package object enums extends KebsSprayJsonEnums with KebsSprayJsonValueEnums { + object uppercase extends KebsSprayJsonEnumsUppercase with KebsSprayJsonValueEnums + object lowercase extends KebsSprayJsonEnumsLowercase with KebsSprayJsonValueEnums +} diff --git a/spray-json-macros/src/main/scala/pl/iterators/kebs/json/macros/KebsSprayMacros.scala b/spray-json/src/main/scala/pl/iterators/kebs/sprayjson/macros/KebsSprayMacros.scala similarity index 99% rename from spray-json-macros/src/main/scala/pl/iterators/kebs/json/macros/KebsSprayMacros.scala rename to spray-json/src/main/scala/pl/iterators/kebs/sprayjson/macros/KebsSprayMacros.scala index 2310bff1..00803e02 100644 --- a/spray-json-macros/src/main/scala/pl/iterators/kebs/json/macros/KebsSprayMacros.scala +++ b/spray-json/src/main/scala/pl/iterators/kebs/sprayjson/macros/KebsSprayMacros.scala @@ -1,4 +1,4 @@ -package pl.iterators.kebs.json.macros +package pl.iterators.kebs.sprayjson.macros import pl.iterators.kebs.core.macros.MacroUtils import spray.json.{JsonFormat, JsonReader, JsonWriter, NullOptions, RootJsonFormat} diff --git a/spray-json/src/main/scala/pl/iterators/kebs/sprayjson/package.scala b/spray-json/src/main/scala/pl/iterators/kebs/sprayjson/package.scala new file mode 100644 index 00000000..6286dcaa --- /dev/null +++ b/spray-json/src/main/scala/pl/iterators/kebs/sprayjson/package.scala @@ -0,0 +1,8 @@ +package pl.iterators.kebs + +import spray.json.DefaultJsonProtocol + +package object sprayjson extends KebsSprayJson with DefaultJsonProtocol { + object snakified extends KebsSprayJsonSnakified with DefaultJsonProtocol + object capitalized extends KebsSprayJsonCapitalized with DefaultJsonProtocol +} diff --git a/spray-json/src/test/scala/pl/iterators/kebs/json/SprayEnumJsonFormatTests.scala b/spray-json/src/test/scala/pl/iterators/kebs/sprayjson/formats/SprayEnumJsonFormatTests.scala similarity index 86% rename from spray-json/src/test/scala/pl/iterators/kebs/json/SprayEnumJsonFormatTests.scala rename to spray-json/src/test/scala/pl/iterators/kebs/sprayjson/formats/SprayEnumJsonFormatTests.scala index a69bede3..68a1463a 100644 --- a/spray-json/src/test/scala/pl/iterators/kebs/json/SprayEnumJsonFormatTests.scala +++ b/spray-json/src/test/scala/pl/iterators/kebs/sprayjson/formats/SprayEnumJsonFormatTests.scala @@ -1,11 +1,12 @@ -package pl.iterators.kebs.json +package pl.iterators.kebs.sprayjson.formats import enumeratum.{Enum, EnumEntry} -import pl.iterators.kebs.json.{KebsEnumFormats, KebsSpray} +import pl.iterators.kebs.sprayjson.{KebsSprayJson, KebsSprayJsonEnums} import spray.json._ import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers import pl.iterators.kebs.enumeratum.KebsEnumeratum +import pl.iterators.kebs.sprayjson.enums.{KebsSprayJsonEnumsLowercase, KebsSprayJsonEnumsUppercase} class SprayEnumJsonFormatTests extends AnyFunSuite with Matchers with KebsEnumeratum { sealed trait Greeting extends EnumEntry @@ -21,9 +22,9 @@ class SprayEnumJsonFormatTests extends AnyFunSuite with Matchers with KebsEnumer import Greeting._ - object KebsProtocol extends DefaultJsonProtocol with KebsSpray with KebsEnumFormats - object KebsProtocolUppercase extends DefaultJsonProtocol with KebsSpray with KebsEnumFormats.Uppercase - object KebsProtocolLowercase extends DefaultJsonProtocol with KebsSpray with KebsEnumFormats.Lowercase + object KebsProtocol extends DefaultJsonProtocol with KebsSprayJson with KebsSprayJsonEnums + object KebsProtocolUppercase extends DefaultJsonProtocol with KebsSprayJson with KebsSprayJsonEnumsUppercase + object KebsProtocolLowercase extends DefaultJsonProtocol with KebsSprayJson with KebsSprayJsonEnumsLowercase test("enum JsonFormat") { import KebsProtocol._ diff --git a/spray-json/src/test/scala/pl/iterators/kebs/json/SprayJsonFormatCapitalizedVariantTests.scala b/spray-json/src/test/scala/pl/iterators/kebs/sprayjson/formats/SprayJsonFormatCapitalizedVariantTests.scala similarity index 93% rename from spray-json/src/test/scala/pl/iterators/kebs/json/SprayJsonFormatCapitalizedVariantTests.scala rename to spray-json/src/test/scala/pl/iterators/kebs/sprayjson/formats/SprayJsonFormatCapitalizedVariantTests.scala index fea7313d..b947010f 100644 --- a/spray-json/src/test/scala/pl/iterators/kebs/json/SprayJsonFormatCapitalizedVariantTests.scala +++ b/spray-json/src/test/scala/pl/iterators/kebs/sprayjson/formats/SprayJsonFormatCapitalizedVariantTests.scala @@ -1,12 +1,13 @@ -package pl.iterators.kebs.json +package pl.iterators.kebs.sprayjson.formats import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers import pl.iterators.kebs.core.macros.CaseClass1ToValueClass +import pl.iterators.kebs.sprayjson.KebsSprayJsonCapitalized import spray.json.{DefaultJsonProtocol, JsNumber, JsObject, JsString, JsonFormat, RootJsonFormat} class SprayJsonFormatCapitalizedVariantTests extends AnyFunSuite with Matchers { - object KebsProtocol extends DefaultJsonProtocol with KebsSpray.Capitalized with CaseClass1ToValueClass + object KebsProtocol extends DefaultJsonProtocol with KebsSprayJsonCapitalized with CaseClass1ToValueClass import KebsProtocol._ case class C(anInteger: Int) diff --git a/spray-json/src/test/scala/pl/iterators/kebs/json/SprayJsonFormatSnakifyVariantTests.scala b/spray-json/src/test/scala/pl/iterators/kebs/sprayjson/formats/SprayJsonFormatSnakifiedVariantTests.scala similarity index 90% rename from spray-json/src/test/scala/pl/iterators/kebs/json/SprayJsonFormatSnakifyVariantTests.scala rename to spray-json/src/test/scala/pl/iterators/kebs/sprayjson/formats/SprayJsonFormatSnakifiedVariantTests.scala index f637019d..7902bfc0 100644 --- a/spray-json/src/test/scala/pl/iterators/kebs/json/SprayJsonFormatSnakifyVariantTests.scala +++ b/spray-json/src/test/scala/pl/iterators/kebs/sprayjson/formats/SprayJsonFormatSnakifiedVariantTests.scala @@ -1,13 +1,13 @@ -package pl.iterators.kebs.json +package pl.iterators.kebs.sprayjson.formats import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers import pl.iterators.kebs.core.macros.CaseClass1ToValueClass +import pl.iterators.kebs.sprayjson.KebsSprayJsonSnakified 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 with CaseClass1ToValueClass - import KebsProtocol._ +class SprayJsonFormatSnakifiedVariantTests extends AnyFunSuite with Matchers { + object KebsProtocol extends DefaultJsonProtocol with KebsSprayJsonSnakified with CaseClass1ToValueClass case class C(anInteger: Int) case class D(intField: Int, stringField: String) @@ -16,36 +16,48 @@ class SprayJsonFormatSnakifyVariantTests extends AnyFunSuite with Matchers { case class Compound(CField: C, DField: D) test("Flat format remains unchanged") { + import KebsProtocol._ + val jf = implicitly[JsonFormat[C]] jf.write(C(10)) shouldBe JsNumber(10) jf.read(JsNumber(10)) shouldBe C(10) } test("Root format 0 remains unchanged") { + import KebsProtocol._ + val jf = implicitly[RootJsonFormat[F.type]] jf.write(F) shouldBe JsObject() jf.read(JsObject()) shouldBe F } test("Root format 1 snakified") { + import KebsProtocol._ + val jf = implicitly[RootJsonFormat[C]] jf.write(C(10)) shouldBe JsObject("an_integer" -> JsNumber(10)) jf.read(JsObject("an_integer" -> JsNumber(0))) shouldBe C(0) } test("Root format 2 snakified") { + import KebsProtocol._ + val jf = implicitly[RootJsonFormat[D]] jf.write(D(10, "abcd")) shouldBe JsObject("int_field" -> JsNumber(10), "string_field" -> JsString("abcd")) jf.read(JsObject("int_field" -> JsNumber(5), "string_field" -> JsString("abcdef"))) shouldBe D(5, "abcdef") } test("Json format 2 snakified") { + import KebsProtocol._ + val jf = implicitly[JsonFormat[D]] jf.write(D(10, "abcd")) shouldBe JsObject("int_field" -> JsNumber(10), "string_field" -> JsString("abcd")) jf.read(JsObject("int_field" -> JsNumber(5), "string_field" -> JsString("abcdef"))) shouldBe D(5, "abcdef") } test("Root format snakified - compound") { + import KebsProtocol._ + val jf = implicitly[JsonFormat[Compound]] jf.write(Compound(C(5), D(10, "abcd"))) shouldBe JsObject( "c_field" -> JsNumber(5), @@ -58,7 +70,8 @@ class SprayJsonFormatSnakifyVariantTests extends AnyFunSuite with Matchers { } test("Root format snakified - case class with > 22 fields (issue #7)") { - import model._ + import pl.iterators.kebs.sprayjson.model._ + import KebsProtocol._ val jf = implicitly[JsonFormat[ClassWith23Fields]] val obj = ClassWith23Fields.Example @@ -94,10 +107,10 @@ 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 + object KebsProtocolNullOptions extends DefaultJsonProtocol with KebsSprayJsonSnakified with NullOptions with CaseClass1ToValueClass import KebsProtocolNullOptions._ - import model._ + import pl.iterators.kebs.sprayjson.model._ val jf = implicitly[JsonFormat[ClassWith23Fields]] val obj = ClassWith23Fields.Example diff --git a/spray-json/src/test/scala/pl/iterators/kebs/json/SprayJsonFormatTests.scala b/spray-json/src/test/scala/pl/iterators/kebs/sprayjson/formats/SprayJsonFormatTests.scala similarity index 97% rename from spray-json/src/test/scala/pl/iterators/kebs/json/SprayJsonFormatTests.scala rename to spray-json/src/test/scala/pl/iterators/kebs/sprayjson/formats/SprayJsonFormatTests.scala index 09728c4f..990e2301 100644 --- a/spray-json/src/test/scala/pl/iterators/kebs/json/SprayJsonFormatTests.scala +++ b/spray-json/src/test/scala/pl/iterators/kebs/sprayjson/formats/SprayJsonFormatTests.scala @@ -1,14 +1,15 @@ -package pl.iterators.kebs.json +package pl.iterators.kebs.sprayjson.formats import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers import pl.iterators.kebs.core.macros.CaseClass1ToValueClass +import pl.iterators.kebs.sprayjson.KebsSprayJson import spray.json._ import java.util.UUID class SprayJsonFormatTests extends AnyFunSuite with Matchers { - object KebsProtocol extends DefaultJsonProtocol with KebsSpray with CaseClass1ToValueClass + object KebsProtocol extends DefaultJsonProtocol with KebsSprayJson with CaseClass1ToValueClass import KebsProtocol._ case class C(i: Int) @@ -145,7 +146,7 @@ class SprayJsonFormatTests extends AnyFunSuite with Matchers { } test("Root format - case class with > 22 fields (issue #7)") { - import model._ + import pl.iterators.kebs.sprayjson.model._ val jf = implicitly[JsonFormat[ClassWith23Fields]] val obj = ClassWith23Fields.Example @@ -181,10 +182,10 @@ 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 + object KebsProtocolNullOptions extends DefaultJsonProtocol with KebsSprayJson with NullOptions import KebsProtocolNullOptions._ - import model._ + import pl.iterators.kebs.sprayjson.model._ val jf = implicitly[JsonFormat[ClassWith23Fields]] val obj = ClassWith23Fields.Example @@ -229,7 +230,7 @@ class SprayJsonFormatTests extends AnyFunSuite with Matchers { } test("Root format - nested case classes with > 22 fields (issue #78)") { - import model._ + import pl.iterators.kebs.sprayjson.model._ val jf = implicitly[JsonFormat[ClassWith23FieldsNested]] diff --git a/spray-json/src/test/scala/pl/iterators/kebs/json/SprayValueEnumJsonFormatTests.scala b/spray-json/src/test/scala/pl/iterators/kebs/sprayjson/formats/SprayValueEnumJsonFormatTests.scala similarity index 87% rename from spray-json/src/test/scala/pl/iterators/kebs/json/SprayValueEnumJsonFormatTests.scala rename to spray-json/src/test/scala/pl/iterators/kebs/sprayjson/formats/SprayValueEnumJsonFormatTests.scala index b341bd4d..b6307e00 100644 --- a/spray-json/src/test/scala/pl/iterators/kebs/json/SprayValueEnumJsonFormatTests.scala +++ b/spray-json/src/test/scala/pl/iterators/kebs/sprayjson/formats/SprayValueEnumJsonFormatTests.scala @@ -1,10 +1,11 @@ -package pl.iterators.kebs.json +package pl.iterators.kebs.sprayjson.formats import enumeratum.values.{LongEnum, LongEnumEntry} 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.sprayjson.{KebsSprayJson, KebsSprayJsonEnums, KebsSprayJsonValueEnums} import spray.json._ class SprayValueEnumJsonFormatTests extends AnyFunSuite with Matchers with KebsValueEnumeratum { @@ -21,7 +22,7 @@ class SprayValueEnumJsonFormatTests extends AnyFunSuite with Matchers with KebsV import LongGreeting._ - object KebsProtocol extends DefaultJsonProtocol with KebsSpray with KebsEnumFormats + object KebsProtocol extends DefaultJsonProtocol with KebsSprayJson with KebsSprayJsonEnums with KebsSprayJsonValueEnums test("value enum JsonFormat") { import KebsProtocol._ diff --git a/spray-json/src/test/scala/pl/iterators/kebs/json/instances/NetInstancesTests.scala b/spray-json/src/test/scala/pl/iterators/kebs/sprayjson/instances/NetInstancesTests.scala similarity index 86% rename from spray-json/src/test/scala/pl/iterators/kebs/json/instances/NetInstancesTests.scala rename to spray-json/src/test/scala/pl/iterators/kebs/sprayjson/instances/NetInstancesTests.scala index 5ea7bbaf..7fc6bbad 100644 --- a/spray-json/src/test/scala/pl/iterators/kebs/json/instances/NetInstancesTests.scala +++ b/spray-json/src/test/scala/pl/iterators/kebs/sprayjson/instances/NetInstancesTests.scala @@ -1,15 +1,15 @@ -package pl.iterators.kebs.json.instances +package pl.iterators.kebs.sprayjson.instances import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers import pl.iterators.kebs.instances.net.URIString import pl.iterators.kebs.core.instances.InstanceConverter.DecodeErrorException -import pl.iterators.kebs.json.KebsSpray +import pl.iterators.kebs.sprayjson.KebsSprayJson import spray.json._ import java.net.URI -class NetInstancesTests extends AnyFunSuite with Matchers with DefaultJsonProtocol with KebsSpray with URIString { +class NetInstancesTests extends AnyFunSuite with Matchers with DefaultJsonProtocol with KebsSprayJson with URIString { test("URI standard format") { val jf = implicitly[JsonFormat[URI]] diff --git a/spray-json/src/test/scala/pl/iterators/kebs/json/instances/TimeInstancesMixinTests.scala b/spray-json/src/test/scala/pl/iterators/kebs/sprayjson/instances/TimeInstancesMixinTests.scala similarity index 93% rename from spray-json/src/test/scala/pl/iterators/kebs/json/instances/TimeInstancesMixinTests.scala rename to spray-json/src/test/scala/pl/iterators/kebs/sprayjson/instances/TimeInstancesMixinTests.scala index 2f1893a5..8ef642b4 100644 --- a/spray-json/src/test/scala/pl/iterators/kebs/json/instances/TimeInstancesMixinTests.scala +++ b/spray-json/src/test/scala/pl/iterators/kebs/sprayjson/instances/TimeInstancesMixinTests.scala @@ -1,4 +1,4 @@ -package pl.iterators.kebs.json.instances +package pl.iterators.kebs.sprayjson.instances import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers @@ -7,7 +7,7 @@ import pl.iterators.kebs.instances.time.LocalDateTimeString import pl.iterators.kebs.instances.time.mixins.{DurationNanosLong, InstantEpochMilliLong} import pl.iterators.kebs.core.instances.InstanceConverter import pl.iterators.kebs.core.macros.ValueClassLike -import pl.iterators.kebs.json.KebsSpray +import pl.iterators.kebs.sprayjson.KebsSprayJson import spray.json._ import java.time._ @@ -16,7 +16,7 @@ import java.time.format.DateTimeFormatter class TimeInstancesMixinTests extends AnyFunSuite with Matchers { test("Instant epoch milli format") { - object TimeInstancesProtocol extends DefaultJsonProtocol with KebsSpray with InstantEpochMilliLong + object TimeInstancesProtocol extends DefaultJsonProtocol with KebsSprayJson with InstantEpochMilliLong import TimeInstancesProtocol._ "implicitly[ValueClassLike[Instant, Long]]" shouldNot typeCheck @@ -31,7 +31,7 @@ class TimeInstancesMixinTests extends AnyFunSuite with Matchers { } test("Duration nanos format, Instant epoch milli format") { - object TimeInstancesProtocol extends DefaultJsonProtocol with KebsSpray with DurationNanosLong with InstantEpochMilliLong + object TimeInstancesProtocol extends DefaultJsonProtocol with KebsSprayJson with DurationNanosLong with InstantEpochMilliLong import TimeInstancesProtocol._ "implicitly[ValueClassLike[Instant, Long]]" shouldNot typeCheck @@ -55,7 +55,7 @@ class TimeInstancesMixinTests extends AnyFunSuite with Matchers { } test("LocalDateTime custom format using companion object") { - object TimeInstancesProtocol extends DefaultJsonProtocol with KebsSpray with LocalDateTimeString { + object TimeInstancesProtocol extends DefaultJsonProtocol with KebsSprayJson with LocalDateTimeString { val formatter: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm") override implicit val localDateTimeFormatter: InstanceConverter[LocalDateTime, String] = @@ -75,7 +75,7 @@ class TimeInstancesMixinTests extends AnyFunSuite with Matchers { } test("LocalDateTime custom format with error handling") { - object TimeInstancesProtocol extends DefaultJsonProtocol with KebsSpray with TimeInstances { + object TimeInstancesProtocol extends DefaultJsonProtocol with KebsSprayJson with TimeInstances { val pattern = "yyyy/MM/dd HH:mm" val formatter: DateTimeFormatter = DateTimeFormatter.ofPattern(pattern) diff --git a/spray-json/src/test/scala/pl/iterators/kebs/json/instances/TimeInstancesTests.scala b/spray-json/src/test/scala/pl/iterators/kebs/sprayjson/instances/TimeInstancesTests.scala similarity index 98% rename from spray-json/src/test/scala/pl/iterators/kebs/json/instances/TimeInstancesTests.scala rename to spray-json/src/test/scala/pl/iterators/kebs/sprayjson/instances/TimeInstancesTests.scala index e3df3b05..f92757f0 100644 --- a/spray-json/src/test/scala/pl/iterators/kebs/json/instances/TimeInstancesTests.scala +++ b/spray-json/src/test/scala/pl/iterators/kebs/sprayjson/instances/TimeInstancesTests.scala @@ -1,15 +1,15 @@ -package pl.iterators.kebs.json.instances +package pl.iterators.kebs.sprayjson.instances import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers import pl.iterators.kebs.core.instances.InstanceConverter.DecodeErrorException import pl.iterators.kebs.instances.TimeInstances -import pl.iterators.kebs.json.KebsSpray +import pl.iterators.kebs.sprayjson.KebsSprayJson import spray.json._ import java.time._ -class TimeInstancesTests extends AnyFunSuite with Matchers with DefaultJsonProtocol with KebsSpray with TimeInstances { +class TimeInstancesTests extends AnyFunSuite with Matchers with DefaultJsonProtocol with KebsSprayJson with TimeInstances { test("No ValueClassLike implicits derived") { import pl.iterators.kebs.core.macros.ValueClassLike diff --git a/spray-json/src/test/scala/pl/iterators/kebs/json/instances/UtilInstancesTests.scala b/spray-json/src/test/scala/pl/iterators/kebs/sprayjson/instances/UtilInstancesTests.scala similarity index 92% rename from spray-json/src/test/scala/pl/iterators/kebs/json/instances/UtilInstancesTests.scala rename to spray-json/src/test/scala/pl/iterators/kebs/sprayjson/instances/UtilInstancesTests.scala index 4a525a44..a270db46 100644 --- a/spray-json/src/test/scala/pl/iterators/kebs/json/instances/UtilInstancesTests.scala +++ b/spray-json/src/test/scala/pl/iterators/kebs/sprayjson/instances/UtilInstancesTests.scala @@ -1,15 +1,15 @@ -package pl.iterators.kebs.json.instances +package pl.iterators.kebs.sprayjson.instances import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers import pl.iterators.kebs.core.instances.InstanceConverter.DecodeErrorException import pl.iterators.kebs.instances.UtilInstances -import pl.iterators.kebs.json.KebsSpray +import pl.iterators.kebs.sprayjson.KebsSprayJson import spray.json._ import java.util.{Currency, Locale, UUID} -class UtilInstancesTests extends AnyFunSuite with Matchers with DefaultJsonProtocol with KebsSpray with UtilInstances { +class UtilInstancesTests extends AnyFunSuite with Matchers with DefaultJsonProtocol with KebsSprayJson with UtilInstances { test("No ValueClassLike implicits derived") { import pl.iterators.kebs.core.macros.ValueClassLike diff --git a/spray-json/src/test/scala/pl/iterators/kebs/json/model/package.scala b/spray-json/src/test/scala/pl/iterators/kebs/sprayjson/model/package.scala similarity index 98% rename from spray-json/src/test/scala/pl/iterators/kebs/json/model/package.scala rename to spray-json/src/test/scala/pl/iterators/kebs/sprayjson/model/package.scala index ffa444df..0b2394a7 100644 --- a/spray-json/src/test/scala/pl/iterators/kebs/json/model/package.scala +++ b/spray-json/src/test/scala/pl/iterators/kebs/sprayjson/model/package.scala @@ -1,4 +1,4 @@ -package pl.iterators.kebs.json +package pl.iterators.kebs.sprayjson package object model { diff --git a/tagged-meta/src/test/scala/pl/iterators/kebs/tag/meta/SprayAnnotationTests.scala b/tagged-meta/src/test/scala/pl/iterators/kebs/tag/meta/SprayAnnotationTests.scala index fceb89ab..0fa5f839 100644 --- a/tagged-meta/src/test/scala/pl/iterators/kebs/tag/meta/SprayAnnotationTests.scala +++ b/tagged-meta/src/test/scala/pl/iterators/kebs/tag/meta/SprayAnnotationTests.scala @@ -3,7 +3,7 @@ package pl.iterators.kebs.tag.meta import _root_.spray.json._ import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import pl.iterators.kebs.json.KebsSpray +import pl.iterators.kebs.sprayjson.KebsSprayJson import pl.iterators.kebs.tagged._ @tagged object SprayTestTags { @@ -42,7 +42,7 @@ import pl.iterators.kebs.tagged._ object SprayTestTagsFromTrait extends SprayTestTagsTrait -class SprayAnnotationTests extends AnyFunSuite with Matchers with KebsSpray with DefaultJsonProtocol { +class SprayAnnotationTests extends AnyFunSuite with Matchers with KebsSprayJson with DefaultJsonProtocol { test("spray implicits are generated (object)") { import SprayTestTags._ implicitly[JsonReader[Name]].read(JsString("Joe")) shouldEqual "Joe" diff --git a/tagged-meta/src/test/scala/pl/iterators/kebs/tag/meta/SprayKebsIssue47Test.scala b/tagged-meta/src/test/scala/pl/iterators/kebs/tag/meta/SprayKebsIssue47Test.scala index f01381d1..85ef1103 100644 --- a/tagged-meta/src/test/scala/pl/iterators/kebs/tag/meta/SprayKebsIssue47Test.scala +++ b/tagged-meta/src/test/scala/pl/iterators/kebs/tag/meta/SprayKebsIssue47Test.scala @@ -3,7 +3,7 @@ package pl.iterators.kebs.tag.meta import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers import pl.iterators.kebs.core.macros.CaseClass1ToValueClass -import pl.iterators.kebs.json.KebsSpray +import pl.iterators.kebs.sprayjson.KebsSprayJson import pl.iterators.kebs.tagged._ import util.Properties.versionNumberString @@ -21,7 +21,7 @@ object dto { case class SomeDto(opt: Option[SomeTaggedValue]) } -class SprayKebsIssue47Test extends AnyFunSuite with Matchers with DefaultJsonProtocol with KebsSpray with CaseClass1ToValueClass { +class SprayKebsIssue47Test extends AnyFunSuite with Matchers with DefaultJsonProtocol with KebsSprayJson with CaseClass1ToValueClass { import dto._ test("diverging implicit bug fixed in 2.13.1") { diff --git a/tagged/shared/src/main/scala/pl/iterators/kebs/tagged/circe/CirceSupport.scala b/tagged/shared/src/main/scala/pl/iterators/kebs/tagged/circe/KebsTaggedCirce.scala similarity index 98% rename from tagged/shared/src/main/scala/pl/iterators/kebs/tagged/circe/CirceSupport.scala rename to tagged/shared/src/main/scala/pl/iterators/kebs/tagged/circe/KebsTaggedCirce.scala index d8dbd56f..f9509e32 100644 --- a/tagged/shared/src/main/scala/pl/iterators/kebs/tagged/circe/CirceSupport.scala +++ b/tagged/shared/src/main/scala/pl/iterators/kebs/tagged/circe/KebsTaggedCirce.scala @@ -8,7 +8,7 @@ import pl.iterators.kebs.tagged.@@ import io.circe.syntax._ import pl.iterators.kebs.tagged._ -trait CirceSupport { +trait KebsTaggedCirce { private def taggedCodec[U: Decoder: Encoder, T]: Codec[U @@ T] = new Codec[U @@ T] { diff --git a/tagged/shared/src/main/scala/pl/iterators/kebs/tagged/circe/package.scala b/tagged/shared/src/main/scala/pl/iterators/kebs/tagged/circe/package.scala index 4dc4efa6..fe8bcbf7 100644 --- a/tagged/shared/src/main/scala/pl/iterators/kebs/tagged/circe/package.scala +++ b/tagged/shared/src/main/scala/pl/iterators/kebs/tagged/circe/package.scala @@ -1,3 +1,3 @@ package pl.iterators.kebs.tagged -package object circe extends CirceSupport +package object circe extends KebsTaggedCirce