Compare commits

...

9 Commits

Author SHA1 Message Date
827cf32c1b modules initialization 2024-06-25 22:04:25 +06:00
98ad6dbeb2 start 0.21.1 2024-06-25 22:03:30 +06:00
63c8f642ec Merge pull request #451 from InsanusMokrassar/0.21.0
0.21.0
2024-06-20 01:53:51 +06:00
3bfe64f797 Update CHANGELOG.md 2024-06-20 01:53:42 +06:00
ec98029467 get back old function to break less API 2024-06-19 20:03:14 +06:00
ab58478686 Revert "fix of build"
This reverts commit 90247667d1.
2024-06-19 19:50:19 +06:00
90247667d1 fix of build 2024-06-19 19:50:00 +06:00
e661185534 fill changelog 2024-06-19 19:36:02 +06:00
d73e4e8e1f migrate onto 0.21.0 2024-06-19 19:30:16 +06:00
16 changed files with 348 additions and 11 deletions

View File

@@ -1,6 +1,22 @@
# Changelog
## 0.20.53
## 0.21.1
* `KSP`:
* Module has been initialized
* `Generator`:
* Module has been initialized
* `Sealed`:
* Module has been initialized
## 0.21.0
**THIS UPDATE CONTAINS BREAKING CHANGES IN `safely*`-ORIENTED FUNCTIONS**
* `Coroutines`:
* **All `safely` functions lost their `supervisorScope` in favor to wrapping `runCatching`**
* `runCatchingSafely` is the main handling function of all `safely` functions
* `launchSafely*` and `asyncSafely*` blocks lost `CoroutineScope` as their receiver
## 0.20.52

View File

@@ -29,6 +29,13 @@ suspend inline fun <T> runCatchingSafely(
}
}
suspend inline fun <T, R> R.runCatchingSafely(
onException: ExceptionHandler<T>,
block: suspend R.() -> T
): Result<T> = runCatchingSafely<T>(onException) {
block()
}
/**
* Launching [runCatchingSafely] with [defaultSafelyExceptionHandler] as `onException` parameter
*/
@@ -36,6 +43,12 @@ suspend inline fun <T> runCatchingSafely(
block: suspend () -> T
): Result<T> = runCatchingSafely(defaultSafelyExceptionHandler, block)
suspend inline fun <T, R> R.runCatchingSafely(
block: suspend R.() -> T
): Result<T> = runCatchingSafely<T> {
block()
}
//suspend inline fun <T, R> T.runCatchingSafely(
// onException: ExceptionHandler<R>,
// block: suspend T.() -> R
@@ -94,11 +107,18 @@ suspend inline fun <T> safely(
suspend inline fun <T> safely(
block: suspend () -> T
): T = safely(defaultSafelyExceptionHandler, block)
suspend inline fun <T, R> R.safely(
block: suspend R.() -> T
): T = safely<T> { block() }
@Deprecated("Renamed", ReplaceWith("runCatchingSafely(block)", "dev.inmo.micro_utils.coroutines.runCatchingSafely"))
suspend fun <T> safelyWithResult(
block: suspend () -> T
): Result<T> = runCatchingSafely(defaultSafelyExceptionHandler, block)
@Deprecated("Renamed", ReplaceWith("this.runCatchingSafely(block)", "dev.inmo.micro_utils.coroutines.runCatchingSafely"))
suspend fun <T, R> R.safelyWithResult(
block: suspend R.() -> T
): Result<T> = safelyWithResult<T> { block() }
/**
* Use this handler in cases you wish to include handling of exceptions by [defaultSafelyWithoutExceptionHandler] and

View File

@@ -8,34 +8,42 @@ fun CoroutineScope.launchSafely(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
onException: ExceptionHandler<Unit> = defaultSafelyExceptionHandler,
block: suspend () -> Unit
block: suspend CoroutineScope.() -> Unit
) = launch(context, start) {
runCatchingSafely(onException, block = block)
runCatchingSafely(onException) {
block()
}
}
fun CoroutineScope.launchSafelyWithoutExceptions(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
onException: ExceptionHandler<Unit?> = defaultSafelyWithoutExceptionHandlerWithNull,
block: suspend () -> Unit
block: suspend CoroutineScope.() -> Unit
) = launch(context, start) {
runCatchingSafelyWithoutExceptions(onException, block = block)
runCatchingSafelyWithoutExceptions(onException) {
block()
}
}
fun <T> CoroutineScope.asyncSafely(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
onException: ExceptionHandler<T> = defaultSafelyExceptionHandler,
block: suspend () -> T
block: suspend CoroutineScope.() -> T
) = async(context, start) {
runCatchingSafely(onException, block = block)
runCatchingSafely(onException) {
block()
}
}
fun <T> CoroutineScope.asyncSafelyWithoutExceptions(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
onException: ExceptionHandler<T?> = defaultSafelyWithoutExceptionHandlerWithNull,
block: suspend () -> T
block: suspend CoroutineScope.() -> T
) = async(context, start) {
runCatchingSafelyWithoutExceptions(onException, block = block)
runCatchingSafelyWithoutExceptions(onException) {
block()
}
}

View File

@@ -15,5 +15,5 @@ crypto_js_version=4.1.1
# Project data
group=dev.inmo
version=0.20.53
android_code_version=259
version=0.21.1
android_code_version=260

View File

@@ -0,0 +1,20 @@
plugins {
id "org.jetbrains.kotlin.jvm"
}
apply from: "$publishJvmOnlyPath"
repositories {
mavenCentral()
}
dependencies {
api project(":micro_utils.common")
api libs.kotlin.poet
api libs.ksp
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

View File

@@ -0,0 +1,49 @@
package dev.inmo.micro_ksp.generator
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSFile
import com.squareup.kotlinpoet.FileSpec
import java.io.File
fun KSClassDeclaration.writeFile(
prefix: String = "",
suffix: String = "",
relatedPath: String = "",
force: Boolean = false,
fileSpecBuilder: () -> FileSpec
) {
val containingFile = containingFile!!
File(
File(
File(containingFile.filePath).parent,
relatedPath
),
"$prefix${simpleName.asString()}$suffix.kt"
).takeIf { force || !it.exists() } ?.apply {
parentFile.mkdirs()
writer().use { writer ->
fileSpecBuilder().writeTo(writer)
}
}
}
fun KSFile.writeFile(
prefix: String = "",
suffix: String = "",
relatedPath: String = "",
force: Boolean = false,
fileSpecBuilder: () -> FileSpec
) {
File(
File(
File(filePath).parent,
relatedPath
),
"$prefix${fileName.dropLastWhile { it != '.' }.removeSuffix(".")}$suffix.kt"
).takeIf { force || !it.exists() } ?.apply {
parentFile.mkdirs()
writer().use { writer ->
fileSpecBuilder().writeTo(writer)
}
}
}

7
ksp/sealed/build.gradle Normal file
View File

@@ -0,0 +1,7 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library"
}
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath"

View File

@@ -0,0 +1,21 @@
plugins {
id "org.jetbrains.kotlin.jvm"
}
apply from: "$publishJvmOnlyPath"
repositories {
mavenCentral()
}
dependencies {
api project(":micro_utils.ksp.generator")
api project(":micro_utils.ksp.sealed")
api libs.kotlin.poet
api libs.ksp
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

View File

@@ -0,0 +1,110 @@
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.processing.CodeGenerator
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.symbol.*
import com.squareup.kotlinpoet.AnnotationSpec
import com.squareup.kotlinpoet.ClassName
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.PropertySpec
import com.squareup.kotlinpoet.asTypeName
import com.squareup.kotlinpoet.ksp.toClassName
import dev.inmo.micro_ksp.generator.writeFile
import dev.inmo.microutils.kps.sealed.GenerateSealedWorkaround
class Processor(
private val codeGenerator: CodeGenerator
) : SymbolProcessor {
private fun KSClassDeclaration.resolveSubclasses(): List<KSClassDeclaration> {
return (getSealedSubclasses().flatMap {
it.resolveSubclasses()
}.ifEmpty {
sequenceOf(this)
}).toList()
}
@OptIn(KspExperimental::class)
private fun FileSpec.Builder.generateSealedWorkaround(
ksClassDeclaration: KSClassDeclaration,
resolver: Resolver
) {
val subClasses = ksClassDeclaration.resolveSubclasses().distinct()
val subClassesNames = subClasses.filter {
when (it.classKind) {
ClassKind.ENUM_ENTRY,
ClassKind.OBJECT -> true
ClassKind.INTERFACE,
ClassKind.CLASS,
ClassKind.ENUM_CLASS,
ClassKind.ANNOTATION_CLASS -> false
}
}.filter {
it.getAnnotationsByType(GenerateSealedWorkaround.Exclude::class).count() == 0
}.sortedBy {
(it.getAnnotationsByType(GenerateSealedWorkaround.Order::class).firstOrNull()) ?.order ?: 0
}.map {
it.toClassName()
}
val className = ksClassDeclaration.toClassName()
val setType = Set::class.asTypeName().parameterizedBy(
ksClassDeclaration.toClassName()
)
addProperty(
PropertySpec.builder(
"values",
setType
).apply {
modifiers.add(
KModifier.PRIVATE
)
initializer(
CodeBlock.of(
"""setOf(${subClassesNames.joinToString(",\n") {it.simpleNames.joinToString(".")}})"""
)
)
}.build()
)
addFunction(
FunSpec.builder("values").apply {
receiver(ClassName(className.packageName, *className.simpleNames.toTypedArray(), "Companion"))
returns(setType)
addCode(
CodeBlock.of(
"""return values"""
)
)
}.build()
)
}
@OptIn(KspExperimental::class)
override fun process(resolver: Resolver): List<KSAnnotated> {
(resolver.getSymbolsWithAnnotation(GenerateSealedWorkaround::class.qualifiedName!!)).filterIsInstance<KSClassDeclaration>().forEach {
val prefix = it.getAnnotationsByType(GenerateSealedWorkaround::class).first().prefix
it.writeFile(prefix = prefix, suffix = "SealedWorkaround") {
FileSpec.builder(
it.packageName.asString(),
"${it.simpleName.getShortName()}SealedWorkaround"
).apply {
addFileComment(
"""
THIS CODE HAVE BEEN GENERATED AUTOMATICALLY
TO REGENERATE IT JUST DELETE FILE
ORIGINAL FILE: ${it.containingFile ?.fileName}
""".trimIndent()
)
generateSealedWorkaround(it, resolver)
}.build()
}
}
return emptyList()
}
}

View File

@@ -0,0 +1,11 @@
package dev.inmo.micro_utils.ksp.sealed.generator
import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
import com.google.devtools.ksp.processing.SymbolProcessorProvider
class Provider : SymbolProcessorProvider {
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor = Processor(
environment.codeGenerator
)
}

View File

@@ -0,0 +1 @@
dev.inmo.micro_utils.ksp.sealed.generator.Provider

View File

@@ -0,0 +1,27 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library"
id "com.google.devtools.ksp"
}
apply from: "$mppProjectWithSerializationPresetPath"
kotlin {
sourceSets {
commonMain {
dependencies {
api project(":micro_utils.ksp.sealed")
}
}
}
}
dependencies {
add("kspCommonMainMetadata", project(":micro_utils.ksp.sealed.generator"))
}
ksp {
}

View File

@@ -0,0 +1,16 @@
package dev.inmo.micro_utils.ksp.sealed.generator.test
import dev.inmo.microutils.kps.sealed.GenerateSealedWorkaround
@GenerateSealedWorkaround
sealed interface Test {
@GenerateSealedWorkaround.Order(2)
object A : Test
@GenerateSealedWorkaround.Exclude
object B : Test
@GenerateSealedWorkaround.Order(0)
object C : Test
// Required for successful sealed workaround generation
companion object
}

View File

@@ -0,0 +1,11 @@
// 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
private val values: Set<Test> = setOf(Test.C,
Test.A)
public fun Test.Companion.values(): Set<Test> = values

View File

@@ -0,0 +1,14 @@
package dev.inmo.microutils.kps.sealed
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS)
annotation class GenerateSealedWorkaround(
val prefix: String = ""
) {
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS)
annotation class Order(val order: Int)
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS)
annotation class Exclude
}

View File

@@ -49,6 +49,12 @@ String[] includes = [
":fsm:common",
":fsm:repos:common",
":ksp:generator",
":ksp:sealed",
":ksp:sealed:generator",
":ksp:sealed:generator:test",
":dokka"
]