https://github.com/bkahlert/kommons-debug
Kotlin Multiplatform Library with print debugging, Unicode and other features you did not know you were missing
Science Score: 18.0%
This score indicates how likely this project is to be science-related based on various indicators:
-
✓CITATION.cff file
Found CITATION.cff file -
○codemeta.json file
-
○.zenodo.json file
-
○DOI references
-
○Academic publication links
-
○Academic email domains
-
○Institutional organization owner
-
○JOSS paper metadata
-
○Scientific vocabulary similarity
Low similarity (7.8%) to scientific vocabulary
Keywords
Repository
Kotlin Multiplatform Library with print debugging, Unicode and other features you did not know you were missing
Basic Info
Statistics
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 1
- Releases: 0
Topics
Metadata Files
README.md
This project moved ...
Please note that this project is now part of the Kommons library.
Below you will find the description of the last release.
Kommons Debug
<!--
-->

About
Kommons Debug is a Kotlin Multiplatform Library that adds:
- powerful print debugging functions:
- runtime information on the running program, its platform and a platform-independent stack trace
- easily accessible Unicode support
- string handling functions
- regex functions such as the possibility to use glob patterns
- facilitated time handling using
Now,Yesterday, andTomorrow - factories to easily implement
of/ofOrNull,from/fromOrNull, andparse/parseOrNull - file handling features such as locating source files and accessing the class path with the NIO2 API
Installation / setup
Kommons Debug is hosted on GitHub with releases provided on Maven Central.
Gradle
implementation("com.bkahlert.kommons:kommons-debug:0.14.0")Maven
xml <dependency> <groupId>com.bkahlert.kommons</groupId> <artifactId>kommons-debug</artifactId> <version>0.14.0</version> </dependency>
Features
Print Debugging
Any?.trace / Any?.inspect
Print tracing information and easily cleanup afterward using IntelliJ's code cleanup feature.
Example
```kotlin data class Foo(val bar: String = "baz") { private val baz = 42.0 fun compute() // used to demonstrate that trace/inspect return their argument unchanged }
Foo().trace.compute() // output: (sample.kt:5) ⟨ Foo(bar=baz) ⟩
Foo().trace("caption").compute() // output: (sample.kt:8) caption ⟨ Foo(bar=baz) ⟩
Foo().trace("details") { it.bar.reversed() }.compute() // output: (sample.kt:11) details ⟨ Foo(bar=baz) ⟩ { "zab" }
Foo().inspect.compute() // output: (sample.kt:14) ⟨ !Foo { baz: !Double 42.0, bar: !String "baz" } ⟩
Foo().inspect("caption").compute() // output: (sample.kt:17) caption ⟨ !Foo { baz: !Double 42.0, bar: !String "baz" } ⟩
Foo().inspect("details") { it.bar.reversed() }.compute() // output: (sample.kt:20) details ⟨ !Foo { baz: !Double 42.0, bar: !String "baz" } ⟩ { !String "zab" } ```

The examples above also work in browsers:


