Skip to content

Commit

Permalink
Scala 3 (#207)
Browse files Browse the repository at this point in the history
  • Loading branch information
xuwei-k committed Mar 6, 2022
1 parent ca10c64 commit c484eda
Show file tree
Hide file tree
Showing 11 changed files with 105 additions and 44 deletions.
8 changes: 7 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
scala: [2.11.12, 2.13.6, 2.12.14]
scala: [2.11.12, 2.13.6, 2.12.14, 3.1.1]
java: [adopt@1.8]
runs-on: ${{ matrix.os }}
steps:
Expand Down Expand Up @@ -55,9 +55,15 @@ jobs:
run: sbt ++${{ matrix.scala }} mimaReportBinaryIssues

- name: Build project
if: matrix.scala == '3.1.1'
run: sbt ++${{ matrix.scala }} clean test

- name: Build project
if: matrix.scala != '3.1.1'
run: sbt ++${{ matrix.scala }} clean coverage test

- name: Upload coverage data to Coveralls
if: matrix.scala != '3.1.1'
env:
COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
COVERALLS_FLAG_NAME: Scala ${{ matrix.scala }}
Expand Down
5 changes: 5 additions & 0 deletions .scalafmt.conf
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,8 @@ align.tokens.add = [
{code = ":=", owner = "Term.ApplyInfix"}
]
rewrite.rules = [RedundantBraces, RedundantParens]

# TODO update scalafmt for Scala 3 support
project.excludeFilters = [
"src/main/scala-3/net/ceedubs/ficus/util/EnumerationUtil.scala"
]
54 changes: 41 additions & 13 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,28 @@ lazy val gcTask = gc := {
System gc ()
}

def Scala3 = "3.1.1"

ThisBuild / githubWorkflowBuild := Seq(
WorkflowStep.Sbt(List("mimaReportBinaryIssues"), name = Some("Report binary compatibility issues")),
WorkflowStep.Sbt(List("clean", "coverage", "test"), name = Some("Build project"))
WorkflowStep.Sbt(
List("clean", "test"),
name = Some("Build project"),
cond = Some(s"matrix.scala == '${Scala3}'")
),
WorkflowStep.Sbt(
List("clean", "coverage", "test"),
name = Some("Build project"),
cond = Some(s"matrix.scala != '${Scala3}'")
)
)

ThisBuild / githubWorkflowBuildPostamble ++= Seq(
// See https://github.com/scoverage/sbt-coveralls#github-actions-integration
WorkflowStep.Sbt(
List("coverageReport", "coveralls"),
name = Some("Upload coverage data to Coveralls"),
cond = Some(s"matrix.scala != '${Scala3}'"),
env = Map(
"COVERALLS_REPO_TOKEN" -> "${{ secrets.GITHUB_TOKEN }}",
"COVERALLS_FLAG_NAME" -> "Scala ${{ matrix.scala }}"
Expand All @@ -29,7 +41,7 @@ ThisBuild / githubWorkflowUseSbtThinClient := false

ThisBuild / githubWorkflowPublishTargetBranches := Seq()

ThisBuild / crossScalaVersions := Seq("2.10.7", "2.11.12", "2.13.6", "2.12.14")
ThisBuild / crossScalaVersions := Seq("2.10.7", "2.11.12", "2.13.6", "2.12.14", Scala3)
ThisBuild / scalaVersion := (ThisBuild / crossScalaVersions).value.last

// Coveralls doesn't really work with Scala 2.10.7 so we are disabling it for CI
Expand Down Expand Up @@ -67,32 +79,41 @@ lazy val root = project
Compile / unmanagedSourceDirectories ++= {
(Compile / unmanagedSourceDirectories).value.map { dir =>
CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, 13)) => file(dir.getPath ++ "-2.13+")
case _ => file(dir.getPath ++ "-2.13-")
case Some((2, 13) | (3, _)) => file(dir.getPath ++ "-2.13+")
case _ => file(dir.getPath ++ "-2.13-")
}
}
},
Test / unmanagedSourceDirectories ++= {
(Test / unmanagedSourceDirectories).value.map { dir =>
CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, 13)) => file(dir.getPath ++ "-2.13+")
case _ => file(dir.getPath ++ "-2.13-")
case Some((2, 13) | (3, _)) => file(dir.getPath ++ "-2.13+")
case _ => file(dir.getPath ++ "-2.13-")
}
}
},
libraryDependencies ++= {
if (scalaBinaryVersion.value != "3") {
Seq(
"com.chuusai" %% "shapeless" % "2.3.3" % Test,
"org.scala-lang" % "scala-reflect" % scalaVersion.value % Provided,
"org.scala-lang" % "scala-compiler" % scalaVersion.value % Provided
)
} else {
Nil
}
},
libraryDependencies ++=
(if (scalaVersion.value.startsWith("2.10"))
Seq("org.specs2" %% "specs2-core" % "3.10.0" % Test, "org.specs2" %% "specs2-scalacheck" % "3.10.0" % Test)
else
Seq("org.specs2" %% "specs2-core" % "4.8.3" % Test, "org.specs2" %% "specs2-scalacheck" % "4.8.3" % Test)) ++
Seq("org.specs2" %% "specs2-core" % "4.8.3" % Test, "org.specs2" %% "specs2-scalacheck" % "4.8.3" % Test)
.map(_ cross CrossVersion.for3Use2_13)) ++
Seq(
"org.scalacheck" %% "scalacheck" % "1.14.1" % Test,
"com.chuusai" %% "shapeless" % "2.3.3" % Test,
"com.typesafe" % "config" % "1.3.4",
"org.scala-lang" % "scala-reflect" % scalaVersion.value % Provided,
"org.scala-lang" % "scala-compiler" % scalaVersion.value % Provided
"org.scalacheck" %% "scalacheck" % "1.14.1" % Test cross CrossVersion.for3Use2_13,
"com.typesafe" % "config" % "1.3.4"
) ++
(if (!scalaVersion.value.startsWith("2.13"))
(if (Set("2.10", "2.11", "2.12").contains(scalaBinaryVersion.value))
Seq(
compilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full),
"org.typelevel" %% "macro-compat" % "1.1.1"
Expand All @@ -115,6 +136,13 @@ lazy val root = project
toPath != "application.conf"
}
},
Test / sources := {
if (scalaBinaryVersion.value == "3") {
Nil // TODO
} else {
(Test / sources).value
}
},
Publish.settings,
releaseCrossBuild := true,
mimaPreviousArtifacts :=
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import scala.language.experimental.macros
import scala.reflect.internal.{Definitions, StdNames, SymbolTable}
import scala.reflect.macros.blackbox

