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

api.SerializationPack.scala Maven / Gradle / Ivy

package reactivemongo.api

import java.util.UUID

import scala.util.Try

import scala.reflect.ClassTag

import reactivemongo.core.protocol.Response

import reactivemongo.api.bson.buffer.{ ReadableBuffer, WritableBuffer }

trait SerializationPack extends SerializationPackCompat { self: Singleton =>
  type Value
  type ElementProducer
  type Document <: Value
  type Writer[A]
  type Reader[A]
  type NarrowValueReader[A]
  private[reactivemongo] type WidenValueReader[A]

  private[reactivemongo] val IsDocument: ClassTag[Document]
  private[reactivemongo] val IsValue: ClassTag[Value]

  def serialize[A](a: A, writer: Writer[A]): Document

  def deserialize[A](document: Document, reader: Reader[A]): A

  private[reactivemongo] def writeToBuffer(
      buffer: WritableBuffer,
      document: Document
    ): WritableBuffer

  private[reactivemongo] def readFromBuffer(buffer: ReadableBuffer): Document

  private[reactivemongo] final def readAndDeserialize[A](
      buffer: ReadableBuffer,
      reader: Reader[A]
    ): A = deserialize(readFromBuffer(buffer), reader)

  private[reactivemongo] def readAndDeserialize[A](
      response: Response,
      reader: Reader[A]
    ): A = {
    val channelBuf = ReadableBuffer(response.documents)

    readAndDeserialize(channelBuf, reader)
  }

  private[reactivemongo] final def serializeAndWrite[A](
      buffer: WritableBuffer,
      document: A,
      writer: Writer[A]
    ): WritableBuffer = writeToBuffer(buffer, serialize(document, writer))

  /** Prepares a writer from the given serialization function. */
  def writer[A](f: A => Document): Writer[A]

  /** Returns true if the given `document` is empty. */
  def isEmpty(document: Document): Boolean

  def widenReader[T](r: NarrowValueReader[T]): WidenValueReader[T]

  private[reactivemongo] def readValue[A](
      value: Value,
      reader: WidenValueReader[A]
    ): Try[A]

  // Returns a Reader from a function
  private[reactivemongo] def reader[A](f: Document => A): Reader[A]

  private[reactivemongo] final def readerOpt[A](
      f: Document => Option[A]
    ): Reader[A] = reader[A] { doc =>
    f(doc) match {
      case Some(v) => v
      case _ =>
        throw reactivemongo.api.bson.exceptions
          .ValueDoesNotMatchException(pretty(doc))
    }
  }

  private[reactivemongo] def narrowIdentityReader: NarrowValueReader[Value]

  private[reactivemongo] def bsonSize(value: Value): Int

  private[reactivemongo] def newBuilder: SerializationPack.Builder[self.type]

  private[reactivemongo] def newDecoder: SerializationPack.Decoder[self.type]

  private[reactivemongo] def pretty(doc: Document): String = doc.toString
}

object SerializationPack {

  /** A builder for serialization simple values (useful for the commands) */
  private[reactivemongo] trait Builder[P <: SerializationPack] {
    protected[reactivemongo] val pack: P

    /** Returns a new document from a sequence of element producers. */
    def document(elements: Seq[pack.ElementProducer]): pack.Document

    def elementProducer(doc: pack.Document): pack.ElementProducer

    /** Returns a new array of values */
    def array(values: Seq[pack.Value]): pack.Value

    /** Returns a byte array as a serialized value. */
    def binary(data: Array[Byte]): pack.Value

    /**
     * Returns a producer of element, for the given `name` and `value`.
     *
     * @param name the element name
     * @param value the element value
     */
    def elementProducer(name: String, value: pack.Value): pack.ElementProducer

    /** Returns a boolean as a serialized value. */
    def boolean(b: Boolean): pack.Value

    /** Returns an integer as a serialized value. */
    def int(i: Int): pack.Value

    /** Returns a long as a serialized value. */
    def long(l: Long): pack.Value

    /** Returns a double as a serialized value. */
    def double(d: Double): pack.Value

    /** Returns a string as a serialized value. */
    def string(s: String): pack.Value

    /** Returns an UUID as a serialized value. */
    def uuid(id: UUID): pack.Value

    /** Returns a timestamp as a serialized value. */
    def timestamp(time: Long): pack.Value

    /** Returns a date/time as a serialized value. */
    def dateTime(time: Long): pack.Value

    /** Returns a regular expression value. */
    def regex(pattern: String, options: String): pack.Value

    /** Returns a new object ID. */
    def generateObjectId(): pack.Value
  }

  /**
   * A decoder for serialization simple values (for internal use).
   *
   * @define returnsNamedElement Returns the named element from the given document
   */
  private[reactivemongo] trait Decoder[P <: SerializationPack] {
    protected[reactivemongo] val pack: P

    /** Extract the value, if and only if it's a document. */
    def asDocument(value: pack.Value): Option[pack.Document]

    /** Returns the names of the document elements. */
    def names(document: pack.Document): Set[String]

    /** @returnsNamedElement, if the element exists. */
    def get(document: pack.Document, name: String): Option[pack.Value]

    /**
     * @returnsNamedElement, if the element exists
     * with expected `T` as value type.
     */
    @com.github.ghik.silencer.silent(".*\\ ev\\ .*is\\ never\\ used.*")
    final def value[T](
        document: pack.Document,
        name: String
      )(implicit
        ev: T <:< pack.Value,
        cls: ClassTag[T]
      ): Option[T] =
      get(document, name).collect { case `cls`(t) => t }

    final def read[T](
        document: pack.Document,
        name: String
      )(implicit
        r: pack.NarrowValueReader[T]
      ): Option[T] = {
      val widenReader = pack.widenReader[T](r)

      get(document, name).flatMap(pack.readValue[T](_, widenReader).toOption)
    }

    /**
     * @returnsNamedElement, if the element is an array field.
     */
    def array(document: pack.Document, name: String): Option[Seq[pack.Value]]

    final def values[T](
        document: pack.Document,
        name: String
      )(implicit
        r: pack.NarrowValueReader[T]
      ): Option[Seq[T]] = {
      val widenReader = pack.widenReader[T](r)

      array(document, name).map {
        _.flatMap(pack.readValue[T](_, widenReader).toOption)
      }
    }

    /**
     * @returnsNamedElement, if the element is a binary field
     */
    def binary(document: pack.Document, name: String): Option[Array[Byte]]

    /**
     * @returnsNamedElement, if the element is a boolean-like field
     * (numeric or boolean).
     */
    def booleanLike(document: pack.Document, name: String): Option[Boolean]

    /**
     * @returnsNamedElement, if the element is a nested document.
     */
    def child(document: pack.Document, name: String): Option[pack.Document]

    /**
     * @returnsNamedElement, if the element is a list of nested documents.
     */
    def children(document: pack.Document, name: String): List[pack.Document]

    /**
     * @returnsNamedElement, if the element is a double field.
     */
    def double(document: pack.Document, name: String): Option[Double]

    /**
     * @returnsNamedElement, if the element is a integer field.
     */
    def int(document: pack.Document, name: String): Option[Int]

    /**
     * @returnsNamedElement, if the element is a long field.
     */
    def long(document: pack.Document, name: String): Option[Long]

    /**
     * @returnsNamedElement, if the element is a string field.
     */
    def string(document: pack.Document, name: String): Option[String]

    /**
     * @returnsNamedElement, if the element is a binary/uuid field.
     */
    def uuid(document: pack.Document, name: String): Option[UUID]
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy