package pipe import ( "fmt" "os" "path/filepath" "sort" ) type Entry struct { Path string Info os.FileInfo Error error Result chan<- interface{} } type Dir struct { Path string Error error Info os.FileInfo Entries [](<-chan interface{}) Result chan<- interface{} } // readDirNames reads the directory named by dirname and returns // a sorted list of directory entries. // taken from filepath/path.go func readDirNames(dirname string) ([]string, error) { f, err := os.Open(dirname) if err != nil { return nil, err } names, err := f.Readdirnames(-1) f.Close() if err != nil { return nil, err } sort.Strings(names) return names, nil } func isDir(fi os.FileInfo) bool { return fi.IsDir() } func isFile(fi os.FileInfo) bool { return fi.Mode()&(os.ModeType|os.ModeCharDevice) == 0 } func walk(path string, done chan struct{}, entCh chan<- Entry, dirCh chan<- Dir, res chan<- interface{}) error { info, err := os.Lstat(path) if err != nil { return err } if !info.IsDir() { return fmt.Errorf("path is not a directory, cannot walk: %s", path) } names, err := readDirNames(path) if err != nil { return err } entries := make([]<-chan interface{}, 0, len(names)) for _, name := range names { subpath := filepath.Join(path, name) ch := make(chan interface{}, 1) entries = append(entries, ch) fi, err := os.Lstat(subpath) if err != nil { // entCh <- Entry{Info: fi, Error: err, Result: ch} return err } if isDir(fi) { err = walk(subpath, done, entCh, dirCh, ch) if err != nil { return err } } else { entCh <- Entry{Info: fi, Path: subpath, Result: ch} } } dirCh <- Dir{Path: path, Info: info, Entries: entries, Result: res} return nil } // Walk takes a path and sends a Job for each file and directory it finds below // the path. When the channel done is closed, processing stops. func Walk(path string, done chan struct{}, entCh chan<- Entry, dirCh chan<- Dir) (<-chan interface{}, error) { resCh := make(chan interface{}, 1) err := walk(path, done, entCh, dirCh, resCh) close(entCh) close(dirCh) return resCh, err }