项目作者: kxbmap

项目描述 :
Scala wrapper for Typesafe config
高级语言: Scala
项目地址: git://github.com/kxbmap/configs.git
创建时间: 2013-04-21T12:46:56Z
项目社区:https://github.com/kxbmap/configs

开源协议:Apache License 2.0

下载


configs

Build Status
Maven Central
Scaladoc

configs is Scala wrapper for Typesafe config.

Usage

Add the following line to your build file:

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

Quick Start

  1. import com.typesafe.config.ConfigFactory
  2. 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:

  1. val config = ConfigFactory.parseString("foo = 42")
  1. val foo = ConfigReader[Int].read(config, "foo")
  2. // foo: configs.Result[Int] = Success(value = 42)
  3. foo.valueOrElse(0)
  4. // res0: Int = 42
  5. val missing = ConfigReader[Int].read(config, "missing")
  6. // missing: configs.Result[Int] = Failure(
  7. // error = ConfigError(
  8. // head = Exceptional(
  9. // throwable = com.typesafe.config.ConfigException$Missing: String: 1: No configuration setting found for key 'missing',
  10. // paths = List("missing")
  11. // ),
  12. // tail = Vector()
  13. // )
  14. // )
  15. missing.valueOrElse(0)
  16. // res1: Int = 0

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

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

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

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

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

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

If failed, Result accumulates error messages:

  1. val config = ConfigFactory.parseString("""
  2. my-config {
  3. bar = 2147483648
  4. baz = [aaa, bbb, ccc]
  5. }
  6. """)
  1. val result = config.get[MyConfig]("my-config")
  2. // result: configs.Result[MyConfig] = Failure(
  3. // error = ConfigError(
  4. // head = Exceptional(
  5. // throwable = com.typesafe.config.ConfigException$Missing: String: 2: No configuration setting found for key 'foo',
  6. // paths = List("my-config", "foo")
  7. // ),
  8. // tail = Vector(
  9. // Exceptional(
  10. // throwable = com.typesafe.config.ConfigException$WrongType: String: 2: bar has type out-of-range value 2147483648 rather than int (32-bit integer),
  11. // paths = List("my-config", "bar")
  12. // ),
  13. // Exceptional(
  14. // throwable = com.typesafe.config.ConfigException$BadValue: String: 4: Invalid value at '0': No number in duration value 'aaa',
  15. // paths = List("my-config", "baz", "0")
  16. // ),
  17. // Exceptional(
  18. // throwable = com.typesafe.config.ConfigException$BadValue: String: 4: Invalid value at '1': No number in duration value 'bbb',
  19. // paths = List("my-config", "baz", "1")
  20. // ),
  21. // Exceptional(
  22. // throwable = com.typesafe.config.ConfigException$BadValue: String: 4: Invalid value at '2': No number in duration value 'ccc',
  23. // paths = List("my-config", "baz", "2")
  24. // )
  25. // )
  26. // )
  27. // )
  28. result.failed.foreach { error =>
  29. error.messages.foreach(println)
  30. }
  31. // [my-config.foo] String: 2: No configuration setting found for key 'foo'
  32. // [my-config.bar] String: 2: bar has type out-of-range value 2147483648 rather than int (32-bit integer)
  33. // [my-config.baz.0] String: 4: Invalid value at '0': No number in duration value 'aaa'
  34. // [my-config.baz.1] String: 4: Invalid value at '1': No number in duration value 'bbb'
  35. // [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:

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

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.

  1. case class ServiceConfig(name: String, port: Int, hosts: List[String])
  2. val config = ConfigFactory.parseString(
  3. """
  4. |name = "foo"
  5. |port = 9876
  6. |hosts = ["localhost", "foo.com"]
  7. """.stripMargin)
  1. (
  2. config.get[String]("name") ~
  3. config.get[Int]("port") ~
  4. config.get[List[String]]("hosts")
  5. )(ServiceConfig) // Alternatively (name, port, hosts) => ServerConfig(name, port, posts)
  6. // res8: configs.Result[ServiceConfig] = Success(
  7. // value = ServiceConfig(
  8. // name = "foo",
  9. // port = 9876,
  10. // hosts = List("localhost", "foo.com")
  11. // )
  12. // )

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:

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

You can get an ADT value from config:

  1. val config = ConfigFactory.parseString("""
  2. tree = {
  3. value = 42
  4. left = Leaf
  5. right {
  6. value = 123
  7. left = Leaf
  8. right = Leaf
  9. }
  10. }
  11. """)
  1. config.get[Tree]("tree")
  2. // res9: configs.Result[Tree] = Success(
  3. // value = Branch(
  4. // value = 42,
  5. // left = Leaf,
  6. // right = Branch(value = 123, left = Leaf, right = Leaf)
  7. // )
  8. // )

Java Beans support

If there is Java Beans class like the follows:

  1. package com.example;
  2. @lombok.Data
  3. public class MyBean {
  4. private int intValue;
  5. private java.util.List<String> stringList;
  6. private java.util.Map<java.util.Locale, java.time.Duration> localeToDuration;
  7. }

Then you define ConfigReader instance using deriveBean macro:

  1. import com.example.MyBean
  2. implicit val myBeanConfigReader: ConfigReader[MyBean] =
  3. ConfigReader.deriveBean[MyBean]

And then you can get Java Beans value:

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

License

Copyright 2013-2016 Tsukasa Kitachi

Apache License, Version 2.0