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

com.autonomousapps.model.Advice.kt Maven / Gradle / Ivy

There is a newer version: 2.0.2
Show newest version
package com.autonomousapps.model

import com.autonomousapps.internal.utils.isTrue
import com.autonomousapps.model.declaration.Declaration
import com.squareup.moshi.JsonClass

/**
 * An "advice" is a kind of _transform_ that users ought to perform to bring their dependency declarations into a more
 * correct state.
 *
 * See also [Usage][com.autonomousapps.model.intermediates.Usage].
 */
@JsonClass(generateAdapter = false)
data class Advice(
  /** The coordinates of the dependency that ought to be modified in some way. */
  val coordinates: Coordinates,
  /** The current configuration on which the dependency has been declared. Will be null for transitive dependencies. */
  val fromConfiguration: String? = null,
  /**
   * The configuration on which the dependency _should_ be declared. Will be null if the dependency is unused and
   * therefore ought to be removed.
   */
  val toConfiguration: String? = null
) : Comparable {

  override fun compareTo(other: Advice): Int = compareBy(Advice::coordinates)
    .thenComparing(compareBy(nullsFirst()) { it.toConfiguration })
    .thenComparing(compareBy(nullsFirst()) { it.fromConfiguration })
    .compare(this, other)

  companion object {
    @JvmStatic
    fun ofAdd(coordinates: Coordinates, toConfiguration: String) = Advice(
      coordinates = coordinates,
      fromConfiguration = null,
      toConfiguration = toConfiguration
    )

    @JvmStatic
    fun ofRemove(coordinates: Coordinates, fromConfiguration: String) = Advice(
      coordinates = coordinates,
      fromConfiguration = fromConfiguration, toConfiguration = null
    )

    @JvmStatic
    internal fun ofRemove(coordinates: Coordinates, declaration: Declaration) =
      ofRemove(coordinates, declaration.configurationName)

    @JvmStatic
    fun ofChange(coordinates: Coordinates, fromConfiguration: String, toConfiguration: String) = Advice(
      coordinates = coordinates,
      fromConfiguration = fromConfiguration,
      toConfiguration = toConfiguration
    )
  }

  /**
   * `compileOnly` dependencies are special. If they are so declared, we assume the user knows what they're doing and do
   * not generally recommend changing them. We also don't recommend _adding_ a compileOnly dependency that is only
   * included transitively (to be less annoying).
   *
   * So, an advice is "compileOnly-advice" only if it is a compileOnly candidate and is declared on a different
   * configuration.
   */
  fun isCompileOnly() = toConfiguration?.endsWith("compileOnly", ignoreCase = true) == true

  fun isRemoveCompileOnly() = isRemove() && fromConfiguration?.endsWith("compileOnly", ignoreCase = true) == true

  fun isRuntimeOnly() = toConfiguration?.endsWith("runtimeOnly", ignoreCase = true) == true

  fun isRemoveRuntimeOnly() = isRemove() && fromConfiguration?.endsWith("runtimeOnly", ignoreCase = true) == true

  /**
   * An advice is "add-advice" if it is undeclared and used, AND is not `compileOnly`.
   */
  fun isAdd() = isAnyAdd() && !isCompileOnly()

  fun isAnyAdd() = fromConfiguration == null && toConfiguration != null

  /**
   * An advice is "remove-advice" if it is declared and not used, AND is not `compileOnly`,
   * AND is not `processor`.
   */
  fun isRemove() = isAnyRemove() && !isCompileOnly() && !isProcessor()

  fun isAnyRemove() = toConfiguration == null

  /**
   * An advice is "change-advice" if it is declared and used (but is on the wrong configuration),
   * AND is not `compileOnly`, AND is not `runtimeOnly`.
   */
  fun isChange() = isAnyChange() && !isCompileOnly() && !isRuntimeOnly()

  /**
   * An advice is "change-advice" if it is declared and used (but is on the wrong configuration).
   */
  fun isAnyChange() = fromConfiguration != null && toConfiguration != null

  /**
   * An advice is "processors-advice" if it is declared on a k/apt or annotationProcessor
   * configuration, and this dependency should be removed.
   */
  fun isProcessor() = toConfiguration == null && fromConfiguration?.let {
    it.endsWith("kapt", ignoreCase = true) || it.endsWith("annotationProcessor", ignoreCase = true)
  }.isTrue()

  /** If this is advice to remove or downgrade an api-like dependency. */
  fun isDowngrade(): Boolean {
    return (isRemove() || isChange() || isCompileOnly())
      && fromConfiguration?.endsWith("api", ignoreCase = true) == true
  }

  /** If this is advice to add a dependency, or change an existing dependency to make it api-like. */
  fun isUpgrade(): Boolean = isAnyAdd() || (isAnyChange() && isToApiLike())

  fun isToApiLike(): Boolean = toConfiguration?.endsWith("api", ignoreCase = true) == true
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy