122 lines
3.0 KiB
Go
122 lines
3.0 KiB
Go
|
package toolkit
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"io/fs"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
)
|
||
|
|
||
|
// Dir is a type which makes it possible to statically assert that a directory
|
||
|
// has already been created.
|
||
|
type Dir struct {
|
||
|
Path string
|
||
|
}
|
||
|
|
||
|
// MkDir creates a Dir at the given path.
|
||
|
//
|
||
|
// If the directory already exists, and mayExist is true, then Dir is returned
|
||
|
// successfully, otherwise an error is returned.
|
||
|
func MkDir(path string, mayExist bool) (Dir, error) {
|
||
|
if path == "" {
|
||
|
panic("Empty path passed to MkDir")
|
||
|
}
|
||
|
|
||
|
{
|
||
|
parentPath := filepath.Dir(path)
|
||
|
parentInfo, err := os.Stat(parentPath)
|
||
|
if err != nil {
|
||
|
return Dir{}, fmt.Errorf(
|
||
|
"checking fs node of parent %q: %w", parentPath, err,
|
||
|
)
|
||
|
} else if !parentInfo.IsDir() {
|
||
|
return Dir{}, fmt.Errorf(
|
||
|
"parent %q is not a directory", parentPath,
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
info, err := os.Stat(path)
|
||
|
if errors.Is(err, fs.ErrNotExist) {
|
||
|
// fine
|
||
|
} else if err != nil {
|
||
|
return Dir{}, fmt.Errorf("checking fs node: %w", err)
|
||
|
} else if !info.IsDir() {
|
||
|
return Dir{}, fmt.Errorf("exists but is not a directory")
|
||
|
} else {
|
||
|
if !mayExist {
|
||
|
return Dir{}, errors.New("directory already exists")
|
||
|
}
|
||
|
return Dir{path}, nil
|
||
|
}
|
||
|
|
||
|
if err := os.Mkdir(path, 0700); err != nil {
|
||
|
return Dir{}, fmt.Errorf("creating directory: %w", err)
|
||
|
}
|
||
|
|
||
|
return Dir{path}, nil
|
||
|
}
|
||
|
|
||
|
// MkChildDir is a helper for joining Dir's path to the given name and calling
|
||
|
// MkDir with the result.
|
||
|
func (d Dir) MkChildDir(name string, mayExist bool) (Dir, error) {
|
||
|
childPath := filepath.Join(d.Path, name)
|
||
|
d, err := MkDir(childPath, mayExist)
|
||
|
if err != nil {
|
||
|
return Dir{}, fmt.Errorf(
|
||
|
"creating child directory %q: %w", childPath, err,
|
||
|
)
|
||
|
}
|
||
|
return d, nil
|
||
|
}
|
||
|
|
||
|
// ChildDirs returns a Dir for every child directory found under this Dir.
|
||
|
func (d Dir) ChildDirs() ([]Dir, error) {
|
||
|
entries, err := os.ReadDir(d.Path)
|
||
|
if errors.Is(err, fs.ErrNotExist) {
|
||
|
return nil, nil
|
||
|
} else if err != nil {
|
||
|
return nil, fmt.Errorf("listing contents: %w", err)
|
||
|
}
|
||
|
|
||
|
dirs := make([]Dir, 0, len(entries))
|
||
|
for _, entry := range entries {
|
||
|
if !entry.IsDir() {
|
||
|
continue
|
||
|
}
|
||
|
dirs = append(dirs, Dir{Path: filepath.Join(d.Path, entry.Name())})
|
||
|
}
|
||
|
|
||
|
return dirs, nil
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
// MkDirHelper is a helper type for creating a set of directories. It will
|
||
|
// collect errors as they occur, allowing error handling to be defered.
|
||
|
type MkDirHelper struct {
|
||
|
errs []error
|
||
|
}
|
||
|
|
||
|
// Maybe returns the given Dir and true if err == nil. If err != nil then false
|
||
|
// is returned, and the error is collected internally such that it will be
|
||
|
// returned as part of the Err() output.
|
||
|
//
|
||
|
// This method is designed to be used in conjunction with a MkDir
|
||
|
// function/method, e.g:
|
||
|
//
|
||
|
// d, ok := h.Maybe(MkDir("some/path", true))
|
||
|
func (m *MkDirHelper) Maybe(d Dir, err error) (Dir, bool) {
|
||
|
if err != nil {
|
||
|
m.errs = append(m.errs, err)
|
||
|
return Dir{}, false
|
||
|
}
|
||
|
return d, true
|
||
|
}
|
||
|
|
||
|
// Err returns a join of all errors which have occurred during its lifetime.
|
||
|
func (m *MkDirHelper) Err() error {
|
||
|
return errors.Join(m.errs...)
|
||
|
}
|