diff --git a/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/booleans/booleans.kt b/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/booleans/booleans.kt index ac54c03..e695d4f 100644 --- a/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/booleans/booleans.kt +++ b/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/booleans/booleans.kt @@ -3,15 +3,15 @@ package com.sksamuel.tribune.core.booleans import arrow.core.leftNel import arrow.core.right import com.sksamuel.tribune.core.Parser -import com.sksamuel.tribune.core.flatMap import com.sksamuel.tribune.core.map +import com.sksamuel.tribune.core.transformEither /** * Transforms a String producing [Parser] into a Boolean producing Parser, * by converting the String to a Boolean using the library function [toBoolean]. */ fun Parser.boolean(): Parser = - flatMap { + transformEither { val b = it.toBoolean() b.right() } @@ -21,7 +21,7 @@ fun Parser.boolean(): Parser = * by converting the String to a Boolean using the library function [toBooleanStrict]. */ fun Parser.booleanStrict(ifError: (String) -> E): Parser = - flatMap { input -> + transformEither { input -> runCatching { input.toBooleanStrict() }.fold({ it.right() }, { ifError(input).leftNel() }) } diff --git a/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/doubles/doubles.kt b/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/doubles/doubles.kt index 9114ad7..7587a04 100644 --- a/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/doubles/doubles.kt +++ b/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/doubles/doubles.kt @@ -4,7 +4,7 @@ import arrow.core.leftNel import arrow.core.right import com.sksamuel.tribune.core.Parser import com.sksamuel.tribune.core.filter -import com.sksamuel.tribune.core.flatMap +import com.sksamuel.tribune.core.transformEither /** * Extends a [Parser] of output type string to parse that string into a double. @@ -15,18 +15,18 @@ import com.sksamuel.tribune.core.flatMap * and a null is considered a failing case. */ fun Parser.double(ifError: (String) -> E): Parser = - flatMap { + transformEither { val d = it.toDoubleOrNull() d?.right() ?: ifError(it).leftNel() } fun Parser.positive(ifError: (Double) -> E): Parser = - flatMap { + transformEither { if (it > 0.0) it.right() else ifError(it).leftNel() } fun Parser.negative(ifError: (Double) -> E): Parser = - flatMap { + transformEither { if (it < 0.0) it.right() else ifError(it).leftNel() } @@ -40,6 +40,6 @@ fun Parser.inrange( range: ClosedFloatingPointRange, ifError: (Double) -> E, ): Parser = - flatMap { + transformEither { if (it in range) it.right() else ifError(it).leftNel() } diff --git a/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/enums/enums.kt b/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/enums/enums.kt index 1e24085..e976672 100644 --- a/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/enums/enums.kt +++ b/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/enums/enums.kt @@ -4,7 +4,7 @@ import arrow.core.Either import arrow.core.leftNel import arrow.core.right import com.sksamuel.tribune.core.Parser -import com.sksamuel.tribune.core.flatMap +import com.sksamuel.tribune.core.transformEither import kotlin.reflect.KClass /** @@ -23,13 +23,13 @@ inline fun , E> Parser.enum(noinline * @param enumClass the KClass corresponding to the enum type. */ fun , E> Parser.enum( - enumClass: KClass, - ifError: (String) -> E, + enumClass: KClass, + ifError: (String) -> E, ): Parser { - return flatMap { symbol -> - runCatching { enumClass.java.enumConstants.first { it.name == symbol } } - .fold({ it.right() }, { ifError(symbol).leftNel() }) - } + return transformEither { symbol -> + runCatching { enumClass.java.enumConstants.first { it.name == symbol } } + .fold({ it.right() }, { ifError(symbol).leftNel() }) + } } /** @@ -50,12 +50,12 @@ inline fun , E> Parser.enum(noinline */ @JvmName("enumOrNull") fun , E> Parser.enum( - enumClass: KClass, - ifError: (String) -> E + enumClass: KClass, + ifError: (String) -> E ): Parser { - return flatMap { symbol -> - if (symbol == null) Either.Right(null) - else runCatching { enumClass.java.enumConstants.first { it.name == symbol } } - .fold({ it.right() }, { ifError(symbol).leftNel() }) - } + return transformEither { symbol -> + if (symbol == null) Either.Right(null) + else runCatching { enumClass.java.enumConstants.first { it.name == symbol } } + .fold({ it.right() }, { ifError(symbol).leftNel() }) + } } diff --git a/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/filter.kt b/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/filter.kt index f6fa80f..fe75f5c 100644 --- a/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/filter.kt +++ b/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/filter.kt @@ -18,7 +18,7 @@ import arrow.core.right * @return a parser which rejects input based on the result of predicate [p] */ fun Parser.filter(p: (O) -> Boolean, ifFalse: (O) -> E): Parser { - return flatMap { if (p(it)) it.right() else ifFalse(it).leftNel() } + return transformEither { if (p(it)) it.right() else ifFalse(it).leftNel() } } /** @@ -31,8 +31,8 @@ fun Parser.filter(p: (O) -> Boolean, ifFalse: (O) -> E): Pars * * @return a parser which rejects input based on the result of predicate [p] */ -fun Parser.nullIf(fn: (O) -> Boolean): Parser = - this.map { if (fn(it)) null else it } +fun Parser.nullIf(p: (O) -> Boolean): Parser = + this.map { if (p(it)) null else it } /** * Returns a [Parser] that produces a null if the input value fails to pass the predicate [p]. @@ -47,6 +47,6 @@ fun Parser.nullIf(fn: (O) -> Boolean): Parser * @return a parser which rejects input based on the result of predicate [p] */ @JvmName("nullIfNullable") -fun Parser.nullIf(fn: (O) -> Boolean): Parser = - this.map { if (it == null || fn(it)) null else it } +fun Parser.nullIf(p: (O) -> Boolean): Parser = + this.map { if (it == null || p(it)) null else it } diff --git a/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/floats/floats.kt b/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/floats/floats.kt index f1637d3..4a37eb1 100644 --- a/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/floats/floats.kt +++ b/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/floats/floats.kt @@ -3,7 +3,7 @@ package com.sksamuel.tribune.core.floats import arrow.core.leftNel import arrow.core.right import com.sksamuel.tribune.core.Parser -import com.sksamuel.tribune.core.flatMap +import com.sksamuel.tribune.core.transformEither /** * Extends a [Parser] of output type string to parse that string into a double. @@ -14,7 +14,7 @@ import com.sksamuel.tribune.core.flatMap * and a null is considered a failing case. */ fun Parser.float(ifError: (String) -> E): Parser = - flatMap { + transformEither { val f = it.toFloatOrNull() f?.right() ?: ifError(it).leftNel() } diff --git a/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/ints/ints.kt b/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/ints/ints.kt index 911633e..459e439 100644 --- a/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/ints/ints.kt +++ b/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/ints/ints.kt @@ -5,13 +5,13 @@ import arrow.core.leftNel import arrow.core.right import com.sksamuel.tribune.core.Parser import com.sksamuel.tribune.core.filter -import com.sksamuel.tribune.core.flatMap +import com.sksamuel.tribune.core.transformEither /** * Chains a [Parser] to convert String -> Int. */ fun Parser.int(ifError: (String) -> E): Parser = - flatMap { + transformEither { val i = it.toIntOrNull() i?.right() ?: ifError(it).leftNel() } @@ -30,33 +30,33 @@ fun Parser.nonNegative(ifError: (Int) -> E): Parser fun Parser.negative(ifError: (Int) -> E): Parser = - flatMap { + transformEither { if (it < 0) it.right() else ifError(it).leftNel() } fun Parser.inrange(range: IntRange, ifError: (Int) -> E): Parser = - flatMap { + transformEither { if (it in range) it.right() else ifError(it).leftNel() } fun Parser.min(min: Int, ifError: (Int) -> E): Parser = - flatMap { + transformEither { if (it >= min) it.right() else ifError(it).leftNel() } @JvmName("minOrNull") fun Parser.min(min: Int, ifError: (Int) -> E): Parser = - flatMap { + transformEither { if (it == null) Either.Right(null) else if (it >= min) it.right() else ifError(it).leftNel() } fun Parser.max(min: Int, ifError: (Int) -> E): Parser = - flatMap { + transformEither { if (it >= min) it.right() else ifError(it).leftNel() } @JvmName("maxOrNull") fun Parser.max(min: Int, ifError: (Int) -> E): Parser = - flatMap { + transformEither { if (it == null) Either.Right(null) else if (it >= min) it.right() else ifError(it).leftNel() } diff --git a/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/longs/longs.kt b/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/longs/longs.kt index dfe651c..854204f 100644 --- a/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/longs/longs.kt +++ b/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/longs/longs.kt @@ -4,8 +4,8 @@ import arrow.core.Either import arrow.core.leftNel import arrow.core.right import com.sksamuel.tribune.core.Parser -import com.sksamuel.tribune.core.flatMap import com.sksamuel.tribune.core.map +import com.sksamuel.tribune.core.transformEither /** * Extends a [Parser] of output type string to parse that string into a long. @@ -16,34 +16,34 @@ import com.sksamuel.tribune.core.map * and a null is considered a failing case. */ fun Parser.long(ifError: (String) -> E): Parser = - flatMap { + transformEither { val l = it.toLongOrNull() l?.right() ?: ifError(it).leftNel() } fun Parser.inrange(range: LongRange, ifError: (Long) -> E): Parser = - flatMap { + transformEither { if (it in range) it.right() else ifError(it).leftNel() } fun Parser.min(min: Long, ifError: (Long) -> E): Parser = - flatMap { + transformEither { if (it >= min) it.right() else ifError(it).leftNel() } fun Parser.max(min: Long, ifError: (Long) -> E): Parser = - flatMap { + transformEither { if (it >= min) it.right() else ifError(it).leftNel() } @JvmName("minOrNull") fun Parser.min(min: Long, ifError: (Long) -> E): Parser = - flatMap { + transformEither { if (it == null) Either.Right(null) else if (it >= min) it.right() else ifError(it).leftNel() } @JvmName("maxOrNull") fun Parser.max(min: Long, ifError: (Long) -> E): Parser = - flatMap { + transformEither { if (it == null) Either.Right(null) else if (it >= min) it.right() else ifError(it).leftNel() } diff --git a/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/map.kt b/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/map.kt index bdf5692..8e69abd 100644 --- a/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/map.kt +++ b/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/map.kt @@ -36,5 +36,23 @@ fun Parser.mapIfNotNull(f: (A) -> B): Parser = * * @return a parser which returns the modified and flattened result of this parser. */ +@JvmName("deprecatedFlatMap") +@Deprecated(message = "Deprecated. Use transformEither()", replaceWith = ReplaceWith("transformEither(f)")) fun Parser.flatMap(f: (A) -> EitherNel): Parser = Parser { this@flatMap.parse(it).flatMap(f) } + +/** + * Returns a [Parser] that maps the result of this parser by invoking the given function [f] + * and flattening the output of that function. + * + * @param f the function invoked to map the output of the underlying parser. + * + * @return a parser which returns the modified and flattened result of this parser. + */ +fun Parser.transformEither(f: (O) -> EitherNel): Parser = + Parser { this@transformEither.parse(it).flatMap(f) } + +fun Parser.flatMap(f: (O) -> Parser): Parser = + Parser { i -> + parse(i).flatMap { o -> f(o).parse(i) } + } diff --git a/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/maps/maps.kt b/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/maps/maps.kt index c9028a3..9194271 100644 --- a/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/maps/maps.kt +++ b/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/maps/maps.kt @@ -2,16 +2,16 @@ package com.sksamuel.tribune.core.maps import arrow.core.sequence import com.sksamuel.tribune.core.Parser -import com.sksamuel.tribune.core.flatMap +import com.sksamuel.tribune.core.transformEither fun Parser, E>.parseKeys(parser: Parser): Parser, E> { - return this.flatMap { input -> - input.map { (key, value) -> parser.parse(key).map { Pair(it, value) } }.sequence().map { it.toMap() } - } + return this.transformEither { input -> + input.map { (key, value) -> parser.parse(key).map { Pair(it, value) } }.sequence().map { it.toMap() } + } } fun Parser, E>.parseValues(parser: Parser): Parser, E> { - return this.flatMap { input -> - input.map { (key, value) -> parser.parse(value).map { Pair(key, it) } }.sequence().map { it.toMap() } - } + return this.transformEither { input -> + input.map { (key, value) -> parser.parse(value).map { Pair(key, it) } }.sequence().map { it.toMap() } + } } 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 237b983..9ccb887 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 @@ -4,7 +4,7 @@ import arrow.core.Either import arrow.core.leftNel import arrow.core.right import com.sksamuel.tribune.core.Parser -import com.sksamuel.tribune.core.flatMap +import com.sksamuel.tribune.core.transformEither /** * Narrows an existing String -> String [Parser] by enforcing an exact length on the input string. @@ -18,7 +18,7 @@ import com.sksamuel.tribune.core.flatMap * @return valid if the input string is less than or equal to [len] or an invalid otherwise. */ fun Parser.length(len: Int, ifError: (String) -> E): Parser = - flatMap { + transformEither { when (it.length) { len -> it.right() else -> ifError(it).leftNel() @@ -38,7 +38,7 @@ fun Parser.length(len: Int, ifError: (String) -> E): Parser */ @JvmName("lengthOrNull") fun Parser.length(len: Int, ifError: (String) -> E): Parser = - flatMap { + transformEither { when { it == null -> Either.Right(null) it.length == len -> it.right() @@ -58,7 +58,7 @@ fun Parser.length(len: Int, ifError: (String) -> E): Parse * @return valid if the input string has acceptable length or an invalid otherwise. */ fun Parser.length(f: (Int) -> Boolean, ifError: (String) -> E): Parser = - flatMap { if (f(it.length)) it.right() else ifError(it).leftNel() } + transformEither { if (f(it.length)) it.right() else ifError(it).leftNel() } /** * Narrows an existing I -> String? [Parser] by enforcing a max length on the input string. @@ -73,7 +73,7 @@ fun Parser.length(f: (Int) -> Boolean, ifError: (String) -> */ @JvmName("maxlenOrNull") fun Parser.maxlen(len: Int, ifError: (String) -> E): Parser = - flatMap { + transformEither { when { it == null -> Either.Right(null) it.length > len -> ifError(it).leftNel() @@ -93,7 +93,7 @@ fun Parser.maxlen(len: Int, ifError: (String) -> E): Parse * @return valid if the input string is less than or equal to [len] or an invalid otherwise. */ fun Parser.maxlen(len: Int, ifError: (String) -> E): Parser = - flatMap { + transformEither { when { it.length > len -> ifError(it).leftNel() else -> it.right() @@ -113,7 +113,7 @@ fun Parser.maxlen(len: Int, ifError: (String) -> E): Parser * @return valid if the input string is greater than or equal to [len] or an invalid otherwise. */ fun Parser.minlen(len: Int, ifError: (String) -> E): Parser = - flatMap { + transformEither { when { it.length < len -> ifError(it).leftNel() else -> it.right() @@ -133,7 +133,7 @@ fun Parser.minlen(len: Int, ifError: (String) -> E): Parser */ @JvmName("minlenOrNull") fun Parser.minlen(len: Int, ifError: (String) -> E): Parser = - flatMap { + transformEither { when { it == null -> Either.Right(null) it.length < len -> ifError(it).leftNel() diff --git a/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/strings/strings.kt b/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/strings/strings.kt index 1776c8b..8ba0df8 100644 --- a/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/strings/strings.kt +++ b/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/strings/strings.kt @@ -3,11 +3,7 @@ package com.sksamuel.tribune.core.strings import arrow.core.Either import arrow.core.leftNel import arrow.core.right -import com.sksamuel.tribune.core.Parser -import com.sksamuel.tribune.core.filter -import com.sksamuel.tribune.core.flatMap -import com.sksamuel.tribune.core.map -import com.sksamuel.tribune.core.mapIfNotNull +import com.sksamuel.tribune.core.* /** * Modifies the output of a String producing [Parser] by trimming the output string @@ -48,7 +44,7 @@ fun Parser.match(regex: Regex, ifError: (String) -> E): Par * @return valid if the input string is not null and not blank, otherwise invalid */ fun Parser.notNullOrBlank(ifError: () -> E): Parser { - return flatMap { if (it.isNullOrBlank()) ifError().leftNel() else it.right() } + return transformEither { if (it.isNullOrBlank()) ifError().leftNel() else it.right() } } /** @@ -63,7 +59,7 @@ fun Parser.notNullOrBlank(ifError: () -> E): Parser Parser.notBlank(ifBlank: () -> E): Parser { - return flatMap { + return transformEither { when { it == null -> Either.Right(null) it.isBlank() -> ifBlank().leftNel() @@ -83,7 +79,7 @@ fun Parser.notBlank(ifBlank: () -> E): Parser Parser.nullOrNotBlank(ifBlank: () -> E): Parser { - return flatMap { + return transformEither { if (it == null) null.right() else if (it.isBlank()) ifBlank().leftNel() else it.right() } } diff --git a/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/values.kt b/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/values.kt index b003d3f..a56cae3 100644 --- a/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/values.kt +++ b/tribune-core/src/main/kotlin/com/sksamuel/tribune/core/values.kt @@ -14,6 +14,6 @@ import arrow.core.right * @return a parser which rejects input if not in the allowed list. */ fun Parser.oneOf(values: List, ifFalse: (A) -> E): Parser { - return flatMap { if (values.contains(it)) it.right() else ifFalse(it).leftNel() } + return transformEither { if (values.contains(it)) it.right() else ifFalse(it).leftNel() } } diff --git a/tribune-datetime/src/main/kotlin/com/sksamuel/tribune/datetime/localdate.kt b/tribune-datetime/src/main/kotlin/com/sksamuel/tribune/datetime/localdate.kt index 898593a..f5622cb 100644 --- a/tribune-datetime/src/main/kotlin/com/sksamuel/tribune/datetime/localdate.kt +++ b/tribune-datetime/src/main/kotlin/com/sksamuel/tribune/datetime/localdate.kt @@ -3,7 +3,7 @@ package com.sksamuel.tribune.datetime import arrow.core.leftNel import arrow.core.right import com.sksamuel.tribune.core.Parser -import com.sksamuel.tribune.core.flatMap +import com.sksamuel.tribune.core.transformEither import kotlinx.datetime.LocalDate /** @@ -11,7 +11,7 @@ import kotlinx.datetime.LocalDate */ fun Parser.toLocalDate( ifError: (String, Throwable) -> E -): Parser = flatMap { +): Parser = transformEither { try { LocalDate.parse(it).right() } catch (t: Throwable) { diff --git a/tribune-datetime/src/main/kotlin/com/sksamuel/tribune/datetime/localdatetime.kt b/tribune-datetime/src/main/kotlin/com/sksamuel/tribune/datetime/localdatetime.kt index f987157..3738836 100644 --- a/tribune-datetime/src/main/kotlin/com/sksamuel/tribune/datetime/localdatetime.kt +++ b/tribune-datetime/src/main/kotlin/com/sksamuel/tribune/datetime/localdatetime.kt @@ -3,7 +3,7 @@ package com.sksamuel.tribune.datetime import arrow.core.leftNel import arrow.core.right import com.sksamuel.tribune.core.Parser -import com.sksamuel.tribune.core.flatMap +import com.sksamuel.tribune.core.transformEither import kotlinx.datetime.LocalDateTime /** @@ -12,7 +12,7 @@ import kotlinx.datetime.LocalDateTime */ fun Parser.toLocalTime( ifError: (String, Throwable) -> E -): Parser = flatMap { +): Parser = transformEither { try { LocalDateTime.parse(it).right() } catch (t: Throwable) {