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

scala.coursier.core.Orders.scala Maven / Gradle / Ivy

The newest version!
package coursier.core

object Orders {

  @deprecated("Will likely be removed at some point in future versions", "2.0.0-RC3")
  trait PartialOrdering[T] extends scala.math.PartialOrdering[T] {
    def lteq(x: T, y: T): Boolean =
      tryCompare(x, y)
        .exists(_ <= 0)
  }

  /** All configurations that each configuration extends, including the ones it extends transitively
    */
  @deprecated("Will likely be removed at some point in future versions", "2.0.0-RC3")
  def allConfigurations(
    configurations: Map[Configuration, Seq[Configuration]]
  ): Map[Configuration, Set[Configuration]] =
    allConfigurations0(configurations)

  private[core] def allConfigurations0(
    configurations: Map[Configuration, Seq[Configuration]]
  ): Map[Configuration, Set[Configuration]] = {
    def allParents(config: Configuration): Set[Configuration] = {
      def helper(configs: Set[Configuration], acc: Set[Configuration]): Set[Configuration] =
        if (configs.isEmpty)
          acc
        else if (configs.exists(acc))
          helper(configs -- acc, acc)
        else if (configs.exists(!configurations.contains(_))) {
          val (remaining, notFound) = configs.partition(configurations.contains)
          helper(remaining, acc ++ notFound)
        }
        else {
          val extraConfigs = configs.flatMap(configurations)
          helper(extraConfigs, acc ++ configs)
        }

      helper(Set(config), Set.empty)
    }

    configurations
      .keys
      .toList
      .map(config => config -> (allParents(config) - config))
      .toMap
  }

  /** Configurations partial order based on configuration mapping `configurations`.
    *
    * @param configurations:
    *   for each configuration, the configurations it directly extends.
    */
  @deprecated("Will likely be removed at some point in future versions", "2.0.0-RC3")
  def configurationPartialOrder(
    configurations: Map[Configuration, Seq[Configuration]]
  ): PartialOrdering[Configuration] =
    configurationPartialOrder0(configurations)

  private def configurationPartialOrder0(
    configurations: Map[Configuration, Seq[Configuration]]
  ): PartialOrdering[Configuration] =
    new PartialOrdering[Configuration] {
      val allParentsMap = allConfigurations0(configurations)

      def tryCompare(x: Configuration, y: Configuration) =
        if (x == y)
          Some(0)
        else if (allParentsMap.get(x).exists(_(y)))
          Some(-1)
        else if (allParentsMap.get(y).exists(_(x)))
          Some(1)
        else
          None
    }

  /** Non-optional < optional */
  @deprecated("Will likely be removed at some point in future versions", "2.0.0-RC3")
  val optionalPartialOrder: PartialOrdering[Boolean] =
    new PartialOrdering[Boolean] {
      def tryCompare(x: Boolean, y: Boolean) =
        Some(
          if (x == y) 0
          else if (x) 1
          else -1
        )
    }

  /** Exclusions partial order.
    *
    * x <= y iff all that x excludes is also excluded by y. x and y not related iff x excludes some
    * elements not excluded by y AND y excludes some elements not excluded by x.
    *
    * In particular, no exclusions <= anything <= Set(("*", "*"))
    */
  @deprecated(
    "Can give incorrect results - will likely be removed at some point in future versions",
    "2.0.0-RC3"
  )
  val exclusionsPartialOrder: PartialOrdering[Set[(Organization, ModuleName)]] =
    new PartialOrdering[Set[(Organization, ModuleName)]] {
      def boolCmp(a: Boolean, b: Boolean) = (a, b) match {
        case (true, true)   => Some(0)
        case (true, false)  => Some(1)
        case (false, true)  => Some(-1)
        case (false, false) => None
      }

      def tryCompare(x: Set[(Organization, ModuleName)], y: Set[(Organization, ModuleName)]) = {
        val (xAll, xExcludeByOrg1, xExcludeByName1, xRemaining0) = Exclusions.partition(x)
        val (yAll, yExcludeByOrg1, yExcludeByName1, yRemaining0) = Exclusions.partition(y)

        boolCmp(xAll, yAll).orElse {
          def filtered(e: Set[(Organization, ModuleName)]) =
            e.filter { case (org, name) =>
              !xExcludeByOrg1(org) && !yExcludeByOrg1(org) &&
              !xExcludeByName1(name) && !yExcludeByName1(name)
            }

          def removeIntersection[T](a: Set[T], b: Set[T]) =
            (a -- b, b -- a)

          def allEmpty(set: Set[_]*) = set.forall(_.isEmpty)

          val (xRemaining1, yRemaining1) =
            (filtered(xRemaining0), filtered(yRemaining0))

          val (xProperRemaining, yProperRemaining) =
            removeIntersection(xRemaining1, yRemaining1)

          val (onlyXExcludeByOrg, onlyYExcludeByOrg) =
            removeIntersection(xExcludeByOrg1, yExcludeByOrg1)

          val (onlyXExcludeByName, onlyYExcludeByName) =
            removeIntersection(xExcludeByName1, yExcludeByName1)

          val (noXProper, noYProper) = (
            allEmpty(xProperRemaining, onlyXExcludeByOrg, onlyXExcludeByName),
            allEmpty(yProperRemaining, onlyYExcludeByOrg, onlyYExcludeByName)
          )

          boolCmp(noYProper, noXProper) // order matters
        }
      }
    }

