From fc29cd896b8eed743b4d4732910eab433328451a Mon Sep 17 00:00:00 2001 From: Bart de Boer Date: Fri, 3 May 2024 00:49:17 +0200 Subject: [PATCH 1/4] update First(n int) so that it can be used for user input --- script.go | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/script.go b/script.go index ea70a56..b959334 100644 --- a/script.go +++ b/script.go @@ -522,13 +522,20 @@ func (p *Pipe) First(n int) *Pipe { if n <= 0 { return NewPipe() } - i := 0 - return p.FilterScan(func(line string, w io.Writer) { - if i >= n { - return + return p.Filter(func(r io.Reader, w io.Writer) error { + scanner := newScanner(r) + i := 0 + for scanner.Scan() { + _, err := fmt.Fprintln(w, scanner.Text()) + if err != nil { + return err + } + i++ + if i >= n { + break + } } - fmt.Fprintln(w, line) - i++ + return scanner.Err() }) } @@ -964,3 +971,10 @@ func newScanner(r io.Reader) *bufio.Scanner { scanner.Buffer(make([]byte, 4096), math.MaxInt) return scanner } + +// Prompt creates a pipe that reads user input from stdin after displaying the +// specified prompt. +func Prompt(prompt string) *Pipe { + fmt.Print(prompt) + return Stdin().First(1) +} From 819d15c4e9090793ec4165d8fea6adfdef0530b7 Mon Sep 17 00:00:00 2001 From: Bart de Boer Date: Sun, 5 May 2024 14:27:54 +0200 Subject: [PATCH 2/4] make the update to First() more elegant and update comment as suggested by John --- script.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/script.go b/script.go index b959334..007e9eb 100644 --- a/script.go +++ b/script.go @@ -514,7 +514,8 @@ func (p *Pipe) FilterScan(filter func(string, io.Writer)) *Pipe { // First produces only the first n lines of the pipe's contents, or all the // lines if there are less than n. If n is zero or negative, there is no output -// at all. +// at all. When n lines have been produced, First stops reading its input and +// sends EOF to its output. func (p *Pipe) First(n int) *Pipe { if p.Error() != nil { return p @@ -524,16 +525,11 @@ func (p *Pipe) First(n int) *Pipe { } return p.Filter(func(r io.Reader, w io.Writer) error { scanner := newScanner(r) - i := 0 - for scanner.Scan() { + for i := 0; i < n && scanner.Scan(); i++ { _, err := fmt.Fprintln(w, scanner.Text()) if err != nil { return err } - i++ - if i >= n { - break - } } return scanner.Err() }) From 9d02069c529cb785e31974623cf6283243a53314 Mon Sep 17 00:00:00 2001 From: Bart de Boer Date: Sun, 5 May 2024 14:28:46 +0200 Subject: [PATCH 3/4] remove the Prompt function --- script.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/script.go b/script.go index 007e9eb..c471f74 100644 --- a/script.go +++ b/script.go @@ -967,10 +967,3 @@ func newScanner(r io.Reader) *bufio.Scanner { scanner.Buffer(make([]byte, 4096), math.MaxInt) return scanner } - -// Prompt creates a pipe that reads user input from stdin after displaying the -// specified prompt. -func Prompt(prompt string) *Pipe { - fmt.Print(prompt) - return Stdin().First(1) -} From 53ab797e9c41400c4afdd895d7d9ebbafaa78fdb Mon Sep 17 00:00:00 2001 From: Bart de Boer Date: Sun, 5 May 2024 22:50:39 +0200 Subject: [PATCH 4/4] add test TestFirstDoesNotConsumeUnnecessaryData --- script_test.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/script_test.go b/script_test.go index dd12e3b..b19b915 100644 --- a/script_test.go +++ b/script_test.go @@ -573,6 +573,24 @@ func TestFirstHasNoEffectGivenLessThanNInputLines(t *testing.T) { } } +func TestFirstDoesNotConsumeUnnecessaryData(t *testing.T) { + t.Parallel() + // First uses a 4096-byte buffer, so will always read at least + // that much, but no more (once N lines have been read). + r := strings.NewReader(strings.Repeat("line\n", 1000)) + got, err := script.NewPipe().WithReader(r).First(1).String() + if err != nil { + t.Fatal(err) + } + want := "line\n" + if want != got { + t.Errorf("want output %q, got %q", want, got) + } + if r.Len() == 0 { + t.Errorf("no data left in reader") + } +} + func TestFreqHandlesLongLines(t *testing.T) { t.Parallel() got, err := script.Echo(longLine).Freq().Slice()