configs

Scala wrapper for Typesafe config

License

License

Categories

Categories

config Application Layer Libs Configuration
GroupId

GroupId

com.github.kxbmap
ArtifactId

ArtifactId

configs_2.12
Last Version

Last Version

0.6.1
Release Date

Release Date

Type

Type

jar
Description

Description

configs
Scala wrapper for Typesafe config
Project URL

Project URL

https://github.com/kxbmap/configs
Project Organization

Project Organization

com.github.kxbmap
Source Code Management

Source Code Management

https://github.com/kxbmap/configs

Download configs_2.12

How to add to project

<!-- https://jarcasting.com/artifacts/com.github.kxbmap/configs_2.12/ -->
<dependency>
    <groupId>com.github.kxbmap</groupId>
    <artifactId>configs_2.12</artifactId>
    <version>0.6.1</version>
</dependency>
// https://jarcasting.com/artifacts/com.github.kxbmap/configs_2.12/
implementation 'com.github.kxbmap:configs_2.12:0.6.1'
// https://jarcasting.com/artifacts/com.github.kxbmap/configs_2.12/
implementation ("com.github.kxbmap:configs_2.12:0.6.1")
'com.github.kxbmap:configs_2.12:jar:0.6.1'
<dependency org="com.github.kxbmap" name="configs_2.12" rev="0.6.1">
  <artifact name="configs_2.12" type="jar" />
</dependency>
@Grapes(
@Grab(group='com.github.kxbmap', module='configs_2.12', version='0.6.1')
)
libraryDependencies += "com.github.kxbmap" % "configs_2.12" % "0.6.1"
[com.github.kxbmap/configs_2.12 "0.6.1"]

Dependencies

compile (4)

Group / Artifact Type Version
org.scala-lang : scala-library jar 2.12.13
com.typesafe : config jar 1.4.1
org.apache.commons : commons-text jar 1.9
org.scala-lang.modules : scala-collection-compat_2.12 jar 2.4.3

provided (1)

Group / Artifact Type Version
org.scala-lang : scala-reflect jar 2.12.13

test (3)

Group / Artifact Type Version
org.projectlombok : lombok jar 1.18.20
com.github.scalaprops : scalaprops_2.12 jar 0.8.2
com.github.scalaprops : scalaprops-scalaz_2.12 jar 0.8.2

Project Modules

There are no modules declared in this project.

configs

Build Status Maven Central Scaladoc

configs is Scala wrapper for Typesafe config.

Usage

Add the following line to your build file:

libraryDependencies += "com.github.kxbmap" %% "configs" % "0.6.1"

Quick Start

import com.typesafe.config.ConfigFactory
import configs.ConfigReader

Result type of get a value from config is configs.Result. If get successfully, returns configs.Result.Success, if not configs.Result.Failure:

val config = ConfigFactory.parseString("foo = 42")
val foo = ConfigReader[Int].read(config, "foo")
// foo: configs.Result[Int] = Success(value = 42)

foo.valueOrElse(0)
// res0: Int = 42

val missing = ConfigReader[Int].read(config, "missing")
// missing: configs.Result[Int] = Failure(
//   error = ConfigError(
//     head = Exceptional(
//       throwable = com.typesafe.config.ConfigException$Missing: String: 1: No configuration setting found for key 'missing',
//       paths = List("missing")
//     ),
//     tail = Vector()
//   )
// )

missing.valueOrElse(0)
// res1: Int = 0

Import configs.syntax._ provides extension methods for Config:

import configs.syntax._
config.get[Int]("foo")
// res2: configs.Result[Int] = Success(value = 42)

get[Option[A]] will return success with value None if path is not exists:

config.get[Option[Int]]("missing")
// res3: configs.Result[Option[Int]] = Success(value = None)

config.getOrElse("missing", 0) // Alias for config.get[Option[Int]]("missing").map(_.getOrElse(0))
// res4: configs.Result[Int] = Success(value = 0)

You can get a case class value out of the box:

import scala.concurrent.duration.FiniteDuration

case class MyConfig(foo: String, bar: Int, baz: List[FiniteDuration])
val config = ConfigFactory.parseString("""
  my-config {
    foo = My config value
    bar = 123456
    baz = [1h, 2m, 3s]
  }
  """)
config.get[MyConfig]("my-config")
// res5: configs.Result[MyConfig] = Success(
//   value = MyConfig(
//     foo = "My config value",
//     bar = 123456,
//     baz = List(1 hour, 2 minutes, 3 seconds)
//   )
// )

If failed, Result accumulates error messages:

val config = ConfigFactory.parseString("""
  my-config {
    bar = 2147483648
    baz = [aaa, bbb, ccc]
  }
  """)
val result = config.get[MyConfig]("my-config")
// result: configs.Result[MyConfig] = Failure(
//   error = ConfigError(
//     head = Exceptional(
//       throwable = com.typesafe.config.ConfigException$Missing: String: 2: No configuration setting found for key 'foo',
//       paths = List("my-config", "foo")
//     ),
//     tail = Vector(
//       Exceptional(
//         throwable = com.typesafe.config.ConfigException$WrongType: String: 2: bar has type out-of-range value 2147483648 rather than int (32-bit integer),
//         paths = List("my-config", "bar")
//       ),
//       Exceptional(
//         throwable = com.typesafe.config.ConfigException$BadValue: String: 4: Invalid value at '0': No number in duration value 'aaa',
//         paths = List("my-config", "baz", "0")
//       ),
//       Exceptional(
//         throwable = com.typesafe.config.ConfigException$BadValue: String: 4: Invalid value at '1': No number in duration value 'bbb',
//         paths = List("my-config", "baz", "1")
//       ),
//       Exceptional(
//         throwable = com.typesafe.config.ConfigException$BadValue: String: 4: Invalid value at '2': No number in duration value 'ccc',
//         paths = List("my-config", "baz", "2")
//       )
//     )
//   )
// )

