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

scala.coursier.util.StringInterpolators.scala Maven / Gradle / Ivy

There is a newer version: 2.1.25-M3
Show newest version
package coursier.util

import coursier.core._
import coursier.ivy.IvyRepository
import coursier.maven.MavenRepository
import coursier.parse.{DependencyParser, ModuleParser}

import scala.language.experimental.macros
import scala.reflect.macros.blackbox

object StringInterpolators {

  // adapted from https://github.com/criteo/cuttle/blob/7e04a8f93c2c44992da270242d8b94ea808ac623/timeseries/src/main/scala/com/criteo/cuttle/timeseries/package.scala#L39-L56

  implicit class SafeOrganization(val sc: StringContext) extends AnyVal {
    def org(args: Any*): Organization = macro safeOrganization
  }

  implicit class SafeModuleName(val sc: StringContext) extends AnyVal {
    def name(args: Any*): ModuleName = macro safeModuleName
  }

  implicit class SafeModule(val sc: StringContext) extends AnyVal {
    def mod(args: Any*): Module = macro safeModule
  }

  implicit class SafeModuleExclusionMatcher(val sc: StringContext) extends AnyVal {
    def excl(args: Any*): ModuleMatchers = macro safeModuleExclusionMatcher
  }

  implicit class SafeModuleInclusionMatcher(val sc: StringContext) extends AnyVal {
    def incl(args: Any*): ModuleMatchers = macro safeModuleInclusionMatcher
  }

  implicit class SafeDependency(val sc: StringContext) extends AnyVal {
    def dep(args: Any*): Dependency = macro safeDependency
  }

  implicit class SafeMavenRepository(val sc: StringContext) extends AnyVal {
    def mvn(args: Any*): MavenRepository = macro safeMavenRepository
  }

  implicit class SafeIvyRepository(val sc: StringContext) extends AnyVal {
    def ivy(args: Any*): IvyRepository = macro safeIvyRepository
  }

  def safeOrganization(c: blackbox.Context)(args: c.Expr[Any]*): c.Expr[Organization] = {
    import c.universe._
    c.prefix.tree match {
      case Apply(_, List(Apply(_, Literal(Constant(orgString: String)) :: Nil))) =>
        // TODO Check for invalid characters
        c.Expr(q"""_root_.coursier.core.Organization($orgString)""")
      case _ =>
        c.abort(c.enclosingPosition, s"Only a single String literal is allowed here")
    }
  }

  def safeModuleName(c: blackbox.Context)(args: c.Expr[Any]*): c.Expr[ModuleName] = {
    import c.universe._
    c.prefix.tree match {
      case Apply(_, List(Apply(_, Literal(Constant(nameString: String)) :: Nil))) =>
        // TODO Check for invalid characters
        c.Expr(q"""_root_.coursier.core.ModuleName($nameString)""")
      case _ =>
        c.abort(c.enclosingPosition, s"Only a single String literal is allowed here")
    }
  }

  def safeModule(c: blackbox.Context)(args: c.Expr[Any]*): c.Expr[Module] = {
    import c.universe._
    c.prefix.tree match {
      case Apply(_, List(Apply(_, Literal(Constant(modString: String)) :: Nil))) =>
        ModuleParser.module(modString, scala.util.Properties.versionNumberString) match {
          case Left(e) =>
            c.abort(c.enclosingPosition, s"Error parsing module $modString: $e")
          case Right(mod) =>
            val attrs = mod.attributes.toSeq.map {
              case (k, v) =>
                q"_root_.scala.Tuple2($k, $v)"
            }
            c.Expr(q"""
              _root_.coursier.core.Module(
                _root_.coursier.core.Organization(${mod.organization.value}),
                _root_.coursier.core.ModuleName(${mod.name.value}),
                _root_.scala.collection.immutable.Map(..$attrs)
              )
            """)
        }
      case _ =>
        c.abort(c.enclosingPosition, s"Only a single String literal is allowed here")
    }
  }

  def safeModuleExclusionMatcher(
    c: blackbox.Context
  )(
    args: c.Expr[Any]*
  ): c.Expr[ModuleMatchers] = {
    import c.universe._
    c.Expr(q"""
      _root_.coursier.util.ModuleMatchers(
        _root_.scala.collection.immutable.Set[_root_.coursier.util.ModuleMatcher](
          _root_.coursier.util.ModuleMatcher(${safeModule(c)(args: _*)})
        )
      )
    """)
  }

  def safeModuleInclusionMatcher(
    c: blackbox.Context
  )(
    args: c.Expr[Any]*
  ): c.Expr[ModuleMatchers] = {
    import c.universe._
    c.Expr(q"""
      _root_.coursier.util.ModuleMatchers(
        _root_.scala.collection.immutable.Set.empty[_root_.coursier.util.ModuleMatcher],
        _root_.scala.collection.immutable.Set[_root_.coursier.util.ModuleMatcher](
          _root_.coursier.util.ModuleMatcher(${safeModule(c)(args: _*)})
        )
      )
    """)
  }

