diff --git a/filesystem.go b/filesystem.go index 56ab744..3c3e33d 100644 --- a/filesystem.go +++ b/filesystem.go @@ -15,6 +15,7 @@ type FileSystem interface { MkdirAll(path string, perm os.FileMode) error Symlink(source, target string) error Link(source, target string) error + RecordLinkAttempt(kind string, source, target string, err error, dryRun bool) SummaryLines() []string IsDryRun() bool } @@ -52,7 +53,10 @@ func (r operationRecord) summaryLine() (string, bool) { status = "DRY-RUN" } - return fmt.Sprintf("%s %s -> %s (%s)", kindLabel, r.source, r.target, status), true + source := FormatSourcePath(r.source) + target := FormatTargetPath(r.target) + + return fmt.Sprintf("%s %s → %s (%s)", kindLabel, source, target, status), true } type baseFileSystem struct { @@ -119,16 +123,20 @@ func (fs *realFileSystem) MkdirAll(path string, perm os.FileMode) error { func (fs *realFileSystem) Symlink(source, target string) error { err := os.Symlink(source, target) - fs.addOperation(opSymlink, source, target, err, false) + fs.RecordLinkAttempt(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) + fs.RecordLinkAttempt(opHardlink, source, target, err, false) return err } +func (fs *realFileSystem) RecordLinkAttempt(kind string, source, target string, err error, dryRun bool) { + fs.addOperation(kind, source, target, err, dryRun) +} + func (fs *realFileSystem) SummaryLines() []string { return summarizeOperations(fs.snapshot()) } @@ -162,15 +170,19 @@ func (fs *dryRunFileSystem) MkdirAll(path string, perm os.FileMode) error { } func (fs *dryRunFileSystem) Symlink(source, target string) error { - fs.addOperation(opSymlink, source, target, nil, true) + fs.RecordLinkAttempt(opSymlink, source, target, nil, true) return nil } func (fs *dryRunFileSystem) Link(source, target string) error { - fs.addOperation(opHardlink, source, target, nil, true) + fs.RecordLinkAttempt(opHardlink, source, target, nil, true) return nil } +func (fs *dryRunFileSystem) RecordLinkAttempt(kind string, source, target string, err error, dryRun bool) { + fs.addOperation(kind, source, target, err, dryRun) +} + func (fs *dryRunFileSystem) SummaryLines() []string { return summarizeOperations(fs.snapshot()) } diff --git a/instruction.go b/instruction.go index 11eafb1..de59532 100644 --- a/instruction.go +++ b/instruction.go @@ -177,19 +177,28 @@ func (instruction *LinkInstruction) RunAsync(status chan (error)) { return } + linkKind := opSymlink + if instruction.Hard { + linkKind = opHardlink + } + reportErr := func(err error) { + filesystem.RecordLinkAttempt(linkKind, instruction.Source, instruction.Target, err, filesystem.IsDryRun()) + status <- err + } + if !FileExists(instruction.Source) { - status <- fmt.Errorf("instruction source %s does not exist", FormatSourcePath(instruction.Source)) + reportErr(fmt.Errorf("instruction source %s does not exist", FormatSourcePath(instruction.Source))) return } if instruction.Files { info, err := os.Stat(instruction.Source) if err != nil { - status <- fmt.Errorf("could not stat source %s; err: %v", FormatSourcePath(instruction.Source), err) + reportErr(fmt.Errorf("could not stat source %s; err: %v", FormatSourcePath(instruction.Source), err)) return } if info.IsDir() { - status <- fmt.Errorf("source %s is a directory but files=true", FormatSourcePath(instruction.Source)) + reportErr(fmt.Errorf("source %s is a directory but files=true", FormatSourcePath(instruction.Source))) return } } @@ -207,31 +216,31 @@ func (instruction *LinkInstruction) RunAsync(status chan (error)) { if FileExists(instruction.Target) { if instruction.Files { if info, err := os.Stat(instruction.Target); err == nil && info.IsDir() { - status <- fmt.Errorf("target %s is a directory but files=true", FormatTargetPath(instruction.Target)) + reportErr(fmt.Errorf("target %s is a directory but files=true", FormatTargetPath(instruction.Target))) return } } if instruction.Force { isSymlink, err := IsSymlink(instruction.Target) if err != nil { - status <- fmt.Errorf("could not determine whether %s is a sym link or not, stopping; err: %v", - FormatTargetPath(instruction.Target), err) + reportErr(fmt.Errorf("could not determine whether %s is a sym link or not, stopping; err: %v", + FormatTargetPath(instruction.Target), err)) return } if instruction.Hard { info, err := os.Stat(instruction.Target) if err != nil { - status <- fmt.Errorf("could not stat %s, stopping; err: %v", - FormatTargetPath(instruction.Target), err) + reportErr(fmt.Errorf("could not stat %s, stopping; err: %v", + FormatTargetPath(instruction.Target), err)) return } if info.Mode().IsRegular() && info.Name() == filepath.Base(instruction.Source) { LogTarget("Overwriting existing file %s", instruction.Target) err := filesystem.Remove(instruction.Target) if err != nil { - status <- fmt.Errorf("could not remove existing file %s; err: %v", - FormatTargetPath(instruction.Target), err) + reportErr(fmt.Errorf("could not remove existing file %s; err: %v", + FormatTargetPath(instruction.Target), err)) return } } @@ -241,27 +250,27 @@ func (instruction *LinkInstruction) RunAsync(status chan (error)) { LogTarget("Removing symlink at %s", instruction.Target) err = filesystem.Remove(instruction.Target) if err != nil { - status <- fmt.Errorf("failed deleting %s due to %v", - FormatTargetPath(instruction.Target), err) + reportErr(fmt.Errorf("failed deleting %s due to %v", + FormatTargetPath(instruction.Target), err)) return } } else { if !instruction.Delete { - status <- fmt.Errorf("refusing to delte actual (non symlink) file %s", - FormatTargetPath(instruction.Target)) + reportErr(fmt.Errorf("refusing to delte actual (non symlink) file %s", + FormatTargetPath(instruction.Target))) return } LogImportant("Deleting (!!!) %s", instruction.Target) err = filesystem.RemoveAll(instruction.Target) if err != nil { - status <- fmt.Errorf("failed deleting %s due to %v", - FormatTargetPath(instruction.Target), err) + reportErr(fmt.Errorf("failed deleting %s due to %v", + FormatTargetPath(instruction.Target), err)) return } } } else { - status <- fmt.Errorf("target %s exists - handle manually or set the 'forced' flag (3rd field)", - FormatTargetPath(instruction.Target)) + reportErr(fmt.Errorf("target %s exists - handle manually or set the 'forced' flag (3rd field)", + FormatTargetPath(instruction.Target))) return } } @@ -270,8 +279,8 @@ func (instruction *LinkInstruction) RunAsync(status chan (error)) { if _, err := os.Stat(targetDir); os.IsNotExist(err) { err = filesystem.MkdirAll(targetDir, 0755) if err != nil { - status <- fmt.Errorf("failed creating directory %s due to %v", - FormatTargetPath(targetDir), err) + reportErr(fmt.Errorf("failed creating directory %s due to %v", + FormatTargetPath(targetDir), err)) return } } diff --git a/instruction_test.go b/instruction_test.go index 4f21f30..03a05d8 100644 --- a/instruction_test.go +++ b/instruction_test.go @@ -420,8 +420,8 @@ func TestLinkInstruction_RunAsync(t *testing.T) { summary := filesystem.SummaryLines() assert.Equal(t, 1, len(summary)) assert.Contains(t, summary[0], "DRY-RUN") - assert.Contains(t, summary[0], srcFile) - assert.Contains(t, summary[0], target) + assert.Contains(t, summary[0], FormatSourcePath(srcFile)) + assert.Contains(t, summary[0], FormatTargetPath(target)) }) }