aboutsummaryrefslogtreecommitdiff
path: root/examples/countlines.go
diff options
context:
space:
mode:
authorGabriel A. Giovanini <mail@gabrielgio.me>2023-12-09 18:14:51 +0100
committerGabriel A. Giovanini <mail@gabrielgio.me>2023-12-09 18:17:36 +0100
commit8049a4e0decd7b233cf2c2339ad0e57a0a029898 (patch)
treec28c8214a7f2482ad8d98c84ab68176029776fb8 /examples/countlines.go
downloadpipe-8049a4e0decd7b233cf2c2339ad0e57a0a029898.tar.gz
pipe-8049a4e0decd7b233cf2c2339ad0e57a0a029898.tar.bz2
pipe-8049a4e0decd7b233cf2c2339ad0e57a0a029898.zip
Initial commit
Diffstat (limited to 'examples/countlines.go')
-rw-r--r--examples/countlines.go135
1 files changed, 135 insertions, 0 deletions
diff --git a/examples/countlines.go b/examples/countlines.go
new file mode 100644
index 0000000..000efa5
--- /dev/null
+++ b/examples/countlines.go
@@ -0,0 +1,135 @@
+package main
+
+import (
+ "bufio"
+ "errors"
+ "flag"
+ "fmt"
+ "os"
+ "path/filepath"
+ "unicode/utf8"
+
+ "git.sr.ht/~gabrielgio/pipe"
+)
+
+var (
+ InvalidInput = errors.New("It was given a file instead of a folder.")
+ ColorGreen = "\033[32m"
+ ColorRed = "\033[31m"
+ ColorReset = "\033[0m"
+)
+
+type (
+ NonTextError struct {
+ Filename string
+ }
+)
+
+func NewNonTextError(filename string) *NonTextError {
+ return &NonTextError{Filename: filename}
+}
+
+func (b *NonTextError) Error() string {
+ return fmt.Sprintf("Non text file %s", b.Filename)
+}
+
+func main() {
+ err := run()
+ if err != nil {
+ fmt.Printf("Error on main process: %s", err.Error())
+ }
+}
+
+func run() error {
+ var (
+ dir = flag.String("dir", "", "Folder to recursively search for files. Default: \"\"")
+ quiet = flag.Bool("quiet", false, "If true only outputs the result. Default: false")
+ nproc = flag.Int("nproc", 4, "Number of goroutines used to count. Default: 4")
+ )
+
+ flag.Parse()
+
+ info, err := os.Stat(*dir)
+ if err != nil {
+ return err
+ }
+
+ if !info.IsDir() {
+ return InvalidInput
+ }
+
+ cerr := make(chan error)
+ go func(c <-chan error) {
+ for e := range c {
+ if !*quiet {
+ fmt.Printf("%sERROR%s: %s\n", ColorRed, ColorReset, e.Error())
+ }
+ }
+ }(cerr)
+
+ c := count(*dir, *nproc, cerr)
+ fmt.Printf("%sCOUNT%s: %d\n", ColorGreen, ColorReset, c)
+
+ return nil
+}
+
+func count(dir string, nproc int, cerr chan<- error) int {
+ cfiles := walkFolder(dir)
+ ccount := pipe.ProcWithError(cfiles, cerr, nproc, countLines)
+ return pipe.TailReduceWithError(ccount, cerr, sum, 0)
+}
+
+func sum(acc, lines int) (int, error) {
+ return acc + lines, nil
+}
+
+func countLines(filename string) (int, error) {
+
+ file, err := os.Open(filename)
+ if err != nil {
+ return 0, err
+ }
+
+ defer file.Close()
+
+ fileScanner := bufio.NewScanner(file)
+ fileScanner.Split(bufio.ScanLines)
+
+ var count int
+ for fileScanner.Scan() {
+ if !utf8.ValidString(string(fileScanner.Text())) {
+ return 0, NewNonTextError(filename)
+ }
+ count++
+ }
+
+ return count, nil
+}
+
+func walkFolder(folder string) <-chan string {
+ c := make(chan string)
+
+ go func(folder string, c chan string) {
+ filepath.Walk(folder, func(path string, info os.FileInfo, err error) error {
+ file, err := os.Open(path)
+ if err != nil {
+ return filepath.SkipDir
+ }
+ defer file.Close()
+
+ fileInfo, err := file.Stat()
+ if err != nil {
+ return filepath.SkipDir
+ }
+
+ if !fileInfo.IsDir() {
+ c <- path
+ }
+ return nil
+ })
+ close(c)
+
+ }(folder, c)
+
+ return c
+}