1
0
mirror of https://github.com/mgechev/revive.git synced 2025-10-30 23:37:49 +02:00

feature: support cbor struct tag in struc-tag rule

This commit is contained in:
chavacava
2025-09-20 18:54:36 +02:00
parent 8be7fa65f0
commit 2a21ae5c1c
4 changed files with 92 additions and 0 deletions

View File

@@ -24,6 +24,7 @@ type tagKey string
const (
keyASN1 tagKey = "asn1"
keyBSON tagKey = "bson"
keyCbor tagKey = "cbor"
keyCodec tagKey = "codec"
keyDatastore tagKey = "datastore"
keyDefault tagKey = "default"
@@ -45,6 +46,7 @@ type tagChecker func(checkCtx *checkContext, tag *structtag.Tag, field *ast.Fiel
var tagCheckers = map[tagKey]tagChecker{
keyASN1: checkASN1Tag,
keyBSON: checkBSONTag,
keyCbor: checkCborTag,
keyCodec: checkCodecTag,
keyDatastore: checkDatastoreTag,
keyDefault: checkDefaultTag,
@@ -385,6 +387,66 @@ func checkBSONTag(checkCtx *checkContext, tag *structtag.Tag, _ *ast.Field) (mes
return "", true
}
func checkCborTag(checkCtx *checkContext, tag *structtag.Tag, _ *ast.Field) (message string, succeeded bool) {
hasToArray := false
hasOmitEmptyOrZero := false
hasKeyAsInt := false
for _, opt := range tag.Options {
switch opt {
case "omitempty", "omitzero":
hasOmitEmptyOrZero = true
case "toarray":
if tag.Name != "" {
return `tag name for option "toarray" should be empty`, false
}
hasToArray = true
case "keyasint":
intKey, err := strconv.Atoi(tag.Name)
if err != nil {
return `tag name for option "keyasint" should be an integer`, false
}
_, ok := checkCtx.usedTagNbr[intKey]
if ok {
return fmt.Sprintf("duplicated integer key %d", intKey), false
}
checkCtx.usedTagNbr[intKey] = true
hasKeyAsInt = true
continue
default:
if !checkCtx.isUserDefined(keyCbor, opt) {
return fmt.Sprintf(msgUnknownOption, opt), false
}
}
}
// Check for duplicated tag names
if tag.Name != "" {
_, ok := checkCtx.usedTagName[tag.Name]
if ok {
return fmt.Sprintf("duplicated tag name %s", tag.Name), false
}
checkCtx.usedTagName[tag.Name] = true
}
// Check for integer tag names without keyasint option
if !hasKeyAsInt {
_, err := strconv.Atoi(tag.Name)
if err == nil {
return `integer tag names are only allowed in presence of "keyasint" option`, false
}
}
if hasToArray && hasOmitEmptyOrZero {
return `options "omitempty" and "omitzero" are ignored in presence of "toarray" option`, false
}
return "", true
}
const structTagCodecSpecialField = "_struct"
func checkCodecTag(checkCtx *checkContext, tag *structtag.Tag, field *ast.Field) (message string, succeeded bool) {

View File

@@ -23,6 +23,7 @@ func TestStructTagWithUserOptions(t *testing.T) {
"toml,unknown",
"spanner,mySpannerOption",
"codec,myCodecOption",
"cbor,myCborOption",
},
})
}

View File

@@ -215,3 +215,24 @@ type TestDuplicatedCodecTags struct {
B int `codec:"field_a"` // MATCH /duplicated tag name "field_a" in codec tag/
C int `codec:"field_c"`
}
type Cbor struct {
RepeatedStr string `cbor:"errors"`
Repeated string `cbor:"1,keyasint"`
Useless string `cbor:"-,omitempty"` // MATCH /useless option omitempty for ignored field in cbor tag/
Inputs string `cbor:",keyasint"` // MATCH /tag name for option "keyasint" should be an integer in cbor tag/
Outputs string `cbor:"inputs,keyasint"` // MATCH /tag name for option "keyasint" should be an integer in cbor tag/
Errors string `cbor:"errors,optempty,keyasint"` // MATCH /unknown option "optempty" in cbor tag/
Inputs2 string `cbor:"-10,omitempty"` // MATCH /integer tag names are only allowed in presence of "keyasint" option in cbor tag/
Outputs2 string `cbor:"-12,toarray"` // MATCH /tag name for option "toarray" should be empty in cbor tag/
RepeatedInt string `cbor:"1,keyasint"` // MATCH /duplicated integer key 1 in cbor tag/
RepeatedStr string `cbor:"errors,omitempty"` // MATCH /duplicated tag name errors in cbor tag/
Useless string `cbor:",toarray,omitempty"` // MATCH /options "omitempty" and "omitzero" are ignored in presence of "toarray" option in cbor tag/
Useless2 string `cbor:",omitzero,toarray"` // MATCH /options "omitempty" and "omitzero" are ignored in presence of "toarray" option in cbor tag/
// OK
InputsOk string `cbor:"8,keyasint"`
OutputsOk string `cbor:"-100,keyasint"`
ErrorsOk string `cbor:"-1,keyasint"`
InputsOk2 string `cbor:"inputs,omitempty"`
OutputsOk2 string `cbor:",toarray"`
}

View File

@@ -102,3 +102,11 @@ type CodecUserOptions struct {
ID int `codec:"user_id,myCodecOption"`
Name string `codec:"full_name,unknownOption"` // MATCH /unknown option "unknownOption" in codec tag/
}
type CborUserOptions struct {
InputsOk string `cbor:"8,keyasint,myCborOption"`
OutputsOk string `cbor:"-100,keyasint,unknownOption"` // MATCH /unknown option "unknownOption" in cbor tag/
ErrorsOk string `cbor:"-1,keyasint"`
InputsOk2 string `cbor:"inputs,omitempty"`
OutputsOk2 string `cbor:",toarray"`
}