diff --git a/zio-schema-avro/shared/src/main/scala/zio/schema/codec/AvroCodec.scala b/zio-schema-avro/shared/src/main/scala/zio/schema/codec/AvroCodec.scala index 4359d0a21..dd4e5e0b0 100644 --- a/zio-schema-avro/shared/src/main/scala/zio/schema/codec/AvroCodec.scala +++ b/zio-schema-avro/shared/src/main/scala/zio/schema/codec/AvroCodec.scala @@ -423,6 +423,12 @@ object AvroCodec { .toEither .left .map(e => DecodeError.MalformedFieldWithPath(Chunk.empty, e.getMessage)) + case StandardType.CurrencyType => + Try(value.asInstanceOf[Utf8]) + .flatMap(c => Try(java.util.Currency.getInstance(c.toString))) + .toEither + .left + .map(e => DecodeError.MalformedFieldWithPath(Chunk.empty, e.getMessage)) } private def decodeMap(value: Any, schema: Schema.Map[Any, Any]) = { @@ -893,6 +899,8 @@ object AvroCodec { case StandardType.ZonedDateTimeType => val zonedDateTime = a.asInstanceOf[java.time.ZonedDateTime] zonedDateTime.toString + case StandardType.CurrencyType => + a.asInstanceOf[java.util.Currency].getCurrencyCode } private def encodeSequence[A](schema: Schema[A], v: Chunk[A]): Any = { diff --git a/zio-schema-avro/shared/src/main/scala/zio/schema/codec/AvroPropMarker.scala b/zio-schema-avro/shared/src/main/scala/zio/schema/codec/AvroPropMarker.scala index 9217fcced..df0aaab24 100644 --- a/zio-schema-avro/shared/src/main/scala/zio/schema/codec/AvroPropMarker.scala +++ b/zio-schema-avro/shared/src/main/scala/zio/schema/codec/AvroPropMarker.scala @@ -25,6 +25,10 @@ object AvroPropMarker { override def propName: String = "zio.schema.codec.avro.either" } + case object CurrencyWrapper extends AvroPropMarker { + override def propName: String = "zio.schema.codec.avro.currency" + } + final case class DurationChronoUnit(chronoUnit: ChronoUnit) extends AvroPropMarker { override def propName: String = DurationChronoUnit.propName override def value: Any = chronoUnit.name() diff --git a/zio-schema-avro/shared/src/main/scala/zio/schema/codec/AvroSchemaCodec.scala b/zio-schema-avro/shared/src/main/scala/zio/schema/codec/AvroSchemaCodec.scala index bb64aeac7..2c458455f 100644 --- a/zio-schema-avro/shared/src/main/scala/zio/schema/codec/AvroSchemaCodec.scala +++ b/zio-schema-avro/shared/src/main/scala/zio/schema/codec/AvroSchemaCodec.scala @@ -378,6 +378,12 @@ object AvroSchemaCodec extends AvroSchemaCodec { .create(SchemaAvro.Type.STRING) .addMarkerProp(StringDiscriminator(StringType.ZoneDateTime)) ) + case StandardType.CurrencyType => + Right( + SchemaAvro + .create(SchemaAvro.Type.STRING) + .addMarkerProp(CurrencyWrapper) + ) } case Optional(codec, _) => for { diff --git a/zio-schema-bson/shared/src/main/scala/zio/schema/codec/BsonSchemaCodec.scala b/zio-schema-bson/shared/src/main/scala/zio/schema/codec/BsonSchemaCodec.scala index 4e57c31fe..e443937e3 100644 --- a/zio-schema-bson/shared/src/main/scala/zio/schema/codec/BsonSchemaCodec.scala +++ b/zio-schema-bson/shared/src/main/scala/zio/schema/codec/BsonSchemaCodec.scala @@ -369,6 +369,7 @@ object BsonSchemaCodec { case StandardType.ZoneIdType => BsonCodec.zoneId.asInstanceOf[BsonCodec[A]] //BsonCodec[java.time.ZoneId] case StandardType.ZoneOffsetType => BsonCodec.zoneOffset.asInstanceOf[BsonCodec[A]] //BsonCodec[java.time.ZoneOffset] + // case StandardType.CurrencyType => BsonCodec.currency.asInstanceOf[BsonCodec[A]] //BsonCodec[java.util.Currency] // TODO: needs implementation in zio-bson } } diff --git a/zio-schema-msg-pack/shared/src/main/scala/zio/schema/codec/MessagePackDecoder.scala b/zio-schema-msg-pack/shared/src/main/scala/zio/schema/codec/MessagePackDecoder.scala index 821baf4b6..a7475d7d8 100644 --- a/zio-schema-msg-pack/shared/src/main/scala/zio/schema/codec/MessagePackDecoder.scala +++ b/zio-schema-msg-pack/shared/src/main/scala/zio/schema/codec/MessagePackDecoder.scala @@ -278,7 +278,8 @@ private[codec] class MessagePackDecoder(bytes: Chunk[Byte]) { decodeString(path).map(OffsetDateTime.parse(_)) case StandardType.ZonedDateTimeType => decodeString(path).map(ZonedDateTime.parse(_)) - case _ => fail(path, s"Unsupported primitive type $standardType") + case StandardType.CurrencyType => decodeString(path).map(java.util.Currency.getInstance(_)) + case _ => fail(path, s"Unsupported primitive type $standardType") } private def decodeOptional[A](path: Path, schema: Schema.Optional[A]): Result[Option[A]] = diff --git a/zio-schema-msg-pack/shared/src/main/scala/zio/schema/codec/MessagePackEncoder.scala b/zio-schema-msg-pack/shared/src/main/scala/zio/schema/codec/MessagePackEncoder.scala index ca267d2fc..d5741d23a 100644 --- a/zio-schema-msg-pack/shared/src/main/scala/zio/schema/codec/MessagePackEncoder.scala +++ b/zio-schema-msg-pack/shared/src/main/scala/zio/schema/codec/MessagePackEncoder.scala @@ -242,6 +242,9 @@ private[codec] class MessagePackEncoder { case (StandardType.ZonedDateTimeType, v: ZonedDateTime) => packer.packString(v.toString) () + case (StandardType.CurrencyType, v: java.util.Currency) => + packer.packString(v.getCurrencyCode) + () case (_, _) => throw new NotImplementedError(s"No encoder for $standardType") } diff --git a/zio-schema-protobuf/shared/src/main/scala/zio/schema/codec/ProtobufCodec.scala b/zio-schema-protobuf/shared/src/main/scala/zio/schema/codec/ProtobufCodec.scala index 01a25f7fd..17611c509 100644 --- a/zio-schema-protobuf/shared/src/main/scala/zio/schema/codec/ProtobufCodec.scala +++ b/zio-schema-protobuf/shared/src/main/scala/zio/schema/codec/ProtobufCodec.scala @@ -95,6 +95,7 @@ object ProtobufCodec { case StandardType.OffsetTimeType => false case StandardType.OffsetDateTimeType => false case StandardType.ZonedDateTimeType => false + case StandardType.CurrencyType => false } } @@ -400,6 +401,8 @@ object ProtobufCodec { encodePrimitive(fieldNumber, StandardType.StringType, v.toString) case (StandardType.ZonedDateTimeType, v: ZonedDateTime) => encodePrimitive(fieldNumber, StandardType.StringType, v.format(DateTimeFormatter.ISO_ZONED_DATE_TIME)) + case (StandardType.CurrencyType, v: java.util.Currency) => + encodePrimitive(fieldNumber, StandardType.StringType, v.getCurrencyCode) case (_, _) => throw new NotImplementedError(s"No encoder for $standardType") } @@ -585,7 +588,8 @@ object ProtobufCodec { OffsetDateTime.parse(stringDecoder(context)) case StandardType.ZonedDateTimeType => ZonedDateTime.parse(stringDecoder(context)) - case st => fail(context, s"Unsupported primitive type $st") + case StandardType.CurrencyType => java.util.Currency.getInstance(stringDecoder(context)) + case st => fail(context, s"Unsupported primitive type $st") } override protected def startCreatingRecord(context: DecoderContext, record: Schema.Record[_]): DecoderContext = diff --git a/zio-schema-thrift/shared/src/main/scala/zio/schema/codec/ThriftCodec.scala b/zio-schema-thrift/shared/src/main/scala/zio/schema/codec/ThriftCodec.scala index 411b5c558..f43dfeb97 100644 --- a/zio-schema-thrift/shared/src/main/scala/zio/schema/codec/ThriftCodec.scala +++ b/zio-schema-thrift/shared/src/main/scala/zio/schema/codec/ThriftCodec.scala @@ -355,6 +355,8 @@ object ThriftCodec { p.writeString(v.toString) case (StandardType.ZonedDateTimeType, v: ZonedDateTime) => p.writeString(v.toString) + case (StandardType.CurrencyType, v: java.util.Currency) => + p.writeString(v.getCurrencyCode) case (_, _) => fail(s"No encoder for $standardType") } @@ -408,6 +410,7 @@ object ThriftCodec { case StandardType.OffsetTimeType => TType.STRING case StandardType.OffsetDateTimeType => TType.STRING case StandardType.ZonedDateTimeType => TType.STRING + case StandardType.CurrencyType => TType.STRING case _ => TType.VOID } @@ -572,6 +575,8 @@ object ThriftCodec { OffsetDateTime.parse(decodeString(context.path)) case StandardType.ZonedDateTimeType => ZonedDateTime.parse(decodeString(context.path)) + case StandardType.CurrencyType => + java.util.Currency.getInstance(decodeString(context.path)) case _ => fail(context, s"Unsupported primitive type $typ") } diff --git a/zio-schema-zio-test/shared/src/main/scala/zio/schema/DeriveGen.scala b/zio-schema-zio-test/shared/src/main/scala/zio/schema/DeriveGen.scala index c2cf96731..4f54d35bf 100644 --- a/zio-schema-zio-test/shared/src/main/scala/zio/schema/DeriveGen.scala +++ b/zio-schema-zio-test/shared/src/main/scala/zio/schema/DeriveGen.scala @@ -513,6 +513,7 @@ object DeriveGen { case StandardType.OffsetTimeType => Gen.offsetTime case StandardType.OffsetDateTimeType => Gen.offsetDateTime case StandardType.ZonedDateTimeType => Gen.zonedDateTime + // case StandardType.CurrencyType => Gen.currency // TODO: needs implementation in zio-test } gen.map(_.asInstanceOf[A]) diff --git a/zio-schema/shared/src/main/scala/zio/schema/Differ.scala b/zio-schema/shared/src/main/scala/zio/schema/Differ.scala index 4f4dee67f..f3eba3faa 100644 --- a/zio-schema/shared/src/main/scala/zio/schema/Differ.scala +++ b/zio-schema/shared/src/main/scala/zio/schema/Differ.scala @@ -4,12 +4,10 @@ import java.math.{ BigInteger, MathContext } import java.time.temporal.{ ChronoField, ChronoUnit } import java.time.{ DayOfWeek, - Duration => JDuration, Instant, LocalDate, LocalDateTime, LocalTime, - Month => JMonth, MonthDay, OffsetDateTime, OffsetTime, @@ -18,13 +16,13 @@ import java.time.{ YearMonth, ZoneId, ZoneOffset, + Duration => JDuration, + Month => JMonth, ZonedDateTime => JZonedDateTime } -import java.util.UUID - +import java.util.{ Currency, UUID } import scala.annotation.nowarn import scala.collection.immutable.ListMap - import zio.schema.diff.Edit import zio.{ Chunk, ChunkBuilder } @@ -255,6 +253,7 @@ object Differ { case Schema.Primitive(StandardType.OffsetDateTimeType, _) => offsetDateTime case Schema.Primitive(StandardType.ZonedDateTimeType, _) => zonedDateTime case Schema.Primitive(StandardType.ZoneOffsetType, _) => zoneOffset + case Schema.Primitive(StandardType.CurrencyType, _) => currency case Schema.Tuple2(leftSchema, rightSchema, _) => fromSchema(leftSchema) <*> fromSchema(rightSchema) case Schema.Optional(schema, _) => fromSchema(schema).optional case Schema.Sequence(schema, g, f, _, _) => @@ -522,6 +521,13 @@ object Differ { } } + val currency: Differ[Currency] = + (thisValue: Currency, thatValue: Currency) => + if (thisValue == thatValue) + Patch.identical + else + Patch.Currency(thatValue) + def tuple[A, B](left: Differ[A], right: Differ[B]): Differ[(A, B)] = (thisValue: (A, B), thatValue: (A, B)) => (thisValue, thatValue) match { diff --git a/zio-schema/shared/src/main/scala/zio/schema/Patch.scala b/zio-schema/shared/src/main/scala/zio/schema/Patch.scala index 5d5e7e70b..47af8a221 100644 --- a/zio-schema/shared/src/main/scala/zio/schema/Patch.scala +++ b/zio-schema/shared/src/main/scala/zio/schema/Patch.scala @@ -206,6 +206,13 @@ object Patch { override def invert: Patch[JZonedDateTime] = ZonedDateTime(localDateTimeDiff.invert, zoneIdDiff.invert) } + final case class Currency(currencyCodeDiff: java.util.Currency) extends Patch[java.util.Currency] { + override def patch(input: java.util.Currency): scala.Either[String, java.util.Currency] = + Right(input) + + override def invert: Patch[java.util.Currency] = Currency(currencyCodeDiff) + } + final case class Tuple[A, B](leftDifference: Patch[A], rightDifference: Patch[B]) extends Patch[(A, B)] { override def isIdentical: Boolean = leftDifference.isIdentical && rightDifference.isIdentical diff --git a/zio-schema/shared/src/main/scala/zio/schema/StandardType.scala b/zio-schema/shared/src/main/scala/zio/schema/StandardType.scala index 8be43c8a4..e5e53d53f 100644 --- a/zio-schema/shared/src/main/scala/zio/schema/StandardType.scala +++ b/zio-schema/shared/src/main/scala/zio/schema/StandardType.scala @@ -3,9 +3,10 @@ package zio.schema import java.math.BigInteger import java.time import java.time._ - import zio.Chunk +import java.util.Currency + sealed trait StandardType[A] extends Ordering[A] { self => def tag: String def defaultValue: Either[String, A] @@ -51,6 +52,7 @@ object StandardType { final val OFFSET_DATE_TIME = "offsetDateTime" final val ZONED_DATE_TIME = "zonedDateTime" final val UUID = "uuid" + final val CURRENCY = "currency" } def fromString(tag: String): Option[StandardType[_]] = @@ -85,6 +87,7 @@ object StandardType { case Tags.OFFSET_DATE_TIME => Some(OffsetDateTimeType) case Tags.ZONED_DATE_TIME => Some(ZonedDateTimeType) case Tags.UUID => Some(UUIDType) + case Tags.CURRENCY => Some(CurrencyType) } def apply[A](implicit standardType: StandardType[A]): StandardType[A] = standardType @@ -290,4 +293,15 @@ object StandardType { override def compare(x: ZonedDateTime, y: ZonedDateTime): Int = x.compareTo(y) } + + implicit object CurrencyType extends StandardType[java.util.Currency] { + + override def tag: String = Tags.CURRENCY + + override def defaultValue: Either[String, Currency] = + Right(java.util.Currency.getInstance(java.util.Locale.getDefault)) + + override def compare(x: Currency, y: Currency): Int = x.getCurrencyCode.compareTo(y.getCurrencyCode) + } + }