From d6496f02e6408fa21cdc632bdf48deeda2777142 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 15 Feb 2026 18:21:57 +0600 Subject: [PATCH 1/7] start 0.28.1 --- CHANGELOG.md | 2 ++ gradle.properties | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d37c2c2b519..b5adb6ab5cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog +## 0.28.1 + ## 0.28.0 **THIS VERSION CONTAINS BREAKING CHANGES DUE TO EXPOSED 1.0.0 UPDATE** diff --git a/gradle.properties b/gradle.properties index aaf41096ef1..be67c15a5ca 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,5 +18,5 @@ crypto_js_version=4.1.1 # Project data group=dev.inmo -version=0.28.0 -android_code_version=309 +version=0.28.1 +android_code_version=310 From b2e6ab51cbfd715bcb3775293ffa6384de29e157 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 15 Feb 2026 18:35:43 +0600 Subject: [PATCH 2/7] update runCatchingLogging to rethrow CanellationException --- .../coroutines/RunCatchingLogging.kt | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/RunCatchingLogging.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/RunCatchingLogging.kt index 7dc9239cd03..a6d875ba1e8 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/RunCatchingLogging.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/RunCatchingLogging.kt @@ -2,11 +2,27 @@ package dev.inmo.micro_utils.coroutines import dev.inmo.kslog.common.KSLog import dev.inmo.kslog.common.e +import kotlin.coroutines.cancellation.CancellationException +/** + * Executes the given [block] within a `runCatching` context and logs any exceptions that occur, excluding + * `CancellationException` which is rethrown. This method simplifies error handling by automatically logging + * the errors using the provided [logger]. + * + * @param T The result type of the [block]. + * @param R The receiver type on which this function operates. + * @param errorMessageBuilder A lambda to build the error log message. By default, it returns a generic error message. + * @param logger The logging instance used for logging errors. Defaults to [KSLog]. + * @param block The code block to execute within the `runCatching` context. + * @return A [Result] representing the outcome of executing the [block]. + */ inline fun R.runCatchingLogging( noinline errorMessageBuilder: R.(Throwable) -> Any = { "Something web wrong" }, logger: KSLog = KSLog, block: R.() -> T ) = runCatching(block).onFailure { - logger.e(it) { errorMessageBuilder(it) } + when (it) { + is CancellationException -> throw it + else -> logger.e(it) { errorMessageBuilder(it) } + } } From 3ae2c84c0855d1f8ecb22b06dc0e92ce37b5ce72 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 15 Feb 2026 18:38:07 +0600 Subject: [PATCH 3/7] fill changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5adb6ab5cf..43ff8c66217 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## 0.28.1 +* `Coroutines`: + * `runCatchingLogging` updated to rethrow `CancellationException` and log other exceptions + ## 0.28.0 **THIS VERSION CONTAINS BREAKING CHANGES DUE TO EXPOSED 1.0.0 UPDATE** From 50369e09045d89defaac6dd4f995ad1fd237e79e Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Tue, 24 Feb 2026 15:11:43 +0600 Subject: [PATCH 4/7] migrate onto 0.29.0 --- CHANGELOG.md | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 43ff8c66217..a71392940c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 0.28.1 +## 0.29.0 * `Coroutines`: * `runCatchingLogging` updated to rethrow `CancellationException` and log other exceptions diff --git a/gradle.properties b/gradle.properties index be67c15a5ca..f0cd81703dd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,5 +18,5 @@ crypto_js_version=4.1.1 # Project data group=dev.inmo -version=0.28.1 +version=0.29.0 android_code_version=310 From 8e8915d84c43c88538af4302ffc64a3bfff8ba2a Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Tue, 24 Feb 2026 15:16:45 +0600 Subject: [PATCH 5/7] update dependencies --- CHANGELOG.md | 5 +++++ gradle/libs.versions.toml | 8 ++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a71392940c3..f80de1b5a86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## 0.29.0 +* `Versions`: + * `Kotlin`: `2.3.0` -> `2.3.10` + * `KSLog`: `1.5.2` -> `1.6.0` + * `KSP`: `2.3.4` -> `2.3.6` + * `Compose`: `1.10.0` -> `1.10.1` * `Coroutines`: * `runCatchingLogging` updated to rethrow `CancellationException` and log other exceptions diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 10b96d634b2..eb757fb6dbc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,14 +1,14 @@ [versions] -kt = "2.3.0" +kt = "2.3.10" kt-serialization = "1.10.0" kt-coroutines = "1.10.2" kotlinx-browser = "0.5.0" -kslog = "1.5.2" +kslog = "1.6.0" -jb-compose = "1.10.0" +jb-compose = "1.10.1" jb-compose-material3 = "1.10.0-alpha05" jb-compose-icons = "1.7.8" jb-exposed = "1.0.0" @@ -28,7 +28,7 @@ koin = "4.1.1" okio = "3.16.4" -ksp = "2.3.4" +ksp = "2.3.6" kotlin-poet = "2.2.0" versions = "0.53.0" From 3df90b199379fdb11247db8e421ec24b61665ec8 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Tue, 24 Feb 2026 15:40:05 +0600 Subject: [PATCH 6/7] update sqlite (wow^^) --- CHANGELOG.md | 1 + gradle/libs.versions.toml | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f80de1b5a86..b9a57008da8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * `KSLog`: `1.5.2` -> `1.6.0` * `KSP`: `2.3.4` -> `2.3.6` * `Compose`: `1.10.0` -> `1.10.1` + * `SQLite`: `3.50.1.0` -> `3.51.2.0` * `Coroutines`: * `runCatchingLogging` updated to rethrow `CancellationException` and log other exceptions diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index eb757fb6dbc..d7ec923ba7a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,8 +14,7 @@ jb-compose-icons = "1.7.8" jb-exposed = "1.0.0" jb-dokka = "2.1.0" -# 3.51.0.0 contains bug, checking with ./gradlew :micro_utils.repos.exposed:jvmTest -sqlite = "3.50.1.0" +sqlite = "3.51.2.0" korlibs = "5.4.0" uuid = "0.8.4" From 4f270d904749548e5b5d63362b7db1a9883b0be5 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Tue, 24 Feb 2026 18:18:10 +0600 Subject: [PATCH 7/7] generate docs for a lot of API (test try) --- .../src/androidMain/kotlin/AlertDialog.kt | 16 + .../pickers/src/androidMain/kotlin/Fling.kt | 10 + .../src/androidMain/kotlin/NumberPicker.kt | 12 + .../src/androidMain/kotlin/SetPicker.kt | 12 + .../src/commonMain/kotlin/StandardColors.kt | 592 ++++++++++++++++++ .../inmo/micro_utils/common/BreakAsPairs.kt | 10 + .../dev/inmo/micro_utils/common/IfBoolean.kt | 42 ++ .../inmo/micro_utils/common/InputAllocator.kt | 26 + .../common/IterableFirstNotNull.kt | 7 + .../dev/inmo/micro_utils/common/JoinTo.kt | 79 +++ .../dev/inmo/micro_utils/common/Mapper.kt | 72 +++ .../inmo/micro_utils/common/PadExtensions.kt | 87 +++ .../micro_utils/common/RangeIntersection.kt | 22 + .../inmo/micro_utils/common/WithReplaced.kt | 19 + .../inmo/micro_utils/coroutines/ActorAsync.kt | 12 + .../micro_utils/coroutines/DataAsDeferred.kt | 7 + .../micro_utils/coroutines/DoInContext.kt | 33 + .../micro_utils/coroutines/DoWithFirst.kt | 112 ++++ .../coroutines/FlowCollectorInvoking.kt | 7 + .../micro_utils/coroutines/FlowDebouncedBy.kt | 28 + .../coroutines/FlowFirstNotNull.kt | 8 + .../micro_utils/coroutines/FlowFlatten.kt | 46 ++ .../micro_utils/coroutines/FlowNullables.kt | 13 + .../coroutines/FlowSubscriptionAsync.kt | 28 + .../inmo/micro_utils/coroutines/FlowsSum.kt | 8 + .../micro_utils/coroutines/LaunchLogging.kt | 46 ++ .../micro_utils/coroutines/ReplaceResult.kt | 10 + .../coroutines/SmartRWLockerExtensions.kt | 17 + .../micro_utils/coroutines/SupervisorJob.kt | 30 + .../dev/inmo/micro_utils/coroutines/DoInIO.kt | 13 + .../coroutines/LaunchInCurrentThread.kt | 10 + .../coroutines/LaunchSynchronously.kt | 34 + .../dev/inmo/micro_utils/crypto/Base64.kt | 51 ++ .../kotlin/dev/inmo/micro_utils/crypto/Hex.kt | 14 + .../kotlin/dev/inmo/micro_utils/crypto/MD5.kt | 14 + .../dev/inmo/micro_utils/fsm/common/State.kt | 8 + .../common/utils/StateHandlingErrorHandler.kt | 20 + .../src/main/kotlin/KClassTypeName.kt | 11 + .../src/main/kotlin/ResolveSubclasses.kt | 7 + .../micro_utils/ktor/client/BodyOrNull.kt | 15 + .../ktor/client/HttpStatusCodeAsThrowable.kt | 7 + .../ktor/client/MPPFileInputProvider.kt | 6 + .../ktor/client/OnUploadCallback.kt | 6 + .../micro_utils/ktor/client/TemporalUpload.kt | 9 + .../inmo/micro_utils/ktor/client/UniUpload.kt | 8 + .../ktor/common/BuildStandardUrl.kt | 27 + .../ktor/common/CorrectCloseException.kt | 5 + .../ktor/common/CorrectWebsocketUrl.kt | 8 + .../ktor/common/FromToDateTimeUrl.kt | 16 + .../micro_utils/ktor/common/InputProvider.kt | 4 + .../micro_utils/ktor/common/MPPFileInput.kt | 6 + .../ktor/common/QueryParamsBuilder.kt | 7 + .../ktor/common/StandardSerializer.kt | 46 ++ .../micro_utils/ktor/common/TemporalFiles.kt | 9 + .../ktor/server/FromToDateTimeInUrl.kt | 6 + .../ktor/server/QueryParameters.kt | 40 ++ .../ktor/server/RespondOrNoContent.kt | 7 + .../language_codes/IetfLangSerializer.kt | 4 + .../dev/inmo/micro_utils/matrix/Matrix.kt | 6 + .../micro_utils/matrix/MatrixFactories.kt | 22 + .../kotlin/dev/inmo/micro_utils/matrix/Row.kt | 5 + .../micro_utils/mime_types/CustomMimeType.kt | 6 + .../inmo/micro_utils/mime_types/MimeType.kt | 15 + .../mime_types/MimeTypeSerializer.kt | 5 + .../micro_utils/pagination/utils/DoForAll.kt | 33 + .../micro_utils/pagination/utils/GetAll.kt | 60 ++ .../pagination/utils/OptionallyReverse.kt | 32 + .../micro_utils/pagination/utils/Paginate.kt | 41 ++ .../pagination/utils/PaginatedIterable.kt | 27 +- .../micro_utils/pagination/QueryExtensions.kt | 17 + .../inmo/micro_utils/repos/KeyValuesRepo.kt | 113 +++- .../dev/inmo/micro_utils/repos/MapperRepo.kt | 50 ++ .../kotlin/dev/inmo/micro_utils/repos/Repo.kt | 5 + .../micro_utils/repos/diff/CRUDRepoDiff.kt | 26 + .../micro_utils/repos/mappers/CRUDMappers.kt | 48 ++ .../pagination/CRUDPaginationExtensions.kt | 21 + .../repos/pagination/MaxPagePagination.kt | 20 + .../src/commonMain/kotlin/SafeWrapper.kt | 63 +- .../serialization/base64/Base64Serializer.kt | 15 + .../typed_serializer/TypedSerializer.kt | 49 ++ .../src/commonMain/kotlin/TransactionsDSL.kt | 17 + 81 files changed, 2519 insertions(+), 6 deletions(-) diff --git a/android/alerts/common/src/androidMain/kotlin/AlertDialog.kt b/android/alerts/common/src/androidMain/kotlin/AlertDialog.kt index 7277b23c819..f47e82f8b2b 100644 --- a/android/alerts/common/src/androidMain/kotlin/AlertDialog.kt +++ b/android/alerts/common/src/androidMain/kotlin/AlertDialog.kt @@ -1,3 +1,6 @@ +/** + * Utility functions for creating Android AlertDialogs with simplified API. + */ @file:Suppress("NOTHING_TO_INLINE", "unused") package dev.inmo.micro_utils.android.alerts.common @@ -6,8 +9,21 @@ import android.app.AlertDialog import android.content.Context import android.content.DialogInterface +/** + * Type alias for alert dialog button callbacks. + */ typealias AlertDialogCallback = (DialogInterface) -> Unit +/** + * Creates an [AlertDialog.Builder] template with configurable title and buttons. + * This provides a simplified API for creating alert dialogs with positive, negative, and neutral buttons. + * + * @param title Optional dialog title + * @param positivePair Optional positive button as a pair of (text, callback) + * @param neutralPair Optional neutral button as a pair of (text, callback) + * @param negativePair Optional negative button as a pair of (text, callback) + * @return An [AlertDialog.Builder] configured with the specified parameters + */ inline fun Context.createAlertDialogTemplate( title: String? = null, positivePair: Pair? = null, diff --git a/android/pickers/src/androidMain/kotlin/Fling.kt b/android/pickers/src/androidMain/kotlin/Fling.kt index 72eae558909..f3d3479a96a 100644 --- a/android/pickers/src/androidMain/kotlin/Fling.kt +++ b/android/pickers/src/androidMain/kotlin/Fling.kt @@ -2,6 +2,16 @@ package dev.inmo.micro_utils.android.pickers import androidx.compose.animation.core.* +/** + * Performs a fling animation with an optional target adjustment. + * If [adjustTarget] is provided, animates to the adjusted target. Otherwise, performs a decay animation. + * + * @param initialVelocity The initial velocity of the fling + * @param animationSpec The decay animation specification + * @param adjustTarget Optional function to adjust the target value based on the calculated target + * @param block Optional block to be executed during the animation + * @return The result of the animation + */ internal suspend fun Animatable.fling( initialVelocity: Float, animationSpec: DecayAnimationSpec, diff --git a/android/pickers/src/androidMain/kotlin/NumberPicker.kt b/android/pickers/src/androidMain/kotlin/NumberPicker.kt index 59265e0f855..f1925677446 100644 --- a/android/pickers/src/androidMain/kotlin/NumberPicker.kt +++ b/android/pickers/src/androidMain/kotlin/NumberPicker.kt @@ -41,6 +41,18 @@ private inline fun PointerInputScope.checkContains(offset: Offset): Boolean { // src: https://gist.github.com/vganin/a9a84653a9f48a2d669910fbd48e32d5 +/** + * A Compose number picker component that allows users to select a number by dragging, using arrow buttons, + * or manually entering a value. + * + * @param number The currently selected number + * @param modifier The modifier to be applied to the picker + * @param range Optional range of valid numbers. If specified, the picker will be limited to this range + * @param textStyle The text style for displaying numbers + * @param arrowsColor The color of the up/down arrow buttons + * @param allowUseManualInput Whether to allow manual keyboard input for the number + * @param onStateChanged Callback invoked when the selected number changes + */ @OptIn(ExperimentalTextApi::class, ExperimentalComposeUiApi::class) @Composable fun NumberPicker( diff --git a/android/pickers/src/androidMain/kotlin/SetPicker.kt b/android/pickers/src/androidMain/kotlin/SetPicker.kt index 60b2fbe34cf..9960d32a666 100644 --- a/android/pickers/src/androidMain/kotlin/SetPicker.kt +++ b/android/pickers/src/androidMain/kotlin/SetPicker.kt @@ -22,6 +22,18 @@ import androidx.compose.ui.unit.dp import kotlinx.coroutines.launch import kotlin.math.* +/** + * A Compose picker component that allows users to select an item from a list by dragging or using arrow buttons. + * + * @param T The type of items in the list + * @param current The currently selected item + * @param dataList The list of items to choose from + * @param modifier The modifier to be applied to the picker + * @param textStyle The text style for displaying items + * @param arrowsColor The color of the up/down arrow buttons + * @param dataToString A composable function to convert items to strings for display. Defaults to [Any.toString] + * @param onStateChanged Callback invoked when the selected item changes + */ @OptIn(ExperimentalTextApi::class, ExperimentalComposeUiApi::class) @Composable fun SetPicker( diff --git a/colors/src/commonMain/kotlin/StandardColors.kt b/colors/src/commonMain/kotlin/StandardColors.kt index 8a779d26f55..25c783a11b3 100644 --- a/colors/src/commonMain/kotlin/StandardColors.kt +++ b/colors/src/commonMain/kotlin/StandardColors.kt @@ -1,298 +1,890 @@ +/** + * Standard HTML/CSS color constants as [HEXAColor] extension properties. + * Provides convenient access to all standard web colors like red, blue, green, etc. + * All colors are defined with full opacity (alpha = 0xFF). + */ package dev.inmo.micro_utils.colors import dev.inmo.micro_utils.colors.common.HEXAColor +/** + * Alice Blue - A pale blue color (#F0F8FF). + */ val HEXAColor.Companion.aliceblue get() = HEXAColor(0xF0F8FFFFu) + +/** + * Antique White - A pale beige color (#FAEBD7). + */ val HEXAColor.Companion.antiquewhite get() = HEXAColor(0xFAEBD7FFu) + +/** + * Aqua - A bright cyan color (#00FFFF). + */ val HEXAColor.Companion.aqua get() = HEXAColor(0x00FFFFFFu) + +/** + * Aquamarine - A medium blue-green color (#7FFFD4). + */ val HEXAColor.Companion.aquamarine get() = HEXAColor(0x7FFFD4FFu) + +/** + * Azure - A pale cyan-blue color (#F0FFFF). + */ val HEXAColor.Companion.azure get() = HEXAColor(0xF0FFFFFFu) + +/** + * Beige - A pale sandy tan color (#F5F5DC). + */ val HEXAColor.Companion.beige get() = HEXAColor(0xF5F5DCFFu) + +/** + * Bisque - A pale orange color (#FFE4C4). + */ val HEXAColor.Companion.bisque get() = HEXAColor(0xFFE4C4FFu) + +/** + * Black - Pure black color (#000000). + */ val HEXAColor.Companion.black get() = HEXAColor(0x000000FFu) + +/** + * Blanched Almond - A pale peachy color (#FFEBCD). + */ val HEXAColor.Companion.blanchedalmond get() = HEXAColor(0xFFEBCDFFu) + +/** + * Blue - Pure blue color (#0000FF). + */ val HEXAColor.Companion.blue get() = HEXAColor(0x0000FFFFu) + +/** + * Blue Violet - A vivid purple-blue color (#8A2BE2). + */ val HEXAColor.Companion.blueviolet get() = HEXAColor(0x8A2BE2FFu) + +/** + * Brown - A dark reddish-brown color (#A52A2A). + */ val HEXAColor.Companion.brown get() = HEXAColor(0xA52A2AFFu) + +/** + * Burlywood - A sandy tan color (#DEB887). + */ val HEXAColor.Companion.burlywood get() = HEXAColor(0xDEB887FFu) + +/** + * Cadet Blue - A grayish-blue color (#5F9EA0). + */ val HEXAColor.Companion.cadetblue get() = HEXAColor(0x5F9EA0FFu) + +/** + * Chartreuse - A bright yellow-green color (#7FFF00). + */ val HEXAColor.Companion.chartreuse get() = HEXAColor(0x7FFF00FFu) + +/** + * Chocolate - A medium brown color (#D2691E). + */ val HEXAColor.Companion.chocolate get() = HEXAColor(0xD2691EFFu) + +/** + * Coral - A vibrant orange-pink color (#FF7F50). + */ val HEXAColor.Companion.coral get() = HEXAColor(0xFF7F50FFu) + +/** + * Cornflower Blue - A medium blue color (#6495ED). + */ val HEXAColor.Companion.cornflowerblue get() = HEXAColor(0x6495EDFFu) + +/** + * Cornsilk - A pale yellow color (#FFF8DC). + */ val HEXAColor.Companion.cornsilk get() = HEXAColor(0xFFF8DCFFu) + +/** + * Crimson - A vivid red color (#DC143C). + */ val HEXAColor.Companion.crimson get() = HEXAColor(0xDC143CFFu) + +/** + * Cyan - A bright cyan color (#00FFFF). + */ val HEXAColor.Companion.cyan get() = HEXAColor(0x00FFFFFFu) + +/** + * Dark Blue - A dark blue color (#00008B). + */ val HEXAColor.Companion.darkblue get() = HEXAColor(0x00008BFFu) + +/** + * Dark Cyan - A dark cyan color (#008B8B). + */ val HEXAColor.Companion.darkcyan get() = HEXAColor(0x008B8BFFu) + +/** + * Dark Goldenrod - A dark golden yellow color (#B8860B). + */ val HEXAColor.Companion.darkgoldenrod get() = HEXAColor(0xB8860BFFu) + +/** + * Dark Gray - A dark gray color (#A9A9A9). + */ val HEXAColor.Companion.darkgray get() = HEXAColor(0xA9A9A9FFu) + +/** + * Dark Green - A dark green color (#006400). + */ val HEXAColor.Companion.darkgreen get() = HEXAColor(0x006400FFu) + +/** + * Dark Grey - A dark gray color (#A9A9A9). + */ val HEXAColor.Companion.darkgrey get() = HEXAColor(0xA9A9A9FFu) + +/** + * Dark Khaki - A brownish-tan color (#BDB76B). + */ val HEXAColor.Companion.darkkhaki get() = HEXAColor(0xBDB76BFFu) + +/** + * Dark Magenta - A dark magenta/purple color (#8B008B). + */ val HEXAColor.Companion.darkmagenta get() = HEXAColor(0x8B008BFFu) + +/** + * Dark Olive Green - A dark olive green color (#556B2F). + */ val HEXAColor.Companion.darkolivegreen get() = HEXAColor(0x556B2FFFu) + +/** + * Dark Orange - A vivid dark orange color (#FF8C00). + */ val HEXAColor.Companion.darkorange get() = HEXAColor(0xFF8C00FFu) + +/** + * Dark Orchid - A dark purple color (#9932CC). + */ val HEXAColor.Companion.darkorchid get() = HEXAColor(0x9932CCFFu) + +/** + * Dark Red - A dark red color (#8B0000). + */ val HEXAColor.Companion.darkred get() = HEXAColor(0x8B0000FFu) + +/** + * Dark Salmon - A muted salmon color (#E9967A). + */ val HEXAColor.Companion.darksalmon get() = HEXAColor(0xE9967AFFu) + +/** + * Dark Sea Green - A muted sea green color (#8FBC8F). + */ val HEXAColor.Companion.darkseagreen get() = HEXAColor(0x8FBC8FFFu) + +/** + * Dark Slate Blue - A dark grayish-blue color (#483D8B). + */ val HEXAColor.Companion.darkslateblue get() = HEXAColor(0x483D8BFFu) + +/** + * Dark Slate Gray - A very dark grayish-cyan color (#2F4F4F). + */ val HEXAColor.Companion.darkslategray get() = HEXAColor(0x2F4F4FFFu) + +/** + * Dark Slate Grey - A very dark grayish-cyan color (#2F4F4F). + */ val HEXAColor.Companion.darkslategrey get() = HEXAColor(0x2F4F4FFFu) + +/** + * Dark Turquoise - A dark turquoise color (#00CED1). + */ val HEXAColor.Companion.darkturquoise get() = HEXAColor(0x00CED1FFu) + +/** + * Dark Violet - A dark violet color (#9400D3). + */ val HEXAColor.Companion.darkviolet get() = HEXAColor(0x9400D3FFu) + +/** + * Deep Pink - A vivid pink color (#FF1493). + */ val HEXAColor.Companion.deeppink get() = HEXAColor(0xFF1493FFu) + +/** + * Deep Sky Blue - A bright sky blue color (#00BFFF). + */ val HEXAColor.Companion.deepskyblue get() = HEXAColor(0x00BFFFFFu) + +/** + * Dim Gray - A dim gray color (#696969). + */ val HEXAColor.Companion.dimgray get() = HEXAColor(0x696969FFu) + +/** + * Dim Grey - A dim gray color (#696969). + */ val HEXAColor.Companion.dimgrey get() = HEXAColor(0x696969FFu) + +/** + * Dodger Blue - A bright blue color (#1E90FF). + */ val HEXAColor.Companion.dodgerblue get() = HEXAColor(0x1E90FFFFu) + +/** + * Firebrick - A dark red brick color (#B22222). + */ val HEXAColor.Companion.firebrick get() = HEXAColor(0xB22222FFu) + +/** + * Floral White - A very pale cream color (#FFFAF0). + */ val HEXAColor.Companion.floralwhite get() = HEXAColor(0xFFFAF0FFu) + +/** + * Forest Green - A medium forest green color (#228B22). + */ val HEXAColor.Companion.forestgreen get() = HEXAColor(0x228B22FFu) + +/** + * Fuchsia - A vivid magenta color (#FF00FF). + */ val HEXAColor.Companion.fuchsia get() = HEXAColor(0xFF00FFFFu) + +/** + * Gainsboro - A light gray color (#DCDCDC). + */ val HEXAColor.Companion.gainsboro get() = HEXAColor(0xDCDCDCFFu) + +/** + * Ghost White - A very pale blue-white color (#F8F8FF). + */ val HEXAColor.Companion.ghostwhite get() = HEXAColor(0xF8F8FFFFu) + +/** + * Gold - A bright golden yellow color (#FFD700). + */ val HEXAColor.Companion.gold get() = HEXAColor(0xFFD700FFu) + +/** + * Goldenrod - A golden yellow color (#DAA520). + */ val HEXAColor.Companion.goldenrod get() = HEXAColor(0xDAA520FFu) + +/** + * Gray - A medium gray color (#808080). + */ val HEXAColor.Companion.gray get() = HEXAColor(0x808080FFu) + +/** + * Green - A pure green color (#008000). + */ val HEXAColor.Companion.green get() = HEXAColor(0x008000FFu) + +/** + * Green Yellow - A bright yellow-green color (#ADFF2F). + */ val HEXAColor.Companion.greenyellow get() = HEXAColor(0xADFF2FFFu) + +/** + * Grey - A medium gray color (#808080). + */ val HEXAColor.Companion.grey get() = HEXAColor(0x808080FFu) + +/** + * Honeydew - A very pale green color (#F0FFF0). + */ val HEXAColor.Companion.honeydew get() = HEXAColor(0xF0FFF0FFu) + +/** + * Hot Pink - A vibrant pink color (#FF69B4). + */ val HEXAColor.Companion.hotpink get() = HEXAColor(0xFF69B4FFu) + +/** + * Indian Red - A medium red color (#CD5C5C). + */ val HEXAColor.Companion.indianred get() = HEXAColor(0xCD5C5CFFu) + +/** + * Indigo - A deep blue-violet color (#4B0082). + */ val HEXAColor.Companion.indigo get() = HEXAColor(0x4B0082FFu) + +/** + * Ivory - A very pale cream color (#FFFFF0). + */ val HEXAColor.Companion.ivory get() = HEXAColor(0xFFFFF0FFu) + +/** + * Khaki - A light tan color (#F0E68C). + */ val HEXAColor.Companion.khaki get() = HEXAColor(0xF0E68CFFu) + +/** + * Lavender - A pale purple color (#E6E6FA). + */ val HEXAColor.Companion.lavender get() = HEXAColor(0xE6E6FAFFu) + +/** + * Lavender Blush - A very pale pink color (#FFF0F5). + */ val HEXAColor.Companion.lavenderblush get() = HEXAColor(0xFFF0F5FFu) + +/** + * Lawn Green - A bright chartreuse green color (#7CFC00). + */ val HEXAColor.Companion.lawngreen get() = HEXAColor(0x7CFC00FFu) + +/** + * Lemon Chiffon - A very pale yellow color (#FFFACD). + */ val HEXAColor.Companion.lemonchiffon get() = HEXAColor(0xFFFACDFFu) + +/** + * Light Blue - A light blue color (#ADD8E6). + */ val HEXAColor.Companion.lightblue get() = HEXAColor(0xADD8E6FFu) + +/** + * Light Coral - A light coral pink color (#F08080). + */ val HEXAColor.Companion.lightcoral get() = HEXAColor(0xF08080FFu) + +/** + * Light Cyan - A very pale cyan color (#E0FFFF). + */ val HEXAColor.Companion.lightcyan get() = HEXAColor(0xE0FFFFFFu) + +/** + * Light Goldenrod Yellow - A pale yellow color (#FAFAD2). + */ val HEXAColor.Companion.lightgoldenrodyellow get() = HEXAColor(0xFAFAD2FFu) + +/** + * Light Gray - A light gray color (#D3D3D3). + */ val HEXAColor.Companion.lightgray get() = HEXAColor(0xD3D3D3FFu) + +/** + * Light Green - A light green color (#90EE90). + */ val HEXAColor.Companion.lightgreen get() = HEXAColor(0x90EE90FFu) + +/** + * Light Grey - A light gray color (#D3D3D3). + */ val HEXAColor.Companion.lightgrey get() = HEXAColor(0xD3D3D3FFu) + +/** + * Light Pink - A light pink color (#FFB6C1). + */ val HEXAColor.Companion.lightpink get() = HEXAColor(0xFFB6C1FFu) + +/** + * Light Salmon - A light salmon color (#FFA07A). + */ val HEXAColor.Companion.lightsalmon get() = HEXAColor(0xFFA07AFFu) + +/** + * Light Sea Green - A medium sea green color (#20B2AA). + */ val HEXAColor.Companion.lightseagreen get() = HEXAColor(0x20B2AAFFu) + +/** + * Light Sky Blue - A light sky blue color (#87CEFA). + */ val HEXAColor.Companion.lightskyblue get() = HEXAColor(0x87CEFAFFu) + +/** + * Light Slate Gray - A light slate gray color (#778899). + */ val HEXAColor.Companion.lightslategray get() = HEXAColor(0x778899FFu) + +/** + * Light Slate Grey - A light slate gray color (#778899). + */ val HEXAColor.Companion.lightslategrey get() = HEXAColor(0x778899FFu) + +/** + * Light Steel Blue - A light steel blue color (#B0C4DE). + */ val HEXAColor.Companion.lightsteelblue get() = HEXAColor(0xB0C4DEFFu) + +/** + * Light Yellow - A very pale yellow color (#FFFFE0). + */ val HEXAColor.Companion.lightyellow get() = HEXAColor(0xFFFFE0FFu) + +/** + * Lime - A bright lime green color (#00FF00). + */ val HEXAColor.Companion.lime get() = HEXAColor(0x00FF00FFu) + +/** + * Lime Green - A lime green color (#32CD32). + */ val HEXAColor.Companion.limegreen get() = HEXAColor(0x32CD32FFu) + +/** + * Linen - A pale beige color (#FAF0E6). + */ val HEXAColor.Companion.linen get() = HEXAColor(0xFAF0E6FFu) + +/** + * Magenta - A bright magenta color (#FF00FF). + */ val HEXAColor.Companion.magenta get() = HEXAColor(0xFF00FFFFu) + +/** + * Maroon - A dark reddish-brown color (#800000). + */ val HEXAColor.Companion.maroon get() = HEXAColor(0x800000FFu) + +/** + * Medium Aquamarine - A medium aquamarine color (#66CDAA). + */ val HEXAColor.Companion.mediumaquamarine get() = HEXAColor(0x66CDAAFFu) + +/** + * Medium Blue - A medium blue color (#0000CD). + */ val HEXAColor.Companion.mediumblue get() = HEXAColor(0x0000CDFFu) + +/** + * Medium Orchid - A medium orchid purple color (#BA55D3). + */ val HEXAColor.Companion.mediumorchid get() = HEXAColor(0xBA55D3FFu) + +/** + * Medium Purple - A medium purple color (#9370DB). + */ val HEXAColor.Companion.mediumpurple get() = HEXAColor(0x9370DBFFu) + +/** + * Medium Sea Green - A medium sea green color (#3CB371). + */ val HEXAColor.Companion.mediumseagreen get() = HEXAColor(0x3CB371FFu) + +/** + * Medium Slate Blue - A medium slate blue color (#7B68EE). + */ val HEXAColor.Companion.mediumslateblue get() = HEXAColor(0x7B68EEFFu) + +/** + * Medium Spring Green - A medium spring green color (#00FA9A). + */ val HEXAColor.Companion.mediumspringgreen get() = HEXAColor(0x00FA9AFFu) + +/** + * Medium Turquoise - A medium turquoise color (#48D1CC). + */ val HEXAColor.Companion.mediumturquoise get() = HEXAColor(0x48D1CCFFu) + +/** + * Medium Violet Red - A medium violet-red color (#C71585). + */ val HEXAColor.Companion.mediumvioletred get() = HEXAColor(0xC71585FFu) + +/** + * Midnight Blue - A very dark blue color (#191970). + */ val HEXAColor.Companion.midnightblue get() = HEXAColor(0x191970FFu) + +/** + * Mint Cream - A very pale mint color (#F5FFFA). + */ val HEXAColor.Companion.mintcream get() = HEXAColor(0xF5FFFAFFu) + +/** + * Misty Rose - A very pale pink color (#FFE4E1). + */ val HEXAColor.Companion.mistyrose get() = HEXAColor(0xFFE4E1FFu) + +/** + * Moccasin - A pale peach color (#FFE4B5). + */ val HEXAColor.Companion.moccasin get() = HEXAColor(0xFFE4B5FFu) + +/** + * Navajo White - A pale peach color (#FFDEAD). + */ val HEXAColor.Companion.navajowhite get() = HEXAColor(0xFFDEADFFu) + +/** + * Navy - A very dark blue color (#000080). + */ val HEXAColor.Companion.navy get() = HEXAColor(0x000080FFu) + +/** + * Old Lace - A very pale cream color (#FDF5E6). + */ val HEXAColor.Companion.oldlace get() = HEXAColor(0xFDF5E6FFu) + +/** + * Olive - A dark yellowish-green color (#808000). + */ val HEXAColor.Companion.olive get() = HEXAColor(0x808000FFu) + +/** + * Olive Drab - A dark olive green color (#6B8E23). + */ val HEXAColor.Companion.olivedrab get() = HEXAColor(0x6B8E23FFu) + +/** + * Orange - A bright orange color (#FFA500). + */ val HEXAColor.Companion.orange get() = HEXAColor(0xFFA500FFu) + +/** + * Orange Red - A bright red-orange color (#FF4500). + */ val HEXAColor.Companion.orangered get() = HEXAColor(0xFF4500FFu) + +/** + * Orchid - A medium orchid purple color (#DA70D6). + */ val HEXAColor.Companion.orchid get() = HEXAColor(0xDA70D6FFu) + +/** + * Pale Goldenrod - A pale goldenrod yellow color (#EEE8AA). + */ val HEXAColor.Companion.palegoldenrod get() = HEXAColor(0xEEE8AAFFu) + +/** + * Pale Green - A pale green color (#98FB98). + */ val HEXAColor.Companion.palegreen get() = HEXAColor(0x98FB98FFu) + +/** + * Pale Turquoise - A pale turquoise color (#AFEEEE). + */ val HEXAColor.Companion.paleturquoise get() = HEXAColor(0xAFEEEEFFu) + +/** + * Pale Violet Red - A medium violet-red color (#DB7093). + */ val HEXAColor.Companion.palevioletred get() = HEXAColor(0xDB7093FFu) + +/** + * Papaya Whip - A pale peach color (#FFEFD5). + */ val HEXAColor.Companion.papayawhip get() = HEXAColor(0xFFEFD5FFu) + +/** + * Peach Puff - A light peach color (#FFDAB9). + */ val HEXAColor.Companion.peachpuff get() = HEXAColor(0xFFDAB9FFu) + +/** + * Peru - A medium brown color (#CD853F). + */ val HEXAColor.Companion.peru get() = HEXAColor(0xCD853FFFu) + +/** + * Pink - A light pink color (#FFC0CB). + */ val HEXAColor.Companion.pink get() = HEXAColor(0xFFC0CBFFu) + +/** + * Plum - A medium purple color (#DDA0DD). + */ val HEXAColor.Companion.plum get() = HEXAColor(0xDDA0DDFFu) + +/** + * Powder Blue - A light blue color (#B0E0E6). + */ val HEXAColor.Companion.powderblue get() = HEXAColor(0xB0E0E6FFu) + +/** + * Purple - A pure purple color (#800080). + */ val HEXAColor.Companion.purple get() = HEXAColor(0x800080FFu) + +/** + * Red - Pure red color (#FF0000). + */ val HEXAColor.Companion.red get() = HEXAColor(0xFF0000FFu) + +/** + * Rosy Brown - A rosy brown color (#BC8F8F). + */ val HEXAColor.Companion.rosybrown get() = HEXAColor(0xBC8F8FFFu) + +/** + * Royal Blue - A vibrant royal blue color (#4169E1). + */ val HEXAColor.Companion.royalblue get() = HEXAColor(0x4169E1FFu) + +/** + * Saddle Brown - A dark brown color (#8B4513). + */ val HEXAColor.Companion.saddlebrown get() = HEXAColor(0x8B4513FFu) + +/** + * Salmon - A light salmon pink color (#FA8072). + */ val HEXAColor.Companion.salmon get() = HEXAColor(0xFA8072FFu) + +/** + * Sandy Brown - A sandy brown color (#F4A460). + */ val HEXAColor.Companion.sandybrown get() = HEXAColor(0xF4A460FFu) + +/** + * Sea Green - A dark sea green color (#2E8B57). + */ val HEXAColor.Companion.seagreen get() = HEXAColor(0x2E8B57FFu) + +/** + * Seashell - A very pale pink-orange color (#FFF5EE). + */ val HEXAColor.Companion.seashell get() = HEXAColor(0xFFF5EEFFu) + +/** + * Sienna - A reddish-brown color (#A0522D). + */ val HEXAColor.Companion.sienna get() = HEXAColor(0xA0522DFFu) + +/** + * Silver - A light gray-silver color (#C0C0C0). + */ val HEXAColor.Companion.silver get() = HEXAColor(0xC0C0C0FFu) + +/** + * Sky Blue - A light sky blue color (#87CEEB). + */ val HEXAColor.Companion.skyblue get() = HEXAColor(0x87CEEBFFu) + +/** + * Slate Blue - A medium slate blue color (#6A5ACD). + */ val HEXAColor.Companion.slateblue get() = HEXAColor(0x6A5ACDFFu) + +/** + * Slate Gray - A slate gray color (#708090). + */ val HEXAColor.Companion.slategray get() = HEXAColor(0x708090FFu) + +/** + * Slate Grey - A slate gray color (#708090). + */ val HEXAColor.Companion.slategrey get() = HEXAColor(0x708090FFu) + +/** + * Snow - A very pale pinkish-white color (#FFFAFA). + */ val HEXAColor.Companion.snow get() = HEXAColor(0xFFFAFAFFu) + +/** + * Spring Green - A bright spring green color (#00FF7F). + */ val HEXAColor.Companion.springgreen get() = HEXAColor(0x00FF7FFFu) + +/** + * Steel Blue - A medium steel blue color (#4682B4). + */ val HEXAColor.Companion.steelblue get() = HEXAColor(0x4682B4FFu) + +/** + * Tan - A light brown tan color (#D2B48C). + */ val HEXAColor.Companion.tan get() = HEXAColor(0xD2B48CFFu) + +/** + * Teal - A dark cyan-blue color (#008080). + */ val HEXAColor.Companion.teal get() = HEXAColor(0x008080FFu) + +/** + * Thistle - A light purple-pink color (#D8BFD8). + */ val HEXAColor.Companion.thistle get() = HEXAColor(0xD8BFD8FFu) + +/** + * Tomato - A vibrant red-orange color (#FF6347). + */ val HEXAColor.Companion.tomato get() = HEXAColor(0xFF6347FFu) + +/** + * Turquoise - A medium turquoise color (#40E0D0). + */ val HEXAColor.Companion.turquoise get() = HEXAColor(0x40E0D0FFu) + +/** + * Violet - A violet color (#EE82EE). + */ val HEXAColor.Companion.violet get() = HEXAColor(0xEE82EEFFu) + +/** + * Wheat - A light tan color (#F5DEB3). + */ val HEXAColor.Companion.wheat get() = HEXAColor(0xF5DEB3FFu) + +/** + * White - Pure white color (#FFFFFF). + */ val HEXAColor.Companion.white get() = HEXAColor(0xFFFFFFFFu) + +/** + * White Smoke - A very light gray color (#F5F5F5). + */ val HEXAColor.Companion.whitesmoke get() = HEXAColor(0xF5F5F5FFu) + +/** + * Yellow - Pure yellow color (#FFFF00). + */ val HEXAColor.Companion.yellow get() = HEXAColor(0xFFFF00FFu) + +/** + * Yellow Green - A medium yellow-green color (#9ACD32). + */ val HEXAColor.Companion.yellowgreen get() = HEXAColor(0x9ACD32FFu) diff --git a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/BreakAsPairs.kt b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/BreakAsPairs.kt index d1e262e6188..6de3f8037b7 100644 --- a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/BreakAsPairs.kt +++ b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/BreakAsPairs.kt @@ -1,5 +1,15 @@ package dev.inmo.micro_utils.common +/** + * Breaks this list into a list of consecutive pairs. + * Each element is paired with the next element in the list. + * For a list of size n, the result will contain n-1 pairs. + * + * Example: `[1, 2, 3, 4].breakAsPairs()` returns `[(1, 2), (2, 3), (3, 4)]` + * + * @param T The type of elements in the list + * @return A list of pairs where each pair consists of consecutive elements + */ fun List.breakAsPairs(): List> { val result = mutableListOf>() diff --git a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/IfBoolean.kt b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/IfBoolean.kt index e6401389c4e..d807a46b4e8 100644 --- a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/IfBoolean.kt +++ b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/IfBoolean.kt @@ -1,5 +1,12 @@ package dev.inmo.micro_utils.common +/** + * Executes the given [block] and returns its result if this Boolean is true, otherwise returns null. + * + * @param T The return type of the block + * @param block The function to execute if this Boolean is true + * @return The result of [block] if true, null otherwise + */ inline fun Boolean.letIfTrue(block: () -> T): T? { return if (this) { block() @@ -8,6 +15,13 @@ inline fun Boolean.letIfTrue(block: () -> T): T? { } } +/** + * Executes the given [block] and returns its result if this Boolean is false, otherwise returns null. + * + * @param T The return type of the block + * @param block The function to execute if this Boolean is false + * @return The result of [block] if false, null otherwise + */ inline fun Boolean.letIfFalse(block: () -> T): T? { return if (this) { null @@ -16,16 +30,37 @@ inline fun Boolean.letIfFalse(block: () -> T): T? { } } +/** + * Executes the given [block] if this Boolean is true and returns this Boolean. + * Similar to [also], but only executes the block when the Boolean is true. + * + * @param block The function to execute if this Boolean is true + * @return This Boolean value + */ inline fun Boolean.alsoIfTrue(block: () -> Unit): Boolean { letIfTrue(block) return this } +/** + * Executes the given [block] if this Boolean is false and returns this Boolean. + * Similar to [also], but only executes the block when the Boolean is false. + * + * @param block The function to execute if this Boolean is false + * @return This Boolean value + */ inline fun Boolean.alsoIfFalse(block: () -> Unit): Boolean { letIfFalse(block) return this } +/** + * Alias for [letIfTrue]. Executes the given [block] and returns its result if this Boolean is true. + * + * @param T The return type of the block + * @param block The function to execute if this Boolean is true + * @return The result of [block] if true, null otherwise + */ inline fun Boolean.ifTrue(block: () -> T): T? { return if (this) { block() @@ -34,6 +69,13 @@ inline fun Boolean.ifTrue(block: () -> T): T? { } } +/** + * Alias for [letIfFalse]. Executes the given [block] and returns its result if this Boolean is false. + * + * @param T The return type of the block + * @param block The function to execute if this Boolean is false + * @return The result of [block] if false, null otherwise + */ inline fun Boolean.ifFalse(block: () -> T): T? { return if (this) { null diff --git a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/InputAllocator.kt b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/InputAllocator.kt index b33b406805d..e40106096ba 100644 --- a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/InputAllocator.kt +++ b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/InputAllocator.kt @@ -6,19 +6,45 @@ import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder +/** + * A function type that allocates and returns a [ByteArray]. + */ typealias ByteArrayAllocator = () -> ByteArray + +/** + * A suspending function type that allocates and returns a [ByteArray]. + */ typealias SuspendByteArrayAllocator = suspend () -> ByteArray +/** + * Converts this [ByteArray] to a [ByteArrayAllocator] that returns this array. + */ val ByteArray.asAllocator: ByteArrayAllocator get() = { this } + +/** + * Converts this [ByteArray] to a [SuspendByteArrayAllocator] that returns this array. + */ val ByteArray.asSuspendAllocator: SuspendByteArrayAllocator get() = { this } + +/** + * Converts this [ByteArrayAllocator] to a [SuspendByteArrayAllocator]. + */ val ByteArrayAllocator.asSuspendAllocator: SuspendByteArrayAllocator get() = { this() } + +/** + * Converts this [SuspendByteArrayAllocator] to a [ByteArrayAllocator] by invoking it and + * wrapping the result in a non-suspending allocator. + */ suspend fun SuspendByteArrayAllocator.asAllocator(): ByteArrayAllocator { return invoke().asAllocator } +/** + * Serializer for [ByteArrayAllocator]. Serializes the result of invoking the allocator. + */ object ByteArrayAllocatorSerializer : KSerializer { private val realSerializer = ByteArraySerializer() override val descriptor: SerialDescriptor = realSerializer.descriptor diff --git a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/IterableFirstNotNull.kt b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/IterableFirstNotNull.kt index b26717d77ac..054a9361f84 100644 --- a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/IterableFirstNotNull.kt +++ b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/IterableFirstNotNull.kt @@ -1,3 +1,10 @@ package dev.inmo.micro_utils.common +/** + * Returns the first non-null element in this iterable. + * + * @param T The type of elements in the iterable (nullable) + * @return The first non-null element + * @throws NoSuchElementException if the iterable contains no non-null elements + */ fun Iterable.firstNotNull() = first { it != null }!! diff --git a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/JoinTo.kt b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/JoinTo.kt index 4506e4c4829..c0b2a82a106 100644 --- a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/JoinTo.kt +++ b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/JoinTo.kt @@ -1,5 +1,19 @@ package dev.inmo.micro_utils.common +/** + * Joins elements of this iterable into a list with separators between elements. + * Each element is transformed using [transform], and separators are generated using [separatorFun]. + * Optional [prefix] and [postfix] can be added to the result. + * Null values from transformations or separator function are skipped. + * + * @param I The type of elements in the input iterable + * @param R The type of elements in the result list + * @param separatorFun A function that generates a separator based on the current element + * @param prefix Optional prefix to add at the beginning of the result + * @param postfix Optional postfix to add at the end of the result + * @param transform A function to transform each element + * @return A list of transformed elements with separators + */ inline fun Iterable.joinTo( separatorFun: (I) -> R?, prefix: R? = null, @@ -25,6 +39,20 @@ inline fun Iterable.joinTo( return result } +/** + * Joins elements of this iterable into a list with a constant separator between elements. + * Each element is transformed using [transform]. + * Optional [prefix] and [postfix] can be added to the result. + * Null values from transformations or separators are skipped. + * + * @param I The type of elements in the input iterable + * @param R The type of elements in the result list + * @param separator The separator to insert between elements + * @param prefix Optional prefix to add at the beginning of the result + * @param postfix Optional postfix to add at the end of the result + * @param transform A function to transform each element + * @return A list of transformed elements with separators + */ inline fun Iterable.joinTo( separator: R? = null, prefix: R? = null, @@ -32,18 +60,55 @@ inline fun Iterable.joinTo( transform: (I) -> R? ): List = joinTo({ separator }, prefix, postfix, transform) +/** + * Joins elements of this iterable into a list with separators between elements. + * Separators are generated using [separatorFun]. + * Optional [prefix] and [postfix] can be added to the result. + * Null values from separator function are skipped. + * + * @param I The type of elements + * @param separatorFun A function that generates a separator based on the current element + * @param prefix Optional prefix to add at the beginning of the result + * @param postfix Optional postfix to add at the end of the result + * @return A list of elements with separators + */ inline fun Iterable.joinTo( separatorFun: (I) -> I?, prefix: I? = null, postfix: I? = null ): List = joinTo(separatorFun, prefix, postfix) { it } +/** + * Joins elements of this iterable into a list with a constant separator between elements. + * Optional [prefix] and [postfix] can be added to the result. + * Null separators are skipped. + * + * @param I The type of elements + * @param separator The separator to insert between elements + * @param prefix Optional prefix to add at the beginning of the result + * @param postfix Optional postfix to add at the end of the result + * @return A list of elements with separators + */ inline fun Iterable.joinTo( separator: I? = null, prefix: I? = null, postfix: I? = null ): List = joinTo({ separator }, prefix, postfix) +/** + * Joins elements of this array into an array with separators between elements. + * Each element is transformed using [transform], and separators are generated using [separatorFun]. + * Optional [prefix] and [postfix] can be added to the result. + * Null values from transformations or separator function are skipped. + * + * @param I The type of elements in the input array + * @param R The type of elements in the result array + * @param separatorFun A function that generates a separator based on the current element + * @param prefix Optional prefix to add at the beginning of the result + * @param postfix Optional postfix to add at the end of the result + * @param transform A function to transform each element + * @return An array of transformed elements with separators + */ inline fun Array.joinTo( separatorFun: (I) -> R?, prefix: R? = null, @@ -51,6 +116,20 @@ inline fun Array.joinTo( transform: (I) -> R? ): Array = asIterable().joinTo(separatorFun, prefix, postfix, transform).toTypedArray() +/** + * Joins elements of this array into an array with a constant separator between elements. + * Each element is transformed using [transform]. + * Optional [prefix] and [postfix] can be added to the result. + * Null values from transformations or separators are skipped. + * + * @param I The type of elements in the input array + * @param R The type of elements in the result array + * @param separator The separator to insert between elements + * @param prefix Optional prefix to add at the beginning of the result + * @param postfix Optional postfix to add at the end of the result + * @param transform A function to transform each element + * @return An array of transformed elements with separators + */ inline fun Array.joinTo( separator: R? = null, prefix: R? = null, diff --git a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/Mapper.kt b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/Mapper.kt index 3494ebfff1b..becebac4dd0 100644 --- a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/Mapper.kt +++ b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/Mapper.kt @@ -2,16 +2,43 @@ package dev.inmo.micro_utils.common import kotlin.jvm.JvmName +/** + * A bidirectional mapper that can convert between two types [T1] and [T2]. + * + * @param T1 The first type + * @param T2 The second type + */ interface SimpleMapper { fun convertToT1(from: T2): T1 fun convertToT2(from: T1): T2 } +/** + * Converts [from] of type [T2] to type [T1] using this mapper. + * + * @param from The value to convert + * @return The converted value of type [T1] + */ @JvmName("convertFromT2") fun SimpleMapper.convert(from: T2) = convertToT1(from) + +/** + * Converts [from] of type [T1] to type [T2] using this mapper. + * + * @param from The value to convert + * @return The converted value of type [T2] + */ @JvmName("convertFromT1") fun SimpleMapper.convert(from: T1) = convertToT2(from) +/** + * Implementation of [SimpleMapper] that uses lambda functions for conversion. + * + * @param T1 The first type + * @param T2 The second type + * @param t1 Function to convert from [T2] to [T1] + * @param t2 Function to convert from [T1] to [T2] + */ class SimpleMapperImpl( private val t1: (T2) -> T1, private val t2: (T1) -> T2, @@ -21,22 +48,58 @@ class SimpleMapperImpl( override fun convertToT2(from: T1): T2 = t2.invoke(from) } +/** + * Creates a [SimpleMapper] using the provided conversion functions. + * + * @param T1 The first type + * @param T2 The second type + * @param t1 Function to convert from [T2] to [T1] + * @param t2 Function to convert from [T1] to [T2] + * @return A new [SimpleMapperImpl] instance + */ @Suppress("NOTHING_TO_INLINE") inline fun simpleMapper( noinline t1: (T2) -> T1, noinline t2: (T1) -> T2, ) = SimpleMapperImpl(t1, t2) +/** + * A bidirectional mapper that can convert between two types [T1] and [T2] using suspending functions. + * + * @param T1 The first type + * @param T2 The second type + */ interface SimpleSuspendableMapper { suspend fun convertToT1(from: T2): T1 suspend fun convertToT2(from: T1): T2 } +/** + * Converts [from] of type [T2] to type [T1] using this suspending mapper. + * + * @param from The value to convert + * @return The converted value of type [T1] + */ @JvmName("convertFromT2") suspend fun SimpleSuspendableMapper.convert(from: T2) = convertToT1(from) + +/** + * Converts [from] of type [T1] to type [T2] using this suspending mapper. + * + * @param from The value to convert + * @return The converted value of type [T2] + */ @JvmName("convertFromT1") suspend fun SimpleSuspendableMapper.convert(from: T1) = convertToT2(from) +/** + * Implementation of [SimpleSuspendableMapper] that uses suspending lambda functions for conversion. + * + * @param T1 The first type + * @param T2 The second type + * @param t1 Suspending function to convert from [T2] to [T1] + * @param t2 Suspending function to convert from [T1] to [T2] + */ class SimpleSuspendableMapperImpl( private val t1: suspend (T2) -> T1, private val t2: suspend (T1) -> T2, @@ -46,6 +109,15 @@ class SimpleSuspendableMapperImpl( override suspend fun convertToT2(from: T1): T2 = t2.invoke(from) } +/** + * Creates a [SimpleSuspendableMapper] using the provided suspending conversion functions. + * + * @param T1 The first type + * @param T2 The second type + * @param t1 Suspending function to convert from [T2] to [T1] + * @param t2 Suspending function to convert from [T1] to [T2] + * @return A new [SimpleSuspendableMapperImpl] instance + */ @Suppress("NOTHING_TO_INLINE") inline fun simpleSuspendableMapper( noinline t1: suspend (T2) -> T1, diff --git a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/PadExtensions.kt b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/PadExtensions.kt index 2bc5621d2ae..123cc1c8947 100644 --- a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/PadExtensions.kt +++ b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/PadExtensions.kt @@ -1,5 +1,14 @@ package dev.inmo.micro_utils.common +/** + * Pads this sequence to the specified [size] using a custom [inserter] function. + * The [inserter] is repeatedly called until the sequence reaches the desired size. + * + * @param T The type of elements in the sequence + * @param size The target size of the padded sequence + * @param inserter A function that takes the current sequence and returns a new padded sequence + * @return A sequence padded to at least the specified size + */ inline fun Sequence.padWith(size: Int, inserter: (Sequence) -> Sequence): Sequence { var result = this while (result.count() < size) { @@ -8,10 +17,36 @@ inline fun Sequence.padWith(size: Int, inserter: (Sequence) -> Sequenc return result } +/** + * Pads this sequence at the end to the specified [size]. + * New elements are generated using [padBlock], which receives the current size as a parameter. + * + * @param T The type of elements in the sequence + * @param size The target size of the padded sequence + * @param padBlock A function that generates padding elements based on the current sequence size + * @return A sequence padded to at least the specified size + */ inline fun Sequence.padEnd(size: Int, padBlock: (Int) -> T): Sequence = padWith(size) { it + padBlock(it.count()) } +/** + * Pads this sequence at the end to the specified [size] using the given element [o]. + * + * @param T The type of elements in the sequence + * @param size The target size of the padded sequence + * @param o The element to use for padding + * @return A sequence padded to at least the specified size + */ inline fun Sequence.padEnd(size: Int, o: T) = padEnd(size) { o } +/** + * Pads this list to the specified [size] using a custom [inserter] function. + * The [inserter] is repeatedly called until the list reaches the desired size. + * + * @param T The type of elements in the list + * @param size The target size of the padded list + * @param inserter A function that takes the current list and returns a new padded list + * @return A list padded to at least the specified size + */ inline fun List.padWith(size: Int, inserter: (List) -> List): List { var result = this while (result.size < size) { @@ -19,14 +54,66 @@ inline fun List.padWith(size: Int, inserter: (List) -> List): List< } return result } + +/** + * Pads this list at the end to the specified [size]. + * New elements are generated using [padBlock], which receives the current size as a parameter. + * + * @param T The type of elements in the list + * @param size The target size of the padded list + * @param padBlock A function that generates padding elements based on the current list size + * @return A list padded to at least the specified size + */ inline fun List.padEnd(size: Int, padBlock: (Int) -> T): List = asSequence().padEnd(size, padBlock).toList() +/** + * Pads this list at the end to the specified [size] using the given element [o]. + * + * @param T The type of elements in the list + * @param size The target size of the padded list + * @param o The element to use for padding + * @return A list padded to at least the specified size + */ inline fun List.padEnd(size: Int, o: T): List = asSequence().padEnd(size, o).toList() +/** + * Pads this sequence at the start to the specified [size]. + * New elements are generated using [padBlock], which receives the current size as a parameter. + * + * @param T The type of elements in the sequence + * @param size The target size of the padded sequence + * @param padBlock A function that generates padding elements based on the current sequence size + * @return A sequence padded to at least the specified size + */ inline fun Sequence.padStart(size: Int, padBlock: (Int) -> T): Sequence = padWith(size) { sequenceOf(padBlock(it.count())) + it } +/** + * Pads this sequence at the start to the specified [size] using the given element [o]. + * + * @param T The type of elements in the sequence + * @param size The target size of the padded sequence + * @param o The element to use for padding + * @return A sequence padded to at least the specified size + */ inline fun Sequence.padStart(size: Int, o: T) = padStart(size) { o } +/** + * Pads this list at the start to the specified [size]. + * New elements are generated using [padBlock], which receives the current size as a parameter. + * + * @param T The type of elements in the list + * @param size The target size of the padded list + * @param padBlock A function that generates padding elements based on the current list size + * @return A list padded to at least the specified size + */ inline fun List.padStart(size: Int, padBlock: (Int) -> T): List = asSequence().padStart(size, padBlock).toList() +/** + * Pads this list at the start to the specified [size] using the given element [o]. + * + * @param T The type of elements in the list + * @param size The target size of the padded list + * @param o The element to use for padding + * @return A list padded to at least the specified size + */ inline fun List.padStart(size: Int, o: T): List = asSequence().padStart(size, o).toList() diff --git a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/RangeIntersection.kt b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/RangeIntersection.kt index e8bcf5a5750..3a59a30ff6d 100644 --- a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/RangeIntersection.kt +++ b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/RangeIntersection.kt @@ -1,17 +1,39 @@ package dev.inmo.micro_utils.common +/** + * Computes the intersection of this range with [other]. Returns a pair representing + * the intersecting range, or null if the ranges don't overlap. + * + * @param T The type of comparable values in the range + * @param other The other range to intersect with + * @return A pair (start, end) representing the intersection, or null if no intersection exists + */ fun > ClosedRange.intersect(other: ClosedRange): Pair? = when { start == other.start && endInclusive == other.endInclusive -> start to endInclusive start > other.endInclusive || other.start > endInclusive -> null else -> maxOf(start, other.start) to minOf(endInclusive, other.endInclusive) } +/** + * Computes the intersection of this [IntRange] with [other]. + * Returns the intersecting range, or null if the ranges don't overlap. + * + * @param other The other range to intersect with + * @return An [IntRange] representing the intersection, or null if no intersection exists + */ fun IntRange.intersect( other: IntRange ): IntRange? = (this as ClosedRange).intersect(other as ClosedRange) ?.let { it.first .. it.second } +/** + * Computes the intersection of this [LongRange] with [other]. + * Returns the intersecting range, or null if the ranges don't overlap. + * + * @param other The other range to intersect with + * @return A [LongRange] representing the intersection, or null if no intersection exists + */ fun LongRange.intersect( other: LongRange ): LongRange? = (this as ClosedRange).intersect(other as ClosedRange) ?.let { diff --git a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/WithReplaced.kt b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/WithReplaced.kt index 2fa36eaf05d..db1bf87c30f 100644 --- a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/WithReplaced.kt +++ b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/WithReplaced.kt @@ -1,5 +1,24 @@ package dev.inmo.micro_utils.common +/** + * Returns a new list with the element at index [i] replaced by applying [block] to it. + * All other elements remain unchanged. + * + * @param T The type of elements in the iterable + * @param i The index of the element to replace + * @param block A function that transforms the element at the given index + * @return A new list with the replaced element + */ fun Iterable.withReplacedAt(i: Int, block: (T) -> T): List = take(i) + block(elementAt(i)) + drop(i + 1) + +/** + * Returns a new list with the first occurrence of element [t] replaced by applying [block] to it. + * All other elements remain unchanged. + * + * @param T The type of elements in the iterable + * @param t The element to replace + * @param block A function that transforms the found element + * @return A new list with the replaced element + */ fun Iterable.withReplaced(t: T, block: (T) -> T): List = withReplacedAt(indexOf(t), block) diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/ActorAsync.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/ActorAsync.kt index fe9215ada50..c74246fc407 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/ActorAsync.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/ActorAsync.kt @@ -5,6 +5,18 @@ import kotlinx.coroutines.* import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.consumeAsFlow +/** + * Creates an actor-style channel that processes messages asynchronously based on markers. + * Messages with the same marker will be processed sequentially, while messages with different markers can be processed concurrently. + * + * @param T The type of messages to process + * @param channelCapacity The capacity of the underlying channel. Defaults to [Channel.UNLIMITED] + * @param markerFactory A factory function that produces a marker for each message. Messages with the same marker + * will be processed sequentially. Defaults to returning null, meaning all messages will be processed sequentially + * @param logger The logger instance used for logging exceptions. Defaults to [KSLog] + * @param block The suspending function that processes each message + * @return A [Channel] that accepts messages to be processed + */ fun CoroutineScope.actorAsync( channelCapacity: Int = Channel.UNLIMITED, markerFactory: suspend (T) -> Any? = { null }, diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/DataAsDeferred.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/DataAsDeferred.kt index e196df51555..4e42dbf9c51 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/DataAsDeferred.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/DataAsDeferred.kt @@ -3,5 +3,12 @@ package dev.inmo.micro_utils.coroutines import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.Deferred +/** + * Wraps this value in a completed [Deferred]. The resulting [Deferred] is immediately completed with this value. + * Useful for converting synchronous values to [Deferred] in contexts that expect deferred values. + * + * @param T The type of the value + * @return A [Deferred] that is already completed with this value + */ val T.asDeferred: Deferred get() = CompletableDeferred(this) diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/DoInContext.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/DoInContext.kt index d1587c784a1..f077ba1d3ca 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/DoInContext.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/DoInContext.kt @@ -3,20 +3,53 @@ package dev.inmo.micro_utils.coroutines import kotlinx.coroutines.* import kotlin.coroutines.CoroutineContext +/** + * Convenience property to access [Dispatchers.Main] for UI operations. + */ inline val UI get() = Dispatchers.Main + +/** + * Convenience property to access [Dispatchers.Default] for CPU-intensive operations. + */ inline val Default get() = Dispatchers.Default +/** + * Executes the given [block] in the specified coroutine [context] and returns its result. + * This is a convenience wrapper around [withContext]. + * + * @param T The return type of the block + * @param context The [CoroutineContext] in which to execute the block + * @param block The suspending function to execute + * @return The result of executing the block + */ suspend inline fun doIn(context: CoroutineContext, noinline block: suspend CoroutineScope.() -> T) = withContext( context, block ) +/** + * Executes the given [block] on the UI/Main dispatcher and returns its result. + * This is a convenience function for executing UI operations. + * + * @param T The return type of the block + * @param block The suspending function to execute on the UI thread + * @return The result of executing the block + */ suspend inline fun doInUI(noinline block: suspend CoroutineScope.() -> T) = doIn( UI, block ) + +/** + * Executes the given [block] on the Default dispatcher and returns its result. + * This is a convenience function for executing CPU-intensive operations. + * + * @param T The return type of the block + * @param block The suspending function to execute on the Default dispatcher + * @return The result of executing the block + */ suspend inline fun doInDefault(noinline block: suspend CoroutineScope.() -> T) = doIn( Default, block diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/DoWithFirst.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/DoWithFirst.kt index 2288349de6b..be27cb9ba55 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/DoWithFirst.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/DoWithFirst.kt @@ -2,6 +2,14 @@ package dev.inmo.micro_utils.coroutines import kotlinx.coroutines.* +/** + * Represents a deferred action that combines a [Deferred] value with a callback to be executed on that value. + * + * @param T The type of the deferred value + * @param O The type of the result after applying the callback + * @param deferred The deferred value to await + * @param callback The suspending function to apply to the deferred value + */ class DeferredAction( val deferred: Deferred, val callback: suspend (T) -> O @@ -9,6 +17,13 @@ class DeferredAction( suspend operator fun invoke() = callback(deferred.await()) } +/** + * A builder for creating multiple deferred computations that can be executed, with only the first completing + * one being used. This is useful for race conditions where you want the result of whichever computation finishes first. + * + * @param T The type of values produced by the deferred computations + * @param scope The [CoroutineScope] in which to create the deferred computations + */ class DoWithFirstBuilder( private val scope: CoroutineScope ) { @@ -22,8 +37,25 @@ class DoWithFirstBuilder( fun build() = deferreds.toList() } +/** + * Creates a [DeferredAction] from this [Deferred] and a [callback] function. + * + * @param T The type of the deferred value + * @param O The type of the result after applying the callback + * @param callback The suspending function to apply to the deferred value + * @return A [DeferredAction] combining the deferred and callback + */ fun Deferred.buildAction(callback: suspend (T) -> O) = DeferredAction(this, callback) +/** + * Invokes the first [DeferredAction] whose deferred value completes, executing its callback and returning the result. + * Other deferred actions are cancelled if [cancelOnResult] is true. + * + * @param O The type of the result after applying callbacks + * @param scope The [CoroutineScope] in which to await the deferred values + * @param cancelOnResult If true, cancels all other deferred actions after the first completes. Defaults to true + * @return The result of invoking the first completed deferred action + */ suspend fun Iterable>.invokeFirstOf( scope: CoroutineScope, cancelOnResult: Boolean = true @@ -33,18 +65,50 @@ suspend fun Iterable>.invokeFirstOf( } } +/** + * Invokes the first [DeferredAction] from the given [variants] whose deferred value completes, + * executing its callback and returning the result. Other deferred actions are cancelled if [cancelOnResult] is true. + * + * @param O The type of the result after applying callbacks + * @param scope The [CoroutineScope] in which to await the deferred values + * @param variants The deferred actions to race + * @param cancelOnResult If true, cancels all other deferred actions after the first completes. Defaults to true + * @return The result of invoking the first completed deferred action + */ suspend fun invokeFirstOf( scope: CoroutineScope, vararg variants: DeferredAction<*, O>, cancelOnResult: Boolean = true ): O = variants.toList().invokeFirstOf(scope, cancelOnResult) +/** + * Awaits the first [Deferred] to complete and invokes the [callback] on its value. + * Other deferred values are cancelled if [cancelOnResult] is true. + * + * @param T The type of the deferred values + * @param O The type of the result after applying the callback + * @param scope The [CoroutineScope] in which to await the deferred values + * @param cancelOnResult If true, cancels all other deferred values after the first completes. Defaults to true + * @param callback The suspending function to apply to the first completed value + * @return The result of applying the callback to the first completed value + */ suspend fun Iterable>.invokeOnFirst( scope: CoroutineScope, cancelOnResult: Boolean = true, callback: suspend (T) -> O ): O = map { it.buildAction(callback) }.invokeFirstOf(scope, cancelOnResult) +/** + * Builds multiple deferred computations using [DoWithFirstBuilder] and invokes [callback] on the first one to complete. + * Other deferred computations are cancelled if [cancelOnResult] is true. + * + * @param T The type of the deferred values + * @param O The type of the result after applying the callback + * @param cancelOnResult If true, cancels all other computations after the first completes. Defaults to true + * @param block Builder DSL to define the deferred computations + * @param callback The suspending function to apply to the first completed value + * @return The result of applying the callback to the first completed value + */ suspend fun CoroutineScope.invokeOnFirstOf( cancelOnResult: Boolean = true, block: DoWithFirstBuilder.() -> Unit, @@ -54,6 +118,18 @@ suspend fun CoroutineScope.invokeOnFirstOf( cancelOnResult ).let { callback(it) } +/** + * Awaits the first [Deferred] from the given [variants] to complete and invokes the [callback] on its value. + * Other deferred values are cancelled if [cancelOnResult] is true. + * + * @param T The type of the deferred values + * @param O The type of the result after applying the callback + * @param scope The [CoroutineScope] in which to await the deferred values + * @param variants The deferred values to race + * @param cancelOnResult If true, cancels all other deferred values after the first completes. Defaults to true + * @param callback The suspending function to apply to the first completed value + * @return The result of applying the callback to the first completed value + */ suspend fun invokeOnFirst( scope: CoroutineScope, vararg variants: Deferred, @@ -61,11 +137,29 @@ suspend fun invokeOnFirst( callback: suspend (T) -> O ): O = variants.toList().invokeOnFirst(scope, cancelOnResult, callback) +/** + * Returns the value of the first [Deferred] from the given [variants] to complete. + * Other deferred values are cancelled if [cancelOnResult] is true. + * + * @param T The type of the deferred values + * @param variants The deferred values to race + * @param cancelOnResult If true, cancels all other deferred values after the first completes. Defaults to true + * @return The value of the first completed deferred + */ suspend fun CoroutineScope.firstOf( variants: Iterable>, cancelOnResult: Boolean = true ) = variants.invokeOnFirst(this, cancelOnResult) { it } +/** + * Builds multiple deferred computations using [DoWithFirstBuilder] and returns the value of the first one to complete. + * Other deferred computations are cancelled if [cancelOnResult] is true. + * + * @param T The type of the deferred values + * @param cancelOnResult If true, cancels all other computations after the first completes. Defaults to true + * @param block Builder DSL to define the deferred computations + * @return The value of the first completed computation + */ suspend fun CoroutineScope.firstOf( cancelOnResult: Boolean = true, block: DoWithFirstBuilder.() -> Unit @@ -74,11 +168,29 @@ suspend fun CoroutineScope.firstOf( cancelOnResult ) +/** + * Returns the value of the first [Deferred] from the given [variants] to complete. + * Other deferred values are cancelled if [cancelOnResult] is true. + * + * @param T The type of the deferred values + * @param variants The deferred values to race + * @param cancelOnResult If true, cancels all other deferred values after the first completes. Defaults to true + * @return The value of the first completed deferred + */ suspend fun CoroutineScope.firstOf( vararg variants: Deferred, cancelOnResult: Boolean = true ) = firstOf(variants.toList(), cancelOnResult) +/** + * Returns the value of the first [Deferred] from this list to complete, using the given [scope]. + * Other deferred values are cancelled if [cancelOnResult] is true. + * + * @param T The type of the deferred values + * @param scope The [CoroutineScope] in which to await the deferred values + * @param cancelOnResult If true, cancels all other deferred values after the first completes. Defaults to true + * @return The value of the first completed deferred + */ suspend fun List>.first( scope: CoroutineScope, cancelOnResult: Boolean = true diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowCollectorInvoking.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowCollectorInvoking.kt index 8ae54ebfc75..0ebe3091998 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowCollectorInvoking.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowCollectorInvoking.kt @@ -2,4 +2,11 @@ package dev.inmo.micro_utils.coroutines import kotlinx.coroutines.flow.FlowCollector +/** + * Operator function that allows a [FlowCollector] to be invoked like a function to emit a value. + * This is a convenient syntax sugar for [FlowCollector.emit]. + * + * @param T The type of values the collector can emit + * @param value The value to emit + */ suspend inline operator fun FlowCollector.invoke(value: T) = emit(value) diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowDebouncedBy.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowDebouncedBy.kt index 08e1ffbc97a..a8549832c6a 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowDebouncedBy.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowDebouncedBy.kt @@ -14,6 +14,15 @@ private value class DebouncedByData( val millisToData: Pair ) +/** + * Debounces a [Flow] with per-marker timeout control. Values with the same marker will be debounced independently. + * For each marker, only the last value within the timeout period will be emitted. + * + * @param T The type of values emitted by the flow + * @param timeout A function that determines the debounce timeout in milliseconds for each value + * @param markerFactory A function that produces a marker for each value. Values with the same marker are debounced together + * @return A [Flow] that emits debounced values + */ fun Flow.debouncedBy(timeout: (T) -> Long, markerFactory: (T) -> Any?): Flow = channelFlow { val jobs = mutableMapOf() val mutex = Mutex() @@ -36,5 +45,24 @@ fun Flow.debouncedBy(timeout: (T) -> Long, markerFactory: (T) -> Any?): F } } +/** + * Debounces a [Flow] with a fixed timeout in milliseconds and per-marker control. + * Values with the same marker will be debounced independently. + * + * @param T The type of values emitted by the flow + * @param timeout The debounce timeout in milliseconds + * @param markerFactory A function that produces a marker for each value. Values with the same marker are debounced together + * @return A [Flow] that emits debounced values + */ fun Flow.debouncedBy(timeout: Long, markerFactory: (T) -> Any?): Flow = debouncedBy({ timeout }, markerFactory) + +/** + * Debounces a [Flow] with a fixed timeout as [Duration] and per-marker control. + * Values with the same marker will be debounced independently. + * + * @param T The type of values emitted by the flow + * @param timeout The debounce timeout as a [Duration] + * @param markerFactory A function that produces a marker for each value. Values with the same marker are debounced together + * @return A [Flow] that emits debounced values + */ fun Flow.debouncedBy(timeout: Duration, markerFactory: (T) -> Any?): Flow = debouncedBy({ timeout.inWholeMilliseconds }, markerFactory) diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowFirstNotNull.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowFirstNotNull.kt index 7626f7757ed..cee50f1bb06 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowFirstNotNull.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowFirstNotNull.kt @@ -3,4 +3,12 @@ package dev.inmo.micro_utils.coroutines import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first +/** + * Returns the first non-null element emitted by this [Flow]. + * Suspends until a non-null element is found. + * + * @param T The type of elements in the flow + * @return The first non-null element + * @throws NoSuchElementException if the flow completes without emitting a non-null element + */ suspend fun Flow.firstNotNull() = first { it != null }!! diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowFlatten.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowFlatten.kt index 6e5c6d4b729..f2708d2082d 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowFlatten.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowFlatten.kt @@ -4,6 +4,14 @@ import kotlinx.coroutines.flow.* import kotlin.js.JsName import kotlin.jvm.JvmName +/** + * Transforms each inner [Flow] element using the given [mapper] function and flattens the result into a single [Flow]. + * + * @param T The type of elements in the inner flows + * @param R The type of elements after applying the mapper + * @param mapper A suspending function to transform each element + * @return A [Flow] of mapped and flattened elements + */ inline fun Flow>.flatMap( crossinline mapper: suspend (T) -> R ) = flow { @@ -14,6 +22,14 @@ inline fun Flow>.flatMap( } } +/** + * Transforms each element from inner [Iterable]s using the given [mapper] function and flattens the result into a single [Flow]. + * + * @param T The type of elements in the iterables + * @param R The type of elements after applying the mapper + * @param mapper A suspending function to transform each element + * @return A [Flow] of mapped and flattened elements + */ @JsName("flatMapIterable") @JvmName("flatMapIterable") inline fun Flow>.flatMap( @@ -22,18 +38,48 @@ inline fun Flow>.flatMap( it.asFlow() }.flatMap(mapper) +/** + * Transforms each inner [Flow] element using the given [mapper] function, flattens the result, + * and filters out null values. + * + * @param T The type of elements in the inner flows + * @param R The type of elements after applying the mapper + * @param mapper A suspending function to transform each element + * @return A [Flow] of non-null mapped and flattened elements + */ inline fun Flow>.flatMapNotNull( crossinline mapper: suspend (T) -> R ) = flatMap(mapper).takeNotNull() +/** + * Transforms each element from inner [Iterable]s using the given [mapper] function, flattens the result, + * and filters out null values. + * + * @param T The type of elements in the iterables + * @param R The type of elements after applying the mapper + * @param mapper A suspending function to transform each element + * @return A [Flow] of non-null mapped and flattened elements + */ @JsName("flatMapNotNullIterable") @JvmName("flatMapNotNullIterable") inline fun Flow>.flatMapNotNull( crossinline mapper: suspend (T) -> R ) = flatMap(mapper).takeNotNull() +/** + * Flattens a [Flow] of [Flow]s into a single [Flow] by collecting all inner flows sequentially. + * + * @param T The type of elements in the inner flows + * @return A [Flow] containing all elements from all inner flows + */ fun Flow>.flatten() = flatMap { it } +/** + * Flattens a [Flow] of [Iterable]s into a single [Flow] by emitting all elements from each iterable. + * + * @param T The type of elements in the iterables + * @return A [Flow] containing all elements from all iterables + */ @JsName("flattenIterable") @JvmName("flattenIterable") fun Flow>.flatten() = flatMap { it } diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowNullables.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowNullables.kt index e6b665e47f3..799dc3a760f 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowNullables.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowNullables.kt @@ -2,5 +2,18 @@ package dev.inmo.micro_utils.coroutines import kotlinx.coroutines.flow.* +/** + * Filters out null values from this [Flow], returning only non-null elements. + * + * @param T The type of elements in the flow (nullable) + * @return A [Flow] containing only non-null elements + */ fun Flow.takeNotNull() = mapNotNull { it } + +/** + * Alias for [takeNotNull]. Filters out null values from this [Flow], returning only non-null elements. + * + * @param T The type of elements in the flow (nullable) + * @return A [Flow] containing only non-null elements + */ fun Flow.filterNotNull() = takeNotNull() diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowSubscriptionAsync.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowSubscriptionAsync.kt index b3f1dcf8565..3e4897391cc 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowSubscriptionAsync.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowSubscriptionAsync.kt @@ -65,6 +65,20 @@ private data class AsyncSubscriptionCommandClearReceiver( } } +/** + * Subscribes to a [Flow] with asynchronous processing based on markers. + * Each value from the flow will be processed by the [block] function. Values with the same marker + * will be processed sequentially in the same coroutine scope, while values with different markers + * can be processed concurrently in separate coroutine scopes. + * + * @param T The type of values emitted by the flow + * @param M The type of markers used to group values + * @param scope The [CoroutineScope] in which to subscribe to the flow + * @param markerFactory A factory function that produces a marker for each emitted value + * @param logger The logger instance used for logging exceptions. Defaults to [KSLog] + * @param block The suspending function that processes each emitted value + * @return A [Job] representing the subscription that can be cancelled + */ fun Flow.subscribeAsync( scope: CoroutineScope, markerFactory: suspend (T) -> M, @@ -122,6 +136,20 @@ fun Flow.subscribeSafelyWithoutExceptionsAsync( } } +/** + * Subscribes to a [Flow] with asynchronous processing based on markers, automatically logging and dropping exceptions. + * Each value from the flow will be processed by the [block] function. Values with the same marker + * will be processed sequentially, while values with different markers can be processed concurrently. + * Any exceptions thrown during processing will be logged and dropped without affecting other messages. + * + * @param T The type of values emitted by the flow + * @param M The type of markers used to group values + * @param scope The [CoroutineScope] in which to subscribe to the flow + * @param markerFactory A factory function that produces a marker for each emitted value + * @param logger The logger instance used for logging exceptions. Defaults to [KSLog] + * @param block The suspending function that processes each emitted value + * @return A [Job] representing the subscription that can be cancelled + */ fun Flow.subscribeLoggingDropExceptionsAsync( scope: CoroutineScope, markerFactory: suspend (T) -> M, diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowsSum.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowsSum.kt index 3c3fe0f87e0..5e5ab419b70 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowsSum.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/FlowsSum.kt @@ -5,4 +5,12 @@ package dev.inmo.micro_utils.coroutines import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.merge +/** + * Merges two flows into a single flow. Values from both flows will be emitted as they become available. + * This is a convenient operator syntax for [merge]. + * + * @param T The type of elements in the flows + * @param other The flow to merge with this flow + * @return A [Flow] that emits values from both flows + */ inline operator fun Flow.plus(other: Flow) = merge(this, other) diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/LaunchLogging.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/LaunchLogging.kt index 4409e84f620..cf2bd4b6ab4 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/LaunchLogging.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/LaunchLogging.kt @@ -6,6 +6,17 @@ import kotlinx.coroutines.* import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext +/** + * Launches a new coroutine with automatic exception logging. If an exception occurs, it will be logged + * using the provided [logger] and then rethrown. + * + * @param errorMessageBuilder A function to build the error message from the caught exception. Defaults to "Something web wrong" + * @param logger The logger instance to use for logging exceptions. Defaults to [KSLog] + * @param context Additional [CoroutineContext] for the new coroutine. Defaults to [EmptyCoroutineContext] + * @param start The coroutine start option. Defaults to [CoroutineStart.DEFAULT] + * @param block The suspending function to execute in the new coroutine + * @return A [Job] representing the launched coroutine + */ fun CoroutineScope.launchLogging( errorMessageBuilder: CoroutineScope.(Throwable) -> Any = { "Something web wrong" }, logger: KSLog = KSLog, @@ -18,6 +29,17 @@ fun CoroutineScope.launchLogging( }.getOrThrow() } +/** + * Launches a new coroutine with automatic exception logging and dropping. If an exception occurs, it will be logged + * using the provided [logger] and then dropped (not rethrown), allowing the coroutine to complete normally. + * + * @param errorMessageBuilder A function to build the error message from the caught exception. Defaults to "Something web wrong" + * @param logger The logger instance to use for logging exceptions. Defaults to [KSLog] + * @param context Additional [CoroutineContext] for the new coroutine. Defaults to [EmptyCoroutineContext] + * @param start The coroutine start option. Defaults to [CoroutineStart.DEFAULT] + * @param block The suspending function to execute in the new coroutine + * @return A [Job] representing the launched coroutine + */ fun CoroutineScope.launchLoggingDropExceptions( errorMessageBuilder: CoroutineScope.(Throwable) -> Any = { "Something web wrong" }, logger: KSLog = KSLog, @@ -30,6 +52,18 @@ fun CoroutineScope.launchLoggingDropExceptions( } // just dropping exception } +/** + * Creates a new async coroutine with automatic exception logging. If an exception occurs, it will be logged + * using the provided [logger] and then rethrown when the [Deferred] is awaited. + * + * @param T The return type of the async computation + * @param errorMessageBuilder A function to build the error message from the caught exception. Defaults to "Something web wrong" + * @param logger The logger instance to use for logging exceptions. Defaults to [KSLog] + * @param context Additional [CoroutineContext] for the new coroutine. Defaults to [EmptyCoroutineContext] + * @param start The coroutine start option. Defaults to [CoroutineStart.DEFAULT] + * @param block The suspending function to execute that returns a value of type [T] + * @return A [Deferred] representing the async computation + */ fun CoroutineScope.asyncLogging( errorMessageBuilder: CoroutineScope.(Throwable) -> Any = { "Something web wrong" }, logger: KSLog = KSLog, @@ -42,6 +76,18 @@ fun CoroutineScope.asyncLogging( }.getOrThrow() } +/** + * Creates a new async coroutine with automatic exception logging and dropping. If an exception occurs, it will be logged + * using the provided [logger] and wrapped in a [Result], which can be checked when the [Deferred] is awaited. + * + * @param T The return type of the async computation + * @param errorMessageBuilder A function to build the error message from the caught exception. Defaults to "Something web wrong" + * @param logger The logger instance to use for logging exceptions. Defaults to [KSLog] + * @param context Additional [CoroutineContext] for the new coroutine. Defaults to [EmptyCoroutineContext] + * @param start The coroutine start option. Defaults to [CoroutineStart.DEFAULT] + * @param block The suspending function to execute that returns a value of type [T] + * @return A [Deferred] containing a [Result] representing the async computation + */ fun CoroutineScope.asyncLoggingDropExceptions( errorMessageBuilder: CoroutineScope.(Throwable) -> Any = { "Something web wrong" }, logger: KSLog = KSLog, diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/ReplaceResult.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/ReplaceResult.kt index a9629015e91..8b7002f609f 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/ReplaceResult.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/ReplaceResult.kt @@ -1,3 +1,13 @@ package dev.inmo.micro_utils.coroutines +/** + * Replaces a failed [Result] with a new value computed from the exception. + * If this [Result] is successful, it is returned as-is. If it represents a failure, + * the [onException] handler is called with the exception to compute a replacement value, + * which is then wrapped in a new [Result]. + * + * @param T The type of the successful value + * @param onException A function that computes a replacement value from the caught exception + * @return The original [Result] if successful, or a new [Result] containing the replacement value + */ inline fun Result.replaceIfFailure(onException: (Throwable) -> T) = if (isSuccess) { this } else { runCatching { onException(exceptionOrNull()!!) } } diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/SmartRWLockerExtensions.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/SmartRWLockerExtensions.kt index f644a4f0206..fede74c2e5f 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/SmartRWLockerExtensions.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/SmartRWLockerExtensions.kt @@ -3,6 +3,14 @@ package dev.inmo.micro_utils.coroutines import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job +/** + * Executes the given [block] and unlocks all provided [lockers] for writing if the block succeeds. + * If the block throws an exception, the lockers will remain locked. + * + * @param lockers Variable number of [SmartRWLocker] instances to unlock on successful execution + * @param block The suspending function to execute + * @return A [Result] containing [Unit] on success or the exception that occurred + */ suspend inline fun alsoWithUnlockingOnSuccess( vararg lockers: SmartRWLocker, block: suspend () -> Unit @@ -14,6 +22,15 @@ suspend inline fun alsoWithUnlockingOnSuccess( } } +/** + * Asynchronously executes the given [block] and unlocks all provided [lockers] for writing if the block succeeds. + * This function launches a new coroutine in the given [scope] and automatically logs and drops any exceptions. + * + * @param scope The [CoroutineScope] in which to launch the coroutine + * @param lockers Variable number of [SmartRWLocker] instances to unlock on successful execution + * @param block The suspending function to execute + * @return A [Job] representing the launched coroutine + */ fun alsoWithUnlockingOnSuccessAsync( scope: CoroutineScope, vararg lockers: SmartRWLocker, diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/SupervisorJob.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/SupervisorJob.kt index 8c4c3d010dc..71529e0deca 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/SupervisorJob.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/SupervisorJob.kt @@ -3,19 +3,49 @@ package dev.inmo.micro_utils.coroutines import kotlinx.coroutines.* import kotlin.coroutines.CoroutineContext +/** + * Creates a [SupervisorJob] linked to this [CoroutineContext]'s job. The new supervisor job will be a child + * of the current job, and optionally combined with [additionalContext]. + * + * @param additionalContext Optional additional context to combine with the supervisor job + * @return A [CoroutineContext] containing the new supervisor job + */ fun CoroutineContext.LinkedSupervisorJob( additionalContext: CoroutineContext? = null ) = SupervisorJob(job).let { if (additionalContext != null) it + additionalContext else it } + +/** + * Creates a [SupervisorJob] linked to this [CoroutineScope]'s job. The new supervisor job will be a child + * of the current scope's job, and optionally combined with [additionalContext]. + * + * @param additionalContext Optional additional context to combine with the supervisor job + * @return A [CoroutineContext] containing the new supervisor job + */ fun CoroutineScope.LinkedSupervisorJob( additionalContext: CoroutineContext? = null ) = coroutineContext.LinkedSupervisorJob(additionalContext) +/** + * Creates a new [CoroutineScope] with a [SupervisorJob] linked to this [CoroutineContext]'s job. + * The new scope's supervisor job will be a child of the current job, and optionally combined with [additionalContext]. + * + * @param additionalContext Optional additional context to combine with the supervisor job + * @return A new [CoroutineScope] with a linked supervisor job + */ fun CoroutineContext.LinkedSupervisorScope( additionalContext: CoroutineContext? = null ) = CoroutineScope( this + LinkedSupervisorJob(additionalContext) ) + +/** + * Creates a new [CoroutineScope] with a [SupervisorJob] linked to this [CoroutineScope]'s job. + * The new scope's supervisor job will be a child of the current scope's job, and optionally combined with [additionalContext]. + * + * @param additionalContext Optional additional context to combine with the supervisor job + * @return A new [CoroutineScope] with a linked supervisor job + */ fun CoroutineScope.LinkedSupervisorScope( additionalContext: CoroutineContext? = null ) = coroutineContext.LinkedSupervisorScope(additionalContext) diff --git a/coroutines/src/jvmMain/kotlin/dev/inmo/micro_utils/coroutines/DoInIO.kt b/coroutines/src/jvmMain/kotlin/dev/inmo/micro_utils/coroutines/DoInIO.kt index fc5f79a7b06..c4e59572b7a 100644 --- a/coroutines/src/jvmMain/kotlin/dev/inmo/micro_utils/coroutines/DoInIO.kt +++ b/coroutines/src/jvmMain/kotlin/dev/inmo/micro_utils/coroutines/DoInIO.kt @@ -2,9 +2,22 @@ package dev.inmo.micro_utils.coroutines import kotlinx.coroutines.* +/** + * Convenience property to access [Dispatchers.IO] for I/O-bound operations. + * This dispatcher is optimized for offloading blocking I/O tasks to a shared pool of threads. + */ val IO get() = Dispatchers.IO +/** + * Executes the given [block] on the IO dispatcher and returns its result. + * This is a convenience function for executing I/O-bound operations like reading files, + * network requests, or database queries. + * + * @param T The return type of the block + * @param block The suspending function to execute on the IO dispatcher + * @return The result of executing the block + */ suspend inline fun doInIO(noinline block: suspend CoroutineScope.() -> T) = doIn( IO, block diff --git a/coroutines/src/jvmMain/kotlin/dev/inmo/micro_utils/coroutines/LaunchInCurrentThread.kt b/coroutines/src/jvmMain/kotlin/dev/inmo/micro_utils/coroutines/LaunchInCurrentThread.kt index d60e191281e..6293588cce9 100644 --- a/coroutines/src/jvmMain/kotlin/dev/inmo/micro_utils/coroutines/LaunchInCurrentThread.kt +++ b/coroutines/src/jvmMain/kotlin/dev/inmo/micro_utils/coroutines/LaunchInCurrentThread.kt @@ -3,6 +3,16 @@ package dev.inmo.micro_utils.coroutines import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +/** + * Launches a coroutine in the current thread using [Dispatchers.Unconfined] and blocks until it completes, + * returning its result. The coroutine will start execution in the current thread and will continue + * in the same thread until the first suspension point. + * + * @param T The return type of the suspending block + * @param block The suspending function to execute in the current thread + * @return The result of the suspending block + * @throws Throwable if the coroutine throws an exception + */ fun launchInCurrentThread(block: suspend CoroutineScope.() -> T): T { val scope = CoroutineScope(Dispatchers.Unconfined) return scope.launchSynchronously(block) diff --git a/coroutines/src/jvmMain/kotlin/dev/inmo/micro_utils/coroutines/LaunchSynchronously.kt b/coroutines/src/jvmMain/kotlin/dev/inmo/micro_utils/coroutines/LaunchSynchronously.kt index d5842a928c6..38b37c3addd 100644 --- a/coroutines/src/jvmMain/kotlin/dev/inmo/micro_utils/coroutines/LaunchSynchronously.kt +++ b/coroutines/src/jvmMain/kotlin/dev/inmo/micro_utils/coroutines/LaunchSynchronously.kt @@ -2,6 +2,16 @@ package dev.inmo.micro_utils.coroutines import kotlinx.coroutines.* +/** + * Launches a coroutine and blocks the current thread until the coroutine completes, returning its result. + * This is useful for bridging between suspending and non-suspending code in JVM environments. + * The coroutine is launched with [CoroutineStart.UNDISPATCHED] to start execution immediately. + * + * @param T The return type of the suspending block + * @param block The suspending function to execute synchronously + * @return The result of the suspending block + * @throws Throwable if the coroutine throws an exception + */ fun CoroutineScope.launchSynchronously(block: suspend CoroutineScope.() -> T): T { var result: Result? = null val objectToSynchronize = Object() @@ -22,7 +32,31 @@ fun CoroutineScope.launchSynchronously(block: suspend CoroutineScope.() -> T return result!!.getOrThrow() } +/** + * Launches a coroutine in a new [CoroutineScope] with [Dispatchers.Default] and blocks the current thread + * until the coroutine completes, returning its result. + * + * @param T The return type of the suspending block + * @param block The suspending function to execute synchronously + * @return The result of the suspending block + * @throws Throwable if the coroutine throws an exception + */ fun launchSynchronously(block: suspend CoroutineScope.() -> T): T = CoroutineScope(Dispatchers.Default).launchSynchronously(block) +/** + * Alias for [launchSynchronously]. Launches a coroutine and blocks the current thread until it completes. + * + * @param T The return type of the suspending block + * @param block The suspending function to execute synchronously + * @return The result of the suspending block + */ fun CoroutineScope.doSynchronously(block: suspend CoroutineScope.() -> T): T = launchSynchronously(block) + +/** + * Alias for [launchSynchronously]. Launches a coroutine in a new scope and blocks the current thread until it completes. + * + * @param T The return type of the suspending block + * @param block The suspending function to execute synchronously + * @return The result of the suspending block + */ fun doSynchronously(block: suspend CoroutineScope.() -> T): T = launchSynchronously(block) diff --git a/crypto/src/commonMain/kotlin/dev/inmo/micro_utils/crypto/Base64.kt b/crypto/src/commonMain/kotlin/dev/inmo/micro_utils/crypto/Base64.kt index 8eff015e7b6..7312cd4983f 100644 --- a/crypto/src/commonMain/kotlin/dev/inmo/micro_utils/crypto/Base64.kt +++ b/crypto/src/commonMain/kotlin/dev/inmo/micro_utils/crypto/Base64.kt @@ -12,12 +12,35 @@ private val BASE64_INVERSE_ALPHABET = IntArray(256) { internal fun Int.toBase64(): Char = BASE64_ALPHABET[this] internal fun Byte.fromBase64(): Byte = BASE64_INVERSE_ALPHABET[toInt() and 0xff].toByte() and BASE64_MASK +/** + * Type alias representing a Base64-encoded string. + */ typealias EncodedBase64String = String + +/** + * Type alias representing a Base64-encoded byte array. + */ typealias EncodedByteArray = ByteArray +/** + * Encodes this string to Base64 format, returning the result as a string. + * + * @return A Base64-encoded string + */ fun SourceString.encodeBase64String(): EncodedBase64String = encodeToByteArray().encodeBase64String() + +/** + * Encodes this string to Base64 format, returning the result as a byte array. + * + * @return A Base64-encoded byte array + */ fun SourceString.encodeBase64(): EncodedByteArray = encodeToByteArray().encodeBase64() +/** + * Encodes this byte array to Base64 format, returning the result as a string. + * + * @return A Base64-encoded string with padding ('=') characters + */ fun SourceBytes.encodeBase64String(): EncodedBase64String = buildString { var i = 0 while (this@encodeBase64String.size > i) { @@ -45,11 +68,33 @@ fun SourceBytes.encodeBase64String(): EncodedBase64String = buildString { i += read } } + +/** + * Encodes this byte array to Base64 format, returning the result as a byte array. + * + * @return A Base64-encoded byte array + */ fun SourceBytes.encodeBase64(): EncodedByteArray = encodeBase64String().encodeToByteArray() +/** + * Decodes this Base64-encoded string back to the original byte array. + * + * @return The decoded byte array + */ fun EncodedBase64String.decodeBase64(): SourceBytes = dropLastWhile { it == BASE64_PAD }.encodeToByteArray().decodeBase64() + +/** + * Decodes this Base64-encoded string back to the original string. + * + * @return The decoded string + */ fun EncodedBase64String.decodeBase64String(): SourceString = decodeBase64().decodeToString() +/** + * Decodes this Base64-encoded byte array back to the original byte array. + * + * @return The decoded byte array + */ fun EncodedByteArray.decodeBase64(): SourceBytes { val result = mutableListOf() val data = ByteArray(4) @@ -74,4 +119,10 @@ fun EncodedByteArray.decodeBase64(): SourceBytes { return result.toByteArray() } + +/** + * Decodes this Base64-encoded byte array back to the original string. + * + * @return The decoded string + */ fun EncodedByteArray.decodeBase64String(): SourceString = decodeBase64().decodeToString() diff --git a/crypto/src/commonMain/kotlin/dev/inmo/micro_utils/crypto/Hex.kt b/crypto/src/commonMain/kotlin/dev/inmo/micro_utils/crypto/Hex.kt index a53b5314f5e..20a8db53afc 100644 --- a/crypto/src/commonMain/kotlin/dev/inmo/micro_utils/crypto/Hex.kt +++ b/crypto/src/commonMain/kotlin/dev/inmo/micro_utils/crypto/Hex.kt @@ -1,7 +1,16 @@ package dev.inmo.micro_utils.crypto +/** + * Character array used for hexadecimal encoding (lowercase). + */ val HEX_ARRAY = "0123456789abcdef".toCharArray() +/** + * Converts this byte array to a hexadecimal string representation (lowercase). + * Each byte is represented as two hex characters. + * + * @return A lowercase hex string (e.g., "48656c6c6f" for "Hello") + */ fun SourceBytes.hex(): String { val hexChars = CharArray(size * 2) for (j in indices) { @@ -12,4 +21,9 @@ fun SourceBytes.hex(): String { return hexChars.concatToString() } +/** + * Converts this string to a hexadecimal representation by first encoding it as UTF-8 bytes. + * + * @return A lowercase hex string representation of the UTF-8 encoded bytes + */ fun SourceString.hex(): String = encodeToByteArray().hex() diff --git a/crypto/src/commonMain/kotlin/dev/inmo/micro_utils/crypto/MD5.kt b/crypto/src/commonMain/kotlin/dev/inmo/micro_utils/crypto/MD5.kt index 83fb48ebfa8..e15833a0d41 100644 --- a/crypto/src/commonMain/kotlin/dev/inmo/micro_utils/crypto/MD5.kt +++ b/crypto/src/commonMain/kotlin/dev/inmo/micro_utils/crypto/MD5.kt @@ -2,7 +2,21 @@ package dev.inmo.micro_utils.crypto import korlibs.crypto.md5 +/** + * Type alias representing an MD5 hash as a hex-encoded string (32 characters). + */ typealias MD5 = String +/** + * Computes the MD5 hash of this byte array and returns it as a lowercase hex string. + * + * @return The MD5 hash as a 32-character lowercase hex string + */ fun SourceBytes.md5(): MD5 = md5().hexLower + +/** + * Computes the MD5 hash of this string (encoded as UTF-8 bytes) and returns it as a lowercase hex string. + * + * @return The MD5 hash as a 32-character lowercase hex string + */ fun SourceString.md5(): MD5 = encodeToByteArray().md5().hexLower diff --git a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/State.kt b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/State.kt index 818c15f0a35..f7e252526cc 100644 --- a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/State.kt +++ b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/State.kt @@ -1,5 +1,13 @@ package dev.inmo.micro_utils.fsm.common +/** + * Represents a state in a finite state machine (FSM). + * Each state must have an associated context that identifies it uniquely within its chain. + */ interface State { + /** + * The context object that uniquely identifies this state within a state chain. + * States with the same context are considered to belong to the same chain. + */ val context: Any } diff --git a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/utils/StateHandlingErrorHandler.kt b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/utils/StateHandlingErrorHandler.kt index 064d27f00fc..7d9c01dc68e 100644 --- a/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/utils/StateHandlingErrorHandler.kt +++ b/fsm/common/src/commonMain/kotlin/dev/inmo/micro_utils/fsm/common/utils/StateHandlingErrorHandler.kt @@ -1,6 +1,26 @@ package dev.inmo.micro_utils.fsm.common.utils +/** + * A handler function type for dealing with errors during state handling in a finite state machine. + * The handler receives the state that caused the error and the thrown exception, and can optionally + * return a new state to continue the chain, or null to end the chain. + * + * @param T The state type that caused the error + * @param Throwable The exception that was thrown during state handling + * @return A new state to continue with, or null to end the state chain + */ typealias StateHandlingErrorHandler = suspend (T, Throwable) -> T? + +/** + * The default error handler that returns null for all errors, effectively ending the state chain. + */ val DefaultStateHandlingErrorHandler: StateHandlingErrorHandler<*> = { _, _ -> null } + +/** + * Returns a typed version of the [DefaultStateHandlingErrorHandler]. + * + * @param T The state type + * @return A [StateHandlingErrorHandler] for type [T] + */ inline fun defaultStateHandlingErrorHandler(): StateHandlingErrorHandler = DefaultStateHandlingErrorHandler as StateHandlingErrorHandler diff --git a/ksp/generator/src/main/kotlin/KClassTypeName.kt b/ksp/generator/src/main/kotlin/KClassTypeName.kt index dc35b0e53a7..c66d5146c66 100644 --- a/ksp/generator/src/main/kotlin/KClassTypeName.kt +++ b/ksp/generator/src/main/kotlin/KClassTypeName.kt @@ -6,6 +6,17 @@ import com.squareup.kotlinpoet.ClassName import com.squareup.kotlinpoet.asTypeName import kotlin.reflect.KClass +/** + * Safely retrieves a [ClassName] from a [KClass] getter, handling cases where the type is not + * yet available during annotation processing (KSP). + * + * When [KSTypeNotPresentException] is caught, it extracts the class name information from the + * exception's [com.google.devtools.ksp.symbol.KSType] to construct a [ClassName]. + * + * @param classnameGetter A lambda that returns the [KClass] to convert + * @return A KotlinPoet [ClassName] representing the class + * @throws Throwable If an exception other than [KSTypeNotPresentException] occurs + */ @Suppress("NOTHING_TO_INLINE") @OptIn(KspExperimental::class) inline fun safeClassName(classnameGetter: () -> KClass<*>) = runCatching { diff --git a/ksp/generator/src/main/kotlin/ResolveSubclasses.kt b/ksp/generator/src/main/kotlin/ResolveSubclasses.kt index ca6acc8c9b5..9169fa36aa9 100644 --- a/ksp/generator/src/main/kotlin/ResolveSubclasses.kt +++ b/ksp/generator/src/main/kotlin/ResolveSubclasses.kt @@ -2,6 +2,13 @@ package dev.inmo.micro_ksp.generator import com.google.devtools.ksp.symbol.KSClassDeclaration +/** + * Recursively resolves all subclasses of this sealed class declaration. + * For sealed classes, it traverses the entire hierarchy of sealed subclasses. + * For non-sealed classes (leaf nodes), it returns the class itself. + * + * @return A list of all concrete (non-sealed) subclass declarations in the hierarchy + */ fun KSClassDeclaration.resolveSubclasses(): List { return (getSealedSubclasses().flatMap { it.resolveSubclasses() diff --git a/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/BodyOrNull.kt b/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/BodyOrNull.kt index c3f2bedc83d..2c87eb57733 100644 --- a/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/BodyOrNull.kt +++ b/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/BodyOrNull.kt @@ -4,10 +4,25 @@ import io.ktor.client.call.body import io.ktor.client.statement.HttpResponse import io.ktor.http.HttpStatusCode +/** + * Returns the response body as type [T] if the [statusFilter] condition is met, otherwise returns null. + * By default, the filter checks if the status code is [HttpStatusCode.OK]. + * + * @param T The type to deserialize the response body to + * @param statusFilter A predicate to determine if the body should be retrieved. Defaults to checking for OK status + * @return The deserialized body of type [T], or null if the filter condition is not met + */ suspend inline fun HttpResponse.bodyOrNull( statusFilter: (HttpResponse) -> Boolean = { it.status == HttpStatusCode.OK } ) = takeIf(statusFilter) ?.body() +/** + * Returns the response body as type [T] if the status code is not [HttpStatusCode.NoContent], otherwise returns null. + * This is useful for handling responses that may return 204 No Content. + * + * @param T The type to deserialize the response body to + * @return The deserialized body of type [T], or null if the status is No Content + */ suspend inline fun HttpResponse.bodyOrNullOnNoContent() = bodyOrNull { it.status != HttpStatusCode.NoContent } diff --git a/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/HttpStatusCodeAsThrowable.kt b/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/HttpStatusCodeAsThrowable.kt index 8f4205c0caf..7e41daf729a 100644 --- a/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/HttpStatusCodeAsThrowable.kt +++ b/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/HttpStatusCodeAsThrowable.kt @@ -4,6 +4,13 @@ import io.ktor.client.plugins.ClientRequestException import io.ktor.client.statement.HttpResponse import io.ktor.http.isSuccess +/** + * Throws a [ClientRequestException] if this [HttpResponse] does not have a successful status code. + * A status code is considered successful if it's in the 2xx range. + * + * @param unsuccessMessage A lambda that provides the error message to use if the response is unsuccessful + * @throws ClientRequestException if the response status is not successful + */ inline fun HttpResponse.throwOnUnsuccess( unsuccessMessage: () -> String ) { diff --git a/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/MPPFileInputProvider.kt b/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/MPPFileInputProvider.kt index 6c16c1d1810..412654f4946 100644 --- a/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/MPPFileInputProvider.kt +++ b/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/MPPFileInputProvider.kt @@ -5,6 +5,12 @@ import dev.inmo.micro_utils.common.filesize import dev.inmo.micro_utils.ktor.common.input import io.ktor.client.request.forms.InputProvider +/** + * Creates a Ktor [InputProvider] from this multiplatform file for use in HTTP client requests. + * The input provider knows the file size and can create input streams on demand. + * + * @return An [InputProvider] for reading this file in HTTP requests + */ fun MPPFile.inputProvider(): InputProvider = InputProvider(filesize) { input() } diff --git a/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/OnUploadCallback.kt b/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/OnUploadCallback.kt index abd79c93b2b..69f4b9edc0e 100644 --- a/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/OnUploadCallback.kt +++ b/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/OnUploadCallback.kt @@ -1,3 +1,9 @@ package dev.inmo.micro_utils.ktor.client +/** + * A callback function type for tracking upload progress. + * + * @param uploaded The number of bytes uploaded so far + * @param count The total number of bytes to be uploaded + */ typealias OnUploadCallback = suspend (uploaded: Long, count: Long) -> Unit diff --git a/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/TemporalUpload.kt b/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/TemporalUpload.kt index d23a292d54f..04ba72ad6fb 100644 --- a/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/TemporalUpload.kt +++ b/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/TemporalUpload.kt @@ -5,6 +5,15 @@ import dev.inmo.micro_utils.ktor.common.* import io.ktor.client.HttpClient import io.ktor.client.content.* +/** + * Uploads a file to a temporary storage on the server. + * The server should provide an endpoint that accepts multipart uploads and returns a [TemporalFileId]. + * + * @param fullTempUploadDraftPath The full URL path to the temporary upload endpoint + * @param file The file to upload + * @param onUpload Progress callback invoked during upload + * @return A [TemporalFileId] that can be used to reference the uploaded file + */ expect suspend fun HttpClient.tempUpload( fullTempUploadDraftPath: String, file: MPPFile, diff --git a/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/UniUpload.kt b/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/UniUpload.kt index 8e5bdc87045..b485c26f063 100644 --- a/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/UniUpload.kt +++ b/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/UniUpload.kt @@ -10,6 +10,14 @@ import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.StringFormat import kotlinx.serialization.json.Json +/** + * Information about a file to upload in a multipart request. + * This allows uploading from custom sources beyond regular files. + * + * @param fileName The name of the file + * @param mimeType The MIME type of the file + * @param inputAllocator A lambda that provides input streams for reading the file data + */ data class UniUploadFileInfo( val fileName: FileName, val mimeType: String, diff --git a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/BuildStandardUrl.kt b/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/BuildStandardUrl.kt index 60b8dc3d99f..1efa096a90d 100644 --- a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/BuildStandardUrl.kt +++ b/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/BuildStandardUrl.kt @@ -1,5 +1,14 @@ package dev.inmo.micro_utils.ktor.common +/** + * Builds a standard URL by combining a base part, subpart, and optional query parameters. + * The base and subpart are joined with a '/', and query parameters are appended. + * + * @param basePart The base part of the URL (e.g., "https://example.com/api") + * @param subpart The subpart of the URL (e.g., "users") + * @param parameters Query parameters as a map. Defaults to an empty map + * @return The complete URL string with query parameters + */ fun buildStandardUrl( basePart: String, subpart: String, @@ -8,6 +17,15 @@ fun buildStandardUrl( parameters ) +/** + * Builds a standard URL by combining a base part, subpart, and query parameters as a list. + * The base and subpart are joined with a '/', and query parameters are appended. + * + * @param basePart The base part of the URL (e.g., "https://example.com/api") + * @param subpart The subpart of the URL (e.g., "users") + * @param parameters Query parameters as a list of key-value pairs + * @return The complete URL string with query parameters + */ fun buildStandardUrl( basePart: String, subpart: String, @@ -16,6 +34,15 @@ fun buildStandardUrl( parameters ) +/** + * Builds a standard URL by combining a base part, subpart, and vararg query parameters. + * The base and subpart are joined with a '/', and query parameters are appended. + * + * @param basePart The base part of the URL (e.g., "https://example.com/api") + * @param subpart The subpart of the URL (e.g., "users") + * @param parameters Query parameters as vararg key-value pairs + * @return The complete URL string with query parameters + */ fun buildStandardUrl( basePart: String, subpart: String, diff --git a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/CorrectCloseException.kt b/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/CorrectCloseException.kt index 2189e312777..e91c3e69a4a 100644 --- a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/CorrectCloseException.kt +++ b/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/CorrectCloseException.kt @@ -1,3 +1,8 @@ package dev.inmo.micro_utils.ktor.common +/** + * An exception used to indicate a correct/normal closure of a connection or stream. + * This is typically used in WebSocket or network communication scenarios where a + * clean shutdown needs to be distinguished from error conditions. + */ object CorrectCloseException : Exception() diff --git a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/CorrectWebsocketUrl.kt b/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/CorrectWebsocketUrl.kt index f1cd5d78b0f..c86874a65b2 100644 --- a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/CorrectWebsocketUrl.kt +++ b/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/CorrectWebsocketUrl.kt @@ -2,6 +2,14 @@ package dev.inmo.micro_utils.ktor.common private val schemaRegex = Regex("^[^:]*://") +/** + * Converts this string to a correct WebSocket URL by ensuring it starts with "ws://" scheme. + * If the URL already starts with "ws", it is returned unchanged. + * If the URL contains a scheme (e.g., "http://"), it is replaced with "ws://". + * If the URL has no scheme, "ws://" is prepended. + * + * @return A properly formatted WebSocket URL + */ val String.asCorrectWebSocketUrl: String get() = if (startsWith("ws")) { this diff --git a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/FromToDateTimeUrl.kt b/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/FromToDateTimeUrl.kt index d6707f69d3a..32fa12f9b6c 100644 --- a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/FromToDateTimeUrl.kt +++ b/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/FromToDateTimeUrl.kt @@ -2,14 +2,30 @@ package dev.inmo.micro_utils.ktor.common import korlibs.time.DateTime +/** + * Type alias representing a date-time range with optional start and end times. + * First element is the "from" date-time, second is the "to" date-time. + */ typealias FromToDateTime = Pair +/** + * Converts this [FromToDateTime] range to URL query parameters. + * Creates "from" and "to" query parameters with Unix millisecond timestamps. + * + * @return A map of query parameters representing the date-time range + */ val FromToDateTime.asFromToUrlPart: QueryParams get() = mapOf( "from" to first ?.unixMillis ?.toString(), "to" to second ?.unixMillis ?.toString() ) +/** + * Extracts a [FromToDateTime] range from URL query parameters. + * Looks for "from" and "to" parameters containing Unix millisecond timestamps. + * + * @return A [FromToDateTime] pair extracted from the query parameters + */ val QueryParams.extractFromToDateTime: FromToDateTime get() = FromToDateTime( get("from") ?.toDoubleOrNull() ?.let { DateTime(it) }, diff --git a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/InputProvider.kt b/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/InputProvider.kt index c03a604c2e1..67aca0e58de 100644 --- a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/InputProvider.kt +++ b/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/InputProvider.kt @@ -2,4 +2,8 @@ package dev.inmo.micro_utils.ktor.common import io.ktor.utils.io.core.Input +/** + * A function type that provides an [Input] instance. + * This is useful for lazy or deferred input creation in Ktor operations. + */ typealias LambdaInputProvider = () -> Input diff --git a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/MPPFileInput.kt b/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/MPPFileInput.kt index 3d1c742fb73..9b43c359e8e 100644 --- a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/MPPFileInput.kt +++ b/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/MPPFileInput.kt @@ -3,4 +3,10 @@ package dev.inmo.micro_utils.ktor.common import dev.inmo.micro_utils.common.MPPFile import io.ktor.utils.io.core.Input +/** + * Creates a Ktor [Input] from this multiplatform file. + * Platform-specific implementations handle file reading for each supported platform. + * + * @return An [Input] stream for reading this file + */ expect fun MPPFile.input(): Input diff --git a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/QueryParamsBuilder.kt b/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/QueryParamsBuilder.kt index 536553f2332..2abfd78b4ad 100644 --- a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/QueryParamsBuilder.kt +++ b/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/QueryParamsBuilder.kt @@ -29,6 +29,13 @@ fun String.includeQueryParams( queryParams: List ): String = "$this${if (contains("?")) "&" else "?"}${queryParams.asUrlQuery}" +/** + * Parses this URL query string into a [QueryParams] map. + * Splits on '&' to separate parameters and '=' to separate keys from values. + * Parameters without values will have null as their value. + * + * @return A map of query parameter keys to their values (or null if no value) + */ val String.parseUrlQuery: QueryParams get() = split("&").map { it.split("=").let { pair -> diff --git a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/StandardSerializer.kt b/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/StandardSerializer.kt index 0e7163665e0..1545588a708 100644 --- a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/StandardSerializer.kt +++ b/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/StandardSerializer.kt @@ -5,27 +5,73 @@ package dev.inmo.micro_utils.ktor.common import kotlinx.serialization.* import kotlinx.serialization.cbor.Cbor +/** + * Type alias for the standard serialization format used in Ktor utilities, which is [BinaryFormat]. + */ typealias StandardKtorSerialFormat = BinaryFormat + +/** + * Type alias for the standard serialization input data type, which is [ByteArray]. + */ typealias StandardKtorSerialInputData = ByteArray + +/** + * The standard Ktor serialization format instance, configured as CBOR. + */ val standardKtorSerialFormat: StandardKtorSerialFormat = Cbor { } +/** + * Decodes data from [StandardKtorSerialInputData] using the standard format. + * + * @param T The type to decode to + * @param deserializationStrategy The deserialization strategy for type [T] + * @param input The byte array input data to decode + * @return The decoded value of type [T] + */ inline fun StandardKtorSerialFormat.decodeDefault( deserializationStrategy: DeserializationStrategy, input: StandardKtorSerialInputData ): T = decodeFromByteArray(deserializationStrategy, input) +/** + * Encodes data to [StandardKtorSerialInputData] using the standard format. + * + * @param T The type to encode + * @param serializationStrategy The serialization strategy for type [T] + * @param data The data to encode + * @return The encoded byte array + */ inline fun StandardKtorSerialFormat.encodeDefault( serializationStrategy: SerializationStrategy, data: T ): StandardKtorSerialInputData = encodeToByteArray(serializationStrategy, data) +/** + * A CBOR instance for serialization operations. + */ val cbor = Cbor {} +/** + * Decodes data from a hex string using the standard binary format. + * + * @param T The type to decode to + * @param deserializationStrategy The deserialization strategy for type [T] + * @param input The hex string to decode + * @return The decoded value of type [T] + */ inline fun StandardKtorSerialFormat.decodeHex( deserializationStrategy: DeserializationStrategy, input: String ): T = decodeFromHexString(deserializationStrategy, input) +/** + * Encodes data to a hex string using the standard binary format. + * + * @param T The type to encode + * @param serializationStrategy The serialization strategy for type [T] + * @param data The data to encode + * @return The encoded hex string + */ inline fun StandardKtorSerialFormat.encodeHex( serializationStrategy: SerializationStrategy, data: T diff --git a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/TemporalFiles.kt b/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/TemporalFiles.kt index 5398a9edf54..c610c728b57 100644 --- a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/TemporalFiles.kt +++ b/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/TemporalFiles.kt @@ -3,8 +3,17 @@ package dev.inmo.micro_utils.ktor.common import kotlin.jvm.JvmInline import kotlinx.serialization.Serializable +/** + * The default subdirectory path for storing temporal files during upload operations. + */ const val DefaultTemporalFilesSubPath = "temp_upload" +/** + * A value class representing a unique identifier for a temporal file. + * Temporal files are typically used for temporary storage during file upload/processing operations. + * + * @param string The string representation of the temporal file ID + */ @Serializable @JvmInline value class TemporalFileId(val string: String) diff --git a/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/FromToDateTimeInUrl.kt b/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/FromToDateTimeInUrl.kt index c266187966a..c7d79604884 100644 --- a/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/FromToDateTimeInUrl.kt +++ b/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/FromToDateTimeInUrl.kt @@ -4,6 +4,12 @@ import korlibs.time.DateTime import dev.inmo.micro_utils.ktor.common.FromToDateTime import io.ktor.http.Parameters +/** + * Extracts a [FromToDateTime] range from Ktor server [Parameters]. + * Looks for "from" and "to" parameters containing Unix millisecond timestamps. + * + * @return A [FromToDateTime] pair extracted from the parameters + */ val Parameters.extractFromToDateTime: FromToDateTime get() = FromToDateTime( get("from") ?.toDoubleOrNull() ?.let { DateTime(it) }, diff --git a/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/QueryParameters.kt b/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/QueryParameters.kt index 241adfb6290..c22bd0e051c 100644 --- a/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/QueryParameters.kt +++ b/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/QueryParameters.kt @@ -4,6 +4,13 @@ import io.ktor.http.HttpStatusCode import io.ktor.server.application.ApplicationCall import io.ktor.server.response.respond +/** + * Retrieves a parameter value by [field] name from the request parameters. + * If the parameter is not present, responds with [HttpStatusCode.BadRequest] (400) and an error message. + * + * @param field The name of the parameter to retrieve + * @return The parameter value, or null if not found (after sending error response) + */ suspend fun ApplicationCall.getParameterOrSendError( field: String ) = parameters[field].also { @@ -12,6 +19,13 @@ suspend fun ApplicationCall.getParameterOrSendError( } } +/** + * Retrieves all parameter values by [field] name from the request parameters. + * If the parameter is not present, responds with [HttpStatusCode.BadRequest] (400) and an error message. + * + * @param field The name of the parameter to retrieve + * @return A list of parameter values, or null if not found (after sending error response) + */ suspend fun ApplicationCall.getParametersOrSendError( field: String ) = parameters.getAll(field).also { @@ -20,14 +34,33 @@ suspend fun ApplicationCall.getParametersOrSendError( } } +/** + * Retrieves a query parameter value by [field] name from the request. + * + * @param field The name of the query parameter to retrieve + * @return The query parameter value, or null if not found + */ fun ApplicationCall.getQueryParameter( field: String ) = request.queryParameters[field] +/** + * Retrieves all query parameter values by [field] name from the request. + * + * @param field The name of the query parameter to retrieve + * @return A list of query parameter values, or null if not found + */ fun ApplicationCall.getQueryParameters( field: String ) = request.queryParameters.getAll(field) +/** + * Retrieves a query parameter value by [field] name from the request. + * If the parameter is not present, responds with [HttpStatusCode.BadRequest] (400) and an error message. + * + * @param field The name of the query parameter to retrieve + * @return The query parameter value, or null if not found (after sending error response) + */ suspend fun ApplicationCall.getQueryParameterOrSendError( field: String ) = getQueryParameter(field).also { @@ -36,6 +69,13 @@ suspend fun ApplicationCall.getQueryParameterOrSendError( } } +/** + * Retrieves all query parameter values by [field] name from the request. + * If the parameter is not present, responds with [HttpStatusCode.BadRequest] (400) and an error message. + * + * @param field The name of the query parameter to retrieve + * @return A list of query parameter values, or null if not found (after sending error response) + */ suspend fun ApplicationCall.getQueryParametersOrSendError( field: String ) = getQueryParameters(field).also { diff --git a/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/RespondOrNoContent.kt b/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/RespondOrNoContent.kt index 3bc770d1ede..c30130aafca 100644 --- a/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/RespondOrNoContent.kt +++ b/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/RespondOrNoContent.kt @@ -4,6 +4,13 @@ import io.ktor.http.HttpStatusCode import io.ktor.server.application.ApplicationCall import io.ktor.server.response.respond +/** + * Responds with the given [data] if it's not null, or responds with [HttpStatusCode.NoContent] (204) if it's null. + * This is useful for API endpoints that may return empty results. + * + * @param T The type of data to respond with + * @param data The data to respond with, or null to respond with No Content + */ suspend inline fun ApplicationCall.respondOrNoContent( data: T? ) { diff --git a/language_codes/src/commonMain/kotlin/dev/inmo/micro_utils/language_codes/IetfLangSerializer.kt b/language_codes/src/commonMain/kotlin/dev/inmo/micro_utils/language_codes/IetfLangSerializer.kt index c83e8c638f4..1ec6b068160 100644 --- a/language_codes/src/commonMain/kotlin/dev/inmo/micro_utils/language_codes/IetfLangSerializer.kt +++ b/language_codes/src/commonMain/kotlin/dev/inmo/micro_utils/language_codes/IetfLangSerializer.kt @@ -5,6 +5,10 @@ import kotlinx.serialization.builtins.serializer import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder +/** + * Serializer for [IetfLang] that serializes language codes as their string representation. + * The language code is serialized as a simple string (e.g., "en-US", "fr", "de-DE"). + */ object IetfLangSerializer : KSerializer { override val descriptor = String.serializer().descriptor diff --git a/matrix/src/commonMain/kotlin/dev/inmo/micro_utils/matrix/Matrix.kt b/matrix/src/commonMain/kotlin/dev/inmo/micro_utils/matrix/Matrix.kt index 3d757d42cb3..7f1001e0f33 100644 --- a/matrix/src/commonMain/kotlin/dev/inmo/micro_utils/matrix/Matrix.kt +++ b/matrix/src/commonMain/kotlin/dev/inmo/micro_utils/matrix/Matrix.kt @@ -1,3 +1,9 @@ package dev.inmo.micro_utils.matrix +/** + * Represents a matrix as a list of rows, where each row is a list of elements. + * This is essentially a 2D structure represented as `List>`. + * + * @param T The type of elements in the matrix + */ typealias Matrix = List> diff --git a/matrix/src/commonMain/kotlin/dev/inmo/micro_utils/matrix/MatrixFactories.kt b/matrix/src/commonMain/kotlin/dev/inmo/micro_utils/matrix/MatrixFactories.kt index 045da40645d..f2e3c23c6a7 100644 --- a/matrix/src/commonMain/kotlin/dev/inmo/micro_utils/matrix/MatrixFactories.kt +++ b/matrix/src/commonMain/kotlin/dev/inmo/micro_utils/matrix/MatrixFactories.kt @@ -1,15 +1,37 @@ package dev.inmo.micro_utils.matrix +/** + * Creates a matrix using a DSL-style builder. + * Allows defining multiple rows using the [MatrixBuilder] API. + * + * @param T The type of elements in the matrix + * @param block A builder lambda to define the matrix structure + * @return A constructed [Matrix] + */ fun matrix(block: MatrixBuilder.() -> Unit): Matrix { return MatrixBuilder().also(block).matrix } +/** + * Creates a single-row matrix using a DSL-style builder. + * + * @param T The type of elements in the matrix + * @param block A builder lambda to define the row elements + * @return A [Matrix] containing a single row + */ fun flatMatrix(block: RowBuilder.() -> Unit): Matrix { return MatrixBuilder().apply { row(block) }.matrix } +/** + * Creates a single-row matrix from the provided elements. + * + * @param T The type of elements in the matrix + * @param elements The elements to include in the single row + * @return A [Matrix] containing a single row with the specified elements + */ fun flatMatrix(vararg elements: T): Matrix { return MatrixBuilder().apply { row { elements.forEach { +it } } diff --git a/matrix/src/commonMain/kotlin/dev/inmo/micro_utils/matrix/Row.kt b/matrix/src/commonMain/kotlin/dev/inmo/micro_utils/matrix/Row.kt index 701457d91b4..69d83fd34d9 100644 --- a/matrix/src/commonMain/kotlin/dev/inmo/micro_utils/matrix/Row.kt +++ b/matrix/src/commonMain/kotlin/dev/inmo/micro_utils/matrix/Row.kt @@ -1,3 +1,8 @@ package dev.inmo.micro_utils.matrix +/** + * Represents a single row in a matrix as a list of elements. + * + * @param T The type of elements in the row + */ typealias Row = List diff --git a/mime_types/src/commonMain/kotlin/dev/inmo/micro_utils/mime_types/CustomMimeType.kt b/mime_types/src/commonMain/kotlin/dev/inmo/micro_utils/mime_types/CustomMimeType.kt index 77098e4bdb4..a6c956ab92d 100644 --- a/mime_types/src/commonMain/kotlin/dev/inmo/micro_utils/mime_types/CustomMimeType.kt +++ b/mime_types/src/commonMain/kotlin/dev/inmo/micro_utils/mime_types/CustomMimeType.kt @@ -1,3 +1,9 @@ package dev.inmo.micro_utils.mime_types +/** + * A custom implementation of [MimeType] that wraps a raw MIME type string. + * Use this when you need to work with MIME types that aren't defined in the standard set. + * + * @param raw The raw MIME type string (e.g., "application/custom", "text/x-custom") + */ data class CustomMimeType(override val raw: String) : MimeType diff --git a/mime_types/src/commonMain/kotlin/dev/inmo/micro_utils/mime_types/MimeType.kt b/mime_types/src/commonMain/kotlin/dev/inmo/micro_utils/mime_types/MimeType.kt index 55468b3baac..a502d3f5ae5 100644 --- a/mime_types/src/commonMain/kotlin/dev/inmo/micro_utils/mime_types/MimeType.kt +++ b/mime_types/src/commonMain/kotlin/dev/inmo/micro_utils/mime_types/MimeType.kt @@ -2,9 +2,24 @@ package dev.inmo.micro_utils.mime_types import kotlinx.serialization.Serializable +/** + * Represents a MIME type (Multipurpose Internet Mail Extensions type). + * A MIME type is a standard way to indicate the nature and format of a document, file, or assortment of bytes. + * + * Examples: "text/html", "application/json", "image/png" + */ @Serializable(MimeTypeSerializer::class) interface MimeType { + /** + * The raw MIME type string (e.g., "text/html", "application/json"). + */ val raw: String + + /** + * An array of file extensions commonly associated with this MIME type. + * For example, "text/html" might have extensions ["html", "htm"]. + * Returns an empty array by default if no extensions are known. + */ val extensions: Array get() = emptyArray() } diff --git a/mime_types/src/commonMain/kotlin/dev/inmo/micro_utils/mime_types/MimeTypeSerializer.kt b/mime_types/src/commonMain/kotlin/dev/inmo/micro_utils/mime_types/MimeTypeSerializer.kt index a668afaf56e..58540f39f3a 100644 --- a/mime_types/src/commonMain/kotlin/dev/inmo/micro_utils/mime_types/MimeTypeSerializer.kt +++ b/mime_types/src/commonMain/kotlin/dev/inmo/micro_utils/mime_types/MimeTypeSerializer.kt @@ -8,6 +8,11 @@ import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder +/** + * Serializer for [MimeType] that serializes MIME types as their raw string representation. + * Uses the [mimeType] factory function to create appropriate [MimeType] instances during deserialization, + * which will return known MIME types when available or create [CustomMimeType] for unknown types. + */ @Suppress("OPT_IN_USAGE") @Serializer(MimeType::class) object MimeTypeSerializer : KSerializer { diff --git a/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/utils/DoForAll.kt b/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/utils/DoForAll.kt index 7bdb926e406..29fc2ea61cd 100644 --- a/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/utils/DoForAll.kt +++ b/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/utils/DoForAll.kt @@ -2,6 +2,16 @@ package dev.inmo.micro_utils.pagination.utils import dev.inmo.micro_utils.pagination.* +/** + * Executes [block] for each page in a paginated sequence. + * The [paginationMapper] determines the next pagination to use based on the current result. + * Stops when [paginationMapper] returns null. + * + * @param T The type of items in each page + * @param initialPagination The pagination to start with. Defaults to [FirstPagePagination] + * @param paginationMapper Function that determines the next pagination based on the current result. Return null to stop + * @param block Function that processes each page and returns a [PaginationResult] + */ inline fun doForAll( initialPagination: Pagination = FirstPagePagination(), paginationMapper: (PaginationResult) -> Pagination?, @@ -12,6 +22,14 @@ inline fun doForAll( } } +/** + * Executes [block] for each page in a paginated sequence, automatically moving to the next page + * until an empty page or the last page is reached. + * + * @param T The type of items in each page + * @param initialPagination The pagination to start with. Defaults to [FirstPagePagination] + * @param block Function that processes each page and returns a [PaginationResult] + */ inline fun doForAllWithNextPaging( initialPagination: Pagination = FirstPagePagination(), block: (Pagination) -> PaginationResult @@ -23,6 +41,14 @@ inline fun doForAllWithNextPaging( ) } +/** + * Executes [block] for each page in a paginated sequence, automatically moving to the next page + * until an empty page or the last page is reached. Uses current page pagination logic. + * + * @param T The type of items in each page + * @param initialPagination The pagination to start with. Defaults to [FirstPagePagination] + * @param block Function that processes each page and returns a [PaginationResult] + */ inline fun doAllWithCurrentPaging( initialPagination: Pagination = FirstPagePagination(), block: (Pagination) -> PaginationResult @@ -34,6 +60,13 @@ inline fun doAllWithCurrentPaging( ) } +/** + * Alias for [doAllWithCurrentPaging]. Executes [block] for each page in a paginated sequence. + * + * @param T The type of items in each page + * @param initialPagination The pagination to start with. Defaults to [FirstPagePagination] + * @param block Function that processes each page and returns a [PaginationResult] + */ inline fun doForAllWithCurrentPaging( initialPagination: Pagination = FirstPagePagination(), block: (Pagination) -> PaginationResult diff --git a/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/utils/GetAll.kt b/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/utils/GetAll.kt index 50ef92769c7..1196b095fb7 100644 --- a/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/utils/GetAll.kt +++ b/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/utils/GetAll.kt @@ -2,6 +2,16 @@ package dev.inmo.micro_utils.pagination.utils import dev.inmo.micro_utils.pagination.* +/** + * Retrieves all items from a paginated source by repeatedly calling [block] with different pagination parameters. + * The [paginationMapper] determines the next pagination to use based on the current result. + * + * @param T The type of items being retrieved + * @param initialPagination The pagination to start with. Defaults to [FirstPagePagination] + * @param paginationMapper Function that determines the next pagination based on the current result. Return null to stop + * @param block Function that retrieves a page of results for the given pagination + * @return A list containing all retrieved items from all pages + */ inline fun getAll( initialPagination: Pagination = FirstPagePagination(), paginationMapper: (PaginationResult) -> Pagination?, @@ -16,6 +26,17 @@ inline fun getAll( return results.toList() } +/** + * Retrieves all items from a paginated source using a receiver context. + * This is useful when the pagination logic depends on the receiver object's state. + * + * @param T The type of items being retrieved + * @param R The receiver type + * @param initialPagination The pagination to start with. Defaults to [FirstPagePagination] + * @param paginationMapper Function that determines the next pagination based on the current result + * @param block Function that retrieves a page of results for the given pagination using the receiver context + * @return A list containing all retrieved items from all pages + */ inline fun R.getAllBy( initialPagination: Pagination = FirstPagePagination(), paginationMapper: R.(PaginationResult) -> Pagination?, @@ -26,6 +47,15 @@ inline fun R.getAllBy( { block(it) } ) +/** + * Retrieves all items from a paginated source, automatically moving to the next page + * until an empty page or the last page is reached. + * + * @param T The type of items being retrieved + * @param initialPagination The pagination to start with. Defaults to [FirstPagePagination] + * @param block Function that retrieves a page of results for the given pagination + * @return A list containing all retrieved items from all pages + */ inline fun getAllWithNextPaging( initialPagination: Pagination = FirstPagePagination(), block: (Pagination) -> PaginationResult @@ -35,6 +65,16 @@ inline fun getAllWithNextPaging( block ) +/** + * Retrieves all items from a paginated source using a receiver context, + * automatically moving to the next page until an empty page or the last page is reached. + * + * @param T The type of items being retrieved + * @param R The receiver type + * @param initialPagination The pagination to start with. Defaults to [FirstPagePagination] + * @param block Function that retrieves a page of results for the given pagination using the receiver context + * @return A list containing all retrieved items from all pages + */ inline fun R.getAllByWithNextPaging( initialPagination: Pagination = FirstPagePagination(), block: R.(Pagination) -> PaginationResult @@ -43,6 +83,15 @@ inline fun R.getAllByWithNextPaging( { block(it) } ) +/** + * Retrieves all items from a paginated source, automatically moving to the next page + * until an empty page or the last page is reached. Uses current page pagination logic. + * + * @param T The type of items being retrieved + * @param initialPagination The pagination to start with. Defaults to [FirstPagePagination] + * @param block Function that retrieves a page of results for the given pagination + * @return A list containing all retrieved items from all pages + */ inline fun getAllWithCurrentPaging( initialPagination: Pagination = FirstPagePagination(), block: (Pagination) -> PaginationResult @@ -52,6 +101,17 @@ inline fun getAllWithCurrentPaging( block ) +/** + * Retrieves all items from a paginated source using a receiver context, + * automatically moving to the next page until an empty page or the last page is reached. + * Uses current page pagination logic. + * + * @param T The type of items being retrieved + * @param R The receiver type + * @param initialPagination The pagination to start with. Defaults to [FirstPagePagination] + * @param block Function that retrieves a page of results for the given pagination using the receiver context + * @return A list containing all retrieved items from all pages + */ inline fun R.getAllByWithCurrentPaging( initialPagination: Pagination = FirstPagePagination(), block: R.(Pagination) -> PaginationResult diff --git a/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/utils/OptionallyReverse.kt b/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/utils/OptionallyReverse.kt index fc4c2a625a6..b7842c4aae1 100644 --- a/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/utils/OptionallyReverse.kt +++ b/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/utils/OptionallyReverse.kt @@ -1,5 +1,13 @@ package dev.inmo.micro_utils.pagination.utils +/** + * Optionally reverses this [Iterable] based on the [reverse] parameter. + * Delegates to specialized implementations for [List] and [Set] for better performance. + * + * @param T The type of items in the iterable + * @param reverse If true, reverses the iterable; otherwise returns it unchanged + * @return The iterable, optionally reversed + */ fun Iterable.optionallyReverse(reverse: Boolean): Iterable = when (this) { is List -> optionallyReverse(reverse) is Set -> optionallyReverse(reverse) @@ -9,17 +17,41 @@ fun Iterable.optionallyReverse(reverse: Boolean): Iterable = when (thi this } } + +/** + * Optionally reverses this [List] based on the [reverse] parameter. + * + * @param T The type of items in the list + * @param reverse If true, reverses the list; otherwise returns it unchanged + * @return The list, optionally reversed + */ fun List.optionallyReverse(reverse: Boolean): List = if (reverse) { reversed() } else { this } + +/** + * Optionally reverses this [Set] based on the [reverse] parameter. + * Note that the resulting set may have a different iteration order than the original. + * + * @param T The type of items in the set + * @param reverse If true, reverses the set; otherwise returns it unchanged + * @return The set, optionally reversed + */ fun Set.optionallyReverse(reverse: Boolean): Set = if (reverse) { reversed().toSet() } else { this } +/** + * Optionally reverses this [Array] based on the [reverse] parameter. + * + * @param T The type of items in the array + * @param reverse If true, creates a reversed copy of the array; otherwise returns it unchanged + * @return The array, optionally reversed + */ inline fun Array.optionallyReverse(reverse: Boolean) = if (reverse) { Array(size) { get(lastIndex - it) diff --git a/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/utils/Paginate.kt b/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/utils/Paginate.kt index 3c11e0667b2..e623a54323d 100644 --- a/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/utils/Paginate.kt +++ b/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/utils/Paginate.kt @@ -2,6 +2,14 @@ package dev.inmo.micro_utils.pagination.utils import dev.inmo.micro_utils.pagination.* +/** + * Paginates this [Iterable] according to the given [Pagination] parameters. + * Returns a [PaginationResult] containing the items within the specified page range. + * + * @param T The type of items in the iterable + * @param with The pagination parameters specifying which page to retrieve + * @return A [PaginationResult] containing the items from the requested page + */ fun Iterable.paginate(with: Pagination): PaginationResult { var i = 0 val result = mutableListOf() @@ -20,6 +28,15 @@ fun Iterable.paginate(with: Pagination): PaginationResult { return result.createPaginationResult(with, i.toLong()) } +/** + * Paginates this [List] according to the given [Pagination] parameters. + * Returns a [PaginationResult] containing the items within the specified page range. + * More efficient than the [Iterable] version as it uses direct indexing. + * + * @param T The type of items in the list + * @param with The pagination parameters specifying which page to retrieve + * @return A [PaginationResult] containing the items from the requested page + */ fun List.paginate(with: Pagination): PaginationResult { if (with.firstIndex >= size || with.lastIndex < 0) { return emptyPaginationResult(with, size.toLong()) @@ -30,6 +47,14 @@ fun List.paginate(with: Pagination): PaginationResult { ) } +/** + * Paginates this [List] according to the given [Pagination] parameters, optionally in reverse order. + * + * @param T The type of items in the list + * @param with The pagination parameters specifying which page to retrieve + * @param reversed If true, the list will be paginated in reverse order + * @return A [PaginationResult] containing the items from the requested page, optionally reversed + */ fun List.paginate(with: Pagination, reversed: Boolean): PaginationResult { return if (reversed) { val actualPagination = with.optionallyReverse( @@ -42,6 +67,14 @@ fun List.paginate(with: Pagination, reversed: Boolean): PaginationResult< } } +/** + * Paginates this [Set] according to the given [Pagination] parameters. + * Returns a [PaginationResult] containing the items within the specified page range. + * + * @param T The type of items in the set + * @param with The pagination parameters specifying which page to retrieve + * @return A [PaginationResult] containing the items from the requested page + */ fun Set.paginate(with: Pagination): PaginationResult { return this.drop(with.firstIndex).take(with.size).createPaginationResult( with, @@ -49,6 +82,14 @@ fun Set.paginate(with: Pagination): PaginationResult { ) } +/** + * Paginates this [Set] according to the given [Pagination] parameters, optionally in reverse order. + * + * @param T The type of items in the set + * @param with The pagination parameters specifying which page to retrieve + * @param reversed If true, the set will be paginated in reverse order + * @return A [PaginationResult] containing the items from the requested page, optionally reversed + */ fun Set.paginate(with: Pagination, reversed: Boolean): PaginationResult { val actualPagination = with.optionallyReverse( size, diff --git a/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/utils/PaginatedIterable.kt b/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/utils/PaginatedIterable.kt index e2f52f31d3e..b7786227c9d 100644 --- a/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/utils/PaginatedIterable.kt +++ b/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/utils/PaginatedIterable.kt @@ -2,6 +2,15 @@ package dev.inmo.micro_utils.pagination.utils import dev.inmo.micro_utils.pagination.* +/** + * An iterator that lazily fetches items from a paginated data source. + * It automatically fetches the next page when the current page is exhausted. + * + * @param T The type of items being iterated + * @param pageSize The size of each page to fetch + * @param countGetter A function that returns the total count of available items + * @param paginationResultGetter A function that fetches a page of results for a given pagination + */ class PaginatedIterator( pageSize: Int, private val countGetter: () -> Long, @@ -22,6 +31,15 @@ class PaginatedIterator( } } +/** + * An iterable that lazily fetches items from a paginated data source. + * It creates a [PaginatedIterator] that automatically fetches pages as needed. + * + * @param T The type of items being iterated + * @param pageSize The size of each page to fetch + * @param countGetter A function that returns the total count of available items + * @param paginationResultGetter A function that fetches a page of results for a given pagination + */ class PaginatedIterable( private val pageSize: Int, private val countGetter: () -> Long, @@ -31,7 +49,14 @@ class PaginatedIterable( } /** - * Will make iterable using incoming [countGetter] and [paginationResultGetter] + * Creates an [Iterable] that lazily fetches items from a paginated data source. + * This is useful for iterating over large datasets without loading all items into memory at once. + * + * @param T The type of items being iterated + * @param countGetter A function that returns the total count of available items + * @param pageSize The size of each page to fetch. Defaults to [defaultPaginationPageSize] + * @param paginationResultGetter A function that fetches a page of results for a given pagination + * @return An [Iterable] that can be used in for-loops or other iterable contexts */ @Suppress("NOTHING_TO_INLINE") inline fun makeIterable( diff --git a/pagination/exposed/src/jvmMain/kotlin/dev/inmo/micro_utils/pagination/QueryExtensions.kt b/pagination/exposed/src/jvmMain/kotlin/dev/inmo/micro_utils/pagination/QueryExtensions.kt index 3965f694872..37c24389e20 100644 --- a/pagination/exposed/src/jvmMain/kotlin/dev/inmo/micro_utils/pagination/QueryExtensions.kt +++ b/pagination/exposed/src/jvmMain/kotlin/dev/inmo/micro_utils/pagination/QueryExtensions.kt @@ -4,6 +4,14 @@ import org.jetbrains.exposed.v1.core.Expression import org.jetbrains.exposed.v1.core.SortOrder import org.jetbrains.exposed.v1.jdbc.Query +/** + * Applies pagination to this Exposed [Query]. + * Sets the limit and offset based on the pagination parameters, and optionally orders the results. + * + * @param with The pagination parameters to apply + * @param orderBy Optional pair of expression and sort order to order the results by + * @return The query with pagination and optional ordering applied + */ fun Query.paginate(with: Pagination, orderBy: Pair, SortOrder>? = null) = limit(with.size) .offset(with.firstIndex.toLong()) @@ -18,6 +26,15 @@ fun Query.paginate(with: Pagination, orderBy: Pair, SortOrder>? = } } +/** + * Applies pagination to this Exposed [Query] with optional ordering and reversal. + * Sets the limit and offset based on the pagination parameters. + * + * @param with The pagination parameters to apply + * @param orderBy Optional expression to order the results by + * @param reversed If true, orders in descending order; otherwise ascending. Defaults to false + * @return The query with pagination and optional ordering applied + */ fun Query.paginate(with: Pagination, orderBy: Expression<*>?, reversed: Boolean = false) = paginate( with, orderBy ?.let { it to if (reversed) SortOrder.DESC else SortOrder.ASC } diff --git a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/KeyValuesRepo.kt b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/KeyValuesRepo.kt index 670d23ff5b5..58f77f6cbfa 100644 --- a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/KeyValuesRepo.kt +++ b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/KeyValuesRepo.kt @@ -6,13 +6,73 @@ import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging import kotlinx.coroutines.flow.Flow +/** + * Read part of [KeyValuesRepo] for one-to-many key-value relationships. + * This repository type allows multiple values to be associated with a single key. + * + * @param Key The type used as the key in all search operations + * @param Value The type of values associated with keys + */ interface ReadKeyValuesRepo : Repo { + /** + * Retrieves a paginated list of values associated with the given key. + * + * @param k The key to search for + * @param pagination The pagination parameters + * @param reversed Whether to reverse the order of results + * @return A [PaginationResult] containing values associated with the key + */ suspend fun get(k: Key, pagination: Pagination, reversed: Boolean = false): PaginationResult + + /** + * Retrieves a paginated list of keys. + * + * @param pagination The pagination parameters + * @param reversed Whether to reverse the order of results + * @return A [PaginationResult] containing keys + */ suspend fun keys(pagination: Pagination, reversed: Boolean = false): PaginationResult + + /** + * Retrieves keys that have the specified value associated with them. + * + * @param v The value to search for + * @param pagination The pagination parameters + * @param reversed Whether to reverse the order of results + * @return A [PaginationResult] containing keys associated with the value + */ suspend fun keys(v: Value, pagination: Pagination, reversed: Boolean = false): PaginationResult + + /** + * Checks if the specified key exists in the repository. + * + * @param k The key to check + * @return `true` if the key exists, `false` otherwise + */ suspend fun contains(k: Key): Boolean + + /** + * Checks if the specified key-value pair exists in the repository. + * + * @param k The key to check + * @param v The value to check + * @return `true` if the key-value pair exists, `false` otherwise + */ suspend fun contains(k: Key, v: Value): Boolean + + /** + * Returns the count of values associated with the specified key. + * + * @param k The key to count values for + * @return The number of values associated with the key + */ suspend fun count(k: Key): Long + + /** + * Returns the total count of key-value pairs in the repository. + * + * @return The total number of key-value pairs + */ suspend fun count(): Long suspend fun getAll(k: Key, reversed: Boolean = false): List { @@ -37,39 +97,84 @@ interface ReadKeyValuesRepo : Repo { } } } + +/** + * Type alias for [ReadKeyValuesRepo] emphasizing one-to-many relationships. + */ typealias ReadOneToManyKeyValueRepo = ReadKeyValuesRepo +/** + * Write part of [KeyValuesRepo] for one-to-many key-value relationships. + * Provides methods for adding, removing, and clearing values associated with keys. + * + * @param Key The type used as the key in all write operations + * @param Value The type of values associated with keys + */ interface WriteKeyValuesRepo : Repo { + /** + * Flow that emits when a new value is added to a key. + */ val onNewValue: Flow> + + /** + * Flow that emits when a value is removed from a key. + */ val onValueRemoved: Flow> + + /** + * Flow that emits when all data for a key is cleared. + */ val onDataCleared: Flow + /** + * Adds values to the specified keys. + * + * @param toAdd A map of keys to lists of values to add + */ suspend fun add(toAdd: Map>) /** - * Removes [Value]s by passed [Key]s without full clear of all data by [Key] + * Removes specific values from keys without clearing all data for those keys. + * + * @param toRemove A map of keys to lists of values to remove */ suspend fun remove(toRemove: Map>) /** - * Removes [v] without full clear of all data by [Key]s with [v] + * Removes a specific value from all keys that contain it, without clearing all data for those keys. + * + * @param v The value to remove */ suspend fun removeWithValue(v: Value) /** - * Fully clear all data by [k] + * Fully clears all data associated with the specified key. + * + * @param k The key to clear */ suspend fun clear(k: Key) + /** - * Clear [v] **with** full clear of all data by [Key]s with [v] + * Clears a specific value from all keys and removes those keys if they become empty. + * + * @param v The value to clear */ suspend fun clearWithValue(v: Value) + /** + * Sets the values for specified keys, clearing any existing values first. + * + * @param toSet A map of keys to lists of values to set + */ suspend fun set(toSet: Map>) { toSet.keys.forEach { key -> clear(key) } add(toSet) } } + +/** + * Type alias for [WriteKeyValuesRepo] emphasizing one-to-many relationships. + */ typealias WriteOneToManyKeyValueRepo = WriteKeyValuesRepo suspend inline fun > REPO.add( diff --git a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/MapperRepo.kt b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/MapperRepo.kt index 3fdf345e073..26e1619acd0 100644 --- a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/MapperRepo.kt +++ b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/MapperRepo.kt @@ -2,28 +2,66 @@ package dev.inmo.micro_utils.repos import dev.inmo.micro_utils.common.* +/** + * Interface for repositories that provide bidirectional mapping between two sets of key-value types. + * This is useful for adapting repositories to work with different key and value types. + * + * @param FromKey The original key type (inner/source) + * @param FromValue The original value type (inner/source) + * @param ToKey The target key type (outer/destination) + * @param ToValue The target value type (outer/destination) + */ @Suppress("UNCHECKED_CAST") interface MapperRepo { + /** + * Provides a mapper for converting keys between inner and outer types. + */ val keyMapper: SimpleSuspendableMapper get() = simpleSuspendableMapper( { it.toInnerKey() }, { it.toOutKey() } ) + + /** + * Provides a mapper for converting values between inner and outer types. + */ val valueMapper: SimpleSuspendableMapper get() = simpleSuspendableMapper( { it.toInnerValue() }, { it.toOutValue() } ) + /** + * Converts a key from the inner type to the outer type. + */ suspend fun FromKey.toOutKey() = this as ToKey + + /** + * Converts a value from the inner type to the outer type. + */ suspend fun FromValue.toOutValue() = this as ToValue + /** + * Converts a key from the outer type to the inner type. + */ suspend fun ToKey.toInnerKey() = this as FromKey + + /** + * Converts a value from the outer type to the inner type. + */ suspend fun ToValue.toInnerValue() = this as FromValue companion object } +/** + * Simple implementation of [MapperRepo] that uses provided conversion functions. + * + * @param FromKey The original key type + * @param FromValue The original value type + * @param ToKey The target key type + * @param ToValue The target value type + */ class SimpleMapperRepo( private val keyFromToTo: suspend FromKey.() -> ToKey, private val valueFromToTo: suspend FromValue.() -> ToValue, @@ -36,6 +74,9 @@ class SimpleMapperRepo( override suspend fun ToValue.toInnerValue(): FromValue = valueToToFrom() } +/** + * Factory function for creating a [SimpleMapperRepo] with custom conversion functions. + */ operator fun MapperRepo.Companion.invoke( keyFromToTo: suspend FromKey.() -> ToKey, valueFromToTo: suspend FromValue.() -> ToValue, @@ -43,6 +84,15 @@ operator fun MapperRepo.Companion.invoke( valueToToFrom: suspend ToValue.() -> FromValue ) = SimpleMapperRepo(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom) +/** + * Creates a [MapperRepo] with optional custom conversion functions. + * By default, uses casting for type conversions. + * + * @param keyFromToTo Function to convert keys from inner to outer type + * @param valueFromToTo Function to convert values from inner to outer type + * @param keyToToFrom Function to convert keys from outer to inner type + * @param valueToToFrom Function to convert values from outer to inner type + */ inline fun mapper( noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey }, noinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue }, diff --git a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/Repo.kt b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/Repo.kt index b05c462dcbb..a02deb69fa8 100644 --- a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/Repo.kt +++ b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/Repo.kt @@ -1,3 +1,8 @@ package dev.inmo.micro_utils.repos +/** + * Base marker interface for all repository types in the MicroUtils library. + * This interface serves as a common ancestor for specialized repository interfaces like + * [ReadCRUDRepo], [WriteCRUDRepo], [ReadKeyValueRepo], [WriteKeyValueRepo], etc. + */ interface Repo diff --git a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/diff/CRUDRepoDiff.kt b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/diff/CRUDRepoDiff.kt index f0ab647577b..6f5c94da16d 100644 --- a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/diff/CRUDRepoDiff.kt +++ b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/diff/CRUDRepoDiff.kt @@ -7,14 +7,40 @@ import dev.inmo.micro_utils.repos.CRUDRepo import dev.inmo.micro_utils.repos.ReadCRUDRepo import dev.inmo.micro_utils.repos.unset +/** + * Computes the difference between this [ReadCRUDRepo] and a map of items. + * Retrieves all items from the repository and compares them with the provided map. + * + * @param Id The type of IDs + * @param Registered The type of objects stored in the repository + * @param other The map to compare against + * @return A [MapDiff] describing added, removed, and updated items + */ suspend fun ReadCRUDRepo.diff(other: Map): MapDiff { return getAll().diff(other) } +/** + * Computes the difference between this map and a [ReadCRUDRepo]. + * Retrieves all items from the repository and compares them with this map. + * + * @param Id The type of IDs + * @param Registered The type of objects + * @param other The repository to compare against + * @return A [MapDiff] describing added, removed, and updated items + */ suspend fun Map.diff(other: ReadCRUDRepo): MapDiff { return diff(other.getAll()) } +/** + * Applies the difference between this map and a [ReadCRUDRepo] to this map. + * Modifies this mutable map to match the state of the repository. + * + * @param Id The type of IDs + * @param Registered The type of objects + * @param other The repository to synchronize with + */ suspend fun MutableMap.applyDiff(other: ReadCRUDRepo) { applyDiff(diff(other)) } \ No newline at end of file diff --git a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/mappers/CRUDMappers.kt b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/mappers/CRUDMappers.kt index e13e4dd8bcd..1c1bb4be17e 100644 --- a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/mappers/CRUDMappers.kt +++ b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/mappers/CRUDMappers.kt @@ -6,6 +6,17 @@ import dev.inmo.micro_utils.repos.* import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map +/** + * A [ReadCRUDRepo] wrapper that maps between different key and value types. + * Allows adapting a repository with one type system to work with another type system. + * + * @param FromId The external ID type exposed by this wrapper + * @param FromRegistered The external object type exposed by this wrapper + * @param ToId The internal ID type used by the underlying repository + * @param ToRegistered The internal object type used by the underlying repository + * @param to The underlying repository to wrap + * @param mapper The mapper that defines the bidirectional type conversions + */ open class MapperReadCRUDRepo( private val to: ReadCRUDRepo, mapper: MapperRepo @@ -41,11 +52,34 @@ open class MapperReadCRUDRepo( ) ?.toInnerValue() } +/** + * Wraps this [ReadCRUDRepo] with a mapper to expose different key and value types. + * + * @param FromKey The desired external ID type + * @param FromValue The desired external object type + * @param ToKey The current internal ID type + * @param ToValue The current internal object type + * @param mapper The mapper defining the type conversions + * @return A mapped repository with the new type system + */ @Suppress("NOTHING_TO_INLINE") inline fun ReadCRUDRepo.withMapper( mapper: MapperRepo ): ReadCRUDRepo = MapperReadCRUDRepo(this, mapper) +/** + * Wraps this [ReadCRUDRepo] with custom conversion functions for keys and values. + * + * @param FromKey The desired external ID type + * @param FromValue The desired external object type + * @param ToKey The current internal ID type + * @param ToValue The current internal object type + * @param keyFromToTo Converts external keys to internal keys + * @param valueFromToTo Converts external values to internal values + * @param keyToToFrom Converts internal keys to external keys + * @param valueToToFrom Converts internal values to external values + * @return A mapped repository with the new type system + */ @Suppress("NOTHING_TO_INLINE") inline fun ReadCRUDRepo.withMapper( noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey }, @@ -56,6 +90,20 @@ inline fun mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom) ) +/** + * A [WriteCRUDRepo] wrapper that maps between different key, value, and input types. + * Allows adapting a repository to work with different type systems for both reading and writing. + * + * @param FromId The external ID type + * @param FromRegistered The external object type for read operations + * @param FromInput The external input type for write operations + * @param ToId The internal ID type + * @param ToRegistered The internal object type for read operations + * @param ToInput The internal input type for write operations + * @param to The underlying repository to wrap + * @param mapper The mapper for keys and values + * @param inputMapper The mapper for input types + */ open class MapperWriteCRUDRepo( private val to: WriteCRUDRepo, mapper: MapperRepo, diff --git a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/pagination/CRUDPaginationExtensions.kt b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/pagination/CRUDPaginationExtensions.kt index d3666d263fb..7478e238f78 100644 --- a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/pagination/CRUDPaginationExtensions.kt +++ b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/pagination/CRUDPaginationExtensions.kt @@ -5,6 +5,17 @@ import dev.inmo.micro_utils.pagination.* import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging import dev.inmo.micro_utils.repos.ReadCRUDRepo +/** + * Retrieves all items from a [ReadCRUDRepo] by iterating through pages starting from the given [pagination]. + * Uses the provided [methodCaller] to fetch each page. + * + * @param T The type of objects in the repository + * @param ID The type of IDs in the repository + * @param REPO The specific repository type + * @param pagination The starting pagination parameters + * @param methodCaller A function that fetches a page of results from the repository + * @return A list of all items across all pages + */ suspend inline fun > REPO.getAll( pagination: Pagination, @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") @@ -13,6 +24,16 @@ suspend inline fun > REPO.getAll( methodCaller(this, it) } +/** + * Retrieves all items from a [ReadCRUDRepo] by iterating through all pages. + * Uses [maxPagePagination] to determine the starting pagination and the provided [methodCaller] to fetch each page. + * + * @param T The type of objects in the repository + * @param ID The type of IDs in the repository + * @param REPO The specific repository type + * @param methodCaller A function that fetches a page of results from the repository + * @return A list of all items across all pages + */ suspend inline fun > REPO.getAll( @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") crossinline methodCaller: suspend REPO.(Pagination) -> PaginationResult diff --git a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/pagination/MaxPagePagination.kt b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/pagination/MaxPagePagination.kt index a55802be9d0..1ff736ebe8d 100644 --- a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/pagination/MaxPagePagination.kt +++ b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/pagination/MaxPagePagination.kt @@ -6,6 +6,26 @@ import dev.inmo.micro_utils.repos.ReadCRUDRepo import dev.inmo.micro_utils.repos.ReadKeyValueRepo import dev.inmo.micro_utils.repos.ReadKeyValuesRepo +/** + * Creates a pagination starting from the first page with size equal to the total count of items in this [ReadCRUDRepo]. + * This effectively creates a single page containing all items. + * + * @return A [FirstPagePagination] with size equal to the repository count + */ suspend inline fun ReadCRUDRepo<*, *>.maxPagePagination() = FirstPagePagination(count().toCoercedInt()) + +/** + * Creates a pagination starting from the first page with size equal to the total count of items in this [ReadKeyValueRepo]. + * This effectively creates a single page containing all items. + * + * @return A [FirstPagePagination] with size equal to the repository count + */ suspend inline fun ReadKeyValueRepo<*, *>.maxPagePagination() = FirstPagePagination(count().toCoercedInt()) + +/** + * Creates a pagination starting from the first page with size equal to the total count of items in this [ReadKeyValuesRepo]. + * This effectively creates a single page containing all items. + * + * @return A [FirstPagePagination] with size equal to the repository count + */ suspend inline fun ReadKeyValuesRepo<*, *>.maxPagePagination() = FirstPagePagination(count().toCoercedInt()) diff --git a/safe_wrapper/src/commonMain/kotlin/SafeWrapper.kt b/safe_wrapper/src/commonMain/kotlin/SafeWrapper.kt index 6426ba6703e..085d51d80d0 100644 --- a/safe_wrapper/src/commonMain/kotlin/SafeWrapper.kt +++ b/safe_wrapper/src/commonMain/kotlin/SafeWrapper.kt @@ -2,16 +2,77 @@ package dev.inmo.micro_utils.safe_wrapper import dev.inmo.micro_utils.coroutines.runCatchingSafely +/** + * A wrapper interface that provides safe and unsafe access to a target object. + * Safe methods wrap operations in [Result] to catch and handle exceptions, + * while unsafe methods execute directly and may throw exceptions. + * + * @param T The type of the wrapped target object + */ interface SafeWrapper { + /** + * Executes a synchronous block on the target, catching any exceptions and returning a [Result]. + * + * @param R The return type of the block + * @param block The operation to execute on the target + * @return A [Result] containing either the successful result or the caught exception + */ fun safe(block: T.() -> R): Result = unsafeTarget().runCatching(block) + + /** + * Executes a synchronous block on the target without exception handling. + * + * @param R The return type of the block + * @param block The operation to execute on the target + * @return The direct result of the block + * @throws Throwable If the block throws an exception + */ fun unsafe(block: T.() -> R): R = unsafeTarget().block() + + /** + * Executes a suspending block on the target, catching any exceptions (except [kotlinx.coroutines.CancellationException]) + * and returning a [Result]. + * + * @param R The return type of the block + * @param block The suspending operation to execute on the target + * @return A [Result] containing either the successful result or the caught exception + */ suspend fun safeS(block: suspend T.() -> R): Result = unsafeTarget().runCatchingSafely(block = block) + + /** + * Executes a suspending block on the target without exception handling. + * + * @param R The return type of the block + * @param block The suspending operation to execute on the target + * @return The direct result of the block + * @throws Throwable If the block throws an exception + */ suspend fun unsafeS(block: suspend T.() -> R): R = unsafeTarget().block() + + /** + * Provides access to the underlying wrapped target object. + * + * @return The target object + */ fun unsafeTarget(): T - class Default(private val t: T) : SafeWrapper { override fun unsafeTarget(): T = t } + /** + * Default implementation of [SafeWrapper] that wraps a provided target. + * + * @param T The type of the wrapped target + */ + class Default(private val t: T) : SafeWrapper { + override fun unsafeTarget(): T = t + } companion object { + /** + * Creates a [SafeWrapper] instance wrapping the provided target. + * + * @param T The type of the target + * @param t The target object to wrap + * @return A [SafeWrapper.Default] instance + */ operator fun invoke(t: T) = Default(t) } } diff --git a/serialization/base64/src/commonMain/kotlin/dev/inmo/micro_utils/serialization/base64/Base64Serializer.kt b/serialization/base64/src/commonMain/kotlin/dev/inmo/micro_utils/serialization/base64/Base64Serializer.kt index b63d05a5815..81a21cb9893 100644 --- a/serialization/base64/src/commonMain/kotlin/dev/inmo/micro_utils/serialization/base64/Base64Serializer.kt +++ b/serialization/base64/src/commonMain/kotlin/dev/inmo/micro_utils/serialization/base64/Base64Serializer.kt @@ -5,6 +5,14 @@ import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder +/** + * A serializer that encodes values to Base64 strings and decodes them back. + * Uses custom conversion functions to transform between type [T] and string representation. + * + * @param T The type to serialize/deserialize + * @param converterFrom Converts from type [T] to a string representation + * @param converterTo Converts from a string representation back to type [T] + */ open class Base64Serializer( private val converterFrom: (T) -> String, private val converterTo: (String) -> T, @@ -14,5 +22,12 @@ open class Base64Serializer( override fun serialize(encoder: Encoder, value: T) = Base64BytesToFromStringSerializer.serialize(encoder, converterFrom(value).encodeToByteArray()) } +/** + * Serializer for [String] values encoded as Base64. + */ object Base64StringSerializer : Base64Serializer({ it }, { it }) + +/** + * Serializer for [ByteArray] values encoded as Base64. + */ object Base64ByteArraySerializer : Base64Serializer({ it.decodeToString() }, { it.encodeToByteArray() }) diff --git a/serialization/typed_serializer/src/commonMain/kotlin/dev/inmo/micro_utils/serialization/typed_serializer/TypedSerializer.kt b/serialization/typed_serializer/src/commonMain/kotlin/dev/inmo/micro_utils/serialization/typed_serializer/TypedSerializer.kt index 006d0728677..d800d946202 100644 --- a/serialization/typed_serializer/src/commonMain/kotlin/dev/inmo/micro_utils/serialization/typed_serializer/TypedSerializer.kt +++ b/serialization/typed_serializer/src/commonMain/kotlin/dev/inmo/micro_utils/serialization/typed_serializer/TypedSerializer.kt @@ -6,6 +6,17 @@ import kotlinx.serialization.descriptors.* import kotlinx.serialization.encoding.* import kotlin.reflect.KClass +/** + * A serializer that includes type information in the serialized output. + * This allows polymorphic serialization where the exact type is preserved in the output + * as a "type" field alongside the "value" field. + * + * The serialized format is: `{"type": "TypeName", "value": {...}}` + * + * @param T The base type that this serializer handles + * @param kClass The Kotlin class of the base type + * @param presetSerializers A map of type names to their serializers for known subtypes + */ open class TypedSerializer( kClass: KClass, presetSerializers: Map> = emptyMap(), @@ -60,29 +71,67 @@ open class TypedSerializer( } + /** + * Adds a new type to this serializer with its associated serializer. + * + * @param O The specific subtype to include + * @param type The type name to use in serialized output + * @param serializer The serializer for this type + */ open fun include(type: String, serializer: KSerializer) { serializers[type] = serializer } + /** + * Removes a type from this serializer by its name. + * + * @param type The type name to remove + */ open fun exclude(type: String) { serializers.remove(type) } } +/** + * Adds a type to this [TypedSerializer] using its class name as the type identifier. + * + * @param T The type to add + * @param kClass The Kotlin class to add + */ @InternalSerializationApi operator fun TypedSerializer.plusAssign(kClass: KClass) { include(kClass.simpleName!!, kClass.serializer()) } +/** + * Removes a type from this [TypedSerializer] using its class name. + * + * @param T The type to remove + * @param kClass The Kotlin class to remove + */ @InternalSerializationApi operator fun TypedSerializer.minusAssign(kClass: KClass) { exclude(kClass.simpleName!!) } +/** + * Creates a [TypedSerializer] for the reified type [T]. + * + * @param T The base type to serialize + * @param presetSerializers A map of type names to serializers for known subtypes + * @return A new [TypedSerializer] instance + */ inline fun TypedSerializer( presetSerializers: Map> = emptyMap() ) = TypedSerializer(T::class, presetSerializers) +/** + * Creates a [TypedSerializer] for the reified type [T] with vararg preset serializers. + * + * @param T The base type to serialize + * @param presetSerializers Pairs of type names to serializers for known subtypes + * @return A new [TypedSerializer] instance + */ inline fun TypedSerializer( vararg presetSerializers: Pair> ) = TypedSerializer(presetSerializers.toMap()) diff --git a/transactions/src/commonMain/kotlin/TransactionsDSL.kt b/transactions/src/commonMain/kotlin/TransactionsDSL.kt index 76db5e960d6..54d1ce24061 100644 --- a/transactions/src/commonMain/kotlin/TransactionsDSL.kt +++ b/transactions/src/commonMain/kotlin/TransactionsDSL.kt @@ -1,6 +1,14 @@ package dev.inmo.micro_utils.transactions +/** + * Type alias for a suspending rollback lambda that receives the error that triggered the rollback. + */ typealias TransactionDSLRollbackLambda = suspend (Throwable) -> Unit + +/** + * DSL context for defining transactional operations with automatic rollback on failure. + * This class manages a list of rollback actions that will be executed in reverse order if an error occurs. + */ class TransactionsDSL internal constructor() { internal val rollbackActions = ArrayList() @@ -8,6 +16,15 @@ class TransactionsDSL internal constructor() { rollbackActions.add(rollbackAction) } } + +/** + * Context provided to rollback actions, containing both the successful result of the action + * and the error that triggered the rollback. + * + * @param T The type of the action result + * @param actionResult The result of the action that succeeded before the transaction failed + * @param error The throwable that caused the transaction to fail + */ class RollbackContext internal constructor ( val actionResult: T, val error: Throwable