diff --git a/arguments.go b/arguments.go index 1960d17..2a9fef7 100644 --- a/arguments.go +++ b/arguments.go @@ -23,16 +23,18 @@ var defaultConcurrency = func() int { const usage = `Link checker for Markdown and HTML Usage: - liche [-c ] [-t ] [-v] ... + liche [-c ] [-r] [-t ] [-v] ... Options: -c, --concurrency Set max number of concurrent HTTP requests. [default: %v] + -r, --recursive Search Markdown and HTML files recursively -t, --timeout Set timeout for HTTP requests in seconds. Disabled by default. -v, --verbose Be verbose.` type arguments struct { filenames []string concurrency int + recursive bool timeout time.Duration verbose bool } @@ -63,6 +65,7 @@ func getArgs() (arguments, error) { return arguments{ args[""].([]string), int(c), + args["--recursive"].(bool), time.Duration(t) * time.Second, args["--verbose"].(bool), }, nil diff --git a/examples/options.feature b/examples/options.feature index fdefe8e..b63306c 100644 --- a/examples/options.feature +++ b/examples/options.feature @@ -41,3 +41,12 @@ Feature: Options """ When I successfully run `liche --concurrency 10 foo.md` Then the stdout should contain exactly "" + + Scenario: Search files recursively + Given a directory named "foo" + And a file named "foo/bar.md" with: + """ + [Google](https://google.com) + """ + When I successfully run `liche --recursive -v foo` + Then the stderr should contain "OK" diff --git a/extensions.go b/extensions.go new file mode 100644 index 0000000..17757ed --- /dev/null +++ b/extensions.go @@ -0,0 +1,14 @@ +package main + +import "path/filepath" + +var extensions = map[string]bool{ + ".htm": true, + ".html": true, + ".md": true, +} + +func isMarkupFile(f string) bool { + _, ok := extensions[filepath.Ext(f)] + return ok +} diff --git a/extensions_test.go b/extensions_test.go new file mode 100644 index 0000000..61d3e68 --- /dev/null +++ b/extensions_test.go @@ -0,0 +1,13 @@ +package main + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestIsMarkupFile(t *testing.T) { + for _, f := range []string{"foo.md", "foo.html", "foo.htm", "foo/bar.md"} { + assert.True(t, isMarkupFile(f)) + } +} diff --git a/main.go b/main.go index 415d4cb..4957d72 100644 --- a/main.go +++ b/main.go @@ -10,6 +10,15 @@ func main() { os.Exit(1) } + if args.recursive { + args.filenames, err = listFilesRecursively(args.filenames) + + if err != nil { + printToStderr(err.Error()) + os.Exit(1) + } + } + rc := make(chan fileResult, len(args.filenames)) s := newSemaphore(args.concurrency) c := newFileChecker(args.timeout, s) @@ -31,3 +40,23 @@ func main() { os.Exit(1) } } + +func listFilesRecursively(fs []string) ([]string, error) { + gs := []string{} + + for _, f := range fs { + i, err := os.Stat(f) + + if err != nil { + return nil, err + } + + if i.IsDir() { + gs = append(gs, listFiles(f)...) + } else { + gs = append(gs, f) + } + } + + return gs, nil +} diff --git a/utils.go b/utils.go index b4b16bc..d9eaa80 100644 --- a/utils.go +++ b/utils.go @@ -3,6 +3,8 @@ package main import ( "fmt" "os" + "path/filepath" + "regexp" "github.com/kr/text" ) @@ -24,3 +26,33 @@ func printToStderr(xs ...interface{}) { func indent(s string) string { return text.Indent(s, "\t") } + +func listFiles(d string) []string { + fc := make(chan string, 1024) + + go func() { + filepath.Walk(d, func(p string, f os.FileInfo, err error) error { + b, err := regexp.MatchString("(^\\.)|(/\\.)", p) + + if err != nil { + return err + } + + if !f.IsDir() && !b && isMarkupFile(p) { + fc <- p + } + + return nil + }) + + close(fc) + }() + + fs := []string{} + + for f := range fc { + fs = append(fs, f) + } + + return fs +} diff --git a/utils_test.go b/utils_test.go new file mode 100644 index 0000000..3d6ce34 --- /dev/null +++ b/utils_test.go @@ -0,0 +1,22 @@ +package main + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestListFiles(t *testing.T) { + fs := listFiles(".") + + assert.NotEqual(t, 0, len(fs)) + + for _, f := range fs { + i, err := os.Stat(f) + + assert.True(t, isMarkupFile(f)) + assert.Equal(t, nil, err) + assert.False(t, i.IsDir()) + } +}