Skip to content

Commit

Permalink
Improved Json API
Browse files Browse the repository at this point in the history
  • Loading branch information
prange committed May 8, 2019
1 parent ab53cdd commit ba21b66
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package org.kantega.niagara.json

import java.math.BigDecimal
import io.vavr.collection.List
import org.kantega.niagara.data.curried

typealias JsonDecoder<A> = (JsonValue) -> JsonResult<A>

Expand All @@ -21,6 +22,19 @@ infix fun <A, B> JsonDecoder<(A) -> B>.apply(v: JsonDecoder<A>): JsonDecoder<B>
fun <A, B> decode(constructor: (A) -> B): JsonDecoder<(A) -> B> =
{ _ -> jOk(constructor) }

fun <A, B, C> decode(constructor: (A, B) -> C): JsonDecoder<(A) -> (B) -> C> =
decode(constructor.curried())

fun <A, B, C, D> decode(constructor: (A, B, C) -> D): JsonDecoder<(A) -> (B) -> (C) -> D> =
decode(constructor.curried())

fun <A, B, C, D, E> decode(constructor: (A, B, C, D) -> E): JsonDecoder<(A) -> (B) -> (C) -> (D) -> E> =
decode(constructor.curried())

fun <A, B, C, D, E, F> decode(constructor: (A, B, C, D, E) -> F): JsonDecoder<(A) -> (B) -> (C) -> (D) -> (E) -> F> =
decode(constructor.curried())


val decodeString: JsonDecoder<String> =
{ it.asString() }

Expand All @@ -45,14 +59,14 @@ fun <A> decodeField(name: String, valueDecoder: JsonDecoder<A>): JsonDecoder<A>
fun <A, B> JsonDecoder<(A) -> B>.field(name: String, decoderForField: JsonDecoder<A>): JsonDecoder<B> =
this.apply(decodeField(name, decoderForField))

fun <A, B> JsonDecoder<(A) -> B>.value(a:A): JsonDecoder<B> =
this.apply({ jOk(a)})
fun <A, B> JsonDecoder<(A) -> B>.value(a: A): JsonDecoder<B> =
this.apply({ jOk(a) })

fun <A> decodeArray(elemDecoder: JsonDecoder<A>): JsonDecoder<List<A>> =
{ it.asArray().bind { list -> list.a.map(elemDecoder).traverseJsonResult() } }

fun <A> List<JsonResult<A>>.traverseJsonResult(): JsonResult<List<A>> =
this.foldRight(jOk(List.empty()),{ ja, jas -> jas.bind { alist -> ja.map { a -> alist.prepend(a) } } })
this.foldRight(jOk(List.empty()), { ja, jas -> jas.bind { alist -> ja.map { a -> alist.prepend(a) } } })

fun <A> JsonDecoder<A>.or(other:JsonDecoder<A>):JsonDecoder<A> =
{jsonValue -> this(jsonValue).orElse({other(jsonValue)}) }
fun <A> JsonDecoder<A>.or(other: JsonDecoder<A>): JsonDecoder<A> =
{ jsonValue -> this(jsonValue).orElse({ other(jsonValue) }) }
31 changes: 18 additions & 13 deletions niagara-json/src/main/kotlin/org/kantega/niagara/json/JsonResult.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import org.kantega.niagara.data.NonEmptyList
sealed class JsonResult<out A> {


abstract fun <T> fold(onFail: (NonEmptyList<String>) -> T, onSuccess: (A) -> T) : T
abstract fun <T> fold(onFail: (NonEmptyList<String>) -> T, onSuccess: (A) -> T): T

fun <B> bind(f: (A) -> JsonResult<B>): JsonResult<B> =
fold({ nel -> JsonResult.fail(nel) }, { a -> f(a) })
Expand All @@ -21,16 +21,16 @@ sealed class JsonResult<out A> {
})

