Skip to content

Commit

Permalink
Refactor linker
Browse files Browse the repository at this point in the history
  • Loading branch information
hypnoglow committed Dec 3, 2016
1 parent c3df95e commit 0ccffd1
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 31 deletions.
70 changes: 70 additions & 0 deletions fs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// This file provides an interface and mocks to os functions used in dotbro.

package main

import "os"

type DirMaker interface {
MkdirAll(path string, perm os.FileMode) error
}

type FakeDirMaker struct {
MkdirAllError error
}

func (f *FakeDirMaker) MkdirAll(path string, perm os.FileMode) error {
return f.MkdirAllError
}

type Symlinker interface {
Symlink(oldname, newname string) error
}

type FakeSymlinker struct {
SymlinkError error
}

func (f *FakeSymlinker) Symlink(oldname, newname string) error {
return f.SymlinkError
}

type Stater interface {
Stat(name string) (os.FileInfo, error)
IsNotExist(err error) bool
}

type MkdirSymlinker interface {
DirMaker
Symlinker
}

type FakeMkdirSymlinker struct {
*FakeDirMaker
*FakeSymlinker
}

type StatDirMaker interface {
Stater
DirMaker
}

// Actual implementation of interface using os package.

type OsDirMaker struct {
}

func (f *OsDirMaker) MkdirAll(path string, perm os.FileMode) error {
return os.MkdirAll(path, perm)
}

type OsSymlinker struct {
}

func (f *OsSymlinker) Symlink(oldname, newname string) error {
return os.Symlink(oldname, newname)
}

type OsMkdirSymlinker struct {
*OsDirMaker
*OsSymlinker
}
6 changes: 1 addition & 5 deletions helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,7 @@ import (
"path/filepath"
)

type Stater interface {
Stat(name string) (os.FileInfo, error)
IsNotExist(err error) bool
}

