Repository: moby/moby
File: client/pkg/jsonmessage/jsonmessage.go (Go)

Code before:
	if isTerminal && jm.Stream == "" && jm.Progress != nil {
		clearLine(out)
		endl = "\r"
		_, _ = fmt.Fprint(out, endl)
	} else if jm.Progress != nil && jm.Progress.String() != "" { // disable progressbar in non-terminal
		return nil
	}
	if jm.ID != "" {
		_, _ = fmt.Fprintf(out, "%s: ", jm.ID)
	}
	if jm.Progress != nil && isTerminal {
		_, _ = fmt.Fprintf(out, "%s %s%s", jm.Status, jm.Progress.String(), endl)
	} else if jm.Stream != "" {
		_, _ = fmt.Fprintf(out, "%s%s", jm.Stream, endl)
	} else {
		_, _ = fmt.Fprintf(out, "%s%s\n", jm.Status, endl)
	}
	return nil
}

type JSONMessagesStream iter.Seq2[JSONMessage, error]

// DisplayJSONMessagesStream reads a JSON message stream from in, and writes
// each [JSONMessage] to out. It returns an error if an invalid JSONMessage
// is received, or if a JSONMessage containers a non-zero [JSONMessage.Error].
//
// Presentation of the JSONMessage depends on whether a terminal is attached,
// and on the terminal width. Progress bars ([JSONProgress]) are suppressed
// on narrower terminals (< 110 characters).
//
//   - isTerminal describes if out is a terminal, in which case it prints
//     a newline ("\n") at the end of each line and moves the cursor while
//     displaying.
//   - terminalFd is the fd of the current terminal (if any), and used
//     to get the terminal width.
//   - auxCallback allows handling the [JSONMessage.Aux] field. It is
//     called if a JSONMessage contains an Aux field, in which case
//     DisplayJSONMessagesStream does not present the JSONMessage.
func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr, isTerminal bool, auxCallback func(JSONMessage)) error {
	var dec = json.NewDecoder(in)
	var f JSONMessagesStream = func(yield func(JSONMessage, error) bool) {
		for {
			var jm JSONMessage
			err := dec.Decode(&jm)
			if errors.Is(err, io.EOF) {
				break
			}
			if !yield(jm, err) {
				return
			}
		}

Review comment (suggestion):
Just a "note to self", as @vvoland 's comment reminded me of that. 

I recall when we moved this package that we also considered cleaning up the API; there was a change in the CLI to improve some bits (passing context etc), but also to properly take advantage of the package name, so changing these to `jsonmessage.Stream`  and `jsonmessage.Display` (or `jsonmessage.DisplayStream`).

https://github.com/docker/cli/blob/v28.5.0/internal/jsonstream/display.go


NOT a blocker for this PR IMO, as it's some bike-shedding, but we should look at that before we release.



Code after:
	if isTerminal && jm.Stream == "" && jm.Progress != nil {
		clearLine(out)
		endl = "\r"
		_, _ = fmt.Fprint(out, endl)
	} else if jm.Progress != nil && jm.Progress.String() != "" { // disable progressbar in non-terminal
		return nil
	}
	if jm.ID != "" {
		_, _ = fmt.Fprintf(out, "%s: ", jm.ID)
	}
	if jm.Progress != nil && isTerminal {
		_, _ = fmt.Fprintf(out, "%s %s%s", jm.Status, jm.Progress.String(), endl)
	} else if jm.Stream != "" {
		_, _ = fmt.Fprintf(out, "%s%s", jm.Stream, endl)
	} else {
		_, _ = fmt.Fprintf(out, "%s%s\n", jm.Status, endl)
	}
	return nil
}

type JSONMessagesStream iter.Seq2[JSONMessage, error]

// DisplayJSONMessagesStream reads a JSON message stream from in, and writes
// each [JSONMessage] to out.
// see DisplayJSONMessages for details
func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr, isTerminal bool, auxCallback func(JSONMessage)) error {
	var dec = json.NewDecoder(in)
	var f JSONMessagesStream = func(yield func(JSONMessage, error) bool) {
		for {
			var jm JSONMessage
			err := dec.Decode(&jm)
			if errors.Is(err, io.EOF) {
				break
			}
			if !yield(jm, err) {
				return
			}
		}
	}

	return DisplayJSONMessages(f, out, terminalFd, isTerminal, auxCallback)
}

// DisplayJSONMessages writes each [JSONMessage] from stream to out.
// It returns an error if an invalid JSONMessage is received, or if
// a JSONMessage containers a non-zero [JSONMessage.Error].
//
// Presentation of the JSONMessage depends on whether a terminal is attached,
// and on the terminal width. Progress bars ([JSONProgress]) are suppressed
// on narrower terminals (< 110 characters).
//