override fun toString(): String {
return fold({ f -> "JsonResult(${f.toList().joinToString { it }})" }, { s -> "JsonResult("+s.toString()+")" })
return fold({ f -> "JsonResult(${f.toList().joinToString { it }})" }, { s -> "JsonResult(" + s.toString() + ")" })
}

companion object {

fun <A> fail(t: Throwable):JsonResult<A> =
fun <A> fail(t: Throwable): JsonResult<A> =
JsonResult.fail(t.javaClass.simpleName + ":" + t.message.orEmpty())

fun <A> fail(msg: String,vararg tail:String):JsonResult<A> =
JsonResult.fail(NonEmptyList.of(msg,*tail))
fun <A> fail(msg: String, vararg tail: String): JsonResult<A> =
JsonResult.fail(NonEmptyList.of(msg, *tail))

fun <A> fail(nel: NonEmptyList<String>) =
JsonFail<A>(nel)
Expand All @@ -40,13 +40,13 @@ sealed class JsonResult<out A> {
}
}

data class JsonSuccess<A>(val a:A):JsonResult<A>(){
data class JsonSuccess<A>(val a: A) : JsonResult<A>() {
override fun <T> fold(onFail: (NonEmptyList<String>) -> T, onSuccess: (A) -> T) =
onSuccess(a)

}

data class JsonFail<A>(val failNel:NonEmptyList<String>):JsonResult<A>(){
data class JsonFail<A>(val failNel: NonEmptyList<String>) : JsonResult<A>() {
override fun <T> fold(onFail: (NonEmptyList<String>) -> T, onSuccess: (A) -> T): T =
onFail(failNel)

Expand All @@ -55,10 +55,10 @@ data class JsonFail<A>(val failNel:NonEmptyList<String>):JsonResult<A>(){
infix fun <A, B> JsonResult<(A) -> B>.apply(v: JsonResult<A>): JsonResult<B> =
when {
this is JsonSuccess && v is JsonSuccess -> jOk(this.a(v.a))
this is JsonFail && v is JsonFail -> JsonResult.fail(this.failNel + v.failNel)
this is JsonFail -> JsonResult.fail(this.failNel)
v is JsonFail -> JsonResult.fail(v.failNel)
else -> throw Error("unreachable code")
this is JsonFail && v is JsonFail -> JsonResult.fail(this.failNel + v.failNel)
this is JsonFail -> JsonResult.fail(this.failNel)
v is JsonFail -> JsonResult.fail(v.failNel)
else -> throw Error("unreachable code")
}

fun <A> JsonResult<JsonValue>.decode(decoder: JsonDecoder<A>): JsonResult<A> =
Expand All @@ -76,12 +76,17 @@ fun <A, B> jLift(f: (A) -> B) =
infix fun <A> JsonResult<A>.orElse(a: A): A =
this.fold({ a }, { c -> c })

infix fun <A> JsonResult<A>.orElse(f: (NonEmptyList<String>) -> A): A =
this.fold({ f(it) }, { c -> c })

infix fun <A> JsonResult<A>.orElse(a: JsonResult<A>): JsonResult<A> =
this.fold({ a }, { this })
this.fold({ a }, { this })

infix fun <A> JsonResult<A>.orElse(f: () -> JsonResult<A>): JsonResult<A> =
this.fold({ f() }, { this })
this.fold({ f() }, { this })

infix fun <A> JsonResult<A>.orElse(f: (NonEmptyList<String>) -> JsonResult<A>): JsonResult<A> =
this.fold({ nel -> f(nel) }, { this })

fun JsonResult<JsonValue>.field(path: String): JsonResult<JsonValue> =
this.field(JsonPath(path))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ interface JsonValue {
asObject().bind { obj ->
obj.m.get(name)
.map { JsonResult.success(it) }
.getOrElse(JsonResult.fail("The field $name did not exist in the object"))
.getOrElse(JsonResult.fail("The field $name did not exist in the object. Available fields are {${obj.m.keySet().mkString(", ")}}"))
}

}
Expand Down

0 comments on commit ba21b66

Please sign in to comment.