mirror of
				https://github.com/InsanusMokrassar/MicroUtils.git
				synced 2025-10-31 12:10:29 +00:00 
			
		
		
		
	Compare commits
	
		
			24 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 59e0e751f1 | |||
| a5ae5e6c2b | |||
| a2b87e63c9 | |||
| d95c283653 | |||
| 6a9ae0c148 | |||
| 040dd517d8 | |||
| 9663c1ca64 | |||
| ffc2d23be7 | |||
| 49b009e59b | |||
| 778b6a555b | |||
| b3730998e9 | |||
| 837758aebe | |||
| 6ac4149aa1 | |||
| 278584ae6a | |||
| 126f9d5f41 | |||
| ce15ff4e0a | |||
| 382b956beb | |||
| 36deab4909 | |||
| 550fc59d9d | |||
| 9100a57458 | |||
| 74d9bbccd9 | |||
| 75e602a349 | |||
| e73644db10 | |||
| 148e6bdae7 | 
							
								
								
									
										21
									
								
								.github/workflows/dokka_push.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								.github/workflows/dokka_push.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | name: Publish KDocs | ||||||
|  | on: | ||||||
|  |   push: | ||||||
|  |     branches: | ||||||
|  |       - master | ||||||
|  | jobs: | ||||||
|  |   publishing: | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |     steps: | ||||||
|  |       - uses: actions/checkout@v2 | ||||||
|  |       - uses: actions/setup-java@v1 | ||||||
|  |         with: | ||||||
|  |           java-version: 1.8 | ||||||
|  |       - name: Build | ||||||
|  |         run: ./gradlew dokkaHtml | ||||||
|  |       - name: Publish KDocs | ||||||
|  |         uses: peaceiris/actions-gh-pages@v3 | ||||||
|  |         with: | ||||||
|  |           github_token: ${{ secrets.GITHUB_TOKEN }} | ||||||
|  |           publish_dir: ./dokka/build/dokka/html | ||||||
|  |           publish_branch: kdocs | ||||||
							
								
								
									
										34
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -1,5 +1,39 @@ | |||||||
| # Changelog | # Changelog | ||||||
|  |  | ||||||
|  | ## 0.4.36 | ||||||
|  |  | ||||||
|  | * All `Android` targets inside common mpp modules now includes JVM code | ||||||
|  |  | ||||||
|  | ## 0.4.35 | ||||||
|  |  | ||||||
|  | * `Versions`: | ||||||
|  |     * `Kotlin Exposed`: `0.30.1` -> `0.30.2` | ||||||
|  | * `Serialization`: | ||||||
|  |     * `TypedSerializer`: | ||||||
|  |         * Project has been inited | ||||||
|  |  | ||||||
|  | ## 0.4.34 | ||||||
|  |  | ||||||
|  | * `Versions`: | ||||||
|  |     * `uuid`: `0.2.3` -> `0.2.4` | ||||||
|  | * `Repos`: | ||||||
|  |     * `AbstractExposedCRUDRepo` now implements `StandardCRUDRepo` | ||||||
|  |     * `AbstractMutableAndroidCRUDRepo` now implements `StandardCRUDRepo` | ||||||
|  |  | ||||||
|  | ## 0.4.33 | ||||||
|  |  | ||||||
|  | * `Versions`: | ||||||
|  |     * `Ktor`: `1.5.2` -> `1.5.3` | ||||||
|  | * `Coroutines` | ||||||
|  |     * Add `WeakJob` workaround: | ||||||
|  |         * `CoroutineScope#weakLaunch` | ||||||
|  |         * `CoroutineScope#weakAsync` | ||||||
|  |  | ||||||
|  | ## 0.4.32 | ||||||
|  |  | ||||||
|  | * `Versions`: | ||||||
|  |     * `Kotlin Exposed`: `0.29.1` -> `0.30.1` | ||||||
|  |  | ||||||
| ## 0.4.31 | ## 0.4.31 | ||||||
|  |  | ||||||
| * `Versions`: | * `Versions`: | ||||||
|   | |||||||
| @@ -25,6 +25,15 @@ allprojects { | |||||||
|         google() |         google() | ||||||
|         maven { url "https://kotlin.bintray.com/kotlinx" } |         maven { url "https://kotlin.bintray.com/kotlinx" } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // 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" | ||||||
|   | |||||||
| @@ -0,0 +1,31 @@ | |||||||
|  | package dev.inmo.micro_utils.coroutines | ||||||
|  |  | ||||||
|  | import kotlinx.coroutines.* | ||||||
|  | import kotlin.coroutines.CoroutineContext | ||||||
|  | import kotlin.coroutines.EmptyCoroutineContext | ||||||
|  |  | ||||||
|  | private fun CoroutineScope.createWeakSubScope() = CoroutineScope(coroutineContext.minusKey(Job)).also { newScope -> | ||||||
|  |     coroutineContext.job.invokeOnCompletion { newScope.cancel() } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fun CoroutineScope.weakLaunch( | ||||||
|  |     context: CoroutineContext = EmptyCoroutineContext, | ||||||
|  |     start: CoroutineStart = CoroutineStart.DEFAULT, | ||||||
|  |     block: suspend CoroutineScope.() -> Unit | ||||||
|  | ): Job { | ||||||
|  |     val scope = createWeakSubScope() | ||||||
|  |     val job = scope.launch(context, start, block) | ||||||
|  |     job.invokeOnCompletion { scope.cancel() } | ||||||
|  |     return job | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fun <T> CoroutineScope.weakAsync( | ||||||
|  |     context: CoroutineContext = EmptyCoroutineContext, | ||||||
|  |     start: CoroutineStart = CoroutineStart.DEFAULT, | ||||||
|  |     block: suspend CoroutineScope.() -> T | ||||||
|  | ): Deferred<T> { | ||||||
|  |     val scope = createWeakSubScope() | ||||||
|  |     val deferred = scope.async(context, start, block) | ||||||
|  |     deferred.invokeOnCompletion { scope.cancel() } | ||||||
|  |     return deferred | ||||||
|  | } | ||||||
| @@ -0,0 +1,40 @@ | |||||||
|  | package dev.inmo.micro_utils.coroutines | ||||||
|  |  | ||||||
|  | import kotlinx.coroutines.* | ||||||
|  | import org.junit.Test | ||||||
|  |  | ||||||
|  | class WeakJob { | ||||||
|  |     @Test | ||||||
|  |     fun `test that weak jobs works correctly`() { | ||||||
|  |         val scope = CoroutineScope(Dispatchers.Default) | ||||||
|  |         lateinit var weakLaunchJob: Job | ||||||
|  |         lateinit var weakAsyncJob: Job | ||||||
|  |         scope.launchSynchronously { | ||||||
|  |             val completeDeferred = Job() | ||||||
|  |             coroutineScope { | ||||||
|  |                 weakLaunchJob = weakLaunch { | ||||||
|  |                     while (isActive) { | ||||||
|  |                         delay(100L) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 weakAsyncJob = weakAsync { | ||||||
|  |                     while (isActive) { | ||||||
|  |                         delay(100L) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 coroutineContext.job.invokeOnCompletion { | ||||||
|  |                     scope.launch { | ||||||
|  |                         delay(1000L) | ||||||
|  |                         completeDeferred.complete() | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 launch { delay(1000L); cancel() } | ||||||
|  |             } | ||||||
|  |             completeDeferred.join() | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         assert(!weakLaunchJob.isActive) | ||||||
|  |         assert(!weakAsyncJob.isActive) | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -20,11 +20,11 @@ allprojects { | |||||||
|  |  | ||||||
|         releaseMode = (project.hasProperty('RELEASE_MODE') && project.property('RELEASE_MODE') == "true") || System.getenv('RELEASE_MODE') == "true" |         releaseMode = (project.hasProperty('RELEASE_MODE') && project.property('RELEASE_MODE') == "true") || System.getenv('RELEASE_MODE') == "true" | ||||||
|  |  | ||||||
|         mppProjectWithSerializationPresetPath = "${rootProject.projectDir.absolutePath}/mppProjectWithSerialization" |         mppProjectWithSerializationPresetPath = "${rootProject.projectDir.absolutePath}/mppProjectWithSerialization.gradle" | ||||||
|         mppJavaProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppJavaProject" |         mppJavaProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppJavaProject.gradle" | ||||||
|         mppAndroidProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppAndroidProject" |         mppAndroidProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppAndroidProject.gradle" | ||||||
|  |  | ||||||
|         defaultAndroidSettingsPresetPath = "${rootProject.projectDir.absolutePath}/defaultAndroidSettings" |         defaultAndroidSettingsPresetPath = "${rootProject.projectDir.absolutePath}/defaultAndroidSettings.gradle" | ||||||
|  |  | ||||||
|         publishGradlePath = "${rootProject.projectDir.absolutePath}/publish.gradle" |         publishGradlePath = "${rootProject.projectDir.absolutePath}/publish.gradle" | ||||||
|         publishMavenPath = "${rootProject.projectDir.absolutePath}/maven.publish.gradle" |         publishMavenPath = "${rootProject.projectDir.absolutePath}/maven.publish.gradle" | ||||||
|   | |||||||
| @@ -9,15 +9,15 @@ android.enableJetifier=true | |||||||
| kotlin_version=1.4.32 | kotlin_version=1.4.32 | ||||||
| kotlin_coroutines_version=1.4.3 | kotlin_coroutines_version=1.4.3 | ||||||
| kotlin_serialisation_core_version=1.1.0 | kotlin_serialisation_core_version=1.1.0 | ||||||
| kotlin_exposed_version=0.29.1 | kotlin_exposed_version=0.30.2 | ||||||
|  |  | ||||||
| ktor_version=1.5.2 | ktor_version=1.5.3 | ||||||
|  |  | ||||||
| klockVersion=2.0.7 | klockVersion=2.0.7 | ||||||
|  |  | ||||||
| github_release_plugin_version=2.2.12 | github_release_plugin_version=2.2.12 | ||||||
|  |  | ||||||
| uuidVersion=0.2.3 | uuidVersion=0.2.4 | ||||||
|  |  | ||||||
| # ANDROID | # ANDROID | ||||||
|  |  | ||||||
| @@ -39,10 +39,10 @@ crypto_js_version=4.0.0 | |||||||
|  |  | ||||||
| # Dokka | # Dokka | ||||||
|  |  | ||||||
| dokka_version=1.4.20 | dokka_version=1.4.30 | ||||||
|  |  | ||||||
| # Project data | # Project data | ||||||
|  |  | ||||||
| group=dev.inmo | group=dev.inmo | ||||||
| version=0.4.31 | version=0.4.36 | ||||||
| android_code_version=35 | android_code_version=40 | ||||||
|   | |||||||
| @@ -8,7 +8,8 @@ import kotlinx.coroutines.flow.* | |||||||
| abstract class AbstractMutableAndroidCRUDRepo<ObjectType, IdType, InputValueType>( | abstract class AbstractMutableAndroidCRUDRepo<ObjectType, IdType, InputValueType>( | ||||||
|     helper: StandardSQLHelper |     helper: StandardSQLHelper | ||||||
| ) : WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>, | ) : WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>, | ||||||
|     AbstractAndroidCRUDRepo<ObjectType, IdType>(helper) { |     AbstractAndroidCRUDRepo<ObjectType, IdType>(helper), | ||||||
|  |     StandardCRUDRepo<ObjectType, IdType, InputValueType> { | ||||||
|     protected val newObjectsChannel = MutableSharedFlow<ObjectType>(64) |     protected val newObjectsChannel = MutableSharedFlow<ObjectType>(64) | ||||||
|     protected val updateObjectsChannel = MutableSharedFlow<ObjectType>(64) |     protected val updateObjectsChannel = MutableSharedFlow<ObjectType>(64) | ||||||
|     protected val deleteObjectsIdsChannel = MutableSharedFlow<IdType>(64) |     protected val deleteObjectsIdsChannel = MutableSharedFlow<IdType>(64) | ||||||
|   | |||||||
| @@ -1,5 +1,7 @@ | |||||||
| package dev.inmo.micro_utils.repos.exposed | package dev.inmo.micro_utils.repos.exposed | ||||||
|  |  | ||||||
|  | import dev.inmo.micro_utils.repos.StandardCRUDRepo | ||||||
|  |  | ||||||
| abstract class AbstractExposedCRUDRepo<ObjectType, IdType, InputValueType>( | abstract class AbstractExposedCRUDRepo<ObjectType, IdType, InputValueType>( | ||||||
|     flowsChannelsSize: Int = 0, |     flowsChannelsSize: Int = 0, | ||||||
|     tableName: String = "" |     tableName: String = "" | ||||||
| @@ -8,4 +10,5 @@ abstract class AbstractExposedCRUDRepo<ObjectType, IdType, InputValueType>( | |||||||
|         flowsChannelsSize, |         flowsChannelsSize, | ||||||
|         tableName |         tableName | ||||||
|     ), |     ), | ||||||
|     ExposedCRUDRepo<ObjectType, IdType> |     ExposedCRUDRepo<ObjectType, IdType>, | ||||||
|  |     StandardCRUDRepo<ObjectType, IdType, InputValueType> | ||||||
|   | |||||||
							
								
								
									
										7
									
								
								serialization/typed_serializer/build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								serialization/typed_serializer/build.gradle
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | plugins { | ||||||
|  |     id "org.jetbrains.kotlin.multiplatform" | ||||||
|  |     id "org.jetbrains.kotlin.plugin.serialization" | ||||||
|  |     id "com.android.library" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | apply from: "$mppProjectWithSerializationPresetPath" | ||||||
| @@ -0,0 +1,74 @@ | |||||||
|  | package dev.inmo.micro_utils.serialization.typed_serializer | ||||||
|  |  | ||||||
|  | import kotlinx.serialization.* | ||||||
|  | import kotlinx.serialization.builtins.serializer | ||||||
|  | import kotlinx.serialization.descriptors.* | ||||||
|  | import kotlinx.serialization.encoding.* | ||||||
|  | import kotlin.reflect.KClass | ||||||
|  |  | ||||||
|  | open class TypedSerializer<T : Any>( | ||||||
|  |     kClass: KClass<T>, | ||||||
|  |     presetSerializers: Map<String, KSerializer<out T>> = emptyMap() | ||||||
|  | ) : KSerializer<T> { | ||||||
|  |     protected val serializers = presetSerializers.toMutableMap() | ||||||
|  |     @InternalSerializationApi | ||||||
|  |     override open val descriptor: SerialDescriptor = buildSerialDescriptor( | ||||||
|  |         "TextSourceSerializer", | ||||||
|  |         SerialKind.CONTEXTUAL | ||||||
|  |     ) { | ||||||
|  |         element("type", String.serializer().descriptor) | ||||||
|  |         element("value", ContextualSerializer(kClass).descriptor) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @InternalSerializationApi | ||||||
|  |     override open fun deserialize(decoder: Decoder): T { | ||||||
|  |         return decoder.decodeStructure(descriptor) { | ||||||
|  |             var type: String? = null | ||||||
|  |             lateinit var result: T | ||||||
|  |             while (true) { | ||||||
|  |                 when (val index = decodeElementIndex(descriptor)) { | ||||||
|  |                     0 -> type = decodeStringElement(descriptor, 0) | ||||||
|  |                     1 -> { | ||||||
|  |                         require(type != null) { "Type is null, but it is expected that was inited already" } | ||||||
|  |                         result = decodeSerializableElement( | ||||||
|  |                             descriptor, | ||||||
|  |                             1, | ||||||
|  |                             serializers.getValue(type) | ||||||
|  |                         ) | ||||||
|  |                     } | ||||||
|  |                     CompositeDecoder.DECODE_DONE -> break | ||||||
|  |                     else -> error("Unexpected index: $index") | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             result | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @InternalSerializationApi | ||||||
|  |     protected open fun <O: T> CompositeEncoder.encode(value: O) { | ||||||
|  |         encodeSerializableElement(descriptor, 1, value::class.serializer() as KSerializer<O>, value) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @InternalSerializationApi | ||||||
|  |     override open fun serialize(encoder: Encoder, value: T) { | ||||||
|  |         encoder.encodeStructure(descriptor) { | ||||||
|  |             val valueSerializer = value::class.serializer() | ||||||
|  |             val type = serializers.keys.first { serializers[it] == valueSerializer } | ||||||
|  |             encodeStringElement(descriptor, 0, type) | ||||||
|  |             encode(value) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     open fun <O: T> include(type: String, serializer: KSerializer<O>) { | ||||||
|  |         serializers[type] = serializer | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     open fun exclude(type: String) { | ||||||
|  |         serializers.remove(type) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | inline fun <reified T : Any> TypedSerializer( | ||||||
|  |     presetSerializers: Map<String, KSerializer<out T>> = emptyMap() | ||||||
|  | ) = TypedSerializer(T::class, presetSerializers) | ||||||
| @@ -0,0 +1,40 @@ | |||||||
|  | package dev.inmo.micro_utils.serialization.typed_serializer | ||||||
|  |  | ||||||
|  | import kotlinx.serialization.Serializable | ||||||
|  | import kotlinx.serialization.builtins.ListSerializer | ||||||
|  | import kotlinx.serialization.json.Json | ||||||
|  | import kotlin.random.Random | ||||||
|  | import kotlin.test.Test | ||||||
|  | import kotlin.test.assertEquals | ||||||
|  |  | ||||||
|  | class TypedSerializerTests { | ||||||
|  |     interface Example { | ||||||
|  |         val number: Number | ||||||
|  |     } | ||||||
|  |     val serialFormat = Json {  } | ||||||
|  |  | ||||||
|  |     @Serializable | ||||||
|  |     data class Example1(override val number: Long) : Example | ||||||
|  |  | ||||||
|  |     @Serializable | ||||||
|  |     data class Example2(override val number: Double) : Example | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     fun testThatSerializerWorksCorrectly() { | ||||||
|  |         val serializer = TypedSerializer( | ||||||
|  |             mapOf( | ||||||
|  |                 "long" to Example1.serializer(), | ||||||
|  |                 "double" to Example2.serializer() | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         val value1 = Example1(Random.nextLong()) | ||||||
|  |         val value2 = Example2(Random.nextDouble()) | ||||||
|  |  | ||||||
|  |         val list = listOf(value1, value2) | ||||||
|  |         val serialized = serialFormat.encodeToString(ListSerializer(serializer), list) | ||||||
|  |         val deserialized = serialFormat.decodeFromString(ListSerializer(serializer), serialized) | ||||||
|  |  | ||||||
|  |         assertEquals(list, deserialized) | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1 @@ | |||||||
|  | <manifest package="dev.inmo.micro_utils.serialization.typed_serializer"/> | ||||||
| @@ -26,6 +26,7 @@ String[] includes = [ | |||||||
|     ":android:alerts:recyclerview", |     ":android:alerts:recyclerview", | ||||||
|     ":serialization:base64", |     ":serialization:base64", | ||||||
|     ":serialization:encapsulator", |     ":serialization:encapsulator", | ||||||
|  |     ":serialization:typed_serializer", | ||||||
|  |  | ||||||
|     ":dokka" |     ":dokka" | ||||||
| ] | ] | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user