diff --git a/build.sbt b/build.sbt index a7df0054..bbeb31ba 100644 --- a/build.sbt +++ b/build.sbt @@ -2,9 +2,9 @@ import sbtcrossproject.{ CrossType, crossProject } val Scala212V = "2.12.17" val Scala213V = "2.13.7" -val Scala3V = "3.2.0" +val Scala3V = "3.2.2" -val circeVersion = "0.14.3" +val circeVersion = "0.14.5" val paradiseVersion = "2.1.1" val jawnVersion = "1.4.0" @@ -100,9 +100,9 @@ lazy val benchmarks = project .settings( moduleName := "circe-generic-extras-benchmarks", libraryDependencies ++= List( - "io.circe" %%% "circe-parser" % circeVersion, - scalaOrganization.value % "scala-reflect" % scalaVersion.value - ) + "io.circe" %%% "circe-parser" % circeVersion + ) ++ (if(scalaBinaryVersion.value.startsWith("2")) List(scalaOrganization.value % "scala-reflect" % scalaVersion.value) + else List.empty) ) .dependsOn(genericExtras.jvm) .enablePlugins(JmhPlugin, NoPublishPlugin) diff --git a/generic-extras/src/main/scala/io/circe/generic/extras/Configuration.scala b/generic-extras/src/main/scala-2/io/circe/generic/extras/Configuration.scala similarity index 100% rename from generic-extras/src/main/scala/io/circe/generic/extras/Configuration.scala rename to generic-extras/src/main/scala-2/io/circe/generic/extras/Configuration.scala diff --git a/generic-extras/src/main/scala-2/io/circe/generic/extras/auto/package.scala b/generic-extras/src/main/scala-2/io/circe/generic/extras/auto/package.scala new file mode 100644 index 00000000..08c9a480 --- /dev/null +++ b/generic-extras/src/main/scala-2/io/circe/generic/extras/auto/package.scala @@ -0,0 +1,9 @@ +package io.circe.generic.extras + +/** + * Fully automatic configurable codec derivation. + * + * Importing the contents of this package object provides [[io.circe.Decoder]] and [[io.circe.Encoder]] instances for + * case classes (if all members have instances), "incomplete" case classes, sealed trait hierarchies, etc. + */ +package object auto extends AutoDerivation diff --git a/generic-extras/src/main/scala-2/io/circe/generic/extras/package.scala b/generic-extras/src/main/scala-2/io/circe/generic/extras/package.scala index 08c9a480..b22865da 100644 --- a/generic-extras/src/main/scala-2/io/circe/generic/extras/package.scala +++ b/generic-extras/src/main/scala-2/io/circe/generic/extras/package.scala @@ -1,9 +1,7 @@ -package io.circe.generic.extras +package io.circe.generic -/** - * Fully automatic configurable codec derivation. - * - * Importing the contents of this package object provides [[io.circe.Decoder]] and [[io.circe.Encoder]] instances for - * case classes (if all members have instances), "incomplete" case classes, sealed trait hierarchies, etc. - */ -package object auto extends AutoDerivation +import io.circe.Codec + +package object extras { + type ExtrasAsObjectCodec[A] = Codec.AsObject[A] with ExtrasDecoder[A] +} diff --git a/generic-extras/src/main/scala-3/io/circe/generic/extras/package.scala b/generic-extras/src/main/scala-3/io/circe/generic/extras/package.scala new file mode 100644 index 00000000..f75d08c2 --- /dev/null +++ b/generic-extras/src/main/scala-3/io/circe/generic/extras/package.scala @@ -0,0 +1,51 @@ +package io.circe.generic + +import io.circe.Codec +import java.util.regex.Pattern + +package object extras { + type ExtrasAsObjectCodec[A] = Codec.AsObject[A] with ExtrasDecoder[A] + type Configuration = io.circe.derivation.Configuration + + object Configuration { + def apply( + transformMemberNames: String => String = Predef.identity, + transformConstructorNames: String => String = Predef.identity, + useDefaults: Boolean = false, + discriminator: Option[String] = None, + strictDecoding: Boolean = false + ): Configuration = Configuration( + transformMemberNames = transformMemberNames, + transformConstructorNames = transformConstructorNames, + useDefaults = useDefaults, + discriminator = discriminator, + strictDecoding = strictDecoding + ) + + val default: Configuration = io.circe.derivation.Configuration.default + private val basePattern: Pattern = Pattern.compile("([A-Z]+)([A-Z][a-z])") + private val swapPattern: Pattern = Pattern.compile("([a-z\\d])([A-Z])") + + val snakeCaseTransformation: String => String = s => { + val partial = basePattern.matcher(s).replaceAll("$1_$2") + swapPattern.matcher(partial).replaceAll("$1_$2").toLowerCase + } + + val screamingSnakeCaseTransformation: String => String = s => { + val partial = basePattern.matcher(s).replaceAll("$1_$2") + swapPattern.matcher(partial).replaceAll("$1_$2").toUpperCase + } + + val kebabCaseTransformation: String => String = s => { + val partial = basePattern.matcher(s).replaceAll("$1-$2") + swapPattern.matcher(partial).replaceAll("$1-$2").toLowerCase + } + + val pascalCaseTransformation: String => String = s => { + s"${s.charAt(0).toUpper}${s.substring(1)}" + } + } + object defaults { + implicit val defaultGenericConfiguration: Configuration = Configuration.default + } +} diff --git a/generic-extras/src/main/scala-3/io/circe/generic/extras/semiauto.scala b/generic-extras/src/main/scala-3/io/circe/generic/extras/semiauto.scala index 5d766d35..4676de2a 100644 --- a/generic-extras/src/main/scala-3/io/circe/generic/extras/semiauto.scala +++ b/generic-extras/src/main/scala-3/io/circe/generic/extras/semiauto.scala @@ -23,15 +23,15 @@ import scala.deriving.Mirror * }}} */ object semiauto { - inline final def deriveConfiguredDecoder[A](using inline A: Mirror.Of[A], configuration: Configuration): Decoder[A] = ??? - inline final def deriveConfiguredEncoder[A](using inline A: Mirror.Of[A], configuration: Configuration): Encoder.AsObject[A] = ??? - inline final def deriveConfiguredCodec[A](using inline A: Mirror.Of[A], configuration: Configuration): Codec.AsObject[A] = ??? + inline final def deriveConfiguredDecoder[A](using inline A: Mirror.Of[A], configuration: Configuration): Decoder[A] = io.circe.derivation.ConfiguredDecoder.derived[A] + inline final def deriveConfiguredEncoder[A](using inline A: Mirror.Of[A], configuration: Configuration): Encoder.AsObject[A] = io.circe.derivation.ConfiguredEncoder.derived[A] + inline final def deriveConfiguredCodec[A](using inline A: Mirror.Of[A], configuration: Configuration): Codec.AsObject[A] = io.circe.derivation.ConfiguredCodec.derived[A] inline final def deriveExtrasDecoder[A](using inline A: Mirror.Of[A], configuration: Configuration): ExtrasDecoder[A] = ??? - inline final def deriveExtrasEncoder[A](using inline A: Mirror.Of[A], configuration: Configuration): Encoder.AsObject[A] = ??? + inline final def deriveExtrasEncoder[A](using inline A: Mirror.Of[A], configuration: Configuration): Encoder.AsObject[A] = deriveConfiguredEncoder[A] inline final def deriveExtrasCodec[A](using inline A: Mirror.Of[A], configuration: Configuration): ExtrasAsObjectCodec[A] = ??? - /** + /** * Derive a decoder for a sealed trait hierarchy made up of case objects. * * Note that this differs from the usual derived decoder in that the leaves of the ADT are represented as JSON diff --git a/generic-extras/src/main/scala/io/circe/generic/extras/package.scala b/generic-extras/src/main/scala/io/circe/generic/extras/package.scala deleted file mode 100644 index b22865da..00000000 --- a/generic-extras/src/main/scala/io/circe/generic/extras/package.scala +++ /dev/null @@ -1,7 +0,0 @@ -package io.circe.generic - -import io.circe.Codec - -package object extras { - type ExtrasAsObjectCodec[A] = Codec.AsObject[A] with ExtrasDecoder[A] -}