8000 Handle empty list keeping backward compatibility by afsalthaj · Pull Request #7891 · zio/zio · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Handle empty list keeping backward compatibility #7891

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions core-tests/shared/src/test/scala/zio/ConfigProviderSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,114 @@ object ConfigProviderSpec extends ZIOBaseSpec {
expectedEmployees = List((10, 0), (20, 1))
expectedDepartments = List(expectedEmployees, expectedEmployees)
} yield assertTrue(result == expectedDepartments)
} +
test("empty list") {
val configProvider =
ConfigProvider.fromMap(
Map(
"departments" -> "<nil>"
)
)

val config = Config.listOf("departments", Config.int)

for {
result <- configProvider.load(config)
} yield assertTrue(result == Nil)
} +
test("empty list optional") {
val configProvider =
ConfigProvider.fromMap(
Map(
"departments" -> "<nil>"
)
)

val config = Config.listOf("departments", Config.int.optional)

for {
result <- configProvider.load(config)
} yield assertTrue(result == Nil)
} +
test("empty list with description") {
val configProvider =
ConfigProvider.fromMap(
Map(
"departments" -> "<nil>"
)
)

val config = Config.listOf("departments", Config.int ?? "Integer")

for {
result <- configProvider.load(config)
} yield assertTrue(result == Nil)
} +
test("empty list with product") {
val configProvider =
ConfigProvider.fromMap(
Map(
"departments" -> "<nil>"
)
)

val member = Config.int("age").zip(Config.string("name"))

val config = Config.listOf("departments", member)

for {
result <- configProvider.load(config)
} yield assertTrue(result == Nil)
} +
test("empty list with product optional") {
val configProvider =
ConfigProvider.fromMap(
Map(
"departments" -> "<nil>"
)
)

val member = Config.int("age").zip(Config.string("name"))

val config = Config.listOf("departments", member.optional)

for {
result <- configProvider.load(config)
} yield assertTrue(result == Nil)
} +
test("empty list with product with description") {
val configProvider =
ConfigProvider.fromMap(
Map(
"departments" -> "<nil>"
)
)

val member = Config.int("age").zip(Config.string("name"))

val config = Config.listOf("departments", member ?? "Member")

for {
result <- configProvider.load(config)
} yield assertTrue(result == Nil)
} +
//FIXME: Failing test
test("empty list within indexed list") {
val configProvider =
ConfigProvider.fromMap(
Map(
"departments[0].ids" -> "<nil>",
"departments[1].ids" -> "1",
"departments[2].ids" -> "1, 2"
)
)

val config = Config.listOf("departments", Config.listOf("ids", Config.int))

for {
result <- configProvider.load(config)
_ = println(result)
} yield assertTrue(result == List(Nil, List(1), List(1, 2)))
}
}
}
40 changes: 26 additions & 14 deletions core/shared/src/main/scala/zio/ConfigProvider.scala
Original file line number Diff line number Diff line change
Expand Up @@ -313,10 +313,12 @@ object ConfigProvider {
.fromEither(primitive.parse(text))
.map(Chunk(_))
.mapError(_.prefixed(path))
else
else {

ZIO
.foreach(splitPathString(text, escapedDelim))(s => ZIO.fromEither(primitive.parse(s.trim)))
.mapError(_.prefixed(path))
}
}

def parsePrimitive[A](
Expand Down Expand Up @@ -466,6 +468,17 @@ object ConfigProvider {
(leftExtension, rightExtension)
}

def returnEmptyListIfValueIsNil[A](
prefix: Chunk[String],
continue: Chunk[String] => ZIO[Any, Error, Chunk[Chunk[A]]]
): ZIO[Any, Error, Chunk[Chunk[A]]] =
(for {
possibleNil <- flat.load(prefix, Config.Text, split = false)
result <- if (possibleNil.headOption.exists(string => string.toLowerCase().trim == "<nil>"))
ZIO.succeed(Chunk(Chunk.empty))
else continue(prefix)
} yield result).orElse(continue(prefix))

def loop[A](prefix: Chunk[String], config: Config[A], split: Boolean)(implicit
trace: Trace
): IO[Config.Error, Chunk[A]] =
Expand All @@ -492,11 +505,12 @@ object ConfigProvider {
.flatMap(set => indicesFrom(set))

values <-
if (indices.isEmpty) loop(prefix, config, split = true).map(Chunk(_))
else
if (indices.isEmpty) {
returnEmptyListIfValueIsNil(prefix = prefix, continue = loop(_, config, split = true).map(Chunk(_)))
} else
ZIO
.foreach(Chunk.fromIterable(indices)) { index =>
loop(prefix :+ QuotedIndex(index), config, split = true)
loop(prefix :+ BracketedIndex(index), config, split = true)
}
.map { chunkChunk =>
val flattened = chunkChunk.flatten
Expand Down Expand Up @@ -691,24 +705,22 @@ object ConfigProvider {
*/
lazy val tag: Tag[ConfigProvider] = Tag[ConfigProvider]

private def indicesFrom(quotedIndices: Set[QuotedIndex]) =
private def indicesFrom(indices: Set[String]) =
ZIO
.foreach(quotedIndices) { quotedIndex =>
ZIO.fromOption(quotedIndex match {
case QuotedIndex(index) => Some(index)
case _ => None
.foreach(indices) { index =>
ZIO.fromOption(index match {
case BracketedIndex(index) => Some(index)
case _ => None
})
}
.mapBoth(_ => Chunk.empty, set => Chunk.fromIterable(set).sorted)
.either
.map(_.merge)

private type QuotedIndex = String

private object QuotedIndex {
private object BracketedIndex {
private lazy val indexRegex = """(\[(\d+)\])""".stripMargin.r

def apply(value: Int): QuotedIndex = s"[${value}]"
def apply(value: Int): String = s"[${value}]"

def unapply(value: String): Option[Int] =
for {
Expand All @@ -729,7 +741,7 @@ object ConfigProvider {
key <- unmakePathString(pathString)
keyWithIndex <-
splitIndexFrom(key) match {
case Some((key, index)) => Chunk(key, QuotedIndex(index))
case Some((key, index)) => Chunk(key, BracketedIndex(index))
case None => Chunk(key)
}
} yield keyWithIndex
Expand Down
0