Skip to content

Instantly share code, notes, and snippets.

@matthewmueller
Last active June 3, 2022 15:01
Show Gist options
  • Save matthewmueller/3570c165004a3dc05085caede0e4bf5d to your computer and use it in GitHub Desktop.
Save matthewmueller/3570c165004a3dc05085caede0e4bf5d to your computer and use it in GitHub Desktop.
Cancelable Reader. Useful for tests where you want to wait for some input, but not hang forever. (Before using, you need to change child-main.go to child/main.go)
package main
import (
"fmt"
"time"
)
func main() {
for i := 0; i < 10; i++ {
time.Sleep(1 * time.Second)
switch {
case i < 5:
fmt.Println("Booting")
case i == 5:
fmt.Println("Started")
case i > 5:
fmt.Println("Running")
}
}
}
package main
import (
"bufio"
"context"
"fmt"
"io"
"log"
"os"
"os/exec"
"time"
)
func main() {
if err := run(); err != nil {
log.Fatal(err)
}
}
func run() error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
cmd := exec.CommandContext(ctx, "go", "run", "child/main.go")
stdout, err := cmd.StdoutPipe()
if err != nil {
return err
}
defer stdout.Close()
cmd.Stderr = os.Stderr
cmd.Env = os.Environ()
if err := cmd.Start(); err != nil {
return err
}
cr, err := getCancelableReader(stdout)
if err != nil {
return err
}
if err := scan(cr); err != nil {
return err
}
return cmd.Wait()
}
type cancelableReader interface {
io.Reader
SetReadDeadline(time.Time) error
}
func getCancelableReader(r io.Reader) (cancelableReader, error) {
if cr := r.(cancelableReader); cr != nil {
return cr, nil
}
return nil, fmt.Errorf("not a cancelable reader")
}
func scan(cr cancelableReader) error {
scanner := bufio.NewScanner(cr)
for scanner.Scan() {
if err := cr.SetReadDeadline(time.Now().Add(2 * time.Second)); err != nil {
return err
}
fmt.Println("scanned", scanner.Text())
}
return scanner.Err()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment