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

org.virtuslab.yaml.YamlEncoder.scala Maven / Gradle / Ivy

The newest version!
package org.virtuslab.yaml

/**
 * A type class that provides a conversion from a given type [[T]] into [[Node]]
 */
trait YamlEncoder[T] { self =>
  def asNode(obj: T): Node

  final def mapContra[T1](f: T1 => T): YamlEncoder[T1] = new YamlEncoder[T1] {
    override def asNode(obj: T1): Node = self.asNode(f(obj))
  }
}

object YamlEncoder extends YamlEncoderCrossCompanionCompat {
  // Define the allowed exceptions in the otherwise disallowed ranges
  private val allowedExceptions = Set('\u0009', '\u000A', '\u000D', '\u0085')

  def isCharNonPrintable(c: Char): Boolean = {
    if (allowedExceptions.contains(c)) false
    else {
      (c >= '\u0000' && c <= '\u001F') || // C0 control block (except allowed exceptions above)
      c == '\u007F' ||                    // DEL
      (c >= '\u0080' && c <= '\u009F') || // C1 control block (except for NEL \u0085)
      (c >= '\uD800' && c <= '\uDFFF') || // Surrogate block
      c == '\uFFFE' || c == '\uFFFF'      // Disallowed specific characters
    }
  }

  def escapeSpecialCharacters(scalar: String): String =
    scalar.flatMap { char =>
      if (isCharNonPrintable(char))
        f"\\u${char.toInt}%04X"
      else
        char.toString
    }

  def apply[T](implicit self: YamlEncoder[T]): YamlEncoder[T] = self

  implicit def forByte: YamlEncoder[Byte]       = v => Node.ScalarNode(v.toString)
  implicit def forChar: YamlEncoder[Char]       = v => Node.ScalarNode(v.toString)
  implicit def forShort: YamlEncoder[Short]     = v => Node.ScalarNode(v.toString)
  implicit def forInt: YamlEncoder[Int]         = v => Node.ScalarNode(v.toString)
  implicit def forLong: YamlEncoder[Long]       = v => Node.ScalarNode(v.toString)
  implicit def forFloat: YamlEncoder[Float]     = v => Node.ScalarNode(v.toString)
  implicit def forDouble: YamlEncoder[Double]   = v => Node.ScalarNode(v.toString)
  implicit def forBoolean: YamlEncoder[Boolean] = v => Node.ScalarNode(v.toString)
  implicit def forString: YamlEncoder[String]   = v => Node.ScalarNode(escapeSpecialCharacters(v))

  implicit def forOption[T](implicit encoder: YamlEncoder[T]): YamlEncoder[Option[T]] = {
    case Some(t) => encoder.asNode(t)
    case None    => Node.ScalarNode("", Tag.nullTag)
  }

  implicit def forSet[T](implicit encoder: YamlEncoder[T]): YamlEncoder[Set[T]] = (nodes) =>
    Node.SequenceNode(nodes.map(encoder.asNode(_)).toSeq, Tag.seq)

  implicit def forSeq[T](implicit encoder: YamlEncoder[T]): YamlEncoder[Seq[T]] = (nodes) =>
    Node.SequenceNode(nodes.map(encoder.asNode(_)), Tag.seq)

  implicit def forList[T](implicit encoder: YamlEncoder[T]): YamlEncoder[List[T]] = (nodes) =>
    Node.SequenceNode(nodes.map(encoder.asNode(_)), Tag.seq)

  // todo support arbitrary node as key in KeyValueNode
  implicit def forMap[K, V](implicit
      keyCodec: YamlEncoder[K],
      valueCodec: YamlEncoder[V]
  ): YamlEncoder[Map[K, V]] = { (nodes) =>
    val mappings = nodes.map { case (key, value) =>
      (keyCodec.asNode(key) -> valueCodec.asNode(value))
    }
    Node.MappingNode(mappings)
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy