Initial import from github
This commit is contained in:
commit
cbb8c42e1f
|
@ -0,0 +1,19 @@
|
||||||
|
Copyright (c) 2020 Laurence Withers
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,7 @@
|
||||||
|
# src.lwithers.me.uk/go/stdinprompt
|
||||||
|
|
||||||
|
[![GoDoc](https://img.shields.io/static/v1?label=godoc&message=reference&color=blue)](https://pkg.go.dev/src.lwithers.me.uk/go/stdinprompt)
|
||||||
|
|
||||||
|
This package provides an `io.Reader` that will prompt the user if no data is
|
||||||
|
available within a short period of time. This can be used to prompt a user that
|
||||||
|
data is awaited on stdin, for example.
|
|
@ -0,0 +1,22 @@
|
||||||
|
package stdinprompt_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"src.lwithers.me.uk/go/stdinprompt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleNew() {
|
||||||
|
q := make([]byte, 4096)
|
||||||
|
in := stdinprompt.New()
|
||||||
|
for {
|
||||||
|
n, err := in.Read(q)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
os.Stdout.Write(bytes.ToUpper(q[:n]))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
Package stdinprompt provides an io.Reader that will prompt the user if nothing
|
||||||
|
is read within a short period of time. It is useful for programs which default
|
||||||
|
to reading from stdin if no commandline arguments are provided, as it can
|
||||||
|
indicate to the user that a program is not actually performing any action until
|
||||||
|
data arrives. The prompt is not displayed if data becomes available before the
|
||||||
|
timeout.
|
||||||
|
*/
|
||||||
|
package stdinprompt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DefaultPromptTime is the timeout period after which, if no data has
|
||||||
|
// been read, we will display a prompt.
|
||||||
|
DefaultPromptTime = 250 * time.Millisecond
|
||||||
|
|
||||||
|
// StdinPromptMsg is the default message displayed.
|
||||||
|
StdinPromptMsg = "Waiting for data on stdin."
|
||||||
|
)
|
||||||
|
|
||||||
|
// New returns a new prompting reader for stdin. The prompt will be written to
|
||||||
|
// stderr.
|
||||||
|
func New() io.Reader {
|
||||||
|
return NewEx(os.Stdin, DefaultPromptTime, os.Stderr, StdinPromptMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEx returns a new prompting reader. The source may be specified along with
|
||||||
|
// the prompt message, timeout and destination.
|
||||||
|
func NewEx(raw io.Reader, when time.Duration, term io.Writer, msg string,
|
||||||
|
) io.Reader {
|
||||||
|
pr := &prompter{
|
||||||
|
raw: raw,
|
||||||
|
tmr: time.AfterFunc(when, func() {
|
||||||
|
fmt.Fprintln(term, msg)
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
return pr
|
||||||
|
}
|
||||||
|
|
||||||
|
type prompter struct {
|
||||||
|
raw io.Reader
|
||||||
|
tmr *time.Timer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pr *prompter) Read(buf []byte) (int, error) {
|
||||||
|
n, err := pr.raw.Read(buf)
|
||||||
|
if n > 0 {
|
||||||
|
pr.tmr.Stop()
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
package stdinprompt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNoPrompt(t *testing.T) {
|
||||||
|
// set up a buffer (which supports io.Reader) with some test data
|
||||||
|
exp := []byte{0xa, 0xb, 0xc, 0xd}
|
||||||
|
in := bytes.NewBuffer(nil)
|
||||||
|
in.Write(exp)
|
||||||
|
|
||||||
|
// set up a message capture buffer
|
||||||
|
msgcap := bytes.NewBuffer(nil)
|
||||||
|
|
||||||
|
// set up the prompter
|
||||||
|
inpr := NewEx(in, DefaultPromptTime, msgcap, StdinPromptMsg)
|
||||||
|
|
||||||
|
// read test data
|
||||||
|
out := make([]byte, len(exp))
|
||||||
|
n, err := inpr.Read(out)
|
||||||
|
if n != len(exp) {
|
||||||
|
t.Errorf("read %d bytes, expected %d", n, len(exp))
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected read error: %v", err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(out, exp) {
|
||||||
|
t.Errorf("read mismatch: got %X expected %X", out, exp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for at least the prompt timer
|
||||||
|
time.Sleep(2 * DefaultPromptTime)
|
||||||
|
|
||||||
|
// verify we didn't get anything in our capture buffer
|
||||||
|
if msgcap.Len() > 0 {
|
||||||
|
t.Errorf("got unexpected error %q", msgcap.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrompt(t *testing.T) {
|
||||||
|
// set up an (unused) input buffer
|
||||||
|
in := bytes.NewBuffer(nil)
|
||||||
|
|
||||||
|
// set up a message capture buffer
|
||||||
|
msgcap := bytes.NewBuffer(nil)
|
||||||
|
|
||||||
|
// set up the prompter
|
||||||
|
_ = NewEx(in, DefaultPromptTime, msgcap, StdinPromptMsg)
|
||||||
|
|
||||||
|
// wait for at least the prompt timer
|
||||||
|
time.Sleep(2 * DefaultPromptTime)
|
||||||
|
|
||||||
|
// verify that we received the expected message in the capture buffer
|
||||||
|
switch {
|
||||||
|
case msgcap.Len() == 0:
|
||||||
|
t.Errorf("no prompt was received")
|
||||||
|
case strings.TrimSpace(msgcap.String()) != StdinPromptMsg:
|
||||||
|
t.Errorf("got unexpected message %q", msgcap.String())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue