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  }