diff --git a/examples/markdown.feature b/examples/markdown.feature index 4be900f..25ac7a4 100644 --- a/examples/markdown.feature +++ b/examples/markdown.feature @@ -92,4 +92,4 @@ Feature: Markdown linkcheck -v foo.md 2>&1 | wc -l """ When I successfully run `sh foo.sh` - Then the stdout should contain exactly "4" + Then the stdout should contain exactly "5" diff --git a/file_checker.go b/file_checker.go index 8b41f1e..059e7a9 100644 --- a/file_checker.go +++ b/file_checker.go @@ -3,6 +3,7 @@ package main import ( "bytes" "io/ioutil" + "strings" "time" "golang.org/x/net/html" @@ -17,20 +18,58 @@ func newFileChecker(timeout time.Duration, verbose bool) fileChecker { return fileChecker{newURLChecker(timeout, verbose)} } -func (c fileChecker) Check(f string) bool { +func (c fileChecker) Check(f string) ([]urlResult, error) { bs, err := ioutil.ReadFile(f) if err != nil { - printToStderr(err.Error()) - return false + return nil, err } n, err := html.Parse(bytes.NewReader(blackfriday.Run(bs))) if err != nil { - printToStderr(err.Error()) + return nil, err + } + + us := extractURLs(n) + rc := make(chan urlResult, len(us)) + rs := make([]urlResult, 0, len(us)) + + go c.urlChecker.CheckMany(us, rc) + + for r := range rc { + rs = append(rs, r) + } + + return rs, nil +} + +type fileResult struct { + filename string + urlResults []urlResult + err error +} + +func (r fileResult) String() string { + ss := make([]string, 0, len(r.urlResults)) + + for _, r := range r.urlResults { + ss = append(ss, "\t"+r.String()) + } + + return strings.Join(append([]string{r.filename}, ss...), "\n") +} + +func (r fileResult) Ok() bool { + if r.err != nil { return false } - return c.urlChecker.CheckMany(extractURLs(n)) + for _, r := range r.urlResults { + if r.err != nil { + return false + } + } + + return true } diff --git a/main.go b/main.go index bf26854..08f5d23 100644 --- a/main.go +++ b/main.go @@ -27,19 +27,29 @@ func main() { args := getArgs() fs := args[""].([]string) - bs := make(chan bool, len(fs)) + rc := make(chan fileResult, len(fs)) c := newFileChecker(5*time.Second, args["--verbose"].(bool)) for _, f := range fs { go func(f string) { - bs <- c.Check(f) + rs, err := c.Check(f) + + if err != nil { + rc <- fileResult{filename: f, err: err} + } + + rc <- fileResult{filename: f, urlResults: rs} }(f) } ok := true for i := 0; i < len(fs); i++ { - ok = <-bs && ok + r := <-rc + + ok = ok && r.Ok() + + printToStderr(r.String()) } if !ok { diff --git a/url_checker.go b/url_checker.go index d1c3794..168fed6 100644 --- a/url_checker.go +++ b/url_checker.go @@ -2,6 +2,7 @@ package main import ( "net/http" + "sync" "time" "github.com/fatih/color" @@ -16,32 +17,35 @@ func newURLChecker(timeout time.Duration, verbose bool) urlChecker { return urlChecker{http.Client{Timeout: timeout}, verbose} } -func (c urlChecker) Check(s string) bool { +func (c urlChecker) Check(s string) error { _, err := c.client.Get(s) - - if err != nil { - printToStderr(color.RedString("ERROR") + "\t" + s + "\t" + color.YellowString(err.Error())) - } else if err == nil && c.verbose { - printToStderr(color.GreenString("OK") + "\t" + s) - } - - return err == nil + return err } -func (c urlChecker) CheckMany(ss []string) bool { - bs := make(chan bool, len(ss)) +func (c urlChecker) CheckMany(ss []string, rc chan<- urlResult) { + wg := sync.WaitGroup{} for _, s := range ss { + wg.Add(1) go func(s string) { - bs <- c.Check(s) + rc <- urlResult{s, c.Check(s)} + wg.Done() }(s) } - ok := true + wg.Wait() + close(rc) +} + +type urlResult struct { + url string + err error +} - for i := 0; i < len(ss); i++ { - ok = <-bs && ok +func (r urlResult) String() string { + if r.err == nil { + return color.GreenString("OK") + "\t" + r.url } - return ok + return color.RedString("ERROR") + "\t" + r.url + "\t" + color.YellowString(r.err.Error()) }