github.com/please-build/puku@v1.7.3-0.20240516143641-f7d7f4941f57/glob/glob.go (about)

     1  package glob
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  )
     7  
     8  type pattern struct {
     9  	dir, glob string
    10  }
    11  
    12  type Globber struct {
    13  	cache map[pattern][]string
    14  }
    15  
    16  type Args struct {
    17  	Include, Exclude []string
    18  }
    19  
    20  func New() *Globber {
    21  	return &Globber{cache: map[pattern][]string{}}
    22  }
    23  
    24  // Glob is a specialised version of the glob builtin from Please. It assumes:
    25  // 1) globs should only match .go files as they're being used in go rules
    26  // 2) go rules will never depend on files outside the package dir, so we don't need to support **
    27  // 3) we don't want symlinks, directories and other non-regular files
    28  func (g *Globber) Glob(dir string, args *Args) ([]string, error) {
    29  	inc := map[string]struct{}{}
    30  	for _, i := range args.Include {
    31  		fs, err := g.glob(dir, i)
    32  		if err != nil {
    33  			return nil, err
    34  		}
    35  
    36  		for _, f := range fs {
    37  			inc[f] = struct{}{}
    38  		}
    39  	}
    40  
    41  	for _, i := range args.Exclude {
    42  		fs, err := g.glob(dir, i)
    43  		if err != nil {
    44  			return nil, err
    45  		}
    46  
    47  		for _, f := range fs {
    48  			delete(inc, f)
    49  		}
    50  	}
    51  
    52  	ret := make([]string, 0, len(inc))
    53  	for i := range inc {
    54  		ret = append(ret, i)
    55  	}
    56  	return ret, nil
    57  }
    58  
    59  // glob matches all regular files in a directory based on a glob pattern
    60  func (g *Globber) glob(dir, glob string) ([]string, error) {
    61  	p := pattern{dir: dir, glob: glob}
    62  	if res, ok := g.cache[p]; ok {
    63  		return res, nil
    64  	}
    65  
    66  	entries, err := os.ReadDir(dir)
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  
    71  	var files []string
    72  	for _, e := range entries {
    73  		// Ignore dirs, symlinks etc.
    74  		if !e.Type().IsRegular() {
    75  			continue
    76  		}
    77  
    78  		// We're globbing for Go files to determine their imports. We can skip any other files.
    79  		if filepath.Ext(e.Name()) != ".go" {
    80  			continue
    81  		}
    82  
    83  		match, err := filepath.Match(glob, e.Name())
    84  		if err != nil {
    85  			return nil, err
    86  		}
    87  
    88  		if match {
    89  			files = append(files, e.Name())
    90  		}
    91  	}
    92  
    93  	g.cache[p] = files
    94  	return files, nil
    95  }