Skip to content

Commit

Permalink
Add renderer for JSON
Browse files Browse the repository at this point in the history
  • Loading branch information
satabin committed Jan 29, 2024
1 parent 9e4007b commit 360515f
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 0 deletions.
106 changes: 106 additions & 0 deletions json/src/main/scala/fs2/data/json/tokens.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,15 @@ package fs2
package data
package json

import fs2.Pure
import fs2.data.text.render.{DocEvent, Renderable, Renderer}

import scala.annotation.switch

sealed abstract class Token(val kind: String) {
def jsonRepr: String
}

object Token {

case object StartObject extends Token("object") {
Expand Down Expand Up @@ -58,4 +64,104 @@ object Token {
def jsonRepr: String = s""""$value""""
}

implicit object renderable extends Renderable[Token] {

private val startObject =
Stream.emits(DocEvent.GroupBegin :: DocEvent.Text("{") :: DocEvent.IndentBegin :: DocEvent.LineBreak :: Nil)

private val endObject =
Stream.emits(DocEvent.IndentEnd :: DocEvent.LineBreak :: DocEvent.Text("}") :: DocEvent.GroupEnd :: Nil)

private val startArray =
Stream.emits(DocEvent.GroupBegin :: DocEvent.Text("[") :: DocEvent.IndentBegin :: DocEvent.LineBreak :: Nil)

private val endArray =
Stream.emits(DocEvent.IndentEnd :: DocEvent.LineBreak :: DocEvent.Text("]") :: DocEvent.GroupEnd :: Nil)

private val objectSep =
Stream.emits(
DocEvent.Text(",") :: DocEvent.IndentEnd :: DocEvent.GroupEnd :: DocEvent.GroupEnd :: DocEvent.Line :: Nil)

private val arraySep =
Stream.emits(DocEvent.Text(",") :: DocEvent.GroupEnd :: DocEvent.Line :: Nil)

private val nullValue =
Stream.emit(DocEvent.Text("null"))

private val trueValue =
Stream.emit(DocEvent.Text("true"))

private val falseValue =
Stream.emit(DocEvent.Text("false"))

private final val FirstObjectKey = 0
private final val ObjectKey = 1
private final val ObjectValue = 2
private final val FirstArrayValue = 3
private final val ArrayValue = 4

override def newRenderer(): Renderer[Token] = new Renderer[Token] {

// the current stack of states, helping to deal with comma and indentation
// states are described right above
private[this] var states = List.empty[Int]

private def separator(): Stream[Pure, DocEvent] =
states match {
case Nil =>
Stream.empty
case state :: rest =>
(state: @switch) match {
case FirstObjectKey =>
states = ObjectValue :: rest
Stream.empty
case ObjectKey =>
states = ObjectValue :: rest
objectSep
case ObjectValue =>
states = ObjectKey :: rest
Stream.emit(DocEvent.GroupBegin)
case FirstArrayValue =>
states = ArrayValue :: rest
Stream.empty
case ArrayValue =>
states = ArrayValue :: rest
arraySep
}
}

override def doc(token: Token): Stream[Pure, DocEvent] =
token match {
case StartObject =>
val res = separator() ++ startObject
states = FirstObjectKey :: states
res
case EndObject =>
states = if (states.isEmpty) Nil else states.tail
endObject
case StartArray =>
val res = separator() ++ startArray
states = FirstArrayValue :: states
res
case EndArray =>
states = if (states.isEmpty) Nil else states.tail
endArray
case Key(value) =>
separator() ++ Stream.emits(
DocEvent.GroupBegin :: DocEvent.IndentBegin :: DocEvent.Text(s""""$value":""") :: DocEvent.Line :: Nil)
case NullValue =>
separator() ++ nullValue
case TrueValue =>
separator() ++ trueValue
case FalseValue =>
separator() ++ falseValue
case NumberValue(value) =>
separator() ++ Stream.emit(DocEvent.Text(value))
case StringValue(value) =>
separator() ++ Stream.emit(DocEvent.Text(s""""$value""""))
}
}

}

}
9 changes: 9 additions & 0 deletions json/src/test/scala/fs2/data/json/RenderSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,13 @@ object RenderSpec extends SimpleIOSuite {

}

pureTest("An object should be properly pretty renderer with line width of 10") {
val input = Stream.emits("""{"field1": "test", "field2": [23, [true, null]]}""")

println(
input.through(tokens[Fallible, Char]).through(fs2.data.text.render.pretty(width = 20, indent = 2)).compile.string)
expect(true)

}

}

0 comments on commit 360515f

Please sign in to comment.