diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c64a48e..5a55aa3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,10 +8,6 @@ on: branches: - main -env: - JAVA_OPTS: -Xms512m -Xmx1024m - GRADLE_OPTS: "-Dorg.gradle.daemon=false -Dorg.gradle.configureondemand=true -Dorg.gradle.jvmargs=-Xmx3g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8" - jobs: check: runs-on: ubuntu-latest diff --git a/.github/workflows/githubpages.yaml b/.github/workflows/githubpages.yaml index ff32486..7a10c83 100644 --- a/.github/workflows/githubpages.yaml +++ b/.github/workflows/githubpages.yaml @@ -4,9 +4,6 @@ on: release: types: [published] -env: - GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.kotlin.dsl.internal.io.timeout=120000 -Dorg.gradle.jvmargs="-Xmx5g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelGC -XX:MaxMetaspaceSize=1g -Dfile.encoding=UTF-8" - jobs: githubpages: runs-on: ubuntu-latest diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 4a6a8fd..f09fd61 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -10,8 +10,6 @@ on: type: string env: - JAVA_OPTS: -Xms1g -Xmx3g - GRADLE_OPTS: "-Dorg.gradle.daemon=false -Dorg.gradle.configureondemand=true -Dorg.gradle.jvmargs=-Xmx3g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8" ORG_GRADLE_PROJECT_mavenCentralUsername: '${{ secrets.SONATYPE_USER }}' ORG_GRADLE_PROJECT_mavenCentralPassword: '${{ secrets.SONATYPE_PWD }}' ORG_GRADLE_PROJECT_signingInMemoryKeyId: '${{ secrets.SIGNING_KEY_ID }}' diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml new file mode 100644 index 0000000..6dfb596 --- /dev/null +++ b/.github/workflows/pull_request.yaml @@ -0,0 +1,23 @@ +name: "Pull Request" + +on: [pull_request] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Java + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: 11 + + - name: Build + uses: gradle/gradle-build-action@v2 + with: + arguments: build diff --git a/.gitignore b/.gitignore index 32da301..14a866b 100644 --- a/.gitignore +++ b/.gitignore @@ -170,4 +170,5 @@ kotlin-js-store # End of https://www.toptal.com/developers/gitignore/api/intellij+all,kotlin,gradle,macos -gradle-build-scan.txt \ No newline at end of file +gradle-build-scan.txt +.kotlin \ No newline at end of file diff --git a/README.md b/README.md index 50e82d7..12c0022 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ In the example below we select the _employees_ `JsonArray`, and then we select _every_ `JsonElement` in the `JsonArray`. We then _select_ the _name_ out of _every_ `JsonElement`. -Instead of `Optional` it now returns `Every`, +Instead of `Optional` it now returns `Traversal`, since we selected _many properties_ instead of a _single property_. Just like before we can apply a function to it using _modify_, @@ -118,7 +118,7 @@ fun main(): Unit { --> ```kotlin val json: JsonElement = Json.decodeFromString(JSON_STRING) - val employeesName: Every = JsonPath.select("employees").every.select("name").string + val employeesName: Traversal = JsonPath.select("employees").every.select("name").string val res: JsonElement = employeesName.modify(json, String::uppercase).also(::println) employeesName.getAll(res).also(::println) ``` @@ -137,7 +137,7 @@ Like before, below we select the _employees_ `JsonArray`, and then we select _every_ `JsonElement` in the `JsonArray`. We then _select_ the _name_ out of _every_ `JsonElement`. -Again, instead of `Optional` it now returns `Every`, +Again, instead of `Optional` it now returns `Traversal`, since we selected _many properties_ instead of a _single property_. You can then, apply a function to it using _modify_ like before. @@ -151,13 +151,13 @@ fun main(): Unit { --> ```kotlin val json: JsonElement = Json.decodeFromString(JSON_STRING) - val employeesName: Every = JsonPath.pathEvery("employees.*.name").string + val employeesName: Traversal = JsonPath.pathEvery("employees.*.name").string val res: JsonElement = employeesName.modify(json, String::uppercase).also(::println) employeesName.getAll(res).also(::println) ``` -> You can get the full code [here](src/jvmTest/kotlin/example/example-readme-03.kt). +> You can get the full code [here](src/jvmTest/kotlin/example/example-readme-04.kt). ```text {"name":"Arrow","address":{"city":"Functional Town","street":{"number":1337,"name":"Functional street"}},"employees":[{"name":"JOHN","lastName":"doe"},{"name":"JANE","lastName":"doe"}]} [JOHN, JANE] -``` \ No newline at end of file +``` diff --git a/build.gradle.kts b/build.gradle.kts index d62f929..c8409fc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,23 +2,22 @@ import kotlinx.knit.KnitPluginExtension import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import io.gitlab.arturbosch.detekt.Detekt import io.gitlab.arturbosch.detekt.extensions.DetektExtension -import kotlinx.kover.api.KoverTaskExtension -import org.gradle.api.JavaVersion.VERSION_1_8 -import org.gradle.api.Project +import org.gradle.api.JavaVersion.VERSION_11 import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED -import org.gradle.api.tasks.testing.logging.TestLogEvent.PASSED import org.gradle.api.tasks.testing.logging.TestLogEvent.SKIPPED import org.gradle.api.tasks.testing.logging.TestLogEvent.STANDARD_ERROR import org.gradle.api.tasks.testing.logging.TestLogEvent.STANDARD_OUT import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.withType import org.jetbrains.dokka.gradle.DokkaTask +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl plugins { application - alias(libs.plugins.kotlin.multiplatform) - alias(libs.plugins.arrow.kotlin) + alias(libs.plugins.kotlin) + alias(libs.plugins.spotless) alias(libs.plugins.kotest.multiplatform) alias(libs.plugins.detekt) alias(libs.plugins.dokka) @@ -32,16 +31,20 @@ repositories { mavenCentral() } -group = property("projects.group").toString() +spotless { + kotlin { + ktfmt().googleStyle() + } +} java { - sourceCompatibility = VERSION_1_8 - targetCompatibility = VERSION_1_8 + sourceCompatibility = VERSION_11 + targetCompatibility = VERSION_11 } tasks { withType().configureEach { - kotlinOptions.jvmTarget = "1.8" + this.compilerOptions.jvmTarget.set(JvmTarget.JVM_11) } withType().configureEach { @@ -49,7 +52,7 @@ tasks { useJUnitPlatform() testLogging { exceptionFormat = TestExceptionFormat.FULL - events = setOf(PASSED, SKIPPED, FAILED, STANDARD_OUT, STANDARD_ERROR) + events = setOf(SKIPPED, FAILED, STANDARD_OUT, STANDARD_ERROR) } } @@ -59,10 +62,36 @@ tasks { } kotlin { + explicitApi() + + jvm() + js(IR) { + browser() + nodejs() + } + + @OptIn(ExperimentalWasmDsl::class) + wasmJs() + linuxX64() + macosX64() + macosArm64() + iosSimulatorArm64() + iosX64() + linuxArm64() + watchosSimulatorArm64() + watchosX64() + watchosArm32() + watchosArm64() + tvosSimulatorArm64() + tvosX64() + tvosArm64() + iosArm64() + mingwX64() + sourceSets { commonMain { dependencies { - implementation(kotlin("stdlib-common")) + implementation(kotlin("stdlib")) api(libs.arrow.optics) api(libs.kotlinx.serialization.json) } @@ -118,12 +147,6 @@ tasks { getByName("knitPrepare").dependsOn(getTasksByName("dokka", true)) - register("cleanDocs") { - val folder = file("docs").also { it.mkdir() } - val docsContent = folder.listFiles().filter { it != folder } - delete(docsContent) - } - withType().configureEach { reports { html.required.set(true) diff --git a/gradle.properties b/gradle.properties index 8310438..68b827c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,6 @@ kotlin.code.style=official -projects.group=io.github.nomisrev -kotlin.mpp.stability.nowarn=true +GROUP=io.github.nomisrev SONATYPE_HOST=S01 RELEASE_SIGNING_ENABLED=true diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a80b22c..b82aa23 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/libs.versions.toml b/libs.versions.toml index 501e2d9..73ea11c 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -1,25 +1,19 @@ [versions] -arrow = "1.2.1" -arrowGradle = "0.12.0-rc.24" -coroutines = "1.8.0" -dokka = "1.9.10" -kotlin = "1.9.22" -kotest = "5.8.0" -kotest-plugin = "5.8.0" -kover = "0.7.6" +arrow = "2.0.0-alpha.1" +dokka = "1.9.20" +kotlin = "2.0.0-RC3" +kotest = "5.9.0" +kover = "0.8.0" detekt = "1.23.5" kotest-arrow="1.4.0" kotlinx-json="1.6.3" kotlinx-knit="0.5.0" -publish="0.27.0" +publish="0.28.0" knit="0.5.0" +spotless="6.25.0" [libraries] -arrow-core = { module = "io.arrow-kt:arrow-core", version.ref = "arrow" } arrow-optics = { module = "io.arrow-kt:arrow-optics", version.ref = "arrow" } -arrow-fx = { module = "io.arrow-kt:arrow-fx-coroutines", version.ref = "arrow" } -coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" } -coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" } dokka-core = { module = "org.jetbrains.dokka:dokka-core", version.ref = "dokka" } kotest-assertionsCore = { module = "io.kotest:kotest-assertions-core", version.ref = "kotest" } kotest-frameworkEngine = { module = "io.kotest:kotest-framework-engine", version.ref = "kotest" } @@ -32,13 +26,13 @@ kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serializa kotlinx-knit-test = { module = "org.jetbrains.kotlinx:kotlinx-knit-test", version.ref = "kotlinx-knit" } [plugins] -arrow-formatter = { id = "io.arrow-kt.arrow-gradle-config-formatter", version.ref = "arrowGradle" } -arrow-kotlin = { id = "io.arrow-kt.arrow-gradle-config-kotlin", version.ref = "arrowGradle" } +kotlin = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" } kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" } -kotest-multiplatform = { id = "io.kotest.multiplatform", version.ref = "kotest-plugin" } +kotest-multiplatform = { id = "io.kotest.multiplatform", version.ref = "kotest" } kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } kotlinx-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } publish = { id = "com.vanniktech.maven.publish", version.ref="publish" } -knit = { id = "org.jetbrains.kotlinx.knit", version.ref="knit" } \ No newline at end of file +knit = { id = "org.jetbrains.kotlinx.knit", version.ref="knit" } +spotless = { id = "com.diffplug.spotless", version.ref ="spotless" } \ No newline at end of file diff --git a/src/commonMain/kotlin/io/github/nomisrev/JsonPath.kt b/src/commonMain/kotlin/io/github/nomisrev/JsonPath.kt index f910942..56952db 100644 --- a/src/commonMain/kotlin/io/github/nomisrev/JsonPath.kt +++ b/src/commonMain/kotlin/io/github/nomisrev/JsonPath.kt @@ -4,8 +4,8 @@ package io.github.nomisrev import arrow.core.Option import arrow.core.None import arrow.core.Some -import arrow.optics.Every import arrow.optics.Optional +import arrow.optics.Traversal import arrow.optics.typeclasses.At import arrow.optics.typeclasses.Index import arrow.optics.typeclasses.FilterIndex @@ -21,7 +21,6 @@ import kotlinx.serialization.serializer * Starting point of the JsonPath DSL * This represents the _root_ of the path you want to define in your `JsonElement` */ -@Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE") public object JsonPath : Optional by Optional.id() /** Extract a [Boolean] value from a [JsonElement] */ @@ -63,8 +62,8 @@ public inline val Optional.`null`: Optional.every: Every - inline get() = this compose Every.jsonElement() +public inline val Optional.every: Traversal + inline get() = this compose Traversal.jsonElement() /** * Select value at [selector]. The following syntax is supported for [selector]: @@ -96,14 +95,14 @@ public fun Optional.select( */ public fun Optional.selectEvery( selector: String -): Every { +): Traversal { val inBrackets = matchNameInBrackets(selector) val ixs = matchIndicesInBrackets(selector) val startIx = matchStartIndex(selector) val startEndIx = matchStartEndIndex(selector) return when { inBrackets != null -> get(inBrackets) - selector == "*" -> this compose Every.jsonElement() // inline definition of [every] + selector == "*" -> this compose Traversal.jsonElement() // inline definition of [every] ixs != null -> filterIndex { it in ixs } startIx != null -> filterIndex { it >= startIx } startEndIx != null -> get(startEndIx.first until startEndIx.second) @@ -136,9 +135,9 @@ public fun Optional.pathEvery( path: String, fieldDelimiter: String = ".", indexDelimiter: String = "[" -): Every = +): Traversal = path.splitTwice(fieldDelimiter, indexDelimiter).fold(this) { - acc: Every, pathSelector -> acc.selectEvery(pathSelector) + acc: Traversal, pathSelector -> acc.selectEvery(pathSelector) } /** @@ -154,7 +153,7 @@ public fun Optional.at( /** Select keys out of an [JsonObject] with the given [predicate] */ public fun Optional.filterKeys( predicate: (keys: String) -> Boolean -): Every = +): Traversal = `object` compose FilterIndex.map().filter(predicate) /** Select a [property] out of a [JsonObject] */ @@ -172,13 +171,13 @@ public operator fun Optional.get( /** Select all indices from the [range] out of a [JsonArray] */ public operator fun Optional.get( range: ClosedRange -): Every = +): Traversal = filterIndex { it in range } /** Select an indices out of a [JsonArray] with the given [predicate] */ public fun Optional.filterIndex( predicate: (index: Int) -> Boolean -): Every = +): Traversal = array compose FilterIndex.list().filter(predicate) /** Extract a value of type [A] with an _implicit_ KotlinX Serializer */ diff --git a/src/commonMain/kotlin/io/github/nomisrev/JsonPathEvery.kt b/src/commonMain/kotlin/io/github/nomisrev/JsonPathEvery.kt index 8fa105f..66d69bc 100644 --- a/src/commonMain/kotlin/io/github/nomisrev/JsonPathEvery.kt +++ b/src/commonMain/kotlin/io/github/nomisrev/JsonPathEvery.kt @@ -4,7 +4,7 @@ package io.github.nomisrev import arrow.core.None import arrow.core.Option import arrow.core.Some -import arrow.optics.Every +import arrow.optics.Traversal import arrow.optics.Optional import arrow.optics.typeclasses.At import arrow.optics.typeclasses.FilterIndex @@ -18,46 +18,46 @@ import kotlinx.serialization.json.JsonObject import kotlinx.serialization.serializer /** Extract a [Boolean] value from a [JsonElement] */ -public inline val Every.boolean: Every +public inline val Traversal.boolean: Traversal inline get() = this@boolean compose Optional.jsonBoolean() /** Extract a [String] value from a [JsonElement] */ -public inline val Every.string: Every +public inline val Traversal.string: Traversal inline get() = this compose Optional.jsonString() /** Extract a [Double] value from a [JsonElement] */ -public inline val Every.double: Every +public inline val Traversal.double: Traversal inline get() = this compose Optional.jsonDouble() /** Extract a [Float] value from a [JsonElement] */ -public inline val Every.float: Every +public inline val Traversal.float: Traversal inline get() = this compose Optional.jsonFloat() /** Extract a [Long] value from a [JsonElement] */ -public inline val Every.long: Every +public inline val Traversal.long: Traversal inline get() = this compose Optional.jsonLong() /** Extract a [Int] value from a [JsonElement] */ -public inline val Every.int: Every +public inline val Traversal.int: Traversal inline get() = this compose Optional.jsonInt() /** Extract a [List] of [JsonElement] from a [JsonElement] */ -public inline val Every.array: Every> +public inline val Traversal.array: Traversal> inline get() = this compose Optional.jsonArray() /** Extract a [Map] of [String] to [JsonElement] from a [JsonElement] */ @Suppress("TopLevelPropertyNaming") -public inline val Every.`object`: Every> +public inline val Traversal.`object`: Traversal> inline get() = this compose Optional.jsonObject() /** Extract `null` from a [JsonElement] */ @Suppress("TopLevelPropertyNaming") -public inline val Every.`null`: Every +public inline val Traversal.`null`: Traversal inline get() = this compose Optional.jsonNull() /** Select _every_ entry in [JsonArray] and [JsonObject] */ -public inline val Every.every: Every - inline get() = this compose Every.jsonElement() +public inline val Traversal.every: Traversal + inline get() = this compose Traversal.jsonElement() /** * Select value at [selector]. The following syntax is supported for [selector]: @@ -65,7 +65,7 @@ public inline val Every.every: Every.select(selector: String): Every { +public fun Traversal.select(selector: String): Traversal { val inBrackets = matchNameInBrackets(selector) val ix = matchIndexInBrackets(selector) return when { @@ -85,16 +85,16 @@ public fun Every.select(selector: String): Every.selectEvery( +public fun Traversal.selectEvery( selector: String -): Every { +): Traversal { val inBrackets = matchNameInBrackets(selector) val ixs = matchIndicesInBrackets(selector) val startIx = matchStartIndex(selector) val startEndIx = matchStartEndIndex(selector) return when { inBrackets != null -> get(inBrackets) - selector == "*" -> this compose Every.jsonElement() // inline definition of [every] + selector == "*" -> this compose Traversal.jsonElement() // inline definition of [every] ixs != null -> filterIndex { it in ixs } startIx != null -> filterIndex { it >= startIx } startEndIx != null -> get(startEndIx.first until startEndIx.second) @@ -109,11 +109,11 @@ public fun Every.selectEvery( * JsonPath.path("addresses[0].street.name") * ``` */ -public fun Every.path( +public fun Traversal.path( path: String, fieldDelimiter: String = ".", indexDelimiter: String = "[" -): Every = +): Traversal = path.splitTwice(fieldDelimiter, indexDelimiter).fold(this) { acc, pathSelector -> acc.select(pathSelector) } /** @@ -123,11 +123,11 @@ public fun Every.path( * JsonPath.path("addresses[0].*.street.name") * ``` */ -public fun Every.pathEvery( +public fun Traversal.pathEvery( path: String, fieldDelimiter: String = ".", indexDelimiter: String = "[" -): Every = +): Traversal = path.splitTwice(fieldDelimiter, indexDelimiter).fold(this) { acc, pathSelector -> acc.selectEvery(pathSelector) } /** @@ -135,41 +135,43 @@ public fun Every.pathEvery( * This allows you to erase the value by setting it to [None], * or allows you to overwrite it by setting a new [Some]. */ -public fun Every.at( +public fun Traversal.at( name: String -): Every> = +): Traversal> = `object` compose At.map().at(name) /** Select keys out of an [JsonObject] with the given [predicate] */ -public fun Every.filterKeys( +public fun Traversal.filterKeys( predicate: (keys: String) -> Boolean -): Every = +): Traversal = `object` compose FilterIndex.map().filter(predicate) /** Select a [property] out of a [JsonObject] */ -public operator fun Every.get(property: String): Every = +public operator fun Traversal.get(property: String): Traversal = `object` compose Index.map().index(property) /** Select an [index] out of a [JsonArray] */ -public operator fun Every.get(index: Int): Every = +public operator fun Traversal.get(index: Int): Traversal = array compose Index.list().index(index) /** Select all indices from the [range] out of a [JsonArray] */ -public operator fun Every.get(range: ClosedRange): Every = +public operator fun Traversal.get( + range: ClosedRange +): Traversal = filterIndex { it in range } /** Select an indices out of a [JsonArray] with the given [predicate] */ -public fun Every.filterIndex( +public fun Traversal.filterIndex( predicate: (index: Int) -> Boolean -): Every = array compose FilterIndex.list().filter(predicate) +): Traversal = array compose FilterIndex.list().filter(predicate) /** Extract a value of type [A] with an _implicit_ KotlinX Serializer */ -public inline fun Every.extract( +public inline fun Traversal.extract( parser: Json = Json -): Every = extract(serializer(), parser) +): Traversal = extract(serializer(), parser) /** Extract a value of type [A] given a [KSerializer] for [A] */ -public fun Every.extract( +public fun Traversal.extract( serializer: KSerializer, parser: Json = Json -): Every = this compose parse(serializer, parser) +): Traversal = this compose parse(serializer, parser) diff --git a/src/commonMain/kotlin/io/github/nomisrev/optics.kt b/src/commonMain/kotlin/io/github/nomisrev/optics.kt index 11162db..953eccc 100644 --- a/src/commonMain/kotlin/io/github/nomisrev/optics.kt +++ b/src/commonMain/kotlin/io/github/nomisrev/optics.kt @@ -2,21 +2,20 @@ package io.github.nomisrev -import arrow.core.Either -import arrow.core.Option -import arrow.core.foldLeft import arrow.core.left import arrow.core.right +import arrow.core.Option import arrow.core.toOption -import arrow.optics.Every +import arrow.core.Either +import arrow.core.fold +import arrow.optics.Prism import arrow.optics.Lens import arrow.optics.Optional -import arrow.optics.PEvery import arrow.optics.POptional -import arrow.optics.Prism +import arrow.optics.Traversal +import arrow.optics.PTraversal import arrow.optics.typeclasses.At import arrow.optics.typeclasses.Index -import arrow.typeclasses.Monoid import kotlinx.serialization.KSerializer import kotlinx.serialization.SerializationException import kotlinx.serialization.json.Json @@ -48,20 +47,20 @@ public fun POptional.Companion.jsonNull(): Optional = Jso public fun POptional.Companion.jsonArray(): Optional> = JsonElementToJsonArray -public fun PEvery.Companion.jsonArray(): Every = JsArrayEvery +public fun PTraversal.Companion.jsonArray(): Traversal = JsArrayTraversal public fun Index.Companion.jsonArray(): Index = JsArrayIndex public fun POptional.Companion.jsonObject(): Optional> = JsonElementToJsonObject -public fun PEvery.Companion.jsonObject(): Every = JsonObjectEvery +public fun PTraversal.Companion.jsonObject(): Traversal = JsonObjectTraversal public fun Index.Companion.jsonObject(): Index = JsonObjectIndex public fun At.Companion.jsonObject(): At> = JsonObjectAt -public fun PEvery.Companion.jsonElement(): Every = JsonElementEvery +public fun PTraversal.Companion.jsonElement(): Traversal = JsonElementTraversal private object JsonObjectIndex : Index { override fun index(i: String): Optional = @@ -81,17 +80,17 @@ private object JsonObjectAt : At> { ) } -private object JsonObjectEvery : Every { +private object JsonObjectTraversal : Traversal { override fun modify(source: JsonObject, map: (focus: JsonElement) -> JsonElement): JsonObject = JsonObject(source.mapValues { (_, focus) -> map(focus) }) - override fun foldMap(M: Monoid, source: JsonObject, map: (focus: JsonElement) -> R): R = - with(M) { source.foldLeft(empty()) { acc, (_, focus) -> acc.combine(map(focus)) } } + override fun foldMap(initial: R, combine: (R, R) -> R, source: JsonObject, map: (focus: JsonElement) -> R): R = + source.fold(initial) { acc, (_, focus) -> combine(acc, map(focus)) } } -private object JsArrayEvery : Every { - override fun foldMap(M: Monoid, source: JsonArray, map: (focus: JsonElement) -> R): R = - with(M) { source.fold(empty()) { acc, json -> acc.combine(map(json)) } } +private object JsArrayTraversal : Traversal { + override fun foldMap(initial: R, combine: (R, R) -> R, source: JsonArray, map: (focus: JsonElement) -> R): R = + source.fold(initial) { acc, json -> combine(acc, map(json)) } override fun modify(source: JsonArray, map: (focus: JsonElement) -> JsonElement): JsonArray = JsonArray(source.map(map)) @@ -217,15 +216,15 @@ private object JsonElementToJsonObject : Optional { - override fun foldMap(M: Monoid, source: JsonElement, map: (focus: JsonElement) -> R): R = - with(M) { - when (source) { - JsonNull -> map(JsonNull) - is JsonObject -> source.foldLeft(empty()) { acc, (_, focus) -> acc.combine(map(focus)) } - is JsonArray -> source.fold(empty()) { acc, json -> acc.combine(map(json)) } - is JsonPrimitive -> map(source) - } +private object JsonElementTraversal : Traversal { + override fun foldMap(initial: R, combine: (R, R) -> R, source: JsonElement, map: (focus: JsonElement) -> R): R = + when (source) { + JsonNull -> map(JsonNull) + is JsonObject -> + source.fold(initial) { acc, (_, focus) -> combine(acc, map(focus)) } + + is JsonArray -> source.fold(initial) { acc, json -> combine(acc, map(json)) } + is JsonPrimitive -> map(source) } override fun modify(source: JsonElement, map: (focus: JsonElement) -> JsonElement): JsonElement = diff --git a/src/commonTest/kotlin/io/github/nomisrev/JsonDSLSpec.kt b/src/commonTest/kotlin/io/github/nomisrev/JsonDSLSpec.kt index 08a38ce..3f54816 100644 --- a/src/commonTest/kotlin/io/github/nomisrev/JsonDSLSpec.kt +++ b/src/commonTest/kotlin/io/github/nomisrev/JsonDSLSpec.kt @@ -90,7 +90,7 @@ class JsonDSLSpec : StringSpec({ "at from object" { checkAll(Arb.json(Arb.city())) { cityJson -> - JsonPath.at("streets").getOrNull(cityJson)?.orNull() shouldBe (cityJson as? JsonObject)?.get("streets") + JsonPath.at("streets").getOrNull(cityJson)?.getOrNull() shouldBe (cityJson as? JsonObject)?.get("streets") } } diff --git a/src/jvmTest/kotlin/ReadMeSpec.kt b/src/jvmTest/kotlin/ReadMeSpec.kt index 49b8395..1944ae8 100644 --- a/src/jvmTest/kotlin/ReadMeSpec.kt +++ b/src/jvmTest/kotlin/ReadMeSpec.kt @@ -26,11 +26,4 @@ class ReadMeSpec : StringSpec({ "[JOHN, JANE]" ) } - - "ExampleReadme04" { - captureOutput("ExampleReadme04") { com.example.exampleReadme04.main() }.verifyOutputLines( - "{\"name\":\"Arrow\",\"address\":{\"city\":\"Functional Town\",\"street\":{\"number\":1337,\"name\":\"Functional street\"}},\"employees\":[{\"name\":\"JOHN\",\"lastName\":\"doe\"},{\"name\":\"JANE\",\"lastName\":\"doe\"}]}", - "[JOHN, JANE]" - ) - } }) diff --git a/src/jvmTest/kotlin/example/example-readme-03.kt b/src/jvmTest/kotlin/example/example-readme-03.kt index 9518f68..7368253 100644 --- a/src/jvmTest/kotlin/example/example-readme-03.kt +++ b/src/jvmTest/kotlin/example/example-readme-03.kt @@ -33,7 +33,7 @@ private const val JSON_STRING = """ fun main(): Unit { val json: JsonElement = Json.decodeFromString(JSON_STRING) - val employeesName: Every = JsonPath.select("employees").every.select("name").string + val employeesName: Traversal = JsonPath.select("employees").every.select("name").string val res: JsonElement = employeesName.modify(json, String::uppercase).also(::println) employeesName.getAll(res).also(::println) } diff --git a/src/jvmTest/kotlin/example/example-readme-04.kt b/src/jvmTest/kotlin/example/example-readme-04.kt index 5051068..7c63221 100644 --- a/src/jvmTest/kotlin/example/example-readme-04.kt +++ b/src/jvmTest/kotlin/example/example-readme-04.kt @@ -2,12 +2,11 @@ @file:Suppress("InvalidPackageDeclaration") package com.example.exampleReadme04 -import arrow.optics.Every -import io.github.nomisrev.JsonPath -import io.github.nomisrev.pathEvery -import io.github.nomisrev.string -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import arrow.optics.* +import io.github.nomisrev.* +import arrow.optics.typeclasses.* private const val JSON_STRING = """ { @@ -31,9 +30,10 @@ private const val JSON_STRING = """ ] }""" -fun main() { +fun main(): Unit { + val json: JsonElement = Json.decodeFromString(JSON_STRING) - val employeesName: Every = JsonPath.pathEvery("employees.*.name").string + val employeesName: Traversal = JsonPath.pathEvery("employees.*.name").string val res: JsonElement = employeesName.modify(json, String::uppercase).also(::println) employeesName.getAll(res).also(::println) }