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

coursier.version.VersionParse.scala Maven / Gradle / Ivy

The newest version!
package coursier.version

import java.util.regex.Pattern.quote

import coursier.version.internal.Compatibility._

object VersionParse {

  def version(s: String): Option[Version] = {
    val trimmed = s.trim
    if (trimmed.isEmpty || trimmed.exists(c => c != '.' && c != '-' && c != '_' && c != '+' && !c.letterOrDigit)) None
    else Some(Version(trimmed))
  }

  // matches revisions with a '+' appended, e.g. "1.2.+", "1.2+" or "1.2.3-+"
  private val latestSubRevision = "(.*[^.-])([.-]?)[+]".r

  def ivyLatestSubRevisionInterval(s: String): Option[VersionInterval] =
    s match {
      case latestSubRevision(prefix, delim) =>
        for {
          from <- version(prefix)
          if from.items.nonEmpty
          max = (if (delim.isEmpty) "." else delim) + "max"
          to <- version(prefix + max)
          // the contrary would mean something went wrong in the loose substitution above
          if from.items.init == to.items.dropRight(1).init
        } yield VersionInterval(Some(from), Some(to), fromIncluded = true, toIncluded = true)
      case _ =>
        None
    }

  def versionInterval(s: String): Option[VersionInterval] = {

    def parseBounds(fromIncluded: Boolean, toIncluded: Boolean, s: String) = {

      val commaIdx = s.indexOf(',')

      if (commaIdx >= 0) {
        val strFrom = s.take(commaIdx)
        val strTo = s.drop(commaIdx + 1)

        for {
          from <- if (strFrom.isEmpty) Some(None) else version(strFrom).map(Some(_))
          to <- if (strTo.isEmpty) Some(None) else version(strTo).map(Some(_))
        } yield VersionInterval(from.filterNot(_.isEmpty), to.filterNot(_.isEmpty), from.forall(!_.isEmpty) && fromIncluded, toIncluded)
      } else if (s.nonEmpty && fromIncluded && toIncluded)
        for (v <- version(s) if !v.isEmpty)
          yield VersionInterval(Some(v), Some(v), fromIncluded, toIncluded)
      else
        None
    }

    for {
      fromIncluded <- if (s.startsWith("[")) Some(true) else if (s.startsWith("(")) Some(false) else None
      toIncluded <- if (s.endsWith("]")) Some(true) else if (s.endsWith(")")) Some(false) else None
      s0 = s.drop(1).dropRight(1)
      itv <- parseBounds(fromIncluded, toIncluded, s0)
    } yield itv
  }

  private val multiVersionIntervalSplit = ("(?" + regexLookbehind + "[" + quote("])") + "]),(?=[" + quote("([") + "])").r

  def multiVersionInterval(s: String): Option[VersionInterval] = {

    // TODO Use a full-fledged (fastparsed-based) parser for this and versionInterval above

    val openCount = s.count(c => c == '[' || c == '(')
    val closeCount = s.count(c => c == ']' || c == ')')

    if (openCount == closeCount && openCount >= 1)
      versionInterval(multiVersionIntervalSplit.split(s).last)
    else
      None
  }

  def versionConstraint(s: String): VersionConstraint = {
    def noConstraint = if (s.isEmpty) Some(VersionConstraint.all) else None

    noConstraint
      .orElse(ivyLatestSubRevisionInterval(s).map(VersionConstraint.interval))
      .orElse(versionInterval(s).orElse(multiVersionInterval(s)).map(VersionConstraint.interval))
      .getOrElse(VersionConstraint.preferred(Version(s)))
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy