Initial import from github

This commit is contained in:
Laurence Withers 2020-02-10 21:07:43 +00:00
commit cbb8c42e1f
6 changed files with 172 additions and 0 deletions

19
LICENSE Normal file
View File

@ -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.

7
README.md Normal file
View File

@ -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.

22
example_test.go Normal file
View File

@ -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]))
}
}

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module src.lwithers.me.uk/go/stdinprompt
go 1.12

57
stdinprompt.go Normal file
View File

@ -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
}

64
unit_test.go Normal file
View File

@ -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())
}
}