181 lines
3.9 KiB
Go
181 lines
3.9 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"sync"
|
|
)
|
|
|
|
var filesystem FileSystem = NewRealFileSystem()
|
|
|
|
// FileSystem abstracts filesystem operations so we can swap implementations
|
|
type FileSystem interface {
|
|
Remove(path string) error
|
|
RemoveAll(path string) error
|
|
MkdirAll(path string, perm os.FileMode) error
|
|
Symlink(source, target string) error
|
|
Link(source, target string) error
|
|
SummaryLines() []string
|
|
IsDryRun() bool
|
|
}
|
|
|
|
const (
|
|
opRemove = "remove"
|
|
opRemoveAll = "remove_all"
|
|
opMkdirAll = "mkdir_all"
|
|
opSymlink = "symlink"
|
|
opHardlink = "hardlink"
|
|
)
|
|
|
|
type operationRecord struct {
|
|
kind string
|
|
source string
|
|
target string
|
|
err error
|
|
dryRun bool
|
|
}
|
|
|
|
func (r operationRecord) summaryLine() (string, bool) {
|
|
if r.kind != opSymlink && r.kind != opHardlink {
|
|
return "", false
|
|
}
|
|
|
|
kindLabel := "Symlink"
|
|
if r.kind == opHardlink {
|
|
kindLabel = "Hardlink"
|
|
}
|
|
|
|
status := "OK"
|
|
if r.err != nil {
|
|
status = fmt.Sprintf("FAIL: %v", r.err)
|
|
} else if r.dryRun {
|
|
status = "DRY-RUN"
|
|
}
|
|
|
|
return fmt.Sprintf("%s %s -> %s (%s)", kindLabel, r.source, r.target, status), true
|
|
}
|
|
|
|
type baseFileSystem struct {
|
|
mu sync.Mutex
|
|
operations []operationRecord
|
|
}
|
|
|
|
func (b *baseFileSystem) addOperation(kind, source, target string, err error, dryRun bool) {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
b.operations = append(b.operations, operationRecord{
|
|
kind: kind,
|
|
source: source,
|
|
target: target,
|
|
err: err,
|
|
dryRun: dryRun,
|
|
})
|
|
}
|
|
|
|
func (b *baseFileSystem) snapshot() []operationRecord {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
ops := make([]operationRecord, len(b.operations))
|
|
copy(ops, b.operations)
|
|
return ops
|
|
}
|
|
|
|
func summarizeOperations(records []operationRecord) []string {
|
|
var lines []string
|
|
for _, record := range records {
|
|
if line, ok := record.summaryLine(); ok {
|
|
lines = append(lines, line)
|
|
}
|
|
}
|
|
return lines
|
|
}
|
|
|
|
type realFileSystem struct {
|
|
baseFileSystem
|
|
}
|
|
|
|
// NewRealFileSystem returns a filesystem implementation that writes to disk
|
|
func NewRealFileSystem() FileSystem {
|
|
return &realFileSystem{}
|
|
}
|
|
|
|
func (fs *realFileSystem) Remove(path string) error {
|
|
err := os.Remove(path)
|
|
fs.addOperation(opRemove, "", path, err, false)
|
|
return err
|
|
}
|
|
|
|
func (fs *realFileSystem) RemoveAll(path string) error {
|
|
err := os.RemoveAll(path)
|
|
fs.addOperation(opRemoveAll, "", path, err, false)
|
|
return err
|
|
}
|
|
|
|
func (fs *realFileSystem) MkdirAll(path string, perm os.FileMode) error {
|
|
err := os.MkdirAll(path, perm)
|
|
fs.addOperation(opMkdirAll, "", path, err, false)
|
|
return err
|
|
}
|
|
|
|
func (fs *realFileSystem) Symlink(source, target string) error {
|
|
err := os.Symlink(source, target)
|
|
fs.addOperation(opSymlink, source, target, err, false)
|
|
return err
|
|
}
|
|
|
|
func (fs *realFileSystem) Link(source, target string) error {
|
|
err := os.Link(source, target)
|
|
fs.addOperation(opHardlink, source, target, err, false)
|
|
return err
|
|
}
|
|
|
|
func (fs *realFileSystem) SummaryLines() []string {
|
|
return summarizeOperations(fs.snapshot())
|
|
}
|
|
|
|
func (fs *realFileSystem) IsDryRun() bool {
|
|
return false
|
|
}
|
|
|
|
type dryRunFileSystem struct {
|
|
baseFileSystem
|
|
}
|
|
|
|
// NewDryRunFileSystem returns a filesystem implementation that only records operations
|
|
func NewDryRunFileSystem() FileSystem {
|
|
return &dryRunFileSystem{}
|
|
}
|
|
|
|
func (fs *dryRunFileSystem) Remove(path string) error {
|
|
fs.addOperation(opRemove, "", path, nil, true)
|
|
return nil
|
|
}
|
|
|
|
func (fs *dryRunFileSystem) RemoveAll(path string) error {
|
|
fs.addOperation(opRemoveAll, "", path, nil, true)
|
|
return nil
|
|
}
|
|
|
|
func (fs *dryRunFileSystem) MkdirAll(path string, perm os.FileMode) error {
|
|
fs.addOperation(opMkdirAll, "", path, nil, true)
|
|
return nil
|
|
}
|
|
|
|
func (fs *dryRunFileSystem) Symlink(source, target string) error {
|
|
fs.addOperation(opSymlink, source, target, nil, true)
|
|
return nil
|
|
}
|
|
|
|
func (fs *dryRunFileSystem) Link(source, target string) error {
|
|
fs.addOperation(opHardlink, source, target, nil, true)
|
|
return nil
|
|
}
|
|
|
|
func (fs *dryRunFileSystem) SummaryLines() []string {
|
|
return summarizeOperations(fs.snapshot())
|
|
}
|
|
|
|
func (fs *dryRunFileSystem) IsDryRun() bool {
|
|
return true
|
|
}
|