Skip to main content

pureconfig module

Import

import refined4s.modules.pureconfig.derivation.types.all.given
import refined4s.modules.pureconfig.derivation.*

Use Drived Instances for Pre-defined Types

To make Newtype, Refined and InlinedRefined have ConfigReader and ConfigWriter type-class instances derived from the actual values, you can simply use

import refined4s.modules.pureconfig.derivation.types.all.given
NOTE

This works only when the actual type already has ConfigReader and ConfigWriter.

info

Using refined4s.modules.pureconfig.derivation.types.all.given is required only when ConfigReader and/or ConfigWriter is required for the pre-defined types.
So if you want your Newtype or Refined or InlinedRefined to have ConfigReader and ConfigWriter instances,
you can use pre-defined traits for pureconfig or the deriving method instead.

import refined4s.*
import refined4s.types.all.*

import com.typesafe.config.*
import pureconfig.generic.derivation.default.*
import pureconfig.*

import scala.jdk.CollectionConverters.*

With derivation.types.all,

import refined4s.modules.pureconfig.derivation.types.all.given

final case class NewtypeApiConfig(api: NewtypeApiConfig.Api) derives ConfigReader
object NewtypeApiConfig {

final case class Api(
id: PosLong,
baseUri: Uri,
endpointPath: NonEmptyString,
additionalId: PosLong,
) derives ConfigReader

}

val confString =
raw"""api {
| id = 123
| base-uri = "https://localhost:8080"
| endpoint-path = "/v1/blah/blah"
| additional-id = 999
|}
|""".stripMargin
// confString: String = """api {
// id = 123
// base-uri = "https://localhost:8080"
// endpoint-path = "/v1/blah/blah"
// additional-id = 999
// }
// """

ConfigSource.string(confString).load[NewtypeApiConfig]
// res1: Either[ConfigReaderFailures, NewtypeApiConfig] = Right(
// value = NewtypeApiConfig(
// api = Api(
// id = 123L,
// baseUri = "https://localhost:8080",
// endpointPath = "/v1/blah/blah",
// additionalId = 999L
// )
// )
// )

With Explicit Pre-defined Pureconfig Support

There are the following pre-defined traits to support pureconfig ConfigReader and ConfigWriter.

  • refined4s.modules.pureconfig.derivation.PureconfigConfigWriter
  • refined4s.modules.pureconfig.derivation.PureconfigNewtypeConfigReader
  • refined4s.modules.pureconfig.derivation.PureconfigRefinedConfigReader
NOTE

This works only when the actual type already has ConfigReader and ConfigWriter.

import refined4s.*
import refined4s.types.all.*
import refined4s.modules.pureconfig.derivation.*
import refined4s.modules.pureconfig.derivation.types.all.given

import com.typesafe.config.*
import pureconfig.generic.derivation.default.*
import pureconfig.*

import scala.jdk.CollectionConverters.*

final case class NewtypeApiConfig(api: NewtypeApiConfig.Api) derives ConfigReader
object NewtypeApiConfig {

final case class Api(
id: Api.Id,
baseUri: Api.NewtypeBaseUri,
endpointPath: Api.RefinedEndpointPath,
additionalId: Api.InlinedRefinedNewtypeId,
) derives ConfigReader
object Api {

type Id = Id.Type
object Id extends InlinedRefined[Long], PureconfigRefinedConfigReader[Long] {

override inline val inlinedExpectedValue = "a positive Long"

override inline def invalidReason(a: Long): String =
"It must be a positive Long"

override inline def predicate(a: Long): Boolean = a > 0L

override inline def inlinedPredicate(inline a: Long): Boolean = a > 0L
}

type NewtypeBaseUri = NewtypeBaseUri.Type
object NewtypeBaseUri extends Newtype[Uri], PureconfigNewtypeConfigReader[Uri]

type RefinedEndpointPath = RefinedEndpointPath.Type
object RefinedEndpointPath extends Refined[String], PureconfigRefinedConfigReader[String] {
override inline def invalidReason(a: String): String =
"It must be a non-empty String"

override inline def predicate(a: String): Boolean = a != ""
}

type InlinedRefinedNewtypeId = InlinedRefinedNewtypeId.Type
object InlinedRefinedNewtypeId extends Newtype[PosLong], PureconfigNewtypeConfigReader[PosLong]
}

}

val confString =
raw"""api {
| id = 123
| base-uri = "https://localhost:8080"
| endpoint-path = "/v1/blah/blah"
| additional-id = 999
|}
|""".stripMargin
// confString: String = """api {
// id = 123
// base-uri = "https://localhost:8080"
// endpoint-path = "/v1/blah/blah"
// additional-id = 999
// }
// """

ConfigSource.string(confString).load[NewtypeApiConfig]
// res3: Either[ConfigReaderFailures, NewtypeApiConfig] = Right(
// value = NewtypeApiConfig(
// api = Api(
// id = 123L,
// baseUri = "https://localhost:8080",
// endpointPath = "/v1/blah/blah",
// additionalId = 999L
// )
// )
// )

With deriving Method

If you want to have explicit ConfigReader and ConfigWriter type-class instances in your Newtype or Refined or InlinedRefined, you can use the deriving method.

NOTE

This works only when the actual type already has ConfigReader and ConfigWriter.

import refined4s.*
import refined4s.types.all.*
import refined4s.modules.pureconfig.derivation.*
import refined4s.modules.pureconfig.derivation.types.all.given

import com.typesafe.config.*
import pureconfig.generic.derivation.default.*
import pureconfig.*

import scala.jdk.CollectionConverters.*

final case class NewtypeApiConfig(api: NewtypeApiConfig.Api) derives ConfigReader
object NewtypeApiConfig {

final case class Api(
id: Api.Id,
baseUri: Api.NewtypeBaseUri,
endpointPath: Api.RefinedEndpointPath,
additionalId: Api.InlinedRefinedNewtypeId,
) derives ConfigReader
object Api {

type Id = Id.Type
object Id extends InlinedRefined[Long] {

override inline val inlinedExpectedValue = "a positive Long"

override inline def invalidReason(a: Long): String =
"It must be a positive Long"

override inline def predicate(a: Long): Boolean = a > 0L

override inline def inlinedPredicate(inline a: Long): Boolean = a > 0L

given configReaderId: ConfigReader[Id] = deriving[ConfigReader]
}

type NewtypeBaseUri = NewtypeBaseUri.Type
object NewtypeBaseUri extends Newtype[Uri] {
given configReaderNewtypeBaseUri: ConfigReader[NewtypeBaseUri] = deriving[ConfigReader]
}

type RefinedEndpointPath = RefinedEndpointPath.Type
object RefinedEndpointPath extends Refined[String] {
override inline def invalidReason(a: String): String =
"It must be a non-empty String"

override inline def predicate(a: String): Boolean = a != ""

given configReaderRefinedEndpointPath: ConfigReader[RefinedEndpointPath] = deriving[ConfigReader]
}

type InlinedRefinedNewtypeId = InlinedRefinedNewtypeId.Type
object InlinedRefinedNewtypeId extends Newtype[PosLong] {
given configReaderInlinedRefinedNewtypeId: ConfigReader[InlinedRefinedNewtypeId] = deriving[ConfigReader]
}
}

}

val confString =
raw"""api {
| id = 123
| base-uri = "https://localhost:8080"
| endpoint-path = "/v1/blah/blah"
| additional-id = 999
|}
|""".stripMargin
// confString: String = """api {
// id = 123
// base-uri = "https://localhost:8080"
// endpoint-path = "/v1/blah/blah"
// additional-id = 999
// }
// """

ConfigSource.string(confString).load[NewtypeApiConfig]
// res5: Either[ConfigReaderFailures, NewtypeApiConfig] = Right(
// value = NewtypeApiConfig(
// api = Api(
// id = 123L,
// baseUri = "https://localhost:8080",
// endpointPath = "/v1/blah/blah",
// additionalId = 999L
// )
// )
// )