mirror of
https://github.com/larksuite/cli.git
synced 2026-07-03 14:02:43 +08:00
83 lines
2.5 KiB
Go
83 lines
2.5 KiB
Go
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package cmdutil
|
|
|
|
import (
|
|
"io"
|
|
"os"
|
|
|
|
"golang.org/x/term"
|
|
)
|
|
|
|
// IOStreams provides the standard input/output/error streams.
|
|
// Commands should use these instead of os.Stdin/Stdout/Stderr
|
|
// to enable testing and output capture.
|
|
type IOStreams struct {
|
|
In io.Reader
|
|
Out io.Writer
|
|
ErrOut io.Writer
|
|
IsTerminal bool
|
|
// OutIsTerminal reports whether Out is an interactive terminal. Mirrors
|
|
// IsTerminal; computed once in NewIOStreams and assignable directly in tests.
|
|
OutIsTerminal bool
|
|
// StderrIsTerminal reports whether ErrOut is an interactive terminal.
|
|
// Advisory warnings written to stderr (e.g. the proxy notice) gate on this
|
|
// so they stay out of non-interactive output (pipes, CI, agent runs).
|
|
// Computed once in NewIOStreams, mirroring IsTerminal; tests assign it
|
|
// directly like cmd/config/bind_test.go does for IsTerminal.
|
|
StderrIsTerminal bool
|
|
}
|
|
|
|
// NewIOStreams builds an IOStreams from arbitrary readers/writers.
|
|
// IsTerminal / OutIsTerminal / StderrIsTerminal are each derived from the
|
|
// underlying *os.File of in / out / errOut respectively; non-file
|
|
// readers/writers (bytes.Buffer, strings.Reader, …) yield false.
|
|
func NewIOStreams(in io.Reader, out, errOut io.Writer) *IOStreams {
|
|
fileIsTerminal := func(v any) bool {
|
|
if f, ok := v.(*os.File); ok {
|
|
return term.IsTerminal(int(f.Fd()))
|
|
}
|
|
return false
|
|
}
|
|
return &IOStreams{
|
|
In: in,
|
|
Out: out,
|
|
ErrOut: errOut,
|
|
IsTerminal: fileIsTerminal(in),
|
|
OutIsTerminal: fileIsTerminal(out),
|
|
StderrIsTerminal: fileIsTerminal(errOut),
|
|
}
|
|
}
|
|
|
|
// SystemIO creates an IOStreams wired to the process's standard file descriptors.
|
|
//
|
|
//nolint:forbidigo // entry point for real stdio
|
|
func SystemIO() *IOStreams {
|
|
return NewIOStreams(os.Stdin, os.Stdout, os.Stderr)
|
|
}
|
|
|
|
// normalizeStreams returns a fresh IOStreams with any nil field filled from
|
|
// SystemIO(). Callers constructing a partial struct like &IOStreams{Out: buf}
|
|
// get a usable result without nil writers leaking into RoundTripper warnings,
|
|
// Cobra I/O, or credential-provider error paths.
|
|
func normalizeStreams(s *IOStreams) *IOStreams {
|
|
if s == nil {
|
|
return SystemIO()
|
|
}
|
|
out := *s
|
|
if out.In == nil || out.Out == nil || out.ErrOut == nil {
|
|
sys := SystemIO()
|
|
if out.In == nil {
|
|
out.In = sys.In
|
|
}
|
|
if out.Out == nil {
|
|
out.Out = sys.Out
|
|
}
|
|
if out.ErrOut == nil {
|
|
out.ErrOut = sys.ErrOut
|
|
}
|
|
}
|
|
return &out
|
|
}
|