Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package meteor
package codec
import java.time.Instant
import java.{util => ju}
import cats._
import cats.implicits._
import meteor.errors._
import software.amazon.awssdk.services.dynamodb.model._
import scala.annotation.tailrec
import scala.collection.immutable
import scala.jdk.CollectionConverters._
/** Provides a decoding function for a given type, attempt to read an Java AttributeValue into type A
* @tparam A
*/
trait Decoder[A] {
/** Attempt to read an AttributeValue to a value of type A
* @param av Java attribute value object
* @return either a value of type A or a decoder error
*/
def read(av: AttributeValue): Either[DecoderError, A]
/** Attempt to read a Map of String and AttributeValue to a value of type A
* @param av Java Map of String and AttributeValue
* @return either a value of type A or a decoder error
*/
def read(av: java.util.Map[String, AttributeValue]): Either[DecoderError, A] =
read(AttributeValue.builder().m(av).build())
/** Create a new decoder given a transformation from A to either B or a decoder error
*
* @param f a function returning either a value or an error message
* @return a new Decoder of type B
*/
def emap[B](f: A => Either[DecoderError, B]): Decoder[B] =
Decoder.instance { av =>
this.read(av).flatMap(f)
}
}
object Decoder {
type FailureOr[U] = Either[DecoderError, U]
def apply[A](implicit dd: Decoder[A]): Decoder[A] = dd
/** Create a new instance of Decoder for type A. Helper methods and more examples can be found in
* [[meteor.syntax]].
*/
def instance[A](f: AttributeValue => Either[DecoderError, A]): Decoder[A] =
(av: AttributeValue) => f(av)
def failed[A](failure: DecoderError): Decoder[A] =
_ => failure.asLeft[A]
def const[A](a: A): Decoder[A] = _ => a.asRight[DecoderError]
implicit def monadForDynamoDbDecoder: Monad[Decoder] =
new Monad[Decoder] {
def pure[A](x: A): Decoder[A] =
Decoder.instance[A](_ => x.asRight[DecoderError])
def flatMap[A, B](fa: Decoder[A])(f: A => Decoder[B]): Decoder[B] =
(av: AttributeValue) => fa.read(av).flatMap(a => f(a).read(av))
def tailRecM[A, B](init: A)(f: A => Decoder[Either[A, B]]): Decoder[B] =
new Decoder[B] {
@tailrec
private def step(
av: AttributeValue,
a: A
): Either[DecoderError, B] =
f(a).read(av) match {
case l @ Left(_) => l.rightCast[B]
case Right(Left(a2)) => step(av, a2)
case Right(Right(b)) => Right(b)
}
def read(av: AttributeValue): Either[DecoderError, B] =
step(av, init)
}
}
implicit val dynamoDecoderForAttributeValue: Decoder[AttributeValue] =
Decoder.instance(av => av.asRight[DecoderError])
implicit def dynamoDecoderForSeq[A: Decoder]: Decoder[immutable.Seq[A]] =
Decoder.instance { av =>
if (av.hasL) {
av.l().asScala.toList.traverse[FailureOr, A](Decoder[A].read)
} else {
DecoderError.invalidTypeFailure(DynamoDbType.L).asLeft[immutable.Seq[A]]
}
}
implicit def dynamoDecoderForList[A: Decoder]: Decoder[List[A]] =
dynamoDecoderForSeq[A].map(_.toList)
implicit val dynamoDecoderForString: Decoder[String] =
Decoder.instance { av =>
if (Option(av.nul()).exists(_.booleanValue())) {
Right[DecoderError, String](null)
} else {
Option(av.s()).toRight(DecoderError.invalidTypeFailure(DynamoDbType.S))
}
}
implicit val dynamoDecoderForUUID: Decoder[ju.UUID] =
Decoder[String].flatMap {
str =>
Either
.catchNonFatal(ju.UUID.fromString(str))
.fold(
e => Decoder.failed(DecoderError(e.getMessage, e.some)),
ok => Decoder.const(ok)
)
}
implicit val dynamoDecoderForBoolean: Decoder[Boolean] =
Decoder.instance { av =>
Option(av.bool())
.map(_.booleanValue())
.toRight(DecoderError.invalidTypeFailure(DynamoDbType.BOOL))
}
implicit val dynamoDecoderForLong: Decoder[Long] =
Decoder.instance { av =>
Option(av.n())
.toRight(DecoderError.invalidTypeFailure(DynamoDbType.N))
.flatMap(n =>
Either.catchNonFatal(n.toLong).leftMap(e =>
DecoderError(e.getMessage, e.some)
)
)
}
implicit val dynamoDecoderForFloat: Decoder[Float] =
Decoder.instance { av =>
Option(av.n())
.toRight(DecoderError.invalidTypeFailure(DynamoDbType.N))
.flatMap(n =>
Either.catchNonFatal(n.toFloat).leftMap(e =>
DecoderError(e.getMessage, e.some)
)
)
}
implicit val dynamoDecoderForDouble: Decoder[Double] =
Decoder.instance { av =>
Option(av.n())
.toRight(DecoderError.invalidTypeFailure(DynamoDbType.N))
.flatMap(n =>
Either.catchNonFatal(n.toDouble).leftMap(e =>
DecoderError(e.getMessage, e.some)
)
)
}
implicit val dynamoDecoderForBigDecimal: Decoder[BigDecimal] =
Decoder.instance { av =>
Option(av.n())
.toRight(DecoderError.invalidTypeFailure(DynamoDbType.N))
.flatMap(n =>
Either.catchNonFatal(BigDecimal(n)).leftMap(e =>
DecoderError(e.getMessage, e.some)
)
)
}
implicit val dynamoDecoderForBigInt: Decoder[BigInt] =
Decoder.instance { av =>
Option(av.n())
.toRight(DecoderError.invalidTypeFailure(DynamoDbType.N))
.flatMap(n =>
Either.catchNonFatal(BigInt(n)).leftMap(e =>
DecoderError(e.getMessage, e.some)
)
)
}
implicit val dynamoDecoderForShort: Decoder[Short] =
Decoder.instance { av =>
Option(av.n())
.toRight(DecoderError.invalidTypeFailure(DynamoDbType.N))
.flatMap(n =>
Either.catchNonFatal(n.toShort).leftMap(e =>
DecoderError(e.getMessage, e.some)
)
)
}
implicit val dynamoDecoderForByte: Decoder[Byte] =
Decoder.instance { av =>
Option(av.n())
.toRight(DecoderError.invalidTypeFailure(DynamoDbType.N))
.flatMap(n =>
Either.catchNonFatal(n.toByte).leftMap(e =>
DecoderError(e.getMessage, e.some)
)
)
}
implicit val dynamoDecoderForInt: Decoder[Int] =
Decoder.instance { av =>
Option(av.n())
.toRight(DecoderError.invalidTypeFailure(DynamoDbType.N))
.flatMap(n =>
Either.catchNonFatal(n.toInt).leftMap(e =>
DecoderError(e.getMessage, e.some)
)
)
}
implicit val dynamoDecoderForInstant: Decoder[Instant] =
dynamoDecoderForLong.flatMap {
ms =>
Either
.catchNonFatal(Instant.ofEpochMilli(ms))
.fold(
e => Decoder.failed(DecoderError(e.getMessage, e.some)),
ok => Decoder.const(ok)
)
}
implicit def dynamoDecoderForMap[A: Decoder]: Decoder[Map[String, A]] =
Decoder.instance {
av =>
if (av.hasM) {
av.m()
.asScala
.map {
case (k, v) =>
(k, Decoder[A].read(v))
}
.foldLeft(Map.empty[String, A].asRight[DecoderError]) {
(fs, fx) =>
for {
s <- fs
x <- fx._2
} yield s + (fx._1 -> x)
}
} else {
DecoderError.invalidTypeFailure(DynamoDbType.M).asLeft[Map[
String,
A
]]
}
}
implicit val dynamoDecoderForUnit: Decoder[Unit] = const(())
implicit val dynamoDecoderForByteArray: Decoder[Array[Byte]] =
Decoder.instance {
av =>
Option(av.b()).map(_.asByteArray()).toRight(
DecoderError.invalidTypeFailure(DynamoDbType.B)
)
}
implicit val dynamoDecoderForSeqByteArray
: Decoder[immutable.Seq[Array[Byte]]] = {
Decoder.instance {
av =>
Option(av.bs()).map { bs =>
bs.asScala.toList.map(_.asByteArray())
}.toRight(
DecoderError.invalidTypeFailure(DynamoDbType.BS)
)
}
}
implicit val dynamoDecoderForListByteArray: Decoder[List[Array[Byte]]] =
dynamoDecoderForSeqByteArray.map(_.toList)
}