Skip to content

Commit e006288

Browse files
committed
⏺ Merge series/0.18 into series/0.19
Includes: - Long literal rendering fix (L suffix) from disneystreaming#1898 - Mill 1.x backport from disneystreaming#1899 - Codegen Scala 3 cross-compilation - Recursive scheme caching improvements Generated files kept as 0.19 (Hints.dynamic) variants; Long literal fix carried through Renderer merge.
2 parents f80b090 + 6b110df commit e006288

File tree

35 files changed

+1136
-79
lines changed

35 files changed

+1136
-79
lines changed

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,21 @@ Prevents using it as an implicit conversion in Scala 2
9797

9898
The handwritten Java `TraitService` implementations for all smithy4s meta traits in the protocol module have been removed and are now generated by smithy4s itself. This should not affect users who are not programmatically using those traits directly. (see [#1901](https://github.com/disneystreaming/smithy4s/pull/1901))
9999

100+
## Fix Long literal rendering in [#1898](https://github.com/disneystreaming/smithy4s/pull/1898)
101+
102+
* codegen: Fix `Long` literal rendering to add `L` suffix, preventing `integer number too large` compilation errors for values exceeding `Int.MaxValue`
103+
104+
# 0.18.49
105+
106+
* mill codegen plugin: now supports Mill 1.x (tested with 1.1.1), in addition to the existing Mill 0.11.x and 0.12.x support
107+
* codegen: Cross comple codegen module against Scala 3
108+
* codegen: Fix `Long` literal rendering to add `L` suffix, preventing `integer number too large` compilation errors for values exceeding `Int.MaxValue` in [#1898](https://github.com/disneystreaming/smithy4s/pull/1898)
109+
110+
# 0.18.48
111+
112+
* codegen: Update codegen of numbers when using dynamic bindings in [#1886](https://github.com/disneystreaming/smithy4s/pull/1886)
113+
* codegen: Add type params to `struct` and `union` in codegen schemas in [#1893](https://github.com/disneystreaming/smithy4s/pull/1893)
114+
100115
# 0.18.47
101116

102117
* core: Add `asSurjection` method to `ValidatedNewtype` class (see [#1873](https://github.com/disneystreaming/smithy4s/pull/1873))

build.sbt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,8 @@ lazy val codegen = projectMatrix
417417
Dependencies.Circe.parser.value,
418418
Dependencies.Circe.generic.value,
419419
("io.get-coursier" %% "coursier" % "2.1.24")
420-
.cross(CrossVersion.for3Use2_13)
420+
.cross(CrossVersion.for3Use2_13),
421+
Dependencies.Mima.core % Test
421422
),
422423
libraryDependencies ++= {
423424
if (scalaVersion.value.startsWith("2."))

modules/bootstrapped/src/generated/smithy4s/example/ArbitraryDataTest.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ object ArbitraryDataTest extends ShapeTag.Companion[ArbitraryDataTest] {
1212
val id: ShapeId = ShapeId("smithy4s.example", "ArbitraryDataTest")
1313

1414
val hints: Hints = Hints(
15-
smithy4s.example.ArbitraryData(smithy4s.Document.obj("str" -> smithy4s.Document.fromString("hello"), "int" -> smithy4s.Document.fromLong(1), "bool" -> smithy4s.Document.fromBoolean(true), "arr" -> smithy4s.Document.array(smithy4s.Document.fromString("one"), smithy4s.Document.fromString("two"), smithy4s.Document.fromString("three")), "obj" -> smithy4s.Document.obj("str" -> smithy4s.Document.fromString("s"), "i" -> smithy4s.Document.fromLong(1)))),
15+
smithy4s.example.ArbitraryData(smithy4s.Document.obj("str" -> smithy4s.Document.fromString("hello"), "int" -> smithy4s.Document.fromLong(1L), "bool" -> smithy4s.Document.fromBoolean(true), "arr" -> smithy4s.Document.array(smithy4s.Document.fromString("one"), smithy4s.Document.fromString("two"), smithy4s.Document.fromString("three")), "obj" -> smithy4s.Document.obj("str" -> smithy4s.Document.fromString("s"), "i" -> smithy4s.Document.fromLong(1L)))),
1616
).lazily
1717

1818

modules/bootstrapped/src/generated/smithy4s/example/HealthResponse.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ object HealthResponse extends ShapeTag.Companion[HealthResponse] {
1313
val id: ShapeId = ShapeId("smithy4s.example", "HealthResponse")
1414

1515
val hints: Hints = Hints(
16-
smithy4s.example.FreeForm(smithy4s.Document.obj("i" -> smithy4s.Document.fromLong(1), "a" -> smithy4s.Document.fromLong(2))),
16+
smithy4s.example.FreeForm(smithy4s.Document.obj("i" -> smithy4s.Document.fromLong(1L), "a" -> smithy4s.Document.fromLong(2L))),
1717
).lazily
1818

1919
// constructor using the original order from the spec
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package smithy4s.example
2+
3+
import smithy4s.Hints
4+
import smithy4s.Schema
5+
import smithy4s.ShapeId
6+
import smithy4s.ShapeTag
7+
import smithy4s.schema.Schema.int
8+
import smithy4s.schema.Schema.struct
9+
10+
final case class LeafNode(value: Int)
11+
12+
object LeafNode extends ShapeTag.Companion[LeafNode] {
13+
val id: ShapeId = ShapeId("smithy4s.example", "LeafNode")
14+
15+
val hints: Hints = Hints.empty
16+
17+
// constructor using the original order from the spec
18+
private def make(value: Int): LeafNode = LeafNode(value)
19+
20+
implicit val schema: Schema[LeafNode] = struct(
21+
int.required[LeafNode]("value", _.value),
22+
)(make).withId(id).addHints(hints)
23+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package smithy4s.example
2+
3+
import smithy4s.Hints
4+
import smithy4s.Schema
5+
import smithy4s.ShapeId
6+
import smithy4s.ShapeTag
7+
import smithy4s.schema.Schema.bijection
8+
import smithy4s.schema.Schema.recursive
9+
import smithy4s.schema.Schema.union
10+
11+
sealed trait Tree extends scala.Product with scala.Serializable { self =>
12+
@inline final def widen: Tree = this
13+
def $ordinal: Int
14+
15+
object project {
16+
def tree: Option[TreeNode] = Tree.TreeCase.alt.project.lift(self).map(_.tree)
17+
def leaf: Option[LeafNode] = Tree.LeafCase.alt.project.lift(self).map(_.leaf)
18+
}
19+
20+
def accept[A](visitor: Tree.Visitor[A]): A = this match {
21+
case value: Tree.TreeCase => visitor.tree(value.tree)
22+
case value: Tree.LeafCase => visitor.leaf(value.leaf)
23+
}
24+
}
25+
object Tree extends ShapeTag.Companion[Tree] {
26+
27+
def tree(tree: TreeNode): Tree = TreeCase(tree)
28+
def leaf(leaf: LeafNode): Tree = LeafCase(leaf)
29+
30+
val id: ShapeId = ShapeId("smithy4s.example", "Tree")
31+
32+
val hints: Hints = Hints.empty
33+
34+
final case class TreeCase(tree: TreeNode) extends Tree { final def $ordinal: Int = 0 }
35+
final case class LeafCase(leaf: LeafNode) extends Tree { final def $ordinal: Int = 1 }
36+
37+
object TreeCase {
38+
val hints: Hints = Hints.empty
39+
val schema: Schema[Tree.TreeCase] = bijection(TreeNode.schema.addHints(hints), Tree.TreeCase(_), _.tree)
40+
val alt = schema.oneOf[Tree]("tree")
41+
}
42+
object LeafCase {
43+
val hints: Hints = Hints.empty
44+
val schema: Schema[Tree.LeafCase] = bijection(LeafNode.schema.addHints(hints), Tree.LeafCase(_), _.leaf)
45+
val alt = schema.oneOf[Tree]("leaf")
46+
}
47+
48+
trait Visitor[A] {
49+
def tree(value: TreeNode): A
50+
def leaf(value: LeafNode): A
51+
}
52+
53+
object Visitor {
54+
trait Default[A] extends Visitor[A] {
55+
def default: A
56+
def tree(value: TreeNode): A = default
57+
def leaf(value: LeafNode): A = default
58+
}
59+
}
60+
61+
implicit val schema: Schema[Tree] = recursive(union(
62+
Tree.TreeCase.alt,
63+
Tree.LeafCase.alt,
64+
){
65+
_.$ordinal
66+
}.withId(id).addHints(hints))
67+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package smithy4s.example
2+
3+
import smithy4s.Hints
4+
import smithy4s.Schema
5+
import smithy4s.ShapeId
6+
import smithy4s.ShapeTag
7+
import smithy4s.schema.Schema.recursive
8+
import smithy4s.schema.Schema.struct
9+
10+
final case class TreeNode(left: Tree, right: Tree)
11+
12+
object TreeNode extends ShapeTag.Companion[TreeNode] {
13+
val id: ShapeId = ShapeId("smithy4s.example", "TreeNode")
14+
15+
val hints: Hints = Hints.empty
16+
17+
// constructor using the original order from the spec
18+
private def make(left: Tree, right: Tree): TreeNode = TreeNode(left, right)
19+
20+
implicit val schema: Schema[TreeNode] = recursive(struct(
21+
Tree.schema.required[TreeNode]("left", _.left),
22+
Tree.schema.required[TreeNode]("right", _.right),
23+
)(make).withId(id).addHints(hints))
24+
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package smithy4s
2+
3+
import munit.FunSuite
4+
5+
import smithy4s.example.{Tree, TreeNode, LeafNode, Foo}
6+
import cats.Hash
7+
import smithy4s.schema._
8+
import scala.annotation.tailrec
9+
import smithy4s.internals.maps.MMap
10+
import smithy4s.interopcats.SchemaVisitorHash
11+
import smithy4s.schema.Schema.recursive
12+
import smithy4s.schema.Schema._
13+
14+
class RecursiveSpec extends FunSuite {
15+
16+
case class Recurse(n: Option[Recurse])
17+
18+
object Recurse {
19+
implicit val schema: Schema[Recurse] = recursive {
20+
struct(
21+
schema.optional[Recurse]("n", _.n)
22+
)(Recurse.apply)
23+
}
24+
}
25+
26+
def buildTree(size: Int): Tree = {
27+
val seed = Math.round(Math.random() * 1000).toInt
28+
val nodes = (1 to size).map(i => Tree.leaf(LeafNode(i * seed))).toList
29+
30+
@tailrec()
31+
def recursiveFold(els: List[Tree]): Tree =
32+
els match {
33+
case head :: Nil => head
34+
case x => {
35+
val joined: List[Tree] = x
36+
.sliding(2, 2)
37+
.flatMap {
38+
case left :: right :: Nil =>
39+
List(Tree.tree(TreeNode(left, right)))
40+
case x => x
41+
}
42+
.toList
43+
recursiveFold(joined)
44+
}
45+
}
46+
47+
recursiveFold(nodes)
48+
}
49+
50+
def buildRecursive(size: Int): Recurse =
51+
(1 to size).foldLeft(Recurse(None))((tail, i) => Recurse(Some(tail)))
52+
53+
def useLazyTestCache[F[_]](store: MMap[Any, Any]) = new CompilationCache[F] {
54+
override def getOrElseUpdate[A](
55+
schema: Schema[A],
56+
fetch: Schema[A] => F[A]
57+
): F[A] = {
58+
store.getOrElseUpdate(schema, fetch(schema)).asInstanceOf[F[A]]
59+
}
60+
}
61+
62+
def ignoreLazyTestCache[F[_]](store: MMap[Any, Any]) =
63+
new CompilationCache[F] {
64+
override def getOrElseUpdate[A](
65+
schema: Schema[A],
66+
fetch: Schema[A] => F[A]
67+
): F[A] = {
68+
if (schema.isInstanceOf[Schema.LazySchema[_]]) { fetch(schema) }
69+
else store.getOrElseUpdate(schema, fetch(schema)).asInstanceOf[F[A]]
70+
}
71+
}
72+
73+
def runTest[A](
74+
caseString: String,
75+
value: Int => A,
76+
buildCache: MMap[Any, Any] => CompilationCache[Hash],
77+
transformSchema: Schema[A] => Schema[A] = (x: Schema[A]) => x
78+
)(implicit schema: Schema[A]) =
79+
test(s"$caseString") {
80+
val store: MMap[Any, Any] = MMap.empty
81+
82+
val updatedSchema = transformSchema(schema)
83+
84+
val hashVisitor: Hash[A] =
85+
SchemaVisitorHash.fromSchema(updatedSchema, buildCache(store))
86+
87+
// Invoke hash with a size that will have some recursion so that the build cache an be materialized
88+
hashVisitor.hash(value(2))
89+
val sizeAfterInitializing = store.size
90+
91+
val sizes = List(10, 100, 256)
92+
sizes.foreach(i => hashVisitor.hash(value(i)))
93+
val sizeAfterHashing = store.size
94+
95+
assertEquals(
96+
sizeAfterHashing,
97+
sizeAfterInitializing,
98+
"cache store size has grown after initialization"
99+
)
100+
}
101+
102+
def addHints[A](schema: Schema[A]): Schema[A] = {
103+
schema.transformHintsTransitively(
104+
_.add(
105+
smithy.api.Documentation("Adding some hints")
106+
)
107+
)
108+
}
109+
110+
def runTestCases[A: Schema](caseString: String, value: Int => A) = {
111+
runTest(
112+
s"$caseString: current cache, hints unchanged",
113+
value,
114+
buildCache = ignoreLazyTestCache
115+
)
116+
runTest(
117+
s"$caseString: current cache, hints transformed",
118+
value,
119+
buildCache = ignoreLazyTestCache,
120+
transformSchema = addHints[A]
121+
)
122+
runTest(
123+
s"$caseString: updated cache, hints unchanged",
124+
value,
125+
buildCache = useLazyTestCache
126+
)
127+
runTest(
128+
s"$caseString: updated cache, hints transformed",
129+
value,
130+
buildCache = useLazyTestCache,
131+
transformSchema = addHints[A]
132+
)
133+
}
134+
135+
runTestCases("Foo", Foo.int)
136+
runTestCases("Tree", buildTree)
137+
runTestCases("Recurse", buildRecursive)
138+
139+
}

modules/bootstrapped/test/src/smithy4s/http/AcceptHeaderSpec.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ final class AcceptHeaderSpec extends FunSuite {
3333
try fa
3434
catch { case e: Throwable => f(e) }
3535
def pure[A](a: A): Id[A] = a
36-
def zipMapAll[A](seq: IndexedSeq[Id[Any]])(f: IndexedSeq[Any] => A): Id[A] =
36+
def zipMapAll[A,B](seq: IndexedSeq[Id[A]])(f: IndexedSeq[A] => B): Id[B] =
3737
f(seq)
3838
}
3939

0 commit comments

Comments
 (0)