Skip to content

Commit

Permalink
Adjust to changed internals
Browse files Browse the repository at this point in the history
  • Loading branch information
kciesielski committed May 27, 2024
1 parent 22f4abb commit 3678d32
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ class CreateDerivedEnumerationPickler[T: ClassTag](

override lazy val writer: Writer[T] =
new TaggedWriter.Node[T](childReadWriters.map(_._2.asInstanceOf[TaggedWriter[T]]): _*) {
override def findWriter(v: Any): (String, ObjectWriter[T]) =
val (t, writer) = super.findWriter(v)
override def findWriterWithKey(v: Any): (String, String, ObjectWriter[T]) =
val (tagKey, tagValue, writer) = super.findWriterWithKey(v)
// Here our custom encoding transforms the value of a singleton object
val overriddenTag = encode(v.asInstanceOf[T]).toString
(overriddenTag, writer)
(tagKey, overriddenTag, writer)
}
}
new Pickler[T](tapirPickle, schema)
Expand All @@ -77,10 +77,14 @@ class CreateDerivedEnumerationPickler[T: ClassTag](
// https://github.com/softwaremill/tapir/issues/3192
override lazy val writer = annotate[C](
SingletonWriter[C](null.asInstanceOf[C]),
Annotator.defaultTagKey, // not used in enumerations
upickleMacros.tagName[C],
Annotator.Checker.Val(upickleMacros.getSingleton[C])
)
override lazy val reader = annotate[C](SingletonReader[C](upickleMacros.getSingleton[C]), upickleMacros.tagName[C])
override lazy val reader = annotate[C](SingletonReader[C](upickleMacros.getSingleton[C]),
Annotator.defaultTagKey, // not used in enumerations
upickleMacros.tagName[C]
)
}
(pickle.reader, pickle.writer)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,8 @@ import scala.quoted.*
import scala.reflect.ClassTag
import scala.util.{Failure, NotGiven, Success, Try}
import java.math.{BigDecimal as JBigDecimal, BigInteger as JBigInteger}
import macros.*

import scala.annotation.implicitNotFound
import sttp.tapir.json.pickler.SubtypeDiscriminator
import sttp.tapir.generic.Configuration

