diff --git a/sjsonnet/src-jvm-native/sjsonnet/Config.scala b/sjsonnet/src-jvm-native/sjsonnet/Config.scala index c009f0e5..cd6e6076 100644 --- a/sjsonnet/src-jvm-native/sjsonnet/Config.scala +++ b/sjsonnet/src-jvm-native/sjsonnet/Config.scala @@ -38,6 +38,11 @@ final case class Config( doc = "Write output as a YAML stream of JSON documents" ) yamlStream: Flag = Flag(), + @arg( + name = "legacy-yaml-stream", + doc = "Use the pre-0.7.2 --yaml-stream formatting (implies --yaml-stream)" + ) + legacyYamlStream: Flag = Flag(), @arg( name = "string", short = 'S', diff --git a/sjsonnet/src-jvm-native/sjsonnet/SjsonnetMainBase.scala b/sjsonnet/src-jvm-native/sjsonnet/SjsonnetMainBase.scala index a1610e61..6bf885fc 100644 --- a/sjsonnet/src-jvm-native/sjsonnet/SjsonnetMainBase.scala +++ b/sjsonnet/src-jvm-native/sjsonnet/SjsonnetMainBase.scala @@ -189,7 +189,11 @@ object SjsonnetMainBase { ) .left .map(_ + envVarsDoc) - config = normalizedArgs.forcedFile.fold(config0)(f => config0.copy(file = f)) + config1 = normalizedArgs.forcedFile.fold(config0)(f => config0.copy(file = f)) + // --legacy-yaml-stream implies --yaml-stream. + config = + if (config1.legacyYamlStream.value) config1.copy(yamlStream = mainargs.Flag(true)) + else config1 _ <- { if (config.noTrailingNewline.value && config.yamlStream.value) Left("ERROR: cannot use --no-trailing-newline with --yaml-stream") @@ -530,6 +534,8 @@ object SjsonnetMainBase { } } + private def isScalar(v: ujson.Value) = !v.isInstanceOf[ujson.Arr] && !v.isInstanceOf[ujson.Obj] + private def jsonTypeName(v: ujson.Value): String = v match { case _: ujson.Obj => "object" case _: ujson.Arr => "array" @@ -741,6 +747,41 @@ object SjsonnetMainBase { "whose keys are filenames and values hold the JSON for that file." ) } + case (None, true) if config.legacyYamlStream.value => + // Pre-0.7.2 --yaml-stream formatting. Retained behind --legacy-yaml-stream: + // array elements render as YAML documents (not JSON), and a non-array + // top-level falls back to normal rendering instead of erroring. + interp.interpret(jsonnetCode, path).flatMap { + case arr: ujson.Arr => + writeToFile(config, wd) { writer => + handleRenderError(config.maxTrace) { + arr.value.toSeq match { + case Nil => // do nothing + case Seq(single) => + val renderer = rendererForConfig(writer, config, () => currentPos) + single.transform(renderer) + writer.write(if (isScalar(single)) "\n..." else "") + case multiple => + for ((v, i) <- multiple.zipWithIndex) { + if (i > 0) writer.write('\n') + if (isScalar(v)) writer.write("--- ") + else if (i != 0) writer.write("---\n") + val renderer = rendererForConfig( + writer, + config.copy(yamlOut = mainargs.Flag(true)), + () => currentPos + ) + v.transform(renderer) + } + } + writer.write('\n') + "" + } + } + + case _ => + renderNormal(config, interp, jsonnetCode, path, wd, () => currentPos, stdoutStream) + } case (None, true) => // YAML stream (--no-trailing-newline is already rejected above for yaml-stream) diff --git a/sjsonnet/test/src-jvm/sjsonnet/MainTests.scala b/sjsonnet/test/src-jvm/sjsonnet/MainTests.scala index f1c29f00..fd5e8320 100644 --- a/sjsonnet/test/src-jvm/sjsonnet/MainTests.scala +++ b/sjsonnet/test/src-jvm/sjsonnet/MainTests.scala @@ -691,6 +691,25 @@ object MainTests extends TestSuite { assert(err.contains("stream mode: top-level object was a object")) } + test("legacyYamlStreamUsesPre072Formatting") { + // Array elements render as YAML documents, not JSON documents. + val (res, out, err) = runMain("[{a: 1}, {b: 2}]", "--exec", "--legacy-yaml-stream") + assert((res, out, err) == ((0, "a: 1\n---\nb: 2\n", ""))) + } + + test("legacyYamlStreamFallsBackForNonArrayTopLevel") { + // Unlike --yaml-stream, a non-array top-level renders normally instead of erroring. + val (res, out, err) = runMain("{a: 1}", "--exec", "--legacy-yaml-stream") + assert((res, out, err) == ((0, "{\n \"a\": 1\n}\n", ""))) + } + + test("legacyYamlStreamImpliesYamlStream") { + // --legacy-yaml-stream is exclusive with --no-trailing-newline, just like --yaml-stream. + val (res, out, err) = runMain("[{a: 1}]", "--exec", "--legacy-yaml-stream", "--no-trailing-newline") + assert(res == 1) + assert(err.contains("cannot use --no-trailing-newline with --yaml-stream")) + } + // -- No trailing newline behavior -- test("noTrailingNewline") {