result.failed.foreach { error =>
  error.messages.foreach(println)
}
// [my-config.foo] String: 2: No configuration setting found for key 'foo'
// [my-config.bar] String: 2: bar has type out-of-range value 2147483648 rather than int (32-bit integer)
// [my-config.baz.0] String: 4: Invalid value at '0': No number in duration value 'aaa'
// [my-config.baz.1] String: 4: Invalid value at '1': No number in duration value 'bbb'
// [my-config.baz.2] String: 4: Invalid value at '2': No number in duration value 'ccc'

You can get a value without key using extract:

val config = ConfigFactory.parseString("""
  foo = My config value
  bar = 123456
  baz = [1h, 2m, 3s]
  """)
config.extract[MyConfig]
// res7: configs.Result[MyConfig] = Success(
//   value = MyConfig(
//     foo = "My config value",
//     bar = 123456,
//     baz = List(1 hour, 2 minutes, 3 seconds)
//   )
// )

You may use the ~ operator to combine multiple results and apply a function with the results passed as arguments, this is useful when you want to construct a complex case class from several config extractors.

case class ServiceConfig(name: String, port: Int, hosts: List[String])

val config = ConfigFactory.parseString(
  """
    |name = "foo"
    |port = 9876
    |hosts = ["localhost", "foo.com"]
  """.stripMargin)
(
  config.get[String]("name") ~
  config.get[Int]("port") ~
  config.get[List[String]]("hosts")
)(ServiceConfig) // Alternatively (name, port, hosts) => ServerConfig(name, port, posts)
// res8: configs.Result[ServiceConfig] = Success(
//   value = ServiceConfig(
//     name = "foo",
//     port = 9876,
//     hosts = List("localhost", "foo.com")
//   )
// )

Supported types

configs can get many type values from config. It is provided by type class ConfigReader.

There are a number of built-in ConfigReader instances:

  • Primitive/Wrapper types
    • Long, Int, Short, Byte, Double, Float, Char, Boolean
    • java.lang.{Long, Integer, Short, Byte, Double, Float, Character, Boolean}
  • Big number types
    • BigInt, BigDecimal
    • java.math.{BigInteger, BigDecimal}
  • String representation types
    • String
    • Symbol, java.util.{UUID, Locale}
    • java.io.File, java.nio.file.Path
    • java.net.{URI, InetAddress}
  • Duration types
    • java.time.Duration
    • scala.concurrent.duration.{Duration, FiniteDuration}
  • Config types
    • com.typesafe.config.{Config, ConfigValue, ConfigList, ConfigObject, ConfigMemorySize}
    • configs.Bytes
  • Enum types
    • Java enum types
    • Scala Enumeration types
  • Collection types
    • F[A] (using CanBuildFrom[Nothing, A, F[A]], e.g. List[String], Seq[Int])
    • M[S, A] (using CanBuildFrom[Nothing, (S, A), M[S, A]], e.g. Map[String, Int], TreeMap[UUID, Config])
    • java.util.{List[A], Map[S, A], Set[A], Collection[A]}, java.lang.Iterable[A]
    • java.util.Properties
  • Optional types
    • Option[A]
    • java.util.{Optional[A], OptionalLong, OptionalInt, OptionalDouble}
  • case classes
  • ADTs (sealed trait + classes/objects). See ADTs support
  • Java Beans. See Java Beans support

In this list, A means any type that is ConfigReader instance. And S means any type that is StringConverter instance.

ADTs support

If there is such an ADT:

sealed trait Tree
case class Branch(value: Int, left: Tree, right: Tree) extends Tree
case object Leaf extends Tree

You can get an ADT value from config:

val config = ConfigFactory.parseString("""
  tree = {
    value = 42
    left = Leaf
    right {
      value = 123
      left = Leaf
      right = Leaf
    }
  }
  """)
config.get[Tree]("tree")
// res9: configs.Result[Tree] = Success(
//   value = Branch(
//     value = 42,
//     left = Leaf,
//     right = Branch(value = 123, left = Leaf, right = Leaf)
//   )
// )

Java Beans support

If there is Java Beans class like the follows:

package com.example;

@lombok.Data
public class MyBean {
    private int intValue;
    private java.util.List<String> stringList;
    private java.util.Map<java.util.Locale, java.time.Duration> localeToDuration;
}

Then you define ConfigReader instance using deriveBean macro:

import com.example.MyBean

implicit val myBeanConfigReader: ConfigReader[MyBean] =
  ConfigReader.deriveBean[MyBean]

And then you can get Java Beans value:

val config = ConfigFactory.parseString("""
  int-value = 42
  string-list = [foo, bar, baz]
  locale-to-duration {
    ja_JP = 42ms
    en_US = 123s
  }
  """)
config.extract[MyBean]
// res11: configs.Result[MyBean] = Success(
//   value = MyBean(intValue=42, stringList=[foo, bar, baz], localeToDuration={en_US=PT2M3S, ja_JP=PT0.042S})
// )

License

Copyright 2013-2016 Tsukasa Kitachi

Apache License, Version 2.0

Versions

Version
0.6.1
0.6.0
0.5.0
0.4.4