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

io.circe.yaml.scalayaml.Parser.scala Maven / Gradle / Ivy

/*
 * Copyright 2016 circe
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.circe.yaml.scalayaml

import cats.data.ValidatedNel
import cats.syntax.all._
import io.circe
import io.circe.Decoder
import io.circe.Json
import io.circe.ParsingFailure
import org.virtuslab.yaml._

import java.io.Reader
import scala.collection.mutable

object Parser extends io.circe.yaml.common.Parser {

  private def readerToString(yaml: Reader): String = {
    val buffer = new Array[Char](4 * 1024)
    val builder = new mutable.StringBuilder(4 * 1024)
    var readBytes = -1
    while ({ readBytes = yaml.read(buffer); readBytes } > 0)
      builder.appendAll(buffer, 0, readBytes)
    builder.result()
  }

  override def parse(yaml: Reader): Either[ParsingFailure, Json] = {
    val string = readerToString(yaml)
    parse(string)
  }

  override def parseDocuments(yaml: Reader): Stream[Either[ParsingFailure, Json]] = {
    val string = readerToString(yaml)
    val parsed = parse(string)
    Stream(parsed)
  }

  override def parseDocuments(yaml: String): Stream[Either[ParsingFailure, Json]] = {
    val parsed = parse(yaml)
    Stream(parsed)
  }

  override def decode[A: Decoder](input: Reader): Either[circe.Error, A] = {
    val string = readerToString(input)
    val parsed = parse(string)
    parsed.flatMap(_.as[A])
  }

  override def decodeAccumulating[A: Decoder](input: Reader): ValidatedNel[circe.Error, A] =
    decode(input).toValidatedNel

  override def parse(input: String): Either[ParsingFailure, Json] = {
    val node = input.asNode
    node match {
      case Right(node) => nodeToJson(node)
      case Left(error) => Left(errorToFailure(error))
    }
  }

  private def scalarNodeToJson(node: Node.ScalarNode): Either[ParsingFailure, Json] = {
    val parsed = YamlDecoder.forAny.construct(node).left.map(errorToFailure)
    parsed.flatMap {
      case null | None   => Json.Null.asRight
      case value: String => Json.fromString(value).asRight
      case value: Int    => Json.fromInt(value).asRight
      case double: Double =>
        Json.fromDouble(double).toRight(ParsingFailure(s"${node.value} cannot be represented as a JSON number.", null))
      case value: Boolean => Json.fromBoolean(value).asRight
      case value: Long    => Json.fromLong(value).asRight
      case float: Float =>
        Json.fromFloat(float).toRight(ParsingFailure(s"${node.value} cannot be represented as a JSON number.", null))
      case value: BigDecimal => Json.fromBigDecimal(value).asRight
      case value: BigInt     => Json.fromBigInt(value).asRight
      case value: Byte       => Json.fromInt(value.toInt).asRight
      case value: Short      => Json.fromInt(value.toInt).asRight
      case value => ParsingFailure(s"Can't map ${value.getClass.getSimpleName} (${node.value}) to JSON.", null).asLeft
    }
  }

  private def nodeToJson(node: Node): Either[ParsingFailure, Json] = node match {
    case node: Node.ScalarNode =>
      scalarNodeToJson(node)
    case Node.SequenceNode(nodes, _) =>
      val values = nodes.traverse(nodeToJson)
      values.map(Json.fromValues)
    case Node.MappingNode(mappings, _) =>
      val fields = mappings.toList.traverse {
        case (Node.ScalarNode(key, _), value) =>
          nodeToJson(value).map(key -> _)
        case (node, _) =>
          Left(ParsingFailure(s"Unexpected ${node.getClass.getSimpleName} type, expected ScalarNode.", null))
      }
      fields.map(Json.fromFields)
  }

  private def errorToFailure(error: YamlError): ParsingFailure =
    ParsingFailure(s"Parsing failed: ${error.msg}", error)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy