terraform/internal/terminal/streams.go

106 lines
4.0 KiB
Go

// Package terminal encapsulates some platform-specific logic for detecting
// if we're running in a terminal and, if so, properly configuring that
// terminal to meet the assumptions that the rest of Terraform makes.
//
// Specifically, Terraform requires a Terminal which supports virtual terminal
// sequences and which accepts UTF-8-encoded text.
//
// This is an abstraction only over the platform-specific detection of and
// possibly initialization of terminals. It's not intended to provide
// higher-level abstractions of the sort provided by packages like termcap or
// curses; ultimately we just assume that terminals are "standard" VT100-like
// terminals and use a subset of control codes that works across the various
// platforms we support. Our approximate target is "xterm-compatible"
// virtual terminals.
package terminal
import (
"fmt"
"os"
)
// Streams represents a collection of three streams that each may or may not
// be connected to a terminal.
//
// If a stream is connected to a terminal then there are more possibilities
// available, such as detecting the current terminal width. If we're connected
// to something else, such as a pipe or a file on disk, the stream will
// typically provide placeholder values or do-nothing stubs for
// terminal-requiring operatons.
//
// Note that it's possible for only a subset of the streams to be connected
// to a terminal. For example, this happens if the user runs Terraform with
// I/O redirection where Stdout might refer to a regular disk file while Stderr
// refers to a terminal, or various other similar combinations.
type Streams struct {
Stdout *OutputStream
Stderr *OutputStream
Stdin *InputStream
}
// Init tries to initialize a terminal, if Terraform is running in one, and
// returns an object describing what it was able to set up.
//
// An error for this function indicates that the current execution context
// can't meet Terraform's assumptions. For example, on Windows Init will return
// an error if Terraform is running in a Windows Console that refuses to
// activate UTF-8 mode, which can happen if we're running on an unsupported old
// version of Windows.
//
// Note that the success of this function doesn't mean that we're actually
// running in a terminal. It could also represent successfully detecting that
// one or more of the input/output streams is not a terminal.
func Init() (*Streams, error) {
// These configure* functions are platform-specific functions in other
// files that use //+build constraints to vary based on target OS.
stderr, err := configureOutputHandle(os.Stderr)
if err != nil {
return nil, err
}
stdout, err := configureOutputHandle(os.Stdout)
if err != nil {
return nil, err
}
stdin, err := configureInputHandle(os.Stdin)
if err != nil {
return nil, err
}
return &Streams{
Stdout: stdout,
Stderr: stderr,
Stdin: stdin,
}, nil
}
// Print is a helper for conveniently calling fmt.Fprint on the Stdout stream.
func (s *Streams) Print(a ...interface{}) (n int, err error) {
return fmt.Fprint(s.Stdout.File, a...)
}
// Printf is a helper for conveniently calling fmt.Fprintf on the Stdout stream.
func (s *Streams) Printf(format string, a ...interface{}) (n int, err error) {
return fmt.Fprintf(s.Stdout.File, format, a...)
}
// Println is a helper for conveniently calling fmt.Fprintln on the Stdout stream.
func (s *Streams) Println(a ...interface{}) (n int, err error) {
return fmt.Fprintln(s.Stdout.File, a...)
}
// Eprint is a helper for conveniently calling fmt.Fprint on the Stderr stream.
func (s *Streams) Eprint(a ...interface{}) (n int, err error) {
return fmt.Fprint(s.Stderr.File, a...)
}
// Eprintf is a helper for conveniently calling fmt.Fprintf on the Stderr stream.
func (s *Streams) Eprintf(format string, a ...interface{}) (n int, err error) {
return fmt.Fprintf(s.Stderr.File, format, a...)
}
// Eprintln is a helper for conveniently calling fmt.Fprintln on the Stderr stream.
func (s *Streams) Eprintln(a ...interface{}) (n int, err error) {
return fmt.Fprintln(s.Stderr.File, a...)
}