Combinators

The combinators defined in ConfigReader provide an easy way to create new ConfigReader instances by transforming existing ones. They are the simplest solution for supporting new simple types and for slightly modifying existing implementations, since the amount of boilerplate required is very small. This section contains some examples of combinators and shows how to work with them in PureConfig.

The simplest combinator is map, which simply transforms the result of an existing reader:

import com.typesafe.config.ConfigFactory
import pureconfig._
import pureconfig.generic.auto._

case class Conf(bytes: Vector[Byte])

// reads an array of bytes from a string
implicit val byteVectorReader: ConfigReader[Vector[Byte]] =
  ConfigReader[String].map(_.getBytes.toVector)
ConfigSource.string("""{ bytes = "Hello world" }""").load[Conf]
// res1: pureconfig.ConfigReader.Result[Conf] = Right(Conf(Vector(72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100)))

emap allows users to validate the inputs and provide detailed failures:

import pureconfig.error._

case class Port(number: Int)
case class Conf(port: Port)

// reads a TCP port, validating the number range
implicit val portReader = ConfigReader[Int].emap {
  case n if n >= 0 && n < 65536 => Right(Port(n))
  case n => Left(CannotConvert(n.toString, "Port", "Invalid port number"))
}
ConfigSource.string("{ port = 8080 }").load[Conf]
// res3: pureconfig.ConfigReader.Result[Conf] = Right(Conf(Port(8080)))

ConfigSource.string("{ port = -1 }").load[Conf]
// res4: pureconfig.ConfigReader.Result[Conf] = Left(ConfigReaderFailures(ConvertFailure(CannotConvert(-1,Port,Invalid port number),None,port),List()))

orElse can be used to provide alternative ways to load a config:

val csvIntListReader = ConfigReader[String].map(_.split(",").map(_.toInt).toList)
implicit val intListReader = ConfigReader[List[Int]].orElse(csvIntListReader)

case class Conf(list: List[Int])
ConfigSource.string("""{ list = [1,2,3] }""").load[Conf]
// res5: pureconfig.ConfigReader.Result[Conf] = Right(Conf(List(1, 2, 3)))

ConfigSource.string("""{ list = "4,5,6" }""").load[Conf]
// res6: pureconfig.ConfigReader.Result[Conf] = Right(Conf(List(4, 5, 6)))