env.go

  1package execenv
  2
  3import (
  4	"encoding/json"
  5	"fmt"
  6	"io"
  7	"os"
  8
  9	"github.com/mattn/go-isatty"
 10
 11	"github.com/MichaelMure/git-bug/cache"
 12	"github.com/MichaelMure/git-bug/repository"
 13)
 14
 15const RootCommandName = "git-bug"
 16
 17const gitBugNamespace = "git-bug"
 18
 19// Env is the environment of a command
 20type Env struct {
 21	Repo    repository.ClockedRepo
 22	Backend *cache.RepoCache
 23	In      In
 24	Out     Out
 25	Err     Out
 26}
 27
 28func NewEnv() *Env {
 29	return &Env{
 30		Repo: nil,
 31		In:   in{Reader: os.Stdin},
 32		Out:  out{Writer: os.Stdout},
 33		Err:  out{Writer: os.Stderr},
 34	}
 35}
 36
 37type In interface {
 38	io.Reader
 39
 40	// IsTerminal tells if the input is a user terminal (rather than a buffer,
 41	// a pipe ...), which tells if we can use interactive features.
 42	IsTerminal() bool
 43
 44	// ForceIsTerminal allow to force the returned value of IsTerminal
 45	// This only works in test scenario.
 46	ForceIsTerminal(value bool)
 47}
 48
 49type Out interface {
 50	io.Writer
 51
 52	Printf(format string, a ...interface{})
 53	Print(a ...interface{})
 54	Println(a ...interface{})
 55	PrintJSON(v interface{}) error
 56
 57	// IsTerminal tells if the output is a user terminal (rather than a buffer,
 58	// a pipe ...), which tells if we can use colors and other interactive features.
 59	IsTerminal() bool
 60
 61	// Raw return the underlying io.Writer, or itself if not.
 62	// This is useful if something need to access the raw file descriptor.
 63	Raw() io.Writer
 64
 65	// String returns what have been written in the output before, as a string.
 66	// This only works in test scenario.
 67	String() string
 68	// Bytes returns what have been written in the output before, as []byte.
 69	// This only works in test scenario.
 70	Bytes() []byte
 71	// Reset clear what has been recorded as written in the output before.
 72	// This only works in test scenario.
 73	Reset()
 74	// ForceIsTerminal allow to force the returned value of IsTerminal
 75	// This only works in test scenario.
 76	ForceIsTerminal(value bool)
 77}
 78
 79type in struct {
 80	io.Reader
 81}
 82
 83func (i in) IsTerminal() bool {
 84	if f, ok := i.Reader.(*os.File); ok {
 85		return isTerminal(f)
 86	}
 87	return false
 88}
 89
 90func (i in) ForceIsTerminal(_ bool) {
 91	panic("only work with a test env")
 92}
 93
 94type out struct {
 95	io.Writer
 96}
 97
 98func (o out) Printf(format string, a ...interface{}) {
 99	_, _ = fmt.Fprintf(o, format, a...)
100}
101
102func (o out) Print(a ...interface{}) {
103	_, _ = fmt.Fprint(o, a...)
104}
105
106func (o out) Println(a ...interface{}) {
107	_, _ = fmt.Fprintln(o, a...)
108}
109
110func (o out) PrintJSON(v interface{}) error {
111	raw, err := json.MarshalIndent(v, "", "    ")
112	if err != nil {
113		return err
114	}
115	o.Println(string(raw))
116	return nil
117}
118
119func (o out) IsTerminal() bool {
120	if f, ok := o.Writer.(*os.File); ok {
121		return isTerminal(f)
122	}
123	return false
124}
125
126func (o out) Raw() io.Writer {
127	return o.Writer
128}
129
130func (o out) String() string {
131	panic("only work with a test env")
132}
133
134func (o out) Bytes() []byte {
135	panic("only work with a test env")
136}
137
138func (o out) Reset() {
139	panic("only work with a test env")
140}
141
142func (o out) ForceIsTerminal(_ bool) {
143	panic("only work with a test env")
144}
145
146func isTerminal(file *os.File) bool {
147	return isatty.IsTerminal(file.Fd()) || isatty.IsCygwinTerminal(file.Fd())
148}