feat: detect os.Stdin/os.Stdout mode

Steve Moyer created

Change summary

commands/execenv/env.go      | 33 ++++++++++++++++++-
commands/execenv/env_test.go | 61 ++++++++++++++++++++++++++++++++++++++
2 files changed, 91 insertions(+), 3 deletions(-)

Detailed changes

commands/execenv/env.go 🔗

@@ -20,19 +20,46 @@ const RootCommandName = "git-bug"
 
 const gitBugNamespace = "git-bug"
 
+type IOMode int
+
+const (
+	UnknownIOMode IOMode = iota
+	TerminalIOMode
+	PipedOrRedirectedIOMode
+)
+
+func getIOMode(io *os.File) IOMode {
+	info, err := io.Stat()
+	if err != nil {
+		panic("only os.StdIn or os.Stdout should be passed to this method")
+	}
+
+	if (info.Mode() & os.ModeCharDevice) == os.ModeCharDevice {
+		return TerminalIOMode
+	}
+
+	return PipedOrRedirectedIOMode
+}
+
 // Env is the environment of a command
 type Env struct {
 	Repo    repository.ClockedRepo
 	Backend *cache.RepoCache
+	In      io.Reader
+	InMode  IOMode
 	Out     Out
+	OutMode IOMode
 	Err     Out
 }
 
 func NewEnv() *Env {
 	return &Env{
-		Repo: nil,
-		Out:  out{Writer: os.Stdout},
-		Err:  out{Writer: os.Stderr},
+		Repo:    nil,
+		In:      os.Stdin,
+		InMode:  getIOMode(os.Stdin),
+		Out:     out{Writer: os.Stdout},
+		OutMode: getIOMode(os.Stdout),
+		Err:     out{Writer: os.Stderr},
 	}
 }
 

commands/execenv/env_test.go 🔗

@@ -0,0 +1,61 @@
+package execenv
+
+import (
+	"os"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestGetIOMode(t *testing.T) {
+	r, w, err := os.Pipe()
+	require.NoError(t, err)
+
+	testcases := []struct {
+		name       string
+		in         *os.File
+		out        *os.File
+		expInMode  IOMode
+		expOutMode IOMode
+	}{
+		{
+			name:       "neither redirected",
+			in:         os.Stdin,
+			out:        os.Stdout,
+			expInMode:  TerminalIOMode,
+			expOutMode: TerminalIOMode,
+		},
+		{
+			name:       "in redirected",
+			in:         w,
+			out:        os.Stdout,
+			expInMode:  TerminalIOMode,
+			expOutMode: TerminalIOMode,
+		},
+		{
+			name:       "out redirected",
+			in:         os.Stdin,
+			out:        r,
+			expInMode:  TerminalIOMode,
+			expOutMode: TerminalIOMode,
+		},
+		{
+			name:       "both redirected",
+			in:         w,
+			out:        r,
+			expInMode:  PipedOrRedirectedIOMode,
+			expOutMode: PipedOrRedirectedIOMode,
+		},
+	}
+
+	for i := range testcases {
+		testcase := testcases[i]
+
+		t.Run(testcase.name, func(t *testing.T) {
+			t.Parallel()
+
+			env := NewEnv()
+			require.NotNil(t, env)
+		})
+	}
+}