All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.circe.config.syntax.scala Maven / Gradle / Ivy

There is a newer version: 0.10.1
Show newest version
/*
 * Copyright 2017 Jonas Fonseca
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.circe
package config

import com.typesafe.config._
import cats.syntax.either._
import scala.concurrent.duration._

/**
 * Implicits for decoding Typesafe Config values and instances using
 * [[io.circe.Decoder circe decoders]].
 *
 * In addition to [[syntax.durationDecoder]] and [[syntax.memorySizeDecoder]]
 * for reading Typesafe Config specific value formats, this module also provides
 * [[syntax.CirceConfigOps]] for decoding loaded configurations.
 *
 * @example
 * {{{
 * scala> import io.circe.generic.auto._
 * scala> import io.circe.config.syntax._
 * scala> import scala.concurrent.duration.FiniteDuration
 * scala> case class ServerSettings(port: Int, host: String, timeout: FiniteDuration)
 * scala> val config = com.typesafe.config.ConfigFactory.parseString("port = 7357, host = localhost, timeout = 5 s")
 * scala> config.as[ServerSettings]
 * res0: Either[io.circe.Error, ServerSettings] = Right(ServerSettings(7357,localhost,5 seconds))
 * }}}
 */
object syntax {
  /**
   * Decoder for reading
   * [[https://github.com/lightbend/config/blob/master/HOCON.md#duration-format duration formats]].
   *
   * @example
   * {{{
   * scala> import io.circe.Json
   * scala> import io.circe.config.syntax._
   * scala> import scala.concurrent.duration.FiniteDuration
   *
   * scala> durationDecoder.decodeJson(Json.fromString("5 seconds"))
   * res0: io.circe.Decoder.Result[FiniteDuration] = Right(5 seconds)
   * scala> durationDecoder.decodeJson(Json.fromString("1 hour"))
   * res1: io.circe.Decoder.Result[FiniteDuration] = Right(1 hour)
   *
   * scala> Json.fromString("200 ms").as[FiniteDuration]
   * res2: io.circe.Decoder.Result[FiniteDuration] = Right(200 milliseconds)
   * }}}
   */
  implicit val durationDecoder: Decoder[FiniteDuration] = Decoder.decodeString.emap { str =>
    Either.catchNonFatal {
      val d = ConfigValueFactory.fromAnyRef(str).atKey("d").getDuration("d")
      Duration.fromNanos(d.toNanos)
    }.leftMap(t => "Decoder[FiniteDuration]")
  }

  /**
   * Decoder for reading
   * [[https://github.com/lightbend/config/blob/master/HOCON.md#size-in-bytes-format memory size in bytes format]]
   * into a
   * [[https://lightbend.github.io/config/latest/api/com/typesafe/config/ConfigMemorySize.html com.typesafe.config.ConfigMemorySize]].
   *
   * @example
   * {{{
   * scala> import io.circe.Json
   * scala> import io.circe.config.syntax._
   * scala> import com.typesafe.config.ConfigMemorySize
   *
   * scala> memorySizeDecoder.decodeJson(Json.fromString("128M"))
   * res0: io.circe.Decoder.Result[ConfigMemorySize] = Right(ConfigMemorySize(134217728))
   * scala> memorySizeDecoder.decodeJson(Json.fromString("4096 KiB"))
   * res1: io.circe.Decoder.Result[ConfigMemorySize] = Right(ConfigMemorySize(4194304))
   *
   * scala> Json.fromString("32 GB").as[ConfigMemorySize]
   * res2: io.circe.Decoder.Result[ConfigMemorySize] = Right(ConfigMemorySize(32000000000))
   * }}}
   */
  implicit val memorySizeDecoder: Decoder[ConfigMemorySize] = Decoder.decodeString.emap { str =>
    Either
      .catchNonFatal(ConfigValueFactory.fromAnyRef(str).atKey("m").getMemorySize("m"))
      .leftMap(t => "Decoder[ConfigMemorySize]")
  }

