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

Improve protobuf support for sealed #568

Merged
merged 11 commits into from
Jul 9, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ private[protobufs] trait ProtobufTransformerImplicitsLowPriorityImplicits1 { thi

// com.google.protobuf.ByteString

// FIXME (2.0.0 cleanup): ByteString should be TotallyBuildIterable

/** @since 0.8.0 */
implicit def totalTransformerFromByteStringToByteCollection[Coll[A] <: IterableOnce[A]](implicit
factory: Factory[Byte, Coll[Byte]]
Expand All @@ -92,6 +94,8 @@ private[protobufs] trait ProtobufTransformerImplicitsLowPriorityImplicits1 { thi

// com.google.protobuf.wrappers.BytesValue

// FIXME (2.0.0 cleanup): BytesValue should be TotallyBuildIterable

/** @since 0.8.0 */
implicit def totalTransformerFromBytesValueToByteCollection[Coll[A] <: IterableOnce[A]](implicit
factory: Factory[Byte, Coll[Byte]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ trait ProtobufsPartialTransformerImplicits extends ProtobufsPartialTransformerIm
implicit def partialTransformerFromEmptyOneOfInstance[From <: scalapb.GeneratedOneof { type ValueType = Nothing }, To]
: PartialTransformer[From, To] =
PartialTransformer(_ => partial.Result.fromEmpty)

/** @since 1.3.0 */
implicit def partialTransformerFromEmptySealedOneOfInstance[From <: scalapb.GeneratedSealedOneof with Singleton, To]
: PartialTransformer[From, To] =
PartialTransformer(_ => partial.Result.fromEmpty)

/** @since 1.3.0 */
implicit def partialTransformerFromUnrecognizedEnumInstance[From <: scalapb.UnrecognizedEnum, To]
: PartialTransformer[From, To] =
PartialTransformer(_ => partial.Result.fromEmpty)
}

private[protobufs] trait ProtobufsPartialTransformerImplicitsLowPriorityImplicits1 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,29 @@ import scala.collection.compat.immutable.ArraySeq

class ProtobufBuildInSpec extends ChimneySpec {

test("transform com.google.protobuf.empty to Unit") {
group("com.google.protobuf.empty") {
val protobuf = pb.wrappers
.WithEmpty(Some(com.google.protobuf.empty.Empty.of()))
val domain = wrappers.WithEmpty(())

locally {
implicit val cfg = TransformerConfiguration.default.enableImplicitConflictResolution(PreferTotalTransformer)
protobuf.transformIntoPartial[wrappers.WithEmpty].asOption ==> Some(domain)
test("totally transform from Unit") {
domain.transformInto[pb.wrappers.WithEmpty] ==> protobuf
domain.transformIntoPartial[pb.wrappers.WithEmpty].asOption ==> Some(protobuf)
}
locally {
implicit val cfg = TransformerConfiguration.default.enableImplicitConflictResolution(PreferPartialTransformer)
protobuf.transformIntoPartial[wrappers.WithEmpty].asOption ==> None

test("partially transform into Unit (with conflict resolution provided)") {
locally {
implicit val cfg = TransformerConfiguration.default.enableImplicitConflictResolution(PreferTotalTransformer)
protobuf.transformIntoPartial[wrappers.WithEmpty].asOption ==> Some(domain)
}
locally {
implicit val cfg = TransformerConfiguration.default.enableImplicitConflictResolution(PreferPartialTransformer)
protobuf.transformIntoPartial[wrappers.WithEmpty].asOption ==> None
}
}
domain.transformInto[pb.wrappers.WithEmpty] ==> protobuf
}

test("transform com.google.protobuf.wrappers to unwrapped values") {
group("com.google.protobuf.wrappers") {
val bytes = ArraySeq(0.toByte, 1.toByte)
val protobuf = pb.wrappers.Wrappers.of(
Some(com.google.protobuf.wrappers.BoolValue.of(true)),
Expand All @@ -55,11 +61,18 @@ class ProtobufBuildInSpec extends ChimneySpec {
200L,
"value"
)
protobuf.transformIntoPartial[wrappers.Wrappers].asOption ==> Some(domain)
domain.transformInto[pb.wrappers.Wrappers] ==> protobuf

test("totally transform from unwrapped values") {
domain.transformInto[pb.wrappers.Wrappers] ==> protobuf
domain.transformIntoPartial[pb.wrappers.Wrappers].asOption ==> Some(protobuf)
}

test("partially transform into unwrapped value") {
protobuf.transformIntoPartial[wrappers.Wrappers].asOption ==> Some(domain)
}
}

test("transform com.google.protobuf.duration to Duration and com.google.protobuf.timestamp to Instant") {
group("com.google.protobuf.duration/com.google.protobuf.timestamp") {
val protobuf = pb.wrappers.TimeInstances.of(
Some(com.google.protobuf.duration.Duration.of(10L, 0)),
Some(com.google.protobuf.duration.Duration.of(0L, 100)),
Expand All @@ -71,7 +84,13 @@ class ProtobufBuildInSpec extends ChimneySpec {
java.time.Instant.ofEpochSecond(12L, 34L)
)

protobuf.transformIntoPartial[wrappers.TimeInstances].asOption ==> Some(domain)
domain.transformIntoPartial[pb.wrappers.TimeInstances].asOption ==> Some(protobuf)
test("partially transform from Duration/Instant") {
// Duration.Infinite cannot be encoded as PB duration!
domain.transformIntoPartial[pb.wrappers.TimeInstances].asOption ==> Some(protobuf)
}

test("totally transform into Duration/Instant") {
protobuf.transformIntoPartial[wrappers.TimeInstances].asOption ==> Some(domain)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,57 @@ import io.scalaland.chimney.fixtures.{addressbook, order}

class ProtobufEnumSpec extends ChimneySpec {

test("transform enum represented as sealed trait hierarchy") {
group("PB enum") {

(addressbook.MOBILE: addressbook.PhoneType)
.transformInto[pb.addressbook.PhoneType] ==> pb.addressbook.PhoneType.MOBILE
(addressbook.HOME: addressbook.PhoneType).transformInto[pb.addressbook.PhoneType] ==> pb.addressbook.PhoneType.HOME
(addressbook.WORK: addressbook.PhoneType).transformInto[pb.addressbook.PhoneType] ==> pb.addressbook.PhoneType.WORK
test("totally transform from sealed trait hierarchy of case objects") {
(addressbook.MOBILE: addressbook.PhoneType)
.transformInto[pb.addressbook.PhoneType] ==> pb.addressbook.PhoneType.MOBILE
(addressbook.HOME: addressbook.PhoneType)
.transformInto[pb.addressbook.PhoneType] ==> pb.addressbook.PhoneType.HOME
(addressbook.WORK: addressbook.PhoneType)
.transformInto[pb.addressbook.PhoneType] ==> pb.addressbook.PhoneType.WORK
}

test("partially transform into sealed trait hierarchy of case objects (with manual handling of unrecognized)") {
(pb.addressbook.PhoneType.MOBILE: pb.addressbook.PhoneType)
.intoPartial[addressbook.PhoneType]
.withEnumCaseHandledPartial[pb.addressbook.PhoneType.Unrecognized](_ => partial.Result.fromEmpty)
.transform
.asOption ==> Some(addressbook.MOBILE)
(pb.addressbook.PhoneType.HOME: pb.addressbook.PhoneType)
.intoPartial[addressbook.PhoneType]
.withEnumCaseHandledPartial[pb.addressbook.PhoneType.Unrecognized](_ => partial.Result.fromEmpty)
.transform
.asOption ==> Some(addressbook.HOME)
(pb.addressbook.PhoneType.WORK: pb.addressbook.PhoneType)
.intoPartial[addressbook.PhoneType]
.withEnumCaseHandledPartial[pb.addressbook.PhoneType.Unrecognized](_ => partial.Result.fromEmpty)
.transform
.asOption ==> Some(addressbook.WORK)
(pb.addressbook.PhoneType.Unrecognized(0): pb.addressbook.PhoneType)
.intoPartial[addressbook.PhoneType]
.withEnumCaseHandledPartial[pb.addressbook.PhoneType.Unrecognized](_ => partial.Result.fromEmpty)
.transform
.asOption ==> None
}

test("partially transform into sealed trait hierarchy of case objects (handling Unrecognized with implicit)") {
// format: off
import protobufs._
// format: on

(pb.addressbook.PhoneType.MOBILE: pb.addressbook.PhoneType)
.transformIntoPartial[addressbook.PhoneType]
.asOption ==> Some(addressbook.MOBILE)
(pb.addressbook.PhoneType.HOME: pb.addressbook.PhoneType)
.transformIntoPartial[addressbook.PhoneType]
.asOption ==> Some(addressbook.HOME)
(pb.addressbook.PhoneType.WORK: pb.addressbook.PhoneType)
.transformIntoPartial[addressbook.PhoneType]
.asOption ==> Some(addressbook.WORK)
(pb.addressbook.PhoneType.Unrecognized(0): pb.addressbook.PhoneType)
.transformIntoPartial[addressbook.PhoneType]
.asOption ==> None
}
}
}
Loading
Loading