-
-
Notifications
You must be signed in to change notification settings - Fork 45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add class named & string/class qualifier #108
feat: add class named & string/class qualifier #108
Conversation
Can you also support complex types? |
Why do you need such a complex structure? |
Well, imagine So that's why I think that if we adding support for type-based qualifiers, then we need to at least support generic-types. |
possible solutionNested Structuresealed interface ListQualifier {
data object Int : ListQualifier
sealed interface Str : ListQualifier {
data object String : Str
}
}
@Module
class ListModule {
@Singleton(createdAtStart = true)
@Qualifier(ListQualifier.Int::class)
fun provideIntList() = listOf(1, 2, 3)
@Singleton(createdAtStart = true)
@Qualifier(ListQualifier.Str.String::class)
fun provideStringList() = listOf("hi", "hello")
}
class MyActivity : ActivityScope() {
private val intList: List<Int> by inject(qualifier<ListQualifier.Int>())
private val stringList: List<String> by inject(qualifier<ListQualifier.Str.String>())
} Flat Structureannotation class IntListQualifier
annotation class StringListQualifier
@Module
class ListModule {
@Singleton(createdAtStart = true)
@Qualifier(IntListQualifier::class)
fun provideIntList() = listOf(1, 2, 3)
@Singleton(createdAtStart = true)
@Qualifier(StringListQualifier::class)
fun provideStringList() = listOf("hi", "hello")
}
class MyActivity : ActivityScope() {
private val intList: List<Int> by inject(qualifier<IntListQualifier>())
private val stringList: List<String> by inject(qualifier<StringListQualifier>())
} Generated CodeNested Structuresingle(qualifier=org.koin.core.qualifier.StringQualifier("com.parsuomash.sdk.di.qualifier.ListQualifier\$Int"),createdAtStart=true) { moduleInstance.provideIntList() } bind(kotlin.collections.List::class)
single(qualifier=org.koin.core.qualifier.StringQualifier("com.parsuomash.sdk.di.qualifier.ListQualifier\$Str\$String"),createdAtStart=true) { moduleInstance.provideStringList() } bind(kotlin.collections.List::class) Flat Structuresingle(qualifier=org.koin.core.qualifier.StringQualifier("com.parsuomash.sdk.di.qualifier.IntListQualifier"),createdAtStart=true) { moduleInstance.provideIntList() } bind(kotlin.collections.List::class)
single(qualifier=org.koin.core.qualifier.StringQualifier("com.parsuomash.sdk.di.qualifier.StringListQualifier"),createdAtStart=true) { moduleInstance.provideStringList() } bind(kotlin.collections.List::class) |
Well that looks kinda hacky. The flat structure looks interesting (except it quickly gets verbose). inline function (aka |
Here is an example of how import kotlin.reflect.KClass
annotation class TypeQualifier(val value: KClass<*>, vararg val params: TypeQualifier)
val TypeQualifier.qualifier: String get() {
var qualifier = value.qualifiedName!!
if (params.isNotEmpty())
qualifier += "<" + params.joinToString { it.qualifier } + ">"
return qualifier
}
inline fun <reified T : Any> typeQualifierOf(value: T): TypeQualifier = when (value) {
is KClass<*> -> TypeQualifier(value)
is TypeQualifier -> value
else -> TypeQualifier(T::class)
}
inline fun typeQualifierOf(value: Any, vararg params: Any)
= TypeQualifier(typeQualifierOf(value).value, *params.map { typeQualifierOf(it) }.toTypedArray())
inline fun <reified T : Any> typeQualifierOfT() = typeQualifierOf(T::class)
inline fun <reified T : Any, reified T1 : Any> typeQualifierOfT2() = typeQualifierOf(T::class, T1::class)
inline fun <reified T : Any, reified T1 : Any, reified T2 : Any> typeQualifierOfT3() = typeQualifierOf(T::class, T1::class, T2::class)
inline fun <reified T : Any, reified T1 : Any, reified T2 : Any, reified T3 : Any> typeQualifierOfT4() = typeQualifierOf(T::class, T1::class, T2::class, T3::class)
operator fun TypeQualifier.plus(other: KClass<*>)
= this + other.toTypeQualifier()
operator fun KClass<*>.plus(other: TypeQualifier)
= toTypeQualifier() + other
fun TypeQualifier.addParam(other: TypeQualifier)
= TypeQualifier(this.value, *params, other)
fun KClass<*>.toTypeQualifier() = typeQualifierOf(this)
// Usage example
inline fun typeQualifierOfMapOf(KType: Any, VType: Any)
= typeQualifierOf(Map::class, KType, VType)
// K, V can't be of complex type, but it's shorter
inline fun <reified K : Any, reified V : Any> typeQualifierOfMapOf()
= Map::class + typeQualifierOfT2<K, V>()
fun main() {
println(typeQualifierOfMapOf(typeQualifierOfMapOf<Int, String>(), String::class).qualifier)
} Use of inline functions with reified types is discussional, since it allows only simple types to be used. Also.. here is a DSL alternative import kotlin.reflect.KClass
annotation class TypeQualifier(val value: KClass<*>, vararg val params: TypeQualifier)
val TypeQualifier.qualifier: String get() {
var qualifier = value.qualifiedName!!
if (params.isNotEmpty())
qualifier += "<" + params.joinToString { it.qualifier } + ">"
return qualifier
}
inline fun <reified T : Any> typeQualifierOf(value: T): TypeQualifier = when (value) {
is KClass<*> -> TypeQualifier(value)
is TypeQualifier -> value
else -> TypeQualifier(T::class)
}
inline fun typeQualifierOf(value: Any, vararg params: Any)
= TypeQualifier(typeQualifierOf(value).value, *params.map { typeQualifierOf(it) }.toTypedArray())
operator fun TypeQualifier.plus(other: TypeQualifier)
= TypeQualifier(this.value, *params, TypeQualifier(other.value), *other.params)
fun TypeQualifier.addParam(other: TypeQualifier)
= TypeQualifier(this.value, *params, other)
operator fun TypeQualifier.plus(other: KClass<*>)
= this + other.toTypeQualifier()
operator fun KClass<*>.plus(other: TypeQualifier)
= toTypeQualifier() + other
fun KClass<*>.toTypeQualifier() = typeQualifierOf(this)
interface TypeQualifierBuilder {
val qualifier: TypeQualifier
operator fun KClass<*>.unaryPlus()
operator fun KClass<*>.invoke(builder: TypeQualifierBuilder.() -> Unit)
}
inline fun <reified T> TypeQualifierBuilder.add() { +T::class }
private class TypeQualifierBuilderImpl : TypeQualifierBuilder {
private var _qualifier: TypeQualifier? = null
override val qualifier get() = _qualifier ?: Unit::class.toTypeQualifier()
private fun addTypeQualifier(value: TypeQualifier) {
_qualifier = _qualifier?.addParam(value) ?: value
}
override fun KClass<*>.unaryPlus() {
addTypeQualifier(this.toTypeQualifier())
}
override fun KClass<*>.invoke(builder: TypeQualifierBuilder.() -> Unit) {
val newQualifier = TypeQualifierBuilderImpl().apply {
+this@invoke
builder()
}.qualifier
addTypeQualifier(newQualifier)
}
}
fun buildTypeQualifier(builder: TypeQualifierBuilder.() -> Unit)
= TypeQualifierBuilderImpl().apply(builder).qualifier
fun main() {
val typeQualifier = buildTypeQualifier {
Map::class {
Map::class {
add<Int>()
+String::class
}
add<String>()
}
}
println(typeQualifier.qualifier)
} |
Also please provide any feedback on my reasoning and code above, because, well, I see reflection of our discussion in commit name, but not in code changes itself. |
I think this method can also be implemented in another PR. |
Anyway your current annotation can't be used to build complex type structure (yeah that recursive-annotation thing, what I suggest, looks even more horrible than my DSL), am I right? |
I should clarify my intentions: even if u don't want to implement support for complex types by yourself, please at least add support for that at annotation side, so next changes can be implemented incrementally, without need of breaking existing interface (without need of rewrite TypeQualifier annotation) |
Ghm.. this whole time we can just use import kotlin.reflect.KClass
import kotlin.reflect.KType
import kotlin.reflect.jvm.jvmErasure
import kotlin.reflect.typeOf
annotation class TypeQualifier(val value: KClass<*>, vararg val params: TypeQualifier)
val TypeQualifier.qualifier: String get() {
var qualifier = value.qualifiedName!!
if (params.isNotEmpty())
qualifier += "<" + params.joinToString { it.qualifier } + ">"
return qualifier
}
fun typeQualifierOf(type: KType): TypeQualifier
= TypeQualifier(type.jvmErasure, *type.arguments.map {
typeQualifierOf(it.type!!)
}.toTypedArray())
inline fun <reified T : Any> typeQualifierOf() = typeQualifierOf(typeOf<T>())
fun main() {
println(typeQualifierOf<Map<Map<Int, String>, String>>().qualifier)
} |
So.. now such simple thing can be implemented in the scope of the same PR, what do you think? |
Would be valuable if defining type annotations could be less verbose. Usually a qualifying annotation is used in many places in the code causing it to look cluttered. Instead of this:
Prefer less verbose definitions like:
This way |
...iler/koin-ksp-compiler/src/jvmMain/kotlin/org/koin/compiler/verify/KoinConfigVerification.kt
Outdated
Show resolved
Hide resolved
compiler/koin-ksp-compiler/src/jvmMain/kotlin/org/koin/compiler/scanner/KspExt.kt
Outdated
Show resolved
Hide resolved
compiler/koin-annotations/src/commonMain/kotlin/org/koin/core/annotation/CoreAnnotations.kt
Outdated
Show resolved
Hide resolved
Signed-off-by: Ghasem Shirdel <ghasem79.dev@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good job
same about preparing 1.4.0 branch - #107 |
I tried to do that. And it only works @Named("CoroutineDispatcherIO")
annotation class NamedCoroutineDispatcherIO
@Named("CoroutineDispatcherMain")
annotation class NamedCoroutineDispatcherMain
@Single
@NamedCoroutineDispatcherIO
fun provideDispatcherIO(): CoroutineDispatcher = Dispatchers.IO
@Single
@NamedCoroutineDispatcherMain
fun provideDispatcherMain(): CoroutineDispatcher = Dispatchers.Main But there is a bug. When using two annotations at the same time. It turns out that there will be an instance of one object. Although it should be different. class Test(
@NamedCoroutineDispatcherIO dispatcherIO: CoroutineDispatcher,
@NamedCoroutineDispatcherMain dispatcherMain: CoroutineDispatcher
) {
init {
// Wrong
println(dispatcherIO.hashCode() == dispatcherMain.hashCode()) // True (expect false)
}
} |
@ghasemdev when are you planning to merge it? |
Signed-off-by: Ghasem Shirdel <ghasem79.dev@gmail.com>
8939e47
to
29e9b4e
Compare
No description provided.