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

scala.coursier.parse.RuleParser.scala Maven / Gradle / Ivy

There is a newer version: 2.1.24
Show newest version
package coursier.parse

import fastparse._, NoWhitespace._
import coursier.core.{Module, ModuleName, Organization}
import coursier.params.rule._
import coursier.util.{ModuleMatcher, ModuleMatchers}

object RuleParser {

  private def ruleParser[X: P](defaultResolution: RuleResolution): P[(Rule, RuleResolution)] = {

    def resolution: P[RuleResolution] = {

      def tryResolve = P("resolve").map(_ => RuleResolution.TryResolve)
      def warn       = P("warn").map(_ => RuleResolution.Warn)
      def fail       = P("fail").map(_ => RuleResolution.Fail)

      def res = P(tryResolve | warn | fail)

      res
    }

    def alwaysFail = P("AlwaysFail").map(_ => AlwaysFail())

    def identifier =
      P {
        CharsWhile { c =>
          (c >= 'a' && c <= 'z') ||
          (c >= 'A' && c <= 'Z') ||
          c == '.' ||
          c == '-' ||
          c == '_' ||
          c == '*'
        }.!
      }
    def module =
      P(identifier ~ ":" ~ identifier).map {
        case (org, name) =>
          Module(Organization(org), ModuleName(name), Map.empty)
      }
    def moduleOrExcludedModule =
      P(("!".? ~ identifier).! ~ ":" ~ identifier).map {
        case (org, name) =>
          Module(Organization(org), ModuleName(name), Map.empty)
      }
    def moduleMatcher = module.map(ModuleMatcher(_))

    def excludes =
      // FIXME There should be better ways to handle whitespaces here…
      P {
        "exclude" ~ " ".rep ~ "=" ~ " ".rep ~
          "[" ~ moduleMatcher.rep(sep = P(" ".rep ~ "," ~ " ".rep)) ~ " ".rep ~ "]"
      }.map { l =>
        ModuleMatchers(l.toSet)
      }

    def includes =
      // FIXME There should be better ways to handle whitespaces here…
      P {
        "include" ~ " ".rep ~ "=" ~ " ".rep ~
          "[" ~ moduleMatcher.rep(sep = P(" ".rep ~ "," ~ " ".rep)) ~ " ".rep ~ "]"
      }.map { l =>
        ModuleMatchers(Set(), l.toSet)
      }

    def sameVersion =
      P {
        "SameVersion(" ~ module.rep(min = 1, sep = P("," ~ " ".rep)) ~ ")"
      }.map { modules =>
        SameVersion(modules.map(ModuleMatcher(_)).toSet)
      }

    def dontBumpRootDependencies =
      P {
        "DontBumpRootDependencies" ~
          ("(" ~ (excludes | includes).rep(sep = P("," ~ " ".rep)) ~ ")").?
      }.map { l =>
        val matchers = l.getOrElse(Nil).foldLeft(ModuleMatchers.all) {
          (m, m0) =>
            ModuleMatchers(m.exclude ++ m0.exclude, m.include ++ m0.include)
        }
        DontBumpRootDependencies(matchers)
      }

    def strict =
      P("Strict" ~ ("(" ~ moduleOrExcludedModule.rep(sep = P("," ~ " ".rep)) ~ ")").?).map {
        modulesOpt =>
          val (exclude, include) =
            modulesOpt.getOrElse(Nil).partition(_.organization.value.startsWith("!"))
          val include0 =
            if (include.isEmpty) Set(ModuleMatcher.all) else include.map(ModuleMatcher(_)).toSet
          val exclude0 = exclude.map(m =>
            m.withOrganization(Organization(m.organization.value.stripPrefix("!")))
          )
          Strict(
            include0,
            exclude0.map(ModuleMatcher(_)).toSet
          )
      }

    def rule =
      P((resolution ~ ":").? ~ (alwaysFail | sameVersion | dontBumpRootDependencies | strict)).map {
        case (resOpt, rule) =>
          rule -> resOpt.getOrElse(defaultResolution)
      }

    rule
  }

  private def rulesParser[X: P](
    defaultResolution: RuleResolution
  ): P[Seq[(Rule, RuleResolution)]] = {

    def rules = P(ruleParser(defaultResolution).rep(min = 1, sep = P("," ~ " ".rep)))

    rules
  }

  def rule(input: String): Either[String, (Rule, RuleResolution)] =
    rule(input, RuleResolution.TryResolve)

  def rule(
    input: String,
    defaultResolution: RuleResolution
  ): Either[String, (Rule, RuleResolution)] =
    fastparse.parse(input, ruleParser(defaultResolution)(_)) match {
      case f: Parsed.Failure =>
        Left(f.msg)
      case Parsed.Success(_, idx) if idx < input.length =>
        Left(s"Unexpected characters at end of rule: '${input.drop(idx)}'")
      case Parsed.Success(rule, _) =>
        Right(rule)
    }

  def rules(input: String): Either[String, Seq[(Rule, RuleResolution)]] =
    rules(input, RuleResolution.TryResolve)

  def rules(
    input: String,
    defaultResolution: RuleResolution
  ): Either[String, Seq[(Rule, RuleResolution)]] =
    fastparse.parse(input, rulesParser(defaultResolution)(_)) match {
      case f: Parsed.Failure =>
        Left(f.msg)
      case Parsed.Success(_, idx) if idx < input.length =>
        Left(s"Unexpected characters at end of rules: '${input.drop(idx)}'")
      case Parsed.Success(rules, idx) =>
        Right(rules)
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy