Skip to content

Commit e1a170b

Browse files
erraggyclaude
andauthored
perf(validator,fixer,builder,joiner): migrate to pathutil for ref string building (#289)
* perf(validator,fixer,builder): migrate to pathutil for ref string building Replace fmt.Sprintf and inline string concatenation with centralized pathutil reference builders across validator, fixer, and builder packages. Changes: - validator/refs.go: Replace 14 fmt.Sprintf calls with pathutil functions - fixer/refs.go: Replace helper functions and inline concatenations with pathutil - fixer/stub_missing_refs.go: Use pathutil for prefix constants and descriptions - fixer/generic_names.go: Use pathutil constants in extractSchemaNameFromRefPath - builder/parameter.go: Replace parameterRefPrefix with pathutil.ParameterRef - builder/response.go: Replace responseRefPrefix with pathutil.ResponseRef Benefits: - Eliminates ~40 fmt.Sprintf/string concat calls - Removes 2 duplicate helper functions - Replaces 14+ hardcoded prefix strings with constants - Centralizes all OAS reference path logic in internal/pathutil Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * perf(joiner): migrate to pathutil for ref string building - Use pathutil.DefinitionRef/SchemaRef in schemaRefPath() - Use pathutil constants in extractSchemaName() - Modernize to strings.CutPrefix for cleaner prefix extraction Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 47200e6 commit e1a170b

7 files changed

Lines changed: 64 additions & 84 deletions

File tree

builder/parameter.go

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"regexp"
77

8+
"github.com/erraggy/oastools/internal/pathutil"
89
"github.com/erraggy/oastools/oaserrors"
910
"github.com/erraggy/oastools/parser"
1011
)
@@ -643,19 +644,10 @@ func hasParamConstraints(cfg *paramConfig) bool {
643644
cfg.defaultValue != nil
644645
}
645646

646-
// parameterRefPrefix returns the appropriate $ref prefix for parameters.
647-
// OAS 2.0 uses "#/parameters/" while OAS 3.x uses "#/components/parameters/".
648-
func (b *Builder) parameterRefPrefix() string {
649-
if b.version == parser.OASVersion20 {
650-
return "#/parameters/"
651-
}
652-
return "#/components/parameters/"
653-
}
654-
655647
// ParameterRef returns a reference to a named parameter.
656648
// This method returns the version-appropriate ref path.
657649
func (b *Builder) ParameterRef(name string) string {
658-
return b.parameterRefPrefix() + name
650+
return pathutil.ParameterRef(name, b.version == parser.OASVersion20)
659651
}
660652

661653
// WithParameterRef adds a parameter reference to the operation.

builder/response.go

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package builder
22

33
import (
4+
"github.com/erraggy/oastools/internal/pathutil"
45
"github.com/erraggy/oastools/parser"
56
)
67

@@ -96,19 +97,10 @@ func (b *Builder) AddResponse(name string, description string, responseType any,
9697
return b
9798
}
9899

99-
// responseRefPrefix returns the appropriate $ref prefix for responses.
100-
// OAS 2.0 uses "#/responses/" while OAS 3.x uses "#/components/responses/".
101-
func (b *Builder) responseRefPrefix() string {
102-
if b.version == parser.OASVersion20 {
103-
return "#/responses/"
104-
}
105-
return "#/components/responses/"
106-
}
107-
108100
// ResponseRef returns a reference to a named response.
109101
// This method returns the version-appropriate ref path.
110102
func (b *Builder) ResponseRef(name string) string {
111-
return b.responseRefPrefix() + name
103+
return pathutil.ResponseRef(name, b.version == parser.OASVersion20)
112104
}
113105

114106
// buildResponsesFromMap converts a map of status codes to responses into

fixer/generic_names.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"strings"
1212
"unicode"
1313

