From dd80122d0d119a151b25e3389aaa0735da277801 Mon Sep 17 00:00:00 2001 From: Jules Ivanic Date: Mon, 13 Apr 2026 20:05:36 +1000 Subject: [PATCH 1/4] Upgrade to Scala 3.3.7 - Change scalaVersion from 2.13.18 to 3.3.7 - Rewrite Cell as a Scala 3 enum - Replace implicit vals/defs with given instances - Replace implicit conversions with given Conversion - Update wildcard imports from _ to * syntax - Replace deprecated private[this] with private - Remove @nowarn annotations on case classes (no longer needed in Scala 3) - Add @unchecked to irrefutable pattern match - Remove Scala 3-incompatible scalafix rules (RemoveUnused, LeakingImplicitClassVal, NoAutoTupling) - Remove semanticdbVersion override (built-in in Scala 3) --- .scalafix.conf | 4 --- build.sbt | 3 +- .../excel/ConcurrentConstantMemoryExcel.scala | 36 +++++++++---------- .../memory/excel/utils/KantanExtension.scala | 4 +-- .../ConcurrentConstantMemoryExcelSpec.scala | 8 +++-- project/BuildHelper.scala | 7 ++-- 6 files changed, 27 insertions(+), 35 deletions(-) diff --git a/.scalafix.conf b/.scalafix.conf index e4a9e39..b9f3825 100644 --- a/.scalafix.conf +++ b/.scalafix.conf @@ -1,11 +1,7 @@ rules = [ DisableSyntax - // ExplicitResultTypes // Waiting for: https://github.com/scalacenter/scalafix/issues/1583 - LeakingImplicitClassVal - NoAutoTupling NoValInForComprehension OrganizeImports - RemoveUnused RedundantSyntax ] diff --git a/build.sbt b/build.sbt index ba9da14..96cfb01 100644 --- a/build.sbt +++ b/build.sbt @@ -1,13 +1,12 @@ import BuildHelper.* ThisBuild / organization := "com.guizmaii" -ThisBuild / scalaVersion := "2.13.18" +ThisBuild / scalaVersion := "3.3.7" ThisBuild / scalafmtCheck := true ThisBuild / scalafmtSbtCheck := true ThisBuild / scalafmtOnCompile := !insideCI.value ThisBuild / semanticdbEnabled := true -ThisBuild / semanticdbVersion := scalafixSemanticdb.revision // use Scalafix compatible version ThisBuild / licenses := Seq("MIT" -> url("https://opensource.org/licenses/MIT")) ThisBuild / homepage := Some(url("https://github.com/guizmaii-opensource/JRubyConcurrentConstantMemoryExcel")) diff --git a/core/src/main/scala/com/colisweb/jruby/concurrent/constant/memory/excel/ConcurrentConstantMemoryExcel.scala b/core/src/main/scala/com/colisweb/jruby/concurrent/constant/memory/excel/ConcurrentConstantMemoryExcel.scala index a5114d9..c708b18 100644 --- a/core/src/main/scala/com/colisweb/jruby/concurrent/constant/memory/excel/ConcurrentConstantMemoryExcel.scala +++ b/core/src/main/scala/com/colisweb/jruby/concurrent/constant/memory/excel/ConcurrentConstantMemoryExcel.scala @@ -13,30 +13,31 @@ import org.apache.poi.xssf.streaming.SXSSFWorkbook import java.io.{File, FileOutputStream} import java.nio.file.{Files, Path} import java.util.UUID -import scala.annotation.{nowarn, switch} +import scala.annotation.switch import scala.collection.immutable.SortedSet import scala.collection.mutable.ListBuffer import scala.io.Codec -sealed abstract class Cell extends Product with Serializable -object Cell { - private[excel] case object BlankCell extends Cell - private[excel] final case class StringCell(value: String) extends Cell - private[excel] final case class NumericCell(value: Double) extends Cell +enum Cell { + case BlankCell + case StringCell(value: String) + case NumericCell(value: Double) +} +object Cell { private[excel] final val BLANK_CELL = 'b' private[excel] final val STRING_CELL = 's' private[excel] final val NUMERIC_CELL = 'n' - implicit private[excel] final val encoder: CellEncoder[Cell] = { + private[excel] given CellEncoder[Cell] = { case BlankCell => s"$BLANK_CELL:" case StringCell(value) => s"$STRING_CELL:$value" case NumericCell(value) => s"$NUMERIC_CELL:$value" } - implicit private[excel] final val decoder: CellDecoder[Cell] = + private[excel] given CellDecoder[Cell] = CellDecoder.fromUnsafe { s => - val Array(cellType, data) = s.split(":", 2) + val Array(cellType, data) = s.split(":", 2): @unchecked (cellType(0): @switch) match { case BLANK_CELL => Cell.BlankCell case STRING_CELL => Cell.StringCell(data) @@ -45,13 +46,11 @@ object Cell { } } -@nowarn // TODO: Remove this nowarm when upgrading to Scala3 final case class Page private[excel] (index: Int, path: Path) private[excel] object Page { - implicit final val ordering: Ordering[Page] = Ordering.by(_.index) + given Ordering[Page] = Ordering.by(_.index) } -@nowarn // TODO: Remove this nowarm when upgrading to Scala3 final case class ConcurrentConstantMemoryState private[excel] ( sheetName: String, headerData: Array[String], @@ -62,16 +61,15 @@ final case class ConcurrentConstantMemoryState private[excel] ( object ConcurrentConstantMemoryExcel { - import kantan.csv._ - import kantan.csv.ops._ + import kantan.csv.* + import kantan.csv.ops.* // https://nrinaudo.github.io/kantan.csv/bom.html - import kantan.codecs.resource.bom._ + import kantan.codecs.resource.bom.* private[excel] type Row = Array[Cell] - implicit private[this] final val codec: Codec = Codec.UTF8 - implicit private[this] final val scheduler: Scheduler = - Scheduler.computation(name = "ConcurrentConstantMemoryExcel-computation") + private given Codec = Codec.UTF8 + private given Scheduler = Scheduler.computation(name = "ConcurrentConstantMemoryExcel-computation") final val blankCell: Cell = Cell.BlankCell @@ -150,7 +148,7 @@ object ConcurrentConstantMemoryExcel { // TODO: Expose the `swallowIOExceptions` parameter in the `writeFile` function ? def clean(swallowIOExceptions: Boolean = false): Task[Unit] = Task { - import better.files._ // better-files `delete()` method also works on directories, unlike the Java one. + import better.files.* // better-files `delete()` method also works on directories, unlike the Java one. cms.tmpDirectory.toScala.delete(swallowIOExceptions) () } diff --git a/core/src/main/scala/com/colisweb/jruby/concurrent/constant/memory/excel/utils/KantanExtension.scala b/core/src/main/scala/com/colisweb/jruby/concurrent/constant/memory/excel/utils/KantanExtension.scala index f42ba81..54ab7ba 100644 --- a/core/src/main/scala/com/colisweb/jruby/concurrent/constant/memory/excel/utils/KantanExtension.scala +++ b/core/src/main/scala/com/colisweb/jruby/concurrent/constant/memory/excel/utils/KantanExtension.scala @@ -6,7 +6,7 @@ import scala.collection.immutable.ArraySeq private[excel] object KantanExtension { - implicit final def arrayEncoder[A](implicit CellEncoder: CellEncoder[A]): RowEncoder[Array[A]] = - (array: Array[A]) => ArraySeq.unsafeWrapArray(array).map(CellEncoder.encode) + given arrayEncoder[A](using cellEncoder: CellEncoder[A]): RowEncoder[Array[A]] = + (array: Array[A]) => ArraySeq.unsafeWrapArray(array).map(cellEncoder.encode) } diff --git a/core/src/test/scala/com/colisweb/jruby/concurrent/constant/memory/excel/ConcurrentConstantMemoryExcelSpec.scala b/core/src/test/scala/com/colisweb/jruby/concurrent/constant/memory/excel/ConcurrentConstantMemoryExcelSpec.scala index 98900ee..36ccb4c 100644 --- a/core/src/test/scala/com/colisweb/jruby/concurrent/constant/memory/excel/ConcurrentConstantMemoryExcelSpec.scala +++ b/core/src/test/scala/com/colisweb/jruby/concurrent/constant/memory/excel/ConcurrentConstantMemoryExcelSpec.scala @@ -8,6 +8,7 @@ import java.io.File import java.nio.file.Files import java.util.Date import scala.annotation.nowarn +import scala.language.implicitConversions @nowarn("msg=unused value") class ConcurrentConstantMemoryExcelSpec extends AnyFlatSpec with Matchers { @@ -16,14 +17,15 @@ class ConcurrentConstantMemoryExcelSpec extends AnyFlatSpec with Matchers { true shouldBe true } - import ConcurrentConstantMemoryExcel._ + import ConcurrentConstantMemoryExcel.* val sheet_name = "SHEET_NAME" val headers = Array("A", "B", "C") // Ugly but handy. Don't abuse of that ! - implicit final def toCell(value: String): Cell = if (value.isEmpty) blankCell else stringCell(value) - implicit final def toCell(value: Double): Cell = numericCell(value) + given Conversion[String, Cell] = value => if (value.isEmpty) blankCell else stringCell(value) + given Conversion[Double, Cell] = value => numericCell(value) + given Conversion[Int, Cell] = value => numericCell(value.toDouble) def newCMSPlz: Atomic[ConcurrentConstantMemoryState] = newWorkbookState(sheet_name, headers) def row(cells: Cell*): Array[Cell] = cells.toArray diff --git a/project/BuildHelper.scala b/project/BuildHelper.scala index a123da2..9baa16d 100644 --- a/project/BuildHelper.scala +++ b/project/BuildHelper.scala @@ -13,11 +13,8 @@ object BuildHelper { lazy val stdSettings = Seq( javacOptions ++= Seq("-source", javaTarget, "-target", javaTarget), - scalacOptions ++= Seq("-Xsource:3"), // TODO: Remove when upgrading to Scala3 - // TODO: Re-enable when upgrading to Scala3 - // scalacOptions ++= Seq("-no-indent"), // See https://x.com/ghostdogpr/status/1706589471469425074 - // TODO: Re-enable when upgrading to Scala3 - // scalacOptions ++= Seq("-language:noAutoTupling"), // See https://github.com/scala/scala3/discussions/19255 + scalacOptions ++= Seq("-no-indent"), // See https://x.com/ghostdogpr/status/1706589471469425074 + scalacOptions ++= Seq("-language:noAutoTupling"), // See https://github.com/scala/scala3/discussions/19255 scalacOptions ++= Seq(s"-release:$javaTarget"), scalacOptions --= (if (insideCI.value) Nil else Seq("-Xfatal-warnings")), // format: off From a5443f5a5818cb8d66376f840120faf7fd39c8a3 Mon Sep 17 00:00:00 2001 From: Jules Ivanic Date: Mon, 13 Apr 2026 20:06:28 +1000 Subject: [PATCH 2/4] gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index c62c5c7..a075de1 100644 --- a/.gitignore +++ b/.gitignore @@ -76,3 +76,5 @@ fabric.properties # Editor-based Rest Client .idea/httpRequests ### Scala template + +.bsp \ No newline at end of file From fffa2f7180f3a024c3fda2dc09e61a6ea2f00f58 Mon Sep 17 00:00:00 2001 From: Jules Ivanic Date: Mon, 13 Apr 2026 20:08:12 +1000 Subject: [PATCH 3/4] Fix Release Drafter failing on pull requests disable-releaser on PR events to prevent invalid target_commitish error. Also fix branch name from main to master. --- .github/workflows/draft.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/draft.yml b/.github/workflows/draft.yml index 1adb557..4219805 100644 --- a/.github/workflows/draft.yml +++ b/.github/workflows/draft.yml @@ -3,7 +3,7 @@ name: Release Drafter on: push: branches: - - main + - master pull_request: types: [opened, reopened, synchronize] @@ -22,5 +22,7 @@ jobs: pull-requests: write steps: - uses: release-drafter/release-drafter@v7.1.1 + with: + disable-releaser: ${{ github.event_name == 'pull_request' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From bc29f62db19d32e8e59f8c56c2cd238507bc6d3a Mon Sep 17 00:00:00 2001 From: Jules Ivanic Date: Mon, 13 Apr 2026 20:09:22 +1000 Subject: [PATCH 4/4] Fix Release Drafter: only run on push to master Remove pull_request trigger that caused invalid target_commitish errors. --- .github/workflows/draft.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/draft.yml b/.github/workflows/draft.yml index 4219805..ab36586 100644 --- a/.github/workflows/draft.yml +++ b/.github/workflows/draft.yml @@ -5,9 +5,6 @@ on: branches: - master - pull_request: - types: [opened, reopened, synchronize] - permissions: contents: read @@ -22,7 +19,5 @@ jobs: pull-requests: write steps: - uses: release-drafter/release-drafter@v7.1.1 - with: - disable-releaser: ${{ github.event_name == 'pull_request' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}