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 }