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

ru.tinkoff.phobos.encoding.ElementEncoder.scala Maven / Gradle / Ivy

The newest version!
package ru.tinkoff.phobos.encoding

import java.time._
import java.util.{Base64, UUID}
import java.time.format.DateTimeFormatter

/** Warning! This is an internal API which may change in future. Do not implement or use this trait directly unless you
  * know what you are doing.
  *
  * Use XmlEncoder for encoding.
  *
  * ElementEncoder instance must exist for every type encoded to XML element.
  *
  * ElementEncoder instance can be created
  *   - from existing instance by using .contramap (mostly used for "simple" types);
  *   - by macros from ru.tinkoff.phobos.derivation.semiauto package (for case classes and sealed traits).
  *
  * This typeclass describes process of encoding some A value to XML document. Name of the element is not defined in
  * typeclass, it should be passed in encodeAsElement method.
  */
trait ElementEncoder[A] { self =>
  def encodeAsElement(
      a: A,
      sw: PhobosStreamWriter,
      localName: String,
      namespaceUri: Option[String],
      preferredNamespacePrefix: Option[String] = None,
  ): Unit

  def contramap[B](f: B => A): ElementEncoder[B] =
    new ElementEncoder[B] {
      def encodeAsElement(
          b: B,
          sw: PhobosStreamWriter,
          localName: String,
          namespaceUri: Option[String],
          preferredNamespacePrefix: Option[String],
      ): Unit =
        self.encodeAsElement(f(b), sw, localName, namespaceUri, preferredNamespacePrefix)
    }
}

object ElementEncoder extends ElementLiteralInstances with DerivedElement {

  def apply[A](implicit instance: ElementEncoder[A]) = instance

  /** Instances
    */
  implicit val stringEncoder: ElementEncoder[String] =
    new ElementEncoder[String] {
      def encodeAsElement(
          a: String,
          sw: PhobosStreamWriter,
          localName: String,
          namespaceUri: Option[String],
          preferredNamespacePrefix: Option[String],
      ): Unit = {
        namespaceUri.fold(sw.writeStartElement(localName))(ns =>
          sw.writeStartElement(preferredNamespacePrefix.orNull, localName, ns),
        )
        sw.writeCharacters(a)
        sw.writeEndElement()
      }
    }

  implicit val unitEncoder: ElementEncoder[Unit] =
    new ElementEncoder[Unit] {
      def encodeAsElement(
          a: Unit,
          sw: PhobosStreamWriter,
          localName: String,
          namespaceUri: Option[String],
          preferredNamespacePrefix: Option[String],
      ): Unit =
        namespaceUri.fold(sw.writeEmptyElement(localName))(ns =>
          sw.writeEmptyElement(preferredNamespacePrefix.orNull, ns, localName),
        )
    }

  implicit val booleanEncoder: ElementEncoder[Boolean]                     = stringEncoder.contramap(_.toString)
  implicit val javaBooleanEncoder: ElementEncoder[java.lang.Boolean]       = booleanEncoder.contramap(_.booleanValue())
  implicit val charEncoder: ElementEncoder[Char]                           = stringEncoder.contramap(_.toString)
  implicit val javaCharacterEncoder: ElementEncoder[java.lang.Character]   = charEncoder.contramap(_.charValue())
  implicit val floatEncoder: ElementEncoder[Float]                         = stringEncoder.contramap(_.toString)
  implicit val javaFloatEncoder: ElementEncoder[java.lang.Float]           = floatEncoder.contramap(_.floatValue())
  implicit val doubleEncoder: ElementEncoder[Double]                       = stringEncoder.contramap(_.toString)
  implicit val javaDoubleEncoder: ElementEncoder[java.lang.Double]         = doubleEncoder.contramap(_.doubleValue())
  implicit val byteEncoder: ElementEncoder[Byte]                           = stringEncoder.contramap(_.toString)
  implicit val javaByteEncoder: ElementEncoder[java.lang.Byte]             = byteEncoder.contramap(_.byteValue())
  implicit val shortEncoder: ElementEncoder[Short]                         = stringEncoder.contramap(_.toString)
  implicit val javaShortEncoder: ElementEncoder[java.lang.Short]           = shortEncoder.contramap(_.shortValue())
  implicit val intEncoder: ElementEncoder[Int]                             = stringEncoder.contramap(_.toString)
  implicit val javaIntegerEncoder: ElementEncoder[java.lang.Integer]       = intEncoder.contramap(_.intValue())
  implicit val longEncoder: ElementEncoder[Long]                           = stringEncoder.contramap(_.toString)
  implicit val javaLongEncoder: ElementEncoder[java.lang.Long]             = longEncoder.contramap(_.longValue())
  implicit val bigIntEncoder: ElementEncoder[BigInt]                       = stringEncoder.contramap(_.toString)
  implicit val javaBigIntegerEncoder: ElementEncoder[java.math.BigInteger] = bigIntEncoder.contramap(BigInt.apply)
  implicit val bigDecimalEncoder: ElementEncoder[BigDecimal]               = stringEncoder.contramap(_.toString)
  implicit val javaBigDecimalEncoder: ElementEncoder[java.math.BigDecimal] =
    bigDecimalEncoder.contramap(BigDecimal.apply)
  implicit val UUIDEncoder: ElementEncoder[UUID] = stringEncoder.contramap(_.toString)

  implicit val base64Encoder: ElementEncoder[Array[Byte]] = stringEncoder.contramap(Base64.getEncoder.encodeToString)

  implicit def optionEncoder[A](implicit encoder: ElementEncoder[A]): ElementEncoder[Option[A]] =
    new ElementEncoder[Option[A]] {
      def encodeAsElement(
          a: Option[A],
          sw: PhobosStreamWriter,
          localName: String,
          namespaceUri: Option[String],
          preferredNamespacePrefix: Option[String],
      ): Unit =
        a.foreach((a: A) => encoder.encodeAsElement(a, sw, localName, namespaceUri, preferredNamespacePrefix))
    }

  implicit def someEncoder[A](implicit e: ElementEncoder[A]): ElementEncoder[Some[A]] = e.contramap(_.get)

  implicit val noneEncoder: ElementEncoder[None.type] =
    new ElementEncoder[None.type] {
      def encodeAsElement(
          a: None.type,
          sw: PhobosStreamWriter,
          localName: String,
          namespaceUri: Option[String],
          preferredNamespacePrefix: Option[String],
      ): Unit =
        ()
    }

  implicit def iteratorEncoder[A](implicit encoder: ElementEncoder[A]): ElementEncoder[Iterator[A]] =
    new ElementEncoder[Iterator[A]] {
      def encodeAsElement(
          as: Iterator[A],
          sw: PhobosStreamWriter,
          localName: String,
          namespaceUri: Option[String],
          preferredNamespacePrefix: Option[String],
      ): Unit =
        as.foreach(a => encoder.encodeAsElement(a, sw, localName, namespaceUri, preferredNamespacePrefix))
    }

  implicit def seqEncoder[A](implicit encoder: ElementEncoder[A]): ElementEncoder[Seq[A]] =
    iteratorEncoder[A].contramap(_.iterator)

  implicit def setEncoder[A](implicit encoder: ElementEncoder[A]): ElementEncoder[Set[A]] =
    iteratorEncoder[A].contramap(_.iterator)

  implicit def listEncoder[A](implicit encoder: ElementEncoder[A]): ElementEncoder[List[A]] =
    iteratorEncoder[A].contramap(_.iterator)

  implicit def vectorEncoder[A](implicit encoder: ElementEncoder[A]): ElementEncoder[Vector[A]] =
    iteratorEncoder[A].contramap(_.iterator)

  implicit val instantEncoder: ElementEncoder[Instant] =
    stringEncoder.contramap(_.toString)

  def instantEncoderWithFormatter(formatter: DateTimeFormatter): ElementEncoder[Instant] =
    stringEncoder.contramap(formatter.format)

  implicit val localDateTimeEncoder: ElementEncoder[LocalDateTime] =
    stringEncoder.contramap(_.toString)

  def localDateTimeEncoderWithFormatter(formatter: DateTimeFormatter): ElementEncoder[LocalDateTime] =
    stringEncoder.contramap(_.format(formatter))

  implicit val zonedDateTimeEncoder: ElementEncoder[ZonedDateTime] =
    stringEncoder.contramap(_.toString)

  def zonedDateTimeEncoderWithFormatter(formatter: DateTimeFormatter): ElementEncoder[ZonedDateTime] =
    stringEncoder.contramap(_.format(formatter))

  implicit val offsetDateTimeEncoder: ElementEncoder[OffsetDateTime] =
    stringEncoder.contramap(_.toString)

  def offsetDateTimeEncoderWithFormatter(formatter: DateTimeFormatter): ElementEncoder[OffsetDateTime] =
    stringEncoder.contramap(_.format(formatter))

  implicit val localDateEncoder: ElementEncoder[LocalDate] =
    stringEncoder.contramap(_.toString)

  def localDateEncoderWithFormatter(formatter: DateTimeFormatter): ElementEncoder[LocalDate] =
    stringEncoder.contramap(_.format(formatter))

  implicit val localTimeEncoder: ElementEncoder[LocalTime] =
    stringEncoder.contramap(_.toString)

  def localTimeEncoderWithFormatter(formatter: DateTimeFormatter): ElementEncoder[LocalTime] =
    stringEncoder.contramap(_.format(formatter))
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy