From da21b10e7bae5a9ba4614d0d359e1861aa53a45d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rui=20Gonc=CC=A7alves?= Date: Fri, 3 Jul 2020 21:14:35 +0100 Subject: [PATCH 1/5] Replace scalariform with scalafmt --- .scalafmt.conf | 4 ++++ build.sbt | 6 +----- project/plugins.sbt | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 .scalafmt.conf diff --git a/.scalafmt.conf b/.scalafmt.conf new file mode 100644 index 000000000..31920862d --- /dev/null +++ b/.scalafmt.conf @@ -0,0 +1,4 @@ +version = 2.6.2 + +align.preset = none +maxColumn = 120 diff --git a/build.sbt b/build.sbt index 613d6647b..d1443fcd1 100644 --- a/build.sbt +++ b/build.sbt @@ -1,7 +1,6 @@ import Dependencies.Version._ import Utilities._ import sbtrelease.ReleasePlugin.autoImport.ReleaseTransformations._ -import scalariform.formatter.preferences._ organization in ThisBuild := "com.github.pureconfig" @@ -101,10 +100,7 @@ lazy val commonSettings = Seq( scalacOptions in (Test, console) := (scalacOptions in (Compile, console)).value, scalacOptions in Tut --= Seq("-Ywarn-unused-import", "-Xmacro-settings:materialize-derivations"), - scalariformPreferences := scalariformPreferences.value - .setPreference(DanglingCloseParenthesis, Prevent) - .setPreference(DoubleIndentConstructorArguments, true) - .setPreference(SpacesAroundMultiImports, true), + scalafmtOnCompile := true, autoAPIMappings := true, diff --git a/project/plugins.sbt b/project/plugins.sbt index 21210c387..94ced9faf 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -4,7 +4,7 @@ addSbtPlugin("com.jsuereth" % "sbt-pgp" % "2.0.1") addSbtPlugin("com.typesafe.sbt" % "sbt-osgi" % "0.9.5") addSbtPlugin("io.spray" % "sbt-boilerplate" % "0.6.1") addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.2.3") -addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.8.3") +addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.0") addSbtPlugin("org.scoverage" % "sbt-coveralls" % "1.2.7") addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.6.1") addSbtPlugin("org.tpolecat" % "tut-plugin" % "0.6.13") From c0b68e1a8da8ec65e1ce3bbbd60672566d3dac17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rui=20Gonc=CC=A7alves?= Date: Tue, 7 Jul 2020 21:53:45 +0100 Subject: [PATCH 2/5] Reformat all files --- .../pureconfig/FactoryCompat.scala | 19 +- .../main/scala/pureconfig/BasicReaders.scala | 87 +-- .../main/scala/pureconfig/BasicWriters.scala | 66 +-- .../scala/pureconfig/CollectionReaders.scala | 55 +- .../scala/pureconfig/CollectionWriters.scala | 52 +- .../main/scala/pureconfig/ConfigConvert.scala | 64 ++- .../main/scala/pureconfig/ConfigCursor.scala | 501 +++++++++--------- .../scala/pureconfig/ConfigFieldMapping.scala | 61 +-- .../main/scala/pureconfig/ConfigReader.scala | 184 +++---- .../main/scala/pureconfig/ConfigSource.scala | 396 +++++++------- .../main/scala/pureconfig/ConfigWriter.scala | 120 +++-- .../scala/pureconfig/ConvertHelpers.scala | 38 +- .../main/scala/pureconfig/DurationUtils.scala | 79 +-- core/src/main/scala/pureconfig/Exported.scala | 16 +- .../scala/pureconfig/ExportedReaders.scala | 10 +- .../scala/pureconfig/ExportedWriters.scala | 10 +- .../scala/pureconfig/FluentConfigCursor.scala | 163 +++--- .../scala/pureconfig/NamingConvention.scala | 31 +- .../main/scala/pureconfig/PeriodUtils.scala | 12 +- .../backend/ConfigFactoryWrapper.scala | 19 +- .../pureconfig/backend/ConfigWrapper.scala | 6 +- .../scala/pureconfig/backend/ErrorUtil.scala | 20 +- .../scala/pureconfig/backend/PathUtil.scala | 26 +- .../pureconfig/configurable/package.scala | 67 ++- .../error/ConfigReaderException.scala | 3 +- .../error/ConfigReaderFailure.scala | 129 ++--- .../error/ConfigReaderFailures.scala | 8 +- .../pureconfig/error/FailureReason.scala | 122 ++--- core/src/main/scala/pureconfig/package.scala | 312 ++++++----- .../scala/pureconfig/syntax/package.scala | 15 +- .../main/scala/pureconfig/Derivation.scala | 93 ++-- .../derivation/LazyContextParser.scala | 88 +-- .../pureconfig/derivation/MacroCompat.scala | 25 +- .../scala/pureconfig/DerivationChecks.scala | 16 +- .../pureconfig/module/akka/package.scala | 4 +- .../module/catseffect/package.scala | 322 ++++++----- .../module/catseffect/syntax/package.scala | 8 +- .../module/catseffect/CatsEffectSuite.scala | 10 +- .../module/cats/EmptyTraversableFound.scala | 8 +- .../module/cats/instances/package.scala | 8 +- .../pureconfig/module/cats/package.scala | 27 +- .../module/cats/syntax/package.scala | 10 +- .../module/cats/CatsLawsSuite.scala | 9 +- .../pureconfig/module/cats/CatsSuite.scala | 26 +- .../module/cats/arbitrary/package.scala | 53 +- .../pureconfig/module/cats/eq/package.scala | 19 +- .../pureconfig/module/circe/package.scala | 14 +- .../pureconfig/module/circe/CirceSuite.scala | 3 +- .../pureconfig/module/cron4s/package.scala | 6 +- .../module/cron4s/Cron4sSuite.scala | 8 +- .../pureconfig/module/enum/package.scala | 6 +- .../module/enumeratum/package.scala | 44 +- .../enumeratum/EnumeratumConvertTest.scala | 65 +-- .../scala/pureconfig/module/fs2/package.scala | 44 +- .../scala/pureconfig/module/fs2Suite.scala | 6 +- .../pureconfig/generic/CoproductHint.scala | 142 ++--- .../pureconfig/generic/ProductHint.scala | 101 ++-- .../error/CoproductHintException.scala | 8 +- .../error/InvalidCoproductOption.scala | 10 +- .../error/NoValidCoproductOptionFound.scala | 35 +- ...UnexpectedValueForFieldCoproductHint.scala | 12 +- .../generic/CoproductConfigWriter.scala | 46 +- .../generic/CoproductReaderOptions.scala | 14 +- .../generic/DerivedConfigReader.scala | 168 +++--- .../generic/DerivedConfigWriter.scala | 67 +-- .../generic/EnumCoproductHint.scala | 29 +- .../EnumerationConfigReaderBuilder.scala | 41 +- .../EnumerationConfigWriterBuilder.scala | 39 +- .../pureconfig/generic/ExportMacros.scala | 10 +- .../pureconfig/generic/MapShapedReader.scala | 40 +- .../pureconfig/generic/MapShapedWriter.scala | 31 +- .../pureconfig/generic/SeqShapedReader.scala | 14 +- .../pureconfig/generic/SeqShapedWriter.scala | 17 +- .../main/scala/pureconfig/generic/auto.scala | 6 +- .../main/scala/pureconfig/generic/hlist.scala | 6 +- .../scala/pureconfig/generic/semiauto.scala | 90 ++-- .../pureconfig/module/hadoop/package.scala | 9 +- .../module/hadoop/HadoopModuleTest.scala | 6 +- .../pureconfig/module/http4s/package.scala | 7 +- .../pureconfig/module/http4s/Http4sTest.scala | 11 +- .../pureconfig/module/javax/package.scala | 6 +- .../module/joda/configurable/package.scala | 47 +- .../pureconfig/module/joda/package.scala | 20 +- .../pureconfig/module/joda/JodaSuite.scala | 5 +- .../module/joda/arbitrary/package.scala | 73 +-- .../joda/configurable/ConfigurableSuite.scala | 2 +- .../magnolia/MagnoliaConfigReader.scala | 140 ++--- .../magnolia/MagnoliaConfigWriter.scala | 59 ++- .../module/magnolia/auto/reader.scala | 12 +- .../module/magnolia/auto/writer.scala | 12 +- .../module/magnolia/semiauto/reader.scala | 8 +- .../module/magnolia/semiauto/writer.scala | 8 +- .../magnolia/CoproductConvertersSuite.scala | 4 +- .../module/magnolia/CoproductHintSuite.scala | 33 +- .../magnolia/DerivationModesSuite.scala | 12 +- .../magnolia/ProductConvertersSuite.scala | 30 +- .../module/magnolia/ProductHintSuite.scala | 35 +- .../magnolia/TupleConvertersSuite.scala | 26 +- .../pureconfig/module/scalaxml/package.scala | 8 +- .../module/scalaxml/ScalaXMLSuite.scala | 3 +- .../module/scalaz/instances/package.scala | 12 +- .../pureconfig/module/scalaz/package.scala | 6 +- .../module/scalaz/syntax/package.scala | 15 +- .../module/scalaz/ScalazLawsSuite.scala | 4 +- .../module/scalaz/ScalazSuite.scala | 4 +- .../module/scalaz/arbitrary/package.scala | 29 +- .../module/scalaz/equal/package.scala | 4 +- .../pureconfig/module/squants/package.scala | 22 +- .../module/squants/SquantsConvertTest.scala | 6 +- .../module/squants/arbitrary/package.scala | 50 +- .../pureconfig/module/sttp/package.scala | 2 +- .../pureconfig/module/sttp/SttpSuite.scala | 10 +- .../module/yaml/YamlConfigSource.scala | 190 +++---- .../pureconfig/module/yaml/package.scala | 162 +++--- .../pureconfig/module/yaml/YamlApiSuite.scala | 276 +++++----- .../module/yaml/YamlConfigSourceSuite.scala | 305 ++++++----- .../src/main/scala/pureconfig/BaseSuite.scala | 12 +- .../pureconfig/ConfigConvertChecks.scala | 130 +++-- .../pureconfig/ConfigReaderMatchers.scala | 15 +- .../src/main/scala/pureconfig/PathUtils.scala | 2 +- .../scala/pureconfig/arbitrary/package.scala | 2 +- .../scala/pureconfig/data/Percentage.scala | 2 +- .../scala/pureconfig/equality/package.scala | 18 +- .../main/scala/pureconfig/gen/package.scala | 17 +- .../src/test/scala/pureconfig/ApiSuite.scala | 95 +++- .../pureconfig/BasicConvertersSuite.scala | 86 +-- .../CollectionConvertersSuite.scala | 26 +- .../scala/pureconfig/ConfigConvertSuite.scala | 19 +- .../scala/pureconfig/ConfigCursorSuite.scala | 133 +++-- .../pureconfig/ConfigFieldMappingSuite.scala | 3 +- .../ConfigReaderExceptionSuite.scala | 58 +- .../ConfigReaderFailureOriginSuite.scala | 42 +- ...ConfigReaderFailuresPrettyPrintSuite.scala | 62 ++- .../scala/pureconfig/ConfigReaderSuite.scala | 30 +- .../scala/pureconfig/ConfigSourceSuite.scala | 138 +++-- .../scala/pureconfig/ConfigWriterSuite.scala | 2 +- .../pureconfig/CoproductConvertersSuite.scala | 4 +- .../scala/pureconfig/CoproductHintSuite.scala | 33 +- .../pureconfig/DerivationModesSuite.scala | 12 +- .../scala/pureconfig/DerivationSuite.scala | 41 +- .../scala/pureconfig/DurationUtilsSuite.scala | 5 +- .../scala/pureconfig/EnumerationsSuite.scala | 11 +- .../pureconfig/FluentConfigCursorSuite.scala | 54 +- .../pureconfig/ProductConvertersSuite.scala | 30 +- .../scala/pureconfig/ProductHintSuite.scala | 37 +- .../pureconfig/TupleConvertersSuite.scala | 26 +- .../backend/ConfigFactoryWrapperSpec.scala | 8 +- .../scala/pureconfig/syntax/SyntaxSpec.scala | 2 +- 148 files changed, 4065 insertions(+), 3302 deletions(-) diff --git a/core/src/main/scala-2.12-/pureconfig/FactoryCompat.scala b/core/src/main/scala-2.12-/pureconfig/FactoryCompat.scala index e295d14e5..ccb24bf06 100644 --- a/core/src/main/scala-2.12-/pureconfig/FactoryCompat.scala +++ b/core/src/main/scala-2.12-/pureconfig/FactoryCompat.scala @@ -4,18 +4,19 @@ import scala.collection.generic.CanBuildFrom import scala.collection.mutable /** - * A compatibility layer for creating `CanBuildFrom`-like generic methods that work both on Scala 2.13 and pre-2.13 - * versions. - * - * @tparam A the type of elements that get added to the builder - * @tparam C the type of collection that it produces - */ + * A compatibility layer for creating `CanBuildFrom`-like generic methods that work both on Scala 2.13 and pre-2.13 + * versions. + * + * @tparam A the type of elements that get added to the builder + * @tparam C the type of collection that it produces + */ trait FactoryCompat[-A, +C] { def newBuilder(): mutable.Builder[A, C] } object FactoryCompat { - implicit def fromCanBuildFrom[From, Elem, To](implicit cbf: CanBuildFrom[From, Elem, To]): FactoryCompat[Elem, To] = new FactoryCompat[Elem, To] { - override def newBuilder() = cbf() - } + implicit def fromCanBuildFrom[From, Elem, To](implicit cbf: CanBuildFrom[From, Elem, To]): FactoryCompat[Elem, To] = + new FactoryCompat[Elem, To] { + override def newBuilder() = cbf() + } } diff --git a/core/src/main/scala/pureconfig/BasicReaders.scala b/core/src/main/scala/pureconfig/BasicReaders.scala index 6d9564015..b2878cb22 100644 --- a/core/src/main/scala/pureconfig/BasicReaders.scala +++ b/core/src/main/scala/pureconfig/BasicReaders.scala @@ -1,16 +1,16 @@ package pureconfig import java.io.File -import java.math.{ BigInteger, BigDecimal => JavaBigDecimal } -import java.net.{ URI, URL } -import java.nio.file.{ Path, Paths } +import java.math.{BigInteger, BigDecimal => JavaBigDecimal} +import java.net.{URI, URL} +import java.nio.file.{Path, Paths} import java.time._ -import java.time.{ Duration => JavaDuration } +import java.time.{Duration => JavaDuration} import java.util.UUID import java.util.regex.Pattern -import scala.concurrent.duration.{ Duration, FiniteDuration } -import scala.math.{ BigDecimal, BigInt } +import scala.concurrent.duration.{Duration, FiniteDuration} +import scala.math.{BigDecimal, BigInt} import scala.reflect.ClassTag import scala.util.matching.Regex import com.typesafe.config._ @@ -20,8 +20,8 @@ import pureconfig.error._ import scala.util.Try /** - * Trait containing `ConfigReader` instances for primitive types. - */ + * Trait containing `ConfigReader` instances for primitive types. + */ trait PrimitiveReaders { implicit val stringConfigReader = ConfigReader.fromCursor(_.asString) @@ -30,7 +30,8 @@ trait PrimitiveReaders { s.size match { case 1 => Right(s.charAt(0)) case len => Left(WrongSizeString(1, len)) - }) + } + ) implicit val booleanConfigReader = ConfigReader.fromCursor(_.asBoolean) @@ -40,8 +41,7 @@ trait PrimitiveReaders { case v => v.toDouble }) - cur.asString.right.flatMap(s => cur.scopeFailure(asStringReader(s))) - .left.flatMap(_ => cur.asDouble) + cur.asString.right.flatMap(s => cur.scopeFailure(asStringReader(s))).left.flatMap(_ => cur.asDouble) }) implicit val floatConfigReader = ConfigReader.fromCursor({ cur => @@ -50,8 +50,7 @@ trait PrimitiveReaders { case v => v.toFloat }) - cur.asString.right.flatMap(s => cur.scopeFailure(asStringReader(s))) - .left.flatMap(_ => cur.asFloat) + cur.asString.right.flatMap(s => cur.scopeFailure(asStringReader(s))).left.flatMap(_ => cur.asFloat) }) implicit val intConfigReader = ConfigReader.fromCursor(_.asInt) @@ -64,8 +63,8 @@ trait PrimitiveReaders { } /** - * Trait containing `ConfigReader` instance for Java Enums. - */ + * Trait containing `ConfigReader` instance for Java Enums. + */ trait JavaEnumReader { implicit def javaEnumReader[A <: Enum[A]](implicit tag: ClassTag[A]): ConfigReader[A] = @@ -76,8 +75,8 @@ trait JavaEnumReader { } /** - * Trait containing `ConfigReader` instances for classes related to file system paths and URIs. - */ + * Trait containing `ConfigReader` instances for classes related to file system paths and URIs. + */ trait UriAndPathReaders { implicit val urlConfigReader = ConfigReader.fromNonEmptyString[URL](catchReadError(new URL(_))) @@ -88,8 +87,8 @@ trait UriAndPathReaders { } /** - * Trait containing `ConfigReader` instances for classes related to regular expressions. - */ + * Trait containing `ConfigReader` instances for classes related to regular expressions. + */ trait RegexReaders { implicit val patternReader = ConfigReader.fromString[Pattern](catchReadError(Pattern.compile)) @@ -97,8 +96,8 @@ trait RegexReaders { } /** - * Trait containing `ConfigReader` instances for `java.time` classes. - */ + * Trait containing `ConfigReader` instances for `java.time` classes. + */ trait JavaTimeReaders { implicit val instantConfigReader: ConfigReader[Instant] = @@ -121,9 +120,9 @@ trait JavaTimeReaders { } /** - * Trait containing `ConfigReader` instances for [[scala.concurrent.duration.Duration]] and - * [[scala.concurrent.duration.FiniteDuration]]. - */ + * Trait containing `ConfigReader` instances for [[scala.concurrent.duration.Duration]] and + * [[scala.concurrent.duration.FiniteDuration]]. + */ trait DurationReaders { implicit val durationConfigReader: ConfigReader[Duration] = @@ -133,8 +132,14 @@ trait DurationReaders { val fromString: String => Either[FailureReason, FiniteDuration] = { string => DurationUtils.fromString(string).right.flatMap { case d: FiniteDuration => Right(d) - case _ => Left(CannotConvert(string, "FiniteDuration", - s"Couldn't parse '$string' into a FiniteDuration because it's infinite.")) + case _ => + Left( + CannotConvert( + string, + "FiniteDuration", + s"Couldn't parse '$string' into a FiniteDuration because it's infinite." + ) + ) } } ConfigReader.fromNonEmptyString[FiniteDuration](fromString) @@ -142,8 +147,8 @@ trait DurationReaders { } /** - * Trait containing `ConfigReader` instances for Java and Scala arbitrary-precision numeric types. - */ + * Trait containing `ConfigReader` instances for Java and Scala arbitrary-precision numeric types. + */ trait NumericReaders { implicit val javaBigIntegerReader: ConfigReader[BigInteger] = @@ -160,8 +165,8 @@ trait NumericReaders { } /** - * Trait containing `ConfigReader` instances for Typesafe config models. - */ + * Trait containing `ConfigReader` instances for Typesafe config models. + */ trait TypesafeConfigReaders { implicit val configConfigReader: ConfigReader[Config] = @@ -192,17 +197,17 @@ trait TypesafeConfigReaders { } /** - * Trait containing `ConfigReader` instances for primitive types and simple classes in Java and Scala standard - * libraries. - */ + * Trait containing `ConfigReader` instances for primitive types and simple classes in Java and Scala standard + * libraries. + */ trait BasicReaders - extends PrimitiveReaders - with JavaEnumReader - with UriAndPathReaders - with RegexReaders - with JavaTimeReaders - with DurationReaders - with NumericReaders - with TypesafeConfigReaders + extends PrimitiveReaders + with JavaEnumReader + with UriAndPathReaders + with RegexReaders + with JavaTimeReaders + with DurationReaders + with NumericReaders + with TypesafeConfigReaders object BasicReaders extends BasicReaders diff --git a/core/src/main/scala/pureconfig/BasicWriters.scala b/core/src/main/scala/pureconfig/BasicWriters.scala index 148195ed7..0302e13a2 100644 --- a/core/src/main/scala/pureconfig/BasicWriters.scala +++ b/core/src/main/scala/pureconfig/BasicWriters.scala @@ -1,23 +1,23 @@ package pureconfig import java.io.File -import java.math.{ BigDecimal => JavaBigDecimal, BigInteger } -import java.net.{ URI, URL } +import java.math.{BigDecimal => JavaBigDecimal, BigInteger} +import java.net.{URI, URL} import java.nio.file.Path import java.time._ -import java.time.{ Duration => JavaDuration } +import java.time.{Duration => JavaDuration} import java.util.UUID import java.util.regex.Pattern -import scala.concurrent.duration.{ Duration, FiniteDuration } -import scala.math.{ BigDecimal, BigInt } +import scala.concurrent.duration.{Duration, FiniteDuration} +import scala.math.{BigDecimal, BigInt} import scala.util.matching.Regex import com.typesafe.config._ /** - * Trait containing `ConfigWriter` instances for primitive types. - */ + * Trait containing `ConfigWriter` instances for primitive types. + */ trait PrimitiveWriters { implicit val stringConfigWriter = ConfigWriter.forPrimitive[String] @@ -32,16 +32,16 @@ trait PrimitiveWriters { } /** - * Trait containing instance for `ConfigWriter` for Java Enum. - */ + * Trait containing instance for `ConfigWriter` for Java Enum. + */ trait JavaEnumWriter { implicit def javaEnumWriter[A <: Enum[A]]: ConfigWriter[A] = ConfigWriter.toDefaultString[A] } /** - * Trait containing `ConfigWriter` instances for classes related to file system paths and URIs. - */ + * Trait containing `ConfigWriter` instances for classes related to file system paths and URIs. + */ trait UriAndPathWriters { implicit val urlConfigWriter = ConfigWriter.toDefaultString[URL] @@ -52,8 +52,8 @@ trait UriAndPathWriters { } /** - * Trait containing `ConfigWriter` instances for classes related to regular expressions. - */ + * Trait containing `ConfigWriter` instances for classes related to regular expressions. + */ trait RegexWriters { implicit val patternWriter = ConfigWriter.toString[Pattern](_.pattern) @@ -61,8 +61,8 @@ trait RegexWriters { } /** - * Trait containing `ConfigWriter` instances for `java.time` classes. - */ + * Trait containing `ConfigWriter` instances for `java.time` classes. + */ trait JavaTimeWriters { implicit val instantConfigWriter = ConfigWriter.toDefaultString[Instant] @@ -79,9 +79,9 @@ trait JavaTimeWriters { } /** - * Trait containing `ConfigWriter` instances for [[scala.concurrent.duration.Duration]] and - * [[scala.concurrent.duration.FiniteDuration]]. - */ + * Trait containing `ConfigWriter` instances for [[scala.concurrent.duration.Duration]] and + * [[scala.concurrent.duration.FiniteDuration]]. + */ trait DurationWriters { implicit val durationConfigWriter = ConfigWriter.toString[Duration](DurationUtils.fromDuration) @@ -89,8 +89,8 @@ trait DurationWriters { } /** - * Trait containing `ConfigWriter` instances for Java and Scala arbitrary-precision numeric types. - */ + * Trait containing `ConfigWriter` instances for Java and Scala arbitrary-precision numeric types. + */ trait NumericWriters { implicit val javaBigDecimalWriter: ConfigWriter[JavaBigDecimal] = ConfigWriter.toDefaultString[JavaBigDecimal] @@ -100,8 +100,8 @@ trait NumericWriters { } /** - * Trait containing `ConfigWriter` instances for Typesafe config models. - */ + * Trait containing `ConfigWriter` instances for Typesafe config models. + */ trait TypesafeConfigWriters { implicit val configConfigWriter: ConfigWriter[Config] = new ConfigWriter[Config] { @@ -126,17 +126,17 @@ trait TypesafeConfigWriters { } /** - * Trait containing `ConfigWriter` instances for primitive types and simple classes in Java and Scala standard - * libraries. - */ + * Trait containing `ConfigWriter` instances for primitive types and simple classes in Java and Scala standard + * libraries. + */ trait BasicWriters - extends PrimitiveWriters - with JavaEnumWriter - with UriAndPathWriters - with RegexWriters - with JavaTimeWriters - with DurationWriters - with NumericWriters - with TypesafeConfigWriters + extends PrimitiveWriters + with JavaEnumWriter + with UriAndPathWriters + with RegexWriters + with JavaTimeWriters + with DurationWriters + with NumericWriters + with TypesafeConfigWriters object BasicWriters extends BasicWriters diff --git a/core/src/main/scala/pureconfig/CollectionReaders.scala b/core/src/main/scala/pureconfig/CollectionReaders.scala index 34d519282..504a97453 100644 --- a/core/src/main/scala/pureconfig/CollectionReaders.scala +++ b/core/src/main/scala/pureconfig/CollectionReaders.scala @@ -4,17 +4,17 @@ import scala.language.higherKinds import scala.reflect.ClassTag /** - * A marker trait signaling that a `ConfigReader` accepts missing (undefined) values. - * - * The standard behavior of `ConfigReader`s that expect required keys in config objects is to return a `KeyNotFound` - * failure when one or more of them are missing. Mixing in this trait into the key's `ConfigReader` signals that if - * a value is missing for the key, the `ConfigReader` can be called with a cursor in the "undefined" state. - */ + * A marker trait signaling that a `ConfigReader` accepts missing (undefined) values. + * + * The standard behavior of `ConfigReader`s that expect required keys in config objects is to return a `KeyNotFound` + * failure when one or more of them are missing. Mixing in this trait into the key's `ConfigReader` signals that if + * a value is missing for the key, the `ConfigReader` can be called with a cursor in the "undefined" state. + */ trait ReadsMissingKeys { this: ConfigReader[_] => } /** - * Trait containing `ConfigReader` instances for collection types. - */ + * Trait containing `ConfigReader` instances for collection types. + */ trait CollectionReaders { implicit def optionReader[A](implicit conv: Derivation[ConfigReader[A]]): ConfigReader[Option[A]] = @@ -25,29 +25,32 @@ trait CollectionReaders { } } - implicit def traversableReader[A, F[A] <: TraversableOnce[A]]( - implicit - configConvert: Derivation[ConfigReader[A]], - cbf: FactoryCompat[A, F[A]]) = new ConfigReader[F[A]] { - - override def from(cur: ConfigCursor): ConfigReader.Result[F[A]] = { - cur.fluent.mapList { valueCur => configConvert.value.from(valueCur) }.right.map { coll => - val builder = cbf.newBuilder() - (builder ++= coll).result() + implicit def traversableReader[A, F[A] <: TraversableOnce[A]](implicit + configConvert: Derivation[ConfigReader[A]], + cbf: FactoryCompat[A, F[A]] + ) = + new ConfigReader[F[A]] { + + override def from(cur: ConfigCursor): ConfigReader.Result[F[A]] = { + cur.fluent.mapList { valueCur => configConvert.value.from(valueCur) }.right.map { coll => + val builder = cbf.newBuilder() + (builder ++= coll).result() + } } } - } - implicit def mapReader[A](implicit reader: Derivation[ConfigReader[A]]) = new ConfigReader[Map[String, A]] { - override def from(cur: ConfigCursor): ConfigReader.Result[Map[String, A]] = { - cur.fluent.mapObject { valueCur => reader.value.from(valueCur) } + implicit def mapReader[A](implicit reader: Derivation[ConfigReader[A]]) = + new ConfigReader[Map[String, A]] { + override def from(cur: ConfigCursor): ConfigReader.Result[Map[String, A]] = { + cur.fluent.mapObject { valueCur => reader.value.from(valueCur) } + } } - } - implicit def arrayReader[A: ClassTag](implicit reader: Derivation[ConfigReader[A]]) = new ConfigReader[Array[A]] { - override def from(cur: ConfigCursor): ConfigReader.Result[Array[A]] = - cur.fluent.mapList(reader.value.from).right.map(_.toArray) - } + implicit def arrayReader[A: ClassTag](implicit reader: Derivation[ConfigReader[A]]) = + new ConfigReader[Array[A]] { + override def from(cur: ConfigCursor): ConfigReader.Result[Array[A]] = + cur.fluent.mapList(reader.value.from).right.map(_.toArray) + } } object CollectionReaders extends CollectionReaders diff --git a/core/src/main/scala/pureconfig/CollectionWriters.scala b/core/src/main/scala/pureconfig/CollectionWriters.scala index a307a0ad5..d62579321 100644 --- a/core/src/main/scala/pureconfig/CollectionWriters.scala +++ b/core/src/main/scala/pureconfig/CollectionWriters.scala @@ -6,49 +6,51 @@ import scala.language.higherKinds import com.typesafe.config._ /** - * A trait signaling that a `ConfigWriter` can write missing (undefined) values. - * - * `ConfigWriter`s always produce a valid `ConfigValue` with their `to` method. This trait adds an extra `toOpt` - * method that parent writers can use in order to decide whether or not they should write a value using this writer. - */ + * A trait signaling that a `ConfigWriter` can write missing (undefined) values. + * + * `ConfigWriter`s always produce a valid `ConfigValue` with their `to` method. This trait adds an extra `toOpt` + * method that parent writers can use in order to decide whether or not they should write a value using this writer. + */ trait WritesMissingKeys[A] { this: ConfigWriter[A] => def toOpt(a: A): Option[ConfigValue] } /** - * Trait containing `ConfigWriter` instances for collection types. - */ + * Trait containing `ConfigWriter` instances for collection types. + */ trait CollectionWriters { implicit def optionWriter[A](implicit conv: Derivation[ConfigWriter[A]]): ConfigWriter[Option[A]] = new ConfigWriter[Option[A]] with WritesMissingKeys[Option[A]] { - override def to(t: Option[A]): ConfigValue = t match { - case Some(v) => conv.value.to(v) - case None => ConfigValueFactory.fromAnyRef(null) - } + override def to(t: Option[A]): ConfigValue = + t match { + case Some(v) => conv.value.to(v) + case None => ConfigValueFactory.fromAnyRef(null) + } def toOpt(t: Option[A]): Option[ConfigValue] = t.map(conv.value.to) } - implicit def traversableWriter[A, F[A] <: TraversableOnce[A]]( - implicit - configConvert: Derivation[ConfigWriter[A]]) = new ConfigWriter[F[A]] { + implicit def traversableWriter[A, F[A] <: TraversableOnce[A]](implicit configConvert: Derivation[ConfigWriter[A]]) = + new ConfigWriter[F[A]] { - override def to(ts: F[A]): ConfigValue = { - ConfigValueFactory.fromIterable(ts.toList.map(configConvert.value.to).asJava) + override def to(ts: F[A]): ConfigValue = { + ConfigValueFactory.fromIterable(ts.toList.map(configConvert.value.to).asJava) + } } - } - implicit def mapWriter[A](implicit configConvert: Derivation[ConfigWriter[A]]) = new ConfigWriter[Map[String, A]] { - override def to(keyVals: Map[String, A]): ConfigValue = { - ConfigValueFactory.fromMap(keyVals.mapValues(configConvert.value.to).toMap.asJava) + implicit def mapWriter[A](implicit configConvert: Derivation[ConfigWriter[A]]) = + new ConfigWriter[Map[String, A]] { + override def to(keyVals: Map[String, A]): ConfigValue = { + ConfigValueFactory.fromMap(keyVals.mapValues(configConvert.value.to).toMap.asJava) + } } - } - implicit def arrayWriter[A](implicit writer: Derivation[ConfigWriter[A]]) = new ConfigWriter[Array[A]] { - override def to(a: Array[A]): ConfigValue = - ConfigValueFactory.fromIterable(a.toList.map(writer.value.to).asJava) - } + implicit def arrayWriter[A](implicit writer: Derivation[ConfigWriter[A]]) = + new ConfigWriter[Array[A]] { + override def to(a: Array[A]): ConfigValue = + ConfigValueFactory.fromIterable(a.toList.map(writer.value.to).asJava) + } } object CollectionWriters extends CollectionWriters diff --git a/core/src/main/scala/pureconfig/ConfigConvert.scala b/core/src/main/scala/pureconfig/ConfigConvert.scala index 8496fa81e..a0856ece9 100644 --- a/core/src/main/scala/pureconfig/ConfigConvert.scala +++ b/core/src/main/scala/pureconfig/ConfigConvert.scala @@ -2,8 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** - * @author Mario Pastorelli - */ + * @author Mario Pastorelli + */ package pureconfig import scala.reflect.ClassTag @@ -12,49 +12,53 @@ import scala.util.Try import pureconfig.error.FailureReason /** - * Trait for objects capable of reading and writing objects of a given type from and to `ConfigValues`. - */ + * Trait for objects capable of reading and writing objects of a given type from and to `ConfigValues`. + */ trait ConfigConvert[A] extends ConfigReader[A] with ConfigWriter[A] { outer => /** - * Transforms the values read and written by this `ConfigConvert` using two functions. - * - * @param f the function applied to values after they are read; a thrown exception will be caught and converted to a pureconfig FailureReason - * @param g the function applied to values before they are written - * @tparam B the type of the returned `ConfigConvert` - * @return a `ConfigConvert` that reads and writes values of type `B` by applying `f` and `g` on read and write, - * respectively. - */ + * Transforms the values read and written by this `ConfigConvert` using two functions. + * + * @param f the function applied to values after they are read; a thrown exception will be caught and converted to a pureconfig FailureReason + * @param g the function applied to values before they are written + * @tparam B the type of the returned `ConfigConvert` + * @return a `ConfigConvert` that reads and writes values of type `B` by applying `f` and `g` on read and write, + * respectively. + */ def xmap[B](f: A => B, g: B => A): ConfigConvert[B] = ConfigConvert(map(f), contramap(g)) /** - * Transforms the values read and written by this `ConfigConvert` using two functions where the reader may - * specify custom failure reason. - * - * @param f the function applied to values after they are read - * @param g the function applied to values before they are written - * @tparam B the type of the returned `ConfigConvert` - * @return a `ConfigConvert` that reads and writes values of type `B` by applying `f` and `g` on read and write, - * respectively. - */ + * Transforms the values read and written by this `ConfigConvert` using two functions where the reader may + * specify custom failure reason. + * + * @param f the function applied to values after they are read + * @param g the function applied to values before they are written + * @tparam B the type of the returned `ConfigConvert` + * @return a `ConfigConvert` that reads and writes values of type `B` by applying `f` and `g` on read and write, + * respectively. + */ def xemap[B](f: A => Either[FailureReason, B], g: B => A): ConfigConvert[B] = ConfigConvert(emap(f), contramap(g)) } /** - * Provides methods to create [[ConfigConvert]] instances. - */ + * Provides methods to create [[ConfigConvert]] instances. + */ object ConfigConvert extends ConvertHelpers { def apply[A](implicit conv: Derivation[ConfigConvert[A]]): ConfigConvert[A] = conv.value - def apply[A](reader: ConfigReader[A], writer: ConfigWriter[A]): ConfigConvert[A] = new ConfigConvert[A] { - def from(cur: ConfigCursor) = reader.from(cur) - def to(a: A) = writer.to(a) - } + def apply[A](reader: ConfigReader[A], writer: ConfigWriter[A]): ConfigConvert[A] = + new ConfigConvert[A] { + def from(cur: ConfigCursor) = reader.from(cur) + def to(a: A) = writer.to(a) + } - implicit def fromReaderAndWriter[A](implicit reader: Derivation[ConfigReader[A]], writer: Derivation[ConfigWriter[A]]) = + implicit def fromReaderAndWriter[A](implicit + reader: Derivation[ConfigReader[A]], + writer: Derivation[ConfigWriter[A]] + ) = ConfigConvert(reader.value, writer.value) def viaString[A](fromF: String => Either[FailureReason, A], toF: A => String): ConfigConvert[A] = @@ -68,7 +72,9 @@ object ConfigConvert extends ConvertHelpers { viaString[A](optF(fromF), toF) } - def viaNonEmptyString[A](fromF: String => Either[FailureReason, A], toF: A => String)(implicit ct: ClassTag[A]): ConfigConvert[A] = { + def viaNonEmptyString[A](fromF: String => Either[FailureReason, A], toF: A => String)(implicit + ct: ClassTag[A] + ): ConfigConvert[A] = { viaString[A](string => ensureNonEmpty(ct)(string).right.flatMap(s => fromF(s)), toF) } diff --git a/core/src/main/scala/pureconfig/ConfigCursor.scala b/core/src/main/scala/pureconfig/ConfigCursor.scala index 34e8dc84f..cd263fdeb 100644 --- a/core/src/main/scala/pureconfig/ConfigCursor.scala +++ b/core/src/main/scala/pureconfig/ConfigCursor.scala @@ -1,7 +1,7 @@ package pureconfig import scala.collection.JavaConverters._ -import scala.util.{ Failure, Success, Try } +import scala.util.{Failure, Success, Try} import com.typesafe.config.ConfigValueType._ import com.typesafe.config._ @@ -9,201 +9,219 @@ import pureconfig.backend.PathUtil import pureconfig.error._ /** - * A wrapper for a `ConfigValue` providing safe navigation through the config and holding positional data for better - * error handling. - */ + * A wrapper for a `ConfigValue` providing safe navigation through the config and holding positional data for better + * error handling. + */ sealed trait ConfigCursor { /** - * The `ConfigValue` to which this cursor points to. - */ + * The `ConfigValue` to which this cursor points to. + */ def value: ConfigValue /** - * The path in the config to which this cursor points as a list of keys in reverse order (deepest key first). - */ + * The path in the config to which this cursor points as a list of keys in reverse order (deepest key first). + */ def pathElems: List[String] /** - * The path in the config to which this cursor points. - */ + * The path in the config to which this cursor points. + */ def path: String = PathUtil.joinPath(pathElems.reverse) /** - * The file system location of the config to which this cursor points. - */ + * The file system location of the config to which this cursor points. + */ def origin: Option[ConfigOrigin] = Option(value).map(_.origin()) /** - * Returns whether this cursor points to an undefined value. A cursor can point to an undefined value when a missing - * config key is requested or when a `null` `ConfigValue` is provided, among other reasons. - * - * @return `true` if this cursor points to an undefined value, `false` otherwise. - */ + * Returns whether this cursor points to an undefined value. A cursor can point to an undefined value when a missing + * config key is requested or when a `null` `ConfigValue` is provided, among other reasons. + * + * @return `true` if this cursor points to an undefined value, `false` otherwise. + */ def isUndefined: Boolean = value == null /** - * Returns whether this cursor points to a `null` config value. An explicit `null` value is different than a missing - * value - `isUndefined` can be used to check for the latter. - * - * @return `true` if this cursor points to a `null` value, `false` otherwise. - */ + * Returns whether this cursor points to a `null` config value. An explicit `null` value is different than a missing + * value - `isUndefined` can be used to check for the latter. + * + * @return `true` if this cursor points to a `null` value, `false` otherwise. + */ def isNull: Boolean = value != null && value.unwrapped == null /** - * Casts this cursor to a string. - * - * @return a `Right` with the string value pointed to by this cursor if the cast can be done, `Left` with a list of - * failures otherwise. - */ + * Casts this cursor to a string. + * + * @return a `Right` with the string value pointed to by this cursor if the cast can be done, `Left` with a list of + * failures otherwise. + */ def asString: ConfigReader.Result[String] = castOrFail(STRING, v => Right(v.unwrapped.asInstanceOf[String])) /** - * Casts this cursor to a boolean. - * - * @return a `Right` with the boolean value pointed to by this cursor if the cast can be done, `Left` with a list of - * failures otherwise. - */ + * Casts this cursor to a boolean. + * + * @return a `Right` with the boolean value pointed to by this cursor if the cast can be done, `Left` with a list of + * failures otherwise. + */ def asBoolean: ConfigReader.Result[Boolean] = castOrFail(BOOLEAN, v => Right(v.unwrapped.asInstanceOf[Boolean])) /** - * Casts this cursor to a long. - * - * @return a `Right` with the long value pointed to by this cursor if the cast can be done, `Left` with a list of - * failures otherwise. - */ + * Casts this cursor to a long. + * + * @return a `Right` with the long value pointed to by this cursor if the cast can be done, `Left` with a list of + * failures otherwise. + */ def asLong: ConfigReader.Result[Long] = - castOrFail(NUMBER, _.unwrapped match { - case i: java.lang.Long => Right(i) - case i: java.lang.Integer => Right(i.longValue()) - case i: java.lang.Double if i.longValue().toDouble == i => Right(i.longValue()) - case v => Left(CannotConvert(v.toString, "Long", "Unable to convert Number to Long")) - }) - - /** - * Casts this cursor to an int. - * - * @return a `Right` with the int value pointed to by this cursor if the cast can be done, `Left` with a list of - * failures otherwise. - */ + castOrFail( + NUMBER, + _.unwrapped match { + case i: java.lang.Long => Right(i) + case i: java.lang.Integer => Right(i.longValue()) + case i: java.lang.Double if i.longValue().toDouble == i => Right(i.longValue()) + case v => Left(CannotConvert(v.toString, "Long", "Unable to convert Number to Long")) + } + ) + + /** + * Casts this cursor to an int. + * + * @return a `Right` with the int value pointed to by this cursor if the cast can be done, `Left` with a list of + * failures otherwise. + */ def asInt: ConfigReader.Result[Int] = - castOrFail(NUMBER, _.unwrapped match { - case i: java.lang.Long if i.intValue().toLong == i => Right(i.intValue()) - case i: java.lang.Integer => Right(i) - case i: java.lang.Double if i.intValue().toDouble == i => Right(i.intValue()) - case v => Left(CannotConvert(v.toString, "Int", "Unable to convert Number to Int")) - }) - - /** - * Casts this cursor to a short. - * - * @return a `Right` with the short value pointed to by this cursor if the cast can be done, `Left` with a list of - * failures otherwise. - */ + castOrFail( + NUMBER, + _.unwrapped match { + case i: java.lang.Long if i.intValue().toLong == i => Right(i.intValue()) + case i: java.lang.Integer => Right(i) + case i: java.lang.Double if i.intValue().toDouble == i => Right(i.intValue()) + case v => Left(CannotConvert(v.toString, "Int", "Unable to convert Number to Int")) + } + ) + + /** + * Casts this cursor to a short. + * + * @return a `Right` with the short value pointed to by this cursor if the cast can be done, `Left` with a list of + * failures otherwise. + */ def asShort: ConfigReader.Result[Short] = - castOrFail(NUMBER, _.unwrapped match { - case i: java.lang.Long if i.shortValue().toLong == i => Right(i.shortValue()) - case i: java.lang.Integer if i.shortValue().toInt == i => Right(i.shortValue()) - case i: java.lang.Double if i.shortValue().toDouble == i => Right(i.shortValue()) - case v => Left(CannotConvert(v.toString, "Short", "Unable to convert Number to Short")) - }) - - /** - * Casts this cursor to a byte. - * - * @return a `Right` with the byte value pointed to by this cursor if the cast can be done, `Left` with a list of - * failures otherwise. - */ + castOrFail( + NUMBER, + _.unwrapped match { + case i: java.lang.Long if i.shortValue().toLong == i => Right(i.shortValue()) + case i: java.lang.Integer if i.shortValue().toInt == i => Right(i.shortValue()) + case i: java.lang.Double if i.shortValue().toDouble == i => Right(i.shortValue()) + case v => Left(CannotConvert(v.toString, "Short", "Unable to convert Number to Short")) + } + ) + + /** + * Casts this cursor to a byte. + * + * @return a `Right` with the byte value pointed to by this cursor if the cast can be done, `Left` with a list of + * failures otherwise. + */ def asByte: ConfigReader.Result[Byte] = - castOrFail(NUMBER, _.unwrapped match { - case i: java.lang.Long if i.byteValue().toLong == i => Right(i.byteValue()) - case i: java.lang.Integer if i.byteValue().toInt == i => Right(i.byteValue()) - case i: java.lang.Double if i.byteValue().toDouble == i => Right(i.byteValue()) - case v => Left(CannotConvert(v.toString, "Byte", "Unable to convert Number to Byte")) - }) - - /** - * Casts this cursor to a double. - * - * @return a `Right` with the double value pointed to by this cursor if the cast can be done, `Left` with a list of - * failures otherwise. - */ + castOrFail( + NUMBER, + _.unwrapped match { + case i: java.lang.Long if i.byteValue().toLong == i => Right(i.byteValue()) + case i: java.lang.Integer if i.byteValue().toInt == i => Right(i.byteValue()) + case i: java.lang.Double if i.byteValue().toDouble == i => Right(i.byteValue()) + case v => Left(CannotConvert(v.toString, "Byte", "Unable to convert Number to Byte")) + } + ) + + /** + * Casts this cursor to a double. + * + * @return a `Right` with the double value pointed to by this cursor if the cast can be done, `Left` with a list of + * failures otherwise. + */ def asDouble: ConfigReader.Result[Double] = - castOrFail(NUMBER, _.unwrapped match { - case i: java.lang.Long if i.doubleValue().toLong == i => Right(i.doubleValue()) - case i: java.lang.Integer if i.doubleValue().toInt == i => Right(i.doubleValue()) - case i: java.lang.Double => Right(i) - case v => Left(CannotConvert(v.toString, "Double", "Unable to convert Number to Double")) - }) - - /** - * Casts this cursor to a float. - * - * @return a `Right` with the float value pointed to by this cursor if the cast can be done, `Left` with a list of - * failures otherwise. - */ + castOrFail( + NUMBER, + _.unwrapped match { + case i: java.lang.Long if i.doubleValue().toLong == i => Right(i.doubleValue()) + case i: java.lang.Integer if i.doubleValue().toInt == i => Right(i.doubleValue()) + case i: java.lang.Double => Right(i) + case v => Left(CannotConvert(v.toString, "Double", "Unable to convert Number to Double")) + } + ) + + /** + * Casts this cursor to a float. + * + * @return a `Right` with the float value pointed to by this cursor if the cast can be done, `Left` with a list of + * failures otherwise. + */ def asFloat: ConfigReader.Result[Float] = - castOrFail(NUMBER, _.unwrapped match { - case i: java.lang.Long if i.floatValue().toLong == i => Right(i.floatValue()) - case i: java.lang.Integer if i.floatValue().toInt == i => Right(i.floatValue()) - case i: java.lang.Double => Right(i.floatValue()) - case v => Left(CannotConvert(v.toString, "Float", "Unable to convert Number to Float")) - }) - - /** - * Casts this cursor to a `ConfigListCursor`. - * - * @return a `Right` with this cursor as a list cursor if the cast can be done, `Left` with a list of failures - * otherwise. - */ + castOrFail( + NUMBER, + _.unwrapped match { + case i: java.lang.Long if i.floatValue().toLong == i => Right(i.floatValue()) + case i: java.lang.Integer if i.floatValue().toInt == i => Right(i.floatValue()) + case i: java.lang.Double => Right(i.floatValue()) + case v => Left(CannotConvert(v.toString, "Float", "Unable to convert Number to Float")) + } + ) + + /** + * Casts this cursor to a `ConfigListCursor`. + * + * @return a `Right` with this cursor as a list cursor if the cast can be done, `Left` with a list of failures + * otherwise. + */ def asListCursor: ConfigReader.Result[ConfigListCursor] = castOrFail(LIST, v => Right(v.asInstanceOf[ConfigList])).right.map(ConfigListCursor(_, pathElems)) /** - * Casts this cursor to a list of cursors. - * - * @return a `Right` with the list pointed to by this cursor if the cast can be done, `Left` with a list of failures - * otherwise. - */ + * Casts this cursor to a list of cursors. + * + * @return a `Right` with the list pointed to by this cursor if the cast can be done, `Left` with a list of failures + * otherwise. + */ def asList: ConfigReader.Result[List[ConfigCursor]] = asListCursor.right.map(_.list) /** - * Casts this cursor to a `ConfigObjectCursor`. - * - * @return a `Right` with this cursor as an object cursor if it points to an object, `Left` with a list of failures - * otherwise. - */ + * Casts this cursor to a `ConfigObjectCursor`. + * + * @return a `Right` with this cursor as an object cursor if it points to an object, `Left` with a list of failures + * otherwise. + */ def asObjectCursor: ConfigReader.Result[ConfigObjectCursor] = castOrFail(OBJECT, v => Right(v.asInstanceOf[ConfigObject])).right.map(ConfigObjectCursor(_, pathElems)) /** - * Casts this cursor to a map from config keys to cursors. - * - * @return a `Right` with the map pointed to by this cursor if the cast can be done, `Left` with a list of failures - * otherwise. - */ + * Casts this cursor to a map from config keys to cursors. + * + * @return a `Right` with the map pointed to by this cursor if the cast can be done, `Left` with a list of failures + * otherwise. + */ def asMap: ConfigReader.Result[Map[String, ConfigCursor]] = asObjectCursor.right.map(_.map) /** - * Returns a cursor to the config at the path composed of given path segments. - * - * @param pathSegments the path of the config for which a cursor should be returned - * @return a `Right` with a cursor to the config at `pathSegments` if such a config exists, a `Left` with a list of - * failures otherwise. - */ + * Returns a cursor to the config at the path composed of given path segments. + * + * @param pathSegments the path of the config for which a cursor should be returned + * @return a `Right` with a cursor to the config at `pathSegments` if such a config exists, a `Left` with a list of + * failures otherwise. + */ @deprecated("Use `.fluent.at(pathSegments).cursor` instead", "0.10.2") final def atPath(pathSegments: PathSegment*): ConfigReader.Result[ConfigCursor] = fluent.at(pathSegments: _*).cursor /** - * Casts this cursor as either a `ConfigListCursor` or a `ConfigObjectCursor`. - * - * @return a `Right` with this cursor as a list or object cursor if the cast can be done, `Left` with a list of - * failures otherwise. - */ + * Casts this cursor as either a `ConfigListCursor` or a `ConfigObjectCursor`. + * + * @return a `Right` with this cursor as a list or object cursor if the cast can be done, `Left` with a list of + * failures otherwise. + */ @deprecated("Use `asListCursor` and/or `asObjectCursor` instead", "0.10.1") def asCollectionCursor: ConfigReader.Result[Either[ConfigListCursor, ConfigObjectCursor]] = { if (isUndefined) { @@ -211,9 +229,7 @@ sealed trait ConfigCursor { } else { val listAtLeft = asListCursor.right.map(Left.apply) lazy val mapAtRight = asObjectCursor.right.map(Right.apply) - listAtLeft - .left.flatMap(_ => mapAtRight) - .left.flatMap(_ => failed(WrongType(value.valueType, Set(LIST, OBJECT)))) + listAtLeft.left.flatMap(_ => mapAtRight).left.flatMap(_ => failed(WrongType(value.valueType, Set(LIST, OBJECT)))) } } @@ -222,46 +238,47 @@ sealed trait ConfigCursor { else FluentConfigCursor(Right(this)) /** - * Returns a failed `ConfigReader` result resulting from scoping a `FailureReason` into the context of this cursor. - * - * This operation is the easiest way to return a failure from a `ConfigReader`. - * - * @param reason the reason of the failure - * @tparam A the returning type of the `ConfigReader` - * @return a failed `ConfigReader` result built by scoping `reason` into the context of this cursor. - */ + * Returns a failed `ConfigReader` result resulting from scoping a `FailureReason` into the context of this cursor. + * + * This operation is the easiest way to return a failure from a `ConfigReader`. + * + * @param reason the reason of the failure + * @tparam A the returning type of the `ConfigReader` + * @return a failed `ConfigReader` result built by scoping `reason` into the context of this cursor. + */ def failed[A](reason: FailureReason): ConfigReader.Result[A] = Left(ConfigReaderFailures(failureFor(reason))) /** - * Returns a `ConfigReaderFailure` resulting from scoping a `FailureReason` into the context of this cursor. - * - * This operation is useful for constructing `ConfigReaderFailures` when there are multiple `FailureReason`s. - * - * @param reason the reason of the failure - * @return a `ConfigReaderFailure` built by scoping `reason` into the context of this cursor. - */ + * Returns a `ConfigReaderFailure` resulting from scoping a `FailureReason` into the context of this cursor. + * + * This operation is useful for constructing `ConfigReaderFailures` when there are multiple `FailureReason`s. + * + * @param reason the reason of the failure + * @return a `ConfigReaderFailure` built by scoping `reason` into the context of this cursor. + */ def failureFor(reason: FailureReason): ConfigReaderFailure = ConvertFailure(reason, this) /** - * Returns a failed `ConfigReader` result resulting from scoping a `Either[FailureReason, A]` into the context of - * this cursor. - * - * This operation is needed when control of the reading process is passed to a place without a `ConfigCursor` - * instance providing the nexessary context (for example, when `ConfigReader.fromString` is used. In those scenarios, - * the call should be wrapped in this method in order to turn `FailureReason` instances into `ConfigReaderFailures`. - * - * @param result the result of a config reading operation - * @tparam A the returning type of the `ConfigReader` - * @return a `ConfigReader` result built by scoping `reason` into the context of this cursor. - */ + * Returns a failed `ConfigReader` result resulting from scoping a `Either[FailureReason, A]` into the context of + * this cursor. + * + * This operation is needed when control of the reading process is passed to a place without a `ConfigCursor` + * instance providing the nexessary context (for example, when `ConfigReader.fromString` is used. In those scenarios, + * the call should be wrapped in this method in order to turn `FailureReason` instances into `ConfigReaderFailures`. + * + * @param result the result of a config reading operation + * @tparam A the returning type of the `ConfigReader` + * @return a `ConfigReader` result built by scoping `reason` into the context of this cursor. + */ def scopeFailure[A](result: Either[FailureReason, A]): ConfigReader.Result[A] = result.left.map { reason => ConfigReaderFailures(failureFor(reason)) } private[this] def castOrFail[A]( - expectedType: ConfigValueType, - cast: ConfigValue => Either[FailureReason, A]): ConfigReader.Result[A] = { + expectedType: ConfigValueType, + cast: ConfigValue => Either[FailureReason, A] + ): ConfigReader.Result[A] = { if (isUndefined) failed(KeyNotFound.forKeys(path, Set())) @@ -273,21 +290,24 @@ sealed trait ConfigCursor { object ConfigCursor { /** - * Builds a `ConfigCursor` for a `ConfigValue` at a path. - * - * @param value the `ConfigValue` to which the cursor should point - * @param pathElems the path of `value` in the config as a list of keys - * @return a `ConfigCursor` for `value` at the path given by `pathElems`. - */ + * Builds a `ConfigCursor` for a `ConfigValue` at a path. + * + * @param value the `ConfigValue` to which the cursor should point + * @param pathElems the path of `value` in the config as a list of keys + * @return a `ConfigCursor` for `value` at the path given by `pathElems`. + */ def apply(value: ConfigValue, pathElems: List[String]): ConfigCursor = SimpleConfigCursor(value, pathElems) /** - * Handle automatic type conversions of `ConfigValue`s the way the - * [[https://github.com/lightbend/config/blob/master/HOCON.md#automatic-type-conversions HOCON specification]] - * describes. This code mimics the behavior of the package-private `com.typesafe.config.impl.DefaultTransformer` class - * in Typesafe Config. - */ - private[pureconfig] def transform(configValue: ConfigValue, requested: ConfigValueType): Either[WrongType, ConfigValue] = { + * Handle automatic type conversions of `ConfigValue`s the way the + * [[https://github.com/lightbend/config/blob/master/HOCON.md#automatic-type-conversions HOCON specification]] + * describes. This code mimics the behavior of the package-private `com.typesafe.config.impl.DefaultTransformer` class + * in Typesafe Config. + */ + private[pureconfig] def transform( + configValue: ConfigValue, + requested: ConfigValueType + ): Either[WrongType, ConfigValue] = { (configValue.valueType(), requested) match { case (valueType, requestedType) if valueType == requestedType => Right(configValue) @@ -337,35 +357,35 @@ object ConfigCursor { } /** - * A simple `ConfigCursor` providing no extra operations. - */ + * A simple `ConfigCursor` providing no extra operations. + */ case class SimpleConfigCursor(value: ConfigValue, pathElems: List[String]) extends ConfigCursor /** - * A `ConfigCursor` pointing to a config list. - */ + * A `ConfigCursor` pointing to a config list. + */ case class ConfigListCursor(value: ConfigList, pathElems: List[String], offset: Int = 0) extends ConfigCursor { @inline private[this] def validIndex(idx: Int) = idx >= 0 && idx < size @inline private[this] def indexKey(idx: Int) = (offset + idx).toString /** - * Returns whether the config list pointed to by this cursor is empty. - */ + * Returns whether the config list pointed to by this cursor is empty. + */ def isEmpty: Boolean = value.isEmpty /** - * Returns the size of the config list pointed to by this cursor. - */ + * Returns the size of the config list pointed to by this cursor. + */ def size: Int = value.size /** - * Returns a cursor to the config at a given index. - * - * @param idx the index of the config for which a cursor should be returned - * @return a `Right` with a cursor to the config at `idx` if such a config exists, a `Left` with a list of failures - * otherwise. - */ + * Returns a cursor to the config at a given index. + * + * @param idx the index of the config for which a cursor should be returned + * @return a `Right` with a cursor to the config at `idx` if such a config exists, a `Left` with a list of failures + * otherwise. + */ def atIndex(idx: Int): ConfigReader.Result[ConfigCursor] = { atIndexOrUndefined(idx) match { case idxCur if idxCur.isUndefined => failed(KeyNotFound.forKeys(indexKey(idx), Set())) @@ -374,23 +394,24 @@ case class ConfigListCursor(value: ConfigList, pathElems: List[String], offset: } /** - * Returns a cursor to the config at a given index. An out of range index will return a cursor to an undefined value. - * - * @param idx the index of the config for which a cursor should be returned - * @return a cursor to the config at `idx` if such a config exists, a cursor to an undefined value otherwise. - */ + * Returns a cursor to the config at a given index. An out of range index will return a cursor to an undefined value. + * + * @param idx the index of the config for which a cursor should be returned + * @return a cursor to the config at `idx` if such a config exists, a cursor to an undefined value otherwise. + */ def atIndexOrUndefined(idx: Int): ConfigCursor = ConfigCursor(if (validIndex(idx)) value.get(idx) else null, indexKey(idx) :: pathElems) /** - * Returns a cursor to the tail of the config list pointed to by this cursor if non-empty. - * - * @return a `Some` with the tail of the config list if the list is not empty, `None` otherwise. - */ + * Returns a cursor to the tail of the config list pointed to by this cursor if non-empty. + * + * @return a `Some` with the tail of the config list if the list is not empty, `None` otherwise. + */ def tailOption: Option[ConfigListCursor] = { if (value.isEmpty) None else { - val newValue = ConfigValueFactory.fromAnyRef(value.asScala.drop(1).asJava) + val newValue = ConfigValueFactory + .fromAnyRef(value.asScala.drop(1).asJava) .withOrigin(value.origin) .asInstanceOf[ConfigList] @@ -399,45 +420,47 @@ case class ConfigListCursor(value: ConfigList, pathElems: List[String], offset: } /** - * Returns a list of cursors to the elements of the config list pointed to by this cursor. - * - * @return a list of cursors to the elements of the config list pointed to by this cursor. - */ + * Returns a list of cursors to the elements of the config list pointed to by this cursor. + * + * @return a list of cursors to the elements of the config list pointed to by this cursor. + */ def list: List[ConfigCursor] = - value.asScala.toList.drop(offset).zipWithIndex.map { case (cv, idx) => ConfigCursor(cv, indexKey(idx) :: pathElems) } + value.asScala.toList.drop(offset).zipWithIndex.map { + case (cv, idx) => ConfigCursor(cv, indexKey(idx) :: pathElems) + } // Avoid resetting the offset when using ConfigCursor's implementation. override def asListCursor: ConfigReader.Result[ConfigListCursor] = Right(this) } /** - * A `ConfigCursor` pointing to a config object. - */ + * A `ConfigCursor` pointing to a config object. + */ case class ConfigObjectCursor(value: ConfigObject, pathElems: List[String]) extends ConfigCursor { /** - * Returns whether the config object pointed to by this cursor is empty. - */ + * Returns whether the config object pointed to by this cursor is empty. + */ def isEmpty: Boolean = value.isEmpty /** - * Returns the size of the config object pointed to by this cursor. - */ + * Returns the size of the config object pointed to by this cursor. + */ def size: Int = value.size /** - * Returns the list of keys of the config object pointed to by this cursor. - */ + * Returns the list of keys of the config object pointed to by this cursor. + */ def keys: Iterable[String] = value.keySet.asScala /** - * Returns a cursor to the config at a given key. - * - * @param key the key of the config for which a cursor should be returned - * @return a `Right` with a cursor to the config at `key` if such a config exists, a `Left` with a list of failures - * otherwise. - */ + * Returns a cursor to the config at a given key. + * + * @param key the key of the config for which a cursor should be returned + * @return a `Right` with a cursor to the config at `key` if such a config exists, a `Left` with a list of failures + * otherwise. + */ def atKey(key: String): ConfigReader.Result[ConfigCursor] = { atKeyOrUndefined(key) match { case keyCur if keyCur.isUndefined => failed(KeyNotFound.forKeys(key, keys)) @@ -446,26 +469,26 @@ case class ConfigObjectCursor(value: ConfigObject, pathElems: List[String]) exte } /** - * Returns a cursor to the config at a given key. A missing key will return a cursor to an undefined value. - * - * @param key the key of the config for which a cursor should be returned - * @return a cursor to the config at `key` if such a config exists, a cursor to an undefined value otherwise. - */ + * Returns a cursor to the config at a given key. A missing key will return a cursor to an undefined value. + * + * @param key the key of the config for which a cursor should be returned + * @return a cursor to the config at `key` if such a config exists, a cursor to an undefined value otherwise. + */ def atKeyOrUndefined(key: String): ConfigCursor = ConfigCursor(value.get(key), key :: pathElems) /** - * Returns a cursor to the object pointed to by this cursor without a given key. - * - * @param key the key to remove on the config object - * @return a cursor to the object pointed to by this cursor without `key`. - */ + * Returns a cursor to the object pointed to by this cursor without a given key. + * + * @param key the key to remove on the config object + * @return a cursor to the object pointed to by this cursor without `key`. + */ def withoutKey(key: String): ConfigObjectCursor = ConfigObjectCursor(value.withoutKey(key), pathElems) /** - * Returns a map of cursors to the elements of the config object pointed to by this cursor. - */ + * Returns a map of cursors to the elements of the config object pointed to by this cursor. + */ def map: Map[String, ConfigCursor] = value.asScala.toMap.map { case (key, cv) => key -> ConfigCursor(cv, key :: pathElems) } diff --git a/core/src/main/scala/pureconfig/ConfigFieldMapping.scala b/core/src/main/scala/pureconfig/ConfigFieldMapping.scala index 7849a165a..1a6913b01 100644 --- a/core/src/main/scala/pureconfig/ConfigFieldMapping.scala +++ b/core/src/main/scala/pureconfig/ConfigFieldMapping.scala @@ -1,19 +1,19 @@ package pureconfig /** - * A mapping between case class fields and their respective keys in the config. - */ + * A mapping between case class fields and their respective keys in the config. + */ trait ConfigFieldMapping extends (String => String) { def apply(fieldName: String): String /** - * Returns a `ConfigFieldMapping` that uses this mapping with some overrides. - * - * @param overrides the overrides for this mapping as pairs (field, configKey) - * @return a `ConfigFieldMapping` that maps fields using `overrides` if the field is present there and otherwise - * uses this mapping. - */ + * Returns a `ConfigFieldMapping` that uses this mapping with some overrides. + * + * @param overrides the overrides for this mapping as pairs (field, configKey) + * @return a `ConfigFieldMapping` that maps fields using `overrides` if the field is present there and otherwise + * uses this mapping. + */ def withOverrides(overrides: (String, String)*) = ConfigFieldMapping(overrides.toMap.withDefault(apply)) } @@ -21,30 +21,31 @@ trait ConfigFieldMapping extends (String => String) { object ConfigFieldMapping { /** - * Creates a ConfigFieldMapping from the provided function, mapping names in - * the object that will receive config values to names in the configuration - * file. - * - * @param f a function that maps names in the object that will receive config - * values to names in the configuration file - * @return a ConfigFieldMapping created from the provided function. - */ - def apply(f: String => String): ConfigFieldMapping = new ConfigFieldMapping { - def apply(fieldName: String): String = f(fieldName) - } + * Creates a ConfigFieldMapping from the provided function, mapping names in + * the object that will receive config values to names in the configuration + * file. + * + * @param f a function that maps names in the object that will receive config + * values to names in the configuration file + * @return a ConfigFieldMapping created from the provided function. + */ + def apply(f: String => String): ConfigFieldMapping = + new ConfigFieldMapping { + def apply(fieldName: String): String = f(fieldName) + } /** - * Creates a ConfigFieldMapping according to the naming conventions specified - * both for the object that will receive config values and for the - * configuration file. - * - * @param typeFieldConvention naming convention used by the fields of the - * object which will receive config values - * @param configFieldConvention naming convention used in the configuration - * file - * @return a ConfigFieldMapping created according to the provided naming - * conventions. - */ + * Creates a ConfigFieldMapping according to the naming conventions specified + * both for the object that will receive config values and for the + * configuration file. + * + * @param typeFieldConvention naming convention used by the fields of the + * object which will receive config values + * @param configFieldConvention naming convention used in the configuration + * file + * @return a ConfigFieldMapping created according to the provided naming + * conventions. + */ def apply(typeFieldConvention: NamingConvention, configFieldConvention: NamingConvention): ConfigFieldMapping = { if (typeFieldConvention == configFieldConvention) { apply(identity) diff --git a/core/src/main/scala/pureconfig/ConfigReader.scala b/core/src/main/scala/pureconfig/ConfigReader.scala index d929378c4..d04ba374e 100644 --- a/core/src/main/scala/pureconfig/ConfigReader.scala +++ b/core/src/main/scala/pureconfig/ConfigReader.scala @@ -7,71 +7,71 @@ import scala.util.Try import com.typesafe.config.ConfigValue import pureconfig.ConvertHelpers._ -import pureconfig.error.{ ConfigReaderFailure, ConfigReaderFailures, FailureReason } +import pureconfig.error.{ConfigReaderFailure, ConfigReaderFailures, FailureReason} /** - * Trait for objects capable of reading objects of a given type from `ConfigValues`. - * - * @tparam A the type of objects readable by this `ConfigReader` - */ + * Trait for objects capable of reading objects of a given type from `ConfigValues`. + * + * @tparam A the type of objects readable by this `ConfigReader` + */ trait ConfigReader[A] { import pureconfig.ConfigReader._ /** - * Convert the configuration given by a cursor into an instance of `A` if possible. - * - * @param cur The cursor from which the config should be loaded - * @return either a list of failures or an object of type `A` - */ + * Convert the configuration given by a cursor into an instance of `A` if possible. + * + * @param cur The cursor from which the config should be loaded + * @return either a list of failures or an object of type `A` + */ def from(cur: ConfigCursor): ConfigReader.Result[A] /** - * Convert the given configuration into an instance of `A` if possible. - * - * @param config The configuration from which the config should be loaded - * @return either a list of failures or an object of type `A` - */ + * Convert the given configuration into an instance of `A` if possible. + * + * @param config The configuration from which the config should be loaded + * @return either a list of failures or an object of type `A` + */ def from(config: ConfigValue): ConfigReader.Result[A] = from(ConfigCursor(config, Nil)) /** - * Maps a function over the results of this reader. - * - * @param f the function to map over this reader - * @tparam B the output type of the function - * @return a `ConfigReader` returning the results of this reader mapped by `f`. - */ + * Maps a function over the results of this reader. + * + * @param f the function to map over this reader + * @tparam B the output type of the function + * @return a `ConfigReader` returning the results of this reader mapped by `f`. + */ def map[B](f: A => B): ConfigReader[B] = fromCursor[B] { cur => from(cur).right.flatMap { v => cur.scopeFailure(toResult(f)(v)) } } /** - * Maps a function that can possibly fail over the results of this reader. - * - * @param f the function to map over this reader - * @tparam B the value read by the function in case of success - * @return a `ConfigReader` returning the results of this reader mapped by `f`, with the resulting `Either` flattened - * as a success or failure. - */ + * Maps a function that can possibly fail over the results of this reader. + * + * @param f the function to map over this reader + * @tparam B the value read by the function in case of success + * @return a `ConfigReader` returning the results of this reader mapped by `f`, with the resulting `Either` flattened + * as a success or failure. + */ def emap[B](f: A => Either[FailureReason, B]): ConfigReader[B] = fromCursor[B] { cur => from(cur).right.flatMap { v => cur.scopeFailure(f(v)) } } /** - * Monadically bind a function over the results of this reader. - * - * @param f the function to bind over this reader - * @tparam B the type of the objects readable by the resulting `ConfigReader` - * @return a `ConfigReader` returning the results of this reader bound by `f`. - */ + * Monadically bind a function over the results of this reader. + * + * @param f the function to bind over this reader + * @tparam B the type of the objects readable by the resulting `ConfigReader` + * @return a `ConfigReader` returning the results of this reader bound by `f`. + */ def flatMap[B](f: A => ConfigReader[B]): ConfigReader[B] = fromCursor[B] { cur => from(cur).right.flatMap(f(_).from(cur)) } /** - * Combines this reader with another, returning both results as a pair. - * - * @param reader the reader to combine with this one - * @tparam B the type of the objects readable by the provided reader - * @return a `ConfigReader` returning the results of both readers as a pair. - */ + * Combines this reader with another, returning both results as a pair. + * + * @param reader the reader to combine with this one + * @tparam B the type of the objects readable by the provided reader + * @return a `ConfigReader` returning the results of both readers as a pair. + */ def zip[B](reader: ConfigReader[B]): ConfigReader[(A, B)] = fromCursor[(A, B)] { cur => (from(cur), reader.from(cur)) match { @@ -83,13 +83,13 @@ trait ConfigReader[A] { } /** - * Combines this reader with another, returning the result of the first one that succeeds. - * - * @param reader the reader to combine with this one - * @tparam AA the type of the objects readable by both readers - * @return a `ConfigReader` returning the results of this reader if it succeeds and the results of `reader` - * otherwise. - */ + * Combines this reader with another, returning the result of the first one that succeeds. + * + * @param reader the reader to combine with this one + * @tparam AA the type of the objects readable by both readers + * @return a `ConfigReader` returning the results of this reader if it succeeds and the results of `reader` + * otherwise. + */ def orElse[AA >: A, B <: AA](reader: => ConfigReader[B]): ConfigReader[AA] = fromCursor[AA] { cur => from(cur) match { @@ -99,57 +99,62 @@ trait ConfigReader[A] { } /** - * Applies a function to configs before passing them to this reader. - * - * @param f the function to apply to input configs - * @return a `ConfigReader` returning the results of this reader when the input configs are mapped using `f`. - */ + * Applies a function to configs before passing them to this reader. + * + * @param f the function to apply to input configs + * @return a `ConfigReader` returning the results of this reader when the input configs are mapped using `f`. + */ def contramapConfig(f: ConfigValue => ConfigValue): ConfigReader[A] = fromCursor[A] { cur => from(ConfigCursor(f(cur.value), cur.pathElems)) } /** - * Applies a function to config cursors before passing them to this reader. - * - * @param f the function to apply to input config cursors - * @return a `ConfigReader` returning the results of this reader when the input cursors are mapped using `f`. - */ + * Applies a function to config cursors before passing them to this reader. + * + * @param f the function to apply to input config cursors + * @return a `ConfigReader` returning the results of this reader when the input cursors are mapped using `f`. + */ def contramapCursor(f: ConfigCursor => ConfigCursor): ConfigReader[A] = fromCursor[A] { cur => from(f(cur)) } } /** - * Provides methods to create [[ConfigReader]] instances. - */ + * Provides methods to create [[ConfigReader]] instances. + */ object ConfigReader extends BasicReaders with CollectionReaders with ProductReaders with ExportedReaders { /** - * The type of most config PureConfig reading methods. - * - * @tparam A the type of the result - */ + * The type of most config PureConfig reading methods. + * + * @tparam A the type of the result + */ type Result[A] = Either[ConfigReaderFailures, A] /** - * Object containing useful constructors and utility methods for `Result`s. - */ + * Object containing useful constructors and utility methods for `Result`s. + */ object Result { /** - * Sequences a collection of `Result`s into a `Result` of a collection. - */ - def sequence[A, CC[X] <: TraversableOnce[X]](rs: CC[ConfigReader.Result[A]])(implicit cbf: FactoryCompat[A, CC[A]]): ConfigReader.Result[CC[A]] = { + * Sequences a collection of `Result`s into a `Result` of a collection. + */ + def sequence[A, CC[X] <: TraversableOnce[X]]( + rs: CC[ConfigReader.Result[A]] + )(implicit cbf: FactoryCompat[A, CC[A]]): ConfigReader.Result[CC[A]] = { rs.foldLeft[ConfigReader.Result[mutable.Builder[A, CC[A]]]](Right(cbf.newBuilder())) { case (Right(builder), Right(a)) => Right(builder += a) case (Left(err), Right(_)) => Left(err) case (Right(_), Left(err)) => Left(err) case (Left(errs), Left(err)) => Left(errs ++ err) - }.right.map(_.result()) + }.right + .map(_.result()) } /** - * Merges two `Result`s using a given function. - */ - def zipWith[A, B, C](first: ConfigReader.Result[A], second: ConfigReader.Result[B])(f: (A, B) => C): ConfigReader.Result[C] = + * Merges two `Result`s using a given function. + */ + def zipWith[A, B, C](first: ConfigReader.Result[A], second: ConfigReader.Result[B])( + f: (A, B) => C + ): ConfigReader.Result[C] = (first, second) match { case (Right(a), Right(b)) => Right(f(a, b)) case (Left(aFailures), Left(bFailures)) => Left(aFailures ++ bFailures) @@ -158,31 +163,32 @@ object ConfigReader extends BasicReaders with CollectionReaders with ProductRead } /** - * Returns a `Result` containing a single failure. - */ + * Returns a `Result` containing a single failure. + */ def fail[A](failure: ConfigReaderFailure): ConfigReader.Result[A] = Left(ConfigReaderFailures(failure)) } def apply[A](implicit reader: Derivation[ConfigReader[A]]): ConfigReader[A] = reader.value /** - * Creates a `ConfigReader` from a function reading a `ConfigCursor`. - * - * @param fromF the function used to read config cursors to values - * @tparam A the type of the objects readable by the returned reader - * @return a `ConfigReader` for reading objects of type `A` using `fromF`. - */ - def fromCursor[A](fromF: ConfigCursor => ConfigReader.Result[A]) = new ConfigReader[A] { - def from(cur: ConfigCursor) = fromF(cur) - } + * Creates a `ConfigReader` from a function reading a `ConfigCursor`. + * + * @param fromF the function used to read config cursors to values + * @tparam A the type of the objects readable by the returned reader + * @return a `ConfigReader` for reading objects of type `A` using `fromF`. + */ + def fromCursor[A](fromF: ConfigCursor => ConfigReader.Result[A]) = + new ConfigReader[A] { + def from(cur: ConfigCursor) = fromF(cur) + } /** - * Creates a `ConfigReader` from a function. - * - * @param fromF the function used to read configs to values - * @tparam A the type of the objects readable by the returned reader - * @return a `ConfigReader` for reading objects of type `A` using `fromF`. - */ + * Creates a `ConfigReader` from a function. + * + * @param fromF the function used to read configs to values + * @tparam A the type of the objects readable by the returned reader + * @return a `ConfigReader` for reading objects of type `A` using `fromF`. + */ def fromFunction[A](fromF: ConfigValue => ConfigReader.Result[A]) = fromCursor(fromF.compose(_.value)) diff --git a/core/src/main/scala/pureconfig/ConfigSource.scala b/core/src/main/scala/pureconfig/ConfigSource.scala index c21757c3d..f81c6c390 100644 --- a/core/src/main/scala/pureconfig/ConfigSource.scala +++ b/core/src/main/scala/pureconfig/ConfigSource.scala @@ -9,70 +9,70 @@ import scala.reflect.ClassTag import com.typesafe.config._ import pureconfig.ConfigReader.Result import pureconfig.backend.ConfigWrapper._ -import pureconfig.backend.{ ConfigFactoryWrapper, PathUtil } -import pureconfig.error.{ CannotRead, ConfigReaderException, ConfigReaderFailures } +import pureconfig.backend.{ConfigFactoryWrapper, PathUtil} +import pureconfig.error.{CannotRead, ConfigReaderException, ConfigReaderFailures} /** - * A representation of a source from which `ConfigValue`s can be loaded, such as a file or a URL. - * - * A source allows users to load configs from this source as any type for which a `ConfigReader` is - * available. Raw configs can also be retrieved as a `ConfigValue`, a `ConfigCursor` or a - * `FluentConfigCursor`. Before using any of the loading methods described, Users can opt to focus - * on a specific part of a config by specifying a namespace. - * - * All config loading methods are lazy and defer resolution of references until needed. - */ + * A representation of a source from which `ConfigValue`s can be loaded, such as a file or a URL. + * + * A source allows users to load configs from this source as any type for which a `ConfigReader` is + * available. Raw configs can also be retrieved as a `ConfigValue`, a `ConfigCursor` or a + * `FluentConfigCursor`. Before using any of the loading methods described, Users can opt to focus + * on a specific part of a config by specifying a namespace. + * + * All config loading methods are lazy and defer resolution of references until needed. + */ trait ConfigSource { /** - * Retrieves a `ConfigValue` from this source. This forces the config to be resolved, if needed. - * - * @return a `ConfigValue` retrieved from this source. - */ + * Retrieves a `ConfigValue` from this source. This forces the config to be resolved, if needed. + * + * @return a `ConfigValue` retrieved from this source. + */ def value(): Result[ConfigValue] /** - * Returns a cursor for a `ConfigValue` retrieved from this source. - * - * @return a cursor for a `ConfigValue` retrieved from this source. - */ + * Returns a cursor for a `ConfigValue` retrieved from this source. + * + * @return a cursor for a `ConfigValue` retrieved from this source. + */ def cursor(): Result[ConfigCursor] = value().right.map(ConfigCursor(_, Nil)) /** - * Returns a fluent cursor for a `ConfigValue` retrieved from this source. - * - * @return a fluent cursor for a `ConfigValue` retrieved from this source. - */ + * Returns a fluent cursor for a `ConfigValue` retrieved from this source. + * + * @return a fluent cursor for a `ConfigValue` retrieved from this source. + */ def fluentCursor(): FluentConfigCursor = FluentConfigCursor(cursor()) /** - * Navigates through the config to focus on a namespace. - * - * @param namespace the namespace to focus on - * @return a new `ConfigSource` focused on the given namespace. - */ + * Navigates through the config to focus on a namespace. + * + * @param namespace the namespace to focus on + * @return a new `ConfigSource` focused on the given namespace. + */ def at(namespace: String): ConfigSource = ConfigSource.fromCursor(fluentCursor().at(PathUtil.splitPath(namespace).map(p => p: PathSegment): _*)) /** - * Loads a configuration of type `A` from this source. - * - * @tparam A the type of the config to be loaded - * @return A `Right` with the configuration if it is possible to create an instance of type - * `A` from this source, a `Failure` with details on why it isn't possible otherwise - */ + * Loads a configuration of type `A` from this source. + * + * @tparam A the type of the config to be loaded + * @return A `Right` with the configuration if it is possible to create an instance of type + * `A` from this source, a `Failure` with details on why it isn't possible otherwise + */ final def load[A](implicit reader: Derivation[ConfigReader[A]]): Result[A] = cursor().right.flatMap(reader.value.from) /** - * Loads a configuration of type `A` from this source. If it is not possible to create an - * instance of `A`, this method throws a `ConfigReaderException`. - * - * @tparam A the type of the config to be loaded - * @return The configuration of type `A` loaded from this source. - */ + * Loads a configuration of type `A` from this source. If it is not possible to create an + * instance of `A`, this method throws a `ConfigReaderException`. + * + * @tparam A the type of the config to be loaded + * @return The configuration of type `A` loaded from this source. + */ @throws[ConfigReaderException[_]] final def loadOrThrow[A: ClassTag](implicit reader: Derivation[ConfigReader[A]]): A = { load[A] match { @@ -83,11 +83,11 @@ trait ConfigSource { } /** - * A `ConfigSource` which is guaranteed to generate config objects (maps) as root values. - * - * @param getConf the thunk to generate a `Config` instance. This parameter won't be memoized so it - * can be used with dynamic sources (e.g. URLs) - */ + * A `ConfigSource` which is guaranteed to generate config objects (maps) as root values. + * + * @param getConf the thunk to generate a `Config` instance. This parameter won't be memoized so it + * can be used with dynamic sources (e.g. URLs) + */ final class ConfigObjectSource private (getConf: () => Result[Config]) extends ConfigSource { def value(): Result[ConfigObject] = @@ -98,45 +98,45 @@ final class ConfigObjectSource private (getConf: () => Result[Config]) extends C value().right.map(ConfigObjectCursor(_, Nil)) /** - * Reads a `Config` from this config source. The returned config is usually unresolved, unless - * the source forces it otherwise. - * - * @return a `Config` provided by this source. - */ + * Reads a `Config` from this config source. The returned config is usually unresolved, unless + * the source forces it otherwise. + * + * @return a `Config` provided by this source. + */ def config(): Result[Config] = getConf() /** - * Merges this source with another one, with the latter being used as a fallback (e.g. the - * source on which this method is called takes priority). Both sources are required to produce - * a config object successfully. - * - * @param cs the config source to use as fallback - * @return a new `ConfigObjectSource` that loads configs from both sources and uses `cs` as a - * fallback for this source - */ + * Merges this source with another one, with the latter being used as a fallback (e.g. the + * source on which this method is called takes priority). Both sources are required to produce + * a config object successfully. + * + * @param cs the config source to use as fallback + * @return a new `ConfigObjectSource` that loads configs from both sources and uses `cs` as a + * fallback for this source + */ def withFallback(cs: ConfigObjectSource): ConfigObjectSource = ConfigObjectSource(Result.zipWith(config(), cs.config())(_.withFallback(_))) /** - * Returns a `ConfigObjectSource` that provides the same config as this one, but falls back to - * providing an empty config when the source cannot be read. It can be used together with - * `.withFallback` to specify optional config files to be merged (like `reference.conf`). - * - * @return a new `ConfigObjectSource` that provides the same config as this one, but falls back - * to an empty config if it cannot be read. - */ + * Returns a `ConfigObjectSource` that provides the same config as this one, but falls back to + * providing an empty config when the source cannot be read. It can be used together with + * `.withFallback` to specify optional config files to be merged (like `reference.conf`). + * + * @return a new `ConfigObjectSource` that provides the same config as this one, but falls back + * to an empty config if it cannot be read. + */ def optional: ConfigObjectSource = recoverWith { case ConfigReaderFailures(_: CannotRead) => ConfigSource.empty } /** - * Applies a function `f` if this source returns a failure, returning an alternative config - * source in those cases. - * - * @param f the function to apply if this source returns a failure - * @return a new `ConfigObjectSource` that provides an alternative config in case this source - * fails - */ + * Applies a function `f` if this source returns a failure, returning an alternative config + * source in those cases. + * + * @param f the function to apply if this source returns a failure + * @return a new `ConfigObjectSource` that provides an alternative config in case this source + * fails + */ def recoverWith(f: PartialFunction[ConfigReaderFailures, ConfigObjectSource]): ConfigObjectSource = ConfigObjectSource(getConf().left.flatMap { failures => f.lift(failures) match { @@ -149,190 +149,192 @@ final class ConfigObjectSource private (getConf: () => Result[Config]) extends C object ConfigObjectSource { /** - * Creates a `ConfigObjectSource` from a `Result[Config]`. The provided argument is allowed - * to change value over time. - * - * @param conf the config to be provided by this source - * @return a `ConfigObjectSource` providing the given config. - */ + * Creates a `ConfigObjectSource` from a `Result[Config]`. The provided argument is allowed + * to change value over time. + * + * @param conf the config to be provided by this source + * @return a `ConfigObjectSource` providing the given config. + */ def apply(conf: => Result[Config]): ConfigObjectSource = new ConfigObjectSource(() => conf) } /** - * Object containing factory methods for building `ConfigSource`s. - * - * The sources provided here use Typesafe Config configs created from files, resources, URLs or - * strings. It also provides sources that delegate the loading component to Typesafe Config, to - * leverage reference configs and overrides, making it easy to switch from using `ConfigFactory` - * to `ConfigSource`. - * - * Other PureConfig modules may provide other ways or building config sources (e.g. for different - * config formats or data sources). - */ + * Object containing factory methods for building `ConfigSource`s. + * + * The sources provided here use Typesafe Config configs created from files, resources, URLs or + * strings. It also provides sources that delegate the loading component to Typesafe Config, to + * leverage reference configs and overrides, making it easy to switch from using `ConfigFactory` + * to `ConfigSource`. + * + * Other PureConfig modules may provide other ways or building config sources (e.g. for different + * config formats or data sources). + */ object ConfigSource { /** - * A config source for the default loading process in Typesafe Config. Typesafe Config stacks - * `reference.conf` resources provided by libraries, application configs (by default - * `application.conf` in resources) and system property overrides, resolves them and merges them - * into a single config. This source is equivalent to - * `defaultOverrides.withFallback(defaultApplication).withFallback(defaultReference)`. - */ + * A config source for the default loading process in Typesafe Config. Typesafe Config stacks + * `reference.conf` resources provided by libraries, application configs (by default + * `application.conf` in resources) and system property overrides, resolves them and merges them + * into a single config. This source is equivalent to + * `defaultOverrides.withFallback(defaultApplication).withFallback(defaultReference)`. + */ val default = ConfigObjectSource(ConfigFactoryWrapper.load()) /** - * A config source for the default loading process in Typesafe Config with a custom application - * config source. Typesafe Config stacks `reference.conf` resources provided by libraries, the - * given file and system property overrides, resolves them and merges them into a single config. - * - * This method is provided here to support use cases that previously depended on - * `ConfigFactory.load(config)`. Creating a custom source by merging the layers manually is - * usually recommended as it makes the config priorities more transparent. - * - * @param appSource the source providing the application config - * @return a `ConfigObjectSource` for the default loading process in Typesafe Config with a - * custom application config source. - */ + * A config source for the default loading process in Typesafe Config with a custom application + * config source. Typesafe Config stacks `reference.conf` resources provided by libraries, the + * given file and system property overrides, resolves them and merges them into a single config. + * + * This method is provided here to support use cases that previously depended on + * `ConfigFactory.load(config)`. Creating a custom source by merging the layers manually is + * usually recommended as it makes the config priorities more transparent. + * + * @param appSource the source providing the application config + * @return a `ConfigObjectSource` for the default loading process in Typesafe Config with a + * custom application config source. + */ def default(appSource: ConfigObjectSource): ConfigObjectSource = ConfigObjectSource(appSource.config().right.flatMap(ConfigFactoryWrapper.load)) /** - * A config source that always provides empty configs. - */ + * A config source that always provides empty configs. + */ val empty = ConfigObjectSource(Right(ConfigFactory.empty)) /** - * A config source for the default reference config in Typesafe Config (`reference.conf` - * resources provided by libraries). Like Typesafe Config, it provides an empty object if - * `reference.conf` files are not found. - * - * As required by - * [[https://github.com/lightbend/config/blob/master/HOCON.md#conventional-configuration-files-for-jvm-apps the HOCON spec]], - * the default reference files are pre-emptively resolved - substitutions in the reference config - * aren't affected by application configs. - */ + * A config source for the default reference config in Typesafe Config (`reference.conf` + * resources provided by libraries). Like Typesafe Config, it provides an empty object if + * `reference.conf` files are not found. + * + * As required by + * [[https://github.com/lightbend/config/blob/master/HOCON.md#conventional-configuration-files-for-jvm-apps the HOCON spec]], + * the default reference files are pre-emptively resolved - substitutions in the reference config + * aren't affected by application configs. + */ val defaultReference = ConfigObjectSource(ConfigFactoryWrapper.defaultReference()) /** - * A config source for the default reference config in Typesafe Config (`reference.conf` - * resources provided by libraries) before being resolved. This can be used as an alternative - * to `defaultReference` for use cases that require `reference.conf` to depend on - * `application.conf`. Like Typesafe Config, it provides an empty object if `reference.conf` - * files are not found. - */ + * A config source for the default reference config in Typesafe Config (`reference.conf` + * resources provided by libraries) before being resolved. This can be used as an alternative + * to `defaultReference` for use cases that require `reference.conf` to depend on + * `application.conf`. Like Typesafe Config, it provides an empty object if `reference.conf` + * files are not found. + */ val defaultReferenceUnresolved = resources("reference.conf").optional /** - * A config source for the default application config in Typesafe Config (by default - * `application.conf` in resources). Like Typesafe Config, it provides an empty object if - * application config files are not found. - */ + * A config source for the default application config in Typesafe Config (by default + * `application.conf` in resources). Like Typesafe Config, it provides an empty object if + * application config files are not found. + */ val defaultApplication = ConfigObjectSource(ConfigFactoryWrapper.defaultApplication()) /** - * A config source for the default overrides in Typesafe Config (by default a map of system - * properties). - */ + * A config source for the default overrides in Typesafe Config (by default a map of system + * properties). + */ val defaultOverrides = ConfigObjectSource(ConfigFactoryWrapper.defaultOverrides()) /** - * A config source for Java system properties. - */ + * A config source for Java system properties. + */ val systemProperties = ConfigObjectSource(ConfigFactoryWrapper.systemProperties()) /** - * Returns a config source that provides configs read from a file. - * - * @param path the path to the file as a string - * @return a config source that provides configs read from a file. - */ + * Returns a config source that provides configs read from a file. + * + * @param path the path to the file as a string + * @return a config source that provides configs read from a file. + */ def file(path: String) = ConfigObjectSource(ConfigFactoryWrapper.parseFile(new File(path))) /** - * Returns a config source that provides configs read from a file. - * - * @param path the path to the file - * @return a config source that provides configs read from a file. - */ + * Returns a config source that provides configs read from a file. + * + * @param path the path to the file + * @return a config source that provides configs read from a file. + */ def file(path: Path) = ConfigObjectSource(ConfigFactoryWrapper.parseFile(path.toFile)) /** - * Returns a config source that provides configs read from a file. - * - * @param file the file - * @return a config source that provides configs read from a file. - */ + * Returns a config source that provides configs read from a file. + * + * @param file the file + * @return a config source that provides configs read from a file. + */ def file(file: File) = ConfigObjectSource(ConfigFactoryWrapper.parseFile(file)) /** - * Returns a config source that provides configs read from a URL. The URL can either point to a - * local file or to a remote HTTP location. - * - * @param url the URL - * @return a config source that provides configs read from a URL. - */ + * Returns a config source that provides configs read from a URL. The URL can either point to a + * local file or to a remote HTTP location. + * + * @param url the URL + * @return a config source that provides configs read from a URL. + */ def url(url: URL) = ConfigObjectSource(ConfigFactoryWrapper.parseURL(url)) /** - * Returns a config source that provides configs read from JVM resource files. If multiple files - * are found, they are merged in no specific order. This method uses Typesafe Config's default - * class loader (`Thread.currentThread().getContextClassLoader()`). - * - * @param name the resource name - * @return a config source that provides configs read from JVM resource files. - */ + * Returns a config source that provides configs read from JVM resource files. If multiple files + * are found, they are merged in no specific order. This method uses Typesafe Config's default + * class loader (`Thread.currentThread().getContextClassLoader()`). + * + * @param name the resource name + * @return a config source that provides configs read from JVM resource files. + */ def resources(name: String) = ConfigObjectSource(ConfigFactoryWrapper.parseResources(name, null)) /** - * Returns a config source that provides configs read from JVM resource files. If multiple files - * are found, they are merged in no specific order. The given class loader will be used to look - * for resources. - * - * @param name the resource name - * @param classLoader the class loader to use to look for resources - * @return a config source that provides configs read from JVM resource files. - */ + * Returns a config source that provides configs read from JVM resource files. If multiple files + * are found, they are merged in no specific order. The given class loader will be used to look + * for resources. + * + * @param name the resource name + * @param classLoader the class loader to use to look for resources + * @return a config source that provides configs read from JVM resource files. + */ def resources(name: String, classLoader: ClassLoader) = ConfigObjectSource(ConfigFactoryWrapper.parseResources(name, classLoader)) /** - * Returns a config source that provides a config parsed from a string. - * - * @param confStr the config content - * @return a config source that provides a config parsed from a string. - */ + * Returns a config source that provides a config parsed from a string. + * + * @param confStr the config content + * @return a config source that provides a config parsed from a string. + */ def string(confStr: String) = ConfigObjectSource(ConfigFactoryWrapper.parseString(confStr)) /** - * Returns a config source that provides a fixed `Config`. - * - * @param conf the config to be provided - * @return a config source that provides the given config. - */ + * Returns a config source that provides a fixed `Config`. + * + * @param conf the config to be provided + * @return a config source that provides the given config. + */ def fromConfig(conf: Config) = ConfigObjectSource(Right(conf)) /** - * Creates a `ConfigSource` from a `ConfigCursor`. - * - * @param cur the cursor to be provided by this source - * @return a `ConfigSource` providing the given cursor. - */ - private[pureconfig] def fromCursor(cur: ConfigCursor): ConfigSource = new ConfigSource { - def value(): Result[ConfigValue] = Right(cur.value) - override def cursor() = Right(cur) - } + * Creates a `ConfigSource` from a `ConfigCursor`. + * + * @param cur the cursor to be provided by this source + * @return a `ConfigSource` providing the given cursor. + */ + private[pureconfig] def fromCursor(cur: ConfigCursor): ConfigSource = + new ConfigSource { + def value(): Result[ConfigValue] = Right(cur.value) + override def cursor() = Right(cur) + } /** - * Creates a `ConfigSource` from a `FluentConfigCursor`. - * - * @param cur the cursor to be provided by this source - * @return a `ConfigSource` providing the given cursor. - */ - private[pureconfig] def fromCursor(cur: FluentConfigCursor): ConfigSource = new ConfigSource { - def value(): Result[ConfigValue] = cur.cursor.right.map(_.value) - override def cursor() = cur.cursor - override def fluentCursor() = cur - } + * Creates a `ConfigSource` from a `FluentConfigCursor`. + * + * @param cur the cursor to be provided by this source + * @return a `ConfigSource` providing the given cursor. + */ + private[pureconfig] def fromCursor(cur: FluentConfigCursor): ConfigSource = + new ConfigSource { + def value(): Result[ConfigValue] = cur.cursor.right.map(_.value) + override def cursor() = cur.cursor + override def fluentCursor() = cur + } } diff --git a/core/src/main/scala/pureconfig/ConfigWriter.scala b/core/src/main/scala/pureconfig/ConfigWriter.scala index e0461ceb1..dcae219ef 100644 --- a/core/src/main/scala/pureconfig/ConfigWriter.scala +++ b/core/src/main/scala/pureconfig/ConfigWriter.scala @@ -1,90 +1,94 @@ package pureconfig -import com.typesafe.config.{ ConfigValue, ConfigValueFactory } +import com.typesafe.config.{ConfigValue, ConfigValueFactory} /** - * Trait for objects capable of writing objects of a given type to `ConfigValues`. - * - * @tparam A the type of objects writable by this `ConfigWriter` - */ + * Trait for objects capable of writing objects of a given type to `ConfigValues`. + * + * @tparam A the type of objects writable by this `ConfigWriter` + */ trait ConfigWriter[A] { import pureconfig.ConfigWriter._ /** - * Converts a type `A` to a `ConfigValue`. - * - * @param a The instance of `A` to convert - * @return The `ConfigValue` obtained from the `A` instance - */ + * Converts a type `A` to a `ConfigValue`. + * + * @param a The instance of `A` to convert + * @return The `ConfigValue` obtained from the `A` instance + */ def to(a: A): ConfigValue /** - * Applies a function to values before passing them to this writer. - * - * @param f the function to apply to input values - * @tparam B the input type of the function - * @return a `ConfigWriter` that writes the results of this writer when the input values are mapped using `f`. - */ + * Applies a function to values before passing them to this writer. + * + * @param f the function to apply to input values + * @tparam B the input type of the function + * @return a `ConfigWriter` that writes the results of this writer when the input values are mapped using `f`. + */ def contramap[B](f: B => A): ConfigWriter[B] = fromFunction[B] { a => to(f(a)) } /** - * Maps a function over the results of this writer. - * - * @param f the function to map over this writer - * @return a `ConfigWriter` returning the results of this writer mapped by `f`. - */ + * Maps a function over the results of this writer. + * + * @param f the function to map over this writer + * @return a `ConfigWriter` returning the results of this writer mapped by `f`. + */ def mapConfig(f: ConfigValue => ConfigValue): ConfigWriter[A] = fromFunction[A] { a => f(to(a)) } } /** - * Provides methods to create [[ConfigWriter]] instances. - */ + * Provides methods to create [[ConfigWriter]] instances. + */ object ConfigWriter extends BasicWriters with CollectionWriters with ProductWriters with ExportedWriters { def apply[A](implicit writer: Derivation[ConfigWriter[A]]): ConfigWriter[A] = writer.value /** - * Creates a `ConfigWriter` from a function. - * - * @param toF the function used to write values to configs - * @tparam A the type of the objects writable by the returned writer - * @return a `ConfigWriter` for writing objects of type `A` using `toF`. - */ - def fromFunction[A](toF: A => ConfigValue) = new ConfigWriter[A] { - def to(a: A) = toF(a) - } + * Creates a `ConfigWriter` from a function. + * + * @param toF the function used to write values to configs + * @tparam A the type of the objects writable by the returned writer + * @return a `ConfigWriter` for writing objects of type `A` using `toF`. + */ + def fromFunction[A](toF: A => ConfigValue) = + new ConfigWriter[A] { + def to(a: A) = toF(a) + } /** - * Returns a `ConfigWriter` for types supported by `ConfigValueFactory.fromAnyRef`. This method should be used - * carefully, as a runtime exception is thrown if the type passed as argument is not supported. - * - * @tparam A the primitive type for which a `ConfigWriter` is to be created - * @return a `ConfigWriter` for the type `A`. - */ - def forPrimitive[A]: ConfigWriter[A] = new ConfigWriter[A] { - def to(t: A) = ConfigValueFactory.fromAnyRef(t) - } + * Returns a `ConfigWriter` for types supported by `ConfigValueFactory.fromAnyRef`. This method should be used + * carefully, as a runtime exception is thrown if the type passed as argument is not supported. + * + * @tparam A the primitive type for which a `ConfigWriter` is to be created + * @return a `ConfigWriter` for the type `A`. + */ + def forPrimitive[A]: ConfigWriter[A] = + new ConfigWriter[A] { + def to(t: A) = ConfigValueFactory.fromAnyRef(t) + } /** - * Returns a `ConfigWriter` that writes objects of a given type as strings created by `.toString`. - * - * @tparam A the type for which a `ConfigWriter` is to be created - * @return a `ConfigWriter` for the type `A`. - */ - def toDefaultString[A]: ConfigWriter[A] = new ConfigWriter[A] { - def to(t: A) = ConfigValueFactory.fromAnyRef(t.toString) - } + * Returns a `ConfigWriter` that writes objects of a given type as strings created by `.toString`. + * + * @tparam A the type for which a `ConfigWriter` is to be created + * @return a `ConfigWriter` for the type `A`. + */ + def toDefaultString[A]: ConfigWriter[A] = + new ConfigWriter[A] { + def to(t: A) = ConfigValueFactory.fromAnyRef(t.toString) + } /** - * Returns a `ConfigWriter` that writes objects of a given type as strings created by a function. - * - * @param toF the function converting an object of type `A` to a string - * @tparam A the type for which a `ConfigWriter` is to be created - * @return a `ConfigWriter` for the type `A`. - */ - def toString[A](toF: A => String): ConfigWriter[A] = new ConfigWriter[A] { - def to(t: A) = ConfigValueFactory.fromAnyRef(toF(t)) - } + * Returns a `ConfigWriter` that writes objects of a given type as strings created by a function. + * + * @param toF the function converting an object of type `A` to a string + * @tparam A the type for which a `ConfigWriter` is to be created + * @return a `ConfigWriter` for the type `A`. + */ + def toString[A](toF: A => String): ConfigWriter[A] = + new ConfigWriter[A] { + def to(t: A) = ConfigValueFactory.fromAnyRef(toF(t)) + } } diff --git a/core/src/main/scala/pureconfig/ConvertHelpers.scala b/core/src/main/scala/pureconfig/ConvertHelpers.scala index 4eb9fdcc0..485cf76bf 100644 --- a/core/src/main/scala/pureconfig/ConvertHelpers.scala +++ b/core/src/main/scala/pureconfig/ConvertHelpers.scala @@ -2,17 +2,19 @@ package pureconfig import scala.reflect.ClassTag import scala.util.control.NonFatal -import scala.util.{ Failure, Success, Try } +import scala.util.{Failure, Success, Try} import pureconfig.error._ /** - * Useful helpers for building `ConfigConvert` instances and dealing with results. - */ + * Useful helpers for building `ConfigConvert` instances and dealing with results. + */ trait ConvertHelpers { @deprecated("Use `ConfigReader.Result.zipWith` instead", "0.10.2") - def combineResults[A, B, C](first: ConfigReader.Result[A], second: ConfigReader.Result[B])(f: (A, B) => C): ConfigReader.Result[C] = + def combineResults[A, B, C](first: ConfigReader.Result[A], second: ConfigReader.Result[B])( + f: (A, B) => C + ): ConfigReader.Result[C] = ConfigReader.Result.zipWith(first, second)(f) @deprecated("Use `ConfigReader.Result.fail` instead", "0.10.2") @@ -21,10 +23,11 @@ trait ConvertHelpers { private[pureconfig] def toResult[A, B](f: A => B): A => Either[FailureReason, B] = v => tryToEither(Try(f(v))) - private[pureconfig] def tryToEither[A](t: Try[A]): Either[FailureReason, A] = t match { - case Success(v) => Right(v) - case Failure(e) => Left(ExceptionThrown(e)) - } + private[pureconfig] def tryToEither[A](t: Try[A]): Either[FailureReason, A] = + t match { + case Success(v) => Right(v) + case Failure(e) => Left(ExceptionThrown(e)) + } private[pureconfig] def ensureNonEmpty[A](implicit ct: ClassTag[A]): String => Either[FailureReason, String] = { case "" => Left(EmptyStringFound(ct.toString)) @@ -32,16 +35,17 @@ trait ConvertHelpers { } def catchReadError[A](f: String => A)(implicit ct: ClassTag[A]): String => Either[FailureReason, A] = { string => - try Right(f(string)) catch { + try Right(f(string)) + catch { case NonFatal(ex) => Left(CannotConvert(string, ct.toString(), ex.toString)) } } /** - * Convert a `String => Try` into a `String => Option[ConfigValueLocation] => Either` such that after application - * - `Success(t)` becomes `_ => Right(t)` - * - `Failure(e)` becomes `location => Left(CannotConvert(value, type, e.getMessage, location)` - */ + * Convert a `String => Try` into a `String => Option[ConfigValueLocation] => Either` such that after application + * - `Success(t)` becomes `_ => Right(t)` + * - `Failure(e)` becomes `location => Left(CannotConvert(value, type, e.getMessage, location)` + */ def tryF[A](f: String => Try[A])(implicit ct: ClassTag[A]): String => Either[FailureReason, A] = { string => f(string) match { case Success(t) => Right(t) @@ -50,10 +54,10 @@ trait ConvertHelpers { } /** - * Convert a `String => Option` into a `String => Option[ConfigValueLocation] => Either` such that after application - * - `Some(t)` becomes `_ => Right(t)` - * - `None` becomes `location => Left(CannotConvert(value, type, "", location)` - */ + * Convert a `String => Option` into a `String => Option[ConfigValueLocation] => Either` such that after application + * - `Some(t)` becomes `_ => Right(t)` + * - `None` becomes `location => Left(CannotConvert(value, type, "", location)` + */ def optF[A](f: String => Option[A])(implicit ct: ClassTag[A]): String => Either[FailureReason, A] = { string => f(string) match { case Some(t) => Right(t) diff --git a/core/src/main/scala/pureconfig/DurationUtils.scala b/core/src/main/scala/pureconfig/DurationUtils.scala index 86f44e1a6..b1cf62c8e 100644 --- a/core/src/main/scala/pureconfig/DurationUtils.scala +++ b/core/src/main/scala/pureconfig/DurationUtils.scala @@ -3,32 +3,45 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package pureconfig -import scala.concurrent.duration.Duration.{ Inf, MinusInf } -import scala.concurrent.duration.{ DAYS, Duration, FiniteDuration, HOURS, MICROSECONDS, MILLISECONDS, MINUTES, NANOSECONDS, SECONDS, TimeUnit } +import scala.concurrent.duration.Duration.{Inf, MinusInf} +import scala.concurrent.duration.{ + DAYS, + Duration, + FiniteDuration, + HOURS, + MICROSECONDS, + MILLISECONDS, + MINUTES, + NANOSECONDS, + SECONDS, + TimeUnit +} import scala.util.Try import scala.util.control.NonFatal -import pureconfig.error.{ CannotConvert, ExceptionThrown, FailureReason } +import pureconfig.error.{CannotConvert, ExceptionThrown, FailureReason} /** - * Utility functions for converting a `String` to a `Duration` and vice versa. The parser accepts the HOCON unit - * syntax. - */ + * Utility functions for converting a `String` to a `Duration` and vice versa. The parser accepts the HOCON unit + * syntax. + */ private[pureconfig] object DurationUtils { + /** - * Convert a string to a Duration while trying to maintain compatibility with Typesafe's abbreviations. - */ + * Convert a string to a Duration while trying to maintain compatibility with Typesafe's abbreviations. + */ val fromString: String => Either[FailureReason, Duration] = { string => if (string == UndefinedDuration) Right(Duration.Undefined) - else try { - Right(parseDuration(addDefaultUnit(justAMinute(itsGreekToMe(string))))) - } catch { - case ex: NumberFormatException => - val err = s"${ex.getMessage}. (try a number followed by any of ns, us, ms, s, m, h, d)" - Left(CannotConvert(string, "Duration", err)) - case NonFatal(t) => - Left(ExceptionThrown(t)) - } + else + try { + Right(parseDuration(addDefaultUnit(justAMinute(itsGreekToMe(string))))) + } catch { + case ex: NumberFormatException => + val err = s"${ex.getMessage}. (try a number followed by any of ns, us, ms, s, m, h, d)" + Left(CannotConvert(string, "Duration", err)) + case NonFatal(t) => + Left(ExceptionThrown(t)) + } } //////////////////////////////////// @@ -49,7 +62,8 @@ private[pureconfig] object DurationUtils { SECONDS -> "s sec second", MILLISECONDS -> "ms milli millisecond", MICROSECONDS -> "µs micro microsecond", - NANOSECONDS -> "ns nano nanosecond") + NANOSECONDS -> "ns nano nanosecond" + ) // Label => TimeUnit protected[pureconfig] val timeUnit: Map[String, TimeUnit] = @@ -86,15 +100,17 @@ private[pureconfig] object DurationUtils { private val addDefaultUnit = { s: String => if (onlyNumberRegex.unapplySeq(s).isDefined) s + " ms" else s } // To maintain compatibility with Typesafe Config, replace "us" with "µs". - private val itsGreekToMe = fauxMuRegex.replaceSomeIn(_: String, m => Some(s"${m.group(1)}${m.group(2)}µs${m.group(3)}")) + private val itsGreekToMe = + fauxMuRegex.replaceSomeIn(_: String, m => Some(s"${m.group(1)}${m.group(2)}µs${m.group(3)}")) // To maintain compatibility with Typesafe Config, replace "m" with "minutes". - private val justAMinute = shortMinuteRegex.replaceSomeIn(_: String, m => Some(s"${m.group(1)}${m.group(2)}minutes${m.group(3)}")) + private val justAMinute = + shortMinuteRegex.replaceSomeIn(_: String, m => Some(s"${m.group(1)}${m.group(2)}minutes${m.group(3)}")) /** - * Format a possibily infinite duration as a string with a suitable time unit using units TypesafeConfig understands. - * Caveat: TypesafeConfig doesn't undersand infinite durations - */ + * Format a possibily infinite duration as a string with a suitable time unit using units TypesafeConfig understands. + * Caveat: TypesafeConfig doesn't undersand infinite durations + */ def fromDuration(d: Duration): String = { d match { case f: FiniteDuration => fromFiniteDuration(f) @@ -110,16 +126,18 @@ private[pureconfig] object DurationUtils { private final val UndefinedDuration = "Undefined" /** - * Format a FiniteDuration as a string with a suitable time unit using units TypesafeConfig understands. - */ + * Format a FiniteDuration as a string with a suitable time unit using units TypesafeConfig understands. + */ def fromFiniteDuration(d: FiniteDuration): String = { d.toNanos match { case 0L => "0" case n => - timeUnitsToLabels.collectFirst { - case (unitInNanos, unitLabel) if n >= unitInNanos && n % unitInNanos == 0 => - s"${n / unitInNanos}$unitLabel" - }.getOrElse(s"${n}ns") + timeUnitsToLabels + .collectFirst { + case (unitInNanos, unitLabel) if n >= unitInNanos && n % unitInNanos == 0 => + s"${n / unitInNanos}$unitLabel" + } + .getOrElse(s"${n}ns") } } @@ -137,5 +155,6 @@ private[pureconfig] object DurationUtils { minuteInNanos -> "m", secondInNanos -> "s", millisecondInNanos -> "ms", - microsecondInNanos -> "us").sortBy(_._1)(implicitly[Ordering[Long]].reverse) + microsecondInNanos -> "us" + ).sortBy(_._1)(implicitly[Ordering[Long]].reverse) } diff --git a/core/src/main/scala/pureconfig/Exported.scala b/core/src/main/scala/pureconfig/Exported.scala index 2ffdf080f..af8551ab5 100644 --- a/core/src/main/scala/pureconfig/Exported.scala +++ b/core/src/main/scala/pureconfig/Exported.scala @@ -1,12 +1,12 @@ package pureconfig /** - * A wrapper for type class instances intended to allow implicits in scope by `import` statements to have lower - * priority than implicits in the relevant companion objects. This behavior requires type classes to provide a bridge - * in their companion objects, as done by `ConfigReader` (see [[pureconfig.ExportedReaders]]) and `ConfigWriter` (see - * [[pureconfig.ExportedWriters]]). - * - * @param instance the type class instance to wrap - * @tparam A the type class - */ + * A wrapper for type class instances intended to allow implicits in scope by `import` statements to have lower + * priority than implicits in the relevant companion objects. This behavior requires type classes to provide a bridge + * in their companion objects, as done by `ConfigReader` (see [[pureconfig.ExportedReaders]]) and `ConfigWriter` (see + * [[pureconfig.ExportedWriters]]). + * + * @param instance the type class instance to wrap + * @tparam A the type class + */ case class Exported[A](instance: A) extends AnyVal diff --git a/core/src/main/scala/pureconfig/ExportedReaders.scala b/core/src/main/scala/pureconfig/ExportedReaders.scala index c4d151782..c632c7dde 100644 --- a/core/src/main/scala/pureconfig/ExportedReaders.scala +++ b/core/src/main/scala/pureconfig/ExportedReaders.scala @@ -1,11 +1,11 @@ package pureconfig /** - * Trait allowing `ConfigReader` instances exported via `Exported` to be used. - * - * This trait should be the last to be mixed into `ConfigReader`'s companion object so that exported readers will - * always have lower precedence than `ConfigReader` instances exposed as implicits through the usual means. - */ + * Trait allowing `ConfigReader` instances exported via `Exported` to be used. + * + * This trait should be the last to be mixed into `ConfigReader`'s companion object so that exported readers will + * always have lower precedence than `ConfigReader` instances exposed as implicits through the usual means. + */ trait ExportedReaders { implicit def exportedReader[A](implicit exported: Exported[ConfigReader[A]]): ConfigReader[A] = exported.instance } diff --git a/core/src/main/scala/pureconfig/ExportedWriters.scala b/core/src/main/scala/pureconfig/ExportedWriters.scala index 0d6645260..e9b46a2d4 100644 --- a/core/src/main/scala/pureconfig/ExportedWriters.scala +++ b/core/src/main/scala/pureconfig/ExportedWriters.scala @@ -1,11 +1,11 @@ package pureconfig /** - * Trait allowing `ConfigWriter` instances exported via `Exported` to be used. - * - * This trait should be the last to be mixed into `ConfigWriter`'s companion object so that exported writers will - * always have lower precedence than `ConfigWriter` instances exposed as implicits through the usual means. - */ + * Trait allowing `ConfigWriter` instances exported via `Exported` to be used. + * + * This trait should be the last to be mixed into `ConfigWriter`'s companion object so that exported writers will + * always have lower precedence than `ConfigWriter` instances exposed as implicits through the usual means. + */ trait ExportedWriters { implicit def exportedWriter[A](implicit exported: Exported[ConfigWriter[A]]): ConfigWriter[A] = exported.instance } diff --git a/core/src/main/scala/pureconfig/FluentConfigCursor.scala b/core/src/main/scala/pureconfig/FluentConfigCursor.scala index f448b8ba6..ace50a073 100644 --- a/core/src/main/scala/pureconfig/FluentConfigCursor.scala +++ b/core/src/main/scala/pureconfig/FluentConfigCursor.scala @@ -1,124 +1,125 @@ package pureconfig /** - * A version of `ConfigCursor` with a more fluent interface, focused on config navigation instead of error handling. - * - * The `at` method, used to access object and list values, is available without a previous need to cast the cursor and - * always returns another cursor instead of a `ConfigReader.Result`. The error handling is left for the last step, where users - * can opt to cast to a primitive value using one of the `as` methods or by requesting a regular cursor with `cursor`. - * - * @param cursor the regular cursor pointed to by this object, wrapped itself into a `Right`, or a `Left` with a list of - * failures in case an error occurred during navigation - */ + * A version of `ConfigCursor` with a more fluent interface, focused on config navigation instead of error handling. + * + * The `at` method, used to access object and list values, is available without a previous need to cast the cursor and + * always returns another cursor instead of a `ConfigReader.Result`. The error handling is left for the last step, where users + * can opt to cast to a primitive value using one of the `as` methods or by requesting a regular cursor with `cursor`. + * + * @param cursor the regular cursor pointed to by this object, wrapped itself into a `Right`, or a `Left` with a list of + * failures in case an error occurred during navigation + */ case class FluentConfigCursor(cursor: ConfigReader.Result[ConfigCursor]) { /** - * Casts this cursor to a string. - * - * @return a `Right` with the string value pointed to by this cursor if the cast can be done, `Left` with a list of - * failures otherwise. - */ + * Casts this cursor to a string. + * + * @return a `Right` with the string value pointed to by this cursor if the cast can be done, `Left` with a list of + * failures otherwise. + */ def asString: ConfigReader.Result[String] = cursor.right.flatMap(_.asString) /** - * Casts this cursor to a boolean. - * - * @return a `Right` with the boolean value pointed to by this cursor if the cast can be done, `Left` with a list of - * failures otherwise. - */ + * Casts this cursor to a boolean. + * + * @return a `Right` with the boolean value pointed to by this cursor if the cast can be done, `Left` with a list of + * failures otherwise. + */ def asBoolean: ConfigReader.Result[Boolean] = cursor.right.flatMap(_.asBoolean) /** - * Casts this cursor to a long. - * - * @return a `Right` with the long value pointed to by this cursor if the cast can be done, `Left` with a list of - * failures otherwise. - */ + * Casts this cursor to a long. + * + * @return a `Right` with the long value pointed to by this cursor if the cast can be done, `Left` with a list of + * failures otherwise. + */ def asLong: ConfigReader.Result[Long] = cursor.right.flatMap(_.asLong) /** - * Casts this cursor to an int. - * - * @return a `Right` with the int value pointed to by this cursor if the cast can be done, `Left` with a list of - * failures otherwise. - */ + * Casts this cursor to an int. + * + * @return a `Right` with the int value pointed to by this cursor if the cast can be done, `Left` with a list of + * failures otherwise. + */ def asInt: ConfigReader.Result[Int] = cursor.right.flatMap(_.asInt) /** - * Casts this cursor to a short. - * - * @return a `Right` with the short value pointed to by this cursor if the cast can be done, `Left` with a list of - * failures otherwise. - */ + * Casts this cursor to a short. + * + * @return a `Right` with the short value pointed to by this cursor if the cast can be done, `Left` with a list of + * failures otherwise. + */ def asShort: ConfigReader.Result[Short] = cursor.right.flatMap(_.asShort) /** - * Casts this cursor to a double. - * - * @return a `Right` with the double value pointed to by this cursor if the cast can be done, `Left` with a list of - * failures otherwise. - */ + * Casts this cursor to a double. + * + * @return a `Right` with the double value pointed to by this cursor if the cast can be done, `Left` with a list of + * failures otherwise. + */ def asDouble: ConfigReader.Result[Double] = cursor.right.flatMap(_.asDouble) /** - * Casts this cursor to a float. - * - * @return a `Right` with the float value pointed to by this cursor if the cast can be done, `Left` with a list of - * failures otherwise. - */ + * Casts this cursor to a float. + * + * @return a `Right` with the float value pointed to by this cursor if the cast can be done, `Left` with a list of + * failures otherwise. + */ def asFloat: ConfigReader.Result[Float] = cursor.right.flatMap(_.asFloat) /** - * Casts this cursor to a `ConfigListCursor`. - * - * @return a `Right` with this cursor as a list cursor if the cast can be done, `Left` with a list of failures - * otherwise. - */ + * Casts this cursor to a `ConfigListCursor`. + * + * @return a `Right` with this cursor as a list cursor if the cast can be done, `Left` with a list of failures + * otherwise. + */ def asListCursor: ConfigReader.Result[ConfigListCursor] = cursor.right.flatMap(_.asListCursor) /** - * Casts this cursor to a `ConfigObjectCursor`. - * - * @return a `Right` with this cursor as an object cursor if it points to an object, `Left` with a list of failures - * otherwise. - */ + * Casts this cursor to a `ConfigObjectCursor`. + * + * @return a `Right` with this cursor as an object cursor if it points to an object, `Left` with a list of failures + * otherwise. + */ def asObjectCursor: ConfigReader.Result[ConfigObjectCursor] = cursor.right.flatMap(_.asObjectCursor) /** - * Returns a cursor to the config at a given path. - * - * @param segments the path of the config for which a cursor should be returned - * @return a `FluentConfigCursor` pointing to the provided path. - */ - def at(segments: PathSegment*): FluentConfigCursor = FluentConfigCursor { - segments.foldLeft(this.cursor) { - case (Right(cur), PathSegment.Key(k)) => cur.asObjectCursor.right.flatMap(_.atKey(k)) - case (Right(cur), PathSegment.Index(i)) => cur.asListCursor.right.flatMap(_.atIndex(i)) - case (Left(err), _) => Left(err) + * Returns a cursor to the config at a given path. + * + * @param segments the path of the config for which a cursor should be returned + * @return a `FluentConfigCursor` pointing to the provided path. + */ + def at(segments: PathSegment*): FluentConfigCursor = + FluentConfigCursor { + segments.foldLeft(this.cursor) { + case (Right(cur), PathSegment.Key(k)) => cur.asObjectCursor.right.flatMap(_.atKey(k)) + case (Right(cur), PathSegment.Index(i)) => cur.asListCursor.right.flatMap(_.atIndex(i)) + case (Left(err), _) => Left(err) + } } - } /** - * Casts this cursor to a `ConfigListCursor` and maps each element to a result. This method tries to map all - * elements, combining failures from all of them if more than one exists. - * - * @param f the function used to map elements - * @tparam A the result type of the elements - * @return a `Right` with the list obtained by mapping all elements of the list pointed to by this cursor if all - * casts and mappings can be done, `Left` with a list of failures otherwise. - */ + * Casts this cursor to a `ConfigListCursor` and maps each element to a result. This method tries to map all + * elements, combining failures from all of them if more than one exists. + * + * @param f the function used to map elements + * @tparam A the result type of the elements + * @return a `Right` with the list obtained by mapping all elements of the list pointed to by this cursor if all + * casts and mappings can be done, `Left` with a list of failures otherwise. + */ def mapList[A](f: ConfigCursor => ConfigReader.Result[A]): ConfigReader.Result[List[A]] = asListCursor.right.flatMap { listCur => ConfigReader.Result.sequence(listCur.list.map(f)) } /** - * Casts this cursor to a `ConfigObjectCursor` and maps each value to a result. This method tries to map all - * elements, combining failures from all of them if more than one exists. - * - * @param f the function used to map values - * @tparam A the result type of the values - * @return a `Right` with the map obtained by mapping all values of the object pointed to by this cursor if all - * casts and mappings can be done, `Left` with a list of failures otherwise. - */ + * Casts this cursor to a `ConfigObjectCursor` and maps each value to a result. This method tries to map all + * elements, combining failures from all of them if more than one exists. + * + * @param f the function used to map values + * @tparam A the result type of the values + * @return a `Right` with the map obtained by mapping all values of the object pointed to by this cursor if all + * casts and mappings can be done, `Left` with a list of failures otherwise. + */ def mapObject[A](f: ConfigCursor => ConfigReader.Result[A]): ConfigReader.Result[Map[String, A]] = asObjectCursor.right.flatMap { objCur => val kvResults = objCur.map.map { case (key, cur) => f(cur).right.map((key, _)) } diff --git a/core/src/main/scala/pureconfig/NamingConvention.scala b/core/src/main/scala/pureconfig/NamingConvention.scala index e1d2fc849..1061f7481 100644 --- a/core/src/main/scala/pureconfig/NamingConvention.scala +++ b/core/src/main/scala/pureconfig/NamingConvention.scala @@ -12,17 +12,14 @@ trait CapitalizedWordsNamingConvention extends NamingConvention { } object CapitalizedWordsNamingConvention { - private val wordBreakPattern = String.format( - "%s|%s|%s", - "(?<=[A-Z])(?=[A-Z][a-z])", - "(?<=[^A-Z])(?=[A-Z])", - "(?<=[A-Za-z])(?=[^A-Za-z])").r + private val wordBreakPattern = + String.format("%s|%s|%s", "(?<=[A-Z])(?=[A-Z][a-z])", "(?<=[^A-Z])(?=[A-Z])", "(?<=[A-Za-z])(?=[^A-Za-z])").r } /** - * CamelCase identifiers look like `camelCase` and `useMorePureconfig` - * @see https://en.wikipedia.org/wiki/Camel_case - */ + * CamelCase identifiers look like `camelCase` and `useMorePureconfig` + * @see https://en.wikipedia.org/wiki/Camel_case + */ object CamelCase extends CapitalizedWordsNamingConvention { def fromTokens(l: Seq[String]): String = { l match { @@ -34,9 +31,9 @@ object CamelCase extends CapitalizedWordsNamingConvention { } /** - * PascalCase identifiers look like e.g.`PascalCase` and `UseMorePureconfig` - * @see https://en.wikipedia.org/wiki/PascalCase - */ + * PascalCase identifiers look like e.g.`PascalCase` and `UseMorePureconfig` + * @see https://en.wikipedia.org/wiki/PascalCase + */ object PascalCase extends CapitalizedWordsNamingConvention { def fromTokens(l: Seq[String]): String = l.map(_.capitalize).mkString } @@ -50,13 +47,13 @@ class StringDelimitedNamingConvention(d: String) extends NamingConvention { } /** - * KebabCase identifiers look like `kebab-case` and `use-more-pureconfig` - * @see http://wiki.c2.com/?KebabCase - */ + * KebabCase identifiers look like `kebab-case` and `use-more-pureconfig` + * @see http://wiki.c2.com/?KebabCase + */ object KebabCase extends StringDelimitedNamingConvention("-") /** - * SnakeCase identifiers look like `snake_case` and `use_more_pureconfig` - * @see https://en.wikipedia.org/wiki/Snake_case - */ + * SnakeCase identifiers look like `snake_case` and `use_more_pureconfig` + * @see https://en.wikipedia.org/wiki/Snake_case + */ object SnakeCase extends StringDelimitedNamingConvention("_") diff --git a/core/src/main/scala/pureconfig/PeriodUtils.scala b/core/src/main/scala/pureconfig/PeriodUtils.scala index 5f3a7d305..35a4715a1 100644 --- a/core/src/main/scala/pureconfig/PeriodUtils.scala +++ b/core/src/main/scala/pureconfig/PeriodUtils.scala @@ -2,19 +2,19 @@ package pureconfig import java.time.Period -import scala.util.{ Success, Try } +import scala.util.{Success, Try} import com.typesafe.config.impl.ConfigImplUtil -import pureconfig.error.{ CannotConvert, FailureReason } +import pureconfig.error.{CannotConvert, FailureReason} /** - * Utility functions for converting a `String` to a `Period`. The parser accepts the HOCON unit syntax. - */ + * Utility functions for converting a `String` to a `Period`. The parser accepts the HOCON unit syntax. + */ private[pureconfig] object PeriodUtils { /** - * Convert a string to a Period while trying to maintain compatibility with Typesafe's abbreviations. - */ + * Convert a string to a Period while trying to maintain compatibility with Typesafe's abbreviations. + */ val fromString: String => Either[FailureReason, Period] = { str => Try(Right(Period.parse(str))).getOrElse(typesafeConfigParsePeriod(str)) } diff --git a/core/src/main/scala/pureconfig/backend/ConfigFactoryWrapper.scala b/core/src/main/scala/pureconfig/backend/ConfigFactoryWrapper.scala index d193b2c72..6634f21ce 100644 --- a/core/src/main/scala/pureconfig/backend/ConfigFactoryWrapper.scala +++ b/core/src/main/scala/pureconfig/backend/ConfigFactoryWrapper.scala @@ -10,9 +10,9 @@ import pureconfig.backend.ErrorUtil._ import pureconfig.error._ /** - * A wrapper of `com.typesafe.config.ConfigFactory` whose methods return [[scala.Either]] instead - * of throwing exceptions - */ + * A wrapper of `com.typesafe.config.ConfigFactory` whose methods return [[scala.Either]] instead + * of throwing exceptions + */ object ConfigFactoryWrapper { private[this] val strictSettings = ConfigParseOptions.defaults.setAllowMissing(false) @@ -52,25 +52,26 @@ object ConfigFactoryWrapper { def parseFile(file: File): ConfigReader.Result[Config] = unsafeToReaderResult( ConfigFactory.parseFile(file, strictSettings), - onIOFailure = Some(CannotReadFile(file.toPath, _))) + onIOFailure = Some(CannotReadFile(file.toPath, _)) + ) /** @see `com.typesafe.config.ConfigFactory.parseFile()` */ def parseFile(path: Path): ConfigReader.Result[Config] = unsafeToReaderResult( ConfigFactory.parseFile(path.toFile, strictSettings), - onIOFailure = Some(CannotReadFile(path, _))) + onIOFailure = Some(CannotReadFile(path, _)) + ) /** @see `com.typesafe.config.ConfigFactory.parseResources()` */ def parseResources(resource: String, classLoader: ClassLoader = null): ConfigReader.Result[Config] = unsafeToReaderResult( ConfigFactory.parseResources(resource, strictSettings.setClassLoader(classLoader)), - onIOFailure = Some(CannotReadResource(resource, _))) + onIOFailure = Some(CannotReadResource(resource, _)) + ) /** @see `com.typesafe.config.ConfigFactory.parseURL()` */ def parseURL(url: URL): ConfigReader.Result[Config] = - unsafeToReaderResult( - ConfigFactory.parseURL(url, strictSettings), - onIOFailure = Some(CannotReadUrl(url, _))) + unsafeToReaderResult(ConfigFactory.parseURL(url, strictSettings), onIOFailure = Some(CannotReadUrl(url, _))) /** Utility methods that parse a file and then calls `ConfigFactory.load` */ def loadFile(path: Path): ConfigReader.Result[Config] = diff --git a/core/src/main/scala/pureconfig/backend/ConfigWrapper.scala b/core/src/main/scala/pureconfig/backend/ConfigWrapper.scala index 549e85eb7..4a6440b9b 100644 --- a/core/src/main/scala/pureconfig/backend/ConfigWrapper.scala +++ b/core/src/main/scala/pureconfig/backend/ConfigWrapper.scala @@ -5,9 +5,9 @@ import pureconfig.ConfigReader.Result import pureconfig.backend.ErrorUtil._ /** - * Provides extension methods for `com.typesafe.config.Config` that return [[scala.Either]] instead - * of throwing exceptions. - */ + * Provides extension methods for `com.typesafe.config.Config` that return [[scala.Either]] instead + * of throwing exceptions. + */ object ConfigWrapper { implicit class SafeConfig(val conf: Config) extends AnyVal { diff --git a/core/src/main/scala/pureconfig/backend/ErrorUtil.scala b/core/src/main/scala/pureconfig/backend/ErrorUtil.scala index a4030b936..79b28ac56 100644 --- a/core/src/main/scala/pureconfig/backend/ErrorUtil.scala +++ b/core/src/main/scala/pureconfig/backend/ErrorUtil.scala @@ -7,21 +7,25 @@ import pureconfig._ import pureconfig.error._ /** - * Contains common utilities to deal with exceptions in unsafe methods. - */ + * Contains common utilities to deal with exceptions in unsafe methods. + */ object ErrorUtil { - def unsafeToReaderResult[A](f: => A, onIOFailure: Option[Option[Throwable] => CannotRead] = None): ConfigReader.Result[A] = { - try Right(f) catch { + def unsafeToReaderResult[A]( + f: => A, + onIOFailure: Option[Option[Throwable] => CannotRead] = None + ): ConfigReader.Result[A] = { + try Right(f) + catch { case e: ConfigException.IO if onIOFailure.nonEmpty => ConfigReader.Result.fail(onIOFailure.get(Option(e.getCause))) case e: ConfigException.Parse => val msg = (if (e.origin != null) - // Removing the error origin from the exception message since origin is stored and used separately: - e.getMessage.stripPrefix(s"${e.origin.description}: ") - else - e.getMessage).stripSuffix(".") + // Removing the error origin from the exception message since origin is stored and used separately: + e.getMessage.stripPrefix(s"${e.origin.description}: ") + else + e.getMessage).stripSuffix(".") ConfigReader.Result.fail(CannotParse(msg, Some(e.origin()))) case e: ConfigException => diff --git a/core/src/main/scala/pureconfig/backend/PathUtil.scala b/core/src/main/scala/pureconfig/backend/PathUtil.scala index 4295623b0..9449e3101 100644 --- a/core/src/main/scala/pureconfig/backend/PathUtil.scala +++ b/core/src/main/scala/pureconfig/backend/PathUtil.scala @@ -5,26 +5,26 @@ import scala.collection.JavaConverters._ import com.typesafe.config.ConfigUtil /** - * An utility class to convert Typesafe Config path expressions to lists of keys and vice-versa, accepting empty path - * expressions and lists in addition to the values supported by `ConfigUtil`. - */ + * An utility class to convert Typesafe Config path expressions to lists of keys and vice-versa, accepting empty path + * expressions and lists in addition to the values supported by `ConfigUtil`. + */ object PathUtil { /** - * Parses a path expression into a list of keys from the root to the leaf value. - * - * @param path the path to parse - * @return a list of keys, from the root to the leaf value, representing the given namespace. - */ + * Parses a path expression into a list of keys from the root to the leaf value. + * + * @param path the path to parse + * @return a list of keys, from the root to the leaf value, representing the given namespace. + */ def splitPath(path: String): List[String] = if (path.isEmpty) Nil else ConfigUtil.splitPath(path).asScala.toList /** - * Converts a list of keys to a path expression compatible with the `get` methods in `Config`. - * - * @param elements the list of keys to convert to a string - * @return a path string compatible with the `get` methods in `Config`. - */ + * Converts a list of keys to a path expression compatible with the `get` methods in `Config`. + * + * @param elements the list of keys to convert to a string + * @return a path string compatible with the `get` methods in `Config`. + */ def joinPath(elements: List[String]): String = if (elements.isEmpty) "" else ConfigUtil.joinPath(elements: _*) } diff --git a/core/src/main/scala/pureconfig/configurable/package.scala b/core/src/main/scala/pureconfig/configurable/package.scala index 3afc381db..a038c223d 100644 --- a/core/src/main/scala/pureconfig/configurable/package.scala +++ b/core/src/main/scala/pureconfig/configurable/package.scala @@ -4,75 +4,74 @@ import java.time._ import java.time.format.DateTimeFormatter import com.typesafe.config.ConfigValueFactory -import pureconfig.ConfigConvert.{ catchReadError, viaNonEmptyString } +import pureconfig.ConfigConvert.{catchReadError, viaNonEmptyString} import pureconfig.error.FailureReason import scala.collection.JavaConverters._ /** - * Provides methods that create [[ConfigConvert]] instances from a set of parameters used to configure the instances. - * - * The result of calling one of the methods can be assigned to an `implicit val` so that `pureconfig` will be able to - * use it: - * {{{ - * implicit val localDateConfigConvert = makeLocalDateConfigConvert(DateTimeFormatter.ISO_TIME) - * }}} - * - * @example we cannot provide a [[ConfigConvert]] for [[java.time.LocalDate]] because traditionally there are many different - * [[java.time.format.DateTimeFormatter]]s to parse a [[java.time.LocalDate]] from a [[java.lang.String]]. This package - * provides a method that takes an input [[java.time.format.DateTimeFormatter]] and returns a [[ConfigConvert]] for - * [[java.time.LocalDate]] which will use that [[java.time.format.DateTimeFormatter]] to parse a [[java.time.LocalDate]]. - */ + * Provides methods that create [[ConfigConvert]] instances from a set of parameters used to configure the instances. + * + * The result of calling one of the methods can be assigned to an `implicit val` so that `pureconfig` will be able to + * use it: + * {{{ + * implicit val localDateConfigConvert = makeLocalDateConfigConvert(DateTimeFormatter.ISO_TIME) + * }}} + * + * @example we cannot provide a [[ConfigConvert]] for [[java.time.LocalDate]] because traditionally there are many different + * [[java.time.format.DateTimeFormatter]]s to parse a [[java.time.LocalDate]] from a [[java.lang.String]]. This package + * provides a method that takes an input [[java.time.format.DateTimeFormatter]] and returns a [[ConfigConvert]] for + * [[java.time.LocalDate]] which will use that [[java.time.format.DateTimeFormatter]] to parse a [[java.time.LocalDate]]. + */ package object configurable { def localDateConfigConvert(formatter: DateTimeFormatter): ConfigConvert[LocalDate] = - viaNonEmptyString[LocalDate]( - catchReadError(LocalDate.parse(_, formatter)), _.format(formatter)) + viaNonEmptyString[LocalDate](catchReadError(LocalDate.parse(_, formatter)), _.format(formatter)) def localTimeConfigConvert(formatter: DateTimeFormatter): ConfigConvert[LocalTime] = - viaNonEmptyString[LocalTime]( - catchReadError(LocalTime.parse(_, formatter)), _.format(formatter)) + viaNonEmptyString[LocalTime](catchReadError(LocalTime.parse(_, formatter)), _.format(formatter)) def localDateTimeConfigConvert(formatter: DateTimeFormatter): ConfigConvert[LocalDateTime] = - viaNonEmptyString[LocalDateTime]( - catchReadError(LocalDateTime.parse(_, formatter)), _.format(formatter)) + viaNonEmptyString[LocalDateTime](catchReadError(LocalDateTime.parse(_, formatter)), _.format(formatter)) def monthDayConfigConvert(formatter: DateTimeFormatter): ConfigConvert[MonthDay] = - viaNonEmptyString[MonthDay]( - catchReadError(MonthDay.parse(_, formatter)), _.format(formatter)) + viaNonEmptyString[MonthDay](catchReadError(MonthDay.parse(_, formatter)), _.format(formatter)) def offsetDateTimeConfigConvert(formatter: DateTimeFormatter): ConfigConvert[OffsetDateTime] = - viaNonEmptyString[OffsetDateTime]( - catchReadError(OffsetDateTime.parse(_, formatter)), _.format(formatter)) + viaNonEmptyString[OffsetDateTime](catchReadError(OffsetDateTime.parse(_, formatter)), _.format(formatter)) def offsetTimeConfigConvert(formatter: DateTimeFormatter): ConfigConvert[OffsetTime] = - viaNonEmptyString[OffsetTime]( - catchReadError(OffsetTime.parse(_, formatter)), _.format(formatter)) + viaNonEmptyString[OffsetTime](catchReadError(OffsetTime.parse(_, formatter)), _.format(formatter)) def yearMonthConfigConvert(formatter: DateTimeFormatter): ConfigConvert[YearMonth] = - viaNonEmptyString[YearMonth]( - catchReadError(YearMonth.parse(_, formatter)), _.format(formatter)) + viaNonEmptyString[YearMonth](catchReadError(YearMonth.parse(_, formatter)), _.format(formatter)) def zonedDateTimeConfigConvert(formatter: DateTimeFormatter): ConfigConvert[ZonedDateTime] = - viaNonEmptyString[ZonedDateTime]( - catchReadError(ZonedDateTime.parse(_, formatter)), _.format(formatter)) + viaNonEmptyString[ZonedDateTime](catchReadError(ZonedDateTime.parse(_, formatter)), _.format(formatter)) - def genericMapReader[K, V](keyParser: String => Either[FailureReason, K])(implicit readerV: Derivation[ConfigReader[V]]): ConfigReader[Map[K, V]] = + def genericMapReader[K, V]( + keyParser: String => Either[FailureReason, K] + )(implicit readerV: Derivation[ConfigReader[V]]): ConfigReader[Map[K, V]] = ConfigReader.fromCursor { cursor => cursor.asMap.right.flatMap { map => map.foldLeft[ConfigReader.Result[Map[K, V]]](Right(Map.empty)) { case (acc, (key, valueCursor)) => val eitherKeyOrError = cursor.scopeFailure(keyParser(key)) val eitherValueOrError = readerV.value.from(valueCursor) - ConfigReader.Result.zipWith(acc, ConfigReader.Result.zipWith(eitherKeyOrError, eitherValueOrError)(_ -> _))(_ + _) + ConfigReader.Result.zipWith(acc, ConfigReader.Result.zipWith(eitherKeyOrError, eitherValueOrError)(_ -> _))( + _ + _ + ) } } } - def genericMapWriter[K, V](keyFormatter: K => String)(implicit writerV: Derivation[ConfigWriter[V]]): ConfigWriter[Map[K, V]] = + def genericMapWriter[K, V]( + keyFormatter: K => String + )(implicit writerV: Derivation[ConfigWriter[V]]): ConfigWriter[Map[K, V]] = ConfigWriter.fromFunction[Map[K, V]](map => ConfigValueFactory.fromMap(map.map { case (key, value) => keyFormatter(key) -> writerV.value.to(value) - }.asJava)) + }.asJava) + ) } diff --git a/core/src/main/scala/pureconfig/error/ConfigReaderException.scala b/core/src/main/scala/pureconfig/error/ConfigReaderException.scala index 77d642ec5..5786b3778 100644 --- a/core/src/main/scala/pureconfig/error/ConfigReaderException.scala +++ b/core/src/main/scala/pureconfig/error/ConfigReaderException.scala @@ -6,7 +6,8 @@ package pureconfig.error import scala.collection.mutable import scala.reflect.ClassTag -final case class ConfigReaderException[A](failures: ConfigReaderFailures)(implicit ct: ClassTag[A]) extends RuntimeException { +final case class ConfigReaderException[A](failures: ConfigReaderFailures)(implicit ct: ClassTag[A]) + extends RuntimeException { override def getMessage: String = { val linesBuffer = mutable.Buffer.empty[String] diff --git a/core/src/main/scala/pureconfig/error/ConfigReaderFailure.scala b/core/src/main/scala/pureconfig/error/ConfigReaderFailure.scala index b0e2ae023..7c13dc181 100644 --- a/core/src/main/scala/pureconfig/error/ConfigReaderFailure.scala +++ b/core/src/main/scala/pureconfig/error/ConfigReaderFailure.scala @@ -11,32 +11,32 @@ import com.typesafe.config.ConfigOrigin import pureconfig.ConfigCursor /** - * A representation of a failure raised from reading a config. The failure contains an optional file system location of - * the configuration that raised the failure. - */ + * A representation of a failure raised from reading a config. The failure contains an optional file system location of + * the configuration that raised the failure. + */ trait ConfigReaderFailure { /** - * A human-readable description of the failure. - */ + * A human-readable description of the failure. + */ def description: String /** - * The optional origin of the failure. - */ + * The optional origin of the failure. + */ def origin: Option[ConfigOrigin] } /** - * A failure occurred when converting from a `ConfigValue` to a given type. The failure contains a path to the - * `ConfigValue` that raised the error. - * - * @param reason the reason for the conversion failure - * @param origin the optional origin of the failure - * @param path the path to the `ConfigValue` that raised the error - */ + * A failure occurred when converting from a `ConfigValue` to a given type. The failure contains a path to the + * `ConfigValue` that raised the error. + * + * @param reason the reason for the conversion failure + * @param origin the optional origin of the failure + * @param path the path to the `ConfigValue` that raised the error + */ case class ConvertFailure(reason: FailureReason, origin: Option[ConfigOrigin], path: String) - extends ConfigReaderFailure { + extends ConfigReaderFailure { def description = reason.description } @@ -44,19 +44,19 @@ case class ConvertFailure(reason: FailureReason, origin: Option[ConfigOrigin], p object ConvertFailure { /** - * Constructs a `ConvertFailure` from a reason and a `ConfigCursor`. - * - * @param reason the reason for the conversion failure - * @param cur the cursor where the failure ocurred - * @return a `ConvertFailure` for the given reason at the given cursor. - */ + * Constructs a `ConvertFailure` from a reason and a `ConfigCursor`. + * + * @param reason the reason for the conversion failure + * @param cur the cursor where the failure ocurred + * @return a `ConvertFailure` for the given reason at the given cursor. + */ def apply(reason: FailureReason, cur: ConfigCursor): ConvertFailure = ConvertFailure(reason, cur.origin, cur.path) } /** - * A failure occurred because a list of files to load was empty. - */ + * A failure occurred because a list of files to load was empty. + */ @deprecated("`loadConfigFromFiles` won't return this failure anymore", "0.10.1") case object NoFilesToRead extends ConfigReaderFailure { def description = "The config files to load must not be empty." @@ -64,86 +64,87 @@ case object NoFilesToRead extends ConfigReaderFailure { } /** - * A failure occurred because an exception was thrown during the reading process. - * - * @param throwable the exception thrown - * @param origin the optional origin of the failure - */ -final case class ThrowableFailure(throwable: Throwable, origin: Option[ConfigOrigin]) - extends ConfigReaderFailure { + * A failure occurred because an exception was thrown during the reading process. + * + * @param throwable the exception thrown + * @param origin the optional origin of the failure + */ +final case class ThrowableFailure(throwable: Throwable, origin: Option[ConfigOrigin]) extends ConfigReaderFailure { def description = s"${throwable.getMessage}." } /** - * A failure occurred due to the inability to read a requested source (such as a file, a resource or a network - * location). - */ + * A failure occurred due to the inability to read a requested source (such as a file, a resource or a network + * location). + */ trait CannotRead extends ConfigReaderFailure { /** - * The source name (like a file path or a URL) - */ + * The source name (like a file path or a URL) + */ def sourceName: String /** - * The source type - */ + * The source type + */ def sourceType: String /** - * An optional exception thrown when trying to read the source - */ + * An optional exception thrown when trying to read the source + */ def reason: Option[Throwable] - def description = reason match { - case Some(ex: FileNotFoundException) => s"Unable to read $sourceType ${ex.getMessage}." // a FileNotFoundException already includes the path - case Some(ex) => s"Unable to read $sourceType $sourceName (${ex.getMessage})." - case None => s"Unable to read $sourceType $sourceName." - } + def description = + reason match { + case Some(ex: FileNotFoundException) => + s"Unable to read $sourceType ${ex.getMessage}." // a FileNotFoundException already includes the path + case Some(ex) => s"Unable to read $sourceType $sourceName (${ex.getMessage})." + case None => s"Unable to read $sourceType $sourceName." + } def origin = None } /** - * A failure occurred due to the inability to read a requested file. - * - * @param path the file system path of the file that couldn't be read - * @param reason an optional exception thrown when trying to read the file - */ + * A failure occurred due to the inability to read a requested file. + * + * @param path the file system path of the file that couldn't be read + * @param reason an optional exception thrown when trying to read the file + */ final case class CannotReadFile(path: Path, reason: Option[Throwable]) extends CannotRead { val sourceType = "file" def sourceName = path.toString } /** - * A failure occurred due to the inability to read a requested URL. - * - * @param url the URL that couldn't be read - * @param reason an optional exception thrown when trying to read the URL - */ + * A failure occurred due to the inability to read a requested URL. + * + * @param url the URL that couldn't be read + * @param reason an optional exception thrown when trying to read the URL + */ final case class CannotReadUrl(url: URL, reason: Option[Throwable]) extends CannotRead { val sourceType = "URL" def sourceName = url.toString } /** - * A failure occurred due to the inability to read a requested resource. - * - * @param resourceName the resource that couldn't be read - * @param reason an optional exception thrown when trying to read the resource - */ + * A failure occurred due to the inability to read a requested resource. + * + * @param resourceName the resource that couldn't be read + * @param reason an optional exception thrown when trying to read the resource + */ final case class CannotReadResource(resourceName: String, reason: Option[Throwable]) extends CannotRead { val sourceType = "resource" def sourceName = resourceName } /** - * A failure occurred due to the inability to parse the configuration. - * - * @param msg the error message from the parser - * @param origin the optional origin of the failure - */ + * A failure occurred due to the inability to parse the configuration. + * + * @param msg the error message from the parser + * @param origin the optional origin of the failure + */ final case class CannotParse(msg: String, origin: Option[ConfigOrigin]) extends ConfigReaderFailure { def description = s"Unable to parse the configuration: $msg." } diff --git a/core/src/main/scala/pureconfig/error/ConfigReaderFailures.scala b/core/src/main/scala/pureconfig/error/ConfigReaderFailures.scala index 270ad32d3..4633ef7d9 100644 --- a/core/src/main/scala/pureconfig/error/ConfigReaderFailures.scala +++ b/core/src/main/scala/pureconfig/error/ConfigReaderFailures.scala @@ -6,8 +6,8 @@ package pureconfig.error import scala.collection.mutable /** - * A non-empty list of ConfigReader failures - */ + * A non-empty list of ConfigReader failures + */ case class ConfigReaderFailures(head: ConfigReaderFailure, tail: ConfigReaderFailure*) { def toList: List[ConfigReaderFailure] = head :: tail.toList @@ -22,7 +22,9 @@ case class ConfigReaderFailures(head: ConfigReaderFailure, tail: ConfigReaderFai def tabs(n: Int): String = " " * ((indentLevel + n) * 2) def descriptionWithOrigin(failure: ConfigReaderFailure, indent: Int): String = { val failureLines = failure.description.split("\n") - (failure.origin.fold(s"${tabs(indent)}- ${failureLines.head}")(f => s"${tabs(indent)}- (${f.description}) ${failureLines.head}") :: + (failure.origin.fold(s"${tabs(indent)}- ${failureLines.head}")(f => + s"${tabs(indent)}- (${f.description}) ${failureLines.head}" + ) :: failureLines.tail.map(l => s"${tabs(indent + 1)}$l").toList).mkString("\n") } diff --git a/core/src/main/scala/pureconfig/error/FailureReason.scala b/core/src/main/scala/pureconfig/error/FailureReason.scala index 8f92a6310..31431e071 100644 --- a/core/src/main/scala/pureconfig/error/FailureReason.scala +++ b/core/src/main/scala/pureconfig/error/FailureReason.scala @@ -3,48 +3,49 @@ package pureconfig.error import scala.annotation.tailrec import scala.collection.mutable -import com.typesafe.config.{ ConfigValue, ConfigValueType } +import com.typesafe.config.{ConfigValue, ConfigValueType} /** - * A representation of a reason why a value failed to be converted. - */ + * A representation of a reason why a value failed to be converted. + */ trait FailureReason { /** - * A human-readable description of the failure. - */ + * A human-readable description of the failure. + */ def description: String } /** - * A general reason given for the failure of a value to be converted to a desired type. - * - * @param value the value that was requested to be converted - * @param toType the target type that the value was requested to be converted to - * @param because the reason why the conversion was not possible - */ + * A general reason given for the failure of a value to be converted to a desired type. + * + * @param value the value that was requested to be converted + * @param toType the target type that the value was requested to be converted to + * @param because the reason why the conversion was not possible + */ final case class CannotConvert(value: String, toType: String, because: String) extends FailureReason { def description = s"Cannot convert '$value' to $toType: $because." } /** - * A failure reason given when there is a collision of keys with different semantics. This error is raised when a key - * that should be used to disambiguate a coproduct is mapped to a field in a product. - * - * @param key the colliding key - * @param existingValue the value of the key - */ + * A failure reason given when there is a collision of keys with different semantics. This error is raised when a key + * that should be used to disambiguate a coproduct is mapped to a field in a product. + * + * @param key the colliding key + * @param existingValue the value of the key + */ final case class CollidingKeys(key: String, existingValue: ConfigValue) extends FailureReason { - def description = s"Key with value '{$existingValue.render(ConfigRenderOptions.concise)}' collides with a key necessary to disambiguate a coproduct." + def description = + s"Key with value '{$existingValue.render(ConfigRenderOptions.concise)}' collides with a key necessary to disambiguate a coproduct." } /** - * A failure reason given when a key is missing from a `ConfigObject` or `ConfigList`. - * - * @param key the key that is missing - * @param candidates a set of candidate keys that might correspond to the - * desired key in case of a misconfigured ProductHint - */ + * A failure reason given when a key is missing from a `ConfigObject` or `ConfigList`. + * + * @param key the key that is missing + * @param candidates a set of candidate keys that might correspond to the + * desired key in case of a misconfigured ProductHint + */ final case class KeyNotFound(key: String, candidates: Set[String] = Set()) extends FailureReason { def description = { if (candidates.nonEmpty) { @@ -82,71 +83,74 @@ object KeyNotFound { } /** - * A failure reason given when an unknown key is found in a `ConfigObject`. The failure is raised when a key of a - * `ConfigObject` is not mapped into a field of a given type and the `allowUnknownKeys` property of the `ProductHint` - * for the type in question is `false`. - * - * @param key the unknown key - */ + * A failure reason given when an unknown key is found in a `ConfigObject`. The failure is raised when a key of a + * `ConfigObject` is not mapped into a field of a given type and the `allowUnknownKeys` property of the `ProductHint` + * for the type in question is `false`. + * + * @param key the unknown key + */ final case class UnknownKey(key: String) extends FailureReason { def description = s"Unknown key." } /** - * A failure reason given when a `ConfigValue` has the wrong type. - * - * @param foundType the `ConfigValueType` that was found - * @param expectedTypes the `ConfigValueType`s that were expected - */ + * A failure reason given when a `ConfigValue` has the wrong type. + * + * @param foundType the `ConfigValueType` that was found + * @param expectedTypes the `ConfigValueType`s that were expected + */ final case class WrongType(foundType: ConfigValueType, expectedTypes: Set[ConfigValueType]) extends FailureReason { def description = s"""Expected type ${expectedTypes.mkString(" or ")}. Found $foundType instead.""" } /** - * A failure reason given when an exception is thrown during a conversion. - * - * @param throwable the `Throwable` that was raised - */ + * A failure reason given when an exception is thrown during a conversion. + * + * @param throwable the `Throwable` that was raised + */ final case class ExceptionThrown(throwable: Throwable) extends FailureReason { def description = s"${throwable.getMessage}." } /** - * A failure reason given when an unexpected empty string is found. - * - * @param typ the type that was attempted to be converted to from an empty string - */ + * A failure reason given when an unexpected empty string is found. + * + * @param typ the type that was attempted to be converted to from an empty string + */ final case class EmptyStringFound(typ: String) extends FailureReason { def description = s"Empty string found when trying to convert to $typ." } /** - * A failure reason given when an unexpected non-empty object is found. The failure happens when using - * `EnumCoproductHint` to write a config. - * - * @param typ the type for which a non-empty object was attempted to be written - */ -@deprecated("`EnumCoproductHint` is deprecated in favor of the `pureconfig.generic.semiauto.deriveEnumeration(Reader|Writer|Convert)[A]` methods", "0.11.0") + * A failure reason given when an unexpected non-empty object is found. The failure happens when using + * `EnumCoproductHint` to write a config. + * + * @param typ the type for which a non-empty object was attempted to be written + */ +@deprecated( + "`EnumCoproductHint` is deprecated in favor of the `pureconfig.generic.semiauto.deriveEnumeration(Reader|Writer|Convert)[A]` methods", + "0.11.0" +) final case class NonEmptyObjectFound(typ: String) extends FailureReason { def description = s"Non-empty object found when using EnumCoproductHint to write a $typ." } /** - * A failure reason given when a list of an unexpected size is found when attempting to read into an `HList`. - * - * @param expected the expected number of elements - * @param found the number of elements found - */ + * A failure reason given when a list of an unexpected size is found when attempting to read into an `HList`. + * + * @param expected the expected number of elements + * @param found the number of elements found + */ final case class WrongSizeList(expected: Int, found: Int) extends FailureReason { def description = s"List of wrong size found. Expected $expected elements. Found $found elements instead." } /** - * A failure reason given when a string is not of the expected size. - * - * @param expected the expected number of characters - * @param found the number of characters found - */ + * A failure reason given when a string is not of the expected size. + * + * @param expected the expected number of characters + * @param found the number of characters found + */ final case class WrongSizeString(expected: Int, found: Int) extends FailureReason { def description = s"String of wrong size found. Expected $expected characters. Found $found characters instead." } diff --git a/core/src/main/scala/pureconfig/package.scala b/core/src/main/scala/pureconfig/package.scala index 42875e2e2..d969bd54d 100644 --- a/core/src/main/scala/pureconfig/package.scala +++ b/core/src/main/scala/pureconfig/package.scala @@ -2,66 +2,68 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** - * @author Mario Pastorelli - */ + * @author Mario Pastorelli + */ -import java.io.{ OutputStream, OutputStreamWriter } +import java.io.{OutputStream, OutputStreamWriter} import java.nio.charset.StandardCharsets.UTF_8 -import java.nio.file.{ Files, Path } +import java.nio.file.{Files, Path} import scala.reflect.ClassTag -import com.typesafe.config.{ Config, ConfigRenderOptions } +import com.typesafe.config.{Config, ConfigRenderOptions} import pureconfig.error._ package object pureconfig { /** - * Load a configuration of type `A` from the standard configuration files - * - * @return A `Success` with the configuration if it is possible to create an instance of type - * `A` from the configuration files, else a `Failure` with details on why it - * isn't possible - */ + * Load a configuration of type `A` from the standard configuration files + * + * @return A `Success` with the configuration if it is possible to create an instance of type + * `A` from the configuration files, else a `Failure` with details on why it + * isn't possible + */ @deprecated("Use `ConfigSource.default.load[A]` instead", "0.12.0") def loadConfig[A](implicit reader: Derivation[ConfigReader[A]]): ConfigReader.Result[A] = ConfigSource.default.load[A] /** - * Load a configuration of type `A` from the standard configuration files - * - * @param namespace the base namespace from which the configuration should be load - * @return A `Success` with the configuration if it is possible to create an instance of type - * `A` from the configuration files, else a `Failure` with details on why it - * isn't possible - */ + * Load a configuration of type `A` from the standard configuration files + * + * @param namespace the base namespace from which the configuration should be load + * @return A `Success` with the configuration if it is possible to create an instance of type + * `A` from the configuration files, else a `Failure` with details on why it + * isn't possible + */ @deprecated("Use `ConfigSource.default.at(namespace).load[A]` instead", "0.12.0") def loadConfig[A](namespace: String)(implicit reader: Derivation[ConfigReader[A]]): ConfigReader.Result[A] = ConfigSource.default.at(namespace).load[A] /** - * Load a configuration of type `A` from the given file. Note that standard configuration - * files are still loaded but can be overridden from the given configuration file - * - * @return A `Success` with the configuration if it is possible to create an instance of type - * `A` from the configuration files, else a `Failure` with details on why it - * isn't possible - */ + * Load a configuration of type `A` from the given file. Note that standard configuration + * files are still loaded but can be overridden from the given configuration file + * + * @return A `Success` with the configuration if it is possible to create an instance of type + * `A` from the configuration files, else a `Failure` with details on why it + * isn't possible + */ @deprecated("Use `ConfigSource.default(ConfigSource.file(path)).load[A]` instead", "0.12.0") def loadConfig[A](path: Path)(implicit reader: Derivation[ConfigReader[A]]): ConfigReader.Result[A] = ConfigSource.default(ConfigSource.file(path)).load[A] /** - * Load a configuration of type `A` from the given file. Note that standard configuration - * files are still loaded but can be overridden from the given configuration file - * - * @param namespace the base namespace from which the configuration should be load - * @return A `Success` with the configuration if it is possible to create an instance of type - * `A` from the configuration files, else a `Failure` with details on why it - * isn't possible - */ + * Load a configuration of type `A` from the given file. Note that standard configuration + * files are still loaded but can be overridden from the given configuration file + * + * @param namespace the base namespace from which the configuration should be load + * @return A `Success` with the configuration if it is possible to create an instance of type + * `A` from the configuration files, else a `Failure` with details on why it + * isn't possible + */ @deprecated("Use `ConfigSource.default(ConfigSource.file(path)).at(namespace).load[A]` instead", "0.12.0") - def loadConfig[A](path: Path, namespace: String)(implicit reader: Derivation[ConfigReader[A]]): ConfigReader.Result[A] = + def loadConfig[A](path: Path, namespace: String)(implicit + reader: Derivation[ConfigReader[A]] + ): ConfigReader.Result[A] = ConfigSource.default(ConfigSource.file(path)).at(namespace).load[A] /** Load a configuration of type `A` from the given `Config` */ @@ -71,160 +73,176 @@ package object pureconfig { /** Load a configuration of type `A` from the given `Config` */ @deprecated("Use `ConfigSource.fromConfig(conf).at(namespace).load[A]` instead", "0.12.0") - def loadConfig[A](conf: Config, namespace: String)(implicit reader: Derivation[ConfigReader[A]]): ConfigReader.Result[A] = + def loadConfig[A](conf: Config, namespace: String)(implicit + reader: Derivation[ConfigReader[A]] + ): ConfigReader.Result[A] = ConfigSource.fromConfig(conf).at(namespace).load[A] /** - * Load a configuration of type `A` from the given `Config`, falling back to the default configuration - * - * @param conf Typesafe configuration to load - * @return A `Success` with the configuration if it is possible to create an instance of type - * `A` from the configuration files, else a `Failure` with details on why it - * isn't possible - */ + * Load a configuration of type `A` from the given `Config`, falling back to the default configuration + * + * @param conf Typesafe configuration to load + * @return A `Success` with the configuration if it is possible to create an instance of type + * `A` from the configuration files, else a `Failure` with details on why it + * isn't possible + */ @deprecated("Use `ConfigSource.fromConfig(conf).withFallback(ConfigSource.default).load[A]` instead", "0.12.0") def loadConfigWithFallback[A](conf: Config)(implicit reader: Derivation[ConfigReader[A]]): ConfigReader.Result[A] = ConfigSource.fromConfig(conf).withFallback(ConfigSource.default).load[A] /** - * Load a configuration of type `A` from the given `Config`, falling back to the default configuration - * - * @param conf Typesafe configuration to load - * @param namespace the base namespace from which the configuration should be load - * @return A `Success` with the configuration if it is possible to create an instance of type - * `A` from the configuration files, else a `Failure` with details on why it - * isn't possible - */ - @deprecated("Use `ConfigSource.fromConfig(conf).withFallback(ConfigSource.default).at(namespace).load[A]` instead", "0.12.0") - def loadConfigWithFallback[A](conf: Config, namespace: String)(implicit reader: Derivation[ConfigReader[A]]): ConfigReader.Result[A] = + * Load a configuration of type `A` from the given `Config`, falling back to the default configuration + * + * @param conf Typesafe configuration to load + * @param namespace the base namespace from which the configuration should be load + * @return A `Success` with the configuration if it is possible to create an instance of type + * `A` from the configuration files, else a `Failure` with details on why it + * isn't possible + */ + @deprecated( + "Use `ConfigSource.fromConfig(conf).withFallback(ConfigSource.default).at(namespace).load[A]` instead", + "0.12.0" + ) + def loadConfigWithFallback[A](conf: Config, namespace: String)(implicit + reader: Derivation[ConfigReader[A]] + ): ConfigReader.Result[A] = ConfigSource.fromConfig(conf).withFallback(ConfigSource.default).at(namespace).load[A] /** - * Load a configuration of type `A` from the standard configuration files - * - * @return the configuration - */ + * Load a configuration of type `A` from the standard configuration files + * + * @return the configuration + */ @throws[ConfigReaderException[_]] @deprecated("Use `ConfigSource.default.loadOrThrow[A]` instead", "0.12.0") def loadConfigOrThrow[A: ClassTag](implicit reader: Derivation[ConfigReader[A]]): A = ConfigSource.default.loadOrThrow[A] /** - * Load a configuration of type `A` from the standard configuration files - * - * @param namespace the base namespace from which the configuration should be load - * @return the configuration - */ + * Load a configuration of type `A` from the standard configuration files + * + * @param namespace the base namespace from which the configuration should be load + * @return the configuration + */ @throws[ConfigReaderException[_]] @deprecated("Use `ConfigSource.default.at(namespace).loadOrThrow[A]` instead", "0.12.0") def loadConfigOrThrow[A: ClassTag](namespace: String)(implicit reader: Derivation[ConfigReader[A]]): A = ConfigSource.default.at(namespace).loadOrThrow[A] /** - * Load a configuration of type `A` from the given file. Note that standard configuration - * files are still loaded but can be overridden from the given configuration file - * - * @return the configuration - */ + * Load a configuration of type `A` from the given file. Note that standard configuration + * files are still loaded but can be overridden from the given configuration file + * + * @return the configuration + */ @throws[ConfigReaderException[_]] @deprecated("Use `ConfigSource.default(ConfigSource.file(path)).loadOrThrow[A]` instead", "0.12.0") def loadConfigOrThrow[A: ClassTag](path: Path)(implicit reader: Derivation[ConfigReader[A]]): A = ConfigSource.default(ConfigSource.file(path)).loadOrThrow[A] /** - * Load a configuration of type `A` from the given file. Note that standard configuration - * files are still loaded but can be overridden from the given configuration file - * - * @param namespace the base namespace from which the configuration should be load - * @return the configuration - */ + * Load a configuration of type `A` from the given file. Note that standard configuration + * files are still loaded but can be overridden from the given configuration file + * + * @param namespace the base namespace from which the configuration should be load + * @return the configuration + */ @throws[ConfigReaderException[_]] @deprecated("Use `ConfigSource.default(ConfigSource.file(path)).at(namespace).loadOrThrow[A]` instead", "0.12.0") def loadConfigOrThrow[A: ClassTag](path: Path, namespace: String)(implicit reader: Derivation[ConfigReader[A]]): A = ConfigSource.default(ConfigSource.file(path)).at(namespace).loadOrThrow[A] /** - * Load a configuration of type `A` from the given `Config` - * - * @param conf Typesafe configuration to load - * @return the configuration - */ + * Load a configuration of type `A` from the given `Config` + * + * @param conf Typesafe configuration to load + * @return the configuration + */ @throws[ConfigReaderException[_]] @deprecated("Use `ConfigSource.fromConfig(conf).loadOrThrow[A]` instead", "0.12.0") def loadConfigOrThrow[A: ClassTag](conf: Config)(implicit reader: Derivation[ConfigReader[A]]): A = ConfigSource.fromConfig(conf).loadOrThrow[A] /** - * Load a configuration of type `A` from the given `Config` - * - * @param conf Typesafe configuration to load - * @param namespace the base namespace from which the configuration should be load - * @return the configuration - */ + * Load a configuration of type `A` from the given `Config` + * + * @param conf Typesafe configuration to load + * @param namespace the base namespace from which the configuration should be load + * @return the configuration + */ @throws[ConfigReaderException[_]] @deprecated("Use `ConfigSource.fromConfig(conf).at(namespace).loadOrThrow[A]` instead", "0.12.0") def loadConfigOrThrow[A: ClassTag](conf: Config, namespace: String)(implicit reader: Derivation[ConfigReader[A]]): A = ConfigSource.fromConfig(conf).at(namespace).loadOrThrow[A] /** - * Load a configuration of type `A` from the given `Config`, falling back to the default configuration - * - * @param conf Typesafe configuration to load - * @return the configuration - */ + * Load a configuration of type `A` from the given `Config`, falling back to the default configuration + * + * @param conf Typesafe configuration to load + * @return the configuration + */ @throws[ConfigReaderException[_]] @deprecated("Use `ConfigSource.fromConfig(conf).withFallback(ConfigSource.default).loadOrThrow[A]` instead", "0.12.0") def loadConfigWithFallbackOrThrow[A: ClassTag](conf: Config)(implicit reader: Derivation[ConfigReader[A]]): A = ConfigSource.fromConfig(conf).withFallback(ConfigSource.default).loadOrThrow[A] /** - * Load a configuration of type `A` from the given `Config`, falling back to the default configuration - * - * @param conf Typesafe configuration to load - * @param namespace the base namespace from which the configuration should be load - * @return the configuration - */ + * Load a configuration of type `A` from the given `Config`, falling back to the default configuration + * + * @param conf Typesafe configuration to load + * @param namespace the base namespace from which the configuration should be load + * @return the configuration + */ @throws[ConfigReaderException[_]] - @deprecated("Use `ConfigSource.fromConfig(conf).withFallback(ConfigSource.default).at(namespace).loadOrThrow[A]` instead", "0.12.0") - def loadConfigWithFallbackOrThrow[A: ClassTag](conf: Config, namespace: String)(implicit reader: Derivation[ConfigReader[A]]): A = + @deprecated( + "Use `ConfigSource.fromConfig(conf).withFallback(ConfigSource.default).at(namespace).loadOrThrow[A]` instead", + "0.12.0" + ) + def loadConfigWithFallbackOrThrow[A: ClassTag](conf: Config, namespace: String)(implicit + reader: Derivation[ConfigReader[A]] + ): A = ConfigSource.fromConfig(conf).withFallback(ConfigSource.default).at(namespace).loadOrThrow[A] /** - * Save the given configuration into a property file - * - * @param conf The configuration to save - * @param outputPath Where to write the configuration - * @param overrideOutputPath Override the path if it already exists - * @param options the config rendering options - */ + * Save the given configuration into a property file + * + * @param conf The configuration to save + * @param outputPath Where to write the configuration + * @param overrideOutputPath Override the path if it already exists + * @param options the config rendering options + */ @throws[IllegalArgumentException] def saveConfigAsPropertyFile[A]( - conf: A, - outputPath: Path, - overrideOutputPath: Boolean = false, - options: ConfigRenderOptions = ConfigRenderOptions.defaults())(implicit writer: Derivation[ConfigWriter[A]]): Unit = { + conf: A, + outputPath: Path, + overrideOutputPath: Boolean = false, + options: ConfigRenderOptions = ConfigRenderOptions.defaults() + )(implicit writer: Derivation[ConfigWriter[A]]): Unit = { if (!overrideOutputPath && Files.isRegularFile(outputPath)) { throw new IllegalArgumentException(s"Cannot save configuration in file '$outputPath' because it already exists") } if (Files isDirectory outputPath) { - throw new IllegalArgumentException(s"Cannot save configuration in file '$outputPath' because it already exists and is a directory") + throw new IllegalArgumentException( + s"Cannot save configuration in file '$outputPath' because it already exists and is a directory" + ) } saveConfigToStream(conf, Files.newOutputStream(outputPath), options) } /** - * Writes the configuration to the output stream and closes the stream - * - * @param conf The configuration to write - * @param outputStream The stream in which the configuration should be written - * @param options the config rendering options - */ + * Writes the configuration to the output stream and closes the stream + * + * @param conf The configuration to write + * @param outputStream The stream in which the configuration should be written + * @param options the config rendering options + */ def saveConfigToStream[A]( - conf: A, - outputStream: OutputStream, - options: ConfigRenderOptions = ConfigRenderOptions.defaults())(implicit writer: Derivation[ConfigWriter[A]]): Unit = { + conf: A, + outputStream: OutputStream, + options: ConfigRenderOptions = ConfigRenderOptions.defaults() + )(implicit writer: Derivation[ConfigWriter[A]]): Unit = { // HOCON requires UTF-8: // https://github.com/lightbend/config/blob/master/HOCON.md#unchanged-from-json @@ -235,39 +253,49 @@ package object pureconfig { } /** - * Loads `files` in order, allowing values in later files to backstop missing values from prior, and converts them - * into a `Config`. - * - * This is a convenience method which enables having default configuration which backstops local configuration. - * - * The behavior of the method if an element of `files` references a file which doesn't exist or can't be read is - * defined by the `failOnReadError` flag. With `failOnReadError = false`, such files will silently be ignored while - * otherwise they would yield a failure (a `Left` value). - * - * @param files Files ordered in decreasing priority containing part or all of a `Config` - * @param failOnReadError Where to return an error if any files fail to read - * @param namespace the base namespace from which the configuration should be load - */ + * Loads `files` in order, allowing values in later files to backstop missing values from prior, and converts them + * into a `Config`. + * + * This is a convenience method which enables having default configuration which backstops local configuration. + * + * The behavior of the method if an element of `files` references a file which doesn't exist or can't be read is + * defined by the `failOnReadError` flag. With `failOnReadError = false`, such files will silently be ignored while + * otherwise they would yield a failure (a `Left` value). + * + * @param files Files ordered in decreasing priority containing part or all of a `Config` + * @param failOnReadError Where to return an error if any files fail to read + * @param namespace the base namespace from which the configuration should be load + */ @deprecated("Construct a custom `ConfigSource` pipeline instead", "0.12.0") - def loadConfigFromFiles[A](files: Traversable[Path], failOnReadError: Boolean = false, namespace: String = "")(implicit reader: Derivation[ConfigReader[A]]): ConfigReader.Result[A] = { - ConfigSource.default( - files.map(ConfigSource.file) - .map(cs => if (failOnReadError) cs else cs.optional) - .foldLeft(ConfigSource.empty)(_.withFallback(_))) + def loadConfigFromFiles[A](files: Traversable[Path], failOnReadError: Boolean = false, namespace: String = "")( + implicit reader: Derivation[ConfigReader[A]] + ): ConfigReader.Result[A] = { + ConfigSource + .default( + files + .map(ConfigSource.file) + .map(cs => if (failOnReadError) cs else cs.optional) + .foldLeft(ConfigSource.empty)(_.withFallback(_)) + ) .at(namespace) .load[A] } /** - * @see [[loadConfigFromFiles]] - * @return the configuration - */ + * @see [[loadConfigFromFiles]] + * @return the configuration + */ @throws[ConfigReaderException[_]] @deprecated("Construct a custom `ConfigSource` pipeline instead", "0.12.0") - def loadConfigFromFilesOrThrow[A: ClassTag](files: Traversable[Path])(implicit reader: Derivation[ConfigReader[A]]): A = { - ConfigSource.default( - files.map(ConfigSource.file(_).optional) - .foldLeft(ConfigSource.empty)(_.withFallback(_))) + def loadConfigFromFilesOrThrow[A: ClassTag]( + files: Traversable[Path] + )(implicit reader: Derivation[ConfigReader[A]]): A = { + ConfigSource + .default( + files + .map(ConfigSource.file(_).optional) + .foldLeft(ConfigSource.empty)(_.withFallback(_)) + ) .loadOrThrow[A] } } diff --git a/core/src/main/scala/pureconfig/syntax/package.scala b/core/src/main/scala/pureconfig/syntax/package.scala index 586637461..4211a576d 100644 --- a/core/src/main/scala/pureconfig/syntax/package.scala +++ b/core/src/main/scala/pureconfig/syntax/package.scala @@ -1,6 +1,6 @@ package pureconfig -import com.typesafe.config.{ ConfigValue, Config => TypesafeConfig } +import com.typesafe.config.{ConfigValue, Config => TypesafeConfig} import pureconfig.error.ConfigReaderException import scala.reflect.ClassTag @@ -10,7 +10,9 @@ package object syntax { def toConfig(implicit writer: Derivation[ConfigWriter[A]]): ConfigValue = writer.value.to(any) } - private def getResultOrThrow[Config](failuresOrResult: ConfigReader.Result[Config])(implicit ct: ClassTag[Config]): Config = { + private def getResultOrThrow[Config]( + failuresOrResult: ConfigReader.Result[Config] + )(implicit ct: ClassTag[Config]): Config = { failuresOrResult match { case Right(config) => config case Left(failures) => throw new ConfigReaderException[Config](failures) @@ -19,16 +21,19 @@ package object syntax { implicit class ConfigValueReaderOps(val conf: ConfigValue) extends AnyVal { def to[A](implicit reader: Derivation[ConfigReader[A]]): ConfigReader.Result[A] = reader.value.from(conf) - def toOrThrow[A](implicit reader: Derivation[ConfigReader[A]], cl: ClassTag[A]): A = getResultOrThrow(reader.value.from(conf))(cl) + def toOrThrow[A](implicit reader: Derivation[ConfigReader[A]], cl: ClassTag[A]): A = + getResultOrThrow(reader.value.from(conf))(cl) } implicit class ConfigCursorReaderOps(val cur: ConfigCursor) extends AnyVal { def to[A](implicit reader: Derivation[ConfigReader[A]]): ConfigReader.Result[A] = reader.value.from(cur) - def toOrThrow[A](implicit reader: Derivation[ConfigReader[A]], cl: ClassTag[A]): A = getResultOrThrow(reader.value.from(cur))(cl) + def toOrThrow[A](implicit reader: Derivation[ConfigReader[A]], cl: ClassTag[A]): A = + getResultOrThrow(reader.value.from(cur))(cl) } implicit class ConfigReaderOps(val conf: TypesafeConfig) extends AnyVal { def to[A](implicit reader: Derivation[ConfigReader[A]]): ConfigReader.Result[A] = conf.root().to[A] - def toOrThrow[A](implicit reader: Derivation[ConfigReader[A]], cl: ClassTag[A]): A = getResultOrThrow(conf.root().to[A])(cl) + def toOrThrow[A](implicit reader: Derivation[ConfigReader[A]], cl: ClassTag[A]): A = + getResultOrThrow(conf.root().to[A])(cl) } } diff --git a/macros/src/main/scala/pureconfig/Derivation.scala b/macros/src/main/scala/pureconfig/Derivation.scala index d0497b583..850f6f93c 100644 --- a/macros/src/main/scala/pureconfig/Derivation.scala +++ b/macros/src/main/scala/pureconfig/Derivation.scala @@ -7,8 +7,10 @@ import scala.reflect.macros.whitebox import pureconfig.derivation._ -@implicitNotFound("""Cannot find an implicit instance of ${A}. -If you are trying to read or write a case class or sealed trait consider using PureConfig's auto derivation by adding `import pureconfig.generic.auto._`""") +@implicitNotFound( + """Cannot find an implicit instance of ${A}. +If you are trying to read or write a case class or sealed trait consider using PureConfig's auto derivation by adding `import pureconfig.generic.auto._`""" +) sealed trait Derivation[A] { def value: A } @@ -61,14 +63,15 @@ class DerivationMacros(val c: whitebox.Context) extends LazyContextParser with M // Determine what to do if an implicit search fails. If we're in the context of an implicit search, we want to set // the provided message in the @implicitNotFound annotation. If we're inside an explicit call to // `materializeDerivation` we want to abort with the message - val onFailedImplicitSearch: String => Nothing = if (isMaterializationExplicitCall) - c.abort(c.enclosingPosition, _) - else - msg => { - setImplicitNotFound(msg) - // cause the implicit to fail materializing - the message is ignored - c.abort(c.enclosingPosition, "") - } + val onFailedImplicitSearch: String => Nothing = + if (isMaterializationExplicitCall) + c.abort(c.enclosingPosition, _) + else + msg => { + setImplicitNotFound(msg) + // cause the implicit to fail materializing - the message is ignored + c.abort(c.enclosingPosition, "") + } // check the `-Xmacro-settings:materialize-derivations` scalac flag and make sure we're not in an explicit // materialization call @@ -143,35 +146,36 @@ class DerivationMacros(val c: whitebox.Context) extends LazyContextParser with M private[this] var currentPath = List[Type]() private[this] var lazyCtxOpt = Option.empty[LazyContext] - override def traverse(tree: Tree) = tree match { - case q"pureconfig.Derivation.Successful.apply[$typ]($valueExpr)" => - currentPath = typ.toType :: currentPath - traverse(valueExpr) - currentPath = currentPath.tail - - case q"pureconfig.Derivation.Failed.apply[$typ]()" => - failures += typ.toType :: currentPath - - case LazyContextTree(lazyCtx) => - // if the `Lazy` context tree is found, store the created `LazyContext` instance and continue traversing from - // the `Lazy` entrypoint. - lazyCtxOpt = Some(lazyCtx) - traverse(lazyCtx.entrypoint) - lazyCtxOpt = None - - case _ => - // for every other tree, check if it is a reference to a lazy implicit. If so, follow the reference and - // continue traversing the corresponding tree with the new `LazyContext`. - lazyCtxOpt.flatMap(_.followRef(tree)) match { - case None => super.traverse(tree) - - case Some((newLazyCtx, lazyTree)) => - val oldLazyCtx = lazyCtxOpt - lazyCtxOpt = Some(newLazyCtx) - traverse(lazyTree) - lazyCtxOpt = oldLazyCtx - } - } + override def traverse(tree: Tree) = + tree match { + case q"pureconfig.Derivation.Successful.apply[$typ]($valueExpr)" => + currentPath = typ.toType :: currentPath + traverse(valueExpr) + currentPath = currentPath.tail + + case q"pureconfig.Derivation.Failed.apply[$typ]()" => + failures += typ.toType :: currentPath + + case LazyContextTree(lazyCtx) => + // if the `Lazy` context tree is found, store the created `LazyContext` instance and continue traversing from + // the `Lazy` entrypoint. + lazyCtxOpt = Some(lazyCtx) + traverse(lazyCtx.entrypoint) + lazyCtxOpt = None + + case _ => + // for every other tree, check if it is a reference to a lazy implicit. If so, follow the reference and + // continue traversing the corresponding tree with the new `LazyContext`. + lazyCtxOpt.flatMap(_.followRef(tree)) match { + case None => super.traverse(tree) + + case Some((newLazyCtx, lazyTree)) => + val oldLazyCtx = lazyCtxOpt + lazyCtxOpt = Some(newLazyCtx) + traverse(lazyTree) + lazyCtxOpt = oldLazyCtx + } + } } traverser.traverse(tree) @@ -217,9 +221,10 @@ class DerivationMacros(val c: whitebox.Context) extends LazyContextParser with M private[this] def prettyPrintType(typ: Type): String = prettyPrintType(typ.toTree) - private[this] def prettyPrintType(typ: Tree): String = typ match { - case tq"Lazy[$lzyArg]" => prettyPrintType(lzyArg) // ignore `Lazy` for the purposes of showing the implicit chain - case tq"$typeclass[$arg]" => s"a $typeclass instance for type $arg" - case _ => s"an implicit value of $typ" - } + private[this] def prettyPrintType(typ: Tree): String = + typ match { + case tq"Lazy[$lzyArg]" => prettyPrintType(lzyArg) // ignore `Lazy` for the purposes of showing the implicit chain + case tq"$typeclass[$arg]" => s"a $typeclass instance for type $arg" + case _ => s"an implicit value of $typ" + } } diff --git a/macros/src/main/scala/pureconfig/derivation/LazyContextParser.scala b/macros/src/main/scala/pureconfig/derivation/LazyContextParser.scala index 1d79663b9..5bb493f78 100644 --- a/macros/src/main/scala/pureconfig/derivation/LazyContextParser.scala +++ b/macros/src/main/scala/pureconfig/derivation/LazyContextParser.scala @@ -3,71 +3,73 @@ package pureconfig.derivation import scala.reflect.macros.whitebox /** - * Encapsulates the logic to parse Scala syntax trees genetrated by `Lazy`, providing a high-level way to navigate - * through such a tree. The code on this class is heavily-dependent on the implementation details of `Lazy`. - */ + * Encapsulates the logic to parse Scala syntax trees genetrated by `Lazy`, providing a high-level way to navigate + * through such a tree. The code on this class is heavily-dependent on the implementation details of `Lazy`. + */ trait LazyContextParser { val c: whitebox.Context import c.universe._ /** - * A representation of all the relevant information of a `Lazy`-generated tree. - * - * @param anonClass the name of the anonymous class containing lazy value definitions - * @param defs a map from lazy value names to their definition - * @param entrypoint the tree serving as entrypoint to a lazy implicit - */ + * A representation of all the relevant information of a `Lazy`-generated tree. + * + * @param anonClass the name of the anonymous class containing lazy value definitions + * @param defs a map from lazy value names to their definition + * @param entrypoint the tree serving as entrypoint to a lazy implicit + */ case class LazyContext(anonClass: TypeName, defs: Map[TermName, Tree], entrypoint: Tree) { /** - * Checks if a `Tree` is a reference to a lazy value generated by `Lazy`. If it is a valid reference, it returns - * a `Tree` with the corresponding definition and a new `LazyContext` to be used in inner searches. The new - * `LazyContext` does not have the followed reference in order to prevent infinite loops when searching (the - * original use case for `Lazy`). - * - * @param tree the tree to be checked - * @return if `tree` is a reference to a `Lazy`-generated value, a `Some` with the respective definition and a new - * `LazyContext`; `None` otherwise. - */ - def followRef(tree: Tree): Option[(LazyContext, Tree)] = tree match { - case q"${ `anonClass` }.this.$lazyRef" => - defs.get(lazyRef).map { lzyTree => (copy(defs = defs - lazyRef), lzyTree) } + * Checks if a `Tree` is a reference to a lazy value generated by `Lazy`. If it is a valid reference, it returns + * a `Tree` with the corresponding definition and a new `LazyContext` to be used in inner searches. The new + * `LazyContext` does not have the followed reference in order to prevent infinite loops when searching (the + * original use case for `Lazy`). + * + * @param tree the tree to be checked + * @return if `tree` is a reference to a `Lazy`-generated value, a `Some` with the respective definition and a new + * `LazyContext`; `None` otherwise. + */ + def followRef(tree: Tree): Option[(LazyContext, Tree)] = + tree match { + case q"${`anonClass`}.this.$lazyRef" => + defs.get(lazyRef).map { lzyTree => (copy(defs = defs - lazyRef), lzyTree) } - case _ => None - } + case _ => None + } } /** - * An object containing an extractor of `LazyContext` instances from Scala trees. - */ + * An object containing an extractor of `LazyContext` instances from Scala trees. + */ object LazyContextTree { /** - * Extracts a `LazyContext` from a `Tree`. - */ - def unapply(tree: Tree): Option[LazyContext] = tree match { - case q"""{ + * Extracts a `LazyContext` from a `Tree`. + */ + def unapply(tree: Tree): Option[LazyContext] = + tree match { + case q"""{ val $outer = { - final class $anonClass extends ${ _ } with ${ _ } { ..$lazyDefs } + final class $anonClass extends ${_} with ${_} { ..$lazyDefs } new $anonClassRef().$entrypoint } - shapeless.Lazy.apply[${ _ }]($outerRef) + shapeless.Lazy.apply[${_}]($outerRef) }""" if isRefTo(outerRef, outer) && isRefTo(anonClassRef, anonClass) => + val lazyDefMap: Map[TermName, Tree] = lazyDefs.collect { + case q"lazy val $valName = $valBody" => valName -> valBody + }.toMap - val lazyDefMap: Map[TermName, Tree] = lazyDefs.collect { - case q"lazy val $valName = $valBody" => valName -> valBody - }.toMap + val entrypointDef = lazyDefMap(entrypoint) + Some(LazyContext(anonClass, lazyDefMap - entrypoint, entrypointDef)) - val entrypointDef = lazyDefMap(entrypoint) - Some(LazyContext(anonClass, lazyDefMap - entrypoint, entrypointDef)) + case _ => None + } - case _ => None - } - - private[this] def isRefTo(tree: Tree, name: Name): Boolean = tree match { - case Ident(otherName) => name == otherName - case _ => false - } + private[this] def isRefTo(tree: Tree, name: Name): Boolean = + tree match { + case Ident(otherName) => name == otherName + case _ => false + } } } diff --git a/macros/src/main/scala/pureconfig/derivation/MacroCompat.scala b/macros/src/main/scala/pureconfig/derivation/MacroCompat.scala index f328bf476..3d9fe5cd9 100644 --- a/macros/src/main/scala/pureconfig/derivation/MacroCompat.scala +++ b/macros/src/main/scala/pureconfig/derivation/MacroCompat.scala @@ -1,13 +1,13 @@ package pureconfig.derivation -import scala.reflect.macros.{ TypecheckException, whitebox } +import scala.reflect.macros.{TypecheckException, whitebox} import pureconfig.Derivation /** - * An API for macro operations that require access to macro or even compiler internals. Since they rely on - * non-public APIs, the code may be distinct between Scala major versions and even between minor versions. - */ + * An API for macro operations that require access to macro or even compiler internals. Since they rely on + * non-public APIs, the code may be distinct between Scala major versions and even between minor versions. + */ trait MacroCompat { val c: whitebox.Context @@ -27,13 +27,22 @@ trait MacroCompat { def inferImplicitValueCompat(typ: Type): Tree = { val cc = c.asInstanceOf[scala.reflect.macros.contexts.Context] val enclosingTree = - cc.openImplicits.headOption.map(_.tree) + cc.openImplicits.headOption + .map(_.tree) .orElse(cc.enclosingMacros.lastOption.map(_.macroApplication)) - .getOrElse(EmptyTree).asInstanceOf[cc.universe.analyzer.global.Tree] + .getOrElse(EmptyTree) + .asInstanceOf[cc.universe.analyzer.global.Tree] val res: cc.Tree = cc.universe.analyzer.inferImplicit( - enclosingTree, typ.asInstanceOf[cc.Type], false, cc.callsiteTyper.context, true, false, cc.enclosingPosition, - (pos, msg) => throw TypecheckException(pos, msg)) + enclosingTree, + typ.asInstanceOf[cc.Type], + false, + cc.callsiteTyper.context, + true, + false, + cc.enclosingPosition, + (pos, msg) => throw TypecheckException(pos, msg) + ) res.asInstanceOf[c.Tree] } diff --git a/macros/src/test/scala/pureconfig/DerivationChecks.scala b/macros/src/test/scala/pureconfig/DerivationChecks.scala index 640bf3e6d..f1dab74f5 100644 --- a/macros/src/test/scala/pureconfig/DerivationChecks.scala +++ b/macros/src/test/scala/pureconfig/DerivationChecks.scala @@ -4,17 +4,17 @@ import scala.language.experimental.macros import scala.reflect.macros.blackbox /** - * Helper methods for testing the behavior of `Derivation`. - */ + * Helper methods for testing the behavior of `Derivation`. + */ object DerivationChecks { /** - * A version of the `shapeless.test.illTyped` macro that allows expected error messages with multiple lines, as well - * as whitespace before and after the message. - * - * @param code the code to check for non-compilation - * @param expected the expected error message as a variable number of lines - */ + * A version of the `shapeless.test.illTyped` macro that allows expected error messages with multiple lines, as well + * as whitespace before and after the message. + * + * @param code the code to check for non-compilation + * @param expected the expected error message as a variable number of lines + */ def illTyped(code: String, expected: String*): Unit = macro DerivationChecksMacros.illTyped } diff --git a/modules/akka/src/main/scala/pureconfig/module/akka/package.scala b/modules/akka/src/main/scala/pureconfig/module/akka/package.scala index 8fba2e4e4..0cd303be8 100644 --- a/modules/akka/src/main/scala/pureconfig/module/akka/package.scala +++ b/modules/akka/src/main/scala/pureconfig/module/akka/package.scala @@ -9,8 +9,8 @@ import pureconfig.ConfigConvert.viaString import pureconfig.ConvertHelpers.catchReadError /** - * ConfigConvert instances for Akka value classes. - */ + * ConfigConvert instances for Akka value classes. + */ package object akka { implicit val timeoutCC: ConfigConvert[Timeout] = ConfigConvert[FiniteDuration].xmap(new Timeout(_), _.duration) diff --git a/modules/cats-effect/src/main/scala/pureconfig/module/catseffect/package.scala b/modules/cats-effect/src/main/scala/pureconfig/module/catseffect/package.scala index b41598cad..559c88a41 100644 --- a/modules/cats-effect/src/main/scala/pureconfig/module/catseffect/package.scala +++ b/modules/cats-effect/src/main/scala/pureconfig/module/catseffect/package.scala @@ -2,15 +2,15 @@ package pureconfig.module import java.io.OutputStream import java.nio.charset.StandardCharsets -import java.nio.file.{ Files, Path } +import java.nio.file.{Files, Path} import scala.language.higherKinds import scala.reflect.ClassTag -import cats.data.{ EitherT, NonEmptyList } -import cats.effect.{ Blocker, ContextShift, Resource, Sync } +import cats.data.{EitherT, NonEmptyList} +import cats.effect.{Blocker, ContextShift, Resource, Sync} import cats.implicits._ -import com.typesafe.config.{ ConfigRenderOptions, Config => TypesafeConfig } +import com.typesafe.config.{ConfigRenderOptions, Config => TypesafeConfig} import pureconfig._ import pureconfig.error.ConfigReaderException @@ -20,15 +20,17 @@ package object catseffect { val defaultNameSpace = "" /** - * Load a configuration of type `A` from a config source - * - * @param cs the config source from where the configuration will be loaded - * @return The returned action will complete with `A` if it is possible to create an instance of type - * `A` from the configuration source, or fail with a ConfigReaderException which in turn contains - * details on why it isn't possible - */ + * Load a configuration of type `A` from a config source + * + * @param cs the config source from where the configuration will be loaded + * @return The returned action will complete with `A` if it is possible to create an instance of type + * `A` from the configuration source, or fail with a ConfigReaderException which in turn contains + * details on why it isn't possible + */ @deprecated("Use `loadF[F, A](cs, blocker)` instead", "0.12.3") - def loadF[F[_], A](cs: ConfigSource)(implicit F: Sync[F], reader: Derivation[ConfigReader[A]], ct: ClassTag[A]): F[A] = { + def loadF[F[_], A]( + cs: ConfigSource + )(implicit F: Sync[F], reader: Derivation[ConfigReader[A]], ct: ClassTag[A]): F[A] = { val delayedLoad = F.delay { cs.load[A].leftMap[Throwable](ConfigReaderException[A]) } @@ -36,140 +38,169 @@ package object catseffect { } /** - * Load a configuration of type `A` from a config source - * - * @param cs the config source from where the configuration will be loaded - * @param blocker the blocking context which will be used to load the configuration. - * @return The returned action will complete with `A` if it is possible to create an instance of type - * `A` from the configuration source, or fail with a ConfigReaderException which in turn contains - * details on why it isn't possible - */ - def loadF[F[_], A](cs: ConfigSource, blocker: Blocker)(implicit F: Sync[F], csf: ContextShift[F], reader: Derivation[ConfigReader[A]], ct: ClassTag[A]): F[A] = + * Load a configuration of type `A` from a config source + * + * @param cs the config source from where the configuration will be loaded + * @param blocker the blocking context which will be used to load the configuration. + * @return The returned action will complete with `A` if it is possible to create an instance of type + * `A` from the configuration source, or fail with a ConfigReaderException which in turn contains + * details on why it isn't possible + */ + def loadF[F[_], A]( + cs: ConfigSource, + blocker: Blocker + )(implicit F: Sync[F], csf: ContextShift[F], reader: Derivation[ConfigReader[A]], ct: ClassTag[A]): F[A] = EitherT(blocker.delay(cs.cursor())) .subflatMap(reader.value.from) .leftMap(ConfigReaderException[A]) .rethrowT /** - * Load a configuration of type `A` from the standard configuration files - * - * @return The returned action will complete with `A` if it is possible to create an instance of type - * `A` from the configuration files, or fail with a ConfigReaderException which in turn contains - * details on why it isn't possible - */ + * Load a configuration of type `A` from the standard configuration files + * + * @return The returned action will complete with `A` if it is possible to create an instance of type + * `A` from the configuration files, or fail with a ConfigReaderException which in turn contains + * details on why it isn't possible + */ @deprecated("Use `loadConfigF[F, A](blocker)` instead", "0.12.3") def loadConfigF[F[_], A](implicit F: Sync[F], reader: Derivation[ConfigReader[A]], ct: ClassTag[A]): F[A] = loadF[F, A](ConfigSource.default) /** - * Load a configuration of type `A` from the standard configuration files - * - * @param blocker the blocking context which will be used to load the configuration. - * @return The returned action will complete with `A` if it is possible to create an instance of type - * `A` from the configuration files, or fail with a ConfigReaderException which in turn contains - * details on why it isn't possible - */ - def loadConfigF[F[_], A](blocker: Blocker)(implicit F: Sync[F], csf: ContextShift[F], reader: Derivation[ConfigReader[A]], ct: ClassTag[A]): F[A] = + * Load a configuration of type `A` from the standard configuration files + * + * @param blocker the blocking context which will be used to load the configuration. + * @return The returned action will complete with `A` if it is possible to create an instance of type + * `A` from the configuration files, or fail with a ConfigReaderException which in turn contains + * details on why it isn't possible + */ + def loadConfigF[F[_], A]( + blocker: Blocker + )(implicit F: Sync[F], csf: ContextShift[F], reader: Derivation[ConfigReader[A]], ct: ClassTag[A]): F[A] = loadF(ConfigSource.default, blocker) /** - * Load a configuration of type `A` from the standard configuration files - * - * @param namespace the base namespace from which the configuration should be load - * @return The returned action will complete with `A` if it is possible to create an instance of type - * `A` from the configuration files, or fail with a ConfigReaderException which in turn contains - * details on why it isn't possible - */ + * Load a configuration of type `A` from the standard configuration files + * + * @param namespace the base namespace from which the configuration should be load + * @return The returned action will complete with `A` if it is possible to create an instance of type + * `A` from the configuration files, or fail with a ConfigReaderException which in turn contains + * details on why it isn't possible + */ @deprecated("Use `ConfigSource.default.at(namespace).loadF[F, A]` instead", "0.12.0") - def loadConfigF[F[_], A](namespace: String)(implicit F: Sync[F], reader: Derivation[ConfigReader[A]], ct: ClassTag[A]): F[A] = + def loadConfigF[F[_], A]( + namespace: String + )(implicit F: Sync[F], reader: Derivation[ConfigReader[A]], ct: ClassTag[A]): F[A] = loadF[F, A](ConfigSource.default.at(namespace)) /** - * Load a configuration of type `A` from the given file. Note that standard configuration - * files are still loaded but can be overridden from the given configuration file - * - * @param path the path of the configuration file from which to load - * @return The returned action will complete with `A` if it is possible to create an instance of type - * `A` from the configuration file, or fail with a ConfigReaderException which in turn contains - * details on why it isn't possible - */ + * Load a configuration of type `A` from the given file. Note that standard configuration + * files are still loaded but can be overridden from the given configuration file + * + * @param path the path of the configuration file from which to load + * @return The returned action will complete with `A` if it is possible to create an instance of type + * `A` from the configuration file, or fail with a ConfigReaderException which in turn contains + * details on why it isn't possible + */ @deprecated("Use `ConfigSource.default(ConfigSource.file(path)).loadF[F, A]` instead", "0.12.0") - def loadConfigF[F[_], A](path: Path)(implicit F: Sync[F], reader: Derivation[ConfigReader[A]], ct: ClassTag[A]): F[A] = + def loadConfigF[F[_], A]( + path: Path + )(implicit F: Sync[F], reader: Derivation[ConfigReader[A]], ct: ClassTag[A]): F[A] = loadF[F, A](ConfigSource.default(ConfigSource.file(path))) /** - * Load a configuration of type `A` from the given file. Note that standard configuration - * files are still loaded but can be overridden from the given configuration file - * - * @param path the path of the configuration file from which to load - * @param namespace the base namespace from which the configuration should be load - * @return The returned action will complete with `A` if it is possible to create an instance of type - * `A` from the configuration file, or fail with a ConfigReaderException which in turn contains - * details on why it isn't possible - */ + * Load a configuration of type `A` from the given file. Note that standard configuration + * files are still loaded but can be overridden from the given configuration file + * + * @param path the path of the configuration file from which to load + * @param namespace the base namespace from which the configuration should be load + * @return The returned action will complete with `A` if it is possible to create an instance of type + * `A` from the configuration file, or fail with a ConfigReaderException which in turn contains + * details on why it isn't possible + */ @deprecated("Use `ConfigSource.default(ConfigSource.file(path)).at(namespace).loadF[F, A]` instead", "0.12.0") - def loadConfigF[F[_], A](path: Path, namespace: String)(implicit F: Sync[F], reader: Derivation[ConfigReader[A]], ct: ClassTag[A]): F[A] = + def loadConfigF[F[_], A](path: Path, namespace: String)(implicit + F: Sync[F], + reader: Derivation[ConfigReader[A]], + ct: ClassTag[A] + ): F[A] = loadF[F, A](ConfigSource.default(ConfigSource.file(path)).at(namespace)) /** - * Load a configuration of type `A` from the given `Config` - * @return The returned action will complete with `A` if it is possible to create an instance of type - * `A` from the configuration object, or fail with a ConfigReaderException which in turn contains - * details on why it isn't possible - */ + * Load a configuration of type `A` from the given `Config` + * @return The returned action will complete with `A` if it is possible to create an instance of type + * `A` from the configuration object, or fail with a ConfigReaderException which in turn contains + * details on why it isn't possible + */ @deprecated("Use `ConfigSource.fromConfig(conf).loadF[F, A]` instead", "0.12.0") - def loadConfigF[F[_], A](conf: TypesafeConfig)(implicit F: Sync[F], reader: Derivation[ConfigReader[A]], ct: ClassTag[A]): F[A] = + def loadConfigF[F[_], A]( + conf: TypesafeConfig + )(implicit F: Sync[F], reader: Derivation[ConfigReader[A]], ct: ClassTag[A]): F[A] = loadF[F, A](ConfigSource.fromConfig(conf)) /** - * Load a configuration of type `A` from the given `Config` - * @return The returned action will complete with `A` if it is possible to create an instance of type - * `A` from the configuration object, or fail with a ConfigReaderException which in turn contains - * details on why it isn't possible - */ + * Load a configuration of type `A` from the given `Config` + * @return The returned action will complete with `A` if it is possible to create an instance of type + * `A` from the configuration object, or fail with a ConfigReaderException which in turn contains + * details on why it isn't possible + */ @deprecated("Use `ConfigSource.fromConfig(conf).at(namespace).loadF[F, A]` instead", "0.12.0") - def loadConfigF[F[_], A](conf: TypesafeConfig, namespace: String)(implicit F: Sync[F], reader: Derivation[ConfigReader[A]], ct: ClassTag[A]): F[A] = + def loadConfigF[F[_], A](conf: TypesafeConfig, namespace: String)(implicit + F: Sync[F], + reader: Derivation[ConfigReader[A]], + ct: ClassTag[A] + ): F[A] = loadF[F, A](ConfigSource.fromConfig(conf).at(namespace)) /** - * Save the given configuration into a property file - * - * @param conf The configuration to save - * @param outputPath Where to write the configuration - * @param overrideOutputPath Override the path if it already exists - * @param options the config rendering options - * @return The return action will save out the supplied configuration upon invocation - */ - @deprecated("Use `blockingSaveConfigAsPropertyFileF[IO, A](conf, outputPat, blocker, overrideOutputPath, options)` instead", "0.12.3") + * Save the given configuration into a property file + * + * @param conf The configuration to save + * @param outputPath Where to write the configuration + * @param overrideOutputPath Override the path if it already exists + * @param options the config rendering options + * @return The return action will save out the supplied configuration upon invocation + */ + @deprecated( + "Use `blockingSaveConfigAsPropertyFileF[IO, A](conf, outputPat, blocker, overrideOutputPath, options)` instead", + "0.12.3" + ) def saveConfigAsPropertyFileF[F[_], A]( - conf: A, - outputPath: Path, - overrideOutputPath: Boolean = false, - options: ConfigRenderOptions = ConfigRenderOptions.defaults())(implicit F: Sync[F], writer: Derivation[ConfigWriter[A]]): F[Unit] = F.delay { - pureconfig.saveConfigAsPropertyFile(conf, outputPath, overrideOutputPath, options) - } + conf: A, + outputPath: Path, + overrideOutputPath: Boolean = false, + options: ConfigRenderOptions = ConfigRenderOptions.defaults() + )(implicit F: Sync[F], writer: Derivation[ConfigWriter[A]]): F[Unit] = + F.delay { + pureconfig.saveConfigAsPropertyFile(conf, outputPath, overrideOutputPath, options) + } /** - * Save the given configuration into a property file - * - * @param conf The configuration to save - * @param outputPath Where to write the configuration - * @param blocker the blocking context which will be used to load the configuration. - * @param overrideOutputPath Override the path if it already exists - * @param options the config rendering options - * @return The return action will save out the supplied configuration upon invocation - */ + * Save the given configuration into a property file + * + * @param conf The configuration to save + * @param outputPath Where to write the configuration + * @param blocker the blocking context which will be used to load the configuration. + * @param overrideOutputPath Override the path if it already exists + * @param options the config rendering options + * @return The return action will save out the supplied configuration upon invocation + */ def blockingSaveConfigAsPropertyFileF[F[_], A]( - conf: A, - outputPath: Path, - blocker: Blocker, - overrideOutputPath: Boolean = false, - options: ConfigRenderOptions = ConfigRenderOptions.defaults())(implicit F: Sync[F], csf: ContextShift[F], writer: Derivation[ConfigWriter[A]]): F[Unit] = { + conf: A, + outputPath: Path, + blocker: Blocker, + overrideOutputPath: Boolean = false, + options: ConfigRenderOptions = ConfigRenderOptions.defaults() + )(implicit F: Sync[F], csf: ContextShift[F], writer: Derivation[ConfigWriter[A]]): F[Unit] = { val fileAlreadyExists = - F.raiseError(new IllegalArgumentException(s"Cannot save configuration in file '$outputPath' because it already exists")) + F.raiseError( + new IllegalArgumentException(s"Cannot save configuration in file '$outputPath' because it already exists") + ) val fileIsDirectory = - F.raiseError(new IllegalArgumentException(s"Cannot save configuration in file '$outputPath' because it is a directory")) + F.raiseError( + new IllegalArgumentException(s"Cannot save configuration in file '$outputPath' because it is a directory") + ) val check = F.suspend { @@ -189,35 +220,38 @@ package object catseffect { } /** - * Writes the configuration to the output stream and closes the stream - * - * @param conf The configuration to write - * @param outputStream The stream in which the configuration should be written - * @param options the config rendering options - * @return The return action will save out the supplied configuration upon invocation - */ + * Writes the configuration to the output stream and closes the stream + * + * @param conf The configuration to write + * @param outputStream The stream in which the configuration should be written + * @param options the config rendering options + * @return The return action will save out the supplied configuration upon invocation + */ @deprecated("Use `blockingSaveConfigToStreamF[IO, A](conf, outputStream, blocker, options)` instead", "0.12.3") def saveConfigToStreamF[F[_], A]( - conf: A, - outputStream: OutputStream, - options: ConfigRenderOptions = ConfigRenderOptions.defaults())(implicit F: Sync[F], writer: Derivation[ConfigWriter[A]]): F[Unit] = F.delay { - pureconfig.saveConfigToStream(conf, outputStream, options) - } + conf: A, + outputStream: OutputStream, + options: ConfigRenderOptions = ConfigRenderOptions.defaults() + )(implicit F: Sync[F], writer: Derivation[ConfigWriter[A]]): F[Unit] = + F.delay { + pureconfig.saveConfigToStream(conf, outputStream, options) + } /** - * Writes the configuration to the output stream and closes the stream - * - * @param conf The configuration to write - * @param outputStream The stream in which the configuration should be written - * @param blocker the blocking context which will be used to load the configuration. - * @param options the config rendering options - * @return The return action will save out the supplied configuration upon invocation - */ + * Writes the configuration to the output stream and closes the stream + * + * @param conf The configuration to write + * @param outputStream The stream in which the configuration should be written + * @param blocker the blocking context which will be used to load the configuration. + * @param options the config rendering options + * @return The return action will save out the supplied configuration upon invocation + */ def blockingSaveConfigToStreamF[F[_], A]( - conf: A, - outputStream: OutputStream, - blocker: Blocker, - options: ConfigRenderOptions = ConfigRenderOptions.defaults())(implicit F: Sync[F], csf: ContextShift[F], writer: Derivation[ConfigWriter[A]]): F[Unit] = + conf: A, + outputStream: OutputStream, + blocker: Blocker, + options: ConfigRenderOptions = ConfigRenderOptions.defaults() + )(implicit F: Sync[F], csf: ContextShift[F], writer: Derivation[ConfigWriter[A]]): F[Unit] = F.delay(writer.value.to(conf)).map { rawConf => // HOCON requires UTF-8: // https://github.com/lightbend/config/blob/master/HOCON.md#unchanged-from-json @@ -230,17 +264,23 @@ package object catseffect { } /** - * Loads `files` in order, allowing values in later files to backstop missing values from prior, and converts them into a `A`. - * - * This is a convenience method which enables having default configuration which backstops local configuration. - * - * Note: If an element of `files` references a file which doesn't exist or can't be read, it will silently be ignored. - * - * @param files Files ordered in decreasing priority containing part or all of a `A`. Must not be empty. - */ + * Loads `files` in order, allowing values in later files to backstop missing values from prior, and converts them into a `A`. + * + * This is a convenience method which enables having default configuration which backstops local configuration. + * + * Note: If an element of `files` references a file which doesn't exist or can't be read, it will silently be ignored. + * + * @param files Files ordered in decreasing priority containing part or all of a `A`. Must not be empty. + */ @deprecated("Construct a custom `ConfigSource` pipeline instead", "0.12.0") - def loadConfigFromFilesF[F[_], A](files: NonEmptyList[Path])(implicit F: Sync[F], reader: Derivation[ConfigReader[A]], ct: ClassTag[A]): F[A] = - loadF[F, A](ConfigSource.default( - files.map(ConfigSource.file(_).optional) - .foldLeft(ConfigSource.empty)(_.withFallback(_)))) + def loadConfigFromFilesF[F[_], A]( + files: NonEmptyList[Path] + )(implicit F: Sync[F], reader: Derivation[ConfigReader[A]], ct: ClassTag[A]): F[A] = + loadF[F, A]( + ConfigSource.default( + files + .map(ConfigSource.file(_).optional) + .foldLeft(ConfigSource.empty)(_.withFallback(_)) + ) + ) } diff --git a/modules/cats-effect/src/main/scala/pureconfig/module/catseffect/syntax/package.scala b/modules/cats-effect/src/main/scala/pureconfig/module/catseffect/syntax/package.scala index 159bf33d5..44d4eda4a 100644 --- a/modules/cats-effect/src/main/scala/pureconfig/module/catseffect/syntax/package.scala +++ b/modules/cats-effect/src/main/scala/pureconfig/module/catseffect/syntax/package.scala @@ -3,8 +3,8 @@ package pureconfig.module.catseffect import scala.language.higherKinds import scala.reflect.ClassTag -import cats.effect.{ Blocker, ContextShift, Sync } -import pureconfig.{ ConfigReader, ConfigSource, Derivation } +import cats.effect.{Blocker, ContextShift, Sync} +import pureconfig.{ConfigReader, ConfigSource, Derivation} import pureconfig.module.catseffect package object syntax { @@ -12,7 +12,9 @@ package object syntax { implicit class CatsEffectConfigSource(private val cs: ConfigSource) extends AnyVal { @inline - final def loadF[F[_], A](blocker: Blocker)(implicit F: Sync[F], csf: ContextShift[F], reader: Derivation[ConfigReader[A]], ct: ClassTag[A]): F[A] = + final def loadF[F[_], A]( + blocker: Blocker + )(implicit F: Sync[F], csf: ContextShift[F], reader: Derivation[ConfigReader[A]], ct: ClassTag[A]): F[A] = catseffect.loadF(cs, blocker) @deprecated("Use `cs.loadF[F, A](blocker)` instead", "0.12.3") diff --git a/modules/cats-effect/src/test/scala/pureconfig/module/catseffect/CatsEffectSuite.scala b/modules/cats-effect/src/test/scala/pureconfig/module/catseffect/CatsEffectSuite.scala index c4c8c68e3..b4effa943 100644 --- a/modules/cats-effect/src/test/scala/pureconfig/module/catseffect/CatsEffectSuite.scala +++ b/modules/cats-effect/src/test/scala/pureconfig/module/catseffect/CatsEffectSuite.scala @@ -1,15 +1,15 @@ package pureconfig.module.catseffect -import java.io.{ BufferedOutputStream, PipedInputStream, PipedOutputStream } -import java.nio.file.{ Path, Paths } +import java.io.{BufferedOutputStream, PipedInputStream, PipedOutputStream} +import java.nio.file.{Path, Paths} import java.util.concurrent.Executors import scala.concurrent.ExecutionContext -import cats.effect.{ Blocker, ContextShift, IO } +import cats.effect.{Blocker, ContextShift, IO} import com.typesafe.config.ConfigFactory -import pureconfig.{ BaseSuite, ConfigSource } -import pureconfig.error.{ ConfigReaderException, ConvertFailure } +import pureconfig.{BaseSuite, ConfigSource} +import pureconfig.error.{ConfigReaderException, ConvertFailure} import pureconfig.generic.auto._ import pureconfig.module.catseffect.syntax._ diff --git a/modules/cats/src/main/scala/pureconfig/module/cats/EmptyTraversableFound.scala b/modules/cats/src/main/scala/pureconfig/module/cats/EmptyTraversableFound.scala index a298d3650..6525421fc 100644 --- a/modules/cats/src/main/scala/pureconfig/module/cats/EmptyTraversableFound.scala +++ b/modules/cats/src/main/scala/pureconfig/module/cats/EmptyTraversableFound.scala @@ -3,10 +3,10 @@ package pureconfig.module.cats import pureconfig.error.FailureReason /** - * A failure representing an unexpected empty traversable - * - * @param typ the type that was attempted to be converted to from an empty string - */ + * A failure representing an unexpected empty traversable + * + * @param typ the type that was attempted to be converted to from an empty string + */ final case class EmptyTraversableFound(typ: String) extends FailureReason { def description = s"Empty collection found when trying to convert to $typ." } diff --git a/modules/cats/src/main/scala/pureconfig/module/cats/instances/package.scala b/modules/cats/src/main/scala/pureconfig/module/cats/instances/package.scala index da455bd80..8e39ccbe6 100644 --- a/modules/cats/src/main/scala/pureconfig/module/cats/instances/package.scala +++ b/modules/cats/src/main/scala/pureconfig/module/cats/instances/package.scala @@ -1,9 +1,9 @@ package pureconfig.module.cats import cats._ -import com.typesafe.config.{ Config, ConfigFactory, ConfigValue } +import com.typesafe.config.{Config, ConfigFactory, ConfigValue} import pureconfig._ -import pureconfig.error.{ ConfigReaderFailure, ConfigReaderFailures } +import pureconfig.error.{ConfigReaderFailure, ConfigReaderFailures} package object instances { @@ -51,9 +51,7 @@ package object instances { fb.to(b).withFallback(fa.to(a)) } - ConfigConvert.fromReaderAndWriter( - Derivation.Successful(reader), - Derivation.Successful(writer)) + ConfigConvert.fromReaderAndWriter(Derivation.Successful(reader), Derivation.Successful(writer)) } } diff --git a/modules/cats/src/main/scala/pureconfig/module/cats/package.scala b/modules/cats/src/main/scala/pureconfig/module/cats/package.scala index e829d352f..b7150dde2 100644 --- a/modules/cats/src/main/scala/pureconfig/module/cats/package.scala +++ b/modules/cats/src/main/scala/pureconfig/module/cats/package.scala @@ -2,20 +2,22 @@ package pureconfig.module import _root_.cats.data._ import _root_.cats.kernel.Order -import _root_.cats.{ Alternative, Foldable } +import _root_.cats.{Alternative, Foldable} import _root_.cats.implicits._ -import pureconfig.{ ConfigReader, ConfigWriter, Exported } +import pureconfig.{ConfigReader, ConfigWriter, Exported} -import scala.collection.immutable.{ SortedMap, SortedSet } +import scala.collection.immutable.{SortedMap, SortedSet} import scala.language.higherKinds import scala.reflect.ClassTag /** - * `ConfigReader` and `ConfigWriter` instances for cats data structures. - */ + * `ConfigReader` and `ConfigWriter` instances for cats data structures. + */ package object cats { - private[pureconfig] def fromNonEmpty[A, B](reader: ConfigReader[A])(fromX: A => Option[B])(implicit ct: ClassTag[A]): ConfigReader[B] = + private[pureconfig] def fromNonEmpty[A, B]( + reader: ConfigReader[A] + )(fromX: A => Option[B])(implicit ct: ClassTag[A]): ConfigReader[B] = reader.emap(x => fromX(x).toRight(EmptyTraversableFound(ct.toString))) implicit def nonEmptyListReader[A](implicit reader: ConfigReader[List[A]]): ConfigReader[NonEmptyList[A]] = @@ -33,15 +35,22 @@ package object cats { implicit def nonEmptySetWriter[A](implicit writer: ConfigWriter[SortedSet[A]]): ConfigWriter[NonEmptySet[A]] = writer.contramap(_.toSortedSet) - implicit def nonEmptyMapReader[A, B](implicit reader: ConfigReader[Map[A, B]], ord: Order[A]): ConfigReader[NonEmptyMap[A, B]] = + implicit def nonEmptyMapReader[A, B](implicit + reader: ConfigReader[Map[A, B]], + ord: Order[A] + ): ConfigReader[NonEmptyMap[A, B]] = fromNonEmpty(reader)(x => NonEmptyMap.fromMap(SortedMap(x.toSeq: _*)(ord.toOrdering))) implicit def nonEmptyMapWriter[A, B](implicit writer: ConfigWriter[Map[A, B]]): ConfigWriter[NonEmptyMap[A, B]] = writer.contramap(_.toSortedMap) // For emptiable foldables not covered by TraversableOnce reader/writer, e.g. Chain. - implicit def lowPriorityNonReducibleReader[A, F[_]: Foldable: Alternative](implicit reader: ConfigReader[List[A]]): Exported[ConfigReader[F[A]]] = + implicit def lowPriorityNonReducibleReader[A, F[_]: Foldable: Alternative](implicit + reader: ConfigReader[List[A]] + ): Exported[ConfigReader[F[A]]] = Exported(reader.map(to => (to foldRight Alternative[F].empty[A])(_.pure[F] <+> _))) - implicit def lowPriorityNonReducibleWriter[A, F[_]: Foldable: Alternative](implicit writer: ConfigWriter[List[A]]): Exported[ConfigWriter[F[A]]] = + implicit def lowPriorityNonReducibleWriter[A, F[_]: Foldable: Alternative](implicit + writer: ConfigWriter[List[A]] + ): Exported[ConfigWriter[F[A]]] = Exported(writer.contramap(_.toList)) implicit def nonEmptyChainReader[A](implicit reader: ConfigReader[Chain[A]]): ConfigReader[NonEmptyChain[A]] = diff --git a/modules/cats/src/main/scala/pureconfig/module/cats/syntax/package.scala b/modules/cats/src/main/scala/pureconfig/module/cats/syntax/package.scala index 749ad2b30..5b06f81a5 100644 --- a/modules/cats/src/main/scala/pureconfig/module/cats/syntax/package.scala +++ b/modules/cats/src/main/scala/pureconfig/module/cats/syntax/package.scala @@ -1,17 +1,17 @@ package pureconfig.module.cats import cats.data.NonEmptyList -import pureconfig.error.{ ConfigReaderFailure, ConfigReaderFailures } +import pureconfig.error.{ConfigReaderFailure, ConfigReaderFailures} package object syntax { implicit class ConfigConvertFailureOps(val failures: ConfigReaderFailures) extends AnyVal { /** - * Converts this into a non-empty list of failures. - * - * @return a non-empty list of failures. - */ + * Converts this into a non-empty list of failures. + * + * @return a non-empty list of failures. + */ def toNonEmptyList: NonEmptyList[ConfigReaderFailure] = NonEmptyList(failures.head, failures.tail.toList) } } diff --git a/modules/cats/src/test/scala/pureconfig/module/cats/CatsLawsSuite.scala b/modules/cats/src/test/scala/pureconfig/module/cats/CatsLawsSuite.scala index b05c20366..aaa0c6751 100644 --- a/modules/cats/src/test/scala/pureconfig/module/cats/CatsLawsSuite.scala +++ b/modules/cats/src/test/scala/pureconfig/module/cats/CatsLawsSuite.scala @@ -4,9 +4,9 @@ import cats.instances.either._ import cats.instances.int._ import cats.instances.tuple._ import cats.instances.unit._ -import cats.kernel.laws.discipline.{ MonoidTests, SemigroupTests } +import cats.kernel.laws.discipline.{MonoidTests, SemigroupTests} import cats.laws.discipline._ -import com.typesafe.config.{ Config, ConfigValue } +import com.typesafe.config.{Config, ConfigValue} import org.scalatest.funsuite.AnyFunSuite import org.scalatestplus.scalacheck.ScalaCheckDrivenPropertyChecks import org.typelevel.discipline.scalatest.FunSuiteDiscipline @@ -17,7 +17,10 @@ import pureconfig.module.cats.eq._ import pureconfig.module.cats.instances._ class CatsLawsSuite extends AnyFunSuite with ScalaCheckDrivenPropertyChecks with FunSuiteDiscipline { - checkAll("ConfigReader[Int]", ApplicativeErrorTests[ConfigReader, ConfigReaderFailures].applicativeError[Int, Int, Int]) + checkAll( + "ConfigReader[Int]", + ApplicativeErrorTests[ConfigReader, ConfigReaderFailures].applicativeError[Int, Int, Int] + ) checkAll("ConfigWriter[Int]", ContravariantSemigroupalTests[ConfigWriter].contravariantSemigroupal[Int, Int, Int]) checkAll("ConfigConvert[Int]", InvariantSemigroupalTests[ConfigConvert].invariantSemigroupal[Int, Int, Int]) diff --git a/modules/cats/src/test/scala/pureconfig/module/cats/CatsSuite.scala b/modules/cats/src/test/scala/pureconfig/module/cats/CatsSuite.scala index 46393e9ac..628e57c0f 100644 --- a/modules/cats/src/test/scala/pureconfig/module/cats/CatsSuite.scala +++ b/modules/cats/src/test/scala/pureconfig/module/cats/CatsSuite.scala @@ -1,6 +1,6 @@ package pureconfig.module.cats -import scala.collection.immutable.{ SortedMap, SortedSet } +import scala.collection.immutable.{SortedMap, SortedSet} import cats.data._ import cats.implicits._ @@ -27,22 +27,38 @@ class CatsSuite extends BaseSuite { it should "return an EmptyTraversableFound when reading empty lists into NonEmptyList" in { val config = parseString("{ numbers: [] }") - config.to[Numbers] should failWith(EmptyTraversableFound("scala.collection.immutable.List"), "numbers", stringConfigOrigin(1)) + config.to[Numbers] should failWith( + EmptyTraversableFound("scala.collection.immutable.List"), + "numbers", + stringConfigOrigin(1) + ) } it should "return an EmptyTraversableFound when reading empty vector into NonEmptyVector" in { val config = parseString("{ numbers: [] }") - config.to[NumVec] should failWith(EmptyTraversableFound("scala.collection.immutable.Vector"), "numbers", stringConfigOrigin(1)) + config.to[NumVec] should failWith( + EmptyTraversableFound("scala.collection.immutable.Vector"), + "numbers", + stringConfigOrigin(1) + ) } it should "return an EmptyTraversableFound when reading empty set into NonEmptySet" in { val config = parseString("{ numbers: [] }") - config.to[NumSet] should failWith(EmptyTraversableFound("scala.collection.immutable.SortedSet"), "numbers", stringConfigOrigin(1)) + config.to[NumSet] should failWith( + EmptyTraversableFound("scala.collection.immutable.SortedSet"), + "numbers", + stringConfigOrigin(1) + ) } it should "return an EmptyTraversableFound when reading empty map into NonEmptyMap" in { val config = parseString("{ numbers{} }") - config.to[NumMap] should failWith(EmptyTraversableFound("scala.collection.immutable.Map"), "numbers", stringConfigOrigin(1)) + config.to[NumMap] should failWith( + EmptyTraversableFound("scala.collection.immutable.Map"), + "numbers", + stringConfigOrigin(1) + ) } it should "return an EmptyTraversableFound when reading empty chain into NonEmptyChain" in { diff --git a/modules/cats/src/test/scala/pureconfig/module/cats/arbitrary/package.scala b/modules/cats/src/test/scala/pureconfig/module/cats/arbitrary/package.scala index 5e7bb38b2..7f63bfc6b 100644 --- a/modules/cats/src/test/scala/pureconfig/module/cats/arbitrary/package.scala +++ b/modules/cats/src/test/scala/pureconfig/module/cats/arbitrary/package.scala @@ -2,11 +2,11 @@ package pureconfig.module.cats import scala.collection.JavaConverters._ -import com.typesafe.config.{ Config, ConfigValue, ConfigValueFactory } -import org.scalacheck.Arbitrary.{ arbitrary => arb } -import org.scalacheck.{ Arbitrary, Cogen, Gen } +import com.typesafe.config.{Config, ConfigValue, ConfigValueFactory} +import org.scalacheck.Arbitrary.{arbitrary => arb} +import org.scalacheck.{Arbitrary, Cogen, Gen} import pureconfig._ -import pureconfig.error.{ ConfigReaderFailures, ConvertFailure } +import pureconfig.error.{ConfigReaderFailures, ConvertFailure} package object arbitrary { @@ -24,17 +24,21 @@ package object arbitrary { Gen.oneOf(true, false), Gen.choose(Int.MinValue, Int.MaxValue), Gen.choose(Double.MinValue, Double.MaxValue), - Gen.alphaStr) + Gen.alphaStr + ) - def genList(maxDepth: Int): Gen[List[Any]] = Gen.choose(0, MaxCollSize).flatMap { sz => - Gen.listOfN(sz, Gen.lzy(genAnyForConfig(maxDepth - 1))) - } + def genList(maxDepth: Int): Gen[List[Any]] = + Gen.choose(0, MaxCollSize).flatMap { sz => + Gen.listOfN(sz, Gen.lzy(genAnyForConfig(maxDepth - 1))) + } if (maxDepth == 0) genScalar - else Gen.frequency( - 3 -> genScalar, - 1 -> genList(maxDepth).map(_.asJava), - 1 -> genAnyMapForConfig(maxDepth).map(_.asJava)) + else + Gen.frequency( + 3 -> genScalar, + 1 -> genList(maxDepth).map(_.asJava), + 1 -> genAnyMapForConfig(maxDepth).map(_.asJava) + ) } implicit val arbConfigValue: Arbitrary[ConfigValue] = Arbitrary { @@ -59,16 +63,21 @@ package object arbitrary { Gen.either(arb[ConfigReaderFailures], arb[Config]).map(ConfigObjectSource(_)) } - implicit def arbConfigReader[A: Arbitrary]: Arbitrary[ConfigReader[A]] = Arbitrary { - arb[ConfigValue => ConfigReader.Result[A]].map(ConfigReader.fromFunction) - } + implicit def arbConfigReader[A: Arbitrary]: Arbitrary[ConfigReader[A]] = + Arbitrary { + arb[ConfigValue => ConfigReader.Result[A]].map(ConfigReader.fromFunction) + } - implicit def arbConfigWriter[A: Cogen]: Arbitrary[ConfigWriter[A]] = Arbitrary { - arb[A => ConfigValue].map(ConfigWriter.fromFunction) - } + implicit def arbConfigWriter[A: Cogen]: Arbitrary[ConfigWriter[A]] = + Arbitrary { + arb[A => ConfigValue].map(ConfigWriter.fromFunction) + } - implicit def arbConfigConvert[A: Arbitrary: Cogen]: Arbitrary[ConfigConvert[A]] = Arbitrary { - for { reader <- arb[ConfigReader[A]]; writer <- arb[ConfigWriter[A]] } - yield ConfigConvert.fromReaderAndWriter(Derivation.Successful(reader), Derivation.Successful(writer)) - } + implicit def arbConfigConvert[A: Arbitrary: Cogen]: Arbitrary[ConfigConvert[A]] = + Arbitrary { + for { reader <- arb[ConfigReader[A]]; writer <- arb[ConfigWriter[A]] } yield ConfigConvert.fromReaderAndWriter( + Derivation.Successful(reader), + Derivation.Successful(writer) + ) + } } diff --git a/modules/cats/src/test/scala/pureconfig/module/cats/eq/package.scala b/modules/cats/src/test/scala/pureconfig/module/cats/eq/package.scala index 36f2b60c6..6f4079425 100644 --- a/modules/cats/src/test/scala/pureconfig/module/cats/eq/package.scala +++ b/modules/cats/src/test/scala/pureconfig/module/cats/eq/package.scala @@ -14,17 +14,18 @@ package object eq { // This is the old implementation (pre-2.0.0) of Eq for functions in Cats. The new implementation requires an instance // of ExhaustiveCheck (see https://github.com/typelevel/cats/pull/2577), which we are unable to provide for the types // we use in tests. For our use case, it's OK to go with Arbitrary values. - private implicit def catsLawsEqForFn1[A, B](implicit A: Arbitrary[A], B: Eq[B]): Eq[A => B] = new Eq[A => B] { - val sampleCnt: Int = 50 - - def eqv(f: A => B, g: A => B): Boolean = { - val samples = List.fill(sampleCnt)(A.arbitrary.sample).collect { - case Some(a) => a - case None => sys.error("Could not generate arbitrary values to compare two functions") + private implicit def catsLawsEqForFn1[A, B](implicit A: Arbitrary[A], B: Eq[B]): Eq[A => B] = + new Eq[A => B] { + val sampleCnt: Int = 50 + + def eqv(f: A => B, g: A => B): Boolean = { + val samples = List.fill(sampleCnt)(A.arbitrary.sample).collect { + case Some(a) => a + case None => sys.error("Could not generate arbitrary values to compare two functions") + } + samples.forall(s => B.eqv(f(s), g(s))) } - samples.forall(s => B.eqv(f(s), g(s))) } - } implicit def configReaderEq[A: Eq]: Eq[ConfigReader[A]] = Eq.by[ConfigReader[A], ConfigValue => ConfigReader.Result[A]](_.from) diff --git a/modules/circe/src/main/scala/pureconfig/module/circe/package.scala b/modules/circe/src/main/scala/pureconfig/module/circe/package.scala index b514957f6..1a2de8584 100644 --- a/modules/circe/src/main/scala/pureconfig/module/circe/package.scala +++ b/modules/circe/src/main/scala/pureconfig/module/circe/package.scala @@ -1,6 +1,6 @@ package pureconfig.module -import pureconfig.{ ConfigWriter, ConfigReader } +import pureconfig.{ConfigWriter, ConfigReader} import com.typesafe.config._ import io.circe._ import com.typesafe.config._ @@ -39,13 +39,15 @@ package object circe { json.fold( ConfigValueFactory.fromAnyRef(null), bool => ConfigValueFactory.fromAnyRef(bool), - jnum => jnum.toLong match { - case Some(long) => ConfigValueFactory.fromAnyRef(long) - case None => ConfigValueFactory.fromAnyRef(jnum.toDouble) - }, + jnum => + jnum.toLong match { + case Some(long) => ConfigValueFactory.fromAnyRef(long) + case None => ConfigValueFactory.fromAnyRef(jnum.toDouble) + }, str => ConfigValueFactory.fromAnyRef(str), arr => ConfigValueFactory.fromIterable(arr.map(jsonToCv).asJava), - obj => ConfigValueFactory.fromMap(obj.toMap.map { case (k, v) => k -> jsonToCv(v) }.asJava)) + obj => ConfigValueFactory.fromMap(obj.toMap.map { case (k, v) => k -> jsonToCv(v) }.asJava) + ) } implicit val circeJsonReader: ConfigReader[Json] = diff --git a/modules/circe/src/test/scala/pureconfig/module/circe/CirceSuite.scala b/modules/circe/src/test/scala/pureconfig/module/circe/CirceSuite.scala index 77b3df0f3..31ff23afb 100644 --- a/modules/circe/src/test/scala/pureconfig/module/circe/CirceSuite.scala +++ b/modules/circe/src/test/scala/pureconfig/module/circe/CirceSuite.scala @@ -13,7 +13,8 @@ import pureconfig.syntax._ class CirceSuite extends AnyFlatSpec with Matchers with EitherValues { case class JsonConf(json: Json) - val confJson = json"""{ "long": 123, "double": 123.123, "alpha": "test", "arr": [1, 2, 3], "map": { "key1": "value1", "key2": "value2" } }""" + val confJson = + json"""{ "long": 123, "double": 123.123, "alpha": "test", "arr": [1, 2, 3], "map": { "key1": "value1", "key2": "value2" } }""" val confString = """ json = { long = 123 diff --git a/modules/cron4s/src/main/scala/pureconfig/module/cron4s/package.scala b/modules/cron4s/src/main/scala/pureconfig/module/cron4s/package.scala index 10d89877e..d6b5cfc72 100644 --- a/modules/cron4s/src/main/scala/pureconfig/module/cron4s/package.scala +++ b/modules/cron4s/src/main/scala/pureconfig/module/cron4s/package.scala @@ -9,8 +9,8 @@ package object cron4s { implicit val cronExprConfigConvert: ConfigConvert[CronExpr] = ConfigConvert.viaNonEmptyString( - str => Cron.parse(str).fold( - err => Left(CannotConvert(str, "CronExpr", err.getMessage)), - expr => Right(expr)), _.toString) + str => Cron.parse(str).fold(err => Left(CannotConvert(str, "CronExpr", err.getMessage)), expr => Right(expr)), + _.toString + ) } diff --git a/modules/cron4s/src/test/scala/pureconfig/module/cron4s/Cron4sSuite.scala b/modules/cron4s/src/test/scala/pureconfig/module/cron4s/Cron4sSuite.scala index d4416f1cd..44983e4ec 100644 --- a/modules/cron4s/src/test/scala/pureconfig/module/cron4s/Cron4sSuite.scala +++ b/modules/cron4s/src/test/scala/pureconfig/module/cron4s/Cron4sSuite.scala @@ -1,11 +1,11 @@ package pureconfig.module.cron4s -import java.util.{ Map => JMap } +import java.util.{Map => JMap} import com.typesafe.config.ConfigFactory import _root_.cron4s.expr.CronExpr import _root_.cron4s.Cron import pureconfig.BaseSuite -import pureconfig.error.{ CannotConvert, ConfigReaderFailures, ConvertFailure } +import pureconfig.error.{CannotConvert, ConfigReaderFailures, ConvertFailure} import pureconfig.generic.auto._ import pureconfig.syntax._ @@ -26,7 +26,9 @@ class Cron4sSuite extends BaseSuite { ConvertFailure( CannotConvert("10-65 * * * * *", "CronExpr", "blank expected at position 3 but found '-'"), stringConfigOrigin(1), - "schedule")) + "schedule" + ) + ) conf.to[Config].left.value shouldEqual errors } diff --git a/modules/enum/src/main/scala/pureconfig/module/enum/package.scala b/modules/enum/src/main/scala/pureconfig/module/enum/package.scala index 9ad8d7e74..ffdf6e137 100644 --- a/modules/enum/src/main/scala/pureconfig/module/enum/package.scala +++ b/modules/enum/src/main/scala/pureconfig/module/enum/package.scala @@ -10,8 +10,8 @@ import scala.reflect.ClassTag package object enum { implicit def enumConfigConvert[A](implicit e: Enum[A], ct: ClassTag[A]): ConfigConvert[A] = { viaNonEmptyString( - s => - e.decode(s).left.map(failure => CannotConvert(s, ct.runtimeClass.getSimpleName, failure.toString)), - e.encode) + s => e.decode(s).left.map(failure => CannotConvert(s, ct.runtimeClass.getSimpleName, failure.toString)), + e.encode + ) } } diff --git a/modules/enumeratum/src/main/scala/pureconfig/module/enumeratum/package.scala b/modules/enumeratum/src/main/scala/pureconfig/module/enumeratum/package.scala index 46185c969..6138930cb 100644 --- a/modules/enumeratum/src/main/scala/pureconfig/module/enumeratum/package.scala +++ b/modules/enumeratum/src/main/scala/pureconfig/module/enumeratum/package.scala @@ -5,7 +5,7 @@ import scala.reflect.ClassTag import _root_.enumeratum._ import _root_.enumeratum.values._ import pureconfig.ConfigConvert -import pureconfig.ConfigConvert.{ viaNonEmptyString, viaNonEmptyStringOpt, viaStringOpt } +import pureconfig.ConfigConvert.{viaNonEmptyString, viaNonEmptyStringOpt, viaStringOpt} import pureconfig.error.CannotConvert package object enumeratum { @@ -13,28 +13,48 @@ package object enumeratum { implicit def enumeratumConfigConvert[A <: EnumEntry](implicit enum: Enum[A], ct: ClassTag[A]): ConfigConvert[A] = viaNonEmptyStringOpt[A](enum.withNameOption, _.entryName) - implicit def enumeratumIntConfigConvert[A <: IntEnumEntry](implicit enum: IntEnum[A], ct: ClassTag[A]): ConfigConvert[A] = + implicit def enumeratumIntConfigConvert[A <: IntEnumEntry](implicit + enum: IntEnum[A], + ct: ClassTag[A] + ): ConfigConvert[A] = viaNonEmptyStringOpt[A](v => enum.withValueOpt(v.toInt), _.value.toString) - implicit def enumeratumLongConfigConvert[A <: LongEnumEntry](implicit enum: LongEnum[A], ct: ClassTag[A]): ConfigConvert[A] = + implicit def enumeratumLongConfigConvert[A <: LongEnumEntry](implicit + enum: LongEnum[A], + ct: ClassTag[A] + ): ConfigConvert[A] = viaNonEmptyStringOpt[A](v => enum.withValueOpt(v.toLong), _.value.toString) - implicit def enumeratumShortConfigConvert[A <: ShortEnumEntry](implicit enum: ShortEnum[A], ct: ClassTag[A]): ConfigConvert[A] = + implicit def enumeratumShortConfigConvert[A <: ShortEnumEntry](implicit + enum: ShortEnum[A], + ct: ClassTag[A] + ): ConfigConvert[A] = viaNonEmptyStringOpt[A](v => enum.withValueOpt(v.toShort), _.value.toString) - implicit def enumeratumStringConfigConvert[A <: StringEnumEntry](implicit enum: StringEnum[A], ct: ClassTag[A]): ConfigConvert[A] = + implicit def enumeratumStringConfigConvert[A <: StringEnumEntry](implicit + enum: StringEnum[A], + ct: ClassTag[A] + ): ConfigConvert[A] = viaStringOpt[A](v => enum.withValueOpt(v), _.value.toString) - implicit def enumeratumByteConfigConvert[A <: ByteEnumEntry](implicit enum: ByteEnum[A], ct: ClassTag[A]): ConfigConvert[A] = + implicit def enumeratumByteConfigConvert[A <: ByteEnumEntry](implicit + enum: ByteEnum[A], + ct: ClassTag[A] + ): ConfigConvert[A] = viaNonEmptyStringOpt[A](v => enum.withValueOpt(v.toByte), _.value.toString) - implicit def enumeratumCharConfigConvert[A <: CharEnumEntry](implicit enum: CharEnum[A], ct: ClassTag[A]): ConfigConvert[A] = + implicit def enumeratumCharConfigConvert[A <: CharEnumEntry](implicit + enum: CharEnum[A], + ct: ClassTag[A] + ): ConfigConvert[A] = viaNonEmptyString[A]( - s => ensureOneChar(s) match { - case Right(v) => Right(enum.withValue(v)) - case Left(msg) => Left(CannotConvert(s, ct.runtimeClass.getSimpleName, msg)) - }, - _.value.toString) + s => + ensureOneChar(s) match { + case Right(v) => Right(enum.withValue(v)) + case Left(msg) => Left(CannotConvert(s, ct.runtimeClass.getSimpleName, msg)) + }, + _.value.toString + ) private val ensureOneChar: Seq[Char] => Either[String, Char] = { case Seq(c) => Right(c) diff --git a/modules/enumeratum/src/test/scala/pureconfig/module/enumeratum/EnumeratumConvertTest.scala b/modules/enumeratum/src/test/scala/pureconfig/module/enumeratum/EnumeratumConvertTest.scala index 1265c5659..0e5eed164 100644 --- a/modules/enumeratum/src/test/scala/pureconfig/module/enumeratum/EnumeratumConvertTest.scala +++ b/modules/enumeratum/src/test/scala/pureconfig/module/enumeratum/EnumeratumConvertTest.scala @@ -1,7 +1,7 @@ package pureconfig.module.enumeratum import com.typesafe.config.ConfigFactory -import enumeratum.EnumEntry.{ Snakecase, Uppercase } +import enumeratum.EnumEntry.{Snakecase, Uppercase} import enumeratum._ import enumeratum.values._ import org.scalatest.Inspectors @@ -20,11 +20,10 @@ class EnumeratumConvertTest extends BaseSuite { case object ShoutGoodBye extends Greeting with Uppercase } - "Enumeratum ConfigConvert" should "parse an enum" in Inspectors.forAll(Greeting.values) { - greeting => - val conf = ConfigFactory.parseString(s"""{greeting:"${greeting.entryName}"}""") - case class Conf(greeting: Greeting) - conf.to[Conf].right.value shouldEqual Conf(greeting) + "Enumeratum ConfigConvert" should "parse an enum" in Inspectors.forAll(Greeting.values) { greeting => + val conf = ConfigFactory.parseString(s"""{greeting:"${greeting.entryName}"}""") + case class Conf(greeting: Greeting) + conf.to[Conf].right.value shouldEqual Conf(greeting) } sealed abstract class IntLibraryItem(val value: Int, val name: String) extends IntEnumEntry @@ -37,11 +36,10 @@ class EnumeratumConvertTest extends BaseSuite { case object CD extends IntLibraryItem(4, name = "cd") } - it should "parse an int enum" in Inspectors.forAll(IntLibraryItem.values) { - item => - val conf = ConfigFactory.parseString(s"""{item:"${item.value}"}""") - case class Conf(item: IntLibraryItem) - conf.to[Conf].right.value shouldEqual Conf(item) + it should "parse an int enum" in Inspectors.forAll(IntLibraryItem.values) { item => + val conf = ConfigFactory.parseString(s"""{item:"${item.value}"}""") + case class Conf(item: IntLibraryItem) + conf.to[Conf].right.value shouldEqual Conf(item) } sealed abstract class LongLibraryItem(val value: Long, val name: String) extends LongEnumEntry @@ -54,11 +52,10 @@ class EnumeratumConvertTest extends BaseSuite { case object CD extends LongLibraryItem(4L, name = "cd") } - it should "parse a long value enum" in Inspectors.forAll(LongLibraryItem.values) { - item => - val conf = ConfigFactory.parseString(s"""{item:"${item.value}"}""") - case class Conf(item: LongLibraryItem) - conf.to[Conf].right.value shouldEqual Conf(item) + it should "parse a long value enum" in Inspectors.forAll(LongLibraryItem.values) { item => + val conf = ConfigFactory.parseString(s"""{item:"${item.value}"}""") + case class Conf(item: LongLibraryItem) + conf.to[Conf].right.value shouldEqual Conf(item) } sealed abstract class ShortLibraryItem(val value: Short, val name: String) extends ShortEnumEntry @@ -71,11 +68,10 @@ class EnumeratumConvertTest extends BaseSuite { case object CD extends ShortLibraryItem(4, name = "cd") } - it should "parse a short value enum" in Inspectors.forAll(ShortLibraryItem.values) { - item => - val conf = ConfigFactory.parseString(s"""{item:"${item.value}"}""") - case class Conf(item: ShortLibraryItem) - conf.to[Conf].right.value shouldEqual Conf(item) + it should "parse a short value enum" in Inspectors.forAll(ShortLibraryItem.values) { item => + val conf = ConfigFactory.parseString(s"""{item:"${item.value}"}""") + case class Conf(item: ShortLibraryItem) + conf.to[Conf].right.value shouldEqual Conf(item) } sealed abstract class StringLibraryItem(val value: String, val number: Int) extends StringEnumEntry @@ -89,11 +85,10 @@ class EnumeratumConvertTest extends BaseSuite { case object Empty extends StringLibraryItem("", number = 5) } - it should "parse a string value enum" in Inspectors.forAll(StringLibraryItem.values) { - item => - val conf = ConfigFactory.parseString(s"""{item:"${item.value}"}""") - case class Conf(item: StringLibraryItem) - conf.to[Conf].right.value shouldEqual Conf(item) + it should "parse a string value enum" in Inspectors.forAll(StringLibraryItem.values) { item => + val conf = ConfigFactory.parseString(s"""{item:"${item.value}"}""") + case class Conf(item: StringLibraryItem) + conf.to[Conf].right.value shouldEqual Conf(item) } sealed abstract class ByteLibraryItem(val value: Byte, val name: String) extends ByteEnumEntry @@ -106,11 +101,10 @@ class EnumeratumConvertTest extends BaseSuite { case object CD extends ByteLibraryItem(4, name = "cd") } - it should "parse a byte value enum" in Inspectors.forAll(ByteLibraryItem.values) { - item => - val conf = ConfigFactory.parseString(s"""{item:"${item.value}"}""") - case class Conf(item: ByteLibraryItem) - conf.to[Conf].right.value shouldEqual Conf(item) + it should "parse a byte value enum" in Inspectors.forAll(ByteLibraryItem.values) { item => + val conf = ConfigFactory.parseString(s"""{item:"${item.value}"}""") + case class Conf(item: ByteLibraryItem) + conf.to[Conf].right.value shouldEqual Conf(item) } sealed abstract class CharLibraryItem(val value: Char, val number: Int) extends CharEnumEntry @@ -123,11 +117,10 @@ class EnumeratumConvertTest extends BaseSuite { case object CD extends CharLibraryItem('d', number = 4) } - it should "parse a char value enum" in Inspectors.forAll(CharLibraryItem.values) { - item => - val conf = ConfigFactory.parseString(s"""{item:"${item.value}"}""") - case class Conf(item: CharLibraryItem) - conf.to[Conf].right.value shouldEqual Conf(item) + it should "parse a char value enum" in Inspectors.forAll(CharLibraryItem.values) { item => + val conf = ConfigFactory.parseString(s"""{item:"${item.value}"}""") + case class Conf(item: CharLibraryItem) + conf.to[Conf].right.value shouldEqual Conf(item) } it should "not parse a char value enum when given a string with more than one character" in { diff --git a/modules/fs2/src/main/scala/pureconfig/module/fs2/package.scala b/modules/fs2/src/main/scala/pureconfig/module/fs2/package.scala index 4e56b0af3..6c0189f48 100644 --- a/modules/fs2/src/main/scala/pureconfig/module/fs2/package.scala +++ b/modules/fs2/src/main/scala/pureconfig/module/fs2/package.scala @@ -5,28 +5,28 @@ import java.nio.charset.StandardCharsets.UTF_8 import scala.language.higherKinds import scala.reflect.ClassTag -import _root_.fs2.{ Stream, text } +import _root_.fs2.{Stream, text} import cats.effect.Sync import cats.implicits._ import com.typesafe.config.ConfigRenderOptions import pureconfig.backend.ConfigFactoryWrapper import pureconfig.error.ConfigReaderException -import pureconfig.{ ConfigReader, ConfigSource, ConfigWriter, Derivation } +import pureconfig.{ConfigReader, ConfigSource, ConfigWriter, Derivation} package object fs2 { /** - * Load a configuration of type `A` from the given byte stream. - * - * @param configStream a stream of bytes representing the contents of a configuration file - * @return The returned action will complete with `A` if it is possible to create an instance of type - * `A` from the configuration stream, or fail with a ConfigReaderException which in turn contains - * details on why it isn't possible - * It can also raise any exception that the stream can raise. - */ - def streamConfig[F[_], A](configStream: Stream[F, Byte])( - implicit - F: Sync[F], reader: Derivation[ConfigReader[A]], ct: ClassTag[A]): F[A] = { + * Load a configuration of type `A` from the given byte stream. + * + * @param configStream a stream of bytes representing the contents of a configuration file + * @return The returned action will complete with `A` if it is possible to create an instance of type + * `A` from the configuration stream, or fail with a ConfigReaderException which in turn contains + * details on why it isn't possible + * It can also raise any exception that the stream can raise. + */ + def streamConfig[F[_], A]( + configStream: Stream[F, Byte] + )(implicit F: Sync[F], reader: Derivation[ConfigReader[A]], ct: ClassTag[A]): F[A] = { for { bytes <- configStream.compile.to[Array] string = new String(bytes, UTF_8) @@ -38,15 +38,15 @@ package object fs2 { } /** - * Writes the configuration to a fs2 byte stream - * - * @param config The configuration to write - * @param options the config rendering options - * @return the configuration as a stream of utf-8 bytes - */ - def saveConfigToStream[F[_], A]( - config: A, - options: ConfigRenderOptions = ConfigRenderOptions.defaults())(implicit writer: Derivation[ConfigWriter[A]]): Stream[F, Byte] = { + * Writes the configuration to a fs2 byte stream + * + * @param config The configuration to write + * @param options the config rendering options + * @return the configuration as a stream of utf-8 bytes + */ + def saveConfigToStream[F[_], A](config: A, options: ConfigRenderOptions = ConfigRenderOptions.defaults())(implicit + writer: Derivation[ConfigWriter[A]] + ): Stream[F, Byte] = { val asString = writer.value.to(config).render(options) Stream.emit(asString).through(text.utf8Encode) diff --git a/modules/fs2/src/test/scala/pureconfig/module/fs2Suite.scala b/modules/fs2/src/test/scala/pureconfig/module/fs2Suite.scala index 455bcaa94..c84971aab 100644 --- a/modules/fs2/src/test/scala/pureconfig/module/fs2Suite.scala +++ b/modules/fs2/src/test/scala/pureconfig/module/fs2Suite.scala @@ -3,15 +3,15 @@ package pureconfig.module import scala.concurrent.ExecutionContext import scala.concurrent.duration._ -import _root_.fs2.{ Stream, text } -import cats.effect.{ IO, Timer } +import _root_.fs2.{Stream, text} +import cats.effect.{IO, Timer} import cats.implicits._ import org.scalatest.EitherValues._ import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers import pureconfig.error.ConfigReaderException import pureconfig.generic.auto._ -import pureconfig.module.{ fs2 => testee } +import pureconfig.module.{fs2 => testee} class fs2Suite extends AnyFlatSpec with Matchers { diff --git a/modules/generic-base/src/main/scala/pureconfig/generic/CoproductHint.scala b/modules/generic-base/src/main/scala/pureconfig/generic/CoproductHint.scala index b625f12ed..263e44cda 100644 --- a/modules/generic-base/src/main/scala/pureconfig/generic/CoproductHint.scala +++ b/modules/generic-base/src/main/scala/pureconfig/generic/CoproductHint.scala @@ -1,62 +1,66 @@ package pureconfig.generic -import com.typesafe.config.{ ConfigObject, ConfigValue, ConfigValueType } +import com.typesafe.config.{ConfigObject, ConfigValue, ConfigValueType} import pureconfig._ import pureconfig.error._ -import pureconfig.generic.CoproductHint.{ Attempt, Use } -import pureconfig.generic.error.{ CoproductHintException, NoValidCoproductOptionFound, UnexpectedValueForFieldCoproductHint } +import pureconfig.generic.CoproductHint.{Attempt, Use} +import pureconfig.generic.error.{ + CoproductHintException, + NoValidCoproductOptionFound, + UnexpectedValueForFieldCoproductHint +} import pureconfig.syntax._ /** - * A trait that can be implemented to disambiguate between the different options of a coproduct or sealed family. - * - * @tparam A the type of the coproduct or sealed family for which this hint applies - */ + * A trait that can be implemented to disambiguate between the different options of a coproduct or sealed family. + * + * @tparam A the type of the coproduct or sealed family for which this hint applies + */ trait CoproductHint[A] { /** - * Given a `ConfigCursor` for the sealed family, disambiguate and return what should be performed when trying to read - * one of the provided coproduct options. This method can decide either to: - * - use the `ConfigCursor` with a single option ([[CoproductHint.Use]]); - * - or attempt different options in a given order ([[CoproductHint.Attempt]]). - * - * This method can return a `Left` if the hint fails to produce a valid [[CoproductHint.Action]]. - * - * @param cursor a `ConfigCursor` at the sealed family option - * @param options the names of the coproduct options for the given type - * @return a `ConfigReader.Result` of [[CoproductHint.Action]] as defined above. - */ + * Given a `ConfigCursor` for the sealed family, disambiguate and return what should be performed when trying to read + * one of the provided coproduct options. This method can decide either to: + * - use the `ConfigCursor` with a single option ([[CoproductHint.Use]]); + * - or attempt different options in a given order ([[CoproductHint.Attempt]]). + * + * This method can return a `Left` if the hint fails to produce a valid [[CoproductHint.Action]]. + * + * @param cursor a `ConfigCursor` at the sealed family option + * @param options the names of the coproduct options for the given type + * @return a `ConfigReader.Result` of [[CoproductHint.Action]] as defined above. + */ def from(cursor: ConfigCursor, options: Seq[String]): ConfigReader.Result[CoproductHint.Action] /** - * Given the `ConfigValue` for a specific class or coproduct option, encode disambiguation information and return a - * config for the sealed family or coproduct. - * - * @param value the `ConfigValue` of the class or coproduct option - * @param name the name of the class or coproduct option - * @return the config for the sealed family or coproduct wrapped in a `Right`, or a `Left` with the failure if some - * error occurred. - */ + * Given the `ConfigValue` for a specific class or coproduct option, encode disambiguation information and return a + * config for the sealed family or coproduct. + * + * @param value the `ConfigValue` of the class or coproduct option + * @param name the name of the class or coproduct option + * @return the config for the sealed family or coproduct wrapped in a `Right`, or a `Left` with the failure if some + * error occurred. + */ def to(value: ConfigValue, name: String): ConfigValue } /** - * Hint where the options are disambiguated by a `key = "value"` field inside the config. - * - * This hint will cause derived `ConfigConvert` instance to fail to convert configs to objects if the object has a - * field with the same name as the disambiguation key. - * - * By default, the field value written is the class or coproduct option name converted to kebab case. This mapping can - * be changed by overriding the method `fieldValue` of this class. - */ + * Hint where the options are disambiguated by a `key = "value"` field inside the config. + * + * This hint will cause derived `ConfigConvert` instance to fail to convert configs to objects if the object has a + * field with the same name as the disambiguation key. + * + * By default, the field value written is the class or coproduct option name converted to kebab case. This mapping can + * be changed by overriding the method `fieldValue` of this class. + */ class FieldCoproductHint[A](key: String) extends CoproductHint[A] { /** - * Returns the field value for a class or coproduct option name. - * - * @param name the name of the class or coproduct option - * @return the field value associated with the given class or coproduct option name. - */ + * Returns the field value for a class or coproduct option name. + * + * @param name the name of the class or coproduct option + * @return the field value associated with the given class or coproduct option name. + */ protected def fieldValue(name: String): String = FieldCoproductHint.defaultMapping(name) def from(cursor: ConfigCursor, options: Seq[String]): ConfigReader.Result[CoproductHint.Action] = { @@ -64,8 +68,11 @@ class FieldCoproductHint[A](key: String) extends CoproductHint[A] { objCur <- cursor.asObjectCursor.right valueCur <- objCur.atKey(key).right valueStr <- valueCur.asString.right - option <- options.find(valueStr == fieldValue(_)) - .toRight(ConfigReaderFailures(valueCur.failureFor(UnexpectedValueForFieldCoproductHint(valueCur.value)))).right + option <- + options + .find(valueStr == fieldValue(_)) + .toRight(ConfigReaderFailures(valueCur.failureFor(UnexpectedValueForFieldCoproductHint(valueCur.value)))) + .right } yield Use(objCur.withoutKey(key), option) } @@ -86,12 +93,18 @@ object FieldCoproductHint { } /** - * Hint where all coproduct options are tried in order. `from` will choose the first option able to deserialize - * the config without errors, while `to` will write the config as is, with no disambiguation information. - */ + * Hint where all coproduct options are tried in order. `from` will choose the first option able to deserialize + * the config without errors, while `to` will write the config as is, with no disambiguation information. + */ class FirstSuccessCoproductHint[A] extends CoproductHint[A] { def from(cursor: ConfigCursor, options: Seq[String]): ConfigReader.Result[CoproductHint.Action] = - Right(Attempt(cursor, options, failures => ConfigReaderFailures(cursor.failureFor(NoValidCoproductOptionFound(cursor.value, failures))))) + Right( + Attempt( + cursor, + options, + failures => ConfigReaderFailures(cursor.failureFor(NoValidCoproductOptionFound(cursor.value, failures))) + ) + ) def to(value: ConfigValue, name: String): ConfigValue = value @@ -100,32 +113,37 @@ class FirstSuccessCoproductHint[A] extends CoproductHint[A] { object CoproductHint { /** - * What should be done when reading a given coproduct option. - */ + * What should be done when reading a given coproduct option. + */ sealed trait Action { + /** - * The `ConfigCursor` to use when trying to read the coproduct option. - */ + * The `ConfigCursor` to use when trying to read the coproduct option. + */ def cursor: ConfigCursor } /** - * An action to only use the provided `ConfigCursor` and not try other options. - * - * @param cursor the `ConfigCursor` to use when reading the coproduct option - * @param option the coproduct option to consider when reading from the provider cursor - */ + * An action to only use the provided `ConfigCursor` and not try other options. + * + * @param cursor the `ConfigCursor` to use when reading the coproduct option + * @param option the coproduct option to consider when reading from the provider cursor + */ case class Use(cursor: ConfigCursor, option: String) extends Action /** - * An action to attempt to use the provided coproduct options, in the specified order, stopping at the first one that - * reads successfully. - * - * @param cursor the `ConfigCursor` to use when reading the coproduct option - * @param options the coproduct options to attempt reading, in order - * @param combineFailures the function to combine all failures in case all attempts to read fail - */ - case class Attempt(cursor: ConfigCursor, options: Seq[String], combineFailures: Seq[(String, ConfigReaderFailures)] => ConfigReaderFailures) extends Action + * An action to attempt to use the provided coproduct options, in the specified order, stopping at the first one that + * reads successfully. + * + * @param cursor the `ConfigCursor` to use when reading the coproduct option + * @param options the coproduct options to attempt reading, in order + * @param combineFailures the function to combine all failures in case all attempts to read fail + */ + case class Attempt( + cursor: ConfigCursor, + options: Seq[String], + combineFailures: Seq[(String, ConfigReaderFailures)] => ConfigReaderFailures + ) extends Action implicit def default[A]: CoproductHint[A] = new FieldCoproductHint[A]("type") } diff --git a/modules/generic-base/src/main/scala/pureconfig/generic/ProductHint.scala b/modules/generic-base/src/main/scala/pureconfig/generic/ProductHint.scala index fda970e33..af944da66 100644 --- a/modules/generic-base/src/main/scala/pureconfig/generic/ProductHint.scala +++ b/modules/generic-base/src/main/scala/pureconfig/generic/ProductHint.scala @@ -2,52 +2,53 @@ package pureconfig.generic import com.typesafe.config.ConfigValue import pureconfig._ -import pureconfig.error.{ ConfigReaderFailures, UnknownKey } +import pureconfig.error.{ConfigReaderFailures, UnknownKey} /** - * A trait that can be implemented to customize how case classes are read from and written to a config. - * - * @tparam A the type of case class for which this hint applies - */ + * A trait that can be implemented to customize how case classes are read from and written to a config. + * + * @tparam A the type of case class for which this hint applies + */ trait ProductHint[A] { /** - * Returns what should be used when attempting to read a case class field with name `fieldName` from the provided - * `ConfigObjectCursor`. - * - * @param cursor the cursor from which to read a value - * @param fieldName the name of the field in `T` - * @return a [[ProductHint.Action]] object that signals which cursor to use in order to read this field, the name - * of the corresponding field in the config object, whether the field should be removed from the object after - * read, and whether to use default values for this particular field. - */ + * Returns what should be used when attempting to read a case class field with name `fieldName` from the provided + * `ConfigObjectCursor`. + * + * @param cursor the cursor from which to read a value + * @param fieldName the name of the field in `T` + * @return a [[ProductHint.Action]] object that signals which cursor to use in order to read this field, the name + * of the corresponding field in the config object, whether the field should be removed from the object after + * read, and whether to use default values for this particular field. + */ def from(cursor: ConfigObjectCursor, fieldName: String): ProductHint.Action /** - * Returns optional failures given the provided `ConfigObjectCursor`. - * - * @param cursor a `ConfigObjectCursor` at the configuration root from where the product was read - * @param usedFields a set of all the used fields when reading the product - * @return an optional non-empty list of failures. - */ + * Returns optional failures given the provided `ConfigObjectCursor`. + * + * @param cursor a `ConfigObjectCursor` at the configuration root from where the product was read + * @param usedFields a set of all the used fields when reading the product + * @return an optional non-empty list of failures. + */ def bottom(cursor: ConfigObjectCursor, usedFields: Set[String]): Option[ConfigReaderFailures] /** - * Returns an optional key-value pair that should be used for the field with name `fieldName` in the `ConfigObject` - * representation of `T`. - * - * @param value the optional serialized value of the field - * @param fieldName the name of the field in `T` - * @return an optional key-value pair to be used in the `ConfigObject` representation of `T`. If `None`, the field is - * omitted from the `ConfigObject` representation. - */ + * Returns an optional key-value pair that should be used for the field with name `fieldName` in the `ConfigObject` + * representation of `T`. + * + * @param value the optional serialized value of the field + * @param fieldName the name of the field in `T` + * @return an optional key-value pair to be used in the `ConfigObject` representation of `T`. If `None`, the field is + * omitted from the `ConfigObject` representation. + */ def to(value: Option[ConfigValue], fieldName: String): Option[(String, ConfigValue)] } private[pureconfig] case class ProductHintImpl[A]( fieldMapping: ConfigFieldMapping, useDefaultArgs: Boolean, - allowUnknownKeys: Boolean) extends ProductHint[A] { + allowUnknownKeys: Boolean +) extends ProductHint[A] { def from(cursor: ConfigObjectCursor, fieldName: String): ProductHint.Action = { val keyStr = fieldMapping(fieldName) @@ -80,41 +81,43 @@ private[pureconfig] case class ProductHintImpl[A]( object ProductHint { /** - * What should be done when attempting to read a given field from a product. - */ + * What should be done when attempting to read a given field from a product. + */ sealed trait Action { + /** - * The `ConfigCursor` to use when trying to read the field. - */ + * The `ConfigCursor` to use when trying to read the field. + */ def cursor: ConfigCursor /** - * The name of the field in the `ConfigObject` representation of the product. - */ + * The name of the field in the `ConfigObject` representation of the product. + */ def field: String } /** - * An action to use the provided `ConfigCursor` when trying to read a given field. - * - * @param cursor the `ConfigCursor` to use when trying to read the field - * @param field the name of the field in the `ConfigObject` representation of the product - */ + * An action to use the provided `ConfigCursor` when trying to read a given field. + * + * @param cursor the `ConfigCursor` to use when trying to read the field + * @param field the name of the field in the `ConfigObject` representation of the product + */ case class Use(cursor: ConfigCursor, field: String) extends Action /** - * An action to either use the provided `ConfigCursor` (if it isn't null or undefined) or fallback to the default - * value in the product's constructor. - * - * @param cursor the `ConfigCursor` to use when trying to read the field - * @param field the name of the field in the `ConfigObject` representation of the product - */ + * An action to either use the provided `ConfigCursor` (if it isn't null or undefined) or fallback to the default + * value in the product's constructor. + * + * @param cursor the `ConfigCursor` to use when trying to read the field + * @param field the name of the field in the `ConfigObject` representation of the product + */ case class UseOrDefault(cursor: ConfigCursor, field: String) extends Action def apply[A]( - fieldMapping: ConfigFieldMapping = ConfigFieldMapping(CamelCase, KebabCase), - useDefaultArgs: Boolean = true, - allowUnknownKeys: Boolean = true): ProductHint[A] = + fieldMapping: ConfigFieldMapping = ConfigFieldMapping(CamelCase, KebabCase), + useDefaultArgs: Boolean = true, + allowUnknownKeys: Boolean = true + ): ProductHint[A] = ProductHintImpl[A](fieldMapping, useDefaultArgs, allowUnknownKeys) implicit def default[A]: ProductHint[A] = apply() diff --git a/modules/generic-base/src/main/scala/pureconfig/generic/error/CoproductHintException.scala b/modules/generic-base/src/main/scala/pureconfig/generic/error/CoproductHintException.scala index b348dba0e..0ffb2ddab 100644 --- a/modules/generic-base/src/main/scala/pureconfig/generic/error/CoproductHintException.scala +++ b/modules/generic-base/src/main/scala/pureconfig/generic/error/CoproductHintException.scala @@ -3,10 +3,10 @@ package pureconfig.generic.error import pureconfig.error.FailureReason /** - * An exception to be thrown on operations inside CoproductHints. - * - * @param failure the reason for the exception - */ + * An exception to be thrown on operations inside CoproductHints. + * + * @param failure the reason for the exception + */ final case class CoproductHintException(failure: FailureReason) extends RuntimeException { override def getMessage: String = failure.description } diff --git a/modules/generic-base/src/main/scala/pureconfig/generic/error/InvalidCoproductOption.scala b/modules/generic-base/src/main/scala/pureconfig/generic/error/InvalidCoproductOption.scala index c9330d924..e8e06be4f 100644 --- a/modules/generic-base/src/main/scala/pureconfig/generic/error/InvalidCoproductOption.scala +++ b/modules/generic-base/src/main/scala/pureconfig/generic/error/InvalidCoproductOption.scala @@ -3,11 +3,11 @@ package pureconfig.generic.error import pureconfig.error.FailureReason /** - * A failure reason given when a provided coproduct option is invalid. This likely signals a bug in a CoproductHint - * implementation, since the provided option isn't a valid one for the CoproductHint's type. - * - * @param option the coproduct option that is invalid - */ + * A failure reason given when a provided coproduct option is invalid. This likely signals a bug in a CoproductHint + * implementation, since the provided option isn't a valid one for the CoproductHint's type. + * + * @param option the coproduct option that is invalid + */ final case class InvalidCoproductOption(option: String) extends FailureReason { def description = s"""|The provided option '$option' is invalid for the CoproductHint's type. There's likely a bug in the diff --git a/modules/generic-base/src/main/scala/pureconfig/generic/error/NoValidCoproductOptionFound.scala b/modules/generic-base/src/main/scala/pureconfig/generic/error/NoValidCoproductOptionFound.scala index d2f9f38fd..cbaa1d887 100644 --- a/modules/generic-base/src/main/scala/pureconfig/generic/error/NoValidCoproductOptionFound.scala +++ b/modules/generic-base/src/main/scala/pureconfig/generic/error/NoValidCoproductOptionFound.scala @@ -1,25 +1,26 @@ package pureconfig.generic.error -import com.typesafe.config.{ ConfigRenderOptions, ConfigValue } -import pureconfig.error.{ ConfigReaderFailures, FailureReason } +import com.typesafe.config.{ConfigRenderOptions, ConfigValue} +import pureconfig.error.{ConfigReaderFailures, FailureReason} /** - * A failure reason given when a valid option for a coproduct cannot be found. - * - * @param value the ConfigValue that was unable to be mapped to a coproduct option - * @param optionFailures the failures produced when attempting to read coproduct options - */ -final case class NoValidCoproductOptionFound(value: ConfigValue, optionFailures: Seq[(String, ConfigReaderFailures)]) extends FailureReason { + * A failure reason given when a valid option for a coproduct cannot be found. + * + * @param value the ConfigValue that was unable to be mapped to a coproduct option + * @param optionFailures the failures produced when attempting to read coproduct options + */ +final case class NoValidCoproductOptionFound(value: ConfigValue, optionFailures: Seq[(String, ConfigReaderFailures)]) + extends FailureReason { def description = { val baseDescription = s"No valid coproduct option found for '${value.render(ConfigRenderOptions.concise())}'." - baseDescription + ( - if (optionFailures.isEmpty) "" - else { - "\n" + optionFailures.map { - case (optionName, failures) => - s"Can't use coproduct option '$optionName':\n" + failures.prettyPrint(1) - }.mkString("\n") - }) + baseDescription + (if (optionFailures.isEmpty) "" + else { + "\n" + optionFailures + .map { + case (optionName, failures) => + s"Can't use coproduct option '$optionName':\n" + failures.prettyPrint(1) + } + .mkString("\n") + }) } } - diff --git a/modules/generic-base/src/main/scala/pureconfig/generic/error/UnexpectedValueForFieldCoproductHint.scala b/modules/generic-base/src/main/scala/pureconfig/generic/error/UnexpectedValueForFieldCoproductHint.scala index 53ba86495..94e3bc563 100644 --- a/modules/generic-base/src/main/scala/pureconfig/generic/error/UnexpectedValueForFieldCoproductHint.scala +++ b/modules/generic-base/src/main/scala/pureconfig/generic/error/UnexpectedValueForFieldCoproductHint.scala @@ -1,14 +1,14 @@ package pureconfig.generic.error -import com.typesafe.config.{ ConfigRenderOptions, ConfigValue } +import com.typesafe.config.{ConfigRenderOptions, ConfigValue} import pureconfig.error.FailureReason /** - * A failure reason given when an unknown value was found in the discriminating field of a config value, when using a - * `FieldCoproductHint`. - * - * @param value the value found in the discriminating field of a config value - */ + * A failure reason given when an unknown value was found in the discriminating field of a config value, when using a + * `FieldCoproductHint`. + * + * @param value the value found in the discriminating field of a config value + */ final case class UnexpectedValueForFieldCoproductHint(value: ConfigValue) extends FailureReason { def description = s"Unexpected value ${value.render(ConfigRenderOptions.concise())} found. Note that the default transformation " + diff --git a/modules/generic/src/main/scala/pureconfig/generic/CoproductConfigWriter.scala b/modules/generic/src/main/scala/pureconfig/generic/CoproductConfigWriter.scala index 6326dc5c5..0265ca614 100644 --- a/modules/generic/src/main/scala/pureconfig/generic/CoproductConfigWriter.scala +++ b/modules/generic/src/main/scala/pureconfig/generic/CoproductConfigWriter.scala @@ -1,37 +1,39 @@ package pureconfig.generic import com.typesafe.config.ConfigValue -import pureconfig.{ ConfigWriter, Derivation } +import pureconfig.{ConfigWriter, Derivation} import shapeless.labelled._ import shapeless._ /** - * A `ConfigWriter` for generic representations of coproducts. - * - * @tparam Original the original type for which `Repr` is the coproduct representation - * @tparam Repr the generic representation - */ + * A `ConfigWriter` for generic representations of coproducts. + * + * @tparam Original the original type for which `Repr` is the coproduct representation + * @tparam Repr the generic representation + */ private[generic] trait CoproductConfigWriter[Original, Repr <: Coproduct] extends ConfigWriter[Repr] object CoproductConfigWriter { - final implicit def cNilWriter[Original]: CoproductConfigWriter[Original, CNil] = new CoproductConfigWriter[Original, CNil] { - override def to(t: CNil): ConfigValue = - throw new IllegalStateException("Cannot encode CNil. This is likely a bug in PureConfig.") - } + final implicit def cNilWriter[Original]: CoproductConfigWriter[Original, CNil] = + new CoproductConfigWriter[Original, CNil] { + override def to(t: CNil): ConfigValue = + throw new IllegalStateException("Cannot encode CNil. This is likely a bug in PureConfig.") + } - final implicit def cConsWriter[Original, Name <: Symbol, V <: Original, T <: Coproduct]( - implicit - coproductHint: CoproductHint[Original], - vName: Witness.Aux[Name], - vConfigWriter: Derivation[Lazy[ConfigWriter[V]]], - tConfigWriter: Lazy[CoproductConfigWriter[Original, T]]): CoproductConfigWriter[Original, FieldType[Name, V] :+: T] = + final implicit def cConsWriter[Original, Name <: Symbol, V <: Original, T <: Coproduct](implicit + coproductHint: CoproductHint[Original], + vName: Witness.Aux[Name], + vConfigWriter: Derivation[Lazy[ConfigWriter[V]]], + tConfigWriter: Lazy[CoproductConfigWriter[Original, T]] + ): CoproductConfigWriter[Original, FieldType[Name, V] :+: T] = new CoproductConfigWriter[Original, FieldType[Name, V] :+: T] { - override def to(t: FieldType[Name, V] :+: T): ConfigValue = t match { - case Inl(l) => - coproductHint.to(vConfigWriter.value.value.to(l), vName.value.name) + override def to(t: FieldType[Name, V] :+: T): ConfigValue = + t match { + case Inl(l) => + coproductHint.to(vConfigWriter.value.value.to(l), vName.value.name) - case Inr(r) => - tConfigWriter.value.to(r) - } + case Inr(r) => + tConfigWriter.value.to(r) + } } } diff --git a/modules/generic/src/main/scala/pureconfig/generic/CoproductReaderOptions.scala b/modules/generic/src/main/scala/pureconfig/generic/CoproductReaderOptions.scala index 9a8106326..d0f74ee2c 100644 --- a/modules/generic/src/main/scala/pureconfig/generic/CoproductReaderOptions.scala +++ b/modules/generic/src/main/scala/pureconfig/generic/CoproductReaderOptions.scala @@ -5,8 +5,8 @@ import shapeless._ import shapeless.labelled._ /** - * A typeclass to collect the `ConfigReader` options for a given coproduct, indexed by the coproduct name. - */ + * A typeclass to collect the `ConfigReader` options for a given coproduct, indexed by the coproduct name. + */ private[generic] trait CoproductReaderOptions[Repr <: Coproduct] { def options: Map[String, ConfigReader[Repr]] } @@ -17,11 +17,11 @@ object CoproductReaderOptions { val options: Map[String, ConfigReader[CNil]] = Map.empty } - implicit def cConsReaderOptions[H, T <: Coproduct, Name <: Symbol]( - implicit - hName: Witness.Aux[Name], - hConfigReader: Derivation[Lazy[ConfigReader[H]]], - tConfigReaderOptions: Lazy[CoproductReaderOptions[T]]): CoproductReaderOptions[FieldType[Name, H] :+: T] = + implicit def cConsReaderOptions[H, T <: Coproduct, Name <: Symbol](implicit + hName: Witness.Aux[Name], + hConfigReader: Derivation[Lazy[ConfigReader[H]]], + tConfigReaderOptions: Lazy[CoproductReaderOptions[T]] + ): CoproductReaderOptions[FieldType[Name, H] :+: T] = new CoproductReaderOptions[FieldType[Name, H] :+: T] { lazy val options = { val optionName = hName.value.name diff --git a/modules/generic/src/main/scala/pureconfig/generic/DerivedConfigReader.scala b/modules/generic/src/main/scala/pureconfig/generic/DerivedConfigReader.scala index 7602a4940..a897cd6fc 100644 --- a/modules/generic/src/main/scala/pureconfig/generic/DerivedConfigReader.scala +++ b/modules/generic/src/main/scala/pureconfig/generic/DerivedConfigReader.scala @@ -6,103 +6,113 @@ import pureconfig.generic.error.InvalidCoproductOption import shapeless._ /** - * A `ConfigReader` derived with `shapeless`. - * - * @tparam A the type of objects readable by this `ConfigReader` - */ + * A `ConfigReader` derived with `shapeless`. + * + * @tparam A the type of objects readable by this `ConfigReader` + */ trait DerivedConfigReader[A] extends ConfigReader[A] object DerivedConfigReader extends DerivedConfigReader1 { - implicit def anyValReader[A, Wrapped]( - implicit - ev: A <:< AnyVal, - generic: Generic[A], - unwrapped: Unwrapped.Aux[A, Wrapped], - reader: ConfigReader[Wrapped]): DerivedConfigReader[A] = new DerivedConfigReader[A] { - - def from(value: ConfigCursor): ConfigReader.Result[A] = - reader.from(value).right.map(unwrapped.wrap) - } - - implicit def tupleReader[A: IsTuple, Repr <: HList, LabelledRepr <: HList, DefaultRepr <: HList]( - implicit - g: Generic.Aux[A, Repr], - gcr: SeqShapedReader[Repr], - lg: LabelledGeneric.Aux[A, LabelledRepr], - default: Default.AsOptions.Aux[A, DefaultRepr], - pr: MapShapedReader[A, LabelledRepr, DefaultRepr]): DerivedConfigReader[A] = new DerivedConfigReader[A] { - - def from(cur: ConfigCursor) = { - // Try to read first as the list representation and afterwards as the product representation (i.e. ConfigObject - // with '_1', '_2', etc. keys). - val cc = cur.asListCursor.right.map(Right.apply).left.flatMap(failure => - cur.asObjectCursor.right.map(Left.apply).left.map(_ => failure)) - - cc.right.flatMap { - case Right(listCur) => tupleAsListReader(listCur) - case Left(objCur) => tupleAsObjectReader(objCur) + implicit def anyValReader[A, Wrapped](implicit + ev: A <:< AnyVal, + generic: Generic[A], + unwrapped: Unwrapped.Aux[A, Wrapped], + reader: ConfigReader[Wrapped] + ): DerivedConfigReader[A] = + new DerivedConfigReader[A] { + + def from(value: ConfigCursor): ConfigReader.Result[A] = + reader.from(value).right.map(unwrapped.wrap) + } + + implicit def tupleReader[A: IsTuple, Repr <: HList, LabelledRepr <: HList, DefaultRepr <: HList](implicit + g: Generic.Aux[A, Repr], + gcr: SeqShapedReader[Repr], + lg: LabelledGeneric.Aux[A, LabelledRepr], + default: Default.AsOptions.Aux[A, DefaultRepr], + pr: MapShapedReader[A, LabelledRepr, DefaultRepr] + ): DerivedConfigReader[A] = + new DerivedConfigReader[A] { + + def from(cur: ConfigCursor) = { + // Try to read first as the list representation and afterwards as the product representation (i.e. ConfigObject + // with '_1', '_2', etc. keys). + val cc = cur.asListCursor.right + .map(Right.apply) + .left + .flatMap(failure => cur.asObjectCursor.right.map(Left.apply).left.map(_ => failure)) + + cc.right.flatMap { + case Right(listCur) => tupleAsListReader(listCur) + case Left(objCur) => tupleAsObjectReader(objCur) + } } } - } - private[pureconfig] def tupleAsListReader[A: IsTuple, Repr <: HList](cur: ConfigListCursor)( - implicit - gen: Generic.Aux[A, Repr], - cr: SeqShapedReader[Repr]): ConfigReader.Result[A] = + private[pureconfig] def tupleAsListReader[A: IsTuple, Repr <: HList]( + cur: ConfigListCursor + )(implicit gen: Generic.Aux[A, Repr], cr: SeqShapedReader[Repr]): ConfigReader.Result[A] = cr.from(cur).right.map(gen.from) - private[pureconfig] def tupleAsObjectReader[A: IsTuple, Repr <: HList, DefaultRepr <: HList](cur: ConfigObjectCursor)( - implicit - gen: LabelledGeneric.Aux[A, Repr], - default: Default.AsOptions.Aux[A, DefaultRepr], - cr: MapShapedReader[A, Repr, DefaultRepr]): ConfigReader.Result[A] = + private[pureconfig] def tupleAsObjectReader[A: IsTuple, Repr <: HList, DefaultRepr <: HList]( + cur: ConfigObjectCursor + )(implicit + gen: LabelledGeneric.Aux[A, Repr], + default: Default.AsOptions.Aux[A, DefaultRepr], + cr: MapShapedReader[A, Repr, DefaultRepr] + ): ConfigReader.Result[A] = cr.from(cur, default(), Set.empty).right.map(gen.from) } trait DerivedConfigReader1 { - final implicit def productReader[A, Repr <: HList, DefaultRepr <: HList]( - implicit - gen: LabelledGeneric.Aux[A, Repr], - default: Default.AsOptions.Aux[A, DefaultRepr], - cc: Lazy[MapShapedReader[A, Repr, DefaultRepr]]): DerivedConfigReader[A] = new DerivedConfigReader[A] { + final implicit def productReader[A, Repr <: HList, DefaultRepr <: HList](implicit + gen: LabelledGeneric.Aux[A, Repr], + default: Default.AsOptions.Aux[A, DefaultRepr], + cc: Lazy[MapShapedReader[A, Repr, DefaultRepr]] + ): DerivedConfigReader[A] = + new DerivedConfigReader[A] { - override def from(cur: ConfigCursor): ConfigReader.Result[A] = { - cur.asObjectCursor.right.flatMap(cc.value.from(_, default(), Set.empty)).right.map(gen.from) + override def from(cur: ConfigCursor): ConfigReader.Result[A] = { + cur.asObjectCursor.right.flatMap(cc.value.from(_, default(), Set.empty)).right.map(gen.from) + } } - } - - final implicit def coproductReader[A, Repr <: Coproduct]( - implicit - gen: LabelledGeneric.Aux[A, Repr], - hint: CoproductHint[A], - readerOptions: CoproductReaderOptions[Repr]): DerivedConfigReader[A] = new DerivedConfigReader[A] { - - override def from(cur: ConfigCursor): ConfigReader.Result[A] = { - def readerFor(option: String) = - readerOptions.options.get(option).map(_.map(gen.from)) - - hint.from(cur, readerOptions.options.keys.toList.sorted).right.flatMap { - case CoproductHint.Use(cursor, option) => - readerFor(option) match { - case Some(value) => value.from(cursor) - case None => ConfigReader.Result.fail[A](cursor.failureFor(InvalidCoproductOption(option))) - } - - case CoproductHint.Attempt(cursor, options, combineF) => - val initial: Either[Vector[(String, ConfigReaderFailures)], A] = Left(Vector.empty) - val res = options.foldLeft(initial) { (curr, option) => - curr.left.flatMap { currentFailures => - readerFor(option) match { - case Some(value) => value.from(cursor).left.map(f => currentFailures :+ (option -> f)) - case None => Left(currentFailures :+ - (option -> ConfigReaderFailures(cursor.failureFor(InvalidCoproductOption(option))))) + + final implicit def coproductReader[A, Repr <: Coproduct](implicit + gen: LabelledGeneric.Aux[A, Repr], + hint: CoproductHint[A], + readerOptions: CoproductReaderOptions[Repr] + ): DerivedConfigReader[A] = + new DerivedConfigReader[A] { + + override def from(cur: ConfigCursor): ConfigReader.Result[A] = { + def readerFor(option: String) = + readerOptions.options.get(option).map(_.map(gen.from)) + + hint.from(cur, readerOptions.options.keys.toList.sorted).right.flatMap { + case CoproductHint.Use(cursor, option) => + readerFor(option) match { + case Some(value) => value.from(cursor) + case None => ConfigReader.Result.fail[A](cursor.failureFor(InvalidCoproductOption(option))) + } + + case CoproductHint.Attempt(cursor, options, combineF) => + val initial: Either[Vector[(String, ConfigReaderFailures)], A] = Left(Vector.empty) + val res = options.foldLeft(initial) { (curr, option) => + curr.left.flatMap { currentFailures => + readerFor(option) match { + case Some(value) => value.from(cursor).left.map(f => currentFailures :+ (option -> f)) + case None => + Left( + currentFailures :+ + (option -> ConfigReaderFailures(cursor.failureFor(InvalidCoproductOption(option)))) + ) + } } } - } - res.left.map(combineF) + res.left.map(combineF) + } } } - } } diff --git a/modules/generic/src/main/scala/pureconfig/generic/DerivedConfigWriter.scala b/modules/generic/src/main/scala/pureconfig/generic/DerivedConfigWriter.scala index 52fa087e5..fdfdd2492 100644 --- a/modules/generic/src/main/scala/pureconfig/generic/DerivedConfigWriter.scala +++ b/modules/generic/src/main/scala/pureconfig/generic/DerivedConfigWriter.scala @@ -5,51 +5,54 @@ import pureconfig._ import shapeless._ /** - * A `ConfigWriter` derived with `shapeless`. - * - * @tparam A the type of objects writable by this `ConfigWriter` - */ + * A `ConfigWriter` derived with `shapeless`. + * + * @tparam A the type of objects writable by this `ConfigWriter` + */ trait DerivedConfigWriter[A] extends ConfigWriter[A] object DerivedConfigWriter extends DerivedConfigWriter1 { - implicit def anyValWriter[A, Wrapped]( - implicit - ev: A <:< AnyVal, - generic: Generic[A], - unwrapped: Unwrapped.Aux[A, Wrapped], - writer: ConfigWriter[Wrapped]): DerivedConfigWriter[A] = + implicit def anyValWriter[A, Wrapped](implicit + ev: A <:< AnyVal, + generic: Generic[A], + unwrapped: Unwrapped.Aux[A, Wrapped], + writer: ConfigWriter[Wrapped] + ): DerivedConfigWriter[A] = new DerivedConfigWriter[A] { override def to(t: A): ConfigValue = writer.to(unwrapped.unwrap(t)) } - implicit def tupleWriter[A: IsTuple, Repr]( - implicit - gen: Generic.Aux[A, Repr], - cc: SeqShapedWriter[Repr]): DerivedConfigWriter[A] = new DerivedConfigWriter[A] { - override def to(t: A): ConfigValue = - cc.to(gen.to(t)) - } + implicit def tupleWriter[A: IsTuple, Repr](implicit + gen: Generic.Aux[A, Repr], + cc: SeqShapedWriter[Repr] + ): DerivedConfigWriter[A] = + new DerivedConfigWriter[A] { + override def to(t: A): ConfigValue = + cc.to(gen.to(t)) + } } trait DerivedConfigWriter1 { - final implicit def productWriter[A, Repr <: HList]( - implicit - gen: LabelledGeneric.Aux[A, Repr], - cc: Lazy[MapShapedWriter[A, Repr]]): DerivedConfigWriter[A] = new DerivedConfigWriter[A] { + final implicit def productWriter[A, Repr <: HList](implicit + gen: LabelledGeneric.Aux[A, Repr], + cc: Lazy[MapShapedWriter[A, Repr]] + ): DerivedConfigWriter[A] = + new DerivedConfigWriter[A] { - override def to(t: A): ConfigValue = { - cc.value.to(gen.to(t)) + override def to(t: A): ConfigValue = { + cc.value.to(gen.to(t)) + } } - } - - final implicit def coproductWriter[F, Repr <: Coproduct]( - implicit - gen: LabelledGeneric.Aux[F, Repr], - cw: Lazy[CoproductConfigWriter[F, Repr]]): DerivedConfigWriter[F] = new DerivedConfigWriter[F] { - def to(t: F): ConfigValue = { - cw.value.to(gen.to(t)) + + final implicit def coproductWriter[F, Repr <: Coproduct](implicit + gen: LabelledGeneric.Aux[F, Repr], + cw: Lazy[CoproductConfigWriter[F, Repr]] + ): DerivedConfigWriter[F] = + new DerivedConfigWriter[F] { + def to(t: F): ConfigValue = { + cw.value.to(gen.to(t)) + } } - } } diff --git a/modules/generic/src/main/scala/pureconfig/generic/EnumCoproductHint.scala b/modules/generic/src/main/scala/pureconfig/generic/EnumCoproductHint.scala index 3cb376a43..01a7f6473 100644 --- a/modules/generic/src/main/scala/pureconfig/generic/EnumCoproductHint.scala +++ b/modules/generic/src/main/scala/pureconfig/generic/EnumCoproductHint.scala @@ -1,27 +1,30 @@ package pureconfig.generic -import com.typesafe.config.{ ConfigObject, ConfigValue, ConfigValueType } +import com.typesafe.config.{ConfigObject, ConfigValue, ConfigValueType} import pureconfig._ import pureconfig.error._ import pureconfig.generic.CoproductHint.Use -import pureconfig.generic.error.{ CoproductHintException, NoValidCoproductOptionFound } +import pureconfig.generic.error.{CoproductHintException, NoValidCoproductOptionFound} import pureconfig.syntax._ /** - * Hint applicable to sealed families of case objects where objects are written and read as strings with their type - * names. Trying to read or write values that are not case objects results in failure. - * - * @tparam A the type of the coproduct or sealed family for which this hint applies - */ -@deprecated("Use `pureconfig.generic.semiauto.deriveEnumerationReader[A]`, `pureconfig.generic.semiauto.deriveEnumerationWriter[A]` and `pureconfig.generic.semiauto.deriveEnumerationConvert[A]` instead", "0.11.0") + * Hint applicable to sealed families of case objects where objects are written and read as strings with their type + * names. Trying to read or write values that are not case objects results in failure. + * + * @tparam A the type of the coproduct or sealed family for which this hint applies + */ +@deprecated( + "Use `pureconfig.generic.semiauto.deriveEnumerationReader[A]`, `pureconfig.generic.semiauto.deriveEnumerationWriter[A]` and `pureconfig.generic.semiauto.deriveEnumerationConvert[A]` instead", + "0.11.0" +) class EnumCoproductHint[A] extends CoproductHint[A] { /** - * Returns the field value for a class or coproduct option name. - * - * @param name the name of the class or coproduct option - * @return the field value associated with the given class or coproduct option name. - */ + * Returns the field value for a class or coproduct option name. + * + * @param name the name of the class or coproduct option + * @return the field value associated with the given class or coproduct option name. + */ protected def fieldValue(name: String): String = name.toLowerCase def from(cursor: ConfigCursor, options: Seq[String]): ConfigReader.Result[CoproductHint.Action] = diff --git a/modules/generic/src/main/scala/pureconfig/generic/EnumerationConfigReaderBuilder.scala b/modules/generic/src/main/scala/pureconfig/generic/EnumerationConfigReaderBuilder.scala index 69e3efb77..090df7e67 100644 --- a/modules/generic/src/main/scala/pureconfig/generic/EnumerationConfigReaderBuilder.scala +++ b/modules/generic/src/main/scala/pureconfig/generic/EnumerationConfigReaderBuilder.scala @@ -2,16 +2,16 @@ package pureconfig.generic import pureconfig.ConfigReader.Result import pureconfig.generic.error.NoValidCoproductOptionFound -import pureconfig.{ ConfigCursor, ConfigReader } +import pureconfig.{ConfigCursor, ConfigReader} import shapeless._ import shapeless.labelled._ /** - * A type class to build `ConfigReader`s for sealed families of case objects where each type is encoded as a - * `ConfigString` based on the type name. - * - * @tparam A the type of objects capable of being read as an enumeration - */ + * A type class to build `ConfigReader`s for sealed families of case objects where each type is encoded as a + * `ConfigString` based on the type name. + * + * @tparam A the type of objects capable of being read as an enumeration + */ trait EnumerationConfigReaderBuilder[A] { def build(transformName: String => String): ConfigReader[A] } @@ -25,28 +25,29 @@ object EnumerationConfigReaderBuilder { } } - implicit def deriveEnumerationReaderBuilderCCons[K <: Symbol, H, T <: Coproduct]( - implicit - vName: Witness.Aux[K], - hGen: LabelledGeneric.Aux[H, HNil], - tReaderBuilder: EnumerationConfigReaderBuilder[T]): EnumerationConfigReaderBuilder[FieldType[K, H] :+: T] = + implicit def deriveEnumerationReaderBuilderCCons[K <: Symbol, H, T <: Coproduct](implicit + vName: Witness.Aux[K], + hGen: LabelledGeneric.Aux[H, HNil], + tReaderBuilder: EnumerationConfigReaderBuilder[T] + ): EnumerationConfigReaderBuilder[FieldType[K, H] :+: T] = new EnumerationConfigReaderBuilder[FieldType[K, H] :+: T] { def build(transformName: String => String): ConfigReader[FieldType[K, H] :+: T] = { lazy val tReader = tReaderBuilder.build(transformName) new ConfigReader[FieldType[K, H] :+: T] { - def from(cur: ConfigCursor): Result[FieldType[K, H] :+: T] = cur.asString match { - case Right(s) if s == transformName(vName.value.name) => Right(Inl(field[K](hGen.from(HNil)))) - case Right(_) => tReader.from(cur).right.map(Inr.apply) - case Left(err) => Left(err) - } + def from(cur: ConfigCursor): Result[FieldType[K, H] :+: T] = + cur.asString match { + case Right(s) if s == transformName(vName.value.name) => Right(Inl(field[K](hGen.from(HNil)))) + case Right(_) => tReader.from(cur).right.map(Inr.apply) + case Left(err) => Left(err) + } } } } - implicit def deriveEnumerationReaderBuilder[A, Repr <: Coproduct]( - implicit - gen: LabelledGeneric.Aux[A, Repr], - reprReaderBuilder: EnumerationConfigReaderBuilder[Repr]): EnumerationConfigReaderBuilder[A] = + implicit def deriveEnumerationReaderBuilder[A, Repr <: Coproduct](implicit + gen: LabelledGeneric.Aux[A, Repr], + reprReaderBuilder: EnumerationConfigReaderBuilder[Repr] + ): EnumerationConfigReaderBuilder[A] = new EnumerationConfigReaderBuilder[A] { def build(transformName: String => String): ConfigReader[A] = { reprReaderBuilder.build(transformName).map(gen.from) diff --git a/modules/generic/src/main/scala/pureconfig/generic/EnumerationConfigWriterBuilder.scala b/modules/generic/src/main/scala/pureconfig/generic/EnumerationConfigWriterBuilder.scala index bb2ea0fda..f77a1657f 100644 --- a/modules/generic/src/main/scala/pureconfig/generic/EnumerationConfigWriterBuilder.scala +++ b/modules/generic/src/main/scala/pureconfig/generic/EnumerationConfigWriterBuilder.scala @@ -1,16 +1,16 @@ package pureconfig.generic -import com.typesafe.config.{ ConfigValue, ConfigValueFactory } +import com.typesafe.config.{ConfigValue, ConfigValueFactory} import pureconfig.ConfigWriter import shapeless._ import shapeless.labelled._ /** - * A type class to build `ConfigWriter`s for sealed families of case objects where each type is encoded as a - * `ConfigString` based on the type name. - * - * @tparam A the type of objects capable of being written as an enumeration - */ + * A type class to build `ConfigWriter`s for sealed families of case objects where each type is encoded as a + * `ConfigString` based on the type name. + * + * @tparam A the type of objects capable of being written as an enumeration + */ trait EnumerationConfigWriterBuilder[A] { def build(transformName: String => String): ConfigWriter[A] } @@ -25,27 +25,28 @@ object EnumerationConfigWriterBuilder { } } - implicit def deriveEnumerationWriterBuilderCCons[K <: Symbol, H, T <: Coproduct]( - implicit - vName: Witness.Aux[K], - hGen: LabelledGeneric.Aux[H, HNil], - tWriterBuilder: EnumerationConfigWriterBuilder[T]): EnumerationConfigWriterBuilder[FieldType[K, H] :+: T] = + implicit def deriveEnumerationWriterBuilderCCons[K <: Symbol, H, T <: Coproduct](implicit + vName: Witness.Aux[K], + hGen: LabelledGeneric.Aux[H, HNil], + tWriterBuilder: EnumerationConfigWriterBuilder[T] + ): EnumerationConfigWriterBuilder[FieldType[K, H] :+: T] = new EnumerationConfigWriterBuilder[FieldType[K, H] :+: T] { def build(transformName: String => String): ConfigWriter[FieldType[K, H] :+: T] = { lazy val tWriter = tWriterBuilder.build(transformName) new ConfigWriter[FieldType[K, H] :+: T] { - def to(a: FieldType[K, H] :+: T): ConfigValue = a match { - case Inl(_) => ConfigValueFactory.fromAnyRef(transformName(vName.value.name)) - case Inr(r) => tWriter.to(r) - } + def to(a: FieldType[K, H] :+: T): ConfigValue = + a match { + case Inl(_) => ConfigValueFactory.fromAnyRef(transformName(vName.value.name)) + case Inr(r) => tWriter.to(r) + } } } } - implicit def deriveEnumerationWriterBuilder[A, Repr <: Coproduct]( - implicit - gen: LabelledGeneric.Aux[A, Repr], - reprWriterBuilder: EnumerationConfigWriterBuilder[Repr]): EnumerationConfigWriterBuilder[A] = + implicit def deriveEnumerationWriterBuilder[A, Repr <: Coproduct](implicit + gen: LabelledGeneric.Aux[A, Repr], + reprWriterBuilder: EnumerationConfigWriterBuilder[Repr] + ): EnumerationConfigWriterBuilder[A] = new EnumerationConfigWriterBuilder[A] { def build(transformName: String => String): ConfigWriter[A] = { reprWriterBuilder.build(transformName).contramap(gen.to) diff --git a/modules/generic/src/main/scala/pureconfig/generic/ExportMacros.scala b/modules/generic/src/main/scala/pureconfig/generic/ExportMacros.scala index aba33a5ee..7da43cd55 100644 --- a/modules/generic/src/main/scala/pureconfig/generic/ExportMacros.scala +++ b/modules/generic/src/main/scala/pureconfig/generic/ExportMacros.scala @@ -5,8 +5,8 @@ import scala.reflect.macros.blackbox import pureconfig._ /** - * Macros used to circumvent divergence checker restrictions in the compiler. - */ + * Macros used to circumvent divergence checker restrictions in the compiler. + */ class ExportMacros(val c: blackbox.Context) { import c.universe._ @@ -14,8 +14,7 @@ class ExportMacros(val c: blackbox.Context) { c.typecheck(q"_root_.shapeless.lazily[_root_.pureconfig.generic.DerivedConfigReader[$a]]", silent = true) match { case EmptyTree => c.abort(c.enclosingPosition, s"Unable to infer value of type $a") case t => - c.Expr[Exported[ConfigReader[A]]]( - q"new _root_.pureconfig.Exported($t: _root_.pureconfig.ConfigReader[$a])") + c.Expr[Exported[ConfigReader[A]]](q"new _root_.pureconfig.Exported($t: _root_.pureconfig.ConfigReader[$a])") } } @@ -23,8 +22,7 @@ class ExportMacros(val c: blackbox.Context) { c.typecheck(q"_root_.shapeless.lazily[_root_.pureconfig.generic.DerivedConfigWriter[$a]]", silent = true) match { case EmptyTree => c.abort(c.enclosingPosition, s"Unable to infer value of type $a") case t => - c.Expr[Exported[ConfigWriter[A]]]( - q"new _root_.pureconfig.Exported($t: _root_.pureconfig.ConfigWriter[$a])") + c.Expr[Exported[ConfigWriter[A]]](q"new _root_.pureconfig.Exported($t: _root_.pureconfig.ConfigWriter[$a])") } } } diff --git a/modules/generic/src/main/scala/pureconfig/generic/MapShapedReader.scala b/modules/generic/src/main/scala/pureconfig/generic/MapShapedReader.scala index 4569c178d..3a94fb6a1 100644 --- a/modules/generic/src/main/scala/pureconfig/generic/MapShapedReader.scala +++ b/modules/generic/src/main/scala/pureconfig/generic/MapShapedReader.scala @@ -4,38 +4,42 @@ import pureconfig._ import pureconfig.error.KeyNotFound import pureconfig.generic.ProductHint.UseOrDefault import shapeless._ -import shapeless.labelled.{ FieldType, field } +import shapeless.labelled.{FieldType, field} /** - * A specialized reader for generic representations that reads values in the shape of a config object, and is capable - * of handling default values. - * - * @tparam Original the original type for which `Repr` is a generic sub-representation - * @tparam Repr the generic representation - * @tparam DefaultRepr the default representation of the original type - */ + * A specialized reader for generic representations that reads values in the shape of a config object, and is capable + * of handling default values. + * + * @tparam Original the original type for which `Repr` is a generic sub-representation + * @tparam Repr the generic representation + * @tparam DefaultRepr the default representation of the original type + */ private[generic] trait MapShapedReader[Original, Repr, DefaultRepr] { def from(cur: ConfigObjectCursor, default: DefaultRepr, usedFields: Set[String]): ConfigReader.Result[Repr] } object MapShapedReader { - implicit def labelledHNilReader[Original]( - implicit - hint: ProductHint[Original]): MapShapedReader[Original, HNil, HNil] = + implicit def labelledHNilReader[Original](implicit + hint: ProductHint[Original] + ): MapShapedReader[Original, HNil, HNil] = new MapShapedReader[Original, HNil, HNil] { def from(cur: ConfigObjectCursor, default: HNil, usedFields: Set[String]): ConfigReader.Result[HNil] = hint.bottom(cur, usedFields).fold[ConfigReader.Result[HNil]](Right(HNil))(Left.apply) } - final implicit def labelledHConsReader[Original, K <: Symbol, H, T <: HList, D <: HList]( - implicit - key: Witness.Aux[K], - hConfigReader: Derivation[Lazy[ConfigReader[H]]], - tConfigReader: Lazy[MapShapedReader[Original, T, D]], - hint: ProductHint[Original]): MapShapedReader[Original, FieldType[K, H] :: T, Option[H] :: D] = + final implicit def labelledHConsReader[Original, K <: Symbol, H, T <: HList, D <: HList](implicit + key: Witness.Aux[K], + hConfigReader: Derivation[Lazy[ConfigReader[H]]], + tConfigReader: Lazy[MapShapedReader[Original, T, D]], + hint: ProductHint[Original] + ): MapShapedReader[Original, FieldType[K, H] :: T, Option[H] :: D] = new MapShapedReader[Original, FieldType[K, H] :: T, Option[H] :: D] { - def from(cur: ConfigObjectCursor, default: Option[H] :: D, usedFields: Set[String]): ConfigReader.Result[FieldType[K, H] :: T] = { + def from( + cur: ConfigObjectCursor, + default: Option[H] :: D, + usedFields: Set[String] + ): ConfigReader.Result[FieldType[K, H] :: T] = { val fieldName = key.value.name val fieldAction = hint.from(cur, fieldName) lazy val reader = hConfigReader.value.value diff --git a/modules/generic/src/main/scala/pureconfig/generic/MapShapedWriter.scala b/modules/generic/src/main/scala/pureconfig/generic/MapShapedWriter.scala index eda56ddb6..016622797 100644 --- a/modules/generic/src/main/scala/pureconfig/generic/MapShapedWriter.scala +++ b/modules/generic/src/main/scala/pureconfig/generic/MapShapedWriter.scala @@ -2,31 +2,32 @@ package pureconfig.generic import scala.collection.JavaConverters._ -import com.typesafe.config.{ ConfigFactory, ConfigObject, ConfigValue } +import com.typesafe.config.{ConfigFactory, ConfigObject, ConfigValue} import pureconfig._ import shapeless._ import shapeless.labelled.FieldType /** - * A `ConfigWriter` for generic representations that writes values in the shape of a config object. - * - * @tparam Original the original type for which `Repr` is a generic sub-representation - * @tparam Repr the generic representation - */ + * A `ConfigWriter` for generic representations that writes values in the shape of a config object. + * + * @tparam Original the original type for which `Repr` is a generic sub-representation + * @tparam Repr the generic representation + */ private[generic] trait MapShapedWriter[Original, Repr] extends ConfigWriter[Repr] object MapShapedWriter { - implicit def labelledHNilWriter[Original]: MapShapedWriter[Original, HNil] = new MapShapedWriter[Original, HNil] { - override def to(t: HNil): ConfigValue = ConfigFactory.parseMap(Map().asJava).root() - } + implicit def labelledHNilWriter[Original]: MapShapedWriter[Original, HNil] = + new MapShapedWriter[Original, HNil] { + override def to(t: HNil): ConfigValue = ConfigFactory.parseMap(Map().asJava).root() + } - final implicit def labelledHConsWriter[Original, K <: Symbol, H, T <: HList]( - implicit - key: Witness.Aux[K], - hConfigWriter: Derivation[Lazy[ConfigWriter[H]]], - tConfigWriter: Lazy[MapShapedWriter[Original, T]], - hint: ProductHint[Original]): MapShapedWriter[Original, FieldType[K, H] :: T] = + final implicit def labelledHConsWriter[Original, K <: Symbol, H, T <: HList](implicit + key: Witness.Aux[K], + hConfigWriter: Derivation[Lazy[ConfigWriter[H]]], + tConfigWriter: Lazy[MapShapedWriter[Original, T]], + hint: ProductHint[Original] + ): MapShapedWriter[Original, FieldType[K, H] :: T] = new MapShapedWriter[Original, FieldType[K, H] :: T] { override def to(t: FieldType[K, H] :: T): ConfigValue = { val rem = tConfigWriter.value.to(t.tail) diff --git a/modules/generic/src/main/scala/pureconfig/generic/SeqShapedReader.scala b/modules/generic/src/main/scala/pureconfig/generic/SeqShapedReader.scala index ec042d10a..f62dcc097 100644 --- a/modules/generic/src/main/scala/pureconfig/generic/SeqShapedReader.scala +++ b/modules/generic/src/main/scala/pureconfig/generic/SeqShapedReader.scala @@ -6,10 +6,10 @@ import shapeless._ import shapeless.ops.hlist.HKernelAux /** - * A `ConfigReader` for generic representations that reads values in the shape of a sequence. - * - * @tparam Repr the generic representation - */ + * A `ConfigReader` for generic representations that reads values in the shape of a sequence. + * + * @tparam Repr the generic representation + */ private[generic] trait SeqShapedReader[Repr] extends ConfigReader[Repr] object SeqShapedReader { @@ -23,7 +23,11 @@ object SeqShapedReader { } } - implicit def hConsReader[H, T <: HList](implicit hr: Derivation[Lazy[ConfigReader[H]]], tr: Lazy[SeqShapedReader[T]], tl: HKernelAux[T]): SeqShapedReader[H :: T] = + implicit def hConsReader[H, T <: HList](implicit + hr: Derivation[Lazy[ConfigReader[H]]], + tr: Lazy[SeqShapedReader[T]], + tl: HKernelAux[T] + ): SeqShapedReader[H :: T] = new SeqShapedReader[H :: T] { def from(cur: ConfigCursor): ConfigReader.Result[H :: T] = { cur.asListCursor.right.flatMap { diff --git a/modules/generic/src/main/scala/pureconfig/generic/SeqShapedWriter.scala b/modules/generic/src/main/scala/pureconfig/generic/SeqShapedWriter.scala index 2015a89b7..f8a31c094 100644 --- a/modules/generic/src/main/scala/pureconfig/generic/SeqShapedWriter.scala +++ b/modules/generic/src/main/scala/pureconfig/generic/SeqShapedWriter.scala @@ -2,15 +2,15 @@ package pureconfig.generic import scala.collection.JavaConverters._ -import com.typesafe.config.{ ConfigList, ConfigValue, ConfigValueFactory } -import pureconfig.{ ConfigWriter, Derivation } +import com.typesafe.config.{ConfigList, ConfigValue, ConfigValueFactory} +import pureconfig.{ConfigWriter, Derivation} import shapeless._ /** - * A `ConfigWriter` for generic representations that writes values in the shape of a sequence. - * - * @tparam Repr the generic representation - */ + * A `ConfigWriter` for generic representations that writes values in the shape of a sequence. + * + * @tparam Repr the generic representation + */ private[generic] trait SeqShapedWriter[Repr] extends ConfigWriter[Repr] object SeqShapedWriter { @@ -19,7 +19,10 @@ object SeqShapedWriter { override def to(v: HNil): ConfigValue = ConfigValueFactory.fromIterable(List().asJava) } - implicit def hConsWriter[H, T <: HList](implicit hw: Derivation[Lazy[ConfigWriter[H]]], tw: Lazy[SeqShapedWriter[T]]): SeqShapedWriter[H :: T] = + implicit def hConsWriter[H, T <: HList](implicit + hw: Derivation[Lazy[ConfigWriter[H]]], + tw: Lazy[SeqShapedWriter[T]] + ): SeqShapedWriter[H :: T] = new SeqShapedWriter[H :: T] { override def to(v: H :: T): ConfigValue = { tw.value.to(v.tail) match { diff --git a/modules/generic/src/main/scala/pureconfig/generic/auto.scala b/modules/generic/src/main/scala/pureconfig/generic/auto.scala index 2095c82d5..e0170a47e 100644 --- a/modules/generic/src/main/scala/pureconfig/generic/auto.scala +++ b/modules/generic/src/main/scala/pureconfig/generic/auto.scala @@ -5,9 +5,9 @@ import scala.language.experimental.macros import pureconfig._ /** - * An object that, when imported, provides implicit `ConfigReader` and `ConfigWriter` instances for value classes, - * tuples, case classes and sealed traits. - */ + * An object that, when imported, provides implicit `ConfigReader` and `ConfigWriter` instances for value classes, + * tuples, case classes and sealed traits. + */ object auto { implicit def exportReader[A]: Exported[ConfigReader[A]] = macro ExportMacros.exportDerivedReader[A] implicit def exportWriter[A]: Exported[ConfigWriter[A]] = macro ExportMacros.exportDerivedWriter[A] diff --git a/modules/generic/src/main/scala/pureconfig/generic/hlist.scala b/modules/generic/src/main/scala/pureconfig/generic/hlist.scala index 5663fd72e..70c6939cb 100644 --- a/modules/generic/src/main/scala/pureconfig/generic/hlist.scala +++ b/modules/generic/src/main/scala/pureconfig/generic/hlist.scala @@ -1,10 +1,10 @@ package pureconfig.generic -import pureconfig.{ ConfigReader, ConfigWriter } +import pureconfig.{ConfigReader, ConfigWriter} /** - * An object that, when imported, provides readers and writers for shapeless `HList` instances. - */ + * An object that, when imported, provides readers and writers for shapeless `HList` instances. + */ object hlist { implicit def hListReader[HL](implicit seqReader: SeqShapedReader[HL]): ConfigReader[HL] = seqReader implicit def hListWriter[HL](implicit seqWriter: SeqShapedWriter[HL]): ConfigWriter[HL] = seqWriter diff --git a/modules/generic/src/main/scala/pureconfig/generic/semiauto.scala b/modules/generic/src/main/scala/pureconfig/generic/semiauto.scala index 94ab95280..a0a171cf7 100644 --- a/modules/generic/src/main/scala/pureconfig/generic/semiauto.scala +++ b/modules/generic/src/main/scala/pureconfig/generic/semiauto.scala @@ -4,69 +4,73 @@ import pureconfig._ import shapeless._ /** - * An object that provides methods for deriving `ConfigReader` and `ConfigWriter` instances on demand for value - * classes, tuples, case classes and sealed traits. - */ + * An object that provides methods for deriving `ConfigReader` and `ConfigWriter` instances on demand for value + * classes, tuples, case classes and sealed traits. + */ object semiauto { final def deriveReader[A](implicit reader: Lazy[DerivedConfigReader[A]]): ConfigReader[A] = reader.value final def deriveWriter[A](implicit writer: Lazy[DerivedConfigWriter[A]]): ConfigWriter[A] = writer.value - final def deriveConvert[A](implicit reader: Lazy[DerivedConfigReader[A]], writer: Lazy[DerivedConfigWriter[A]]): ConfigConvert[A] = + final def deriveConvert[A](implicit + reader: Lazy[DerivedConfigReader[A]], + writer: Lazy[DerivedConfigWriter[A]] + ): ConfigConvert[A] = ConfigConvert.fromReaderAndWriter(Derivation.Successful(reader.value), Derivation.Successful(writer.value)) /** - * Derive a `ConfigReader` for a sealed family of case objects where each type is encoded as the kebab-case - * representation of the type name. - */ - final def deriveEnumerationReader[A]( - implicit - readerBuilder: Lazy[EnumerationConfigReaderBuilder[A]]): ConfigReader[A] = + * Derive a `ConfigReader` for a sealed family of case objects where each type is encoded as the kebab-case + * representation of the type name. + */ + final def deriveEnumerationReader[A](implicit + readerBuilder: Lazy[EnumerationConfigReaderBuilder[A]] + ): ConfigReader[A] = deriveEnumerationReader(ConfigFieldMapping(PascalCase, KebabCase)) /** - * Derive a `ConfigWriter` for a sealed family of case objects where each type is encoded as the kebab-case - * representation of the type name. - */ - final def deriveEnumerationWriter[A]( - implicit - writerBuilder: Lazy[EnumerationConfigWriterBuilder[A]]): ConfigWriter[A] = + * Derive a `ConfigWriter` for a sealed family of case objects where each type is encoded as the kebab-case + * representation of the type name. + */ + final def deriveEnumerationWriter[A](implicit + writerBuilder: Lazy[EnumerationConfigWriterBuilder[A]] + ): ConfigWriter[A] = deriveEnumerationWriter(ConfigFieldMapping(PascalCase, KebabCase)) /** - * Derive a `ConfigConvert` for a sealed family of case objects where each type is encoded as the kebab-case - * representation of the type name. - */ - final def deriveEnumerationConvert[A]( - implicit - readerBuilder: Lazy[EnumerationConfigReaderBuilder[A]], - writerBuilder: Lazy[EnumerationConfigWriterBuilder[A]]): ConfigConvert[A] = + * Derive a `ConfigConvert` for a sealed family of case objects where each type is encoded as the kebab-case + * representation of the type name. + */ + final def deriveEnumerationConvert[A](implicit + readerBuilder: Lazy[EnumerationConfigReaderBuilder[A]], + writerBuilder: Lazy[EnumerationConfigWriterBuilder[A]] + ): ConfigConvert[A] = deriveEnumerationConvert(ConfigFieldMapping(PascalCase, KebabCase)) /** - * Derive a `ConfigReader` for a sealed family of case objects where each type is encoded with the `transformName` - * function applied to the type name. - */ - final def deriveEnumerationReader[A](transformName: String => String)( - implicit - readerBuilder: Lazy[EnumerationConfigReaderBuilder[A]]): ConfigReader[A] = readerBuilder.value.build(transformName) + * Derive a `ConfigReader` for a sealed family of case objects where each type is encoded with the `transformName` + * function applied to the type name. + */ + final def deriveEnumerationReader[A](transformName: String => String)(implicit + readerBuilder: Lazy[EnumerationConfigReaderBuilder[A]] + ): ConfigReader[A] = readerBuilder.value.build(transformName) /** - * Derive a `ConfigWriter` for a sealed family of case objects where each type is encoded with the `transformName` - * function applied to the type name. - */ - final def deriveEnumerationWriter[A](transformName: String => String)( - implicit - writerBuilder: Lazy[EnumerationConfigWriterBuilder[A]]): ConfigWriter[A] = writerBuilder.value.build(transformName) + * Derive a `ConfigWriter` for a sealed family of case objects where each type is encoded with the `transformName` + * function applied to the type name. + */ + final def deriveEnumerationWriter[A](transformName: String => String)(implicit + writerBuilder: Lazy[EnumerationConfigWriterBuilder[A]] + ): ConfigWriter[A] = writerBuilder.value.build(transformName) /** - * Derive a `ConfigConvert` for a sealed family of case objects where each type is encoded with the `transformName` - * function applied to the type name. - */ - final def deriveEnumerationConvert[A](transformName: String => String)( - implicit - readerBuilder: Lazy[EnumerationConfigReaderBuilder[A]], - writerBuilder: Lazy[EnumerationConfigWriterBuilder[A]]): ConfigConvert[A] = + * Derive a `ConfigConvert` for a sealed family of case objects where each type is encoded with the `transformName` + * function applied to the type name. + */ + final def deriveEnumerationConvert[A](transformName: String => String)(implicit + readerBuilder: Lazy[EnumerationConfigReaderBuilder[A]], + writerBuilder: Lazy[EnumerationConfigWriterBuilder[A]] + ): ConfigConvert[A] = ConfigConvert.fromReaderAndWriter( Derivation.Successful(readerBuilder.value.build(transformName)), - Derivation.Successful(writerBuilder.value.build(transformName))) + Derivation.Successful(writerBuilder.value.build(transformName)) + ) } diff --git a/modules/hadoop/src/main/scala/pureconfig/module/hadoop/package.scala b/modules/hadoop/src/main/scala/pureconfig/module/hadoop/package.scala index b54bdf56a..c972c2681 100644 --- a/modules/hadoop/src/main/scala/pureconfig/module/hadoop/package.scala +++ b/modules/hadoop/src/main/scala/pureconfig/module/hadoop/package.scala @@ -6,11 +6,10 @@ import pureconfig.ConfigConvert import scala.util.Try /** - * `ConfigConvert` instances for Hadoop data structures. - */ + * `ConfigConvert` instances for Hadoop data structures. + */ package object hadoop { - implicit val pathConvert: ConfigConvert[Path] = ConfigConvert.viaNonEmptyStringTry[Path]( - s => Try(new Path(s)), - _.toString) + implicit val pathConvert: ConfigConvert[Path] = + ConfigConvert.viaNonEmptyStringTry[Path](s => Try(new Path(s)), _.toString) } diff --git a/modules/hadoop/src/test/scala/pureconfig/module/hadoop/HadoopModuleTest.scala b/modules/hadoop/src/test/scala/pureconfig/module/hadoop/HadoopModuleTest.scala index 74fee42f8..85414c5e7 100644 --- a/modules/hadoop/src/test/scala/pureconfig/module/hadoop/HadoopModuleTest.scala +++ b/modules/hadoop/src/test/scala/pureconfig/module/hadoop/HadoopModuleTest.scala @@ -1,9 +1,9 @@ package pureconfig.module.hadoop -import com.typesafe.config.{ ConfigFactory, ConfigRenderOptions } +import com.typesafe.config.{ConfigFactory, ConfigRenderOptions} import org.apache.hadoop.fs.Path -import pureconfig.{ BaseSuite, ConfigWriter } -import pureconfig.error.{ CannotConvert, EmptyStringFound } +import pureconfig.{BaseSuite, ConfigWriter} +import pureconfig.error.{CannotConvert, EmptyStringFound} import pureconfig.generic.auto._ import pureconfig.syntax._ diff --git a/modules/http4s/src/main/scala/pureconfig/module/http4s/package.scala b/modules/http4s/src/main/scala/pureconfig/module/http4s/package.scala index bfad7e957..feec72d1e 100644 --- a/modules/http4s/src/main/scala/pureconfig/module/http4s/package.scala +++ b/modules/http4s/src/main/scala/pureconfig/module/http4s/package.scala @@ -2,15 +2,14 @@ package pureconfig.module import org.http4s.Uri import pureconfig.error.CannotConvert -import pureconfig.{ ConfigReader, ConfigWriter } +import pureconfig.{ConfigReader, ConfigWriter} package object http4s { implicit val uriReader: ConfigReader[Uri] = ConfigReader.fromString(str => - Uri.fromString(str).fold( - err => Left(CannotConvert(str, "Uri", err.sanitized)), - uri => Right(uri))) + Uri.fromString(str).fold(err => Left(CannotConvert(str, "Uri", err.sanitized)), uri => Right(uri)) + ) implicit val uriWriter: ConfigWriter[Uri] = ConfigWriter[String].contramap(_.renderString) } diff --git a/modules/http4s/src/test/scala/pureconfig/module/http4s/Http4sTest.scala b/modules/http4s/src/test/scala/pureconfig/module/http4s/Http4sTest.scala index f86ffc508..ebbf80ac0 100644 --- a/modules/http4s/src/test/scala/pureconfig/module/http4s/Http4sTest.scala +++ b/modules/http4s/src/test/scala/pureconfig/module/http4s/Http4sTest.scala @@ -2,8 +2,8 @@ package pureconfig.module.http4s import com.typesafe.config.ConfigFactory import org.http4s.Uri -import pureconfig.{ BaseSuite, ConfigReader, ConfigWriter } -import pureconfig.error.{ CannotConvert, ConfigReaderFailures, ConvertFailure } +import pureconfig.{BaseSuite, ConfigReader, ConfigWriter} +import pureconfig.error.{CannotConvert, ConfigReaderFailures, ConvertFailure} import pureconfig.generic.auto._ import pureconfig.syntax._ @@ -20,14 +20,17 @@ class Http4sTest extends BaseSuite { "reading the uri config" should "get a CannotConvert error" in { val conf = ConfigFactory.parseString(s"""{uri:"\\\\"}""") - val errors = ConfigReaderFailures(ConvertFailure(CannotConvert("\\", "Uri", "Invalid URI"), stringConfigOrigin(1), "uri")) + val errors = + ConfigReaderFailures(ConvertFailure(CannotConvert("\\", "Uri", "Invalid URI"), stringConfigOrigin(1), "uri")) conf.to[ServerConfig].left.value shouldEqual errors } "Uri ConfigReader and ConfigWriter" should "be able to round-trip reading/writing of Uri" in { val expectedComplexUri = - Uri.unsafeFromString("http://www.google.ps/search?hl=en&client=firefox-a&hs=42F&rls=org.mozilla%3Aen-US%3Aofficial&q=The+type+%27Microsoft.Practices.ObjectBuilder.Locator%27+is+defined+in+an+assembly+that+is+not+referenced.+You+must+add+a+reference+to+assembly+&aq=f&aqi=&aql=&oq=") + Uri.unsafeFromString( + "http://www.google.ps/search?hl=en&client=firefox-a&hs=42F&rls=org.mozilla%3Aen-US%3Aofficial&q=The+type+%27Microsoft.Practices.ObjectBuilder.Locator%27+is+defined+in+an+assembly+that+is+not+referenced.+You+must+add+a+reference+to+assembly+&aq=f&aqi=&aql=&oq=" + ) val configValue = ConfigWriter[Uri].to(expectedComplexUri) val Right(actualUri) = ConfigReader[Uri].from(configValue) diff --git a/modules/javax/src/main/scala/pureconfig/module/javax/package.scala b/modules/javax/src/main/scala/pureconfig/module/javax/package.scala index 956565f56..427de054c 100644 --- a/modules/javax/src/main/scala/pureconfig/module/javax/package.scala +++ b/modules/javax/src/main/scala/pureconfig/module/javax/package.scala @@ -3,11 +3,11 @@ package pureconfig.module import _root_.javax.security.auth.kerberos.KerberosPrincipal import _root_.javax.security.auth.x500.X500Principal import pureconfig.ConfigConvert -import pureconfig.ConfigConvert.{ catchReadError, viaString } +import pureconfig.ConfigConvert.{catchReadError, viaString} /** - * ConfigConvert instances for javax value classes. - */ + * ConfigConvert instances for javax value classes. + */ package object javax { implicit val readKerberosPrincipal: ConfigConvert[KerberosPrincipal] = viaString[KerberosPrincipal](catchReadError(s => new KerberosPrincipal(s)), _.toString) diff --git a/modules/joda/src/main/scala/pureconfig/module/joda/configurable/package.scala b/modules/joda/src/main/scala/pureconfig/module/joda/configurable/package.scala index 733cb7ae1..d2827efe8 100644 --- a/modules/joda/src/main/scala/pureconfig/module/joda/configurable/package.scala +++ b/modules/joda/src/main/scala/pureconfig/module/joda/configurable/package.scala @@ -6,45 +6,38 @@ import pureconfig.ConfigConvert import pureconfig.ConfigConvert._ /** - * Provides methods that create [[ConfigConvert]] instances from a set of parameters used to configure the instances. - * - * The result of calling one of the methods can be assigned to an `implicit val` so that `pureconfig` will be able to - * use it: - * {{{ - * implicit val localDateConfigConvert = makeLocalDateConfigConvert(ISODateTimeFormat) - * }}} - * - * @example we cannot provide a [[ConfigConvert]] for [[org.joda.time.LocalDate]] because traditionally there are many different - * [[org.joda.time.format.DateTimeFormatter]]s to parse a [[org.joda.time.LocalDate]] from a [[java.lang.String]]. This package - * provides a method that takes an input [[org.joda.time.format.DateTimeFormatter]] and returns a [[ConfigConvert]] for - * [[org.joda.time.LocalDate]] which will use that [[org.joda.time.format.DateTimeFormatter]] to parse a [[org.joda.time.LocalDate]]. - */ + * Provides methods that create [[ConfigConvert]] instances from a set of parameters used to configure the instances. + * + * The result of calling one of the methods can be assigned to an `implicit val` so that `pureconfig` will be able to + * use it: + * {{{ + * implicit val localDateConfigConvert = makeLocalDateConfigConvert(ISODateTimeFormat) + * }}} + * + * @example we cannot provide a [[ConfigConvert]] for [[org.joda.time.LocalDate]] because traditionally there are many different + * [[org.joda.time.format.DateTimeFormatter]]s to parse a [[org.joda.time.LocalDate]] from a [[java.lang.String]]. This package + * provides a method that takes an input [[org.joda.time.format.DateTimeFormatter]] and returns a [[ConfigConvert]] for + * [[org.joda.time.LocalDate]] which will use that [[org.joda.time.format.DateTimeFormatter]] to parse a [[org.joda.time.LocalDate]]. + */ package object configurable { def dateTimeConfigConvert(formatter: DateTimeFormatter): ConfigConvert[DateTime] = - viaNonEmptyString[DateTime]( - catchReadError(DateTime.parse(_, formatter)), formatter.print) + viaNonEmptyString[DateTime](catchReadError(DateTime.parse(_, formatter)), formatter.print) def localDateConfigConvert(formatter: DateTimeFormatter): ConfigConvert[LocalDate] = - viaNonEmptyString[LocalDate]( - catchReadError(LocalDate.parse(_, formatter)), formatter.print) + viaNonEmptyString[LocalDate](catchReadError(LocalDate.parse(_, formatter)), formatter.print) def localTimeConfigConvert(formatter: DateTimeFormatter): ConfigConvert[LocalTime] = - viaNonEmptyString[LocalTime]( - catchReadError(LocalTime.parse(_, formatter)), formatter.print) + viaNonEmptyString[LocalTime](catchReadError(LocalTime.parse(_, formatter)), formatter.print) def localDateTimeConfigConvert(formatter: DateTimeFormatter): ConfigConvert[LocalDateTime] = - viaNonEmptyString[LocalDateTime]( - catchReadError(LocalDateTime.parse(_, formatter)), formatter.print) + viaNonEmptyString[LocalDateTime](catchReadError(LocalDateTime.parse(_, formatter)), formatter.print) def monthDayConfigConvert(formatter: DateTimeFormatter): ConfigConvert[MonthDay] = - viaNonEmptyString[MonthDay]( - catchReadError(MonthDay.parse(_, formatter)), formatter.print) + viaNonEmptyString[MonthDay](catchReadError(MonthDay.parse(_, formatter)), formatter.print) def yearMonthConfigConvert(formatter: DateTimeFormatter): ConfigConvert[YearMonth] = - viaNonEmptyString[YearMonth]( - catchReadError(YearMonth.parse(_, formatter)), formatter.print) + viaNonEmptyString[YearMonth](catchReadError(YearMonth.parse(_, formatter)), formatter.print) def periodConfigConvert(formatter: PeriodFormatter): ConfigConvert[Period] = - viaNonEmptyString[Period]( - catchReadError(Period.parse(_, formatter)), formatter.print) + viaNonEmptyString[Period](catchReadError(Period.parse(_, formatter)), formatter.print) } diff --git a/modules/joda/src/main/scala/pureconfig/module/joda/package.scala b/modules/joda/src/main/scala/pureconfig/module/joda/package.scala index 24f40c134..5597ba88d 100644 --- a/modules/joda/src/main/scala/pureconfig/module/joda/package.scala +++ b/modules/joda/src/main/scala/pureconfig/module/joda/package.scala @@ -1,27 +1,23 @@ package pureconfig.module -import org.joda.time.{ DateTimeZone, Duration, Instant, Interval } -import org.joda.time.format.{ DateTimeFormat, DateTimeFormatter } -import pureconfig.{ ConfigConvert, ConfigReader } -import pureconfig.ConfigConvert.{ catchReadError, viaNonEmptyString } +import org.joda.time.{DateTimeZone, Duration, Instant, Interval} +import org.joda.time.format.{DateTimeFormat, DateTimeFormatter} +import pureconfig.{ConfigConvert, ConfigReader} +import pureconfig.ConfigConvert.{catchReadError, viaNonEmptyString} package object joda { implicit def instantConfigConvert: ConfigConvert[Instant] = ConfigConvert[Long].xmap(new Instant(_), _.getMillis) implicit def intervalConfigConvert: ConfigConvert[Interval] = - viaNonEmptyString[Interval]( - catchReadError(Interval.parseWithOffset), _.toString) + viaNonEmptyString[Interval](catchReadError(Interval.parseWithOffset), _.toString) implicit def durationConfigConvert: ConfigConvert[Duration] = - viaNonEmptyString[Duration]( - catchReadError(Duration.parse), _.toString) + viaNonEmptyString[Duration](catchReadError(Duration.parse), _.toString) implicit def dateTimeFormatterConfigConvert: ConfigReader[DateTimeFormatter] = - ConfigReader.fromNonEmptyString[DateTimeFormatter]( - catchReadError(DateTimeFormat.forPattern)) + ConfigReader.fromNonEmptyString[DateTimeFormatter](catchReadError(DateTimeFormat.forPattern)) implicit def dateTimeZoneConfigConvert: ConfigConvert[DateTimeZone] = - viaNonEmptyString[DateTimeZone]( - catchReadError(DateTimeZone.forID), _.getID) + viaNonEmptyString[DateTimeZone](catchReadError(DateTimeZone.forID), _.getID) } diff --git a/modules/joda/src/test/scala/pureconfig/module/joda/JodaSuite.scala b/modules/joda/src/test/scala/pureconfig/module/joda/JodaSuite.scala index b66a7d08f..ee1eeb154 100644 --- a/modules/joda/src/test/scala/pureconfig/module/joda/JodaSuite.scala +++ b/modules/joda/src/test/scala/pureconfig/module/joda/JodaSuite.scala @@ -1,7 +1,7 @@ package pureconfig.module.joda import org.joda.time._ -import org.joda.time.format.{ DateTimeFormat, DateTimeFormatter } +import org.joda.time.format.{DateTimeFormat, DateTimeFormatter} import pureconfig.BaseSuite import pureconfig.module.joda.arbitrary._ @@ -16,5 +16,6 @@ class JodaSuite extends BaseSuite { checkArbitrary[DateTimeZone] checkReadString[DateTimeFormatter]( - "yyyy-MM-dd'T'HH:mm:ss.SSSZZZ" -> DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZZZ")) + "yyyy-MM-dd'T'HH:mm:ss.SSSZZZ" -> DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZZZ") + ) } diff --git a/modules/joda/src/test/scala/pureconfig/module/joda/arbitrary/package.scala b/modules/joda/src/test/scala/pureconfig/module/joda/arbitrary/package.scala index 5dd4c8aac..25867f8ec 100644 --- a/modules/joda/src/test/scala/pureconfig/module/joda/arbitrary/package.scala +++ b/modules/joda/src/test/scala/pureconfig/module/joda/arbitrary/package.scala @@ -3,7 +3,7 @@ package pureconfig.module.joda import scala.collection.JavaConverters._ import org.joda.time._ -import org.scalacheck.{ Arbitrary, Gen } +import org.scalacheck.{Arbitrary, Gen} import pureconfig.gen._ package object arbitrary { @@ -13,53 +13,56 @@ package object arbitrary { Arbitrary(Arbitrary.arbitrary[Long].map(new Instant(_))) implicit val intervalArbitrary: Arbitrary[Interval] = - Arbitrary(Arbitrary.arbitrary[(DateTime, DateTime)] - .map { i => - val (from, to) = if (i._1.isBefore(i._2)) i else i.swap - // Comparing new Interval(foo) with Interval.parseWithOffset(foo) will return false - // See http://www.joda.org/joda-time/apidocs/org/joda/time/Interval.html#parseWithOffset-java.lang.String- - Interval.parseWithOffset(new Interval(from, to).toString) - }) + Arbitrary( + Arbitrary + .arbitrary[(DateTime, DateTime)] + .map { i => + val (from, to) = if (i._1.isBefore(i._2)) i else i.swap + // Comparing new Interval(foo) with Interval.parseWithOffset(foo) will return false + // See http://www.joda.org/joda-time/apidocs/org/joda/time/Interval.html#parseWithOffset-java.lang.String- + Interval.parseWithOffset(new Interval(from, to).toString) + } + ) implicit val durationArbitrary: Arbitrary[Duration] = Arbitrary(Arbitrary.arbitrary[Long].map(new Duration(_))) - implicit val localTimeArbitrary: Arbitrary[LocalTime] = Arbitrary( - genLocalTime.map { t => - new LocalTime(t.getHour, t.getMinute, t.getSecond, nanoToMilli(t.getNano)) - }) + implicit val localTimeArbitrary: Arbitrary[LocalTime] = Arbitrary(genLocalTime.map { t => + new LocalTime(t.getHour, t.getMinute, t.getSecond, nanoToMilli(t.getNano)) + }) - implicit val localDateArbitrary: Arbitrary[LocalDate] = Arbitrary( - genLocalDate.map { d => - new LocalDate(d.getYear, d.getMonthValue, d.getDayOfMonth) - }) + implicit val localDateArbitrary: Arbitrary[LocalDate] = Arbitrary(genLocalDate.map { d => + new LocalDate(d.getYear, d.getMonthValue, d.getDayOfMonth) + }) - implicit val localDateTimeArbitrary: Arbitrary[LocalDateTime] = Arbitrary( - genLocalDateTime.map { d => - new LocalDateTime( - d.getYear, d.getMonthValue, d.getDayOfMonth, - d.getHour, d.getMinute, d.getSecond, nanoToMilli(d.getNano)) - }) + implicit val localDateTimeArbitrary: Arbitrary[LocalDateTime] = Arbitrary(genLocalDateTime.map { d => + new LocalDateTime( + d.getYear, + d.getMonthValue, + d.getDayOfMonth, + d.getHour, + d.getMinute, + d.getSecond, + nanoToMilli(d.getNano) + ) + }) - implicit val monthDayArbitrary: Arbitrary[MonthDay] = Arbitrary( - genMonthDay.map { m => - new MonthDay(m.getMonthValue, m.getDayOfMonth) - }) + implicit val monthDayArbitrary: Arbitrary[MonthDay] = Arbitrary(genMonthDay.map { m => + new MonthDay(m.getMonthValue, m.getDayOfMonth) + }) - implicit val yearMonthArbitrary: Arbitrary[YearMonth] = Arbitrary( - genYearMonth.map { y => - new YearMonth(y.getYear, y.getMonthValue) - }) + implicit val yearMonthArbitrary: Arbitrary[YearMonth] = Arbitrary(genYearMonth.map { y => + new YearMonth(y.getYear, y.getMonthValue) + }) implicit val zoneIdArbitrary: Arbitrary[DateTimeZone] = Arbitrary(Gen.oneOf(DateTimeZone.getAvailableIDs.asScala.toSeq).map(DateTimeZone.forID)) implicit val dateTimeArbitrary: Arbitrary[DateTime] = - Arbitrary( - for { - zoneId <- zoneIdArbitrary.arbitrary - localDateTime <- localDateTimeArbitrary.arbitrary if !zoneId.isLocalDateTimeGap(localDateTime) - } yield localDateTime.toDateTime(zoneId)) + Arbitrary(for { + zoneId <- zoneIdArbitrary.arbitrary + localDateTime <- localDateTimeArbitrary.arbitrary if !zoneId.isLocalDateTimeGap(localDateTime) + } yield localDateTime.toDateTime(zoneId)) implicit val periodArbitrary: Arbitrary[Period] = Arbitrary(Arbitrary.arbitrary[Int].map { i => diff --git a/modules/joda/src/test/scala/pureconfig/module/joda/configurable/ConfigurableSuite.scala b/modules/joda/src/test/scala/pureconfig/module/joda/configurable/ConfigurableSuite.scala index e074cf4b0..1fef5f09d 100644 --- a/modules/joda/src/test/scala/pureconfig/module/joda/configurable/ConfigurableSuite.scala +++ b/modules/joda/src/test/scala/pureconfig/module/joda/configurable/ConfigurableSuite.scala @@ -1,7 +1,7 @@ package pureconfig.module.joda.configurable import org.joda.time._ -import org.joda.time.format.{ DateTimeFormat, ISOPeriodFormat, PeriodFormatter } +import org.joda.time.format.{DateTimeFormat, ISOPeriodFormat, PeriodFormatter} import pureconfig.BaseSuite import pureconfig.module.joda.arbitrary._ diff --git a/modules/magnolia/src/main/scala/pureconfig/module/magnolia/MagnoliaConfigReader.scala b/modules/magnolia/src/main/scala/pureconfig/module/magnolia/MagnoliaConfigReader.scala index b9aad5a43..51872a261 100644 --- a/modules/magnolia/src/main/scala/pureconfig/module/magnolia/MagnoliaConfigReader.scala +++ b/modules/magnolia/src/main/scala/pureconfig/module/magnolia/MagnoliaConfigReader.scala @@ -2,14 +2,14 @@ package pureconfig.module.magnolia import _root_.magnolia._ import pureconfig._ -import pureconfig.error.{ ConfigReaderFailures, KeyNotFound, WrongSizeList } +import pureconfig.error.{ConfigReaderFailures, KeyNotFound, WrongSizeList} import pureconfig.generic.ProductHint.UseOrDefault import pureconfig.generic.error.InvalidCoproductOption -import pureconfig.generic.{ CoproductHint, ProductHint } +import pureconfig.generic.{CoproductHint, ProductHint} /** - * An object containing Magnolia `combine` and `dispatch` methods to generate `ConfigReader` instances. - */ + * An object containing Magnolia `combine` and `dispatch` methods to generate `ConfigReader` instances. + */ object MagnoliaConfigReader { def combine[A](ctx: CaseClass[ConfigReader, A])(implicit hint: ProductHint[A]): ConfigReader[A] = @@ -17,80 +17,92 @@ object MagnoliaConfigReader { else if (ctx.isValueClass) combineValueClass(ctx) else combineCaseClass(ctx) - private def combineCaseClass[A](ctx: CaseClass[ConfigReader, A])(implicit hint: ProductHint[A]): ConfigReader[A] = new ConfigReader[A] { - def from(cur: ConfigCursor): ConfigReader.Result[A] = { - cur.asObjectCursor.flatMap { objCur => - val actions = ctx.parameters.map { param => param.label -> hint.from(objCur, param.label) }.toMap + private def combineCaseClass[A](ctx: CaseClass[ConfigReader, A])(implicit hint: ProductHint[A]): ConfigReader[A] = + new ConfigReader[A] { + def from(cur: ConfigCursor): ConfigReader.Result[A] = { + cur.asObjectCursor.flatMap { objCur => + val actions = ctx.parameters.map { param => param.label -> hint.from(objCur, param.label) }.toMap - val res = ctx.constructEither[ConfigReaderFailures, Param[ConfigReader, A]#PType] { param => - val fieldHint = actions(param.label) - lazy val reader = param.typeclass - (fieldHint, param.default) match { - case (UseOrDefault(cursor, _), Some(defaultValue)) if cursor.isUndefined => - Right(defaultValue) - case (action, _) if reader.isInstanceOf[ReadsMissingKeys] || !action.cursor.isUndefined => - reader.from(action.cursor) - case _ => - cur.failed(KeyNotFound.forKeys(fieldHint.field, objCur.keys)) - } - }.left.map(_.reduce(_ ++ _)) + val res = ctx + .constructEither[ConfigReaderFailures, Param[ConfigReader, A]#PType] { param => + val fieldHint = actions(param.label) + lazy val reader = param.typeclass + (fieldHint, param.default) match { + case (UseOrDefault(cursor, _), Some(defaultValue)) if cursor.isUndefined => + Right(defaultValue) + case (action, _) if reader.isInstanceOf[ReadsMissingKeys] || !action.cursor.isUndefined => + reader.from(action.cursor) + case _ => + cur.failed(KeyNotFound.forKeys(fieldHint.field, objCur.keys)) + } + } + .left + .map(_.reduce(_ ++ _)) - val usedFields = actions.map(_._2.field).toSet - ConfigReader.Result.zipWith(res, hint.bottom(objCur, usedFields).toLeft(()))((r, _) => r) + val usedFields = actions.map(_._2.field).toSet + ConfigReader.Result.zipWith(res, hint.bottom(objCur, usedFields).toLeft(()))((r, _) => r) + } } } - } - private def combineTuple[A: ProductHint](ctx: CaseClass[ConfigReader, A]): ConfigReader[A] = new ConfigReader[A] { - def from(cur: ConfigCursor): ConfigReader.Result[A] = { - val collCur = cur.asListCursor.map(Right.apply).left.flatMap(failure => - cur.asObjectCursor.map(Left.apply).left.map(_ => failure)) + private def combineTuple[A: ProductHint](ctx: CaseClass[ConfigReader, A]): ConfigReader[A] = + new ConfigReader[A] { + def from(cur: ConfigCursor): ConfigReader.Result[A] = { + val collCur = cur.asListCursor + .map(Right.apply) + .left + .flatMap(failure => cur.asObjectCursor.map(Left.apply).left.map(_ => failure)) - collCur.flatMap { - case Left(objCur) => combineCaseClass(ctx).from(objCur) - case Right(listCur) => - if (listCur.size != ctx.parameters.length) { - cur.failed(WrongSizeList(ctx.parameters.length, listCur.size)) - } else { - val fields = ConfigReader.Result.sequence(ctx.parameters.zip(listCur.list).map { - case (param, cur) => param.typeclass.from(cur) - }) - fields.map(ctx.rawConstruct) - } + collCur.flatMap { + case Left(objCur) => combineCaseClass(ctx).from(objCur) + case Right(listCur) => + if (listCur.size != ctx.parameters.length) { + cur.failed(WrongSizeList(ctx.parameters.length, listCur.size)) + } else { + val fields = ConfigReader.Result.sequence(ctx.parameters.zip(listCur.list).map { + case (param, cur) => param.typeclass.from(cur) + }) + fields.map(ctx.rawConstruct) + } + } } } - } - private def combineValueClass[A](ctx: CaseClass[ConfigReader, A]): ConfigReader[A] = new ConfigReader[A] { - def from(cur: ConfigCursor): ConfigReader.Result[A] = - ctx.constructMonadic[ConfigReader.Result, Param[ConfigReader, A]#PType](_.typeclass.from(cur)) - } + private def combineValueClass[A](ctx: CaseClass[ConfigReader, A]): ConfigReader[A] = + new ConfigReader[A] { + def from(cur: ConfigCursor): ConfigReader.Result[A] = + ctx.constructMonadic[ConfigReader.Result, Param[ConfigReader, A]#PType](_.typeclass.from(cur)) + } - def dispatch[A](ctx: SealedTrait[ConfigReader, A])(implicit hint: CoproductHint[A]): ConfigReader[A] = new ConfigReader[A] { - def from(cur: ConfigCursor): ConfigReader.Result[A] = { - def readerFor(option: String) = - ctx.subtypes.find(_.typeName.short == option).map(_.typeclass) + def dispatch[A](ctx: SealedTrait[ConfigReader, A])(implicit hint: CoproductHint[A]): ConfigReader[A] = + new ConfigReader[A] { + def from(cur: ConfigCursor): ConfigReader.Result[A] = { + def readerFor(option: String) = + ctx.subtypes.find(_.typeName.short == option).map(_.typeclass) - hint.from(cur, ctx.subtypes.map(_.typeName.short).sorted).right.flatMap { - case CoproductHint.Use(cur, option) => - readerFor(option) match { - case Some(value) => value.from(cur) - case None => ConfigReader.Result.fail[A](cur.failureFor(InvalidCoproductOption(option))) - } + hint.from(cur, ctx.subtypes.map(_.typeName.short).sorted).right.flatMap { + case CoproductHint.Use(cur, option) => + readerFor(option) match { + case Some(value) => value.from(cur) + case None => ConfigReader.Result.fail[A](cur.failureFor(InvalidCoproductOption(option))) + } - case CoproductHint.Attempt(cur, options, combineF) => - val initial: Either[Vector[(String, ConfigReaderFailures)], A] = Left(Vector.empty) - val res = options.foldLeft(initial) { (curr, option) => - curr.left.flatMap { currentFailures => - readerFor(option) match { - case Some(value) => value.from(cur).left.map(f => currentFailures :+ (option -> f)) - case None => Left(currentFailures :+ - (option -> ConfigReaderFailures(cur.failureFor(InvalidCoproductOption(option))))) + case CoproductHint.Attempt(cur, options, combineF) => + val initial: Either[Vector[(String, ConfigReaderFailures)], A] = Left(Vector.empty) + val res = options.foldLeft(initial) { (curr, option) => + curr.left.flatMap { currentFailures => + readerFor(option) match { + case Some(value) => value.from(cur).left.map(f => currentFailures :+ (option -> f)) + case None => + Left( + currentFailures :+ + (option -> ConfigReaderFailures(cur.failureFor(InvalidCoproductOption(option)))) + ) + } } } - } - res.left.map(combineF) + res.left.map(combineF) + } } } - } } diff --git a/modules/magnolia/src/main/scala/pureconfig/module/magnolia/MagnoliaConfigWriter.scala b/modules/magnolia/src/main/scala/pureconfig/module/magnolia/MagnoliaConfigWriter.scala index d228bf128..56feba239 100644 --- a/modules/magnolia/src/main/scala/pureconfig/module/magnolia/MagnoliaConfigWriter.scala +++ b/modules/magnolia/src/main/scala/pureconfig/module/magnolia/MagnoliaConfigWriter.scala @@ -4,13 +4,13 @@ import scala.collection.JavaConverters._ import scala.reflect.ClassTag import _root_.magnolia._ -import com.typesafe.config.{ ConfigValue, ConfigValueFactory } +import com.typesafe.config.{ConfigValue, ConfigValueFactory} import pureconfig._ -import pureconfig.generic.{ CoproductHint, ProductHint } +import pureconfig.generic.{CoproductHint, ProductHint} /** - * An object containing Magnolia `combine` and `dispatch` methods to generate `ConfigWriter` instances. - */ + * An object containing Magnolia `combine` and `dispatch` methods to generate `ConfigWriter` instances. + */ object MagnoliaConfigWriter { def combine[A](ctx: CaseClass[ConfigWriter, A])(implicit hint: ProductHint[A]): ConfigWriter[A] = @@ -18,34 +18,39 @@ object MagnoliaConfigWriter { else if (ctx.isValueClass) combineValueClass(ctx) else combineCaseClass(ctx) - private def combineCaseClass[A](ctx: CaseClass[ConfigWriter, A])(implicit hint: ProductHint[A]): ConfigWriter[A] = new ConfigWriter[A] { - def to(a: A): ConfigValue = { - val fieldValues = ctx.parameters.map { param => - val valueOpt = param.typeclass match { - case tc: WritesMissingKeys[param.PType @unchecked] => - tc.toOpt(param.dereference(a)) - case tc => - Some(tc.to(param.dereference(a))) + private def combineCaseClass[A](ctx: CaseClass[ConfigWriter, A])(implicit hint: ProductHint[A]): ConfigWriter[A] = + new ConfigWriter[A] { + def to(a: A): ConfigValue = { + val fieldValues = ctx.parameters.map { param => + val valueOpt = param.typeclass match { + case tc: WritesMissingKeys[param.PType @unchecked] => + tc.toOpt(param.dereference(a)) + case tc => + Some(tc.to(param.dereference(a))) + } + hint.to(valueOpt, param.label) } - hint.to(valueOpt, param.label) + ConfigValueFactory.fromMap(fieldValues.flatten.toMap.asJava) } - ConfigValueFactory.fromMap(fieldValues.flatten.toMap.asJava) } - } - private def combineTuple[A](ctx: CaseClass[ConfigWriter, A]): ConfigWriter[A] = new ConfigWriter[A] { - override def to(a: A): ConfigValue = ConfigValueFactory.fromIterable( - ctx.parameters.map { param => param.typeclass.to(param.dereference(a)) }.asJava) - } + private def combineTuple[A](ctx: CaseClass[ConfigWriter, A]): ConfigWriter[A] = + new ConfigWriter[A] { + override def to(a: A): ConfigValue = + ConfigValueFactory.fromIterable(ctx.parameters.map { param => param.typeclass.to(param.dereference(a)) }.asJava) + } - private def combineValueClass[A](ctx: CaseClass[ConfigWriter, A]): ConfigWriter[A] = new ConfigWriter[A] { - override def to(a: A): ConfigValue = - ctx.parameters.map { param => param.typeclass.to(param.dereference(a)) }.head - } + private def combineValueClass[A](ctx: CaseClass[ConfigWriter, A]): ConfigWriter[A] = + new ConfigWriter[A] { + override def to(a: A): ConfigValue = + ctx.parameters.map { param => param.typeclass.to(param.dereference(a)) }.head + } - def dispatch[A: ClassTag](ctx: SealedTrait[ConfigWriter, A])(implicit hint: CoproductHint[A]): ConfigWriter[A] = new ConfigWriter[A] { - def to(a: A): ConfigValue = ctx.dispatch(a) { subtype => - hint.to(subtype.typeclass.to(subtype.cast(a)), subtype.typeName.short) + def dispatch[A: ClassTag](ctx: SealedTrait[ConfigWriter, A])(implicit hint: CoproductHint[A]): ConfigWriter[A] = + new ConfigWriter[A] { + def to(a: A): ConfigValue = + ctx.dispatch(a) { subtype => + hint.to(subtype.typeclass.to(subtype.cast(a)), subtype.typeName.short) + } } - } } diff --git a/modules/magnolia/src/main/scala/pureconfig/module/magnolia/auto/reader.scala b/modules/magnolia/src/main/scala/pureconfig/module/magnolia/auto/reader.scala index 7f9e575b1..815847d03 100644 --- a/modules/magnolia/src/main/scala/pureconfig/module/magnolia/auto/reader.scala +++ b/modules/magnolia/src/main/scala/pureconfig/module/magnolia/auto/reader.scala @@ -3,14 +3,14 @@ package pureconfig.module.magnolia.auto import scala.language.experimental.macros import magnolia._ -import pureconfig.{ ConfigReader, Exported } -import pureconfig.generic.{ CoproductHint, ProductHint } -import pureconfig.module.magnolia.{ ExportedMagnolia, MagnoliaConfigReader } +import pureconfig.{ConfigReader, Exported} +import pureconfig.generic.{CoproductHint, ProductHint} +import pureconfig.module.magnolia.{ExportedMagnolia, MagnoliaConfigReader} /** - * An object that, when imported, provides implicit `ConfigReader` instances for value classes, tuples, case classes and - * sealed traits. The generation of `ConfigReader`s is done by Magnolia. - */ + * An object that, when imported, provides implicit `ConfigReader` instances for value classes, tuples, case classes and + * sealed traits. The generation of `ConfigReader`s is done by Magnolia. + */ object reader { type Typeclass[A] = ConfigReader[A] diff --git a/modules/magnolia/src/main/scala/pureconfig/module/magnolia/auto/writer.scala b/modules/magnolia/src/main/scala/pureconfig/module/magnolia/auto/writer.scala index d64b521c7..973ffb9a5 100644 --- a/modules/magnolia/src/main/scala/pureconfig/module/magnolia/auto/writer.scala +++ b/modules/magnolia/src/main/scala/pureconfig/module/magnolia/auto/writer.scala @@ -4,14 +4,14 @@ import scala.language.experimental.macros import scala.reflect.ClassTag import magnolia._ -import pureconfig.{ ConfigWriter, Exported } -import pureconfig.generic.{ CoproductHint, ProductHint } -import pureconfig.module.magnolia.{ ExportedMagnolia, MagnoliaConfigWriter } +import pureconfig.{ConfigWriter, Exported} +import pureconfig.generic.{CoproductHint, ProductHint} +import pureconfig.module.magnolia.{ExportedMagnolia, MagnoliaConfigWriter} /** - * An object that, when imported, provides implicit `ConfigWriter` instances for value classes, tuples, case classes and - * sealed traits. The generation of `ConfigWriter`s is done by Magnolia. - */ + * An object that, when imported, provides implicit `ConfigWriter` instances for value classes, tuples, case classes and + * sealed traits. The generation of `ConfigWriter`s is done by Magnolia. + */ object writer { type Typeclass[A] = ConfigWriter[A] diff --git a/modules/magnolia/src/main/scala/pureconfig/module/magnolia/semiauto/reader.scala b/modules/magnolia/src/main/scala/pureconfig/module/magnolia/semiauto/reader.scala index ddc5664a7..98eee0f8c 100644 --- a/modules/magnolia/src/main/scala/pureconfig/module/magnolia/semiauto/reader.scala +++ b/modules/magnolia/src/main/scala/pureconfig/module/magnolia/semiauto/reader.scala @@ -4,13 +4,13 @@ import scala.language.experimental.macros import magnolia._ import pureconfig.ConfigReader -import pureconfig.generic.{ CoproductHint, ProductHint } +import pureconfig.generic.{CoproductHint, ProductHint} import pureconfig.module.magnolia.MagnoliaConfigReader /** - * An object that, when imported, provides methods for deriving `ConfigReader` instances on demand for value classes, - * tuples, case classes and sealed traits. The generation of `ConfigReader`s is done by Magnolia. - */ + * An object that, when imported, provides methods for deriving `ConfigReader` instances on demand for value classes, + * tuples, case classes and sealed traits. The generation of `ConfigReader`s is done by Magnolia. + */ object reader { type Typeclass[A] = ConfigReader[A] diff --git a/modules/magnolia/src/main/scala/pureconfig/module/magnolia/semiauto/writer.scala b/modules/magnolia/src/main/scala/pureconfig/module/magnolia/semiauto/writer.scala index 7c5f44da6..36c1bdfe4 100644 --- a/modules/magnolia/src/main/scala/pureconfig/module/magnolia/semiauto/writer.scala +++ b/modules/magnolia/src/main/scala/pureconfig/module/magnolia/semiauto/writer.scala @@ -5,13 +5,13 @@ import scala.reflect.ClassTag import magnolia._ import pureconfig.ConfigWriter -import pureconfig.generic.{ CoproductHint, ProductHint } +import pureconfig.generic.{CoproductHint, ProductHint} import pureconfig.module.magnolia.MagnoliaConfigWriter /** - * An object that, when imported, provides methods for deriving `ConfigWriter` instances on demand for value classes, - * tuples, case classes and sealed traits. The generation of `ConfigWriter`s is done by Magnolia. - */ + * An object that, when imported, provides methods for deriving `ConfigWriter` instances on demand for value classes, + * tuples, case classes and sealed traits. The generation of `ConfigWriter`s is done by Magnolia. + */ object writer { type Typeclass[A] = ConfigWriter[A] diff --git a/modules/magnolia/src/test/scala/pureconfig/module/magnolia/CoproductConvertersSuite.scala b/modules/magnolia/src/test/scala/pureconfig/module/magnolia/CoproductConvertersSuite.scala index 46093cfbd..05cc7ea2d 100644 --- a/modules/magnolia/src/test/scala/pureconfig/module/magnolia/CoproductConvertersSuite.scala +++ b/modules/magnolia/src/test/scala/pureconfig/module/magnolia/CoproductConvertersSuite.scala @@ -2,8 +2,8 @@ package pureconfig.module.magnolia import scala.language.higherKinds -import com.typesafe.config.{ ConfigFactory, ConfigObject, ConfigValueFactory } -import org.scalacheck.{ Arbitrary, Gen } +import com.typesafe.config.{ConfigFactory, ConfigObject, ConfigValueFactory} +import org.scalacheck.{Arbitrary, Gen} import pureconfig._ import pureconfig.error._ import pureconfig.module.magnolia.auto.reader._ diff --git a/modules/magnolia/src/test/scala/pureconfig/module/magnolia/CoproductHintSuite.scala b/modules/magnolia/src/test/scala/pureconfig/module/magnolia/CoproductHintSuite.scala index 2fd62e02a..13cf93981 100644 --- a/modules/magnolia/src/test/scala/pureconfig/module/magnolia/CoproductHintSuite.scala +++ b/modules/magnolia/src/test/scala/pureconfig/module/magnolia/CoproductHintSuite.scala @@ -6,7 +6,7 @@ import com.typesafe.config._ import pureconfig._ import pureconfig.error._ import pureconfig.generic._ -import pureconfig.generic.error.{ CoproductHintException, UnexpectedValueForFieldCoproductHint } +import pureconfig.generic.error.{CoproductHintException, UnexpectedValueForFieldCoproductHint} import pureconfig.module.magnolia.auto.reader._ import pureconfig.module.magnolia.auto.writer._ @@ -33,21 +33,38 @@ class CoproductHintSuite extends BaseSuite { it should "fail to read values that are not objects when using a FieldCoproductHint" in { val conf = ConfigValueFactory.fromAnyRef("Dog") ConfigConvert[AnimalConfig].from(conf) should failWith( - WrongType(ConfigValueType.STRING, Set(ConfigValueType.OBJECT))) + WrongType(ConfigValueType.STRING, Set(ConfigValueType.OBJECT)) + ) } it should "fail to read values in the discriminating field that are not strings when using a FieldCoproductHint" in { val conf = ConfigFactory.parseString("{ which-animal { type = Dog }, age = 2 }") - ConfigConvert[AnimalConfig].from(conf.root()) should be(Left( - ConfigReaderFailures( - ConvertFailure(WrongType(ConfigValueType.OBJECT, Set(ConfigValueType.STRING)), stringConfigOrigin(1), "which-animal")))) + ConfigConvert[AnimalConfig].from(conf.root()) should be( + Left( + ConfigReaderFailures( + ConvertFailure( + WrongType(ConfigValueType.OBJECT, Set(ConfigValueType.STRING)), + stringConfigOrigin(1), + "which-animal" + ) + ) + ) + ) } it should "fail with an appropriate reason if an unexpected value is found at the discriminating field when using a FieldCoproductHint" in { val conf = ConfigFactory.parseString("{ which-animal = unexpected, age = 2 }") - ConfigConvert[AnimalConfig].from(conf.root()) should be(Left( - ConfigReaderFailures( - ConvertFailure(UnexpectedValueForFieldCoproductHint(ConfigValueFactory.fromAnyRef("unexpected")), stringConfigOrigin(1), "which-animal")))) + ConfigConvert[AnimalConfig].from(conf.root()) should be( + Left( + ConfigReaderFailures( + ConvertFailure( + UnexpectedValueForFieldCoproductHint(ConfigValueFactory.fromAnyRef("unexpected")), + stringConfigOrigin(1), + "which-animal" + ) + ) + ) + ) } it should "fail to read when the hint field conflicts with a field of an option when using a FieldCoproductHint" in { diff --git a/modules/magnolia/src/test/scala/pureconfig/module/magnolia/DerivationModesSuite.scala b/modules/magnolia/src/test/scala/pureconfig/module/magnolia/DerivationModesSuite.scala index 70e164970..48698dd02 100644 --- a/modules/magnolia/src/test/scala/pureconfig/module/magnolia/DerivationModesSuite.scala +++ b/modules/magnolia/src/test/scala/pureconfig/module/magnolia/DerivationModesSuite.scala @@ -15,7 +15,11 @@ class DerivationModesSuite extends BaseSuite { val person = Person("John", "Doe") val conf = ConfigFactory.parseString("{ type: person, name: John, surname: Doe }") - case class CustomCaseClass(customObject: CustomObject, mapCustomObject: Map[String, CustomObject], mapListCustomObject: Map[String, List[CustomObject]]) + case class CustomCaseClass( + customObject: CustomObject, + mapCustomObject: Map[String, CustomObject], + mapListCustomObject: Map[String, List[CustomObject]] + ) case class CustomObject(value: Int) object CustomObject { implicit val pureconfigReader: ConfigReader[CustomObject] = ConfigReader.fromStringOpt { @@ -33,9 +37,9 @@ class DerivationModesSuite extends BaseSuite { val customCaseClass = CustomCaseClass( CustomObject(453), Map("a" -> CustomObject(453), "b" -> CustomObject(45)), - Map("x" -> List(CustomObject(45), CustomObject(453), CustomObject(1)))) - val customConf = ConfigFactory.parseString( - """{ + Map("x" -> List(CustomObject(45), CustomObject(453), CustomObject(1))) + ) + val customConf = ConfigFactory.parseString("""{ | custom-object = "eaaxacaca" | map-custom-object { a = "eaaxacaca", b = "a" } | map-list-custom-object { x = ["a", "eaaxacaca", "cvbc"]} diff --git a/modules/magnolia/src/test/scala/pureconfig/module/magnolia/ProductConvertersSuite.scala b/modules/magnolia/src/test/scala/pureconfig/module/magnolia/ProductConvertersSuite.scala index e922c9ae2..e1623e418 100644 --- a/modules/magnolia/src/test/scala/pureconfig/module/magnolia/ProductConvertersSuite.scala +++ b/modules/magnolia/src/test/scala/pureconfig/module/magnolia/ProductConvertersSuite.scala @@ -3,12 +3,12 @@ package pureconfig.module.magnolia import scala.collection.JavaConverters._ import scala.language.higherKinds -import com.typesafe.config.{ ConfigFactory, ConfigRenderOptions, ConfigValueFactory } +import com.typesafe.config.{ConfigFactory, ConfigRenderOptions, ConfigValueFactory} import org.scalacheck.Arbitrary import org.scalacheck.ScalacheckShapeless._ import pureconfig._ import pureconfig.ConfigConvert.catchReadError -import pureconfig.error.{ KeyNotFound, WrongType } +import pureconfig.error.{KeyNotFound, WrongType} import pureconfig.module.magnolia.auto.reader._ import pureconfig.module.magnolia.auto.writer._ @@ -22,10 +22,11 @@ class ProductConvertersSuite extends BaseSuite { /* A configuration with a field of a type that is unknown to `ConfigConvert` */ class MyType(myField: String) { def getMyField: String = myField - override def equals(obj: Any): Boolean = obj match { - case mt: MyType => myField.equals(mt.getMyField) - case _ => false - } + override def equals(obj: Any): Boolean = + obj match { + case mt: MyType => myField.equals(mt.getMyField) + case _ => false + } } case class ConfigWithUnknownType(d: MyType) @@ -42,14 +43,14 @@ class ProductConvertersSuite extends BaseSuite { it should s"be able to override all of the ConfigConvert instances used to parse ${classOf[FlatConfig]}" in forAll { (config: FlatConfig) => implicit val readBoolean = ConfigReader.fromString[Boolean](catchReadError(_ => false)) - implicit val readDouble = ConfigReader.fromString[Double](catchReadError(_ => 1D)) - implicit val readFloat = ConfigReader.fromString[Float](catchReadError(_ => 2F)) + implicit val readDouble = ConfigReader.fromString[Double](catchReadError(_ => 1d)) + implicit val readFloat = ConfigReader.fromString[Float](catchReadError(_ => 2f)) implicit val readInt = ConfigReader.fromString[Int](catchReadError(_ => 3)) implicit val readLong = ConfigReader.fromString[Long](catchReadError(_ => 4L)) implicit val readString = ConfigReader.fromString[String](catchReadError(_ => "foobar")) implicit val readOption = ConfigConvert.viaString[Option[String]](catchReadError(_ => None), _ => " ") val cc = ConfigConvert[FlatConfig] - cc.from(cc.to(config)) shouldBe Right(FlatConfig(false, 1D, 2F, 3, 4L, "foobar", None)) + cc.from(cc.to(config)) shouldBe Right(FlatConfig(false, 1d, 2f, 3, 4L, "foobar", None)) } val emptyConf = ConfigFactory.empty().root() @@ -78,7 +79,8 @@ class ProductConvertersSuite extends BaseSuite { implicit val defaultInt = new ConfigReader[Int] with ReadsMissingKeys { def from(cur: ConfigCursor) = - if (cur.isUndefined) Right(42) else { + if (cur.isUndefined) Right(42) + else { val s = cur.value.render(ConfigRenderOptions.concise) cur.scopeFailure(catchReadError(_.toInt)(implicitly)(s)) } @@ -113,7 +115,13 @@ class ProductConvertersSuite extends BaseSuite { it should "consider default arguments by default" in { case class InnerConf(e: Int, g: Int) - case class Conf(a: Int, b: String = "default", c: Int = 42, d: InnerConf = InnerConf(43, 44), e: Option[Int] = Some(45)) + case class Conf( + a: Int, + b: String = "default", + c: Int = 42, + d: InnerConf = InnerConf(43, 44), + e: Option[Int] = Some(45) + ) val conf1 = ConfigFactory.parseMap(Map("a" -> 2).asJava).root() ConfigConvert[Conf].from(conf1).right.value shouldBe Conf(2, "default", 42, InnerConf(43, 44), Some(45)) diff --git a/modules/magnolia/src/test/scala/pureconfig/module/magnolia/ProductHintSuite.scala b/modules/magnolia/src/test/scala/pureconfig/module/magnolia/ProductHintSuite.scala index 37ef12a85..2778cd15f 100644 --- a/modules/magnolia/src/test/scala/pureconfig/module/magnolia/ProductHintSuite.scala +++ b/modules/magnolia/src/test/scala/pureconfig/module/magnolia/ProductHintSuite.scala @@ -3,7 +3,7 @@ package pureconfig.module.magnolia import scala.collection.JavaConverters._ import scala.language.higherKinds -import com.typesafe.config.{ ConfigFactory, ConfigObject, ConfigValueType } +import com.typesafe.config.{ConfigFactory, ConfigObject, ConfigValueType} import pureconfig._ import pureconfig.error._ import pureconfig.generic.ProductHint @@ -46,19 +46,23 @@ class ProductHintSuite extends BaseSuite { "camel-case-string", "camel-case-conf", "this-is-an-int", - "this-is-another-int") + "this-is-another-int" + ) } it should "allow customizing the field mapping through a product hint" in { - val conf = ConfigFactory.parseString("""{ + val conf = ConfigFactory + .parseString("""{ A = 2 B = "two" - }""").root() + }""") + .root() case class SampleConf(a: Int, b: String) ConfigConvert[SampleConf].from(conf).left.value.toList should contain theSameElementsAs Seq( ConvertFailure(KeyNotFound("a", Set("A")), stringConfigOrigin(1), ""), - ConvertFailure(KeyNotFound("b", Set("B")), stringConfigOrigin(1), "")) + ConvertFailure(KeyNotFound("b", Set("B")), stringConfigOrigin(1), "") + ) implicit val productHint = ProductHint[SampleConf](ConfigFieldMapping(_.toUpperCase)) ConfigConvert[SampleConf].from(conf) shouldBe Right(SampleConf(2, "two")) @@ -89,7 +93,8 @@ class ProductHintSuite extends BaseSuite { "camelCaseString", "camelCaseConf", "thisIsAnInt", - "thisIsAnotherInt") + "thisIsAnotherInt" + ) } it should "read pascal case config keys to pascal case fields when configured to do so" in { @@ -117,7 +122,8 @@ class ProductHintSuite extends BaseSuite { "CamelCaseString", "CamelCaseConf", "ThisIsAnInt", - "ThisIsAnotherInt") + "ThisIsAnotherInt" + ) } it should "allow customizing the field mapping only for specific types" in { @@ -169,12 +175,20 @@ class ProductHintSuite extends BaseSuite { conf.getConfig("conf").to[Conf] shouldBe Left( ConfigReaderFailures( ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER)), stringConfigOrigin(3), "a"), - ConvertFailure(UnknownKey("b"), stringConfigOrigin(4), "b"))) + ConvertFailure(UnknownKey("b"), stringConfigOrigin(4), "b") + ) + ) } it should "not use default arguments if specified through a product hint" in { case class InnerConf(e: Int, g: Int) - case class Conf(a: Int, b: String = "default", c: Int = 42, d: InnerConf = InnerConf(43, 44), e: Option[Int] = Some(45)) + case class Conf( + a: Int, + b: String = "default", + c: Int = 42, + d: InnerConf = InnerConf(43, 44), + e: Option[Int] = Some(45) + ) implicit val productHint = ProductHint[Conf](useDefaultArgs = false) @@ -182,6 +196,7 @@ class ProductHintSuite extends BaseSuite { conf1.to[Conf].left.value.toList should contain theSameElementsAs Seq( ConvertFailure(KeyNotFound("b"), emptyConfigOrigin, ""), ConvertFailure(KeyNotFound("c"), emptyConfigOrigin, ""), - ConvertFailure(KeyNotFound("d"), emptyConfigOrigin, "")) + ConvertFailure(KeyNotFound("d"), emptyConfigOrigin, "") + ) } } diff --git a/modules/magnolia/src/test/scala/pureconfig/module/magnolia/TupleConvertersSuite.scala b/modules/magnolia/src/test/scala/pureconfig/module/magnolia/TupleConvertersSuite.scala index f11f55ce6..89da09ba6 100644 --- a/modules/magnolia/src/test/scala/pureconfig/module/magnolia/TupleConvertersSuite.scala +++ b/modules/magnolia/src/test/scala/pureconfig/module/magnolia/TupleConvertersSuite.scala @@ -3,7 +3,7 @@ package pureconfig.module.magnolia import scala.collection.JavaConverters._ import scala.language.higherKinds -import com.typesafe.config.{ ConfigValueFactory, ConfigValueType } +import com.typesafe.config.{ConfigValueFactory, ConfigValueType} import org.scalacheck.ScalacheckShapeless._ import pureconfig._ import pureconfig.error._ @@ -31,26 +31,38 @@ class TupleConvertersSuite extends BaseSuite { // Check writers checkWrite[Tuple1[Int]](Tuple1(1) -> ConfigValueFactory.fromIterable(List(1).asJava)) checkWrite[(String, Int)](("one", 2) -> ConfigValueFactory.fromIterable(List("one", 2).asJava)) - checkWrite[(Int, (Long, String), Boolean)]((1, (2l, "three"), false) -> ConfigValueFactory.fromIterable(List(1, List(2l, "three").asJava, false).asJava)) + checkWrite[(Int, (Long, String), Boolean)]( + (1, (2L, "three"), false) -> ConfigValueFactory.fromIterable(List(1, List(2L, "three").asJava, false).asJava) + ) // Check errors checkFailures[(String, Int)]( ConfigValueFactory.fromAnyRef(Map("_1" -> "one", "_2" -> "two").asJava) -> ConfigReaderFailures( - ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER)), emptyConfigOrigin, "_2"))) + ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER)), emptyConfigOrigin, "_2") + ) + ) checkFailures[(String, Int)]( ConfigValueFactory.fromIterable(List("one", "two").asJava) -> ConfigReaderFailures( - ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER)), emptyConfigOrigin, "1"))) + ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER)), emptyConfigOrigin, "1") + ) + ) checkFailures[(Int, Int, Int)]( ConfigValueFactory.fromIterable(List(1, "one").asJava) -> ConfigReaderFailures( - ConvertFailure(WrongSizeList(3, 2), emptyConfigOrigin, ""))) + ConvertFailure(WrongSizeList(3, 2), emptyConfigOrigin, "") + ) + ) checkFailures[(Int, Int, Int)]( ConfigValueFactory.fromAnyRef(Map("_1" -> "one", "_2" -> 2).asJava) -> ConfigReaderFailures( ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER)), emptyConfigOrigin, "_1"), - ConvertFailure(KeyNotFound("_3", Set()), emptyConfigOrigin, ""))) + ConvertFailure(KeyNotFound("_3", Set()), emptyConfigOrigin, "") + ) + ) checkFailures[(String, Int)]( ConfigValueFactory.fromAnyRef("str") -> ConfigReaderFailures( - ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.LIST)), emptyConfigOrigin, ""))) + ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.LIST)), emptyConfigOrigin, "") + ) + ) } diff --git a/modules/scala-xml/src/main/scala/pureconfig/module/scalaxml/package.scala b/modules/scala-xml/src/main/scala/pureconfig/module/scalaxml/package.scala index 176406b0d..3c601def4 100644 --- a/modules/scala-xml/src/main/scala/pureconfig/module/scalaxml/package.scala +++ b/modules/scala-xml/src/main/scala/pureconfig/module/scalaxml/package.scala @@ -1,13 +1,13 @@ package pureconfig.module -import scala.xml.{ Elem, XML } +import scala.xml.{Elem, XML} import pureconfig.ConvertHelpers.catchReadError -import pureconfig.{ ConfigReader, ConfigWriter } +import pureconfig.{ConfigReader, ConfigWriter} /** - * [[ConfigReader]] and [[ConfigWriter]] instances for Scala-XML's data structures. - */ + * [[ConfigReader]] and [[ConfigWriter]] instances for Scala-XML's data structures. + */ package object scalaxml { implicit def elemReader: ConfigReader[Elem] = diff --git a/modules/scala-xml/src/test/scala/pureconfig/module/scalaxml/ScalaXMLSuite.scala b/modules/scala-xml/src/test/scala/pureconfig/module/scalaxml/ScalaXMLSuite.scala index a3c0537e3..df87ad5fb 100644 --- a/modules/scala-xml/src/test/scala/pureconfig/module/scalaxml/ScalaXMLSuite.scala +++ b/modules/scala-xml/src/test/scala/pureconfig/module/scalaxml/ScalaXMLSuite.scala @@ -20,8 +20,7 @@ class ScalaXMLSuite extends AnyFlatSpec with Matchers with EitherValues { it should "be able to read a config with XML" in { - val config = parseString( - s"""{ people = + val config = parseString(s"""{ people = | \"\"\"$sampleXML\"\"\" | }""".stripMargin) config.to[Config] shouldEqual Right(Config(sampleXML)) diff --git a/modules/scalaz/src/main/scala/pureconfig/module/scalaz/instances/package.scala b/modules/scalaz/src/main/scala/pureconfig/module/scalaz/instances/package.scala index f5c0ba943..853b2ed9b 100644 --- a/modules/scalaz/src/main/scala/pureconfig/module/scalaz/instances/package.scala +++ b/modules/scalaz/src/main/scala/pureconfig/module/scalaz/instances/package.scala @@ -1,15 +1,15 @@ package pureconfig.module.scalaz import com.typesafe.config.ConfigValue -import pureconfig.{ ConfigConvert, ConfigReader, ConfigWriter } -import pureconfig.error.{ ConfigReaderFailure, ConfigReaderFailures, FailureReason } +import pureconfig.{ConfigConvert, ConfigReader, ConfigWriter} +import pureconfig.error.{ConfigReaderFailure, ConfigReaderFailures, FailureReason} -import scalaz.{ Contravariant, Equal, InvariantFunctor, MonadError, Semigroup, Show } +import scalaz.{Contravariant, Equal, InvariantFunctor, MonadError, Semigroup, Show} /** - * Instances of `scalaz` type classes for `ConfigReader`, `ConfigWriter`, `ConfigConvert` - * and other `pureconfig` citizens. - */ + * Instances of `scalaz` type classes for `ConfigReader`, `ConfigWriter`, `ConfigConvert` + * and other `pureconfig` citizens. + */ package object instances { implicit val configReaderInstance: MonadError[ConfigReader, ConfigReaderFailures] = diff --git a/modules/scalaz/src/main/scala/pureconfig/module/scalaz/package.scala b/modules/scalaz/src/main/scala/pureconfig/module/scalaz/package.scala index 2c44991f3..5c4b4ad22 100644 --- a/modules/scalaz/src/main/scala/pureconfig/module/scalaz/package.scala +++ b/modules/scalaz/src/main/scala/pureconfig/module/scalaz/package.scala @@ -1,12 +1,12 @@ package pureconfig.module -import _root_.scalaz.{ ==>>, ICons, INil, IList, ISet, Maybe, NonEmptyList, Order } +import _root_.scalaz.{==>>, ICons, INil, IList, ISet, Maybe, NonEmptyList, Order} import pureconfig._ import pureconfig.error.FailureReason /** - * `ConfigReader` and `ConfigWriter` instances for `scalaz` data structures. - */ + * `ConfigReader` and `ConfigWriter` instances for `scalaz` data structures. + */ package object scalaz { case object EmptyIListFound extends FailureReason { diff --git a/modules/scalaz/src/main/scala/pureconfig/module/scalaz/syntax/package.scala b/modules/scalaz/src/main/scala/pureconfig/module/scalaz/syntax/package.scala index 410cf126b..3b3666ffd 100644 --- a/modules/scalaz/src/main/scala/pureconfig/module/scalaz/syntax/package.scala +++ b/modules/scalaz/src/main/scala/pureconfig/module/scalaz/syntax/package.scala @@ -1,16 +1,16 @@ package pureconfig.module.scalaz import com.typesafe.config.ConfigValue -import pureconfig.{ ConfigConvert, ConfigReader } -import pureconfig.error.{ ConfigReaderFailure, ConfigReaderFailures, FailureReason } +import pureconfig.{ConfigConvert, ConfigReader} +import pureconfig.error.{ConfigReaderFailure, ConfigReaderFailures, FailureReason} -import scalaz.{ \/, Maybe, NonEmptyList, Validation } +import scalaz.{\/, Maybe, NonEmptyList, Validation} import scala.reflect.ClassTag /** - * Useful extension methods that bring `scalaz` data structures into `pureconfig` world. - */ + * Useful extension methods that bring `scalaz` data structures into `pureconfig` world. + */ package object syntax { implicit final class ConfigConvertCompanionObjectOps(val co: ConfigConvert.type) extends AnyVal { @@ -20,7 +20,10 @@ package object syntax { def viaStringMaybe[A: ClassTag](fromF: String => Maybe[A], toF: A => String): ConfigConvert[A] = co.viaStringOpt[A](fromF.andThen(_.toOption), toF) - def viaNonEmptyStringDisjunction[A: ClassTag](fromF: String => FailureReason \/ A, toF: A => String): ConfigConvert[A] = + def viaNonEmptyStringDisjunction[A: ClassTag]( + fromF: String => FailureReason \/ A, + toF: A => String + ): ConfigConvert[A] = co.viaNonEmptyString(fromF.andThen(_.toEither), toF) def viaNonEmptyStringMaybe[A: ClassTag](fromF: String => Maybe[A], toF: A => String): ConfigConvert[A] = diff --git a/modules/scalaz/src/test/scala/pureconfig/module/scalaz/ScalazLawsSuite.scala b/modules/scalaz/src/test/scala/pureconfig/module/scalaz/ScalazLawsSuite.scala index df7c0b980..4e4b06267 100644 --- a/modules/scalaz/src/test/scala/pureconfig/module/scalaz/ScalazLawsSuite.scala +++ b/modules/scalaz/src/test/scala/pureconfig/module/scalaz/ScalazLawsSuite.scala @@ -1,14 +1,14 @@ package pureconfig.module.scalaz import com.typesafe.config.ConfigValue -import org.scalacheck.{ Prop, Properties } +import org.scalacheck.{Prop, Properties} import org.scalatest.funsuite.AnyFunSuite import org.scalatestplus.scalacheck.Checkers import pureconfig.error.ConfigReaderFailures import pureconfig.module.scalaz.arbitrary._ import pureconfig.module.scalaz.equal._ import pureconfig.module.scalaz.instances._ -import pureconfig.{ ConfigConvert, ConfigReader, ConfigWriter } +import pureconfig.{ConfigConvert, ConfigReader, ConfigWriter} import scalaz.scalacheck.ScalazProperties._ import scalaz.std.anyVal.intInstance diff --git a/modules/scalaz/src/test/scala/pureconfig/module/scalaz/ScalazSuite.scala b/modules/scalaz/src/test/scala/pureconfig/module/scalaz/ScalazSuite.scala index acde13df6..68bce46a4 100644 --- a/modules/scalaz/src/test/scala/pureconfig/module/scalaz/ScalazSuite.scala +++ b/modules/scalaz/src/test/scala/pureconfig/module/scalaz/ScalazSuite.scala @@ -1,11 +1,11 @@ package pureconfig.module.scalaz import com.typesafe.config.ConfigFactory.parseString -import pureconfig.{ BaseSuite, ConfigConvertChecks } +import pureconfig.{BaseSuite, ConfigConvertChecks} import pureconfig.generic.auto._ import pureconfig.syntax._ -import scalaz.{ ==>>, IList, ISet, Maybe, NonEmptyList } +import scalaz.{==>>, IList, ISet, Maybe, NonEmptyList} import scalaz.scalacheck.ScalazArbitrary._ import scalaz.std.anyVal.intInstance import scalaz.std.string._ diff --git a/modules/scalaz/src/test/scala/pureconfig/module/scalaz/arbitrary/package.scala b/modules/scalaz/src/test/scala/pureconfig/module/scalaz/arbitrary/package.scala index afef56ca0..13753af79 100644 --- a/modules/scalaz/src/test/scala/pureconfig/module/scalaz/arbitrary/package.scala +++ b/modules/scalaz/src/test/scala/pureconfig/module/scalaz/arbitrary/package.scala @@ -1,9 +1,9 @@ package pureconfig.module.scalaz -import com.typesafe.config.{ ConfigValue, ConfigValueFactory } -import org.scalacheck.{ Arbitrary, Cogen, Gen } -import org.scalacheck.Arbitrary.{ arbitrary => arb } -import pureconfig.{ ConfigConvert, ConfigReader, ConfigWriter, Derivation } +import com.typesafe.config.{ConfigValue, ConfigValueFactory} +import org.scalacheck.{Arbitrary, Cogen, Gen} +import org.scalacheck.Arbitrary.{arbitrary => arb} +import pureconfig.{ConfigConvert, ConfigReader, ConfigWriter, Derivation} import pureconfig.error._ import scala.collection.JavaConverters._ @@ -17,24 +17,18 @@ package object arbitrary { private[this] def genAny(depth: Int): Gen[Any] = { val genScalar: Gen[Any] = - Gen.oneOf( - Gen.oneOf(true, false), - Gen.choose(Double.MinValue, Double.MaxValue), - Gen.alphaStr) + Gen.oneOf(Gen.oneOf(true, false), Gen.choose(Double.MinValue, Double.MaxValue), Gen.alphaStr) def genList(depth: Int): Gen[List[Any]] = - Gen.choose(0, MaxCollectionLength).flatMap(n => - Gen.listOfN(n, Gen.lzy(genAny(depth - 1)))) + Gen.choose(0, MaxCollectionLength).flatMap(n => Gen.listOfN(n, Gen.lzy(genAny(depth - 1)))) def genMap(depth: Int): Gen[Map[String, Any]] = - Gen.choose(0, MaxCollectionLength).flatMap(n => - Gen.mapOfN(n, for { k <- Gen.alphaStr; v <- Gen.lzy(genAny(depth - 1)) } yield (k, v))) + Gen + .choose(0, MaxCollectionLength) + .flatMap(n => Gen.mapOfN(n, for { k <- Gen.alphaStr; v <- Gen.lzy(genAny(depth - 1)) } yield (k, v))) if (depth == 0) genScalar - else Gen.frequency( - 3 -> genScalar, - 1 -> genList(depth).map(_.asJava), - 1 -> genMap(depth).map(_.asJava)) + else Gen.frequency(3 -> genScalar, 1 -> genList(depth).map(_.asJava), 1 -> genMap(depth).map(_.asJava)) } implicit val arbConfigValue: Arbitrary[ConfigValue] = @@ -49,7 +43,8 @@ package object arbitrary { ^(Gen.posNum[Int], Gen.posNum[Int])(WrongSizeString.apply), ^(Gen.posNum[Int], Gen.posNum[Int])(WrongSizeList.apply), Gen.alphaStr.map(k => KeyNotFound.apply(k)), - Gen.alphaStr.map(UnknownKey.apply)) + Gen.alphaStr.map(UnknownKey.apply) + ) implicit val arbConfigReaderFailures: Arbitrary[ConfigReaderFailures] = Arbitrary { diff --git a/modules/scalaz/src/test/scala/pureconfig/module/scalaz/equal/package.scala b/modules/scalaz/src/test/scala/pureconfig/module/scalaz/equal/package.scala index a4babca25..77c702cc1 100644 --- a/modules/scalaz/src/test/scala/pureconfig/module/scalaz/equal/package.scala +++ b/modules/scalaz/src/test/scala/pureconfig/module/scalaz/equal/package.scala @@ -1,8 +1,8 @@ package pureconfig.module.scalaz import com.typesafe.config.ConfigValue -import org.scalacheck.{ Arbitrary, Gen } -import pureconfig.{ ConfigConvert, ConfigReader, ConfigWriter } +import org.scalacheck.{Arbitrary, Gen} +import pureconfig.{ConfigConvert, ConfigReader, ConfigWriter} import pureconfig.module.scalaz.arbitrary._ import pureconfig.module.scalaz.instances._ diff --git a/modules/squants/src/main/scala/pureconfig/module/squants/package.scala b/modules/squants/src/main/scala/pureconfig/module/squants/package.scala index 03c591fed..fa129bf0f 100644 --- a/modules/squants/src/main/scala/pureconfig/module/squants/package.scala +++ b/modules/squants/src/main/scala/pureconfig/module/squants/package.scala @@ -15,17 +15,17 @@ import _root_.squants.time._ import pureconfig.ConfigConvert._ /** - * Provides [[ConfigConvert]] instances for Squants [[_root_.squants.Dimension]]. - * - * Note: All of the machinery can disappear if the `parse` method of [[_root_.squants.Dimension]] - * is made public. (see: https://github.com/typelevel/squants/issues/184). After that, something - * like this should suffice: - * - * {{{ - * implicit def dimensionConfigConvert[A <: Quantity[A]](dim: Dimension[A])(implicit tag: ClassTag[A]) = - * viaNonEmptyStringTry[A](dim.parse, _.toString) - * }}} - */ + * Provides [[ConfigConvert]] instances for Squants [[_root_.squants.Dimension]]. + * + * Note: All of the machinery can disappear if the `parse` method of [[_root_.squants.Dimension]] + * is made public. (see: https://github.com/typelevel/squants/issues/184). After that, something + * like this should suffice: + * + * {{{ + * implicit def dimensionConfigConvert[A <: Quantity[A]](dim: Dimension[A])(implicit tag: ClassTag[A]) = + * viaNonEmptyStringTry[A](dim.parse, _.toString) + * }}} + */ package object squants { // electro diff --git a/modules/squants/src/test/scala/pureconfig/module/squants/SquantsConvertTest.scala b/modules/squants/src/test/scala/pureconfig/module/squants/SquantsConvertTest.scala index ee158f9af..195b338c7 100644 --- a/modules/squants/src/test/scala/pureconfig/module/squants/SquantsConvertTest.scala +++ b/modules/squants/src/test/scala/pureconfig/module/squants/SquantsConvertTest.scala @@ -5,7 +5,7 @@ import scala.reflect.runtime.universe._ import _root_.squants._ import _root_.squants.market._ import pureconfig.module.squants.arbitrary._ -import pureconfig.{ BaseSuite, ConfigConvert, Derivation } +import pureconfig.{BaseSuite, ConfigConvert, Derivation} class SquantsConvertTest extends BaseSuite { implicit val mc: MoneyContext = defaultMoneyContext @@ -78,7 +78,9 @@ class SquantsConvertTest extends BaseSuite { checkArbitrary[market.Money] - def checkDimension[T <: Quantity[T]](dim: Dimension[T])(implicit tag: TypeTag[T], cc: Derivation[ConfigConvert[T]]): Unit = { + def checkDimension[T <: Quantity[T]]( + dim: Dimension[T] + )(implicit tag: TypeTag[T], cc: Derivation[ConfigConvert[T]]): Unit = { implicit val arbitrary = quantityAbitrary(dim) checkArbitrary[T] } diff --git a/modules/squants/src/test/scala/pureconfig/module/squants/arbitrary/package.scala b/modules/squants/src/test/scala/pureconfig/module/squants/arbitrary/package.scala index 9d51a7d91..205a0869e 100644 --- a/modules/squants/src/test/scala/pureconfig/module/squants/arbitrary/package.scala +++ b/modules/squants/src/test/scala/pureconfig/module/squants/arbitrary/package.scala @@ -1,17 +1,16 @@ package pureconfig.module.squants -import org.scalacheck.{ Arbitrary, Gen } +import org.scalacheck.{Arbitrary, Gen} import squants.market._ -import squants.{ Dimension, Quantity } +import squants.{Dimension, Quantity} package object arbitrary { def quantityAbitrary[A <: Quantity[A]](dim: Dimension[A]): Arbitrary[A] = { - Arbitrary( - for { - n <- Arbitrary.arbitrary[Double] - u <- Gen.oneOf(dim.units.toList) - } yield u(n)) + Arbitrary(for { + n <- Arbitrary.arbitrary[Double] + u <- Gen.oneOf(dim.units.toList) + } yield u(n)) } // Money.units is not implemented so we need an explicit Arbitrary @@ -19,13 +18,36 @@ package object arbitrary { // BTC is not included: fails on input: 0E-15 val currencies = - List(USD, ARS, AUD, BRL, CAD, CHF, CLP, CNY, CZK, DKK, EUR, GBP, - HKD, INR, JPY, KRW, MXN, MYR, NOK, NZD, RUB, SEK, XAG, XAU) + List( + USD, + ARS, + AUD, + BRL, + CAD, + CHF, + CLP, + CNY, + CZK, + DKK, + EUR, + GBP, + HKD, + INR, + JPY, + KRW, + MXN, + MYR, + NOK, + NZD, + RUB, + SEK, + XAG, + XAU + ) - Arbitrary( - for { - n <- Arbitrary.arbitrary[Double] - c <- Gen.oneOf(currencies) - } yield Money(BigDecimal(n).setScale(c.formatDecimals, BigDecimal.RoundingMode.HALF_EVEN), c)) + Arbitrary(for { + n <- Arbitrary.arbitrary[Double] + c <- Gen.oneOf(currencies) + } yield Money(BigDecimal(n).setScale(c.formatDecimals, BigDecimal.RoundingMode.HALF_EVEN), c)) } } diff --git a/modules/sttp/src/main/scala/pureconfig/module/sttp/package.scala b/modules/sttp/src/main/scala/pureconfig/module/sttp/package.scala index 6b6b8ebd1..6ba13fd68 100644 --- a/modules/sttp/src/main/scala/pureconfig/module/sttp/package.scala +++ b/modules/sttp/src/main/scala/pureconfig/module/sttp/package.scala @@ -6,7 +6,7 @@ import _root_.sttp.model.Uri._ import pureconfig.ConfigReader import pureconfig.error.CannotConvert -import scala.util.{ Failure, Success, Try } +import scala.util.{Failure, Success, Try} package object sttp { diff --git a/modules/sttp/src/test/scala/pureconfig/module/sttp/SttpSuite.scala b/modules/sttp/src/test/scala/pureconfig/module/sttp/SttpSuite.scala index 16c3eda1a..7aaf74e3c 100644 --- a/modules/sttp/src/test/scala/pureconfig/module/sttp/SttpSuite.scala +++ b/modules/sttp/src/test/scala/pureconfig/module/sttp/SttpSuite.scala @@ -4,7 +4,7 @@ import sttp.model.Uri import sttp.model.Uri._ import com.typesafe.config.ConfigFactory import pureconfig.BaseSuite -import pureconfig.error.{ CannotConvert, ConfigReaderFailures, ConvertFailure } +import pureconfig.error.{CannotConvert, ConfigReaderFailures, ConvertFailure} import pureconfig.generic.auto._ import pureconfig.syntax._ @@ -25,12 +25,10 @@ class SttpSuite extends BaseSuite { val failure = ConvertFailure( - reason = CannotConvert( - value = "sttp.readthedocs.io", - toType = "sttp.model.Uri", - because = "missing scheme"), + reason = CannotConvert(value = "sttp.readthedocs.io", toType = "sttp.model.Uri", because = "missing scheme"), origin = stringConfigOrigin(1), - path = "uri") + path = "uri" + ) config.to[AppConfig].left.value shouldBe ConfigReaderFailures(failure) } diff --git a/modules/yaml/src/main/scala/pureconfig/module/yaml/YamlConfigSource.scala b/modules/yaml/src/main/scala/pureconfig/module/yaml/YamlConfigSource.scala index f29bcf6dd..19aa5e61b 100644 --- a/modules/yaml/src/main/scala/pureconfig/module/yaml/YamlConfigSource.scala +++ b/modules/yaml/src/main/scala/pureconfig/module/yaml/YamlConfigSource.scala @@ -1,36 +1,37 @@ package pureconfig.module.yaml import java.io._ -import java.net.{ URI, URL } -import java.nio.file.{ Files, Path, Paths } +import java.net.{URI, URL} +import java.nio.file.{Files, Path, Paths} import java.util.Base64 import scala.collection.JavaConverters._ import scala.util.Try import scala.util.control.NonFatal -import com.typesafe.config.{ ConfigOrigin, ConfigOriginFactory, ConfigValue, ConfigValueFactory } +import com.typesafe.config.{ConfigOrigin, ConfigOriginFactory, ConfigValue, ConfigValueFactory} import org.yaml.snakeyaml.Yaml import org.yaml.snakeyaml.constructor.SafeConstructor -import org.yaml.snakeyaml.error.{ Mark, MarkedYAMLException, YAMLException } +import org.yaml.snakeyaml.error.{Mark, MarkedYAMLException, YAMLException} import org.yaml.snakeyaml.nodes.Tag import pureconfig.ConfigReader.Result import pureconfig.error._ -import pureconfig.module.yaml.error.{ NonStringKeyFound, UnsupportedYamlType } -import pureconfig.{ ConfigObjectSource, ConfigSource } +import pureconfig.module.yaml.error.{NonStringKeyFound, UnsupportedYamlType} +import pureconfig.{ConfigObjectSource, ConfigSource} /** - * A `ConfigSource` that reads configs from YAML documents in a stream, file or string. - * - * @param getReader the thunk to generate a `Reader` instance from which the YAML document will be - * read. This parameter won't be memoized so it can be used with dynamic sources - * (e.g. URLs) - * @param uri the optional URI of the source. Used only to provide better error messages. - * @param onIOFailure an optional function used to provide a custom failure when IO errors happen - */ + * A `ConfigSource` that reads configs from YAML documents in a stream, file or string. + * + * @param getReader the thunk to generate a `Reader` instance from which the YAML document will be + * read. This parameter won't be memoized so it can be used with dynamic sources + * (e.g. URLs) + * @param uri the optional URI of the source. Used only to provide better error messages. + * @param onIOFailure an optional function used to provide a custom failure when IO errors happen + */ final class YamlConfigSource private ( getReader: () => Reader, uri: Option[URI] = None, - onIOFailure: Option[Option[Throwable] => CannotRead] = None) extends ConfigSource { + onIOFailure: Option[Option[Throwable] => CannotRead] = None +) extends ConfigSource { // instances of `Yaml` are not thread safe private[this] def loader = new Yaml(new CustomConstructor()) @@ -42,30 +43,34 @@ final class YamlConfigSource private ( } /** - * Converts this YAML source to a config object source to allow merging with other sources. This - * operation is not reversible. The new source will load with an error if this document does not - * contain an object. - * - * @return a config object source that produces YAML object documents read by this source - */ + * Converts this YAML source to a config object source to allow merging with other sources. This + * operation is not reversible. The new source will load with an error if this document does not + * contain an object. + * + * @return a config object source that produces YAML object documents read by this source + */ def asObjectSource: ConfigObjectSource = ConfigObjectSource(fluentCursor().asObjectCursor.right.map(_.value.toConfig)) /** - * Returns a new source that produces a multi-document YAML read by this source as a config list. - * - * @return a new source that produces a multi-document YAML read by this source as a config list. - */ - def multiDoc: ConfigSource = new ConfigSource { - def value(): Result[ConfigValue] = { - usingReader { reader => - loader.loadAll(reader).asScala - .map(yamlObjToConfigValue) - .foldRight(Right(Nil): Result[List[ConfigValue]])(Result.zipWith(_, _)(_ :: _)) - .right.map { cvs => ConfigValueFactory.fromIterable(cvs.asJava) } + * Returns a new source that produces a multi-document YAML read by this source as a config list. + * + * @return a new source that produces a multi-document YAML read by this source as a config list. + */ + def multiDoc: ConfigSource = + new ConfigSource { + def value(): Result[ConfigValue] = { + usingReader { reader => + loader + .loadAll(reader) + .asScala + .map(yamlObjToConfigValue) + .foldRight(Right(Nil): Result[List[ConfigValue]])(Result.zipWith(_, _)(_ :: _)) + .right + .map { cvs => ConfigValueFactory.fromIterable(cvs.asJava) } + } } } - } // YAML has special support for timestamps and the built-in `SafeConstructor` parses values into Java `Date` // instances. However, date readers are expecting strings and the original format may matter to them. This class @@ -78,35 +83,37 @@ final class YamlConfigSource private ( // (https://bitbucket.org/asomov/snakeyaml/wiki/Documentation#markdown-header-loading-yaml) private[this] def yamlObjToConfigValue(obj: AnyRef): Result[ConfigValue] = { - def aux(obj: AnyRef): Result[AnyRef] = obj match { - case m: java.util.Map[AnyRef @unchecked, AnyRef @unchecked] => - val entries: Iterable[Result[(String, AnyRef)]] = m.asScala.map { - case (k: String, v) => aux(v).right.map { v: AnyRef => k -> v } - case (k, _) => Left(ConfigReaderFailures(NonStringKeyFound(k.toString, k.getClass.getSimpleName))) - } - Result.sequence(entries).right.map(_.toMap.asJava) + def aux(obj: AnyRef): Result[AnyRef] = + obj match { + case m: java.util.Map[AnyRef @unchecked, AnyRef @unchecked] => + val entries: Iterable[Result[(String, AnyRef)]] = m.asScala.map { + case (k: String, v) => aux(v).right.map { v: AnyRef => k -> v } + case (k, _) => Left(ConfigReaderFailures(NonStringKeyFound(k.toString, k.getClass.getSimpleName))) + } + Result.sequence(entries).right.map(_.toMap.asJava) - case xs: java.util.List[AnyRef @unchecked] => - Result.sequence(xs.asScala.map(aux)).right.map(_.toList.asJava) + case xs: java.util.List[AnyRef @unchecked] => + Result.sequence(xs.asScala.map(aux)).right.map(_.toList.asJava) - case s: java.util.Set[AnyRef @unchecked] => - Result.sequence(s.asScala.map(aux)).right.map(_.toSet.asJava) + case s: java.util.Set[AnyRef @unchecked] => + Result.sequence(s.asScala.map(aux)).right.map(_.toSet.asJava) - case _: java.lang.Integer | _: java.lang.Long | _: java.lang.Double | _: java.lang.String | _: java.lang.Boolean => - Right(obj) // these types are supported directly by `ConfigValueFactory.fromAnyRef` + case _: java.lang.Integer | _: java.lang.Long | _: java.lang.Double | _: java.lang.String | + _: java.lang.Boolean => + Right(obj) // these types are supported directly by `ConfigValueFactory.fromAnyRef` - case _: java.math.BigInteger => - Right(obj.toString) + case _: java.math.BigInteger => + Right(obj.toString) - case ba: Array[Byte] => - Right(Base64.getEncoder.encodeToString(ba)) + case ba: Array[Byte] => + Right(Base64.getEncoder.encodeToString(ba)) - case null => - Right(null) + case null => + Right(null) - case _ => // this shouldn't happen - Left(ConfigReaderFailures(UnsupportedYamlType(obj.toString, obj.getClass.getSimpleName))) - } + case _ => // this shouldn't happen + Left(ConfigReaderFailures(UnsupportedYamlType(obj.toString, obj.getClass.getSimpleName))) + } aux(obj).right.map(ConfigValueFactory.fromAnyRef) } @@ -138,44 +145,49 @@ final class YamlConfigSource private ( object YamlConfigSource { /** - * Returns a YAML source that provides configs read from a file. - * - * @param path the path to the file as a string - * @return a YAML source that provides configs read from a file. - */ - def file(path: String) = new YamlConfigSource( - () => new FileReader(path), - uri = Some(new File(path).toURI), - onIOFailure = Some(CannotReadFile(Paths.get(path), _))) + * Returns a YAML source that provides configs read from a file. + * + * @param path the path to the file as a string + * @return a YAML source that provides configs read from a file. + */ + def file(path: String) = + new YamlConfigSource( + () => new FileReader(path), + uri = Some(new File(path).toURI), + onIOFailure = Some(CannotReadFile(Paths.get(path), _)) + ) /** - * Returns a YAML source that provides configs read from a file. - * - * @param path the path to the file - * @return a YAML source that provides configs read from a file. - */ - def file(path: Path) = new YamlConfigSource( - () => Files.newBufferedReader(path), - uri = Some(path.toUri), - onIOFailure = Some(CannotReadFile(path, _))) + * Returns a YAML source that provides configs read from a file. + * + * @param path the path to the file + * @return a YAML source that provides configs read from a file. + */ + def file(path: Path) = + new YamlConfigSource( + () => Files.newBufferedReader(path), + uri = Some(path.toUri), + onIOFailure = Some(CannotReadFile(path, _)) + ) /** - * Returns a YAML source that provides configs read from a file. - * - * @param file the file - * @return a YAML source that provides configs read from a file. - */ - def file(file: File) = new YamlConfigSource( - () => new FileReader(file), - uri = Some(file.toURI), - onIOFailure = Some(CannotReadFile(file.toPath, _))) + * Returns a YAML source that provides configs read from a file. + * + * @param file the file + * @return a YAML source that provides configs read from a file. + */ + def file(file: File) = + new YamlConfigSource( + () => new FileReader(file), + uri = Some(file.toURI), + onIOFailure = Some(CannotReadFile(file.toPath, _)) + ) /** - * Returns a YAML source that provides a config parsed from a string. - * - * @param confStr the YAML content - * @return a YAML source that provides a config parsed from a string. - */ - def string(confStr: String) = new YamlConfigSource( - () => new StringReader(confStr)) + * Returns a YAML source that provides a config parsed from a string. + * + * @param confStr the YAML content + * @return a YAML source that provides a config parsed from a string. + */ + def string(confStr: String) = new YamlConfigSource(() => new StringReader(confStr)) } diff --git a/modules/yaml/src/main/scala/pureconfig/module/yaml/package.scala b/modules/yaml/src/main/scala/pureconfig/module/yaml/package.scala index 4301732b1..0b31c3d7d 100644 --- a/modules/yaml/src/main/scala/pureconfig/module/yaml/package.scala +++ b/modules/yaml/src/main/scala/pureconfig/module/yaml/package.scala @@ -10,53 +10,59 @@ import pureconfig.error._ package object yaml { /** - * Loads a configuration of type `Config` from the given YAML file. - * - * @param path the path of the YAML file to read - * @return A `Success` with the configuration if it is possible to create an instance of type - * `Config` from the YAML file, else a `Failure` with details on why it isn't possible - */ + * Loads a configuration of type `Config` from the given YAML file. + * + * @param path the path of the YAML file to read + * @return A `Success` with the configuration if it is possible to create an instance of type + * `Config` from the YAML file, else a `Failure` with details on why it isn't possible + */ @deprecated("Use `YamlConfigSource.file(path).load[Config]` instead", "0.12.1") def loadYaml[Config](path: Path)(implicit reader: Derivation[ConfigReader[Config]]): ConfigReader.Result[Config] = { YamlConfigSource.file(path).load[Config] } /** - * Loads a configuration of type `Config` from the given YAML file. - * - * @param path the path of the YAML file to read - * @param namespace the base namespace from which the configuration should be load - * @return A `Success` with the configuration if it is possible to create an instance of type - * `Config` from the YAML file, else a `Failure` with details on why it isn't possible - */ + * Loads a configuration of type `Config` from the given YAML file. + * + * @param path the path of the YAML file to read + * @param namespace the base namespace from which the configuration should be load + * @return A `Success` with the configuration if it is possible to create an instance of type + * `Config` from the YAML file, else a `Failure` with details on why it isn't possible + */ @deprecated("Use `YamlConfigSource.file(path).at(namespace).load[Config]` instead", "0.12.1") - def loadYaml[Config](path: Path, namespace: String)(implicit reader: Derivation[ConfigReader[Config]]): ConfigReader.Result[Config] = { + def loadYaml[Config](path: Path, namespace: String)(implicit + reader: Derivation[ConfigReader[Config]] + ): ConfigReader.Result[Config] = { YamlConfigSource.file(path).at(namespace).load[Config] } /** - * Loads a configuration of type `Config` from the given string. - * - * @param content the string containing the YAML document - * @return A `Success` with the configuration if it is possible to create an instance of type - * `Config` from `content`, else a `Failure` with details on why it isn't possible - */ + * Loads a configuration of type `Config` from the given string. + * + * @param content the string containing the YAML document + * @return A `Success` with the configuration if it is possible to create an instance of type + * `Config` from `content`, else a `Failure` with details on why it isn't possible + */ @deprecated("Use `YamlConfigSource.string(content).load[Config]` instead", "0.12.1") - def loadYaml[Config](content: String)(implicit reader: Derivation[ConfigReader[Config]]): ConfigReader.Result[Config] = { + def loadYaml[Config]( + content: String + )(implicit reader: Derivation[ConfigReader[Config]]): ConfigReader.Result[Config] = { YamlConfigSource.string(content).load[Config] } @deprecated("Use `YamlConfigSource.string(content).at(namespace).load[Config]` instead", "0.12.1") - def loadYaml[Config](content: String, namespace: String)(implicit reader: Derivation[ConfigReader[Config]]): ConfigReader.Result[Config] = { + def loadYaml[Config](content: String, namespace: String)(implicit + reader: Derivation[ConfigReader[Config]] + ): ConfigReader.Result[Config] = { YamlConfigSource.string(content).at(namespace).load[Config] } /** - * Loads a configuration of type `Config` from the given YAML file. - * - * @param path the path of the YAML file to read - * @return the configuration - */ + * Loads a configuration of type `Config` from the given YAML file. + * + * @param path the path of the YAML file to read + * @return the configuration + */ @throws[ConfigReaderException[_]] @deprecated("Use `YamlConfigSource.file(path).loadOrThrow[Config]` instead", "0.12.1") def loadYamlOrThrow[Config: ClassTag](path: Path)(implicit reader: Derivation[ConfigReader[Config]]): Config = { @@ -64,24 +70,26 @@ package object yaml { } /** - * Loads a configuration of type `Config` from the given YAML file. - * - * @param path the path of the YAML file to read - * @param namespace the base namespace from which the configuration should be load - * @return the configuration - */ + * Loads a configuration of type `Config` from the given YAML file. + * + * @param path the path of the YAML file to read + * @param namespace the base namespace from which the configuration should be load + * @return the configuration + */ @throws[ConfigReaderException[_]] @deprecated("Use `YamlConfigSource.file(path).at(namespace).loadOrThrow[Config]` instead", "0.12.1") - def loadYamlOrThrow[Config: ClassTag](path: Path, namespace: String)(implicit reader: Derivation[ConfigReader[Config]]): Config = { + def loadYamlOrThrow[Config: ClassTag](path: Path, namespace: String)(implicit + reader: Derivation[ConfigReader[Config]] + ): Config = { YamlConfigSource.file(path).at(namespace).loadOrThrow[Config] } /** - * Loads a configuration of type `Config` from the given string. - * - * @param content the string containing the YAML document - * @return the configuration - */ + * Loads a configuration of type `Config` from the given string. + * + * @param content the string containing the YAML document + * @return the configuration + */ @throws[ConfigReaderException[_]] @deprecated("Use `YamlConfigSource.string(content).loadOrThrow[Config]` instead", "0.12.1") def loadYamlOrThrow[Config: ClassTag](content: String)(implicit reader: Derivation[ConfigReader[Config]]): Config = { @@ -89,53 +97,57 @@ package object yaml { } /** - * Loads a configuration of type `Config` from the given string. - * - * @param content the string containing the YAML document - * @param namespace the base namespace from which the configuration should be load - * @return the configuration - */ + * Loads a configuration of type `Config` from the given string. + * + * @param content the string containing the YAML document + * @param namespace the base namespace from which the configuration should be load + * @return the configuration + */ @throws[ConfigReaderException[_]] @deprecated("Use `YamlConfigSource.string(content).at(namespace).loadOrThrow[Config]` instead", "0.12.1") - def loadYamlOrThrow[Config: ClassTag](content: String, namespace: String)(implicit reader: Derivation[ConfigReader[Config]]): Config = { + def loadYamlOrThrow[Config: ClassTag](content: String, namespace: String)(implicit + reader: Derivation[ConfigReader[Config]] + ): Config = { YamlConfigSource.string(content).at(namespace).loadOrThrow[Config] } /** - * Loads a configuration of type `Config` from the given multi-document YAML file. `Config` must have a - * `ConfigReader` supporting reading from config lists. - * - * @param path the path of the YAML file to read - * @return A `Success` with the configuration if it is possible to create an instance of type - * `Config` from the multi-document YAML file, else a `Failure` with details on why it - * isn't possible - */ + * Loads a configuration of type `Config` from the given multi-document YAML file. `Config` must have a + * `ConfigReader` supporting reading from config lists. + * + * @param path the path of the YAML file to read + * @return A `Success` with the configuration if it is possible to create an instance of type + * `Config` from the multi-document YAML file, else a `Failure` with details on why it + * isn't possible + */ @deprecated("Use `YamlConfigSource.file(path).multiDoc.load[Config]` instead", "0.12.1") def loadYamls[Config](path: Path)(implicit reader: Derivation[ConfigReader[Config]]): ConfigReader.Result[Config] = { YamlConfigSource.file(path).multiDoc.load[Config] } /** - * Loads a configuration of type `Config` from the given multi-document string. `Config` must have a - * `ConfigReader` supporting reading from config lists. - * - * @param content the string containing the YAML documents - * @return A `Success` with the configuration if it is possible to create an instance of type - * `Config` from the multi-document string, else a `Failure` with details on why it - * isn't possible - */ + * Loads a configuration of type `Config` from the given multi-document string. `Config` must have a + * `ConfigReader` supporting reading from config lists. + * + * @param content the string containing the YAML documents + * @return A `Success` with the configuration if it is possible to create an instance of type + * `Config` from the multi-document string, else a `Failure` with details on why it + * isn't possible + */ @deprecated("Use `YamlConfigSource.string(content).multiDoc.load[Config]` instead", "0.12.1") - def loadYamls[Config](content: String)(implicit reader: Derivation[ConfigReader[Config]]): ConfigReader.Result[Config] = { + def loadYamls[Config]( + content: String + )(implicit reader: Derivation[ConfigReader[Config]]): ConfigReader.Result[Config] = { YamlConfigSource.string(content).multiDoc.load[Config] } /** - * Loads a configuration of type `Config` from the given multi-document YAML file. `Config` must have a - * `ConfigReader` supporting reading from config lists. - * - * @param path the path of the YAML file to read - * @return the configuration - */ + * Loads a configuration of type `Config` from the given multi-document YAML file. `Config` must have a + * `ConfigReader` supporting reading from config lists. + * + * @param path the path of the YAML file to read + * @return the configuration + */ @throws[ConfigReaderException[_]] @deprecated("Use `YamlConfigSource.file(path).multiDoc.loadOrThrow[Config]` instead", "0.12.1") def loadYamlsOrThrow[Config: ClassTag](path: Path)(implicit reader: Derivation[ConfigReader[Config]]): Config = { @@ -143,12 +155,12 @@ package object yaml { } /** - * Loads a configuration of type `Config` from the given multi-document string. `Config` must have a - * `ConfigReader` supporting reading from config lists. - * - * @param content the string containing the YAML documents - * @return the configuration - */ + * Loads a configuration of type `Config` from the given multi-document string. `Config` must have a + * `ConfigReader` supporting reading from config lists. + * + * @param content the string containing the YAML documents + * @return the configuration + */ @throws[ConfigReaderException[_]] @deprecated("Use `YamlConfigSource.string(content).multiDoc.loadOrThrow[Config]` instead", "0.12.1") def loadYamlsOrThrow[Config: ClassTag](content: String)(implicit reader: Derivation[ConfigReader[Config]]): Config = { diff --git a/modules/yaml/src/test/scala/pureconfig/module/yaml/YamlApiSuite.scala b/modules/yaml/src/test/scala/pureconfig/module/yaml/YamlApiSuite.scala index a0c7063f7..ba28371fa 100644 --- a/modules/yaml/src/test/scala/pureconfig/module/yaml/YamlApiSuite.scala +++ b/modules/yaml/src/test/scala/pureconfig/module/yaml/YamlApiSuite.scala @@ -1,9 +1,9 @@ package pureconfig.module.yaml import java.net.URLDecoder -import java.nio.file.{ Files, Path, Paths } +import java.nio.file.{Files, Path, Paths} -import com.typesafe.config.{ ConfigValue, ConfigValueType } +import com.typesafe.config.{ConfigValue, ConfigValueType} import org.scalatest.EitherValues import pureconfig.BaseSuite import pureconfig.error._ @@ -29,99 +29,120 @@ class YamlApiSuite extends BaseSuite with EitherValues { s: Set[Int], xs: List[Long], map: Map[String, Double], - inner: InnerConf) + inner: InnerConf + ) behavior of "YAML loading" it should "loadYaml from a simple YAML file" in { - loadYaml[Conf](resourcePath("basic.yaml")) shouldBe Right(Conf( - "abc", - true, - BigInt("1234567890123456789012345678901234567890"), - "dGhpcyBpcyBhIG1lc3NhZ2U=", - None, - Set(4, 6, 8), - List(10L, 10000L, 10000000L, 10000000000L), - Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), - InnerConf(42, "def"))) + loadYaml[Conf](resourcePath("basic.yaml")) shouldBe Right( + Conf( + "abc", + true, + BigInt("1234567890123456789012345678901234567890"), + "dGhpcyBpcyBhIG1lc3NhZ2U=", + None, + Set(4, 6, 8), + List(10L, 10000L, 10000000L, 10000000000L), + Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), + InnerConf(42, "def") + ) + ) } it should "loadYaml from a string" in { - loadYaml[Conf](resourceContents("basic.yaml")) shouldBe Right(Conf( - "abc", - true, - BigInt("1234567890123456789012345678901234567890"), - "dGhpcyBpcyBhIG1lc3NhZ2U=", - None, - Set(4, 6, 8), - List(10L, 10000L, 10000000L, 10000000000L), - Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), - InnerConf(42, "def"))) + loadYaml[Conf](resourceContents("basic.yaml")) shouldBe Right( + Conf( + "abc", + true, + BigInt("1234567890123456789012345678901234567890"), + "dGhpcyBpcyBhIG1lc3NhZ2U=", + None, + Set(4, 6, 8), + List(10L, 10000L, 10000000L, 10000000000L), + Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), + InnerConf(42, "def") + ) + ) } it should "loadYaml from string content with empty namespace" in { - loadYaml[Conf](resourceContents("basic.yaml"), "") shouldBe Right(Conf( - "abc", - true, - BigInt("1234567890123456789012345678901234567890"), - "dGhpcyBpcyBhIG1lc3NhZ2U=", - None, - Set(4, 6, 8), - List(10L, 10000L, 10000000L, 10000000000L), - Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), - InnerConf(42, "def"))) + loadYaml[Conf](resourceContents("basic.yaml"), "") shouldBe Right( + Conf( + "abc", + true, + BigInt("1234567890123456789012345678901234567890"), + "dGhpcyBpcyBhIG1lc3NhZ2U=", + None, + Set(4, 6, 8), + List(10L, 10000L, 10000000L, 10000000000L), + Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), + InnerConf(42, "def") + ) + ) } it should "fail to loadYaml from string content with non existent namespace" in { loadYaml[Conf](resourceContents("basic.yaml"), "foo") shouldBe Left( - ConfigReaderFailures(ConvertFailure(KeyNotFound("foo", Set.empty), emptyConfigOrigin, ""))) + ConfigReaderFailures(ConvertFailure(KeyNotFound("foo", Set.empty), emptyConfigOrigin, "")) + ) } it should "fail to loadYaml from path with non existent namespace" in { loadYaml[Conf](resourcePath("basic.yaml"), "foo") shouldBe Left( - ConfigReaderFailures(ConvertFailure(KeyNotFound("foo", Set.empty), emptyConfigOrigin, ""))) + ConfigReaderFailures(ConvertFailure(KeyNotFound("foo", Set.empty), emptyConfigOrigin, "")) + ) } it should "loadYaml from a path with empty namespace" in { - loadYaml[Conf](resourcePath("basic.yaml"), "") shouldBe Right(Conf( - "abc", - true, - BigInt("1234567890123456789012345678901234567890"), - "dGhpcyBpcyBhIG1lc3NhZ2U=", - None, - Set(4, 6, 8), - List(10L, 10000L, 10000000L, 10000000000L), - Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), - InnerConf(42, "def"))) + loadYaml[Conf](resourcePath("basic.yaml"), "") shouldBe Right( + Conf( + "abc", + true, + BigInt("1234567890123456789012345678901234567890"), + "dGhpcyBpcyBhIG1lc3NhZ2U=", + None, + Set(4, 6, 8), + List(10L, 10000L, 10000000L, 10000000000L), + Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), + InnerConf(42, "def") + ) + ) } it should "loadYaml from string content with a specific namespace of a Map" in { - loadYaml[Map[String, String]](resourceContents("basic.yaml"), "map") - .right - .value should contain theSameElementsAs Map("a" -> "1.5", "b" -> "2.5", "c" -> "3.5") + loadYaml[Map[String, String]]( + resourceContents("basic.yaml"), + "map" + ).right.value should contain theSameElementsAs Map("a" -> "1.5", "b" -> "2.5", "c" -> "3.5") } it should "loadYaml from string content with a specific namespace of a BigInt" in { - loadYaml[BigInt](resourceContents("basic.yaml"), "n") - .right - .value shouldBe BigInt("1234567890123456789012345678901234567890") + loadYaml[BigInt](resourceContents("basic.yaml"), "n").right.value shouldBe BigInt( + "1234567890123456789012345678901234567890" + ) } it should "fail to loadYaml of an array from string content with a non existent specific namespace" in { loadYaml[BigInt](resourceContents("array.yaml"), "n") shouldBe Left( - ConfigReaderFailures(ConvertFailure(WrongType(ConfigValueType.LIST, Set(ConfigValueType.OBJECT)), emptyConfigOrigin, ""))) + ConfigReaderFailures( + ConvertFailure(WrongType(ConfigValueType.LIST, Set(ConfigValueType.OBJECT)), emptyConfigOrigin, "") + ) + ) } it should "loadYaml from a path with a specific namespace of a Map" in { - loadYaml[Map[String, String]](resourcePath("basic.yaml"), "map") - .right - .value should contain theSameElementsAs Map("a" -> "1.5", "b" -> "2.5", "c" -> "3.5") + loadYaml[Map[String, String]](resourcePath("basic.yaml"), "map").right.value should contain theSameElementsAs Map( + "a" -> "1.5", + "b" -> "2.5", + "c" -> "3.5" + ) } it should "loadYaml from a path with a specific namespace of a BigInt" in { - loadYaml[BigInt](resourceContents("basic.yaml"), "n") - .right - .value shouldBe BigInt("1234567890123456789012345678901234567890") + loadYaml[BigInt](resourceContents("basic.yaml"), "n").right.value shouldBe BigInt( + "1234567890123456789012345678901234567890" + ) } it should "loadYamlOrThrow from a simple YAML file" in { @@ -134,7 +155,8 @@ class YamlApiSuite extends BaseSuite with EitherValues { Set(4, 6, 8), List(10L, 10000L, 10000000L, 10000000000L), Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), - InnerConf(42, "def")) + InnerConf(42, "def") + ) } it should "loadYamlOrThrow from a string" in { @@ -147,79 +169,96 @@ class YamlApiSuite extends BaseSuite with EitherValues { Set(4, 6, 8), List(10L, 10000L, 10000000L, 10000000000L), Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), - InnerConf(42, "def")) + InnerConf(42, "def") + ) } it should "loadYamls from a multi-document YAML file" in { - loadYamls[List[InnerConf]](resourcePath("multi.yaml")) shouldBe Right(List( - InnerConf(1, "abc"), - InnerConf(2, "def"), - InnerConf(3, "ghi"))) + loadYamls[List[InnerConf]](resourcePath("multi.yaml")) shouldBe Right( + List(InnerConf(1, "abc"), InnerConf(2, "def"), InnerConf(3, "ghi")) + ) - loadYamls[(InnerConf, Conf)](resourcePath("multi2.yaml")) shouldBe Right(( - InnerConf(1, "abc"), - Conf( - "abc", - true, - BigInt("1234567890123456789012345678901234567890"), - "dGhpcyBpcyBhIG1lc3NhZ2U=", - None, - Set(4, 6, 8), - List(10L, 10000L, 10000000L, 10000000000L), - Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), - InnerConf(42, "def")))) + loadYamls[(InnerConf, Conf)](resourcePath("multi2.yaml")) shouldBe Right( + ( + InnerConf(1, "abc"), + Conf( + "abc", + true, + BigInt("1234567890123456789012345678901234567890"), + "dGhpcyBpcyBhIG1lc3NhZ2U=", + None, + Set(4, 6, 8), + List(10L, 10000L, 10000000L, 10000000000L), + Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), + InnerConf(42, "def") + ) + ) + ) } it should "loadYamls from a string" in { - loadYamls[(InnerConf, Conf)](resourceContents("multi2.yaml")) shouldBe Right(( - InnerConf(1, "abc"), - Conf( - "abc", - true, - BigInt("1234567890123456789012345678901234567890"), - "dGhpcyBpcyBhIG1lc3NhZ2U=", - None, - Set(4, 6, 8), - List(10L, 10000L, 10000000L, 10000000000L), - Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), - InnerConf(42, "def")))) + loadYamls[(InnerConf, Conf)](resourceContents("multi2.yaml")) shouldBe Right( + ( + InnerConf(1, "abc"), + Conf( + "abc", + true, + BigInt("1234567890123456789012345678901234567890"), + "dGhpcyBpcyBhIG1lc3NhZ2U=", + None, + Set(4, 6, 8), + List(10L, 10000L, 10000000L, 10000000000L), + Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), + InnerConf(42, "def") + ) + ) + ) } it should "loadYamlsOrThrow from a multi-document YAML file" in { loadYamlsOrThrow[List[InnerConf]](resourcePath("multi.yaml")) shouldBe List( InnerConf(1, "abc"), InnerConf(2, "def"), - InnerConf(3, "ghi")) + InnerConf(3, "ghi") + ) - loadYamlsOrThrow[(InnerConf, Conf)](resourcePath("multi2.yaml")) shouldBe (( - InnerConf(1, "abc"), - Conf( - "abc", - true, - BigInt("1234567890123456789012345678901234567890"), - "dGhpcyBpcyBhIG1lc3NhZ2U=", - None, - Set(4, 6, 8), - List(10L, 10000L, 10000000L, 10000000000L), - Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), - InnerConf(42, "def")))) + loadYamlsOrThrow[(InnerConf, Conf)](resourcePath("multi2.yaml")) shouldBe ( + ( + InnerConf(1, "abc"), + Conf( + "abc", + true, + BigInt("1234567890123456789012345678901234567890"), + "dGhpcyBpcyBhIG1lc3NhZ2U=", + None, + Set(4, 6, 8), + List(10L, 10000L, 10000000L, 10000000000L), + Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), + InnerConf(42, "def") + ) + ) + ) } it should "loadYamlsOrThrow from a string" in { val content = new String(Files.readAllBytes(resourcePath("multi2.yaml"))) - loadYamlsOrThrow[(InnerConf, Conf)](content) shouldBe (( - InnerConf(1, "abc"), - Conf( - "abc", - true, - BigInt("1234567890123456789012345678901234567890"), - "dGhpcyBpcyBhIG1lc3NhZ2U=", - None, - Set(4, 6, 8), - List(10L, 10000L, 10000000L, 10000000000L), - Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), - InnerConf(42, "def")))) + loadYamlsOrThrow[(InnerConf, Conf)](content) shouldBe ( + ( + InnerConf(1, "abc"), + Conf( + "abc", + true, + BigInt("1234567890123456789012345678901234567890"), + "dGhpcyBpcyBhIG1lc3NhZ2U=", + None, + Set(4, 6, 8), + List(10L, 10000L, 10000000L, 10000000000L), + Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), + InnerConf(42, "def") + ) + ) + ) } it should "fail with a domain error when a file does not exist" in { @@ -235,11 +274,12 @@ class YamlApiSuite extends BaseSuite with EitherValues { } it should "fail with the correct config origin filled when a YAML fails to be parsed" in { - loadYaml[ConfigValue](resourcePath("illegal.yaml")) should failWith(CannotParse( - "mapping values are not allowed here", - urlConfigOrigin(resourcePath("illegal.yaml").toUri.toURL, 3))) + loadYaml[ConfigValue](resourcePath("illegal.yaml")) should failWith( + CannotParse("mapping values are not allowed here", urlConfigOrigin(resourcePath("illegal.yaml").toUri.toURL, 3)) + ) - loadYaml[ConfigValue](resourceContents("illegal.yaml")) should failWith(CannotParse( - "mapping values are not allowed here", None)) + loadYaml[ConfigValue](resourceContents("illegal.yaml")) should failWith( + CannotParse("mapping values are not allowed here", None) + ) } } diff --git a/modules/yaml/src/test/scala/pureconfig/module/yaml/YamlConfigSourceSuite.scala b/modules/yaml/src/test/scala/pureconfig/module/yaml/YamlConfigSourceSuite.scala index fe1039572..b62a86096 100644 --- a/modules/yaml/src/test/scala/pureconfig/module/yaml/YamlConfigSourceSuite.scala +++ b/modules/yaml/src/test/scala/pureconfig/module/yaml/YamlConfigSourceSuite.scala @@ -1,12 +1,12 @@ package pureconfig.module.yaml import java.net.URLDecoder -import java.nio.file.{ Files, Path, Paths } +import java.nio.file.{Files, Path, Paths} import java.time.Instant -import com.typesafe.config.{ ConfigValue, ConfigValueType } +import com.typesafe.config.{ConfigValue, ConfigValueType} import org.scalatest.EitherValues -import pureconfig.{ BaseSuite, ConfigSource } +import pureconfig.{BaseSuite, ConfigSource} import pureconfig.error._ import pureconfig.generic.auto._ import pureconfig.module.yaml.error.NonStringKeyFound @@ -29,99 +29,123 @@ class YamlConfigSourceSuite extends BaseSuite with EitherValues { s: Set[Int], xs: List[Long], map: Map[String, Double], - inner: InnerConf) + inner: InnerConf + ) behavior of "YAML loading" it should "loadYaml from a simple YAML file" in { - YamlConfigSource.file(resourcePath("basic.yaml")).load[Conf] shouldBe Right(Conf( - "abc", - true, - BigInt("1234567890123456789012345678901234567890"), - "dGhpcyBpcyBhIG1lc3NhZ2U=", - None, - Set(4, 6, 8), - List(10L, 10000L, 10000000L, 10000000000L), - Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), - InnerConf(42, "def"))) + YamlConfigSource.file(resourcePath("basic.yaml")).load[Conf] shouldBe Right( + Conf( + "abc", + true, + BigInt("1234567890123456789012345678901234567890"), + "dGhpcyBpcyBhIG1lc3NhZ2U=", + None, + Set(4, 6, 8), + List(10L, 10000L, 10000000L, 10000000000L), + Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), + InnerConf(42, "def") + ) + ) } it should "loadYaml from a string" in { - YamlConfigSource.string(resourceContents("basic.yaml")).load[Conf] shouldBe Right(Conf( - "abc", - true, - BigInt("1234567890123456789012345678901234567890"), - "dGhpcyBpcyBhIG1lc3NhZ2U=", - None, - Set(4, 6, 8), - List(10L, 10000L, 10000000L, 10000000000L), - Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), - InnerConf(42, "def"))) + YamlConfigSource.string(resourceContents("basic.yaml")).load[Conf] shouldBe Right( + Conf( + "abc", + true, + BigInt("1234567890123456789012345678901234567890"), + "dGhpcyBpcyBhIG1lc3NhZ2U=", + None, + Set(4, 6, 8), + List(10L, 10000L, 10000000L, 10000000000L), + Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), + InnerConf(42, "def") + ) + ) } it should "loadYaml from string content with empty namespace" in { - YamlConfigSource.string(resourceContents("basic.yaml")).at("").load[Conf] shouldBe Right(Conf( - "abc", - true, - BigInt("1234567890123456789012345678901234567890"), - "dGhpcyBpcyBhIG1lc3NhZ2U=", - None, - Set(4, 6, 8), - List(10L, 10000L, 10000000L, 10000000000L), - Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), - InnerConf(42, "def"))) + YamlConfigSource.string(resourceContents("basic.yaml")).at("").load[Conf] shouldBe Right( + Conf( + "abc", + true, + BigInt("1234567890123456789012345678901234567890"), + "dGhpcyBpcyBhIG1lc3NhZ2U=", + None, + Set(4, 6, 8), + List(10L, 10000L, 10000000L, 10000000000L), + Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), + InnerConf(42, "def") + ) + ) } it should "fail to loadYaml from string content with non existent namespace" in { YamlConfigSource.string(resourceContents("basic.yaml")).at("foo").load[Conf] shouldBe Left( - ConfigReaderFailures(ConvertFailure(KeyNotFound("foo", Set.empty), emptyConfigOrigin, ""))) + ConfigReaderFailures(ConvertFailure(KeyNotFound("foo", Set.empty), emptyConfigOrigin, "")) + ) } it should "fail to loadYaml from path with non existent namespace" in { YamlConfigSource.file(resourcePath("basic.yaml")).at("foo").load[Conf] shouldBe Left( - ConfigReaderFailures(ConvertFailure(KeyNotFound("foo", Set.empty), emptyConfigOrigin, ""))) + ConfigReaderFailures(ConvertFailure(KeyNotFound("foo", Set.empty), emptyConfigOrigin, "")) + ) } it should "loadYaml from a path with empty namespace" in { - YamlConfigSource.file(resourcePath("basic.yaml")).at("").load[Conf] shouldBe Right(Conf( - "abc", - true, - BigInt("1234567890123456789012345678901234567890"), - "dGhpcyBpcyBhIG1lc3NhZ2U=", - None, - Set(4, 6, 8), - List(10L, 10000L, 10000000L, 10000000000L), - Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), - InnerConf(42, "def"))) + YamlConfigSource.file(resourcePath("basic.yaml")).at("").load[Conf] shouldBe Right( + Conf( + "abc", + true, + BigInt("1234567890123456789012345678901234567890"), + "dGhpcyBpcyBhIG1lc3NhZ2U=", + None, + Set(4, 6, 8), + List(10L, 10000L, 10000000L, 10000000000L), + Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), + InnerConf(42, "def") + ) + ) } it should "loadYaml from string content with a specific namespace of a Map" in { - YamlConfigSource.string(resourceContents("basic.yaml")).at("map").load[Map[String, String]] + YamlConfigSource + .string(resourceContents("basic.yaml")) + .at("map") + .load[Map[String, String]] .right .value should contain theSameElementsAs Map("a" -> "1.5", "b" -> "2.5", "c" -> "3.5") } it should "loadYaml from string content with a specific namespace of a BigInt" in { - YamlConfigSource.string(resourceContents("basic.yaml")).at("n").load[BigInt] - .right - .value shouldBe BigInt("1234567890123456789012345678901234567890") + YamlConfigSource.string(resourceContents("basic.yaml")).at("n").load[BigInt].right.value shouldBe BigInt( + "1234567890123456789012345678901234567890" + ) } it should "fail to loadYaml of an array from string content with a non existent specific namespace" in { YamlConfigSource.string(resourceContents("array.yaml")).at("n").load[BigInt] shouldBe Left( - ConfigReaderFailures(ConvertFailure(WrongType(ConfigValueType.LIST, Set(ConfigValueType.OBJECT)), emptyConfigOrigin, ""))) + ConfigReaderFailures( + ConvertFailure(WrongType(ConfigValueType.LIST, Set(ConfigValueType.OBJECT)), emptyConfigOrigin, "") + ) + ) } it should "loadYaml from a path with a specific namespace of a Map" in { - YamlConfigSource.file(resourcePath("basic.yaml")).at("map").load[Map[String, String]] + YamlConfigSource + .file(resourcePath("basic.yaml")) + .at("map") + .load[Map[String, String]] .right .value should contain theSameElementsAs Map("a" -> "1.5", "b" -> "2.5", "c" -> "3.5") } it should "loadYaml from a path with a specific namespace of a BigInt" in { - YamlConfigSource.string(resourceContents("basic.yaml")).at("n").load[BigInt] - .right - .value shouldBe BigInt("1234567890123456789012345678901234567890") + YamlConfigSource.string(resourceContents("basic.yaml")).at("n").load[BigInt].right.value shouldBe BigInt( + "1234567890123456789012345678901234567890" + ) } it should "not change date formats when parsing YAML documents" in { @@ -144,7 +168,8 @@ class YamlConfigSourceSuite extends BaseSuite with EitherValues { Set(4, 6, 8), List(10L, 10000L, 10000000L, 10000000000L), Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), - InnerConf(42, "def")) + InnerConf(42, "def") + ) } it should "loadYamlOrThrow from a string" in { @@ -157,100 +182,128 @@ class YamlConfigSourceSuite extends BaseSuite with EitherValues { Set(4, 6, 8), List(10L, 10000L, 10000000L, 10000000000L), Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), - InnerConf(42, "def")) + InnerConf(42, "def") + ) } it should "loadYamls from a multi-document YAML file" in { - YamlConfigSource.file(resourcePath("multi.yaml")).multiDoc.load[List[InnerConf]] shouldBe Right(List( - InnerConf(1, "abc"), - InnerConf(2, "def"), - InnerConf(3, "ghi"))) - - YamlConfigSource.file(resourcePath("multi2.yaml")).multiDoc.load[(InnerConf, Conf)] shouldBe Right(( - InnerConf(1, "abc"), - Conf( - "abc", - true, - BigInt("1234567890123456789012345678901234567890"), - "dGhpcyBpcyBhIG1lc3NhZ2U=", - None, - Set(4, 6, 8), - List(10L, 10000L, 10000000L, 10000000000L), - Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), - InnerConf(42, "def")))) + YamlConfigSource.file(resourcePath("multi.yaml")).multiDoc.load[List[InnerConf]] shouldBe Right( + List(InnerConf(1, "abc"), InnerConf(2, "def"), InnerConf(3, "ghi")) + ) + + YamlConfigSource.file(resourcePath("multi2.yaml")).multiDoc.load[(InnerConf, Conf)] shouldBe Right( + ( + InnerConf(1, "abc"), + Conf( + "abc", + true, + BigInt("1234567890123456789012345678901234567890"), + "dGhpcyBpcyBhIG1lc3NhZ2U=", + None, + Set(4, 6, 8), + List(10L, 10000L, 10000000L, 10000000000L), + Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), + InnerConf(42, "def") + ) + ) + ) } it should "loadYamls from a string" in { - YamlConfigSource.string(resourceContents("multi2.yaml")).multiDoc.load[(InnerConf, Conf)] shouldBe Right(( - InnerConf(1, "abc"), - Conf( - "abc", - true, - BigInt("1234567890123456789012345678901234567890"), - "dGhpcyBpcyBhIG1lc3NhZ2U=", - None, - Set(4, 6, 8), - List(10L, 10000L, 10000000L, 10000000000L), - Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), - InnerConf(42, "def")))) + YamlConfigSource.string(resourceContents("multi2.yaml")).multiDoc.load[(InnerConf, Conf)] shouldBe Right( + ( + InnerConf(1, "abc"), + Conf( + "abc", + true, + BigInt("1234567890123456789012345678901234567890"), + "dGhpcyBpcyBhIG1lc3NhZ2U=", + None, + Set(4, 6, 8), + List(10L, 10000L, 10000000L, 10000000000L), + Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), + InnerConf(42, "def") + ) + ) + ) } it should "loadYamlsOrThrow from a multi-document YAML file" in { YamlConfigSource.file(resourcePath("multi.yaml")).multiDoc.loadOrThrow[List[InnerConf]] shouldBe List( InnerConf(1, "abc"), InnerConf(2, "def"), - InnerConf(3, "ghi")) - - YamlConfigSource.file(resourcePath("multi2.yaml")).multiDoc.loadOrThrow[(InnerConf, Conf)] shouldBe (( - InnerConf(1, "abc"), - Conf( - "abc", - true, - BigInt("1234567890123456789012345678901234567890"), - "dGhpcyBpcyBhIG1lc3NhZ2U=", - None, - Set(4, 6, 8), - List(10L, 10000L, 10000000L, 10000000000L), - Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), - InnerConf(42, "def")))) + InnerConf(3, "ghi") + ) + + YamlConfigSource.file(resourcePath("multi2.yaml")).multiDoc.loadOrThrow[(InnerConf, Conf)] shouldBe ( + ( + InnerConf(1, "abc"), + Conf( + "abc", + true, + BigInt("1234567890123456789012345678901234567890"), + "dGhpcyBpcyBhIG1lc3NhZ2U=", + None, + Set(4, 6, 8), + List(10L, 10000L, 10000000L, 10000000000L), + Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), + InnerConf(42, "def") + ) + ) + ) } it should "loadYamlsOrThrow from a string" in { val content = new String(Files.readAllBytes(resourcePath("multi2.yaml"))) - YamlConfigSource.string(content).multiDoc.loadOrThrow[(InnerConf, Conf)] shouldBe (( - InnerConf(1, "abc"), - Conf( - "abc", - true, - BigInt("1234567890123456789012345678901234567890"), - "dGhpcyBpcyBhIG1lc3NhZ2U=", - None, - Set(4, 6, 8), - List(10L, 10000L, 10000000L, 10000000000L), - Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), - InnerConf(42, "def")))) + YamlConfigSource.string(content).multiDoc.loadOrThrow[(InnerConf, Conf)] shouldBe ( + ( + InnerConf(1, "abc"), + Conf( + "abc", + true, + BigInt("1234567890123456789012345678901234567890"), + "dGhpcyBpcyBhIG1lc3NhZ2U=", + None, + Set(4, 6, 8), + List(10L, 10000L, 10000000L, 10000000000L), + Map("a" -> 1.5, "b" -> 2.5, "c" -> 3.5), + InnerConf(42, "def") + ) + ) + ) } it should "fail with a domain error when a file does not exist" in { YamlConfigSource.file(Paths.get("nonexisting.yaml")).load[ConfigValue] should failWithType[CannotReadFile] - YamlConfigSource.file(Paths.get("nonexisting.yaml")).multiDoc.load[List[ConfigValue]] should failWithType[CannotReadFile] - a[ConfigReaderException[_]] should be thrownBy YamlConfigSource.file(Paths.get("nonexisting.yaml")).loadOrThrow[ConfigValue] - a[ConfigReaderException[_]] should be thrownBy YamlConfigSource.file(Paths.get("nonexisting.yaml")).multiDoc.loadOrThrow[List[ConfigValue]] + YamlConfigSource + .file(Paths.get("nonexisting.yaml")) + .multiDoc + .load[List[ConfigValue]] should failWithType[CannotReadFile] + a[ConfigReaderException[_]] should be thrownBy YamlConfigSource + .file(Paths.get("nonexisting.yaml")) + .loadOrThrow[ConfigValue] + a[ConfigReaderException[_]] should be thrownBy YamlConfigSource + .file(Paths.get("nonexisting.yaml")) + .multiDoc + .loadOrThrow[List[ConfigValue]] } it should "fail with a domain error when a non-string key is found" in { YamlConfigSource.file(resourcePath("non_string_keys.yaml")).load[ConfigValue] should failWithType[NonStringKeyFound] - YamlConfigSource.string(resourceContents("non_string_keys.yaml")).load[ConfigValue] should failWithType[NonStringKeyFound] + YamlConfigSource + .string(resourceContents("non_string_keys.yaml")) + .load[ConfigValue] should failWithType[NonStringKeyFound] } it should "fail with the correct config origin filled when a YAML fails to be parsed" in { - YamlConfigSource.file(resourcePath("illegal.yaml")).load[ConfigValue] should failWith(CannotParse( - "mapping values are not allowed here", - urlConfigOrigin(resourcePath("illegal.yaml").toUri.toURL, 3))) + YamlConfigSource.file(resourcePath("illegal.yaml")).load[ConfigValue] should failWith( + CannotParse("mapping values are not allowed here", urlConfigOrigin(resourcePath("illegal.yaml").toUri.toURL, 3)) + ) - YamlConfigSource.string(resourceContents("illegal.yaml")).load[ConfigValue] should failWith(CannotParse( - "mapping values are not allowed here", None)) + YamlConfigSource.string(resourceContents("illegal.yaml")).load[ConfigValue] should failWith( + CannotParse("mapping values are not allowed here", None) + ) } it should "allow being converted to an object source" in { @@ -261,9 +314,11 @@ class YamlConfigSourceSuite extends BaseSuite with EitherValues { } it should "be mergeable with non-YAML configs" in { - ConfigSource.string(s"{ str: bcd, map { a: 0.5, b: $${map.c} } }") + ConfigSource + .string(s"{ str: bcd, map { a: 0.5, b: $${map.c} } }") .withFallback(YamlConfigSource.file(resourcePath("basic.yaml")).asObjectSource) - .load[Conf] shouldBe Right(Conf( + .load[Conf] shouldBe Right( + Conf( "bcd", true, BigInt("1234567890123456789012345678901234567890"), @@ -272,6 +327,8 @@ class YamlConfigSourceSuite extends BaseSuite with EitherValues { Set(4, 6, 8), List(10L, 10000L, 10000000L, 10000000000L), Map("a" -> 0.5, "b" -> 3.5, "c" -> 3.5), - InnerConf(42, "def"))) + InnerConf(42, "def") + ) + ) } } diff --git a/tests/src/main/scala/pureconfig/BaseSuite.scala b/tests/src/main/scala/pureconfig/BaseSuite.scala index 7f89506fe..6e23f18ec 100644 --- a/tests/src/main/scala/pureconfig/BaseSuite.scala +++ b/tests/src/main/scala/pureconfig/BaseSuite.scala @@ -6,9 +6,9 @@ import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers class BaseSuite - extends AnyFlatSpec - with ConfigConvertChecks - with Matchers - with ConfigReaderMatchers - with EitherValues - with ScalaCheckDrivenPropertyChecks + extends AnyFlatSpec + with ConfigConvertChecks + with Matchers + with ConfigReaderMatchers + with EitherValues + with ScalaCheckDrivenPropertyChecks diff --git a/tests/src/main/scala/pureconfig/ConfigConvertChecks.scala b/tests/src/main/scala/pureconfig/ConfigConvertChecks.scala index 44830c7bc..452238a3f 100644 --- a/tests/src/main/scala/pureconfig/ConfigConvertChecks.scala +++ b/tests/src/main/scala/pureconfig/ConfigConvertChecks.scala @@ -3,7 +3,7 @@ package pureconfig import scala.reflect.ClassTag import scala.reflect.runtime.universe._ -import com.typesafe.config.{ ConfigRenderOptions, ConfigValue, ConfigValueFactory } +import com.typesafe.config.{ConfigRenderOptions, ConfigValue, ConfigValueFactory} import org.scalacheck.Arbitrary import org.scalactic.Equality import org.scalatest.EitherValues @@ -14,51 +14,63 @@ import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers /** - * Add utilities to a scalatest `FlatSpec` to test `ConfigConvert` instances - */ + * Add utilities to a scalatest `FlatSpec` to test `ConfigConvert` instances + */ trait ConfigConvertChecks { this: AnyFlatSpec with Matchers with ScalaCheckDrivenPropertyChecks with EitherValues => /** - * For each value of type `A`, check that the value produced by converting to and then from `ConfigValue` is the same - * of the original value - * - * Note that this method doesn't check all the values but only the values that can be created by `Arbitrary[A]` and - * only the `ConfigValue` created by `ConfigConvert[A].to`. While `Arbitrary[A]` is usually comprehensive, - * `ConfigConvert[A].from` could support different kind of values that `ConfigConvert[A].to` doesn't produce - * because, for instance, multiple representation of `a: A` are possible. Use [[checkRead]] for those representations. - */ - def checkArbitrary[A](implicit cc: Derivation[ConfigConvert[A]], arb: Arbitrary[A], tpe: TypeTag[A], equality: Equality[A]): Unit = + * For each value of type `A`, check that the value produced by converting to and then from `ConfigValue` is the same + * of the original value + * + * Note that this method doesn't check all the values but only the values that can be created by `Arbitrary[A]` and + * only the `ConfigValue` created by `ConfigConvert[A].to`. While `Arbitrary[A]` is usually comprehensive, + * `ConfigConvert[A].from` could support different kind of values that `ConfigConvert[A].to` doesn't produce + * because, for instance, multiple representation of `a: A` are possible. Use [[checkRead]] for those representations. + */ + def checkArbitrary[A](implicit + cc: Derivation[ConfigConvert[A]], + arb: Arbitrary[A], + tpe: TypeTag[A], + equality: Equality[A] + ): Unit = it should s"read an arbitrary ${tpe.tpe}" in forAll { a: A => cc.value.from(cc.value.to(a)).right.value shouldEqual a } /** - * A more generic version of [[checkArbitrary]] where the type which will be written as `ConfigValue` is - * different from the type which will be read from that `ConfigValue`. The idea being is to test the reading - * part of a `ConfigConvert` by providing another type for which it's easy to create `Arbitrary` instances - * and write the values to a configuration. - * - * For instance, to test that `Double` can be read from percentages, like `"42 %"`, we can create a dummy - * [[pureconfig.data.Percentage]] class which contains an integer from `0` to `100`, write that percentage to - * a `ConfigValue` representing a `String` and then try to read the percentage from the `ConfigValue` via - * `ConfigConvert[Double].from`. Creating an instance of `Arbitrary[Percentage]` is simple, same for - * `ConfigConvert[Percentage]`. - * - * @param f a function used to convert a value of type `T2` to a value of type `T1`. The result of the conversion - * to and from a `ConfigValue` will be tested against the output of this function. - * @param cr the `ConfigConvert` used to read a value from a `ConfigValue`. This is the instance that we want to test - * @param cw the `ConfigConvert` used to write a value to a `ConfigValue`. This is the dummy instance used to test `cr` - * @param arb the `Arbitrary` used to generate values to write a `ConfigValue` via `cw` - */ - def checkArbitrary2[A, B](f: B => A)(implicit cr: ConfigConvert[A], cw: ConfigConvert[B], arb: Arbitrary[B], tpe1: TypeTag[A], tpe2: TypeTag[B], equality: Equality[A]): Unit = + * A more generic version of [[checkArbitrary]] where the type which will be written as `ConfigValue` is + * different from the type which will be read from that `ConfigValue`. The idea being is to test the reading + * part of a `ConfigConvert` by providing another type for which it's easy to create `Arbitrary` instances + * and write the values to a configuration. + * + * For instance, to test that `Double` can be read from percentages, like `"42 %"`, we can create a dummy + * [[pureconfig.data.Percentage]] class which contains an integer from `0` to `100`, write that percentage to + * a `ConfigValue` representing a `String` and then try to read the percentage from the `ConfigValue` via + * `ConfigConvert[Double].from`. Creating an instance of `Arbitrary[Percentage]` is simple, same for + * `ConfigConvert[Percentage]`. + * + * @param f a function used to convert a value of type `T2` to a value of type `T1`. The result of the conversion + * to and from a `ConfigValue` will be tested against the output of this function. + * @param cr the `ConfigConvert` used to read a value from a `ConfigValue`. This is the instance that we want to test + * @param cw the `ConfigConvert` used to write a value to a `ConfigValue`. This is the dummy instance used to test `cr` + * @param arb the `Arbitrary` used to generate values to write a `ConfigValue` via `cw` + */ + def checkArbitrary2[A, B](f: B => A)(implicit + cr: ConfigConvert[A], + cw: ConfigConvert[B], + arb: Arbitrary[B], + tpe1: TypeTag[A], + tpe2: TypeTag[B], + equality: Equality[A] + ): Unit = it should s"read a ${tpe1.tpe} from an arbitrary ${tpe2.tpe}" in forAll { b: B => cr.from(cw.to(b)).right.value shouldEqual f(b) } /** - * For each pair of value of type `A` and `ConfigValue`, check that `ConfigReader[A].from` - * successfully converts the latter into to former. Useful to test specific values - */ + * For each pair of value of type `A` and `ConfigValue`, check that `ConfigReader[A].from` + * successfully converts the latter into to former. Useful to test specific values + */ def checkRead[A: Equality](reprsToValues: (ConfigValue, A)*)(implicit cr: ConfigReader[A], tpe: TypeTag[A]): Unit = for ((repr, value) <- reprsToValues) { it should s"read the value $value of type ${tpe.tpe} from ${repr.render(ConfigRenderOptions.concise())}" in { @@ -71,9 +83,9 @@ trait ConfigConvertChecks { this: AnyFlatSpec with Matchers with ScalaCheckDrive checkRead[A](strsToValues.map { case (s, a) => ConfigValueFactory.fromAnyRef(s) -> a }: _*) /** - * For each pair of value of type `A` and `ConfigValue`, check that `ConfigWriter[A].to` - * successfully converts the former into the latter. Useful to test specific values - */ + * For each pair of value of type `A` and `ConfigValue`, check that `ConfigWriter[A].to` + * successfully converts the former into the latter. Useful to test specific values + */ def checkWrite[A: Equality](valuesToReprs: (A, ConfigValue)*)(implicit cw: ConfigWriter[A], tpe: TypeTag[A]): Unit = for ((value, repr) <- valuesToReprs) { it should s"write the value $value of type ${tpe.tpe} to ${repr.render(ConfigRenderOptions.concise())}" in { @@ -86,10 +98,10 @@ trait ConfigConvertChecks { this: AnyFlatSpec with Matchers with ScalaCheckDrive checkWrite[A](valuesToStrs.map { case (a, s) => a -> ConfigValueFactory.fromAnyRef(s) }: _*) /** - * For each pair of value of type `A` and `ConfigValue`, check that `ConfigReader[A].from` - * successfully converts the latter into to former and `ConfigWriter[A].to` successfully converts the former into the - * latter. - */ + * For each pair of value of type `A` and `ConfigValue`, check that `ConfigReader[A].from` + * successfully converts the latter into to former and `ConfigWriter[A].to` successfully converts the former into the + * latter. + */ def checkReadWrite[A: ConfigReader: ConfigWriter: TypeTag: Equality](reprsValues: (ConfigValue, A)*): Unit = { checkRead[A](reprsValues: _*) checkWrite[A](reprsValues.map(_.swap): _*) @@ -102,31 +114,35 @@ trait ConfigConvertChecks { this: AnyFlatSpec with Matchers with ScalaCheckDrive } /** - * Check that `cc` returns error of type `E` when trying to read each value passed with `values` - * - * @param values the values that should not be conver - * @param cr the `ConfigConvert` to test - */ - def checkFailure[A, E <: FailureReason](values: ConfigValue*)(implicit cr: ConfigReader[A], tpe: TypeTag[A], eTag: ClassTag[E]): Unit = + * Check that `cc` returns error of type `E` when trying to read each value passed with `values` + * + * @param values the values that should not be conver + * @param cr the `ConfigConvert` to test + */ + def checkFailure[A, E <: FailureReason]( + values: ConfigValue* + )(implicit cr: ConfigReader[A], tpe: TypeTag[A], eTag: ClassTag[E]): Unit = for (value <- values) { it should s"fail when it tries to read a value of type ${tpe.tpe} " + s"from ${value.render(ConfigRenderOptions.concise())}" in { - val result = cr.from(value) - result.left.value.toList should have size 1 - result.left.value.head should matchPattern { case ConvertFailure(_: E, _, _) => } - } + val result = cr.from(value) + result.left.value.toList should have size 1 + result.left.value.head should matchPattern { case ConvertFailure(_: E, _, _) => } + } } /** - * For each pair of `ConfigValue` and `ConfigReaderFailures`, check that `cr` - * fails with the provided errors when trying to read the provided - * `ConfigValue`. - */ - def checkFailures[A](valuesToErrors: (ConfigValue, ConfigReaderFailures)*)(implicit cr: ConfigReader[A], tpe: TypeTag[A]): Unit = + * For each pair of `ConfigValue` and `ConfigReaderFailures`, check that `cr` + * fails with the provided errors when trying to read the provided + * `ConfigValue`. + */ + def checkFailures[A]( + valuesToErrors: (ConfigValue, ConfigReaderFailures)* + )(implicit cr: ConfigReader[A], tpe: TypeTag[A]): Unit = for ((value, errors) <- valuesToErrors) { it should s"fail when it tries to read a value of type ${tpe.tpe} " + s"from ${value.render(ConfigRenderOptions.concise())}" in { - cr.from(value).left.value.toList should contain theSameElementsAs errors.toList - } + cr.from(value).left.value.toList should contain theSameElementsAs errors.toList + } } } diff --git a/tests/src/main/scala/pureconfig/ConfigReaderMatchers.scala b/tests/src/main/scala/pureconfig/ConfigReaderMatchers.scala index 23212e7a0..58d9b9098 100644 --- a/tests/src/main/scala/pureconfig/ConfigReaderMatchers.scala +++ b/tests/src/main/scala/pureconfig/ConfigReaderMatchers.scala @@ -2,11 +2,11 @@ package pureconfig import java.net.URL -import com.typesafe.config.{ ConfigOrigin, ConfigOriginFactory } +import com.typesafe.config.{ConfigOrigin, ConfigOriginFactory} import scala.reflect.ClassTag import org.scalatest._ -import org.scalatest.matchers.{ MatchResult, Matcher } +import org.scalatest.matchers.{MatchResult, Matcher} import pureconfig.error._ import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers @@ -17,9 +17,10 @@ trait ConfigReaderMatchers { this: AnyFlatSpec with Matchers => matchPattern { case Left(ConfigReaderFailures(ConvertFailure(`reason`, _, _))) => } def failWith( - reason: FailureReason, - path: String, - origin: Option[ConfigOrigin] = None): Matcher[ConfigReader.Result[Any]] = + reason: FailureReason, + path: String, + origin: Option[ConfigOrigin] = None + ): Matcher[ConfigReader.Result[Any]] = be(Left(ConfigReaderFailures(ConvertFailure(reason, origin, path)))) def failWith(failure: ConfigReaderFailure): Matcher[ConfigReader.Result[Any]] = @@ -28,7 +29,9 @@ trait ConfigReaderMatchers { this: AnyFlatSpec with Matchers => def failWithType[Reason <: FailureReason: ClassTag]: Matcher[ConfigReader.Result[Any]] = matchPattern { case Left(ConfigReaderFailures(ConvertFailure(_: Reason, _, _))) => } - def failWithType[Failure <: ConfigReaderFailure: ClassTag](implicit dummy: DummyImplicit): Matcher[ConfigReader.Result[Any]] = + def failWithType[Failure <: ConfigReaderFailure: ClassTag](implicit + dummy: DummyImplicit + ): Matcher[ConfigReader.Result[Any]] = matchPattern { case Left(ConfigReaderFailures(_: Failure)) => } def failLike(pf: PartialFunction[ConfigReaderFailure, MatchResult]) = diff --git a/tests/src/main/scala/pureconfig/PathUtils.scala b/tests/src/main/scala/pureconfig/PathUtils.scala index 25a41ddd9..6e6840a6f 100644 --- a/tests/src/main/scala/pureconfig/PathUtils.scala +++ b/tests/src/main/scala/pureconfig/PathUtils.scala @@ -1,7 +1,7 @@ package pureconfig import java.nio.charset.StandardCharsets -import java.nio.file.{ Files, Path, Paths } +import java.nio.file.{Files, Path, Paths} object PathUtils { diff --git a/tests/src/main/scala/pureconfig/arbitrary/package.scala b/tests/src/main/scala/pureconfig/arbitrary/package.scala index 61a1a4ff2..edd26b1aa 100644 --- a/tests/src/main/scala/pureconfig/arbitrary/package.scala +++ b/tests/src/main/scala/pureconfig/arbitrary/package.scala @@ -1,6 +1,6 @@ package pureconfig -import org.scalacheck.{ Arbitrary, Gen } +import org.scalacheck.{Arbitrary, Gen} import pureconfig.gen._ package object arbitrary { diff --git a/tests/src/main/scala/pureconfig/data/Percentage.scala b/tests/src/main/scala/pureconfig/data/Percentage.scala index 0dbab2045..2f04c1473 100644 --- a/tests/src/main/scala/pureconfig/data/Percentage.scala +++ b/tests/src/main/scala/pureconfig/data/Percentage.scala @@ -4,7 +4,7 @@ import pureconfig.ConfigConvert import pureconfig.error.CannotConvert final case class Percentage(value: Int) { - def toDoubleFraction: Double = value.toDouble / 100D + def toDoubleFraction: Double = value.toDouble / 100d def toFloatFraction: Float = value.toFloat / 100f } diff --git a/tests/src/main/scala/pureconfig/equality/package.scala b/tests/src/main/scala/pureconfig/equality/package.scala index 6f05c1d91..572d8e53e 100644 --- a/tests/src/main/scala/pureconfig/equality/package.scala +++ b/tests/src/main/scala/pureconfig/equality/package.scala @@ -10,17 +10,19 @@ import org.scalactic.TypeCheckedTripleEquals._ package object equality { implicit final val PatternEquality = new Equality[Pattern] { - def areEqual(a: Pattern, b: Any): Boolean = b match { - case bp: Pattern => a.pattern === bp.pattern - case _ => false - } + def areEqual(a: Pattern, b: Any): Boolean = + b match { + case bp: Pattern => a.pattern === bp.pattern + case _ => false + } } implicit final val RegexEquality = new Equality[Regex] { - override def areEqual(a: Regex, b: Any): Boolean = b match { - case r: Regex => PatternEquality.areEqual(a.pattern, r.pattern) - case _ => false - } + override def areEqual(a: Regex, b: Any): Boolean = + b match { + case r: Regex => PatternEquality.areEqual(a.pattern, r.pattern) + case _ => false + } } } diff --git a/tests/src/main/scala/pureconfig/gen/package.scala b/tests/src/main/scala/pureconfig/gen/package.scala index 174aac234..949979385 100644 --- a/tests/src/main/scala/pureconfig/gen/package.scala +++ b/tests/src/main/scala/pureconfig/gen/package.scala @@ -1,19 +1,20 @@ package pureconfig import java.io.File -import java.math.{ BigInteger, BigDecimal => JavaBigDecimal } -import java.nio.file.{ Path, Paths } +import java.math.{BigInteger, BigDecimal => JavaBigDecimal} +import java.nio.file.{Path, Paths} import java.time._ -import java.time.{ Duration => JavaDuration } +import java.time.{Duration => JavaDuration} -import org.scalacheck.{ Arbitrary, Gen } +import org.scalacheck.{Arbitrary, Gen} import pureconfig.data._ import scala.collection.JavaConverters._ -import scala.concurrent.duration.{ Duration, FiniteDuration } +import scala.concurrent.duration.{Duration, FiniteDuration} package object gen { val genFiniteDuration: Gen[FiniteDuration] = - Gen.choose(Long.MinValue + 1, Long.MaxValue) + Gen + .choose(Long.MinValue + 1, Long.MaxValue) .suchThat(_ != 8092048641075763L) // doesn't work, see #182 .map(Duration.fromNanos) @@ -29,9 +30,7 @@ package object gen { } yield JavaDuration.ofSeconds(seconds, nanoseconds) val genDuration: Gen[Duration] = - Gen.frequency( - 1 -> Gen.oneOf(Duration.Inf, Duration.MinusInf, Duration.Undefined), - 99 -> genFiniteDuration) + Gen.frequency(1 -> Gen.oneOf(Duration.Inf, Duration.MinusInf, Duration.Undefined), 99 -> genFiniteDuration) val genInstant: Gen[Instant] = Gen.choose(Instant.MIN.getEpochSecond, Instant.MAX.getEpochSecond).map(Instant.ofEpochSecond) diff --git a/tests/src/test/scala/pureconfig/ApiSuite.scala b/tests/src/test/scala/pureconfig/ApiSuite.scala index 21d66dea1..01b967415 100644 --- a/tests/src/test/scala/pureconfig/ApiSuite.scala +++ b/tests/src/test/scala/pureconfig/ApiSuite.scala @@ -6,7 +6,7 @@ package pureconfig import java.nio.file.Path import java.util.concurrent.TimeUnit -import com.typesafe.config.{ ConfigFactory, ConfigValueType } +import com.typesafe.config.{ConfigFactory, ConfigValueType} import pureconfig.PathUtils._ import scala.concurrent.duration.FiniteDuration @@ -23,69 +23,100 @@ class ApiSuite extends BaseSuite { it should "loadConfig from reference.conf" in { case class Conf(d: Double, i: Int, s: String) - loadConfig[Conf] shouldBe Right(Conf(0D, 0, "app_value")) + loadConfig[Conf] shouldBe Right(Conf(0d, 0, "app_value")) } it should "loadConfig from reference.conf with a namespace" in { case class Conf(f: Float) - loadConfig[Conf](namespace = "foo") shouldBe Right(Conf(3.0F)) + loadConfig[Conf](namespace = "foo") shouldBe Right(Conf(3.0f)) } it should "loadConfig config objects from a Typesafe Config" in { case class Conf(d: Double, i: Int) val conf = ConfigFactory.parseString("{ d: 0.5, i: 10 }") - loadConfig[Conf](conf = conf) shouldBe Right(Conf(0.5D, 10)) + loadConfig[Conf](conf = conf) shouldBe Right(Conf(0.5d, 10)) } it should "loadConfig config objects from a Typesafe Config with a namespace" in { case class Conf(f: Float) val conf = ConfigFactory.parseString("foo.bar { f: 1.0 }") - loadConfig[Conf](conf = conf, namespace = "foo.bar") shouldBe Right(Conf(1.0F)) - loadConfig[Conf](conf = conf, namespace = "bar.foo") should failWith(KeyNotFound("bar", Set.empty), "", stringConfigOrigin(1)) + loadConfig[Conf](conf = conf, namespace = "foo.bar") shouldBe Right(Conf(1.0f)) + loadConfig[Conf](conf = conf, namespace = "bar.foo") should failWith( + KeyNotFound("bar", Set.empty), + "", + stringConfigOrigin(1) + ) } it should "loadConfig other values from a Typesafe Config with a namespace" in { val conf = ConfigFactory.parseString("foo { bar { f: 1.0 }, baz: 3.4 }") - loadConfig[Float](conf = conf, namespace = "foo.bar.f") shouldBe Right(1.0F) + loadConfig[Float](conf = conf, namespace = "foo.bar.f") shouldBe Right(1.0f) loadConfig[Float](conf = conf, namespace = "foo.bar.h") should failWith( - KeyNotFound("h", Set.empty), "foo.bar", stringConfigOrigin(1)) + KeyNotFound("h", Set.empty), + "foo.bar", + stringConfigOrigin(1) + ) loadConfig[Float](conf = conf, namespace = "foo.baz.f") should failWith( - WrongType(ConfigValueType.NUMBER, Set(ConfigValueType.OBJECT)), "foo.baz", stringConfigOrigin(1)) + WrongType(ConfigValueType.NUMBER, Set(ConfigValueType.OBJECT)), + "foo.baz", + stringConfigOrigin(1) + ) loadConfig[Float](conf = conf, namespace = "bar.foo.f") should failWith( - KeyNotFound("bar", Set.empty), "", stringConfigOrigin(1)) + KeyNotFound("bar", Set.empty), + "", + stringConfigOrigin(1) + ) - loadConfig[Option[Float]](conf = conf, namespace = "foo.bar.f") shouldBe Right(Some(1.0F)) + loadConfig[Option[Float]](conf = conf, namespace = "foo.bar.f") shouldBe Right(Some(1.0f)) // With the introduction of `ConfigSource`s we dropped support for reading missing // keys as `None` when an `Option[A]` is loaded as a root value // loadConfig[Option[Float]](conf = conf, namespace = "foo.bar.h") shouldBe Right(None) loadConfig[Option[Float]](conf = conf, namespace = "foo.bar.h") should failWith( - KeyNotFound("h", Set.empty), "foo.bar", stringConfigOrigin(1)) + KeyNotFound("h", Set.empty), + "foo.bar", + stringConfigOrigin(1) + ) loadConfig[Option[Float]](conf = conf, namespace = "foo.baz.f") should failWith( - WrongType(ConfigValueType.NUMBER, Set(ConfigValueType.OBJECT)), "foo.baz", stringConfigOrigin(1)) + WrongType(ConfigValueType.NUMBER, Set(ConfigValueType.OBJECT)), + "foo.baz", + stringConfigOrigin(1) + ) loadConfig[Option[Float]](conf = conf, namespace = "bar.foo.f") should failWith( - KeyNotFound("bar", Set.empty), "", stringConfigOrigin(1)) + KeyNotFound("bar", Set.empty), + "", + stringConfigOrigin(1) + ) } it should "handle correctly namespaces with special chars" in { val conf = ConfigFactory.parseString(""" "fo.o" { "ba r" { f: 1.0 }, "ba z": 3.4 }""") - loadConfig[Float](conf = conf, namespace = "\"fo.o\".\"ba r\".f") shouldBe Right(1.0F) + loadConfig[Float](conf = conf, namespace = "\"fo.o\".\"ba r\".f") shouldBe Right(1.0f) loadConfig[Float](conf = conf, namespace = "\"fo.o\".\"ba r\".h") should failWith( - KeyNotFound("h", Set.empty), "\"fo.o\".\"ba r\"", stringConfigOrigin(1)) + KeyNotFound("h", Set.empty), + "\"fo.o\".\"ba r\"", + stringConfigOrigin(1) + ) loadConfig[Float](conf = conf, namespace = "\"fo.o\".\"ba z\".h") should failWith( - WrongType(ConfigValueType.NUMBER, Set(ConfigValueType.OBJECT)), "\"fo.o\".\"ba z\"", stringConfigOrigin(1)) + WrongType(ConfigValueType.NUMBER, Set(ConfigValueType.OBJECT)), + "\"fo.o\".\"ba z\"", + stringConfigOrigin(1) + ) loadConfig[Float](conf = conf, namespace = "\"b.a.r\".foo.f") should failWith( - KeyNotFound("b.a.r", Set.empty), "", stringConfigOrigin(1)) + KeyNotFound("b.a.r", Set.empty), + "", + stringConfigOrigin(1) + ) } it should "loadConfig from a configuration file" in { @@ -113,10 +144,17 @@ class ApiSuite extends BaseSuite { case class SparkAppConf(name: String) case class SparkLocalConf(dir: String) case class SparkNetwork(timeout: FiniteDuration) - case class SparkConf(master: String, app: SparkAppConf, local: SparkLocalConf, driver: DriverConf, executor: ExecutorConf, extraListeners: Seq[String], network: SparkNetwork) + case class SparkConf( + master: String, + app: SparkAppConf, + local: SparkLocalConf, + driver: DriverConf, + executor: ExecutorConf, + extraListeners: Seq[String], + network: SparkNetwork + ) case class SparkRootConf(spark: SparkConf) - val configFile = createTempFile( - """spark { + val configFile = createTempFile("""spark { | app.name="myApp" | master="local[*]" | driver { @@ -159,13 +197,14 @@ class ApiSuite extends BaseSuite { "loadConfigFromFiles" should "load a complete configuration from a single file" in { case class Conf(b: Boolean, d: Double) val files = listResourcesFromNames("/conf/loadConfigFromFiles/priority2.conf") - loadConfigFromFiles[Conf](files) shouldBe Right(Conf(false, 0.001D)) + loadConfigFromFiles[Conf](files) shouldBe Right(Conf(false, 0.001d)) } it should "fill in missing values from the lower priority files" in { case class Conf(f: Float) - val files = listResourcesFromNames("/conf/loadConfigFromFiles/priority1.conf", "/conf/loadConfigFromFiles/priority2.conf") - loadConfigFromFiles[Conf](files) shouldBe Right(Conf(0.99F)) + val files = + listResourcesFromNames("/conf/loadConfigFromFiles/priority1.conf", "/conf/loadConfigFromFiles/priority2.conf") + loadConfigFromFiles[Conf](files) shouldBe Right(Conf(0.99f)) } it should "use an empty config if the list of files is empty" in { @@ -177,13 +216,13 @@ class ApiSuite extends BaseSuite { it should "merge reference.conf with the provided files" in { case class Conf(b: Boolean, d: Double, sref: String) // sref defined in reference.conf val files = listResourcesFromNames("/conf/loadConfigFromFiles/priority2.conf") - loadConfigFromFiles[Conf](files) shouldBe Right(Conf(false, 0.001D, "wow")) + loadConfigFromFiles[Conf](files) shouldBe Right(Conf(false, 0.001d, "wow")) } it should "ignore files that don't exist when failOnReadError is false" in { case class Conf(b: Boolean, d: Double) val files = listResourcesFromNames("/conf/loadConfigFromFiles/priority2.conf") :+ nonExistingPath - loadConfigFromFiles[Conf](files) shouldBe Right(Conf(false, 0.001D)) + loadConfigFromFiles[Conf](files) shouldBe Right(Conf(false, 0.001d)) } it should "fail if any of the files doesn't exist and failOnReadError is true" in { @@ -195,13 +234,13 @@ class ApiSuite extends BaseSuite { it should "use a namespace if given" in { case class Conf(f: Float) val files = listResourcesFromNames("/conf/loadConfigFromFiles/outerobject.conf") - loadConfigFromFiles[Conf](files, namespace = "foo") shouldBe Right(Conf(3.0F)) + loadConfigFromFiles[Conf](files, namespace = "foo") shouldBe Right(Conf(3.0f)) } "loadConfigWithFallback" should "fallback if no config keys are found" in { case class Conf(f: Float, o: Option[Int], d: Double) val priority1Conf = ConfigFactory.load("conf/loadConfigFromFiles/priority1.conf") // `f` and `o` are defined in priority1.conf, `d` is defined in reference.conf - loadConfigWithFallback[Conf](priority1Conf) shouldBe Right(Conf(0.99F, None, 0.0)) + loadConfigWithFallback[Conf](priority1Conf) shouldBe Right(Conf(0.99f, None, 0.0)) } } diff --git a/tests/src/test/scala/pureconfig/BasicConvertersSuite.scala b/tests/src/test/scala/pureconfig/BasicConvertersSuite.scala index bd3c899a8..409a7e9e6 100644 --- a/tests/src/test/scala/pureconfig/BasicConvertersSuite.scala +++ b/tests/src/test/scala/pureconfig/BasicConvertersSuite.scala @@ -1,11 +1,11 @@ package pureconfig import java.io.File -import java.math.{ BigInteger, BigDecimal => JavaBigDecimal } -import java.net.{ URI, URL } +import java.math.{BigInteger, BigDecimal => JavaBigDecimal} +import java.net.{URI, URL} import java.nio.file.Path import java.time._ -import java.time.{ Duration => JavaDuration } +import java.time.{Duration => JavaDuration} import java.util.UUID import java.util.regex.Pattern @@ -17,7 +17,7 @@ import pureconfig.error._ import pureconfig.generic.auto._ import scala.collection.JavaConverters._ import scala.collection.immutable -import scala.concurrent.duration.{ Duration, FiniteDuration, _ } +import scala.concurrent.duration.{Duration, FiniteDuration, _} import scala.util.matching.Regex class BasicConvertersSuite extends BaseSuite { @@ -29,22 +29,29 @@ class BasicConvertersSuite extends BaseSuite { checkFailure[Duration, EmptyStringFound](ConfigValueFactory.fromAnyRef("")) checkFailures[Duration]( ConfigValueFactory.fromIterable(List(1).asJava) -> ConfigReaderFailures( - ConvertFailure(WrongType(ConfigValueType.LIST, Set(ConfigValueType.STRING)), emptyConfigOrigin, ""))) + ConvertFailure(WrongType(ConfigValueType.LIST, Set(ConfigValueType.STRING)), emptyConfigOrigin, "") + ) + ) checkArbitrary[JavaDuration] checkFailure[JavaDuration, EmptyStringFound](ConfigValueFactory.fromAnyRef("")) checkFailures[JavaDuration]( ConfigValueFactory.fromIterable(List(1).asJava) -> ConfigReaderFailures( - ConvertFailure(WrongType(ConfigValueType.LIST, Set(ConfigValueType.STRING)), emptyConfigOrigin, ""))) + ConvertFailure(WrongType(ConfigValueType.LIST, Set(ConfigValueType.STRING)), emptyConfigOrigin, "") + ) + ) checkArbitrary[FiniteDuration] checkFailure[FiniteDuration, EmptyStringFound](ConfigValueFactory.fromAnyRef("")) checkFailures[FiniteDuration]( ConfigValueFactory.fromIterable(List(1).asJava) -> ConfigReaderFailures( - ConvertFailure(WrongType(ConfigValueType.LIST, Set(ConfigValueType.STRING)), emptyConfigOrigin, ""))) + ConvertFailure(WrongType(ConfigValueType.LIST, Set(ConfigValueType.STRING)), emptyConfigOrigin, "") + ) + ) checkFailure[FiniteDuration, CannotConvert]( ConfigConvert[Duration].to(Duration.MinusInf), - ConfigConvert[Duration].to(Duration.Inf)) + ConfigConvert[Duration].to(Duration.Inf) + ) checkReadString[FiniteDuration]( "5 seconds" -> 5.seconds, @@ -52,10 +59,10 @@ class BasicConvertersSuite extends BaseSuite { "28ms" -> 28.millis, "28" -> 28.millis, "28 milliseconds" -> 28.millis, - "1d" -> 1.day) + "1d" -> 1.day + ) - checkRead[FiniteDuration]( - ConfigValueFactory.fromAnyRef(28) -> 28.millis) + checkRead[FiniteDuration](ConfigValueFactory.fromAnyRef(28) -> 28.millis) checkArbitrary[Instant] @@ -70,14 +77,12 @@ class BasicConvertersSuite extends BaseSuite { "42" -> Period.ofDays(42), "4 weeks" -> Period.ofWeeks(4), "13 months" -> Period.ofMonths(13), - "2y" -> Period.ofYears(2)) + "2y" -> Period.ofYears(2) + ) - checkRead[Period]( - ConfigValueFactory.fromAnyRef(42) -> Period.ofDays(42)) + checkRead[Period](ConfigValueFactory.fromAnyRef(42) -> Period.ofDays(42)) - checkFailure[Period, CannotConvert]( - ConfigValueFactory.fromAnyRef("4kb"), - ConfigValueFactory.fromAnyRef("x weeks")) + checkFailure[Period, CannotConvert](ConfigValueFactory.fromAnyRef("4kb"), ConfigValueFactory.fromAnyRef("x weeks")) checkArbitrary[Year] @@ -92,23 +97,30 @@ class BasicConvertersSuite extends BaseSuite { ConfigValueFactory.fromAnyRef("yes") -> true, ConfigValueFactory.fromAnyRef("on") -> true, ConfigValueFactory.fromAnyRef("no") -> false, - ConfigValueFactory.fromAnyRef("off") -> false) + ConfigValueFactory.fromAnyRef("off") -> false + ) checkArbitrary[Double] checkArbitrary2[Double, Percentage](_.toDoubleFraction) checkFailures[Double]( ConfigValueFactory.fromAnyRef("") -> ConfigReaderFailures( - ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER)), emptyConfigOrigin, "")), + ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER)), emptyConfigOrigin, "") + ), ConfigValueFactory.fromIterable(List(1, 2, 3, 4).asJava) -> ConfigReaderFailures( - ConvertFailure(WrongType(ConfigValueType.LIST, Set(ConfigValueType.NUMBER)), emptyConfigOrigin, ""))) + ConvertFailure(WrongType(ConfigValueType.LIST, Set(ConfigValueType.NUMBER)), emptyConfigOrigin, "") + ) + ) checkArbitrary[Float] checkArbitrary2[Float, Percentage](_.toFloatFraction) checkFailures[Float]( ConfigValueFactory.fromAnyRef("") -> ConfigReaderFailures( - ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER)), emptyConfigOrigin, "")), + ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER)), emptyConfigOrigin, "") + ), ConfigValueFactory.fromIterable(List(1, 2, 3, 4).asJava) -> ConfigReaderFailures( - ConvertFailure(WrongType(ConfigValueType.LIST, Set(ConfigValueType.NUMBER)), emptyConfigOrigin, ""))) + ConvertFailure(WrongType(ConfigValueType.LIST, Set(ConfigValueType.NUMBER)), emptyConfigOrigin, "") + ) + ) checkArbitrary[Int] @@ -133,7 +145,8 @@ class BasicConvertersSuite extends BaseSuite { checkReadWriteString[Month]("JULY" -> Month.JULY) checkFailure[DayOfWeek, CannotConvert]( ConfigValueFactory.fromAnyRef("thursday"), // lowercase string vs upper case enum - ConfigValueFactory.fromAnyRef("this is not a day")) // no such value + ConfigValueFactory.fromAnyRef("this is not a day") + ) // no such value checkReadWriteString[Pattern]("(a|b)" -> Pattern.compile("(a|b)")) checkFailure[Pattern, CannotConvert](ConfigValueFactory.fromAnyRef("(a|b")) // missing closing ')' @@ -141,40 +154,39 @@ class BasicConvertersSuite extends BaseSuite { checkReadWriteString[Regex]("(a|b)" -> new Regex("(a|b)")) checkFailure[Regex, CannotConvert](ConfigValueFactory.fromAnyRef("(a|b")) // missing closing ')' - checkReadWriteString[URL]( - "http://host/path?with=query¶m" -> new URL("http://host/path?with=query¶m")) + checkReadWriteString[URL]("http://host/path?with=query¶m" -> new URL("http://host/path?with=query¶m")) - checkReadWriteString[URI]( - "http://host/path?with=query¶m" -> new URI("http://host/path?with=query¶m")) + checkReadWriteString[URI]("http://host/path?with=query¶m" -> new URI("http://host/path?with=query¶m")) checkReadWrite[ConfigList]( ConfigValueFactory.fromIterable(List().asJava) -> ConfigValueFactory.fromIterable(List().asJava), - ConfigValueFactory.fromIterable(List(1, 2, 3).asJava) -> ConfigValueFactory.fromIterable(List(1, 2, 3).asJava)) + ConfigValueFactory.fromIterable(List(1, 2, 3).asJava) -> ConfigValueFactory.fromIterable(List(1, 2, 3).asJava) + ) checkReadWrite[ConfigValue]( ConfigValueFactory.fromAnyRef(4) -> ConfigValueFactory.fromAnyRef(4), ConfigValueFactory.fromAnyRef("str") -> ConfigValueFactory.fromAnyRef("str"), - ConfigValueFactory.fromAnyRef(List(1, 2, 3).asJava) -> ConfigValueFactory.fromAnyRef(List(1, 2, 3).asJava)) + ConfigValueFactory.fromAnyRef(List(1, 2, 3).asJava) -> ConfigValueFactory.fromAnyRef(List(1, 2, 3).asJava) + ) - checkReadWrite[ConfigMemorySize]( - ConfigValueFactory.fromAnyRef(400L) -> ConfigMemorySize.ofBytes(400L)) + checkReadWrite[ConfigMemorySize](ConfigValueFactory.fromAnyRef(400L) -> ConfigMemorySize.ofBytes(400L)) checkRead[ConfigMemorySize]( ConfigValueFactory.fromAnyRef("400b") -> ConfigMemorySize.ofBytes(400), ConfigValueFactory.fromAnyRef("400k") -> ConfigMemorySize.ofBytes(400 * 1024), ConfigValueFactory.fromAnyRef("400m") -> ConfigMemorySize.ofBytes(400 * 1024 * 1024), - ConfigValueFactory.fromAnyRef("400MB") -> ConfigMemorySize.ofBytes(400 * 1000 * 1000)) + ConfigValueFactory.fromAnyRef("400MB") -> ConfigMemorySize.ofBytes(400 * 1000 * 1000) + ) checkWrite[ConfigMemorySize]( ConfigMemorySize.ofBytes(400 * 1024 * 1024) -> ConfigValueFactory.fromAnyRef(400 * 1024 * 1024), - ConfigMemorySize.ofBytes(400 * 1000 * 1000) -> ConfigValueFactory.fromAnyRef(400 * 1000 * 1000)) + ConfigMemorySize.ofBytes(400 * 1000 * 1000) -> ConfigValueFactory.fromAnyRef(400 * 1000 * 1000) + ) checkFailure[ConfigMemorySize, ExceptionThrown](ConfigValueFactory.fromAnyRef("100noSuchUnit")) { val conf = ConfigFactory.parseString("""{ v1 = 3, v2 = 4 }""".stripMargin) - checkReadWrite[ConfigObject]( - conf.root() -> ConfigValueFactory.fromMap(Map("v1" -> 3, "v2" -> 4).asJava)) + checkReadWrite[ConfigObject](conf.root() -> ConfigValueFactory.fromMap(Map("v1" -> 3, "v2" -> 4).asJava)) - checkReadWrite[Config]( - conf.root() -> conf) + checkReadWrite[Config](conf.root() -> conf) } } diff --git a/tests/src/test/scala/pureconfig/CollectionConvertersSuite.scala b/tests/src/test/scala/pureconfig/CollectionConvertersSuite.scala index 4debaefdd..85bdc7904 100644 --- a/tests/src/test/scala/pureconfig/CollectionConvertersSuite.scala +++ b/tests/src/test/scala/pureconfig/CollectionConvertersSuite.scala @@ -1,10 +1,10 @@ package pureconfig import scala.collection.JavaConverters._ -import scala.collection.immutable.{ HashSet, ListSet, Queue, TreeSet } +import scala.collection.immutable.{HashSet, ListSet, Queue, TreeSet} -import com.typesafe.config.{ ConfigFactory, ConfigValueFactory, ConfigValueType } -import pureconfig.error.{ ConfigReaderFailures, ConvertFailure, WrongType } +import com.typesafe.config.{ConfigFactory, ConfigValueFactory, ConfigValueType} +import pureconfig.error.{ConfigReaderFailures, ConvertFailure, WrongType} class CollectionConvertersSuite extends BaseSuite { implicit override val generatorDrivenConfig = PropertyCheckConfiguration(minSuccessful = 100) @@ -18,13 +18,17 @@ class CollectionConvertersSuite extends BaseSuite { // order of keys maintained ConfigValueFactory.fromMap(Map("2" -> 1, "0" -> 2, "1" -> 3).asJava) -> List(2, 3, 1), ConfigValueFactory.fromMap(Map("3" -> 2, "1" -> 4).asJava) -> List(4, 2), - ConfigValueFactory.fromMap(Map("1" -> 1, "a" -> 2).asJava) -> List(1)) + ConfigValueFactory.fromMap(Map("1" -> 1, "a" -> 2).asJava) -> List(1) + ) checkFailures[List[Int]]( ConfigValueFactory.fromMap(Map("b" -> 1, "a" -> 2).asJava) -> ConfigReaderFailures( - ConvertFailure(WrongType(ConfigValueType.OBJECT, Set(ConfigValueType.LIST)), emptyConfigOrigin, "")), + ConvertFailure(WrongType(ConfigValueType.OBJECT, Set(ConfigValueType.LIST)), emptyConfigOrigin, "") + ), ConfigValueFactory.fromMap(Map().asJava) -> ConfigReaderFailures( - ConvertFailure(WrongType(ConfigValueType.OBJECT, Set(ConfigValueType.LIST)), emptyConfigOrigin, ""))) + ConvertFailure(WrongType(ConfigValueType.OBJECT, Set(ConfigValueType.LIST)), emptyConfigOrigin, "") + ) + ) checkArbitrary[ListSet[Int]] @@ -32,16 +36,18 @@ class CollectionConvertersSuite extends BaseSuite { checkFailures[Map[String, Int]]( // nested map should fail ConfigFactory.parseString("conf.a=1").root() -> ConfigReaderFailures( - ConvertFailure(WrongType(ConfigValueType.OBJECT, Set(ConfigValueType.NUMBER)), stringConfigOrigin(1), "conf")), + ConvertFailure(WrongType(ConfigValueType.OBJECT, Set(ConfigValueType.NUMBER)), stringConfigOrigin(1), "conf") + ), // wrong value type should fail ConfigFactory.parseString("{ a=b }").root() -> ConfigReaderFailures( - ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER)), stringConfigOrigin(1), "a"))) + ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER)), stringConfigOrigin(1), "a") + ) + ) checkArbitrary[Queue[Boolean]] checkArbitrary[Set[Double]] - checkRead[Set[Int]]( - ConfigValueFactory.fromMap(Map("1" -> 4, "2" -> 5, "3" -> 6).asJava) -> Set(4, 5, 6)) + checkRead[Set[Int]](ConfigValueFactory.fromMap(Map("1" -> 4, "2" -> 5, "3" -> 6).asJava) -> Set(4, 5, 6)) checkArbitrary[Stream[String]] diff --git a/tests/src/test/scala/pureconfig/ConfigConvertSuite.scala b/tests/src/test/scala/pureconfig/ConfigConvertSuite.scala index c70987b2b..46e9c96f6 100644 --- a/tests/src/test/scala/pureconfig/ConfigConvertSuite.scala +++ b/tests/src/test/scala/pureconfig/ConfigConvertSuite.scala @@ -1,8 +1,8 @@ package pureconfig -import com.typesafe.config.{ ConfigValue, ConfigValueFactory, ConfigValueType } -import org.scalacheck.{ Arbitrary, Gen } -import pureconfig.error.{ ExceptionThrown, WrongType, CannotConvert } +import com.typesafe.config.{ConfigValue, ConfigValueFactory, ConfigValueType} +import org.scalacheck.{Arbitrary, Gen} +import pureconfig.error.{ExceptionThrown, WrongType, CannotConvert} import ConfigConvertSuite._ class ConfigConvertSuite extends BaseSuite { @@ -12,7 +12,8 @@ class ConfigConvertSuite extends BaseSuite { // generate configs that always read correctly as strings, but not always as integers val genConfig: Gen[ConfigValue] = - Gen.frequency(80 -> Gen.chooseNum(Int.MinValue, Int.MaxValue), 20 -> Gen.alphaStr) + Gen + .frequency(80 -> Gen.chooseNum(Int.MinValue, Int.MaxValue), 20 -> Gen.alphaStr) .map(ConfigValueFactory.fromAnyRef) implicit val arbConfig = Arbitrary(genConfig) @@ -27,10 +28,10 @@ class ConfigConvertSuite extends BaseSuite { it should "have a xmap method that wraps exceptions in a ConfigReaderFailure" in { val throwable = new Exception("Exception message.") val cc = ConfigConvert[Int].xmap[String]({ _ => throw throwable }, { _: String => 42 }) - cc.from(ConfigValueFactory.fromAnyRef(1)) should failWith( - ExceptionThrown(throwable)) + cc.from(ConfigValueFactory.fromAnyRef(1)) should failWith(ExceptionThrown(throwable)) cc.from(ConfigValueFactory.fromAnyRef("test")) should failWith( - WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER))) + WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER)) + ) } it should "have a xemap method that allows specifying custom failure messages" in { @@ -52,8 +53,6 @@ object ConfigConvertSuite { if (i % 2 == 0) Right(EvenInt(i)) else Left(err(i)) implicit val configConvert: ConfigConvert[EvenInt] = - ConfigConvert[Int].xemap( - i => safely(i).left.map(s => CannotConvert(i.toString, "EvenInt", s)), - _.i) + ConfigConvert[Int].xemap(i => safely(i).left.map(s => CannotConvert(i.toString, "EvenInt", s)), _.i) } } diff --git a/tests/src/test/scala/pureconfig/ConfigCursorSuite.scala b/tests/src/test/scala/pureconfig/ConfigCursorSuite.scala index 16d4a31d4..7e8290dde 100644 --- a/tests/src/test/scala/pureconfig/ConfigCursorSuite.scala +++ b/tests/src/test/scala/pureconfig/ConfigCursorSuite.scala @@ -1,7 +1,7 @@ package pureconfig import com.typesafe.config._ -import pureconfig.error.{ CannotConvert, ConvertFailure, KeyNotFound, WrongType } +import pureconfig.error.{CannotConvert, ConvertFailure, KeyNotFound, WrongType} class ConfigCursorSuite extends BaseSuite { @@ -28,91 +28,91 @@ class ConfigCursorSuite extends BaseSuite { cursor("true").asString shouldBe Right("true") cursor("null").asString should failWith( - WrongType(ConfigValueType.NULL, Set(ConfigValueType.STRING)), defaultPathStr, stringConfigOrigin(1)) + WrongType(ConfigValueType.NULL, Set(ConfigValueType.STRING)), + defaultPathStr, + stringConfigOrigin(1) + ) cursor("[1, 2]").asString should failWith( - WrongType(ConfigValueType.LIST, Set(ConfigValueType.STRING)), defaultPathStr, stringConfigOrigin(1)) + WrongType(ConfigValueType.LIST, Set(ConfigValueType.STRING)), + defaultPathStr, + stringConfigOrigin(1) + ) cursor("{ a: 1, b: 2 }").asString should failWith( - WrongType(ConfigValueType.OBJECT, Set(ConfigValueType.STRING)), defaultPathStr, stringConfigOrigin(1)) + WrongType(ConfigValueType.OBJECT, Set(ConfigValueType.STRING)), + defaultPathStr, + stringConfigOrigin(1) + ) } it should "allow being cast to boolean in a safe way" in { cursor("true").asBoolean shouldBe Right(true) cursor("false").asBoolean shouldBe Right(false) - cursor("abc").asBoolean should failWith( - WrongType(ConfigValueType.STRING, Set(ConfigValueType.BOOLEAN))) - cursor("1").asBoolean should failWith( - WrongType(ConfigValueType.NUMBER, Set(ConfigValueType.BOOLEAN))) - cursor("TRUE").asBoolean should failWith( - WrongType(ConfigValueType.STRING, Set(ConfigValueType.BOOLEAN))) + cursor("abc").asBoolean should failWith(WrongType(ConfigValueType.STRING, Set(ConfigValueType.BOOLEAN))) + cursor("1").asBoolean should failWith(WrongType(ConfigValueType.NUMBER, Set(ConfigValueType.BOOLEAN))) + cursor("TRUE").asBoolean should failWith(WrongType(ConfigValueType.STRING, Set(ConfigValueType.BOOLEAN))) } it should "allow being cast to long in a safe way" in { - cursor("3").asLong shouldBe Right(3l) - - cursor("abc").asLong should failWith( - WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER))) - cursor("true").asLong should failWith( - WrongType(ConfigValueType.BOOLEAN, Set(ConfigValueType.NUMBER))) - cursor("1.1").asLong should failWith( - CannotConvert("1.1", "Long", "Unable to convert Number to Long")) + cursor("3").asLong shouldBe Right(3L) + + cursor("abc").asLong should failWith(WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER))) + cursor("true").asLong should failWith(WrongType(ConfigValueType.BOOLEAN, Set(ConfigValueType.NUMBER))) + cursor("1.1").asLong should failWith(CannotConvert("1.1", "Long", "Unable to convert Number to Long")) } it should "allow being cast to int in a safe way" in { cursor("3").asInt shouldBe Right(3) - cursor("abc").asInt should failWith( - WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER))) - cursor("true").asInt should failWith( - WrongType(ConfigValueType.BOOLEAN, Set(ConfigValueType.NUMBER))) - cursor("1.1").asInt should failWith( - CannotConvert("1.1", "Int", "Unable to convert Number to Int")) + cursor("abc").asInt should failWith(WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER))) + cursor("true").asInt should failWith(WrongType(ConfigValueType.BOOLEAN, Set(ConfigValueType.NUMBER))) + cursor("1.1").asInt should failWith(CannotConvert("1.1", "Int", "Unable to convert Number to Int")) } it should "allow being cast to short in a safe way" in { cursor("3").asShort shouldBe Right(3) - cursor("abc").asShort should failWith( - WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER))) - cursor("true").asShort should failWith( - WrongType(ConfigValueType.BOOLEAN, Set(ConfigValueType.NUMBER))) - cursor("1.1").asShort should failWith( - CannotConvert("1.1", "Short", "Unable to convert Number to Short")) + cursor("abc").asShort should failWith(WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER))) + cursor("true").asShort should failWith(WrongType(ConfigValueType.BOOLEAN, Set(ConfigValueType.NUMBER))) + cursor("1.1").asShort should failWith(CannotConvert("1.1", "Short", "Unable to convert Number to Short")) } it should "allow being cast to double in a safe way" in { cursor("3").asDouble shouldBe Right(3.0) cursor("3.1").asDouble shouldBe Right(3.1) cursor("21412415121234567L").asDouble should failWith( - WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER))) + WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER)) + ) - cursor("abc").asDouble should failWith( - WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER))) - cursor("true").asDouble should failWith( - WrongType(ConfigValueType.BOOLEAN, Set(ConfigValueType.NUMBER))) + cursor("abc").asDouble should failWith(WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER))) + cursor("true").asDouble should failWith(WrongType(ConfigValueType.BOOLEAN, Set(ConfigValueType.NUMBER))) } it should "allow being cast to float in a safe way" in { cursor("3").asFloat shouldBe Right(3.0) cursor("1.1").asFloat shouldBe Right(1.1f) - cursor("abc").asFloat should failWith( - WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER))) - cursor("true").asFloat should failWith( - WrongType(ConfigValueType.BOOLEAN, Set(ConfigValueType.NUMBER))) + cursor("abc").asFloat should failWith(WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER))) + cursor("true").asFloat should failWith(WrongType(ConfigValueType.BOOLEAN, Set(ConfigValueType.NUMBER))) } it should "allow being casted to a list cursor in a safe way" in { cursor("abc").asListCursor should failWith( - WrongType(ConfigValueType.STRING, Set(ConfigValueType.LIST)), defaultPathStr, stringConfigOrigin(1)) + WrongType(ConfigValueType.STRING, Set(ConfigValueType.LIST)), + defaultPathStr, + stringConfigOrigin(1) + ) cursor("[1, 2]").asListCursor shouldBe Right(ConfigListCursor(conf("[1, 2]").asInstanceOf[ConfigList], defaultPath)) cursor("{ a: 1, b: 2 }").asListCursor should failWith( - WrongType(ConfigValueType.OBJECT, Set(ConfigValueType.LIST)), defaultPathStr, stringConfigOrigin(1)) + WrongType(ConfigValueType.OBJECT, Set(ConfigValueType.LIST)), + defaultPathStr, + stringConfigOrigin(1) + ) cursor("{ 0: a, 1: b }").asListCursor shouldBe Right(ConfigListCursor(conf("""["a", "b"]""").asInstanceOf[ConfigList], defaultPath)) @@ -124,18 +124,27 @@ class ConfigCursorSuite extends BaseSuite { Right(ConfigListCursor(conf("""["a"]""").asInstanceOf[ConfigList], defaultPath)) cursor("{}").asListCursor should failWith( - WrongType(ConfigValueType.OBJECT, Set(ConfigValueType.LIST)), defaultPathStr, stringConfigOrigin(1)) + WrongType(ConfigValueType.OBJECT, Set(ConfigValueType.LIST)), + defaultPathStr, + stringConfigOrigin(1) + ) } it should "allow being casted to a list of cursors in a safe way" in { cursor("abc").asList should failWith( - WrongType(ConfigValueType.STRING, Set(ConfigValueType.LIST)), defaultPathStr, stringConfigOrigin(1)) + WrongType(ConfigValueType.STRING, Set(ConfigValueType.LIST)), + defaultPathStr, + stringConfigOrigin(1) + ) cursor("[1, 2]").asList shouldBe Right(List(cursor("1", "0" :: defaultPath), cursor("2", "1" :: defaultPath))) cursor("{ a: 1, b: 2 }").asList should failWith( - WrongType(ConfigValueType.OBJECT, Set(ConfigValueType.LIST)), defaultPathStr, stringConfigOrigin(1)) + WrongType(ConfigValueType.OBJECT, Set(ConfigValueType.LIST)), + defaultPathStr, + stringConfigOrigin(1) + ) cursor("{ 3: a, 10: b }").asList shouldBe Right(List(cursor("a", "0" :: defaultPath), cursor("b", "1" :: defaultPath))) @@ -144,15 +153,24 @@ class ConfigCursorSuite extends BaseSuite { Right(List(cursor("a", "0" :: defaultPath))) cursor("{}").asList should failWith( - WrongType(ConfigValueType.OBJECT, Set(ConfigValueType.LIST)), defaultPathStr, stringConfigOrigin(1)) + WrongType(ConfigValueType.OBJECT, Set(ConfigValueType.LIST)), + defaultPathStr, + stringConfigOrigin(1) + ) } it should "allow being casted to an object cursor in a safe way" in { cursor("abc").asObjectCursor should failWith( - WrongType(ConfigValueType.STRING, Set(ConfigValueType.OBJECT)), defaultPathStr, stringConfigOrigin(1)) + WrongType(ConfigValueType.STRING, Set(ConfigValueType.OBJECT)), + defaultPathStr, + stringConfigOrigin(1) + ) cursor("[1, 2]").asObjectCursor should failWith( - WrongType(ConfigValueType.LIST, Set(ConfigValueType.OBJECT)), defaultPathStr, stringConfigOrigin(1)) + WrongType(ConfigValueType.LIST, Set(ConfigValueType.OBJECT)), + defaultPathStr, + stringConfigOrigin(1) + ) cursor("{ a: 1, b: 2 }").asObjectCursor shouldBe Right(ConfigObjectCursor(conf("{ a: 1, b: 2 }").asInstanceOf[ConfigObject], defaultPath)) @@ -160,10 +178,16 @@ class ConfigCursorSuite extends BaseSuite { it should "allow being casted to a map of cursors in a safe way" in { cursor("abc").asMap should failWith( - WrongType(ConfigValueType.STRING, Set(ConfigValueType.OBJECT)), defaultPathStr, stringConfigOrigin(1)) + WrongType(ConfigValueType.STRING, Set(ConfigValueType.OBJECT)), + defaultPathStr, + stringConfigOrigin(1) + ) cursor("[1, 2]").asMap should failWith( - WrongType(ConfigValueType.LIST, Set(ConfigValueType.OBJECT)), defaultPathStr, stringConfigOrigin(1)) + WrongType(ConfigValueType.LIST, Set(ConfigValueType.OBJECT)), + defaultPathStr, + stringConfigOrigin(1) + ) cursor("{ a: 1, b: 2 }").asMap shouldBe Right(Map("a" -> cursor("1", "a" :: defaultPath), "b" -> cursor("2", "b" :: defaultPath))) @@ -208,7 +232,8 @@ class ConfigCursorSuite extends BaseSuite { it should "provide a tailOption method that keeps the absolute paths correct" in { listCursor("[1, 2]").tailOption shouldBe Some(listCursor("[2]").copy(offset = 1)) listCursor("[1, 2]").tailOption.get.atIndex(0) shouldBe Right(cursor("2", "1" :: defaultPath)) - listCursor("[1, 2]").tailOption.get.atIndex(1) should failWith(KeyNotFound("2", Set()), defaultPathStr, stringConfigOrigin(1)) + listCursor("[1, 2]").tailOption.get + .atIndex(1) should failWith(KeyNotFound("2", Set()), defaultPathStr, stringConfigOrigin(1)) listCursor("[]").tailOption shouldBe None } @@ -240,7 +265,8 @@ class ConfigCursorSuite extends BaseSuite { it should "allow access to a given key in a safe way" in { objCursor("{ a: 1, b: 2 }").atKey("a") shouldBe Right(cursor("1", "a" :: defaultPath)) - objCursor("{ a: 1, b: 2 }").atKey("c") should failWith(KeyNotFound("c", Set()), defaultPathStr, stringConfigOrigin(1)) + objCursor("{ a: 1, b: 2 }") + .atKey("c") should failWith(KeyNotFound("c", Set()), defaultPathStr, stringConfigOrigin(1)) } it should "allow access to a given key returning an undefined value cursor on non-existing keys" in { @@ -254,7 +280,10 @@ class ConfigCursorSuite extends BaseSuite { } it should "provide a direct conversion to a map of cursors" in { - objCursor("{ a: 1, b: 2 }").map shouldBe Map("a" -> cursor("1", "a" :: defaultPath), "b" -> cursor("2", "b" :: defaultPath)) + objCursor("{ a: 1, b: 2 }").map shouldBe Map( + "a" -> cursor("1", "a" :: defaultPath), + "b" -> cursor("2", "b" :: defaultPath) + ) objCursor("{}").map shouldBe Map.empty } } diff --git a/tests/src/test/scala/pureconfig/ConfigFieldMappingSuite.scala b/tests/src/test/scala/pureconfig/ConfigFieldMappingSuite.scala index d354f7c0e..44eac4911 100644 --- a/tests/src/test/scala/pureconfig/ConfigFieldMappingSuite.scala +++ b/tests/src/test/scala/pureconfig/ConfigFieldMappingSuite.scala @@ -20,8 +20,7 @@ class ConfigFieldMappingSuite extends AnyFlatSpec with Matchers { } it should "allow defining mappings with some overrides" in { - val mapping = ConfigFieldMapping(CamelCase, SnakeCase).withOverrides( - "theUglyFld" -> "the_ugly_field") + val mapping = ConfigFieldMapping(CamelCase, SnakeCase).withOverrides("theUglyFld" -> "the_ugly_field") mapping("theBeautifulField") === "the_beautiful_field" mapping("theUglyFld") === "the_ugly_field" diff --git a/tests/src/test/scala/pureconfig/ConfigReaderExceptionSuite.scala b/tests/src/test/scala/pureconfig/ConfigReaderExceptionSuite.scala index d255b51a3..5b3f5b2f7 100644 --- a/tests/src/test/scala/pureconfig/ConfigReaderExceptionSuite.scala +++ b/tests/src/test/scala/pureconfig/ConfigReaderExceptionSuite.scala @@ -36,7 +36,8 @@ class ConfigReaderExceptionSuite extends BaseSuite with Inside { exception.failures.toList.toSet shouldBe Set( ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER)), stringConfigOrigin(3), "a"), ConvertFailure(KeyNotFound("b", Set()), stringConfigOrigin(2), ""), - ConvertFailure(KeyNotFound("c", Set()), stringConfigOrigin(2), "")) + ConvertFailure(KeyNotFound("c", Set()), stringConfigOrigin(2), "") + ) exception.getMessage shouldBe s"""|Cannot convert configuration to a pureconfig.ConfigReaderExceptionSuite$$Conf. Failures are: @@ -62,14 +63,16 @@ class ConfigReaderExceptionSuite extends BaseSuite with Inside { } exception1.failures.toList.toSet shouldBe Set( - ConvertFailure(WrongType(ConfigValueType.NUMBER, Set(ConfigValueType.OBJECT)), stringConfigOrigin(3), "")) + ConvertFailure(WrongType(ConfigValueType.NUMBER, Set(ConfigValueType.OBJECT)), stringConfigOrigin(3), "") + ) val exception2 = intercept[ConfigReaderException[_]] { conf.root().toOrThrow[ParentConf] } exception2.failures.toList.toSet shouldBe Set( - ConvertFailure(WrongType(ConfigValueType.NUMBER, Set(ConfigValueType.OBJECT)), stringConfigOrigin(3), "conf")) + ConvertFailure(WrongType(ConfigValueType.NUMBER, Set(ConfigValueType.OBJECT)), stringConfigOrigin(3), "conf") + ) } case class MapConf(values: Map[String, MapConf]) @@ -96,8 +99,17 @@ class ConfigReaderExceptionSuite extends BaseSuite with Inside { } exception.failures.toList.toSet shouldBe Set( - ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.OBJECT)), stringConfigOrigin(12), "values.b"), - ConvertFailure(WrongType(ConfigValueType.NUMBER, Set(ConfigValueType.OBJECT)), stringConfigOrigin(6), "values.a.values.c")) + ConvertFailure( + WrongType(ConfigValueType.STRING, Set(ConfigValueType.OBJECT)), + stringConfigOrigin(12), + "values.b" + ), + ConvertFailure( + WrongType(ConfigValueType.NUMBER, Set(ConfigValueType.OBJECT)), + stringConfigOrigin(6), + "values.a.values.c" + ) + ) } sealed trait A @@ -129,17 +141,19 @@ class ConfigReaderExceptionSuite extends BaseSuite with Inside { } exception.failures.toList.toSet shouldBe Set( - ConvertFailure(UnexpectedValueForFieldCoproductHint(ConfigValueFactory.fromAnyRef("unexpected")), stringConfigOrigin(5), "values.v1.type"), - ConvertFailure(KeyNotFound("type", Set()), stringConfigOrigin(12), "values.v3")) + ConvertFailure( + UnexpectedValueForFieldCoproductHint(ConfigValueFactory.fromAnyRef("unexpected")), + stringConfigOrigin(5), + "values.v1.type" + ), + ConvertFailure(KeyNotFound("type", Set()), stringConfigOrigin(12), "values.v3") + ) } case class CamelCaseConf(camelCaseInt: Int, camelCaseString: String) case class KebabCaseConf(kebabCaseInt: Int, kebabCaseString: String) case class SnakeCaseConf(snakeCaseInt: Int, snakeCaseString: String) - case class EnclosingConf( - camelCaseConf: CamelCaseConf, - kebabCaseConf: KebabCaseConf, - snakeCaseConf: SnakeCaseConf) + case class EnclosingConf(camelCaseConf: CamelCaseConf, kebabCaseConf: KebabCaseConf, snakeCaseConf: SnakeCaseConf) it should "include candidate keys in case of a suspected misconfigured ProductHint" in { val conf = ConfigFactory.parseString("""{ @@ -163,9 +177,18 @@ class ConfigReaderExceptionSuite extends BaseSuite with Inside { exception.failures.toList.toSet shouldBe Set( ConvertFailure(KeyNotFound("camel-case-int", Set("camelCaseInt")), stringConfigOrigin(2), "camel-case-conf"), - ConvertFailure(KeyNotFound("camel-case-string", Set("camelCaseString")), stringConfigOrigin(2), "camel-case-conf"), + ConvertFailure( + KeyNotFound("camel-case-string", Set("camelCaseString")), + stringConfigOrigin(2), + "camel-case-conf" + ), ConvertFailure(KeyNotFound("snake-case-int", Set("snake_case_int")), stringConfigOrigin(10), "snake-case-conf"), - ConvertFailure(KeyNotFound("snake-case-string", Set("snake_case_string")), stringConfigOrigin(10), "snake-case-conf")) + ConvertFailure( + KeyNotFound("snake-case-string", Set("snake_case_string")), + stringConfigOrigin(10), + "snake-case-conf" + ) + ) } it should "have failures with the proper file system location of the values that raised errors, if available" in { @@ -180,9 +203,9 @@ class ConfigReaderExceptionSuite extends BaseSuite with Inside { inside(exception.failures.toList) { case List( - ConvertFailure(KeyNotFound("a", _), Some(origin1), ""), - ConvertFailure(WrongType(ConfigValueType.STRING, types), Some(origin2), "c") - ) => + ConvertFailure(KeyNotFound("a", _), Some(origin1), ""), + ConvertFailure(WrongType(ConfigValueType.STRING, types), Some(origin2), "c") + ) => origin1.url shouldBe url origin1.lineNumber shouldBe 1 types should contain only ConfigValueType.NUMBER @@ -237,6 +260,7 @@ class ConfigReaderExceptionSuite extends BaseSuite with Inside { exception.failures.toList.toSet shouldBe Set( ConvertFailure(WrongSizeList(3, 4), stringConfigOrigin(3), "hlist"), - ConvertFailure(WrongSizeList(3, 6), stringConfigOrigin(4), "tuple")) + ConvertFailure(WrongSizeList(3, 6), stringConfigOrigin(4), "tuple") + ) } } diff --git a/tests/src/test/scala/pureconfig/ConfigReaderFailureOriginSuite.scala b/tests/src/test/scala/pureconfig/ConfigReaderFailureOriginSuite.scala index 9462b3ed9..696b4ab50 100644 --- a/tests/src/test/scala/pureconfig/ConfigReaderFailureOriginSuite.scala +++ b/tests/src/test/scala/pureconfig/ConfigReaderFailureOriginSuite.scala @@ -5,16 +5,16 @@ package pureconfig import java.net.URL -import com.typesafe.config.{ ConfigFactory, ConfigValueType } -import org.scalatest.{ EitherValues, Inside } +import com.typesafe.config.{ConfigFactory, ConfigValueType} +import org.scalatest.{EitherValues, Inside} import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers import pureconfig.error._ import pureconfig.generic.auto._ /** - * Suite of tests related to the origin of ConfigValues that raised failures. - */ + * Suite of tests related to the origin of ConfigValues that raised failures. + */ class ConfigReaderFailureOriginSuite extends BaseSuite with EitherValues with Inside { "Loading configuration from files" should "show proper error locations when loading a single file" in { import pureconfig.syntax._ @@ -26,15 +26,9 @@ class ConfigReaderFailureOriginSuite extends BaseSuite with EitherValues with In inside(conf.get("conf").to[Conf].left.value.toList) { case List( - ConvertFailure( - KeyNotFound("a", _), - Some(origin1), - ""), - ConvertFailure( - WrongType(ConfigValueType.STRING, valueTypes), - Some(origin2), - "c") - ) => + ConvertFailure(KeyNotFound("a", _), Some(origin1), ""), + ConvertFailure(WrongType(ConfigValueType.STRING, valueTypes), Some(origin2), "c") + ) => origin1.filename() should endWith(file) origin1.url() shouldBe new URL("file", "", workingDir + file) origin1.lineNumber() shouldBe 1 @@ -48,19 +42,10 @@ class ConfigReaderFailureOriginSuite extends BaseSuite with EitherValues with In inside(conf.get("other-conf").to[Conf].left.value.toList) { case List( - ConvertFailure( - KeyNotFound("a", _), - Some(origin1), - ""), - ConvertFailure( - KeyNotFound("b", _), - Some(origin2), - ""), - ConvertFailure( - WrongType(ConfigValueType.STRING, valueTypes2), - Some(origin3), - "c") - ) => + ConvertFailure(KeyNotFound("a", _), Some(origin1), ""), + ConvertFailure(KeyNotFound("b", _), Some(origin2), ""), + ConvertFailure(WrongType(ConfigValueType.STRING, valueTypes2), Some(origin3), "c") + ) => origin1.filename() should endWith(file) origin1.url shouldBe new URL("file", "", workingDir + file) origin1.lineNumber shouldBe 7 @@ -86,10 +71,7 @@ class ConfigReaderFailureOriginSuite extends BaseSuite with EitherValues with In val conf = ConfigFactory.load(file1).withFallback(ConfigFactory.load(file2)).root() inside(conf.get("conf").to[Conf].left.value.toList) { - case List(ConvertFailure( - WrongType(ConfigValueType.STRING, valueTypes), - Some(origin), - "a")) => + case List(ConvertFailure(WrongType(ConfigValueType.STRING, valueTypes), Some(origin), "a")) => valueTypes should contain only ConfigValueType.NUMBER origin.url() shouldBe new URL("file", "", workingDir + file2) origin.lineNumber() shouldBe 2 diff --git a/tests/src/test/scala/pureconfig/ConfigReaderFailuresPrettyPrintSuite.scala b/tests/src/test/scala/pureconfig/ConfigReaderFailuresPrettyPrintSuite.scala index b23cc0359..6949d97be 100644 --- a/tests/src/test/scala/pureconfig/ConfigReaderFailuresPrettyPrintSuite.scala +++ b/tests/src/test/scala/pureconfig/ConfigReaderFailuresPrettyPrintSuite.scala @@ -12,8 +12,8 @@ import pureconfig.error._ import pureconfig.generic.error._ /** - * Suite of tests related to the pretty printing of config reader failures. - */ + * Suite of tests related to the pretty printing of config reader failures. + */ class ConfigReaderFailuresPrettyPrintSuite extends BaseSuite { "A ConfigReaderFailures prettyPrint method" should "print errors with a configurable identation" in { @@ -22,7 +22,8 @@ class ConfigReaderFailuresPrettyPrintSuite extends BaseSuite { val failures = ConfigReaderFailures( ThrowableFailure(new Exception("Throwable error"), origin(12)), ConvertFailure(KeyNotFound("unknown_key"), None, "path"), - CannotReadResource("resourceName", None)) + CannotReadResource("resourceName", None) + ) failures.prettyPrint(0) shouldBe s"""|- (file:/tmp/config: 12) Throwable error. @@ -43,7 +44,8 @@ class ConfigReaderFailuresPrettyPrintSuite extends BaseSuite { val failures = ConfigReaderFailures( ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER)), None, "a"), ConvertFailure(KeyNotFound("b", Set()), None, ""), - ConvertFailure(KeyNotFound("c", Set()), None, "")) + ConvertFailure(KeyNotFound("c", Set()), None, "") + ) failures.prettyPrint() shouldBe s"""|at the root: @@ -54,15 +56,15 @@ class ConfigReaderFailuresPrettyPrintSuite extends BaseSuite { } it should "print errors that occur at the root of the config" in { - val failures1 = ConfigReaderFailures( - ConvertFailure(WrongType(ConfigValueType.NUMBER, Set(ConfigValueType.OBJECT)), None, "")) + val failures1 = + ConfigReaderFailures(ConvertFailure(WrongType(ConfigValueType.NUMBER, Set(ConfigValueType.OBJECT)), None, "")) failures1.prettyPrint() shouldBe s"""|at the root: | - Expected type OBJECT. Found NUMBER instead.""".stripMargin - val failures2 = ConfigReaderFailures( - ConvertFailure(WrongType(ConfigValueType.NUMBER, Set(ConfigValueType.OBJECT)), None, "conf")) + val failures2 = + ConfigReaderFailures(ConvertFailure(WrongType(ConfigValueType.NUMBER, Set(ConfigValueType.OBJECT)), None, "conf")) failures2.prettyPrint() shouldBe s"""|at 'conf': @@ -71,9 +73,9 @@ class ConfigReaderFailuresPrettyPrintSuite extends BaseSuite { it should "print the full error path" in { val failures = ConfigReaderFailures( - ConvertFailure( - WrongType(ConfigValueType.STRING, Set(ConfigValueType.OBJECT)), None, "values.b"), - ConvertFailure(WrongType(ConfigValueType.NUMBER, Set(ConfigValueType.OBJECT)), None, "values.a.values.c")) + ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.OBJECT)), None, "values.b"), + ConvertFailure(WrongType(ConfigValueType.NUMBER, Set(ConfigValueType.OBJECT)), None, "values.a.values.c") + ) failures.prettyPrint() shouldBe s"""|at 'values.a.values.c': @@ -84,8 +86,13 @@ class ConfigReaderFailuresPrettyPrintSuite extends BaseSuite { it should "print a message displaying relevant errors for coproduct derivation" in { val failures = ConfigReaderFailures( - ConvertFailure(UnexpectedValueForFieldCoproductHint(ConfigValueFactory.fromAnyRef("unexpected")), None, "values.v1.type"), - ConvertFailure(KeyNotFound("type", Set()), None, "values.v3")) + ConvertFailure( + UnexpectedValueForFieldCoproductHint(ConfigValueFactory.fromAnyRef("unexpected")), + None, + "values.v1.type" + ), + ConvertFailure(KeyNotFound("type", Set()), None, "values.v3") + ) failures.prettyPrint() shouldBe s"""|at 'values.v1.type': @@ -99,7 +106,8 @@ class ConfigReaderFailuresPrettyPrintSuite extends BaseSuite { ConvertFailure(KeyNotFound("camel-case-int", Set("camelCaseInt")), None, "camel-case-conf"), ConvertFailure(KeyNotFound("camel-case-string", Set("camelCaseString")), None, "camel-case-conf"), ConvertFailure(KeyNotFound("snake-case-int", Set("snake_case_int")), None, "snake-case-conf"), - ConvertFailure(KeyNotFound("snake-case-string", Set("snake_case_string")), None, "snake-case-conf")) + ConvertFailure(KeyNotFound("snake-case-string", Set("snake_case_string")), None, "snake-case-conf") + ) failures.prettyPrint() shouldBe s"""|at 'camel-case-conf': @@ -121,7 +129,8 @@ class ConfigReaderFailuresPrettyPrintSuite extends BaseSuite { val failures = ConfigReaderFailures( ConvertFailure(KeyNotFound("a", Set()), urlConfigOrigin(url, 1), ""), - ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER)), urlConfigOrigin(url, 3), "c")) + ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER)), urlConfigOrigin(url, 3), "c") + ) failures.prettyPrint() shouldBe s"""|at the root: @@ -135,8 +144,8 @@ class ConfigReaderFailuresPrettyPrintSuite extends BaseSuite { val file = "conf/malformed/a.conf" val url = new URL("file://" + workingDir + file) - val failures = ConfigReaderFailures( - CannotParse("Expecting close brace } or a comma, got end of file", urlConfigOrigin(url, 2))) + val failures = + ConfigReaderFailures(CannotParse("Expecting close brace } or a comma, got end of file", urlConfigOrigin(url, 2))) failures.prettyPrint() shouldBe s"""|- (file:${workingDir}${file}: 2) Unable to parse the configuration: Expecting close brace } or a comma, got end of file.""".stripMargin @@ -148,7 +157,8 @@ class ConfigReaderFailuresPrettyPrintSuite extends BaseSuite { val path = java.nio.file.Paths.get(workingDir + file) val failures = ConfigReaderFailures( - CannotReadFile(path, Some(new java.io.FileNotFoundException(workingDir + file + " (No such file or directory)")))) + CannotReadFile(path, Some(new java.io.FileNotFoundException(workingDir + file + " (No such file or directory)"))) + ) failures.prettyPrint() shouldBe s"""|- Unable to read file ${workingDir}${file} (No such file or directory).""".stripMargin @@ -157,7 +167,8 @@ class ConfigReaderFailuresPrettyPrintSuite extends BaseSuite { it should "print a message showing lists of wrong size" in { val failures = ConfigReaderFailures( ConvertFailure(WrongSizeList(3, 4), None, "hlist"), - ConvertFailure(WrongSizeList(3, 6), None, "tuple")) + ConvertFailure(WrongSizeList(3, 6), None, "tuple") + ) failures.prettyPrint() shouldBe s"""|at 'hlist': @@ -174,10 +185,15 @@ class ConfigReaderFailuresPrettyPrintSuite extends BaseSuite { Seq( "Option1" -> ConfigReaderFailures( ConvertFailure(KeyNotFound("b", Set()), None, "a"), - ConvertFailure(UnknownKey("c"), None, "a.C")), - "Option2" -> ConfigReaderFailures( - ConvertFailure(KeyNotFound("c", Set("C")), None, "a")))), - None, "a")) + ConvertFailure(UnknownKey("c"), None, "a.C") + ), + "Option2" -> ConfigReaderFailures(ConvertFailure(KeyNotFound("c", Set("C")), None, "a")) + ) + ), + None, + "a" + ) + ) failures.prettyPrint() shouldBe s"""|at 'a': diff --git a/tests/src/test/scala/pureconfig/ConfigReaderSuite.scala b/tests/src/test/scala/pureconfig/ConfigReaderSuite.scala index 8e019185c..4447cc043 100644 --- a/tests/src/test/scala/pureconfig/ConfigReaderSuite.scala +++ b/tests/src/test/scala/pureconfig/ConfigReaderSuite.scala @@ -1,7 +1,14 @@ package pureconfig -import com.typesafe.config.{ ConfigFactory, ConfigObject, ConfigOriginFactory, ConfigParseOptions, ConfigValue, ConfigValueFactory } -import org.scalacheck.{ Arbitrary, Gen } +import com.typesafe.config.{ + ConfigFactory, + ConfigObject, + ConfigOriginFactory, + ConfigParseOptions, + ConfigValue, + ConfigValueFactory +} +import org.scalacheck.{Arbitrary, Gen} import pureconfig.error._ class ConfigReaderSuite extends BaseSuite { @@ -10,13 +17,15 @@ class ConfigReaderSuite extends BaseSuite { val intReader = ConfigReader[Int] val strReader = ConfigReader[String] - def intSummedReader(n: Int) = new ConfigReader[Int] { - def from(cur: ConfigCursor) = intReader.from(cur).right.map(_ + n) - } + def intSummedReader(n: Int) = + new ConfigReader[Int] { + def from(cur: ConfigCursor) = intReader.from(cur).right.map(_ + n) + } // generate configs that always read correctly as strings, but not always as integers val genConfig: Gen[ConfigValue] = - Gen.frequency(80 -> Gen.chooseNum(Int.MinValue, Int.MaxValue), 20 -> Gen.alphaStr) + Gen + .frequency(80 -> Gen.chooseNum(Int.MinValue, Int.MaxValue), 20 -> Gen.alphaStr) .map(ConfigValueFactory.fromAnyRef) val genFailureReason: Gen[FailureReason] = @@ -38,10 +47,11 @@ class ConfigReaderSuite extends BaseSuite { } it should "have a correct emap method" in forAll { (conf: ConfigValue, f: Int => Either[FailureReason, String]) => - def getReason[A](failures: ConfigReaderFailures): FailureReason = failures match { - case ConfigReaderFailures(ConvertFailure(reason, _, _)) => reason - case _ => throw new Exception(s"Unexpected value: $failures") - } + def getReason[A](failures: ConfigReaderFailures): FailureReason = + failures match { + case ConfigReaderFailures(ConvertFailure(reason, _, _)) => reason + case _ => throw new Exception(s"Unexpected value: $failures") + } intReader.emap(f).from(conf).left.map(getReason) shouldEqual intReader.from(conf).left.map(getReason).right.flatMap(f) } diff --git a/tests/src/test/scala/pureconfig/ConfigSourceSuite.scala b/tests/src/test/scala/pureconfig/ConfigSourceSuite.scala index 5fb7e4a0a..4ade7af18 100644 --- a/tests/src/test/scala/pureconfig/ConfigSourceSuite.scala +++ b/tests/src/test/scala/pureconfig/ConfigSourceSuite.scala @@ -2,7 +2,7 @@ package pureconfig import java.io.IOException -import com.typesafe.config.{ Config, ConfigFactory, ConfigValueType } +import com.typesafe.config.{Config, ConfigFactory, ConfigValueType} import pureconfig.PathUtils._ import pureconfig.error._ import pureconfig.generic.auto._ @@ -13,60 +13,82 @@ class ConfigSourceSuite extends BaseSuite { it should "load from application.conf and reference.conf by default" in { case class Conf(d: Double, i: Int, s: String) - ConfigSource.default.load[Conf] shouldBe Right(Conf(0D, 0, "app_value")) + ConfigSource.default.load[Conf] shouldBe Right(Conf(0d, 0, "app_value")) } it should "allow reading at a namespace" in { case class Conf(f: Float) - ConfigSource.default.at("foo").load[Conf] shouldBe Right(Conf(3.0F)) + ConfigSource.default.at("foo").load[Conf] shouldBe Right(Conf(3.0f)) } it should "allow reading from a config object" in { case class Conf(d: Double, i: Int) val conf = ConfigFactory.parseString("{ d: 0.5, i: 10 }") - ConfigSource.fromConfig(conf).load[Conf] shouldBe Right(Conf(0.5D, 10)) + ConfigSource.fromConfig(conf).load[Conf] shouldBe Right(Conf(0.5d, 10)) } it should "allow reading from a config object at a namespace" in { case class Conf(f: Float) val conf = ConfigFactory.parseString("foo.bar { f: 1.0 }") - ConfigSource.fromConfig(conf).at("foo.bar").load[Conf] shouldBe Right(Conf(1.0F)) - ConfigSource.fromConfig(conf).at("bar.foo").load[Conf] should failWith(KeyNotFound("bar", Set.empty), "", stringConfigOrigin(1)) + ConfigSource.fromConfig(conf).at("foo.bar").load[Conf] shouldBe Right(Conf(1.0f)) + ConfigSource.fromConfig(conf).at("bar.foo").load[Conf] should failWith( + KeyNotFound("bar", Set.empty), + "", + stringConfigOrigin(1) + ) } it should "allow reading scalar values from a config object at a namespace" in { val conf = ConfigFactory.parseString("foo { bar { f: 1.0 }, baz: 3.4 }") val source = ConfigSource.fromConfig(conf) - source.at("foo.bar.f").load[Float] shouldBe Right(1.0F) + source.at("foo.bar.f").load[Float] shouldBe Right(1.0f) source.at("foo.bar.h").load[Float] should failWith(KeyNotFound("h", Set.empty), "foo.bar", stringConfigOrigin(1)) source.at("bar.foo.f").load[Float] should failWith(KeyNotFound("bar", Set.empty), "", stringConfigOrigin(1)) source.at("foo.baz.f").load[Float] should failWith( - WrongType(ConfigValueType.NUMBER, Set(ConfigValueType.OBJECT)), "foo.baz", stringConfigOrigin(1)) - - source.at("foo.bar.f").load[Option[Float]] shouldBe Right(Some(1.0F)) - source.at("foo.bar.h").load[Option[Float]] should failWith(KeyNotFound("h", Set.empty), "foo.bar", stringConfigOrigin(1)) + WrongType(ConfigValueType.NUMBER, Set(ConfigValueType.OBJECT)), + "foo.baz", + stringConfigOrigin(1) + ) + + source.at("foo.bar.f").load[Option[Float]] shouldBe Right(Some(1.0f)) + source.at("foo.bar.h").load[Option[Float]] should failWith( + KeyNotFound("h", Set.empty), + "foo.bar", + stringConfigOrigin(1) + ) source.at("bar.foo.f").load[Option[Float]] should failWith(KeyNotFound("bar", Set.empty), "", stringConfigOrigin(1)) source.at("foo.baz.f").load[Option[Float]] should failWith( - WrongType(ConfigValueType.NUMBER, Set(ConfigValueType.OBJECT)), "foo.baz", stringConfigOrigin(1)) + WrongType(ConfigValueType.NUMBER, Set(ConfigValueType.OBJECT)), + "foo.baz", + stringConfigOrigin(1) + ) source.at("foo").at("baz").at("f").load[Option[Float]] should failWith( - WrongType(ConfigValueType.NUMBER, Set(ConfigValueType.OBJECT)), "foo.baz", stringConfigOrigin(1)) + WrongType(ConfigValueType.NUMBER, Set(ConfigValueType.OBJECT)), + "foo.baz", + stringConfigOrigin(1) + ) } it should "handle correctly namespaces with special chars" in { val conf = ConfigFactory.parseString(""" "fo.o" { "ba r" { f: 1.0 }, "ba z": 3.4 }""") val source = ConfigSource.fromConfig(conf) - source.at("\"fo.o\".\"ba r\".f").load[Float] shouldBe Right(1.0F) + source.at("\"fo.o\".\"ba r\".f").load[Float] shouldBe Right(1.0f) source.at("\"fo.o\".\"ba r\".h").load[Float] should failWith( - KeyNotFound("h", Set.empty), "\"fo.o\".\"ba r\"", stringConfigOrigin(1)) + KeyNotFound("h", Set.empty), + "\"fo.o\".\"ba r\"", + stringConfigOrigin(1) + ) source.at("\"fo.o\".\"ba z\".h").load[Float] should failWith( - WrongType(ConfigValueType.NUMBER, Set(ConfigValueType.OBJECT)), "\"fo.o\".\"ba z\"", stringConfigOrigin(1)) + WrongType(ConfigValueType.NUMBER, Set(ConfigValueType.OBJECT)), + "\"fo.o\".\"ba z\"", + stringConfigOrigin(1) + ) - source.at("\"b.a.r\".foo.f").load[Float] should failWith( - KeyNotFound("b.a.r", Set.empty), "", stringConfigOrigin(1)) + source.at("\"b.a.r\".foo.f").load[Float] should failWith(KeyNotFound("b.a.r", Set.empty), "", stringConfigOrigin(1)) } it should "allow reading from a configuration file" in { @@ -119,32 +141,33 @@ class ConfigSourceSuite extends BaseSuite { val overrides = ConfigSource.resources("conf/configSource/overrides.conf") val main = ConfigSource.resources("conf/configSource/main.conf") - overrides. - withFallback(main). - withFallback(defaults). - at("my-service").load[MyService] shouldBe Right(MyService("example.com", 8081, true)) + overrides.withFallback(main).withFallback(defaults).at("my-service").load[MyService] shouldBe Right( + MyService("example.com", 8081, true) + ) val nonExisting1 = ConfigSource.resources("nonExistingResource1") val nonExisting2 = ConfigSource.resources("nonExistingResource2") - overrides. - withFallback(nonExisting1). - withFallback(main). - withFallback(defaults). - at("my-service").load[MyService] should matchPattern { - case Left(ConfigReaderFailures(CannotReadResource("nonExistingResource1", _))) => - } - - overrides. - withFallback(nonExisting1). - withFallback(main). - withFallback(nonExisting2). - withFallback(defaults). - at("my-service").load[MyService].left.map(_.toList) should matchPattern { - case Left(List( - CannotReadResource("nonExistingResource1", _), - CannotReadResource("nonExistingResource2", _))) => - } + overrides + .withFallback(nonExisting1) + .withFallback(main) + .withFallback(defaults) + .at("my-service") + .load[MyService] should matchPattern { + case Left(ConfigReaderFailures(CannotReadResource("nonExistingResource1", _))) => + } + + overrides + .withFallback(nonExisting1) + .withFallback(main) + .withFallback(nonExisting2) + .withFallback(defaults) + .at("my-service") + .load[MyService] + .left + .map(_.toList) should matchPattern { + case Left(List(CannotReadResource("nonExistingResource1", _), CannotReadResource("nonExistingResource2", _))) => + } } it should "allow chaining optional fallback sources" in { @@ -152,26 +175,29 @@ class ConfigSourceSuite extends BaseSuite { val overrides = ConfigSource.resources("conf/configSource/overrides.conf") val main = ConfigSource.resources("conf/configSource/main.conf") - overrides.optional. - withFallback(main.optional). - withFallback(defaults.optional). - at("my-service").load[MyService] shouldBe Right(MyService("example.com", 8081, true)) + overrides.optional + .withFallback(main.optional) + .withFallback(defaults.optional) + .at("my-service") + .load[MyService] shouldBe Right(MyService("example.com", 8081, true)) val nonExisting1 = ConfigSource.resources("nonExistingResource1") val nonExisting2 = ConfigSource.resources("nonExistingResource2") - overrides.optional. - withFallback(nonExisting1.optional). - withFallback(main.optional). - withFallback(defaults.optional). - at("my-service").load[MyService] shouldBe Right(MyService("example.com", 8081, true)) - - overrides.optional. - withFallback(nonExisting1.optional). - withFallback(main.optional). - withFallback(nonExisting2.optional). - withFallback(defaults.optional). - at("my-service").load[MyService] shouldBe Right(MyService("example.com", 8081, true)) + overrides.optional + .withFallback(nonExisting1.optional) + .withFallback(main.optional) + .withFallback(defaults.optional) + .at("my-service") + .load[MyService] shouldBe Right(MyService("example.com", 8081, true)) + + overrides.optional + .withFallback(nonExisting1.optional) + .withFallback(main.optional) + .withFallback(nonExisting2.optional) + .withFallback(defaults.optional) + .at("my-service") + .load[MyService] shouldBe Right(MyService("example.com", 8081, true)) } it should "fail when an optional source exists but has syntax errors" in { diff --git a/tests/src/test/scala/pureconfig/ConfigWriterSuite.scala b/tests/src/test/scala/pureconfig/ConfigWriterSuite.scala index ca00ba2c3..1a409c028 100644 --- a/tests/src/test/scala/pureconfig/ConfigWriterSuite.scala +++ b/tests/src/test/scala/pureconfig/ConfigWriterSuite.scala @@ -1,6 +1,6 @@ package pureconfig -import com.typesafe.config.{ ConfigFactory, ConfigValue } +import com.typesafe.config.{ConfigFactory, ConfigValue} class ConfigWriterSuite extends BaseSuite { implicit override val generatorDrivenConfig = PropertyCheckConfiguration(minSuccessful = 100) diff --git a/tests/src/test/scala/pureconfig/CoproductConvertersSuite.scala b/tests/src/test/scala/pureconfig/CoproductConvertersSuite.scala index bfe588204..a9dffdce2 100644 --- a/tests/src/test/scala/pureconfig/CoproductConvertersSuite.scala +++ b/tests/src/test/scala/pureconfig/CoproductConvertersSuite.scala @@ -1,7 +1,7 @@ package pureconfig -import com.typesafe.config.{ ConfigFactory, ConfigObject, ConfigValueFactory } -import org.scalacheck.{ Arbitrary, Gen } +import com.typesafe.config.{ConfigFactory, ConfigObject, ConfigValueFactory} +import org.scalacheck.{Arbitrary, Gen} import pureconfig.error._ import pureconfig.generic.auto._ diff --git a/tests/src/test/scala/pureconfig/CoproductHintSuite.scala b/tests/src/test/scala/pureconfig/CoproductHintSuite.scala index a39eba0dc..d39ed5f12 100644 --- a/tests/src/test/scala/pureconfig/CoproductHintSuite.scala +++ b/tests/src/test/scala/pureconfig/CoproductHintSuite.scala @@ -4,7 +4,7 @@ import com.typesafe.config._ import pureconfig.error._ import pureconfig.generic._ import pureconfig.generic.auto._ -import pureconfig.generic.error.{ CoproductHintException, UnexpectedValueForFieldCoproductHint } +import pureconfig.generic.error.{CoproductHintException, UnexpectedValueForFieldCoproductHint} class CoproductHintSuite extends BaseSuite { @@ -29,21 +29,38 @@ class CoproductHintSuite extends BaseSuite { it should "fail to read values that are not objects when using a FieldCoproductHint" in { val conf = ConfigValueFactory.fromAnyRef("Dog") ConfigConvert[AnimalConfig].from(conf) should failWith( - WrongType(ConfigValueType.STRING, Set(ConfigValueType.OBJECT))) + WrongType(ConfigValueType.STRING, Set(ConfigValueType.OBJECT)) + ) } it should "fail to read values in the discriminating field that are not strings when using a FieldCoproductHint" in { val conf = ConfigFactory.parseString("{ which-animal { type = Dog }, age = 2 }") - ConfigConvert[AnimalConfig].from(conf.root()) should be(Left( - ConfigReaderFailures( - ConvertFailure(WrongType(ConfigValueType.OBJECT, Set(ConfigValueType.STRING)), stringConfigOrigin(1), "which-animal")))) + ConfigConvert[AnimalConfig].from(conf.root()) should be( + Left( + ConfigReaderFailures( + ConvertFailure( + WrongType(ConfigValueType.OBJECT, Set(ConfigValueType.STRING)), + stringConfigOrigin(1), + "which-animal" + ) + ) + ) + ) } it should "fail with an appropriate reason if an unexpected value is found at the discriminating field when using a FieldCoproductHint" in { val conf = ConfigFactory.parseString("{ which-animal = unexpected, age = 2 }") - ConfigConvert[AnimalConfig].from(conf.root()) should be(Left( - ConfigReaderFailures( - ConvertFailure(UnexpectedValueForFieldCoproductHint(ConfigValueFactory.fromAnyRef("unexpected")), stringConfigOrigin(1), "which-animal")))) + ConfigConvert[AnimalConfig].from(conf.root()) should be( + Left( + ConfigReaderFailures( + ConvertFailure( + UnexpectedValueForFieldCoproductHint(ConfigValueFactory.fromAnyRef("unexpected")), + stringConfigOrigin(1), + "which-animal" + ) + ) + ) + ) } it should "fail to read when the hint field conflicts with a field of an option when using a FieldCoproductHint" in { diff --git a/tests/src/test/scala/pureconfig/DerivationModesSuite.scala b/tests/src/test/scala/pureconfig/DerivationModesSuite.scala index 00297c831..b2da3c5fb 100644 --- a/tests/src/test/scala/pureconfig/DerivationModesSuite.scala +++ b/tests/src/test/scala/pureconfig/DerivationModesSuite.scala @@ -12,7 +12,11 @@ class DerivationModesSuite extends BaseSuite { val person = Person("John", "Doe") val conf = ConfigFactory.parseString("{ type: person, name: John, surname: Doe }") - case class CustomCaseClass(customObject: CustomObject, mapCustomObject: Map[String, CustomObject], mapListCustomObject: Map[String, List[CustomObject]]) + case class CustomCaseClass( + customObject: CustomObject, + mapCustomObject: Map[String, CustomObject], + mapListCustomObject: Map[String, List[CustomObject]] + ) case class CustomObject(value: Int) object CustomObject { implicit val pureconfigReader: ConfigReader[CustomObject] = ConfigReader.fromStringOpt { @@ -30,9 +34,9 @@ class DerivationModesSuite extends BaseSuite { val customCaseClass = CustomCaseClass( CustomObject(453), Map("a" -> CustomObject(453), "b" -> CustomObject(45)), - Map("x" -> List(CustomObject(45), CustomObject(453), CustomObject(1)))) - val customConf = ConfigFactory.parseString( - """{ + Map("x" -> List(CustomObject(45), CustomObject(453), CustomObject(1))) + ) + val customConf = ConfigFactory.parseString("""{ | custom-object = "eaaxacaca" | map-custom-object { a = "eaaxacaca", b = "a" } | map-list-custom-object { x = ["a", "eaaxacaca", "cvbc"]} diff --git a/tests/src/test/scala/pureconfig/DerivationSuite.scala b/tests/src/test/scala/pureconfig/DerivationSuite.scala index 475588137..e12e13693 100644 --- a/tests/src/test/scala/pureconfig/DerivationSuite.scala +++ b/tests/src/test/scala/pureconfig/DerivationSuite.scala @@ -47,16 +47,15 @@ class DerivationSuite extends BaseSuite { } it should "fail with a message indicating the root reason when an implicit cannot be found" in { - illTyped( - "implicitly[Derivation[ConfigReader[Custom]]]", - "could not find a ConfigReader instance for type Custom") + illTyped("implicitly[Derivation[ConfigReader[Custom]]]", "could not find a ConfigReader instance for type Custom") illTyped( "implicitly[Derivation[ConfigReader[ConfC]]]", "could not derive a ConfigReader instance for type ConfC, because:", " - missing a ConfigReader instance for type Option\\[Custom\\], because:", " - missing a ConfigReader instance for type Custom", - " - missing a ConfigReader instance for type Custom2") + " - missing a ConfigReader instance for type Custom2" + ) illTyped( "implicitly[Derivation[ConfigReader[Conf]]]", @@ -64,31 +63,33 @@ class DerivationSuite extends BaseSuite { " - missing a ConfigReader instance for type ConfC, because:", " - missing a ConfigReader instance for type Option\\[Custom\\], because:", " - missing a ConfigReader instance for type Custom", - " - missing a ConfigReader instance for type Custom2") + " - missing a ConfigReader instance for type Custom2" + ) illTyped( "implicitly[Derivation[ConfigReader[RecFailConf1]]]", "could not derive a ConfigReader instance for type RecFailConf1, because:", " - missing a ConfigReader instance for type RecFailConf2, because:", - " - missing a ConfigReader instance for type Custom") + " - missing a ConfigReader instance for type Custom" + ) illTyped( "implicitly[Derivation[ConfigReader[RecFailConf2]]]", "could not derive a ConfigReader instance for type RecFailConf2, because:", - " - missing a ConfigReader instance for type Custom") + " - missing a ConfigReader instance for type Custom" + ) // --- - illTyped( - "implicitly[Derivation[ConfigWriter[Custom]]]", - "could not find a ConfigWriter instance for type Custom") + illTyped("implicitly[Derivation[ConfigWriter[Custom]]]", "could not find a ConfigWriter instance for type Custom") illTyped( "implicitly[Derivation[ConfigWriter[ConfC]]]", "could not derive a ConfigWriter instance for type ConfC, because:", " - missing a ConfigWriter instance for type Option\\[Custom\\], because:", " - missing a ConfigWriter instance for type Custom", - " - missing a ConfigWriter instance for type Custom2") + " - missing a ConfigWriter instance for type Custom2" + ) illTyped( "implicitly[Derivation[ConfigWriter[Conf]]]", @@ -96,7 +97,8 @@ class DerivationSuite extends BaseSuite { " - missing a ConfigWriter instance for type ConfC, because:", " - missing a ConfigWriter instance for type Option\\[Custom\\], because:", " - missing a ConfigWriter instance for type Custom", - " - missing a ConfigWriter instance for type Custom2") + " - missing a ConfigWriter instance for type Custom2" + ) // --- @@ -104,7 +106,8 @@ class DerivationSuite extends BaseSuite { "implicitly[Derivation[ConfigConvert[Custom]]]", "could not derive a ConfigConvert instance for type Custom, because:", " - missing a ConfigReader instance for type Custom", - " - missing a ConfigWriter instance for type Custom") + " - missing a ConfigWriter instance for type Custom" + ) illTyped( "implicitly[Derivation[ConfigConvert[Option[Custom]]]]", @@ -112,13 +115,15 @@ class DerivationSuite extends BaseSuite { " - missing a ConfigReader instance for type Option\\[Custom\\], because:", " - missing a ConfigReader instance for type Custom", " - missing a ConfigWriter instance for type Option\\[Custom\\], because:", - " - missing a ConfigWriter instance for type Custom") + " - missing a ConfigWriter instance for type Custom" + ) illTyped( "implicit val cr = customReader; implicitly[Derivation[ConfigConvert[Option[Custom]]]]", "could not derive a ConfigConvert instance for type Option\\[Custom\\], because:", " - missing a ConfigWriter instance for type Option\\[Custom\\], because:", - " - missing a ConfigWriter instance for type Custom") + " - missing a ConfigWriter instance for type Custom" + ) illTyped( "Derivation.materializeDerivation[ConfigReader[Conf]]", @@ -126,13 +131,15 @@ class DerivationSuite extends BaseSuite { " - missing a ConfigReader instance for type ConfC, because:", " - missing a ConfigReader instance for type Option\\[Custom\\], because:", " - missing a ConfigReader instance for type Custom", - " - missing a ConfigReader instance for type Custom2") + " - missing a ConfigReader instance for type Custom2" + ) } it should "fallback to a regular implicit search if it's not at the root of that search" in { illTyped( "implicitly[ConfigReader[Conf]]", - "could not find implicit value for parameter e: pureconfig.ConfigReader\\[DerivationSuite.this.Conf\\]") + "could not find implicit value for parameter e: pureconfig.ConfigReader\\[DerivationSuite.this.Conf\\]" + ) { implicit val cr = customReader diff --git a/tests/src/test/scala/pureconfig/DurationUtilsSuite.scala b/tests/src/test/scala/pureconfig/DurationUtilsSuite.scala index 62aebfed5..1819f73ba 100644 --- a/tests/src/test/scala/pureconfig/DurationUtilsSuite.scala +++ b/tests/src/test/scala/pureconfig/DurationUtilsSuite.scala @@ -7,7 +7,7 @@ import scala.concurrent.duration._ import com.typesafe.config.ConfigValueFactory import org.scalatest.Inspectors import pureconfig.DurationUtilsSuite._ -import pureconfig.error.{ CannotConvert, ConvertFailure, ExceptionThrown } +import pureconfig.error.{CannotConvert, ConvertFailure, ExceptionThrown} class DurationUtilsSuite extends BaseSuite { @@ -74,7 +74,8 @@ class DurationUtilsSuite extends BaseSuite { } it should "report a helpful error message when failing to convert a bad duration" in { val badDuration = "10 lordsALeaping" - BasicReaders.durationConfigReader.from(ConfigValueFactory.fromAnyRef(badDuration)) should failWithType[CannotConvert] + BasicReaders.durationConfigReader + .from(ConfigValueFactory.fromAnyRef(badDuration)) should failWithType[CannotConvert] } it should "correctly round trip when converting Duration.Undefined" in { fromS(fromD(Duration.Undefined)) shouldEqual Right(Duration.Undefined) diff --git a/tests/src/test/scala/pureconfig/EnumerationsSuite.scala b/tests/src/test/scala/pureconfig/EnumerationsSuite.scala index b912bdb81..42fdd0ea3 100644 --- a/tests/src/test/scala/pureconfig/EnumerationsSuite.scala +++ b/tests/src/test/scala/pureconfig/EnumerationsSuite.scala @@ -1,6 +1,6 @@ package pureconfig -import com.typesafe.config.{ ConfigFactory, ConfigValueFactory, ConfigValueType } +import com.typesafe.config.{ConfigFactory, ConfigValueFactory, ConfigValueType} import pureconfig.error.WrongType import pureconfig.generic.error.NoValidCoproductOptionFound import pureconfig.generic.semiauto._ @@ -23,8 +23,13 @@ class EnumerationsSuite extends BaseSuite { ConfigReader[Color].from(ConfigValueFactory.fromAnyRef("sunny-yellow")) shouldBe Right(SunnyYellow) val unknownValue = ConfigValueFactory.fromAnyRef("blue") - ConfigReader[Color].from(unknownValue) should failWith(NoValidCoproductOptionFound(unknownValue, Seq.empty), "", emptyConfigOrigin) - ConfigReader[Color].from(conf.root()) should failWith(WrongType(ConfigValueType.OBJECT, Set(ConfigValueType.STRING)), "", stringConfigOrigin(1)) + ConfigReader[Color] + .from(unknownValue) should failWith(NoValidCoproductOptionFound(unknownValue, Seq.empty), "", emptyConfigOrigin) + ConfigReader[Color].from(conf.root()) should failWith( + WrongType(ConfigValueType.OBJECT, Set(ConfigValueType.STRING)), + "", + stringConfigOrigin(1) + ) } it should "provide methods to derive writers for enumerations encoded as sealed traits" in { diff --git a/tests/src/test/scala/pureconfig/FluentConfigCursorSuite.scala b/tests/src/test/scala/pureconfig/FluentConfigCursorSuite.scala index 6707fad5e..e8e71965e 100644 --- a/tests/src/test/scala/pureconfig/FluentConfigCursorSuite.scala +++ b/tests/src/test/scala/pureconfig/FluentConfigCursorSuite.scala @@ -15,7 +15,11 @@ class FluentConfigCursorSuite extends BaseSuite { def cursor(confStr: String, pathElems: List[String] = defaultPath): FluentConfigCursor = ConfigCursor(conf(confStr), pathElems).fluent - def failedCursor(reason: FailureReason, path: String, origin: Option[ConfigOrigin] = stringConfigOrigin(1)): FluentConfigCursor = + def failedCursor( + reason: FailureReason, + path: String, + origin: Option[ConfigOrigin] = stringConfigOrigin(1) + ): FluentConfigCursor = FluentConfigCursor(Left(ConfigReaderFailures(ConvertFailure(reason, origin, path)))) behavior of "FluentConfigCursor" @@ -28,7 +32,10 @@ class FluentConfigCursorSuite extends BaseSuite { it should "allow being casted to a list cursor in a safe way" in { cursor("abc").asListCursor should failWith( - WrongType(ConfigValueType.STRING, Set(ConfigValueType.LIST)), defaultPathStr, stringConfigOrigin(1)) + WrongType(ConfigValueType.STRING, Set(ConfigValueType.LIST)), + defaultPathStr, + stringConfigOrigin(1) + ) cursor("[1, 2]").asListCursor shouldBe Right(ConfigListCursor(conf("[1, 2]").asInstanceOf[ConfigList], defaultPath)) @@ -36,7 +43,10 @@ class FluentConfigCursorSuite extends BaseSuite { it should "allow being casted to an object cursor in a safe way" in { cursor("abc").asObjectCursor should failWith( - WrongType(ConfigValueType.STRING, Set(ConfigValueType.OBJECT)), defaultPathStr, stringConfigOrigin(1)) + WrongType(ConfigValueType.STRING, Set(ConfigValueType.OBJECT)), + defaultPathStr, + stringConfigOrigin(1) + ) cursor("{ a: 1, b: 2 }").asObjectCursor shouldBe Right(ConfigObjectCursor(conf("{ a: 1, b: 2 }").asInstanceOf[ConfigObject], defaultPath)) @@ -67,11 +77,24 @@ class FluentConfigCursorSuite extends BaseSuite { cursor("[true, notTrue, false, nay]").mapList(_.asBoolean) shouldBe Left( ConfigReaderFailures( - ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.BOOLEAN)), stringConfigOrigin(1), s"$defaultPathStr.1"), - ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.BOOLEAN)), stringConfigOrigin(1), s"$defaultPathStr.3"))) + ConvertFailure( + WrongType(ConfigValueType.STRING, Set(ConfigValueType.BOOLEAN)), + stringConfigOrigin(1), + s"$defaultPathStr.1" + ), + ConvertFailure( + WrongType(ConfigValueType.STRING, Set(ConfigValueType.BOOLEAN)), + stringConfigOrigin(1), + s"$defaultPathStr.3" + ) + ) + ) cursor("abc").mapList(_.asInt) should failWith( - WrongType(ConfigValueType.STRING, Set(ConfigValueType.LIST)), defaultPathStr, stringConfigOrigin(1)) + WrongType(ConfigValueType.STRING, Set(ConfigValueType.LIST)), + defaultPathStr, + stringConfigOrigin(1) + ) } it should "allow for config object mapping with error aggregation" in { @@ -79,10 +102,23 @@ class FluentConfigCursorSuite extends BaseSuite { cursor("{ a: abc, b: 5, c: [6] }").mapObject(_.asInt) shouldBe Left( ConfigReaderFailures( - ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER)), stringConfigOrigin(1), s"$defaultPathStr.a"), - ConvertFailure(WrongType(ConfigValueType.LIST, Set(ConfigValueType.NUMBER)), stringConfigOrigin(1), s"$defaultPathStr.c"))) + ConvertFailure( + WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER)), + stringConfigOrigin(1), + s"$defaultPathStr.a" + ), + ConvertFailure( + WrongType(ConfigValueType.LIST, Set(ConfigValueType.NUMBER)), + stringConfigOrigin(1), + s"$defaultPathStr.c" + ) + ) + ) cursor("abc").mapObject(_.asInt) should failWith( - WrongType(ConfigValueType.STRING, Set(ConfigValueType.OBJECT)), defaultPathStr, stringConfigOrigin(1)) + WrongType(ConfigValueType.STRING, Set(ConfigValueType.OBJECT)), + defaultPathStr, + stringConfigOrigin(1) + ) } } diff --git a/tests/src/test/scala/pureconfig/ProductConvertersSuite.scala b/tests/src/test/scala/pureconfig/ProductConvertersSuite.scala index 0efbbc07b..a62873ada 100644 --- a/tests/src/test/scala/pureconfig/ProductConvertersSuite.scala +++ b/tests/src/test/scala/pureconfig/ProductConvertersSuite.scala @@ -2,11 +2,11 @@ package pureconfig import scala.collection.JavaConverters._ -import com.typesafe.config.{ ConfigFactory, ConfigRenderOptions, ConfigValueFactory } +import com.typesafe.config.{ConfigFactory, ConfigRenderOptions, ConfigValueFactory} import org.scalacheck.Arbitrary import org.scalacheck.ScalacheckShapeless._ import pureconfig.ConfigConvert.catchReadError -import pureconfig.error.{ KeyNotFound, WrongType } +import pureconfig.error.{KeyNotFound, WrongType} import pureconfig.generic.auto._ class ProductConvertersSuite extends BaseSuite { @@ -19,10 +19,11 @@ class ProductConvertersSuite extends BaseSuite { /* A configuration with a field of a type that is unknown to `ConfigConvert` */ class MyType(myField: String) { def getMyField: String = myField - override def equals(obj: Any): Boolean = obj match { - case mt: MyType => myField.equals(mt.getMyField) - case _ => false - } + override def equals(obj: Any): Boolean = + obj match { + case mt: MyType => myField.equals(mt.getMyField) + case _ => false + } } case class ConfigWithUnknownType(d: MyType) @@ -39,14 +40,14 @@ class ProductConvertersSuite extends BaseSuite { it should s"be able to override all of the ConfigConvert instances used to parse ${classOf[FlatConfig]}" in forAll { (config: FlatConfig) => implicit val readBoolean = ConfigReader.fromString[Boolean](catchReadError(_ => false)) - implicit val readDouble = ConfigReader.fromString[Double](catchReadError(_ => 1D)) - implicit val readFloat = ConfigReader.fromString[Float](catchReadError(_ => 2F)) + implicit val readDouble = ConfigReader.fromString[Double](catchReadError(_ => 1d)) + implicit val readFloat = ConfigReader.fromString[Float](catchReadError(_ => 2f)) implicit val readInt = ConfigReader.fromString[Int](catchReadError(_ => 3)) implicit val readLong = ConfigReader.fromString[Long](catchReadError(_ => 4L)) implicit val readString = ConfigReader.fromString[String](catchReadError(_ => "foobar")) implicit val readOption = ConfigConvert.viaString[Option[String]](catchReadError(_ => None), _ => " ") val cc = ConfigConvert[FlatConfig] - cc.from(cc.to(config)) shouldBe Right(FlatConfig(false, 1D, 2F, 3, 4L, "foobar", None)) + cc.from(cc.to(config)) shouldBe Right(FlatConfig(false, 1d, 2f, 3, 4L, "foobar", None)) } val emptyConf = ConfigFactory.empty().root() @@ -75,7 +76,8 @@ class ProductConvertersSuite extends BaseSuite { implicit val defaultInt = new ConfigReader[Int] with ReadsMissingKeys { def from(cur: ConfigCursor) = - if (cur.isUndefined) Right(42) else { + if (cur.isUndefined) Right(42) + else { val s = cur.value.render(ConfigRenderOptions.concise) cur.scopeFailure(catchReadError(_.toInt)(implicitly)(s)) } @@ -110,7 +112,13 @@ class ProductConvertersSuite extends BaseSuite { it should "consider default arguments by default" in { case class InnerConf(e: Int, g: Int) - case class Conf(a: Int, b: String = "default", c: Int = 42, d: InnerConf = InnerConf(43, 44), e: Option[Int] = Some(45)) + case class Conf( + a: Int, + b: String = "default", + c: Int = 42, + d: InnerConf = InnerConf(43, 44), + e: Option[Int] = Some(45) + ) val conf1 = ConfigFactory.parseMap(Map("a" -> 2).asJava).root() ConfigConvert[Conf].from(conf1).right.value shouldBe Conf(2, "default", 42, InnerConf(43, 44), Some(45)) diff --git a/tests/src/test/scala/pureconfig/ProductHintSuite.scala b/tests/src/test/scala/pureconfig/ProductHintSuite.scala index f58a9e1f4..07581cfb6 100644 --- a/tests/src/test/scala/pureconfig/ProductHintSuite.scala +++ b/tests/src/test/scala/pureconfig/ProductHintSuite.scala @@ -1,7 +1,7 @@ package pureconfig -import com.typesafe.config.{ ConfigFactory, ConfigObject, ConfigValueType } -import pureconfig.error.{ ConfigReaderFailures, ConvertFailure, KeyNotFound, UnknownKey, WrongType } +import com.typesafe.config.{ConfigFactory, ConfigObject, ConfigValueType} +import pureconfig.error.{ConfigReaderFailures, ConvertFailure, KeyNotFound, UnknownKey, WrongType} import pureconfig.generic.auto._ import pureconfig.generic.ProductHint import pureconfig.syntax._ @@ -42,19 +42,23 @@ class ProductHintSuite extends BaseSuite { "camel-case-string", "camel-case-conf", "this-is-an-int", - "this-is-another-int") + "this-is-another-int" + ) } it should "allow customizing the field mapping through a product hint" in { - val conf = ConfigFactory.parseString("""{ + val conf = ConfigFactory + .parseString("""{ A = 2 B = "two" - }""").root() + }""") + .root() case class SampleConf(a: Int, b: String) ConfigConvert[SampleConf].from(conf).left.value.toList should contain theSameElementsAs Seq( ConvertFailure(KeyNotFound("a", Set("A")), stringConfigOrigin(1), ""), - ConvertFailure(KeyNotFound("b", Set("B")), stringConfigOrigin(1), "")) + ConvertFailure(KeyNotFound("b", Set("B")), stringConfigOrigin(1), "") + ) implicit val productHint = ProductHint[SampleConf](ConfigFieldMapping(_.toUpperCase)) ConfigConvert[SampleConf].from(conf) shouldBe Right(SampleConf(2, "two")) @@ -85,7 +89,8 @@ class ProductHintSuite extends BaseSuite { "camelCaseString", "camelCaseConf", "thisIsAnInt", - "thisIsAnotherInt") + "thisIsAnotherInt" + ) } it should "read pascal case config keys to pascal case fields when configured to do so" in { @@ -113,7 +118,8 @@ class ProductHintSuite extends BaseSuite { "CamelCaseString", "CamelCaseConf", "ThisIsAnInt", - "ThisIsAnotherInt") + "ThisIsAnotherInt" + ) } it should "allow customizing the field mapping only for specific types" in { @@ -165,12 +171,20 @@ class ProductHintSuite extends BaseSuite { conf.getConfig("conf").to[Conf] shouldBe Left( ConfigReaderFailures( ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER)), stringConfigOrigin(3), "a"), - ConvertFailure(UnknownKey("b"), stringConfigOrigin(4), "b"))) + ConvertFailure(UnknownKey("b"), stringConfigOrigin(4), "b") + ) + ) } it should "not use default arguments if specified through a product hint" in { case class InnerConf(e: Int, g: Int) - case class Conf(a: Int, b: String = "default", c: Int = 42, d: InnerConf = InnerConf(43, 44), e: Option[Int] = Some(45)) + case class Conf( + a: Int, + b: String = "default", + c: Int = 42, + d: InnerConf = InnerConf(43, 44), + e: Option[Int] = Some(45) + ) implicit val productHint = ProductHint[Conf](useDefaultArgs = false) @@ -178,6 +192,7 @@ class ProductHintSuite extends BaseSuite { conf1.to[Conf].left.value.toList should contain theSameElementsAs Seq( ConvertFailure(KeyNotFound("b"), emptyConfigOrigin, ""), ConvertFailure(KeyNotFound("c"), emptyConfigOrigin, ""), - ConvertFailure(KeyNotFound("d"), emptyConfigOrigin, "")) + ConvertFailure(KeyNotFound("d"), emptyConfigOrigin, "") + ) } } diff --git a/tests/src/test/scala/pureconfig/TupleConvertersSuite.scala b/tests/src/test/scala/pureconfig/TupleConvertersSuite.scala index 3aeb86ce3..28c440838 100644 --- a/tests/src/test/scala/pureconfig/TupleConvertersSuite.scala +++ b/tests/src/test/scala/pureconfig/TupleConvertersSuite.scala @@ -2,7 +2,7 @@ package pureconfig import scala.collection.JavaConverters._ -import com.typesafe.config.{ ConfigValueFactory, ConfigValueType } +import com.typesafe.config.{ConfigValueFactory, ConfigValueType} import org.scalacheck.ScalacheckShapeless._ import pureconfig.error._ import pureconfig.generic.auto._ @@ -28,26 +28,38 @@ class TupleConvertersSuite extends BaseSuite { // Check writers checkWrite[Tuple1[Int]](Tuple1(1) -> ConfigValueFactory.fromIterable(List(1).asJava)) checkWrite[(String, Int)](("one", 2) -> ConfigValueFactory.fromIterable(List("one", 2).asJava)) - checkWrite[(Int, (Long, String), Boolean)]((1, (2l, "three"), false) -> ConfigValueFactory.fromIterable(List(1, List(2l, "three").asJava, false).asJava)) + checkWrite[(Int, (Long, String), Boolean)]( + (1, (2L, "three"), false) -> ConfigValueFactory.fromIterable(List(1, List(2L, "three").asJava, false).asJava) + ) // Check errors checkFailures[(String, Int)]( ConfigValueFactory.fromAnyRef(Map("_1" -> "one", "_2" -> "two").asJava) -> ConfigReaderFailures( - ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER)), emptyConfigOrigin, "_2"))) + ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER)), emptyConfigOrigin, "_2") + ) + ) checkFailures[(String, Int)]( ConfigValueFactory.fromIterable(List("one", "two").asJava) -> ConfigReaderFailures( - ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER)), emptyConfigOrigin, "1"))) + ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER)), emptyConfigOrigin, "1") + ) + ) checkFailures[(Int, Int, Int)]( ConfigValueFactory.fromIterable(List(1, "one").asJava) -> ConfigReaderFailures( - ConvertFailure(WrongSizeList(3, 2), emptyConfigOrigin, ""))) + ConvertFailure(WrongSizeList(3, 2), emptyConfigOrigin, "") + ) + ) checkFailures[(Int, Int, Int)]( ConfigValueFactory.fromAnyRef(Map("_1" -> "one", "_2" -> 2).asJava) -> ConfigReaderFailures( ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.NUMBER)), emptyConfigOrigin, "_1"), - ConvertFailure(KeyNotFound("_3", Set()), emptyConfigOrigin, ""))) + ConvertFailure(KeyNotFound("_3", Set()), emptyConfigOrigin, "") + ) + ) checkFailures[(String, Int)]( ConfigValueFactory.fromAnyRef("str") -> ConfigReaderFailures( - ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.LIST)), emptyConfigOrigin, ""))) + ConvertFailure(WrongType(ConfigValueType.STRING, Set(ConfigValueType.LIST)), emptyConfigOrigin, "") + ) + ) } diff --git a/tests/src/test/scala/pureconfig/backend/ConfigFactoryWrapperSpec.scala b/tests/src/test/scala/pureconfig/backend/ConfigFactoryWrapperSpec.scala index aba8d9127..eae705a54 100644 --- a/tests/src/test/scala/pureconfig/backend/ConfigFactoryWrapperSpec.scala +++ b/tests/src/test/scala/pureconfig/backend/ConfigFactoryWrapperSpec.scala @@ -2,10 +2,10 @@ package pureconfig.backend import java.io.FileNotFoundException -import com.typesafe.config.{ ConfigException, ConfigFactory } +import com.typesafe.config.{ConfigException, ConfigFactory} import pureconfig.BaseSuite import pureconfig.PathUtils._ -import pureconfig.error.{ CannotParse, CannotReadFile } +import pureconfig.error.{CannotParse, CannotReadFile} class ConfigFactoryWrapperSpec extends BaseSuite { @@ -27,7 +27,9 @@ class ConfigFactoryWrapperSpec extends BaseSuite { behavior of "ConfigFactoryWrapper.loadFile" it should "return a Left when a file does not exist" in { - ConfigFactory.load(ConfigFactory.parseFile(nonExistingPath.toFile)) shouldEqual ConfigFactory.load(ConfigFactory.empty) + ConfigFactory.load(ConfigFactory.parseFile(nonExistingPath.toFile)) shouldEqual ConfigFactory.load( + ConfigFactory.empty + ) ConfigFactoryWrapper.loadFile(nonExistingPath) should failLike { case CannotReadFile(`nonExistingPath`, Some(reason)) => be(a[FileNotFoundException])(reason) } diff --git a/tests/src/test/scala/pureconfig/syntax/SyntaxSpec.scala b/tests/src/test/scala/pureconfig/syntax/SyntaxSpec.scala index f75a63d10..984601f0f 100644 --- a/tests/src/test/scala/pureconfig/syntax/SyntaxSpec.scala +++ b/tests/src/test/scala/pureconfig/syntax/SyntaxSpec.scala @@ -1,6 +1,6 @@ package pureconfig.syntax -import scala.collection.immutable.{ List, Map } +import scala.collection.immutable.{List, Map} import com.typesafe.config.ConfigFactory import org.scalatest.flatspec.AnyFlatSpec From cff6fdd99a2069d06e37610ed9a41e78ef466188 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rui=20Gonc=CC=A7alves?= Date: Tue, 7 Jul 2020 22:01:55 +0100 Subject: [PATCH 3/5] Configure scalafmt in example project --- example/.scalafmt.conf | 4 ++++ example/build.sbt | 2 ++ example/project/plugins.sbt | 2 +- .../scala/pureconfig/example/conf/Email.scala | 17 +++++++++-------- .../scala/pureconfig/example/conf/Main.scala | 6 +++--- .../scala/pureconfig/example/conf/package.scala | 4 ++-- 6 files changed, 21 insertions(+), 14 deletions(-) create mode 100644 example/.scalafmt.conf diff --git a/example/.scalafmt.conf b/example/.scalafmt.conf new file mode 100644 index 000000000..31920862d --- /dev/null +++ b/example/.scalafmt.conf @@ -0,0 +1,4 @@ +version = 2.6.2 + +align.preset = none +maxColumn = 120 diff --git a/example/build.sbt b/example/build.sbt index 378e61489..663872401 100644 --- a/example/build.sbt +++ b/example/build.sbt @@ -27,3 +27,5 @@ scalacOptions ++= Seq( "-Xlint", "-Ywarn-numeric-widen", "-Ywarn-value-discard") + +scalafmtOnCompile := true diff --git a/example/project/plugins.sbt b/example/project/plugins.sbt index 39a9d53d2..3c7aa8496 100644 --- a/example/project/plugins.sbt +++ b/example/project/plugins.sbt @@ -1,2 +1,2 @@ logLevel := Level.Warn -addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.8.3") +addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.0") diff --git a/example/src/main/scala/pureconfig/example/conf/Email.scala b/example/src/main/scala/pureconfig/example/conf/Email.scala index ee0eab293..4c72fdf0a 100644 --- a/example/src/main/scala/pureconfig/example/conf/Email.scala +++ b/example/src/main/scala/pureconfig/example/conf/Email.scala @@ -3,19 +3,20 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package pureconfig.example -import scala.util.{ Failure, Success, Try } +import scala.util.{Failure, Success, Try} /** - * This is not a production-quality email address validator. - * It is provided only for illustration purposes. - */ + * This is not a production-quality email address validator. + * It is provided only for illustration purposes. + */ object Email { private val regex = """^([a-zA-Z0-9!#$%&'.*+/=?^_`{|}~;-]+)@([a-zA-Z0-9.-]+)$""".r - def fromString(str: String): Try[Email] = str match { - case regex(local, domain) => Success(new Email(s"$local@$domain")) - case _ => Failure(new IllegalArgumentException(s"$str is not a valid email address")) - } + def fromString(str: String): Try[Email] = + str match { + case regex(local, domain) => Success(new Email(s"$local@$domain")) + case _ => Failure(new IllegalArgumentException(s"$str is not a valid email address")) + } } class Email private (address: String) { diff --git a/example/src/main/scala/pureconfig/example/conf/Main.scala b/example/src/main/scala/pureconfig/example/conf/Main.scala index fc4dc5d5b..ff27ecab8 100644 --- a/example/src/main/scala/pureconfig/example/conf/Main.scala +++ b/example/src/main/scala/pureconfig/example/conf/Main.scala @@ -2,8 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** - * @author Mario Pastorelli (mario.pastorelli@teralitycs.ch) - */ + * @author Mario Pastorelli (mario.pastorelli@teralitycs.ch) + */ package pureconfig.example import java.nio.file.Path @@ -28,7 +28,7 @@ dirwatch.email.port=12345 dirwatch.email.message="Dirwatch new path found report" dirwatch.email.recipients=["recipient1@domain.tld","recipient2@domain.tld"] dirwatch.email.sender="sender@domain.tld" -*/ + */ object Main extends App { case class Config(dirwatch: DirWatchConfig) diff --git a/example/src/main/scala/pureconfig/example/conf/package.scala b/example/src/main/scala/pureconfig/example/conf/package.scala index 78e8df5b9..a86ea3a2b 100644 --- a/example/src/main/scala/pureconfig/example/conf/package.scala +++ b/example/src/main/scala/pureconfig/example/conf/package.scala @@ -2,8 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** - * @author Mario Pastorelli (mario.pastorelli@teralitycs.ch) - */ + * @author Mario Pastorelli (mario.pastorelli@teralitycs.ch) + */ package pureconfig.example import pureconfig.ConfigConvert From 18c3f58c88ad49b244584ed48022f9b95b18336a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rui=20Gonc=CC=A7alves?= Date: Tue, 7 Jul 2020 22:12:44 +0100 Subject: [PATCH 4/5] Add check formatting step on CI --- .github/workflows/ci.yml | 3 +++ .travis.yml | 1 + 2 files changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 920fae487..0a2336b06 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,6 +20,9 @@ jobs: with: java-version: ${{ matrix.jdk }} + - name: Check formatting + run: sbt "++${{ matrix.scala }} scalafmtCheckAll" + - name: Compile run: sbt coverage "++${{ matrix.scala }} compile" diff --git a/.travis.yml b/.travis.yml index f90f0678a..faf69d8e4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,7 @@ env: script: - > sbt coverage + "++$TRAVIS_SCALA_VERSION scalafmtCheckAll" "++$TRAVIS_SCALA_VERSION test" "++$TRAVIS_SCALA_VERSION doc" "++$TRAVIS_SCALA_VERSION publishLocal" From 26cdb4d74a76b5b5c133fdaf7f409bfa74765262 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rui=20Gonc=CC=A7alves?= Date: Tue, 7 Jul 2020 22:22:06 +0100 Subject: [PATCH 5/5] Format Scala 2.13 only files --- .../scala-2.13/pureconfig/FactoryCompat.scala | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/core/src/main/scala-2.13/pureconfig/FactoryCompat.scala b/core/src/main/scala-2.13/pureconfig/FactoryCompat.scala index 9e8816d27..fe62908b3 100644 --- a/core/src/main/scala-2.13/pureconfig/FactoryCompat.scala +++ b/core/src/main/scala-2.13/pureconfig/FactoryCompat.scala @@ -4,18 +4,19 @@ import scala.collection.Factory import scala.collection.mutable /** - * A compatibility layer for creating `CanBuildFrom`-like generic methods that work both on Scala 2.13 and pre-2.13 - * versions. - * - * @tparam A the type of elements that get added to the builder - * @tparam C the type of collection that it produces - */ + * A compatibility layer for creating `CanBuildFrom`-like generic methods that work both on Scala 2.13 and pre-2.13 + * versions. + * + * @tparam A the type of elements that get added to the builder + * @tparam C the type of collection that it produces + */ trait FactoryCompat[-A, +C] { def newBuilder(): mutable.Builder[A, C] } object FactoryCompat { - implicit def fromFactory[A, C](implicit factory: Factory[A, C]): FactoryCompat[A, C] = new FactoryCompat[A, C] { - override def newBuilder() = factory.newBuilder - } + implicit def fromFactory[A, C](implicit factory: Factory[A, C]): FactoryCompat[A, C] = + new FactoryCompat[A, C] { + override def newBuilder() = factory.newBuilder + } }