Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revise bijection surjection #351

Merged
merged 29 commits into from
Aug 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
d6bee77
Revised bijection/surjection schema
Baccata Jul 18, 2022
a687cff
Rename Surjection to Refinement
Baccata Jul 18, 2022
3bd182a
Rename Validator to RefinementProvider
Baccata Jul 18, 2022
d9d488a
Add smart constructors
Baccata Jul 18, 2022
b7e89d2
add example/smoke test for refined
lewisjkl Jul 22, 2022
a5e6e89
add java trait for refined; filter out canonical shapes
lewisjkl Jul 25, 2022
3f1e964
update renderer to work with refined types
lewisjkl Jul 27, 2022
bfa0924
update how refined/validated are rendered
lewisjkl Jul 27, 2022
0d6da40
rm hints when they are captured by refined/validated
lewisjkl Jul 27, 2022
1e89b6e
working with list, set, map
lewisjkl Jul 27, 2022
2fe6062
lock down refinements to simple or collection shapes
lewisjkl Jul 27, 2022
0fbf709
disallow constrained shapes
lewisjkl Jul 27, 2022
f5f10ac
refactor decl method back to option
lewisjkl Jul 27, 2022
b77e435
fix tests compilation issue
lewisjkl Jul 27, 2022
d0d09fc
add headers
lewisjkl Jul 27, 2022
4955acc
fix json test compile errors
lewisjkl Jul 27, 2022
061872d
Removed FieldValidator construct
Baccata Jul 28, 2022
aad9b81
Addressing comments
Baccata Jul 28, 2022
d3c2696
Merge pull request #333 from disneystreaming/revise-biject-surject-ex…
Baccata Jul 28, 2022
66e7a43
add unwrap trait with example spec
lewisjkl Jul 28, 2022
605028f
unwrap working in structure
lewisjkl Jul 28, 2022
41d1701
unwrapping of collections
lewisjkl Jul 28, 2022
41302c9
add docs
lewisjkl Jul 28, 2022
440c48c
Merge pull request #338 from disneystreaming/unwrap-trait
Baccata Jul 29, 2022
3a924d4
make refinement provider implicit
lewisjkl Aug 1, 2022
2ebcd04
Using PartiallyApplied pattern for refinement
Baccata Aug 1, 2022
664b8b5
Merge pull request #346 from disneystreaming/implicit-refined-provider
Baccata Aug 2, 2022
c409d45
handle merge conflicts
lewisjkl Aug 2, 2022
be976d7
update examples
lewisjkl Aug 2, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,8 @@ lazy val example = projectMatrix
(ThisBuild / baseDirectory).value / "sampleSpecs" / "importerror.smithy",
(ThisBuild / baseDirectory).value / "sampleSpecs" / "adtMember.smithy",
(ThisBuild / baseDirectory).value / "sampleSpecs" / "brands.smithy",
(ThisBuild / baseDirectory).value / "sampleSpecs" / "brandscommon.smithy"
(ThisBuild / baseDirectory).value / "sampleSpecs" / "brandscommon.smithy",
(ThisBuild / baseDirectory).value / "sampleSpecs" / "refined.smithy"
),
Compile / resourceDirectory := (ThisBuild / baseDirectory).value / "modules" / "example" / "resources",
isCE3 := true,
Expand Down
25 changes: 19 additions & 6 deletions modules/codegen/src/smithy4s/codegen/CollisionAvoidance.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import smithy4s.codegen.Hint.Native
import smithy4s.codegen.Type.Alias
import smithy4s.codegen.Type.PrimitiveType
import smithy4s.codegen.TypedNode._
import smithy4s.codegen.Type.ExternalType

