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

upickle.Implicits.scala Maven / Gradle / Ivy

The newest version!
package upickle

import scala.reflect.ClassTag
import scala.concurrent.duration.{FiniteDuration, Duration}
import acyclic.file
import TupleCodecs._
import Aliases._
import Fns._

object BaseCodecs {

//  implicit final val NothingR = R[Nothing]{case x => ???}
//  implicit final val NothingW = W[Nothing](x => ???)

  private[this] type JPF[T] = PartialFunction[Js.Value, T]
  private[this] final val booleanReaderFunc: JPF[Boolean] = validate("Boolean"){
    case Js.True => true
    case Js.False => false
  }
  implicit final val BooleanRW = RW[Boolean](
    if (_) Js.True else Js.False,
    booleanReaderFunc
  )
  implicit final val UnitRW = RW[Unit](
    _ => Js.Obj(),
    {case _ => ()}
  )

  private[this] def numericStringReaderFunc[T](func: String => T): JPF[T] = validate("Number"){
    case x: Js.Str => func(x.value)
  }
  private[this] def NumericStringReadWriter[T](func: String => T) = RW[T](
    x => Js.Str(x.toString),
    numericStringReaderFunc[T](func)
  )
  private[this] def numericReaderFunc[T: Numeric](func: Double => T, func2: String => T): JPF[T] = validate("Number"){
    case n @ Js.Num(x) => try{func(x) } catch {case e: NumberFormatException => throw Invalid.Data(n, "Number")}
    case s @ Js.Str(x) => try{func2(x) } catch {case e: NumberFormatException => throw Invalid.Data(s, "Number")}
  }

  private[this] def NumericReadWriter[T: Numeric](func: Double => T, func2: String => T): RW[T] = RW[T](
    {
      case x @ Double.PositiveInfinity => Js.Str(x.toString)
      case x @ Double.NegativeInfinity => Js.Str(x.toString)
      case x => Js.Num(implicitly[Numeric[T]].toDouble(x))
    },
    numericReaderFunc[T](func, func2)
  )
  private[this] final val stringReaderFunc: JPF[String] = validate("String"){
    case x: Js.Str => x.value
  }
  implicit final val StringRW = RW[String](Js.Str, stringReaderFunc)

  implicit final val CharRW = NumericStringReadWriter[Char](_(0))
  implicit final val ByteRW = NumericReadWriter(_.toByte, _.toByte)
  implicit final val ShortRW = NumericReadWriter(_.toShort, _.toShort)
  implicit final val IntRW = NumericReadWriter(_.toInt, _.toInt)
  implicit final val LongRW = NumericStringReadWriter[Long](_.toLong)
  implicit final val FloatRW = NumericReadWriter(_.toFloat, _.toFloat)
  implicit final val DoubleRW = NumericReadWriter(_.toDouble, _.toDouble)
}

object StdlibCodecs {
  import collection.generic.CanBuildFrom

  object All extends Durations with Eithers with Options with Arrays with Maps

  trait Seqs {
    implicit def SeqishR[T: R, V[_]]
    (implicit cbf: CanBuildFrom[Nothing, T, V[T]]): R[V[T]] = R[V[T]](
      validate("Array(n)"){case Js.Arr(x@_*) => x.map(readJs[T]).to[V]}
    )

    implicit def SeqishW[T: W, V[_] <: Iterable[_]]: W[V[T]] = W[V[T]]{
      (x: V[T]) => Js.Arr(x.iterator.asInstanceOf[Iterator[T]].map(writeJs(_)).toArray:_*)
    }

    protected def SeqLikeW[T: W, V[_]](g: V[T] => Option[Seq[T]]): W[V[T]] = W[V[T]](
      x => Js.Arr(g(x).get.map(x => writeJs(x)):_*)
    )
    protected def SeqLikeR[T: R, V[_]](f: Seq[T] => V[T]): R[V[T]] = R[V[T]](
      validate("Array(n)"){case Js.Arr(x@_*) => f(x.map(readJs[T]))}
    )
  }

  trait Maps extends Seqs {
    implicit def MapW[K: W, V: W] =  W[Map[K, V]](
      x => Js.Arr(x.toSeq.map(writeJs[(K, V)]):_*)
    )
    implicit def MapR[K: R, V: R] = R[Map[K, V]](
      validate("Array(n)"){case x: Js.Arr => x.value.map(readJs[(K, V)]).toMap}
    )
  }

  trait Options extends Seqs {
    implicit def OptionW[T: W]: W[Option[T]] = SeqLikeW[T, Option](x => Some(x.toSeq))
    implicit def SomeW[T: W] = W[Some[T]](OptionW[T].write)
    implicit def NoneW(implicit I: W[Int]): W[None.type] = W[None.type](OptionW[Int].write)
    implicit def OptionR[T: R]: R[Option[T]] = SeqLikeR[T, Option](_.headOption)
    implicit def SomeR[T: R] = R[Some[T]](OptionR[T].read andThen (_.asInstanceOf[Some[T]]))
    implicit def NoneR(implicit I: R[Int]): R[None.type] = R[None.type](OptionR[Int].read andThen (_.asInstanceOf[None.type]))
  }

  trait Arrays extends Seqs {
    implicit def ArrayW[T: W: ClassTag] = SeqLikeW[T, Array](Array.unapplySeq)
    implicit def ArrayR[T: R: ClassTag] = SeqLikeR[T, Array](x => Array.apply(x:_*))
  }

  trait Eithers {
    implicit def EitherR[A: R, B: R]: R[Either[A, B]] = R[Either[A, B]](
      RightR[A, B].read orElse LeftR[A, B].read
    )
    implicit def RightR[A: R, B: R]: R[Right[A, B]] = R[Right[A, B]] {
      case Js.Arr(Js.Num(1), x) => Right(readJs[B](x))
    }
    implicit def LeftR[A: R, B: R]: R[Left[A, B]] = R[Left[A, B]] {
      case Js.Arr(Js.Num(0), x) => Left(readJs[A](x))
    }

    implicit def RightW[A: W, B: W]: W[Right[A, B]] = W[Right[A, B]](EitherW[A, B].write)

    implicit def LeftW[A: W, B: W]: W[Left[A, B]] = W[Left[A, B]](EitherW[A, B].write)

    implicit def EitherW[A: W, B: W]: W[Either[A, B]] = W[Either[A, B]]{
      case Left(t) => Js.Arr(Js.Num(0), writeJs(t))
      case Right(t) => Js.Arr(Js.Num(1), writeJs(t))
    }
  }

  trait Durations {
    import BaseCodecs.{StringRW, LongRW}

    implicit final val DurationW: W[Duration] = W[Duration]{
      case Duration.Inf => writeJs("inf")
      case Duration.MinusInf => writeJs("-inf")
      case x if x eq Duration.Undefined => writeJs("undef")
      case x => writeJs(x.toNanos)
    }

    implicit final val InfiniteW = W[Duration.Infinite](DurationW.write)
    implicit final val InfiniteR = R[Duration.Infinite]{
      case Js.Str("inf") => Duration.Inf
      case Js.Str("-inf") => Duration.MinusInf
      case Js.Str("undef") => Duration.Undefined
    }

    implicit final val FiniteW = W[FiniteDuration](DurationW.write)
    implicit final val FiniteR = R[FiniteDuration]{
      case x: Js.Str => Duration.fromNanos(x.value.toLong)
    }

    implicit final val DurationR = R[Duration](validate("DurationString"){FiniteR.read orElse InfiniteR.read})
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy