55 "fmt"
66 "regexp"
77 "sort"
8+ "sync"
89)
910
1011// DeprecatedField represents a deprecated field with its replacement information
@@ -14,21 +15,37 @@ type DeprecatedField struct {
1415 Description string // Description from the schema
1516}
1617
17- // GetMainWorkflowDeprecatedFields returns a list of deprecated fields from the main workflow schema
18- func GetMainWorkflowDeprecatedFields () ([]DeprecatedField , error ) {
19- log .Print ("Getting deprecated fields from main workflow schema" )
20- // Parse the schema JSON
21- var schemaDoc map [string ]any
22- if err := json .Unmarshal ([]byte (mainWorkflowSchema ), & schemaDoc ); err != nil {
23- return nil , fmt .Errorf ("failed to parse main workflow schema: %w" , err )
24- }
18+ // deprecatedFieldsCache caches the result of parsing the main workflow schema so that
19+ // the expensive 414KB JSON unmarshal is only performed once per process lifetime.
20+ // Both the result and any error are cached permanently: since mainWorkflowSchema is an
21+ // embedded compile-time constant, a parse failure is always a programming error (not
22+ // transient), so re-parsing on subsequent calls would produce the same failure.
23+ var (
24+ deprecatedFieldsOnce sync.Once
25+ deprecatedFieldsCache []DeprecatedField
26+ deprecatedFieldsErr error
27+ )
2528
26- fields , err := extractDeprecatedFields (schemaDoc )
27- if err != nil {
28- return nil , err
29- }
30- log .Printf ("Found %d deprecated fields in main workflow schema" , len (fields ))
31- return fields , nil
29+ // GetMainWorkflowDeprecatedFields returns a list of deprecated fields from the main workflow schema.
30+ // The result is cached after the first call so the schema is only parsed once per process.
31+ // Callers must not modify the returned slice.
32+ func GetMainWorkflowDeprecatedFields () ([]DeprecatedField , error ) {
33+ deprecatedFieldsOnce .Do (func () {
34+ log .Print ("Getting deprecated fields from main workflow schema" )
35+ var schemaDoc map [string ]any
36+ if err := json .Unmarshal ([]byte (mainWorkflowSchema ), & schemaDoc ); err != nil {
37+ deprecatedFieldsErr = fmt .Errorf ("failed to parse main workflow schema: %w" , err )
38+ return
39+ }
40+ fields , err := extractDeprecatedFields (schemaDoc )
41+ if err != nil {
42+ deprecatedFieldsErr = err
43+ return
44+ }
45+ deprecatedFieldsCache = fields
46+ log .Printf ("Found %d deprecated fields in main workflow schema" , len (fields ))
47+ })
48+ return deprecatedFieldsCache , deprecatedFieldsErr
3249}
3350
3451// extractDeprecatedFields extracts deprecated fields from a schema document
@@ -75,20 +92,21 @@ func extractDeprecatedFields(schemaDoc map[string]any) ([]DeprecatedField, error
7592 return deprecated , nil
7693}
7794
78- // extractReplacementFromDescription extracts the replacement field name from a description
79- // It looks for patterns like "Use 'field-name' instead" or "Deprecated: Use 'field-name'"
80- func extractReplacementFromDescription (description string ) string {
81- // Common patterns in deprecation messages
82- patterns := []string {
83- `[Uu]se '([^']+)' instead` ,
84- `[Uu]se "([^"]+)" instead` ,
85- `[Uu]se ` + "`" + `([^` + "`" + `]+)` + "`" + ` instead` ,
86- `[Rr]eplace(?:d)? (?:with|by) '([^']+)'` ,
87- `[Rr]eplace(?:d)? (?:with|by) "([^"]+)"` ,
88- }
95+ // replacementPatterns are pre-compiled regexes used by extractReplacementFromDescription.
96+ // Pre-compiling avoids repeated compilation overhead when extracting replacements from
97+ // many deprecated field descriptions.
98+ var replacementPatterns = []* regexp.Regexp {
99+ regexp .MustCompile (`[Uu]se '([^']+)' instead` ),
100+ regexp .MustCompile (`[Uu]se "([^"]+)" instead` ),
101+ regexp .MustCompile ("[Uu]se `([^`]+)` instead" ),
102+ regexp .MustCompile (`[Rr]eplace(?:d)? (?:with|by) '([^']+)'` ),
103+ regexp .MustCompile (`[Rr]eplace(?:d)? (?:with|by) "([^"]+)"` ),
104+ }
89105
90- for _ , pattern := range patterns {
91- re := regexp .MustCompile (pattern )
106+ // extractReplacementFromDescription extracts the replacement field name from a description.
107+ // It looks for patterns like "Use 'field-name' instead" or "Deprecated: Use 'field-name'".
108+ func extractReplacementFromDescription (description string ) string {
109+ for _ , re := range replacementPatterns {
92110 if match := re .FindStringSubmatch (description ); len (match ) >= 2 {
93111 return match [1 ]
94112 }
0 commit comments