Skip to content

Commit 9d00618

Browse files
committed
Improve scaffolding to filter existing multiline code fragments
Signed-off-by: Adam Snyder <[email protected]>
1 parent 9817db7 commit 9d00618

File tree

4 files changed

+99
-30
lines changed

4 files changed

+99
-30
lines changed

pkg/machinery/scaffold.go

Lines changed: 76 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -353,23 +353,87 @@ func getValidCodeFragments(i Inserter) CodeFragmentsMap {
353353
return codeFragments
354354
}
355355

356-
// filterExistingValues removes the single-line values that already exists
357-
// TODO: Add support for multi-line duplicate values
356+
// filterExistingValues removes code fragments that already exist in the content.
358357
func filterExistingValues(content string, codeFragmentsMap CodeFragmentsMap) error {
359-
scanner := bufio.NewScanner(strings.NewReader(content))
360-
for scanner.Scan() {
361-
line := scanner.Text()
362-
for marker, codeFragments := range codeFragmentsMap {
363-
for i, codeFragment := range codeFragments {
364-
if strings.TrimSpace(line) == strings.TrimSpace(codeFragment) {
365-
codeFragmentsMap[marker] = append(codeFragments[:i], codeFragments[i+1:]...)
366-
}
358+
for marker, codeFragments := range codeFragmentsMap {
359+
codeFragmentsOut := codeFragments[:0]
360+
361+
for _, codeFragment := range codeFragments {
362+
exists, err := codeFragmentExists(content, codeFragment)
363+
if err != nil {
364+
return err
367365
}
368-
if len(codeFragmentsMap[marker]) == 0 {
369-
delete(codeFragmentsMap, marker)
366+
if !exists {
367+
codeFragmentsOut = append(codeFragmentsOut, codeFragment)
370368
}
371369
}
370+
371+
if len(codeFragmentsOut) == 0 {
372+
delete(codeFragmentsMap, marker)
373+
} else {
374+
codeFragmentsMap[marker] = codeFragmentsOut
375+
}
376+
}
377+
378+
return nil
379+
}
380+
381+
// codeFragmentExists checks if the codeFragment exists in the content.
382+
func codeFragmentExists(content, codeFragment string) (bool, error) {
383+
exists := false
384+
codeFragmentTrimmed := strings.TrimSpace(codeFragment)
385+
scanLines := len(strings.Split(codeFragmentTrimmed, "\n"))
386+
scanFunc := func(contentGroup string) bool {
387+
if contentGroup == codeFragmentTrimmed {
388+
exists = true
389+
return false
390+
}
391+
return true
392+
}
393+
394+
if err := scanMultiline(content, scanLines, scanFunc); err != nil {
395+
return false, err
372396
}
397+
398+
return exists, nil
399+
}
400+
401+
// scanMultiline scans a string while buffering the specified number of scanLines. It calls scanFunc
402+
// for every group of lines. The content passed to scanFunc will have trimmed whitespace. It
403+
// continues scanning the content as long as scanFunc returns true.
404+
func scanMultiline(content string, scanLines int, scanFunc func(contentGroup string) bool) error {
405+
scanner := bufio.NewScanner(strings.NewReader(content))
406+
407+
// Optimized simple case.
408+
if scanLines == 1 {
409+
for scanner.Scan() {
410+
if !scanFunc(strings.TrimSpace(scanner.Text())) {
411+
return scanner.Err()
412+
}
413+
}
414+
return scanner.Err()
415+
}
416+
417+
// Complex case.
418+
bufferedLines := make([]string, scanLines)
419+
bufferedLinesIndex := 0
420+
var sb strings.Builder
421+
422+
for scanner.Scan() {
423+
bufferedLines[bufferedLinesIndex] = scanner.Text()
424+
bufferedLinesIndex = (bufferedLinesIndex + 1) % scanLines
425+
426+
sb.Reset()
427+
for i := 0; i < scanLines; i++ {
428+
_, _ = sb.WriteString(bufferedLines[(bufferedLinesIndex+i)%scanLines])
429+
_ = sb.WriteByte('\n')
430+
}
431+
432+
if !scanFunc(strings.TrimSpace(sb.String())) {
433+
return scanner.Err()
434+
}
435+
}
436+
373437
return scanner.Err()
374438
}
375439

pkg/machinery/scaffold_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,29 @@ var b int
347347
},
348348
},
349349
),
350+
Entry("should filter already existing multi-line code fragments",
351+
pathYaml,
352+
`
353+
if err := something(); err != nil {
354+
return err
355+
}
356+
357+
#+kubebuilder:scaffold:-
358+
`,
359+
`
360+
if err := something(); err != nil {
361+
return err
362+
}
363+
364+
#+kubebuilder:scaffold:-
365+
`,
366+
fakeInserter{
367+
fakeBuilder: fakeBuilder{path: pathYaml},
368+
codeFragments: CodeFragmentsMap{
369+
NewMarkerFor(pathYaml, "-"): {"if err := something(); err != nil {\n\treturn err\n}\n\n"},
370+
},
371+
},
372+
),
350373
Entry("should not insert anything if no code fragment",
351374
pathYaml,
352375
`

testdata/project-v3-config/main.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,6 @@ func main() {
7878
os.Exit(1)
7979
}
8080

81-
if err = (&controllers.CaptainReconciler{
82-
Client: mgr.GetClient(),
83-
Scheme: mgr.GetScheme(),
84-
}).SetupWithManager(mgr); err != nil {
85-
setupLog.Error(err, "unable to create controller", "controller", "Captain")
86-
os.Exit(1)
87-
}
8881
if err = (&controllers.CaptainReconciler{
8982
Client: mgr.GetClient(),
9083
Scheme: mgr.GetScheme(),

testdata/project-v3/main.go

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -85,17 +85,6 @@ func main() {
8585
setupLog.Error(err, "unable to create controller", "controller", "Captain")
8686
os.Exit(1)
8787
}
88-
if err = (&controllers.CaptainReconciler{
89-
Client: mgr.GetClient(),
90-
Scheme: mgr.GetScheme(),
91-
}).SetupWithManager(mgr); err != nil {
92-
setupLog.Error(err, "unable to create controller", "controller", "Captain")
93-
os.Exit(1)
94-
}
95-
if err = (&crewv1.Captain{}).SetupWebhookWithManager(mgr); err != nil {
96-
setupLog.Error(err, "unable to create webhook", "webhook", "Captain")
97-
os.Exit(1)
98-
}
9988
if err = (&crewv1.Captain{}).SetupWebhookWithManager(mgr); err != nil {
10089
setupLog.Error(err, "unable to create webhook", "webhook", "Captain")
10190
os.Exit(1)

0 commit comments

Comments
 (0)