zio.config.ConfigDescriptorModule
collectAll
is an alias to sequence
.
collectAll
is an alias to sequence
. In Functional Programming terms,
it is a Traverse implementation for ConfigDescriptor.
In other words, it allows us to convert a List
of ConfigDescriptor[A]
to ConfigDescriptor[List[A]]
.
Example:
final case class Variables(variable1: Int, variable2: Option[Int]) object CollectAllExample extends App with EitherImpureOps { val listOfConfig: List[ConfigDescriptor[Variables]] = List("GROUP1", "GROUP2", "GROUP3", "GROUP4") .map( group => (int(s"${group}_VARIABLE1") zip int(s"${group}_VARIABLE2").optional).to[Variables] ) val configOfList: ConfigDescriptor[List[Variables]] = collectAll(listOfConfig.head, listOfConfig.tail: _*) val map = Map( "GROUP1_VARIABLE1" -> "1", "GROUP1_VARIABLE2" -> "2", "GROUP2_VARIABLE1" -> "3", "GROUP2_VARIABLE2" -> "4", "GROUP3_VARIABLE1" -> "5", "GROUP3_VARIABLE2" -> "6", "GROUP4_VARIABLE1" -> "7" ) // loadOrThrow here is only for the purpose of example val result: List[Variables] = read(configOfList from ConfigSource.fromMap(map, "constant")).loadOrThrow val written: PropertyTree[String, String] = write(configOfList, result).loadOrThrow assert( result == List(Variables(1, Some(2)), Variables(3, Some(4)), Variables(5, Some(6)), Variables(7, None)) )
enumeration allows user to up-cast all the subtypes to its super type defined by D
.
enumeration allows user to up-cast all the subtypes to its super type defined by D
.
This is mainly useful in defining coproducts
(sealed trait
)
Example:
sealed trait D case class A(a: String) extends D case class B(b: Int) extends D case class C(c: Double) extends D val config: ConfigDescriptor[D] = enumeration[D]( string("a").to[A], int("b").to[B], double("c").to[C] )
Currently enumeration supports to a maximum of 9 terms. If you have more terms, use orElse
to combine the terms.
enumeration[D](a, b, c, d, e, f, g, h) orElse enumeration[D](i, j, k)
NOTE:
Use zio-config-magnolia for better compile time safety when it comes to sealed trait
,
as it has strong compile time behaviour and makes sure all subtypes are being handled.
On the other hand, enumeration
doesn't complain at compile time if you forgot
to pass the config descriptor of any of the subtype.
Example:
import zio.config.magnolia._ val config = descriptor[D]
head
describes getting the head of a possible list value
head
describes getting the head of a possible list value
Example:
final case class Config(userName: String, port: Option[Int]) object Config { val source = ConfigSource.fromMap(Map("USERNAME" -> "af,sa", "PORT" -> "1"), valueDelimiter = Some(',')) val databaseConfig: ConfigDescriptor[Config] = (head("USERNAME")(string) zip int("PORT").optional).to[Config] } read(Config.databaseConfig from Config.source) // returns Config("af", 1)
head
describes getting the head of a possible list value
head
describes getting the head of a possible list value
Example:
final case class Config(userName: String, port: Option[Int]) object Config { val source = ConfigSource.fromMap(Map("USERNAME" -> "af,sa", "PORT" -> "1"), valueDelimiter = Some(',')) val databaseConfig: ConfigDescriptor[Config] = (head(string("USERNAME")) zip int("PORT").optional).to[Config] } read(Config.databaseConfig from Config.source) // returns Config("af", 1)
list("xyz")(confgDescriptor)
represents just a list variant of configDescriptor within the key xyz
.
list("xyz")(confgDescriptor)
represents just a list variant of configDescriptor within the key xyz
.
Note that, nested("xyz")(list(configDescriptor))
is same as list("xyz")(configDescriptor)
.
For example: list("key")(string)
implies value of key
is of the type List[String]
Here is a more detailed example.
We know val config = string("USERNAME") from source
represents a program that says, there exists a key called
"USERNAME" (in some ConfigSource called source)
with a value that is of the type String
.
list("xyz")(config)
would then imply, there exists a list of USERNAME -> value
pair within the key "xyz".
val json = s""" | xyz : [ | { | "USERNAME" : "value1" | }, | | { | "USERNAME" : "value2" | } | ] |""".stripMargin val config = string("USERNAME") // Within the key "xyz", we have a list of key-value pair, where key is always "USERNAME" // NOTE: In HOCON, there is always a need of key (in this case, xyz) at parent level. val listConfig = list("xyz")(config) val userNames: ZIO[Any, ReadError[String], List[String]] = read(listConfig from ypesafeConfigSource.fromHoconString(json))
returns
List(value1, value2)
list(confgDescriptor)
represents just a list variant of configuration extraction.
list(confgDescriptor)
represents just a list variant of configuration extraction.
For example, we know val config = string("USERNAME") from source
represents a program that says, there exists a key called
"USERNAME" (in some ConfigSource called source)
with a value that is of the type String
.
list(config)
would then imply, there exists a list of USERNAME -> value
pair.
Given below is a complete example:
val json = s""" | xyz : [ | { | "USERNAME" : "value1" | }, | | { | "USERNAME" : "value2" | } | ] |""".stripMargin val config = string("USERNAME") // Within the key "xyz", we have a list of key-value pair, where key is always "USERNAME" // NOTE: In HOCON, there is always a need of key (in this case, xyz) at parent level. val listConfig = nested("xyz")(list(config)) val userNames: ZIO[Any, ReadError[String], List[String]] = read(listConfig from TypesafeConfigSource.fromHoconString(json))
returns
List(value1, value2)
NOTE:
nested("xyz")(list(string("USERNAME"))
is same as list("xyz")(string("USERNAME"))
listOrSingleton
is a flexible version of list
.
listOrSingleton
is a flexible version of list
. This means, even if the value is not of the type List
it considers the value a singleton and returns List(singleValue)
We list("xyz")(confgDescriptor)
represents just a list variant of configDescriptor within the key xyz
.
That is list("key")(string)
implies value of key
is of the type List[String]
However if the value of key
was not a list, but instead a simple string, and if we are using listOrSingleton
it will be considered as a List
.
Here is a more detailed example.
val json = s""" | USERNAME : { | "USERNAME" : "abc" | } |""".stripMargin val config = string("USERNAME") val usernames: ZIO[Any, ReadError[String], List[String]] = read(listOrSingleton("configs")(config) from TypesafeConfigSource.fromHoconString(json))
returns
List(value1)
map("xyz")(confgDescriptor)
represents retrieving a map (of key value pairs) that exists within the key "xyz"
map("xyz")(confgDescriptor)
represents retrieving a map (of key value pairs) that exists within the key "xyz"
Let's explain this in detail with an example: int("URL") implies there exists a value of the type string under the key "URL"
On similar lines, map("URL")(int) implies there exists a value of the type Map
under the key URL
and the type of the
value of each key in the map is of the type Int.
Sidee note: Obviously, for complex types such as Map,
you can also rely on zio-config-magnolia that allows you to retrieve
any value of the type Map[String, A] for all type A,
that has an instance of Description
(refer zio-config-magnolia api docs)
val config = map("xyz")(int) val source: ConfigSource = TypesafeConfigSource.fromHoconString( "xyz" : { "key1" : "1" "key2" : "2" "key3" : "3" } ) // Forming a TypesafeConfigSource from string returned an Either (being able to capture errors) because // the HOCON string can be an invalid string. val result = read(config from source) // Right(Map("key1" -> 1, "key2" -> 2, "key3" -> 3))
We explained map
using TypesafeConfigSource. However, for zio-config source doesn't really matter.
For example, lets try to fetch a map from a flattened scala Map.
val source = ConfigSource.fromMap( Map( "xyz_key1" -> "1", "xyz_key2" -> "2", "xyz_key3" -> "3" ), keyDelimiter = Some('_') ) val config = read(config from source) // Right( Map("key1" -> 1, "key2" -> 2, "key3" -> 3))
Retrieve a Map
given an existing ConfigDescriptor
.
Retrieve a Map
given an existing ConfigDescriptor
.
map(configDescriptor)
is similar to map(path)(configDescriptor)
except
that there is no path
associated with it. For the same reason, you would need
the second version given below: def map[A](path: K)(desc: => ConfigDescriptor[A])
Before we try to understand the semantics of map(configDescriptor)
, let's understand the
semantics of map(path)(configDescriptor)
; a function with the same name given below,
but it takes a path as well.
map("xyz")(confgDescriptor)
represents retrieving a map (of key value pairs) that exists within the key "xyz"
Let's explain this in detail with an example: int("URL") implies there exists a value of the type string under the key "URL"
On similar lines, map("URL")(int) implies there exists a value of the type Map
under the key URL
and the type of the
value of each key in the map is of the type Int.
Sidee note: Obviously, for complex types such as Map,
you can also rely on zio-config-magnolia that allows you to retrieve
any value of the type Map[String, A] for all type A,
that has an instance of Description
(refer zio-config-magnolia api docs)
val config = map("xyz")(int) val source: ConfigSource = TypesafeConfigSource.fromHoconString( "xyz" : { "key1" : "1" "key2" : "2" "key3" : "3" } ) // Forming a TypesafeConfigSource from string returned an Either (being able to capture errors) because // the HOCON string can be an invalid string. val result = sourceOrFailed.flatMap(source => read(config from source)) // Map("key1" -> 1, "key2" -> 2, "key3" -> 3)
We explained map
using TypesafeConfigSource. However, for zio-config source doesn't really matter.
For example, lets try to fetch a map from a flattened scala Map.
val source = ConfigSource.fromMap( Map( "xyz_key1" -> "1", "xyz_key2" -> "2", "xyz_key3" -> "3" ), keyDelimiter = Some('_') ) val config = read(config from source) // Map("key1" -> 1, "key2" -> 2, "key3" -> 3)
Now what does it mean if we say val config = map(int("id"))
instead of val config = map("id")(int)
The difference is map("id")(int)
implies there exists a map within the key id
, whose values of are of the type Int
On the other hand map(int("id"))
implies there exists a map hose value is of the type {"id" : "Int"}
Example:
val mapConfig = map(int("id")) // This means there exists a Map whose value is of the type {"String" : "Int"}. val sourceOrFailure: ConfigSource = TypesafeConfigSource.fromHoconString( s""" "abc" : { "key1" : { "id" : "2" }, "key2" : { "id" : "3" } } """" ) val result = read(nested("abc")(map(int("id"))) from source) // Map("key1" -> 1, "key2" -> 2)
This is really useful when the config source consist of a map but you need to fetch the value of the keys in the map from an nested key within itself. In this example it is "id".
nested allows us to retrieve a config from a path K
, where K
is typically String
.
nested allows us to retrieve a config from a path K
, where K
is typically String
.
Example :
val config = nested("key")(string) val mapSource = ConfigSource.fromMap( "key" : "value" ) val result = read(config from mapSource) // "value"
Note that string("key")
is same as that of nested("key")(string)
Similar to list
however the size of the values shouldn't be zero
Similar to list
however the size of the values shouldn't be zero
Similar to list
however the size of the values shouldn't be zero.
Similar to list
however the size of the values shouldn't be zero.
Also it can accept a singleton value as a NonEmptyChunk
set("xyz")(confgDescriptor)
represents just a set variant of configDescriptor within the key xyz
.
set("xyz")(confgDescriptor)
represents just a set variant of configDescriptor within the key xyz
.
Note that, nested("xyz")(set(configDescriptor))
is same as set("xyz")(configDescriptor)
.
For example: set("key")(string)
implies value of key
is of the type Set[String]
Here is a more detailed example.
list("xyz")(string)
would then imply, there exists a set of type String under "xyz"
val json = s""" | xyz : ["a", "b"] |""".stripMargin val source: ConfigSource = TypesafeConfigSource.fromHoconString(json) read(set("xyz")(string) from source)
returns
List(value1, value2)
set("xyz")(confgDescriptor)
represents just a set variant of configDescriptor within the key xyz
.
set("xyz")(confgDescriptor)
represents just a set variant of configDescriptor within the key xyz
.
Note that, nested("xyz")(set(configDescriptor))
is same as set("xyz")(configDescriptor)
.
For example: set("key")(string)
implies value of key
is of the type Set[String]
Here is a more detailed example.
list("xyz")(string)
would then imply, there exists a set of type String under "xyz"
val json = s""" | xyz : ["a", "b"] |""".stripMargin val source: ConfigSource = TypesafeConfigSource.fromHoconString(json) read(set("xyz")(string) from source)
returns
Right(List(value1, value2))