Config derivation with ZIO config and Magnolia.

Automatic config derivation is a very convenient way to read a config file into a lot of case classes.

Listing 1. app.conf
coffee {
  ingredients = "coffeeBeans,hot water"
  brewTime = 60 seconds
}
Listing 2. DeriveConfigApp.scala
package config

import zio.config._
import zio.config.magnolia._
import zio.config.typesafe.TypesafeConfigProvider
import zio.{Config, ZIO, ZIOAppDefault}
import java.nio.file.Paths

object DeriveConfigApp extends ZIOAppDefault {
  case class CoffeeConfig(ingredients: List[String], brewTime: zio.Duration)
  case class AppConfig(coffee: CoffeeConfig)

  implicit val appConfigDescriptor: Config[AppConfig] = deriveConfig[AppConfig]

  def readResource[A](filePath: String)(implicit config: Config[A]): ZIO[Any, Config.Error, A] = {
    val file = Paths.get(getClass.getClassLoader.getResource(filePath).toURI).toFile
    val c = config.from(TypesafeConfigProvider.fromHoconFile(file))
    read(c)
  }

  def run = for {
    appConfig <- readResource[AppConfig]("app.conf")
    coffee = appConfig.coffee
    _ <- ZIO.logInfo("start brewing coffee")
    _ <- ZIO.foreach(coffee.ingredients)(i => ZIO.logInfo(s"adding ingredient: $i"))
    _ <- ZIO.logInfo(s"waiting for ${coffee.brewTime.getSeconds} seconds")
    _ <- ZIO.logInfo(s"done. enjoy!")
  } yield ()
}

But as we can see in this output, it’s not always exactly as we want it:

start brewing coffee
adding ingredient: coffeeBeans,hot water
waiting for 60 seconds
done. enjoy!

If we want to read the comma-separated String as a List, we’ll have to provide our own Config descriptor. One might be tempted to add this:

implicit val stringListDescriptor: Config[List[String]] = deriveConfig[String].map(_.split(",").toList)

However, this does not work together with deriveConfig[AppConfig]. To make it available for automatic derivation, we’ll have to add it as a DeriveConfig:

implicit val stringListDescriptor: DeriveConfig[Set[String]] = DeriveConfig[String].map(_.split(","))
implicit val appConfigDescriptor: Config[AppConfig] = deriveConfig[AppConfig]

Now all is well:

start brewing coffee
adding ingredient: coffeeBeans
adding ingredient: hot water
waiting for 60 seconds
done. enjoy!
shadow-left