forked from thought-machine/please
-
Notifications
You must be signed in to change notification settings - Fork 0
/
stdin.go
68 lines (62 loc) · 1.71 KB
/
stdin.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package cli
import (
"bufio"
"os"
"strings"
)
var seenStdin = false // Used to track that we don't try to read stdin twice
// ReadStdin reads a sequence of space-delimited words from standard input.
// Words are pushed onto the returned channel asynchronously.
func ReadStdin() <-chan string {
c := make(chan string)
if seenStdin {
log.Fatalf("Repeated - on command line; can't reread stdin.")
}
seenStdin = true
go func() {
scanner := bufio.NewScanner(os.Stdin)
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
s := strings.TrimSpace(scanner.Text())
if s != "" {
c <- s
}
}
if err := scanner.Err(); err != nil {
log.Fatalf("Error reading stdin: %s", err)
}
close(c)
}()
return c
}
// ReadAllStdin reads standard input in its entirety to a slice.
// Since this reads it completely before returning it won't handle a slow input
// very nicely. ReadStdin is therefore preferable when possible.
func ReadAllStdin() []string {
var ret []string
for s := range ReadStdin() {
ret = append(ret, s)
}
return ret
}
// StdinStrings is a type used for flags; it accepts a slice of strings but also
// if it's a single - it reads its contents from stdin.
type StdinStrings []string
// Get reads stdin if needed and returns the contents of this slice.
func (s StdinStrings) Get() []string {
if len(s) == 1 && s[0] == "-" {
return ReadAllStdin()
} else if ContainsString("-", s) {
log.Fatalf("Cannot pass - to read stdin along with other arguments.")
}
return s
}
// ContainsString returns true if the given slice contains an individual string.
func ContainsString(needle string, haystack []string) bool {
for _, straw := range haystack {
if needle == straw {
return true
}
}
return false
}