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

io.circe.KeyDecoder.scala Maven / Gradle / Ivy

There is a newer version: 0.13.0
Show newest version
package io.circe

import cats.MonadError
import java.io.Serializable
import java.util.UUID
import scala.annotation.tailrec

/**
 * A type class that provides a conversion from a string used as a JSON key to a
 * value of type `A`.
 */
trait KeyDecoder[A] extends Serializable { self =>

  /**
   * Attempt to convert a String to a value.
   */
  def apply(key: String): Option[A]

  /**
   * Construct an instance for type `B` from an instance for type `A`.
   */
  final def map[B](f: A => B): KeyDecoder[B] = new KeyDecoder[B] {
    final def apply(key: String): Option[B] = self(key).map(f)
  }

  /**
   * Construct an instance for type `B` from an instance for type `A` given a
   * monadic function.
   */
  final def flatMap[B](f: A => KeyDecoder[B]): KeyDecoder[B] = new KeyDecoder[B] {
    final def apply(key: String): Option[B] = self(key).flatMap(a => f(a)(key))
  }
}

object KeyDecoder {
  def apply[A](implicit A: KeyDecoder[A]): KeyDecoder[A] = A

  def instance[A](f: String => Option[A]): KeyDecoder[A] = new KeyDecoder[A] {
    final def apply(key: String): Option[A] = f(key)
  }

  private[this] def numberInstance[A](f: String => A): KeyDecoder[A] = new KeyDecoder[A] {
    final def apply(key: String): Option[A] = try Some(f(key))
    catch {
      case _: NumberFormatException => None
    }
  }

  /**
   * A [[KeyDecoder]] that will always succeed.
   */
  private[circe] abstract class AlwaysKeyDecoder[A] extends KeyDecoder[A] {
    def decodeSafe(key: String): A

    final def apply(key: String): Option[A] = Some(decodeSafe(key))
  }

  implicit val decodeKeyString: KeyDecoder[String] = new AlwaysKeyDecoder[String] {
    final def decodeSafe(key: String): String = key
  }

  implicit val decodeKeySymbol: KeyDecoder[Symbol] = new AlwaysKeyDecoder[Symbol] {
    final def decodeSafe(key: String): Symbol = Symbol(key)
  }

  implicit val decodeKeyUUID: KeyDecoder[UUID] = new KeyDecoder[UUID] {
    final def apply(key: String): Option[UUID] = if (key.length == 36) {
      try Some(UUID.fromString(key))
      catch {
        case _: IllegalArgumentException => None
      }
    } else None
  }

  implicit val decodeKeyByte: KeyDecoder[Byte] = numberInstance(java.lang.Byte.parseByte)
  implicit val decodeKeyShort: KeyDecoder[Short] = numberInstance(java.lang.Short.parseShort)
  implicit val decodeKeyInt: KeyDecoder[Int] = numberInstance(java.lang.Integer.parseInt)
  implicit val decodeKeyLong: KeyDecoder[Long] = numberInstance(java.lang.Long.parseLong)

  implicit val keyDecoderInstances: MonadError[KeyDecoder, Unit] = new MonadError[KeyDecoder, Unit] {
    final def pure[A](a: A): KeyDecoder[A] = new KeyDecoder[A] {
      final def apply(key: String): Option[A] = Some(a)
    }

    override final def map[A, B](fa: KeyDecoder[A])(f: A => B): KeyDecoder[B] = fa.map(f)

    final def flatMap[A, B](fa: KeyDecoder[A])(f: A => KeyDecoder[B]): KeyDecoder[B] = fa.flatMap(f)

    final def raiseError[A](e: Unit): KeyDecoder[A] = new KeyDecoder[A] {
      final def apply(key: String): Option[A] = None
    }

    final def handleErrorWith[A](fa: KeyDecoder[A])(f: Unit => KeyDecoder[A]): KeyDecoder[A] = new KeyDecoder[A] {
      final def apply(key: String): Option[A] = fa(key).orElse(f(())(key))
    }

    final def tailRecM[A, B](a: A)(f: A => KeyDecoder[Either[A, B]]): KeyDecoder[B] = new KeyDecoder[B] {
      @tailrec
      private[this] def step(key: String, a1: A): Option[B] = f(a1)(key) match {
        case None           => None
        case Some(Left(a2)) => step(key, a2)
        case Some(Right(b)) => Some(b)
      }

      final def apply(key: String): Option[B] = step(key, a)
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy