diff --git a/internal/count/count.go b/internal/count/count.go index 868f229..9c16b5d 100644 --- a/internal/count/count.go +++ b/internal/count/count.go @@ -31,6 +31,7 @@ type ( // Result encodes the result of a counting operation on a file. type Result struct { + Err error `json:"-"` Name string `json:"name"` Lines Lines `json:"lines"` Bytes Bytes `json:"bytes"` @@ -72,7 +73,7 @@ func (r Results) Display(w io.Writer, toJSON bool) error { } // One performs a counting operation on a single reader, returning the result and any error. -func One(in io.Reader, name string) (Result, error) { +func One(in io.Reader, name string) Result { var ( lc Lines bc Bytes @@ -84,7 +85,7 @@ func One(in io.Reader, name string) (Result, error) { _, err := io.Copy(multi, in) if err != nil { - return Result{}, err + return Result{Err: err} } return Result{ @@ -93,12 +94,12 @@ func One(in io.Reader, name string) (Result, error) { Bytes: bc, Words: wc, Chars: cc, - }, nil + } } // All performs counting operations on multiple files concurrently // gathering up the results and returning. -func All(files []string) (Results, error) { +func All(files []string) Results { jobs := make(chan string) counts := make(chan Result) @@ -139,7 +140,7 @@ func All(files []string) (Results, error) { results = append(results, count) } - return results, nil + return results } // worker is a concurrent worker contributing to counting in files, @@ -151,7 +152,8 @@ func worker(counts chan<- Result, files <-chan string, wg *sync.WaitGroup) { for file := range files { info, err := os.Stat(file) if err != nil { - panic(err) // TODO: Not this + counts <- Result{Err: err} + return } if info.IsDir() { // Skip directories @@ -160,17 +162,10 @@ func worker(counts chan<- Result, files <-chan string, wg *sync.WaitGroup) { f, err := os.Open(file) if err != nil { - // If we can't open the file, just close it and move on - // TODO: Handle this better - f.Close() - continue - } - result, err := One(f, file) - if err != nil { - // Same as above - f.Close() - continue + counts <- Result{Err: err} + return } + result := One(f, file) f.Close() counts <- result } diff --git a/internal/count/count_test.go b/internal/count/count_test.go index 4e3d7b4..45bd088 100644 --- a/internal/count/count_test.go +++ b/internal/count/count_test.go @@ -37,8 +37,8 @@ var ( func TestCount(t *testing.T) { tests := []struct { - name string in io.Reader + name string want count.Result }{ { @@ -105,9 +105,9 @@ func TestCount(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := count.One(tt.in, "") + got := count.One(tt.in, "") - test.Ok(t, err) + test.Ok(t, got.Err) test.Equal(t, got, tt.want) }) } @@ -121,8 +121,10 @@ func TestCountAll(t *testing.T) { filepath.Join("testdata", "TestCount", "dir"), } - results, err := count.All(files) - test.Ok(t, err) + results := count.All(files) + for _, result := range results { + test.Ok(t, result.Err) + } want := count.Results{ { @@ -297,7 +299,7 @@ func BenchmarkCount(b *testing.B) { b.ResetTimer() for range b.N { - _, err := count.One(r, "bench") + _ = count.One(r, "bench") if err != nil { b.Fatalf("Count returned an error: %v", err) } diff --git a/main.go b/main.go index 29ba7f8..e0e059b 100644 --- a/main.go +++ b/main.go @@ -6,15 +6,16 @@ import ( "io" "os" "slices" + "time" "github.com/FollowTheProcess/cli" "github.com/FollowTheProcess/gowc/internal/count" ) var ( - version = "dev" // gowc version number, set at compile time by ldflags - commit = "unknown" // gowc commit hash, set at compile time with ldflags - date = "?" // gowc build date, set at compile time with ldflags + version = "dev" // gowc version number, set at compile time by ldflags + commit = "" // gowc commit hash, set at compile time with ldflags + date = "" // gowc build date, set at compile time with ldflags ) func main() { @@ -58,6 +59,7 @@ type countOptions struct { func doCount(options *countOptions) func(cmd *cli.Command, args []string) error { return func(cmd *cli.Command, args []string) error { + start := time.Now() stdout := cmd.Stdout() switch len(args) { case 0: @@ -71,12 +73,13 @@ func doCount(options *countOptions) func(cmd *cli.Command, args []string) error return fmt.Errorf("nothing to read from stdin") } - result, err := count.One(fd, "stdin") - if err != nil { + result := count.One(fd, "stdin") + if result.Err != nil { + return result.Err + } + if err := result.Display(stdout, options.json); err != nil { return err } - return result.Display(stdout, options.json) - case 1: // Read from the file path := args[0] @@ -86,23 +89,32 @@ func doCount(options *countOptions) func(cmd *cli.Command, args []string) error } defer file.Close() - result, err := count.One(file, path) - if err != nil { + result := count.One(file, path) + if result.Err != nil { + return result.Err + } + if err := result.Display(stdout, options.json); err != nil { return err } - return result.Display(stdout, options.json) default: // Count many files - results, err := count.All(args) - if err != nil { - return err + results := count.All(args) + for _, result := range results { + if result.Err != nil { + return result.Err + } } // Sort the results by name so it's deterministic slices.SortFunc(results, cmpResult) - return results.Display(stdout, options.json) + if err := results.Display(stdout, options.json); err != nil { + return err + } } + + fmt.Fprintf(cmd.Stderr(), "\ntook %v\n", time.Since(start)) + return nil } } diff --git a/main_test.go b/main_test.go index 314dd76..3be45d4 100644 --- a/main_test.go +++ b/main_test.go @@ -40,7 +40,7 @@ func TestCountFile(t *testing.T) { func TestCountMany(t *testing.T) { snap := snapshot.New(t, snapshot.Update(*update)) - files := []string{ + args := []string{ filepath.Join("internal", "count", "testdata", "TestCount", "moby_dick.txt"), filepath.Join("internal", "count", "testdata", "TestCount", "another.txt"), filepath.Join("internal", "count", "testdata", "TestCount", "onemore.txt"), @@ -48,7 +48,6 @@ func TestCountMany(t *testing.T) { } stdout := &bytes.Buffer{} stderr := &bytes.Buffer{} - args := files err := run(os.Stdin, stdout, stderr, args) test.Ok(t, err)