Skip to content

Commit 4be2223

Browse files
committed
In output-mode inject do not fail if file not found
The following scenarios can happen for --output-mode inject: - file exists, comments exist: inject output between comments - file exists, comments not found: append output at the end of file - file exists, but empty: save the whole output into the file - file not found: create the target file and save the ooutput into it Signed-off-by: Khosrow Moossavi <khos2ow@gmail.com>
1 parent f957813 commit 4be2223

7 files changed

+173
-56
lines changed

docs/user-guide/configuration.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -185,9 +185,9 @@ flag) is not empty. Insersion behavior can be controlled by `output.mode` (or
185185

186186
- `inject` (default)
187187

188-
Partially replace the `output-file` with generated output. This will fail if
189-
`output-file` doesn't exist. Also will fail if `output-file` doesn't already
190-
have surrounding comments.
188+
Partially replace the `output-file` with generated output. This will create
189+
the `output-file` if it doesn't exist. It will also append to `output-file`
190+
if it doesn't have surrounding comments.
191191

192192
- `replace`
193193

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<!-- BEGIN_TF_DOCS -->
2+
Lorem ipsum dolor sit amet, consectetur adipiscing elit
3+
<!-- END_TF_DOCS -->
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<!-- BEGIN_TF_DOCS -->
2+
Lorem ipsum dolor sit amet, consectetur adipiscing elit
3+
<!-- END_TF_DOCS -->
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Foo
2+
3+
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
4+
5+
## Bar
6+
7+
sed do eiusmod tempor incididunt ut labore et dolore magna
8+
aliqua.
9+
10+
- Ut enim ad minim veniam
11+
- quis nostrud exercitation
12+
13+
ullamco laboris nisi ut aliquip ex ea commodo consequat.
14+
Duis aute irure dolor in reprehenderit in voluptate velit
15+
16+
## Baz
17+
18+
esse cillum dolore eu fugiat nulla pariatur.
19+
20+
<!-- BEGIN_TF_DOCS -->
21+
Lorem ipsum dolor sit amet, consectetur adipiscing elit
22+
<!-- END_TF_DOCS -->
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Foo
2+
3+
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
4+
5+
## Bar
6+
7+
sed do eiusmod tempor incididunt ut labore et dolore magna
8+
aliqua.
9+
10+
- Ut enim ad minim veniam
11+
- quis nostrud exercitation
12+
13+
ullamco laboris nisi ut aliquip ex ea commodo consequat.
14+
Duis aute irure dolor in reprehenderit in voluptate velit
15+
16+
## Baz
17+
18+
esse cillum dolore eu fugiat nulla pariatur.

internal/cli/writer.go

Lines changed: 64 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
)
2222

