Skip to content

Commit

Permalink
Dependency directive: symbolic version numbers (#413)
Browse files Browse the repository at this point in the history
* Dependency directive: symbolic version numbers

* Test multiple symbols; docs
  • Loading branch information
ennru authored Feb 18, 2020
1 parent a4ae94e commit 02380eb
Show file tree
Hide file tree
Showing 3 changed files with 181 additions and 8 deletions.
40 changes: 32 additions & 8 deletions core/src/main/scala/com/lightbend/paradox/markdown/Directive.scala
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,9 @@ case class InlineGroupDirective(groups: Seq[String]) extends InlineDirective(gro
* Dependency directive.
*/
case class DependencyDirective(ctx: Writer.Context) extends LeafBlockDirective("dependency") {
val VersionSymbol = "symbol"
val VersionValue = "value"

val variables = ctx.properties
val ScalaVersion = variables.get("scala.version")
val ScalaBinaryVersion = variables.get("scala.binary.version")
Expand All @@ -709,6 +712,9 @@ case class DependencyDirective(ctx: Writer.Context) extends LeafBlockDirective("
def renderDependency(tools: String, node: DirectiveNode, printer: Printer): Unit = {
val classes = Seq("dependency", node.attributes.classesString).filter(_.nonEmpty)

val symbolPostfixes = node.attributes.keys().asScala.toSeq
.filter(_.startsWith(VersionSymbol)).sorted.map(_.replace(VersionSymbol, ""))

val dependencyPostfixes = node.attributes.keys().asScala.toSeq
.filter(_.startsWith("group")).sorted.map(_.replace("group", ""))

Expand All @@ -729,36 +735,42 @@ case class DependencyDirective(ctx: Writer.Context) extends LeafBlockDirective("
""
}

val symbols = symbolPostfixes.map { sp =>
requiredCoordinate(VersionSymbol + sp)
}.toSet

def sbt(group: String, artifact: String, version: String, scope: Option[String], classifier: Option[String]): String = {
val scopeString = scope.map {
case s @ ("runtime" | "compile" | "test") => " % " + s.capitalize
case s => s""" % "$s""""
}
val classifierString = classifier.map(" classifier " + '"' + _ + '"')
val extra = (scopeString ++ classifierString).mkString
val versionOrSymbol = if (symbols.contains(version)) version else s""""$version""""
(ScalaVersion, ScalaBinaryVersion) match {
case (Some(scalaVersion), _) if artifact.endsWith("_" + scalaVersion) =>
val strippedArtifact = artifact.substring(0, artifact.length - 1 - scalaVersion.length)
s""""$group" % "$strippedArtifact" % "$version"$extra cross CrossVersion.full"""
s""""$group" % "$strippedArtifact" % ${versionOrSymbol}$extra cross CrossVersion.full"""

case (_, Some(scalaBinVersion)) if artifact.endsWith("_" + scalaBinVersion) =>
val strippedArtifact = artifact.substring(0, artifact.length - 1 - scalaBinVersion.length)
s""""$group" %% "$strippedArtifact" % "$version"$extra"""
s""""$group" %% "$strippedArtifact" % ${versionOrSymbol}$extra"""

case _ =>
s""""$group" % "$artifact" % "$version"$extra"""
s""""$group" % "$artifact" % $versionOrSymbol$extra"""
}
}

def gradle(group: String, artifact: String, version: String, scope: Option[String], classifier: Option[String]): String = {
val conf = scope.getOrElse("compile")
val extra = classifier.map(c => s", classifier: '$c'").getOrElse("")
s"""$conf group: '$group', name: '$artifact', version: '$version'$extra""".stripMargin
val v = if (symbols.contains(version)) s"versions.$version" else s"'$version'"
s"""$conf group: '$group', name: '$artifact', version: $v$extra""".stripMargin
}

def mvn(group: String, artifact: String, version: String, scope: Option[String], classifier: Option[String]): String = {
val elements =
Seq("groupId" -> group, "artifactId" -> artifact, "version" -> version) ++
Seq("groupId" -> group, "artifactId" -> artifact, "version" -> { if (symbols.contains(version)) s"$${$version}" else version }) ++
classifier.map("classifier" -> _) ++ scope.map("scope" -> _)
elements.map {
case (element, value) => s" <$element>$value</$element>"
Expand All @@ -769,6 +781,9 @@ case class DependencyDirective(ctx: Writer.Context) extends LeafBlockDirective("
tools.split("[,]").map(_.trim).filter(_.nonEmpty).foreach { tool =>
val (lang, code) = tool match {
case "sbt" =>
val symbolVals = symbolPostfixes.map { sp =>
s"""val ${requiredCoordinate(VersionSymbol + sp)} = "${requiredCoordinate(VersionValue + sp)}"\n"""
}.mkString
val artifacts = dependencyPostfixes.map { dp =>
sbt(
requiredCoordinate(s"group$dp"),
Expand All @@ -785,9 +800,13 @@ case class DependencyDirective(ctx: Writer.Context) extends LeafBlockDirective("
Seq("libraryDependencies ++= Seq(", artifacts.map(a => s" $a").mkString(",\n"), ")").mkString("\n")
}

("scala", libraryDependencies)
("scala", symbolVals + libraryDependencies)

case "gradle" | "Gradle" =>
val symbolProperties = if (symbols.isEmpty) "" else
symbolPostfixes.map { sp =>
s""" ${requiredCoordinate(VersionSymbol + sp)}: "${requiredCoordinate(VersionValue + sp)}""""
}.mkString("versions += [\n", ",\n", "\n]\n")
val artifacts = dependencyPostfixes.map { dp =>
gradle(
requiredCoordinate(s"group$dp"),
Expand All @@ -801,9 +820,14 @@ case class DependencyDirective(ctx: Writer.Context) extends LeafBlockDirective("
val libraryDependencies =
Seq("dependencies {", artifacts.map(a => s" $a").mkString(",\n"), "}").mkString("\n")

("gradle", libraryDependencies)
("gradle", symbolProperties + libraryDependencies)

case "maven" | "Maven" | "mvn" =>
val symbolProperties = if (symbols.isEmpty) "" else
symbolPostfixes.map { sp =>
val symb = s"""${requiredCoordinate(VersionSymbol + sp)}"""
s""" &lt;$symb&gt;${requiredCoordinate(VersionValue + sp)}&lt;/$symb&gt;"""
}.mkString("&lt;properties&gt;\n", "\n", "\n&lt;/properties&gt;\n")
val artifacts = dependencyPostfixes.map { dp =>
mvn(
requiredCoordinate(s"group$dp"),
Expand All @@ -814,7 +838,7 @@ case class DependencyDirective(ctx: Writer.Context) extends LeafBlockDirective("
)
}

("xml", artifacts.mkString("\n"))
("xml", symbolProperties + artifacts.mkString("\n"))
}

printer.print(s"""<dt>$tool</dt>""")
Expand Down
42 changes: 42 additions & 0 deletions docs/src/main/paradox/directives/dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ Which will render as:
classifier="assets"
}

## Multiple dependencies

It is also possible to render more than one dependency in a list. In that case library coordinates need to be appended with the
same suffix. For example

Expand All @@ -60,3 +62,43 @@ will be rendered as:
group="com.example" artifact="domain" version="0.1.0"
group2="com.example" artifact2="another-domain" version2="0.2.1"
}

## Symbolic version numbers

When multiple dependencies always use the same version, symbolic version names can be shown. For example

```scala
@@dependency[sbt,Maven,gradle] {
symbol="AkkaVersion"
value="2.5.29"
symbol2="AkkaHttpVersion"
value2="10.1.0"
group="com.typesafe.akka"
artifact="akka-stream_$scala.binary.version$"
version="AkkaVersion"
group2="com.typesafe.akka"
artifact2="akka-actor-typed_$scala.binary.version$"
version2="AkkaVersion"
group3="com.typesafe.akka"
artifact3="akka-http_$scala.binary.version$"
version3="AkkaHttpVersion"
}
```

will be rendered as:

@@dependency[sbt,Maven,gradle] {
symbol="AkkaVersion"
value="2.5.29"
symbol2="AkkaHttpVersion"
value2="10.1.0"
group="com.typesafe.akka"
artifact="akka-stream_$scala.binary.version$"
version="AkkaVersion"
group2="com.typesafe.akka"
artifact2="akka-actor-typed_$scala.binary.version$"
version2="AkkaVersion"
group3="com.typesafe.akka"
artifact3="akka-http_$scala.binary.version$"
version3="AkkaHttpVersion"
}
Original file line number Diff line number Diff line change
Expand Up @@ -231,4 +231,111 @@ class DependencyDirectiveSpec extends MarkdownBaseSpec {
|</dl>
""")
}

it should "render symbolic version values" in {
markdown("""
|@@dependency[sbt,Maven,gradle] {
| symbol="AkkaHttpVersion"
| value="10.1.0"
| group="com.typesafe.akka"
| artifact="akka-http_$scala.binary.version$"
| version="AkkaHttpVersion"
|}""") shouldEqual html(s"""
|<dl class="dependency">
|<dt>sbt</dt>
|<dd>
|<pre class="prettyprint">
|<code class="language-scala">
|val AkkaHttpVersion = "10.1.0"
|libraryDependencies += "com.typesafe.akka" %% "akka-http" % AkkaHttpVersion</code></pre>
|</dd>
|<dt>Maven</dt>
|<dd>
|<pre class="prettyprint">
|<code class="language-xml">
|&lt;properties&gt;
| &lt;AkkaHttpVersion&gt;10.1.0&lt;/AkkaHttpVersion&gt;
|&lt;/properties&gt;
|&lt;dependency&gt;
| &lt;groupId&gt;com.typesafe.akka&lt;/groupId&gt;
| &lt;artifactId&gt;akka-http_2.12&lt;/artifactId&gt;
| &lt;version&gt;$${AkkaHttpVersion}&lt;/version&gt;
|&lt;/dependency&gt;</code></pre>
|</dd>
|<dt>gradle</dt>
|<dd>
|<pre class="prettyprint">
|<code class="language-gradle">
|versions += [
| AkkaHttpVersion: "10.1.0"
|]
|dependencies {
| compile group: 'com.typesafe.akka', name: 'akka-http_2.12', version: versions.AkkaHttpVersion
|}</code>
|</pre>
|</dd>
|</dl>""")
}

it should "render multiple symbolic version values" in {
markdown("""
|@@dependency[sbt,Maven,gradle] {
| symbol="AkkaVersion"
| value="2.5.29"
| symbol2="AkkaHttpVersion"
| value2="10.1.0"
| group="com.typesafe.akka"
| artifact="akka-stream_$scala.binary.version$"
| version="AkkaVersion"
| group2="com.typesafe.akka"
| artifact2="akka-http_$scala.binary.version$"
| version2="AkkaHttpVersion"
|}""") shouldEqual html(s"""
|<dl class="dependency">
|<dt>sbt</dt>
|<dd>
|<pre class="prettyprint">
|<code class="language-scala">
|val AkkaVersion = "2.5.29"
|val AkkaHttpVersion = "10.1.0"
|libraryDependencies ++= Seq(
| "com.typesafe.akka" %% "akka-stream" % AkkaVersion,
| "com.typesafe.akka" %% "akka-http" % AkkaHttpVersion
|)</code></pre>
|</dd>
|<dt>Maven</dt>
|<dd>
|<pre class="prettyprint">
|<code class="language-xml">
|&lt;properties&gt;
| &lt;AkkaVersion&gt;2.5.29&lt;/AkkaVersion&gt;
| &lt;AkkaHttpVersion&gt;10.1.0&lt;/AkkaHttpVersion&gt;
|&lt;/properties&gt;
|&lt;dependency&gt;
| &lt;groupId&gt;com.typesafe.akka&lt;/groupId&gt;
| &lt;artifactId&gt;akka-stream_2.12&lt;/artifactId&gt;
| &lt;version&gt;$${AkkaVersion}&lt;/version&gt;
|&lt;/dependency&gt;
|&lt;dependency&gt;
| &lt;groupId&gt;com.typesafe.akka&lt;/groupId&gt;
| &lt;artifactId&gt;akka-http_2.12&lt;/artifactId&gt;
| &lt;version&gt;$${AkkaHttpVersion}&lt;/version&gt;
|&lt;/dependency&gt;</code></pre>
|</dd>
|<dt>gradle</dt>
|<dd>
|<pre class="prettyprint">
|<code class="language-gradle">
|versions += [
| AkkaVersion: "2.5.29",
| AkkaHttpVersion: "10.1.0"
|]
|dependencies {
| compile group: 'com.typesafe.akka', name: 'akka-stream_2.12', version: versions.AkkaVersion,
| compile group: 'com.typesafe.akka', name: 'akka-http_2.12', version: versions.AkkaHttpVersion
|}</code>
|</pre>
|</dd>
|</dl>""")
}
}

0 comments on commit 02380eb

Please sign in to comment.