Skip to content

Commit

Permalink
Merge pull request #198 from rreckel/develop
Browse files Browse the repository at this point in the history
  • Loading branch information
eed3si9n authored Aug 6, 2023
2 parents 85e3705 + abf290c commit d4b63f2
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/main/scala/sbtbuildinfo/BuildInfoPlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,6 @@ object BuildInfoPlugin extends AutoPlugin {
buildInfoKeys := Seq(name, version, scalaVersion, sbtVersion),
buildInfoBuildNumber := buildNumberTask(baseDirectory.value, 1),
buildInfoOptions := Seq(),
buildInfoRenderFactory := ScalaCaseObjectRenderer.apply
buildInfoRenderFactory := (if(scalaVersion.value.startsWith("3")) Scala3CaseObjectRenderer.apply else ScalaCaseObjectRenderer.apply)
)
}
66 changes: 66 additions & 0 deletions src/main/scala/sbtbuildinfo/Scala3CaseClassRenderer.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package sbtbuildinfo

case class Scala3CaseClassRenderer(options: Seq[BuildInfoOption], pkg: String, obj: String) extends Scala3Renderer {
override def fileType = BuildInfoType.Source
override def extension = "scala"

val traitNames = options.collect{case BuildInfoOption.Traits(ts @ _*) => ts}.flatten
val objTraits = if (traitNames.isEmpty) "" else "extends " ++ traitNames.mkString(" with ")

// It is safe to add `import scala.Predef` even though we need to keep `-Ywarn-unused-import` in mind
// because we always generate code that has a reference to `String`. If the "base" generated code were to be
// changed and no longer contain a reference to `String`, we would need to remove `import scala.Predef` and
// fully qualify every reference. Note it is NOT safe to use `import scala._` because of the possibility of
// the project using `-Ywarn-unused-import` because we do not always generated references that are part of
// `scala` such as `scala.Option`.
val importScalaPredef = options.contains(BuildInfoOption.ImportScalaPredef)
def header = List(
"// $COVERAGE-OFF$",
s"package $pkg",
""
)
val imports = if (importScalaPredef) List(
"import scala.Predef.*",
""
) else Nil
val generateComment = List(
s"/** This file was generated by sbt-buildinfo. */"
)

def footer = List("// $COVERAGE-ON$")

override def renderKeys(buildInfoResults: Seq[BuildInfoResult]) =
header ++ imports ++ generateComment ++
caseClassDefinitionBegin ++
buildInfoResults.flatMap(caseClassParameter).mkString(",\n").split("\n") ++
caseClassDefinitionEnd ++
toMapLines(buildInfoResults) ++
toJsonLines ++
caseClassEnd ++
List("") ++
caseObjectLine(buildInfoResults) ++
footer

private def caseClassDefinitionBegin = List(
withPkgPriv(s"case class $obj(")
)

private def caseClassParameter(r: BuildInfoResult): Seq[String] = {
val typeDecl = getType(r.typeExpr) getOrElse "scala.Any"

List(
s" ${r.identifier}: $typeDecl"
)
}

private def caseClassDefinitionEnd = List(s") $objTraits {", "")
private def caseClassEnd = List("}")

private def caseObjectLine(buildInfoResults: Seq[BuildInfoResult]) = List(
withPkgPriv(s"case object $obj {"),
s" def apply(): $obj = new $obj(${buildInfoResults.map(r => s"\n ${r.identifier} = ${quote(r.value)}").mkString(",")})",
s" val get = apply()",
s" val value = apply()",
s"}"
)
}
71 changes: 71 additions & 0 deletions src/main/scala/sbtbuildinfo/Scala3CaseObjectRenderer.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package sbtbuildinfo

case class Scala3CaseObjectRenderer(options: Seq[BuildInfoOption], pkg: String, obj: String) extends Scala3Renderer {

override def fileType = BuildInfoType.Source
override def extension = "scala"
val traitNames = options.collect{case BuildInfoOption.Traits(ts @ _*) => ts}.flatten
val objTraits = if (traitNames.isEmpty) "" else " extends " ++ traitNames.mkString(" with ")
val constantValue = options.contains(BuildInfoOption.ConstantValue)

// It is safe to add `import scala.Predef` even though we need to keep `-Ywarn-unused-import` in mind
// because we always generate code that has a reference to `String`. If the "base" generated code were to be
// changed and no longer contain a reference to `String`, we would need to remove `import scala.Predef` and
// fully qualify every reference. Note it is NOT safe to use `import scala._` because of the possibility of
// the project using `-Ywarn-unused-import` because we do not always generated references that are part of
// `scala` such as `scala.Option`.
val importScalaPredef = options.contains(BuildInfoOption.ImportScalaPredef)
def header = List(
"// $COVERAGE-OFF$",
s"package $pkg",
""
)
val imports = if (importScalaPredef) List(
"import scala.Predef.*",
""
) else Nil
val objectHeader = List(
s"/** This object was generated by sbt-buildinfo. */",
withPkgPriv(s"case object $obj$objTraits {")
)

def footer = List("}", "// $COVERAGE-ON$")

override def renderKeys(buildInfoResults: Seq[BuildInfoResult]) =
header ++ imports ++ objectHeader ++
buildInfoResults.flatMap(line) ++
Seq(toStringLines(buildInfoResults)) ++
toMapLines(buildInfoResults) ++
toJsonLines ++
footer

private val constantTypes = Set("scala.Int", "scala.Long", "scala.Double", "scala.Boolean", "scala.Symbol", "String")

private def line(result: BuildInfoResult): Seq[String] = {
import result._
val (typeDecl, modifier) =
getType(result.typeExpr) match {
case Some(tp) if !constantValue || !constantTypes(tp) =>
(s": $tp", "")
case _ if constantValue =>
("", "final ")
case _ =>
("", "")
}
List(
s" /** The value is ${quote(value)}. */",
s" ${modifier}val $identifier$typeDecl = ${quote(value)}"
)
}

def toStringLines(results: Seq[BuildInfoResult]): String = {
val idents = results.map(_.identifier)
val fmt = idents.map("%s: %%s" format _).mkString(", ")
val vars = idents.mkString(", ")
s""" override val toString: String = {
| "$fmt".format(
| $vars
| )
| }""".stripMargin
}
}
29 changes: 29 additions & 0 deletions src/main/scala/sbtbuildinfo/Scala3Renderer.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package sbtbuildinfo

abstract class Scala3Renderer extends ScalaRenderer {

override protected def toJsonLines: Seq[String] =
if (options contains BuildInfoOption.ToJson)
List(
"""| private def quote(x: scala.Any): String = "\"" + x + "\""
| private def toJsonValue[T <: Matchable](value: T): String = {
| value match {
| case elem: scala.collection.Seq[? <: Matchable] => elem.map(toJsonValue).mkString("[", ",", "]")
| case elem: scala.Option[? <: Matchable] => elem.map(toJsonValue).getOrElse("null")
| case elem: scala.collection.Map[?, ? <: Matchable] => elem.map {
| case (k, v) => toJsonValue(k.toString) + ":" + toJsonValue(v)
| }.mkString("{", ", ", "}")
| case d: scala.Double => d.toString
| case f: scala.Float => f.toString
| case l: scala.Long => l.toString
| case i: scala.Int => i.toString
| case s: scala.Short => s.toString
| case bool: scala.Boolean => bool.toString
| case str: String => quote(str)
| case other => quote(other.toString)
| }
| }
|
| val toJson: String = toJsonValue(toMap)""".stripMargin)
else Nil
}

0 comments on commit d4b63f2

Please sign in to comment.