github.com/yoheimuta/protolint@v0.49.8-0.20240515023657-4ecaebb7575d/internal/addon/rules/enumFieldNamesZeroValueEndWithRule.go (about)

     1  package rules
     2  
     3  import (
     4  	"strings"
     5  
     6  	"github.com/yoheimuta/go-protoparser/v4/lexer"
     7  	"github.com/yoheimuta/protolint/linter/autodisable"
     8  	"github.com/yoheimuta/protolint/linter/fixer"
     9  	"github.com/yoheimuta/protolint/linter/rule"
    10  
    11  	"github.com/yoheimuta/go-protoparser/v4/parser"
    12  
    13  	"github.com/yoheimuta/protolint/linter/report"
    14  	"github.com/yoheimuta/protolint/linter/visitor"
    15  )
    16  
    17  const (
    18  	defaultSuffix = "UNSPECIFIED"
    19  )
    20  
    21  // EnumFieldNamesZeroValueEndWithRule verifies that the zero value enum should have the suffix (e.g. "UNSPECIFIED", "INVALID").
    22  // See https://developers.google.com/protocol-buffers/docs/style#enums.
    23  type EnumFieldNamesZeroValueEndWithRule struct {
    24  	RuleWithSeverity
    25  	suffix          string
    26  	fixMode         bool
    27  	autoDisableType autodisable.PlacementType
    28  }
    29  
    30  // NewEnumFieldNamesZeroValueEndWithRule creates a new EnumFieldNamesZeroValueEndWithRule.
    31  func NewEnumFieldNamesZeroValueEndWithRule(
    32  	severity rule.Severity,
    33  	suffix string,
    34  	fixMode bool,
    35  	autoDisableType autodisable.PlacementType,
    36  ) EnumFieldNamesZeroValueEndWithRule {
    37  	if len(suffix) == 0 {
    38  		suffix = defaultSuffix
    39  	}
    40  	if autoDisableType != autodisable.Noop {
    41  		fixMode = false
    42  	}
    43  	return EnumFieldNamesZeroValueEndWithRule{
    44  		RuleWithSeverity: RuleWithSeverity{severity: severity},
    45  		suffix:           suffix,
    46  		fixMode:          fixMode,
    47  		autoDisableType:  autoDisableType,
    48  	}
    49  }
    50  
    51  // ID returns the ID of this rule.
    52  func (r EnumFieldNamesZeroValueEndWithRule) ID() string {
    53  	return "ENUM_FIELD_NAMES_ZERO_VALUE_END_WITH"
    54  }
    55  
    56  // Purpose returns the purpose of this rule.
    57  func (r EnumFieldNamesZeroValueEndWithRule) Purpose() string {
    58  	return `Verifies that the zero value enum should have the suffix (e.g. "UNSPECIFIED", "INVALID").`
    59  }
    60  
    61  // IsOfficial decides whether or not this rule belongs to the official guide.
    62  func (r EnumFieldNamesZeroValueEndWithRule) IsOfficial() bool {
    63  	return true
    64  }
    65  
    66  // Apply applies the rule to the proto.
    67  func (r EnumFieldNamesZeroValueEndWithRule) Apply(proto *parser.Proto) ([]report.Failure, error) {
    68  	base, err := visitor.NewBaseFixableVisitor(r.ID(), r.fixMode, proto, string(r.Severity()))
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	v := &enumFieldNamesZeroValueEndWithVisitor{
    74  		BaseFixableVisitor: base,
    75  		suffix:             r.suffix,
    76  	}
    77  	return visitor.RunVisitorAutoDisable(v, proto, r.ID(), r.autoDisableType)
    78  }
    79  
    80  type enumFieldNamesZeroValueEndWithVisitor struct {
    81  	*visitor.BaseFixableVisitor
    82  	suffix string
    83  }
    84  
    85  // VisitEnumField checks the enum field.
    86  func (v *enumFieldNamesZeroValueEndWithVisitor) VisitEnumField(field *parser.EnumField) bool {
    87  	if field.Number == "0" && !strings.HasSuffix(field.Ident, v.suffix) {
    88  		v.AddFailuref(field.Meta.Pos, "EnumField name %q with zero value should have the suffix %q", field.Ident, v.suffix)
    89  
    90  		expected := field.Ident + "_" + v.suffix
    91  		err := v.Fixer.SearchAndReplace(field.Meta.Pos, func(lex *lexer.Lexer) fixer.TextEdit {
    92  			lex.Next()
    93  			return fixer.TextEdit{
    94  				Pos:     lex.Pos.Offset,
    95  				End:     lex.Pos.Offset + len(lex.Text) - 1,
    96  				NewText: []byte(expected),
    97  			}
    98  		})
    99  		if err != nil {
   100  			panic(err)
   101  		}
   102  	}
   103  	return false
   104  }