Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: allow space in go tag value #2628

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 63 additions & 3 deletions plugin/modelgen/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -450,17 +450,71 @@ func GoTagFieldHook(td *ast.Definition, fd *ast.FieldDefinition, f *Field) (*Fie
return f, nil
}

// splitTagsBySpace split tags by space, except when space is inside quotes
func splitTagsBySpace(tagsString string) []string {
var tags []string
var currentTag string
inQuotes := false

for _, c := range tagsString {
if c == '"' {
inQuotes = !inQuotes
}
if c == ' ' && !inQuotes {
tags = append(tags, currentTag)
currentTag = ""
} else {
currentTag += string(c)
}
}
tags = append(tags, currentTag)

return tags
}

// containsInvalidSpace checks if the tagsString contains invalid space
func containsInvalidSpace(valuesString string) bool {
// get rid of quotes
valuesString = strings.ReplaceAll(valuesString, "\"", "")
if strings.Contains(valuesString, ",") {
// split by comma,
values := strings.Split(valuesString, ",")
for _, value := range values {
if strings.TrimSpace(value) != value {
return true
}
}
return false
}
if strings.Contains(valuesString, ";") {
// split by semicolon, which is common in gorm
values := strings.Split(valuesString, ";")
for _, value := range values {
if strings.TrimSpace(value) != value {
return true
}
}
return false
}
// single value
if strings.TrimSpace(valuesString) != valuesString {
return true
}
return false
}

func removeDuplicateTags(t string) string {
processed := make(map[string]bool)
tt := strings.Split(t, " ")
tt := splitTagsBySpace(t)
returnTags := ""

// iterate backwards through tags so appended goTag directives are prioritized
for i := len(tt) - 1; i >= 0; i-- {
ti := tt[i]
// check if ti contains ":", and not contains any empty space. if not, tag is in wrong format
if !strings.Contains(ti, ":") || strings.Contains(ti, " ") {
panic(fmt.Errorf("wrong format of tags: %s. goTag directive should be in format: @goTag(key: \"something\", value:\"value1,value2,etc\"), no empty space is allowed", t))
// correct example: json:"name"
if !strings.Contains(ti, ":") {
panic(fmt.Errorf("wrong format of tags: %s. goTag directive should be in format: @goTag(key: \"something\", value:\"value\"), ", t))
}

kv := strings.Split(ti, ":")
Expand All @@ -472,6 +526,12 @@ func removeDuplicateTags(t string) string {
if len(returnTags) > 0 {
returnTags = " " + returnTags
}

isContained := containsInvalidSpace(kv[1])
if isContained {
panic(fmt.Errorf("tag value should not contain any leading or trailing spaces: %s", kv[1]))
}

returnTags = kv[0] + ":" + kv[1] + returnTags
}

Expand Down
116 changes: 115 additions & 1 deletion plugin/modelgen/models_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -428,13 +428,37 @@ func TestRemoveDuplicate(t *testing.T) {
want: "something:\"name2\" json:\"name3\"",
},
{
name: "Test value with empty space",
name: "Test tag value with leading empty space",
args: args{
t: "json:\"name, name2\"",
},
want: "json:\"name, name2\"",
wantPanic: true,
},
{
name: "Test tag value with trailing empty space",
args: args{
t: "json:\"name,name2 \"",
},
want: "json:\"name,name2 \"",
wantPanic: true,
},
{
name: "Test tag value with space in between",
args: args{
t: "gorm:\"unique;not null\"",
},
want: "gorm:\"unique;not null\"",
wantPanic: false,
},
{
name: "Test mix use of gorm and json tags",
args: args{
t: "gorm:\"unique;not null\" json:\"name,name2\"",
},
want: "gorm:\"unique;not null\" json:\"name,name2\"",
wantPanic: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand All @@ -448,3 +472,93 @@ func TestRemoveDuplicate(t *testing.T) {
})
}
}

func Test_containsInvalidSpace(t *testing.T) {
type args struct {
valuesString string
}
tests := []struct {
name string
args args
want bool
}{
{
name: "Test tag value with leading empty space",
args: args{
valuesString: "name, name2",
},
want: true,
},
{
name: "Test tag value with trailing empty space",
args: args{
valuesString: "name ,name2",
},
want: true,
},
{
name: "Test tag value with valid empty space in words",
args: args{
valuesString: "accept this,name2",
},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, containsInvalidSpace(tt.args.valuesString), "containsInvalidSpace(%v)", tt.args.valuesString)
})
}
}

func Test_splitTagsBySpace(t *testing.T) {
type args struct {
tagsString string
}
tests := []struct {
name string
args args
want []string
}{
{
name: "multiple tags, single value",
args: args{
tagsString: "json:\"name\" something:\"name2\" json:\"name3\"",
},
want: []string{"json:\"name\"", "something:\"name2\"", "json:\"name3\""},
},
{
name: "multiple tag, multiple values",
args: args{
tagsString: "json:\"name\" something:\"name2\" json:\"name3,name4\"",
},
want: []string{"json:\"name\"", "something:\"name2\"", "json:\"name3,name4\""},
},
{
name: "single tag, single value",
args: args{
tagsString: "json:\"name\"",
},
want: []string{"json:\"name\""},
},
{
name: "single tag, multiple values",
args: args{
tagsString: "json:\"name,name2\"",
},
want: []string{"json:\"name,name2\""},
},
{
name: "space in value",
args: args{
tagsString: "gorm:\"not nul,name2\"",
},
want: []string{"gorm:\"not nul,name2\""},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, splitTagsBySpace(tt.args.tagsString), "splitTagsBySpace(%v)", tt.args.tagsString)
})
}
}