Skip to content

Commit d9d17a0

Browse files
authored
feat: add xast function to append new switch clause (#4797)
* feat: add xast function to append new switch clause * add changelog * fix lint * improve the error handler * remove unused verifiers
1 parent a74b03c commit d9d17a0

File tree

7 files changed

+366
-98
lines changed

7 files changed

+366
-98
lines changed

changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
### Features
66

77
- [#4790](https://github.com/ignite/cli/pull/4790) Remove global vars and struct placeholders.
8+
- [#4797](https://github.com/ignite/cli/pull/4797) Add `xast` function to append new switch clause.
89

910
## [`v29.3.1`](https://github.com/ignite/cli/releases/tag/v29.3.1)
1011

ignite/pkg/xast/function.go

Lines changed: 148 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,30 @@ import (
1616
type (
1717
// functionOpts represent the options for functions.
1818
functionOpts struct {
19-
funcName string // Name of the function to modify.
20-
newParams []functionParam // Parameters to add to the function.
21-
body string // New function body content.
22-
newLines []functionLine // Lines to insert at specific positions.
23-
insideCall functionCalls // Function calls to modify.
24-
insideStruct functionStructs // Struct literals to modify.
25-
appendTestCase []string // Test cases to append.
26-
appendCode []string // Code to append at the end.
27-
returnVars []string // Return variables to modify.
19+
newParams []functionParam // Parameters to add to the function.
20+
body string // New function body content.
21+
newLines []functionLine // Lines to insert at specific positions.
22+
insideCall functionCalls // Function calls to modify.
23+
insideStruct functionStructs // Struct literals to modify.
24+
appendTestCase []string // Test cases to append.
25+
appendCode []string // Code to append at the end.
26+
returnVars []string // Return variables to modify.
27+
appendSwitch functionSwitches // Switch cases to append.
2828
}
2929

3030
// FunctionOptions configures code generation.
3131
FunctionOptions func(*functionOpts)
3232

33+
// functionStruct represents a struct literal to modify.
34+
functionSwitch struct {
35+
condition string // Condition to find.
36+
switchCase string // Switch case to insert.
37+
switchBody string // Code to insert.
38+
}
39+
40+
functionSwitches []functionSwitch
41+
functionSwitchesMap map[string]functionSwitches
42+
3343
// functionStruct represents a struct literal to modify.
3444
functionStruct struct {
3545
name string // Name of the struct type.
@@ -83,6 +93,19 @@ func (s functionStructs) Map() functionStructsMap {
8393
return structMap
8494
}
8595

96+
// Map converts a slice of functionStructs to a map keyed by struct name.
97+
func (s functionSwitches) Map() functionSwitchesMap {
98+
switchesMap := make(functionSwitchesMap)
99+
for _, c := range s {
100+
switches, ok := switchesMap[c.condition]
101+
if !ok {
102+
switches = make(functionSwitches, 0)
103+
}
104+
switchesMap[c.condition] = append(switches, c)
105+
}
106+
return switchesMap
107+
}
108+
86109
// Map converts a slice of functionCalls to a map keyed by function name.
87110
func (c functionCalls) Map() functionCallsMap {
88111
callMap := make(functionCallsMap)
@@ -173,10 +196,20 @@ func NewFuncReturn(returnVars ...string) FunctionOptions {
173196
}
174197
}
175198

199+
// AppendSwitchCase inserts a new case with the code at a specific switch condition statement.
200+
func AppendSwitchCase(condition, switchCase, switchBody string) FunctionOptions {
201+
return func(c *functionOpts) {
202+
c.appendSwitch = append(c.appendSwitch, functionSwitch{
203+
condition: condition,
204+
switchCase: switchCase,
205+
switchBody: switchBody,
206+
})
207+
}
208+
}
209+
176210
// newFunctionOptions creates a new functionOpts with defaults.
177-
func newFunctionOptions(funcName string) functionOpts {
211+
func newFunctionOptions() functionOpts {
178212
return functionOpts{
179-
funcName: funcName,
180213
newParams: make([]functionParam, 0),
181214
body: "",
182215
newLines: make([]functionLine, 0),
@@ -191,7 +224,7 @@ func newFunctionOptions(funcName string) functionOpts {
191224
// ModifyFunction modifies a function in Go source code using functional options.
192225
func ModifyFunction(content string, funcName string, functions ...FunctionOptions) (string, error) {
193226
// Collect all function options.
194-
opts := newFunctionOptions(funcName)
227+
opts := newFunctionOptions()
195228
for _, fn := range functions {
196229
fn(&opts)
197230
}
@@ -256,6 +289,55 @@ func modifyReturnVars(fileSet *token.FileSet, returnVars []string) ([]ast.Expr,
256289
return stmts, nil
257290
}
258291

292+
// appendSwitchCase appends a new case to a switch statement.
293+
func appendSwitchCase(fileSet *token.FileSet, stmt ast.Node, fs functionSwitches) error {
294+
for _, f := range fs {
295+
// Parse the new case code
296+
newRetExpr, err := parser.ParseExprFrom(fileSet, "", []byte(f.switchCase), parser.ParseComments)
297+
if err != nil {
298+
return err
299+
}
300+
301+
bodyStmt, err := codeToBlockStmt(fileSet, f.switchBody)
302+
if err != nil {
303+
return err
304+
}
305+
306+
// Create a new case clause
307+
newCase := &ast.CaseClause{
308+
List: []ast.Expr{newRetExpr},
309+
Body: bodyStmt.List,
310+
Case: token.NoPos, // Keep first item aligned with case keyword
311+
Colon: token.NoPos, // Keep colon aligned with case keyword
312+
}
313+
314+
// Handle different types of switch statements
315+
switch statement := stmt.(type) {
316+
case *ast.TypeSwitchStmt:
317+
statement.Body.List = appendCaseToList(statement.Body.List, newCase)
318+
case *ast.SwitchStmt:
319+
statement.Body.List = appendCaseToList(statement.Body.List, newCase)
320+
default:
321+
return errors.Errorf("unsupported switch statement type: %T", stmt)
322+
}
323+
}
324+
return nil
325+
}
326+
327+
// appendCaseToList handles inserting a case clause into a list of statements,
328+
// placing it before any default case if one exists.
329+
func appendCaseToList(list []ast.Stmt, newCase *ast.CaseClause) []ast.Stmt {
330+
if len(list) > 0 {
331+
lastCase, isDefault := list[len(list)-1].(*ast.CaseClause)
332+
if isDefault && len(lastCase.List) == 0 {
333+
// Insert before default.
334+
return append(list[:len(list)-1], newCase, list[len(list)-1])
335+
}
336+
}
337+
338+
return append(list, newCase)
339+
}
340+
259341
// addParams adds new parameters to a function declaration.
260342
func addParams(funcDecl *ast.FuncDecl, newParams []functionParam) error {
261343
for _, p := range newParams {
@@ -371,7 +453,7 @@ func addTestCase(fSet *token.FileSet, funcDecl *ast.FuncDecl, testCase []string)
371453
// structToBlockStmt parses struct literal code into AST expression.
372454
func structToBlockStmt(fSet *token.FileSet, code string) (ast.Expr, error) {
373455
newFuncContent := toStruct(code)
374-
newContent, err := parser.ParseExprFrom(fSet, "temp.go", newFuncContent, parser.AllErrors)
456+
newContent, err := parser.ParseExprFrom(fSet, "", newFuncContent, parser.AllErrors)
375457
if err != nil {
376458
return nil, err
377459
}
@@ -498,7 +580,8 @@ func formatNode(fileSet *token.FileSet, n ast.Node) (string, error) {
498580
return "", err
499581
}
500582

501-
return buf.String(), nil
583+
node := strings.TrimSpace(buf.String())
584+
return node, nil
502585
}
503586

504587
// applyFunctionOptions applies all modifications to a function.
@@ -526,20 +609,19 @@ func applyFunctionOptions(fileSet *token.FileSet, f *ast.FuncDecl, opts *functio
526609

527610
// Create maps for tracking modifications.
528611
var (
529-
callMap = opts.insideCall.Map()
530-
callMapCheck = opts.insideCall.Map()
531-
structMap = opts.insideStruct.Map()
532-
structMapCheck = opts.insideStruct.Map()
612+
callMap = opts.insideCall.Map()
613+
callMapCheck = opts.insideCall.Map()
614+
structMap = opts.insideStruct.Map()
615+
structMapCheck = opts.insideStruct.Map()
616+
switchesCasesMap = opts.appendSwitch.Map()
617+
switchesCasesMapCheck = opts.appendSwitch.Map()
533618
)
534619

535620
// Apply all modifications.
536-
var (
537-
found bool
538-
errInspect error
539-
)
621+
var errInspect error
540622
ast.Inspect(f, func(n ast.Node) bool {
541623
funcDecl, ok := n.(*ast.FuncDecl)
542-
if !ok || funcDecl.Name.Name != opts.funcName {
624+
if !ok {
543625
return true
544626
}
545627

@@ -567,6 +649,39 @@ func applyFunctionOptions(fileSet *token.FileSet, f *ast.FuncDecl, opts *functio
567649
return false
568650
}
569651

652+
for _, bodyList := range funcDecl.Body.List {
653+
var stmt ast.Stmt
654+
var buf bytes.Buffer
655+
switch expr := bodyList.(type) {
656+
case *ast.TypeSwitchStmt:
657+
stmt = expr
658+
if err := format.Node(&buf, fileSet, expr.Assign); err != nil {
659+
errInspect = err
660+
return false
661+
}
662+
case *ast.SwitchStmt:
663+
stmt = expr
664+
if err := format.Node(&buf, fileSet, expr.Tag); err != nil {
665+
errInspect = err
666+
return false
667+
}
668+
default:
669+
continue
670+
}
671+
672+
switchCase, ok := switchesCasesMap[buf.String()]
673+
if !ok {
674+
continue
675+
}
676+
677+
if err := appendSwitchCase(fileSet, stmt, switchCase); err != nil {
678+
errInspect = err
679+
return false
680+
}
681+
682+
delete(switchesCasesMapCheck, buf.String())
683+
}
684+
570685
// Modify function calls and struct literals.
571686
ast.Inspect(funcDecl, func(n ast.Node) bool {
572687
switch expr := n.(type) {
@@ -610,31 +725,28 @@ func applyFunctionOptions(fileSet *token.FileSet, f *ast.FuncDecl, opts *functio
610725
return false
611726
}
612727

613-
// Verify all modifications were applied.
614-
if len(callMapCheck) > 0 {
615-
errInspect = errors.Errorf("function calls not found: %v", callMapCheck)
616-
return false
617-
}
618-
if len(structMapCheck) > 0 {
619-
errInspect = errors.Errorf("function structs not found: %v", structMapCheck)
620-
return false
621-
}
622-
623728
// Add test cases.
624729
if err := addTestCase(fileSet, funcDecl, opts.appendTestCase); err != nil {
625730
errInspect = err
626731
return false
627732
}
628733

629-
found = true
630734
return false
631735
})
632736

633737
if errInspect != nil {
634738
return errInspect
635739
}
636-
if !found {
637-
return errors.Errorf("function %s not found in file content", opts.funcName)
740+
741+
// Verify all modifications were applied.
742+
if len(callMapCheck) > 0 {
743+
return errors.Errorf("function calls not found: %v", callMapCheck)
744+
}
745+
if len(structMapCheck) > 0 {
746+
return errors.Errorf("function structs not found: %v", structMapCheck)
747+
}
748+
if len(switchesCasesMapCheck) > 0 {
749+
return errors.Errorf("function switch not found: %v", switchesCasesMapCheck)
638750
}
639751

640752
return nil

0 commit comments

Comments
 (0)