Main interface and usage

The signature of the package will include a context, the root folder, the search term, and a couple of optional parameters:

The function would look something like this:

type Options struct {
Contents bool
Exclude []string
}

func FileSearch(ctx context.Context, root, term string, o *Options)

Since it should be a concurrent function, the return type could be a channel of result, which could be either an error or a series of matches in a file. Since we can search for the names of content, the latter could have more than one match:

type Result struct {
Err error
File string
Matches []Match
}

type Match struct {
Line int
Text string
}

The previous function will return a receive-only channel of the Result type:

func FileSearch(ctx context.Context, root, term string, o *Options) <-chan Result

Here, this function would keep receiving values from the channel until it gets closed:

for r := range FileSearch(ctx, directory, searchTerm, options) {
if r.Err != nil {
fmt.Printf("%s - error: %s\n", r.File, r.Err)
continue
}
if !options.Contents {
fmt.Printf("%s - match\n", r.File)
continue
}
fmt.Printf("%s - matches:\n", r.File)
for _, m := range r.Matches {
fmt.Printf("\t%d:%s\n", m.Line, m.Text)
}
}