  def safeDependency(c: blackbox.Context)(args: c.Expr[Any]*): c.Expr[Dependency] = {
    import c.universe._
    c.prefix.tree match {
      case Apply(_, List(Apply(_, Literal(Constant(modString: String)) :: Nil))) =>
        // same default configuration as coursier.Dependency.apply
        DependencyParser.dependency(
          modString,
          scala.util.Properties.versionNumberString,
          Configuration.empty
        ) match {
          case Left(e) =>
            c.abort(c.enclosingPosition, s"Error parsing module $modString: $e")
          case Right(dep) =>
            val attrs = dep.module.attributes.toSeq.map {
              case (k, v) =>
                q"_root_.scala.Tuple2($k, $v)"
            }
            val excls = dep.minimizedExclusions.toSeq().map {
              case (org, name) =>
                q"_root_.scala.Tuple2(_root_.coursier.core.Organization(${org.value}), _root_.coursier.core.ModuleName(${name.value}))"
            }
            val overrides = dep.overridesMap.flatten.toSeq.sortBy(_._1.repr).map {
              case (key, values) =>
                val key0 = q"""_root_.coursier.core.DependencyManagement.Key(
                  _root_.coursier.core.Organization(${key.organization.value}),
                  _root_.coursier.core.ModuleName(${key.name.value}),
                  _root_.coursier.core.Type(${key.`type`.value}),
                  _root_.coursier.core.Classifier(${key.classifier.value})
                )"""
                val excls = values.minimizedExclusions.toSeq().map {
                  case (org, name) =>
                    q"_root_.scala.Tuple2(_root_.coursier.core.Organization(${org.value}), _root_.coursier.core.ModuleName(${name.value}))"
                }
                val values0 = q"""_root_.coursier.core.DependencyManagement.Values(
                  _root_.coursier.core.Configuration(${values.config.value}),
                  ${values.version},
                  _root_.coursier.core.MinimizedExclusions(_root_.scala.collection.immutable.Set[(_root_.coursier.core.Organization, _root_.coursier.core.ModuleName)](..$excls)),
                  ${values.optional}
                )"""
                q"_root_.scala.Tuple2($key0, $values0)"
            }
            val boms = dep.bomDependencies.map { bomDep =>
              val attrs = bomDep.module.attributes.toSeq.map {
                case (k, v) =>
                  q"_root_.scala.Tuple2($k, $v)"
              }
              q"""_root_.coursier.core.BomDependency(
                _root_.coursier.core.Module(
                  _root_.coursier.core.Organization(${bomDep.module.organization.value}),
                  _root_.coursier.core.ModuleName(${bomDep.module.name.value}),
                  _root_.scala.collection.immutable.Map(..$attrs)
                ),
                ${bomDep.version},
                _root_.coursier.core.Configuration(${bomDep.config.value}),
                ${bomDep.forceOverrideVersions}
              )"""
            }
            c.Expr(q"""
              _root_.coursier.core.Dependency(
                _root_.coursier.core.Module(
                  _root_.coursier.core.Organization(${dep.module.organization.value}),
                  _root_.coursier.core.ModuleName(${dep.module.name.value}),
                  _root_.scala.collection.immutable.Map(..$attrs)
                ),
                ${dep.version},
                _root_.coursier.core.Configuration(${dep.configuration.value}),
                _root_.coursier.core.MinimizedExclusions(_root_.scala.collection.immutable.Set[(_root_.coursier.core.Organization, _root_.coursier.core.ModuleName)](..$excls)),
                _root_.coursier.core.Publication(
                  ${dep.publication.name},
                  _root_.coursier.core.Type(${dep.publication.`type`.value}),
                  _root_.coursier.core.Extension(${dep.publication.ext.value}),
                  _root_.coursier.core.Classifier(${dep.publication.classifier.value})
                ),
                ${dep.optional},
                ${dep.transitive},
                _root_.scala.collection.immutable.Map(),
                _root_.scala.collection.immutable.Nil,
                _root_.scala.collection.immutable.Seq(..$boms),
                _root_.coursier.core.Overrides(
                  _root_.scala.collection.immutable.Map[_root_.coursier.core.DependencyManagement.Key, _root_.coursier.core.DependencyManagement.Values](..$overrides)
                )
              )
            """)
        }
      case _ =>
        c.abort(c.enclosingPosition, s"Only a single String literal is allowed here")
    }
  }

  def safeMavenRepository(c: blackbox.Context)(args: c.Expr[Any]*): c.Expr[MavenRepository] = {
    import c.universe._
    c.prefix.tree match {
      case Apply(_, List(Apply(_, Literal(Constant(root: String)) :: Nil))) =>
        // FIXME Check that there's no query string, fragment, … in uri?
        val uri = new java.net.URI(root)
        c.Expr(q"""_root_.coursier.maven.MavenRepository($root)""")
      case _ =>
        c.abort(c.enclosingPosition, s"Only a single String literal is allowed here")
    }
  }

  def safeIvyRepository(c: blackbox.Context)(args: c.Expr[Any]*): c.Expr[IvyRepository] = {
    import c.universe._
    c.prefix.tree match {
      case Apply(_, List(Apply(_, Literal(Constant(str: String)) :: Nil))) =>
        // FIXME Check that there's no query string, fragment, … in uri?
        val r = IvyRepository.parse(str) match {
          case Left(e) =>
            c.abort(c.enclosingPosition, s"Malformed Ivy repository '$str': $e")
          case Right(r0) => r0
        }
        // Here, ideally, we should lift r as an Expr, but this is quite cumbersome to do (it involves lifting
        // Seq[coursier.ivy.Pattern.Chunk], where coursier.ivy.Pattern.Chunk is an ADT, …
        c.Expr(
          q"""_root_.coursier.ivy.IvyRepository.parse($str).left.map(e => sys.error("Error parsing Ivy repository: " + e)).merge"""
        )
      case _ =>
        c.abort(c.enclosingPosition, s"Only a single String literal is allowed here")
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy