diff --git a/modules/codegen-plugin/src/sbt-test/codegen-plugin/defaults/build.sbt b/modules/codegen-plugin/src/sbt-test/codegen-plugin/defaults/build.sbt index a83619528..73c267cea 100644 --- a/modules/codegen-plugin/src/sbt-test/codegen-plugin/defaults/build.sbt +++ b/modules/codegen-plugin/src/sbt-test/codegen-plugin/defaults/build.sbt @@ -1,6 +1,6 @@ lazy val root = (project in file(".")) .enablePlugins(Smithy4sCodegenPlugin) .settings( - scalaVersion := "2.13.6", + scalaVersion := "2.13.8", libraryDependencies += "com.disneystreaming.smithy4s" %% "smithy4s-core" % smithy4sVersion.value ) diff --git a/modules/codegen-plugin/src/sbt-test/codegen-plugin/defaults/src/main/scala/Main.scala b/modules/codegen-plugin/src/sbt-test/codegen-plugin/defaults/src/main/scala/Main.scala new file mode 100644 index 000000000..e227037f0 --- /dev/null +++ b/modules/codegen-plugin/src/sbt-test/codegen-plugin/defaults/src/main/scala/Main.scala @@ -0,0 +1,27 @@ +/* + * Copyright 2021-2022 Disney Streaming + * + * Licensed under the Tomorrow Open Source Technology License, Version 1.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://disneystreaming.github.io/TOST-1.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package demo + +object Main extends App { + try { + println(smithy.api.NonEmptyString("nope").value) + } catch { + case _: java.lang.ExceptionInInitializerError => + println("failed") + sys.exit(1) + } +} diff --git a/modules/codegen-plugin/src/sbt-test/codegen-plugin/defaults/test b/modules/codegen-plugin/src/sbt-test/codegen-plugin/defaults/test index 77df0c0a4..815ca2fe4 100644 --- a/modules/codegen-plugin/src/sbt-test/codegen-plugin/defaults/test +++ b/modules/codegen-plugin/src/sbt-test/codegen-plugin/defaults/test @@ -2,3 +2,7 @@ > compile $ exists target/scala-2.13/src_managed/main/smithy4s/example/ObjectService.scala $ exists target/scala-2.13/resource_managed/main/smithy4s.example.ObjectService.json + +# check if code can run, this can reveal runtime issues +# such as initialization errors +> run \ No newline at end of file diff --git a/modules/codegen/src/smithy4s/codegen/CollisionAvoidance.scala b/modules/codegen/src/smithy4s/codegen/CollisionAvoidance.scala index 771f9ed76..7adc15d48 100644 --- a/modules/codegen/src/smithy4s/codegen/CollisionAvoidance.scala +++ b/modules/codegen/src/smithy4s/codegen/CollisionAvoidance.scala @@ -70,12 +70,13 @@ object CollisionAvoidance { recursive, hints.map(modHint) ) - case TypeAlias(name, originalName, tpe, isUnwrapped, hints) => + case TypeAlias(name, originalName, tpe, isUnwrapped, rec, hints) => TypeAlias( protect(name.capitalize), originalName, modType(tpe), isUnwrapped, + rec, hints.map(modHint) ) case Enumeration(name, originalName, values, hints) => diff --git a/modules/codegen/src/smithy4s/codegen/IR.scala b/modules/codegen/src/smithy4s/codegen/IR.scala index f7ae3d69c..8d5b93e80 100644 --- a/modules/codegen/src/smithy4s/codegen/IR.scala +++ b/modules/codegen/src/smithy4s/codegen/IR.scala @@ -84,6 +84,7 @@ case class TypeAlias( originalName: String, tpe: Type, isUnwrapped: Boolean, + recursive: Boolean = false, hints: List[Hint] = Nil ) extends Decl diff --git a/modules/codegen/src/smithy4s/codegen/Renderer.scala b/modules/codegen/src/smithy4s/codegen/Renderer.scala index 1ae65f403..3c79022b9 100644 --- a/modules/codegen/src/smithy4s/codegen/Renderer.scala +++ b/modules/codegen/src/smithy4s/codegen/Renderer.scala @@ -115,8 +115,8 @@ private[codegen] class Renderer(compilationUnit: CompilationUnit) { self => case p: Product => renderProduct(p) case union @ Union(_, originalName, alts, recursive, hints) => renderUnion(union.nameRef, originalName, alts, recursive, hints) - case ta @ TypeAlias(_, originalName, tpe, _, hints) => - renderTypeAlias(ta.nameRef, originalName, tpe, hints) + case ta @ TypeAlias(_, originalName, tpe, _, recursive, hints) => + renderTypeAlias(ta.nameRef, originalName, tpe, recursive, hints) case enumeration @ Enumeration(_, originalName, values, hints) => renderEnum(enumeration.nameRef, originalName, values, hints) case _ => Lines.empty @@ -124,7 +124,7 @@ private[codegen] class Renderer(compilationUnit: CompilationUnit) { self => def renderPackageContents: Lines = { val typeAliases = compilationUnit.declarations.collect { - case TypeAlias(name, _, _, _, _) => + case TypeAlias(name, _, _, _, _, _) => line"type $name = ${compilationUnit.namespace}.${name}.Type" } @@ -691,17 +691,22 @@ private[codegen] class Renderer(compilationUnit: CompilationUnit) { self => name: NameRef, originalName: String, tpe: Type, + recursive: Boolean, hints: List[Hint] ): Lines = { + val definition = + if (recursive) line"$recursive_(" + else Line.empty val trailingCalls = line".withId(id).addHints(hints)${renderConstraintValidation(hints)}" + val closing = if (recursive) ")" else "" lines( obj(name, line"$Newtype_[$tpe]")( renderId(originalName), renderHintsVal(hints), line"val underlyingSchema : $Schema_[$tpe] = ${tpe.schemaRef}$trailingCalls", lines( - line"implicit val schema : $Schema_[$name] = $bijection_(underlyingSchema, asBijection)" + line"implicit val schema : $Schema_[$name] = $definition$bijection_(underlyingSchema, asBijection)$closing" ) ) ) diff --git a/modules/codegen/src/smithy4s/codegen/SmithyToIR.scala b/modules/codegen/src/smithy4s/codegen/SmithyToIR.scala index 819f775d3..06b6383f8 100644 --- a/modules/codegen/src/smithy4s/codegen/SmithyToIR.scala +++ b/modules/codegen/src/smithy4s/codegen/SmithyToIR.scala @@ -81,12 +81,17 @@ private[codegen] class SmithyToIR(model: Model, namespace: String) { private def getDefault(shape: Shape): Option[Decl] = { val hints = traitsToHints(shape.getAllTraits().asScala.values.toList) + val recursive = hints.exists { + case Hint.Trait => true + case _ => false + } + shape.tpe.flatMap { case Type.Alias(_, name, tpe: Type.ExternalType, isUnwrapped) => val newHints = hints.filterNot(_ == tpe.refinementHint) - TypeAlias(name, name, tpe, isUnwrapped, newHints).some + TypeAlias(name, name, tpe, isUnwrapped, recursive, newHints).some case Type.Alias(_, name, tpe, isUnwrapped) => - TypeAlias(name, name, tpe, isUnwrapped, hints).some + TypeAlias(name, name, tpe, isUnwrapped, recursive, hints).some case Type.PrimitiveType(_) => None case other => TypeAlias( @@ -94,6 +99,7 @@ private[codegen] class SmithyToIR(model: Model, namespace: String) { shape.name, other, isUnwrapped = false, + recursive, hints ).some } @@ -143,9 +149,13 @@ private[codegen] class SmithyToIR(model: Model, namespace: String) { ) override def structureShape(shape: StructureShape): Option[Decl] = { - val rec = isRecursive(shape.getId()) - val hints = traitsToHints(shape.getAllTraits().asScala.values.toList) + val isTrait = hints.exists { + case Hint.Trait => true + case _ => false + } + val rec = isRecursive(shape.getId()) || isTrait + val mixins = shape.getMixins.asScala.flatMap(_.tpe).toList val isMixin = shape.hasTrait(classOf[MixinTrait]) val p = @@ -167,8 +177,12 @@ private[codegen] class SmithyToIR(model: Model, namespace: String) { val rec = isRecursive(shape.getId()) val hints = traitsToHints(shape.getAllTraits().asScala.values.toList) + val isTrait = hints.exists { + case Hint.Trait => true + case _ => false + } NonEmptyList.fromList(shape.alts).map { case alts => - Union(shape.name, shape.name, alts, rec, hints) + Union(shape.name, shape.name, alts, rec || isTrait, hints) } } diff --git a/modules/example/src/smithy4s/example/ArbitraryData.scala b/modules/example/src/smithy4s/example/ArbitraryData.scala index 032b053e3..8e7da65dd 100644 --- a/modules/example/src/smithy4s/example/ArbitraryData.scala +++ b/modules/example/src/smithy4s/example/ArbitraryData.scala @@ -3,6 +3,7 @@ package smithy4s.example import smithy4s.Schema import smithy4s.Hints import smithy4s.ShapeId +import smithy4s.schema.Schema.recursive import smithy4s.schema.Schema.bijection import smithy4s.Document import smithy4s.Newtype @@ -14,5 +15,5 @@ object ArbitraryData extends Newtype[Document] { smithy.api.Trait(None, None, None, None), ) val underlyingSchema : Schema[Document] = document.withId(id).addHints(hints) - implicit val schema : Schema[ArbitraryData] = bijection(underlyingSchema, asBijection) + implicit val schema : Schema[ArbitraryData] = recursive(bijection(underlyingSchema, asBijection)) } \ No newline at end of file diff --git a/modules/example/src/smithy4s/example/TestTrait.scala b/modules/example/src/smithy4s/example/TestTrait.scala index becb247cf..864eea169 100644 --- a/modules/example/src/smithy4s/example/TestTrait.scala +++ b/modules/example/src/smithy4s/example/TestTrait.scala @@ -4,6 +4,7 @@ import smithy4s.Schema import smithy4s.Hints import smithy4s.ShapeId import smithy4s.schema.Schema.struct +import smithy4s.schema.Schema.recursive import smithy4s.ShapeTag case class TestTrait(orderType: Option[OrderType] = None) @@ -14,9 +15,9 @@ object TestTrait extends ShapeTag.Companion[TestTrait] { smithy.api.Trait(None, None, None, None), ) - implicit val schema: Schema[TestTrait] = struct( + implicit val schema: Schema[TestTrait] = recursive(struct( OrderType.schema.optional[TestTrait]("orderType", _.orderType), ){ TestTrait.apply - }.withId(id).addHints(hints) + }.withId(id).addHints(hints)) } \ No newline at end of file