Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions internal/cli/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ var FmtCmd = &cobra.Command{
err := crieRun.Run(runner.LintTypeFmt)

if err != nil {
log.Fatal(errchain.From(err).Error("crie format"))
log.Fatal(errchain.From(err).Link("crie format"))
}
},
}
Expand All @@ -59,7 +59,7 @@ var LsCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
err := crieRun.Languages.Show(os.Stdout)
if err != nil {
log.Fatal(errchain.From(err).Error("crie list failed"))
log.Fatal(errchain.From(err).Link("crie list failed"))
}
},
}
Expand All @@ -74,7 +74,7 @@ var ChkCmd = &cobra.Command{
err := crieRun.Run(runner.LintTypeChk)

if err != nil {
log.Fatal(errchain.From(err).Error("crie check"))
log.Fatal(errchain.From(err).Link("crie check"))
}
},
}
Expand All @@ -90,7 +90,7 @@ Find the file extensions that dont have an associated regex match within crieRun
Run: func(cmd *cobra.Command, args []string) {
err := crieRun.NoStandards()
if err != nil {
log.Fatal(errchain.From(err).Error("finding unassociated files"))
log.Fatal(errchain.From(err).Link("finding unassociated files"))
}
},
}
Expand Down Expand Up @@ -191,7 +191,7 @@ func stage(lintType runner.LintType) error {
log.Infof("❨ %s ❩", lintType.String())
err := crieRun.Run(lintType)
if err != nil {
return errchain.From(err).ErrorF("crie %s", lintType)
return errchain.From(err).LinkF("crie %s", lintType)
}
return nil
}
Expand Down
2 changes: 1 addition & 1 deletion internal/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ func addCrieCommand(cmd *cobra.Command) {

func errFatal(err error) {
if err != nil {
log.Fatal(errchain.From(err).Error("incorrect viper configuration"))
log.Fatal(errchain.From(err).Link("incorrect viper configuration"))
}
}

Expand Down
4 changes: 2 additions & 2 deletions internal/config/language/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ func LoadFile(path string) (*Languages, error) {

configData, err := os.ReadFile(path)
if err != nil {
return nil, errchain.From(err).ErrorF("readding config file %s", path)
return nil, errchain.From(err).LinkF("readding config file %s", path)
}

var c Languages
if err = yaml.Unmarshal(configData, &c); err != nil {
return nil, errchain.From(err).ErrorF("parsing config file %s", path)
return nil, errchain.From(err).LinkF("parsing config file %s", path)
}

merge(&defaultLanguageConfig, &c)
Expand Down
26 changes: 17 additions & 9 deletions internal/errchain/err.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,35 @@ package errchain

import "fmt"

// ErrorChain wraps an error and provides helpers to add contextual messages
// Chain represents a helper that wraps an existing error and allows
// adding contextual messages while preserving the original error using
// Go's error wrapping.
type Chain interface {
LinkF(format string, a ...any) error
Link(msg string) error
}

// errorChain wraps an error and provides helpers to add contextual messages
// while preserving the original error using Go's error wrapping.
type ErrorChain struct {
type errorChain struct {
err error
}

// From creates a new ErrorChain rooted at the provided error so that
// From creates a new errorChain rooted at the provided error so that
// additional context can be added in a consistent manner.
func From(err error) ErrorChain {
return ErrorChain{err: err}
func From(err error) Chain {
return errorChain{err: err}
}

// ErrorF formats a contextual message with the given format and arguments,
// LinkF formats a contextual message with the given format and arguments,
// and returns the original error wrapped with that context.
func (ec ErrorChain) ErrorF(format string, a ...any) error {
func (ec errorChain) LinkF(format string, a ...any) error {
msg := fmt.Sprintf(format, a...)
return fmt.Errorf("%s ⟶ %w", msg, ec.err)
}

// Error adds a contextual message and returns the original error wrapped
// Link adds a contextual message and returns the original error wrapped
// with that context.
func (ec ErrorChain) Error(msg string) error {
func (ec errorChain) Link(msg string) error {
return fmt.Errorf("%s ⟶ %w", msg, ec.err)
}
8 changes: 4 additions & 4 deletions internal/errchain/err_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
func TestError_WrapsAndAddsContext(t *testing.T) {
base := errors.New("base failure")

err := From(base).Error("doing important work")
err := From(base).Link("doing important work")

// It should wrap the base error
assert.ErrorIs(t, err, base)
Expand All @@ -24,7 +24,7 @@ func TestError_WrapsAndAddsContext(t *testing.T) {
func TestErrorF_WrapsAndFormats(t *testing.T) {
base := errors.New("not found")

err := From(base).ErrorF("fetching id %d", 42)
err := From(base).LinkF("fetching id %d", 42)

assert.ErrorIs(t, err, base)

Expand All @@ -37,9 +37,9 @@ func TestChaining_PreservesOriginalAndAllContexts(t *testing.T) {
base := errors.New("disk full")

// First wrap
err1 := From(base).Error("writing cache")
err1 := From(base).Link("writing cache")
// Second wrap by starting a new chain from the previous error
err2 := From(err1).ErrorF("attempt %d", 3)
err2 := From(err1).LinkF("attempt %d", 3)

assert.ErrorIs(t, err2, base)

Expand Down
29 changes: 29 additions & 0 deletions internal/filelist/dir.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package filelist

import (
"os"
"path/filepath"
)

// FromDir walks the provided directory and returns a list of files
// relative to dir. Directories themselves are not included.
func FromDir(dir string) ([]string, error) {
var files []string

// Create an initial file list
err := filepath.Walk(dir, func(currPath string, f os.FileInfo, err error) error {
if !f.IsDir() {
relPath, err := filepath.Rel(dir, currPath)
if err != nil {
return err
}
files = append(files, relPath)
}
return nil
})
if err != nil {
return nil, err
}

return files, nil
}
36 changes: 36 additions & 0 deletions internal/filelist/dir_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package filelist

import (
"os"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
)

func plainHelper(t *testing.T, files []string, dir string, contents string) {
t.Helper()
for _, file := range files {
testFilePath := filepath.Join(dir, file)
err := os.WriteFile(testFilePath, []byte(contents), 0644)
assert.NoError(t, err)
}
}

func TestFromDir(t *testing.T) {
tmpDir := t.TempDir()
subDir := filepath.Join(tmpDir, "sub")
assert.NoError(t, os.Mkdir(subDir, 0755))

dirFile := filepath.Join("sub", "dir.txt")
expFiles := []string{"a.txt", "b.txt", "c.txt", dirFile}
plainHelper(t, expFiles, tmpDir, "test")

files, err := FromDir(tmpDir)
assert.NoError(t, err)
assert.ElementsMatch(t, expFiles, files)

files, err = FromDir(subDir)
assert.NoError(t, err)
assert.ElementsMatch(t, baseFiles(dirFile), files)
}
Loading
Loading