case class Generated[+A](value: A) extends AnyVal

trait ArbitraryTypeReader {
implicit def arbitraryTypeValueReader[T]: Generated[ValueReader[T]] =
macro ArbitraryTypeReaderMacros.arbitraryTypeValueReader[T]
Expand Down
5 changes: 5 additions & 0 deletions src/main/scala-2/net/ceedubs/ficus/util/EnumerationUtil.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package net.ceedubs.ficus.util

private[ficus] object EnumerationUtil {
type EnumValue[A <: Enumeration] = A#Value
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package net.ceedubs.ficus.readers

// TODO
trait ArbitraryTypeReader
9 changes: 9 additions & 0 deletions src/main/scala-3/net/ceedubs/ficus/util/EnumerationUtil.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package net.ceedubs.ficus.util

private[ficus] object EnumerationUtil {
private[this] type Aux[A] = { type Value = A }

type EnumValue[A <: Enumeration] = A match {
case Aux[a] => a
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package net.ceedubs.ficus.readers

import net.ceedubs.ficus.util.EnumerationUtil.EnumValue

trait CaseInsensitiveEnumerationReader extends EnumerationReader {

override protected def findEnumValue[T <: Enumeration](enum: T, configValue: String): Option[T#Value] =
enum.values.find(_.toString.toLowerCase == configValue.toLowerCase)
override protected def findEnumValue[T <: Enumeration](`enum`: T, configValue: String): Option[EnumValue[T]] =
`enum`.values.find(_.toString.toLowerCase == configValue.toLowerCase)
}
53 changes: 27 additions & 26 deletions src/main/scala/net/ceedubs/ficus/readers/EnumerationReader.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,41 @@ package net.ceedubs.ficus.readers

import com.typesafe.config.ConfigException.{BadValue, Generic}
import com.typesafe.config.Config

import net.ceedubs.ficus.util.EnumerationUtil.EnumValue
import scala.reflect.ClassTag
import scala.util.{Failure, Success, Try}

trait EnumerationReader {
implicit def enumerationValueReader[T <: Enumeration: ClassTag]: ValueReader[T#Value] = new ValueReader[T#Value] {
def read(config: Config, path: String): T#Value = {
val c = implicitly[ClassTag[T]].runtimeClass
val enum = Try(c.getField("MODULE$")) match {
case Success(m) => m.get(null).asInstanceOf[T]
case Failure(e) =>
throw new Generic(
"Cannot get instance of enum: " + c.getCanonicalName + "; " +
"make sure the enum is an object and it's not contained in a class or trait",
e
)
}
implicit def enumerationValueReader[T <: Enumeration: ClassTag]: ValueReader[EnumValue[T]] =
new ValueReader[EnumValue[T]] {
def read(config: Config, path: String): EnumValue[T] = {
val c = implicitly[ClassTag[T]].runtimeClass
val `enum` = Try(c.getField("MODULE$")) match {
case Success(m) => m.get(null).asInstanceOf[T]
case Failure(e) =>
throw new Generic(
"Cannot get instance of enum: " + c.getCanonicalName + "; " +
"make sure the enum is an object and it's not contained in a class or trait",
e
)
}

val value = config.getString(path)
findEnumValue(enum, value)
.getOrElse(
throw new BadValue(
config.origin(),
path,
value + " isn't a valid value for enum: " +
"" + c.getCanonicalName + "; allowed values: " + enum.values.mkString(", ")
val value = config.getString(path)
findEnumValue(`enum`, value)
.getOrElse(
throw new BadValue(
config.origin(),
path,
value + " isn't a valid value for enum: " +
"" + c.getCanonicalName + "; allowed values: " + `enum`.values.mkString(", ")
)
)
)
.asInstanceOf[T#Value]
.asInstanceOf[EnumValue[T]]
}
}
}

protected def findEnumValue[T <: Enumeration](enum: T, configValue: String): Option[T#Value] =
enum.values.find(_.toString == configValue)
protected def findEnumValue[T <: Enumeration](`enum`: T, configValue: String): Option[EnumValue[T]] =
`enum`.values.find(_.toString == configValue)
}

object EnumerationReader extends EnumerationReader
3 changes: 3 additions & 0 deletions src/main/scala/net/ceedubs/ficus/readers/Generated.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package net.ceedubs.ficus.readers

case class Generated[+A](value: A) extends AnyVal

0 comments on commit c484eda

Please sign in to comment.