diff --git a/core/src/main/scala/borscht/Node.scala b/core/src/main/scala/borscht/Node.scala index f36f169..1006e78 100644 --- a/core/src/main/scala/borscht/Node.scala +++ b/core/src/main/scala/borscht/Node.scala @@ -71,6 +71,18 @@ trait CfgNode extends Node with Iterable[(String, Node)] : final def map[T: NodeParser](ref: String*): Map[String, T] = get[Map[String, T]](ref: _*) getOrElse Map.empty + final def properties(ref: String*): Map[String, String] = + node(ref: _*) map (properties(ref.mkString("."), _, Map.empty)) getOrElse Map.empty + + private def properties(prefix: String, node: Node, result: Map[String, String]): Map[String, String] = node match + case scalar: ScalarNode => result + (prefix -> scalar.asString) + case cfg: CfgNode => + val p = prefix + "." + (cfg foldLeft result) { case (r, (k, n)) => properties(p + k, n, r) } + case seq: SeqNode => + val p = prefix + "." + (seq.iterator.zipWithIndex foldLeft result) { case (r, (n, i)) => properties(p + i, n, r) } + final def node(ref: String*): Option[Node] = if (ref.isEmpty) Some(this) else val it = ref.iterator diff --git a/test/src/test/scala/borscht/CfgNodeTest.scala b/test/src/test/scala/borscht/CfgNodeTest.scala index a4781e2..35c3e44 100644 --- a/test/src/test/scala/borscht/CfgNodeTest.scala +++ b/test/src/test/scala/borscht/CfgNodeTest.scala @@ -1,7 +1,7 @@ package borscht import borscht.parsers.NodeParserString -import borscht.test.cfg +import borscht.test.* import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers @@ -33,3 +33,22 @@ class CfgNodeTest extends AnyFlatSpec with Matchers: it should "throw a NodeParserException if there are multiple keys from the map" in { a[NodeParserException] should be thrownBy cfg("key1" -> "value", "key2" -> "value").oneOf(OneOfMap) } + + "properties" should "return empty map for unexistent node" in { + cfg().properties("unexistent") shouldBe empty + } + + it should "return a correct map" in { + cfg("key1" -> + cfg("key2" -> + cfg("cfg" -> cfg( + "seq" -> seq(1, 2, 3), + "scalar1" -> "value1", + "scalar2" -> "value2", + "empty" -> cfg())))).properties("key1", "key2") shouldEqual Map( + "key1.key2.cfg.seq.0" -> "1", + "key1.key2.cfg.seq.1" -> "2", + "key1.key2.cfg.seq.2" -> "3", + "key1.key2.cfg.scalar1" -> "value1", + "key1.key2.cfg.scalar2" -> "value2") + } diff --git a/test/src/test/scala/borscht/typed/types/TimeTypesTest.scala b/test/src/test/scala/borscht/typed/types/TimeTypesTest.scala index a64514a..739af13 100644 --- a/test/src/test/scala/borscht/typed/types/TimeTypesTest.scala +++ b/test/src/test/scala/borscht/typed/types/TimeTypesTest.scala @@ -59,12 +59,13 @@ class TimeTypesTest extends AnyFlatSpec with Matchers: it should "be parsed with adjuster and time zone" in { val zone = "Europe/London" - val before = ZonedDateTime.now.minusMonths(1) + val zoneId = ZoneId.of(zone) + val before = ZonedDateTime.now(zoneId).minusMonths(1) val now = RefTypeNow(zone).parser(Nil) map (_(scalar("-0000-01-00"))) now match case Right(ref: Ref[?]) => val value = ref.cast[ZonedDateTime].value - val after = ZonedDateTime.now.minusMonths(1) + val after = ZonedDateTime.now(zoneId).minusMonths(1) value.getZone shouldBe ZoneId.of(zone) value should (be >= before and be <= after) case _ => fail("Unexpected result $now")