From 17705f96ac93f714ecc1f4a6bd5496cda64beef3 Mon Sep 17 00:00:00 2001 From: Giorgio Garofalo Date: Sun, 28 Jul 2024 13:01:06 +0200 Subject: [PATCH] Improve errors on invalid ranges in `.read` --- .../quarkdown/function/value/data/Range.kt | 24 +++++++++++++++++++ .../kotlin/eu/iamgio/quarkdown/stdlib/Data.kt | 12 ++++++++-- .../eu/iamgio/quarkdown/stdlib/DataTest.kt | 7 ++++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/core/src/main/kotlin/eu/iamgio/quarkdown/function/value/data/Range.kt b/core/src/main/kotlin/eu/iamgio/quarkdown/function/value/data/Range.kt index 3a5be20f..550d3ad9 100644 --- a/core/src/main/kotlin/eu/iamgio/quarkdown/function/value/data/Range.kt +++ b/core/src/main/kotlin/eu/iamgio/quarkdown/function/value/data/Range.kt @@ -53,6 +53,30 @@ data class Range(val start: Int?, val end: Int?) : Iterable { .iterator() } + /** + * @param bounds range to check if this range is in. Both its `start` and `end` values must be non-null + * @param lowerBound lower bound of [bounds] to use if [start] is `null` + * @param upperBound upper bound of [bounds] to use if [end] is `null` + * @return whether this range is contained within [bounds] + */ + fun isIn( + bounds: Range, + lowerBound: Int = bounds.start!!, + upperBound: Int = bounds.end!!, + ): Boolean { + val start = start ?: lowerBound + val end = end ?: upperBound + return start >= bounds.start!! && end <= bounds.end!! + } + + /** + * Checks if this range (bounds) contains another range. + * Both [start] and [end] of the bounds range must be non-null. + * @param range range to check if this range contains + * @return whether this range contains [range]. + */ + operator fun contains(range: Range) = range.isIn(this) + override fun toString() = "${start ?: ""}..${end ?: ""}" companion object { diff --git a/stdlib/src/main/kotlin/eu/iamgio/quarkdown/stdlib/Data.kt b/stdlib/src/main/kotlin/eu/iamgio/quarkdown/stdlib/Data.kt index e0deb188..4c119921 100644 --- a/stdlib/src/main/kotlin/eu/iamgio/quarkdown/stdlib/Data.kt +++ b/stdlib/src/main/kotlin/eu/iamgio/quarkdown/stdlib/Data.kt @@ -72,9 +72,17 @@ fun read( } // Lines from the file in the given range. - val lines = file.readLines().subList(lineRange) + val lines = file.readLines() - return lines.joinToString(System.lineSeparator()).wrappedAsValue() + // Check if the range is in bounds. + val bounds = Range(1, lines.size) + if (lineRange !in bounds) { + throw FunctionRuntimeException("Invalid range $lineRange in bounds $bounds") + } + + return lines.subList(lineRange) + .joinToString(System.lineSeparator()) + .wrappedAsValue() } /** diff --git a/stdlib/src/test/kotlin/eu/iamgio/quarkdown/stdlib/DataTest.kt b/stdlib/src/test/kotlin/eu/iamgio/quarkdown/stdlib/DataTest.kt index f6aa67c1..07b37884 100644 --- a/stdlib/src/test/kotlin/eu/iamgio/quarkdown/stdlib/DataTest.kt +++ b/stdlib/src/test/kotlin/eu/iamgio/quarkdown/stdlib/DataTest.kt @@ -11,6 +11,7 @@ import java.io.File import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertFails import kotlin.test.assertIs private const val DATA_FOLDER = "src/test/resources/data" @@ -53,6 +54,12 @@ class DataTest { "Line 4${LINE_SEPARATOR}Line 5", read(context, path, Range(4, null)).unwrappedValue, ) + + // Out of bounds ranges. + assertFails { read(context, path, Range(1, 8)) } + assertFails { read(context, path, Range(0, 3)) } + assertFails { read(context, path, Range(null, 9)) } + assertFails { read(context, path, Range(9, null)) } } @Test