object CollisionAvoidance {
def apply(compilationUnit: CompilationUnit): CompilationUnit = {
Expand Down Expand Up @@ -70,11 +71,12 @@ object CollisionAvoidance {
recursive,
hints.map(modHint)
)
case TypeAlias(name, originalName, tpe, hints) =>
case TypeAlias(name, originalName, tpe, isUnwrapped, hints) =>
TypeAlias(
protect(name.capitalize),
originalName,
modType(tpe),
isUnwrapped,
hints.map(modHint)
)
case Enumeration(name, originalName, values, hints) =>
Expand All @@ -97,9 +99,17 @@ object CollisionAvoidance {
Type.Collection(collectionType, modType(member))
case Type.Map(key, value) => Type.Map(modType(key), modType(value))
case Type.Ref(namespace, name) => Type.Ref(namespace, name.capitalize)
case Alias(namespace, name, tpe) =>
Alias(namespace, protect(name.capitalize), modType(tpe))
case Alias(namespace, name, tpe, isUnwrapped) =>
Alias(namespace, protect(name.capitalize), modType(tpe), isUnwrapped)
case PrimitiveType(prim) => PrimitiveType(prim)
case ExternalType(name, fqn, pFqn, under, refinementHint) =>
ExternalType(
protect(name),
fqn,
pFqn,
modType(under),
modNativeHint(refinementHint)
)
}

private def modField(field: Field): Field = {
Expand Down Expand Up @@ -134,10 +144,13 @@ object CollisionAvoidance {
private def modRef(ref: Type.Ref): Type.Ref =
Type.Ref(ref.namespace, ref.name.capitalize)

private def modNativeHint(hint: Hint.Native): Hint.Native =
Native(smithy4s.recursion.preprocess(modTypedNode)(hint.typedNode))

private def modHint(hint: Hint): Hint = hint match {
case Native(nt) => Native(smithy4s.recursion.preprocess(modTypedNode)(nt))
case Constraint(tr) => Constraint(modRef(tr))
case other => other
case n: Native => modNativeHint(n)
case Constraint(tr, nat) => Constraint(modRef(tr), modNativeHint(nat))
case other => other
}

private def modProduct(p: Product): Product = {
Expand Down
21 changes: 17 additions & 4 deletions modules/codegen/src/smithy4s/codegen/IR.scala
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ case class TypeAlias(
name: String,
originalName: String,
tpe: Type,
isUnwrapped: Boolean,
hints: List[Hint] = Nil
) extends Decl

Expand Down Expand Up @@ -150,8 +151,8 @@ object Alt {

sealed trait Type {
def dealiased: Type = this match {
case Type.Alias(_, _, tpe) => tpe.dealiased
case other => other
case Type.Alias(_, _, tpe, _) => tpe.dealiased
case other => other
}

def isResolved: Boolean = dealiased == this
Expand Down Expand Up @@ -190,8 +191,20 @@ object Type {
case class Ref(namespace: String, name: String) extends Type {
def show = namespace + "." + name
}
case class Alias(namespace: String, name: String, tpe: Type) extends Type
case class Alias(
namespace: String,
name: String,
tpe: Type,
isUnwrapped: Boolean
) extends Type
case class PrimitiveType(prim: Primitive) extends Type
case class ExternalType(
name: String,
fullyQualifiedName: String,
providerImport: Option[String],
underlyingTpe: Type,
refinementHint: Hint.Native
) extends Type
}

sealed abstract class CollectionType(val tpe: String)
Expand All @@ -209,7 +222,7 @@ object Hint {
case object Error extends Hint
case object PackedInputs extends Hint
case object ErrorMessage extends Hint
case class Constraint(tr: Type.Ref) extends Hint
case class Constraint(tr: Type.Ref, native: Native) extends Hint
case class Protocol(traits: List[Type.Ref]) extends Hint
// traits that get rendered generically
case class Native(typedNode: Fix[TypedNode]) extends Hint
Expand Down
80 changes: 45 additions & 35 deletions modules/codegen/src/smithy4s/codegen/Renderer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ private[codegen] class Renderer(compilationUnit: CompilationUnit) { self =>
renderProduct(name, originalName, fields, recursive, hints)
case Union(name, originalName, alts, recursive, hints) =>
renderUnion(name, originalName, alts, recursive, hints)
case TypeAlias(name, originalName, tpe, hints) =>
case TypeAlias(name, originalName, tpe, _, hints) =>
renderTypeAlias(name, originalName, tpe, hints)
case Enumeration(name, originalName, values, hints) =>
renderEnum(name, originalName, values, hints)
Expand All @@ -99,7 +99,7 @@ private[codegen] class Renderer(compilationUnit: CompilationUnit) { self =>

def renderPackageContents: Lines = {
val typeAliases = compilationUnit.declarations.collect {
case TypeAlias(name, _, _, _) =>
case TypeAlias(name, _, _, _, _) =>
s"type $name = ${compilationUnit.namespace}.${name}.Type"
}

Expand Down Expand Up @@ -338,6 +338,7 @@ private[codegen] class Renderer(compilationUnit: CompilationUnit) { self =>
adtParent: Option[String] = None,
additionalLines: Lines = Lines.empty
): Lines = {

val decl = line"case class $name(${renderArgs(fields)})"
val imports = syntaxImport
val schemaImplicit = if (adtParent.isEmpty) "implicit " else ""
Expand Down Expand Up @@ -367,17 +368,16 @@ private[codegen] class Renderer(compilationUnit: CompilationUnit) { self =>
newline,
if (fields.nonEmpty) {
val renderedFields =
fields.map {
case Field(fieldName, realName, tpe, required, hints) =>
val req = if (required) "required" else "optional"
if (hints.isEmpty) {
line"""${tpe.schemaRef}.$req[$name]("$realName", _.$fieldName)"""
} else {
val mh = memberHints(hints)
fields.map { case Field(fieldName, realName, tpe, required, hints) =>
val req = if (required) "required" else "optional"
if (hints.isEmpty) {
line"""${tpe.schemaRef}.$req[$name]("$realName", _.$fieldName)"""
} else {
val mh = memberHints(hints)
// format: off
line"""${tpe.schemaRef}.$req[$name]("$realName", _.$fieldName).addHints($mh)${renderFieldConstraintValidation(hints, tpe)}"""
line"""${tpe.schemaRef}${renderConstraintValidation(hints)}.$req[$name]("$realName", _.$fieldName).addHints($mh)"""
// format: on
}
}
}
if (fields.size <= 22) {
val definition = if (recursive) "recursive(struct" else "struct"
Expand Down Expand Up @@ -486,7 +486,7 @@ private[codegen] class Renderer(compilationUnit: CompilationUnit) { self =>
// format: off
lines(
line"case object $cn extends $name",
line"""private val ${cn}Alt = $Schema_.constant($cn).oneOf[$name]("$realName").addHints(hints)${renderConstraintValidation(altHints)}""",
line"""private val ${cn}Alt = $Schema_.constant($cn)${renderConstraintValidation(altHints)}.oneOf[$name]("$realName").addHints(hints)""",
line"private val ${cn}AltWithValue = ${cn}Alt($cn)"
)
// format: on
Expand Down Expand Up @@ -619,7 +619,7 @@ private[codegen] class Renderer(compilationUnit: CompilationUnit) { self =>
renderHintsVal(hints),
line"val underlyingSchema : $Schema_[$tpe] = ${tpe.schemaRef}$trailingCalls",
lines(
s"implicit val schema : $Schema_[$name] = bijection(underlyingSchema, $name.make, (_ : $name).value)"
s"implicit val schema : $Schema_[$name] = bijection(underlyingSchema, asBijection)"
)
)
).addImports(imports)
Expand Down Expand Up @@ -665,11 +665,25 @@ private[codegen] class Renderer(compilationUnit: CompilationUnit) { self =>
line"$col(${member.schemaRef})"
case Type.Map(key, value) =>
line"map(${key.schemaRef}, ${value.schemaRef})"
case Type.Alias(ns, name, Type.PrimitiveType(_)) =>
case Type.Alias(
ns,
name,
_,
false
) =>
line"$name.schema".addImport(ns + "." + name)
case Type.Alias(ns, name, _) =>
case Type.Alias(ns, name, _, _) =>
line"$name.underlyingSchema".addImport(ns + "." + name)
case Type.Ref(ns, name) => line"$name.schema".addImport(ns + "." + name)
case Type.ExternalType(
_,
fqn,
maybeProviderImport,
underlyingTpe,
hint
) =>
line"${underlyingTpe.schemaRef}.refined[$fqn](${renderNativeHint(hint)})"
.addImport(maybeProviderImport.getOrElse(""))
}

private def schemaRefP(primitive: Primitive): String = primitive match {
Expand All @@ -692,9 +706,9 @@ private[codegen] class Renderer(compilationUnit: CompilationUnit) { self =>
}

def name: Option[String] = tpe match {
case Type.Alias(_, name, _) => Some(name)
case Type.Ref(_, name) => Some(name)
case _ => None
case Type.Alias(_, name, _, _) => Some(name)
case Type.Ref(_, name) => Some(name)
case _ => None
}
}

Expand Down Expand Up @@ -729,15 +743,17 @@ private[codegen] class Renderer(compilationUnit: CompilationUnit) { self =>
enumValueClassName(enumValue.name, enumValue.value, enumValue.ordinal)
}

private def renderHint(hint: Hint): Option[Line] = hint match {
case Hint.Native(typedNode) =>
private def renderNativeHint(hint: Hint.Native): Line =
Line(
smithy4s.recursion
.cata(renderTypedNode)(typedNode)
.cata(renderTypedNode)(hint.typedNode)
.run(true)
._2
.some
.map(Line(_))
case _ => None
)

private def renderHint(hint: Hint): Option[Line] = hint match {
case h: Hint.Native => renderNativeHint(h).some
case _ => None
}

def renderId(name: String, ns: String = namespace): Line =
Expand All @@ -757,20 +773,14 @@ private[codegen] class Renderer(compilationUnit: CompilationUnit) { self =>
}

def renderConstraintValidation(hints: List[Hint]): Line = {
val tags = hints.collect { case Hint.Constraint(tr) => tr }
if (tags.isEmpty) Line.empty
else {
tags.map(t => line".validated[${t.renderFull}]").intercalate(Line.empty)
}
}

def renderFieldConstraintValidation(hints: List[Hint], tpe: Type): Line = {
val tags = hints.collect { case Hint.Constraint(tr) => tr }
val tags = hints.collect { case t: Hint.Constraint => t }
if (tags.isEmpty) Line.empty
else {
tags
.map(t => line".validated[${t.renderFull}, $tpe]")
.intercalate(Line.dot)
.map { tag =>
line".validated(${renderNativeHint(tag.native)})"
}
.intercalate(Line.empty)
}
}

Expand Down
Loading