Skip to content

Commit

Permalink
Improved nullIf
Browse files Browse the repository at this point in the history
  • Loading branch information
sksamuel committed Dec 30, 2023
1 parent 4669514 commit 0996104
Show file tree
Hide file tree
Showing 8 changed files with 46 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ 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

/**
* Extends a [Parser] of output type string to parse that string into a double.
Expand Down Expand Up @@ -44,10 +43,3 @@ fun <I, E> Parser<I, Double, E>.inrange(
flatMap {
if (it in range) it.right() else ifError(it).leftNel()
}

fun <I, E> Parser<I, Double, E>.nullIf(fn: (Double) -> Boolean): Parser<I, Double?, E> =
this.map { if (fn(it)) null else it }

@JvmName("nullIfNullable")
fun <I, E> Parser<I, Double?, E>.nullIf(fn: (Double) -> Boolean): Parser<I, Double?, E> =
this.map { if (it == null || fn(it)) null else it }
32 changes: 31 additions & 1 deletion tribune-core/src/main/kotlin/com/sksamuel/tribune/core/filter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,36 @@ import arrow.core.right
*
* @return a parser which rejects input based on the result of predicate [p]
*/
fun <I, A, E> Parser<I, A, E>.filter(p: (A) -> Boolean, ifFalse: (A) -> E): Parser<I, A, E> {
fun <I, O, E> Parser<I, O, E>.filter(p: (O) -> Boolean, ifFalse: (O) -> E): Parser<I, O, E> {
return flatMap { if (p(it)) it.right() else ifFalse(it).leftNel() }
}

/**
* Returns a [Parser] that produces a null if the input value fails to pass the predicate [p].
*
* In other words, if the underlying parser returns a valid output, that output is then
* passed to the given predicate, and if that predicate returns false, a null is produced.
*
* @param p the predicate to test input
*
* @return a parser which rejects input based on the result of predicate [p]
*/
fun <I, O : Any, E> Parser<I, O, E>.nullIf(fn: (O) -> Boolean): Parser<I, O?, E> =
this.map { if (fn(it)) null else it }

/**
* Returns a [Parser] that produces a null if the input value fails to pass the predicate [p].
*
* In other words, if the underlying parser returns a valid output, that output is then
* passed to the given predicate, and if that predicate returns false, a null is produced.
*
* This variant of [nullIf] allows the output of the preceeding parser to already be null.
*
* @param p the predicate to test input
*
* @return a parser which rejects input based on the result of predicate [p]
*/
@JvmName("nullIfNullable")
fun <I, O, E> Parser<I, O?, E>.nullIf(fn: (O) -> Boolean): Parser<I, O?, E> =
this.map { if (it == null || fn(it)) null else it }

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ 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

/**
* Extends a [Parser] of output type string to parse that string into a double.
Expand All @@ -19,10 +18,3 @@ fun <I, E> Parser<I, String, E>.float(ifError: (String) -> E): Parser<I, Float,
val f = it.toFloatOrNull()
f?.right() ?: ifError(it).leftNel()
}

fun <I, E> Parser<I, Float, E>.nullIf(fn: (Float) -> Boolean): Parser<I, Float?, E> =
this.map { if (fn(it)) null else it }

@JvmName("nullIfNullable")
fun <I, E> Parser<I, Float?, E>.nullIf(fn: (Float) -> Boolean): Parser<I, Float?, E> =
this.map { if (it == null || fn(it)) null else it }
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ 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

/**
* Chains a [Parser] to convert String -> Int.
Expand Down Expand Up @@ -61,10 +60,3 @@ fun <I, E> Parser<I, Int?, E>.max(min: Int, ifError: (Int) -> E): Parser<I, Int?
flatMap {
if (it == null) Either.Right(null) else if (it >= min) it.right() else ifError(it).leftNel()
}

fun <I, E> Parser<I, Int, E>.nullIf(fn: (Int) -> Boolean): Parser<I, Int?, E> =
this.map { if (fn(it)) null else it }

@JvmName("nullIfNullable")
fun <I, E> Parser<I, Int?, E>.nullIf(fn: (Int) -> Boolean): Parser<I, Int?, E> =
this.map { if (it == null || fn(it)) null else it }
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,3 @@ fun <I, E> Parser<I, Long?, E>.max(min: Long, ifError: (Long) -> E): Parser<I, L
flatMap {
if (it == null) Either.Right(null) else if (it >= min) it.right() else ifError(it).leftNel()
}

fun <I, E> Parser<I, Long, E>.nullIf(fn: (Long) -> Boolean): Parser<I, Long?, E> =
this.map { if (fn(it)) null else it }

@JvmName("nullIfNullable")
fun <I, E> Parser<I, Long?, E>.nullIf(fn: (Long) -> Boolean): Parser<I, Long?, E> =
this.map { if (it == null || fn(it)) null else it }
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,3 @@ fun <I, E> Parser<I, String?, E>.nullOrNotBlank(ifBlank: () -> E): Parser<I, Str
if (it == null) null.right() else if (it.isBlank()) ifBlank().leftNel() else it.right()
}
}

fun <I, E> Parser<I, String, E>.nullIf(fn: (String) -> Boolean): Parser<I, String?, E> =
this.map { if (fn(it)) null else it }

@JvmName("nullIfNullable")
fun <I, E> Parser<I, String?, E>.nullIf(fn: (String) -> Boolean): Parser<I, String?, E> =
this.map { if (it == null || fn(it)) null else it }
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.sksamuel.tribune.core

import com.sksamuel.tribune.core.doubles.nullIf
import com.sksamuel.tribune.core.ints.min
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
Expand Down Expand Up @@ -39,6 +38,8 @@ class ComposeTest : FunSpec() {
}
}

val q = Parsers.nullableString.nullIf { it.length > 3 }

val weightParser = Parsers.nullableDouble
.nullIf { it <= 0.0 }
.mapIfNotNull { ParsedWeight(it) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package com.sksamuel.tribune.core
import arrow.core.Either
import arrow.core.leftNel
import arrow.core.right
import com.sksamuel.tribune.core.strings.nullIf
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe

Expand All @@ -15,6 +14,19 @@ class NullTest : FunSpec() {
p.parse("abc") shouldBe "wibble".right()
}

test("nullIf on O -> A?") {
val p = Parser<String>().nullIf { "foo" == it }
p.parse("foo") shouldBe null.right()
p.parse("bar") shouldBe "bar".right()
}

test("nullIf on O? -> A?") {
val p = Parser<String?>().nullIf { "foo" == it }
p.parse(null) shouldBe null.right()
p.parse("foo") shouldBe null.right()
p.parse("bar") shouldBe "bar".right()
}

test("withDefault on I? -> A?") {
val p = Parser<String?>().withDefault { "wibble" }
p.parse("abc") shouldBe "abc".right()
Expand Down

0 comments on commit 0996104

Please sign in to comment.