Skip to content
Permalink
Browse files

Merge branch 'master' into update_gif

  • Loading branch information
melrief committed Aug 27, 2017
2 parents d8f7b91 + 2f6d86d commit dc434357e44bc74f8947dd96e787d61a668c4582
@@ -1,12 +1,21 @@
### 0.7.3 (unreleased)
### 0.8.0 (Aug 27, 2017)

- New features
- `loadConfig` methods now allow loading any type from a config when using a namespace, and not only types represented
by config objects;
- `ConfigFieldMapping` now has a `withOverrides` method that allows users to easily define exceptional cases to an
existing mapping;
- `ConfigReader` and `ConfigWriter` for `java.math.BigDecimal` and `java.math.BigInteger`;
- `ConfigReader` for `Boolean`s allows reading them from "yes", "no", "on" and "off" strings.
- `ConfigReader` for `Boolean`s allows reading them from "yes", "no", "on" and "off" strings;
- `ConfigReader` and `ConfigWriter` for `shapeless.HList`;
- `ConfigReader` for Scala tuples can now read from `ConfigLists`s;
- Added an experimental way to debug when a converter fails to be derived because an implicit is not found. See
[the documentation](https://github.com/pureconfig/pureconfig/blob/master/docs/debuging-implicits-not-found.md) for
more information on how to enable it.

- Breaking changes
- `ConfigWriter` for tuples now writes them as `ConfigList`s, instead of a `ConfigObject` with keys `_1`, `_2`, and so on.

- Bug fixes
- A breaking change introduced in v0.7.1 where `loadConfigFromFiles` stopped allowing missing files was reverted;
- `loadConfig` methods no longer throw an exception when passed a namespace where one of the keys is not a config
@@ -32,6 +32,7 @@ Click on the demo gif below to see how PureConfig effortlessly translates your c
- [Default field values](docs/override-behaviour-for-case-classes.md#default-field-values)
- [Unknown keys](docs/override-behaviour-for-case-classes.md#unknown-keys)
- [Override behaviour for sealed families](docs/override-behaviour-for-sealed-families.md)
- [Debugging implicits not found](docs/debugging-implicits-not-found.md)
- [Error handling](docs/error-handling.md)
- [Handling missing keys](docs/handling-missing-keys.md)
- [Example](docs/example.md)
@@ -79,15 +80,15 @@ Add PureConfig to your library dependencies. For Scala `2.11` and `2.12`:

```scala
libraryDependencies ++= Seq(
"com.github.pureconfig" %% "pureconfig" % "0.7.2"
"com.github.pureconfig" %% "pureconfig" % "0.8.0"
)
```

For Scala `2.10` you need also the Macro Paradise plugin:

```scala
libraryDependencies ++= Seq(
"com.github.pureconfig" %% "pureconfig" % "0.7.2",
"com.github.pureconfig" %% "pureconfig" % "0.8.0",
compilerPlugin("org.scalamacros" % "paradise" % "2.0.1" cross CrossVersion.patch)
)
```
@@ -112,7 +113,7 @@ val config: Either[pureconfig.error.ConfigReaderFailures,YourConfClass] = loadCo
```


## Supported Types
## Supported types

Currently supported types for fields are:
- `String`, `Boolean`, `Double` (standard
@@ -134,7 +135,8 @@ Currently supported types for fields are:
- Typesafe `ConfigValue`, `ConfigObject` and `ConfigList`;
- value classes for which readers and writers of the inner type are used;
- case classes;
- sealed families of case classes (ADTs).
- sealed families of case classes (ADTs);
- `shapeless.HList`s of elements whose type is in this list.

# Example

@@ -10,6 +10,7 @@ import pureconfig.ConvertHelpers._
import pureconfig.error._
import shapeless._
import shapeless.labelled._
import shapeless.ops.hlist.HKernelAux

/**
* The default behavior of ConfigReaders that are implicitly derived in PureConfig is to raise a
@@ -35,6 +36,39 @@ trait DerivedReaders extends DerivedReaders1 {
def from(value: ConfigValue): Either[ConfigReaderFailures, T] =
reader.from(value).right.map(unwrapped.wrap)
}

// used for tuples
implicit def deriveTupleInstance[F: IsTuple, Repr <: HList, LRepr <: HList, DefaultRepr <: HList](
implicit
g: Generic.Aux[F, Repr],
gcr: ConfigReader[Repr],
lg: LabelledGeneric.Aux[F, LRepr],
default: Default.AsOptions.Aux[F, DefaultRepr],
pr: WrappedDefaultValue[F, LRepr, DefaultRepr]): ConfigReader[F] = new ConfigReader[F] {
override def from(value: ConfigValue) = {
// Try to read first as the product representation (i.e.
// ConfigObject with '_1', '_2', etc. keys) and afterwards as the Generic
// representation (i.e. ConfigList).
value.valueType match {
case ConfigValueType.OBJECT => deriveTupleInstanceAsObject(value)
case ConfigValueType.LIST => deriveTupleInstanceAsList(value)
case other => fail(WrongType(other, Set(ConfigValueType.LIST, ConfigValueType.OBJECT), ConfigValueLocation(value), ""))
}
}
}

private[pureconfig] def deriveTupleInstanceAsList[F: IsTuple, Repr <: HList](value: ConfigValue)(
implicit
gen: Generic.Aux[F, Repr],
cr: ConfigReader[Repr]): Either[ConfigReaderFailures, F] =
cr.from(value).right.map(gen.from)

private[pureconfig] def deriveTupleInstanceAsObject[F: IsTuple, Repr <: HList, DefaultRepr <: HList](value: ConfigValue)(
implicit
gen: LabelledGeneric.Aux[F, Repr],
default: Default.AsOptions.Aux[F, DefaultRepr],
cr: WrappedDefaultValue[F, Repr, DefaultRepr]): Either[ConfigReaderFailures, F] =
cr.fromWithDefault(value, default()).right.map(gen.from)
}

/**
@@ -44,15 +78,15 @@ trait DerivedReaders1 {

private[pureconfig] trait WrappedConfigReader[Wrapped, SubRepr] extends ConfigReader[SubRepr]

private[pureconfig] trait WrappedDefaultValue[Wrapped, SubRepr <: HList, DefaultRepr <: HList] {
protected[pureconfig] trait WrappedDefaultValue[Wrapped, SubRepr <: HList, DefaultRepr <: HList] {
def fromWithDefault(config: ConfigValue, default: DefaultRepr): Either[ConfigReaderFailures, SubRepr] = config match {
case co: ConfigObject => fromConfigObject(co, default)
case other => fail(WrongType(other.valueType, Set(ConfigValueType.OBJECT), ConfigValueLocation(other), ""))
}
def fromConfigObject(co: ConfigObject, default: DefaultRepr): Either[ConfigReaderFailures, SubRepr]
}

implicit final def hNilConfigReader[Wrapped](
implicit final def labelledHNilConfigReader[Wrapped](
implicit
hint: ProductHint[Wrapped]): WrappedDefaultValue[Wrapped, HNil, HNil] = new WrappedDefaultValue[Wrapped, HNil, HNil] {

@@ -68,7 +102,7 @@ trait DerivedReaders1 {
}
}

implicit final def hConsConfigReader[Wrapped, K <: Symbol, V, T <: HList, U <: HList](
implicit final def labelledHConsConfigReader[Wrapped, K <: Symbol, V, T <: HList, U <: HList](
implicit
key: Witness.Aux[K],
vFieldReader: Derivation[Lazy[ConfigReader[V]]],
@@ -198,6 +232,32 @@ trait DerivedReaders1 {
}
}

implicit final lazy val hNilConfigReader: ConfigReader[HNil] =
new ConfigReader[HNil] {
def from(cv: ConfigValue): Either[ConfigReaderFailures, HNil] = {
cv match {
case cl: ConfigList if cl.size == 0 => Right(HNil)
case cl: ConfigList => fail(WrongSizeList(0, cl.size, ConfigValueLocation(cv), ""))
case other => fail(WrongType(other.valueType, Set(ConfigValueType.LIST), ConfigValueLocation(other), ""))
}
}
}

implicit final def hConsConfigReader[H, T <: HList](implicit hr: Derivation[Lazy[ConfigReader[H]]], tr: Lazy[ConfigReader[T]], tl: HKernelAux[T]): ConfigReader[H :: T] =
new ConfigReader[H :: T] {
def from(cv: ConfigValue): Either[ConfigReaderFailures, H :: T] = {
cv match {
case cl: ConfigList if cl.size != tl().length + 1 => fail(WrongSizeList(tl().length + 1, cl.size, ConfigValueLocation(cv), ""))
case cl: ConfigList =>
val sl = cl.asScala
val hv = hr.value.value.from(sl.head)
val tv = tr.value.from(ConfigValueFactory.fromAnyRef(sl.tail.asJava))
combineResults(hv, tv)(_ :: _)
case other => fail(WrongType(other.valueType, Set(ConfigValueType.LIST), ConfigValueLocation(other), ""))
}
}
}

implicit final def deriveProductInstance[F, Repr <: HList, DefaultRepr <: HList](
implicit
gen: LabelledGeneric.Aux[F, Repr],
@@ -18,20 +18,31 @@ trait DerivedWriters extends DerivedWriters1 {
new ConfigWriter[T] {
override def to(t: T): ConfigValue = writer.to(unwrapped.unwrap(t))
}

// used for tuples
implicit final def deriveTupleInstance[F: IsTuple, Repr](
implicit
gen: Generic.Aux[F, Repr],
cc: ConfigWriter[Repr]): ConfigWriter[F] = new ConfigWriter[F] {
override def to(t: F): ConfigValue =
cc.to(gen.to(t))
}
}

/**
* Trait containing `ConfigWriter` instances for collection, product and coproduct types.
*/
trait DerivedWriters1 {

private[pureconfig] trait WrappedConfigWriter[Wrapped, SubRepr] extends ConfigWriter[SubRepr]
private[pureconfig] trait WrappedConfigWriter[Wrapped, SubRepr] {
def to(cv: SubRepr): ConfigValue
}

implicit final def hNilConfigWriter[Wrapped]: WrappedConfigWriter[Wrapped, HNil] = new WrappedConfigWriter[Wrapped, HNil] {
implicit final def labelledHNilConfigWriter[Wrapped]: WrappedConfigWriter[Wrapped, HNil] = new WrappedConfigWriter[Wrapped, HNil] {
override def to(t: HNil): ConfigValue = ConfigFactory.parseMap(Map().asJava).root()
}

implicit final def hConsConfigWriter[Wrapped, K <: Symbol, V, T <: HList, U <: HList](
implicit final def labelledHConsConfigWriter[Wrapped, K <: Symbol, V, T <: HList, U <: HList](
implicit
key: Witness.Aux[K],
vFieldConvert: Derivation[Lazy[ConfigWriter[V]]],
@@ -108,8 +119,24 @@ trait DerivedWriters1 {
}
}

implicit final lazy val hNilConfigWriter: ConfigWriter[HNil] = new ConfigWriter[HNil] {
override def to(v: HNil): ConfigValue = ConfigValueFactory.fromIterable(List().asJava)
}

implicit final def hConsConfigWriter[H, T <: HList](implicit hw: Derivation[Lazy[ConfigWriter[H]]], tw: Lazy[ConfigWriter[T]]): ConfigWriter[H :: T] =
new ConfigWriter[H :: T] {
override def to(v: (H :: T)): ConfigValue = {
tw.value.to(v.tail) match {
case cl: ConfigList =>
ConfigValueFactory.fromIterable((hw.value.value.to(v.head) +: cl.asScala).asJava)
case other =>
throw new Exception(s"Unexpected value $other when trying to write a `ConfigValue` from an `HList`.")
}
}
}

// used for both products and coproducts
implicit final def deriveGenericInstance[F, Repr](
implicit final def deriveLabelledGenericInstance[F, Repr](
implicit
gen: LabelledGeneric.Aux[F, Repr],
cc: Lazy[WrappedConfigWriter[F, Repr]]): ConfigWriter[F] = new ConfigWriter[F] {
@@ -255,7 +255,7 @@ final case class ThrowableFailure(throwable: Throwable, location: Option[ConfigV
}

/**
* A failure representing an unexpected empty string
* A failure representing an unexpected empty string.
*
* @param typ the type that was attempted to be converted to from an empty string
* @param location an optional location of the ConfigValue that raised the
@@ -283,6 +283,21 @@ final case class NonEmptyObjectFound(typ: String, location: Option[ConfigValueLo
this.copy(location = location orElse parentLocation, path = if (path.isEmpty) parentKey else parentKey + "." + path)
}

/**
* A failure representing that a list of an unexpected size was found when attempting to read into an HList.
*
* @param expected the expected number of elements
* @param found the number of elements found
* @param location an optional location of the ConfigValue that raised the failure
* @param path the path to the value which was a list of an unexpected size
*/
final case class WrongSizeList(expected: Int, found: Int, location: Option[ConfigValueLocation], path: String) extends ConvertFailure {
def description = s"List of wrong size found. Expected $expected elements. Found $found elements instead."

def withImprovedContext(parentKey: String, parentLocation: Option[ConfigValueLocation]) =
this.copy(location = location orElse parentLocation, path = if (path.isEmpty) parentKey else parentKey + "." + path)
}

/**
* A failure representing the inability to find a valid choice for a given coproduct.
*
@@ -32,6 +32,7 @@ Click on the demo gif below to see how PureConfig effortlessly translates your c
- [Default field values](docs/override-behaviour-for-case-classes.md#default-field-values)
- [Unknown keys](docs/override-behaviour-for-case-classes.md#unknown-keys)
- [Override behaviour for sealed families](docs/override-behaviour-for-sealed-families.md)
- [Debugging implicits not found](docs/debugging-implicits-not-found.md)
- [Error handling](docs/error-handling.md)
- [Handling missing keys](docs/handling-missing-keys.md)
- [Example](docs/example.md)
@@ -79,15 +80,15 @@ Add PureConfig to your library dependencies. For Scala `2.11` and `2.12`:

```scala
libraryDependencies ++= Seq(
"com.github.pureconfig" %% "pureconfig" % "0.7.2"
"com.github.pureconfig" %% "pureconfig" % "0.8.0"
)
```

For Scala `2.10` you need also the Macro Paradise plugin:

```scala
libraryDependencies ++= Seq(
"com.github.pureconfig" %% "pureconfig" % "0.7.2",
"com.github.pureconfig" %% "pureconfig" % "0.8.0",
compilerPlugin("org.scalamacros" % "paradise" % "2.0.1" cross CrossVersion.patch)
)
```
@@ -112,7 +113,7 @@ val config: Either[pureconfig.error.ConfigReaderFailures,YourConfClass] = loadCo
```


## Supported Types
## Supported types

Currently supported types for fields are:
- `String`, `Boolean`, `Double` (standard
@@ -134,7 +135,8 @@ Currently supported types for fields are:
- Typesafe `ConfigValue`, `ConfigObject` and `ConfigList`;
- value classes for which readers and writers of the inner type are used;
- case classes;
- sealed families of case classes (ADTs).
- sealed families of case classes (ADTs);
- `shapeless.HList`s of elements whose type is in this list.

# Example

0 comments on commit dc43435

Please sign in to comment.
You can’t perform that action at this time.