// TODO: remove in favor of StatDirMaker
type DirCheckMaker interface {
Stater
MkdirAll(path string, perm os.FileMode) error
Expand Down
27 changes: 17 additions & 10 deletions linker.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ import (
"path/filepath"
)

type Linker struct {
outputer IOutputer
fs MkdirSymlinker
}

func NewLinker(outputer IOutputer, fs MkdirSymlinker) Linker {
return Linker{
outputer: outputer,
fs: fs,
}
}

// needSymlink reports whether source file needs to be symlinked to destination path.
func needSymlink(src, dest string) (bool, error) {
fi, err := os.Lstat(dest)
Expand Down Expand Up @@ -96,23 +108,18 @@ func backupCopy(filename, backupDir string) error {
return err
}

// setSymlink symlinks scrAbs to destAbs
func setSymlink(srcAbs string, destAbs string) error {
var err error

// todo: if dry-run, just print
// SetSymlink symlinks scrAbs to destAbs.
func (l *Linker) SetSymlink(srcAbs string, destAbs string) error {

dir := path.Dir(destAbs)
err = os.MkdirAll(dir, 0700)
if err != nil {
if err := l.fs.MkdirAll(dir, 0700); err != nil {
return err
}

err = os.Symlink(srcAbs, destAbs)
if err != nil {
if err := l.fs.Symlink(srcAbs, destAbs); err != nil {
return err
}

outputer.OutInfo(" ✓ set symlink %s -> %s", srcAbs, destAbs)
l.outputer.OutInfo(" ✓ set symlink %s -> %s", srcAbs, destAbs)
return nil
}
72 changes: 62 additions & 10 deletions linker_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package main

import (
"fmt"
"errors"
"io/ioutil"
"os"
"path"
"reflect"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -155,16 +156,67 @@ func TestBackupCopy(t *testing.T) {
assert.Equal(t, filenameContent, backupContent)
}

func TestSetSymlink(t *testing.T) {
type FakeOutputer struct{}

os.RemoveAll("/tmp/dotbro") // Cleanup
func (o *FakeOutputer) OutVerbose(format string, v ...interface{}) {
return
}

srcAbs := "/tmp/dotbro/linker/TestSetSymlink/file"
destAbs := "/tmp/dotbro/linker/TestSetSymlink/filesymlink"
err := setSymlink(srcAbs, destAbs)
assert.Nil(t, err)
func (o *FakeOutputer) OutInfo(format string, v ...interface{}) {
return
}

func (o *FakeOutputer) OutWarn(format string, v ...interface{}) {
return
}

func (o *FakeOutputer) OutError(format string, v ...interface{}) {
return
}

func TestNewLinker(t *testing.T) {
cases := []struct {
mkdirSymlinker *FakeMkdirSymlinker
srcAbs string
destAbs string
expectedError error
}{
{
mkdirSymlinker: &FakeMkdirSymlinker{
&FakeDirMaker{MkdirAllError: nil},
&FakeSymlinker{SymlinkError: nil},
},
srcAbs: "/src/path",
destAbs: "/dest/path",
expectedError: nil,
},
{
mkdirSymlinker: &FakeMkdirSymlinker{
&FakeDirMaker{MkdirAllError: errors.New("Permission denied")},
&FakeSymlinker{SymlinkError: nil},
},
srcAbs: "/src/path",
destAbs: "/dest/path",
expectedError: errors.New("Permission denied"),
},
{
mkdirSymlinker: &FakeMkdirSymlinker{
&FakeDirMaker{MkdirAllError: nil},
&FakeSymlinker{SymlinkError: errors.New("File exists")},
},
srcAbs: "/src/path",
destAbs: "/dest/path",
expectedError: errors.New("File exists"),
},
}

for _, c := range cases {
linker := NewLinker(&FakeOutputer{}, c.mkdirSymlinker)

err := linker.SetSymlink(c.srcAbs, c.destAbs)
if !reflect.DeepEqual(err, c.expectedError) {
t.Errorf("Expected err to be %v but it was %v\n", c.expectedError, err)
}
}

// Calling again should return an error since the link already exists
err = setSymlink(srcAbs, destAbs)
assert.EqualError(t, err, fmt.Sprintf("symlink %s %s: file exists", srcAbs, destAbs))
}
16 changes: 10 additions & 6 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ func (dcm *OsDirCheckMaker) MkdirAll(path string, perm os.FileMode) error {
}

var (
osStater = new(OsStater)
osDirCheckMaker = new(OsDirCheckMaker)
osStater = new(OsStater)
osDirCheckMaker = new(OsDirCheckMaker)
osMkdirSymlinker = new(OsMkdirSymlinker)
)

func main() {
Expand Down Expand Up @@ -157,8 +158,10 @@ func addAction(filename string, config *Configuration) error {
return err
}

linker := NewLinker(&outputer, osMkdirSymlinker)

// Add a symlink to the moved file
if err = setSymlink(newPath, filename); err != nil {
if err = linker.SetSymlink(newPath, filename); err != nil {
return err
}

Expand Down Expand Up @@ -187,10 +190,11 @@ func installAction(config *Configuration) error {
}

mapping := getMapping(config, srcDirAbs)
linker := NewLinker(&outputer, osMkdirSymlinker)

outputer.OutInfo("Installing dotfiles...")
for src, dst := range mapping {
installDotfile(src, dst, config, srcDirAbs)
installDotfile(src, dst, linker, config, srcDirAbs)
}

return nil
Expand Down Expand Up @@ -285,7 +289,7 @@ func getMapping(config *Configuration, srcDirAbs string) map[string]string {
return mapping
}

func installDotfile(src, dest string, config *Configuration, srcDirAbs string) {
func installDotfile(src, dest string, linker Linker, config *Configuration, srcDirAbs string) {
srcAbs := srcDirAbs + "/" + src
destAbs := config.Directories.Destination + "/" + dest

Expand Down Expand Up @@ -324,7 +328,7 @@ func installDotfile(src, dest string, config *Configuration, srcDirAbs string) {
}
}

err = setSymlink(srcAbs, destAbs)
err = linker.SetSymlink(srcAbs, destAbs)
if err != nil {
outputer.OutError("Error creating symlink from %s to %s: %s", srcAbs, destAbs, err)
exit(1)
Expand Down
7 changes: 7 additions & 0 deletions outputer.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ const (
OutputerModeVerbose OutputerMode = "9"
)

type IOutputer interface {
OutVerbose(format string, v ...interface{})
OutInfo(format string, v ...interface{})
OutWarn(format string, v ...interface{})
OutError(format string, v ...interface{})
}

// Outputer is a logger that shows the output to the user.
type Outputer struct {
Mode OutputerMode
Expand Down

0 comments on commit 0ccffd1

Please sign in to comment.