-
Notifications
You must be signed in to change notification settings - Fork 5
Open
Labels
enhancementNew feature or requestNew feature or requestgood first issueGood for newcomersGood for newcomersprotoc-gen-go-http
Description
Description
After implementing support for all field types, refactor the mock generator to use a more maintainable and extensible pattern for field type handling.
Current Problem
The generateMockFieldAssignments
method will become very large with many similar patterns repeated across different field types. This makes it:
- Hard to maintain
- Prone to inconsistencies
- Difficult to extend with new features
Proposed Refactoring
1. Field Handler Interface
Create a consistent interface for field generation:
type FieldHandler interface {
CanHandle(field *protogen.Field) bool
Generate(g *Generator, gf *protogen.GeneratedFile, field *protogen.Field, varName string, depth int)
}
type FieldHandlerRegistry struct {
handlers []FieldHandler
}
func (r *FieldHandlerRegistry) GetHandler(field *protogen.Field) FieldHandler {
for _, h := range r.handlers {
if h.CanHandle(field) {
return h
}
}
return &DefaultFieldHandler{}
}
2. Specialized Handlers
type StringFieldHandler struct{}
func (h *StringFieldHandler) CanHandle(field *protogen.Field) bool {
return field.Desc.Kind() == protoreflect.StringKind
}
func (h *StringFieldHandler) Generate(
g *Generator,
gf *protogen.GeneratedFile,
field *protogen.Field,
varName string,
depth int,
) {
fieldName := field.GoName
fieldPath := g.getFieldPath(field)
if field.Desc.IsList() {
g.generateRepeatedField(gf, field, varName, func(i int) string {
return fmt.Sprintf(`selectStringExample("%s", %s)`,
fieldPath, g.getDefaultGenerator(field))
})
} else {
gf.P(varName, ".", fieldName, ` = selectStringExample("`,
fieldPath, `", `, g.getDefaultGenerator(field), `)`)
}
}
3. Shared Generation Utilities
func (g *Generator) generateRepeatedField(
gf *protogen.GeneratedFile,
field *protogen.Field,
varName string,
valueGenerator func(index int) string,
) {
fieldName := field.GoName
fieldType := g.getFieldType(field)
gf.P(varName, ".", fieldName, " = []", fieldType, "{")
for i := 0; i < g.config.ListSize; i++ {
gf.P(valueGenerator(i), ",")
}
gf.P("}")
}
func (g *Generator) generateMapField(
gf *protogen.GeneratedFile,
field *protogen.Field,
varName string,
keyGen func(int) string,
valueGen func(int) string,
) {
// Shared map generation logic
}
4. Configuration Object
type MockConfig struct {
ListSize int
MapSize int
MaxDepth int
SkipOptionalAt int
IncludeExamples bool
SmartDefaults bool
}
func (g *Generator) Configure(cfg MockConfig) {
g.config = cfg
}
5. Simplified Main Method
func (g *Generator) generateMockFieldAssignments(
gf *protogen.GeneratedFile,
message *protogen.Message,
varName string,
) {
g.generateMockFieldAssignmentsWithDepth(gf, message, varName, 0)
}
func (g *Generator) generateMockFieldAssignmentsWithDepth(
gf *protogen.GeneratedFile,
message *protogen.Message,
varName string,
depth int,
) {
if depth >= g.config.MaxDepth {
return
}
for _, field := range message.Fields {
if !g.shouldGenerateField(field, depth) {
continue
}
handler := g.registry.GetHandler(field)
handler.Generate(g, gf, field, varName, depth)
}
}
Benefits
Maintainability
- Each field type handler is isolated and testable
- Common patterns are extracted to utilities
- Easy to find and fix type-specific bugs
Extensibility
- New field types just need a new handler
- Custom behavior can be added via handler composition
- Plugin system for project-specific mock generation
Consistency
- Shared utilities ensure consistent behavior
- Configuration is centralized
- Patterns are enforced through interfaces
Testing
- Each handler can be unit tested independently
- Mock the registry for testing specific scenarios
- Easier to test edge cases per type
Migration Plan
- Phase 1: Implement the handler interface and registry
- Phase 2: Create handlers for existing supported types
- Phase 3: Migrate one type at a time to handlers
- Phase 4: Extract common utilities
- Phase 5: Remove old switch statement
- Phase 6: Add new types using handler pattern
Files Affected
internal/httpgen/mock_generator.go
- Main refactoringinternal/httpgen/mock_handlers.go
- New file for handlersinternal/httpgen/mock_config.go
- New file for configurationinternal/httpgen/mock_utils.go
- New file for utilities
Success Criteria
- All existing functionality preserved
- Code coverage maintained or improved
- New field types can be added in < 50 lines
- Performance not degraded
- Generated output identical to before refactoring
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or requestgood first issueGood for newcomersGood for newcomersprotoc-gen-go-http