From f07a47a1bf491f715e8d4674b2dc3e7d790a3ee7 Mon Sep 17 00:00:00 2001 From: "Sarthak S. Waghmare" Date: Sat, 16 Aug 2025 01:00:40 +0530 Subject: [PATCH] feature: support spanner struct tag in struct-tag rule (#1479) --- RULES_DESCRIPTIONS.md | 2 +- rule/struct_tag.go | 16 +++++++++++++++- test/struct_tag_test.go | 1 + testdata/struct_tag.go | 14 ++++++++++++++ testdata/struct_tag_user_options.go | 5 +++++ 5 files changed, 36 insertions(+), 2 deletions(-) diff --git a/RULES_DESCRIPTIONS.md b/RULES_DESCRIPTIONS.md index 2463f86..e916ca9 100644 --- a/RULES_DESCRIPTIONS.md +++ b/RULES_DESCRIPTIONS.md @@ -1104,7 +1104,7 @@ _Configuration_: N/A _Description_: Struct tags are not checked at compile time. This rule spots errors in struct tags of the following types: -asn1, bson, datastore, default, json, mapstructure, properties, protobuf, required, toml, url, validate, xml, yaml. +asn1, bson, datastore, default, json, mapstructure, properties, protobuf, required, spanner, toml, url, validate, xml, yaml. _Configuration_: (optional) list of user defined options. diff --git a/rule/struct_tag.go b/rule/struct_tag.go index a4cfec7..396ffbb 100644 --- a/rule/struct_tag.go +++ b/rule/struct_tag.go @@ -30,6 +30,7 @@ const ( keyProperties tagKey = "properties" keyProtobuf tagKey = "protobuf" keyRequired tagKey = "required" + keySpanner tagKey = "spanner" keyTOML tagKey = "toml" keyURL tagKey = "url" keyValidate tagKey = "validate" @@ -49,6 +50,7 @@ var tagCheckers = map[tagKey]tagChecker{ keyProperties: checkPropertiesTag, keyProtobuf: checkProtobufTag, keyRequired: checkRequiredTag, + keySpanner: checkSpannerTag, keyTOML: checkTOMLTag, keyURL: checkURLTag, keyValidate: checkValidateTag, @@ -198,7 +200,7 @@ func (w lintStructTagRule) checkTagNameIfNeed(checkCtx *checkContext, tag *struc key := tagKey(tag.Key) switch key { - case keyBSON, keyJSON, keyXML, keyYAML, keyProtobuf: + case keyBSON, keyJSON, keyXML, keyYAML, keyProtobuf, keySpanner: default: return "", true } @@ -558,6 +560,18 @@ func checkYAMLTag(checkCtx *checkContext, tag *structtag.Tag, _ ast.Expr) (messa return "", true } +func checkSpannerTag(checkCtx *checkContext, tag *structtag.Tag, _ ast.Expr) (message string, succeeded bool) { + if tag.Name == "-" { + return "", true + } + for _, opt := range tag.Options { + if !checkCtx.isUserDefined(keySpanner, opt) { + return fmt.Sprintf(msgUnknownOption, opt), false + } + } + return "", true +} + func checkValidateOptionsAlternatives(checkCtx *checkContext, alternatives []string) (message string, succeeded bool) { for _, alternative := range alternatives { alternative := strings.TrimSpace(alternative) diff --git a/test/struct_tag_test.go b/test/struct_tag_test.go index 389f0a8..40cc8a8 100644 --- a/test/struct_tag_test.go +++ b/test/struct_tag_test.go @@ -21,6 +21,7 @@ func TestStructTagWithUserOptions(t *testing.T) { "mapstructure,myMapstructureOption", "validate,displayName", "toml,unknown", + "spanner,mySpannerOption", }, }) } diff --git a/testdata/struct_tag.go b/testdata/struct_tag.go index e9d9933..2382008 100644 --- a/testdata/struct_tag.go +++ b/testdata/struct_tag.go @@ -88,6 +88,12 @@ type TestDuplicatedProtobufTags struct { C int `protobuf:"varint,name=c"` // MATCH /duplicated tag name "c" in protobuf tag/ } +type SpannerDuplicatedTags struct { + A int `spanner:"field_a"` + B int `spanner:"field_a"` // MATCH /duplicated tag name "field_a" in spanner tag/ + C int `spanner:"field_c"` +} + // test case from // sigs.k8s.io/kustomize/api/types/helmchartargs.go @@ -182,3 +188,11 @@ type PropertiesTags struct { Field []string `properties:",default=a;b;c"` Field map[string]string `properties:"myName,omitempty"` // MATCH /unknown or malformed option "omitempty" in properties tag/ } + +type SpannerUser struct { + ID int `spanner:"user_id"` + Name string `spanner:"full_name"` + Email string `spanner:"-"` // Valid: ignore field + CreatedAt time.Time `spanner:"created_at"` + UpdatedAt time.Time `spanner:"updated_at,unknown"` // MATCH /unknown option "unknown" in spanner tag/ +} \ No newline at end of file diff --git a/testdata/struct_tag_user_options.go b/testdata/struct_tag_user_options.go index f2a925b..7ec12e2 100644 --- a/testdata/struct_tag_user_options.go +++ b/testdata/struct_tag_user_options.go @@ -46,3 +46,8 @@ type TomlUser struct { Username string `toml:"username,omitempty"` Location string `toml:"location,unknown"` } + +type SpannerUserOptions struct { + ID int `spanner:"user_id,mySpannerOption"` + Name string `spanner:"full_name,unknownOption"` // MATCH /unknown option "unknownOption" in spanner tag/ +} \ No newline at end of file