Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
dhpiggott committed Mar 7, 2024
1 parent 8229575 commit a7de7a5
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ object DefaultRequiredUnknownFieldRetentionExample extends ShapeTag.Companion[De
implicit val schema: Schema[DefaultRequiredUnknownFieldRetentionExample] = struct(
string.optional[DefaultRequiredUnknownFieldRetentionExample]("foo", _.foo),
string.optional[DefaultRequiredUnknownFieldRetentionExample]("bar", _.bar),
document.required[DefaultRequiredUnknownFieldRetentionExample]("retainedUnknownFields", _.retainedUnknownFields).addHints(alloy.UnknownDocumentFieldRetention(), smithy.api.Default(smithy4s.Document.nullDoc)),
document.required[DefaultRequiredUnknownFieldRetentionExample]("retainedUnknownFields", _.retainedUnknownFields).addHints(smithy.api.Default(smithy4s.Document.nullDoc), alloy.UnknownJsonFieldRetention(), alloy.UnknownDocumentFieldRetention()),
)(make).withId(id).addHints(hints)
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ object DefaultUnknownFieldRetentionExample extends ShapeTag.Companion[DefaultUnk
implicit val schema: Schema[DefaultUnknownFieldRetentionExample] = struct(
string.optional[DefaultUnknownFieldRetentionExample]("foo", _.foo),
string.optional[DefaultUnknownFieldRetentionExample]("bar", _.bar),
document.field[DefaultUnknownFieldRetentionExample]("retainedUnknownFields", _.retainedUnknownFields).addHints(alloy.UnknownDocumentFieldRetention(), smithy.api.Default(smithy4s.Document.nullDoc)),
document.field[DefaultUnknownFieldRetentionExample]("retainedUnknownFields", _.retainedUnknownFields).addHints(alloy.UnknownDocumentFieldRetention(), smithy.api.Default(smithy4s.Document.nullDoc), alloy.UnknownJsonFieldRetention()),
)(make).withId(id).addHints(hints)
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ object RequiredUnknownFieldRetentionExample extends ShapeTag.Companion[RequiredU
implicit val schema: Schema[RequiredUnknownFieldRetentionExample] = struct(
string.optional[RequiredUnknownFieldRetentionExample]("foo", _.foo),
string.optional[RequiredUnknownFieldRetentionExample]("bar", _.bar),
document.required[RequiredUnknownFieldRetentionExample]("retainedUnknownFields", _.retainedUnknownFields).addHints(alloy.UnknownDocumentFieldRetention()),
document.required[RequiredUnknownFieldRetentionExample]("retainedUnknownFields", _.retainedUnknownFields).addHints(alloy.UnknownDocumentFieldRetention(), alloy.UnknownJsonFieldRetention()),
)(make).withId(id).addHints(hints)
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ object UnknownFieldRetentionExample extends ShapeTag.Companion[UnknownFieldReten
implicit val schema: Schema[UnknownFieldRetentionExample] = struct(
string.optional[UnknownFieldRetentionExample]("foo", _.foo),
string.optional[UnknownFieldRetentionExample]("bar", _.bar),
document.optional[UnknownFieldRetentionExample]("retainedUnknownFields", _.retainedUnknownFields).addHints(alloy.UnknownDocumentFieldRetention()),
document.optional[UnknownFieldRetentionExample]("retainedUnknownFields", _.retainedUnknownFields).addHints(alloy.UnknownDocumentFieldRetention(), alloy.UnknownJsonFieldRetention()),
)(make).withId(id).addHints(hints)
}
2 changes: 2 additions & 0 deletions modules/json/src/smithy4s/json/internals/JCodec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package internals
import com.github.plokhotnyuk.jsoniter_scala.core._

/**
* // TODO: Update this
* Construct that expresses the ability to decode an http message,
* the metadata of which will have already been decoded and staged
* in a Map[String, Any] indexed by field.
Expand All @@ -36,6 +37,7 @@ private[internals] trait JCodec[A] extends JsonCodec[A] {
def expecting: String

/**
* // TODO: Update this
* States whether this codec expects data
* from the body of an http request (as opposed to
* from headers, query params, etc). Used to prevent
Expand Down
124 changes: 76 additions & 48 deletions modules/json/src/smithy4s/json/internals/SchemaVisitorJCodec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import smithy.api.JsonName
import smithy.api.TimestampFormat
import alloy.Discriminated
import alloy.Nullable
import alloy.UnknownJsonFieldRetention
import alloy.Untagged
import smithy4s.internals.DiscriminatedUnionMember
import smithy4s.schema._
Expand All @@ -36,7 +37,6 @@ import smithy4s.Timestamp
import scala.collection.compat.immutable.ArraySeq
import scala.collection.immutable.VectorBuilder
import scala.collection.mutable.ListBuffer
import scala.collection.mutable.{Map => MMap}
import scala.collection.immutable.ListMap

private[smithy4s] class SchemaVisitorJCodec(
Expand All @@ -47,7 +47,6 @@ private[smithy4s] class SchemaVisitorJCodec(
preserveMapOrder: Boolean,
val cache: CompilationCache[JCodec]
) extends SchemaVisitor.Cached[JCodec] { self =>
private val emptyMetadata: MMap[String, Any] = MMap.empty

object PrimitiveJCodecs {
val boolean: JCodec[Boolean] =
Expand Down Expand Up @@ -1299,23 +1298,70 @@ private[smithy4s] class SchemaVisitorJCodec(
)
}

trait UnknownFieldsEncoder[A] {
def apply(a: A): JsonWriter => Unit
}

object UnknownFieldsEncoder
extends SchemaVisitor.Default[UnknownFieldsEncoder] { self =>

override def default[A]: UnknownFieldsEncoder[A] = _ => _ => ()

override def primitive[P](
shapeId: ShapeId,
hints: Hints,
tag: Primitive[P]
): UnknownFieldsEncoder[P] = document =>
tag match {
case PDocument =>
// document match {
// case Document.DObject =>
documentJCodec.encodeValue(document, _)
// case _ => _ =>()
// }

case _ => _ => ()
}

override def option[A](
schema: Schema[A]
): UnknownFieldsEncoder[Option[A]] = {
val encoder = self(schema)
locally {
case Some(a) => encoder.apply(a)
case None => _ => ()
}
}
}

private def fieldEncoder[Z, A](
field: Field[Z, A]
): (Z, JsonWriter) => Unit = {
val codec = apply(field.schema)
val jLabel = jsonLabel(field)
val writeLabel: JsonWriter => Unit =
if (jLabel.forall(JsonWriter.isNonEscapedAscii)) {
_.writeNonEscapedAsciiKey(jLabel)
} else _.writeKey(jLabel)

if (explicitDefaultsEncoding) { (z: Z, out: JsonWriter) =>
writeLabel(out)
codec.encodeValue(field.get(z), out)
} else { (z: Z, out: JsonWriter) =>
field.foreachUnlessDefault(z) { (a: A) =>
println(s"hints: ${field.hints}")
if (
field.hints
.has(UnknownJsonFieldRetention)
) {
println("here")
val unknownFieldsEncoder = UnknownFieldsEncoder(field.schema)
(z: Z, out: JsonWriter) => unknownFieldsEncoder(field.get(z))(out)
} else {
println("there")
val codec = apply(field.schema)
val jLabel = jsonLabel(field)
val writeLabel: JsonWriter => Unit =
if (jLabel.forall(JsonWriter.isNonEscapedAscii)) {
_.writeNonEscapedAsciiKey(jLabel)
} else _.writeKey(jLabel)

if (explicitDefaultsEncoding) { (z: Z, out: JsonWriter) =>
writeLabel(out)
codec.encodeValue(a, out)
codec.encodeValue(field.get(z), out)
} else { (z: Z, out: JsonWriter) =>
field.foreachUnlessDefault(z) { (a: A) =>
writeLabel(out)
codec.encodeValue(a, out)
}
}
}
}
Expand Down Expand Up @@ -1349,21 +1395,13 @@ private[smithy4s] class SchemaVisitorJCodec(
private[this] val documentEncoders =
fields.map(labelledField => fieldEncoder(labelledField._1))

def expecting: String = "object"
override def expecting: String = "object"

override def canBeKey = false

def decodeValue(cursor: Cursor, in: JsonReader): Z =
decodeValue_(cursor, in)(emptyMetadata)

private def decodeValue_(
cursor: Cursor,
in: JsonReader
): scala.collection.Map[String, Any] => Z = {
override def decodeValue(cursor: Cursor, in: JsonReader): Z = {
val buffer = new util.HashMap[String, Any](handlers.size << 1, 0.5f)
if (in.isNextToken('{')) {
// In this case, metadata and payload are mixed together
// and values field values must be sought from either.
if (!in.isNextToken('}')) {
in.rollbackToken()
while ({
Expand All @@ -1375,37 +1413,27 @@ private[smithy4s] class SchemaVisitorJCodec(
if (!in.isCurrentToken('}')) in.objectEndOrCommaError()
}
} else in.decodeError("Expected JSON object")

// At this point, we have parsed the json and retrieved
// all the values that interest us for the construction
// of our domain object.
// We therefore reconcile the values pulled from the json
// with the ones pull the metadata, and call the constructor
// on it.
{ (meta: scala.collection.Map[String, Any]) =>
meta.foreach(kv => buffer.put(kv._1, kv._2))
val stage2 = new VectorBuilder[Any]
fields.foreach { case (f, jsonLabel, default) =>
stage2 += {
val value = buffer.get(f.label)
if (value == null) {
if (default == null)
cursor.requiredFieldError(jsonLabel, jsonLabel)
else default
} else value
}
val stage2 = new VectorBuilder[Any]
fields.foreach { case (f, jsonLabel, default) =>
stage2 += {
val value = buffer.get(f.label)
if (value == null) {
if (default == null)
cursor.requiredFieldError(jsonLabel, jsonLabel)
else default
} else value
}
const(stage2.result())
}
const(stage2.result())
}

def encodeValue(z: Z, out: JsonWriter): Unit =
override def encodeValue(z: Z, out: JsonWriter): Unit =
encode(z, out, documentEncoders)

def decodeKey(in: JsonReader): Z =
override def decodeKey(in: JsonReader): Z =
in.decodeError("Cannot use products as keys")

def encodeKey(x: Z, out: JsonWriter): Unit =
override def encodeKey(x: Z, out: JsonWriter): Unit =
out.encodeError("Cannot use products as keys")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,7 @@ class SchemaVisitorJCodecTests() extends FunSuite {
)

val json = writeToString(unknownFieldRetentionExample)
println(s"json $json")
val expectedJson = """{foo:"foo",bar:"bar",unknownField1:"unknownString1",unknownField2:"unknownString2"}"""

val roundTripped = readFromString[UnknownFieldRetentionExample](json)
Expand Down
5 changes: 5 additions & 0 deletions sampleSpecs/unknownFieldRetention.smithy
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ $version: "2"
namespace smithy4s.example

use alloy#unknownDocumentFieldRetention
use alloy#unknownJsonFieldRetention

structure UnknownFieldRetentionExample {
foo: String
bar: String
@unknownDocumentFieldRetention
@unknownJsonFieldRetention
retainedUnknownFields: Document
}

Expand All @@ -16,6 +18,7 @@ structure DefaultUnknownFieldRetentionExample {
bar: String
@default
@unknownDocumentFieldRetention
@unknownJsonFieldRetention
retainedUnknownFields: Document
}

Expand All @@ -24,6 +27,7 @@ structure RequiredUnknownFieldRetentionExample {
bar: String
@required
@unknownDocumentFieldRetention
@unknownJsonFieldRetention
retainedUnknownFields: Document
}

Expand All @@ -33,5 +37,6 @@ structure DefaultRequiredUnknownFieldRetentionExample {
@default
@required
@unknownDocumentFieldRetention
@unknownJsonFieldRetention
retainedUnknownFields: Document
}

0 comments on commit a7de7a5

Please sign in to comment.