Skip to content

Commit

Permalink
Merge pull request #285 from a-khakimov/hocon-list
Browse files Browse the repository at this point in the history
  • Loading branch information
2m committed Feb 14, 2023
2 parents 8b92418 + 547f9f7 commit 925ec7f
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 2 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/.idea/
/project
/target
/.bsp/
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,23 @@ val config = ConfigFactory.parseString("""
| elements = 2
| burst-duration = 100 millis
| check-interval = 2 weeks
| values = [ first, second ]
|}
""".stripMargin)

case class Rate(elements: Int, burstDuration: FiniteDuration, checkInterval: Period)
case class Rate(
elements: Int,
burstDuration: FiniteDuration,
checkInterval: Period,
values: List[String]
)

val hocon = hoconAt(config)("rate")
(
hocon("elements").as[Int],
hocon("burst-duration").as[FiniteDuration],
hocon("check-interval").as[Period]
hocon("check-interval").as[Period],
hocon("values").as[List[String]]
).parMapN(Rate.apply).load[IO].map { rate =>
assertEquals(rate.burstDuration, 100.millis)
assertEquals(rate.checkInterval, Period.ofWeeks(2))
Expand Down
13 changes: 13 additions & 0 deletions src/main/scala/Hocon.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package lt.dvim.ciris

import scala.jdk.CollectionConverters._
import scala.util.Try

import ciris._
Expand Down Expand Up @@ -48,6 +49,18 @@ trait HoconConfigDecoders {
implicit val stringHoconDecoder: ConfigDecoder[HoconConfigValue, String] =
ConfigDecoder[HoconConfigValue].map(_.atKey("t").getString("t"))

implicit def listHoconDecoder[T](implicit
decoder: ConfigDecoder[HoconConfigValue, T]
): ConfigDecoder[HoconConfigValue, List[T]] =
ConfigDecoder[HoconConfigValue]
.map(_.atKey("t").getList("t").asScala.toList)
.mapEither { (key, list) =>
list.map(decoder.decode(key, _)).partitionMap(identity) match {
case (Nil, rights) => Right(rights)
case (firstLeft :: _, _) => Left(firstLeft)
}
}

implicit val javaTimeDurationHoconDecoder: ConfigDecoder[HoconConfigValue, java.time.Duration] =
ConfigDecoder[HoconConfigValue].map(_.atKey("t").getDuration("t"))

Expand Down
43 changes: 43 additions & 0 deletions src/test/scala/HoconSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ class HoconSpec extends CatsEffectSuite {
| dur = 10 ms
| bool = true
| per = 2 weeks
| listInt = [ 1, 2, 3, 4 ]
| listString = [ a, b, c, d ]
| listBool = [ true, false, true ]
| listDouble = [ 1.12, 2.34, 2.33 ]
| listDur = [ 10 ms, 15 ms, 1 s ]
| invalidList = [ 1, a, true ]
| }
|}
|subst {
Expand Down Expand Up @@ -59,6 +65,43 @@ class HoconSpec extends CatsEffectSuite {
test("parse Period") {
nested("per").as[java.time.Period].load[IO] assertEquals java.time.Period.ofWeeks(2)
}
test("parse List[Int]") {
nested("listInt").as[List[Int]].load[IO] assertEquals List(1, 2, 3, 4)
}
test("parse List[Long]") {
nested("listInt").as[List[Long]].load[IO] assertEquals List(1L, 2, 3, 4)
}
test("parse List[String]") {
nested("listString").as[List[String]].load[IO] assertEquals List("a", "b", "c", "d")
}
test("parse List[Bool]") {
nested("listBool").as[List[Boolean]].load[IO] assertEquals List(true, false, true)
}
test("parse List[Double]") {
nested("listDouble").as[List[Double]].load[IO] assertEquals List(1.12, 2.34, 2.33)
}
test("parse List[java Duration]") {
nested("listDur").as[List[java.time.Duration]].load[IO] assertEquals List(
java.time.Duration.ofMillis(10),
java.time.Duration.ofMillis(15),
java.time.Duration.ofSeconds(1)
)
}
test("parse List[scala Duration]") {
nested("listDur").as[List[FiniteDuration]].load[IO] assertEquals List(10.millis, 15.millis, 1.second)
}
test("handle decode error for invalid list") {
nested("invalidList")
.as[List[Int]]
.attempt[IO]
.map {
case Left(error) => error.messages.toList.head
case Right(_) => "config loaded"
}
.assertEquals(
"Nested.config.invalidList with value a cannot be converted to Int"
)
}
test("handle missing") {
nested("missing")
.as[Int]
Expand Down

0 comments on commit 925ec7f

Please sign in to comment.