  /**
   * Decoder for converting [[io.circe.Json]] to
   * [[https://lightbend.github.io/config/latest/api/com/typesafe/config/ConfigValue.html com.typesafe.config.ConfigValue]].
   *
   * Maps any circe JSON AST to the Typesafe Config AST.
   *
   * @example
   * {{{
   * scala> import io.circe.Json
   * scala> import com.typesafe.config.ConfigValue
   * scala> import io.circe.config.syntax._
   *
   * scala> val hostJson = Json.fromString("localhost")
   * scala> val portJson = Json.fromInt(8080)
   * scala> val serverJson = Json.obj("host" -> hostJson, "port" -> portJson)
   *
   * scala> configValueDecoder.decodeJson(hostJson)
   * res0: io.circe.Decoder.Result[ConfigValue] = Right(Quoted("localhost"))
   *
   * scala> configValueDecoder.decodeJson(portJson)
   * res1: io.circe.Decoder.Result[ConfigValue] = Right(ConfigLong(8080))
   *
   * scala> serverJson.as[ConfigValue]
   * res2: io.circe.Decoder.Result[ConfigValue] = Right(SimpleConfigObject({"host":"localhost","port":8080}))
   * }}}
   *
   * @see [[configDecoder]] for decoding circe JSON objects to a Typesafe Config instance.
   */
  implicit val configValueDecoder: Decoder[ConfigValue] = Decoder.decodeJson.emap { json =>
    Either.catchNonFatal(jsonToConfigValue(json)).leftMap(t => "Decoder[ConfigValue]")
  }

  /**
   * Decoder for converting [[io.circe.Json]] to
   * [[https://lightbend.github.io/config/latest/api/com/typesafe/config/Config.html com.typesafe.config.Config]].
   *
   * Converts a circe JSON object to a Typesafe Config instance.
   *
   * @example
   * {{{
   * scala> import io.circe.Json
   * scala> import com.typesafe.config.Config
   * scala> import io.circe.config.syntax._
   *
   * scala> val hostJson = Json.fromString("localhost")
   * scala> val portJson = Json.fromInt(8080)
   * scala> val serverJson = Json.obj("host" -> hostJson, "port" -> portJson)
   *
   * scala> configDecoder.decodeJson(Json.obj("host" -> hostJson))
   * res0: io.circe.Decoder.Result[Config] = Right(Config(SimpleConfigObject({"host":"localhost"})))
   * scala> serverJson.as[Config]
   * res1: io.circe.Decoder.Result[Config] = Right(Config(SimpleConfigObject({"host":"localhost","port":8080})))
   *
   * scala> portJson.as[Config]
   * res2: io.circe.Decoder.Result[Config] = Left(DecodingFailure(JSON must be an object, was type NUMBER, List()))
   * }}}
   *
   * @see [[configValueDecoder]] for decoding any circe JSON AST.
   */
  implicit val configDecoder: Decoder[Config] = configValueDecoder.emap { value =>
    value match {
      case obj: ConfigObject => Right(obj.toConfig)
      case other => Left(s"JSON must be an object, was type ${other.valueType}")
    }
  }

  /**
   * Enriches
   * [[https://lightbend.github.io/config/latest/api/com/typesafe/config/Config.html com.typesafe.config.Config]]
   * instances with methods to decode to a specific type.
   */
  implicit class CirceConfigOps(val config: Config) extends AnyVal {
    /**
     * Read config settings into the specified type.
     */
    def as[A: Decoder]: Either[io.circe.Error, A] =
      parser.decode[A](config)

    /**
     * Read config settings at given path into the specified type.
     */
    def as[A: Decoder](path: String): Either[io.circe.Error, A] =
      if (config.hasPath(path)) parser.decode[A](config.getConfig(path))
      else Left(ParsingFailure("Path not found in config", new ConfigException.Missing(path)))
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy