diff --git a/cmd/jsonnet/cmd.go b/cmd/jsonnet/cmd.go index 464108d1b..9dab7ae78 100644 --- a/cmd/jsonnet/cmd.go +++ b/cmd/jsonnet/cmd.go @@ -51,6 +51,7 @@ func usage(o io.Writer) { fmt.Fprintln(o, " files") fmt.Fprintln(o, " -y / --yaml-stream Write output as a YAML stream of JSON documents") fmt.Fprintln(o, " -S / --string Expect a string, manifest as plain text") + fmt.Fprintln(o, " --no-trailing-newline Do not add a trailing newline to the output") fmt.Fprintln(o, " -s / --max-stack Number of allowed stack frames") fmt.Fprintln(o, " -t / --max-trace Max length of stack trace before cropping") fmt.Fprintln(o, " --version Print version") @@ -256,6 +257,8 @@ func processArgs(givenArgs []string, config *config, vm *jsonnet.VM) (processArg config.evalStream = true } else if arg == "-S" || arg == "--string" { vm.StringOutput = true + } else if arg == "--no-trailing-newline" { + vm.OutputNewline = false } else if len(arg) > 1 && arg[0] == '-' { return processArgsStatusFailure, fmt.Errorf("unrecognized argument: %s", arg) } else { @@ -263,6 +266,12 @@ func processArgs(givenArgs []string, config *config, vm *jsonnet.VM) (processArg } } + // --no-trailing-newline is meaningless when used with --yaml-stream + // so we explicitly reject it to prevent people from relying on it. + if config.evalStream && !vm.OutputNewline { + return processArgsStatusFailure, fmt.Errorf("cannot use --no-trailing-newline with --yaml-stream") + } + want := "filename" if config.filenameIsCode { want = "code" diff --git a/interpreter.go b/interpreter.go index 4bec1f6e9..26df98a2e 100644 --- a/interpreter.go +++ b/interpreter.go @@ -902,7 +902,7 @@ func (i *interpreter) manifestString(buf *bytes.Buffer, v value) error { } } -func (i *interpreter) manifestAndSerializeMulti(v value, stringOutputMode bool) (r map[string]string, err error) { +func (i *interpreter) manifestAndSerializeMulti(v value, stringOutputMode bool, outputNewline bool) (r map[string]string, err error) { r = make(map[string]string) json, err := i.manifestJSON(v) if err != nil { @@ -911,21 +911,23 @@ func (i *interpreter) manifestAndSerializeMulti(v value, stringOutputMode bool) switch json := json.(type) { case map[string]interface{}: for filename, fileJSON := range json { + var buf bytes.Buffer if stringOutputMode { switch val := fileJSON.(type) { case string: - r[filename] = val + buf.WriteString(val) default: msg := fmt.Sprintf("multi mode: top-level object's key %s has a value of type %T, "+ "should be a string", filename, val) return r, makeRuntimeError(msg, i.getCurrentStackTrace()) } } else { - var buf bytes.Buffer serializeJSON(fileJSON, true, "", &buf) + } + if outputNewline { buf.WriteString("\n") - r[filename] = buf.String() } + r[filename] = buf.String() } default: msg := fmt.Sprintf("multi mode: top-level object was a %s, "+ @@ -1350,15 +1352,7 @@ func evaluateAux(i *interpreter, node ast.Node, tla vmExtMap) (value, error) { return result, nil } -// TODO(sbarzowski) this function takes far too many arguments - build interpreter in vm instead -func evaluate(node ast.Node, ext vmExtMap, tla vmExtMap, nativeFuncs map[string]*NativeFunction, - maxStack int, ic *importCache, traceOut io.Writer, stringOutputMode bool, evalHook EvalHook) (string, error) { - - i, err := buildInterpreter(ext, nativeFuncs, maxStack, ic, traceOut, evalHook) - if err != nil { - return "", err - } - +func evaluate(i *interpreter, node ast.Node, tla vmExtMap, stringOutputMode bool, outputNewline bool) (string, error) { result, err := evaluateAux(i, node, tla) if err != nil { return "", err @@ -1375,39 +1369,25 @@ func evaluate(node ast.Node, ext vmExtMap, tla vmExtMap, nativeFuncs map[string] if err != nil { return "", err } - buf.WriteString("\n") + if outputNewline { + buf.WriteString("\n") + } return buf.String(), nil } -// TODO(sbarzowski) this function takes far too many arguments - build interpreter in vm instead -func evaluateMulti(node ast.Node, ext vmExtMap, tla vmExtMap, nativeFuncs map[string]*NativeFunction, - maxStack int, ic *importCache, traceOut io.Writer, stringOutputMode bool, evalHook EvalHook) (map[string]string, error) { - - i, err := buildInterpreter(ext, nativeFuncs, maxStack, ic, traceOut, evalHook) - if err != nil { - return nil, err - } - +func evaluateMulti(i *interpreter, node ast.Node, tla vmExtMap, stringOutputMode bool, outputNewline bool) (map[string]string, error) { result, err := evaluateAux(i, node, tla) if err != nil { return nil, err } i.stack.setCurrentTrace(manifestationTrace()) - manifested, err := i.manifestAndSerializeMulti(result, stringOutputMode) + manifested, err := i.manifestAndSerializeMulti(result, stringOutputMode, outputNewline) i.stack.clearCurrentTrace() return manifested, err } -// TODO(sbarzowski) this function takes far too many arguments - build interpreter in vm instead -func evaluateStream(node ast.Node, ext vmExtMap, tla vmExtMap, nativeFuncs map[string]*NativeFunction, - maxStack int, ic *importCache, traceOut io.Writer, evalHook EvalHook) ([]string, error) { - - i, err := buildInterpreter(ext, nativeFuncs, maxStack, ic, traceOut, evalHook) - if err != nil { - return nil, err - } - +func evaluateStream(i *interpreter, node ast.Node, tla vmExtMap) ([]string, error) { result, err := evaluateAux(i, node, tla) if err != nil { return nil, err diff --git a/main_test.go b/main_test.go index 37fd5193a..b14f07268 100644 --- a/main_test.go +++ b/main_test.go @@ -108,6 +108,7 @@ type jsonnetInput struct { input []byte eKind evalKind stringOutputMode bool + noNewline bool // not nice to have a negative flag, but it gives the more relevant default extVars map[string]string extCode map[string]string } @@ -134,6 +135,7 @@ func runInternalJsonnet(i jsonnetInput) jsonnetResult { errFormatter := termErrorFormatter{pretty: true, maxStackTraceSize: 9} vm.StringOutput = i.stringOutputMode + vm.OutputNewline = !i.noNewline for name, value := range i.extVars { vm.ExtVar(name, value) } @@ -336,6 +338,7 @@ func runTest(t *testing.T, test *mainTest) { input: input, eKind: eKind, stringOutputMode: strings.HasSuffix(test.golden, "_string_output.golden"), + noNewline: strings.Contains(test.golden, "_no_newline"), extVars: test.meta.extVars, extCode: test.meta.extCode, }) diff --git a/testdata/cpp-tests-override/help.golden.stdout b/testdata/cpp-tests-override/help.golden.stdout new file mode 100644 index 000000000..11f96d1f5 --- /dev/null +++ b/testdata/cpp-tests-override/help.golden.stdout @@ -0,0 +1,55 @@ +Jsonnet commandline interpreter (Go implementation) v0.21.0 + +jsonnet {