Skip to content

Commit 925ec7f

Browse files
authored
Merge pull request #285 from a-khakimov/hocon-list
2 parents 8b92418 + 547f9f7 commit 925ec7f

File tree

4 files changed

+69
-2
lines changed

4 files changed

+69
-2
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/.idea/
2+
/project
3+
/target
4+
/.bsp/

README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,23 @@ val config = ConfigFactory.parseString("""
4747
| elements = 2
4848
| burst-duration = 100 millis
4949
| check-interval = 2 weeks
50+
| values = [ first, second ]
5051
|}
5152
""".stripMargin)
5253

53-
case class Rate(elements: Int, burstDuration: FiniteDuration, checkInterval: Period)
54+
case class Rate(
55+
elements: Int,
56+
burstDuration: FiniteDuration,
57+
checkInterval: Period,
58+
values: List[String]
59+
)
5460

5561
val hocon = hoconAt(config)("rate")
5662
(
5763
hocon("elements").as[Int],
5864
hocon("burst-duration").as[FiniteDuration],
59-
hocon("check-interval").as[Period]
65+
hocon("check-interval").as[Period],
66+
hocon("values").as[List[String]]
6067
).parMapN(Rate.apply).load[IO].map { rate =>
6168
assertEquals(rate.burstDuration, 100.millis)
6269
assertEquals(rate.checkInterval, Period.ofWeeks(2))

src/main/scala/Hocon.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package lt.dvim.ciris
1818

19+
import scala.jdk.CollectionConverters._
1920
import scala.util.Try
2021

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

52+
implicit def listHoconDecoder[T](implicit
53+
decoder: ConfigDecoder[HoconConfigValue, T]
54+
): ConfigDecoder[HoconConfigValue, List[T]] =
55+
ConfigDecoder[HoconConfigValue]
56+
.map(_.atKey("t").getList("t").asScala.toList)
57+
.mapEither { (key, list) =>
58+
list.map(decoder.decode(key, _)).partitionMap(identity) match {
59+
case (Nil, rights) => Right(rights)
60+
case (firstLeft :: _, _) => Left(firstLeft)
61+
}
62+
}
63+
5164
implicit val javaTimeDurationHoconDecoder: ConfigDecoder[HoconConfigValue, java.time.Duration] =
5265
ConfigDecoder[HoconConfigValue].map(_.atKey("t").getDuration("t"))
5366

src/test/scala/HoconSpec.scala

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ class HoconSpec extends CatsEffectSuite {
3131
| dur = 10 ms
3232
| bool = true
3333
| per = 2 weeks
34+
| listInt = [ 1, 2, 3, 4 ]
35+
| listString = [ a, b, c, d ]
36+
| listBool = [ true, false, true ]
37+
| listDouble = [ 1.12, 2.34, 2.33 ]
38+
| listDur = [ 10 ms, 15 ms, 1 s ]
39+
| invalidList = [ 1, a, true ]
3440
| }
3541
|}
3642
|subst {
@@ -59,6 +65,43 @@ class HoconSpec extends CatsEffectSuite {
5965
test("parse Period") {
6066
nested("per").as[java.time.Period].load[IO] assertEquals java.time.Period.ofWeeks(2)
6167
}
68+
test("parse List[Int]") {
69+
nested("listInt").as[List[Int]].load[IO] assertEquals List(1, 2, 3, 4)
70+
}
71+
test("parse List[Long]") {
72+
nested("listInt").as[List[Long]].load[IO] assertEquals List(1L, 2, 3, 4)
73+
}
74+
test("parse List[String]") {
75+
nested("listString").as[List[String]].load[IO] assertEquals List("a", "b", "c", "d")
76+
}
77+
test("parse List[Bool]") {
78+
nested("listBool").as[List[Boolean]].load[IO] assertEquals List(true, false, true)
79+
}
80+
test("parse List[Double]") {
81+
nested("listDouble").as[List[Double]].load[IO] assertEquals List(1.12, 2.34, 2.33)
82+
}
83+
test("parse List[java Duration]") {
84+
nested("listDur").as[List[java.time.Duration]].load[IO] assertEquals List(
85+
java.time.Duration.ofMillis(10),
86+
java.time.Duration.ofMillis(15),
87+
java.time.Duration.ofSeconds(1)
88+
)
89+
}
90+
test("parse List[scala Duration]") {
91+
nested("listDur").as[List[FiniteDuration]].load[IO] assertEquals List(10.millis, 15.millis, 1.second)
92+
}
93+
test("handle decode error for invalid list") {
94+
nested("invalidList")
95+
.as[List[Int]]
96+
.attempt[IO]
97+
.map {
98+
case Left(error) => error.messages.toList.head
99+
case Right(_) => "config loaded"
100+
}
101+
.assertEquals(
102+
"Nested.config.invalidList with value a cannot be converted to Int"
103+
)
104+
}
62105
test("handle missing") {
63106
nested("missing")
64107
.as[Int]

0 commit comments

Comments
 (0)