Skip to content

Commit da49981

Browse files
committed
feat: description_validator now correctly parses stringvalidator.OneOf arguments with commas and appends possible values to string attribute descriptions.
1 parent d66a807 commit da49981

File tree

3 files changed

+72
-23
lines changed

3 files changed

+72
-23
lines changed

internal/convert/description_validator.go

Lines changed: 46 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,18 @@ import (
99
// oneOfRegex matches "stringvalidator.OneOf(...)" and captures the content inside.
1010
var oneOfRegex = regexp.MustCompile(`stringvalidator\.OneOf\(([\s\S]*?)\)`)
1111

12-
// AppendValidators appends possible values from validators to the description.
12+
// stringLiteralRegex matches a Go string literal (double-quoted).
13+
// It handles escaped quotes.
14+
var stringLiteralRegex = regexp.MustCompile(`"((?:[^"\\]|\\.)*)"`)
15+
16+
// AppendValidators parses stringvalidator.OneOf validators from the provided Validators object
17+
// and appends the possible values to the Description.
18+
//
19+
// It currently supports:
20+
// - stringvalidator.OneOf
21+
//
22+
// Arguments inside OneOf are expected to be string literals.
23+
// Commas inside quotes are preserved.
1324
func (d *Description) AppendValidators(v Validators) {
1425
if d.description == nil {
1526
empty := ""
@@ -22,31 +33,43 @@ func (d *Description) AppendValidators(v Validators) {
2233
}
2334

2435
// Check for stringvalidator.OneOf
25-
matches := oneOfRegex.FindStringSubmatch(custom.SchemaDefinition)
26-
if len(matches) > 1 {
27-
// matches[1] contains the arguments inside OneOf(...)
28-
// e.g. " \"a\", \"b\", " or "\n\"a\",\n\"b\",\n"
29-
30-
// Simple extraction: verify it looks like a list of strings
31-
args := matches[1]
32-
33-
// Split by comma
34-
parts := strings.Split(args, ",")
35-
var values []string
36-
for _, p := range parts {
37-
p = strings.TrimSpace(p)
38-
p = strings.Trim(p, "\"") // remove quotes
39-
if p != "" {
40-
values = append(values, fmt.Sprintf("`%s`", p))
36+
// Note: This regex might match multiple times if there are multiple OneOf calls,
37+
// though typically there's only one per validator set.
38+
allMatches := oneOfRegex.FindAllStringSubmatch(custom.SchemaDefinition, -1)
39+
40+
for _, matches := range allMatches {
41+
if len(matches) > 1 {
42+
// matches[1] contains the arguments inside OneOf(...)
43+
args := matches[1]
44+
45+
// Parse string literals from the arguments
46+
// This avoids issues with commas inside quotes
47+
literalMatches := stringLiteralRegex.FindAllStringSubmatch(args, -1)
48+
49+
var values []string
50+
for _, lm := range literalMatches {
51+
if len(lm) > 1 {
52+
// lm[1] is the content inside the quotes
53+
// We re-wrap it in backticks for markdown display, escaping backticks if needed
54+
val := lm[1]
55+
val = strings.ReplaceAll(val, "`", "` + \"`\" + `") // unlikely but safe
56+
values = append(values, fmt.Sprintf("`%s`", val))
57+
}
4158
}
42-
}
4359

44-
if len(values) > 0 {
45-
suffix := fmt.Sprintf("Possible values: %s", strings.Join(values, ", "))
46-
if *d.description != "" {
47-
*d.description += "\n"
60+
if len(values) > 0 {
61+
suffix := fmt.Sprintf("Possible values: %s", strings.Join(values, ", "))
62+
63+
// Avoid appending if already present (simple duplicate check)
64+
if strings.Contains(*d.description, suffix) {
65+
continue
66+
}
67+
68+
if *d.description != "" {
69+
*d.description += "\n"
70+
}
71+
*d.description += suffix
4872
}
49-
*d.description += suffix
5073
}
5174
}
5275
}

internal/convert/description_validator_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@ func TestDescription_AppendValidators(t *testing.T) {
4747
}),
4848
expectedDesc: "Desc\nPossible values: `val1`, `val2`",
4949
},
50+
{
51+
name: "Commas inside quotes",
52+
initialDesc: stringPtr("Desc"),
53+
validators: NewValidators(ValidatorTypeString, specschema.CustomValidators{
54+
{
55+
SchemaDefinition: `stringvalidator.OneOf("a,b", "c")`,
56+
},
57+
}),
58+
expectedDesc: "Desc\nPossible values: `a,b`, `c`",
59+
},
5060
{
5161
name: "No OneOf",
5262
initialDesc: stringPtr("Desc"),
@@ -57,6 +67,20 @@ func TestDescription_AppendValidators(t *testing.T) {
5767
}),
5868
expectedDesc: "Desc",
5969
},
70+
{
71+
name: "Multiple OneOf (should append both)",
72+
initialDesc: stringPtr("Desc"),
73+
validators: NewValidators(ValidatorTypeString, specschema.CustomValidators{
74+
{
75+
SchemaDefinition: `stringvalidator.OneOf("a", "b")`,
76+
},
77+
{
78+
// This case is artificial, usually there's only one OneOf, but good to test behavior
79+
SchemaDefinition: `stringvalidator.OneOf("c", "d")`,
80+
},
81+
}),
82+
expectedDesc: "Desc\nPossible values: `a`, `b`\nPossible values: `c`, `d`",
83+
},
6084
}
6185

6286
for _, tt := range tests {

internal/datasource/string_attribute.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ func NewGeneratorStringAttribute(name string, a *datasource.StringAttribute) (Ge
4141

4242
v := convert.NewValidators(convert.ValidatorTypeString, a.Validators.CustomValidators())
4343

44+
d.AppendValidators(v)
45+
4446
return GeneratorStringAttribute{
4547
AssociatedExternalType: schema.NewAssocExtType(a.AssociatedExternalType),
4648
ComputedOptionalRequired: c,

0 commit comments

Comments
 (0)