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

com.autonomousapps.model.intermediates.Usage.kt Maven / Gradle / Ivy

There is a newer version: 2.6.1
Show newest version
// Copyright (c) 2024. Tony Robalik.
// SPDX-License-Identifier: Apache-2.0
package com.autonomousapps.model.intermediates

import com.autonomousapps.model.Advice
import com.autonomousapps.model.Coordinates
import com.autonomousapps.model.ModuleCoordinates
import com.autonomousapps.model.declaration.Bucket
import com.autonomousapps.model.declaration.Variant
import com.squareup.moshi.JsonClass

@JsonClass(generateAdapter = false)
internal data class Usage(
  val buildType: String?,
  val flavor: String?,
  val variant: Variant,
  val bucket: Bucket,
  val reasons: Set
) {

  companion object {
    val BY_VARIANT: Comparator = compareBy { it.variant }
  }

  /**
   * Transform the variant-specific [usages][Usage] of a specific dependency, represented by its
   * [coordinates][Coordinates], into a set of [advice][Advice]. This set may have zero or more elements.
   */
  interface Transform {
    fun reduce(usages: Set): Set
  }
}

internal class UsageBuilder(
  traces: Set,
  private val variants: Collection
) {

  val dependencyUsages: Map>
  val annotationProcessingUsages: Map>

  init {
    val theDependencyUsages = mutableMapOf>()
    val theAnnotationProcessingUsages = mutableMapOf>()

    traces.forEach { report ->
      report.dependencies.forEach { trace ->
        theDependencyUsages.merge(report, trace)
      }
      report.annotationProcessors.forEach { trace ->
        theAnnotationProcessingUsages.merge(report, trace)
      }
    }

    addMissingVariants(theDependencyUsages)
    addMissingVariants(theAnnotationProcessingUsages)

    dependencyUsages = theDependencyUsages
    annotationProcessingUsages = theAnnotationProcessingUsages
  }

  // The advice computation that follows expects every dependency to be associated with a usage for _each_ variant
  // present in the build. To ensure this is the case, we add usages for missing variants
  // (Bucket.NONE and Reason.UNDECLARED).
  private fun addMissingVariants(map: MutableMap>) {
    map.forEach { (_, theseUsages) ->
      if (theseUsages.size < variants.size) {
        variants.filterNot { variant ->
          theseUsages.any { it.variant == variant }
        }.forEach { missingVariant ->
          theseUsages += Usage(
            buildType = null,
            flavor = null,
            variant = missingVariant,
            bucket = Bucket.NONE,
            reasons = setOf(Reason.Undeclared)
          )
        }
      }
    }
  }

  private fun MutableMap>.merge(
    report: DependencyTraceReport,
    trace: DependencyTraceReport.Trace
  ) {
    val usage = Usage(
      buildType = report.buildType,
      flavor = report.flavor,
      variant = report.variant,
      bucket = trace.bucket,
      reasons = trace.reasons
    )

    val other = keys.find { it.identifier == trace.coordinates.identifier }
    if (other != null) {
      val otherVersion = (other as? ModuleCoordinates)?.resolvedVersion
      val thisVersion = (trace.coordinates as? ModuleCoordinates)?.resolvedVersion
      if (otherVersion != null && thisVersion != null && otherVersion != thisVersion) {
        // This mutates the existing set in place
        val usages = get(other)!!.apply { add(usage) }

        // If the new coordinates are "greater than" the other coordinates, add the new and remove the old
        if (thisVersion > otherVersion) {
          put(trace.coordinates, usages)
          remove(other)
        }

        // We're done
        return
      }
    }

    merge(trace.coordinates, mutableSetOf(usage)) { acc, inc ->
      acc.apply { addAll(inc) }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy