github.com/richardwilkes/toolbox@v1.121.0/xio/fs/walk.go (about) 1 // Copyright (c) 2016-2024 by Richard A. Wilkes. All rights reserved. 2 // 3 // This Source Code Form is subject to the terms of the Mozilla Public 4 // License, version 2.0. If a copy of the MPL was not distributed with 5 // this file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 // 7 // This Source Code Form is "Incompatible With Secondary Licenses", as 8 // defined by the Mozilla Public License, version 2.0. 9 10 package fs 11 12 import ( 13 "errors" 14 "net/http" 15 "os" 16 "path/filepath" 17 "sort" 18 19 "github.com/richardwilkes/toolbox/xio" 20 ) 21 22 // Walk performs the same function as filepath.Walk() does, but works on http.FileSystem objects. 23 func Walk(fs http.FileSystem, root string, walkFn filepath.WalkFunc) error { 24 info, err := stat(fs, root) 25 if err != nil { 26 err = walkFn(root, nil, err) 27 } else { 28 err = walk(fs, root, info, walkFn) 29 } 30 if errors.Is(err, filepath.SkipDir) { 31 return nil 32 } 33 return err 34 } 35 36 func stat(fs http.FileSystem, path string) (os.FileInfo, error) { 37 f, err := fs.Open(path) 38 if err != nil { 39 return nil, err 40 } 41 var info os.FileInfo 42 info, err = f.Stat() 43 xio.CloseIgnoringErrors(f) 44 return info, err 45 } 46 47 func walk(fs http.FileSystem, path string, info os.FileInfo, walkFn filepath.WalkFunc) error { 48 if !info.IsDir() { 49 return walkFn(path, info, nil) 50 } 51 names, err := readDirNames(fs, path) 52 err1 := walkFn(path, info, err) 53 if err != nil || err1 != nil { 54 return err1 55 } 56 for _, name := range names { 57 filename := filepath.Join(path, name) 58 var fileInfo os.FileInfo 59 if fileInfo, err = stat(fs, filename); err != nil { 60 if err = walkFn(filename, fileInfo, err); err != nil && !errors.Is(err, filepath.SkipDir) { 61 return err 62 } 63 } else { 64 err = walk(fs, filename, fileInfo, walkFn) 65 if err != nil { 66 if !fileInfo.IsDir() || !errors.Is(err, filepath.SkipDir) { 67 return err 68 } 69 } 70 } 71 } 72 return nil 73 } 74 75 func readDirNames(fs http.FileSystem, dirname string) ([]string, error) { 76 f, err := fs.Open(dirname) 77 if err != nil { 78 return nil, err 79 } 80 var list []os.FileInfo 81 list, err = f.Readdir(-1) 82 xio.CloseIgnoringErrors(f) 83 if err != nil { 84 return nil, err 85 } 86 names := make([]string, len(list)) 87 for i := range list { 88 names[i] = list[i].Name() 89 } 90 sort.Strings(names) 91 return names, nil 92 }