github.com/eliastor/durgaform@v0.0.0-20220816172711-d0ab2d17673e/internal/terminal/streams.go (about)

     1  // Package terminal encapsulates some platform-specific logic for detecting
     2  // if we're running in a terminal and, if so, properly configuring that
     3  // terminal to meet the assumptions that the rest of Durgaform makes.
     4  //
     5  // Specifically, Durgaform requires a Terminal which supports virtual terminal
     6  // sequences and which accepts UTF-8-encoded text.
     7  //
     8  // This is an abstraction only over the platform-specific detection of and
     9  // possibly initialization of terminals. It's not intended to provide
    10  // higher-level abstractions of the sort provided by packages like termcap or
    11  // curses; ultimately we just assume that terminals are "standard" VT100-like
    12  // terminals and use a subset of control codes that works across the various
    13  // platforms we support. Our approximate target is "xterm-compatible"
    14  // virtual terminals.
    15  package terminal
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  )
    21  
    22  // Streams represents a collection of three streams that each may or may not
    23  // be connected to a terminal.
    24  //
    25  // If a stream is connected to a terminal then there are more possibilities
    26  // available, such as detecting the current terminal width. If we're connected
    27  // to something else, such as a pipe or a file on disk, the stream will
    28  // typically provide placeholder values or do-nothing stubs for
    29  // terminal-requiring operatons.
    30  //
    31  // Note that it's possible for only a subset of the streams to be connected
    32  // to a terminal. For example, this happens if the user runs Durgaform with
    33  // I/O redirection where Stdout might refer to a regular disk file while Stderr
    34  // refers to a terminal, or various other similar combinations.
    35  type Streams struct {
    36  	Stdout *OutputStream
    37  	Stderr *OutputStream
    38  	Stdin  *InputStream
    39  }
    40  
    41  // Init tries to initialize a terminal, if Durgaform is running in one, and
    42  // returns an object describing what it was able to set up.
    43  //
    44  // An error for this function indicates that the current execution context
    45  // can't meet Durgaform's assumptions. For example, on Windows Init will return
    46  // an error if Durgaform is running in a Windows Console that refuses to
    47  // activate UTF-8 mode, which can happen if we're running on an unsupported old
    48  // version of Windows.
    49  //
    50  // Note that the success of this function doesn't mean that we're actually
    51  // running in a terminal. It could also represent successfully detecting that
    52  // one or more of the input/output streams is not a terminal.
    53  func Init() (*Streams, error) {
    54  	// These configure* functions are platform-specific functions in other
    55  	// files that use //+build constraints to vary based on target OS.
    56  
    57  	stderr, err := configureOutputHandle(os.Stderr)
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  	stdout, err := configureOutputHandle(os.Stdout)
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  	stdin, err := configureInputHandle(os.Stdin)
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  
    70  	return &Streams{
    71  		Stdout: stdout,
    72  		Stderr: stderr,
    73  		Stdin:  stdin,
    74  	}, nil
    75  }
    76  
    77  // Print is a helper for conveniently calling fmt.Fprint on the Stdout stream.
    78  func (s *Streams) Print(a ...interface{}) (n int, err error) {
    79  	return fmt.Fprint(s.Stdout.File, a...)
    80  }
    81  
    82  // Printf is a helper for conveniently calling fmt.Fprintf on the Stdout stream.
    83  func (s *Streams) Printf(format string, a ...interface{}) (n int, err error) {
    84  	return fmt.Fprintf(s.Stdout.File, format, a...)
    85  }
    86  
    87  // Println is a helper for conveniently calling fmt.Fprintln on the Stdout stream.
    88  func (s *Streams) Println(a ...interface{}) (n int, err error) {
    89  	return fmt.Fprintln(s.Stdout.File, a...)
    90  }
    91  
    92  // Eprint is a helper for conveniently calling fmt.Fprint on the Stderr stream.
    93  func (s *Streams) Eprint(a ...interface{}) (n int, err error) {
    94  	return fmt.Fprint(s.Stderr.File, a...)
    95  }
    96  
    97  // Eprintf is a helper for conveniently calling fmt.Fprintf on the Stderr stream.
    98  func (s *Streams) Eprintf(format string, a ...interface{}) (n int, err error) {
    99  	return fmt.Fprintf(s.Stderr.File, format, a...)
   100  }
   101  
   102  // Eprintln is a helper for conveniently calling fmt.Fprintln on the Stderr stream.
   103  func (s *Streams) Eprintln(a ...interface{}) (n int, err error) {
   104  	return fmt.Fprintln(s.Stderr.File, a...)
   105  }