github.com/grailbio/bigslice@v0.0.0-20230519005545-30c4c12152ad/internal/walker/walker.go (about) 1 // Copyright 2017 GRAIL, Inc. All rights reserved. 2 // Use of this source code is governed by the Apache 2.0 3 // license that can be found in the LICENSE file. 4 5 package walker 6 7 import ( 8 "os" 9 "path/filepath" 10 "sort" 11 ) 12 13 // Walker walks a recursive directory hierarchy, exposing a scanner-like interface. 14 type Walker struct { 15 root string 16 err error 17 path string 18 info os.FileInfo 19 todo []string 20 } 21 22 // Init initializes a walker to walk from a root path. 23 func (w *Walker) Init(root string) { 24 w.root = root 25 w.todo = append(w.todo, w.root) 26 } 27 28 // Scan advances the walker to the next entry in the hierarchy. 29 // It returns false either when the scan stops because we have 30 // reached the end of the input or else because there was error. 31 // After Scan returns, the Err method returns any error that occurred 32 // during scanning. 33 func (w *Walker) Scan() bool { 34 again: 35 if len(w.todo) == 0 || w.err != nil { 36 return false 37 } 38 w.path, w.todo = w.todo[0], w.todo[1:] 39 w.info, w.err = os.Stat(w.path) 40 if os.IsNotExist(w.err) { 41 w.err = nil 42 goto again 43 } else if w.err != nil { 44 return false 45 } 46 if w.info.IsDir() { 47 var paths []string 48 paths, w.err = readDirNames(w.path) 49 if w.err != nil { 50 return false 51 } 52 for i := range paths { 53 paths[i] = filepath.Join(w.path, paths[i]) 54 } 55 w.todo = append(paths, w.todo...) 56 } 57 return true 58 } 59 60 // Path returns the most recent path that was scanned. 61 func (w *Walker) Path() string { 62 return w.path 63 } 64 65 // Relpath returns the most recent path that was scanned, relative to 66 // the scan root directory. 67 func (w *Walker) Relpath() string { 68 path, err := filepath.Rel(w.root, w.Path()) 69 if err != nil { 70 panic("bad path") 71 } 72 return path 73 } 74 75 // Info returns the os.FileInfo for the most recent path scanned. 76 func (w *Walker) Info() os.FileInfo { 77 return w.info 78 } 79 80 // Err returns the first error that occurred while scanning. 81 func (w *Walker) Err() error { 82 return w.err 83 } 84 85 // readDirNames reads the directory named by dirname and returns 86 // a sorted list of directory entries. 87 func readDirNames(dirname string) ([]string, error) { 88 f, err := os.Open(dirname) 89 if err != nil { 90 return nil, err 91 } 92 names, err := f.Readdirnames(-1) 93 f.Close() 94 if err != nil { 95 return nil, err 96 } 97 sort.Strings(names) 98 return names, nil 99 }