Skip to content

Commit

Permalink
Added indent with indicators to yaml-v12 (circe#406)
Browse files Browse the repository at this point in the history
* Added indentWithIndicator option

* Deprecate Printer(.Config) and introduce PrinterBuilder

* Moved PrinterTest to use PrinterBuilder

* Added circe-yaml PrinterBuilder that uses circe-yaml-common.

-Also support NonPrintableStyle

* Added file headers and ran scalafmt

* Run scalafix

* formatting

* Make sure we have correct deprecation version

* Fixed issues found in review.

---------

Co-authored-by: Anssi Heiska <anssi.heiska@digia.com>
Co-authored-by: Erlend Hamnaberg <erlend@hamnaberg.net>
  • Loading branch information
3 people committed Jun 14, 2024
1 parent b349a85 commit b28a5b1
Show file tree
Hide file tree
Showing 8 changed files with 699 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,9 @@ object Printer {
case object Folded extends StringStyle
}

sealed trait NonPrintableStyle
object NonPrintableStyle {
case object Binary extends NonPrintableStyle
case object Escape extends NonPrintableStyle
}
}
8 changes: 5 additions & 3 deletions circe-yaml-v12/src/main/scala/io/circe/yaml/v12/Printer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import org.snakeyaml.engine.v2.api.DumpSettings
import scala.collection.JavaConverters._

object Printer {
@deprecated("Use Printer.builder instead", since = "1.15.2")
final case class Config(
preserveOrder: Boolean = false,
dropNullKeys: Boolean = false,
Expand All @@ -39,6 +40,7 @@ object Printer {
explicitEnd: Boolean = false
)

@deprecated("Use Printer.builder instead", since = "1.15.2")
def make(config: Config = Config()): common.Printer = {
import config._
new PrinterImpl(
Expand Down Expand Up @@ -68,7 +70,7 @@ object Printer {
)
}

lazy val spaces2: common.Printer = make()
lazy val spaces4: common.Printer = make(Config(indent = 4))

def builder: PrinterBuilder = PrinterBuilder()
lazy val spaces2: common.Printer = builder.withIndent(2).build()
lazy val spaces4: common.Printer = builder.withIndent(4).build()
}
161 changes: 161 additions & 0 deletions circe-yaml-v12/src/main/scala/io/circe/yaml/v12/PrinterBuilder.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/*
* 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.v12

import io.circe.yaml.common
import io.circe.yaml.common.Printer._
import org.snakeyaml.engine.v2.api.DumpSettings
import org.snakeyaml.engine.v2.common.{ NonPrintableStyle => SnakeNonPrintableStyle }

import scala.collection.JavaConverters._

final class PrinterBuilder private (
preserveOrder: Boolean = false,
dropNullKeys: Boolean = false,
indent: Int = 2,
maxScalarWidth: Int = 80,
splitLines: Boolean = true,
indicatorIndent: Int = 0,
indentWithIndicator: Boolean = false,
tags: Map[String, String] = Map.empty,
sequenceStyle: FlowStyle = FlowStyle.Block,
mappingStyle: FlowStyle = FlowStyle.Block,
stringStyle: StringStyle = StringStyle.Plain,
lineBreak: LineBreak = LineBreak.Unix,
explicitStart: Boolean = false,
explicitEnd: Boolean = false,
nonPrintableStyle: NonPrintableStyle = NonPrintableStyle.Escape
) {

private def copy(
preserveOrder: Boolean = this.preserveOrder,
dropNullKeys: Boolean = this.dropNullKeys,
indent: Int = this.indent,
maxScalarWidth: Int = this.maxScalarWidth,
splitLines: Boolean = this.splitLines,
indicatorIndent: Int = this.indicatorIndent,
indentWithIndicator: Boolean = this.indentWithIndicator,
tags: Map[String, String] = this.tags,
sequenceStyle: FlowStyle = this.sequenceStyle,
mappingStyle: FlowStyle = this.mappingStyle,
stringStyle: StringStyle = this.stringStyle,
lineBreak: LineBreak = this.lineBreak,
explicitStart: Boolean = this.explicitStart,
explicitEnd: Boolean = this.explicitEnd,
nonPrintableStyle: NonPrintableStyle = this.nonPrintableStyle
): PrinterBuilder =
new PrinterBuilder(
preserveOrder = preserveOrder,
dropNullKeys = dropNullKeys,
indent = indent,
maxScalarWidth = maxScalarWidth,
splitLines = splitLines,
indicatorIndent = indicatorIndent,
indentWithIndicator = indentWithIndicator,
tags = tags,
sequenceStyle = sequenceStyle,
mappingStyle = mappingStyle,
stringStyle = stringStyle,
lineBreak = lineBreak,
explicitStart = explicitStart,
explicitEnd = explicitEnd,
nonPrintableStyle = nonPrintableStyle
)

def withPreserveOrder(preserveOrder: Boolean): PrinterBuilder =
copy(preserveOrder = preserveOrder)

def withDropNullKeys(dropNullKeys: Boolean): PrinterBuilder =
copy(dropNullKeys = dropNullKeys)

def withIndent(indent: Int): PrinterBuilder =
copy(indent = indent)

def withMaxScalarWidth(maxScalarWidth: Int): PrinterBuilder =
copy(maxScalarWidth = maxScalarWidth)

def withSplitLines(splitLines: Boolean): PrinterBuilder =
copy(splitLines = splitLines)

def withIndicatorIndent(indicatorIndent: Int): PrinterBuilder =
copy(indicatorIndent = indicatorIndent)

def withIndentWithIndicator(indentWithIndicator: Boolean): PrinterBuilder =
copy(indentWithIndicator = indentWithIndicator)

def withTags(tags: Map[String, String]): PrinterBuilder =
copy(tags = tags)

def withSequenceStyle(sequenceStyle: common.Printer.FlowStyle): PrinterBuilder =
copy(sequenceStyle = sequenceStyle)

def withMappingStyle(mappingStyle: common.Printer.FlowStyle): PrinterBuilder =
copy(mappingStyle = mappingStyle)

def withStringStyle(stringStyle: common.Printer.StringStyle): PrinterBuilder =
copy(stringStyle = stringStyle)

def withLineBreak(lineBreak: common.Printer.LineBreak): PrinterBuilder =
copy(lineBreak = lineBreak)

def withExplicitStart(explicitStart: Boolean): PrinterBuilder =
copy(explicitStart = explicitStart)

def withExplicitEnd(explicitEnd: Boolean): PrinterBuilder =
copy(explicitEnd = explicitEnd)

def withNonPrintableStyle(nonPrintableStyle: NonPrintableStyle): PrinterBuilder =
copy(nonPrintableStyle = nonPrintableStyle)

def build(): common.Printer =
new PrinterImpl(
stringStyle,
preserveOrder,
dropNullKeys,
mappingStyle,
sequenceStyle,
DumpSettings
.builder()
.setIndent(indent)
.setWidth(maxScalarWidth)
.setSplitLines(splitLines)
.setIndicatorIndent(indicatorIndent)
.setIndentWithIndicator(indentWithIndicator)
.setTagDirective(tags.asJava)
.setDefaultScalarStyle(stringStyle.toScalarStyle)
.setExplicitStart(explicitStart)
.setExplicitEnd(explicitEnd)
.setBestLineBreak {
lineBreak match {
case LineBreak.Unix => "\n"
case LineBreak.Windows => "\r\n"
case LineBreak.Mac => "\r"
}
}
.setNonPrintableStyle {
nonPrintableStyle match {
case NonPrintableStyle.Binary => SnakeNonPrintableStyle.BINARY
case NonPrintableStyle.Escape => SnakeNonPrintableStyle.ESCAPE
}
}
.build()
)
}

object PrinterBuilder {
def apply(): PrinterBuilder = new PrinterBuilder()
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import org.snakeyaml.engine.v2.serializer.Serializer
import java.io.StringWriter
import scala.collection.JavaConverters._

class PrinterImpl(
private class PrinterImpl(
stringStyle: StringStyle,
preserveOrder: Boolean,
dropNullKeys: Boolean,
Expand Down
64 changes: 51 additions & 13 deletions circe-yaml-v12/src/test/scala/io/circe/yaml/v12/PrinterTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class PrinterTests extends AnyFreeSpec with Matchers {
val json = Json.obj("foo" -> Json.arr((0 until 3).map(_.toString).map(Json.fromString): _*))

"Block" in {
val printer = Printer.make(Printer.Config(sequenceStyle = FlowStyle.Block, mappingStyle = FlowStyle.Block))
val printer = Printer.builder.withSequenceStyle(FlowStyle.Block).withMappingStyle(FlowStyle.Block).build()
printer.pretty(json) shouldEqual
"""foo:
|- '0'
Expand All @@ -39,7 +39,7 @@ class PrinterTests extends AnyFreeSpec with Matchers {
}

"Flow" in {
val printer = Printer.make(Printer.Config(sequenceStyle = FlowStyle.Flow, mappingStyle = FlowStyle.Flow))
val printer = Printer.builder.withSequenceStyle(FlowStyle.Block).withMappingStyle(FlowStyle.Flow).build()
printer.pretty(json) shouldEqual
"""{foo: ['0', '1', '2']}
|""".stripMargin
Expand All @@ -50,7 +50,7 @@ class PrinterTests extends AnyFreeSpec with Matchers {
val kvPairs = Seq("d" -> 4, "a" -> 1, "b" -> 2, "c" -> 3)
val json = Json.obj(kvPairs.map { case (k, v) => k -> Json.fromInt(v) }: _*)
"true" in {
val printer = Printer.make(Printer.Config(preserveOrder = true))
val printer = Printer.builder.withPreserveOrder(true).build()
printer.pretty(json) shouldEqual
"""d: 4
|a: 1
Expand All @@ -68,36 +68,36 @@ class PrinterTests extends AnyFreeSpec with Matchers {
val json = Json.obj("foo" -> Json.fromString(foosPlain))

"Plain" in {
val printer = Printer.make(Printer.Config(splitLines = false, stringStyle = StringStyle.Plain))
val printer = Printer.builder.withSplitLines(false).withStringStyle(StringStyle.Plain).build()
printer.pretty(json) shouldEqual
s"""foo: $foosPlain
|""".stripMargin
}

"Double quoted" in {
val printer = Printer.make(Printer.Config(stringStyle = StringStyle.DoubleQuoted))
val printer = Printer.builder.withStringStyle(StringStyle.DoubleQuoted).build()
printer.pretty(json) shouldEqual
s"""foo: "${foosSplit.mkString("\\\n \\ ")}"
|""".stripMargin
}

"Single quoted" in {
val printer = Printer.make(Printer.Config(stringStyle = StringStyle.SingleQuoted))
val printer = Printer.builder.withStringStyle(StringStyle.SingleQuoted).build()
printer.pretty(json) shouldEqual
s"""foo: '${foosSplit.mkString("\n ")}'
|""".stripMargin
}

"Folded" in {
val printer = Printer.make(Printer.Config(stringStyle = StringStyle.Folded))
val printer = Printer.builder.withStringStyle(StringStyle.Folded).build()
printer.pretty(json) shouldEqual
s"""foo: >-
| $foosFolded
|""".stripMargin
}

"Literal" in {
val printer = Printer.make(Printer.Config(stringStyle = StringStyle.Literal))
val printer = Printer.builder.withStringStyle(StringStyle.Literal).build()
printer.pretty(json) shouldEqual
s"""foo: |-
| $foosPlain
Expand All @@ -108,7 +108,7 @@ class PrinterTests extends AnyFreeSpec with Matchers {

"Plain with newlines" in {
val json = Json.obj("foo" -> Json.fromString("abc\nxyz\n"))
val printer = Printer.make(Printer.Config(stringStyle = StringStyle.Plain))
val printer = Printer.builder.withStringStyle(StringStyle.Plain).build()
printer.pretty(json) shouldEqual
s"""foo: |
| abc
Expand All @@ -118,7 +118,8 @@ class PrinterTests extends AnyFreeSpec with Matchers {

"Drop null keys" in {
val json = Json.obj("nullField" -> Json.Null, "nonNullField" -> Json.fromString("foo"))
Printer.make(Printer.Config(dropNullKeys = true)).pretty(json) shouldEqual "nonNullField: foo\n"
val printer = Printer.builder.withDropNullKeys(true).build()
printer.pretty(json) shouldEqual "nonNullField: foo\n"
}

"Root integer" in {
Expand All @@ -140,19 +141,56 @@ class PrinterTests extends AnyFreeSpec with Matchers {
val json = Json.arr(Json.fromString("foo"), Json.fromString("bar"))

"Unix" in {
Printer.make(Printer.Config(lineBreak = LineBreak.Unix)).pretty(json) shouldEqual
Printer.builder.withLineBreak(LineBreak.Unix).build().pretty(json) shouldEqual
"- foo\n- bar\n"
}

"Windows" in {
Printer.make(Printer.Config(lineBreak = LineBreak.Windows)).pretty(json) shouldEqual
Printer.builder.withLineBreak(LineBreak.Windows).build().pretty(json) shouldEqual
"- foo\r\n- bar\r\n"
}

"Mac" in {
Printer.make(Printer.Config(lineBreak = LineBreak.Mac)).pretty(json) shouldEqual
Printer.builder.withLineBreak(LineBreak.Mac).build().pretty(json) shouldEqual
"- foo\r- bar\r"
}
}

"Indicator indent" - {
val firstEl = Json.obj("a" -> Json.fromString("b"), "c" -> Json.fromString("d"))
val secondEl = Json.obj("aa" -> Json.fromString("bb"), "cc" -> Json.fromString("dd"))
val json = Json.obj("root" -> Json.arr(firstEl, secondEl))

"Default" in {
Printer.spaces2.pretty(json) shouldEqual
"""root:
|- a: b
| c: d
|- aa: bb
| cc: dd
|""".stripMargin
}

"Indent without indentWithIndicator" in {
Printer.builder.withIndicatorIndent(2).build().pretty(json) shouldEqual
"""root:
| -
| a: b
| c: d
| -
| aa: bb
| cc: dd
|""".stripMargin
}

"Indent with indentWithIndicator" in {
Printer.builder.withIndentWithIndicator(true).withIndicatorIndent(2).build().pretty(json) shouldEqual
"""root:
| - a: b
| c: d
| - aa: bb
| cc: dd
|""".stripMargin
}
}
}
Loading

0 comments on commit b28a5b1

Please sign in to comment.