github.com/posener/gitfs@v1.2.2-0.20200410105819-ea4e48d73ab9/internal/glob/glob.go (about)

     1  package glob
     2  
     3  import (
     4  	"path/filepath"
     5  	"strings"
     6  
     7  	"github.com/pkg/errors"
     8  )
     9  
    10  // Patterns can glob-match files or directories.
    11  type Patterns []string
    12  
    13  // New returns a new glob pattern. It returns an error if any of the
    14  // patterns is invalid.
    15  func New(patterns ...string) (Patterns, error) {
    16  	if err := checkPatterns(patterns); err != nil {
    17  		return nil, err
    18  	}
    19  	return Patterns(patterns), nil
    20  }
    21  
    22  // Match a path to the defined patterns. If it is a file a full match
    23  // is required. If it is a directory, only matching a prefix of any of
    24  // the patterns is required.
    25  func (p Patterns) Match(path string, isDir bool) bool {
    26  	if len(p) == 0 {
    27  		return true
    28  	}
    29  	path = filepath.Clean(path)
    30  	return (isDir && p.matchPrefix(path)) || (!isDir && p.matchFull(path))
    31  }
    32  
    33  // matchFull finds a matching of the whole name to any of the patterns.
    34  func (p Patterns) matchFull(name string) bool {
    35  	for _, pattern := range p {
    36  		if ok, _ := filepath.Match(pattern, name); ok {
    37  			return true
    38  		}
    39  	}
    40  	return false
    41  }
    42  
    43  // matchPrefix finds a matching of prefix to a prefix of any of the patterns.
    44  func (p Patterns) matchPrefix(prefix string) bool {
    45  	parts := strings.Split(prefix, string(filepath.Separator))
    46  nextPattern:
    47  	for _, pattern := range p {
    48  		patternParts := strings.Split(pattern, string(filepath.Separator))
    49  		if len(patternParts) < len(parts) {
    50  			continue
    51  		}
    52  		for i := 0; i < len(parts); i++ {
    53  			if ok, _ := filepath.Match(patternParts[i], parts[i]); !ok {
    54  				continue nextPattern
    55  			}
    56  		}
    57  		return true
    58  	}
    59  	return false
    60  }
    61  
    62  // checkPattens checks the validity of the patterns.
    63  func checkPatterns(patterns []string) error {
    64  	var badPatterns []string
    65  	for _, pattern := range patterns {
    66  		_, err := filepath.Match(pattern, "x")
    67  		if err != nil {
    68  			badPatterns = append(badPatterns, pattern)
    69  			return errors.Wrap(err, pattern)
    70  		}
    71  	}
    72  	if len(badPatterns) > 0 {
    73  		return errors.Wrap(filepath.ErrBadPattern, strings.Join(badPatterns, ", "))
    74  	}
    75  	return nil
    76  }