Skip to content

Commit

Permalink
feat(generic): better generic sealed trait log encoder (#79)
Browse files Browse the repository at this point in the history
  • Loading branch information
Honza Strnad committed Mar 30, 2020
1 parent 0424c93 commit fbd1f16
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 20 deletions.
60 changes: 43 additions & 17 deletions generic/src/main/scala/slog4s/generic/internal/Common.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,46 @@ package slog4s.generic.internal
import magnolia.{CaseClass, SealedTrait}
import slog4s.{LogEncoder, StructureBuilder}

private final class CaseClassEncoder[T](caseClass: CaseClass[LogEncoder, T])
extends LogEncoder[T] {
override def encode[O](
value: T
)(implicit structureBuilder: StructureBuilder[O]): O = {
if (caseClass.isObject) {
structureBuilder.string(caseClass.typeName.short)
} else {
structureBuilder.map(encodeFields(value))
}
}

def encodeSealedMember[O](
value: T
)(implicit structureBuilder: StructureBuilder[O]): O = {
if (caseClass.isObject) {
structureBuilder.string(caseClass.typeName.short)
} else {
structureBuilder.structure(
caseClass.typeName.short,
encodeFields(value)
)
}
}

private def encodeFields[O](
value: T
)(implicit structureBuilder: StructureBuilder[O]): Map[String, O] = {
val params = caseClass.parameters.map { param =>
param.label -> param.typeclass.encode(param.dereference(value))
}
params.toMap
}
}

private[generic] object Common {
def combine[T](
caseClass: CaseClass[LogEncoder, T]
): LogEncoder[T] = {
new LogEncoder[T] {
override def encode[O](
value: T
)(implicit structureBuilder: StructureBuilder[O]): O = {
if (caseClass.isObject) {
structureBuilder.string(caseClass.typeName.short)
} else {
val params = caseClass.parameters.map { param =>
param.label -> param.typeclass.encode(param.dereference(value))
}
structureBuilder.structure(caseClass.typeName.short, params.toMap)
}
}
}
}
): LogEncoder[T] = new CaseClassEncoder[T](caseClass)

def dispatch[T](
sealedTrait: SealedTrait[LogEncoder, T]
): LogEncoder[T] = {
Expand All @@ -30,7 +51,12 @@ private[generic] object Common {
value: T
)(implicit structureBuilder: StructureBuilder[O]): O = {
sealedTrait.dispatch(value) { subtype =>
subtype.typeclass.encode(subtype.cast(value))
subtype.typeclass match {
case caseClassEncoder: CaseClassEncoder[subtype.SType] =>
caseClassEncoder.encodeSealedMember(subtype.cast(value))
case other => other.encode(subtype.cast(value))
}

}
}
}
Expand Down
8 changes: 6 additions & 2 deletions generic/src/test/scala/slog4s/generic/DerivationTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,11 @@ class DerivationTest extends AnyFunSpec {
}
it("sealed trait") {
import TestClasses.SealedTrait
test[SealedTrait](SealedTrait.Value(42), Map("value" -> 42))
test(SealedTrait.Value(42), Map("value" -> 42))
test[SealedTrait](
SealedTrait.Value(42),
Map("value" -> 42, "type" -> "Value")
)
}
it("sealed trait (case object)") {
sealed trait Foo
Expand Down Expand Up @@ -103,7 +107,7 @@ class DerivationTest extends AnyFunSpec {
override def string(value: String): Any = value

override def structure(name: String, attributes: Map[String, Any]): Any =
attributes
attributes.updated("type", name)

override def option(value: Option[Any]): Any = value.orNull

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ private[slf4j] object MarkerStructureBuilder {
override def string(value: String): Any = value

override def structure(name: String, attributes: Map[String, Any]): Any =
attributes.asJava
(attributes.updated("type", name)).asJava

override def option(value: Option[Any]): Any = value.orNull

Expand Down

0 comments on commit fbd1f16

Please sign in to comment.