2323
const (
24-
errFileEmpty = "file content is empty"
2524
errTemplateEmpty = "template is missing"
2625
errBeginCommentMissing = "begin comment is missing"
2726
errEndCommentMissing = "end comment is missing"
@@ -31,6 +30,7 @@ const (
3130
// stdoutWriter writes content to os.Stdout.
3231
type stdoutWriter struct{}
3332

33+
// Write content to Stdout
3434
func (sw *stdoutWriter) Write(p []byte) (int, error) {
3535
return os.Stdout.Write([]byte(string(p) + "\n"))
3636
}
@@ -60,11 +60,10 @@ type fileWriter struct {
6060
writer io.Writer
6161
}
6262

63+
// Write content to target file
6364
func (fw *fileWriter) Write(p []byte) (int, error) {
6465
filename := fw.fullFilePath()
6566

66-
var buf bytes.Buffer
67-
6867
if fw.template == "" {
6968
// template is optional for mode replace
7069
if fw.mode == outputModeReplace {
@@ -73,61 +72,97 @@ func (fw *fileWriter) Write(p []byte) (int, error) {
7372
return 0, errors.New(errTemplateEmpty)
7473
}
7574

76-
tmpl := template.Must(template.New("content").Parse(fw.template))
77-
if err := tmpl.ExecuteTemplate(&buf, "content", struct {
78-
Content string
79-
}{
80-
Content: string(p),
81-
}); err != nil {
75+
// apply template to generated output
76+
buf, err := fw.apply(p)
77+
if err != nil {
8278
return 0, err
8379
}
8480

8581
// Replace the content of 'filename' with generated output,
86-
// no further processing is reequired for mode 'replace'.
82+
// no further processing is required for mode 'replace'.
8783
if fw.mode == outputModeReplace {
8884
return fw.write(filename, buf.Bytes())
8985
}
9086

91-
content := buf.String()
92-
93-
f, err := os.ReadFile(filename)
87+
content, err := os.ReadFile(filename)
9488
if err != nil {
95-
return 0, err
89+
// In mode 'inject', if target file not found:
90+
// create it and save the generated output into it.
91+
return fw.write(filename, buf.Bytes())
92+
}
93+
94+
if len(content) == 0 {
95+
// In mode 'inject', if target file is found BUT it's empty:
96+
// save the generated output into it.
97+
return fw.write(filename, buf.Bytes())
98+
}
99+
100+
return fw.inject(filename, string(content), buf.String())
101+
}
102+
103+
// fullFilePath of the target file. If file is absolute path it will be
104+
// used as is, otherwise dir (i.e. module root folder) will be joined to
105+
// it.
106+
func (fw *fileWriter) fullFilePath() string {
107+
if filepath.IsAbs(fw.file) {
108+
return fw.file
109+
}
110+
return filepath.Join(fw.dir, fw.file)
111+
}
112+
113+
// apply template to generated output
114+
func (fw *fileWriter) apply(p []byte) (bytes.Buffer, error) {
115+
type content struct {
116+
Content string
96117
}
97118

98-
fc := string(f)
99-
if fc == "" {
100-
return 0, errors.New(errFileEmpty)
119+
var buf bytes.Buffer
120+
121+
tmpl := template.Must(template.New("content").Parse(fw.template))
122+
err := tmpl.ExecuteTemplate(&buf, "content", content{string(p)})
123+
124+
return buf, err
125+
}
126+
127+
// inject generated output into file.
128+
func (fw *fileWriter) inject(filename string, content string, generated string) (int, error) {
129+
before := strings.Index(content, fw.begin)
130+
after := strings.Index(content, fw.end)
131+
132+
// current file content doesn't have surrounding
133+
// so we're going to append the generated output
134+
// to current file.
135+
if before < 0 && after < 0 {
136+
return fw.write(filename, []byte(content+"\n"+generated))
101137
}
102138

103-
before := strings.Index(fc, fw.begin)
139+
// begin comment is missing
104140
if before < 0 {
105141
return 0, errors.New(errBeginCommentMissing)
106142
}
107-
content = fc[:before] + content
108143

109-
after := strings.Index(fc, fw.end)
144+
generated = content[:before] + generated
145+
146+
// end comment is missing
110147
if after < 0 {
111148
return 0, errors.New(errEndCommentMissing)
112149
}
150+
151+
// end comment is before begin comment
113152
if after < before {
114153
return 0, errors.New(errEndCommentBeforeBegin)
115154
}
116-
content += fc[after+len(fw.end):]
117155

118-
return fw.write(filename, []byte(content))
156+
generated += content[after+len(fw.end):]
157+
158+
return fw.write(filename, []byte(generated))
119159
}
120160

161+
// wrtie the content to io.Writer. If no io.Writer is available,
162+
// it will be written to 'filename'.
121163
func (fw *fileWriter) write(filename string, p []byte) (int, error) {
122164
if fw.writer != nil {
123165
return fw.writer.Write(p)
124166
}
125167
return len(p), os.WriteFile(filename, p, 0644)
126168
}
127-
128-
func (fw *fileWriter) fullFilePath() string {
129-
if filepath.IsAbs(fw.file) {
130-
return fw.file
131-
}
132-
return filepath.Join(fw.dir, fw.file)
133-
}

internal/cli/writer_test.go

Lines changed: 60 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,42 @@ func TestFileWriter(t *testing.T) {
8080
wantErr: false,
8181
errMsg: "",
8282
},
83+
"ModeInjectEmptyFile": {
84+
file: "empty-file.md",
85+
mode: "inject",
86+
template: OutputTemplate,
87+
begin: outputBeginComment,
88+
end: outputEndComment,
89+
writer: &bytes.Buffer{},
90+
91+
expected: "mode-inject-empty-file",
92+
wantErr: false,
93+
errMsg: "",
94+
},
95+
"ModeInjectNoCommentAppend": {
96+
file: "mode-inject-no-comment.md",
97+
mode: "inject",
98+
template: OutputTemplate,
99+
begin: outputBeginComment,
100+
end: outputEndComment,
101+
writer: &bytes.Buffer{},
102+
103+
expected: "mode-inject-no-comment",
104+
wantErr: false,
105+
errMsg: "",
106+
},
107+
"ModeInjectFileMissing": {
108+
file: "file-missing.md",
109+
mode: "inject",
110+
template: OutputTemplate,
111+
begin: outputBeginComment,
112+
end: outputEndComment,
113+
writer: &bytes.Buffer{},
114+
115+
expected: "mode-inject-file-missing",
116+
wantErr: false,
117+
errMsg: "",
118+
},
83119
"ModeReplaceWithComment": {
84120
file: "mode-replace.md",
85121
mode: "replace",
@@ -92,6 +128,30 @@ func TestFileWriter(t *testing.T) {
92128
wantErr: false,
93129
errMsg: "",
94130
},
131+
"ModeReplaceWithCommentEmptyFile": {
132+
file: "mode-replace.md",
133+
mode: "replace",
134+
template: OutputTemplate,
135+
begin: outputBeginComment,
136+
end: outputEndComment,
137+
writer: &bytes.Buffer{},
138+
139+
expected: "mode-replace-with-comment",
140+
wantErr: false,
141+
errMsg: "",
142+
},
143+
"ModeReplaceWithCommentFileMissing": {
144+
file: "file-missing.md",
145+
mode: "replace",
146+
template: OutputTemplate,
147+
begin: outputBeginComment,
148+
end: outputEndComment,
149+
writer: &bytes.Buffer{},
150+
151+
expected: "mode-replace-with-comment",
152+
wantErr: false,
153+
errMsg: "",
154+
},
95155
"ModeReplaceWithoutComment": {
96156
file: "mode-replace.md",
97157
mode: "replace",
@@ -118,18 +178,6 @@ func TestFileWriter(t *testing.T) {
118178
},
119179

120180
// Error writes
121-
"ModeInjectNoFile": {
122-
file: "file-missing.md",
123-
mode: "inject",
124-
template: OutputTemplate,
125-
begin: outputBeginComment,
126-
end: outputEndComment,
127-
writer: nil,
128-
129-
expected: "",
130-
wantErr: true,
131-
errMsg: "open testdata/writer/file-missing.md: no such file or directory",
132-
},
133181
"EmptyTemplate": {
134182
file: "not-applicable.md",
135183
mode: "inject",
@@ -142,18 +190,6 @@ func TestFileWriter(t *testing.T) {
142190
wantErr: true,
143191
errMsg: "template is missing",
144192
},
145-
"EmptyFile": {
146-
file: "empty-file.md",
147-
mode: "inject",
148-
template: OutputTemplate,
149-
begin: outputBeginComment,
150-
end: outputEndComment,
151-
writer: nil,
152-
153-
expected: "",
154-
wantErr: true,
155-
errMsg: "file content is empty",
156-
},
157193
"BeginCommentMissing": {
158194
file: "begin-comment-missing.md",
159195
mode: "inject",

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy