isle/go/toolkit/dir.go
Brian Picciano 8c3e6a2845 Separate Daemon and Network logic into separate packages
In a world where the daemon can manage more than one network, the Daemon
is really responsible only for knowing which networks are currently
joined, creating/joining/leaving networks, and routing incoming RPC
requests to the correct network handler as needed.

The new network package, with its Network interface, inherits most of
the logic that Daemon used to have, leaving Daemon only the parts needed
for the functionality just described. There's a lot of cleanup done here
in order to really nail down the separation of concerns between the two,
especially around directory creation.
2024-09-09 16:34:00 +02:00

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...)
}