From e28748af5850f74a055aa15f3c594dcb9294cb2c Mon Sep 17 00:00:00 2001 From: Albert Meltzer <7529386+kitbellew@users.noreply.github.com> Date: Fri, 21 May 2021 22:55:36 -0700 Subject: [PATCH] ConfDecoderEx: allow extending collections --- docs/reference.md | 25 +++++++++++++++++++ .../scala/metaconfig/ConfDecoderExT.scala | 20 +++++++++++++++ .../DeriveConfDecoderExJVMSuite.scala | 15 +++++++++-- 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/docs/reference.md b/docs/reference.md index 425ce6df..e489eeaa 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -202,6 +202,31 @@ for `ConfDecoderExT[A, A]`. If a decoder for type `T` is defined, the package defines implicits to derive decoders for `Option[T]`, `Seq[T]` and `Map[String, T]`. +There's also a special for _extending_ collections rather than redefining them +(works only for `ConfDecoderEx`, not the original `ConfDecoder`): +``` +// sets list +a = [ ... ] +// sets map +a = { + b { ... } + c { ... } +} + +// extends list +a = { + // must be the only key + "+" = [ ... ] +} +// extends map +a = { + // must be the only key + "+" = { + d { ... } + } +} +``` + ## ConfEncoder To convert a class instance into `Conf` use `ConfEncoder[T]`. It's possible to diff --git a/metaconfig-core/shared/src/main/scala/metaconfig/ConfDecoderExT.scala b/metaconfig-core/shared/src/main/scala/metaconfig/ConfDecoderExT.scala index a3eb59f2..691b5214 100644 --- a/metaconfig-core/shared/src/main/scala/metaconfig/ConfDecoderExT.scala +++ b/metaconfig-core/shared/src/main/scala/metaconfig/ConfDecoderExT.scala @@ -110,6 +110,17 @@ object ConfDecoderExT { fromPartial( s"Map[String, ${classTag.runtimeClass.getName}]" ) { + case (stateOpt, Conf.Obj(List(("+", Conf.Obj(values))))) => + val res = + buildFrom(none, values, ev, factory)(_._2, (x, y) => (x._1, y)) + res.map { x => + stateOpt.fold(x) { state => + val builder = factory.newBuilder + builder ++= state + builder ++= x + builder.result() + } + } case (_, Conf.Obj(values)) => buildFrom(none, values, ev, factory)(_._2, (x, y) => (x._1, y)) } @@ -136,6 +147,15 @@ object ConfDecoderExT { fromPartial( s"List[${classTag.runtimeClass.getName}]" ) { + case (stateOpt, Conf.Obj(List(("+", Conf.Lst(values))))) => + buildFrom(none, values, ev, factory)(identity, (_, x) => x).map { x => + stateOpt.fold(x) { state => + val builder = factory.newBuilder + builder ++= state + builder ++= x + builder.result() + } + } case (_, Conf.Lst(values)) => buildFrom(none, values, ev, factory)(identity, (_, x) => x) } diff --git a/metaconfig-tests/jvm/src/test/scala/metaconfig/DeriveConfDecoderExJVMSuite.scala b/metaconfig-tests/jvm/src/test/scala/metaconfig/DeriveConfDecoderExJVMSuite.scala index 2199c9c2..59343780 100644 --- a/metaconfig-tests/jvm/src/test/scala/metaconfig/DeriveConfDecoderExJVMSuite.scala +++ b/metaconfig-tests/jvm/src/test/scala/metaconfig/DeriveConfDecoderExJVMSuite.scala @@ -31,12 +31,18 @@ class DeriveConfDecoderExJVMSuite extends munit.FunSuite { | k3 { param = 3 } | } |} + |d { + | "+" = [{ + | b { param = 40 } + | }] + |} |""".stripMargin, Nested( a = 14, c = Nested2(a = "n2", b = OneParam(4), c = Map("k3" -> OneParam(3))), d = Seq( - Nested2("n1", OneParam(2), Map("k1" -> OneParam(1))) + Nested2("n1", OneParam(2), Map("k1" -> OneParam(1))), + Nested2(b = OneParam(40)) ) ), Nested( @@ -54,6 +60,11 @@ class DeriveConfDecoderExJVMSuite extends munit.FunSuite { | a = "xxx" | b { | b { param = 3 } + | c { + | "+" = { + | k3 { param = 33 } + | } + | } | } |} |""".stripMargin, @@ -63,7 +74,7 @@ class DeriveConfDecoderExJVMSuite extends munit.FunSuite { b = Nested2( a = "zzz", b = OneParam(3), - c = Map("k2" -> OneParam(2)) + c = Map("k2" -> OneParam(2), "k3" -> OneParam(33)) ) ) ),