Config Writers
The main use case for PureConfig, as described in the homepage, is to load configuration files to Scala classes in a typesafe and boilerplate-free way. However, there are situations where users may have the need to do the inverse operation: to write a config file from a Scala data structure. An example would be to save a config after it is changed in-app.
Just as PureConfig provides a ConfigReader
interface for reading configurations, it also provides a ConfigWriter
for
writing configs.
All types mentioned at Built-in Supported Types are supported both in reading and in writing operations:
import pureconfig._
import pureconfig.generic.auto._
sealed trait MyAdt
case class AdtA(a: String) extends MyAdt
case class AdtB(b: Int) extends MyAdt
final case class Port(value: Int) extends AnyVal
case class MyClass(
boolean: Boolean,
port: Port,
adt: MyAdt,
list: List[Double],
map: Map[String, String],
option: Option[String])
val confObj = MyClass(true, Port(8080), AdtB(1), List(1.0, 0.2), Map("key" -> "value"), None)
ConfigWriter[MyClass].to(confObj)
// res1: com.typesafe.config.ConfigValue = SimpleConfigObject({"adt":{"b":1,"type":"adt-b"},"boolean":true,"list":[1.0,0.2],"map":{"key":"value"},"port":8080})
The mechanisms with which PureConfig finds out how to write a type to a config are the same as ones used with
ConfigReader
. Therefore, you can use most tutorials and tips at Supporting New Types
and Overriding Behavior for Types for creating ConfigWriter
instances, too.
ConfigWriter
also has useful combinators and factory methods to simplify new implementations:
class MyInt(value: Int) {
def getValue: Int = value
override def toString: String = s"MyInt($value)"
}
implicit val myIntWriter = ConfigWriter[Int].contramap[MyInt](_.getValue)
ConfigWriter[MyInt].to(new MyInt(1))
// res2: com.typesafe.config.ConfigValue = ConfigInt(1)
Finally, if you need both the reading and the writing part for a custom type, you can implement a ConfigConvert
:
implicit val myIntConvert = ConfigConvert[Int].xmap[MyInt](new MyInt(_), _.getValue)
val conf = ConfigWriter[MyInt].to(new MyInt(1))
// conf: com.typesafe.config.ConfigValue = ConfigInt(1)
ConfigReader[MyInt].from(conf)
// res3: ConfigReader.Result[MyInt] = Right(MyInt(1))
A ConfigConvert
implements both the ConfigReader
and ConfigWriter
interfaces and can be used everywhere one of
them is needed.