The PureConfig is a lib for laoding configuraiton files. It can handle many types automatically and support writing configuration.

Quick Start

PureConfig uses Typesafe Config to load configurations. It supports reading file in HOCON (prefered as a superset of the other two formats), JSON, and Java .properties format.

Build import: ivy"com.github.pureconfig::pureconfig:0.12.3"

A sample code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import pureconfig.ConfigSource
// required for case class or sealed trait
import pureconfig.generic.auto._

object appConfig {

  case class AppConfig(
      httpPort: Int = 8080,
      appEnvironment: String = "Production"
  )

  def load(): AppConfig = ConfigSource.default.loadOrThrow[AppConfig]
}

Loading a Config

The ConfigSources.default loads a config from application.conf. Other sources include ConfigSource.file, ConfigSource.resources, ConfigSource.url, and ConfigSource.string.

Each source has several loading methods:

  • load[A]: returns an Either[ConfigReaderFailures, A]
  • loadOrThrow[A]: returns an A or throws an exception.
  • config(): reads as a raw config.
  • cursor(): reads as a ConfigCursor
  • withFallBack(defaultSource).load[A]: load with a default source.
  • optional.withFallback(defaultSource): produce an empty config if a source is missing.
  • recoverWith { case ...}.load[A]: use an alternative soruce if the primary can’t be read.
  • at("my-path").load[A]: to load a specific path in the configuration file.

Supporting Types

Most standard Scala and Java type. Additionally, PureConfig also hands the following collections and composite Scala structure: Option, TraversableOnce collections, Map from String key or types convertible to String (must be configured first), shapeless.HList, case class, class with only public val and var parameters in construtor, sealed case classes (ADTs).

To support a new type T, there should be an implicit instance of ConfigReader[T]. There are three ways to build such an instance for a new type:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// a new type
class MyInt(value: Int) { override def toString: String = s"MyInt($value)" }

// using an existing instance:
implicit val myIntReader = ConfigReader[Int].map(n => new MyInt(n))

// using a `ConfigReader` factory method
implicit val myIntReader = ConfigReader.fromString[MyInt](
  ConvertHelpers.catchReadError(s => new MyInt(s.toInt))
)

// creating a new `ConfigReader` from scratch
implicit val myIntReader = new ConfigReader[MyInt] {
  def from(cur: ConfigCursor) = cur.asString.map(s => new MyInt(s.toInt))
}

// load
ConfigSource.string("{ n: 1 }").load[Conf]

For some types, PureConfig cannot automatically derive a reader because there are multiple ways to convert a configuration value to them. For those types, use methods in pureconfig.configurable. For example, localDataConfigConvert for Java’s LocalDate and genericMapReader[A, B] for a map.

Error Handling

Most load methods return an ConfigReader.Rsult[A], an alias for Either[ConfigReaderFailures, A]. There are several types of ConfigRaderFailure, one is ConvertFailure that has a reason FailureReason, an optional location in the config file and a path in the config. The FailureReason could be ConnotConvert, KeyNotFound, or WrongType.