stario/terminal.go

74 lines
1.9 KiB
Go
Raw Normal View History

package stario
import (
"context"
"errors"
"os"
"golang.org/x/term"
)
// ErrTerminalNotTTY reports that terminal-only input was requested from a
// non-terminal stdin.
var ErrTerminalNotTTY = errors.New("terminal input requires a tty")
var terminalCheckFunc = term.IsTerminal
// IsTerminal reports whether os.Stdin is attached to a terminal.
func IsTerminal() bool {
return IsTerminalFile(os.Stdin)
}
// IsTerminalFD reports whether fd is attached to a terminal.
func IsTerminalFD(fd int) bool {
return terminalCheckFunc(fd)
}
// IsTerminalFile reports whether file is attached to a terminal.
func IsTerminalFile(file *os.File) bool {
if file == nil {
return false
}
return IsTerminalFD(int(file.Fd()))
}
// ReadPasswordContext reads one password-style line from the terminal.
//
// It returns ErrTerminalNotTTY when stdin is not a terminal. If ctx is canceled
// while waiting for input, this function returns ctx.Err() without waiting for
// the underlying terminal read to finish.
func ReadPasswordContext(ctx context.Context, hint string, defaultVal string) (string, error) {
return ReadPasswordContextWithMask(ctx, hint, defaultVal, "")
}
// ReadPasswordContextWithMask is like ReadPasswordContext but echoes the given
// mask string while the user types.
func ReadPasswordContextWithMask(ctx context.Context, hint string, defaultVal string, mask string) (string, error) {
if ctx == nil {
ctx = context.Background()
}
if err := ctx.Err(); err != nil {
return "", err
}
if !IsTerminal() {
return "", ErrTerminalNotTTY
}
session, err := rawTerminalSessionFactory(hint, true)
if err != nil {
return "", err
}
resultCh := make(chan InputMsg, 1)
go func() {
defer session.Close()
resultCh <- rawLineInputSession(session, defaultVal, mask, rawInputSignalReturnError)
}()
select {
case result := <-resultCh:
return result.String()
case <-ctx.Done():
session.Abort()
<-resultCh
return "", ctx.Err()
}
}