object Pickler:
Expand Down Expand Up @@ -56,7 +54,7 @@ object Pickler:
type ParamV = V
val subtypeDiscriminator: SubtypeDiscriminator[T] = new CustomSubtypeDiscriminator[T] {
type V = ParamV
override lazy val fieldName = c.discriminator.getOrElse(SubtypeDiscriminator.DefaultFieldName)
override lazy val fieldName = c.discriminator
override def extractor = extractorFn
override def asString = asStringFn
override lazy val mapping = paramMapping
Expand Down Expand Up @@ -353,8 +351,6 @@ object Pickler:
val childDefaults = enrichedChildSchemas.map(_.default.map(_._1))

val tapirPickle = new TapirPickle[T] {
override def tagName = config.discriminator.getOrElse(super.tagName)

override lazy val writer: Writer[T] =
macroProductW[T](
schema,
Expand All @@ -367,7 +363,8 @@ object Pickler:
schema,
childPicklers.map([a] => (obj: a) => obj.asInstanceOf[Pickler[a]].innerUpickle.reader),
childDefaults,
product
product,
config
)
}
Pickler[T](tapirPickle, schema)
Expand All @@ -387,7 +384,6 @@ object Pickler:
): Pickler[T] =
val childPicklersList = childPicklers.productIterator.toList.asInstanceOf[List[Pickler[_ <: T]]]
val tapirPickle = new TapirPickle[T] {
override def tagName = subtypeDiscriminator.fieldName
override lazy val writer: Writer[T] =
macroSumW[T](
childPicklersList,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package sttp.tapir.json.pickler

import sttp.tapir.generic.Configuration
import upickle.core.Annotator

final case class PicklerConfiguration(genericDerivationConfig: Configuration) {
export genericDerivationConfig.{toEncodedName, discriminator, toDiscriminatorValue}
export genericDerivationConfig.{toEncodedName, toDiscriminatorValue}

def discriminator: String = genericDerivationConfig.discriminator.getOrElse(Annotator.defaultTagKey)

def withSnakeCaseMemberNames: PicklerConfiguration = PicklerConfiguration(genericDerivationConfig.withSnakeCaseMemberNames)
def withScreamingSnakeCaseMemberNames: PicklerConfiguration = PicklerConfiguration(
Expand Down Expand Up @@ -32,6 +35,6 @@ final case class PicklerConfiguration(genericDerivationConfig: Configuration) {

object PicklerConfiguration {
given default: PicklerConfiguration = PicklerConfiguration(
Configuration.default.copy(discriminator = Some(SubtypeDiscriminator.DefaultFieldName))
Configuration.default.copy(discriminator = Some(Annotator.defaultTagKey))
)
}
25 changes: 17 additions & 8 deletions json/pickler/src/main/scala/sttp/tapir/json/pickler/Readers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,17 @@ private[pickler] trait Readers extends ReadersVersionSpecific with UpickleHelper
override def findReader(s: String) = if (s == leafTagValue) r else null
}

override def annotate[V](rw: Reader[V], n: String) = {
LeafWrapper(new TaggedReader.Leaf[V](n, rw), rw, n)
override def annotate[V](rw: Reader[V], key: String, value: String) = {
LeafWrapper(new TaggedReader.Leaf[V](key, value, rw), rw, value)
}

inline def macroProductR[T](schema: Schema[T], childReaders: Tuple, childDefaults: List[Option[Any]], m: Mirror.ProductOf[T]): Reader[T] =
inline def macroProductR[T](
schema: Schema[T],
childReaders: Tuple,
childDefaults: List[Option[Any]],
m: Mirror.ProductOf[T],
config: PicklerConfiguration
): Reader[T] =
val schemaFields = schema.schemaType.asInstanceOf[SchemaType.SProduct[T]].fields

val reader = new CaseClassReadereader[T](upickleMacros.paramsCount[T], upickleMacros.checkErrorMissingKeysCount[T]()) {
Expand All @@ -44,8 +50,9 @@ private[pickler] trait Readers extends ReadersVersionSpecific with UpickleHelper
}
}

inline if upickleMacros.isSingleton[T] then annotate[T](SingletonReader[T](upickleMacros.getSingleton[T]), upickleMacros.tagName[T])
else if upickleMacros.isMemberOfSealedHierarchy[T] then annotate[T](reader, upickleMacros.tagName[T])
inline if upickleMacros.isSingleton[T] then
annotate[T](SingletonReader[T](upickleMacros.getSingleton[T]), config.discriminator, upickleMacros.tagName[T])
else if upickleMacros.isMemberOfSealedHierarchy[T] then annotate[T](reader, config.discriminator, upickleMacros.tagName[T])
else reader

inline def macroSumR[T](childPicklers: List[Pickler[_]], subtypeDiscriminator: SubtypeDiscriminator[T]): Reader[T] =
Expand All @@ -60,25 +67,27 @@ private[pickler] trait Readers extends ReadersVersionSpecific with UpickleHelper
.map { case (k, v) => (k, v.innerUpickle.reader) }
.map {
case (k, leaf) if leaf.isInstanceOf[LeafWrapper[_]] =>
TaggedReader.Leaf[T](discriminator.asString(k), leaf.asInstanceOf[LeafWrapper[_]].r.asInstanceOf[Reader[T]])
TaggedReader
.Leaf[T](discriminator.fieldName, discriminator.asString(k), leaf.asInstanceOf[LeafWrapper[_]].r.asInstanceOf[Reader[T]])
case (_, otherKindOfReader) =>
otherKindOfReader
}

new TaggedReader.Node[T](readersFromMapping.asInstanceOf[Seq[TaggedReader[T]]]: _*)
new TaggedReader.Node[T](discriminator.fieldName, readersFromMapping.asInstanceOf[Seq[TaggedReader[T]]]: _*)

case discriminator: DefaultSubtypeDiscriminator[T] =>
val readers = childPicklers.map(cp => {
(cp.schema.name, cp.innerUpickle.reader) match {
case (Some(sName), wrappedReader: Readers#LeafWrapper[_]) =>
TaggedReader.Leaf[T](
discriminator.fieldName,
discriminator.toValue(sName),
wrappedReader.r.asInstanceOf[Reader[T]]
)
case _ =>
cp.innerUpickle.reader.asInstanceOf[Reader[T]]
}
})
Reader.merge(readers: _*)
Reader.merge(subtypeDiscriminator.fieldName, readers: _*)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ import sttp.tapir.Schema.SName
private[pickler] sealed trait SubtypeDiscriminator[T]:
def fieldName: String

object SubtypeDiscriminator:
val DefaultFieldName = "$type"

/** Describes non-standard encoding/decoding for subtypes in sealed hierarchies. Allows specifying an extractor function, for example to
* read subtype discriminator from a field. Requires also mapping in the opposite direction, to specify how to read particular
* discriminator values into concrete subtype picklers.
Expand All @@ -27,4 +24,4 @@ private[pickler] case class DefaultSubtypeDiscriminator[T](fieldName: String, to

private[pickler] object DefaultSubtypeDiscriminator:
def apply[T](config: PicklerConfiguration): DefaultSubtypeDiscriminator[T] =
new DefaultSubtypeDiscriminator[T](config.discriminator.getOrElse(SubtypeDiscriminator.DefaultFieldName), config.toDiscriminatorValue)
new DefaultSubtypeDiscriminator[T](config.discriminator, config.toDiscriminatorValue)
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,14 @@ private[pickler] trait Writers extends WritersVersionSpecific with UpickleHelper
inline if upickleMacros.isMemberOfSealedHierarchy[T] && !isEnumeration[T] then
annotate[T](
writer,
config.discriminator,
schema.name.map(config.toDiscriminatorValue).getOrElse(upickleMacros.tagName[T]),
Annotator.Checker.Cls(implicitly[ClassTag[T]].runtimeClass)
) // tagName is responsible for extracting the @tag annotation meaning the discriminator value
else if upickleMacros.isSingleton[T]
then // moved after "if MemberOfSealed" to handle case objects in hierarchy as case classes - with discriminator, for consistency
// here we handle enums
annotate[T](SingletonWriter[T](null.asInstanceOf[T]), upickleMacros.tagName[T], Annotator.Checker.Val(upickleMacros.getSingleton[T]))
annotate[T](SingletonWriter[T](null.asInstanceOf[T]), config.discriminator, upickleMacros.tagName[T], Annotator.Checker.Val(upickleMacros.getSingleton[T]))
else writer

inline def macroSumW[T: ClassTag](childPicklers: => List[Pickler[? <: T]], subtypeDiscriminator: SubtypeDiscriminator[T])(using
Expand All @@ -84,14 +85,14 @@ private[pickler] trait Writers extends WritersVersionSpecific with UpickleHelper
val writers: List[TaggedWriter[_ <: T]] = childPicklers.map(_.innerUpickle.writer.asInstanceOf[TaggedWriter[_ <: T]])

new TaggedWriter.Node[T](writers: _*) {
override def findWriter(v: Any): (String, ObjectWriter[T]) = {
override def findWriterWithKey(v: Any): (String, String, ObjectWriter[T]) = {
subtypeDiscriminator match {
case discriminator: CustomSubtypeDiscriminator[T] =>
val (tag, w) = super.findWriter(v)
val (tagKey, tagValue, w) = super.findWriterWithKey(v)
val overriddenTag = discriminator.writeUnsafe(v) // here we use our discirminator instead of uPickle's
(overriddenTag, w)
(tagKey, overriddenTag, w)
case _ =>
super.findWriter(v)
super.findWriterWithKey(v)
}
}
}

0 comments on commit 3678d32

Please sign in to comment.