  private def fallbackConfigIfNecessary(dep: Dependency, configs: Set[Configuration]): Dependency =
    Parse.withFallbackConfig(dep.configuration) match {
      case Some((main, fallback)) =>
        val config0 =
          if (configs(main))
            main
          else if (configs(fallback))
            fallback
          else
            dep.configuration

        dep.withConfiguration(config0)
      case _ =>
        dep
    }

  /** Assume all dependencies have same `module`, `version`, and `artifact`; see `minDependencies`
    * if they don't.
    */
  @deprecated(
    "Can give incorrect results - will likely be removed at some point in future versions",
    "2.0.0-RC3"
  )
  def minDependenciesUnsafe(
    dependencies: Set[Dependency],
    configs: Map[Configuration, Seq[Configuration]]
  ): Set[Dependency] = {
    val availableConfigs = configs.keySet
    val groupedDependencies = dependencies
      .map(fallbackConfigIfNecessary(_, availableConfigs))
      .groupBy(dep => (dep.optional, dep.configuration))
      .mapValues { deps =>
        deps.head.withExclusions(deps.foldLeft(Exclusions.one)((acc, dep) =>
          Exclusions.meet(acc, dep.exclusions)
        ))
      }
      .toList

    val remove =
      for {
        List(((xOpt, xScope), xDep), ((yOpt, yScope), yDep)) <- groupedDependencies.combinations(2)
        optCmp   <- optionalPartialOrder.tryCompare(xOpt, yOpt).iterator
        scopeCmp <- configurationPartialOrder0(configs).tryCompare(xScope, yScope).iterator
        if optCmp * scopeCmp >= 0
        exclCmp <- exclusionsPartialOrder.tryCompare(xDep.exclusions, yDep.exclusions).iterator
        if optCmp * exclCmp >= 0
        if scopeCmp * exclCmp >= 0
        xIsMin = optCmp < 0 || scopeCmp < 0 || exclCmp < 0
        yIsMin = optCmp > 0 || scopeCmp > 0 || exclCmp > 0
        if xIsMin || yIsMin // should be always true, unless xDep == yDep, which shouldn't happen
      } yield if (xIsMin) yDep else xDep

    groupedDependencies.map(_._2).toSet -- remove
  }

  /** Minified representation of `dependencies`.
    *
    * The returned set brings exactly the same things as `dependencies`, with no redundancy.
    */
  @deprecated(
    "Can give incorrect results - will likely be removed at some point in future versions, use DependencySet.minimizedSet instead to minimize a dependency set",
    "2.0.0-RC3"
  )
  def minDependencies(
    dependencies: Set[Dependency],
    configs: ((Module, String)) => Map[Configuration, Seq[Configuration]]
  ): Set[Dependency] =
    dependencies
      .groupBy(
        _.withConfiguration(Configuration.empty).withExclusions(Set.empty).withOptional(false)
      )
      .mapValues(deps => minDependenciesUnsafe(deps, configs(deps.head.moduleVersion)))
      .valuesIterator
      .fold(Set.empty)(_ ++ _)

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy