trait DeriveConfigDescriptor extends AnyRef
- Alphabetic
- By Inheritance
- DeriveConfigDescriptor
- AnyRef
- Any
- Hide All
- Show All
- Public
- Protected
Type Members
- case class Descriptor[T](desc: config.ConfigDescriptor[T], isObject: Boolean = false) extends Product with Serializable
- type Typeclass[T] = Descriptor[T]
Value Members
- final def !=(arg0: Any): Boolean
- Definition Classes
- AnyRef → Any
- final def ##: Int
- Definition Classes
- AnyRef → Any
- final def ==(arg0: Any): Boolean
- Definition Classes
- AnyRef → Any
- final def asInstanceOf[T0]: T0
- Definition Classes
- Any
- def clone(): AnyRef
- Attributes
- protected[lang]
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.CloneNotSupportedException]) @native()
- final def combine[T](caseClass: CaseClass[Descriptor, T]): Descriptor[T]
- def descriptor[T](implicit config: Descriptor[T]): config.ConfigDescriptor[T]
descriptor[A] allows the user to automatically derive
ConfigDescriptor
instead of using the ConfigDescriptor dsl explicitly (i.e, manual implementation).descriptor[A] allows the user to automatically derive
ConfigDescriptor
instead of using the ConfigDescriptor dsl explicitly (i.e, manual implementation). While manual implementation can be verbose, it is a recommended to use it when it comes to simple configurations.On the other hand, automatic derivation can become handly when the config is complex with relatively larger number of parameters, or when it is constantly changing during the software lifecycle, or it's just complex structure with nested products and coproducts.
Below given is a small example to show the usage of
descriptor[A]
.Example :
final case class MyConfig(appName: String, port: Int, jdbcUrl: String) val configDesc: ConfigDescriptor[MyConfig] val config = read(configDesc from ConfigSource.fromMap(Map.empty))
descriptor[MyConfig]
works only if all the types that formsMyConfig
has an instance ofDescriptor
. For almost all the important types, zio-config-magnolia already provides implicit instances forDescriptor
.However, say you have a type ZonedDateTime, for which zio-config hasn't provided instance of
Descriptor
, then it will fail to compile.case class MyConfig(x: ZonedDateTime)
In this case, define a Descriptor for ZonedDateTime using
implicit def deriveForZonedDateTime: Descriptor[ZonedDateTime] = Descriptor[String].transformOrFail(string => Try(ZonedDateTime.parse(string).toEither.swap.map(_.getMessage).swap, r => Right(r.toString)) descriptor[MyConfig] // then works
descriptor[A]
can also handle sealed traits, maps list etc.Example:
sealed trait A object A { case class B(x: String, y: String) extends A case class C(z: String) extends A case object D extends A } val config = descriptor[A] val mapSource = ConfigSource.fromMap(Map("B.x" -> "l", "B.y" -> "m") val result = read(config from mapSource) // Right(B("x", "y")) val typesafeSource = TypesafeConfigSource.fromHoconString( s""" { B : { x : l y : m } } """ val result = typesafeSource.flatMap(source => read(config from source)) // Right(B("x", "y")) )
While sealed trait can be fairly straight forward, there are historical errors users make with any advanced config libraries.
Example: What happens if there is another
B
in the same package but for a different parent sealed trait name ?sealed trait X object X { case class B(x: String, y: String) extends X case class C(z: String) extends X case object D extends X } sealed trait Y object Y { case class B(x: String, y: String) extends Y case class Z(value: String) extends Y } final case class MyConfig(xOrY: Either[X, Y]) val typesafeSource = TypesafeConfigSource.fromHoconString( s""" xOrY: { B : { x : l, y : m } } """ )
For zio-config, Either[X, Y] implies, it tries to fetch X and if it fails, it falls over to trying to read Y.
However, in the above case, the output will be always X while user might have intended to provide Y.
This was just an example, but similar conflicts can occur and zio-config-magnolia has strong semantics to handle such scenarios. The best way is to indicate the name of the sealed trait itself.
That is
Example:
import zio.config._ // This implies, not only we are making use of the names of the case classes (or case objects) but the actual // name of the sealed trait as well. val betterDerivation = new DeriveConfigDescriptor { override def sealedTraitStrategy: Descriptor.SealedTraitStrategy = wrapSubClassName && wrapSealedTraitName }
If the source is HOCON, then
betterDerivation.descriptor[MyConfig]
can read:
xOrY: { X : { B : { x : xyz y : xyz } } }
Providing the name of the sealed traits is least commonly used. This is why the default derivation of sealed trait doesn't consider it.
There is a third way of config derivation, especially for those who would like to migrate pure-config's implementation. In this case, we ignore the sealed-trait name, but we consider the sub-class name but not as a parent but part of the product itself.
import zio.config._ val customDerivation = new DeriveConfigDescriptor { override def sealedTraitStrategy: Descriptor.SealedTraitStrategy = labelSubClassName("type") && ignoreSealedTraitName }
If the source is HOCON, then
betterDerivation.descriptor[MyConfig]
can read:
x: { type : B x : r y : z }
betterDerivation.descriptor[MyConfig] }}}
x: { type : B x : r y : z }
betterDerivation.descriptor[MyConfig] }}}
xOrY: { X : { B : { x : xyz y : xyz } } }
Providing the name of the sealed traits is least commonly used. This is why the default derivation of sealed trait doesn't consider it.
There is a third way of config derivation, especially for those who would like to migrate pure-config's implementation. In this case, we ignore the sealed-trait name, but we consider the sub-class name but not as a parent but part of the product itself.
import zio.config._ val customDerivation = new DeriveConfigDescriptor { override def sealedTraitStrategy: Descriptor.SealedTraitStrategy = labelSubClassName("type") && ignoreSealedTraitName }
If the source is HOCON, then
betterDerivation.descriptor[MyConfig]
can read:
x: { type : B x : r y : z }
betterDerivation.descriptor[MyConfig] }}}
x: { type : B x : r y : z }
- final def dispatch[T](sealedTrait: SealedTrait[Descriptor, T]): Descriptor[T]
- def eitherDesc[A, B](left: config.ConfigDescriptor[A], right: config.ConfigDescriptor[B]): config.ConfigDescriptor[Either[A, B]]
- Attributes
- protected
- final def eq(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef
- def equals(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef → Any
- def finalize(): Unit
- Attributes
- protected[lang]
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.Throwable])
- final def getClass(): Class[_ <: AnyRef]
- Definition Classes
- AnyRef → Any
- Annotations
- @native()
- implicit macro def getDescriptor[T]: Descriptor[T]
- def hashCode(): Int
- Definition Classes
- AnyRef → Any
- Annotations
- @native()
- implicit val implicitBigDecimalDesc: Descriptor[BigDecimal]
- implicit val implicitBigIntDesc: Descriptor[BigInt]
- implicit val implicitBooleanDesc: Descriptor[Boolean]
- implicit val implicitByteDesc: Descriptor[Byte]
- implicit val implicitDoubleDesc: Descriptor[Double]
- implicit val implicitDurationDesc: Descriptor[Duration]
- implicit def implicitEitherDesc[A, B](implicit arg0: Descriptor[A], arg1: Descriptor[B]): Descriptor[Either[A, B]]
- implicit val implicitFileDesc: Descriptor[File]
- implicit val implicitFloatDesc: Descriptor[Float]
- implicit val implicitInstantDesc: Descriptor[Instant]
- implicit val implicitIntDesc: Descriptor[Int]
- implicit val implicitJavaFilePathDesc: Descriptor[Path]
- implicit def implicitListDesc[A](implicit arg0: Descriptor[A]): Descriptor[List[A]]
- implicit val implicitLocalDateDesc: Descriptor[LocalDate]
- implicit val implicitLocalDateTimeDesc: Descriptor[LocalDateTime]
- implicit val implicitLocalTimeDesc: Descriptor[LocalTime]
- implicit val implicitLongDesc: Descriptor[Long]
- implicit def implicitMapDesc[K, A](implicit arg0: Descriptor[A]): Descriptor[Map[String, A]]
- implicit def implicitOptionDesc[A](implicit arg0: Descriptor[A]): Descriptor[Option[A]]
- implicit val implicitScalaDurationDesc: Descriptor[Duration]
- implicit def implicitSetDesc[A](implicit arg0: Descriptor[A]): Descriptor[Set[A]]
- implicit val implicitShortDesc: Descriptor[Short]
- implicit val implicitStringDesc: Descriptor[String]
- implicit val implicitUUIDDesc: Descriptor[UUID]
- implicit val implicitUriDesc: Descriptor[URI]
- implicit val implicitUrlDesc: Descriptor[URL]
- final def isInstanceOf[T0]: Boolean
- Definition Classes
- Any
- def listDesc[A](desc: config.ConfigDescriptor[A]): config.ConfigDescriptor[List[A]]
- Attributes
- protected
- def mapClassName(name: String): String
Strategy on how to name the class names in the source config (if they are used in the config) By default, zio-config doesn't make assumptions that config keys or class names, especially when there is sealed traits or case objects
Strategy on how to name the class names in the source config (if they are used in the config) By default, zio-config doesn't make assumptions that config keys or class names, especially when there is sealed traits or case objects
Example:
sealed trait Credentials object Credentials { final case class UsernamePassword(username: String, password: String) extends Credentials final case class Token(username: String, tokenId: String) extends Credentials. } final case class MyConfig(auth: Credentials)
Given:
import zio.config._ val customDerivation = new DeriveConfigDescriptor { override def mapClassName(name: String): String = toKebabCase(name) } // Usage: customDerivation.descriptor[MyConfig]
If the source is HOCON, then
customDerivation.descriptor[MyConfig]
can read:
auth : { username-password : { username : xyz password : abc } }
Alternative solution:
sealed trait Credentials @name("username-password") case class UsernamePassword(username: String, password: String) extends Credentials @name("token") case class Token(username: String, tokenId: String) extends Credentials.
With the above structure, if the source is HOCON, then
descriptor[Credentials]
can read:
auth : { username-password : { username : xyz password : abc } }
The latter solution is more specific to each sealed traits.
descriptor[Credentials] }}}
auth : { username-password : { username : xyz password : abc } }
The latter solution is more specific to each sealed traits.
customDerivation.descriptor[MyConfig] }}}
auth : { username-password : { username : xyz password : abc } }
Alternative solution:
sealed trait Credentials @name("username-password") case class UsernamePassword(username: String, password: String) extends Credentials @name("token") case class Token(username: String, tokenId: String) extends Credentials.
With the above structure, if the source is HOCON, then
descriptor[Credentials]
can read:
auth : { username-password : { username : xyz password : abc } }
The latter solution is more specific to each sealed traits.
descriptor[Credentials] }}}
auth : { username-password : { username : xyz password : abc } }
The latter solution is more specific to each sealed traits.
- def mapDesc[A](desc: config.ConfigDescriptor[A]): config.ConfigDescriptor[Map[String, A]]
- Attributes
- protected
- def mapFieldName(name: String): String
Strategy on how to name the field names in the actual config
Strategy on how to name the field names in the actual config
val customDerivation = new DeriveConfigDescriptor { override def mapFieldName(name: String): String = name.toUpperCase } // Usage: customDerivation.descriptor[MyConfig]
Given,
sealed trait Credentials object Credentials { final case class UsernamePassword(username: String, password: String) extends Credentials final case class Token(username: String, tokenId: String) extends Credentials. } final case class MyConfig(auth: Credentials)
If the source is HOCON, then
customDerivation.descriptor[MyConfig]
can read:
auth : { username_password : { USERNAME : xyz PASSWORD : abc } }
customDerivation.descriptor[MyConfig] }}}
auth : { username_password : { USERNAME : xyz PASSWORD : abc } }
- final def ne(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef
- final def notify(): Unit
- Definition Classes
- AnyRef
- Annotations
- @native()
- final def notifyAll(): Unit
- Definition Classes
- AnyRef
- Annotations
- @native()
- def optionDesc[A](configDesc: config.ConfigDescriptor[A]): config.ConfigDescriptor[Option[A]]
- Attributes
- protected
- final def prepareClassName(annotations: Seq[Any], name: String): String
- final def prepareFieldName(annotations: Seq[Any], name: String): String
- def sealedTraitStrategy: SealedTraitStrategy
- def setDesc[A](desc: config.ConfigDescriptor[A]): config.ConfigDescriptor[Set[A]]
- Attributes
- protected
- final def synchronized[T0](arg0: => T0): T0
- Definition Classes
- AnyRef
- def toString(): String
- Definition Classes
- AnyRef → Any
- final def wait(): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.InterruptedException])
- final def wait(arg0: Long, arg1: Int): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.InterruptedException])
- final def wait(arg0: Long): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.InterruptedException]) @native()
- final def wrapSealedTrait[T](label: String, desc: config.ConfigDescriptor[T]): config.ConfigDescriptor[T]
- object Descriptor extends Serializable