github.com/yoheimuta/protolint@v0.49.8-0.20240515023657-4ecaebb7575d/internal/addon/rules/enumFieldNamesPrefixRule.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/protolint/linter/strs"
    12  
    13  	"github.com/yoheimuta/go-protoparser/v4/parser"
    14  	"github.com/yoheimuta/protolint/linter/report"
    15  	"github.com/yoheimuta/protolint/linter/visitor"
    16  )
    17  
    18  // EnumFieldNamesPrefixRule verifies that enum field names are prefixed with its ENUM_NAME_UPPER_SNAKE_CASE.
    19  // See https://developers.google.com/protocol-buffers/docs/style#enums.
    20  type EnumFieldNamesPrefixRule struct {
    21  	RuleWithSeverity
    22  	fixMode         bool
    23  	autoDisableType autodisable.PlacementType
    24  }
    25  
    26  // NewEnumFieldNamesPrefixRule creates a new EnumFieldNamesPrefixRule.
    27  func NewEnumFieldNamesPrefixRule(
    28  	severity rule.Severity,
    29  	fixMode bool,
    30  	autoDisableType autodisable.PlacementType,
    31  ) EnumFieldNamesPrefixRule {
    32  	if autoDisableType != autodisable.Noop {
    33  		fixMode = false
    34  	}
    35  	return EnumFieldNamesPrefixRule{
    36  		RuleWithSeverity: RuleWithSeverity{severity: severity},
    37  		fixMode:          fixMode,
    38  		autoDisableType:  autoDisableType,
    39  	}
    40  }
    41  
    42  // ID returns the ID of this rule.
    43  func (r EnumFieldNamesPrefixRule) ID() string {
    44  	return "ENUM_FIELD_NAMES_PREFIX"
    45  }
    46  
    47  // Purpose returns the purpose of this rule.
    48  func (r EnumFieldNamesPrefixRule) Purpose() string {
    49  	return `Verifies that enum field names are prefixed with its ENUM_NAME_UPPER_SNAKE_CASE.`
    50  }
    51  
    52  // IsOfficial decides whether or not this rule belongs to the official guide.
    53  func (r EnumFieldNamesPrefixRule) IsOfficial() bool {
    54  	return true
    55  }
    56  
    57  // Apply applies the rule to the proto.
    58  func (r EnumFieldNamesPrefixRule) Apply(proto *parser.Proto) ([]report.Failure, error) {
    59  	base, err := visitor.NewBaseFixableVisitor(r.ID(), r.fixMode, proto, string(r.Severity()))
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  
    64  	v := &enumFieldNamesPrefixVisitor{
    65  		BaseFixableVisitor: base,
    66  	}
    67  	return visitor.RunVisitorAutoDisable(v, proto, r.ID(), r.autoDisableType)
    68  }
    69  
    70  type enumFieldNamesPrefixVisitor struct {
    71  	*visitor.BaseFixableVisitor
    72  	enumName string
    73  }
    74  
    75  // VisitEnum checks the enum.
    76  func (v *enumFieldNamesPrefixVisitor) VisitEnum(enum *parser.Enum) bool {
    77  	v.enumName = enum.EnumName
    78  	return true
    79  }
    80  
    81  // VisitEnumField checks the enum field.
    82  func (v *enumFieldNamesPrefixVisitor) VisitEnumField(field *parser.EnumField) bool {
    83  	expectedPrefix := strs.ToUpperSnakeCase(v.enumName)
    84  	if !strings.HasPrefix(field.Ident, expectedPrefix) {
    85  		v.AddFailuref(field.Meta.Pos, "EnumField name %q should have the prefix %q", field.Ident, expectedPrefix)
    86  
    87  		expected := expectedPrefix + "_" + field.Ident
    88  		err := v.Fixer.SearchAndReplace(field.Meta.Pos, func(lex *lexer.Lexer) fixer.TextEdit {
    89  			lex.Next()
    90  			return fixer.TextEdit{
    91  				Pos:     lex.Pos.Offset,
    92  				End:     lex.Pos.Offset + len(lex.Text) - 1,
    93  				NewText: []byte(expected),
    94  			}
    95  		})
    96  		if err != nil {
    97  			panic(err)
    98  		}
    99  	}
   100  	return false
   101  }