Packages

trait DeriveConfigDescriptor extends AnyRef

Self Type
DeriveConfigDescriptor
Linear Supertypes
Known Subclasses
Ordering
  1. Alphabetic
  2. By Inheritance
Inherited
  1. DeriveConfigDescriptor
  2. AnyRef
  3. Any
  1. Hide All
  2. Show All
Visibility
  1. Public
  2. Protected

Type Members

  1. case class Descriptor[T](desc: config.ConfigDescriptor[T], isObject: Boolean = false) extends Product with Serializable
  2. type Typeclass[T] = Descriptor[T]

Value Members

  1. final def !=(arg0: Any): Boolean
    Definition Classes
    AnyRef → Any
  2. final def ##: Int
    Definition Classes
    AnyRef → Any
  3. final def ==(arg0: Any): Boolean
    Definition Classes
    AnyRef → Any
  4. final def asInstanceOf[T0]: T0
    Definition Classes
    Any
  5. def clone(): AnyRef
    Attributes
    protected[lang]
    Definition Classes
    AnyRef
    Annotations
    @throws(classOf[java.lang.CloneNotSupportedException]) @native()
  6. final def combine[T](caseClass: CaseClass[Descriptor, T]): Descriptor[T]
  7. 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 forms MyConfig has an instance of Descriptor. For almost all the important types, zio-config-magnolia already provides implicit instances for Descriptor.

    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
    }
  8. final def dispatch[T](sealedTrait: SealedTrait[Descriptor, T]): Descriptor[T]
  9. def eitherDesc[A, B](left: config.ConfigDescriptor[A], right: config.ConfigDescriptor[B]): config.ConfigDescriptor[Either[A, B]]
    Attributes
    protected
  10. final def eq(arg0: AnyRef): Boolean
    Definition Classes
    AnyRef
  11. def equals(arg0: AnyRef): Boolean
    Definition Classes
    AnyRef → Any
  12. def finalize(): Unit
    Attributes
    protected[lang]
    Definition Classes
    AnyRef
    Annotations
    @throws(classOf[java.lang.Throwable])
  13. final def getClass(): Class[_ <: AnyRef]
    Definition Classes
    AnyRef → Any
    Annotations
    @native()
  14. implicit macro def getDescriptor[T]: Descriptor[T]
  15. def hashCode(): Int
    Definition Classes
    AnyRef → Any
    Annotations
    @native()
  16. implicit val implicitBigDecimalDesc: Descriptor[BigDecimal]
  17. implicit val implicitBigIntDesc: Descriptor[BigInt]
  18. implicit val implicitBooleanDesc: Descriptor[Boolean]
  19. implicit val implicitByteDesc: Descriptor[Byte]
  20. implicit val implicitDoubleDesc: Descriptor[Double]
  21. implicit val implicitDurationDesc: Descriptor[Duration]
  22. implicit def implicitEitherDesc[A, B](implicit arg0: Descriptor[A], arg1: Descriptor[B]): Descriptor[Either[A, B]]
  23. implicit val implicitFileDesc: Descriptor[File]
  24. implicit val implicitFloatDesc: Descriptor[Float]
  25. implicit val implicitInstantDesc: Descriptor[Instant]
  26. implicit val implicitIntDesc: Descriptor[Int]
  27. implicit val implicitJavaFilePathDesc: Descriptor[Path]
  28. implicit def implicitListDesc[A](implicit arg0: Descriptor[A]): Descriptor[List[A]]
  29. implicit val implicitLocalDateDesc: Descriptor[LocalDate]
  30. implicit val implicitLocalDateTimeDesc: Descriptor[LocalDateTime]
  31. implicit val implicitLocalTimeDesc: Descriptor[LocalTime]
  32. implicit val implicitLongDesc: Descriptor[Long]
  33. implicit def implicitMapDesc[K, A](implicit arg0: Descriptor[A]): Descriptor[Map[String, A]]
  34. implicit def implicitOptionDesc[A](implicit arg0: Descriptor[A]): Descriptor[Option[A]]
  35. implicit val implicitScalaDurationDesc: Descriptor[Duration]
  36. implicit def implicitSetDesc[A](implicit arg0: Descriptor[A]): Descriptor[Set[A]]
  37. implicit val implicitShortDesc: Descriptor[Short]
  38. implicit val implicitStringDesc: Descriptor[String]
  39. implicit val implicitUUIDDesc: Descriptor[UUID]
  40. implicit val implicitUriDesc: Descriptor[URI]
  41. implicit val implicitUrlDesc: Descriptor[URL]
  42. final def isInstanceOf[T0]: Boolean
    Definition Classes
    Any
  43. def listDesc[A](desc: config.ConfigDescriptor[A]): config.ConfigDescriptor[List[A]]
    Attributes
    protected
  44. 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.

  45. def mapDesc[A](desc: config.ConfigDescriptor[A]): config.ConfigDescriptor[Map[String, A]]
    Attributes
    protected
  46. 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
      }
    }
  47. final def ne(arg0: AnyRef): Boolean
    Definition Classes
    AnyRef
  48. final def notify(): Unit
    Definition Classes
    AnyRef
    Annotations
    @native()
  49. final def notifyAll(): Unit
    Definition Classes
    AnyRef
    Annotations
    @native()
  50. def optionDesc[A](configDesc: config.ConfigDescriptor[A]): config.ConfigDescriptor[Option[A]]
    Attributes
    protected
  51. final def prepareClassName(annotations: Seq[Any], name: String): String
  52. final def prepareFieldName(annotations: Seq[Any], name: String): String
  53. def sealedTraitStrategy: SealedTraitStrategy
  54. def setDesc[A](desc: config.ConfigDescriptor[A]): config.ConfigDescriptor[Set[A]]
    Attributes
    protected
  55. final def synchronized[T0](arg0: => T0): T0
    Definition Classes
    AnyRef
  56. def toString(): String
    Definition Classes
    AnyRef → Any
  57. final def wait(): Unit
    Definition Classes
    AnyRef
    Annotations
    @throws(classOf[java.lang.InterruptedException])
  58. final def wait(arg0: Long, arg1: Int): Unit
    Definition Classes
    AnyRef
    Annotations
    @throws(classOf[java.lang.InterruptedException])
  59. final def wait(arg0: Long): Unit
    Definition Classes
    AnyRef
    Annotations
    @throws(classOf[java.lang.InterruptedException]) @native()
  60. final def wrapSealedTrait[T](label: String, desc: config.ConfigDescriptor[T]): config.ConfigDescriptor[T]
  61. object Descriptor extends Serializable

Inherited from AnyRef

Inherited from Any

Ungrouped