aboutsummaryrefslogblamecommitdiffstats
path: root/commands/execenv/env.go
blob: e693807e6ddecc44737c4e9cb874242f9741700d (plain) (tree)
1
2
3
4
5
6
7
8
9
               

        
                       



             
                                    
                           
 
                                              


                                                   

                                 
                                 
 

                                      




                                      

 
                    
                    



                                             


         











                                                                                  

                    
 


                                               
                                      
 


                                                                                        

                                                                                   




                                                                              








                                                                                   



                                                                          
 












                                             

 














                                                      
 








                                                     




                                             

 









                                                          



                              

                                          

 

                                          

 

                                          

 

                                          
 
 

                                                                                 
 
package execenv

import (
	"encoding/json"
	"fmt"
	"io"
	"os"

	"github.com/mattn/go-isatty"
	"golang.org/x/term"

	"github.com/MichaelMure/git-bug/cache"
	"github.com/MichaelMure/git-bug/repository"
)

const RootCommandName = "git-bug"

const gitBugNamespace = "git-bug"

// Env is the environment of a command
type Env struct {
	Repo    repository.ClockedRepo
	Backend *cache.RepoCache
	In      In
	Out     Out
	Err     Out
}

func NewEnv() *Env {
	return &Env{
		Repo: nil,
		In:   in{Reader: os.Stdin},
		Out:  out{Writer: os.Stdout},
		Err:  out{Writer: os.Stderr},
	}
}

type In interface {
	io.Reader

	// IsTerminal tells if the input is a user terminal (rather than a buffer,
	// a pipe ...), which tells if we can use interactive features.
	IsTerminal() bool

	// ForceIsTerminal allow to force the returned value of IsTerminal
	// This only works in test scenario.
	ForceIsTerminal(value bool)
}

type Out interface {
	io.Writer

	Printf(format string, a ...interface{})
	Print(a ...interface{})
	Println(a ...interface{})
	PrintJSON(v interface{}) error

	// IsTerminal tells if the output is a user terminal (rather than a buffer,
	// a pipe ...), which tells if we can use colors and other interactive features.
	IsTerminal() bool
	// Width return the width of the attached terminal, or a good enough value.
	Width() int

	// Raw return the underlying io.Writer, or itself if not.
	// This is useful if something need to access the raw file descriptor.
	Raw() io.Writer

	// String returns what have been written in the output before, as a string.
	// This only works in test scenario.
	String() string
	// Bytes returns what have been written in the output before, as []byte.
	// This only works in test scenario.
	Bytes() []byte
	// Reset clear what has been recorded as written in the output before.
	// This only works in test scenario.
	Reset()
	// ForceIsTerminal allow to force the returned value of IsTerminal
	// This only works in test scenario.
	ForceIsTerminal(value bool)
}

type in struct {
	io.Reader
}

func (i in) IsTerminal() bool {
	if f, ok := i.Reader.(*os.File); ok {
		return isTerminal(f)
	}
	return false
}

func (i in) ForceIsTerminal(_ bool) {
	panic("only work with a test env")
}

type out struct {
	io.Writer
}

func (o out) Printf(format string, a ...interface{}) {
	_, _ = fmt.Fprintf(o, format, a...)
}

func (o out) Print(a ...interface{}) {
	_, _ = fmt.Fprint(o, a...)
}

func (o out) Println(a ...interface{}) {
	_, _ = fmt.Fprintln(o, a...)
}

func (o out) PrintJSON(v interface{}) error {
	raw, err := json.MarshalIndent(v, "", "    ")
	if err != nil {
		return err
	}
	o.Println(string(raw))
	return nil
}

func (o out) IsTerminal() bool {
	if f, ok := o.Writer.(*os.File); ok {
		return isTerminal(f)
	}
	return false
}

func (o out) Width() int {
	if f, ok := o.Raw().(*os.File); ok {
		width, _, err := term.GetSize(int(f.Fd()))
		if err == nil {
			return width
		}
	}
	return 80
}

func (o out) Raw() io.Writer {
	return o.Writer
}

func (o out) String() string {
	panic("only work with a test env")
}

func (o out) Bytes() []byte {
	panic("only work with a test env")
}

func (o out) Reset() {
	panic("only work with a test env")
}

func (o out) ForceIsTerminal(_ bool) {
	panic("only work with a test env")
}

func isTerminal(file *os.File) bool {
	return isatty.IsTerminal(file.Fd()) || isatty.IsCygwinTerminal(file.Fd())
}