mirror of
				https://github.com/InsanusMokrassar/MicroUtils.git
				synced 2025-10-31 04:05:32 +00:00 
			
		
		
		
	Compare commits
	
		
			106 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| f40d33db2a | |||
| a5eb0fbd24 | |||
| 307c8030af | |||
| a9016465fa | |||
| 358b70eb5f | |||
| 6fcbb80a71 | |||
|  | 643f6c420b | ||
| ac68b0b941 | |||
| d62f67bd88 | |||
| 8718c5e310 | |||
| e8273ab80c | |||
| 2718605987 | |||
| d99538d80b | |||
| ce7a1e4e21 | |||
| 921734763d | |||
| 18c608f569 | |||
| b915f6ece2 | |||
| 24347b422c | |||
| e5f4ae647f | |||
| 72202b8a21 | |||
| dbc14d41de | |||
| 1dca5ea00d | |||
| f03d1d788c | |||
| fcac6f9fa8 | |||
| ca0cd433c9 | |||
| 39589fdbd0 | |||
| 605fc3cff9 | |||
| 12cd6f48f8 | |||
| 1f57478d10 | |||
| 7ef4c5d282 | |||
| f09d92be32 | |||
| 6245b36bdb | |||
| 54cc353bcc | |||
| b7abba099c | |||
| c5dbd10335 | |||
| a44e3e953d | |||
| ee2521cb01 | |||
| 4625dfb857 | |||
| b9a2653066 | |||
| fcaa327660 | |||
| 496117d517 | |||
| 8ce7d37b72 | |||
| 25391609b9 | |||
| f0b7b9c5e5 | |||
| 301cdaa2c2 | |||
| fce7ec8912 | |||
| 24bd403549 | |||
| 46c89c48a9 | |||
| bad9a53fdb | |||
| 0bce7bd60a | |||
| 2f70a1cfb4 | |||
| bfb6e738ee | |||
| c7ad9aae07 | |||
| fecd719239 | |||
| 18d6ac31b5 | |||
| d8dbebfc7e | |||
| 16463d0eb9 | |||
| 837cac644d | |||
| e83e0a8535 | |||
| 2e309c31a6 | |||
| 625db02651 | |||
| e3144c08c1 | |||
| 40e25970ad | |||
| 4ccb8a8d11 | |||
| 26d1c3d9ef | |||
| cc8fe7b001 | |||
| 4601eab864 | |||
| 1ee9c88ffd | |||
| 7feac213f6 | |||
| c75a9c0f61 | |||
| afd0c9784f | |||
| f1ddbea06e | |||
| 42bf3cd4e3 | |||
| 27f5549f56 | |||
| b0569f8421 | |||
| 8d86f29325 | |||
| aeca32498a | |||
| e8232664f3 | |||
| efa869f91a | |||
| dbff3d7cf1 | |||
| 9e70f73684 | |||
| 9f2f0de0c4 | |||
| bf293a8f8f | |||
| 5fbc1a132f | |||
| 36093b9741 | |||
| 8713fb04c5 | |||
| ea82a59f31 | |||
| 18f67cd4f2 | |||
| 120e7228c7 | |||
| 892fa90c37 | |||
| 491dac5bf0 | |||
| 2ab06fbafd | |||
| e68735d061 | |||
| 9ba4d98c30 | |||
| 19bbfd4916 | |||
| d94cd7ea94 | |||
| 0dd8f41eb0 | |||
| f1ab5ab51f | |||
| a8056f3120 | |||
| 09c0cdebb5 | |||
| 629d7c7a82 | |||
| a0dd1aec3d | |||
| 17d6377902 | |||
| ad401105a1 | |||
| 4913e99c2e | |||
| 16b97029cf | 
							
								
								
									
										7
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							| @@ -17,8 +17,9 @@ jobs: | |||||||
|           mv gradle.properties.tmp gradle.properties |           mv gradle.properties.tmp gradle.properties | ||||||
|       - name: Build |       - name: Build | ||||||
|         run: ./gradlew build |         run: ./gradlew build | ||||||
|       - name: Publish |       - name: Publish to InmoNexus | ||||||
|         continue-on-error: true |         continue-on-error: true | ||||||
|         run: ./gradlew publishAllPublicationsToGiteaRepository |         run: ./gradlew publishAllPublicationsToInmoNexusRepository | ||||||
|         env: |         env: | ||||||
|           GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} |           INMONEXUS_USER: ${{ secrets.INMONEXUS_USER }} | ||||||
|  |           INMONEXUS_PASSWORD: ${{ secrets.INMONEXUS_PASSWORD }} | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,4 +1,5 @@ | |||||||
| .idea | .idea | ||||||
|  | .kotlin | ||||||
| out/* | out/* | ||||||
| *.iml | *.iml | ||||||
| target | target | ||||||
| @@ -17,5 +18,6 @@ publishing.sh | |||||||
|  |  | ||||||
| local.* | local.* | ||||||
| local/ | local/ | ||||||
|  | **/*.local.* | ||||||
|  |  | ||||||
| .kotlin/ | .kotlin/ | ||||||
|   | |||||||
							
								
								
									
										169
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										169
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -1,5 +1,174 @@ | |||||||
| # Changelog | # Changelog | ||||||
|  |  | ||||||
|  | ## 0.24.2 | ||||||
|  |  | ||||||
|  | * `Versions`: | ||||||
|  |   * `Exposed`: `0.57.0` -> `0.58.0` | ||||||
|  | * `Ksp`: | ||||||
|  |   * `Sealed`: | ||||||
|  |     * Add annotation `GenerateSealedTypesWorkaround` which allow to generate `subtypes` lists | ||||||
|  |  | ||||||
|  | ## 0.24.1 | ||||||
|  |  | ||||||
|  | * `Versions`: | ||||||
|  |   * `Serialization`: `1.7.3` -> `1.8.0` | ||||||
|  |   * `SQLite`: `3.47.1.0` -> `3.47.2.0` | ||||||
|  |   * `Koin`: `4.0.0` -> `3.10.2` | ||||||
|  |   * `OKio`: `3.9.1` -> `3.10.2` | ||||||
|  |  | ||||||
|  | ## 0.24.0 | ||||||
|  |  | ||||||
|  | * `Versions`: | ||||||
|  |   * `Coroutines`: `1.9.0` -> `1.10.1` | ||||||
|  |   * `KSLog`: `1.3.6` -> `1.4.0` | ||||||
|  |   * `Compose`: `1.7.1` -> `1.7.3` | ||||||
|  |   * `Ktor`: `3.0.2` -> `3.0.3` | ||||||
|  | * `Common`: | ||||||
|  |   * Rename `Progress` to more common `Percentage`. `Progress` now is typealias | ||||||
|  |   * Fix of `Progress.compareTo` extension | ||||||
|  |  | ||||||
|  | ## 0.23.2 | ||||||
|  |  | ||||||
|  | * `Versions`: | ||||||
|  |   * `Kotlin`: `2.0.21` -> `2.1.0` | ||||||
|  |   * `Exposed`: `0.56.0` -> `0.57.0` | ||||||
|  |   * `Xerial SQLite`: `3.47.0.0` -> `3.47.1.0` | ||||||
|  |   * `Ktor`: `3.0.1` -> `3.0.2` | ||||||
|  | * `Coroutines`: | ||||||
|  |   * Small refactor in `AccumulatorFlow` to use `runCatching` instead of `runCatchingSafely` | ||||||
|  |  | ||||||
|  | ## 0.23.1 | ||||||
|  |  | ||||||
|  | * `Versions`: | ||||||
|  |   * `Compose`: `1.7.0` -> `1.7.1` | ||||||
|  |   * `Exposed`: `0.55.0` -> `0.56.0` | ||||||
|  |   * `Xerial SQLite`: `3.46.1.3` -> `3.47.0.0` | ||||||
|  |   * `Android CoreKTX`: `1.13.1` -> `1.15.0` | ||||||
|  |   * `Android Fragment`: `1.8.4` -> `1.8.5` | ||||||
|  | * `Coroutines`: | ||||||
|  |   * `Compose`: | ||||||
|  |     * Add `StyleSheetsAggregator` | ||||||
|  |  | ||||||
|  | ## 0.23.0 | ||||||
|  |  | ||||||
|  | **THIS UPDATE MAY CONTAINS SOME BREAKING CHANGES (INCLUDING BREAKING CHANGES IN BYTECODE LAYER) RELATED TO UPDATE OF | ||||||
|  | KTOR DEPENDENCY** | ||||||
|  |  | ||||||
|  | **THIS UPDATE CONTAINS CHANGES ACCORDING TO MIGRATION [GUIDE FROM KTOR](https://ktor.io/docs/migrating-3.html)** | ||||||
|  |  | ||||||
|  | * `Versions`: | ||||||
|  |   * `Ktor`: `2.3.12` -> `3.0.1` | ||||||
|  | * `Ktor`: | ||||||
|  |   * `Common`: | ||||||
|  |     * Extension `Input.downloadToTempFile` has changed its receiver to `Source`. Its API can be broken | ||||||
|  |   * `Client`: | ||||||
|  |     * Extension `HttpClient.tempUpload` has changed type of `onUpload` argument from `OnUploadCallback` to `ProgressListener` | ||||||
|  |     * All extensions `HttpClient.uniUpload` have changed type of `onUpload` argument from `OnUploadCallback` to `ProgressListener` | ||||||
|  |   * `Server`: | ||||||
|  |     * Remove redundant `ApplicationCall.respond` extension due to its presence in the ktor library | ||||||
|  |  | ||||||
|  | ## 0.22.9 | ||||||
|  |  | ||||||
|  | * `Repos`: | ||||||
|  |   * `Cache`: | ||||||
|  |     * Add direct caching repos | ||||||
|  |  | ||||||
|  | ## 0.22.8 | ||||||
|  |  | ||||||
|  | * `Common`: | ||||||
|  |   * Add `List.breakAsPairs` extension | ||||||
|  |   * Add `Sequence.padWith`/`Sequence.padStart`/`Sequence.padEnd` and `List.padWith`/`List.padStart`/`List.padEnd` extensions | ||||||
|  |  | ||||||
|  | ## 0.22.7 | ||||||
|  |  | ||||||
|  | * `Versions`: | ||||||
|  |   * `Kotlin`: `2.0.20` -> `2.0.21` | ||||||
|  |   * `Compose`: `1.7.0-rc01` -> `1.7.0` | ||||||
|  | * `KSP`: | ||||||
|  |   * `Sealed`: | ||||||
|  |     * Change package of `GenerateSealedWorkaround`. Migration: replace `dev.inmo.microutils.kps.sealed.GenerateSealedWorkaround` -> `dev.inmo.micro_utils.ksp.sealed.GenerateSealedWorkaround` | ||||||
|  |  | ||||||
|  | ## 0.22.6 | ||||||
|  |  | ||||||
|  | * `KSP`: | ||||||
|  |   * `Generator`: | ||||||
|  |     * Add extension `KSClassDeclaration.buildSubFileName` | ||||||
|  |     * Add extension `KSClassDeclaration.companion` | ||||||
|  |     * Add extension `KSClassDeclaration.resolveSubclasses` | ||||||
|  |   * `Sealed`: | ||||||
|  |     * Improvements | ||||||
|  |  | ||||||
|  | ## 0.22.5 | ||||||
|  |  | ||||||
|  | * `Versions`: | ||||||
|  |   * `Compose`: `1.7.0-beta02` -> `1.7.0-rc01` | ||||||
|  |   * `SQLite`: `3.46.1.2` -> `3.46.1.3` | ||||||
|  |   * `AndroidXFragment`: `1.8.3` -> `1.8.4` | ||||||
|  | * `Common`: | ||||||
|  |   * Add extension `withReplacedAt`/`withReplaced` ([#489](https://github.com/InsanusMokrassar/MicroUtils/issues/489)) | ||||||
|  | * `Coroutines`: | ||||||
|  |   * Add extension `Flow.debouncedBy` | ||||||
|  | * `Ktor`: | ||||||
|  |   * `Server`: | ||||||
|  |     * Add `KtorApplicationConfigurator.Routing.Static` as solution for [#488](https://github.com/InsanusMokrassar/MicroUtils/issues/488) | ||||||
|  |  | ||||||
|  | ## 0.22.4 | ||||||
|  |  | ||||||
|  | * `Versions`: | ||||||
|  |   * `Exposed`: `0.54.0` -> `0.55.0` | ||||||
|  |   * `SQLite`: `3.46.1.0` -> `3.46.1.2` | ||||||
|  |  | ||||||
|  | ## 0.22.3 | ||||||
|  |  | ||||||
|  | * `Versions`: | ||||||
|  |   * `Serialization`: `1.7.2` -> `1.7.3` | ||||||
|  |   * `Coroutines`: `1.8.1` -> `1.9.0` | ||||||
|  |   * `Compose`: `1.7.0-alpha03` -> `1.7.0-beta02` | ||||||
|  |   * `Koin`: `3.5.6` -> `4.0.0` | ||||||
|  |   * `Okio`: `3.9.0` -> `3.9.1` | ||||||
|  |   * `AndroidFragment`: `1.8.2` -> `1.8.3` | ||||||
|  |   * `androidx.compose.material3:material3` has been replaced with `org.jetbrains.compose.material3:material3` | ||||||
|  | * `Common`: | ||||||
|  |   * `JS`: | ||||||
|  |     * Add several useful extensions | ||||||
|  |   * `Compose`: | ||||||
|  |     * `JS`: | ||||||
|  |       * Add several useful extensions | ||||||
|  |  | ||||||
|  | ## 0.22.2 | ||||||
|  |  | ||||||
|  | * `Versions`: | ||||||
|  |     * `Exposed`: `0.53.0` -> `0.54.0` | ||||||
|  |     * `SQLite`: `3.46.0.1` -> `3.46.1.0` | ||||||
|  |  | ||||||
|  | ## 0.22.1 | ||||||
|  |  | ||||||
|  | * `Versions`: | ||||||
|  |     * `Kotlin`: `2.0.10` -> `2.0.20` | ||||||
|  |     * `Serialization`: `1.7.1` -> `1.7.2` | ||||||
|  |     * `KSLog`: `1.3.5` -> `1.3.6` | ||||||
|  |     * `Compose`: `1.7.0-alpha02` -> `1.7.0-alpha03` | ||||||
|  |     * `Ktor`: `2.3.11` -> `2.3.12` | ||||||
|  |  | ||||||
|  | ## 0.22.0 | ||||||
|  |  | ||||||
|  | **THIS UPDATE CONTAINS BREAKING CHANGES ACCORDING TO UPDATE UP TO KOTLIN 2.0** | ||||||
|  |  | ||||||
|  | * `Versions`: | ||||||
|  |     * `Kotlin`: `1.9.23` -> `2.0.10` | ||||||
|  |     * `Serialization`: `1.6.3` -> `1.7.1` | ||||||
|  |     * `KSLog`: `1.3.4` -> `1.3.5` | ||||||
|  |     * `Compose`: `1.6.2` -> `1.7.0-alpha02` | ||||||
|  |     * `Exposed`: `0.50.1` -> `0.53.0` | ||||||
|  |     * `AndroidAppCompat`: `1.6.1` -> `1.7.0` | ||||||
|  |     * `AndroidFragment`: `1.7.1` -> `1.8.2` | ||||||
|  |  | ||||||
|  | ## 0.21.6 | ||||||
|  |  | ||||||
|  | * `KSP`: | ||||||
|  |     * `Sealed`: | ||||||
|  |         * Fixes in generation | ||||||
|  |  | ||||||
| ## 0.21.5 | ## 0.21.5 | ||||||
|  |  | ||||||
| * `KSP`: | * `KSP`: | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ plugins { | |||||||
|     id "com.android.library" |     id "com.android.library" | ||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "$mppAndroidProjectPresetPath" | apply from: "$mppAndroidProject" | ||||||
|  |  | ||||||
| kotlin { | kotlin { | ||||||
|     sourceSets { |     sourceSets { | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ plugins { | |||||||
|     id "com.android.library" |     id "com.android.library" | ||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "$mppAndroidProjectPresetPath" | apply from: "$mppAndroidProject" | ||||||
|  |  | ||||||
| kotlin { | kotlin { | ||||||
|     sourceSets { |     sourceSets { | ||||||
|   | |||||||
| @@ -3,9 +3,10 @@ plugins { | |||||||
|     id "org.jetbrains.kotlin.plugin.serialization" |     id "org.jetbrains.kotlin.plugin.serialization" | ||||||
|     id "com.android.library" |     id "com.android.library" | ||||||
|     alias(libs.plugins.jb.compose) |     alias(libs.plugins.jb.compose) | ||||||
|  |     alias(libs.plugins.kt.jb.compose) | ||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "$mppProjectWithSerializationAndComposePresetPath" | apply from: "$mppProjectWithSerializationAndCompose" | ||||||
|  |  | ||||||
| kotlin { | kotlin { | ||||||
|     sourceSets { |     sourceSets { | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ plugins { | |||||||
|     id "com.android.library" |     id "com.android.library" | ||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "$mppAndroidProjectPresetPath" | apply from: "$mppAndroidProject" | ||||||
|  |  | ||||||
| kotlin { | kotlin { | ||||||
|     sourceSets { |     sourceSets { | ||||||
|   | |||||||
| @@ -3,15 +3,16 @@ plugins { | |||||||
|     id "org.jetbrains.kotlin.plugin.serialization" |     id "org.jetbrains.kotlin.plugin.serialization" | ||||||
|     id "com.android.library" |     id "com.android.library" | ||||||
|     alias(libs.plugins.jb.compose) |     alias(libs.plugins.jb.compose) | ||||||
|  |     alias(libs.plugins.kt.jb.compose) | ||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "$mppProjectWithSerializationAndComposePresetPath" | apply from: "$mppProjectWithSerializationAndCompose" | ||||||
|  |  | ||||||
| kotlin { | kotlin { | ||||||
|     sourceSets { |     sourceSets { | ||||||
|         androidMain { |         androidMain { | ||||||
|             dependencies { |             dependencies { | ||||||
|                 api libs.android.compose.material3 |                 api libs.jb.compose.material3 | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -29,15 +29,6 @@ allprojects { | |||||||
|         maven { url "https://maven.pkg.jetbrains.space/public/p/compose/dev" } |         maven { url "https://maven.pkg.jetbrains.space/public/p/compose/dev" } | ||||||
|         maven { url "https://nexus.inmo.dev/repository/maven-releases/" } |         maven { url "https://nexus.inmo.dev/repository/maven-releases/" } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // temporal crutch until legacy tests will be stabled or legacy target will be removed |  | ||||||
|     if (it != rootProject.findProject("docs")) { |  | ||||||
|         tasks.whenTaskAdded { task -> |  | ||||||
|             if(task.name == "jsLegacyBrowserTest" || task.name == "jsLegacyNodeTest") { |  | ||||||
|                 task.enabled = false |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "./extensions.gradle" | apply from: "./extensions.gradle" | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ plugins { | |||||||
|     id "com.android.library" |     id "com.android.library" | ||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath" | apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" | ||||||
|  |  | ||||||
| kotlin { | kotlin { | ||||||
|     sourceSets { |     sourceSets { | ||||||
|   | |||||||
| @@ -4,4 +4,4 @@ plugins { | |||||||
|     id "com.android.library" |     id "com.android.library" | ||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath" | apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ plugins { | |||||||
|     id "com.android.library" |     id "com.android.library" | ||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath" | apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" | ||||||
|  |  | ||||||
| kotlin { | kotlin { | ||||||
|     sourceSets { |     sourceSets { | ||||||
|   | |||||||
| @@ -3,9 +3,10 @@ plugins { | |||||||
|     id "org.jetbrains.kotlin.plugin.serialization" |     id "org.jetbrains.kotlin.plugin.serialization" | ||||||
|     id "com.android.library" |     id "com.android.library" | ||||||
|     alias(libs.plugins.jb.compose) |     alias(libs.plugins.jb.compose) | ||||||
|  |     alias(libs.plugins.kt.jb.compose) | ||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "$mppComposeJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath" | apply from: "$mppComposeJvmJsAndroidLinuxMingwLinuxArm64Project" | ||||||
|  |  | ||||||
| kotlin { | kotlin { | ||||||
|     sourceSets { |     sourceSets { | ||||||
|   | |||||||
| @@ -0,0 +1,19 @@ | |||||||
|  | package dev.inmo.micro_utils.common.compose | ||||||
|  |  | ||||||
|  | import org.jetbrains.compose.web.dom.AttrBuilderContext | ||||||
|  | import org.w3c.dom.Element | ||||||
|  |  | ||||||
|  | operator fun <T : Element> AttrBuilderContext<T>?.plus( | ||||||
|  |     other: AttrBuilderContext<T>? | ||||||
|  | ) = when (this) { | ||||||
|  |     null -> other ?: {} | ||||||
|  |     else -> when (other) { | ||||||
|  |         null -> this ?: {} | ||||||
|  |         else -> { | ||||||
|  |             { | ||||||
|  |                 invoke(this) | ||||||
|  |                 other(this) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,22 @@ | |||||||
|  | package dev.inmo.micro_utils.common.compose | ||||||
|  |  | ||||||
|  | import androidx.compose.runtime.Composable | ||||||
|  | import androidx.compose.runtime.DisposableEffect | ||||||
|  | import androidx.compose.runtime.DisposableEffectResult | ||||||
|  | import androidx.compose.runtime.DisposableEffectScope | ||||||
|  | import org.jetbrains.compose.web.attributes.AttrsScope | ||||||
|  | import org.jetbrains.compose.web.dom.ElementScope | ||||||
|  | import org.w3c.dom.Element | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * This function must be called in the context of your tag content. It works like default [AttrsScope.ref], | ||||||
|  |  * but able to be used several times. Uses [DisposableEffect] under the hood | ||||||
|  |  */ | ||||||
|  | @Composable | ||||||
|  | fun <T : Element> ElementScope<T>.ref( | ||||||
|  |     block: DisposableEffectScope.(T) -> DisposableEffectResult | ||||||
|  | ) { | ||||||
|  |     DisposableEffect(0) { | ||||||
|  |         block(scopeElement) | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,11 @@ | |||||||
|  | package dev.inmo.micro_utils.common.compose | ||||||
|  |  | ||||||
|  | import org.jetbrains.compose.web.dom.AttrBuilderContext | ||||||
|  |  | ||||||
|  | fun tagClasses(vararg classnames: String): AttrBuilderContext<*> = { | ||||||
|  |     classes(*classnames) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fun tagId(id: String): AttrBuilderContext<*> = { | ||||||
|  |     id(id) | ||||||
|  | } | ||||||
| @@ -0,0 +1,13 @@ | |||||||
|  | package dev.inmo.micro_utils.common | ||||||
|  |  | ||||||
|  | fun <T> List<T>.breakAsPairs(): List<Pair<T, T>> { | ||||||
|  |     val result = mutableListOf<Pair<T, T>>() | ||||||
|  |  | ||||||
|  |     for (i in 0 until size - 1) { | ||||||
|  |         val first = get(i) | ||||||
|  |         val second = get(i + 1) | ||||||
|  |         result.add(first to second) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return result | ||||||
|  | } | ||||||
| @@ -8,7 +8,7 @@ private inline fun <T> getObject( | |||||||
|     additional: MutableList<T>, |     additional: MutableList<T>, | ||||||
|     iterator: Iterator<T> |     iterator: Iterator<T> | ||||||
| ): T? = when { | ): T? = when { | ||||||
|     additional.isNotEmpty() -> additional.removeFirst() |     additional.isNotEmpty() -> additional.removeAt(0) | ||||||
|     iterator.hasNext() -> iterator.next() |     iterator.hasNext() -> iterator.next() | ||||||
|     else -> null |     else -> null | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,32 @@ | |||||||
|  | package dev.inmo.micro_utils.common | ||||||
|  |  | ||||||
|  | inline fun <T> Sequence<T>.padWith(size: Int, inserter: (Sequence<T>) -> Sequence<T>): Sequence<T> { | ||||||
|  |     var result = this | ||||||
|  |     while (result.count() < size) { | ||||||
|  |         result = inserter(result) | ||||||
|  |     } | ||||||
|  |     return result | ||||||
|  | } | ||||||
|  |  | ||||||
|  | inline fun <T> Sequence<T>.padEnd(size: Int, padBlock: (Int) -> T): Sequence<T> = padWith(size) { it + padBlock(it.count()) } | ||||||
|  |  | ||||||
|  | inline fun <T> Sequence<T>.padEnd(size: Int, o: T) = padEnd(size) { o } | ||||||
|  |  | ||||||
|  | inline fun <T> List<T>.padWith(size: Int, inserter: (List<T>) -> List<T>): List<T> { | ||||||
|  |     var result = this | ||||||
|  |     while (result.size < size) { | ||||||
|  |         result = inserter(result) | ||||||
|  |     } | ||||||
|  |     return result | ||||||
|  | } | ||||||
|  | inline fun <T> List<T>.padEnd(size: Int, padBlock: (Int) -> T): List<T> = asSequence().padEnd(size, padBlock).toList() | ||||||
|  |  | ||||||
|  | inline fun <T> List<T>.padEnd(size: Int, o: T): List<T> = asSequence().padEnd(size, o).toList() | ||||||
|  |  | ||||||
|  | inline fun <T> Sequence<T>.padStart(size: Int, padBlock: (Int) -> T): Sequence<T> = padWith(size) { sequenceOf(padBlock(it.count())) + it } | ||||||
|  |  | ||||||
|  | inline fun <T> Sequence<T>.padStart(size: Int, o: T) = padStart(size) { o } | ||||||
|  |  | ||||||
|  | inline fun <T> List<T>.padStart(size: Int, padBlock: (Int) -> T): List<T> = asSequence().padStart(size, padBlock).toList() | ||||||
|  |  | ||||||
|  | inline fun <T> List<T>.padStart(size: Int, o: T): List<T> = asSequence().padStart(size, o).toList() | ||||||
| @@ -0,0 +1,76 @@ | |||||||
|  | package dev.inmo.micro_utils.common | ||||||
|  |  | ||||||
|  | import kotlinx.serialization.Serializable | ||||||
|  | import kotlin.jvm.JvmInline | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Contains [of1] as main value, where 100% of percentage is when of1 == 1 | ||||||
|  |  * | ||||||
|  |  * @see invoke | ||||||
|  |  * @see partOfTotal | ||||||
|  |  * @see of100 | ||||||
|  |  */ | ||||||
|  | @Serializable | ||||||
|  | @JvmInline | ||||||
|  | value class Percentage private constructor( | ||||||
|  |     /** | ||||||
|  |      * Value of percentage. When it equals to 1, means 100% | ||||||
|  |      */ | ||||||
|  |     val of1: Double | ||||||
|  | ) { | ||||||
|  |     /** | ||||||
|  |      * Same as [of1], but float (using [Double.toFloat]) | ||||||
|  |      */ | ||||||
|  |     val of1Float | ||||||
|  |         get() = of1.toFloat() | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Represent this percentage as common percentage where 100% is 100% | ||||||
|  |      */ | ||||||
|  |     val of100 | ||||||
|  |         get() = of1 * 100 | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Same as [of100], but float (using [Double.toFloat]) | ||||||
|  |      */ | ||||||
|  |     val of100Float | ||||||
|  |         get() = of100.toFloat() | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Same as [of100], but int (using [Double.toInt]) | ||||||
|  |      */ | ||||||
|  |     val of100Int | ||||||
|  |         get() = of100.toInt() | ||||||
|  |  | ||||||
|  |     companion object { | ||||||
|  |         val rangeOfValues = 0.0 .. 1.0 | ||||||
|  |  | ||||||
|  |         val START = Percentage(rangeOfValues.start) | ||||||
|  |         val COMPLETED = Percentage(rangeOfValues.endInclusive) | ||||||
|  |  | ||||||
|  |         operator fun invoke(of1: Double) = Percentage(of1.coerceIn(rangeOfValues)) | ||||||
|  |         operator fun invoke(part: Number, total: Number) = Percentage( | ||||||
|  |             part.toDouble() / total.toDouble() | ||||||
|  |         ) | ||||||
|  |         fun of1(of1: Double) = Percentage(of1 = of1) | ||||||
|  |         fun of100(of100: Double) = Percentage(of1 = of100 / 100) | ||||||
|  |         fun partOfTotal(part: Number, total: Number) = Percentage(part = part, total = total) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | typealias Progress = Percentage | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Will return [this] [Progress] if [Percentage.of1] in `0 .. 1` range | ||||||
|  |  */ | ||||||
|  | fun Progress.ensureStrictOrNull(): Progress? = if (of1 in Percentage.rangeOfValues) this else null | ||||||
|  | /** | ||||||
|  |  * Will return [this] [Progress] if [Percentage.of1] in `0 .. 1` range. Otherwise, will throw error | ||||||
|  |  * [IllegalArgumentException] due to [require] failure | ||||||
|  |  */ | ||||||
|  | fun Progress.ensureStrictOrThrow(): Progress { | ||||||
|  |     require(of1 in Percentage.rangeOfValues) { | ||||||
|  |         "For strict checks value of percentage must be in ${Percentage.rangeOfValues}, but actual value is $of1" | ||||||
|  |     } | ||||||
|  |     return this | ||||||
|  | } | ||||||
| @@ -0,0 +1,80 @@ | |||||||
|  | @file:Suppress( | ||||||
|  |   "RemoveRedundantCallsOfConversionMethods", | ||||||
|  |   "RedundantVisibilityModifier", | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | package dev.inmo.micro_utils.common | ||||||
|  |  | ||||||
|  | import kotlin.Byte | ||||||
|  | import kotlin.Double | ||||||
|  | import kotlin.Float | ||||||
|  | import kotlin.Int | ||||||
|  | import kotlin.Long | ||||||
|  | import kotlin.Short | ||||||
|  | import kotlin.Suppress | ||||||
|  |  | ||||||
|  | public operator fun Percentage.plus(other: Percentage): Percentage = Percentage(of1 + other.of1) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.minus(other: Percentage): Percentage = Percentage(of1 - other.of1) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.plus(i: Byte): Percentage = Percentage((of1 + i).toDouble()) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.minus(i: Byte): Percentage = Percentage((of1 - i).toDouble()) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.times(i: Byte): Percentage = Percentage((of1 * i).toDouble()) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.div(i: Byte): Percentage = Percentage((of1 / i).toDouble()) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.rem(i: Byte): Percentage = Percentage((of1 % i).toDouble()) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.plus(i: Short): Percentage = Percentage((of1 + i).toDouble()) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.minus(i: Short): Percentage = Percentage((of1 - i).toDouble()) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.times(i: Short): Percentage = Percentage((of1 * i).toDouble()) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.div(i: Short): Percentage = Percentage((of1 / i).toDouble()) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.rem(i: Short): Percentage = Percentage((of1 % i).toDouble()) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.plus(i: Int): Percentage = Percentage((of1 + i).toDouble()) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.minus(i: Int): Percentage = Percentage((of1 - i).toDouble()) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.times(i: Int): Percentage = Percentage((of1 * i).toDouble()) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.div(i: Int): Percentage = Percentage((of1 / i).toDouble()) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.rem(i: Int): Percentage = Percentage((of1 % i).toDouble()) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.plus(i: Long): Percentage = Percentage((of1 + i).toDouble()) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.minus(i: Long): Percentage = Percentage((of1 - i).toDouble()) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.times(i: Long): Percentage = Percentage((of1 * i).toDouble()) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.div(i: Long): Percentage = Percentage((of1 / i).toDouble()) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.rem(i: Long): Percentage = Percentage((of1 % i).toDouble()) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.plus(i: Float): Percentage = Percentage((of1 + i).toDouble()) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.minus(i: Float): Percentage = Percentage((of1 - i).toDouble()) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.times(i: Float): Percentage = Percentage((of1 * i).toDouble()) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.div(i: Float): Percentage = Percentage((of1 / i).toDouble()) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.rem(i: Float): Percentage = Percentage((of1 % i).toDouble()) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.plus(i: Double): Percentage = Percentage((of1 + i).toDouble()) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.minus(i: Double): Percentage = Percentage((of1 - i).toDouble()) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.times(i: Double): Percentage = Percentage((of1 * i).toDouble()) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.div(i: Double): Percentage = Percentage((of1 / i).toDouble()) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.rem(i: Double): Percentage = Percentage((of1 % i).toDouble()) | ||||||
|  |  | ||||||
|  | public operator fun Percentage.compareTo(other: Percentage): Int = (of1.compareTo(other.of1)) | ||||||
| @@ -1,37 +0,0 @@ | |||||||
| package dev.inmo.micro_utils.common |  | ||||||
|  |  | ||||||
| import kotlinx.serialization.Serializable |  | ||||||
| import kotlin.jvm.JvmInline |  | ||||||
|  |  | ||||||
| @Serializable |  | ||||||
| @JvmInline |  | ||||||
| value class Progress private constructor( |  | ||||||
|     val of1: Double |  | ||||||
| ) { |  | ||||||
|     val of1Float |  | ||||||
|         get() = of1.toFloat() |  | ||||||
|     val of100 |  | ||||||
|         get() = of1 * 100 |  | ||||||
|     val of100Float |  | ||||||
|         get() = of100.toFloat() |  | ||||||
|     val of100Int |  | ||||||
|         get() = of100.toInt() |  | ||||||
|  |  | ||||||
|     init { |  | ||||||
|         require(of1 in rangeOfValues) { |  | ||||||
|             "Progress main value should be in $rangeOfValues, but incoming value is $of1" |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     companion object { |  | ||||||
|         val rangeOfValues = 0.0 .. 1.0 |  | ||||||
|  |  | ||||||
|         val START = Progress(rangeOfValues.start) |  | ||||||
|         val COMPLETED = Progress(rangeOfValues.endInclusive) |  | ||||||
|  |  | ||||||
|         operator fun invoke(of1: Double) = Progress(of1.coerceIn(rangeOfValues)) |  | ||||||
|         operator fun invoke(part: Number, total: Number) = Progress( |  | ||||||
|             part.toDouble() / total.toDouble() |  | ||||||
|         ) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,80 +0,0 @@ | |||||||
| @file:Suppress( |  | ||||||
|   "RemoveRedundantCallsOfConversionMethods", |  | ||||||
|   "RedundantVisibilityModifier", |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| package dev.inmo.micro_utils.common |  | ||||||
|  |  | ||||||
| import kotlin.Byte |  | ||||||
| import kotlin.Double |  | ||||||
| import kotlin.Float |  | ||||||
| import kotlin.Int |  | ||||||
| import kotlin.Long |  | ||||||
| import kotlin.Short |  | ||||||
| import kotlin.Suppress |  | ||||||
|  |  | ||||||
| public operator fun Progress.plus(other: Progress): Progress = Progress(of1 + other.of1) |  | ||||||
|  |  | ||||||
| public operator fun Progress.minus(other: Progress): Progress = Progress(of1 - other.of1) |  | ||||||
|  |  | ||||||
| public operator fun Progress.plus(i: Byte): Progress = Progress((of1 + i).toDouble()) |  | ||||||
|  |  | ||||||
| public operator fun Progress.minus(i: Byte): Progress = Progress((of1 - i).toDouble()) |  | ||||||
|  |  | ||||||
| public operator fun Progress.times(i: Byte): Progress = Progress((of1 * i).toDouble()) |  | ||||||
|  |  | ||||||
| public operator fun Progress.div(i: Byte): Progress = Progress((of1 / i).toDouble()) |  | ||||||
|  |  | ||||||
| public operator fun Progress.rem(i: Byte): Progress = Progress((of1 % i).toDouble()) |  | ||||||
|  |  | ||||||
| public operator fun Progress.plus(i: Short): Progress = Progress((of1 + i).toDouble()) |  | ||||||
|  |  | ||||||
| public operator fun Progress.minus(i: Short): Progress = Progress((of1 - i).toDouble()) |  | ||||||
|  |  | ||||||
| public operator fun Progress.times(i: Short): Progress = Progress((of1 * i).toDouble()) |  | ||||||
|  |  | ||||||
| public operator fun Progress.div(i: Short): Progress = Progress((of1 / i).toDouble()) |  | ||||||
|  |  | ||||||
| public operator fun Progress.rem(i: Short): Progress = Progress((of1 % i).toDouble()) |  | ||||||
|  |  | ||||||
| public operator fun Progress.plus(i: Int): Progress = Progress((of1 + i).toDouble()) |  | ||||||
|  |  | ||||||
| public operator fun Progress.minus(i: Int): Progress = Progress((of1 - i).toDouble()) |  | ||||||
|  |  | ||||||
| public operator fun Progress.times(i: Int): Progress = Progress((of1 * i).toDouble()) |  | ||||||
|  |  | ||||||
| public operator fun Progress.div(i: Int): Progress = Progress((of1 / i).toDouble()) |  | ||||||
|  |  | ||||||
| public operator fun Progress.rem(i: Int): Progress = Progress((of1 % i).toDouble()) |  | ||||||
|  |  | ||||||
| public operator fun Progress.plus(i: Long): Progress = Progress((of1 + i).toDouble()) |  | ||||||
|  |  | ||||||
| public operator fun Progress.minus(i: Long): Progress = Progress((of1 - i).toDouble()) |  | ||||||
|  |  | ||||||
| public operator fun Progress.times(i: Long): Progress = Progress((of1 * i).toDouble()) |  | ||||||
|  |  | ||||||
| public operator fun Progress.div(i: Long): Progress = Progress((of1 / i).toDouble()) |  | ||||||
|  |  | ||||||
| public operator fun Progress.rem(i: Long): Progress = Progress((of1 % i).toDouble()) |  | ||||||
|  |  | ||||||
| public operator fun Progress.plus(i: Float): Progress = Progress((of1 + i).toDouble()) |  | ||||||
|  |  | ||||||
| public operator fun Progress.minus(i: Float): Progress = Progress((of1 - i).toDouble()) |  | ||||||
|  |  | ||||||
| public operator fun Progress.times(i: Float): Progress = Progress((of1 * i).toDouble()) |  | ||||||
|  |  | ||||||
| public operator fun Progress.div(i: Float): Progress = Progress((of1 / i).toDouble()) |  | ||||||
|  |  | ||||||
| public operator fun Progress.rem(i: Float): Progress = Progress((of1 % i).toDouble()) |  | ||||||
|  |  | ||||||
| public operator fun Progress.plus(i: Double): Progress = Progress((of1 + i).toDouble()) |  | ||||||
|  |  | ||||||
| public operator fun Progress.minus(i: Double): Progress = Progress((of1 - i).toDouble()) |  | ||||||
|  |  | ||||||
| public operator fun Progress.times(i: Double): Progress = Progress((of1 * i).toDouble()) |  | ||||||
|  |  | ||||||
| public operator fun Progress.div(i: Double): Progress = Progress((of1 / i).toDouble()) |  | ||||||
|  |  | ||||||
| public operator fun Progress.rem(i: Double): Progress = Progress((of1 % i).toDouble()) |  | ||||||
|  |  | ||||||
| public operator fun Progress.compareTo(other: Progress): Int = (of1 - other.of1).toInt() |  | ||||||
| @@ -0,0 +1,5 @@ | |||||||
|  | package dev.inmo.micro_utils.common | ||||||
|  |  | ||||||
|  | fun <T> Iterable<T>.withReplacedAt(i: Int, block: (T) -> T): List<T> = take(i) + block(elementAt(i)) + drop(i + 1) | ||||||
|  | fun <T> Iterable<T>.withReplaced(t: T, block: (T) -> T): List<T> = withReplacedAt(indexOf(t), block) | ||||||
|  |  | ||||||
| @@ -0,0 +1,29 @@ | |||||||
|  | package dev.inmo.micro_utils.common | ||||||
|  |  | ||||||
|  | import kotlin.test.Test | ||||||
|  | import kotlin.test.assertEquals | ||||||
|  |  | ||||||
|  | class PercentageTests { | ||||||
|  |     @Test | ||||||
|  |     fun testCompareTo() { | ||||||
|  |         val step = 0.01 | ||||||
|  |  | ||||||
|  |         var i = Percentage.START.of1 | ||||||
|  |         while (i <= Percentage.COMPLETED.of1) { | ||||||
|  |             val percentageI = Percentage(i) | ||||||
|  |  | ||||||
|  |             var j = Percentage.START.of1 | ||||||
|  |             while (j <= Percentage.COMPLETED.of1) { | ||||||
|  |                 val percentageJ = Percentage(j) | ||||||
|  |  | ||||||
|  |                 assertEquals(percentageI.of1.compareTo(percentageJ.of1), percentageI.compareTo(percentageJ)) | ||||||
|  |                 assertEquals(percentageI.of1 > percentageJ.of1, percentageI > percentageJ) | ||||||
|  |                 assertEquals(percentageI.of1 < percentageJ.of1, percentageI < percentageJ) | ||||||
|  |  | ||||||
|  |                 j += step | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             i += step | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,21 @@ | |||||||
|  | package dev.inmo.micro_utils.common | ||||||
|  |  | ||||||
|  | import kotlin.test.Test | ||||||
|  | import kotlin.test.assertEquals | ||||||
|  |  | ||||||
|  | class WithReplacedTest { | ||||||
|  |     @Test | ||||||
|  |     fun testReplaced() { | ||||||
|  |         val data = 0 until 10 | ||||||
|  |         val testData = Int.MAX_VALUE | ||||||
|  |  | ||||||
|  |         for (i in 0 until data.last) { | ||||||
|  |             val withReplaced = data.withReplacedAt(i) { | ||||||
|  |                 testData | ||||||
|  |             } | ||||||
|  |             val dataAsMutableList = data.toMutableList() | ||||||
|  |             dataAsMutableList[i] = testData | ||||||
|  |             assertEquals(withReplaced, dataAsMutableList.toList()) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,13 @@ | |||||||
|  | package dev.inmo.micro_utils.common | ||||||
|  |  | ||||||
|  | import kotlinx.browser.window | ||||||
|  |  | ||||||
|  | fun copyToClipboard(text: String): Boolean { | ||||||
|  |     return runCatching { | ||||||
|  |         window.navigator.clipboard.writeText( | ||||||
|  |             text | ||||||
|  |         ) | ||||||
|  |     }.onFailure { | ||||||
|  |         it.printStackTrace() | ||||||
|  |     }.isSuccess | ||||||
|  | } | ||||||
| @@ -0,0 +1,29 @@ | |||||||
|  | package dev.inmo.micro_utils.common | ||||||
|  |  | ||||||
|  | import kotlinx.browser.window | ||||||
|  | import org.w3c.files.Blob | ||||||
|  | import org.w3c.files.BlobPropertyBag | ||||||
|  | import kotlin.js.json | ||||||
|  |  | ||||||
|  | external class ClipboardItem(data: dynamic) | ||||||
|  |  | ||||||
|  | inline fun Blob.convertToClipboardItem(): ClipboardItem { | ||||||
|  |     val itemData: dynamic = json(this.type to this) | ||||||
|  |     return ClipboardItem(itemData) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | suspend fun copyImageURLToClipboard(imageUrl: String): Boolean { | ||||||
|  |     return runCatching { | ||||||
|  |         val response = window.fetch(imageUrl).await() | ||||||
|  |         val blob = response.blob().await() | ||||||
|  |         val data = arrayOf( | ||||||
|  |             Blob( | ||||||
|  |                 arrayOf(blob), | ||||||
|  |                 BlobPropertyBag("image/png") | ||||||
|  |             ).convertToClipboardItem() | ||||||
|  |         ).asDynamic() | ||||||
|  |         window.navigator.clipboard.write(data) | ||||||
|  |     }.onFailure { | ||||||
|  |         it.printStackTrace() | ||||||
|  |     }.isSuccess | ||||||
|  | } | ||||||
| @@ -4,7 +4,7 @@ plugins { | |||||||
|     id "com.android.library" |     id "com.android.library" | ||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath" | apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" | ||||||
|  |  | ||||||
| kotlin { | kotlin { | ||||||
|     sourceSets { |     sourceSets { | ||||||
|   | |||||||
| @@ -3,9 +3,10 @@ plugins { | |||||||
|     id "org.jetbrains.kotlin.plugin.serialization" |     id "org.jetbrains.kotlin.plugin.serialization" | ||||||
|     id "com.android.library" |     id "com.android.library" | ||||||
|     alias(libs.plugins.jb.compose) |     alias(libs.plugins.jb.compose) | ||||||
|  |     alias(libs.plugins.kt.jb.compose) | ||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "$mppComposeJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath" | apply from: "$mppComposeJvmJsAndroidLinuxMingwLinuxArm64Project" | ||||||
|  |  | ||||||
| kotlin { | kotlin { | ||||||
|     sourceSets { |     sourceSets { | ||||||
|   | |||||||
| @@ -0,0 +1,66 @@ | |||||||
|  | package dev.inmo.micro_utils.coroutines.compose | ||||||
|  |  | ||||||
|  | import androidx.compose.runtime.Composable | ||||||
|  | import androidx.compose.runtime.collectAsState | ||||||
|  | import androidx.compose.runtime.remember | ||||||
|  | import dev.inmo.micro_utils.coroutines.SpecialMutableStateFlow | ||||||
|  | import kotlinx.coroutines.flow.StateFlow | ||||||
|  | import kotlinx.coroutines.flow.asStateFlow | ||||||
|  | import kotlinx.coroutines.flow.debounce | ||||||
|  | import org.jetbrains.compose.web.css.CSSRulesHolder | ||||||
|  | import org.jetbrains.compose.web.css.Style | ||||||
|  | import org.jetbrains.compose.web.css.StyleSheet | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Aggregator of Compose CSS StyleSheet. Allowing to add [StyleSheet] in it and draw it in one place without requiring | ||||||
|  |  * to add `Style(stylesheet)` on every compose function call | ||||||
|  |  */ | ||||||
|  | object StyleSheetsAggregator { | ||||||
|  |     private val _stylesFlow = SpecialMutableStateFlow<Set<CSSRulesHolder>>(emptySet()) | ||||||
|  |     val stylesFlow: StateFlow<Set<CSSRulesHolder>> = _stylesFlow.asStateFlow() | ||||||
|  |  | ||||||
|  |     @Composable | ||||||
|  |     fun draw() { | ||||||
|  |         _stylesFlow.debounce(13L).collectAsState(emptySet()).value.forEach { | ||||||
|  |             Style(it) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Adding [styleSheet] into the [Set] of included stylesheets. If you called [enableStyleSheetsAggregator], | ||||||
|  |      * new styles will be enabled in the document | ||||||
|  |      */ | ||||||
|  |     fun addStyleSheet(styleSheet: CSSRulesHolder) { | ||||||
|  |         _stylesFlow.value += styleSheet | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Removed [styleSheet] into the [Set] of included stylesheets | ||||||
|  |      */ | ||||||
|  |     fun removeStyleSheet(styleSheet: CSSRulesHolder) { | ||||||
|  |         _stylesFlow.value -= styleSheet | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Drawing [StyleSheetsAggregator] in place. You may pass [Set] of [CSSRulesHolder]/[StyleSheet]s as preset of styles | ||||||
|  |  */ | ||||||
|  | @Composable | ||||||
|  | fun enableStyleSheetsAggregator( | ||||||
|  |     stylesPreset: Set<CSSRulesHolder> = emptySet(), | ||||||
|  | ) { | ||||||
|  |     remember { | ||||||
|  |         stylesPreset.forEach { | ||||||
|  |             StyleSheetsAggregator.addStyleSheet(it) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     StyleSheetsAggregator.draw() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Will include [this] [CSSRulesHolder]/[StyleSheet] in the [StyleSheetsAggregator] using its | ||||||
|  |  * [StyleSheetsAggregator.addStyleSheet] | ||||||
|  |  */ | ||||||
|  | fun CSSRulesHolder.includeInStyleSheetsAggregator() { | ||||||
|  |     StyleSheetsAggregator.addStyleSheet(this) | ||||||
|  | } | ||||||
| @@ -68,9 +68,9 @@ class AccumulatorFlow<T>( | |||||||
|     override suspend fun collectSafely(collector: FlowCollector<T>) { |     override suspend fun collectSafely(collector: FlowCollector<T>) { | ||||||
|         val channel = Channel<T>(Channel.UNLIMITED, BufferOverflow.SUSPEND) |         val channel = Channel<T>(Channel.UNLIMITED, BufferOverflow.SUSPEND) | ||||||
|         steps.send(SubscribeAccumulatorFlowStep(channel)) |         steps.send(SubscribeAccumulatorFlowStep(channel)) | ||||||
|         val result = runCatchingSafely { |         val result = runCatching { | ||||||
|             for (data in channel) { |             for (data in channel) { | ||||||
|                 val emitResult = runCatchingSafely { |                 val emitResult = runCatching { | ||||||
|                     collector.emit(data) |                     collector.emit(data) | ||||||
|                 } |                 } | ||||||
|                 if (emitResult.isSuccess || emitResult.exceptionOrNull() is CancellationException) { |                 if (emitResult.isSuccess || emitResult.exceptionOrNull() is CancellationException) { | ||||||
|   | |||||||
| @@ -0,0 +1,40 @@ | |||||||
|  | package dev.inmo.micro_utils.coroutines | ||||||
|  |  | ||||||
|  | import kotlinx.coroutines.Job | ||||||
|  | import kotlinx.coroutines.async | ||||||
|  | import kotlinx.coroutines.delay | ||||||
|  | import kotlinx.coroutines.flow.* | ||||||
|  | import kotlinx.coroutines.sync.Mutex | ||||||
|  | import kotlinx.coroutines.sync.withLock | ||||||
|  | import kotlin.jvm.JvmInline | ||||||
|  | import kotlin.time.Duration | ||||||
|  |  | ||||||
|  | @JvmInline | ||||||
|  | private value class DebouncedByData<T>( | ||||||
|  |     val millisToData: Pair<Long, T> | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | fun <T> Flow<T>.debouncedBy(timeout: (T) -> Long, markerFactory: (T) -> Any?): Flow<T> = channelFlow { | ||||||
|  |     val jobs = mutableMapOf<Any?, Job>() | ||||||
|  |     val mutex = Mutex() | ||||||
|  |     subscribe(this) { | ||||||
|  |         mutex.withLock { | ||||||
|  |             val marker = markerFactory(it) | ||||||
|  |             lateinit var job: Job | ||||||
|  |             job = async { | ||||||
|  |                 delay(timeout(it)) | ||||||
|  |                 mutex.withLock { | ||||||
|  |                     if (jobs[marker] === job) { | ||||||
|  |                         this@channelFlow.send(it) | ||||||
|  |                         jobs.remove(marker) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             jobs[marker] ?.cancel() | ||||||
|  |             jobs[marker] = job | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fun <T> Flow<T>.debouncedBy(timeout: Long, markerFactory: (T) -> Any?): Flow<T> = debouncedBy({ timeout }, markerFactory) | ||||||
|  | fun <T> Flow<T>.debouncedBy(timeout: Duration, markerFactory: (T) -> Any?): Flow<T> = debouncedBy({ timeout.inWholeMilliseconds }, markerFactory) | ||||||
| @@ -13,7 +13,7 @@ import kotlin.contracts.contract | |||||||
|  * * [unlockWrite] will just unlock [writeMutex] |  * * [unlockWrite] will just unlock [writeMutex] | ||||||
|  */ |  */ | ||||||
| class SmartRWLocker(private val readPermits: Int = Int.MAX_VALUE, writeIsLocked: Boolean = false) { | class SmartRWLocker(private val readPermits: Int = Int.MAX_VALUE, writeIsLocked: Boolean = false) { | ||||||
|     private val _readSemaphore = SmartSemaphore.Mutable(permits = readPermits, acquiredPermits = 0) |     private val _readSemaphore = SmartSemaphore.Mutable(permits = readPermits, acquiredPermits = if (writeIsLocked) readPermits else 0) | ||||||
|     private val _writeMutex = SmartMutex.Mutable(locked = writeIsLocked) |     private val _writeMutex = SmartMutex.Mutable(locked = writeIsLocked) | ||||||
|  |  | ||||||
|     val readSemaphore: SmartSemaphore.Immutable = _readSemaphore.immutable() |     val readSemaphore: SmartSemaphore.Immutable = _readSemaphore.immutable() | ||||||
|   | |||||||
							
								
								
									
										42
									
								
								coroutines/src/commonTest/kotlin/DebouncedByTests.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								coroutines/src/commonTest/kotlin/DebouncedByTests.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | import dev.inmo.micro_utils.coroutines.debouncedBy | ||||||
|  | import kotlinx.coroutines.flow.asFlow | ||||||
|  | import kotlinx.coroutines.test.runTest | ||||||
|  | import kotlin.test.Test | ||||||
|  | import kotlin.test.assertEquals | ||||||
|  | import kotlin.test.assertTrue | ||||||
|  |  | ||||||
|  | class DebouncedByTests { | ||||||
|  |     @Test | ||||||
|  |     fun testThatParallelDebouncingWorksCorrectly() = runTest { | ||||||
|  |         val dataToMarkerFactories = listOf( | ||||||
|  |             1 to 0, | ||||||
|  |             2 to 1, | ||||||
|  |             3 to 2, | ||||||
|  |             4 to 0, | ||||||
|  |             5 to 1, | ||||||
|  |             6 to 2, | ||||||
|  |             7 to 0, | ||||||
|  |             8 to 1, | ||||||
|  |             9 to 2, | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         val collected = mutableListOf<Int>() | ||||||
|  |  | ||||||
|  |         dataToMarkerFactories.asFlow().debouncedBy(10L) { | ||||||
|  |             it.second | ||||||
|  |         }.collect { | ||||||
|  |             when (it.second) { | ||||||
|  |                 0 -> assertEquals(7, it.first) | ||||||
|  |                 1 -> assertEquals(8, it.first) | ||||||
|  |                 2 -> assertEquals(9, it.first) | ||||||
|  |                 else -> error("wtf") | ||||||
|  |             } | ||||||
|  |             collected.add(it.first) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         val expectedList = listOf(7, 8, 9) | ||||||
|  |         assertEquals(expectedList, collected) | ||||||
|  |         assertTrue { collected.containsAll(expectedList) } | ||||||
|  |         assertTrue { expectedList.containsAll(collected) } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -4,7 +4,7 @@ plugins { | |||||||
|     id "com.android.library" |     id "com.android.library" | ||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath" | apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" | ||||||
|  |  | ||||||
| kotlin { | kotlin { | ||||||
|     sourceSets { |     sourceSets { | ||||||
|   | |||||||
| @@ -130,4 +130,4 @@ tasks.dokkaHtml { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "$defaultAndroidSettingsPresetPath" | apply from: "$defaultAndroidSettings" | ||||||
|   | |||||||
| @@ -1,43 +1,32 @@ | |||||||
| allprojects { | File templatesFolder = new File("$rootProject.projectDir.absolutePath${File.separatorChar}gradle${File.separatorChar}templates") | ||||||
|     ext { |  | ||||||
|         projectByName = { name -> |  | ||||||
|             for (subproject in rootProject.subprojects) { |  | ||||||
|                 if (subproject.name == name) { |  | ||||||
|                     return subproject |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             return null |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         internalProject = { name -> | Map properties = new HashMap<String, String>() | ||||||
| //            if (releaseMode) { |  | ||||||
| //                "$group:$name:$version" |  | ||||||
| //            } else { |  | ||||||
| //                projectByName("$name") |  | ||||||
| //            } |  | ||||||
|             projectByName("$name") |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         releaseMode = (project.hasProperty('RELEASE_MODE') && project.property('RELEASE_MODE') == "true") || System.getenv('RELEASE_MODE') == "true" | if (templatesFolder.exists() && templatesFolder.isDirectory()) { | ||||||
| //        String compilerPluginVersionFromProperties = (String) project.properties["compose.kotlinCompilerPluginVersion"] |     templatesFolder.listFiles().each { | ||||||
| //        String compilerPluginVersionFromLibrariesVersions = libs.versions.compose.kotlin.get() |         properties[it.name - ".gradle"] = it.absolutePath | ||||||
| //        composePluginKotlinVersion = compilerPluginVersionFromProperties |     } | ||||||
| //        if (compilerPluginVersionFromProperties == null) { | } | ||||||
| //            composePluginKotlinVersion = compilerPluginVersionFromLibrariesVersions |  | ||||||
| //        } | properties["projectByName"] = { name -> | ||||||
|  |     for (subproject in rootProject.subprojects) { | ||||||
|         mppProjectWithSerializationPresetPath = "${rootProject.projectDir.absolutePath}/mppJvmJsAndroidProject.gradle" |         if (subproject.name == name) { | ||||||
|         mppProjectWithSerializationAndComposePresetPath = "${rootProject.projectDir.absolutePath}/mppProjectWithSerializationAndCompose.gradle" |             return subproject | ||||||
|         mppJavaProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppJavaProject.gradle" |         } | ||||||
|         mppJvmJsLinuxMingwProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppJvmJsLinuxMingwProject.gradle" |     } | ||||||
|         mppJvmJsLinuxMingwLinuxArm64ProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppJvmJsLinuxMingwLinuxArm64Project.gradle" |     return null | ||||||
|         mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppJvmJsAndroidLinuxMingwLinuxArm64Project.gradle" | } | ||||||
|         mppComposeJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppComposeJvmJsAndroidLinuxMingwLinuxArm64Project.gradle" |  | ||||||
|         mppAndroidProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppAndroidProject.gradle" | properties["internalProject"] = { name -> | ||||||
|  |     projectByName("$name") | ||||||
|         defaultAndroidSettingsPresetPath = "${rootProject.projectDir.absolutePath}/defaultAndroidSettings.gradle" | } | ||||||
|  |  | ||||||
|         publishGradlePath = "${rootProject.projectDir.absolutePath}/publish.gradle" | allprojects { | ||||||
|         publishJvmOnlyPath = "${rootProject.projectDir.absolutePath}/jvm.publish.gradle" |     ext { | ||||||
|  |         releaseMode = (project.hasProperty('RELEASE_MODE') && project.property('RELEASE_MODE') == "true") || System.getenv('RELEASE_MODE') == "true" | ||||||
|  |  | ||||||
|  |         properties.forEach { k, v -> | ||||||
|  |             it[k] = v | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ plugins { | |||||||
|     id "com.android.library" |     id "com.android.library" | ||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath" | apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" | ||||||
|  |  | ||||||
| kotlin { | kotlin { | ||||||
|     sourceSets { |     sourceSets { | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ plugins { | |||||||
|     id "com.android.library" |     id "com.android.library" | ||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath" | apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" | ||||||
|  |  | ||||||
| kotlin { | kotlin { | ||||||
|     sourceSets { |     sourceSets { | ||||||
|   | |||||||
| @@ -15,5 +15,5 @@ crypto_js_version=4.1.1 | |||||||
| # Project data | # Project data | ||||||
|  |  | ||||||
| group=dev.inmo | group=dev.inmo | ||||||
| version=0.21.5 | version=0.24.2 | ||||||
| android_code_version=264 | android_code_version=281 | ||||||
|   | |||||||
| @@ -1,45 +1,47 @@ | |||||||
| [versions] | [versions] | ||||||
|  |  | ||||||
| kt = "1.9.23" | kt = "2.1.0" | ||||||
| kt-serialization = "1.6.3" | kt-serialization = "1.8.0" | ||||||
| kt-coroutines = "1.8.1" | kt-coroutines = "1.10.1" | ||||||
|  |  | ||||||
| kslog = "1.3.4" | kslog = "1.4.0" | ||||||
|  |  | ||||||
| jb-compose = "1.6.2" | jb-compose = "1.7.3" | ||||||
| jb-exposed = "0.50.1" | jb-exposed = "0.58.0" | ||||||
| jb-dokka = "1.9.20" | jb-dokka = "2.0.0" | ||||||
|  |  | ||||||
|  | sqlite = "3.47.2.0" | ||||||
|  |  | ||||||
| korlibs = "5.4.0" | korlibs = "5.4.0" | ||||||
| uuid = "0.8.4" | uuid = "0.8.4" | ||||||
|  |  | ||||||
| ktor = "2.3.11" | ktor = "3.0.3" | ||||||
|  |  | ||||||
| gh-release = "2.5.2" | gh-release = "2.5.2" | ||||||
|  |  | ||||||
| koin = "3.5.6" | koin = "4.0.1" | ||||||
|  |  | ||||||
| okio = "3.9.0" | okio = "3.10.2" | ||||||
|  |  | ||||||
| ksp = "1.9.23-1.0.20" | ksp = "2.1.0-1.0.29" | ||||||
| kotlin-poet = "1.16.0" | kotlin-poet = "1.18.1" | ||||||
|  |  | ||||||
| versions = "0.51.0" | versions = "0.51.0" | ||||||
|  |  | ||||||
| android-gradle = "8.2.0" | android-gradle = "8.2.2" | ||||||
| dexcount = "4.0.0" | dexcount = "4.0.0" | ||||||
|  |  | ||||||
| android-coreKtx = "1.13.1" | android-coreKtx = "1.15.0" | ||||||
| android-recyclerView = "1.3.2" | android-recyclerView = "1.3.2" | ||||||
| android-appCompat = "1.6.1" | android-appCompat = "1.7.0" | ||||||
| android-fragment = "1.7.1" | android-fragment = "1.8.5" | ||||||
| android-espresso = "3.5.1" | android-espresso = "3.6.1" | ||||||
| android-test = "1.1.5" | android-test = "1.2.1" | ||||||
| android-compose-material3 = "1.2.1" | android-compose-material3 = "1.3.0" | ||||||
|  |  | ||||||
| android-props-minSdk = "21" | android-props-minSdk = "21" | ||||||
| android-props-compileSdk = "34" | android-props-compileSdk = "35" | ||||||
| android-props-buildTools = "34.0.0" | android-props-buildTools = "35.0.0" | ||||||
|  |  | ||||||
| [libraries] | [libraries] | ||||||
|  |  | ||||||
| @@ -52,6 +54,7 @@ kt-serialization-cbor = { module = "org.jetbrains.kotlinx:kotlinx-serialization- | |||||||
| kt-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kt-coroutines" } | kt-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kt-coroutines" } | ||||||
| kt-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kt-coroutines" } | kt-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kt-coroutines" } | ||||||
| kt-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kt-coroutines" } | kt-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kt-coroutines" } | ||||||
|  | kt-coroutines-debug = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-debug", version.ref = "kt-coroutines" } | ||||||
|  |  | ||||||
|  |  | ||||||
| ktor-io = { module = "io.ktor:ktor-io", version.ref = "ktor" } | ktor-io = { module = "io.ktor:ktor-io", version.ref = "ktor" } | ||||||
| @@ -79,12 +82,14 @@ koin = { module = "io.insert-koin:koin-core", version.ref = "koin" } | |||||||
|  |  | ||||||
|  |  | ||||||
| jb-exposed = { module = "org.jetbrains.exposed:exposed-core", version.ref = "jb-exposed" } | jb-exposed = { module = "org.jetbrains.exposed:exposed-core", version.ref = "jb-exposed" } | ||||||
|  | jb-exposed-jdbc = { module = "org.jetbrains.exposed:exposed-jdbc", version.ref = "jb-exposed" } | ||||||
|  | sqlite = { module = "org.xerial:sqlite-jdbc", version.ref = "sqlite" } | ||||||
|  |  | ||||||
|  | jb-compose-material3 = { module = "org.jetbrains.compose.material3:material3", version.ref = "jb-compose" } | ||||||
|  |  | ||||||
| android-coreKtx = { module = "androidx.core:core-ktx", version.ref = "android-coreKtx" } | android-coreKtx = { module = "androidx.core:core-ktx", version.ref = "android-coreKtx" } | ||||||
| android-recyclerView = { module = "androidx.recyclerview:recyclerview", version.ref = "android-recyclerView" } | android-recyclerView = { module = "androidx.recyclerview:recyclerview", version.ref = "android-recyclerView" } | ||||||
| android-appCompat-resources = { module = "androidx.appcompat:appcompat-resources", version.ref = "android-appCompat" } | android-appCompat-resources = { module = "androidx.appcompat:appcompat-resources", version.ref = "android-appCompat" } | ||||||
| android-compose-material3 = { module = "androidx.compose.material3:material3", version.ref = "android-compose-material3" } |  | ||||||
| android-fragment = { module = "androidx.fragment:fragment", version.ref = "android-fragment" } | android-fragment = { module = "androidx.fragment:fragment", version.ref = "android-fragment" } | ||||||
| android-espresso = { module = "androidx.test.espresso:espresso-core", version.ref = "android-espresso" } | android-espresso = { module = "androidx.test.espresso:espresso-core", version.ref = "android-espresso" } | ||||||
| android-test-junit = { module = "androidx.test.ext:junit", version.ref = "android-test" } | android-test-junit = { module = "androidx.test.ext:junit", version.ref = "android-test" } | ||||||
| @@ -113,5 +118,6 @@ buildscript-android-dexcount = { module = "com.getkeepsafe.dexcount:dexcount-gra | |||||||
| [plugins] | [plugins] | ||||||
|  |  | ||||||
| jb-compose = { id = "org.jetbrains.compose", version.ref = "jb-compose" } | jb-compose = { id = "org.jetbrains.compose", version.ref = "jb-compose" } | ||||||
|  | kt-jb-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kt" } | ||||||
|  |  | ||||||
| versions = { id = "com.github.ben-manes.versions", version.ref = "versions" } | versions = { id = "com.github.ben-manes.versions", version.ref = "versions" } | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ android { | |||||||
| 
 | 
 | ||||||
|     defaultConfig { |     defaultConfig { | ||||||
|         minSdkVersion libs.versions.android.props.minSdk.get().toInteger() |         minSdkVersion libs.versions.android.props.minSdk.get().toInteger() | ||||||
|  |         compileSdkVersion libs.versions.android.props.compileSdk.get().toInteger() | ||||||
|         targetSdkVersion libs.versions.android.props.compileSdk.get().toInteger() |         targetSdkVersion libs.versions.android.props.compileSdk.get().toInteger() | ||||||
|         versionCode "${android_code_version}".toInteger() |         versionCode "${android_code_version}".toInteger() | ||||||
|         versionName "$version" |         versionName "$version" | ||||||
| @@ -1,7 +1,7 @@ | |||||||
| project.version = "$version" | project.version = "$version" | ||||||
| project.group = "$group" | project.group = "$group" | ||||||
| 
 | 
 | ||||||
| apply from: "$publishGradlePath" | apply from: "$publish" | ||||||
| 
 | 
 | ||||||
| kotlin { | kotlin { | ||||||
|     androidTarget { |     androidTarget { | ||||||
| @@ -29,7 +29,7 @@ kotlin { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| apply from: "$defaultAndroidSettingsPresetPath" | apply from: "$defaultAndroidSettings" | ||||||
| 
 | 
 | ||||||
| java { | java { | ||||||
|     sourceCompatibility = JavaVersion.VERSION_17 |     sourceCompatibility = JavaVersion.VERSION_17 | ||||||
| @@ -1,7 +1,7 @@ | |||||||
| project.version = "$version" | project.version = "$version" | ||||||
| project.group = "$group" | project.group = "$group" | ||||||
| 
 | 
 | ||||||
| apply from: "$publishGradlePath" | apply from: "$publish" | ||||||
| 
 | 
 | ||||||
| kotlin { | kotlin { | ||||||
|     jvm { |     jvm { | ||||||
| @@ -12,8 +12,20 @@ kotlin { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     js (IR) { |     js (IR) { | ||||||
|         browser() |         browser { | ||||||
|         nodejs() |             testTask { | ||||||
|  |                 useMocha { | ||||||
|  |                     timeout = "60000" | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         nodejs { | ||||||
|  |             testTask { | ||||||
|  |                 useMocha { | ||||||
|  |                     timeout = "60000" | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|     androidTarget { |     androidTarget { | ||||||
|         publishAllLibraryVariants() |         publishAllLibraryVariants() | ||||||
| @@ -77,7 +89,6 @@ kotlin { | |||||||
|         jsTest { |         jsTest { | ||||||
|             dependencies { |             dependencies { | ||||||
|                 implementation kotlin('test-js') |                 implementation kotlin('test-js') | ||||||
|                 implementation kotlin('test-junit') |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         nativeMain.dependsOn commonMain |         nativeMain.dependsOn commonMain | ||||||
| @@ -89,7 +100,7 @@ kotlin { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| apply from: "$defaultAndroidSettingsPresetPath" | apply from: "$defaultAndroidSettings" | ||||||
| 
 | 
 | ||||||
| java { | java { | ||||||
|     sourceCompatibility = JavaVersion.VERSION_17 |     sourceCompatibility = JavaVersion.VERSION_17 | ||||||
| @@ -1,7 +1,7 @@ | |||||||
| project.version = "$version" | project.version = "$version" | ||||||
| project.group = "$group" | project.group = "$group" | ||||||
| 
 | 
 | ||||||
| apply from: "$publishGradlePath" | apply from: "$publish" | ||||||
| 
 | 
 | ||||||
| kotlin { | kotlin { | ||||||
|     jvm { |     jvm { | ||||||
| @@ -1,7 +1,7 @@ | |||||||
| project.version = "$version" | project.version = "$version" | ||||||
| project.group = "$group" | project.group = "$group" | ||||||
| 
 | 
 | ||||||
| apply from: "$publishGradlePath" | apply from: "$publish" | ||||||
| 
 | 
 | ||||||
| kotlin { | kotlin { | ||||||
|     jvm { |     jvm { | ||||||
| @@ -12,8 +12,20 @@ kotlin { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     js (IR) { |     js (IR) { | ||||||
|         browser() |         browser { | ||||||
|         nodejs() |             testTask { | ||||||
|  |                 useMocha { | ||||||
|  |                     timeout = "60000" | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         nodejs { | ||||||
|  |             testTask { | ||||||
|  |                 useMocha { | ||||||
|  |                     timeout = "60000" | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|     androidTarget { |     androidTarget { | ||||||
|         publishAllLibraryVariants() |         publishAllLibraryVariants() | ||||||
| @@ -36,7 +48,7 @@ kotlin { | |||||||
|         } |         } | ||||||
|         commonTest { |         commonTest { | ||||||
|             dependencies { |             dependencies { | ||||||
|                 implementation kotlin('test-common') |                 implementation kotlin('test') | ||||||
|                 implementation kotlin('test-annotations-common') |                 implementation kotlin('test-annotations-common') | ||||||
|                 implementation libs.kt.coroutines.test |                 implementation libs.kt.coroutines.test | ||||||
|             } |             } | ||||||
| @@ -57,7 +69,6 @@ kotlin { | |||||||
|         jsTest { |         jsTest { | ||||||
|             dependencies { |             dependencies { | ||||||
|                 implementation kotlin('test-js') |                 implementation kotlin('test-js') | ||||||
|                 implementation kotlin('test-junit') |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         nativeMain.dependsOn commonMain |         nativeMain.dependsOn commonMain | ||||||
| @@ -69,7 +80,7 @@ kotlin { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| apply from: "$defaultAndroidSettingsPresetPath" | apply from: "$defaultAndroidSettings" | ||||||
| 
 | 
 | ||||||
| java { | java { | ||||||
|     sourceCompatibility = JavaVersion.VERSION_17 |     sourceCompatibility = JavaVersion.VERSION_17 | ||||||
| @@ -1,7 +1,7 @@ | |||||||
| project.version = "$version" | project.version = "$version" | ||||||
| project.group = "$group" | project.group = "$group" | ||||||
| 
 | 
 | ||||||
| apply from: "$publishGradlePath" | apply from: "$publish" | ||||||
| 
 | 
 | ||||||
| kotlin { | kotlin { | ||||||
|     jvm { |     jvm { | ||||||
| @@ -12,8 +12,20 @@ kotlin { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     js (IR) { |     js (IR) { | ||||||
|         browser() |         browser { | ||||||
|         nodejs() |             testTask { | ||||||
|  |                 useMocha { | ||||||
|  |                     timeout = "60000" | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         nodejs { | ||||||
|  |             testTask { | ||||||
|  |                 useMocha { | ||||||
|  |                     timeout = "60000" | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|     linuxX64() |     linuxX64() | ||||||
|     mingwX64() |     mingwX64() | ||||||
| @@ -41,17 +53,6 @@ kotlin { | |||||||
|         jsTest { |         jsTest { | ||||||
|             dependencies { |             dependencies { | ||||||
|                 implementation kotlin('test-js') |                 implementation kotlin('test-js') | ||||||
|                 implementation kotlin('test-junit') |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         mingwX64Test { |  | ||||||
|             dependencies { |  | ||||||
|                 implementation kotlin('test-junit') |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         linuxX64Test { |  | ||||||
|             dependencies { |  | ||||||
|                 implementation kotlin('test-junit') |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @@ -1,7 +1,7 @@ | |||||||
| project.version = "$version" | project.version = "$version" | ||||||
| project.group = "$group" | project.group = "$group" | ||||||
| 
 | 
 | ||||||
| apply from: "$publishGradlePath" | apply from: "$publish" | ||||||
| 
 | 
 | ||||||
| kotlin { | kotlin { | ||||||
|     jvm { |     jvm { | ||||||
| @@ -12,8 +12,20 @@ kotlin { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     js (IR) { |     js (IR) { | ||||||
|         browser() |         browser { | ||||||
|         nodejs() |             testTask { | ||||||
|  |                 useMocha { | ||||||
|  |                     timeout = "60000" | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         nodejs { | ||||||
|  |             testTask { | ||||||
|  |                 useMocha { | ||||||
|  |                     timeout = "60000" | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|     androidTarget { |     androidTarget { | ||||||
|         publishAllLibraryVariants() |         publishAllLibraryVariants() | ||||||
| @@ -58,7 +70,6 @@ kotlin { | |||||||
|         jsTest { |         jsTest { | ||||||
|             dependencies { |             dependencies { | ||||||
|                 implementation kotlin('test-js') |                 implementation kotlin('test-js') | ||||||
|                 implementation kotlin('test-junit') |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         androidUnitTest { |         androidUnitTest { | ||||||
| @@ -69,17 +80,10 @@ kotlin { | |||||||
|                 implementation compose.uiTest |                 implementation compose.uiTest | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         androidInstrumentedTest { |  | ||||||
|             dependencies { |  | ||||||
|                 implementation kotlin('test-junit') |  | ||||||
|                 implementation libs.android.test.junit |  | ||||||
|                 implementation libs.android.espresso |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| apply from: "$defaultAndroidSettingsPresetPath" | apply from: "$defaultAndroidSettings" | ||||||
| 
 | 
 | ||||||
| java { | java { | ||||||
|     sourceCompatibility = JavaVersion.VERSION_17 |     sourceCompatibility = JavaVersion.VERSION_17 | ||||||
							
								
								
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							| @@ -1,5 +1,5 @@ | |||||||
| distributionBase=GRADLE_USER_HOME | distributionBase=GRADLE_USER_HOME | ||||||
| distributionPath=wrapper/dists | distributionPath=wrapper/dists | ||||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip | distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip | ||||||
| zipStoreBase=GRADLE_USER_HOME | zipStoreBase=GRADLE_USER_HOME | ||||||
| zipStorePath=wrapper/dists | zipStorePath=wrapper/dists | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ plugins { | |||||||
|     id "com.android.library" |     id "com.android.library" | ||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath" | apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" | ||||||
|  |  | ||||||
| kotlin { | kotlin { | ||||||
|     sourceSets { |     sourceSets { | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ plugins { | |||||||
|     id "org.jetbrains.kotlin.jvm" |     id "org.jetbrains.kotlin.jvm" | ||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "$publishJvmOnlyPath" | apply from: "$publish_jvm" | ||||||
|  |  | ||||||
| repositories { | repositories { | ||||||
|     mavenCentral() |     mavenCentral() | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ plugins { | |||||||
|     id "com.google.devtools.ksp" |     id "com.google.devtools.ksp" | ||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath" | apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" | ||||||
|  |  | ||||||
|  |  | ||||||
| kotlin { | kotlin { | ||||||
|   | |||||||
| @@ -4,4 +4,4 @@ plugins { | |||||||
|     id "com.android.library" |     id "com.android.library" | ||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath" | apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ plugins { | |||||||
|     id "org.jetbrains.kotlin.jvm" |     id "org.jetbrains.kotlin.jvm" | ||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "$publishJvmOnlyPath" | apply from: "$publish_jvm" | ||||||
|  |  | ||||||
| repositories { | repositories { | ||||||
|     mavenCentral() |     mavenCentral() | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ plugins { | |||||||
|     id "com.google.devtools.ksp" |     id "com.google.devtools.ksp" | ||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath" | apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" | ||||||
|  |  | ||||||
|  |  | ||||||
| kotlin { | kotlin { | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ plugins { | |||||||
|     id "org.jetbrains.kotlin.jvm" |     id "org.jetbrains.kotlin.jvm" | ||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "$publishJvmOnlyPath" | apply from: "$publish_jvm" | ||||||
|  |  | ||||||
| repositories { | repositories { | ||||||
|     mavenCentral() |     mavenCentral() | ||||||
|   | |||||||
| @@ -0,0 +1,13 @@ | |||||||
|  | package dev.inmo.micro_ksp.generator | ||||||
|  |  | ||||||
|  | import com.google.devtools.ksp.symbol.KSClassDeclaration | ||||||
|  |  | ||||||
|  | val KSClassDeclaration.buildSubFileName: String | ||||||
|  |     get() { | ||||||
|  |         val parentDeclarationCaptured = parentDeclaration | ||||||
|  |         val simpleNameString = simpleName.asString() | ||||||
|  |         return when (parentDeclarationCaptured) { | ||||||
|  |             is KSClassDeclaration -> parentDeclarationCaptured.buildSubFileName | ||||||
|  |             else -> "" | ||||||
|  |         } + simpleNameString | ||||||
|  |     } | ||||||
| @@ -0,0 +1,8 @@ | |||||||
|  | package dev.inmo.micro_ksp.generator | ||||||
|  |  | ||||||
|  | import com.google.devtools.ksp.symbol.KSClassDeclaration | ||||||
|  |  | ||||||
|  | val KSClassDeclaration.companion | ||||||
|  |     get() = declarations.firstNotNullOfOrNull { | ||||||
|  |         (it as? KSClassDeclaration)?.takeIf { it.isCompanionObject } | ||||||
|  |     } | ||||||
							
								
								
									
										11
									
								
								ksp/generator/src/main/kotlin/ResolveSubclasses.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								ksp/generator/src/main/kotlin/ResolveSubclasses.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | package dev.inmo.micro_ksp.generator | ||||||
|  |  | ||||||
|  | import com.google.devtools.ksp.symbol.KSClassDeclaration | ||||||
|  |  | ||||||
|  | fun KSClassDeclaration.resolveSubclasses(): List<KSClassDeclaration> { | ||||||
|  |     return (getSealedSubclasses().flatMap { | ||||||
|  |         it.resolveSubclasses() | ||||||
|  |     }.ifEmpty { | ||||||
|  |         sequenceOf(this) | ||||||
|  |     }).toList() | ||||||
|  | } | ||||||
| @@ -4,4 +4,4 @@ plugins { | |||||||
|     id "com.android.library" |     id "com.android.library" | ||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath" | apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ plugins { | |||||||
|     id "org.jetbrains.kotlin.jvm" |     id "org.jetbrains.kotlin.jvm" | ||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "$publishJvmOnlyPath" | apply from: "$publish_jvm" | ||||||
|  |  | ||||||
| repositories { | repositories { | ||||||
|     mavenCentral() |     mavenCentral() | ||||||
|   | |||||||
| @@ -0,0 +1,17 @@ | |||||||
|  | package dev.inmo.micro_utils.ksp.sealed.generator | ||||||
|  |  | ||||||
|  | import com.google.devtools.ksp.KspExperimental | ||||||
|  | import com.google.devtools.ksp.getAnnotationsByType | ||||||
|  | import com.google.devtools.ksp.symbol.KSClassDeclaration | ||||||
|  | import dev.inmo.micro_utils.ksp.sealed.GenerateSealedTypesWorkaround | ||||||
|  | import dev.inmo.micro_utils.ksp.sealed.GenerateSealedWorkaround | ||||||
|  | import dev.inmo.microutils.kps.sealed.GenerateSealedWorkaround as OldGenerateSealedWorkaround | ||||||
|  |  | ||||||
|  | @OptIn(KspExperimental::class) | ||||||
|  | val KSClassDeclaration.getGenerateSealedWorkaroundAnnotation | ||||||
|  |     get() = (getAnnotationsByType(GenerateSealedWorkaround::class).firstOrNull() ?: getAnnotationsByType(OldGenerateSealedWorkaround::class).firstOrNull()) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @OptIn(KspExperimental::class) | ||||||
|  | val KSClassDeclaration.getGenerateSealedTypesWorkaroundAnnotation | ||||||
|  |     get() = getAnnotationsByType(GenerateSealedTypesWorkaround::class).firstOrNull() | ||||||
| @@ -6,19 +6,17 @@ import com.google.devtools.ksp.processing.CodeGenerator | |||||||
| import com.google.devtools.ksp.processing.Resolver | import com.google.devtools.ksp.processing.Resolver | ||||||
| import com.google.devtools.ksp.processing.SymbolProcessor | import com.google.devtools.ksp.processing.SymbolProcessor | ||||||
| import com.google.devtools.ksp.symbol.* | import com.google.devtools.ksp.symbol.* | ||||||
| import com.squareup.kotlinpoet.ClassName | import com.squareup.kotlinpoet.* | ||||||
| import com.squareup.kotlinpoet.CodeBlock |  | ||||||
| import com.squareup.kotlinpoet.FileSpec |  | ||||||
| import com.squareup.kotlinpoet.FunSpec |  | ||||||
| import com.squareup.kotlinpoet.KModifier |  | ||||||
| import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy | import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy | ||||||
| import com.squareup.kotlinpoet.PropertySpec |  | ||||||
| import com.squareup.kotlinpoet.asTypeName |  | ||||||
| import com.squareup.kotlinpoet.ksp.toClassName | import com.squareup.kotlinpoet.ksp.toClassName | ||||||
|  | import dev.inmo.micro_ksp.generator.buildSubFileName | ||||||
|  | import dev.inmo.micro_ksp.generator.companion | ||||||
| import dev.inmo.micro_ksp.generator.findSubClasses | import dev.inmo.micro_ksp.generator.findSubClasses | ||||||
| import dev.inmo.micro_ksp.generator.writeFile | import dev.inmo.micro_ksp.generator.writeFile | ||||||
| import dev.inmo.microutils.kps.sealed.GenerateSealedWorkaround | import dev.inmo.micro_utils.ksp.sealed.GenerateSealedTypesWorkaround | ||||||
|  | import dev.inmo.micro_utils.ksp.sealed.GenerateSealedWorkaround | ||||||
| import java.io.File | import java.io.File | ||||||
|  | import kotlin.reflect.KClass | ||||||
|  |  | ||||||
| class Processor( | class Processor( | ||||||
|     private val codeGenerator: CodeGenerator |     private val codeGenerator: CodeGenerator | ||||||
| @@ -26,7 +24,8 @@ class Processor( | |||||||
|     private fun KSClassDeclaration.findSealedConnection(potentialSealedParent: KSClassDeclaration): Boolean { |     private fun KSClassDeclaration.findSealedConnection(potentialSealedParent: KSClassDeclaration): Boolean { | ||||||
|         val targetClassname = potentialSealedParent.qualifiedName ?.asString() |         val targetClassname = potentialSealedParent.qualifiedName ?.asString() | ||||||
|         return superTypes.any { |         return superTypes.any { | ||||||
|             targetClassname == ((it.resolve().declaration as? KSClassDeclaration) ?.qualifiedName ?.asString()) || (it is KSClassDeclaration && it.getSealedSubclasses().any() && it.findSealedConnection(potentialSealedParent)) |             val itAsDeclaration = it.resolve().declaration as? KSClassDeclaration ?: return@any false | ||||||
|  |             targetClassname == (itAsDeclaration.qualifiedName ?.asString()) || (itAsDeclaration.getSealedSubclasses().any() && itAsDeclaration.findSealedConnection(potentialSealedParent)) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -50,10 +49,10 @@ class Processor( | |||||||
|         ksClassDeclaration: KSClassDeclaration, |         ksClassDeclaration: KSClassDeclaration, | ||||||
|         resolver: Resolver |         resolver: Resolver | ||||||
|     ) { |     ) { | ||||||
|         val annotation = ksClassDeclaration.getAnnotationsByType(GenerateSealedWorkaround::class).first() |         val annotation = ksClassDeclaration.getGenerateSealedWorkaroundAnnotation | ||||||
|         val subClasses = ksClassDeclaration.resolveSubclasses( |         val subClasses = ksClassDeclaration.resolveSubclasses( | ||||||
|             searchIn = resolver.getAllFiles(), |             searchIn = resolver.getAllFiles(), | ||||||
|             allowNonSealed = annotation.includeNonSealedSubTypes |             allowNonSealed = annotation ?.includeNonSealedSubTypes ?: false | ||||||
|         ).distinct() |         ).distinct() | ||||||
|         val subClassesNames = subClasses.filter { |         val subClassesNames = subClasses.filter { | ||||||
|             when (it.classKind) { |             when (it.classKind) { | ||||||
| @@ -92,7 +91,10 @@ class Processor( | |||||||
|         ) |         ) | ||||||
|         addFunction( |         addFunction( | ||||||
|             FunSpec.builder("values").apply { |             FunSpec.builder("values").apply { | ||||||
|                 receiver(ClassName(className.packageName, *className.simpleNames.toTypedArray(), "Companion")) |                 val companion = ksClassDeclaration.takeIf { it.isCompanionObject } ?.toClassName() | ||||||
|  |                     ?: ksClassDeclaration.companion ?.toClassName() | ||||||
|  |                     ?: ClassName(className.packageName, *className.simpleNames.toTypedArray(), "Companion") | ||||||
|  |                 receiver(companion) | ||||||
|                 returns(setType) |                 returns(setType) | ||||||
|                 addCode( |                 addCode( | ||||||
|                     CodeBlock.of( |                     CodeBlock.of( | ||||||
| @@ -103,10 +105,68 @@ class Processor( | |||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @OptIn(KspExperimental::class) | ||||||
|  |     private fun FileSpec.Builder.generateSealedTypesWorkaround( | ||||||
|  |         ksClassDeclaration: KSClassDeclaration, | ||||||
|  |         resolver: Resolver | ||||||
|  |     ) { | ||||||
|  |         val annotation = ksClassDeclaration.getGenerateSealedTypesWorkaroundAnnotation | ||||||
|  |         val subClasses = ksClassDeclaration.resolveSubclasses( | ||||||
|  |             searchIn = resolver.getAllFiles(), | ||||||
|  |             allowNonSealed = annotation ?.includeNonSealedSubTypes ?: false | ||||||
|  |         ).distinct() | ||||||
|  |         val subClassesNames = subClasses.filter { | ||||||
|  |             it.getAnnotationsByType(GenerateSealedWorkaround.Exclude::class).count() == 0 | ||||||
|  |         }.sortedBy { | ||||||
|  |             (it.getAnnotationsByType(GenerateSealedWorkaround.Order::class).firstOrNull()) ?.order ?: 0 | ||||||
|  |         }.map { | ||||||
|  |             it.toClassName() | ||||||
|  |         }.toList() | ||||||
|  |         val className = ksClassDeclaration.toClassName() | ||||||
|  |         val setType = Set::class.asTypeName().parameterizedBy( | ||||||
|  |             KClass::class.asTypeName().parameterizedBy( | ||||||
|  |                 TypeVariableName( | ||||||
|  |                     "out ${ksClassDeclaration.asStarProjectedType().toClassName().simpleNames.joinToString(".")}", | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|  |         addProperty( | ||||||
|  |             PropertySpec.builder( | ||||||
|  |                 "subtypes", | ||||||
|  |                 setType | ||||||
|  |             ).apply { | ||||||
|  |                 modifiers.add( | ||||||
|  |                     KModifier.PRIVATE | ||||||
|  |                 ) | ||||||
|  |                 initializer( | ||||||
|  |                     CodeBlock.of( | ||||||
|  |                         """setOf(${subClassesNames.joinToString(",\n") { it.simpleNames.joinToString(".") + "::class" }})""" | ||||||
|  |                     ) | ||||||
|  |                 ) | ||||||
|  |             }.build() | ||||||
|  |         ) | ||||||
|  |         addFunction( | ||||||
|  |             FunSpec.builder("subtypes").apply { | ||||||
|  |                 val companion = ksClassDeclaration.takeIf { it.isCompanionObject } ?.toClassName() | ||||||
|  |                     ?: ksClassDeclaration.companion ?.toClassName() | ||||||
|  |                     ?: ClassName(className.packageName, *className.simpleNames.toTypedArray(), "Companion") | ||||||
|  |                 receiver(companion) | ||||||
|  |                 returns(setType) | ||||||
|  |                 addCode( | ||||||
|  |                     CodeBlock.of( | ||||||
|  |                         """return subtypes""" | ||||||
|  |                     ) | ||||||
|  |                 ) | ||||||
|  |             }.build() | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  |  | ||||||
|     @OptIn(KspExperimental::class) |     @OptIn(KspExperimental::class) | ||||||
|     override fun process(resolver: Resolver): List<KSAnnotated> { |     override fun process(resolver: Resolver): List<KSAnnotated> { | ||||||
|         (resolver.getSymbolsWithAnnotation(GenerateSealedWorkaround::class.qualifiedName!!)).filterIsInstance<KSClassDeclaration>().forEach { |         (resolver.getSymbolsWithAnnotation(GenerateSealedWorkaround::class.qualifiedName!!)).filterIsInstance<KSClassDeclaration>().forEach { | ||||||
|             val prefix = it.getAnnotationsByType(GenerateSealedWorkaround::class).first().prefix |             val prefix = (it.getGenerateSealedWorkaroundAnnotation) ?.prefix ?.takeIf { | ||||||
|  |                 it.isNotEmpty() | ||||||
|  |             } ?: it.buildSubFileName.replaceFirst(it.simpleName.asString(), "") | ||||||
|             it.writeFile(prefix = prefix, suffix = "SealedWorkaround") { |             it.writeFile(prefix = prefix, suffix = "SealedWorkaround") { | ||||||
|                 FileSpec.builder( |                 FileSpec.builder( | ||||||
|                     it.packageName.asString(), |                     it.packageName.asString(), | ||||||
| @@ -123,6 +183,26 @@ class Processor( | |||||||
|                 }.build() |                 }.build() | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |         (resolver.getSymbolsWithAnnotation(GenerateSealedTypesWorkaround::class.qualifiedName!!)).filterIsInstance<KSClassDeclaration>().forEach { | ||||||
|  |             val prefix = (it.getGenerateSealedTypesWorkaroundAnnotation) ?.prefix ?.takeIf { | ||||||
|  |                 it.isNotEmpty() | ||||||
|  |             } ?: it.buildSubFileName.replaceFirst(it.simpleName.asString(), "") | ||||||
|  |             it.writeFile(prefix = prefix, suffix = "SealedTypesWorkaround") { | ||||||
|  |                 FileSpec.builder( | ||||||
|  |                     it.packageName.asString(), | ||||||
|  |                     "${it.simpleName.getShortName()}SealedTypesWorkaround" | ||||||
|  |                 ).apply { | ||||||
|  |                     addFileComment( | ||||||
|  |                         """ | ||||||
|  |                         THIS CODE HAVE BEEN GENERATED AUTOMATICALLY | ||||||
|  |                         TO REGENERATE IT JUST DELETE FILE | ||||||
|  |                         ORIGINAL FILE: ${it.containingFile ?.fileName} | ||||||
|  |                         """.trimIndent() | ||||||
|  |                     ) | ||||||
|  |                     generateSealedTypesWorkaround(it, resolver) | ||||||
|  |                 }.build() | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|         return emptyList() |         return emptyList() | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ plugins { | |||||||
|     id "com.google.devtools.ksp" |     id "com.google.devtools.ksp" | ||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath" | apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" | ||||||
|  |  | ||||||
|  |  | ||||||
| kotlin { | kotlin { | ||||||
|   | |||||||
| @@ -1,14 +1,19 @@ | |||||||
| package dev.inmo.micro_utils.ksp.sealed.generator.test | package dev.inmo.micro_utils.ksp.sealed.generator.test | ||||||
|  |  | ||||||
| import dev.inmo.microutils.kps.sealed.GenerateSealedWorkaround | import dev.inmo.micro_utils.ksp.sealed.GenerateSealedTypesWorkaround | ||||||
|  | import dev.inmo.micro_utils.ksp.sealed.GenerateSealedWorkaround | ||||||
|  |  | ||||||
| @GenerateSealedWorkaround | @GenerateSealedWorkaround | ||||||
|  | @GenerateSealedTypesWorkaround | ||||||
| sealed interface Test { | sealed interface Test { | ||||||
|     @GenerateSealedWorkaround.Order(2) |     @GenerateSealedWorkaround.Order(2) | ||||||
|  |     @GenerateSealedTypesWorkaround.Order(2) | ||||||
|     object A : Test |     object A : Test | ||||||
|     @GenerateSealedWorkaround.Exclude |     @GenerateSealedWorkaround.Exclude | ||||||
|  |     @GenerateSealedTypesWorkaround.Exclude | ||||||
|     object B : Test |     object B : Test | ||||||
|     @GenerateSealedWorkaround.Order(0) |     @GenerateSealedWorkaround.Order(0) | ||||||
|  |     @GenerateSealedTypesWorkaround.Order(0) | ||||||
|     object C : Test |     object C : Test | ||||||
|  |  | ||||||
|     // Required for successful sealed workaround generation |     // Required for successful sealed workaround generation | ||||||
|   | |||||||
| @@ -0,0 +1,12 @@ | |||||||
|  | // THIS CODE HAVE BEEN GENERATED AUTOMATICALLY | ||||||
|  | // TO REGENERATE IT JUST DELETE FILE | ||||||
|  | // ORIGINAL FILE: Test.kt | ||||||
|  | package dev.inmo.micro_utils.ksp.`sealed`.generator.test | ||||||
|  |  | ||||||
|  | import kotlin.collections.Set | ||||||
|  | import kotlin.reflect.KClass | ||||||
|  |  | ||||||
|  | private val subtypes: Set<KClass<out Test>> = setOf(Test.C::class, | ||||||
|  |     Test.A::class) | ||||||
|  |  | ||||||
|  | public fun Test.Companion.subtypes(): Set<KClass<out Test>> = subtypes | ||||||
| @@ -0,0 +1,15 @@ | |||||||
|  | package dev.inmo.micro_utils.ksp.sealed | ||||||
|  |  | ||||||
|  | @Retention(AnnotationRetention.BINARY) | ||||||
|  | @Target(AnnotationTarget.CLASS) | ||||||
|  | annotation class GenerateSealedTypesWorkaround( | ||||||
|  |     val prefix: String = "", | ||||||
|  |     val includeNonSealedSubTypes: Boolean = false, | ||||||
|  | ) { | ||||||
|  |     @Retention(AnnotationRetention.BINARY) | ||||||
|  |     @Target(AnnotationTarget.CLASS) | ||||||
|  |     annotation class Order(val order: Int) | ||||||
|  |     @Retention(AnnotationRetention.BINARY) | ||||||
|  |     @Target(AnnotationTarget.CLASS) | ||||||
|  |     annotation class Exclude | ||||||
|  | } | ||||||
| @@ -1,10 +1,10 @@ | |||||||
| package dev.inmo.microutils.kps.sealed | package dev.inmo.micro_utils.ksp.sealed | ||||||
|  |  | ||||||
| @Retention(AnnotationRetention.BINARY) | @Retention(AnnotationRetention.BINARY) | ||||||
| @Target(AnnotationTarget.CLASS) | @Target(AnnotationTarget.CLASS) | ||||||
| annotation class GenerateSealedWorkaround( | annotation class GenerateSealedWorkaround( | ||||||
|     val prefix: String = "", |     val prefix: String = "", | ||||||
|     val includeNonSealedSubTypes: Boolean = false |     val includeNonSealedSubTypes: Boolean = false, | ||||||
| ) { | ) { | ||||||
|     @Retention(AnnotationRetention.BINARY) |     @Retention(AnnotationRetention.BINARY) | ||||||
|     @Target(AnnotationTarget.CLASS) |     @Target(AnnotationTarget.CLASS) | ||||||
|   | |||||||
| @@ -0,0 +1,6 @@ | |||||||
|  | package dev.inmo.microutils.kps.sealed | ||||||
|  |  | ||||||
|  | import dev.inmo.micro_utils.ksp.sealed.GenerateSealedWorkaround | ||||||
|  |  | ||||||
|  | @Deprecated("Replaced", ReplaceWith("GenerateSealedWorkaround", "dev.inmo.micro_utils.ksp.sealed.GenerateSealedWorkaround")) | ||||||
|  | typealias GenerateSealedWorkaround = GenerateSealedWorkaround | ||||||
| @@ -4,7 +4,7 @@ plugins { | |||||||
|     id "com.android.library" |     id "com.android.library" | ||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath" | apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" | ||||||
|  |  | ||||||
| kotlin { | kotlin { | ||||||
|     sourceSets { |     sourceSets { | ||||||
|   | |||||||
| @@ -3,9 +3,10 @@ package dev.inmo.micro_utils.ktor.client | |||||||
| import dev.inmo.micro_utils.common.MPPFile | import dev.inmo.micro_utils.common.MPPFile | ||||||
| import dev.inmo.micro_utils.ktor.common.* | import dev.inmo.micro_utils.ktor.common.* | ||||||
| import io.ktor.client.HttpClient | import io.ktor.client.HttpClient | ||||||
|  | import io.ktor.client.content.* | ||||||
|  |  | ||||||
| expect suspend fun HttpClient.tempUpload( | expect suspend fun HttpClient.tempUpload( | ||||||
|     fullTempUploadDraftPath: String, |     fullTempUploadDraftPath: String, | ||||||
|     file: MPPFile, |     file: MPPFile, | ||||||
|     onUpload: OnUploadCallback = { _, _ -> } |     onUpload: ProgressListener = ProgressListener { _, _ -> } | ||||||
| ): TemporalFileId | ): TemporalFileId | ||||||
|   | |||||||
| @@ -4,8 +4,8 @@ import dev.inmo.micro_utils.common.FileName | |||||||
| import dev.inmo.micro_utils.common.MPPFile | import dev.inmo.micro_utils.common.MPPFile | ||||||
| import dev.inmo.micro_utils.ktor.common.LambdaInputProvider | import dev.inmo.micro_utils.ktor.common.LambdaInputProvider | ||||||
| import io.ktor.client.HttpClient | import io.ktor.client.HttpClient | ||||||
|  | import io.ktor.client.content.* | ||||||
| import io.ktor.http.Headers | import io.ktor.http.Headers | ||||||
| import io.ktor.utils.io.core.Input |  | ||||||
| import kotlinx.serialization.DeserializationStrategy | import kotlinx.serialization.DeserializationStrategy | ||||||
| import kotlinx.serialization.StringFormat | import kotlinx.serialization.StringFormat | ||||||
| import kotlinx.serialization.json.Json | import kotlinx.serialization.json.Json | ||||||
| @@ -31,7 +31,7 @@ expect suspend fun <T> HttpClient.uniUpload( | |||||||
|     resultDeserializer: DeserializationStrategy<T>, |     resultDeserializer: DeserializationStrategy<T>, | ||||||
|     headers: Headers = Headers.Empty, |     headers: Headers = Headers.Empty, | ||||||
|     stringFormat: StringFormat = Json, |     stringFormat: StringFormat = Json, | ||||||
|     onUpload: OnUploadCallback = { _, _ -> } |     onUpload: ProgressListener = ProgressListener { _, _ -> } | ||||||
| ): T? | ): T? | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -46,7 +46,7 @@ suspend fun <T> HttpClient.uniUpload( | |||||||
|     additionalData: Map<String, Any> = emptyMap(), |     additionalData: Map<String, Any> = emptyMap(), | ||||||
|     headers: Headers = Headers.Empty, |     headers: Headers = Headers.Empty, | ||||||
|     stringFormat: StringFormat = Json, |     stringFormat: StringFormat = Json, | ||||||
|     onUpload: OnUploadCallback = { _, _ -> } |     onUpload: ProgressListener = ProgressListener { _, _ -> } | ||||||
| ): T? = uniUpload( | ): T? = uniUpload( | ||||||
|     url, |     url, | ||||||
|     additionalData + ("bytes" to file), |     additionalData + ("bytes" to file), | ||||||
| @@ -68,7 +68,7 @@ suspend fun <T> HttpClient.uniUpload( | |||||||
|     additionalData: Map<String, Any> = emptyMap(), |     additionalData: Map<String, Any> = emptyMap(), | ||||||
|     headers: Headers = Headers.Empty, |     headers: Headers = Headers.Empty, | ||||||
|     stringFormat: StringFormat = Json, |     stringFormat: StringFormat = Json, | ||||||
|     onUpload: OnUploadCallback = { _, _ -> } |     onUpload: ProgressListener = ProgressListener { _, _ -> } | ||||||
| ): T? = uniUpload( | ): T? = uniUpload( | ||||||
|     url, |     url, | ||||||
|     additionalData + ("bytes" to info), |     additionalData + ("bytes" to info), | ||||||
| @@ -93,7 +93,7 @@ suspend fun <T> HttpClient.uniUpload( | |||||||
|     additionalData: Map<String, Any> = emptyMap(), |     additionalData: Map<String, Any> = emptyMap(), | ||||||
|     headers: Headers = Headers.Empty, |     headers: Headers = Headers.Empty, | ||||||
|     stringFormat: StringFormat = Json, |     stringFormat: StringFormat = Json, | ||||||
|     onUpload: OnUploadCallback = { _, _ -> } |     onUpload: ProgressListener = ProgressListener { _, _ -> } | ||||||
| ): T? = uniUpload( | ): T? = uniUpload( | ||||||
|     url, |     url, | ||||||
|     UniUploadFileInfo(fileName, mimeType, inputAllocator), |     UniUploadFileInfo(fileName, mimeType, inputAllocator), | ||||||
|   | |||||||
| @@ -5,16 +5,15 @@ import dev.inmo.micro_utils.coroutines.LinkedSupervisorJob | |||||||
| import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions | import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions | ||||||
| import dev.inmo.micro_utils.ktor.common.TemporalFileId | import dev.inmo.micro_utils.ktor.common.TemporalFileId | ||||||
| import io.ktor.client.HttpClient | import io.ktor.client.HttpClient | ||||||
|  | import io.ktor.client.content.* | ||||||
| import kotlinx.coroutines.* | import kotlinx.coroutines.* | ||||||
| import org.w3c.dom.mediasource.ENDED |  | ||||||
| import org.w3c.dom.mediasource.ReadyState |  | ||||||
| import org.w3c.xhr.* | import org.w3c.xhr.* | ||||||
| import org.w3c.xhr.XMLHttpRequest.Companion.DONE | import org.w3c.xhr.XMLHttpRequest.Companion.DONE | ||||||
|  |  | ||||||
| suspend fun tempUpload( | suspend fun tempUpload( | ||||||
|     fullTempUploadDraftPath: String, |     fullTempUploadDraftPath: String, | ||||||
|     file: MPPFile, |     file: MPPFile, | ||||||
|     onUpload: OnUploadCallback |     onUpload: ProgressListener | ||||||
| ): TemporalFileId { | ): TemporalFileId { | ||||||
|     val formData = FormData() |     val formData = FormData() | ||||||
|     val answer = CompletableDeferred<TemporalFileId>(currentCoroutineContext().job) |     val answer = CompletableDeferred<TemporalFileId>(currentCoroutineContext().job) | ||||||
| @@ -28,7 +27,7 @@ suspend fun tempUpload( | |||||||
|     val request = XMLHttpRequest() |     val request = XMLHttpRequest() | ||||||
|     request.responseType = XMLHttpRequestResponseType.TEXT |     request.responseType = XMLHttpRequestResponseType.TEXT | ||||||
|     request.upload.onprogress = { |     request.upload.onprogress = { | ||||||
|         subscope.launchSafelyWithoutExceptions { onUpload(it.loaded.toLong(), it.total.toLong()) } |         subscope.launchSafelyWithoutExceptions { onUpload.onProgress(it.loaded.toLong(), it.total.toLong()) } | ||||||
|     } |     } | ||||||
|     request.onload = { |     request.onload = { | ||||||
|         if (request.status == 200.toShort()) { |         if (request.status == 200.toShort()) { | ||||||
| @@ -60,5 +59,5 @@ suspend fun tempUpload( | |||||||
| actual suspend fun HttpClient.tempUpload( | actual suspend fun HttpClient.tempUpload( | ||||||
|     fullTempUploadDraftPath: String, |     fullTempUploadDraftPath: String, | ||||||
|     file: MPPFile, |     file: MPPFile, | ||||||
|     onUpload: OnUploadCallback |     onUpload: ProgressListener | ||||||
| ): TemporalFileId = dev.inmo.micro_utils.ktor.client.tempUpload(fullTempUploadDraftPath, file, onUpload) | ): TemporalFileId = dev.inmo.micro_utils.ktor.client.tempUpload(fullTempUploadDraftPath, file, onUpload) | ||||||
|   | |||||||
| @@ -1,10 +1,10 @@ | |||||||
| package dev.inmo.micro_utils.ktor.client | package dev.inmo.micro_utils.ktor.client | ||||||
|  |  | ||||||
| import dev.inmo.micro_utils.common.MPPFile | import dev.inmo.micro_utils.common.MPPFile | ||||||
| import dev.inmo.micro_utils.common.Progress |  | ||||||
| import dev.inmo.micro_utils.coroutines.LinkedSupervisorJob | import dev.inmo.micro_utils.coroutines.LinkedSupervisorJob | ||||||
| import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions | import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions | ||||||
| import io.ktor.client.HttpClient | import io.ktor.client.HttpClient | ||||||
|  | import io.ktor.client.content.* | ||||||
| import io.ktor.http.Headers | import io.ktor.http.Headers | ||||||
| import io.ktor.utils.io.core.readBytes | import io.ktor.utils.io.core.readBytes | ||||||
| import kotlinx.coroutines.CompletableDeferred | import kotlinx.coroutines.CompletableDeferred | ||||||
| @@ -36,7 +36,7 @@ actual suspend fun <T> HttpClient.uniUpload( | |||||||
|     resultDeserializer: DeserializationStrategy<T>, |     resultDeserializer: DeserializationStrategy<T>, | ||||||
|     headers: Headers, |     headers: Headers, | ||||||
|     stringFormat: StringFormat, |     stringFormat: StringFormat, | ||||||
|     onUpload: OnUploadCallback |     onUpload: ProgressListener | ||||||
| ): T? { | ): T? { | ||||||
|     val formData = FormData() |     val formData = FormData() | ||||||
|     val answer = CompletableDeferred<T?>(currentCoroutineContext().job) |     val answer = CompletableDeferred<T?>(currentCoroutineContext().job) | ||||||
| @@ -66,7 +66,7 @@ actual suspend fun <T> HttpClient.uniUpload( | |||||||
|     } |     } | ||||||
|     request.responseType = XMLHttpRequestResponseType.TEXT |     request.responseType = XMLHttpRequestResponseType.TEXT | ||||||
|     request.upload.onprogress = { |     request.upload.onprogress = { | ||||||
|         subscope.launchSafelyWithoutExceptions { onUpload(it.loaded.toLong(), it.total.toLong()) } |         subscope.launchSafelyWithoutExceptions { onUpload.onProgress(it.loaded.toLong(), it.total.toLong()) } | ||||||
|     } |     } | ||||||
|     request.onload = { |     request.onload = { | ||||||
|         if (request.status == 200.toShort()) { |         if (request.status == 200.toShort()) { | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ import dev.inmo.micro_utils.common.MPPFile | |||||||
| import dev.inmo.micro_utils.common.filename | import dev.inmo.micro_utils.common.filename | ||||||
| import dev.inmo.micro_utils.ktor.common.TemporalFileId | import dev.inmo.micro_utils.ktor.common.TemporalFileId | ||||||
| import io.ktor.client.HttpClient | import io.ktor.client.HttpClient | ||||||
|  | import io.ktor.client.content.* | ||||||
| import io.ktor.client.plugins.onUpload | import io.ktor.client.plugins.onUpload | ||||||
| import io.ktor.client.request.forms.formData | import io.ktor.client.request.forms.formData | ||||||
| import io.ktor.client.request.forms.submitFormWithBinaryData | import io.ktor.client.request.forms.submitFormWithBinaryData | ||||||
| @@ -18,7 +19,7 @@ internal val MPPFile.mimeType: String | |||||||
| actual suspend fun HttpClient.tempUpload( | actual suspend fun HttpClient.tempUpload( | ||||||
|     fullTempUploadDraftPath: String, |     fullTempUploadDraftPath: String, | ||||||
|     file: MPPFile, |     file: MPPFile, | ||||||
|     onUpload: OnUploadCallback |     onUpload: ProgressListener | ||||||
| ): TemporalFileId { | ): TemporalFileId { | ||||||
|     val inputProvider = file.inputProvider() |     val inputProvider = file.inputProvider() | ||||||
|     val fileId = submitFormWithBinaryData( |     val fileId = submitFormWithBinaryData( | ||||||
|   | |||||||
| @@ -1,8 +1,7 @@ | |||||||
| package dev.inmo.micro_utils.ktor.client | package dev.inmo.micro_utils.ktor.client | ||||||
|  |  | ||||||
| import dev.inmo.micro_utils.common.Progress |  | ||||||
| import io.ktor.client.HttpClient | import io.ktor.client.HttpClient | ||||||
| import io.ktor.client.engine.mergeHeaders | import io.ktor.client.content.* | ||||||
| import io.ktor.client.plugins.onUpload | import io.ktor.client.plugins.onUpload | ||||||
| import io.ktor.client.request.HttpRequestBuilder | import io.ktor.client.request.HttpRequestBuilder | ||||||
| import io.ktor.client.request.forms.InputProvider | import io.ktor.client.request.forms.InputProvider | ||||||
| @@ -20,7 +19,6 @@ import kotlinx.serialization.DeserializationStrategy | |||||||
| import kotlinx.serialization.InternalSerializationApi | import kotlinx.serialization.InternalSerializationApi | ||||||
| import kotlinx.serialization.SerializationStrategy | import kotlinx.serialization.SerializationStrategy | ||||||
| import kotlinx.serialization.StringFormat | import kotlinx.serialization.StringFormat | ||||||
| import kotlinx.serialization.encodeToString |  | ||||||
| import kotlinx.serialization.serializer | import kotlinx.serialization.serializer | ||||||
| import java.io.File | import java.io.File | ||||||
|  |  | ||||||
| @@ -39,7 +37,7 @@ actual suspend fun <T> HttpClient.uniUpload( | |||||||
|     resultDeserializer: DeserializationStrategy<T>, |     resultDeserializer: DeserializationStrategy<T>, | ||||||
|     headers: Headers, |     headers: Headers, | ||||||
|     stringFormat: StringFormat, |     stringFormat: StringFormat, | ||||||
|     onUpload: OnUploadCallback |     onUpload: ProgressListener | ||||||
| ): T? { | ): T? { | ||||||
|     val withBinary = data.values.any { it is File || it is UniUploadFileInfo } |     val withBinary = data.values.any { it is File || it is UniUploadFileInfo } | ||||||
|  |  | ||||||
| @@ -76,7 +74,7 @@ actual suspend fun <T> HttpClient.uniUpload( | |||||||
|             appendAll(headers) |             appendAll(headers) | ||||||
|         } |         } | ||||||
|         onUpload { bytesSentTotal, contentLength -> |         onUpload { bytesSentTotal, contentLength -> | ||||||
|             onUpload(bytesSentTotal, contentLength) |             onUpload.onProgress(bytesSentTotal, contentLength) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ import dev.inmo.micro_utils.common.filename | |||||||
| import dev.inmo.micro_utils.ktor.common.TemporalFileId | import dev.inmo.micro_utils.ktor.common.TemporalFileId | ||||||
| import dev.inmo.micro_utils.mime_types.getMimeTypeOrAny | import dev.inmo.micro_utils.mime_types.getMimeTypeOrAny | ||||||
| import io.ktor.client.HttpClient | import io.ktor.client.HttpClient | ||||||
|  | import io.ktor.client.content.* | ||||||
| import io.ktor.client.plugins.onUpload | import io.ktor.client.plugins.onUpload | ||||||
| import io.ktor.client.request.forms.formData | import io.ktor.client.request.forms.formData | ||||||
| import io.ktor.client.request.forms.submitFormWithBinaryData | import io.ktor.client.request.forms.submitFormWithBinaryData | ||||||
| @@ -18,7 +19,7 @@ internal val MPPFile.mimeType: String | |||||||
| actual suspend fun HttpClient.tempUpload( | actual suspend fun HttpClient.tempUpload( | ||||||
|     fullTempUploadDraftPath: String, |     fullTempUploadDraftPath: String, | ||||||
|     file: MPPFile, |     file: MPPFile, | ||||||
|     onUpload: OnUploadCallback |     onUpload: ProgressListener | ||||||
| ): TemporalFileId { | ): TemporalFileId { | ||||||
|     val inputProvider = file.inputProvider() |     val inputProvider = file.inputProvider() | ||||||
|     val fileId = submitFormWithBinaryData( |     val fileId = submitFormWithBinaryData( | ||||||
|   | |||||||
| @@ -1,9 +1,8 @@ | |||||||
| package dev.inmo.micro_utils.ktor.client | package dev.inmo.micro_utils.ktor.client | ||||||
|  |  | ||||||
| import dev.inmo.micro_utils.common.MPPFile | import dev.inmo.micro_utils.common.MPPFile | ||||||
| import dev.inmo.micro_utils.common.Progress |  | ||||||
| import io.ktor.client.HttpClient | import io.ktor.client.HttpClient | ||||||
| import io.ktor.client.engine.mergeHeaders | import io.ktor.client.content.* | ||||||
| import io.ktor.client.plugins.onUpload | import io.ktor.client.plugins.onUpload | ||||||
| import io.ktor.client.request.HttpRequestBuilder | import io.ktor.client.request.HttpRequestBuilder | ||||||
| import io.ktor.client.request.forms.InputProvider | import io.ktor.client.request.forms.InputProvider | ||||||
| @@ -21,7 +20,6 @@ import kotlinx.serialization.DeserializationStrategy | |||||||
| import kotlinx.serialization.InternalSerializationApi | import kotlinx.serialization.InternalSerializationApi | ||||||
| import kotlinx.serialization.SerializationStrategy | import kotlinx.serialization.SerializationStrategy | ||||||
| import kotlinx.serialization.StringFormat | import kotlinx.serialization.StringFormat | ||||||
| import kotlinx.serialization.encodeToString |  | ||||||
| import kotlinx.serialization.serializer | import kotlinx.serialization.serializer | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -39,7 +37,7 @@ actual suspend fun <T> HttpClient.uniUpload( | |||||||
|     resultDeserializer: DeserializationStrategy<T>, |     resultDeserializer: DeserializationStrategy<T>, | ||||||
|     headers: Headers, |     headers: Headers, | ||||||
|     stringFormat: StringFormat, |     stringFormat: StringFormat, | ||||||
|     onUpload: OnUploadCallback |     onUpload: ProgressListener | ||||||
| ): T? { | ): T? { | ||||||
|     val withBinary = data.values.any { it is MPPFile || it is UniUploadFileInfo } |     val withBinary = data.values.any { it is MPPFile || it is UniUploadFileInfo } | ||||||
|  |  | ||||||
| @@ -75,9 +73,7 @@ actual suspend fun <T> HttpClient.uniUpload( | |||||||
|         headers { |         headers { | ||||||
|             appendAll(headers) |             appendAll(headers) | ||||||
|         } |         } | ||||||
|         onUpload { bytesSentTotal, contentLength -> |         onUpload(onUpload) | ||||||
|             onUpload(bytesSentTotal, contentLength) |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     val response = if (withBinary) { |     val response = if (withBinary) { | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ import dev.inmo.micro_utils.common.filename | |||||||
| import dev.inmo.micro_utils.ktor.common.TemporalFileId | import dev.inmo.micro_utils.ktor.common.TemporalFileId | ||||||
| import dev.inmo.micro_utils.mime_types.getMimeTypeOrAny | import dev.inmo.micro_utils.mime_types.getMimeTypeOrAny | ||||||
| import io.ktor.client.HttpClient | import io.ktor.client.HttpClient | ||||||
|  | import io.ktor.client.content.* | ||||||
| import io.ktor.client.plugins.onUpload | import io.ktor.client.plugins.onUpload | ||||||
| import io.ktor.client.request.forms.formData | import io.ktor.client.request.forms.formData | ||||||
| import io.ktor.client.request.forms.submitFormWithBinaryData | import io.ktor.client.request.forms.submitFormWithBinaryData | ||||||
| @@ -18,7 +19,7 @@ internal val MPPFile.mimeType: String | |||||||
| actual suspend fun HttpClient.tempUpload( | actual suspend fun HttpClient.tempUpload( | ||||||
|     fullTempUploadDraftPath: String, |     fullTempUploadDraftPath: String, | ||||||
|     file: MPPFile, |     file: MPPFile, | ||||||
|     onUpload: OnUploadCallback |     onUpload: ProgressListener | ||||||
| ): TemporalFileId { | ): TemporalFileId { | ||||||
|     val inputProvider = file.inputProvider() |     val inputProvider = file.inputProvider() | ||||||
|     val fileId = submitFormWithBinaryData( |     val fileId = submitFormWithBinaryData( | ||||||
|   | |||||||
| @@ -1,9 +1,8 @@ | |||||||
| package dev.inmo.micro_utils.ktor.client | package dev.inmo.micro_utils.ktor.client | ||||||
|  |  | ||||||
| import dev.inmo.micro_utils.common.MPPFile | import dev.inmo.micro_utils.common.MPPFile | ||||||
| import dev.inmo.micro_utils.common.Progress |  | ||||||
| import io.ktor.client.HttpClient | import io.ktor.client.HttpClient | ||||||
| import io.ktor.client.engine.mergeHeaders | import io.ktor.client.content.* | ||||||
| import io.ktor.client.plugins.onUpload | import io.ktor.client.plugins.onUpload | ||||||
| import io.ktor.client.request.HttpRequestBuilder | import io.ktor.client.request.HttpRequestBuilder | ||||||
| import io.ktor.client.request.forms.InputProvider | import io.ktor.client.request.forms.InputProvider | ||||||
| @@ -21,7 +20,6 @@ import kotlinx.serialization.DeserializationStrategy | |||||||
| import kotlinx.serialization.InternalSerializationApi | import kotlinx.serialization.InternalSerializationApi | ||||||
| import kotlinx.serialization.SerializationStrategy | import kotlinx.serialization.SerializationStrategy | ||||||
| import kotlinx.serialization.StringFormat | import kotlinx.serialization.StringFormat | ||||||
| import kotlinx.serialization.encodeToString |  | ||||||
| import kotlinx.serialization.serializer | import kotlinx.serialization.serializer | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -39,7 +37,7 @@ actual suspend fun <T> HttpClient.uniUpload( | |||||||
|     resultDeserializer: DeserializationStrategy<T>, |     resultDeserializer: DeserializationStrategy<T>, | ||||||
|     headers: Headers, |     headers: Headers, | ||||||
|     stringFormat: StringFormat, |     stringFormat: StringFormat, | ||||||
|     onUpload: OnUploadCallback |     onUpload: ProgressListener | ||||||
| ): T? { | ): T? { | ||||||
|     val withBinary = data.values.any { it is MPPFile || it is UniUploadFileInfo } |     val withBinary = data.values.any { it is MPPFile || it is UniUploadFileInfo } | ||||||
|  |  | ||||||
| @@ -75,9 +73,7 @@ actual suspend fun <T> HttpClient.uniUpload( | |||||||
|         headers { |         headers { | ||||||
|             appendAll(headers) |             appendAll(headers) | ||||||
|         } |         } | ||||||
|         onUpload { bytesSentTotal, contentLength -> |         onUpload(onUpload) | ||||||
|             onUpload(bytesSentTotal, contentLength) |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     val response = if (withBinary) { |     val response = if (withBinary) { | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ import dev.inmo.micro_utils.common.filename | |||||||
| import dev.inmo.micro_utils.ktor.common.TemporalFileId | import dev.inmo.micro_utils.ktor.common.TemporalFileId | ||||||
| import dev.inmo.micro_utils.mime_types.getMimeTypeOrAny | import dev.inmo.micro_utils.mime_types.getMimeTypeOrAny | ||||||
| import io.ktor.client.HttpClient | import io.ktor.client.HttpClient | ||||||
|  | import io.ktor.client.content.* | ||||||
| import io.ktor.client.plugins.onUpload | import io.ktor.client.plugins.onUpload | ||||||
| import io.ktor.client.request.forms.formData | import io.ktor.client.request.forms.formData | ||||||
| import io.ktor.client.request.forms.submitFormWithBinaryData | import io.ktor.client.request.forms.submitFormWithBinaryData | ||||||
| @@ -18,7 +19,7 @@ internal val MPPFile.mimeType: String | |||||||
| actual suspend fun HttpClient.tempUpload( | actual suspend fun HttpClient.tempUpload( | ||||||
|     fullTempUploadDraftPath: String, |     fullTempUploadDraftPath: String, | ||||||
|     file: MPPFile, |     file: MPPFile, | ||||||
|     onUpload: OnUploadCallback |     onUpload: ProgressListener | ||||||
| ): TemporalFileId { | ): TemporalFileId { | ||||||
|     val inputProvider = file.inputProvider() |     val inputProvider = file.inputProvider() | ||||||
|     val fileId = submitFormWithBinaryData( |     val fileId = submitFormWithBinaryData( | ||||||
|   | |||||||
| @@ -1,9 +1,8 @@ | |||||||
| package dev.inmo.micro_utils.ktor.client | package dev.inmo.micro_utils.ktor.client | ||||||
|  |  | ||||||
| import dev.inmo.micro_utils.common.MPPFile | import dev.inmo.micro_utils.common.MPPFile | ||||||
| import dev.inmo.micro_utils.common.Progress |  | ||||||
| import io.ktor.client.HttpClient | import io.ktor.client.HttpClient | ||||||
| import io.ktor.client.engine.mergeHeaders | import io.ktor.client.content.* | ||||||
| import io.ktor.client.plugins.onUpload | import io.ktor.client.plugins.onUpload | ||||||
| import io.ktor.client.request.HttpRequestBuilder | import io.ktor.client.request.HttpRequestBuilder | ||||||
| import io.ktor.client.request.forms.InputProvider | import io.ktor.client.request.forms.InputProvider | ||||||
| @@ -21,7 +20,6 @@ import kotlinx.serialization.DeserializationStrategy | |||||||
| import kotlinx.serialization.InternalSerializationApi | import kotlinx.serialization.InternalSerializationApi | ||||||
| import kotlinx.serialization.SerializationStrategy | import kotlinx.serialization.SerializationStrategy | ||||||
| import kotlinx.serialization.StringFormat | import kotlinx.serialization.StringFormat | ||||||
| import kotlinx.serialization.encodeToString |  | ||||||
| import kotlinx.serialization.serializer | import kotlinx.serialization.serializer | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -39,7 +37,7 @@ actual suspend fun <T> HttpClient.uniUpload( | |||||||
|     resultDeserializer: DeserializationStrategy<T>, |     resultDeserializer: DeserializationStrategy<T>, | ||||||
|     headers: Headers, |     headers: Headers, | ||||||
|     stringFormat: StringFormat, |     stringFormat: StringFormat, | ||||||
|     onUpload: OnUploadCallback |     onUpload: ProgressListener | ||||||
| ): T? { | ): T? { | ||||||
|     val withBinary = data.values.any { it is MPPFile || it is UniUploadFileInfo } |     val withBinary = data.values.any { it is MPPFile || it is UniUploadFileInfo } | ||||||
|  |  | ||||||
| @@ -75,9 +73,7 @@ actual suspend fun <T> HttpClient.uniUpload( | |||||||
|         headers { |         headers { | ||||||
|             appendAll(headers) |             appendAll(headers) | ||||||
|         } |         } | ||||||
|         onUpload { bytesSentTotal, contentLength -> |         onUpload(onUpload) | ||||||
|             onUpload(bytesSentTotal, contentLength) |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     val response = if (withBinary) { |     val response = if (withBinary) { | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ plugins { | |||||||
|     id "com.android.library" |     id "com.android.library" | ||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath" | apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" | ||||||
|  |  | ||||||
| kotlin { | kotlin { | ||||||
|     sourceSets { |     sourceSets { | ||||||
|   | |||||||
| @@ -1,13 +1,12 @@ | |||||||
| package dev.inmo.micro_utils.ktor.common | package dev.inmo.micro_utils.ktor.common | ||||||
|  |  | ||||||
| import io.ktor.utils.io.core.Input | import io.ktor.utils.io.streams.* | ||||||
| import io.ktor.utils.io.core.copyTo | import kotlinx.io.Source | ||||||
| import io.ktor.utils.io.streams.asOutput | import kotlinx.io.readTo | ||||||
| import java.io.File | import java.io.File | ||||||
| import java.io.InputStream |  | ||||||
| import java.util.UUID | import java.util.UUID | ||||||
|  |  | ||||||
| fun Input.downloadToTempFile( | fun Source.downloadToTempFile( | ||||||
|     fileName: String = UUID.randomUUID().toString(), |     fileName: String = UUID.randomUUID().toString(), | ||||||
|     fileExtension: String? = ".temp", |     fileExtension: String? = ".temp", | ||||||
|     folder: File? = null |     folder: File? = null | ||||||
| @@ -17,7 +16,7 @@ fun Input.downloadToTempFile( | |||||||
|     folder |     folder | ||||||
| ).apply { | ).apply { | ||||||
|     outputStream().use { |     outputStream().use { | ||||||
|         copyTo(it.asOutput()) |         it.writePacket(this@downloadToTempFile) | ||||||
|     } |     } | ||||||
|     deleteOnExit() |     deleteOnExit() | ||||||
| } | } | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ plugins { | |||||||
|     id "org.jetbrains.kotlin.plugin.serialization" |     id "org.jetbrains.kotlin.plugin.serialization" | ||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "$mppJavaProjectPresetPath" | apply from: "$mppJavaProject" | ||||||
|  |  | ||||||
| kotlin { | kotlin { | ||||||
|     sourceSets { |     sourceSets { | ||||||
|   | |||||||
| @@ -1,15 +0,0 @@ | |||||||
| package dev.inmo.micro_utils.ktor.server |  | ||||||
|  |  | ||||||
| import io.ktor.server.application.ApplicationCall |  | ||||||
| import io.ktor.server.response.responseType |  | ||||||
| import io.ktor.util.InternalAPI |  | ||||||
| import io.ktor.util.reflect.TypeInfo |  | ||||||
|  |  | ||||||
| @InternalAPI |  | ||||||
| suspend fun <T : Any> ApplicationCall.respond( |  | ||||||
|     message: T, |  | ||||||
|     typeInfo: TypeInfo |  | ||||||
| ) { |  | ||||||
|     response.responseType = typeInfo |  | ||||||
|     response.pipeline.execute(this, message as Any) |  | ||||||
| } |  | ||||||
| @@ -4,13 +4,13 @@ import com.benasher44.uuid.uuid4 | |||||||
| import io.ktor.http.content.PartData | import io.ktor.http.content.PartData | ||||||
| import io.ktor.utils.io.copyTo | import io.ktor.utils.io.copyTo | ||||||
| import io.ktor.utils.io.core.copyTo | import io.ktor.utils.io.core.copyTo | ||||||
| import io.ktor.utils.io.jvm.javaio.copyTo | import io.ktor.utils.io.jvm.javaio.* | ||||||
| import io.ktor.utils.io.streams.asOutput | import io.ktor.utils.io.streams.* | ||||||
| import java.io.File | import java.io.File | ||||||
|  |  | ||||||
| fun PartData.FileItem.download(target: File) { | fun PartData.FileItem.download(target: File) { | ||||||
|     provider().use { input -> |     provider().toInputStream().use { input -> | ||||||
|         target.outputStream().asOutput().use { |         target.outputStream().use { | ||||||
|             input.copyTo(it) |             input.copyTo(it) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -25,9 +25,9 @@ fun PartData.FileItem.downloadToTemporalFile(): File { | |||||||
| } | } | ||||||
|  |  | ||||||
| fun PartData.BinaryItem.download(target: File) { | fun PartData.BinaryItem.download(target: File) { | ||||||
|     provider().use { input -> |     provider().inputStream().use { input -> | ||||||
|         target.outputStream().use { |         target.outputStream().use { | ||||||
|             input.copyTo(it.asOutput()) |             input.copyTo(it) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| package dev.inmo.micro_utils.ktor.server | package dev.inmo.micro_utils.ktor.server | ||||||
|  |  | ||||||
| import dev.inmo.micro_utils.ktor.server.configurators.KtorApplicationConfigurator | import dev.inmo.micro_utils.ktor.server.configurators.KtorApplicationConfigurator | ||||||
| import io.ktor.server.application.Application | import io.ktor.server.application.* | ||||||
| import io.ktor.server.cio.CIO | import io.ktor.server.cio.CIO | ||||||
| import io.ktor.server.cio.CIOApplicationEngine | import io.ktor.server.cio.CIOApplicationEngine | ||||||
| import io.ktor.server.engine.* | import io.ktor.server.engine.* | ||||||
| @@ -11,20 +11,22 @@ fun <TEngine : ApplicationEngine, TConfiguration : ApplicationEngine.Configurati | |||||||
|     engine: ApplicationEngineFactory<TEngine, TConfiguration>, |     engine: ApplicationEngineFactory<TEngine, TConfiguration>, | ||||||
|     host: String = "localhost", |     host: String = "localhost", | ||||||
|     port: Int = Random.nextInt(1024, 65535), |     port: Int = Random.nextInt(1024, 65535), | ||||||
|     additionalEngineEnvironmentConfigurator: ApplicationEngineEnvironmentBuilder.() -> Unit = {}, |     additionalEngineEnvironmentConfigurator: EngineConnectorBuilder.() -> Unit = {}, | ||||||
|     additionalConfigurationConfigurator: TConfiguration.() -> Unit = {}, |     additionalConfigurationConfigurator: TConfiguration.() -> Unit = {}, | ||||||
|  |     environment: ApplicationEnvironment = applicationEnvironment(), | ||||||
|     block: Application.() -> Unit |     block: Application.() -> Unit | ||||||
| ): TEngine = embeddedServer( | ): EmbeddedServer<TEngine, TConfiguration> = embeddedServer<TEngine, TConfiguration>( | ||||||
|     engine, |     engine, | ||||||
|     applicationEngineEnvironment { |     environment, | ||||||
|         module(block) |     { | ||||||
|         connector { |         connector { | ||||||
|             this.host = host |             this.host = host | ||||||
|             this.port = port |             this.port = port | ||||||
|  |             additionalEngineEnvironmentConfigurator() | ||||||
|         } |         } | ||||||
|         additionalEngineEnvironmentConfigurator() |         additionalConfigurationConfigurator() | ||||||
|     }, |     }, | ||||||
|     additionalConfigurationConfigurator |     module = block | ||||||
| ) | ) | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -35,15 +37,17 @@ fun <TEngine : ApplicationEngine, TConfiguration : ApplicationEngine.Configurati | |||||||
| fun createKtorServer( | fun createKtorServer( | ||||||
|     host: String = "localhost", |     host: String = "localhost", | ||||||
|     port: Int = Random.nextInt(1024, 65535), |     port: Int = Random.nextInt(1024, 65535), | ||||||
|     additionalEngineEnvironmentConfigurator: ApplicationEngineEnvironmentBuilder.() -> Unit = {}, |     additionalEngineEnvironmentConfigurator: EngineConnectorBuilder.() -> Unit = {}, | ||||||
|     additionalConfigurationConfigurator: CIOApplicationEngine.Configuration.() -> Unit = {}, |     additionalConfigurationConfigurator: CIOApplicationEngine.Configuration.() -> Unit = {}, | ||||||
|  |     environment: ApplicationEnvironment = applicationEnvironment(), | ||||||
|     block: Application.() -> Unit |     block: Application.() -> Unit | ||||||
| ): CIOApplicationEngine = createKtorServer( | ): EmbeddedServer<CIOApplicationEngine, CIOApplicationEngine.Configuration> = createKtorServer( | ||||||
|     CIO, |     CIO, | ||||||
|     host, |     host, | ||||||
|     port, |     port, | ||||||
|     additionalEngineEnvironmentConfigurator, |     additionalEngineEnvironmentConfigurator, | ||||||
|     additionalConfigurationConfigurator, |     additionalConfigurationConfigurator, | ||||||
|  |     environment, | ||||||
|     block |     block | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -51,15 +55,17 @@ fun <TEngine : ApplicationEngine, TConfiguration : ApplicationEngine.Configurati | |||||||
|     engine: ApplicationEngineFactory<TEngine, TConfiguration>, |     engine: ApplicationEngineFactory<TEngine, TConfiguration>, | ||||||
|     host: String = "localhost", |     host: String = "localhost", | ||||||
|     port: Int = Random.nextInt(1024, 65535), |     port: Int = Random.nextInt(1024, 65535), | ||||||
|     additionalEngineEnvironmentConfigurator: ApplicationEngineEnvironmentBuilder.() -> Unit = {}, |     additionalEngineEnvironmentConfigurator: EngineConnectorBuilder.() -> Unit = {}, | ||||||
|     additionalConfigurationConfigurator: TConfiguration.() -> Unit = {}, |     additionalConfigurationConfigurator: TConfiguration.() -> Unit = {}, | ||||||
|  |     environment: ApplicationEnvironment = applicationEnvironment(), | ||||||
|     configurators: List<KtorApplicationConfigurator> |     configurators: List<KtorApplicationConfigurator> | ||||||
| ): TEngine = createKtorServer( | ): EmbeddedServer<TEngine, TConfiguration> = createKtorServer( | ||||||
|     engine, |     engine, | ||||||
|     host, |     host, | ||||||
|     port, |     port, | ||||||
|     additionalEngineEnvironmentConfigurator, |     additionalEngineEnvironmentConfigurator, | ||||||
|     additionalConfigurationConfigurator |     additionalConfigurationConfigurator, | ||||||
|  |     environment, | ||||||
| ) { | ) { | ||||||
|     configurators.forEach { it.apply { configure() } } |     configurators.forEach { it.apply { configure() } } | ||||||
| } | } | ||||||
| @@ -73,6 +79,7 @@ fun createKtorServer( | |||||||
|     host: String = "localhost", |     host: String = "localhost", | ||||||
|     port: Int = Random.nextInt(1024, 65535), |     port: Int = Random.nextInt(1024, 65535), | ||||||
|     configurators: List<KtorApplicationConfigurator>, |     configurators: List<KtorApplicationConfigurator>, | ||||||
|     additionalEngineEnvironmentConfigurator: ApplicationEngineEnvironmentBuilder.() -> Unit = {}, |     additionalEngineEnvironmentConfigurator: EngineConnectorBuilder.() -> Unit = {}, | ||||||
|     additionalConfigurationConfigurator: CIOApplicationEngine.Configuration.() -> Unit = {}, |     additionalConfigurationConfigurator: CIOApplicationEngine.Configuration.() -> Unit = {}, | ||||||
| ): ApplicationEngine = createKtorServer(CIO, host, port, additionalEngineEnvironmentConfigurator, additionalConfigurationConfigurator, configurators) |     environment: ApplicationEnvironment = applicationEnvironment(), | ||||||
|  | ): EmbeddedServer<CIOApplicationEngine, CIOApplicationEngine.Configuration> = createKtorServer(CIO, host, port, additionalEngineEnvironmentConfigurator, additionalConfigurationConfigurator, environment, configurators) | ||||||
|   | |||||||
| @@ -17,6 +17,7 @@ import io.ktor.server.response.respondText | |||||||
| import io.ktor.server.routing.Route | import io.ktor.server.routing.Route | ||||||
| import io.ktor.server.routing.post | import io.ktor.server.routing.post | ||||||
| import kotlinx.coroutines.* | import kotlinx.coroutines.* | ||||||
|  | import kotlinx.coroutines.channels.BufferOverflow | ||||||
| import kotlinx.coroutines.flow.* | import kotlinx.coroutines.flow.* | ||||||
| import kotlinx.coroutines.sync.Mutex | import kotlinx.coroutines.sync.Mutex | ||||||
| import kotlinx.coroutines.sync.withLock | import kotlinx.coroutines.sync.withLock | ||||||
| @@ -26,7 +27,10 @@ import java.nio.file.attribute.FileTime | |||||||
|  |  | ||||||
| class TemporalFilesRoutingConfigurator( | class TemporalFilesRoutingConfigurator( | ||||||
|     private val subpath: String = DefaultTemporalFilesSubPath, |     private val subpath: String = DefaultTemporalFilesSubPath, | ||||||
|     private val temporalFilesUtilizer: TemporalFilesUtilizer = TemporalFilesUtilizer |     private val temporalFilesUtilizer: TemporalFilesUtilizer = TemporalFilesUtilizer, | ||||||
|  |     filesFlowReplay: Int = 0, | ||||||
|  |     filesFlowExtraBufferCapacity: Int = Int.MAX_VALUE, | ||||||
|  |     filesFlowOnBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND | ||||||
| ) : ApplicationRoutingConfigurator.Element { | ) : ApplicationRoutingConfigurator.Element { | ||||||
|     interface TemporalFilesUtilizer { |     interface TemporalFilesUtilizer { | ||||||
|         fun start(filesMap: MutableMap<TemporalFileId, MPPFile>, filesMutex: Mutex, onNewFileFlow: Flow<TemporalFileId>): Job |         fun start(filesMap: MutableMap<TemporalFileId, MPPFile>, filesMutex: Mutex, onNewFileFlow: Flow<TemporalFileId>): Job | ||||||
| @@ -74,7 +78,11 @@ class TemporalFilesRoutingConfigurator( | |||||||
|  |  | ||||||
|     private val temporalFilesMap = mutableMapOf<TemporalFileId, MPPFile>() |     private val temporalFilesMap = mutableMapOf<TemporalFileId, MPPFile>() | ||||||
|     private val temporalFilesMutex = Mutex() |     private val temporalFilesMutex = Mutex() | ||||||
|     private val filesFlow = MutableSharedFlow<TemporalFileId>() |     private val filesFlow = MutableSharedFlow<TemporalFileId>( | ||||||
|  |         replay = filesFlowReplay, | ||||||
|  |         extraBufferCapacity = filesFlowExtraBufferCapacity, | ||||||
|  |         onBufferOverflow = filesFlowOnBufferOverflow | ||||||
|  |     ) | ||||||
|     val utilizerJob = temporalFilesUtilizer.start(temporalFilesMap, temporalFilesMutex, filesFlow.asSharedFlow()) |     val utilizerJob = temporalFilesUtilizer.start(temporalFilesMap, temporalFilesMutex, filesFlow.asSharedFlow()) | ||||||
|  |  | ||||||
|     override fun Route.invoke() { |     override fun Route.invoke() { | ||||||
| @@ -111,7 +119,7 @@ class TemporalFilesRoutingConfigurator( | |||||||
|                     temporalFilesMap[fileId] = file |                     temporalFilesMap[fileId] = file | ||||||
|                 } |                 } | ||||||
|                 call.respondText(fileId.string) |                 call.respondText(fileId.string) | ||||||
|                 launchSafelyWithoutExceptions { filesFlow.emit(fileId) } |                 filesFlow.emit(fileId) | ||||||
|             } ?: call.respond(HttpStatusCode.BadRequest) |             } ?: call.respond(HttpStatusCode.BadRequest) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ import dev.inmo.micro_utils.ktor.common.downloadToTempFile | |||||||
| import io.ktor.http.content.* | import io.ktor.http.content.* | ||||||
| import io.ktor.server.application.ApplicationCall | import io.ktor.server.application.ApplicationCall | ||||||
| import io.ktor.server.request.receiveMultipart | import io.ktor.server.request.receiveMultipart | ||||||
|  | import io.ktor.utils.io.* | ||||||
| import io.ktor.utils.io.core.* | import io.ktor.utils.io.core.* | ||||||
| import kotlinx.coroutines.currentCoroutineContext | import kotlinx.coroutines.currentCoroutineContext | ||||||
| import kotlinx.coroutines.isActive | import kotlinx.coroutines.isActive | ||||||
| @@ -47,7 +48,7 @@ suspend fun ApplicationCall.uniloadMultipart( | |||||||
|         onBinaryChannelItem |         onBinaryChannelItem | ||||||
|     ) { |     ) { | ||||||
|         when (it.name) { |         when (it.name) { | ||||||
|             "bytes" -> resultInput = it.provider() |             "bytes" -> resultInput = it.provider().readBuffer() | ||||||
|             else -> onCustomFileItem(it) |             else -> onCustomFileItem(it) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -2,8 +2,7 @@ package dev.inmo.micro_utils.ktor.server.configurators | |||||||
|  |  | ||||||
| import dev.inmo.micro_utils.ktor.server.configurators.ApplicationRoutingConfigurator.Element | import dev.inmo.micro_utils.ktor.server.configurators.ApplicationRoutingConfigurator.Element | ||||||
| import io.ktor.server.application.* | import io.ktor.server.application.* | ||||||
| import io.ktor.server.routing.Route | import io.ktor.server.routing.* | ||||||
| import io.ktor.server.routing.Routing |  | ||||||
| import kotlinx.serialization.Contextual | import kotlinx.serialization.Contextual | ||||||
| import kotlinx.serialization.Serializable | import kotlinx.serialization.Serializable | ||||||
|  |  | ||||||
| @@ -19,9 +18,7 @@ class ApplicationRoutingConfigurator( | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     override fun Application.configure() { |     override fun Application.configure() { | ||||||
|         pluginOrNull(Routing) ?.apply { |         routing { | ||||||
|             rootInstaller.apply { invoke() } |  | ||||||
|         } ?: install(Routing) { |  | ||||||
|             rootInstaller.apply { invoke() } |             rootInstaller.apply { invoke() } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -1,7 +1,99 @@ | |||||||
| package dev.inmo.micro_utils.ktor.server.configurators | package dev.inmo.micro_utils.ktor.server.configurators | ||||||
|  |  | ||||||
| import io.ktor.server.application.Application | import io.ktor.server.application.* | ||||||
|  | import io.ktor.server.http.content.* | ||||||
|  | import io.ktor.server.plugins.cachingheaders.* | ||||||
|  | import io.ktor.server.plugins.statuspages.* | ||||||
|  | import io.ktor.server.routing.* | ||||||
|  | import io.ktor.server.sessions.* | ||||||
|  | import kotlinx.serialization.Contextual | ||||||
|  | import kotlinx.serialization.Serializable | ||||||
|  | import java.io.File | ||||||
|  |  | ||||||
| interface KtorApplicationConfigurator { | interface KtorApplicationConfigurator { | ||||||
|  |     @Serializable | ||||||
|  |     class Routing( | ||||||
|  |         private val elements: List<@Contextual Element> | ||||||
|  |     ) : KtorApplicationConfigurator { | ||||||
|  |         fun interface Element { operator fun Route.invoke() } | ||||||
|  |         private val rootInstaller = Element { | ||||||
|  |             elements.forEach { | ||||||
|  |                 it.apply { invoke() } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         override fun Application.configure() { | ||||||
|  |             routing { | ||||||
|  |                 rootInstaller.apply { invoke() } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * @param pathToFolder Contains [Pair]s where firsts are paths in urls and seconds are folders file paths | ||||||
|  |          * @param pathToResource Contains [Pair]s where firsts are paths in urls and seconds are packages in resources | ||||||
|  |          */ | ||||||
|  |         class Static( | ||||||
|  |             private val pathToFolder: List<Pair<String, String>> = emptyList(), | ||||||
|  |             private val pathToResource: List<Pair<String, String>> = emptyList(), | ||||||
|  |         ) : Element { | ||||||
|  |             override fun Route.invoke() { | ||||||
|  |                 pathToFolder.forEach { | ||||||
|  |                     staticFiles( | ||||||
|  |                         it.first, | ||||||
|  |                         File(it.second) | ||||||
|  |                     ) | ||||||
|  |                 } | ||||||
|  |                 pathToResource.forEach { | ||||||
|  |                     staticResources( | ||||||
|  |                         it.first, | ||||||
|  |                         it.second | ||||||
|  |                     ) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     class StatusPages( | ||||||
|  |         private val elements: List<@Contextual Element> | ||||||
|  |     ) : KtorApplicationConfigurator { | ||||||
|  |         fun interface Element { operator fun StatusPagesConfig.invoke() } | ||||||
|  |  | ||||||
|  |         override fun Application.configure() { | ||||||
|  |             install(StatusPages) { | ||||||
|  |                 elements.forEach { | ||||||
|  |                     it.apply { invoke() } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     class Sessions( | ||||||
|  |         private val elements: List<@Contextual Element> | ||||||
|  |     ) : KtorApplicationConfigurator { | ||||||
|  |         fun interface Element { operator fun SessionsConfig.invoke() } | ||||||
|  |  | ||||||
|  |         override fun Application.configure() { | ||||||
|  |             install(Sessions) { | ||||||
|  |                 elements.forEach { | ||||||
|  |                     it.apply { invoke() } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     class CachingHeaders( | ||||||
|  |         private val elements: List<@Contextual Element> | ||||||
|  |     ) : KtorApplicationConfigurator { | ||||||
|  |         fun interface Element { operator fun CachingHeadersConfig.invoke() } | ||||||
|  |  | ||||||
|  |         override fun Application.configure() { | ||||||
|  |             install(CachingHeaders) { | ||||||
|  |                 elements.forEach { | ||||||
|  |                     it.apply { invoke() } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     fun Application.configure() |     fun Application.configure() | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,4 +4,4 @@ plugins { | |||||||
|     id "com.android.library" |     id "com.android.library" | ||||||
| } | } | ||||||
|  |  | ||||||
| apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath" | apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								language_codes/generator/full.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								language_codes/generator/full.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -22,17 +22,24 @@ private const val baseClassSerializerAnnotationName = "@Serializable(${baseClass | |||||||
|  |  | ||||||
| @Serializable | @Serializable | ||||||
| private data class LanguageCode( | private data class LanguageCode( | ||||||
|     @SerialName("alpha2") |  | ||||||
|     val tag: String, |  | ||||||
|     @SerialName("English") |     @SerialName("English") | ||||||
|     val title: String |     val title: String, | ||||||
| ) |     @SerialName("alpha2") | ||||||
|  |     val alpha: String? = null, | ||||||
|  |     @SerialName("alpha3-b") | ||||||
|  |     val alpha2: String? = null, | ||||||
|  |     @SerialName("alpha3-t") | ||||||
|  |     val alpha3: String? = null, | ||||||
|  | ) { | ||||||
|  |     val tag: String | ||||||
|  |         get() = alpha ?: alpha2 ?: alpha3!! | ||||||
|  | } | ||||||
|  |  | ||||||
| fun String.adaptAsTitle() = if (first().isDigit()) { | fun String.adaptAsTitle() = (if (first().isDigit()) { | ||||||
|     "L$this" |     "L$this" | ||||||
| } else { | } else { | ||||||
|     this |     this | ||||||
| } | }).replace(".", "_").replace("'", "_") | ||||||
|  |  | ||||||
| fun String.normalized() = Normalizer.normalize(this, Normalizer.Form.NFD).replace(Regex("[^\\p{ASCII}]"), "") | fun String.normalized() = Normalizer.normalize(this, Normalizer.Form.NFD).replace(Regex("[^\\p{ASCII}]"), "") | ||||||
|  |  | ||||||
| @@ -73,7 +80,10 @@ data class Tag( | |||||||
|     val title: String, |     val title: String, | ||||||
|     val tag: String, |     val tag: String, | ||||||
|     val subtags: List<Tag> |     val subtags: List<Tag> | ||||||
| ) | ) { | ||||||
|  |     val adaptedTitle | ||||||
|  |         get() = title.adaptAsTitle() | ||||||
|  | } | ||||||
|  |  | ||||||
| private fun printLanguageCodeAndTags( | private fun printLanguageCodeAndTags( | ||||||
|     tag: Tag, |     tag: Tag, | ||||||
| @@ -81,17 +91,19 @@ private fun printLanguageCodeAndTags( | |||||||
|     indents: String = "    " |     indents: String = "    " | ||||||
| ): String = if (tag.subtags.isEmpty()) { | ): String = if (tag.subtags.isEmpty()) { | ||||||
| """${indents}${baseClassSerializerAnnotationName} | """${indents}${baseClassSerializerAnnotationName} | ||||||
| ${indents}object ${tag.title} : ${parent ?.title ?: baseClassName}() { override val code: String = "${tag.tag}"${parent ?.let { parent -> "; override val parentLang: ${parent.title} get() = ${parent.title};" } ?: ""} }""" | ${indents}object ${tag.adaptedTitle} : ${parent ?.adaptedTitle ?: baseClassName} { override val code: String = "${tag.tag}"${parent ?.let { parent -> "; override val parentLang: ${parent.adaptedTitle} get() = ${parent.adaptedTitle}" } ?: ""}; override fun toString() = code }""" | ||||||
| } else { | } else { | ||||||
| """ | """ | ||||||
| ${indents}${baseClassSerializerAnnotationName} | ${indents}${baseClassSerializerAnnotationName} | ||||||
| ${indents}sealed class ${tag.title} : ${parent ?.title ?: baseClassName}() { | ${indents}sealed interface ${tag.adaptedTitle} : ${parent ?.adaptedTitle ?: baseClassName} { | ||||||
| ${indents}    override val code: String = "${tag.tag}"${parent ?.let { parent -> "\n${indents}    override val parentLang: ${parent.title} get() = ${parent.title};" } ?: ""} |  | ||||||
|  |  | ||||||
| ${tag.subtags.joinToString("\n") { printLanguageCodeAndTags(it, tag, "${indents}    ") }} | ${tag.subtags.joinToString("\n") { printLanguageCodeAndTags(it, tag, "${indents}    ") }} | ||||||
|  |  | ||||||
| ${indents}    ${baseClassSerializerAnnotationName} | ${indents}    ${baseClassSerializerAnnotationName} | ||||||
| ${indents}    companion object : ${tag.title}() | ${indents}    companion object : ${tag.adaptedTitle} { | ||||||
|  | ${indents}        override val code: String = "${tag.tag}"${parent ?.let { parent -> "\n${indents}        override val parentLang: ${parent.adaptedTitle} get() = ${parent.adaptedTitle};" } ?: ""} | ||||||
|  | ${indents}        override fun toString() = code | ||||||
|  | ${indents}    } | ||||||
| ${indents}} | ${indents}} | ||||||
| """ | """ | ||||||
| } | } | ||||||
| @@ -105,23 +117,22 @@ import kotlinx.serialization.Serializable | |||||||
|  * https://datahub.io/core/language-codes/ files (base and tags) and create the whole hierarchy using it. |  * https://datahub.io/core/language-codes/ files (base and tags) and create the whole hierarchy using it. | ||||||
|  */ |  */ | ||||||
| ${baseClassSerializerAnnotationName} | ${baseClassSerializerAnnotationName} | ||||||
| sealed class $baseClassName { | sealed interface $baseClassName { | ||||||
|     abstract val code: String |     val code: String | ||||||
|     open val parentLang: $baseClassName? |     val parentLang: $baseClassName? | ||||||
|         get() = null |         get() = code.split("-").takeIf { it.size > 1 } ?.first() ?.let(::$unknownBaseClassName) | ||||||
|     open val withoutDialect: String |     val withoutDialect: String | ||||||
|         get() = parentLang ?.code ?: code |         get() = parentLang ?.code ?: code | ||||||
|  |  | ||||||
| ${tags.joinToString("\n") { printLanguageCodeAndTags(it, indents = "    ") } } | ${tags.joinToString("\n") { printLanguageCodeAndTags(it, indents = "    ") } } | ||||||
|  |  | ||||||
|     $baseClassSerializerAnnotationName |     $baseClassSerializerAnnotationName | ||||||
|     data class $unknownBaseClassName (override val code: String) : $baseClassName() { |     data class $unknownBaseClassName (override val code: String) : $baseClassName { | ||||||
|         override val parentLang = code.dropLastWhile { it != '-' }.removeSuffix("-").takeIf { it.length > 0 } ?.let(::$unknownBaseClassName) |         override val parentLang = code.dropLastWhile { it != '-' }.removeSuffix("-").takeIf { it.length > 0 } ?.let(::$unknownBaseClassName) | ||||||
|     } |     } | ||||||
|     @Deprecated("Renamed", ReplaceWith("$baseClassName.$unknownBaseClassName", "${if (prePackage.isNotEmpty()) "$prePackage." else ""}$baseClassName.$unknownBaseClassName")) |     @Deprecated("Renamed", ReplaceWith("$baseClassName.$unknownBaseClassName", "${if (prePackage.isNotEmpty()) "$prePackage." else ""}$baseClassName.$unknownBaseClassName")) | ||||||
|     val $oldUnknownBaseClassName = $unknownBaseClassName |     val $oldUnknownBaseClassName | ||||||
|  |         get() = $unknownBaseClassName | ||||||
|     override fun toString() = code |  | ||||||
| } | } | ||||||
| @Deprecated("Renamed", ReplaceWith("$baseClassName", "${if (prePackage.isNotEmpty()) "$prePackage." else ""}$baseClassName")) | @Deprecated("Renamed", ReplaceWith("$baseClassName", "${if (prePackage.isNotEmpty()) "$prePackage." else ""}$baseClassName")) | ||||||
| typealias $oldBaseClassName = $baseClassName | typealias $oldBaseClassName = $baseClassName | ||||||
| @@ -133,7 +144,7 @@ fun createStringConverterCode(tags: List<Tag>, prePackage: String): String { | |||||||
|         pretitle: String = baseClassName, |         pretitle: String = baseClassName, | ||||||
|         indents: String = "        " |         indents: String = "        " | ||||||
|     ): String { |     ): String { | ||||||
|         val currentTitle = "$pretitle.${tag.title}" |         val currentTitle = "$pretitle.${tag.adaptedTitle}" | ||||||
|         return """${indents}$currentTitle.code -> $currentTitle${if (tag.subtags.isNotEmpty()) tag.subtags.joinToString("\n", "\n") { createDeserializeVariantForTag(it, currentTitle, indents) } else ""}""" |         return """${indents}$currentTitle.code -> $currentTitle${if (tag.subtags.isNotEmpty()) tag.subtags.joinToString("\n", "\n") { createDeserializeVariantForTag(it, currentTitle, indents) } else ""}""" | ||||||
|     } |     } | ||||||
|     fun createInheritorVariantForTag( |     fun createInheritorVariantForTag( | ||||||
| @@ -141,7 +152,7 @@ fun createStringConverterCode(tags: List<Tag>, prePackage: String): String { | |||||||
|         pretitle: String = baseClassName, |         pretitle: String = baseClassName, | ||||||
|         indents: String = "        " |         indents: String = "        " | ||||||
|     ): String { |     ): String { | ||||||
|         val currentTitle = "$pretitle.${tag.title}" |         val currentTitle = "$pretitle.${tag.adaptedTitle}" | ||||||
|         val subtags = if (tag.subtags.isNotEmpty()) { |         val subtags = if (tag.subtags.isNotEmpty()) { | ||||||
|             tag.subtags.joinToString(",\n", ",\n") { createInheritorVariantForTag(it, currentTitle, "$indents    ") } |             tag.subtags.joinToString(",\n", ",\n") { createInheritorVariantForTag(it, currentTitle, "$indents    ") } | ||||||
|         } else { |         } else { | ||||||
| @@ -155,7 +166,7 @@ fun createStringConverterCode(tags: List<Tag>, prePackage: String): String { | |||||||
|         indents: String = "        ", |         indents: String = "        ", | ||||||
|         codeSuffix: String = "" |         codeSuffix: String = "" | ||||||
|     ): String { |     ): String { | ||||||
|         val currentTitle = "$pretitle.${tag.title}" |         val currentTitle = "$pretitle.${tag.adaptedTitle}" | ||||||
|         val subtags = if (tag.subtags.isNotEmpty()) { |         val subtags = if (tag.subtags.isNotEmpty()) { | ||||||
|             tag.subtags.joinToString(",\n", ",\n") { createInheritorVariantForMapForTag(it, currentTitle, "$indents    ", codeSuffix) } |             tag.subtags.joinToString(",\n", ",\n") { createInheritorVariantForMapForTag(it, currentTitle, "$indents    ", codeSuffix) } | ||||||
|         } else { |         } else { | ||||||
| @@ -266,7 +277,7 @@ suspend fun main(vararg args: String) { | |||||||
|     File(outputFolder, "LanguageCodes.kt").apply { |     File(outputFolder, "LanguageCodes.kt").apply { | ||||||
|         delete() |         delete() | ||||||
|         createNewFile() |         createNewFile() | ||||||
|         writeText(targetPackagePrefix + buildKtFileContent(tags, targetPackage ?: "")) |         writeText("@file:Suppress(\"SERIALIZER_TYPE_INCOMPATIBLE\")\n\n" + targetPackagePrefix + buildKtFileContent(tags, targetPackage ?: "")) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     File(outputFolder, "StringToLanguageCodes.kt").apply { |     File(outputFolder, "StringToLanguageCodes.kt").apply { | ||||||
|   | |||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user