PureConfig
libraryDependencies += "com.avast" %% "sst-pureconfig" % "0.18.4"
This subproject allows you to load your application’s configuration file into a case class. It uses PureConfig library to do so. PureConfig uses Lightbend Config which means that your application’s configuration will be in HOCON format.
Loading of a configuration is effectful, so it is wrapped in F[_]
which is Sync
. This module also tweaks the error messages a little.
Usage and typeclass derivation
Unfortunately there are some incompatible changes between Scala 2 and Scala 3 series of PureConfig when it comes to typeclass derivation. We’ve created different implementation for each series in order to unblock migration to Scala 3 for SST users. Read on for further details on usage of each.
Scala 2
Sample usage:
import com.avast.sst.pureconfig.PureConfigModule
import pureconfig.ConfigReader
import pureconfig.generic.semiauto.deriveReader
import zio.interop.catz.*
import zio.Task
final case class ServerConfiguration(listenAddress: String, listenPort: Int)
implicit val serverConfigurationReader: ConfigReader[ServerConfiguration] = deriveReader
val maybeConfiguration = PureConfigModule.make[Task, ServerConfiguration]
We also provide modules with implicits for many of SST modules. Look for sst-*-pureconfig
subprojects to get implicit
instances of ConfigReader
for specific libraries, e.g.:
import com.avast.sst.http4s.server.pureconfig.implicits.*
The default *.pureconfig.implicits._
import contains the default naming convention for PureConfig which is kebab-case
for the configuration file and camelCase
for the case class field names. You can either choose a different naming convention by different
import (e.g. *.pureconfig.implicits.CamelCase
) or you can create your own
and extend
the ConfigReaders
trait
.
Scala 3
Unlike for Scala 2, pureconfig in Scala 3 supports only kebab-case
derivation. This is reflected in the structure of the implicits we provide. The derivation API has also changed. See official docs for more details.
Sample usage:
import com.avast.sst.pureconfig.PureConfigModule
import pureconfig.ConfigReader
import zio.interop.catz.*
import zio.Task
import pureconfig.generic.derivation.default.*
final case class ServerConfiguration(listenAddress: String, listenPort: Int)
implicit val serverConfigurationReader: ConfigReader[ServerConfiguration] = ConfigReader.derived
val maybeConfiguration = PureConfigModule.make[Task, ServerConfiguration]
Toggle
Sometimes it is useful to be able to disable some functionality via config. That usually means to add a new boolean field to particular configuration case class. You have to be aware of this new field when you work with the configuration and check the value of it to enable/disable particular functionality.
Toggle
is a more safe approach for this pattern as it can be either Enabled[T]
or Disabled
.
So it will force you to deal with the possibility of disabled functionality.
It also simplifies the workflow and particular config case class as it does not require any changes to it.
The boolean flag is build in the Toggle
itself and the value is converted to Enabled[T]
or Disabled
.
For more details please see the example below:
case class MyConfig(myService: Toggle[ServiceConfig])
case class ServiceConfig(url: String, timeout: Duration)
my-service {
enabled = false
url = "http://www.example.com/v1/some-endpoint"
timeout = 5s
}
WithConfig
Sometimes you need to load the configuration both as your custom configuration class and raw Config
when you deal with legacy code. Using WithConfig
case class allows you to load both objects simultaneously.
Example:
case class MyConfig(myService: WithConfig[ServiceConfig])
case class ServiceConfig(url: String, timeout: Duration)
val serviceConfig: Config = myConfig.myService.config
val urlFromConfig: String = serviceConfig.getString("url")
val serviceConfigCustom: ServiceConfig = myConfig.myService.value
val urlFromCustomObject: String = serviceConfigCustom.url