mirror of
https://github.com/mgechev/revive.git
synced 2025-11-23 22:04:49 +02:00
feature: support cbor struct tag in struct-tag rule (#1527)
This commit is contained in:
@@ -1293,6 +1293,7 @@ The list of [supported tags](https://go.dev/wiki/Well-known-struct-tags):
|
||||
| ------------- | ------------------------------------------------------------------------ |
|
||||
| `asn1` | <https://pkg.go.dev/encoding/asn1> |
|
||||
| `bson` | <https://pkg.go.dev/go.mongodb.org/mongo-driver/bson> |
|
||||
| `cbor` | <https://pkg.go.dev/github.com/fxamacker/cbor/v2> |
|
||||
| `datastore` | <https://pkg.go.dev/cloud.google.com/go/datastore> |
|
||||
| `default` | The type of "default" must match the type of the field. |
|
||||
| `json` | <https://pkg.go.dev/encoding/json> |
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -23,6 +23,7 @@ func TestStructTagWithUserOptions(t *testing.T) {
|
||||
"toml,unknown",
|
||||
"spanner,mySpannerOption",
|
||||
"codec,myCodecOption",
|
||||
"cbor,myCborOption",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
21
testdata/struct_tag.go
vendored
21
testdata/struct_tag.go
vendored
@@ -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"`
|
||||
}
|
||||
|
||||
8
testdata/struct_tag_user_options.go
vendored
8
testdata/struct_tag_user_options.go
vendored
@@ -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"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user