diff --git a/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/strings/lengths.kt b/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/strings/lengths.kt index 6ffdcb9..98eb7b6 100644 --- a/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/strings/lengths.kt +++ b/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/strings/lengths.kt @@ -1,5 +1,6 @@ package com.sksamuel.tribune.core.strings +import arrow.core.Either import arrow.core.leftNel import arrow.core.right import com.sksamuel.tribune.core.Parser @@ -39,7 +40,28 @@ fun Parser.length(f: (Int) -> Boolean, ifError: (String) -> flatMap { if (f(it.length)) it.right() else ifError(it).leftNel() } /** - * Narrows an existing String -> String [Parser] by enforcing a max length on the input string. + * Narrows an existing I -> String? [Parser] by enforcing a max length on the input string. + * + * For strings which have length longer than the given [len] arguments, an invalid is + * returned with the error message generated by [ifError]. + * + * @param len the max length, inclusive + * @param ifError the error generating function + * + * @return valid if the input string is less than or equal to [len] or an invalid otherwise. + */ +@JvmName("maxlenOrNull") +fun Parser.maxlen(len: Int, ifError: (String) -> E): Parser = + flatMap { + when { + it == null -> Either.Right(null) + it.length > len -> ifError(it).leftNel() + else -> it.right() + } + } + +/** + * Narrows an existing I -> String? [Parser] by enforcing a max length on the input string. * * For strings which have length longer than the given [len] arguments, an invalid is * returned with the error message generated by [ifError]. @@ -59,7 +81,7 @@ fun Parser.maxlen(len: Int, ifError: (String) -> E): Parser /** - * Narrows an existing String -> String [Parser] by enforcing a min length on the input string. + * Narrows an existing I -> String [Parser] by enforcing a min length on the input string. * * For strings which have length shorter than the given [len] arguments, an invalid is * returned with the error message generated by [ifError]. @@ -76,3 +98,24 @@ fun Parser.minlen(len: Int, ifError: (String) -> E): Parser else -> it.right() } } + +/** + * Narrows an existing I -> String? [Parser] by enforcing a min length on the input string. + * + * For strings which have length shorter than the given [len] arguments, an invalid is + * returned with the error message generated by [ifError]. + * + * @param len the max length, inclusive + * @param ifError the error generating function + * + * @return valid if the input string is greater than or equal to [len] or an invalid otherwise. + */ +@JvmName("minlenOrNull") +fun Parser.minlen(len: Int, ifError: (String) -> E): Parser = + flatMap { + when { + it == null -> Either.Right(null) + it.length < len -> ifError(it).leftNel() + else -> it.right() + } + } diff --git a/tribune-core/src/test/kotlin/com/sksamuel/tribune/core/strings.kt b/tribune-core/src/test/kotlin/com/sksamuel/tribune/core/strings.kt index 5d6266d..70e2309 100644 --- a/tribune-core/src/test/kotlin/com/sksamuel/tribune/core/strings.kt +++ b/tribune-core/src/test/kotlin/com/sksamuel/tribune/core/strings.kt @@ -1,5 +1,6 @@ package com.sksamuel.tribune.core +import arrow.core.Either import arrow.core.leftNel import arrow.core.right import com.sksamuel.tribune.core.strings.length @@ -35,11 +36,26 @@ class StringTest : FunSpec() { p.parse("abcd") shouldBe Foo("abcd").right() } + test("min length or null") { + val p = Parser().minlen(4) { "too short" }.mapIfNotNull { Foo(it) } + p.parse("abc") shouldBe "too short".leftNel() + p.parse("abcd") shouldBe Foo("abcd").right() + p.parse(null) shouldBe Either.Right(null) + } + test("max length") { - val p = Parser().maxlen(4) { "too long" }.map { Foo(it) } + val p = Parser().maxlen(4) { "too long" }.notNull { "not null" }.map { Foo(it) } + p.parse("abcde") shouldBe "too long".leftNel() + p.parse("abcd") shouldBe Foo("abcd").right() + p.parse("abc") shouldBe Foo("abc").right() + } + + test("max length on nullable string") { + val p = Parser().maxlen(4) { "too long" }.mapIfNotNull { Foo(it) } p.parse("abcde") shouldBe "too long".leftNel() p.parse("abcd") shouldBe Foo("abcd").right() p.parse("abc") shouldBe Foo("abc").right() + p.parse(null) shouldBe Either.Right(null) } test("length") {