All Downloads are FREE. Search and download functionalities are using the official Maven repository.

japgolly.webapputil.indexeddb.ValueCodec.scala Maven / Gradle / Ivy

There is a newer version: 2.0.0-RC12
Show newest version
package japgolly.webapputil.indexeddb

import japgolly.scalajs.react.{AsyncCallback, CallbackTo}
import japgolly.webapputil.binary._
import java.util.UUID
import org.scalajs.dom.IDBValue
import scala.reflect.ClassTag
import scala.scalajs.js
import scala.scalajs.js.typedarray.ArrayBuffer

final case class ValueCodec[A](encode: A => CallbackTo[IDBValue],
                               decode: IDBValue => CallbackTo[A]) {

  def xmap[B](onDecode: A => B)(onEncode: B => A): ValueCodec[B] =
    // Delegating because decoding can fail and must be wrapped to be pure
    xmapSync(
      a => CallbackTo(onDecode(a)))(
      b => CallbackTo(onEncode(b)))

  def xmapSync[B](onDecode: A => CallbackTo[B])(onEncode: B => CallbackTo[A]): ValueCodec[B] =
    ValueCodec[B](
      encode = onEncode(_).flatMap(encode),
      decode = decode(_).flatMap(onDecode))

  def async: ValueCodec.Async[A] =
    ValueCodec.Async(
      encode = encode.andThen(_.asAsyncCallback),
      decode = decode.andThen(_.asAsyncCallback))

  type ThisIsBinary = ValueCodec[A] =:= ValueCodec[BinaryData]

  def compress(c: Compression)(implicit ev: ThisIsBinary): ValueCodec[BinaryData] =
    ev(this).xmap(c.decompressOrThrow)(c.compress)
}

object ValueCodec {

  lazy val binary: ValueCodec[BinaryData] =
    apply(
      encode = b => CallbackTo.pure(b.unsafeArrayBuffer),
      decode = d => CallbackTo(BinaryData.unsafeFromArrayBuffer(d.asInstanceOf[ArrayBuffer]))
    )

  lazy val boolean: ValueCodec[Boolean] =
    primative("Boolean")

  lazy val double: ValueCodec[Double] =
    primative("Double")

  lazy val int: ValueCodec[Int] =
    primative("Int")

  lazy val long: ValueCodec[Long] =
    string.xmap(_.toLong)(_.toString)

  def primative[A: ClassTag](name: String): ValueCodec[A] =
    apply(
      encode = a => CallbackTo.pure(a),
      decode = d => CallbackTo(
        (d: Any) match {
          case a: A => a
          case x    => throw new js.JavaScriptException(s"Invalid IDB value found. $name expected, got: $x")
        }
      )
    )

  lazy val string: ValueCodec[String] =
    primative("String")

  lazy val uuid: ValueCodec[UUID] =
    string.xmap(UUID.fromString)(_.toString)

  // ===================================================================================================================

  final case class Async[A](encode: A => AsyncCallback[IDBValue],
                            decode: IDBValue => AsyncCallback[A]) {

    def xmap[B](onDecode: A => B)(onEncode: B => A): Async[B] =
      // Delegating because decoding can fail and must be wrapped to be pure
      xmapAsync(
        a => AsyncCallback.delay(onDecode(a)))(
        b => AsyncCallback.delay(onEncode(b)))

    def xmapAsync[B](onDecode: A => AsyncCallback[B])(onEncode: B => AsyncCallback[A]): Async[B] =
      Async[B](
        encode = onEncode(_).flatMap(encode),
        decode = decode(_).flatMap(onDecode))

    type ThisIsBinary = Async[A] =:= Async[BinaryData]

    def xmapBinaryFormat[B](fmt: BinaryFormat[B])(implicit ev: ThisIsBinary): Async[B] =
      ev(this).xmapAsync(fmt.decode)(fmt.encode)
  }

  object Async {

    lazy val binary: ValueCodec.Async[BinaryData] =
      ValueCodec.binary.async

    def binary[A](fmt: BinaryFormat[A]): ValueCodec.Async[A] =
      binary.xmapBinaryFormat(fmt)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy