github.com/blend/go-sdk@v1.20220411.3/profanity/contents.go (about) 1 /* 2 3 Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file. 5 6 */ 7 8 package profanity 9 10 import ( 11 "bufio" 12 "bytes" 13 "fmt" 14 "strings" 15 16 "github.com/blend/go-sdk/ex" 17 "github.com/blend/go-sdk/validate" 18 ) 19 20 var ( 21 _ Rule = (*Contents)(nil) 22 ) 23 24 // Errors 25 const ( 26 ErrContentsRequired ex.Class = "contents rule spec must provide `contains`, `glob` or `regex` values" 27 ) 28 29 // Contents creates a new contents rule. 30 // It failes if any of the expressions match. 31 type Contents struct { 32 // Contains is a filter set that uses `strings.Contains` as the predicate. 33 Contains *ContainsFilter `yaml:"contains,omitempty"` 34 // Glob is a filter set that uses `Glob` as the predicate. 35 Glob *GlobFilter `yaml:"glob,omitempty"` 36 // Regex is a filter set that uses `regexp.MustMatch` as the predicate 37 Regex *RegexFilter `yaml:"regex,omitempty"` 38 } 39 40 // Validate returns validators. 41 func (cm Contents) Validate() error { 42 if cm.Contains == nil && cm.Glob == nil && cm.Regex == nil { 43 return validate.Error(ErrContentsRequired, nil) 44 } 45 var hasInclude bool 46 hasInclude = hasInclude || (cm.Contains != nil && len(cm.Contains.Include) > 0) 47 hasInclude = hasInclude || (cm.Glob != nil && len(cm.Glob.Include) > 0) 48 hasInclude = hasInclude || (cm.Regex != nil && len(cm.Regex.Include) > 0) 49 if !hasInclude { 50 return validate.Error(ErrContentsRequired, nil) 51 } 52 return nil 53 } 54 55 // Check implements Rule. 56 func (cm Contents) Check(filename string, contents []byte) (result RuleResult) { 57 scanner := bufio.NewScanner(bytes.NewReader(contents)) 58 59 var notOK bool 60 var line int 61 var lineText string 62 var containsInclude, containsExclude string 63 var globInclude, globExclude string 64 var regexInclude, regexExclude string 65 var tokens []string 66 67 for scanner.Scan() { 68 line++ 69 lineText = scanner.Text() 70 71 if cm.Contains != nil { 72 containsInclude, containsExclude = cm.Contains.Match(lineText) 73 if cm.Contains.AllowMatch(containsInclude, containsExclude) { 74 if containsInclude != "" { 75 tokens = append(tokens, fmt.Sprintf("contents contains include: %q", containsInclude)) 76 } 77 if containsExclude != "" { 78 tokens = append(tokens, fmt.Sprintf("contents contains exclude: %q", containsExclude)) 79 } 80 notOK = true 81 } 82 } 83 if cm.Glob != nil { 84 globInclude, globExclude = cm.Glob.Match(lineText) 85 if cm.Glob.AllowMatch(globInclude, globExclude) { 86 if globInclude != "" { 87 tokens = append(tokens, fmt.Sprintf("contents glob include: %q", globInclude)) 88 } 89 if globExclude != "" { 90 tokens = append(tokens, fmt.Sprintf("contents glob exclude: %q", globExclude)) 91 } 92 notOK = true 93 } 94 } 95 if cm.Regex != nil { 96 regexInclude, regexExclude = cm.Regex.Match(lineText) 97 if cm.Regex.AllowMatch(regexInclude, regexExclude) { 98 if regexInclude != "" { 99 tokens = append(tokens, fmt.Sprintf("contents regex include: %q", regexInclude)) 100 } 101 if regexExclude != "" { 102 tokens = append(tokens, fmt.Sprintf("contents regex exclude: %q", regexExclude)) 103 } 104 notOK = true 105 } 106 } 107 if notOK { 108 result = RuleResult{ 109 File: filename, 110 Line: line, 111 Message: strings.Join(tokens, ", "), 112 } 113 return 114 } 115 } 116 117 return RuleResult{OK: true} 118 } 119 120 // String implements fmt.Stringer. 121 func (cm Contents) String() string { 122 var tokens []string 123 if len(cm.Contains.Filter.Include) > 0 { 124 tokens = append(tokens, fmt.Sprintf("contain: %s", cm.Contains.String())) 125 } 126 if len(cm.Glob.Filter.Include) > 0 { 127 tokens = append(tokens, fmt.Sprintf("glob: %s", cm.Glob.String())) 128 } 129 if len(cm.Regex.Filter.Include) > 0 { 130 tokens = append(tokens, fmt.Sprintf("regex: %s", cm.Glob.String())) 131 } 132 return fmt.Sprintf("[contents %s]", strings.Join(tokens, ",")) 133 }