14+
"github.com/erraggy/oastools/internal/pathutil"
1415
"github.com/erraggy/oastools/parser"
1516
"golang.org/x/text/cases"
1617
"golang.org/x/text/language"
@@ -551,11 +552,11 @@ func rewriteSchemaRefsRecursive(schema *parser.Schema, renames map[string]string
551552
// Returns empty string if not a schema reference.
552553
func extractSchemaNameFromRefPath(ref string) string {
553554
// OAS 3.x style
554-
if name, found := strings.CutPrefix(ref, "#/components/schemas/"); found {
555+
if name, found := strings.CutPrefix(ref, pathutil.RefPrefixSchemas); found {
555556
return name
556557
}
557558
// OAS 2.0 style
558-
if name, found := strings.CutPrefix(ref, "#/definitions/"); found {
559+
if name, found := strings.CutPrefix(ref, pathutil.RefPrefixDefinitions); found {
559560
return name
560561
}
561562
return ""

fixer/refs.go

Lines changed: 28 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"fmt"
99
"strings"
1010

11+
"github.com/erraggy/oastools/internal/pathutil"
1112
"github.com/erraggy/oastools/parser"
1213
)
1314

@@ -812,7 +813,7 @@ func (c *RefCollector) IsResponseReferenced(name string, version parser.OASVersi
812813
// IsRequestBodyReferenced returns true if a request body with the given name is referenced.
813814
// This is only applicable for OAS 3.x documents.
814815
func (c *RefCollector) IsRequestBodyReferenced(name string) bool {
815-
ref := "#/components/requestBodies/" + name
816+
ref := pathutil.RequestBodyRef(name)
816817
return c.isRefReferenced(ref, RefTypeRequestBody)
817818
}
818819

@@ -831,31 +832,31 @@ func (c *RefCollector) IsHeaderReferenced(name string, version parser.OASVersion
831832
// OAS 2.0 doesn't have a global headers definition
832833
return false
833834
}
834-
ref = "#/components/headers/" + name
835+
ref = pathutil.HeaderRef(name)
835836
return c.isRefReferenced(ref, RefTypeHeader)
836837
}
837838

838839
// IsLinkReferenced returns true if a link with the given name is referenced.
839840
func (c *RefCollector) IsLinkReferenced(name string) bool {
840-
ref := "#/components/links/" + name
841+
ref := pathutil.LinkRef(name)
841842
return c.isRefReferenced(ref, RefTypeLink)
842843
}
843844

844845
// IsCallbackReferenced returns true if a callback with the given name is referenced.
845846
func (c *RefCollector) IsCallbackReferenced(name string) bool {
846-
ref := "#/components/callbacks/" + name
847+
ref := pathutil.CallbackRef(name)
847848
return c.isRefReferenced(ref, RefTypeCallback)
848849
}
849850

850851
// IsExampleReferenced returns true if an example with the given name is referenced.
851852
func (c *RefCollector) IsExampleReferenced(name string) bool {
852-
ref := "#/components/examples/" + name
853+
ref := pathutil.ExampleRef(name)
853854
return c.isRefReferenced(ref, RefTypeExample)
854855
}
855856

856857
// IsPathItemReferenced returns true if a path item with the given name is referenced.
857858
func (c *RefCollector) IsPathItemReferenced(name string) bool {
858-
ref := "#/components/pathItems/" + name
859+
ref := pathutil.PathItemRef(name)
859860
return c.isRefReferenced(ref, RefTypePathItem)
860861
}
861862

@@ -920,43 +921,34 @@ func (c *RefCollector) getRefsByType(refType RefType) []string {
920921
// schemaRefPath returns the reference path for a schema name.
921922
func schemaRefPath(name string, version parser.OASVersion) string {
922923
if version == parser.OASVersion20 {
923-
return "#/definitions/" + name
924+
return pathutil.DefinitionRef(name)
924925
}
925-
return "#/components/schemas/" + name
926+
return pathutil.SchemaRef(name)
926927
}
927928

928929
// parameterRefPath returns the reference path for a parameter name.
929930
func parameterRefPath(name string, version parser.OASVersion) string {
930-
if version == parser.OASVersion20 {
931-
return "#/parameters/" + name
932-
}
933-
return "#/components/parameters/" + name
931+
return pathutil.ParameterRef(name, version == parser.OASVersion20)
934932
}
935933

936934
// responseRefPath returns the reference path for a response name.
937935
func responseRefPath(name string, version parser.OASVersion) string {
938-
if version == parser.OASVersion20 {
939-
return "#/responses/" + name
940-
}
941-
return "#/components/responses/" + name
936+
return pathutil.ResponseRef(name, version == parser.OASVersion20)
942937
}
943938

944939
// securitySchemeRefPath returns the reference path for a security scheme name.
945940
func securitySchemeRefPath(name string, version parser.OASVersion) string {
946-
if version == parser.OASVersion20 {
947-
return "#/securityDefinitions/" + name
948-
}
949-
return "#/components/securitySchemes/" + name
941+
return pathutil.SecuritySchemeRef(name, version == parser.OASVersion20)
950942
}
951943

952944
// ExtractSchemaNameFromRef extracts the schema name from a reference path.
953945
// Returns empty string if the reference is not a schema reference.
954946
func ExtractSchemaNameFromRef(ref string, version parser.OASVersion) string {
955947
var prefix string
956948
if version == parser.OASVersion20 {
957-
prefix = "#/definitions/"
949+
prefix = pathutil.RefPrefixDefinitions
958950
} else {
959-
prefix = "#/components/schemas/"
951+
prefix = pathutil.RefPrefixSchemas
960952
}
961953

962954
if name, found := strings.CutPrefix(ref, prefix); found {
@@ -970,24 +962,24 @@ func ExtractSchemaNameFromRef(ref string, version parser.OASVersion) string {
970962
func ExtractComponentNameFromRef(ref string) (componentType, name string) {
971963
// OAS 2.0 patterns
972964
oas2Prefixes := map[string]string{
973-
"#/definitions/": "schema",
974-
"#/parameters/": "parameter",
975-
"#/responses/": "response",
976-
"#/securityDefinitions/": "securityScheme",
965+
pathutil.RefPrefixDefinitions: "schema",
966+
pathutil.RefPrefixParameters: "parameter",
967+
pathutil.RefPrefixResponses: "response",
968+
pathutil.RefPrefixSecurityDefinitions: "securityScheme",
977969
}
978970

979971
// OAS 3.x patterns
980972
oas3Prefixes := map[string]string{
981-
"#/components/schemas/": "schema",
982-
"#/components/parameters/": "parameter",
983-
"#/components/responses/": "response",
984-
"#/components/requestBodies/": "requestBody",
985-
"#/components/headers/": "header",
986-
"#/components/securitySchemes/": "securityScheme",
987-
"#/components/links/": "link",
988-
"#/components/callbacks/": "callback",
989-
"#/components/examples/": "example",
990-
"#/components/pathItems/": "pathItem",
973+
pathutil.RefPrefixSchemas: "schema",
974+
pathutil.RefPrefixParameters3: "parameter",
975+
pathutil.RefPrefixResponses3: "response",
976+
pathutil.RefPrefixRequestBodies: "requestBody",
977+
pathutil.RefPrefixHeaders: "header",
978+
pathutil.RefPrefixSecuritySchemes: "securityScheme",
979+
pathutil.RefPrefixLinks: "link",
980+
pathutil.RefPrefixCallbacks: "callback",
981+
pathutil.RefPrefixExamples: "example",
982+
pathutil.RefPrefixPathItems: "pathItem",
991983
}
992984

993985
// Try OAS 3.x patterns first (more specific)

fixer/stub_missing_refs.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"sort"
1010
"strings"
1111

12+
"github.com/erraggy/oastools/internal/pathutil"
1213
"github.com/erraggy/oastools/parser"
1314
)
1415

@@ -37,9 +38,9 @@ func isLocalRef(ref string) bool {
3738
func extractResponseNameFromRef(ref string, version parser.OASVersion) string {
3839
var prefix string
3940
if version == parser.OASVersion20 {
40-
prefix = "#/responses/"
41+
prefix = pathutil.RefPrefixResponses
4142
} else {
42-
prefix = "#/components/responses/"
43+
prefix = pathutil.RefPrefixResponses3
4344
}
4445

4546
if name, found := strings.CutPrefix(ref, prefix); found {
@@ -119,7 +120,7 @@ func (f *Fixer) stubSchemaOAS2(doc *parser.OAS2Document, name string, result *Fi
119120
fix := Fix{
120121
Type: FixTypeStubMissingRef,
121122
Path: fmt.Sprintf("definitions.%s", name),
122-
Description: fmt.Sprintf("Created stub schema for missing reference #/definitions/%s", name),
123+
Description: "Created stub schema for missing reference " + pathutil.DefinitionRef(name),
123124
Before: nil,
124125
After: stub,
125126
}
@@ -153,7 +154,7 @@ func (f *Fixer) stubResponseOAS2(doc *parser.OAS2Document, name string, result *
153154
fix := Fix{
154155
Type: FixTypeStubMissingRef,
155156
Path: fmt.Sprintf("responses.%s", name),
156-
Description: fmt.Sprintf("Created stub response for missing reference #/responses/%s", name),
157+
Description: "Created stub response for missing reference " + pathutil.ResponseRef(name, true),
157158
Before: nil,
158159
After: stub,
159160
}
@@ -238,7 +239,7 @@ func (f *Fixer) stubSchemaOAS3(doc *parser.OAS3Document, name string, result *Fi
238239
fix := Fix{
239240
Type: FixTypeStubMissingRef,
240241
Path: fmt.Sprintf("components.schemas.%s", name),
241-
Description: fmt.Sprintf("Created stub schema for missing reference #/components/schemas/%s", name),
242+
Description: "Created stub schema for missing reference " + pathutil.SchemaRef(name),
242243
Before: nil,
243244
After: stub,
244245
}
@@ -277,7 +278,7 @@ func (f *Fixer) stubResponseOAS3(doc *parser.OAS3Document, name string, result *
277278
fix := Fix{
278279
Type: FixTypeStubMissingRef,
279280
Path: fmt.Sprintf("components.responses.%s", name),
280-
Description: fmt.Sprintf("Created stub response for missing reference #/components/responses/%s", name),
281+
Description: "Created stub response for missing reference " + pathutil.ResponseRef(name, false),
281282
Before: nil,
282283
After: stub,
283284
}

joiner/rewriter.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"reflect"
66
"strings"
77

8+
"github.com/erraggy/oastools/internal/pathutil"
89
"github.com/erraggy/oastools/parser"
910
)
1011

@@ -50,9 +51,9 @@ func (r *SchemaRewriter) RewriteDocument(doc any) error {
5051
// schemaRefPath returns the $ref path for a schema name based on OAS version
5152
func schemaRefPath(name string, version parser.OASVersion) string {
5253
if version == parser.OASVersion20 {
53-
return "#/definitions/" + name
54+
return pathutil.DefinitionRef(name)
5455
}
55-
return "#/components/schemas/" + name
56+
return pathutil.SchemaRef(name)
5657
}
5758

5859
// rewriteOAS3Document rewrites all references in an OAS 3.x document
@@ -402,12 +403,12 @@ func (r *SchemaRewriter) rewriteOperation(op *parser.Operation) {
402403
// extractSchemaName extracts the schema name from a $ref path
403404
func extractSchemaName(ref string) string {
404405
// Handle "#/components/schemas/Name"
405-
if strings.HasPrefix(ref, "#/components/schemas/") {
406-
return strings.TrimPrefix(ref, "#/components/schemas/")
406+
if name, found := strings.CutPrefix(ref, pathutil.RefPrefixSchemas); found {
407+
return name
407408
}
408409
// Handle "#/definitions/Name"
409-
if strings.HasPrefix(ref, "#/definitions/") {
410-
return strings.TrimPrefix(ref, "#/definitions/")
410+
if name, found := strings.CutPrefix(ref, pathutil.RefPrefixDefinitions); found {
411+
return name
411412
}
412413
return ""
413414
}

0 commit comments

Comments
 (0)