From 4b4b4b5a9fd606d23892b4443d131657b8c20fe6 Mon Sep 17 00:00:00 2001 From: Lorenzo Gabriele Date: Thu, 17 Oct 2024 15:56:35 +0200 Subject: [PATCH 01/14] Update libraries and Scala to latest versions --- .gitignore | 1 + build.sbt | 49 +++------- js/src/main/scala/toml/PlatformRules.scala | 4 +- jvm/src/main/scala/toml/PlatformRules.scala | 13 +-- jvm/src/test/scala/toml/DateGenSpec.scala | 13 +-- jvm/src/test/scala/toml/DateSpec.scala | 7 +- jvm/src/test/scala/toml/FileSpec.scala | 7 +- .../src/main/scala/toml/PlatformRules.scala | 4 +- project/build.properties | 2 +- project/plugins.sbt | 8 +- shared/src/main/scala/toml/Constants.scala | 22 ++--- shared/src/main/scala/toml/Rules.scala | 92 ++++++++++--------- shared/src/main/scala/toml/Toml.scala | 8 +- shared/src/test/scala/toml/CodecSpec.scala | 8 +- shared/src/test/scala/toml/EmbedSpec.scala | 26 +++--- .../src/test/scala/toml/GeneratedSpec.scala | 34 ++++--- .../src/test/scala/toml/GenerationSpec.scala | 4 +- shared/src/test/scala/toml/ParseSpec.scala | 4 +- shared/src/test/scala/toml/RulesSpec.scala | 7 +- shared/src/test/scala/toml/TestHelpers.scala | 22 ++--- version.sbt | 2 +- 21 files changed, 165 insertions(+), 172 deletions(-) diff --git a/.gitignore b/.gitignore index d9adc9c..f2a4aae 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ lib_managed/ src_managed/ project/boot/ project/plugins/project/ +/.bsp # Scala-IDE specific .scala_dependencies diff --git a/build.sbt b/build.sbt index 625368e..2f64727 100644 --- a/build.sbt +++ b/build.sbt @@ -1,21 +1,21 @@ // shadow sbt-scalajs' crossProject and CrossType from Scala.js 0.6.x import sbtcrossproject.CrossPlugin.autoImport.{crossProject, CrossType} -val Scala2_11 = "2.11.12" -val Scala2_12 = "2.12.10" -val Scala2_13 = "2.13.1" -val FastParse = "1.0.1" -val Shapeless = "2.3.3" -val ScalaCheck = "1.14.2" -val ScalaTest = "3.0.8" -val ScalaTestNative = "3.2.0-SNAP10" +val Scala2_12 = "2.12.19" +val Scala2_13 = "2.13.14" +val FastParse = "3.1.1" +val Shapeless = "2.3.12" +val ScalaCheck = "1.18.1" +val ScalaTest = "3.2.19" + +val ScalaTestScalaCheck = s"$ScalaTest.0" val SharedSettings = Seq( name := "toml-scala", organization := "tech.sparse", scalaVersion := Scala2_13, - crossScalaVersions := Seq(Scala2_13, Scala2_12, Scala2_11), + crossScalaVersions := Seq(Scala2_13, Scala2_12), pomExtra := https://github.com/sparsetech/toml-scala @@ -40,7 +40,7 @@ val SharedSettings = Seq( lazy val root = project.in(file(".")) .aggregate(toml.js, toml.jvm, toml.native) .settings(SharedSettings: _*) - .settings(skip in publish := true) + .settings(publish / skip := true) lazy val toml = crossProject(JSPlatform, JVMPlatform, NativePlatform) @@ -48,29 +48,10 @@ lazy val toml = .settings(SharedSettings) .settings( libraryDependencies ++= Seq( - "org.scalameta" %%% "fastparse" % FastParse, - "com.chuusai" %%% "shapeless" % Shapeless - ) - ) - .jsSettings( - libraryDependencies ++= Vector( - "org.scalacheck" %%% "scalacheck" % ScalaCheck % "test", - "org.scalatest" %%% "scalatest" % ScalaTest % "test" + "com.lihaoyi" %%% "fastparse" % FastParse, + "com.chuusai" %%% "shapeless" % Shapeless, + "org.scalacheck" %%% "scalacheck" % ScalaCheck % Test, + "org.scalatest" %%% "scalatest" % ScalaTest % Test, + "org.scalatestplus" %%% s"scalacheck-${ScalaCheck.split('.').take(2).mkString("-")}" % ScalaTestScalaCheck % Test ) ) - .jvmSettings( - libraryDependencies ++= Vector( - "org.scalacheck" %% "scalacheck" % ScalaCheck % "test", - "org.scalatest" %% "scalatest" % ScalaTest % "test" - ) - ) - .nativeSettings( - scalaVersion := Scala2_11, - crossScalaVersions := Seq(Scala2_11), - // See https://github.com/scalalandio/chimney/issues/78#issuecomment-419705142 - nativeLinkStubs := true, - libraryDependencies ++= Vector( - "org.scalatest" %%% "scalatest" % ScalaTestNative % "test" - ), - excludeFilter in Test := "*GeneratedSpec*" || "*Generators*" - ) diff --git a/js/src/main/scala/toml/PlatformRules.scala b/js/src/main/scala/toml/PlatformRules.scala index 6bfe4d2..4969a41 100644 --- a/js/src/main/scala/toml/PlatformRules.scala +++ b/js/src/main/scala/toml/PlatformRules.scala @@ -1,7 +1,7 @@ package toml -import scala.meta.internal.fastparse.all._ +import fastparse._ trait PlatformRules { - val date = StringIn().map(_ => null.asInstanceOf[Value]) + def date[$: P] = StringIn().map(_ => null.asInstanceOf[Value]) } diff --git a/jvm/src/main/scala/toml/PlatformRules.scala b/jvm/src/main/scala/toml/PlatformRules.scala index 94bfb12..98f2f32 100644 --- a/jvm/src/main/scala/toml/PlatformRules.scala +++ b/jvm/src/main/scala/toml/PlatformRules.scala @@ -2,13 +2,14 @@ package toml import java.time._ -import scala.meta.internal.fastparse.all._ +import fastparse._ +import fastparse.NoWhitespace._ trait PlatformRules { this: Rules => private val TenPowers = List(1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000) - val localTime: Parser[Value.Time] = P( + def localTime[$: P]: P[Value.Time] = P( digit.rep(2).! ~ ":" ~ digit.rep(2).! ~ ":" ~ digit.rep(2).! ~ ("." ~ digit.rep.!).? ).map { case (h, m, s, ns) => val nano = ns.map { str => @@ -19,24 +20,24 @@ trait PlatformRules { this: Rules => Value.Time(LocalTime.of(h.toInt, m.toInt, s.toInt, nano)) } - val localDate: Parser[Value.Date] = P( + def localDate[$: P]: P[Value.Date] = P( digit.rep(4).! ~ "-" ~ digit.rep(2).! ~ "-" ~ digit.rep(2).! ).map { case (y, m, d) => Value.Date(LocalDate.of(y.toInt, m.toInt, d.toInt)) } - val localDateTime: Parser[Value.DateTime] = P( + def localDateTime[$: P]: P[Value.DateTime] = P( localDate ~ "T" ~ localTime ).map { case (date, time) => Value.DateTime(LocalDateTime.of(date.value, time.value)) } - val offsetDateTime: Parser[Value.OffsetDateTime] = P( + def offsetDateTime[$: P]: P[Value.OffsetDateTime] = P( localDateTime ~ ("Z" | (("-" | "+") ~ digit.rep(2) ~ ":" ~ digit.rep(2))).! ).map { case (dateTime, offset) => Value.OffsetDateTime( OffsetDateTime.of(dateTime.value, ZoneOffset.of(offset))) } - val date = P(offsetDateTime | localDateTime | localDate | localTime) + def date[$: P] = P(offsetDateTime | localDateTime | localDate | localTime) } diff --git a/jvm/src/test/scala/toml/DateGenSpec.scala b/jvm/src/test/scala/toml/DateGenSpec.scala index 837f652..a569142 100644 --- a/jvm/src/test/scala/toml/DateGenSpec.scala +++ b/jvm/src/test/scala/toml/DateGenSpec.scala @@ -1,26 +1,27 @@ package toml import org.scalatest.prop._ -import org.scalatest.{Matchers, PropSpec} +import org.scalatest.propspec.AnyPropSpec +import org.scalatest.matchers.should.Matchers import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks -import scala.meta.internal.fastparse.all._ -import scala.meta.internal.fastparse.core.Parsed.{Failure, Success} +import fastparse._ +import fastparse.Parsed.{Failure, Success} -class DateGenSpec extends PropSpec with ScalaCheckPropertyChecks with Matchers { +class DateGenSpec extends AnyPropSpec with ScalaCheckPropertyChecks with Matchers { import TestHelpers._ property("parse dates following the RFC 3339 spec (`date` parser)") { import Generators.Dates._ forAll(dateFormatGen) { s => - shouldBeSuccess(Rules.offsetDateTime.parse(s)) + shouldBeSuccess(parse(s, Rules.offsetDateTime(_))) } } property("parse dates following the RFC 3339 spec") { import Generators.Dates._ forAll(dateFormatGen) { s => - shouldBeSuccess(Rules.elem.parse(s)) + shouldBeSuccess(parse(s, Rules.elem(_))) } } } diff --git a/jvm/src/test/scala/toml/DateSpec.scala b/jvm/src/test/scala/toml/DateSpec.scala index d307db6..35246fe 100644 --- a/jvm/src/test/scala/toml/DateSpec.scala +++ b/jvm/src/test/scala/toml/DateSpec.scala @@ -2,10 +2,11 @@ package toml import java.time._ -import org.scalatest.{FunSuite, Matchers} -import scala.meta.internal.fastparse.core.Parsed._ +import org.scalatest.funsuite.AnyFunSuite +import org.scalatest.matchers.should.Matchers +import fastparse.Parsed._ -class DateSpec extends FunSuite with Matchers { +class DateSpec extends AnyFunSuite with Matchers { import TestHelpers._ test("Parse local date") { diff --git a/jvm/src/test/scala/toml/FileSpec.scala b/jvm/src/test/scala/toml/FileSpec.scala index dbc9258..8044945 100644 --- a/jvm/src/test/scala/toml/FileSpec.scala +++ b/jvm/src/test/scala/toml/FileSpec.scala @@ -3,10 +3,11 @@ package toml import java.time._ import java.io.File -import org.scalatest.{FunSuite, Matchers} -import scala.meta.internal.fastparse.core.Parsed._ +import org.scalatest.matchers.should.Matchers +import org.scalatest.funsuite.AnyFunSuite +import fastparse.Parsed._ -class FileSpec extends FunSuite with Matchers { +class FileSpec extends AnyFunSuite with Matchers { import TestHelpers._ test("Parse multi-line strings") { diff --git a/native/src/main/scala/toml/PlatformRules.scala b/native/src/main/scala/toml/PlatformRules.scala index 6bfe4d2..4969a41 100644 --- a/native/src/main/scala/toml/PlatformRules.scala +++ b/native/src/main/scala/toml/PlatformRules.scala @@ -1,7 +1,7 @@ package toml -import scala.meta.internal.fastparse.all._ +import fastparse._ trait PlatformRules { - val date = StringIn().map(_ => null.asInstanceOf[Value]) + def date[$: P] = StringIn().map(_ => null.asInstanceOf[Value]) } diff --git a/project/build.properties b/project/build.properties index dca663d..be54e77 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version = 1.2.8 +sbt.version = 1.10.0 diff --git a/project/plugins.sbt b/project/plugins.sbt index 5040e26..a3b9603 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,6 +1,6 @@ logLevel := Level.Warn -addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "0.6.1") -addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "0.6.1") -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.29") -addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.3.9") +addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.3.0") +addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "1.3.0") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.16.0") +addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.4") diff --git a/shared/src/main/scala/toml/Constants.scala b/shared/src/main/scala/toml/Constants.scala index 9291ffb..8aa01d9 100644 --- a/shared/src/main/scala/toml/Constants.scala +++ b/shared/src/main/scala/toml/Constants.scala @@ -1,20 +1,20 @@ package toml object Constants { - val SingleQuote = '\'' - val DoubleQuote = '"' + final val SingleQuote = '\'' + final val DoubleQuote = '"' - val MultiLineSingleQuote = "'''" - val MultiLineDoubleQuote = "\"\"\"" + final val MultiLineSingleQuote = "'''" + final val MultiLineDoubleQuote = "\"\"\"" - val NumbersRange = '0' to '9' - val LettersRange = ('a' to 'z') ++ ('A' to 'Z') + final val NumbersRange = "0-9" + final val LettersRange = "a-zA-Z" - val Dashes = Set('-', '_') + final val Dashes = "\\-_" - val Lf = "\n" - val CrLf = "\r\n" + final val Lf = "\n" + final val CrLf = "\r\n" - val WhitespaceChars = Set(' ', '\t') - val EscapeChars = Set('\b', '\t', '\n', '\f', '\r', '"', '\\') + final val WhitespaceChars = " \t" + final val EscapeChars = "\b\t\n\f\r\"\\" } diff --git a/shared/src/main/scala/toml/Rules.scala b/shared/src/main/scala/toml/Rules.scala index eedae83..3751efa 100644 --- a/shared/src/main/scala/toml/Rules.scala +++ b/shared/src/main/scala/toml/Rules.scala @@ -1,6 +1,7 @@ package toml -import scala.meta.internal.fastparse.all._ +import fastparse._ +import NoWhitespace._ private[toml] case class NamedFunction[T, V](f: T => V, name: String) extends (T => V) @@ -22,46 +23,47 @@ class Rules(extensions: Set[Extension]) extends PlatformRules { val UntilNewline = NamedFunction(!CrLf.contains(_: Char), "UntilNewline") - val newLine = P(StringIn(CrLf, Lf)) - val charsChunk = P(CharsWhile(UntilNewline)) - val comment = P("#" ~ charsChunk.? ~ &(newLine | End)) - val whitespace = P(CharIn(WhitespaceChars.toList)) + def newLine[$: P] = P(StringIn(CrLf, Lf)) + def charsChunk[$: P] = P(CharsWhile(UntilNewline)) + def comment[$: P] = P("#" ~ charsChunk.? ~ &(newLine | End)) + def whitespace[$: P] = P(CharIn(WhitespaceChars)) - val skip = P(NoCut(NoTrace((whitespace | comment | newLine).rep))) - val skipWs = P(NoCut(NoTrace(whitespace.rep))) + def skip[$: P] = P(NoCut(NoTrace((whitespace | comment | newLine).rep))) + def skipWs[$: P] = P(NoCut(NoTrace(whitespace.rep))) - val letter = P(CharIn(LettersRange)) - val digit = P(CharIn(NumbersRange)) - val digits = P(digit.rep(1)) - val dash = P(CharIn(Dashes.toList)) + def letter[$: P] = P(CharIn(LettersRange)) + def digit[$: P] = P(CharIn(NumbersRange)) + def digits[$: P] = P(digit.rep(1)) + def dash[$: P] = P(CharIn(Dashes)) val StringChars = NamedFunction(!"\"\\".contains(_: Char), "StringChars") - val strChars = P(CharsWhile(StringChars)) + def strChars[$: P] = P(CharsWhile(StringChars)) - val hexDigit = P(CharIn('0' to '9', 'a' to 'f', 'A' to 'F')) - val unicodeEsc = P("u" ~ hexDigit.rep(4)) - val unicodeEscLong = P("U" ~ hexDigit.rep(8)) - val escape = P("\\" ~ ( + + def hexDigit[$: P] = P(CharIn("0-9", "a-f", "A-F")) + def unicodeEsc[$: P] = P("u" ~ hexDigit.rep(4)) + def unicodeEscLong[$: P] = P("U" ~ hexDigit.rep(8)) + def escape[$: P] = P("\\" ~ ( CharIn("\"/\\bfnrt") | unicodeEsc | unicodeEscLong )) - val basicStr: Parser[Value.Str] = - P(DoubleQuote.toString ~/ (strChars | escape).rep.! ~ DoubleQuote.toString) + def basicStr[$: P]: P[Value.Str] = + P(DoubleQuote.toString ~/ (escape | strChars).rep.! ~ DoubleQuote.toString) .map(str => Value.Str(Unescape.unescapeJavaString(str))) - val literalStr: Parser[Value.Str] = + def literalStr[$: P]: P[Value.Str] = P( SingleQuote.toString ~/ (!SingleQuote.toString ~ AnyChar).rep.! ~ SingleQuote.toString ).map(Value.Str) - val multiLineBasicStr: Parser[Value.Str] = + def multiLineBasicStr[$: P]: P[Value.Str] = P( MultiLineDoubleQuote ~/ newLine.? ~ (!MultiLineDoubleQuote ~ AnyChar).rep.! ~ MultiLineDoubleQuote ).map(str => Value.Str(Unescape.unescapeJavaString(str))) - val multiLineLiteralStr: Parser[Value.Str] = + def multiLineLiteralStr[$: P]: P[Value.Str] = P( MultiLineSingleQuote ~/ newLine.? ~ @@ -69,7 +71,7 @@ class Rules(extensions: Set[Extension]) extends PlatformRules { MultiLineSingleQuote ).map(Value.Str) - val string: Parser[Value.Str] = P( + def string[$: P]: P[Value.Str] = P( multiLineBasicStr | multiLineLiteralStr | basicStr | @@ -77,13 +79,13 @@ class Rules(extensions: Set[Extension]) extends PlatformRules { def rmUnderscore(s: String) = s.replace("_", "") - val sign = P(CharIn("+-")) - val integral = P(digits.rep(min = 1, sep = "_")) - val fractional = P("." ~ integral) - val exponent = P(CharIn("eE") ~ sign.? ~ integral) - val integer: Parser[Value.Num] = + def sign[$: P] = P(CharIn("+\\-")) + def integral[$: P] = P(digits.rep(min = 1, sep = "_")) + def fractional[$: P] = P("." ~ integral) + def exponent[$: P] = P(CharIn("eE") ~ sign.? ~ integral) + def integer[$: P]: P[Value.Num] = P(sign.? ~ integral).!.map(s => Value.Num(rmUnderscore(s).toLong)) - val double: Parser[Value.Real] = + def double[$: P]: P[Value.Real] = P( sign.?.! ~ ( @@ -99,46 +101,46 @@ class Rules(extensions: Set[Extension]) extends PlatformRules { if (sign == "-") Value.Real(-value) else Value.Real(value) } - val `true` = P("true") .map(_ => Value.Bool(true)) - val `false` = P("false").map(_ => Value.Bool(false)) - val boolean = P(`true` | `false`) + def `true`[$: P] = P("true") .map(_ => Value.Bool(true)) + def `false`[$: P] = P("false").map(_ => Value.Bool(false)) + def boolean[$: P] = P(`true` | `false`) - val bareKey = P((letter | digit | dash).rep(min = 1)).! - val validKey: Parser[String] = + def bareKey[$: P] = P((letter | digit | dash).rep(1)).! + def validKey[$: P]: P[String] = P(NoCut(basicStr.map(_.value)) | NoCut(literalStr.map(_.value)) | bareKey) - val pair: Parser[(String, Value)] = + def pair[$: P]: P[(String, Value)] = P(validKey ~ skipWs ~ "=" ~ skipWs ~ elem) - val array: Parser[Value.Arr] = + def array[$: P]: P[Value.Arr] = P("[" ~ skip ~ elem.rep(sep = skip ~ "," ~ skip) ~ ",".? ~ skip ~ "]") .map(l => Value.Arr(l.toList)) - val inlineTable: Parser[Value.Tbl] = + def inlineTable[$: P]: P[Value.Tbl] = (if (extensions.contains(MultiLineInlineTables)) P("{" ~ skip ~ pair.rep(sep = skip ~ "," ~ skip) ~ ",".? ~ skip ~ "}") else P("{" ~ skipWs ~ pair.rep(sep = skipWs ~ "," ~ skipWs) ~ skipWs ~ "}") ).map(p => Value.Tbl(p.toMap)) - val tableIds: Parser[Seq[String]] = + def tableIds[$: P]: P[Seq[String]] = P(validKey.rep(min = 1, sep = skipWs ~ "." ~ skipWs).map(_.toSeq)) - val tableDef: Parser[Seq[String]] = + def tableDef[$: P]: P[Seq[String]] = P("[" ~ skipWs ~ tableIds ~ skipWs ~ "]") - val tableArrayDef: Parser[Seq[String]] = + def tableArrayDef[$: P]: P[Seq[String]] = P("[[" ~ skipWs ~ tableIds ~ skipWs ~ "]]") - val pairNode: Parser[Node.Pair] = pair.map { case (k, v) => Node.Pair(k, v) } - val table: Parser[Node.NamedTable] = + def pairNode[$: P]: P[Node.Pair] = pair.map { case (k, v) => Node.Pair(k, v) } + def table[$: P]: P[Node.NamedTable] = P(tableDef ~ skip ~ pair.rep(sep = skip)).map { case (a, b) => Node.NamedTable(a.toList, b.toList) } - val tableArray: Parser[Node.NamedArray] = + def tableArray[$: P]: P[Node.NamedArray] = P(tableArrayDef ~ skip ~ pair.rep(sep = skip)).map { case (a, b) => Node.NamedArray(a.toList, b.toList) } - lazy val elem: Parser[Value] = + def elem[$: P]: P[Value] = P(date | string | boolean | double | integer | array | inlineTable) - val node: Parser[Node] = P(pairNode | table | tableArray) - val root: Parser[Root] = P(skip ~ node.rep(sep = skip) ~ skip ~ End) + def node[$: P]: P[Node] = P(pairNode | table | tableArray) + def root[$: P]: P[Root] = P(skip ~ node.rep(sep = skip) ~ skip ~ End) .map(nodes => Root(nodes.toList)) } diff --git a/shared/src/main/scala/toml/Toml.scala b/shared/src/main/scala/toml/Toml.scala index 751ca62..0ec11f3 100644 --- a/shared/src/main/scala/toml/Toml.scala +++ b/shared/src/main/scala/toml/Toml.scala @@ -2,13 +2,13 @@ package toml import shapeless._ -import scala.meta.internal.fastparse.core.Parsed._ +import fastparse.Parsed object Toml { def parse(toml: String, extensions: Set[Extension] = Set()): Either[Parse.Error, Value.Tbl] = - new Rules(extensions).root.parse(toml) match { - case Success(v, _) => Embed.root(v) - case f: Failure[_, _] => Left(List() -> f.msg) + fastparse.parse(toml, new Rules(extensions).root(_)) match { + case Parsed.Success(v, _) => Embed.root(v) + case f: Parsed.Failure => Left(List() -> f.msg) } def generate(root: Root): String = Generate.generate(root) diff --git a/shared/src/test/scala/toml/CodecSpec.scala b/shared/src/test/scala/toml/CodecSpec.scala index 1d2fe94..af0c2ff 100644 --- a/shared/src/test/scala/toml/CodecSpec.scala +++ b/shared/src/test/scala/toml/CodecSpec.scala @@ -1,10 +1,10 @@ package toml -import org.scalatest.FunSuite +import org.scalatest.funsuite.AnyFunSuite import Codecs._ -class CodecSpec extends FunSuite { +class CodecSpec extends AnyFunSuite { test("Booleans") { case class BoolPair(a: Boolean, b: Boolean) @@ -20,7 +20,7 @@ class CodecSpec extends FunSuite { } test("Strings") { - val elem = Rules.elem.parse("\"test\"").get.value + val elem = fastparse.parse("\"test\"", Rules.elem(_)).get.value val result = new Toml.CodecHelperValue[String].apply(elem) assert(result == Right("test")) } @@ -53,7 +53,7 @@ class CodecSpec extends FunSuite { } test("Lists") { - val elem = Rules.elem.parse("""["test", "a"]""").get.value + val elem = fastparse.parse("""["test", "a"]""", Rules.elem(_)).get.value val result = new Toml.CodecHelperValue[List[String]].apply(elem) assert(result == Right(List("test", "a"))) } diff --git a/shared/src/test/scala/toml/EmbedSpec.scala b/shared/src/test/scala/toml/EmbedSpec.scala index 54b93dd..e5db6fa 100644 --- a/shared/src/test/scala/toml/EmbedSpec.scala +++ b/shared/src/test/scala/toml/EmbedSpec.scala @@ -1,13 +1,13 @@ package toml -import org.scalatest.FunSuite +import org.scalatest.funsuite.AnyFunSuite import Value._ -class EmbedSpec extends FunSuite { +class EmbedSpec extends AnyFunSuite { test("One pair") { val pair = """a = 1""" - val node = Rules.root.parse(pair).get.value + val node = fastparse.parse(pair, Rules.root(_)).get.value assert(Embed.root(node) == Right(Tbl(Map("a" -> Num(1))))) } @@ -16,7 +16,7 @@ class EmbedSpec extends FunSuite { """b = 2 |a = 1 """.stripMargin - val node2 = Rules.root.parse(pairs).get.value + val node2 = fastparse.parse(pairs, Rules.root(_)).get.value assert(Embed.root(node2) == Right(Tbl(Map( "a" -> Num(1), "b" -> Num(2))))) } @@ -27,7 +27,7 @@ class EmbedSpec extends FunSuite { |[table] |a = 1 """.stripMargin - val node = Rules.root.parse(table).get.value + val node = fastparse.parse(table, Rules.root(_)).get.value assert(Embed.root(node) == Right(Tbl(Map("table" -> Tbl(Map("a" -> Num(1))))))) } @@ -39,7 +39,7 @@ class EmbedSpec extends FunSuite { |[table] |b = 2 """.stripMargin - val node = Rules.root.parse(table).get.value + val node = fastparse.parse(table, Rules.root(_)).get.value assert(Embed.root(node) == Right(Tbl(Map("a" -> Num(1), "table" -> Tbl(Map("b" -> Num(2))))))) } @@ -50,7 +50,7 @@ class EmbedSpec extends FunSuite { |[table.table2] |value = 42 """.stripMargin - val node = Rules.root.parse(table).get.value + val node = fastparse.parse(table, Rules.root(_)).get.value assert(Embed.root(node) == Right(Tbl(Map("table" -> Tbl(Map("table2" -> @@ -65,7 +65,7 @@ class EmbedSpec extends FunSuite { |[table.table3] |value = 42 """.stripMargin - val node = Rules.root.parse(table).get.value + val node = fastparse.parse(table, Rules.root(_)).get.value assert(Embed.root(node) == Right(Tbl(Map( "table" -> Tbl(Map( @@ -80,7 +80,7 @@ class EmbedSpec extends FunSuite { |[table.table3] |value = 42 """.stripMargin - val node = Rules.root.parse(table).get.value + val node = fastparse.parse(table, Rules.root(_)).get.value assert(Embed.root(node) == Right(Tbl(Map( "table" -> Tbl(Map( @@ -96,7 +96,7 @@ class EmbedSpec extends FunSuite { | { x = 7, y = 8, z = 9 }, | { x = 2, y = 4, z = 8 } ] """.stripMargin - val node = Rules.root.parse(tableList).get.value + val node = fastparse.parse(tableList, Rules.root(_)).get.value assert(Embed.root(node) == Right(Tbl(Map( "points" -> Arr(List( @@ -119,7 +119,7 @@ class EmbedSpec extends FunSuite { |colour = "grey" """.stripMargin - val node = Rules.root.parse(array).get.value + val node = fastparse.parse(array, Rules.root(_)).get.value assert(Embed.root(node) == Right(Tbl(Map( "products" -> Arr(List( Tbl(Map("name" -> Str("Hammer"), "sku" -> Num(738594937), "colour" -> Str("blue"))), @@ -142,7 +142,7 @@ class EmbedSpec extends FunSuite { |colour = "grey" """.stripMargin - val node = Rules.root.parse(array).get.value + val node = fastparse.parse(array, Rules.root(_)).get.value assert(Embed.root(node) == Right(Tbl(Map( "products" -> Arr(List( Tbl(Map("name" -> Str("Hammer"), "sku" -> Num(738594937))), @@ -174,7 +174,7 @@ class EmbedSpec extends FunSuite { | name = "plantain" """.stripMargin - val node = Rules.root.parse(array).get.value + val node = fastparse.parse(array, Rules.root(_)).get.value assert(Embed.root(node) == Right(Tbl(Map( "fruit" -> Arr(List( Tbl(Map( diff --git a/shared/src/test/scala/toml/GeneratedSpec.scala b/shared/src/test/scala/toml/GeneratedSpec.scala index 2d62ccf..dd850d4 100644 --- a/shared/src/test/scala/toml/GeneratedSpec.scala +++ b/shared/src/test/scala/toml/GeneratedSpec.scala @@ -1,19 +1,21 @@ package toml import org.scalatest.prop._ -import org.scalatest.{Matchers, PropSpec} -import scala.meta.internal.fastparse.all._ -import scala.meta.internal.fastparse.core.Parsed.{Failure, Success} +import org.scalatest.propspec.AnyPropSpec +import org.scalatest.matchers.should.Matchers +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks +import fastparse._ +import fastparse.Parsed.{Failure, Success} import scala.util.Try -class GeneratedSpec extends PropSpec with PropertyChecks with Matchers { +class GeneratedSpec extends AnyPropSpec with ScalaCheckPropertyChecks with Matchers { import TestHelpers._ property("Parse arrays") { import Generators.Arrays._ forAll(arrayGen) { s: String => - shouldBeSuccess(Rules.elem.parse(s)) + shouldBeSuccess(parse(s, Rules.elem(_))) } } @@ -21,7 +23,7 @@ class GeneratedSpec extends PropSpec with PropertyChecks with Matchers { import Generators.Numbers._ forAll(validLongGen) { s: String => val expected = Success(Value.Num(Rules.rmUnderscore(s).toLong), s.length) - Rules.elem.parse(s) shouldBe expected + parse(s, Rules.elem(_)) shouldBe expected } } @@ -29,7 +31,7 @@ class GeneratedSpec extends PropSpec with PropertyChecks with Matchers { import Generators.Numbers._ forAll(validDoubleGen) { s: String => val expected = Success(Value.Real(Rules.rmUnderscore(s).toDouble), s.length) - Rules.elem.parse(s) shouldBe expected + parse(s, Rules.elem(_)) shouldBe expected } } @@ -38,56 +40,58 @@ class GeneratedSpec extends PropSpec with PropertyChecks with Matchers { forAll(validBoolGen) { s: String => val expected = Success(toBool(s), s.length) - Rules.elem.parse(s) shouldBe expected + parse(s, Rules.elem(_)) shouldBe expected } } property("Detect if booleans are not lowercase") { import Generators.Booleans._ forAll(invalidBoolGen) { s: String => - shouldBeFailure(Rules.elem.parse(s)) + shouldBeFailure(parse(s, Rules.elem(_))) } } property("Detect if any string is unbalanced (missing quote)") { import Generators.Strings._ forAll(invalidStrGen) { s: String => - shouldBeFailure(Rules.elem.parse(s)) + shouldBeFailure(parse(s, Rules.elem(_))) } } property("Parse pairs (key and value)") { import Generators.Tables._ forAll(pairGen) { s: String => - shouldBeSuccess[(String, Value)](Rules.pair.parse(s)) + shouldBeSuccess[(String, Value)](parse(s, Rules.pair(_))) } } property("Parse pairs (with `root` parser)") { import Generators.Tables._ forAll(pairGen) { s: String => - shouldBeSuccess(Rules.root.parse(s)) + shouldBeSuccess(parse(s, Rules.root(_))) } } property("Parse table definitions") { import Generators.Tables._ forAll(tableDefGen) { s: String => - shouldBeSuccess[Seq[String]](Rules.tableDef.parse(s)) + shouldBeSuccess[Seq[String]](parse(s, Rules.tableDef(_))) } } property("Parse tables") { + import NoWhitespace._ import Generators.Tables._ + def p[$: P] = P(Rules.skip ~ Rules.table) forAll(tableGen) { s: String => - shouldBeSuccess[Node.NamedTable]((Rules.skip ~ Rules.table).parse(s)) + shouldBeSuccess[Node.NamedTable](parse(s, p(_))) } } property("Parse tables (with `root` parser)") { import Generators.Tables._ forAll(tableGen) { s: String => - shouldBeSuccess(Rules.root.parse(s)) + shouldBeSuccess(parse(s, Rules.root(_))) } } } diff --git a/shared/src/test/scala/toml/GenerationSpec.scala b/shared/src/test/scala/toml/GenerationSpec.scala index 492ad63..1bf35d0 100644 --- a/shared/src/test/scala/toml/GenerationSpec.scala +++ b/shared/src/test/scala/toml/GenerationSpec.scala @@ -3,9 +3,9 @@ package toml import Node._ import Value._ -import org.scalatest.FunSuite +import org.scalatest.funsuite.AnyFunSuite -class GenerationSpec extends FunSuite { +class GenerationSpec extends AnyFunSuite { def check(root: Root, expected: String): Unit = { val generated = Toml.generate(root) val parsed = Toml.parse(generated) diff --git a/shared/src/test/scala/toml/ParseSpec.scala b/shared/src/test/scala/toml/ParseSpec.scala index 92fdbc5..33133dc 100644 --- a/shared/src/test/scala/toml/ParseSpec.scala +++ b/shared/src/test/scala/toml/ParseSpec.scala @@ -3,9 +3,9 @@ package toml import Value._ import Node._ -import org.scalatest.FunSuite +import org.scalatest.funsuite.AnyFunSuite -class ParseSpec extends FunSuite { +class ParseSpec extends AnyFunSuite { test("Parse strings") { val toml = """ diff --git a/shared/src/test/scala/toml/RulesSpec.scala b/shared/src/test/scala/toml/RulesSpec.scala index 0a117cb..a3354e3 100644 --- a/shared/src/test/scala/toml/RulesSpec.scala +++ b/shared/src/test/scala/toml/RulesSpec.scala @@ -1,9 +1,10 @@ package toml -import org.scalatest.{FunSuite, Matchers} -import scala.meta.internal.fastparse.core.Parsed._ +import org.scalatest.matchers.should.Matchers +import org.scalatest.funsuite.AnyFunSuite +import fastparse.Parsed._ -class RulesSpec extends FunSuite with Matchers { +class RulesSpec extends AnyFunSuite with Matchers { import TestHelpers._ val smallTest = diff --git a/shared/src/test/scala/toml/TestHelpers.scala b/shared/src/test/scala/toml/TestHelpers.scala index a35ca2e..2c5335a 100644 --- a/shared/src/test/scala/toml/TestHelpers.scala +++ b/shared/src/test/scala/toml/TestHelpers.scala @@ -1,32 +1,32 @@ package toml -import scala.meta.internal.fastparse.all._ -import scala.meta.internal.fastparse.core.Parsed._ +import fastparse._ +import fastparse.Parsed._ -import org.scalatest.Matchers +import org.scalatest.matchers.should.Matchers object TestHelpers { import Matchers._ def testSuccess(example: String, rules: Rules = Rules): Root = - rules.root.parse(example) match { + parse(example, rules.root(_), verboseFailures = true) match { case Success(v, _) => v - case f: Failure[_, _] => fail(s"Failed to parse `$example`: ${f.msg}") + case f: Failure => fail(s"Failed to parse `$example`: ${f.longMsg}") } def testFailure(example: String, rules: Rules = Rules): Unit = - rules.root.parse(example) match { + parse(example,rules.root(_)) match { case Success(_, _) => fail(s"Did not fail: $example") - case _: Failure[_, _] => + case _: Failure => } def shouldBeSuccess[T](r: Parsed[T]): Unit = r match { - case s: Success[T, _, _] => - case f: Failure[_, _] => fail(s"$r is not a Success: $f") + case s: Success[T] => + case f: Failure => fail(s"$r is not a Success: $f") } def shouldBeFailure[T](r: Parsed[T]): Unit = r match { - case s: Success[T, _, _] => fail(s"$r is not a Failure.") - case f: Failure[_, _] => + case s: Success[T] => fail(s"$r is not a Failure.") + case f: Failure => } } diff --git a/version.sbt b/version.sbt index 60b60a5..162fbe5 100644 --- a/version.sbt +++ b/version.sbt @@ -1,4 +1,4 @@ -version in ThisBuild := { +ThisBuild / version := { import sys.process._ val version = Seq("git", "describe", "--tags").!!.trim.tail println("[info] Setting version to: " + version) From e41d7de05f8277c3b17225320434505adfabc30c Mon Sep 17 00:00:00 2001 From: Lorenzo Gabriele Date: Thu, 17 Oct 2024 20:57:27 +0200 Subject: [PATCH 02/14] Basic support for Scala 3 (no derivation) --- build.sbt | 13 +++-- .../toml/PlatformCodecs.scala | 0 .../toml/PlatformCodecs.scala | 0 .../toml/PlatformCodecs.scala | 0 .../main/{scala => scala-2}/toml/Codecs.scala | 0 .../scala-2/toml/TomlVersionSpecific.scala | 52 +++++++++++++++++++ .../toml/util/RecordToMap.scala | 0 .../scala-3/toml/TomlVersionSpecific.scala | 5 ++ shared/src/main/scala-3/toml/shapeless.scala | 11 ++++ shared/src/main/scala/toml/Rules.scala | 4 +- shared/src/main/scala/toml/Toml.scala | 51 +----------------- 11 files changed, 81 insertions(+), 55 deletions(-) rename js/src/main/{scala => scala-2}/toml/PlatformCodecs.scala (100%) rename jvm/src/main/{scala => scala-2}/toml/PlatformCodecs.scala (100%) rename native/src/main/{scala => scala-2}/toml/PlatformCodecs.scala (100%) rename shared/src/main/{scala => scala-2}/toml/Codecs.scala (100%) create mode 100644 shared/src/main/scala-2/toml/TomlVersionSpecific.scala rename shared/src/main/{scala => scala-2}/toml/util/RecordToMap.scala (100%) create mode 100644 shared/src/main/scala-3/toml/TomlVersionSpecific.scala create mode 100644 shared/src/main/scala-3/toml/shapeless.scala diff --git a/build.sbt b/build.sbt index 2f64727..5614544 100644 --- a/build.sbt +++ b/build.sbt @@ -3,8 +3,10 @@ import sbtcrossproject.CrossPlugin.autoImport.{crossProject, CrossType} val Scala2_12 = "2.12.19" val Scala2_13 = "2.13.14" +val Scala3 = "3.3.3" val FastParse = "3.1.1" val Shapeless = "2.3.12" +val Shapeless3 = "3.4.3" val ScalaCheck = "1.18.1" val ScalaTest = "3.2.19" @@ -15,7 +17,7 @@ val SharedSettings = Seq( organization := "tech.sparse", scalaVersion := Scala2_13, - crossScalaVersions := Seq(Scala2_13, Scala2_12), + crossScalaVersions := Seq(Scala3, Scala2_13, Scala2_12), pomExtra := https://github.com/sparsetech/toml-scala @@ -49,9 +51,14 @@ lazy val toml = .settings( libraryDependencies ++= Seq( "com.lihaoyi" %%% "fastparse" % FastParse, - "com.chuusai" %%% "shapeless" % Shapeless, "org.scalacheck" %%% "scalacheck" % ScalaCheck % Test, "org.scalatest" %%% "scalatest" % ScalaTest % Test, "org.scalatestplus" %%% s"scalacheck-${ScalaCheck.split('.').take(2).mkString("-")}" % ScalaTestScalaCheck % Test - ) + ), + libraryDependencies += { + if(scalaVersion.value.startsWith("3.")) + "org.typelevel" %%% "shapeless3-deriving" % Shapeless3 + else + "com.chuusai" %%% "shapeless" % Shapeless + } ) diff --git a/js/src/main/scala/toml/PlatformCodecs.scala b/js/src/main/scala-2/toml/PlatformCodecs.scala similarity index 100% rename from js/src/main/scala/toml/PlatformCodecs.scala rename to js/src/main/scala-2/toml/PlatformCodecs.scala diff --git a/jvm/src/main/scala/toml/PlatformCodecs.scala b/jvm/src/main/scala-2/toml/PlatformCodecs.scala similarity index 100% rename from jvm/src/main/scala/toml/PlatformCodecs.scala rename to jvm/src/main/scala-2/toml/PlatformCodecs.scala diff --git a/native/src/main/scala/toml/PlatformCodecs.scala b/native/src/main/scala-2/toml/PlatformCodecs.scala similarity index 100% rename from native/src/main/scala/toml/PlatformCodecs.scala rename to native/src/main/scala-2/toml/PlatformCodecs.scala diff --git a/shared/src/main/scala/toml/Codecs.scala b/shared/src/main/scala-2/toml/Codecs.scala similarity index 100% rename from shared/src/main/scala/toml/Codecs.scala rename to shared/src/main/scala-2/toml/Codecs.scala diff --git a/shared/src/main/scala-2/toml/TomlVersionSpecific.scala b/shared/src/main/scala-2/toml/TomlVersionSpecific.scala new file mode 100644 index 0000000..f598c3c --- /dev/null +++ b/shared/src/main/scala-2/toml/TomlVersionSpecific.scala @@ -0,0 +1,52 @@ +package toml + +import shapeless._ + +trait TomlVersionSpecific { + + class CodecHelperGeneric[A] { + def apply[D <: HList, R <: HList](table: Value.Tbl)(implicit + generic: LabelledGeneric.Aux[A, R], + defaults: Default.AsRecord.Aux[A, D], + defaultMapper: util.RecordToMap[D], + codec: Codec[R] + ): Either[Parse.Error, A] = { + val d = defaultMapper(defaults()) + codec(table, d, 0).right.map(generic.from) + } + + def apply[D <: HList, R <: HList]( + toml: String, + extensions: Set[Extension] + )(implicit + generic: LabelledGeneric.Aux[A, R], + defaults: Default.AsRecord.Aux[A, D], + defaultMapper: util.RecordToMap[D], + codec: Codec[R] + ): Either[Parse.Error, A] = { + val d = defaultMapper(defaults()) + parse(toml, extensions).right + .flatMap(codec(_, d, 0).right.map(generic.from)) + } + + def apply[D <: HList, R <: HList](toml: String)(implicit + generic: LabelledGeneric.Aux[A, R], + defaults: Default.AsRecord.Aux[A, D], + defaultMapper: util.RecordToMap[D], + codec: Codec[R] + ): Either[Parse.Error, A] = apply(toml, Set()) + } + + class CodecHelperValue[A] { + def apply(value: Value)(implicit codec: Codec[A]): Either[Parse.Error, A] = + codec(value, Map(), 0) + + def apply(toml: String, extensions: Set[Extension] = Set())(implicit + codec: Codec[A] + ): Either[Parse.Error, A] = + parse(toml, extensions).right.flatMap(codec(_, Map(), 0)) + } + + def parseAs[T]: CodecHelperGeneric[T] = new CodecHelperGeneric[T] + def parseAsValue[T]: CodecHelperValue[T] = new CodecHelperValue[T] +} diff --git a/shared/src/main/scala/toml/util/RecordToMap.scala b/shared/src/main/scala-2/toml/util/RecordToMap.scala similarity index 100% rename from shared/src/main/scala/toml/util/RecordToMap.scala rename to shared/src/main/scala-2/toml/util/RecordToMap.scala diff --git a/shared/src/main/scala-3/toml/TomlVersionSpecific.scala b/shared/src/main/scala-3/toml/TomlVersionSpecific.scala new file mode 100644 index 0000000..c74b081 --- /dev/null +++ b/shared/src/main/scala-3/toml/TomlVersionSpecific.scala @@ -0,0 +1,5 @@ +package toml + +trait TomlVersionSpecific { + +} diff --git a/shared/src/main/scala-3/toml/shapeless.scala b/shared/src/main/scala-3/toml/shapeless.scala new file mode 100644 index 0000000..bc9a518 --- /dev/null +++ b/shared/src/main/scala-3/toml/shapeless.scala @@ -0,0 +1,11 @@ +package toml + +type HList = Tuple + +type Lazy[T] = T + +// object Witness { +// type Aux[T] +// } + +// given Aux[T] = null diff --git a/shared/src/main/scala/toml/Rules.scala b/shared/src/main/scala/toml/Rules.scala index 3751efa..ca076c1 100644 --- a/shared/src/main/scala/toml/Rules.scala +++ b/shared/src/main/scala/toml/Rules.scala @@ -55,7 +55,7 @@ class Rules(extensions: Set[Extension]) extends PlatformRules { SingleQuote.toString ~/ (!SingleQuote.toString ~ AnyChar).rep.! ~ SingleQuote.toString - ).map(Value.Str) + ).map(Value.Str.apply) def multiLineBasicStr[$: P]: P[Value.Str] = P( MultiLineDoubleQuote ~/ @@ -69,7 +69,7 @@ class Rules(extensions: Set[Extension]) extends PlatformRules { newLine.? ~ (!MultiLineSingleQuote ~ AnyChar).rep.! ~ MultiLineSingleQuote - ).map(Value.Str) + ).map(Value.Str.apply) def string[$: P]: P[Value.Str] = P( multiLineBasicStr | diff --git a/shared/src/main/scala/toml/Toml.scala b/shared/src/main/scala/toml/Toml.scala index 0ec11f3..ba7f7f0 100644 --- a/shared/src/main/scala/toml/Toml.scala +++ b/shared/src/main/scala/toml/Toml.scala @@ -1,10 +1,8 @@ package toml -import shapeless._ - import fastparse.Parsed -object Toml { +object Toml extends TomlVersionSpecific { def parse(toml: String, extensions: Set[Extension] = Set()): Either[Parse.Error, Value.Tbl] = fastparse.parse(toml, new Rules(extensions).root(_)) match { case Parsed.Success(v, _) => Embed.root(v) @@ -12,51 +10,4 @@ object Toml { } def generate(root: Root): String = Generate.generate(root) - - class CodecHelperGeneric[A] { - def apply[D <: HList, R <: HList](table: Value.Tbl)(implicit - generic : LabelledGeneric.Aux[A, R], - defaults : Default.AsRecord.Aux[A, D], - defaultMapper: util.RecordToMap[D], - codec : Codec[R] - ): Either[Parse.Error, A] = { - val d = defaultMapper(defaults()) - codec(table, d, 0).right.map(generic.from) - } - - def apply[D <: HList, R <: HList]( - toml : String, - extensions: Set[Extension] - )(implicit - generic : LabelledGeneric.Aux[A, R], - defaults : Default.AsRecord.Aux[A, D], - defaultMapper: util.RecordToMap[D], - codec : Codec[R] - ): Either[Parse.Error, A] = { - val d = defaultMapper(defaults()) - parse(toml, extensions) - .right - .flatMap(codec(_, d, 0).right.map(generic.from)) - } - - def apply[D <: HList, R <: HList](toml: String)( - implicit - generic : LabelledGeneric.Aux[A, R], - defaults : Default.AsRecord.Aux[A, D], - defaultMapper: util.RecordToMap[D], - codec : Codec[R] - ): Either[Parse.Error, A] = apply(toml, Set()) - } - - class CodecHelperValue[A] { - def apply(value: Value)(implicit codec: Codec[A]): Either[Parse.Error, A] = - codec(value, Map(), 0) - - def apply(toml: String, extensions: Set[Extension] = Set()) - (implicit codec: Codec[A]): Either[Parse.Error, A] = - parse(toml, extensions).right.flatMap(codec(_, Map(), 0)) - } - - def parseAs [T]: CodecHelperGeneric[T] = new CodecHelperGeneric[T] - def parseAsValue[T]: CodecHelperValue [T] = new CodecHelperValue[T] } From f9eb126757f3378fc7b8c33bb12a0bd9fc2e503c Mon Sep 17 00:00:00 2001 From: Lorenzo Gabriele Date: Thu, 17 Oct 2024 21:09:49 +0200 Subject: [PATCH 03/14] Remove shapeless3 dependency --- build.sbt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/build.sbt b/build.sbt index 5614544..d813926 100644 --- a/build.sbt +++ b/build.sbt @@ -6,7 +6,6 @@ val Scala2_13 = "2.13.14" val Scala3 = "3.3.3" val FastParse = "3.1.1" val Shapeless = "2.3.12" -val Shapeless3 = "3.4.3" val ScalaCheck = "1.18.1" val ScalaTest = "3.2.19" @@ -55,10 +54,10 @@ lazy val toml = "org.scalatest" %%% "scalatest" % ScalaTest % Test, "org.scalatestplus" %%% s"scalacheck-${ScalaCheck.split('.').take(2).mkString("-")}" % ScalaTestScalaCheck % Test ), - libraryDependencies += { + libraryDependencies ++= { if(scalaVersion.value.startsWith("3.")) - "org.typelevel" %%% "shapeless3-deriving" % Shapeless3 + Seq.empty else - "com.chuusai" %%% "shapeless" % Shapeless + Seq("com.chuusai" %%% "shapeless" % Shapeless) } ) From 57814b852fbe53be75038e76fb374d4b05e14798 Mon Sep 17 00:00:00 2001 From: Lorenzo Gabriele Date: Thu, 17 Oct 2024 21:12:58 +0200 Subject: [PATCH 04/14] cleanup --- .../src/main/scala-3/toml/TomlVersionSpecific.scala | 4 +--- shared/src/main/scala-3/toml/shapeless.scala | 11 ----------- 2 files changed, 1 insertion(+), 14 deletions(-) delete mode 100644 shared/src/main/scala-3/toml/shapeless.scala diff --git a/shared/src/main/scala-3/toml/TomlVersionSpecific.scala b/shared/src/main/scala-3/toml/TomlVersionSpecific.scala index c74b081..abb796f 100644 --- a/shared/src/main/scala-3/toml/TomlVersionSpecific.scala +++ b/shared/src/main/scala-3/toml/TomlVersionSpecific.scala @@ -1,5 +1,3 @@ package toml -trait TomlVersionSpecific { - -} +trait TomlVersionSpecific diff --git a/shared/src/main/scala-3/toml/shapeless.scala b/shared/src/main/scala-3/toml/shapeless.scala deleted file mode 100644 index bc9a518..0000000 --- a/shared/src/main/scala-3/toml/shapeless.scala +++ /dev/null @@ -1,11 +0,0 @@ -package toml - -type HList = Tuple - -type Lazy[T] = T - -// object Witness { -// type Aux[T] -// } - -// given Aux[T] = null From 0b6f954a9be8f6af3d99a56a520d3c293176d7de Mon Sep 17 00:00:00 2001 From: Lorenzo Gabriele Date: Thu, 17 Oct 2024 21:27:06 +0200 Subject: [PATCH 05/14] Add Github Action to run tests --- .github/workflows/ci.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..7f9f5e9 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,21 @@ +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + ci: + runs-on: ubuntu-22.04 + + steps: + - uses: actions/checkout@v3 + - uses: coursier/cache-action@v6 + - uses: coursier/setup-action@v1 + with: + jvm: ${{ matrix.jvm }} + apps: sbt + - name: Run tests + run: sbt +test From 10570dd854f55d2f5ca6e41098b7ca78d9797df8 Mon Sep 17 00:00:00 2001 From: Lorenzo Gabriele Date: Fri, 18 Oct 2024 11:32:43 +0200 Subject: [PATCH 06/14] Fetch all commits --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7f9f5e9..4f53e56 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,6 +12,8 @@ jobs: steps: - uses: actions/checkout@v3 + with: + fetch-depth: 0 - uses: coursier/cache-action@v6 - uses: coursier/setup-action@v1 with: From 241d2980f8e54480cf97e74134694bcfa45ce6e6 Mon Sep 17 00:00:00 2001 From: Lorenzo Gabriele Date: Fri, 18 Oct 2024 11:35:48 +0200 Subject: [PATCH 07/14] Add jvms to matrix --- .github/workflows/ci.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4f53e56..8c2a3f9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,14 +6,18 @@ on: branches: - master + jobs: ci: + strategy: + matrix: + jvm: + - temurin:1.8.0-422 + - temurin:1.21 runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 - with: - fetch-depth: 0 - uses: coursier/cache-action@v6 - uses: coursier/setup-action@v1 with: From 2df4e3e48fbb0a54b65566851b6342055f77d88f Mon Sep 17 00:00:00 2001 From: Lorenzo Gabriele Date: Fri, 18 Oct 2024 12:54:55 +0200 Subject: [PATCH 08/14] fetch depth 0 --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8c2a3f9..6471eae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,6 +18,8 @@ jobs: steps: - uses: actions/checkout@v3 + with: + fetch-depth: 0 - uses: coursier/cache-action@v6 - uses: coursier/setup-action@v1 with: From 3c10021113d5883d592f296dafdd40de31b2333a Mon Sep 17 00:00:00 2001 From: Lorenzo Gabriele Date: Fri, 18 Oct 2024 15:06:26 +0200 Subject: [PATCH 09/14] Not fail when can't set version --- version.sbt | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/version.sbt b/version.sbt index 162fbe5..4d8ac69 100644 --- a/version.sbt +++ b/version.sbt @@ -1,6 +1,11 @@ +import scala.sys.process._ +import scala.util.Try + ThisBuild / version := { - import sys.process._ - val version = Seq("git", "describe", "--tags").!!.trim.tail - println("[info] Setting version to: " + version) - version + val current = (ThisBuild / version).value + val v = + Try(Seq("git", "describe", "--tags").!!.trim.tail) + .getOrElse(current) + println(s"[info] Setting version to: $v") + v } From 30d96ade2d7967219e5f0263fa4ad8a0f09b0613 Mon Sep 17 00:00:00 2001 From: Lorenzo Gabriele Date: Fri, 18 Oct 2024 15:09:51 +0200 Subject: [PATCH 10/14] Fix compilation for Scala 2 --- shared/src/main/scala-2/toml/TomlVersionSpecific.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shared/src/main/scala-2/toml/TomlVersionSpecific.scala b/shared/src/main/scala-2/toml/TomlVersionSpecific.scala index f598c3c..025166c 100644 --- a/shared/src/main/scala-2/toml/TomlVersionSpecific.scala +++ b/shared/src/main/scala-2/toml/TomlVersionSpecific.scala @@ -4,6 +4,8 @@ import shapeless._ trait TomlVersionSpecific { + def parse(toml: String, extensions: Set[Extension] = Set()): Either[Parse.Error, Value.Tbl] + class CodecHelperGeneric[A] { def apply[D <: HList, R <: HList](table: Value.Tbl)(implicit generic: LabelledGeneric.Aux[A, R], From 7a69b085aca6c5cdfe04ee7850eb275d9cebee60 Mon Sep 17 00:00:00 2001 From: Lorenzo Gabriele Date: Fri, 18 Oct 2024 16:05:21 +0200 Subject: [PATCH 11/14] Fix `\\` escape --- shared/src/main/scala/toml/Rules.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/src/main/scala/toml/Rules.scala b/shared/src/main/scala/toml/Rules.scala index ca076c1..4cdec32 100644 --- a/shared/src/main/scala/toml/Rules.scala +++ b/shared/src/main/scala/toml/Rules.scala @@ -44,7 +44,7 @@ class Rules(extensions: Set[Extension]) extends PlatformRules { def unicodeEsc[$: P] = P("u" ~ hexDigit.rep(4)) def unicodeEscLong[$: P] = P("U" ~ hexDigit.rep(8)) def escape[$: P] = P("\\" ~ ( - CharIn("\"/\\bfnrt") | unicodeEsc | unicodeEscLong + CharIn("\"/\\\\bfnrt") | unicodeEsc | unicodeEscLong )) def basicStr[$: P]: P[Value.Str] = From 13588a7de8f227b4fd17b7eed35ceef1f26a9595 Mon Sep 17 00:00:00 2001 From: Lorenzo Gabriele Date: Fri, 18 Oct 2024 16:12:43 +0200 Subject: [PATCH 12/14] Fix date P for Scala Native and JS to not consume chars --- .../src/main/scala-2/toml/PlatformCodecs.scala | 0 js-native/src/main/scala/toml/PlatformRules.scala | 7 +++++++ {js => js-native}/src/main/scala/toml/PlatformValue.scala | 0 js/src/main/scala/toml/PlatformRules.scala | 7 ------- native/src/main/scala-2/toml/PlatformCodecs.scala | 3 --- native/src/main/scala/toml/PlatformRules.scala | 7 ------- native/src/main/scala/toml/PlatformValue.scala | 3 --- 7 files changed, 7 insertions(+), 20 deletions(-) rename {js => js-native}/src/main/scala-2/toml/PlatformCodecs.scala (100%) create mode 100644 js-native/src/main/scala/toml/PlatformRules.scala rename {js => js-native}/src/main/scala/toml/PlatformValue.scala (100%) delete mode 100644 js/src/main/scala/toml/PlatformRules.scala delete mode 100644 native/src/main/scala-2/toml/PlatformCodecs.scala delete mode 100644 native/src/main/scala/toml/PlatformRules.scala delete mode 100644 native/src/main/scala/toml/PlatformValue.scala diff --git a/js/src/main/scala-2/toml/PlatformCodecs.scala b/js-native/src/main/scala-2/toml/PlatformCodecs.scala similarity index 100% rename from js/src/main/scala-2/toml/PlatformCodecs.scala rename to js-native/src/main/scala-2/toml/PlatformCodecs.scala diff --git a/js-native/src/main/scala/toml/PlatformRules.scala b/js-native/src/main/scala/toml/PlatformRules.scala new file mode 100644 index 0000000..3e54e75 --- /dev/null +++ b/js-native/src/main/scala/toml/PlatformRules.scala @@ -0,0 +1,7 @@ +package toml + +import fastparse._ + +trait PlatformRules { + def date[$: P] = P(CharIn()).map(_ => null.asInstanceOf[Value]) +} diff --git a/js/src/main/scala/toml/PlatformValue.scala b/js-native/src/main/scala/toml/PlatformValue.scala similarity index 100% rename from js/src/main/scala/toml/PlatformValue.scala rename to js-native/src/main/scala/toml/PlatformValue.scala diff --git a/js/src/main/scala/toml/PlatformRules.scala b/js/src/main/scala/toml/PlatformRules.scala deleted file mode 100644 index 4969a41..0000000 --- a/js/src/main/scala/toml/PlatformRules.scala +++ /dev/null @@ -1,7 +0,0 @@ -package toml - -import fastparse._ - -trait PlatformRules { - def date[$: P] = StringIn().map(_ => null.asInstanceOf[Value]) -} diff --git a/native/src/main/scala-2/toml/PlatformCodecs.scala b/native/src/main/scala-2/toml/PlatformCodecs.scala deleted file mode 100644 index a07db60..0000000 --- a/native/src/main/scala-2/toml/PlatformCodecs.scala +++ /dev/null @@ -1,3 +0,0 @@ -package toml - -trait PlatformCodecs diff --git a/native/src/main/scala/toml/PlatformRules.scala b/native/src/main/scala/toml/PlatformRules.scala deleted file mode 100644 index 4969a41..0000000 --- a/native/src/main/scala/toml/PlatformRules.scala +++ /dev/null @@ -1,7 +0,0 @@ -package toml - -import fastparse._ - -trait PlatformRules { - def date[$: P] = StringIn().map(_ => null.asInstanceOf[Value]) -} diff --git a/native/src/main/scala/toml/PlatformValue.scala b/native/src/main/scala/toml/PlatformValue.scala deleted file mode 100644 index 87be6e9..0000000 --- a/native/src/main/scala/toml/PlatformValue.scala +++ /dev/null @@ -1,3 +0,0 @@ -package toml - -trait PlatformValue From e058ecc8b98a0247732ac5049a74aa8b7a8baafe Mon Sep 17 00:00:00 2001 From: Lorenzo Gabriele Date: Sat, 19 Oct 2024 09:54:26 +0200 Subject: [PATCH 13/14] Move Scala 2 only code to specific directory --- jvm/src/test/scala-2/toml/DateSpec.scala | 20 +++++ jvm/src/test/scala/toml/DateSpec.scala | 78 ------------------- .../{scala => scala-2}/toml/CodecSpec.scala | 0 .../src/test/scala/toml/GeneratedSpec.scala | 22 +++--- .../src/test/scala/toml/GenerationSpec.scala | 2 +- shared/src/test/scala/toml/Generators.scala | 4 +- 6 files changed, 34 insertions(+), 92 deletions(-) create mode 100644 jvm/src/test/scala-2/toml/DateSpec.scala delete mode 100644 jvm/src/test/scala/toml/DateSpec.scala rename shared/src/test/{scala => scala-2}/toml/CodecSpec.scala (100%) diff --git a/jvm/src/test/scala-2/toml/DateSpec.scala b/jvm/src/test/scala-2/toml/DateSpec.scala new file mode 100644 index 0000000..962461f --- /dev/null +++ b/jvm/src/test/scala-2/toml/DateSpec.scala @@ -0,0 +1,20 @@ +package toml + +import java.time._ + +import org.scalatest.funsuite.AnyFunSuite +import org.scalatest.matchers.should.Matchers +import fastparse.Parsed._ + +class DateSpec extends AnyFunSuite with Matchers { + import TestHelpers._ + + test("Codec derivation") { + import Codecs._ + + case class Root(ld: LocalDate) + + val toml = "ld = 1979-05-27" + assert(Toml.parseAs[Root](toml) == Right(Root(LocalDate.of(1979, 5, 27)))) + } +} diff --git a/jvm/src/test/scala/toml/DateSpec.scala b/jvm/src/test/scala/toml/DateSpec.scala deleted file mode 100644 index 35246fe..0000000 --- a/jvm/src/test/scala/toml/DateSpec.scala +++ /dev/null @@ -1,78 +0,0 @@ -package toml - -import java.time._ - -import org.scalatest.funsuite.AnyFunSuite -import org.scalatest.matchers.should.Matchers -import fastparse.Parsed._ - -class DateSpec extends AnyFunSuite with Matchers { - import TestHelpers._ - - test("Parse local date") { - val toml = "ld = 1979-05-27" - val nodes = testSuccess(toml).nodes - assert(nodes(0) == Node.Pair("ld", Value.Date(LocalDate.of(1979, 5, 27)))) - } - - test("Parse local time") { - val toml = - """ - |lt1 = 07:32:00 - |lt2 = 00:32:00.999999 - |lt3 = 00:32:00.555 - """.stripMargin - - val nodes = testSuccess(toml).nodes - assert(nodes(0) == Node.Pair("lt1", Value.Time(LocalTime.of(7, 32, 0, 0)))) - assert(nodes(1) == Node.Pair("lt2", Value.Time(LocalTime.of(0, 32, 0, 999999000)))) - assert(nodes(2) == Node.Pair("lt3", Value.Time(LocalTime.of(0, 32, 0, 555000000)))) - } - - test("Parse local date time") { - val toml = - """ - |ldt1 = 1979-05-27T07:32:00 - |ldt2 = 1979-05-27T00:32:00.999999 - """.stripMargin - val nodes = testSuccess(toml).nodes - assert(nodes(0) == Node.Pair("ldt1", Value.DateTime(LocalDateTime.of( - LocalDate.of(1979, 5, 27), LocalTime.of(7, 32, 0, 0))))) - assert(nodes(1) == Node.Pair("ldt2", Value.DateTime(LocalDateTime.of( - LocalDate.of(1979, 5, 27), LocalTime.of(0, 32, 0, 999999000))))) - } - - test("Parse offset date time") { - val toml = - """ - |odt1 = 1979-05-27T07:32:00Z - |odt2 = 1979-05-27T00:32:00-07:00 - |odt3 = 1979-05-27T00:32:00.999999-07:00 - """.stripMargin - val nodes = testSuccess(toml).nodes - assert(nodes(0) == Node.Pair("odt1", Value.OffsetDateTime( - OffsetDateTime.of( - LocalDateTime.of( - LocalDate.of(1979, 5, 27), LocalTime.of(7, 32, 0) - ), ZoneOffset.of("Z"))))) - assert(nodes(1) == Node.Pair("odt2", Value.OffsetDateTime( - OffsetDateTime.of( - LocalDateTime.of( - LocalDate.of(1979, 5, 27), LocalTime.of(0, 32, 0) - ), ZoneOffset.of("-07:00"))))) - assert(nodes(2) == Node.Pair("odt3", Value.OffsetDateTime( - OffsetDateTime.of( - LocalDateTime.of( - LocalDate.of(1979, 5, 27), LocalTime.of(0, 32, 0, 999999000) - ), ZoneOffset.of("-07:00"))))) - } - - test("Codec derivation") { - import Codecs._ - - case class Root(ld: LocalDate) - - val toml = "ld = 1979-05-27" - assert(Toml.parseAs[Root](toml) == Right(Root(LocalDate.of(1979, 5, 27)))) - } -} diff --git a/shared/src/test/scala/toml/CodecSpec.scala b/shared/src/test/scala-2/toml/CodecSpec.scala similarity index 100% rename from shared/src/test/scala/toml/CodecSpec.scala rename to shared/src/test/scala-2/toml/CodecSpec.scala diff --git a/shared/src/test/scala/toml/GeneratedSpec.scala b/shared/src/test/scala/toml/GeneratedSpec.scala index dd850d4..1bcc3a3 100644 --- a/shared/src/test/scala/toml/GeneratedSpec.scala +++ b/shared/src/test/scala/toml/GeneratedSpec.scala @@ -14,14 +14,14 @@ class GeneratedSpec extends AnyPropSpec with ScalaCheckPropertyChecks with Match property("Parse arrays") { import Generators.Arrays._ - forAll(arrayGen) { s: String => + forAll(arrayGen) { (s: String) => shouldBeSuccess(parse(s, Rules.elem(_))) } } property("Parse integers") { import Generators.Numbers._ - forAll(validLongGen) { s: String => + forAll(validLongGen) { (s: String) => val expected = Success(Value.Num(Rules.rmUnderscore(s).toLong), s.length) parse(s, Rules.elem(_)) shouldBe expected } @@ -29,7 +29,7 @@ class GeneratedSpec extends AnyPropSpec with ScalaCheckPropertyChecks with Match ignore("Parse doubles") { import Generators.Numbers._ - forAll(validDoubleGen) { s: String => + forAll(validDoubleGen) { (s: String) => val expected = Success(Value.Real(Rules.rmUnderscore(s).toDouble), s.length) parse(s, Rules.elem(_)) shouldBe expected } @@ -38,7 +38,7 @@ class GeneratedSpec extends AnyPropSpec with ScalaCheckPropertyChecks with Match property("Parse booleans") { import Generators.Booleans._ - forAll(validBoolGen) { s: String => + forAll(validBoolGen) { (s: String) => val expected = Success(toBool(s), s.length) parse(s, Rules.elem(_)) shouldBe expected } @@ -46,35 +46,35 @@ class GeneratedSpec extends AnyPropSpec with ScalaCheckPropertyChecks with Match property("Detect if booleans are not lowercase") { import Generators.Booleans._ - forAll(invalidBoolGen) { s: String => + forAll(invalidBoolGen) { (s: String) => shouldBeFailure(parse(s, Rules.elem(_))) } } property("Detect if any string is unbalanced (missing quote)") { import Generators.Strings._ - forAll(invalidStrGen) { s: String => + forAll(invalidStrGen) { (s: String) => shouldBeFailure(parse(s, Rules.elem(_))) } } property("Parse pairs (key and value)") { import Generators.Tables._ - forAll(pairGen) { s: String => + forAll(pairGen) { (s: String) => shouldBeSuccess[(String, Value)](parse(s, Rules.pair(_))) } } property("Parse pairs (with `root` parser)") { import Generators.Tables._ - forAll(pairGen) { s: String => + forAll(pairGen) { (s: String) => shouldBeSuccess(parse(s, Rules.root(_))) } } property("Parse table definitions") { import Generators.Tables._ - forAll(tableDefGen) { s: String => + forAll(tableDefGen) { (s: String) => shouldBeSuccess[Seq[String]](parse(s, Rules.tableDef(_))) } } @@ -83,14 +83,14 @@ class GeneratedSpec extends AnyPropSpec with ScalaCheckPropertyChecks with Match import NoWhitespace._ import Generators.Tables._ def p[$: P] = P(Rules.skip ~ Rules.table) - forAll(tableGen) { s: String => + forAll(tableGen) { (s: String) => shouldBeSuccess[Node.NamedTable](parse(s, p(_))) } } property("Parse tables (with `root` parser)") { import Generators.Tables._ - forAll(tableGen) { s: String => + forAll(tableGen) { (s: String) => shouldBeSuccess(parse(s, Rules.root(_))) } } diff --git a/shared/src/test/scala/toml/GenerationSpec.scala b/shared/src/test/scala/toml/GenerationSpec.scala index 1bf35d0..39c8eaa 100644 --- a/shared/src/test/scala/toml/GenerationSpec.scala +++ b/shared/src/test/scala/toml/GenerationSpec.scala @@ -140,7 +140,7 @@ class GenerationSpec extends AnyFunSuite { "-Yno-adapted-args", "-Ywarn-numeric-widen", "-feature" - ).map(Str))))) + ).map(Str.apply))))) val table = """ diff --git a/shared/src/test/scala/toml/Generators.scala b/shared/src/test/scala/toml/Generators.scala index 0996dcf..3beb434 100644 --- a/shared/src/test/scala/toml/Generators.scala +++ b/shared/src/test/scala/toml/Generators.scala @@ -10,8 +10,8 @@ object Generators { def enquoteStr(s: String, q: Char): String = q + s + q def quotedStrGen(quote: Char): Gen[String] = for { - s <- Gen.alphaStr if s.nonEmpty - } yield enquoteStr(s.filter(_ != quote), quote) + s <- Gen.alphaStr if s.nonEmpty && !s.contains(quote) + } yield enquoteStr(s, quote) def doubleQuoteStrGen: Gen[String] = quotedStrGen(DoubleQuote) def singleQuoteStrGen: Gen[String] = quotedStrGen(SingleQuote) From 72ea9d44b1ae34c1bf81d0d66bfeaafb5fd7c9eb Mon Sep 17 00:00:00 2001 From: Lorenzo Gabriele Date: Sat, 19 Oct 2024 09:55:03 +0200 Subject: [PATCH 14/14] Fix bug escaping unicode strings --- shared/src/main/scala/toml/Unescape.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/src/main/scala/toml/Unescape.scala b/shared/src/main/scala/toml/Unescape.scala index 5da17de..804937b 100644 --- a/shared/src/main/scala/toml/Unescape.scala +++ b/shared/src/main/scala/toml/Unescape.scala @@ -31,11 +31,11 @@ object Unescape { case Some('r') => "\r" case Some('\"') => "\"" case Some('\\') => "\\" - case Some('u') if i + 4 >= str.length => // U+XXXX + case Some('u') if i + 4 <= str.length => // U+XXXX val dec = str.slice(i, i + 4) i += 4 decodeUnicodeShort(dec) - case Some('U') if i + 8 >= str.length => // U+XXXXXXXX + case Some('U') if i + 8 <= str.length => // U+XXXXXXXX val dec = str.slice(i, i + 8) i += 8 decodeUnicodeShort(dec)