Skip to content

Commit

Permalink
feat: Add mode to inject templates after marker (#153)
Browse files Browse the repository at this point in the history
* Add fake enum

* Add test

* Add implementation

* Fixed toolchain not available

* lint

* Update injector.go

* refactor

* update docs

* support skipping empty/null injections

* more test cases

* update linter

---------

Co-authored-by: Anthony Chung <[email protected]>
  • Loading branch information
hay-kot and ac-cb authored May 15, 2024
1 parent 5953b58 commit 705389d
Show file tree
Hide file tree
Showing 10 changed files with 375 additions and 268 deletions.
11 changes: 11 additions & 0 deletions app/commands/cmd_lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ func (ctrl *Controller) Lint(ctx *cli.Context) error {
q.Prompt.IsConfirm(),
q.Prompt.IsSelect(),
q.Prompt.IsMultiSelect(),
q.Prompt.IsInputLoop(),
q.Prompt.IsTextInput(),
}

isAny := false
Expand Down Expand Up @@ -84,6 +86,15 @@ func (ctrl *Controller) Lint(ctx *cli.Context) error {
}
}

// Validate injectjons
for _, injection := range pf.Inject {
if injection.Mode != "" {
if injection.Mode != "before" && injection.Mode != "after" {
errs = append(errs, fmt.Errorf("invalid injection mode: %s", injection.Mode))
}
}
}

for _, err := range errs {
log.Error().Err(err).Msg("")
}
Expand Down
57 changes: 35 additions & 22 deletions app/scaffold/injector.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package scaffold

import (
"bufio"
"bytes"
"errors"
"io"
"strings"
Expand All @@ -23,38 +24,50 @@ func indentation(b string) string {
// Inject will read the reader line by line and find the line
// that contains the string "at". It will then insert the data
// before that line.
func Inject(r io.Reader, data string, at string) ([]byte, error) {
bldr := strings.Builder{}
newline := func(s string) {
bldr.WriteString(s)
bldr.WriteString("\n")
func Inject(r io.Reader, data string, at string, mode Mode) ([]byte, error) {
var buf bytes.Buffer

// Write a line to the buffer
writeLine := func(line string) {
buf.WriteString(line)
buf.WriteString("\n")
}

// Write multiple lines with indentation to the buffer
writeLines := func(lines []string, indent string) {
for _, l := range lines {
if l != "" {
writeLine(indent + l)
}
}
}

found := false
var (
inserted = false
found = false
scanner = bufio.NewScanner(r)
linesToInsert = strings.Split(data, "\n")
)

scanner := bufio.NewScanner(r)
// Loop through each line in the reader
for scanner.Scan() {
line := scanner.Text()

if strings.Contains(line, at) {
// Found the line, insert the data
// before this line
indent := indentation(line)

lines := strings.Split(data, "\n")

for _, l := range lines {
if l == "" {
continue
}

newline(indent + l)
if mode != After {
writeLines(linesToInsert, indentation(line))
inserted = true
}

found = true
}

newline(line)
writeLine(line)

// If in 'after' mode and the insertion point is found, insert after it
if mode == After && found && !inserted {
writeLines(linesToInsert, indentation(line))
inserted = true
}
}

if err := scanner.Err(); err != nil {
Expand All @@ -65,5 +78,5 @@ func Inject(r io.Reader, data string, at string) ([]byte, error) {
return nil, ErrInjectMarkerNotFound
}

return []byte(bldr.String()), nil
return buf.Bytes(), nil
}
83 changes: 66 additions & 17 deletions app/scaffold/injector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,19 @@ import (
"github.com/stretchr/testify/assert"
)

var t1 = `---
hello world
indented line
# Inject Marker
`

var t1Want = `---
func TestInject(t *testing.T) {
const Marker = "# Inject Marker"
const Input = `---
hello world
indented line
injected line 1
injected line 2
# Inject Marker
`

func TestInject(t *testing.T) {
type args struct {
s string
data string
at string
mode Mode
}
tests := []struct {
name string
Expand All @@ -36,25 +30,80 @@ func TestInject(t *testing.T) {
{
name: "inject",
args: args{
s: t1,
s: Input,
data: "injected line 1\ninjected line 2",
at: "# Inject Marker",
at: Marker,
},
want: t1Want,
want: `---
hello world
indented line
injected line 1
injected line 2
# Inject Marker
`,
},
{
name: "inject no marker",
name: "inject after",
args: args{
s: t1,
s: Input,
data: "injected line 1\ninjected line 2",
at: "# Inject Marker 2",
at: Marker,
mode: After,
},
want: `---
hello world
indented line
# Inject Marker
injected line 1
injected line 2
`,
},
{
name: "don't inject empty data",
args: args{
s: Input,
data: "",
at: Marker,
},
want: Input,
},
{
name: "don't inject empty lines",
args: args{
s: Input,
data: "\n\n\n\n",
at: Marker,
},
want: Input,
},
{
name: "inject no marker",
args: args{
s: Input,
data: "injected",
at: Marker + "invalid",
},
wantErr: true,
},
{
name: "preserve manual indentation",
args: args{
s: Input,
data: " injected",
at: Marker,
mode: After,
},
want: `---
hello world
indented line
# Inject Marker
injected
`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := Inject(strings.NewReader(tt.args.s), tt.args.data, tt.args.at)
got, err := Inject(strings.NewReader(tt.args.s), tt.args.data, tt.args.at, tt.args.mode)

switch {
case tt.wantErr:
Expand Down
8 changes: 8 additions & 0 deletions app/scaffold/project_scaffold_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,18 @@ type Rewrite struct {
To string `yaml:"to"`
}

type Mode string

const (
Before Mode = "before"
After Mode = "after"
)

type Injectable struct {
Name string `yaml:"name"`
Path string `yaml:"path"`
At string `yaml:"at"`
Mode Mode `yaml:"mode"`
Template string `yaml:"template"`
}

Expand Down
7 changes: 6 additions & 1 deletion app/scaffold/render_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,12 @@ func RenderRWFS(eng *engine.Engine, args *RWFSArgs, vars engine.Vars) error {
return err
}

outbytes, err := Inject(f, out, injection.At)
// Assume that empty string or only whitespace is not a valid injection.
if out == "" || strings.TrimSpace(out) == "" {
continue
}

outbytes, err := Inject(f, out, injection.At, injection.Mode)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export default withMermaid(
base: "/scaffold/",
title: "Scaffold",
description: "A Project and Template Scaffolding Tool",
head: [["link", { rel: "icon", href: "/favicon.webp" }]],
head: [["link", { rel: "icon", href: "/scaffold/favicon.webp" }]],
themeConfig: {
search: {
provider: "local",
Expand Down
Loading

0 comments on commit 705389d

Please sign in to comment.