From cf0853f3d1dcc904408205f3b2e89e556d4993e9 Mon Sep 17 00:00:00 2001 From: Ivan Klass Date: Sat, 11 Mar 2023 00:02:04 +0100 Subject: [PATCH 1/8] Add pureconfig for scala3 --- .scalafmt.conf | 2 +- build.sbt | 19 ++++--- .../TypeDescribeVersionSpecific.scala | 19 +++++++ .../TypeDescribeVersionSpecific.scala | 7 +++ .../refined/pureconfig/BaseInstances.scala | 52 +++++++++++++++++++ .../timepit/refined/pureconfig/package.scala | 40 ++------------ .../pureconfig/SpecDerivedInstances.scala | 19 +++++++ .../pureconfig/SpecDerivedInstances.scala | 10 ++++ .../timepit/refined/pureconfig/Config.scala | 4 ++ .../pureconfig/RefTypeConfigConvertSpec.scala | 39 +++++++------- 10 files changed, 146 insertions(+), 65 deletions(-) create mode 100644 modules/pureconfig/shared/src/main/scala-3.0+/eu/timepit/refined/pureconfig/TypeDescribeVersionSpecific.scala create mode 100644 modules/pureconfig/shared/src/main/scala-3.0-/eu/timepit/refined/pureconfig/TypeDescribeVersionSpecific.scala create mode 100644 modules/pureconfig/shared/src/main/scala/eu/timepit/refined/pureconfig/BaseInstances.scala create mode 100644 modules/pureconfig/shared/src/test/scala-3.0+/eu/timepit/refined/pureconfig/SpecDerivedInstances.scala create mode 100644 modules/pureconfig/shared/src/test/scala-3.0-/eu/timepit/refined/pureconfig/SpecDerivedInstances.scala create mode 100644 modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/Config.scala diff --git a/.scalafmt.conf b/.scalafmt.conf index 338adf342..ecbecba5a 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,5 +1,5 @@ version = 3.7.2 -runner.dialect = scala213 +runner.dialect = Scala213Source3 maxColumn = 100 docstrings.style = keep rewrite.rules = [PreferCurlyFors, RedundantBraces] diff --git a/build.sbt b/build.sbt index 0e32e6893..195427ceb 100644 --- a/build.sbt +++ b/build.sbt @@ -53,7 +53,7 @@ val moduleCrossPlatformMatrix: Map[String, List[Platform]] = Map( ) val moduleCrossScalaVersionsMatrix: (String, Platform) => List[String] = { - case ("benchmark" | "eval" | "pureconfig" | "scalaz" | "scodec" | "shapeless", _) => + case ("benchmark" | "eval" | "scalaz" | "scodec" | "shapeless", _) => List(Scala_2_12, Scala_2_13) case _ => List(Scala_2_12, Scala_2_13, Scala_3) @@ -219,9 +219,14 @@ lazy val pureconfig = myCrossProject("pureconfig") .dependsOn(core % "compile->compile;test->test") .settings( libraryDependencies ++= macroParadise(Test).value ++ Seq( - "com.github.pureconfig" %% "pureconfig-core" % pureconfigVersion, - "com.github.pureconfig" %% "pureconfig-generic" % pureconfigVersion % Test - ) + "com.github.pureconfig" %% "pureconfig-core" % pureconfigVersion + ) ++ (CrossVersion.partialVersion(scalaVersion.value) match { + case Some((2, 12 | 13)) => + Seq( + "com.github.pureconfig" %% "pureconfig-generic" % pureconfigVersion % Test + ) + case _ => Seq.empty + }) ) lazy val pureconfigJVM = pureconfig.jvm @@ -450,10 +455,8 @@ lazy val compileSettings = Def.settings( if (dir.getName != "scala") Seq(dir) else CrossVersion.partialVersion(scalaVersion.value) match { - case Some((2, 12)) => Seq(file(dir.getPath + "-3.0-")) - case Some((2, 13)) => Seq(file(dir.getPath + "-3.0-")) - case Some((0, _)) => Seq(file(dir.getPath + "-3.0+")) - case Some((3, _)) => Seq(file(dir.getPath + "-3.0+")) + case Some((2, 12 | 13)) => Seq(file(dir.getPath + "-3.0-")) + case Some((0 | 3, _)) => Seq(file(dir.getPath + "-3.0+")) case other => sys.error(s"unmanagedSourceDirectories for scalaVersion $other not set") } } diff --git a/modules/pureconfig/shared/src/main/scala-3.0+/eu/timepit/refined/pureconfig/TypeDescribeVersionSpecific.scala b/modules/pureconfig/shared/src/main/scala-3.0+/eu/timepit/refined/pureconfig/TypeDescribeVersionSpecific.scala new file mode 100644 index 000000000..e73ae8bbf --- /dev/null +++ b/modules/pureconfig/shared/src/main/scala-3.0+/eu/timepit/refined/pureconfig/TypeDescribeVersionSpecific.scala @@ -0,0 +1,19 @@ +package eu.timepit.refined.pureconfig + +import scala.quoted.{Expr, Quotes, Type, quotes} + +final class DescribeType[T](val repr: String) + +object DescribeType: + + private def macroImpl[T: Type](using Quotes): Expr[DescribeType[T]] = { + import quotes.reflect.* + '{ new DescribeType[T](${ Expr(TypeRepr.of[T].show) }) } + } + + inline given describeType[T]: DescribeType[T] = ${ macroImpl[T]} + +private[pureconfig] trait TypeDescribeVersionSpecific { + + protected def getDescription[A](d: DescribeType[A]): String = d.repr +} diff --git a/modules/pureconfig/shared/src/main/scala-3.0-/eu/timepit/refined/pureconfig/TypeDescribeVersionSpecific.scala b/modules/pureconfig/shared/src/main/scala-3.0-/eu/timepit/refined/pureconfig/TypeDescribeVersionSpecific.scala new file mode 100644 index 000000000..682add0c2 --- /dev/null +++ b/modules/pureconfig/shared/src/main/scala-3.0-/eu/timepit/refined/pureconfig/TypeDescribeVersionSpecific.scala @@ -0,0 +1,7 @@ +package eu.timepit.refined.pureconfig + +private[pureconfig] trait TypeDescribeVersionSpecific { + final type DescribeType[A] = scala.reflect.runtime.universe.WeakTypeTag[A] + + protected def getDescription[A](d: DescribeType[A]): String = d.tpe.toString +} diff --git a/modules/pureconfig/shared/src/main/scala/eu/timepit/refined/pureconfig/BaseInstances.scala b/modules/pureconfig/shared/src/main/scala/eu/timepit/refined/pureconfig/BaseInstances.scala new file mode 100644 index 000000000..7bae58402 --- /dev/null +++ b/modules/pureconfig/shared/src/main/scala/eu/timepit/refined/pureconfig/BaseInstances.scala @@ -0,0 +1,52 @@ +package eu.timepit.refined.pureconfig + +import _root_.pureconfig.{ConfigCursor, ConfigReader, ConfigWriter} +import _root_.pureconfig.error.{CannotConvert, ConvertFailure, ConfigReaderFailures} +import eu.timepit.refined.api.{RefType, Validate} +import com.typesafe.config.ConfigValue + +trait BaseInstances extends TypeDescribeVersionSpecific { + + implicit def refTypeConfigReader[F[_, _], T, P](implicit + configReader: ConfigReader[T], + refType: RefType[F], + validate: Validate[T, P], + describeType: DescribeType[F[T, P]] + ): ConfigReader[F[T, P]] = new ConfigReader[F[T, P]] { + + override def from(cur: ConfigCursor): Either[ConfigReaderFailures, F[T, P]] = + configReader.from(cur) match { + case Right(t) => + refType.refine[P](t) match { + case Left(because) => + Left( + ConfigReaderFailures( + ConvertFailure( + reason = CannotConvert( + value = cur.valueOpt.map(_.render()).getOrElse("none"), + toType = getDescription(describeType), + because = because + ), + cur = cur + ) + ) + ) + + case Right(refined) => + Right(refined) + } + + case Left(configReaderFailures) => + Left(configReaderFailures) + } + } + + implicit def refTypeConfigWriter[F[_, _], T, P](implicit + configWriter: ConfigWriter[T], + refType: RefType[F] + ): ConfigWriter[F[T, P]] = + new ConfigWriter[F[T, P]] { + override def to(a: F[T, P]): ConfigValue = configWriter.to(refType.unwrap(a)) + } + +} diff --git a/modules/pureconfig/shared/src/main/scala/eu/timepit/refined/pureconfig/package.scala b/modules/pureconfig/shared/src/main/scala/eu/timepit/refined/pureconfig/package.scala index f34858a57..44f4136a2 100644 --- a/modules/pureconfig/shared/src/main/scala/eu/timepit/refined/pureconfig/package.scala +++ b/modules/pureconfig/shared/src/main/scala/eu/timepit/refined/pureconfig/package.scala @@ -1,47 +1,15 @@ package eu.timepit.refined -import _root_.pureconfig.{ConfigConvert, ConfigCursor} -import _root_.pureconfig.error.{CannotConvert, ConfigReaderFailures, ConvertFailure} -import com.typesafe.config.ConfigValue +import _root_.pureconfig.ConfigConvert import eu.timepit.refined.api.{RefType, Validate} -import scala.reflect.runtime.universe.WeakTypeTag -package object pureconfig { +package object pureconfig extends BaseInstances { implicit def refTypeConfigConvert[F[_, _], T, P](implicit configConvert: ConfigConvert[T], refType: RefType[F], validate: Validate[T, P], - typeTag: WeakTypeTag[F[T, P]] + typeTag: DescribeType[F[T, P]] ): ConfigConvert[F[T, P]] = - new ConfigConvert[F[T, P]] { - override def from(cur: ConfigCursor): Either[ConfigReaderFailures, F[T, P]] = - configConvert.from(cur) match { - case Right(t) => - refType.refine[P](t) match { - case Left(because) => - Left( - ConfigReaderFailures( - ConvertFailure( - reason = CannotConvert( - value = cur.valueOpt.map(_.render()).getOrElse("none"), - toType = typeTag.tpe.toString, - because = because - ), - cur = cur - ) - ) - ) - - case Right(refined) => - Right(refined) - } - - case Left(configReaderFailures) => - Left(configReaderFailures) - } - - override def to(t: F[T, P]): ConfigValue = - configConvert.to(refType.unwrap(t)) - } + ConfigConvert.fromReaderAndWriter(refTypeConfigReader, refTypeConfigWriter) } diff --git a/modules/pureconfig/shared/src/test/scala-3.0+/eu/timepit/refined/pureconfig/SpecDerivedInstances.scala b/modules/pureconfig/shared/src/test/scala-3.0+/eu/timepit/refined/pureconfig/SpecDerivedInstances.scala new file mode 100644 index 000000000..800b487b3 --- /dev/null +++ b/modules/pureconfig/shared/src/test/scala-3.0+/eu/timepit/refined/pureconfig/SpecDerivedInstances.scala @@ -0,0 +1,19 @@ +package eu.timepit.refined.pureconfig + +import _root_.pureconfig.{ConfigReader, ConfigWriter} +import com.typesafe.config.ConfigValueFactory +import _root_.pureconfig.generic.derivation.default.derived +import eu.timepit.refined.types.numeric.PosInt + +import java.util.{Map as JMap} + +trait SpecDerivedInstances { + + given ConfigReader[Config] = ConfigReader.derived + // we only care that this part of config writer (refined type) compiles + private val posIntConfWriter: ConfigWriter[PosInt] = summon + // there's no builtin generic derivation of ConfigWriter[ADT] for scala 3 yet + given ConfigWriter[Config] = posIntConfWriter.contramap[Config](_.value).mapConfig(cfg => + ConfigValueFactory.fromMap(JMap.of("value", cfg)) + ) +} diff --git a/modules/pureconfig/shared/src/test/scala-3.0-/eu/timepit/refined/pureconfig/SpecDerivedInstances.scala b/modules/pureconfig/shared/src/test/scala-3.0-/eu/timepit/refined/pureconfig/SpecDerivedInstances.scala new file mode 100644 index 000000000..05bffed1c --- /dev/null +++ b/modules/pureconfig/shared/src/test/scala-3.0-/eu/timepit/refined/pureconfig/SpecDerivedInstances.scala @@ -0,0 +1,10 @@ +package eu.timepit.refined.pureconfig + +import _root_.pureconfig.{ConfigReader, ConfigWriter} +import _root_.pureconfig.generic.auto + +trait SpecDerivedInstances { + + implicit val configReader: ConfigReader[Config] = auto.exportReader.instance + implicit val configWriter: ConfigWriter[Config] = auto.exportWriter.instance +} diff --git a/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/Config.scala b/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/Config.scala new file mode 100644 index 000000000..0637cd16d --- /dev/null +++ b/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/Config.scala @@ -0,0 +1,4 @@ +package eu.timepit.refined.pureconfig + +import eu.timepit.refined.types.numeric.PosInt +case class Config(value: PosInt) diff --git a/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala b/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala index e681bb6ac..6571f1c34 100644 --- a/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala +++ b/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala @@ -6,11 +6,10 @@ import org.scalacheck.Prop._ import org.scalacheck.Properties import pureconfig._ import pureconfig.error.{CannotConvert, ConfigReaderFailures, ConvertFailure, WrongType} -import pureconfig.generic.auto._ -class RefTypeConfigConvertSpec extends Properties("RefTypeConfigConvert") { - - case class Config(value: PosInt) +class RefTypeConfigConvertSpec + extends Properties("RefTypeConfigConvert") + with SpecDerivedInstances { property("load success") = secure { loadConfigWithValue("1") ?= @@ -18,12 +17,13 @@ class RefTypeConfigConvertSpec extends Properties("RefTypeConfigConvert") { } property("load failure (predicate)") = secure { - val expected1 = Left( + + def expectedFailure(toTypeMsg: String) = Left( ConfigReaderFailures( ConvertFailure( reason = CannotConvert( value = "0", - toType = "eu.timepit.refined.api.Refined[Int,eu.timepit.refined.numeric.Positive]", + toType = toTypeMsg, because = "Predicate failed: (0 > 0)." ), origin = Some(ConfigOriginFactory.newSimple("String").withLineNumber(1)), @@ -32,26 +32,25 @@ class RefTypeConfigConvertSpec extends Properties("RefTypeConfigConvert") { ) ) + val expected1 = expectedFailure( + "eu.timepit.refined.api.Refined[Int,eu.timepit.refined.numeric.Positive]" + ) + // Allow "scala.Int" instead of just "Int" in the toType parameter. // For some reason Scala 2.12 with sbt 1.1.2 uses the former. - val expected2 = Left( - ConfigReaderFailures( - ConvertFailure( - reason = CannotConvert( - value = "0", - toType = - "eu.timepit.refined.api.Refined[scala.Int,eu.timepit.refined.numeric.Positive]", - because = "Predicate failed: (0 > 0)." - ), - origin = Some(ConfigOriginFactory.newSimple("String").withLineNumber(1)), - path = "value" - ) - ) + val expected2 = expectedFailure( + "eu.timepit.refined.api.Refined[scala.Int,eu.timepit.refined.numeric.Positive]" + ) + + // scala 3 macro provide type representation in this way + val expected3 = expectedFailure( + "eu.timepit.refined.api.Refined[scala.Int, eu.timepit.refined.numeric.Greater[shapeless.nat._0]]" ) val actual = loadConfigWithValue("0") (actual ?= expected1) || - (actual ?= expected2) + (actual ?= expected2) || + (actual ?= expected3) } property("load failure (wrong type)") = secure { From a5aa7f795b7ec9b0c8319ae1fe28aef25f0c760d Mon Sep 17 00:00:00 2001 From: Ivan Klass Date: Sat, 11 Mar 2023 10:34:25 +0100 Subject: [PATCH 2/8] proper scalafmt dialect, fix for old Java version --- .scalafmt.conf | 7 ++++++- .../pureconfig/TypeDescribeVersionSpecific.scala | 2 +- .../refined/pureconfig/SpecDerivedInstances.scala | 12 ++++++------ 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/.scalafmt.conf b/.scalafmt.conf index ecbecba5a..ce8d58d0c 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,8 +1,13 @@ version = 3.7.2 -runner.dialect = Scala213Source3 +runner.dialect = scala213 maxColumn = 100 docstrings.style = keep rewrite.rules = [PreferCurlyFors, RedundantBraces] rewriteTokens { "⇒": "=>" } +fileOverride { + "glob:**/src/*/scala-3.0+/**" { + runner.dialect = scala3 + } +} diff --git a/modules/pureconfig/shared/src/main/scala-3.0+/eu/timepit/refined/pureconfig/TypeDescribeVersionSpecific.scala b/modules/pureconfig/shared/src/main/scala-3.0+/eu/timepit/refined/pureconfig/TypeDescribeVersionSpecific.scala index e73ae8bbf..4945cf062 100644 --- a/modules/pureconfig/shared/src/main/scala-3.0+/eu/timepit/refined/pureconfig/TypeDescribeVersionSpecific.scala +++ b/modules/pureconfig/shared/src/main/scala-3.0+/eu/timepit/refined/pureconfig/TypeDescribeVersionSpecific.scala @@ -11,7 +11,7 @@ object DescribeType: '{ new DescribeType[T](${ Expr(TypeRepr.of[T].show) }) } } - inline given describeType[T]: DescribeType[T] = ${ macroImpl[T]} + inline given describeType[T]: DescribeType[T] = ${ macroImpl[T] } private[pureconfig] trait TypeDescribeVersionSpecific { diff --git a/modules/pureconfig/shared/src/test/scala-3.0+/eu/timepit/refined/pureconfig/SpecDerivedInstances.scala b/modules/pureconfig/shared/src/test/scala-3.0+/eu/timepit/refined/pureconfig/SpecDerivedInstances.scala index 800b487b3..f44ddcb15 100644 --- a/modules/pureconfig/shared/src/test/scala-3.0+/eu/timepit/refined/pureconfig/SpecDerivedInstances.scala +++ b/modules/pureconfig/shared/src/test/scala-3.0+/eu/timepit/refined/pureconfig/SpecDerivedInstances.scala @@ -1,11 +1,11 @@ package eu.timepit.refined.pureconfig +import eu.timepit.refined.types.numeric.PosInt import _root_.pureconfig.{ConfigReader, ConfigWriter} -import com.typesafe.config.ConfigValueFactory import _root_.pureconfig.generic.derivation.default.derived -import eu.timepit.refined.types.numeric.PosInt +import com.typesafe.config.ConfigValueFactory -import java.util.{Map as JMap} +import scala.jdk.CollectionConverters.* trait SpecDerivedInstances { @@ -13,7 +13,7 @@ trait SpecDerivedInstances { // we only care that this part of config writer (refined type) compiles private val posIntConfWriter: ConfigWriter[PosInt] = summon // there's no builtin generic derivation of ConfigWriter[ADT] for scala 3 yet - given ConfigWriter[Config] = posIntConfWriter.contramap[Config](_.value).mapConfig(cfg => - ConfigValueFactory.fromMap(JMap.of("value", cfg)) - ) + given ConfigWriter[Config] = posIntConfWriter + .contramap[Config](_.value) + .mapConfig(cfg => ConfigValueFactory.fromMap(Map("value" -> cfg).asJava)) } From e9be4bd8ecbe51288ab80328c800e27e47d40da6 Mon Sep 17 00:00:00 2001 From: Ivan Klass Date: Sat, 11 Mar 2023 11:09:19 +0100 Subject: [PATCH 3/8] removed pureconfig.generic --- build.sbt | 8 +----- .../pureconfig/SpecDerivedInstances.scala | 19 ------------- .../pureconfig/SpecDerivedInstances.scala | 10 ------- .../pureconfig/RefTypeConfigConvertSpec.scala | 28 +++++++++++++++++-- 4 files changed, 26 insertions(+), 39 deletions(-) delete mode 100644 modules/pureconfig/shared/src/test/scala-3.0+/eu/timepit/refined/pureconfig/SpecDerivedInstances.scala delete mode 100644 modules/pureconfig/shared/src/test/scala-3.0-/eu/timepit/refined/pureconfig/SpecDerivedInstances.scala diff --git a/build.sbt b/build.sbt index 195427ceb..ff8e1cd94 100644 --- a/build.sbt +++ b/build.sbt @@ -220,13 +220,7 @@ lazy val pureconfig = myCrossProject("pureconfig") .settings( libraryDependencies ++= macroParadise(Test).value ++ Seq( "com.github.pureconfig" %% "pureconfig-core" % pureconfigVersion - ) ++ (CrossVersion.partialVersion(scalaVersion.value) match { - case Some((2, 12 | 13)) => - Seq( - "com.github.pureconfig" %% "pureconfig-generic" % pureconfigVersion % Test - ) - case _ => Seq.empty - }) + ) ) lazy val pureconfigJVM = pureconfig.jvm diff --git a/modules/pureconfig/shared/src/test/scala-3.0+/eu/timepit/refined/pureconfig/SpecDerivedInstances.scala b/modules/pureconfig/shared/src/test/scala-3.0+/eu/timepit/refined/pureconfig/SpecDerivedInstances.scala deleted file mode 100644 index f44ddcb15..000000000 --- a/modules/pureconfig/shared/src/test/scala-3.0+/eu/timepit/refined/pureconfig/SpecDerivedInstances.scala +++ /dev/null @@ -1,19 +0,0 @@ -package eu.timepit.refined.pureconfig - -import eu.timepit.refined.types.numeric.PosInt -import _root_.pureconfig.{ConfigReader, ConfigWriter} -import _root_.pureconfig.generic.derivation.default.derived -import com.typesafe.config.ConfigValueFactory - -import scala.jdk.CollectionConverters.* - -trait SpecDerivedInstances { - - given ConfigReader[Config] = ConfigReader.derived - // we only care that this part of config writer (refined type) compiles - private val posIntConfWriter: ConfigWriter[PosInt] = summon - // there's no builtin generic derivation of ConfigWriter[ADT] for scala 3 yet - given ConfigWriter[Config] = posIntConfWriter - .contramap[Config](_.value) - .mapConfig(cfg => ConfigValueFactory.fromMap(Map("value" -> cfg).asJava)) -} diff --git a/modules/pureconfig/shared/src/test/scala-3.0-/eu/timepit/refined/pureconfig/SpecDerivedInstances.scala b/modules/pureconfig/shared/src/test/scala-3.0-/eu/timepit/refined/pureconfig/SpecDerivedInstances.scala deleted file mode 100644 index 05bffed1c..000000000 --- a/modules/pureconfig/shared/src/test/scala-3.0-/eu/timepit/refined/pureconfig/SpecDerivedInstances.scala +++ /dev/null @@ -1,10 +0,0 @@ -package eu.timepit.refined.pureconfig - -import _root_.pureconfig.{ConfigReader, ConfigWriter} -import _root_.pureconfig.generic.auto - -trait SpecDerivedInstances { - - implicit val configReader: ConfigReader[Config] = auto.exportReader.instance - implicit val configWriter: ConfigWriter[Config] = auto.exportWriter.instance -} diff --git a/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala b/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala index 6571f1c34..b7dcc4930 100644 --- a/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala +++ b/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala @@ -1,5 +1,6 @@ package eu.timepit.refined.pureconfig +import com.typesafe.config.ConfigValueFactory import com.typesafe.config.{ConfigOriginFactory, ConfigValueType} import eu.timepit.refined.types.numeric.PosInt import org.scalacheck.Prop._ @@ -7,9 +8,30 @@ import org.scalacheck.Properties import pureconfig._ import pureconfig.error.{CannotConvert, ConfigReaderFailures, ConvertFailure, WrongType} -class RefTypeConfigConvertSpec - extends Properties("RefTypeConfigConvert") - with SpecDerivedInstances { +import scala.jdk.CollectionConverters._ + +class RefTypeConfigConvertSpec extends Properties("RefTypeConfigConvert") { + + // our main concern is that refined instances are provided and three lines below compile + val posIntReader: ConfigReader[PosInt] = implicitly + val posIntWriter: ConfigWriter[PosInt] = implicitly + val posIntConvert: ConfigConvert[PosInt] = implicitly + + case class Config(value: PosInt) + + // manually composing instances not to depend on pureconfig.generic or scala3 derivations + implicit val reader: ConfigReader[Config] = new ConfigReader[Config] { + override def from(cur: ConfigCursor): ConfigReader.Result[Config] = for { + obj <- cur.asObjectCursor + value <- obj.atKey("value") + posInt <- posIntReader.from(value) + } yield Config(posInt) + } + + implicit val writer: ConfigWriter[Config] = + posIntWriter + .contramap[Config](_.value) + .mapConfig(cfg => ConfigValueFactory.fromMap(Map("value" -> cfg).asJava)) property("load success") = secure { loadConfigWithValue("1") ?= From ec8bc8452df361b7af6ca1b084db87dcbb8f89dd Mon Sep 17 00:00:00 2001 From: Ivan Klass Date: Sat, 11 Mar 2023 11:11:01 +0100 Subject: [PATCH 4/8] remove separate file for example Config --- .../src/test/scala/eu/timepit/refined/pureconfig/Config.scala | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/Config.scala diff --git a/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/Config.scala b/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/Config.scala deleted file mode 100644 index 0637cd16d..000000000 --- a/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/Config.scala +++ /dev/null @@ -1,4 +0,0 @@ -package eu.timepit.refined.pureconfig - -import eu.timepit.refined.types.numeric.PosInt -case class Config(value: PosInt) From a11c96ad22724c8d2f387fb285ebd2b5e79d1b84 Mon Sep 17 00:00:00 2001 From: Ivan Klass Date: Sat, 11 Mar 2023 11:14:24 +0100 Subject: [PATCH 5/8] Yet another type description form for scala3 --- .../refined/pureconfig/RefTypeConfigConvertSpec.scala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala b/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala index b7dcc4930..79858f45e 100644 --- a/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala +++ b/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala @@ -68,11 +68,15 @@ class RefTypeConfigConvertSpec extends Properties("RefTypeConfigConvert") { val expected3 = expectedFailure( "eu.timepit.refined.api.Refined[scala.Int, eu.timepit.refined.numeric.Greater[shapeless.nat._0]]" ) + val expected4 = expectedFailure( + "eu.timepit.refined.api.Refined$package.Refined[scala.Int, eu.timepit.refined.numeric.Greater[shapeless.nat._0]" + ) val actual = loadConfigWithValue("0") (actual ?= expected1) || (actual ?= expected2) || - (actual ?= expected3) + (actual ?= expected3) || + (actual ?= expected4) } property("load failure (wrong type)") = secure { From bb055319cbee0544d0f7f700420a3b6319d3dd9b Mon Sep 17 00:00:00 2001 From: Ivan Klass Date: Sat, 11 Mar 2023 14:21:51 +0100 Subject: [PATCH 6/8] fix test typo (missing closing ]) --- .../timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala b/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala index 79858f45e..6a958016a 100644 --- a/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala +++ b/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala @@ -69,7 +69,7 @@ class RefTypeConfigConvertSpec extends Properties("RefTypeConfigConvert") { "eu.timepit.refined.api.Refined[scala.Int, eu.timepit.refined.numeric.Greater[shapeless.nat._0]]" ) val expected4 = expectedFailure( - "eu.timepit.refined.api.Refined$package.Refined[scala.Int, eu.timepit.refined.numeric.Greater[shapeless.nat._0]" + "eu.timepit.refined.api.Refined$package.Refined[scala.Int, eu.timepit.refined.numeric.Greater[shapeless.nat._0]]" ) val actual = loadConfigWithValue("0") From d0a71affd77d0ecc799909fef6758325be820bca Mon Sep 17 00:00:00 2001 From: Ivan Klass Date: Sat, 11 Mar 2023 14:39:27 +0100 Subject: [PATCH 7/8] fix for old scala - not using jdk converters --- .../refined/pureconfig/RefTypeConfigConvertSpec.scala | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala b/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala index 6a958016a..f0a842ae8 100644 --- a/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala +++ b/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala @@ -8,7 +8,7 @@ import org.scalacheck.Properties import pureconfig._ import pureconfig.error.{CannotConvert, ConfigReaderFailures, ConvertFailure, WrongType} -import scala.jdk.CollectionConverters._ +import java.util.{HashMap => JMap} class RefTypeConfigConvertSpec extends Properties("RefTypeConfigConvert") { @@ -31,7 +31,13 @@ class RefTypeConfigConvertSpec extends Properties("RefTypeConfigConvert") { implicit val writer: ConfigWriter[Config] = posIntWriter .contramap[Config](_.value) - .mapConfig(cfg => ConfigValueFactory.fromMap(Map("value" -> cfg).asJava)) + .mapConfig(cfg => + ConfigValueFactory.fromMap { + val m = new JMap[String, Any]() + m.put("value", cfg) + m + } + ) property("load success") = secure { loadConfigWithValue("1") ?= From ae47168e365d50ee2e54b5b9eb6a77865f211956 Mon Sep 17 00:00:00 2001 From: Ivan Klass Date: Sat, 11 Mar 2023 15:42:42 +0100 Subject: [PATCH 8/8] fix false-positive missing interpolator [scala/bug/issues/10217] for 2.12 --- .../timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala b/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala index f0a842ae8..d44b55ff1 100644 --- a/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala +++ b/modules/pureconfig/shared/src/test/scala/eu/timepit/refined/pureconfig/RefTypeConfigConvertSpec.scala @@ -75,7 +75,7 @@ class RefTypeConfigConvertSpec extends Properties("RefTypeConfigConvert") { "eu.timepit.refined.api.Refined[scala.Int, eu.timepit.refined.numeric.Greater[shapeless.nat._0]]" ) val expected4 = expectedFailure( - "eu.timepit.refined.api.Refined$package.Refined[scala.Int, eu.timepit.refined.numeric.Greater[shapeless.nat._0]]" + s"eu.timepit.refined.api.Refined$$package.Refined[scala.Int, eu.timepit.refined.numeric.Greater[shapeless.nat._0]]" ) val actual = loadConfigWithValue("0")