From 4716b1669fd4b96c5769796cbad6df346b9e9906 Mon Sep 17 00:00:00 2001 From: Michael Bryzek Date: Tue, 9 Jul 2024 16:21:17 -0400 Subject: [PATCH] Iterate on anorm parsers for scala3 (#703) --- generator/app/controllers/Generators.scala | 13 +++- .../models/generator/anorm/Conversions.scala | 31 +++++++-- .../generator/anorm/ParserGenerator.scala | 64 ++++++++++++------- 3 files changed, 78 insertions(+), 30 deletions(-) diff --git a/generator/app/controllers/Generators.scala b/generator/app/controllers/Generators.scala index 23db4c16..440037ed 100644 --- a/generator/app/controllers/Generators.scala +++ b/generator/app/controllers/Generators.scala @@ -75,7 +75,18 @@ object Generators { attributes = Seq("scala_generator") ), status = lib.generator.Status.Alpha, - codeGenerator = Some(scala.generator.anorm.ParserGenerator28) + codeGenerator = Some(scala.generator.anorm.ParserGenerator28Scala2) + ), + CodeGenTarget( + metaData = Generator( + key = "anorm_2_9_scala_3_parsers", + name = "Anorm 2.9 Scala 3 parsers", + description = Some("Generates anorm parsers. See https://playframework.github.io/anorm/"), + language = Some("Scala"), + attributes = Seq("scala_generator") + ), + status = lib.generator.Status.Alpha, + codeGenerator = Some(scala.generator.anorm.ParserGenerator29Scala3) ), CodeGenTarget( metaData = Generator( diff --git a/scala-generator/src/main/scala/models/generator/anorm/Conversions.scala b/scala-generator/src/main/scala/models/generator/anorm/Conversions.scala index 7e69ca90..33df7f5c 100644 --- a/scala-generator/src/main/scala/models/generator/anorm/Conversions.scala +++ b/scala-generator/src/main/scala/models/generator/anorm/Conversions.scala @@ -4,8 +4,9 @@ import scala.annotation.nowarn import lib.Text._ import scala.generator.{ScalaPrimitive, ScalaService} +import scala.models.ScalaVersion -object Conversions { +case class Conversions(scalaVersion: ScalaVersion) { private val JavaPrimitiveTypes = Seq( ScalaPrimitive.Boolean, @@ -86,17 +87,25 @@ package %s { private def standard( types: Seq[ScalaPrimitive], ): String = { + val jsObject = if (scalaVersion.major >= 3) { + Nil + } else { + Seq(s"implicit val columnToJsObject: Column[play.api.libs.json.JsObject] = Util.parser { _.as[play.api.libs.json.JsObject] }") + } ( Seq( - Seq( - s"implicit val columnToJsObject: Column[play.api.libs.json.JsObject] = Util.parser { _.as[play.api.libs.json.JsObject] }", + jsObject ++ Seq( s"implicit val columnToJsValue: Column[play.api.libs.json.JsValue] = Util.parser { _.as[play.api.libs.json.JsValue] }" ) ) ++ types.map { t => - Seq( - s"implicit val columnToSeq${t.shortName}: Column[Seq[${t.fullName}]] = Util.parser { _.as[Seq[${t.fullName}]] }", - s"implicit val columnToMap${t.shortName}: Column[Map[String, ${t.fullName}]] = Util.parser { _.as[Map[String, ${t.fullName}]] }" - ) + if (scalaVersion.major >= 3) { + Nil + } else { + Seq( + s"implicit val columnToSeq${t.shortName}: Column[Seq[${t.fullName}]] = Util.parser { _.as[Seq[${t.fullName}]] }", + s"implicit val columnToMap${t.shortName}: Column[Map[String, ${t.fullName}]] = Util.parser { _.as[Map[String, ${t.fullName}]] }" + ) + } } ).flatten.mkString("\n") } @@ -104,6 +113,14 @@ package %s { private case class Name(shortName: String, qualifiedName: String) private def buildCollectionConversions(ssd: ScalaService): Option[String] = { + if (scalaVersion.major >= 3) { + None + } else { + buildCollectionConversionsScala2(ssd) + } + } + + private def buildCollectionConversionsScala2(ssd: ScalaService): Option[String] = { ( ssd.enums.map(e => Name(e.name, e.qualifiedName)) ++ ssd.models.map(m => Name(m.name, m.qualifiedName)) ++ diff --git a/scala-generator/src/main/scala/models/generator/anorm/ParserGenerator.scala b/scala-generator/src/main/scala/models/generator/anorm/ParserGenerator.scala index 5085def6..f29a455b 100644 --- a/scala-generator/src/main/scala/models/generator/anorm/ParserGenerator.scala +++ b/scala-generator/src/main/scala/models/generator/anorm/ParserGenerator.scala @@ -1,7 +1,7 @@ package scala.generator.anorm import scala.generator._ -import scala.models.{ApiBuilderComments, Attributes, DateTimeTypeConfig, DateTypeConfig} +import scala.models.{ApiBuilderComments, Attributes, DateTimeTypeConfig, DateTypeConfig, ScalaVersion} import io.apibuilder.generator.v0.models.{File, InvocationForm} import generator.ServiceFileNames import lib.generator.CodeGenerator @@ -11,6 +11,7 @@ import lib.Text._ import scala.annotation.tailrec object ParserGenerator24 extends ParserGenerator { + override val version: ScalaVersion = ScalaVersion(2) override def attributes(ssd: ScalaService): ParserGeneratorPlayVersionSpecificAttributes = ParserGeneratorPlayVersionSpecificAttributes( imports = Seq( "anorm.{Column, MetaDataItem, TypeDoesNotMatch}", @@ -20,39 +21,58 @@ object ParserGenerator24 extends ParserGenerator { ) } -object ParserGenerator26 extends ParserGenerator { +object ParserGenerator26 extends AbstractParserGenerator26(ScalaVersion(2)) + +class AbstractParserGenerator26(override val version: ScalaVersion) extends ParserGenerator { override def attributes(ssd: ScalaService): ParserGeneratorPlayVersionSpecificAttributes = { - val dateImports = ssd.attributes.dateType match { - case DateTypeConfig.JodaLocalDate => Seq( - "play.api.libs.json.JodaReads._", - ) - case _ => Nil - } - val dateTimeImports = ssd.attributes.dateTimeType match { - case DateTimeTypeConfig.JodaDateTime => Seq( - "play.api.libs.json.JodaReads._", - ) - case _ => Nil - } ParserGeneratorPlayVersionSpecificAttributes( - imports = Seq( - "anorm.{Column, MetaDataItem, TypeDoesNotMatch}", - "play.api.libs.json.{JsArray, JsObject, JsValue}", - "scala.util.{Failure, Success, Try}", - ) ++ (dateImports ++ dateTimeImports).distinct + baseImports ++ jodaImports(ssd) + ) + } + + private[this] val baseImports: Seq[String] = { + Seq( + "anorm.{Column, MetaDataItem, TypeDoesNotMatch}", + "play.api.libs.json.{JsArray, JsObject, JsValue}", + "scala.util.{Failure, Success, Try}", ) } + + private[this] def jodaImports(ssd: ScalaService): Seq[String] = { + if (version.major >= 3) { + Nil + } else { + val dateImports = ssd.attributes.dateType match { + case DateTypeConfig.JodaLocalDate => Seq( + "play.api.libs.json.JodaReads._", + ) + case _ => Nil + } + val dateTimeImports = ssd.attributes.dateTimeType match { + case DateTimeTypeConfig.JodaDateTime => Seq( + "play.api.libs.json.JodaReads._", + ) + case _ => Nil + } + (dateImports ++ dateTimeImports).distinct + } + } } -object ParserGenerator28 extends ParserGenerator { +object ParserGenerator28Scala2 extends AbstractParserGenerator(ScalaVersion(2)) +object ParserGenerator29Scala3 extends AbstractParserGenerator(ScalaVersion(3)) +abstract class AbstractParserGenerator(override val version: ScalaVersion) extends ParserGenerator { override def attributes(ssd: ScalaService): ParserGeneratorPlayVersionSpecificAttributes = - ParserGenerator26.attributes(ssd) + new AbstractParserGenerator26(version).attributes(ssd) } case class ParserGeneratorPlayVersionSpecificAttributes(imports: Seq[String]) trait ParserGenerator extends CodeGenerator { + def version: ScalaVersion + private[this] lazy val conversions = Conversions(version) + def attributes(ssd: ScalaService): ParserGeneratorPlayVersionSpecificAttributes override def invoke(form: InvocationForm): Either[Seq[String], Seq[File]] = { @@ -73,7 +93,7 @@ trait ParserGenerator extends CodeGenerator { form.service.application.key, form.service.version, "Conversions", - header ++ Conversions.code(ssd, attributes(ssd)), + header ++ conversions.code(ssd, attributes(ssd)), Some("Scala") ), ServiceFileNames.toFile(