From fb50ddb76ef79926c49768a6df37337a368862b1 Mon Sep 17 00:00:00 2001 From: Alexander Ioffe Date: Tue, 31 Dec 2024 02:09:06 -0500 Subject: [PATCH] Add default args. Add example. Add docs. Set version --- README.md | 56 ++++++++++++++++++- build.gradle.kts | 2 +- .../kotlin/io/exoquery/pprint/Walker.kt | 10 ++-- .../kotlin/io/exoquery/pprint/KmpExamples.kt | 40 ++++++++++++- 4 files changed, 98 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index c1dd99e..5e60042 100644 --- a/README.md +++ b/README.md @@ -402,7 +402,7 @@ println(CustomPPrinter5(PPrinterConfig()).invoke(p)) //> PersonBorn(name = "Joe") ``` -## Using `elementName` metadata - Kotlin Multiplatform +## Using `elementName` metadata in Kotlin Multiplatform If you want to filter out fields based on `elementName` in Kotlin Multiplatform inside of the PPrinter you need to override the `treeifyComposite` method. @@ -430,7 +430,7 @@ println(CustomPPrinter6(PersonBorn.serializer(), PPrinterConfig()).i //> PersonBorn(name = "Joe") ``` -#### Sealed Hierarchies in KMP +#### Sealed Hierarchies in Kotlin Multiplatform According to the `kotlinx-serialization` documentation, every member of a sealed hierarchy must be annotated with `@Serializable`. For example, in the following hierarchy: @@ -448,7 +448,57 @@ Every member is annotated with `@Serializable`. This requirement extends to PPrint-Multiplatform as well since it relies on `kotlinx-serialization` to traverse the hierarchy. -#### How do deal with Custom Fields in KMP +## Manual Printing in Kotlin Multiplatform + +If you want to print values with a simple and well-defined structure in Kotlin Multiplatform, you can use the the PPrinterManual class. +For example: +```kotlin +data class Person(val name: String, val age: Int) + +class CustomPPrinter7 : PPrinterManual() { + override fun treeify(x: Any?, elementName: String?, escapeUnicode: Boolean, showFieldNames: Boolean): Tree = + when (x) { + is Person -> Tree.Apply("Person", listOf(Tree.KeyValue("name", Tree.Literal(x.name)), Tree.KeyValue("age", Tree.Literal(x.age))).iterator()) + else -> super.treeify(x, elementName, escapeUnicode, showFieldNames) // will just use Tree.Literal(x.toString()) + } +} + +val p = Person("Joe", 42) +println(CustomPPrinter7().invoke(p)) +//> Person(name = "Joe", age = 42) +``` + +Frequently, it will be useful to combine manual printers and automatic printers. For example: +```kotlin +data class PersonFavorite(val name: String, val age: Int, val favoriteColor: Colors) /// See the Sealed Hierarchies section above + +class ColorsPrinter(config: PPrinterConfig): PPrinter(Colors.serializer(), config) + +class CustomPPrinter7(config: PPrinterConfig): PPrinterManual(config) { + fun treeifyThis(x: Any?, elementName: String?) = + treeify(x, elementName, config.defaultEscapeUnicode, config.defaultShowFieldNames) + + override fun treeify(x: Any?, elementName: String?, escapeUnicode: Boolean, showFieldNames: Boolean): Tree = + when (x) { + is PersonFavorite -> + Tree.Apply( + "PersonFavorite", + iteratorOf(treeifyThis(x.name, "name"), treeifyThis(x.age, "age"), treeifyThis(x.favoriteColor, "favoriteColor")), + elementName + ) + is Colors -> ColorsPrinter(config).treeify(x, elementName, escapeUnicode, showFieldNames) + else -> super.treeify(x, elementName, escapeUnicode, showFieldNames) + } +} + +val joe = PersonFavorite("Joe", 123, Colors.Custom("FF0000")) +val printer = CustomPPrinter7(PPrinterConfig()) +val p = printer(joe) +println(p) +//> PersonFavorite("Joe", 123, Custom(value = "FF0000")) +``` + +#### How do deal with Custom Fields in Kotlin Multiplatform In general whenever you have a atom-property i.e. something not generic you can just mark the field as @Contextual so long as there is a specific case defined for it in `treeifyWith`. However if you are using a type such as diff --git a/build.gradle.kts b/build.gradle.kts index e7188be..9a8f37e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -20,7 +20,7 @@ allprojects { allprojects { group = "io.exoquery" - version = "3.0.0.E" + version = "3.0.0" } subprojects { diff --git a/pprint-kotlin-core/src/commonMain/kotlin/io/exoquery/pprint/Walker.kt b/pprint-kotlin-core/src/commonMain/kotlin/io/exoquery/pprint/Walker.kt index faab528..f59d0ef 100644 --- a/pprint-kotlin-core/src/commonMain/kotlin/io/exoquery/pprint/Walker.kt +++ b/pprint-kotlin-core/src/commonMain/kotlin/io/exoquery/pprint/Walker.kt @@ -15,29 +15,29 @@ sealed interface Tree { /** * Foo(aa, bbb, cccc) */ - data class Apply(val prefix: String, val body: Iterator, override val elementName: String?): Tree + data class Apply(val prefix: String, val body: Iterator, override val elementName: String? = null): Tree /** * LHS op RHS */ - data class Infix(val lhs: Tree, val op: String, val rhs: Tree, override val elementName: String?): Tree + data class Infix(val lhs: Tree, val op: String, val rhs: Tree, override val elementName: String? = null): Tree /** * "xyz" */ - data class Literal(val body: String, override val elementName: String?): Tree{ + data class Literal(val body: String, override val elementName: String? = null): Tree{ val hasNewLine = body.any { c -> c == '\n' || c == '\r' } } /** * x = y */ - data class KeyValue(val key: String, val value: Tree, override val elementName: String?): Tree + data class KeyValue(val key: String, val value: Tree, override val elementName: String? = null): Tree /** * xyz */ - data class Lazy(val body0: (Ctx) -> Iterator, override val elementName: String?): Tree + data class Lazy(val body0: (Ctx) -> Iterator, override val elementName: String? = null): Tree data class Ctx( val width: Int, diff --git a/pprint-kotlin-kmp/src/jvmTest/kotlin/io/exoquery/pprint/KmpExamples.kt b/pprint-kotlin-kmp/src/jvmTest/kotlin/io/exoquery/pprint/KmpExamples.kt index 76c8314..b5bed6e 100644 --- a/pprint-kotlin-kmp/src/jvmTest/kotlin/io/exoquery/pprint/KmpExamples.kt +++ b/pprint-kotlin-kmp/src/jvmTest/kotlin/io/exoquery/pprint/KmpExamples.kt @@ -3,6 +3,7 @@ package io.exoquery.pprint import io.exoquery.kmp.pprint.PPrintSequenceSerializer import io.exoquery.kmp.pprint.PPrinter import io.exoquery.kmp.pprint +import io.exoquery.kmp.pprint.PPrinterManual import kotlinx.serialization.* import java.time.LocalDate import java.time.format.DateTimeFormatter @@ -168,6 +169,42 @@ fun customPrinter6() { println(p) } +@Serializable +sealed interface Colors { + @Serializable object Red : Colors + @Serializable object Green : Colors + @Serializable object Blue : Colors + @Serializable data class Custom(val value: String) : Colors +} + +data class PersonFavorite(val name: String, val age: Int, val favoriteColor: Colors) /// See the Sealed Hierarchies section above + +class ColorsPrinter(config: PPrinterConfig): PPrinter(Colors.serializer(), config) + +class CustomPPrinter7(config: PPrinterConfig): PPrinterManual(config) { + fun treeifyThis(x: Any?, elementName: String?) = + treeify(x, elementName, config.defaultEscapeUnicode, config.defaultShowFieldNames) + + override fun treeify(x: Any?, elementName: String?, escapeUnicode: Boolean, showFieldNames: Boolean): Tree = + when (x) { + is PersonFavorite -> + Tree.Apply( + "PersonFavorite", + iteratorOf(treeifyThis(x.name, "name"), treeifyThis(x.age, "age"), treeifyThis(x.favoriteColor, "favoriteColor")), + elementName + ) + is Colors -> ColorsPrinter(config).treeify(x, elementName, escapeUnicode, showFieldNames) + else -> super.treeify(x, elementName, escapeUnicode, showFieldNames) + } +} + +fun customPrinter7() { + val joe = PersonFavorite("Joe", 123, Colors.Custom("FF0000")) + val printer = CustomPPrinter7(PPrinterConfig(showGenericForCollections = true)) + val p = printer(joe) + println(p) +} + @OptIn(ExperimentalSerializationApi::class) fun main() { // showMap() @@ -180,5 +217,6 @@ fun main() { // GADT1.gadt() //GADT2.gadt() - customPrinter6() + //customPrinter6() + customPrinter7() } \ No newline at end of file