@ 260812.1

250514.4 · bc5b513dc10438260287d7bf1b1a785d4b9720d1

fastwalk: add example tests

reading declarations

Changes to example_test.go. All files

example_test.go

new file mode 100644@@ -0,0 +1,178 @@+package fastwalk_test++import (+	"fmt"+	"io/fs"+	"os"+	"path/filepath"+	"strings"++	"github.com/charlievieth/fastwalk"+)++func CreateFiles(files map[string]string) (root string, cleanup func()) {+	tempdir, err := os.MkdirTemp("", "fastwalk-example-*")+	if err != nil {+		panic(err)+	}++	symlinks := map[string]string{}+	for path, contents := range files {+		file := filepath.Join(tempdir, "/src", path)+		if err := os.MkdirAll(filepath.Dir(file), 0755); err != nil {+			panic(err)+		}+		if strings.HasPrefix(contents, "LINK:") {+			symlinks[file] = filepath.FromSlash(strings.TrimPrefix(contents, "LINK:"))+			continue+		}+		if err := os.WriteFile(file, []byte(contents), 0644); err != nil {+			panic(err)+		}+	}++	// Create symlinks after all other files. Otherwise, directory symlinks on+	// Windows are unusable (see https://golang.org/issue/39183).+	for file, dst := range symlinks {+		if err := os.Symlink(dst, file); err != nil {+			panic(err)+		}+	}++	return filepath.Join(tempdir, "src") + "/", func() { os.RemoveAll(tempdir) }+}++func PrettyPrintEntry(root, path string, de fs.DirEntry) {+	rel, err := filepath.Rel(root, path)+	if err != nil {+		panic(err)+	}+	rel = filepath.ToSlash(rel)+	if de.Type()&fs.ModeSymlink != 0 {+		dst, err := os.Readlink(path)+		if err != nil {+			panic(err)+		}+		fmt.Printf("%s: %s -> %s\n", de.Type(), rel, filepath.ToSlash(dst))+	} else {+		fmt.Printf("%s: %s\n", de.Type(), rel)+	}+}++// This example shows how the [fastwalk.Config] Follow field can be used to+// efficiently and safely follow symlinks. The below example contains a symlink+// loop ("bar/symloop"), which fastwalk detects and does not follow.+//+// NOTE: We still call the [fs.WalkDirFunc] on the symlink that creates a loop,+// but we do not follow/traverse it.+func ExampleWalk_follow() {+	// Setup+	root, cleanup := CreateFiles(map[string]string{+		"bar/bar.go":  "one",+		"bar/symlink": "LINK:bar.go",+		"foo/foo.go":  "two",+		"foo/symdir":  "LINK:../bar/",+		"foo/broken":  "LINK:nope.txt", // Broken symlink+		"foo/foo":     "LINK:../foo/",  // Symlink loop+	})+	defer cleanup()++	conf := fastwalk.Config{+		Follow: true,+	}+	err := fastwalk.Walk(&conf, root, func(path string, de fs.DirEntry, err error) error {+		if err != nil {+			return err+		}+		PrettyPrintEntry(root, path, de)+		return nil+	})+	if err != nil {+		panic(err)+	}+	// Unordered output:+	// d---------: .+	// d---------: bar+	// ----------: bar/bar.go+	// L---------: bar/symlink -> bar.go+	// d---------: foo+	// L---------: foo/broken -> nope.txt+	// ----------: foo/foo.go+	// L---------: foo/foo -> ../foo/+	// L---------: foo/symdir -> ../bar/+	// L---------: foo/symdir/symlink -> bar.go+	// ----------: foo/symdir/bar.go+}++func ExampleWalk() {+	root, cleanup := CreateFiles(map[string]string{+		"bar/b.txt": "",+		"foo/f.txt": "",+		// Since Config.Follow is set to false, the symbolic link "link" will+		// be visited, but we will not traverse into it (since our walk func+		// does not return [fastwalk.ErrTraverseLink]).+		"link": "LINK:bar",+	})+	defer cleanup()++	err := fastwalk.Walk(nil, root, func(path string, de fs.DirEntry, err error) error {+		if err != nil {+			return err+		}+		PrettyPrintEntry(root, path, de)+		return nil+	})+	if err != nil {+		panic(err)+	}+	// Unordered output:+	// d---------: .+	// d---------: bar+	// d---------: foo+	// L---------: link -> bar+	// ----------: bar/b.txt+	// ----------: foo/f.txt+}++func ExampleWalk_traverseLink() {+	root, cleanup := CreateFiles(map[string]string{+		"bar/b.txt": "",+		"foo/f.txt": "",+		// This link is followed since our walk func returns ErrTraverseLink+		// when a symlink that resolves to a directory is encountered.+		"link": "LINK:bar",+	})+	defer cleanup()++	err := fastwalk.Walk(nil, root, func(path string, de fs.DirEntry, err error) error {+		if err != nil {+			return err+		}+		PrettyPrintEntry(root, path, de)++		// It is strongly recommended to use fastwalk.Config.Follow instead+		// of manually handling link traversal since this will fail if the+		// link points to a file (and not a directory).+		if de.Type()&fs.ModeSymlink != 0 {+			fi, err := fastwalk.StatDirEntry(path, de)+			if err != nil {+				return err+			}+			if fi.IsDir() {+				return fastwalk.ErrTraverseLink+			}+		}+		return nil+	})+	if err != nil {+		panic(err)+	}+	// Unordered output:+	// d---------: .+	// d---------: bar+	// d---------: foo+	// L---------: link -> bar+	// ----------: bar/b.txt+	// ----------: foo/f.txt+	// ----------: link/b.txt+}