forked from toolshed/abra
chore: vendor
This commit is contained in:
278
vendor/gotest.tools/v3/fs/ops.go
vendored
Normal file
278
vendor/gotest.tools/v3/fs/ops.go
vendored
Normal file
@ -0,0 +1,278 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
const defaultFileMode = 0644
|
||||
|
||||
// PathOp is a function which accepts a [Path] and performs an operation on that
|
||||
// path. When called with real filesystem objects ([File] or [Dir]) a PathOp modifies
|
||||
// the filesystem at the path. When used with a [Manifest] object a PathOp updates
|
||||
// the manifest to expect a value.
|
||||
type PathOp func(path Path) error
|
||||
|
||||
type manifestResource interface {
|
||||
SetMode(mode os.FileMode)
|
||||
SetUID(uid uint32)
|
||||
SetGID(gid uint32)
|
||||
}
|
||||
|
||||
type manifestFile interface {
|
||||
manifestResource
|
||||
SetContent(content io.ReadCloser)
|
||||
}
|
||||
|
||||
type manifestDirectory interface {
|
||||
manifestResource
|
||||
AddSymlink(path, target string) error
|
||||
AddFile(path string, ops ...PathOp) error
|
||||
AddDirectory(path string, ops ...PathOp) error
|
||||
}
|
||||
|
||||
// WithContent writes content to a file at [Path]
|
||||
func WithContent(content string) PathOp {
|
||||
return func(path Path) error {
|
||||
if m, ok := path.(manifestFile); ok {
|
||||
m.SetContent(io.NopCloser(strings.NewReader(content)))
|
||||
return nil
|
||||
}
|
||||
return os.WriteFile(path.Path(), []byte(content), defaultFileMode)
|
||||
}
|
||||
}
|
||||
|
||||
// WithBytes write bytes to a file at [Path]
|
||||
func WithBytes(raw []byte) PathOp {
|
||||
return func(path Path) error {
|
||||
if m, ok := path.(manifestFile); ok {
|
||||
m.SetContent(io.NopCloser(bytes.NewReader(raw)))
|
||||
return nil
|
||||
}
|
||||
return os.WriteFile(path.Path(), raw, defaultFileMode)
|
||||
}
|
||||
}
|
||||
|
||||
// WithReaderContent copies the reader contents to the file at [Path]
|
||||
func WithReaderContent(r io.Reader) PathOp {
|
||||
return func(path Path) error {
|
||||
if m, ok := path.(manifestFile); ok {
|
||||
m.SetContent(io.NopCloser(r))
|
||||
return nil
|
||||
}
|
||||
f, err := os.OpenFile(path.Path(), os.O_WRONLY, defaultFileMode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = io.Copy(f, r)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// AsUser changes ownership of the file system object at [Path]
|
||||
func AsUser(uid, gid int) PathOp {
|
||||
return func(path Path) error {
|
||||
if m, ok := path.(manifestResource); ok {
|
||||
m.SetUID(uint32(uid))
|
||||
m.SetGID(uint32(gid))
|
||||
return nil
|
||||
}
|
||||
return os.Chown(path.Path(), uid, gid)
|
||||
}
|
||||
}
|
||||
|
||||
// WithFile creates a file in the directory at path with content
|
||||
func WithFile(filename, content string, ops ...PathOp) PathOp {
|
||||
return func(path Path) error {
|
||||
if m, ok := path.(manifestDirectory); ok {
|
||||
ops = append([]PathOp{WithContent(content), WithMode(defaultFileMode)}, ops...)
|
||||
return m.AddFile(filename, ops...)
|
||||
}
|
||||
|
||||
fullpath := filepath.Join(path.Path(), filepath.FromSlash(filename))
|
||||
if err := createFile(fullpath, content); err != nil {
|
||||
return err
|
||||
}
|
||||
return applyPathOps(&File{path: fullpath}, ops)
|
||||
}
|
||||
}
|
||||
|
||||
func createFile(fullpath string, content string) error {
|
||||
return os.WriteFile(fullpath, []byte(content), defaultFileMode)
|
||||
}
|
||||
|
||||
// WithFiles creates all the files in the directory at path with their content
|
||||
func WithFiles(files map[string]string) PathOp {
|
||||
return func(path Path) error {
|
||||
if m, ok := path.(manifestDirectory); ok {
|
||||
for filename, content := range files {
|
||||
// TODO: remove duplication with WithFile
|
||||
if err := m.AddFile(filename, WithContent(content), WithMode(defaultFileMode)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
for filename, content := range files {
|
||||
fullpath := filepath.Join(path.Path(), filepath.FromSlash(filename))
|
||||
if err := createFile(fullpath, content); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// FromDir copies the directory tree from the source path into the new [Dir]
|
||||
func FromDir(source string) PathOp {
|
||||
return func(path Path) error {
|
||||
if _, ok := path.(manifestDirectory); ok {
|
||||
return fmt.Errorf("use manifest.FromDir")
|
||||
}
|
||||
return copyDirectory(source, path.Path())
|
||||
}
|
||||
}
|
||||
|
||||
// WithDir creates a subdirectory in the directory at path. Additional [PathOp]
|
||||
// can be used to modify the subdirectory
|
||||
func WithDir(name string, ops ...PathOp) PathOp {
|
||||
const defaultMode = 0755
|
||||
return func(path Path) error {
|
||||
if m, ok := path.(manifestDirectory); ok {
|
||||
ops = append([]PathOp{WithMode(defaultMode)}, ops...)
|
||||
return m.AddDirectory(name, ops...)
|
||||
}
|
||||
|
||||
fullpath := filepath.Join(path.Path(), filepath.FromSlash(name))
|
||||
err := os.MkdirAll(fullpath, defaultMode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return applyPathOps(&Dir{path: fullpath}, ops)
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the PathOps to the [File]
|
||||
func Apply(t assert.TestingT, path Path, ops ...PathOp) {
|
||||
if ht, ok := t.(helperT); ok {
|
||||
ht.Helper()
|
||||
}
|
||||
assert.NilError(t, applyPathOps(path, ops))
|
||||
}
|
||||
|
||||
func applyPathOps(path Path, ops []PathOp) error {
|
||||
for _, op := range ops {
|
||||
if err := op(path); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithMode sets the file mode on the directory or file at [Path]
|
||||
func WithMode(mode os.FileMode) PathOp {
|
||||
return func(path Path) error {
|
||||
if m, ok := path.(manifestResource); ok {
|
||||
m.SetMode(mode)
|
||||
return nil
|
||||
}
|
||||
return os.Chmod(path.Path(), mode)
|
||||
}
|
||||
}
|
||||
|
||||
func copyDirectory(source, dest string) error {
|
||||
entries, err := os.ReadDir(source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, entry := range entries {
|
||||
sourcePath := filepath.Join(source, entry.Name())
|
||||
destPath := filepath.Join(dest, entry.Name())
|
||||
err = copyEntry(entry, destPath, sourcePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyEntry(entry os.DirEntry, destPath string, sourcePath string) error {
|
||||
if entry.IsDir() {
|
||||
if err := os.Mkdir(destPath, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
return copyDirectory(sourcePath, destPath)
|
||||
}
|
||||
info, err := entry.Info()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.Mode()&os.ModeSymlink != 0 {
|
||||
return copySymLink(sourcePath, destPath)
|
||||
}
|
||||
return copyFile(sourcePath, destPath)
|
||||
}
|
||||
|
||||
func copySymLink(source, dest string) error {
|
||||
link, err := os.Readlink(source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Symlink(link, dest)
|
||||
}
|
||||
|
||||
func copyFile(source, dest string) error {
|
||||
content, err := os.ReadFile(source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(dest, content, 0644)
|
||||
}
|
||||
|
||||
// WithSymlink creates a symlink in the directory which links to target.
|
||||
// Target must be a path relative to the directory.
|
||||
//
|
||||
// Note: the argument order is the inverse of [os.Symlink] to be consistent with
|
||||
// the other functions in this package.
|
||||
func WithSymlink(path, target string) PathOp {
|
||||
return func(root Path) error {
|
||||
if v, ok := root.(manifestDirectory); ok {
|
||||
return v.AddSymlink(path, target)
|
||||
}
|
||||
return os.Symlink(filepath.Join(root.Path(), target), filepath.Join(root.Path(), path))
|
||||
}
|
||||
}
|
||||
|
||||
// WithHardlink creates a link in the directory which links to target.
|
||||
// Target must be a path relative to the directory.
|
||||
//
|
||||
// Note: the argument order is the inverse of [os.Link] to be consistent with
|
||||
// the other functions in this package.
|
||||
func WithHardlink(path, target string) PathOp {
|
||||
return func(root Path) error {
|
||||
if _, ok := root.(manifestDirectory); ok {
|
||||
return fmt.Errorf("WithHardlink not implemented for manifests")
|
||||
}
|
||||
return os.Link(filepath.Join(root.Path(), target), filepath.Join(root.Path(), path))
|
||||
}
|
||||
}
|
||||
|
||||
// WithTimestamps sets the access and modification times of the file system object
|
||||
// at path.
|
||||
func WithTimestamps(atime, mtime time.Time) PathOp {
|
||||
return func(root Path) error {
|
||||
if _, ok := root.(manifestDirectory); ok {
|
||||
return fmt.Errorf("WithTimestamp not implemented for manifests")
|
||||
}
|
||||
return os.Chtimes(root.Path(), atime, mtime)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user