func (g *Graph) Walk(fn func(Target) error) error {
	errors := newWalkError()
	err := g.dag.Walk(func(v dag.Vertex) error {
		target := g.Target(v.(string))
		if _, ok := target.(*rootTarget); ok {
			return nil
		}
		err := fn(target)
		errors.Add(target, err)
		return err
	})
	if err == dag.ErrWalk {
		return errors
	}
	return err
}
Walk wraps the underlying Walk function to coerce it to a Target first.
package main
import (
	"context"
	"fmt"
	"io"
	"sync"
	"github.com/ejholmes/walk/internal/dag"
)
type WalkError struct {
	mu     sync.Mutex
	Errors map[string]error
}
func newWalkError() *WalkError {
	return &WalkError{Errors: make(map[string]error)}
}
func (e *WalkError) Error() string {
	return fmt.Sprintf("%d %s failed", len(e.Errors), pluralize(len(e.Errors), "target", "targets"))
}
func (e *WalkError) Add(t Target, err error) {
	if err == nil {
		return
	}
	e.mu.Lock()
	defer e.mu.Unlock()
	e.Errors[t.Name()] = err
}
type Graph struct {
	mu  sync.Mutex
	m   map[string]Target
	dag *dag.AcyclicGraph
}
func newGraph() *Graph {
	return &Graph{
		m:   make(map[string]Target),
		dag: new(dag.AcyclicGraph),
	}
}
func (g *Graph) Add(target Target) Target {
	g.mu.Lock()
	defer g.mu.Unlock()
	if t := g.target(target.Name()); t != nil {
		return t
	}
	g.m[target.Name()] = target
	g.dag.Add(target.Name())
	return nil
}
func (g *Graph) Connect(target, dependency Target) {
	g.dag.Connect(dag.BasicEdge(target.Name(), dependency.Name()))
}
func (g *Graph) Target(name string) Target {
	g.mu.Lock()
	target := g.target(name)
	g.mu.Unlock()
	return target
}