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  }