cmd/streamdata: prompt helpers
These read user input from the terminal. They are quite sound, but only manually tested for now. Signed-off-by: Yonah <contrib@gensokyo.uk>
This commit is contained in:
83
cmd/streamdata/prompt.go
Normal file
83
cmd/streamdata/prompt.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
"syscall"
|
||||
|
||||
"hakurei.app/ext"
|
||||
)
|
||||
|
||||
// toErr writes panic value satisfying error to the value pointed to by errP,
|
||||
// and re-panics other non-nil values.
|
||||
func toErr(errP *error) {
|
||||
r := recover()
|
||||
if r == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err, ok := r.(error); !ok {
|
||||
panic(r)
|
||||
} else if *errP == nil {
|
||||
*errP = err
|
||||
}
|
||||
}
|
||||
|
||||
// isattyStd checks whether standard streams used for prompts are terminals.
|
||||
func isattyStd() {
|
||||
if !ext.Isatty(syscall.Stdin) {
|
||||
panic(errors.New("stdin does not appear to be a terminal"))
|
||||
}
|
||||
if !ext.Isatty(syscall.Stderr) {
|
||||
panic(errors.New("stdin does not appear to be a terminal"))
|
||||
}
|
||||
}
|
||||
|
||||
// prompt prompts the user and reads the response as a string.
|
||||
func prompt(br *bufio.Reader, p string) (string, bool) {
|
||||
os.Stderr.WriteString(p)
|
||||
s, err := br.ReadString('\n')
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return "", false
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
return s[:len(s)-1], true
|
||||
}
|
||||
|
||||
// noResponseUint is returned by promptUint when the user responds with a single
|
||||
// newline character.
|
||||
const noResponseUint = math.MaxUint64
|
||||
|
||||
// promptUint prompts the user for an unsigned integer until EOF or a valid
|
||||
// response is read.
|
||||
func promptUint(br *bufio.Reader, p string) (uint64, bool) {
|
||||
invalid:
|
||||
s, ok := prompt(br, p)
|
||||
if !ok {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
if s == "" {
|
||||
return noResponseUint, false
|
||||
}
|
||||
|
||||
v, err := strconv.ParseUint(s, 10, 64)
|
||||
if err != nil {
|
||||
goto invalid
|
||||
}
|
||||
return v, true
|
||||
}
|
||||
|
||||
// require panics with an error if ok is false, and returns v otherwise.
|
||||
func require[T any](v T, ok bool) T {
|
||||
if !ok {
|
||||
panic(errors.New("cancelled"))
|
||||
}
|
||||
return v
|
||||
}
|
||||
Reference in New Issue
Block a user