All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.com.r0adkll.kimchi.annotations.ContributesMultibinding.kt Maven / Gradle / Ivy

// Copyright (C) 2024 r0adkll
// SPDX-License-Identifier: Apache-2.0
package com.r0adkll.kimchi.annotations

import kotlin.reflect.KClass

/**
 * Generate a kotlin-inject multibinding function for an annotated class and contributes this multibinding
 * function to the given [scope]. Take this example:
 * ```
 * interface AppInitializer
 *
 * @Inject
 * class LoggingInitializer : AppInitializer
 *
 * @ContributesTo(AppScope::class)
 * interface LoggingListenerModule {
 *   fun LoggingInitializer.binds: AppInitializer
 *     @Provides get() = this
 * }
 * ```
 * This is a lot of boilerplate. You can replace this entire module with the
 * [ContributesMultibinding] annotation. The equivalent would be:
 * ```
 * interface AppInitializer
 *
 * @ContributesMultibinding(AppScope::class)
 * @Inject
 * class LoggingInitializer : AppInitializer
 * ```
 * Notice that it's optional to specify [boundType], if there is only exactly one super type. If
 * there are multiple super types, then it's required to specify the parameter:
 * ```
 * @ContributesMultibinding(
 *   scope = AppScope::class,
 *   boundType = AppInitializer::class
 * )
 * @Inject
 * class AnalyticsInitializer : AppInitializer, AnalyticsListener
 * ```
 *
 * [ContributesMultibinding] supports qualifiers. If you annotate the class additionally with a
 * qualifier, then the generated multibinding function will be annotated with the same qualifier,
 * e.g.
 * ```
 * @ContributesMultibinding(AppScope::class)
 * @Named("Prod")
 * @Inject
 * class LoggingInitializer : AppInitializer
 *
 * // Will generate this binding function.
 * val LoggingInitializer.bind: AppInitializer
 *   @Provides @Named(name = "beverages")
 *   @IntoSet
 *   get() = this
 * ```
 *
 * To generate a Map multibindings function you need to annotate the class with the map key. Kimchi
 * will use the map key as hint to generate a binding function for a map instead of a set:
 * ```
 * @MapKey
 * annotation class BindingKey(val value: String)
 *
 * @ContributesMultibinding(AppScope::class)
 * @BindingKey("abc")
 * @Inject
 * class LoggingInitializer : AppInitializer
 *
 * // Will generate this binding function.
 * @Provides
 * @IntoMap
 * public fun provideAppInitializer_abc(
 * `value`: LoggingInitializer
 * ): Pair = ("abc" to `value`)
 * ```
 * Note that map keys must allow classes as target. Kimchi provides a few built-in MapKeys that you can use
 * out of the box, such as [StringKey]. [IntKey], and [LongKey]. You can provide any custom map key and Kimchi
 * will bind your dependencies keyed to the first and only parameter of the annotation key you use.
 *
 * Contributed multibindings can replace other contributed modules and contributed multibindings
 * with the [replaces] parameter. This is especially helpful for different multibindings in
 * tests.
 * ```
 * @ContributesMultibinding(
 *   scope = AppScope::class,
 *   replaces = [MainInitializer::class]
 * )
 * @Inject
 * class FakeInitializer : AppInitializer
 * ```
 * [ContributesMultibinding] supports Kotlin objects, e.g.
 * ```
 * @ContributesMultibinding(AppScope::class)
 * object MainInitializer : Initializer
 */
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@Repeatable
annotation class ContributesMultibinding(
  /**
   * The merging scope in which to include this multibinding
   */
  val scope: KClass<*>,
  /**
   * The type that this class is bound to. This class will be included in the collection for
   * [boundType].
   */
  val boundType: KClass<*> = Unit::class,
  /**
   * This contributed multibinding will replace these contributed classes. The array is allowed to
   * include other contributed multibindings. All replaced classes must use the same scope.
   */
  val replaces: Array> = [],
)




© 2015 - 2024 Weber Informatics LLC | Privacy Policy