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

     1  package rules
     2  
     3  import (
     4  	"strings"
     5  
     6  	"github.com/yoheimuta/go-protoparser/v4/parser"
     7  	"github.com/yoheimuta/protolint/internal/linter/config"
     8  	"github.com/yoheimuta/protolint/linter/report"
     9  	"github.com/yoheimuta/protolint/linter/rule"
    10  	"github.com/yoheimuta/protolint/linter/visitor"
    11  )
    12  
    13  // QuoteConsistentRule verifies that the use of quote for strings is consistent.
    14  type QuoteConsistentRule struct {
    15  	RuleWithSeverity
    16  	quote config.QuoteType
    17  
    18  	fixMode bool
    19  }
    20  
    21  // NewQuoteConsistentRule creates a new QuoteConsistentRule.
    22  func NewQuoteConsistentRule(
    23  	severity rule.Severity,
    24  	quote config.QuoteType,
    25  	fixMode bool,
    26  ) QuoteConsistentRule {
    27  	return QuoteConsistentRule{
    28  		RuleWithSeverity: RuleWithSeverity{severity: severity},
    29  		quote:            quote,
    30  		fixMode:          fixMode,
    31  	}
    32  }
    33  
    34  // ID returns the ID of this rule.
    35  func (r QuoteConsistentRule) ID() string {
    36  	return "QUOTE_CONSISTENT"
    37  }
    38  
    39  // Purpose returns the purpose of this rule.
    40  func (r QuoteConsistentRule) Purpose() string {
    41  	return "Verifies that the use of quote for strings is consistent."
    42  }
    43  
    44  // IsOfficial decides whether or not this rule belongs to the official guide.
    45  func (r QuoteConsistentRule) IsOfficial() bool {
    46  	return true
    47  }
    48  
    49  // Apply applies the rule to the proto.
    50  func (r QuoteConsistentRule) Apply(proto *parser.Proto) ([]report.Failure, error) {
    51  	base, err := visitor.NewBaseFixableVisitor(r.ID(), r.fixMode, proto, string(r.Severity()))
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  
    56  	v := &quoteConsistentVisitor{
    57  		BaseFixableVisitor: base,
    58  		quote:              r.quote,
    59  	}
    60  	return visitor.RunVisitor(v, proto, r.ID())
    61  }
    62  
    63  type quoteConsistentVisitor struct {
    64  	*visitor.BaseFixableVisitor
    65  	quote config.QuoteType
    66  }
    67  
    68  func (v quoteConsistentVisitor) VisitSyntax(s *parser.Syntax) bool {
    69  	str := s.ProtobufVersionQuote
    70  	converted := convertConsistentQuote(str, v.quote)
    71  	if str != converted {
    72  		v.AddFailuref(s.Meta.Pos, "Quoted string should be %s but was %s.", converted, str)
    73  		v.Fixer.ReplaceText(s.Meta.Pos.Line, str, converted)
    74  	}
    75  	return false
    76  }
    77  
    78  func (v quoteConsistentVisitor) VisitImport(i *parser.Import) (next bool) {
    79  	str := i.Location
    80  	converted := convertConsistentQuote(str, v.quote)
    81  	if str != converted {
    82  		v.AddFailuref(i.Meta.Pos, "Quoted string should be %s but was %s.", converted, str)
    83  		v.Fixer.ReplaceText(i.Meta.Pos.Line, str, converted)
    84  	}
    85  	return false
    86  }
    87  
    88  func (v quoteConsistentVisitor) VisitOption(o *parser.Option) (next bool) {
    89  	str := o.Constant
    90  	converted := convertConsistentQuote(str, v.quote)
    91  	if str != converted {
    92  		v.AddFailuref(o.Meta.Pos, "Quoted string should be %s but was %s.", converted, str)
    93  		v.Fixer.ReplaceText(o.Meta.Pos.Line, str, converted)
    94  	}
    95  	return false
    96  }
    97  
    98  func (v quoteConsistentVisitor) VisitEnumField(f *parser.EnumField) (next bool) {
    99  	for _, option := range f.EnumValueOptions {
   100  		str := option.Constant
   101  		converted := convertConsistentQuote(str, v.quote)
   102  		if str != converted {
   103  			v.AddFailuref(f.Meta.Pos, "Quoted string should be %s but was %s.", converted, str)
   104  			v.Fixer.ReplaceText(f.Meta.Pos.Line, str, converted)
   105  		}
   106  	}
   107  	return false
   108  }
   109  
   110  func (v quoteConsistentVisitor) VisitField(f *parser.Field) (next bool) {
   111  	for _, option := range f.FieldOptions {
   112  		str := option.Constant
   113  		converted := convertConsistentQuote(str, v.quote)
   114  		if str != converted {
   115  			v.AddFailuref(f.Meta.Pos, "Quoted string should be %s but was %s.", converted, str)
   116  			v.Fixer.ReplaceText(f.Meta.Pos.Line, str, converted)
   117  		}
   118  	}
   119  	return false
   120  }
   121  
   122  func convertConsistentQuote(s string, quote config.QuoteType) string {
   123  	var valid, invalid string
   124  	if quote == config.DoubleQuote {
   125  		valid, invalid = "\"", "'"
   126  	} else {
   127  		valid, invalid = "'", "\""
   128  	}
   129  
   130  	if strings.HasPrefix(s, invalid) && strings.HasSuffix(s, invalid) {
   131  		return valid + s[1:len(s)-1] + valid
   132  	}
   133  	return s
   134  }