Any.renderType()
Renders any object's type
Examples
```kotlin "string".renderType() // String
class Foo(val bar: Any = "baz") foo().renderType() // Foo
val lambda: (String) -> Unit = {} lambda.renderType() // (String)->Unit ```
Any?.render() / Any.asString()
Renders any object depending on whether its toString() is overridden:
- If there is a custom
toString()it's simply used. - if there is no custom
toString()the object is serialized in the form structurally
Examples
```kotlin "string".render() // string
class Foo(val bar: Any = "baz")
foo().render() // { bar: "baz" } foo(foo()).render(typed = true) // Foo { bar: Foo { bar: "baz" } }
foo().asString() // { bar: "baz" } foo(null).asString(excludeNullValues = false) // { } ```
Any?.asEmoji()
Renders any object as an emoji.
Examples
kotlin
null.asEmoji() // "❔"
true.asEmoji() // "✅"
false.asEmoji() // "❌"
Now.asEmoji() // "🕝"
Now.asEmoji(Floor) // "🕑"
"other".asEmoji() // "🔣"
Any.properties
Contains a map of the object's properties with each entry representing the name and value of a property.
Examples
```kotlin "string".properties // { length: 6 }
class Foo(val bar: Any = "baz") foo().properties // { bar: "baz" } foo(foo()).properties // { bar: { bar: "baz" } } ```
URL / URI / Path / File // open / locate [only JVM]
Any URL, URI, Path and File can be opened locally using open.
kotlin
URL("file:///home/john/dev/project/src/jvm/kotlin/packages/source.kt").open()
In order to only open the directory containing an above-mentioned resource
locate can be used.
kotlin
URL("file:///home/john/dev/project/src/jvm/kotlin/packages/source.kt").locate()
Runtime
Program
Reflects the running program and provides:
Program.isDebugging: Returns whether the program is running in debug mode.Program.isIntelliJ: Returns whether the program is running in IntelliJ.Program.onExit: Allows registering callbacks that are invoked when the program exits.
Platform
Reflects the platform the program runs on (e.g. Platform.JVM)
and provides:
Platform.ansiSupport: Returns to what extent ANSI escape codes are supported.Platform.fileSeparator: Returns the separator used to separate path segments.
Stack Trace
Access the current stack trace by a simple call to StackTrace.get()
or locate a specific caller using StackTrace.get().findLastKnownCallOrNull.
Examples
```kotlin fun foo(block: () -> StackTraceElement?) = block() fun bar(block: () -> StackTraceElement?) = block()
foo { bar { StackTrace.findLastKnownCallOrNull("bar") } }?.function // "foo" foo { bar { StackTrace.findLastKnownCallOrNull(::bar) } }?.function // "foo" ```
Unicode Support
Handling user input requires functions to handle Unicode correctly, unless you're not afraid of the following:
kotlin
"👨🏾🦱".substring(0, 3) // "👨?", skin tone gone, curly hair gone
"👩👩👦👦".substring(1, 7) // "?👩?", wife gone, kids gone
Decode any string to a sequence / list of code points using String.asCodePointSequence / String.toCodePointList.
Decode any string to a sequence / list of graphemes using String.asGraphemeSequence / String.toGraphemeList.
Transliterations and transforms can be done using String.transform.
Examples
```kotlin "a".asCodePoint().name // "LATIN SMALL LETTER A" "a𝕓c̳🔤".toCharArray() // "a", "?", "?", "c", "̳", "?", "?" "a𝕓c̳🔤".toCodePointList() // "a", "𝕓", "c", "̳", "🫠" "a𝕓c̳🔤".toGraphemeList() // "a", "𝕓", "c̳", "🫠"
"a𝕓🫠🇩🇪👨🏾🦱👩👩👦👦".length // 27 (= number of Java chars) "a𝕓🫠🇩🇪👨🏾🦱👩👩👦👦".asText(CodePoint).length // 16 (= number of Unicode code points) "a𝕓🫠🇩🇪👨🏾🦱👩👩👦👦".asText(Grapheme).length // 6 (= visually perceivable units)
"a𝕓🫠🇩🇪👨🏾🦱👩👩👦👦".truncate(7.characters) // "a\uD835 … 👦" "a𝕓🫠🇩🇪👨🏾🦱👩👩👦👦".truncate(7.codePoints) // "a𝕓 … 👦" "a𝕓🫠🇩🇪👨🏾🦱👩👩👦👦".truncate(7.graphemes) // "a𝕓 … 👨🏾🦱👩👩👦👦"
"© А-З Ä-ö-ß".transform("deDE", "deDE-ASCII") // "(C) A-Z AE-oe-ss" ```
UTF-16 Char vs Code Point vs Grapheme Cluster
| UTF-16 | Char
(Java, JavaScript, Kotlin, ...) | Unicode
Code Point | Unicode
Grapheme Cluster |
|:---------------------------------------:|------------------------------------------------------|-------------------------------------------|------------------------------|
| \u0061 | a (LATIN SMALL LETTER A) | a | a |
| \uD835
\uDD53 | 𝕓 (MATHEMATICAL DOUBLE-STRUCK SMALL B) | 𝕓 | 𝕓 |
| \uD83E
\uDEE0
\uD83C
\uDDE9 | ? (HIGH SURROGATES D83E)
? (LOW SURROGATES DEE0) | 🫠 (MELTING FACE EMOJI) | 🫠 |
| \uD83C
\uDDE9 | ? (HIGH SURROGATES D83C)
? (LOW SURROGATES DDE9) | [D] (REGIONAL INDICATOR SYMBOL LETTER D) | 🇩🇪 |
| \uD83C
\uDDEA | ? (HIGH SURROGATES D83C)
? (LOW SURROGATES DDEA) | [E] (REGIONAL INDICATOR SYMBOL LETTER E) | |
| \uD83D
\uDC68 | ? (HIGH SURROGATES D83D)
? (LOW SURROGATES DC68) | 👨 (MAN) | 👨🏾🦱 |
| \uD83C
\uDFFE | ? (HIGH SURROGATES D83C)
? (LOW SURROGATES DFFE) | 🏾 (EMOJI MODIFIER FITZPATRICK TYPE-5) | |
| \u200D | [ZWJ] (ZERO WIDTH JOINER) | [ZWJ] (ZERO WIDTH JOINER) | |
| \uD83E
\uDDB1 | ? (HIGH SURROGATES D83E)
? (LOW SURROGATES DDB1) | 🦱 (EMOJI COMPONENT CURLY HAIR) | |
| \uD83D
\uDC69 | ? (HIGH SURROGATES D83D)
? (LOW SURROGATES DC69) | 👩 (WOMAN) | 👩👩👦👦 |
| \u200D | [ZWJ] (ZERO WIDTH JOINER) | [ZWJ] (ZERO WIDTH JOINER) | |
| \uD83D
\uDC69 | ? (HIGH SURROGATES D83D)
? (LOW SURROGATES DC69) | 👩 (WOMAN) | |
| \u200D | [ZWJ] (ZERO WIDTH JOINER) | [ZWJ] (ZERO WIDTH JOINER) | |
| \uD83D
\uDC66 | ? (HIGH SURROGATES D83D)
? (LOW SURROGATES DC66) | 👦 (BOY) | |
| \u200D | [ZWJ] (ZERO WIDTH JOINER) | [ZWJ] (ZERO WIDTH JOINER) | |
| \uD83D
\uDC66 | ? (HIGH SURROGATES D83D)
? (LOW SURROGATES DC66) | 👦 (BOY) | |
String Handling
quoted: quotes and escapes an existing stringansiRemoved: removes ANSI escape sequencesspaced/startSpaced/endSpaced: adds a space before and/or after a string if there isn't already onetruncate/truncateStart/truncateEnd: truncates a string to a given lengthtoIdentifier: create an identifier from any string that resembles itrandomString: create a random string- LineSeparators: many extension functions to work with usual and exotic Unicode line breaks.
Examples
```kotlin "string".quoted // "string" """{ bar: "baz" }""".quoted // "{ bar: \"baz\" }"
""" line 1 "line 2" """.quoted // "line1\n\"line2\""
"\u001B[1mbold \u001B[34mand blue\u001B[0m".ansiRemoved // "bold and blue"
"\u001B[34m↗\u001B(B\u001B[m \u001B]8;;https://example.com\u001B\link\u001B]8;;\u001B\".ansiRemoved // "↗ link"
"string".spaced // " string "
"bar".withPrefix("foo") // "foobar" "foo bar".withPrefix("foo") // "foo bar" "foo".withSuffix("bar") // "foobar"
"12345678901234567890".truncate() // "123456 … 567890" "12345678901234567890".truncateStart() // " … 901234567890" "12345678901234567890".truncateEnd() // "123456789012 … "
"1👋 xy-z".toIdentifier() // "i__xy-z3"
randomString() // returns "Ax-212kss0-xTzy5" (16 characters by default) ```
Capitalize / decapitalize strings using capitalize/decapitalize or
manipulate the case style using toCasesString or any of its specializations.
Examples
```kotlin "fooBar".capitalize() // "FooBar" "FooBar".decapitalize() // "fooBar"
"FooBar".toCamelCasedString() // "fooBar" "FooBar".toPascalCasedString() // "FooBar" "FooBar".toScreamingSnakeCasedString() // "FOO_BAR" "FooBar".toKebabCasedString() // "foo-bar" "FooBar".toTitleCasedString() // "Foo Bar"
enum class FooBar { FooBaz }
FooBar::class.simpleCamelCasedName // "fooBar" FooBar::class.simplePascalCasedName // "FooBar" FooBar::class.simpleScreamingSnakeCasedName // "FOO_BAR" FooBar::class.simpleKebabCasedName // "foo-bar" FooBar::class.simpleTitleCasedName // "Foo Bar"
FooBar.FooBaz.camelCasedName // "fooBaz" FooBar.FooBaz.pascalCasedName // "FooBaz" FooBar.FooBaz.screamingSnakeCasedName // "FOO_BAZ" FooBar.FooBaz.kebabCasedName // "foo-baz" FooBar.FooBaz.titleCasedName // "Foo Baz ```
Easily check edge-case with a fluent interface as does requireNotNull does:
Examples
kotlin
requireNotEmpty("abc") // passes and returns "abc"
requireNotBlank(" ") // throws IllegalArgumentException
checkNotEmpty("abc") // passes and returns "abc"
checkNotBlank(" ") // throws IllegalStateException
"abc".takeIfNotEmpty() // returns "abc"
" ".takeIfNotBlank() // returns null
"abc".takeUnlessEmpty() // returns "abc"
" ".takeUnlessBlank() // returns null
Regular Expressions
Regex can be authored as follows:
```kotlin Regex("foo") + Regex("bar") // Regex("foobar") Regex("foo") + "bar" // Regex("foobar")
Regex("foo") or Regex("bar") // Regex("foo|bar") Regex("foo") or "bar" // Regex("foo|bar")
Regex.fromLiteralAlternates( // Regex("\[foo\]|bar\?") "[foo]", "bar?" )
Regex("foo").optional() // Regex("(?:foo)?") Regex("foo").repeatAny() // Regex("(?:foo)*") Regex("foo").repeatAtLeastOnce() // Regex("(?:foo)+") Regex("foo").repeat(2, 5) // Regex("(?:foo){2,5}")
Regex("foo").group() // Regex("(?:foo)")
Regex("foo").group("name") // Regex("(?
Find matches easier:
```kotlin
// get group by name
Regex("(?
// get group value by name
Regex("(?
// find all values
Regex("(?
// match URLs / URIs Regex.UrlRegex.findAll(/* ... /) Regex.UriRegex.findAll(/ ... */) ```
Match multiline strings with simple glob patterns:
```kotlin // matching within lines with wildcard "foo.bar()".matchesGlob("foo.*") // ✅
// matching across lines with multiline wildcard """ foo .bar() .baz() """.matchesGlob( """ foo .**() """.trimIndent() // ✅ )
""" foo .bar() .baz() """.matchesGlob( """ foo .() """.trimIndent() // ❌ ( doesn't match across lines) ) ```
Alternatively, you can use matchesCurly if you prefer SLF4J / Logback style
wildcards {} and {{}}.
Collections and Ranges
Require or check emptiness of collections and arrays using requireNotEmpty
and checkNotEmpty.
Iterate any type of closed ranges using asIterable.
Examples
kotlin
(-4.2..42.0)
.asIterable { it + 9 }
.map { it.toInt() } // [-4, 4, 13, 22, 31, 40]
Time Handling
```kotlin Now + 2.seconds // 2 seconds in the future Today - 3.days // 3 days in the past Yesterday - 2.days // 3 days in the past Tomorrow + 1.days // the day after tomorrow Instant.parse("1910-06-22T13:00:00Z") + 5.minutes // 1910-06-22T12:05:00Z LocalDate.parse("1910-06-22") - 2.days // 1910-06-20 SystemLocations.Temp.createTempFile().age // < 1ms
Now.toMomentString() // "now" (Now - 12.hours).toMomentString() // "12h ago" (Now + 3.days).toMomentString() // "in 3d" (Today - 1.days).toMomentString() // "yesterday" ```
Byte Handling
toHexadecimalString(), toOctalString(), toBinaryString()
The extension functions
toHexadecimalString()toOctalString()toBinaryString()
... are provided for:
ByteByteArrayIntLongUByteUByteArrayUIntULong
Examples
```kotlin val byteArray = byteArrayOf(0x00, 0x7f, -0x80, -0x01) val largeByteArrayOf = byteArrayOf(-0x01, -0x01, -0x01, -0x01, -0x01, -0x01, -0x01, -0x01, -0x01, -0x01, -0x01, -0x01, -0x01, -0x01, -0x01, -0x01) val veryLargeByteArray = byteArrayOf(0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
byteArray.map { it.toHexadecimalString() } // "00", "7f", "80", "ff" byteArray.toHexadecimalString() // "007f80ff" largeByteArrayOf.toHexadecimalString() // "ffffffffffffffffffffffffffffffff" veryLargeByteArray.toHexadecimalString() // "0100000000000000000000000000000000"
byteArray.map { it.toOctalString() } // "000", "177", "200", "377" byteArray.toOctalString() // "000177200377" largeByteArrayOf.toOctalString() // "377377377377377377377377377377377377377377377377" veryLargeByteArray.toOctalString() // "001000000000000000000000000000000000000000000000000"
byteArray.map { it.toBinaryString() } // "00000000", "01111111", "10000000", "11111111" byteArray.toBinaryString() // "00000000011111111000000011111111" largeByteArrayOf.toBinaryString() // "111111111111111111111111111...111111" veryLargeByteArray.toBinaryString() // "00000001000000000000000000000000000...000000" ```
Further conversions:
Int.toByteArray()Long.toByteArray()UInt.toUByteArray()ULong.toUByteArray()
Checksums
Compute MD5, SHA-1, and SHA-256 checksums for arbitrary files.
Examples
kotlin
val file = SystemLocations.Home / ".gitconfig"
file.md5Checksum()
file.sha1Checksum()
file.sha256Checksum()
Factories
The Factory interface provides
the factory builders creator, converter, and parser to easily implement
the factory methods of/ofOrNull, from/fromOrNull, and parse/parseOrNull
as shown in the following example:
``kotlin
data class Version(val major: Int, val minor: Int, val patch: Int) {
companion object : Parser<Version> by (parser { // Theparsingmethod supports the following outcomes:
it.split('.').let { (major, minor, patch) -> // - return aVersioninstance in case of success
Version(major.toInt(), minor.toInt(), patch.toInt()) // - returnnull` a generic ParsingException is thrown.
} // - If you throw an exception it will be wrapped in a ParsingException.
})
}
Version.parseOrNull("1.2.3") // returns Version(1, 2, 3) Version.parse("invalid") // throws ParsingException: "Failed to parse "invalid" into an instance of Version" ```
File Handling [only JVM]
Easily access your working directory with SystemLocations.Work,
your home directory with SystemLocations.Home and your system's
temporary directory with SystemLocations.Temp.
Create files with contents in one call using
createTextFilecreateBinaryFilecreateTempTextFilecreateTempBinaryFile
Safely read files with
useInputStream, useBufferedInputStream, useReader, and useBufferedReader,
and write files with
useOutputStream, useBufferedOutputStream, useWriter, and useBufferedWriter.
Find the class directory, the source directory or the source file itself of a class.
Example
kotlin
Foo::class.findClassesDirectoryOrNull() // /home/john/dev/project/build/classes/kotlin/jvm/test
Foo::class.findSourceDirectoryOrNull() // /home/john/dev/project/src/jvmTest/kotlin
Foo::class.findSourceFileOrNull() // /home/john/dev/project/src/jvmTest/kotlin/packages/source.kt
Access class path resources like any other NIO 2 path using the classpath URI scheme.
Example
kotlin
Paths.get("classpath:dir/to/resource").readText()
Paths.get("classpath:dir/to/resource").readBytes()
Paths.get("classpath:dir/to/resource").copyToDirectory(SystemLocations.Temp)
Paths.get("classpath:dir/to/resource").useBufferedReader { it.readLine() }
Miscellaneous
Scaling
```kotlin 0.5.scale(+0.5) // = +0.75 (0.5 scaled 50% closer to +1.0) 0.5.scale(-0.5) // = -0.25 (0.5 scaled 50% closer to -1.0)
4.0.scale(+0.5, -10.0..+10.0) // = +7.0 (4.0 scaled 50% closer to +10.0) 4.0.scale(-0.5, -10.0..+10.0) // = -4.0 (4.0 scaled 50% closer to -10.0) ```
Either
Generic either type that can be used as a replacement for Result,
i.e. in cases where the alternative value doesn't necessarily mean failure.
Available methods are:
getLeftOrThrow/getRightOrThrowgetLeftOrElse/getRightOrElsegetLeftOrDefault/getRightOrDefaultfold/mapLeft/mapRightonLeft/onLefttoResult/Result.toEither
kotlin
val foo: Either<Foo, Bar> = Left(Foo)
foo.getLeftOrThrow() // returns Foo
foo.getRighttOrThrow() // throws an exception
Contributing
Want to contribute? Awesome! The most basic way to show your support is to star the project, or to raise issues. You can also support this project by making a PayPal donation to ensure this journey continues indefinitely!
Thanks again for your support, it is much appreciated! :pray:
License
MIT. See LICENSE for more details.
Owner
- Name: Björn Kahlert
- Login: bkahlert
- Kind: user
- Location: Berlin, Germany
- Website: bkahlert.com
- Repositories: 23
- Profile: https://github.com/bkahlert
Citation (CITATION.cff)
cff-version: 1.2.0
message: "If you use this software, please cite it as below."
authors:
- given-names: "Björn"
family-names: "Kahlert"
orcid: "https://orcid.org/0000-0003-1705-4067"
title: "Kommons Debug — A Kotlin Multiplatform Library to support print debugging"
version: 0.14.0
date-released: 2022-08-08
url: "https://github.com/bkahlert/kommons-debug"
GitHub Events
Total
Last Year
Dependencies
- com.bkahlert.kommons:kommons-test 0.3.2 implementation
- org.slf4j:slf4j-api 1.7.36 implementation
- org.slf4j:slf4j-simple 1.7.36 implementation
- 298 dependencies