@@ -18,37 +18,35 @@ var (
1818
1919// IO is an interface to work with input/output.
2020type IO interface {
21- Stdin () io.Reader
22- Stdout () io.Writer
23- Prompts () (io.Reader , io.Writer , error )
24- IsStdinPiped () bool
25- IsStdoutPiped () bool
21+ Stdin () Reader
22+ Stdout () Writer
23+ Prompts () (Reader , Writer , error )
2624}
2725
2826// UserIO is a middleware between input and output to the CLI program.
2927// It implements userIO.Prompter and can be passed to libraries.
3028type UserIO struct {
31- Input * os. File
32- Output * os. File
33- tty * os. File
29+ Input Reader
30+ Output Writer
31+ tty file
3432 ttyAvailable bool
3533}
3634
3735// NewStdUserIO creates a new UserIO middleware only from os.Stdin and os.Stdout.
3836func NewStdUserIO () UserIO {
3937 return UserIO {
40- Input : os .Stdin ,
41- Output : os .Stdout ,
38+ Input : file { os .Stdin } ,
39+ Output : file { os .Stdout } ,
4240 }
4341}
4442
4543// Stdin returns the UserIO's Input.
46- func (o UserIO ) Stdin () io. Reader {
44+ func (o UserIO ) Stdin () Reader {
4745 return o .Input
4846}
4947
5048// Stdout returns the UserIO's Output.
51- func (o UserIO ) Stdout () io. Writer {
49+ func (o UserIO ) Stdout () Writer {
5250 return o .Output
5351}
5452
@@ -57,8 +55,8 @@ func (o UserIO) Stdout() io.Writer {
5755// bypass stdin and stdout by connecting to /dev/tty on Unix systems when
5856// available. On systems where tty is not available and when either input
5957// or output is piped, prompting is not possible so an error is returned.
60- func (o UserIO ) Prompts () (io. Reader , io. Writer , error ) {
61- if o .IsStdoutPiped () || o .IsStdinPiped () {
58+ func (o UserIO ) Prompts () (Reader , Writer , error ) {
59+ if o .Input . IsPiped () || o .Output . IsPiped () {
6260 if o .ttyAvailable {
6361 return o .tty , o .tty , nil
6462 }
@@ -67,30 +65,12 @@ func (o UserIO) Prompts() (io.Reader, io.Writer, error) {
6765 return o .Input , o .Output , nil
6866}
6967
70- func (o UserIO ) IsStdinPiped () bool {
71- return isPiped (o .Input )
72- }
73-
74- func (o UserIO ) IsStdoutPiped () bool {
75- return isPiped (o .Output )
76- }
77-
78- // readPassword reads one line of input from the terminal without echoing the user input.
79- func readPassword (r io.Reader ) (string , error ) {
80- file , ok := r .(* os.File )
81- if ! ok {
82- return "" , ErrCannotAsk
83- }
84- // this case happens among other things when input is piped and ReadPassword is called.
85- if ! terminal .IsTerminal (int (file .Fd ())) {
86- return "" , ErrCannotAsk
87- }
88-
89- password , err := terminal .ReadPassword (int (file .Fd ()))
90- if err != nil {
91- return "" , err
92- }
93- return string (password ), nil
68+ // Reader can read input for a CLI program.
69+ type Reader interface {
70+ io.Reader
71+ // ReadPassword reads a line of input from a terminal without local echo.
72+ ReadPassword () ([]byte , error )
73+ IsPiped () bool
9474}
9575
9676// Readln reads 1 line of input from a io.Reader. The newline character is not included in the response.
@@ -104,10 +84,31 @@ func Readln(r io.Reader) (string, error) {
10484 return s .Text (), nil
10585}
10686
107- // isPiped checks whether the file is a pipe.
87+ // Writer can write output for a CLI program.
88+ type Writer interface {
89+ io.Writer
90+ IsPiped () bool
91+ }
92+
93+ // file implements the Reader and Writer interface.
94+ type file struct {
95+ * os.File
96+ }
97+
98+ // ReadPassword reads from a terminal without echoing back the typed input.
99+ func (f file ) ReadPassword () ([]byte , error ) {
100+ // this case happens among other things when input is piped and ReadPassword is called.
101+ if ! terminal .IsTerminal (int (f .Fd ())) {
102+ return nil , ErrCannotAsk
103+ }
104+
105+ return terminal .ReadPassword (int (f .Fd ()))
106+ }
107+
108+ // IsPiped checks whether the file is a pipe.
108109// If the file does not exist, it returns false.
109- func isPiped ( file * os. File ) bool {
110- stat , err := file .Stat ()
110+ func ( f file ) IsPiped ( ) bool {
111+ stat , err := f .Stat ()
111112 if err != nil {
112113 return false
113114 }
0 commit comments