Skip to content

Commit 9b3aabc

Browse files
refactor: consolidate style functions with ParameterOptions pattern (WIP)
Complete runtime consolidation: - All Style*Param functions now take (paramName, value, ParameterOptions) - All Bind*Param functions use ParameterOptions - Query entry points: BindFormQueryParam, BindDeepObjectQueryParam, BindSpaceDelimitedQueryParam, BindPipeDelimitedQueryParam - Single-value entry points: BindFormParam, BindSimpleParam, BindLabelParam, BindMatrixParam - Delete 12 explode-specific files (6 bind + 6 style) WIP: Codegen (operation.go, gather_operations.go) and templates not yet updated. Code does not compile end-to-end.
1 parent d3bf618 commit 9b3aabc

13 files changed

Lines changed: 552 additions & 1951 deletions

codegen/internal/runtime/params/style_deep_object.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
// DeepObject style is only valid for query parameters with object values and must be exploded.
1515
// Objects: paramName[key1]=value1&paramName[key2]=value2
1616
// Nested: paramName[outer][inner]=value
17-
func StyleDeepObjectParam(paramName string, paramLocation ParamLocation, value any) (string, error) {
17+
func StyleDeepObjectParam(paramName string, value any, opts ParameterOptions) (string, error) {
1818
// deepObject always requires explode=true
1919
return MarshalDeepObject(value, paramName)
2020
}

codegen/internal/runtime/params/style_form_explode.go

Lines changed: 0 additions & 145 deletions
This file was deleted.

codegen/internal/runtime/params/style_label.go

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,15 @@ import (
1515
"github.com/oapi-codegen/oapi-codegen-exp/codegen/internal/runtime/types"
1616
)
1717

18-
// StyleLabelParam serializes a value using label style (RFC 6570) without exploding.
19-
// Label style prefixes values with a dot.
20-
// Primitives: .value
21-
// Arrays: .a,b,c
22-
// Objects: .key1,value1,key2,value2
23-
func StyleLabelParam(paramName string, paramLocation ParamLocation, value any) (string, error) {
18+
// StyleLabelParam serializes a value using label style (RFC 6570).
19+
// Label style prefixes values with a dot. Path parameters only.
20+
//
21+
// Non-explode: Primitives: .value Arrays: .a,b,c Objects: .key1,value1,key2,value2
22+
// Explode: Primitives: .value Arrays: .a.b.c Objects: .key1=value1.key2=value2
23+
func StyleLabelParam(paramName string, value any, opts ParameterOptions) (string, error) {
2424
t := reflect.TypeOf(value)
2525
v := reflect.ValueOf(value)
2626

27-
// Dereference pointers
2827
if t.Kind() == reflect.Ptr {
2928
if v.IsNil() {
3029
return "", fmt.Errorf("value is a nil pointer")
@@ -33,15 +32,14 @@ func StyleLabelParam(paramName string, paramLocation ParamLocation, value any) (
3332
t = v.Type()
3433
}
3534

36-
// Check for TextMarshaler (but not time.Time or Date)
3735
if tu, ok := value.(encoding.TextMarshaler); ok {
3836
innerT := reflect.Indirect(reflect.ValueOf(value)).Type()
3937
if !innerT.ConvertibleTo(reflect.TypeOf(time.Time{})) && !innerT.ConvertibleTo(reflect.TypeOf(types.Date{})) {
4038
b, err := tu.MarshalText()
4139
if err != nil {
4240
return "", fmt.Errorf("error marshaling '%s' as text: %w", value, err)
4341
}
44-
return "." + escapeParameterString(string(b), paramLocation), nil
42+
return "." + escapeParameterString(string(b), opts.ParamLocation), nil
4543
}
4644
}
4745

@@ -52,44 +50,41 @@ func StyleLabelParam(paramName string, paramLocation ParamLocation, value any) (
5250
for i := 0; i < n; i++ {
5351
sliceVal[i] = v.Index(i).Interface()
5452
}
55-
return styleLabelSlice(paramName, paramLocation, sliceVal)
53+
return styleLabelSlice(paramName, opts, sliceVal)
5654
case reflect.Struct:
57-
return styleLabelStruct(paramName, paramLocation, value)
55+
return styleLabelStruct(paramName, opts, value)
5856
case reflect.Map:
59-
return styleLabelMap(paramName, paramLocation, value)
57+
return styleLabelMap(paramName, opts, value)
6058
default:
61-
return styleLabelPrimitive(paramLocation, value)
62-
}
63-
}
64-
65-
func styleLabelPrimitive(paramLocation ParamLocation, value any) (string, error) {
66-
strVal, err := primitiveToString(value)
67-
if err != nil {
68-
return "", err
59+
strVal, err := primitiveToString(value)
60+
if err != nil {
61+
return "", err
62+
}
63+
return "." + escapeParameterString(strVal, opts.ParamLocation), nil
6964
}
70-
return "." + escapeParameterString(strVal, paramLocation), nil
7165
}
7266

73-
func styleLabelSlice(paramName string, paramLocation ParamLocation, values []any) (string, error) {
74-
// Label without explode: .a,b,c
67+
func styleLabelSlice(paramName string, opts ParameterOptions, values []any) (string, error) {
7568
parts := make([]string, len(values))
7669
for i, v := range values {
7770
part, err := primitiveToString(v)
7871
if err != nil {
7972
return "", fmt.Errorf("error formatting '%s': %w", paramName, err)
8073
}
81-
parts[i] = escapeParameterString(part, paramLocation)
74+
parts[i] = escapeParameterString(part, opts.ParamLocation)
8275
}
83-
return "." + strings.Join(parts, ","), nil
76+
sep := ","
77+
if opts.Explode {
78+
sep = "."
79+
}
80+
return "." + strings.Join(parts, sep), nil
8481
}
8582

86-
func styleLabelStruct(paramName string, paramLocation ParamLocation, value any) (string, error) {
87-
// Check for known types first
83+
func styleLabelStruct(paramName string, opts ParameterOptions, value any) (string, error) {
8884
if timeVal, ok := marshalKnownTypes(value); ok {
89-
return "." + escapeParameterString(timeVal, paramLocation), nil
85+
return "." + escapeParameterString(timeVal, opts.ParamLocation), nil
9086
}
9187

92-
// Check for json.Marshaler
9388
if m, ok := value.(json.Marshaler); ok {
9489
buf, err := m.MarshalJSON()
9590
if err != nil {
@@ -101,25 +96,32 @@ func styleLabelStruct(paramName string, paramLocation ParamLocation, value any)
10196
if err = e.Decode(&i2); err != nil {
10297
return "", fmt.Errorf("failed to unmarshal JSON: %w", err)
10398
}
104-
return StyleLabelParam(paramName, paramLocation, i2)
99+
return StyleLabelParam(paramName, i2, opts)
105100
}
106101

107-
// Build field dictionary
108102
fieldDict, err := structToFieldDict(value)
109103
if err != nil {
110104
return "", err
111105
}
112106

113-
// Label style without explode: .key1,value1,key2,value2
107+
if opts.Explode {
108+
var parts []string
109+
for _, k := range sortedKeys(fieldDict) {
110+
v := escapeParameterString(fieldDict[k], opts.ParamLocation)
111+
parts = append(parts, k+"="+v)
112+
}
113+
return "." + strings.Join(parts, "."), nil
114+
}
115+
114116
var parts []string
115117
for _, k := range sortedKeys(fieldDict) {
116-
v := escapeParameterString(fieldDict[k], paramLocation)
118+
v := escapeParameterString(fieldDict[k], opts.ParamLocation)
117119
parts = append(parts, k, v)
118120
}
119121
return "." + strings.Join(parts, ","), nil
120122
}
121123

122-
func styleLabelMap(paramName string, paramLocation ParamLocation, value any) (string, error) {
124+
func styleLabelMap(paramName string, opts ParameterOptions, value any) (string, error) {
123125
dict, ok := value.(map[string]any)
124126
if !ok {
125127
return "", errors.New("map not of type map[string]any")
@@ -134,10 +136,18 @@ func styleLabelMap(paramName string, paramLocation ParamLocation, value any) (st
134136
fieldDict[fieldName] = str
135137
}
136138

137-
// Label style without explode: .key1,value1,key2,value2
139+
if opts.Explode {
140+
var parts []string
141+
for _, k := range sortedKeys(fieldDict) {
142+
v := escapeParameterString(fieldDict[k], opts.ParamLocation)
143+
parts = append(parts, k+"="+v)
144+
}
145+
return "." + strings.Join(parts, "."), nil
146+
}
147+
138148
var parts []string
139149
for _, k := range sortedKeys(fieldDict) {
140-
v := escapeParameterString(fieldDict[k], paramLocation)
150+
v := escapeParameterString(fieldDict[k], opts.ParamLocation)
141151
parts = append(parts, k, v)
142152
}
143153
return "." + strings.Join(parts, ","), nil

0 commit comments

Comments
 (0)