From cc8efbaf13d57021bee8d4b5bd8a1e46ebe54730 Mon Sep 17 00:00:00 2001 From: Rose Hogenson Date: Fri, 11 Apr 2025 21:26:04 -0700 Subject: Add documentation --- internal/wfs/wfs.go | 84 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 64 insertions(+), 20 deletions(-) (limited to 'internal/wfs/wfs.go') diff --git a/internal/wfs/wfs.go b/internal/wfs/wfs.go index bcdca00..4168b58 100644 --- a/internal/wfs/wfs.go +++ b/internal/wfs/wfs.go @@ -1,11 +1,14 @@ +// Package wfs implements a "writable file system" in the spirit of [fs.FS]. package wfs import ( + "errors" "io" "io/fs" "path" ) +// ReadLinkFS is backported from the latest go master. type ReadLinkFS interface { fs.FS @@ -13,6 +16,9 @@ type ReadLinkFS interface { Lstat(string) (fs.FileInfo, error) } +// ReadLink returns the destination of the named symbolic link. +// +// If fsys does not implement [ReadLinkFS], then ReadLink returns an error. func ReadLink(fsys fs.FS, name string) (string, error) { sym, ok := fsys.(ReadLinkFS) if !ok { @@ -21,6 +27,12 @@ func ReadLink(fsys fs.FS, name string) (string, error) { return sym.ReadLink(name) } +// Lstat returns an [fs.FileInfo] describing the named file. +// If the file is a symbolic link, the returned [fs.FileInfo] describes the +// symbolic link. Lstat makes no attempt to follow the link. +// +// If fsys does not implement [ReadLinkFS], then Lstat is identical +// to [fs.Stat]. func Lstat(fsys fs.FS, name string) (fs.FileInfo, error) { sym, ok := fsys.(ReadLinkFS) if !ok { @@ -29,6 +41,7 @@ func Lstat(fsys fs.FS, name string) (fs.FileInfo, error) { return sym.Lstat(name) } +// An FS provides access to a writable hierarchical file system. type FS interface { fs.FS @@ -39,12 +52,16 @@ type FS interface { Chmod(string, fs.FileMode) error } +// A MkdirModeFS is a file system with a mkdir method that accepts a file mode. type MkdirModeFS interface { FS MkdirMode(string, fs.FileMode) error } +// MkdirMode creates a directory with the given file permission. If fsys +// implements [MkdirModeFS], MkdirMode calls fsys.MkdirMode. Otherwise, +// MkdirMode calls Mkdir and then Chmod to set the mode. func MkdirMode(fsys FS, name string, mode fs.FileMode) error { if fsys, ok := fsys.(MkdirModeFS); ok { return fsys.MkdirMode(name, mode) @@ -56,31 +73,58 @@ func MkdirMode(fsys FS, name string, mode fs.FileMode) error { } func removeDir(fsys FS, dir string) error { - entries, err := fs.ReadDir(fsys, dir) - if err != nil { - return err - } - for _, f := range entries { - name := path.Join(dir, f.Name()) - if f.IsDir() { - err = removeDir(fsys, name) - } else { - err = fsys.Remove(name) - } - if err != nil { - return err + entries, readErr := fs.ReadDir(fsys, dir) + var err error + for _, d := range entries { + if err1 := removeAll(fsys, path.Join(dir, d.Name()), d); err == nil { + err = err1 } } - return fsys.Remove(dir) + if err == nil { + err = readErr + } + err1 := fsys.Remove(dir) + if err1 == nil || errors.Is(err, fs.ErrNotExist) { + return nil + } + if err == nil { + err = err1 + } + return err } -func RemoveAll(fsys FS, path string) error { - stat, err := Lstat(fsys, path) - if err != nil { +func removeAll(fsys FS, path string, d fs.DirEntry) error { + err := fsys.Remove(path) + if err == nil || errors.Is(err, fs.ErrNotExist) { + return nil + } + if !d.IsDir() { return err } - if stat.IsDir() { - return removeDir(fsys, path) + return removeDir(fsys, path) +} + +// RemoveAll removes path and any children it contains. It removes everything it +// can but returns the first error it encounters. If the path does not exist, +// RemoveAll returns nil (no error). +func RemoveAll(fsys FS, path string) error { + // Simple case: if Remove works, we're done. + err := fsys.Remove(path) + if err == nil || errors.Is(err, fs.ErrNotExist) { + return nil + } + + // Otherwise, is this a directory we need to recurse into? + dir, serr := Lstat(fsys, path) + if serr != nil { + if errors.Is(serr, fs.ErrNotExist) { + return nil + } + return serr + } + if !dir.IsDir() { + // Not a directory; return the error from Remove. + return err } - return fsys.Remove(path) + return removeDir(fsys, path) } -- cgit v1.3.1