Part of duplicate code analysis: #8475
Summary
Two config validation files (validation_rules.go and validation_errors.go) contain 14+ functions that all follow an identical structural pattern: emit an entry/failure debug log and return a &ValidationError{Field, Message, JSONPath, Suggestion}. The repeated structure increases the cost of logging format changes and makes the codebase harder to scan.
Duplication Details
Pattern A: Log-entry → condition check → log-failure → return &ValidationError{}
File: internal/config/validation_rules.go
- Severity: High
- Occurrences: 5 functions (PortRange, PositiveInteger, TimeoutMinimum, TimeoutRange, AbsolutePath)
- Locations: lines 21–33, 50–62, 66–78, 82–95, 205–238+
// PortRange — lines 21-33
func PortRange(port int, jsonPath string) *ValidationError {
logValidation.Printf("Validating port range: port=%d, jsonPath=%s", port, jsonPath)
if port < 1 || port > 65535 {
logValidation.Printf("Port validation failed: port=%d out of range", port)
return &ValidationError{
Field: "port", Message: fmt.Sprintf("...%d", port),
JSONPath: jsonPath, Suggestion: "Use a valid port number (e.g., 8080)",
}
}
return nil
}
// PositiveInteger — lines 50-62 (identical structure, different field/message)
func PositiveInteger(value int, fieldName, jsonPath string) *ValidationError {
logValidation.Printf("Validating positive integer: field=%s, value=%d, jsonPath=%s", ...)
if value < 1 {
logValidation.Printf("Positive integer validation failed: %s=%d ...", ...)
return &ValidationError{Field: fieldName, Message: ..., JSONPath: jsonPath, Suggestion: ...}
}
return nil
}
// TimeoutMinimum, TimeoutRange, AbsolutePath — same 3-section structure
Pattern B: Log-entry → immediately return &ValidationError{}
File: internal/config/validation_errors.go
- Severity: Medium
- Occurrences: 7 constructor functions (UnsupportedType, UndefinedVariable, MissingRequired, UnsupportedField, InvalidPattern, InvalidValue, SchemaValidationError)
- Locations: lines 39–120
// UnsupportedType — lines 39-47
func UnsupportedType(fieldName, actualType, jsonPath, suggestion string) *ValidationError {
logValidation.Printf("Validation error: unsupported type at %s.%s, type=%s", jsonPath, fieldName, actualType)
return &ValidationError{
Field: fieldName, Message: fmt.Sprintf("unsupported type '%s' for field '%s'", actualType, fieldName),
JSONPath: jsonPath, Suggestion: suggestion,
}
}
// UndefinedVariable, MissingRequired, InvalidPattern, InvalidValue, ... — identical log+return pattern
Impact Analysis
- Maintainability: Changing the validation log format (e.g., adding a structured
level= prefix) requires editing all 12+ call sites across both files
- Bug Risk: Medium — divergent log formats across validation functions can obscure root cause during debugging
- Code Bloat: ~84 lines of repetitive boilerplate; each new validation rule adds another 10-line block
Refactoring Recommendations
-
Extract a shared logAndReturn helper for Pattern B
// In validation_errors.go
func newValidationError(logMsg, field, message, jsonPath, suggestion string) *ValidationError {
logValidation.Printf(logMsg)
return &ValidationError{Field: field, Message: message, JSONPath: jsonPath, Suggestion: suggestion}
}
Each constructor becomes a 1–2 line call. Estimated effort: 1–2 hours.
-
Extract a validateInt helper for Pattern A rule functions
// In validation_rules.go
func validateInt(fieldName, jsonPath string, value int, check func(int) bool, buildErr func() *ValidationError) *ValidationError {
logValidation.Printf("Validating %s: value=%d, jsonPath=%s", fieldName, value, jsonPath)
if !check(value) {
logValidation.Printf("%s validation failed: %s=%d", fieldName, fieldName, value)
return buildErr()
}
return nil
}
Estimated effort: 2–3 hours.
-
Accept Pattern B as intentional constructors (lower effort)
- The functions in
validation_errors.go are already a thin constructor layer; add a comment to the file header documenting the expected structure for new constructors
- Estimated effort: 30 minutes
Implementation Checklist
Parent Issue
See parent analysis report: #8475
Related to #8475
Generated by Duplicate Code Detector · 229.8 AIC · ⊞ 8.5K · ◷
Part of duplicate code analysis: #8475
Summary
Two config validation files (
validation_rules.goandvalidation_errors.go) contain 14+ functions that all follow an identical structural pattern: emit an entry/failure debug log and return a&ValidationError{Field, Message, JSONPath, Suggestion}. The repeated structure increases the cost of logging format changes and makes the codebase harder to scan.Duplication Details
Pattern A: Log-entry → condition check → log-failure → return
&ValidationError{}File:
internal/config/validation_rules.goPattern B: Log-entry → immediately return
&ValidationError{}File:
internal/config/validation_errors.goImpact Analysis
level=prefix) requires editing all 12+ call sites across both filesRefactoring Recommendations
Extract a shared
logAndReturnhelper for Pattern BEach constructor becomes a 1–2 line call. Estimated effort: 1–2 hours.
Extract a
validateInthelper for Pattern A rule functionsEstimated effort: 2–3 hours.
Accept Pattern B as intentional constructors (lower effort)
validation_errors.goare already a thin constructor layer; add a comment to the file header documenting the expected structure for new constructorsImplementation Checklist
logAndReturnorvalidateInthelpersmake test)Parent Issue
See parent analysis report: #8475
Related to #8475