github.com/oalders/ppath@v0.1.1/audit/audit.go (about)

     1  // Package audit provides config parsing and path checking logic for ppath.
     2  package audit
     3  
     4  import (
     5  	"log"
     6  	"os"
     7  
     8  	"github.com/mattn/go-zglob"
     9  	"github.com/pkg/errors"
    10  )
    11  
    12  // Command is a single command section in the config file.
    13  type Command struct {
    14  	Exclude interface{}
    15  	Include interface{}
    16  }
    17  
    18  // Precious is an entire config file.
    19  type Precious struct {
    20  	Commands map[string]Command
    21  }
    22  
    23  type matchCache map[string]bool
    24  
    25  // Paths audits the paths contained in a Precious struct and returns false if
    26  // any of them cannot be found.
    27  func Paths(config *Precious) (bool, error) {
    28  	success := true
    29  	ignoreConfig, err := PpathConfig(".ppath.toml")
    30  	if err != nil {
    31  		return false, err
    32  	}
    33  
    34  	seen := make(matchCache)
    35  	for commandName, command := range config.Commands {
    36  		lists := map[string][]string{
    37  			"exclude": patternList(command.Exclude),
    38  			"include": patternList(command.Include),
    39  		}
    40  		for section, list := range lists {
    41  			if len(list) > 0 {
    42  				ok, err := patternsOk(seen, ignoreConfig, commandName, section, list)
    43  				if err != nil {
    44  					return false, err
    45  				}
    46  				if !ok {
    47  					success = false
    48  				}
    49  			}
    50  		}
    51  	}
    52  
    53  	return success, nil
    54  }
    55  
    56  func patternsOk(seen matchCache, ppath *Ppath, commandName, section string, patterns []string) (bool, error) {
    57  	success := true
    58  	for _, pattern := range patterns {
    59  		matched, exists := seen[pattern]
    60  
    61  		if exists && matched {
    62  			continue
    63  		}
    64  
    65  		// For our purposes found and ignored are the same thing.
    66  		if patternIgnored(ppath, commandName, pattern) {
    67  			seen[pattern] = true
    68  			continue
    69  		}
    70  
    71  		if !exists {
    72  			matches, err := zglob.Glob(pattern)
    73  
    74  			if err != nil {
    75  				if !errors.Is(err, os.ErrNotExist) {
    76  					return false, errors.Wrapf(
    77  						err,
    78  						"matching %s pattern '%s'",
    79  						section,
    80  						pattern,
    81  					)
    82  				}
    83  			} else if len(matches) > 0 {
    84  				seen[pattern] = true
    85  				continue
    86  			}
    87  		}
    88  
    89  		seen[pattern] = false
    90  		success = false
    91  		log.Printf("%s %s pattern %s was not found", commandName, section, pattern)
    92  	}
    93  
    94  	return success, nil
    95  }
    96  
    97  func patternList(patternGroup interface{}) []string {
    98  	var patterns []string
    99  	if maybeList, ok := patternGroup.([]interface{}); ok {
   100  		for _, pattern := range maybeList {
   101  			str, _ := pattern.(string)
   102  			patterns = append(patterns, str)
   103  		}
   104  	} else if str, ok := patternGroup.(string); ok {
   105  		patterns = append(patterns, str)
   106  	}
   107  
   108  	return patterns
   109  }
   110  
   111  func patternIgnored(config *Ppath, commandName, pattern string) bool {
   112  	for _, v := range config.Ignore {
   113  		if pattern == v {
   114  			return true
   115  		}
   116  	}
   117  
   118  	for name, command := range config.Commands {
   119  		if name != commandName {
   120  			continue
   121  		}
   122  		for _, v := range command.Ignore {
   123  			if pattern == v {
   124  				return true
   125  			}
   126  		}
   127  	}
   128  
   129  	return false
   130  }