Skip to content

Commit

Permalink
Remove the Derivation macro (pureconfig#979)
Browse files Browse the repository at this point in the history
* Remove the `Derivation` macro

* Adapt FAQ to describe how to use splain

* Reintroduce blank lines in example's documentation
  • Loading branch information
jcazevedo authored Mar 21, 2021
1 parent f352db1 commit f42bdae
Show file tree
Hide file tree
Showing 35 changed files with 197 additions and 732 deletions.
5 changes: 0 additions & 5 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ organization in ThisBuild := "com.github.pureconfig"
lazy val core = (project in file("core"))
.enablePlugins(BoilerplatePlugin, SbtOsgi)
.settings(commonSettings)
.dependsOn(macros)

lazy val macros = (project in file("macros"))
.settings(commonSettings)

lazy val testkit = (project in file("testkit"))
.settings(commonSettings)
Expand Down Expand Up @@ -89,7 +85,6 @@ lazy val commonSettings = Seq(
scalacOptions ++= lintFlags.value,

scalacOptions in Test ~= { _.filterNot(_.contains("-Ywarn-unused")) },
scalacOptions in Test += "-Xmacro-settings:materialize-derivations",

scalacOptions in (Compile, console) --= Seq("-Xfatal-warnings", "-Ywarn-unused-import", "-Ywarn-unused:_,-implicits"),
scalacOptions in (Test, console) := (scalacOptions in (Compile, console)).value,
Expand Down
16 changes: 8 additions & 8 deletions core/src/main/scala/pureconfig/CollectionReaders.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,39 +15,39 @@ trait ReadsMissingKeys { this: ConfigReader[_] => }
*/
trait CollectionReaders {

implicit def optionReader[A](implicit conv: Derivation[ConfigReader[A]]): ConfigReader[Option[A]] =
implicit def optionReader[A](implicit conv: ConfigReader[A]): ConfigReader[Option[A]] =
new ConfigReader[Option[A]] with ReadsMissingKeys {
override def from(cur: ConfigCursor): ConfigReader.Result[Option[A]] = {
if (cur.isUndefined || cur.isNull) Right(None)
else conv.value.from(cur).map(Some(_))
else conv.from(cur).map(Some(_))
}
}

implicit def traversableReader[A, F[A] <: TraversableOnce[A]](implicit
configConvert: Derivation[ConfigReader[A]],
configConvert: ConfigReader[A],
cbf: FactoryCompat[A, F[A]]
): ConfigReader[F[A]] =
new ConfigReader[F[A]] {

override def from(cur: ConfigCursor): ConfigReader.Result[F[A]] = {
cur.fluent.mapList { valueCur => configConvert.value.from(valueCur) }.map { coll =>
cur.fluent.mapList { valueCur => configConvert.from(valueCur) }.map { coll =>
val builder = cbf.newBuilder()
(builder ++= coll).result()
}
}
}

implicit def mapReader[A](implicit reader: Derivation[ConfigReader[A]]): ConfigReader[Map[String, A]] =
implicit def mapReader[A](implicit reader: ConfigReader[A]): ConfigReader[Map[String, A]] =
new ConfigReader[Map[String, A]] {
override def from(cur: ConfigCursor): ConfigReader.Result[Map[String, A]] = {
cur.fluent.mapObject { valueCur => reader.value.from(valueCur) }
cur.fluent.mapObject { valueCur => reader.from(valueCur) }
}
}

implicit def arrayReader[A: ClassTag](implicit reader: Derivation[ConfigReader[A]]): ConfigReader[Array[A]] =
implicit def arrayReader[A: ClassTag](implicit reader: ConfigReader[A]): ConfigReader[Array[A]] =
new ConfigReader[Array[A]] {
override def from(cur: ConfigCursor): ConfigReader.Result[Array[A]] =
cur.fluent.mapList(reader.value.from).map(_.toArray)
cur.fluent.mapList(reader.from).map(_.toArray)
}
}

Expand Down
18 changes: 9 additions & 9 deletions core/src/main/scala/pureconfig/CollectionWriters.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,38 +18,38 @@ trait WritesMissingKeys[A] { this: ConfigWriter[A] =>
*/
trait CollectionWriters {

implicit def optionWriter[A](implicit conv: Derivation[ConfigWriter[A]]): ConfigWriter[Option[A]] =
implicit def optionWriter[A](implicit conv: 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 Some(v) => conv.to(v)
case None => ConfigValueFactory.fromAnyRef(null)
}

def toOpt(t: Option[A]): Option[ConfigValue] = t.map(conv.value.to)
def toOpt(t: Option[A]): Option[ConfigValue] = t.map(conv.to)
}

implicit def traversableWriter[A, F[A] <: TraversableOnce[A]](implicit
configConvert: Derivation[ConfigWriter[A]]
configConvert: ConfigWriter[A]
): ConfigWriter[F[A]] =
new ConfigWriter[F[A]] {

override def to(ts: F[A]): ConfigValue = {
ConfigValueFactory.fromIterable(ts.toList.map(configConvert.value.to).asJava)
ConfigValueFactory.fromIterable(ts.toList.map(configConvert.to).asJava)
}
}

implicit def mapWriter[A](implicit configConvert: Derivation[ConfigWriter[A]]): ConfigWriter[Map[String, A]] =
implicit def mapWriter[A](implicit configConvert: ConfigWriter[A]): ConfigWriter[Map[String, A]] =
new ConfigWriter[Map[String, A]] {
override def to(keyVals: Map[String, A]): ConfigValue = {
ConfigValueFactory.fromMap(keyVals.mapValues(configConvert.value.to).toMap.asJava)
ConfigValueFactory.fromMap(keyVals.mapValues(configConvert.to).toMap.asJava)
}
}

implicit def arrayWriter[A](implicit writer: Derivation[ConfigWriter[A]]): ConfigWriter[Array[A]] =
implicit def arrayWriter[A](implicit writer: ConfigWriter[A]): ConfigWriter[Array[A]] =
new ConfigWriter[Array[A]] {
override def to(a: Array[A]): ConfigValue =
ConfigValueFactory.fromIterable(a.toList.map(writer.value.to).asJava)
ConfigValueFactory.fromIterable(a.toList.map(writer.to).asJava)
}
}

Expand Down
8 changes: 4 additions & 4 deletions core/src/main/scala/pureconfig/ConfigConvert.scala
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ trait ConfigConvert[A] extends ConfigReader[A] with ConfigWriter[A] { outer =>
*/
object ConfigConvert extends ConvertHelpers {

def apply[A](implicit conv: Derivation[ConfigConvert[A]]): ConfigConvert[A] = conv.value
def apply[A](implicit conv: ConfigConvert[A]): ConfigConvert[A] = conv

def apply[A](reader: ConfigReader[A], writer: ConfigWriter[A]): ConfigConvert[A] =
new ConfigConvert[A] {
Expand All @@ -51,10 +51,10 @@ object ConfigConvert extends ConvertHelpers {
}

implicit def fromReaderAndWriter[A](implicit
reader: Derivation[ConfigReader[A]],
writer: Derivation[ConfigWriter[A]]
reader: ConfigReader[A],
writer: ConfigWriter[A]
): ConfigConvert[A] =
ConfigConvert(reader.value, writer.value)
ConfigConvert(reader, writer)

def viaString[A](fromF: String => Either[FailureReason, A], toF: A => String): ConfigConvert[A] =
ConfigConvert(ConfigReader.fromString(fromF), ConfigWriter.toString(toF))
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/pureconfig/ConfigReader.scala
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ object ConfigReader extends BasicReaders with CollectionReaders with ProductRead
def fail[A](failure: ConfigReaderFailure): ConfigReader.Result[A] = Left(ConfigReaderFailures(failure))
}

def apply[A](implicit reader: Derivation[ConfigReader[A]]): ConfigReader[A] = reader.value
def apply[A](implicit reader: ConfigReader[A]): ConfigReader[A] = reader

/** Creates a `ConfigReader` from a function reading a `ConfigCursor`.
*
Expand Down
5 changes: 2 additions & 3 deletions core/src/main/scala/pureconfig/ConfigSource.scala
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,7 @@ trait ConfigSource {
* @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().flatMap(reader.value.from)
final def load[A](implicit reader: ConfigReader[A]): Result[A] = cursor().flatMap(reader.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`.
Expand All @@ -67,7 +66,7 @@ trait ConfigSource {
* @return The configuration of type `A` loaded from this source.
*/
@throws[ConfigReaderException[_]]
final def loadOrThrow[A: ClassTag](implicit reader: Derivation[ConfigReader[A]]): A = {
final def loadOrThrow[A: ClassTag](implicit reader: ConfigReader[A]): A = {
load[A] match {
case Right(config) => config
case Left(failures) => throw new ConfigReaderException[A](failures)
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/pureconfig/ConfigWriter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ trait ConfigWriter[A] {
*/
object ConfigWriter extends BasicWriters with CollectionWriters with ProductWriters with ExportedWriters {

def apply[A](implicit writer: Derivation[ConfigWriter[A]]): ConfigWriter[A] = writer.value
def apply[A](implicit writer: ConfigWriter[A]): ConfigWriter[A] = writer

/** Creates a `ConfigWriter` from a function.
*
Expand Down
8 changes: 4 additions & 4 deletions core/src/main/scala/pureconfig/configurable/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,12 @@ package object configurable {

def genericMapReader[K, V](
keyParser: String => Either[FailureReason, K]
)(implicit readerV: Derivation[ConfigReader[V]]): ConfigReader[Map[K, V]] =
)(implicit readerV: ConfigReader[V]): ConfigReader[Map[K, V]] =
ConfigReader.fromCursor { cursor =>
cursor.asMap.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)
val eitherValueOrError = readerV.from(valueCursor)
ConfigReader.Result.zipWith(acc, ConfigReader.Result.zipWith(eitherKeyOrError, eitherValueOrError)(_ -> _))(
_ + _
)
Expand All @@ -65,10 +65,10 @@ package object configurable {

def genericMapWriter[K, V](
keyFormatter: K => String
)(implicit writerV: Derivation[ConfigWriter[V]]): ConfigWriter[Map[K, V]] =
)(implicit writerV: 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)
keyFormatter(key) -> writerV.to(value)
}.asJava)
)
}
49 changes: 21 additions & 28 deletions core/src/main/scala/pureconfig/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ package object pureconfig {
* isn't possible
*/
@deprecated("Use `ConfigSource.default.load[A]` instead", "0.12.0")
def loadConfig[A](implicit reader: Derivation[ConfigReader[A]]): ConfigReader.Result[A] =
def loadConfig[A](implicit reader: ConfigReader[A]): ConfigReader.Result[A] =
ConfigSource.default.load[A]

/** Load a configuration of type `A` from the standard configuration files
Expand All @@ -33,7 +33,7 @@ package object pureconfig {
* 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] =
def loadConfig[A](namespace: String)(implicit reader: 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
Expand All @@ -44,7 +44,7 @@ package object pureconfig {
* 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] =
def loadConfig[A](path: Path)(implicit reader: 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
Expand All @@ -56,21 +56,17 @@ package object pureconfig {
* 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: ConfigReader[A]): ConfigReader.Result[A] =
ConfigSource.default(ConfigSource.file(path)).at(namespace).load[A]

/** Load a configuration of type `A` from the given `Config` */
@deprecated("Use `ConfigSource.fromConfig(conf).load[A]` instead", "0.12.0")
def loadConfig[A](conf: Config)(implicit reader: Derivation[ConfigReader[A]]): ConfigReader.Result[A] =
def loadConfig[A](conf: Config)(implicit reader: ConfigReader[A]): ConfigReader.Result[A] =
ConfigSource.fromConfig(conf).load[A]

/** 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: 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
Expand All @@ -81,7 +77,7 @@ package object pureconfig {
* 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] =
def loadConfigWithFallback[A](conf: Config)(implicit reader: 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
Expand All @@ -97,7 +93,7 @@ package object pureconfig {
"0.12.0"
)
def loadConfigWithFallback[A](conf: Config, namespace: String)(implicit
reader: Derivation[ConfigReader[A]]
reader: ConfigReader[A]
): ConfigReader.Result[A] =
ConfigSource.fromConfig(conf).withFallback(ConfigSource.default).at(namespace).load[A]

Expand All @@ -107,8 +103,7 @@ package object pureconfig {
*/
@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]
def loadConfigOrThrow[A: ClassTag](implicit reader: ConfigReader[A]): A = ConfigSource.default.loadOrThrow[A]

/** Load a configuration of type `A` from the standard configuration files
*
Expand All @@ -117,7 +112,7 @@ package object pureconfig {
*/
@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 =
def loadConfigOrThrow[A: ClassTag](namespace: String)(implicit reader: ConfigReader[A]): A =
ConfigSource.default.at(namespace).loadOrThrow[A]

/** Load a configuration of type `A` from the given file. Note that standard configuration
Expand All @@ -127,7 +122,7 @@ package object pureconfig {
*/
@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 =
def loadConfigOrThrow[A: ClassTag](path: Path)(implicit reader: ConfigReader[A]): A =
ConfigSource.default(ConfigSource.file(path)).loadOrThrow[A]

/** Load a configuration of type `A` from the given file. Note that standard configuration
Expand All @@ -138,7 +133,7 @@ package object pureconfig {
*/
@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 =
def loadConfigOrThrow[A: ClassTag](path: Path, namespace: String)(implicit reader: ConfigReader[A]): A =
ConfigSource.default(ConfigSource.file(path)).at(namespace).loadOrThrow[A]

/** Load a configuration of type `A` from the given `Config`
Expand All @@ -148,7 +143,7 @@ package object pureconfig {
*/
@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 =
def loadConfigOrThrow[A: ClassTag](conf: Config)(implicit reader: ConfigReader[A]): A =
ConfigSource.fromConfig(conf).loadOrThrow[A]

/** Load a configuration of type `A` from the given `Config`
Expand All @@ -159,7 +154,7 @@ package object pureconfig {
*/
@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 =
def loadConfigOrThrow[A: ClassTag](conf: Config, namespace: String)(implicit reader: 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
Expand All @@ -169,7 +164,7 @@ package object pureconfig {
*/
@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 =
def loadConfigWithFallbackOrThrow[A: ClassTag](conf: Config)(implicit reader: 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
Expand All @@ -183,9 +178,7 @@ package object pureconfig {
"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 =
def loadConfigWithFallbackOrThrow[A: ClassTag](conf: Config, namespace: String)(implicit reader: ConfigReader[A]): A =
ConfigSource.fromConfig(conf).withFallback(ConfigSource.default).at(namespace).loadOrThrow[A]

/** Save the given configuration into a property file
Expand All @@ -201,7 +194,7 @@ package object pureconfig {
outputPath: Path,
overrideOutputPath: Boolean = false,
options: ConfigRenderOptions = ConfigRenderOptions.defaults()
)(implicit writer: Derivation[ConfigWriter[A]]): Unit = {
)(implicit writer: ConfigWriter[A]): Unit = {

if (!overrideOutputPath && Files.isRegularFile(outputPath)) {
throw new IllegalArgumentException(s"Cannot save configuration in file '$outputPath' because it already exists")
Expand All @@ -225,12 +218,12 @@ package object pureconfig {
conf: A,
outputStream: OutputStream,
options: ConfigRenderOptions = ConfigRenderOptions.defaults()
)(implicit writer: Derivation[ConfigWriter[A]]): Unit = {
)(implicit writer: ConfigWriter[A]): Unit = {

// HOCON requires UTF-8:
// https://github.com/lightbend/config/blob/master/HOCON.md#unchanged-from-json
val printOutputStream = new OutputStreamWriter(outputStream, UTF_8)
val rawConf = writer.value.to(conf)
val rawConf = writer.to(conf)
printOutputStream.write(rawConf.render(options))
printOutputStream.close()
}
Expand All @@ -250,7 +243,7 @@ package object pureconfig {
*/
@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]]
implicit reader: ConfigReader[A]
): ConfigReader.Result[A] = {
ConfigSource
.default(
Expand All @@ -270,7 +263,7 @@ package object pureconfig {
@deprecated("Construct a custom `ConfigSource` pipeline instead", "0.12.0")
def loadConfigFromFilesOrThrow[A: ClassTag](
files: Traversable[Path]
)(implicit reader: Derivation[ConfigReader[A]]): A = {
)(implicit reader: ConfigReader[A]): A = {
ConfigSource
.default(
files
Expand Down
Loading

0 comments on commit f42bdae

Please sign in to comment.