diff --git a/src/internal/evaluation/evaluation.go b/src/internal/evaluation/evaluation.go index ea1fb13..5a4b0d1 100644 --- a/src/internal/evaluation/evaluation.go +++ b/src/internal/evaluation/evaluation.go @@ -34,16 +34,16 @@ type RenderMode int const ( // DefaultRendering preserves current rendering mode of parent - DefaultRendering RenderMode = iota + DefaultMode RenderMode = iota // TemplateRendering enables template rendering for itself and all children recursively - TemplateRendering + TemplateMode // CopyRendering disables template rendering for itself and all children recursively - CopyRendering + CopyMode // InsertRendering enables template insertion, but only for a single file - InsertRendering + InsertMode ) // EvalBoolExpression determines whether given go template expression evaluates to true or false @@ -103,12 +103,12 @@ func evalFileName(context Context, name string) (string, bool, RenderMode, error // Evaluate expression value, err := EvalBoolExpression(context, exp) if err != nil { - return "", false, DefaultRendering, fmt.Errorf("failed to eval double-bracket expression in name %q: %w", name, err) + return "", false, DefaultMode, fmt.Errorf("failed to eval double-bracket expression in name %q: %w", name, err) } // Should we exclude file/folder? if !value { - return "", false, DefaultRendering, nil + return "", false, DefaultMode, nil } // Remove expression from name @@ -118,7 +118,7 @@ func evalFileName(context Context, name string) (string, bool, RenderMode, error // Double-brace expressions (ie: "{{.name}}") in names get interpolated as expected outputName, err := EvalTemplate(context, name) if err != nil { - return "", false, DefaultRendering, fmt.Errorf("failed to evaluate double-brace expression in name %q: %w", name, err) + return "", false, DefaultMode, fmt.Errorf("failed to evaluate double-brace expression in name %q: %w", name, err) } // Determine render mode and remove .tmpl/.notmpl extensions @@ -134,23 +134,23 @@ var insertExtensionRegexp = regexp.MustCompile(`\.insert($|\.)`) func getRenderModeAndRemoveExtension(name string) (RenderMode, string) { name, ok := removeRegexp(name, tmplExtensionRegexp) if ok { - return TemplateRendering, name + return TemplateMode, name } name, ok = removeRegexp(name, notmplExtensionRegexp) if ok { - return CopyRendering, name + return CopyMode, name } name, ok = removeRegexp(name, insertExtensionRegexp) if ok { - return InsertRendering, name + return InsertMode, name } - return DefaultRendering, name + return DefaultMode, name } func removeRegexp(input string, regexp *regexp.Regexp) (string, bool) { - output := regexp.ReplaceAllString(input, "") + output := regexp.ReplaceAllString(input, "$1") return output, len(output) != len(input) } diff --git a/src/internal/evaluation/evaluation_test.go b/src/internal/evaluation/evaluation_test.go index 1052c08..854e3cf 100644 --- a/src/internal/evaluation/evaluation_test.go +++ b/src/internal/evaluation/evaluation_test.go @@ -151,49 +151,73 @@ func TestEvalFileName(t *testing.T) { Name: `Name with true [[ .TRUE_VAR ]]conditional`, ExpectedName: `Name with true conditional`, ExpectedInclude: true, - ExpectedRender: DefaultRendering, + ExpectedRender: DefaultMode, }, { Name: `Name with false [[ .FALSE_VAR ]]conditional`, ExpectedName: ``, ExpectedInclude: false, - ExpectedRender: DefaultRendering, + ExpectedRender: DefaultMode, }, { Name: `Name with false [[ .EMPTY_VAR ]]conditional`, ExpectedName: ``, ExpectedInclude: false, - ExpectedRender: DefaultRendering, + ExpectedRender: DefaultMode, }, { Name: `Name with variable {{ .VAR1 }}`, ExpectedName: `Name with variable value1`, ExpectedInclude: true, - ExpectedRender: DefaultRendering, + ExpectedRender: DefaultMode, }, { Name: `Plain name`, ExpectedName: `Plain name`, ExpectedInclude: true, - ExpectedRender: DefaultRendering, + ExpectedRender: DefaultMode, }, { Name: "abcprojektdef {{.VAR1}} ABC_PROJEKT_DEF", ExpectedName: "abcmyprojectdef value1 ABC_MYPROJECT_DEF", ExpectedInclude: true, - ExpectedRender: DefaultRendering, + ExpectedRender: DefaultMode, }, { Name: "Name of file to render.tmpl", ExpectedName: "Name of file to render", ExpectedInclude: true, - ExpectedRender: TemplateRendering, + ExpectedRender: TemplateMode, + }, + { + Name: "Name of file to render.tmpl.txt", + ExpectedName: "Name of file to render.txt", + ExpectedInclude: true, + ExpectedRender: TemplateMode, }, { Name: "Name of file NOT to render.notmpl", ExpectedName: "Name of file NOT to render", ExpectedInclude: true, - ExpectedRender: CopyRendering, + ExpectedRender: CopyMode, + }, + { + Name: "Name of file NOT to render.notmpl.txt", + ExpectedName: "Name of file NOT to render.txt", + ExpectedInclude: true, + ExpectedRender: CopyMode, + }, + { + Name: "Name of file NOT to render.insert", + ExpectedName: "Name of file NOT to render", + ExpectedInclude: true, + ExpectedRender: InsertMode, + }, + { + Name: "Name of file NOT to render.insert.txt", + ExpectedName: "Name of file NOT to render.txt", + ExpectedInclude: true, + ExpectedRender: InsertMode, }, } diff --git a/src/internal/evaluation/getEntries_test.go b/src/internal/evaluation/getEntries_test.go index fab873f..ef6c473 100644 --- a/src/internal/evaluation/getEntries_test.go +++ b/src/internal/evaluation/getEntries_test.go @@ -37,9 +37,9 @@ func TestGetEntries(t *testing.T) { "file1.txt", }, Expected: []entry{ - {input: "dir1/file2.txt", output: "dir1/file2.txt"}, - {input: "dir2/file3.txt", output: "dir2/file3.txt"}, - {input: "file1.txt", output: "file1.txt"}, + {input: "dir1/file2.txt", output: "dir1/file2.txt", mode: CopyMode}, + {input: "dir2/file3.txt", output: "dir2/file3.txt", mode: CopyMode}, + {input: "file1.txt", output: "file1.txt", mode: CopyMode}, }, }, { @@ -50,9 +50,9 @@ func TestGetEntries(t *testing.T) { "file1.txt.tmpl", }, Expected: []entry{ - {input: "dir1/file2.txt.tmpl", output: "dir1/file2.txt", render: true}, - {input: "dir2/file3.txt", output: "dir2/file3.txt", render: false}, - {input: "file1.txt.tmpl", output: "file1.txt", render: true}, + {input: "dir1/file2.txt.tmpl", output: "dir1/file2.txt", mode: TemplateMode}, + {input: "dir2/file3.txt", output: "dir2/file3.txt", mode: CopyMode}, + {input: "file1.txt.tmpl", output: "file1.txt", mode: TemplateMode}, }, }, { @@ -69,15 +69,15 @@ func TestGetEntries(t *testing.T) { "dir2/dir/file2.txt.tmpl", }, Expected: []entry{ - {input: "dir1.tmpl/file1.txt", output: "dir1/file1.txt", render: true}, - {input: "dir1.tmpl/file2.txt", output: "dir1/file2.txt", render: true}, - {input: "dir1.tmpl/file3.txt.tmpl", output: "dir1/file3.txt", render: true}, - {input: "dir1.tmpl/dir/file1.txt", output: "dir1/dir/file1.txt", render: true}, - {input: "dir1.tmpl/dir/file2.txt.tmpl", output: "dir1/dir/file2.txt", render: true}, - {input: "dir2/file1.txt", output: "dir2/file1.txt", render: false}, - {input: "dir2/file2.txt.tmpl", output: "dir2/file2.txt", render: true}, - {input: "dir2/dir/file1.txt", output: "dir2/dir/file1.txt", render: false}, - {input: "dir2/dir/file2.txt.tmpl", output: "dir2/dir/file2.txt", render: true}, + {input: "dir1.tmpl/file1.txt", output: "dir1/file1.txt", mode: TemplateMode}, + {input: "dir1.tmpl/file2.txt", output: "dir1/file2.txt", mode: TemplateMode}, + {input: "dir1.tmpl/file3.txt.tmpl", output: "dir1/file3.txt", mode: TemplateMode}, + {input: "dir1.tmpl/dir/file1.txt", output: "dir1/dir/file1.txt", mode: TemplateMode}, + {input: "dir1.tmpl/dir/file2.txt.tmpl", output: "dir1/dir/file2.txt", mode: TemplateMode}, + {input: "dir2/file1.txt", output: "dir2/file1.txt", mode: CopyMode}, + {input: "dir2/file2.txt.tmpl", output: "dir2/file2.txt", mode: TemplateMode}, + {input: "dir2/dir/file1.txt", output: "dir2/dir/file1.txt", mode: CopyMode}, + {input: "dir2/dir/file2.txt.tmpl", output: "dir2/dir/file2.txt", mode: TemplateMode}, }, }, { @@ -91,12 +91,12 @@ func TestGetEntries(t *testing.T) { "dir.tmpl/dir2.notmpl/dir/file2.txt.tmpl", }, Expected: []entry{ - {input: "dir.tmpl/file.txt", output: "dir/file.txt", render: true}, - {input: "dir.tmpl/dir1/file.txt", output: "dir/dir1/file.txt", render: true}, - {input: "dir.tmpl/dir2.notmpl/file1.txt", output: "dir/dir2/file1.txt", render: false}, - {input: "dir.tmpl/dir2.notmpl/file2.txt.tmpl", output: "dir/dir2/file2.txt", render: true}, - {input: "dir.tmpl/dir2.notmpl/dir/file1.txt", output: "dir/dir2/dir/file1.txt", render: false}, - {input: "dir.tmpl/dir2.notmpl/dir/file2.txt.tmpl", output: "dir/dir2/dir/file2.txt", render: true}, + {input: "dir.tmpl/file.txt", output: "dir/file.txt", mode: TemplateMode}, + {input: "dir.tmpl/dir1/file.txt", output: "dir/dir1/file.txt", mode: TemplateMode}, + {input: "dir.tmpl/dir2.notmpl/file1.txt", output: "dir/dir2/file1.txt", mode: CopyMode}, + {input: "dir.tmpl/dir2.notmpl/file2.txt.tmpl", output: "dir/dir2/file2.txt", mode: TemplateMode}, + {input: "dir.tmpl/dir2.notmpl/dir/file1.txt", output: "dir/dir2/dir/file1.txt", mode: CopyMode}, + {input: "dir.tmpl/dir2.notmpl/dir/file2.txt.tmpl", output: "dir/dir2/dir/file2.txt", mode: TemplateMode}, }, }, { @@ -107,7 +107,7 @@ func TestGetEntries(t *testing.T) { "dir1/file3[[.UNDEFINED_VAR]].txt.tmpl", }, Expected: []entry{ - {input: "dir1/file1[[.TRUE_VAR]].txt.tmpl", output: "dir1/file1.txt", render: true}, + {input: "dir1/file1[[.TRUE_VAR]].txt.tmpl", output: "dir1/file1.txt", mode: TemplateMode}, }, }, { @@ -118,7 +118,7 @@ func TestGetEntries(t *testing.T) { "dir3[[.UNDEFINED_VAR]]/file3.txt", }, Expected: []entry{ - {input: "dir1[[.TRUE_VAR]]/file1.txt", output: "dir1/file1.txt"}, + {input: "dir1[[.TRUE_VAR]]/file1.txt", output: "dir1/file1.txt", mode: CopyMode}, }, }, { @@ -127,7 +127,7 @@ func TestGetEntries(t *testing.T) { "dir1{{.VAR1}}/file1{{.VAR2}}.txt.tmpl", }, Expected: []entry{ - {input: "dir1{{.VAR1}}/file1{{.VAR2}}.txt.tmpl", output: "dir1value1/file1value2.txt", render: true}, + {input: "dir1{{.VAR1}}/file1{{.VAR2}}.txt.tmpl", output: "dir1value1/file1value2.txt", mode: TemplateMode}, }, }, { @@ -136,7 +136,7 @@ func TestGetEntries(t *testing.T) { "dir1{{.VAR1}}[[.TRUE_VAR]]/file1{{.VAR2}}[[.TRUE_VAR]].txt.tmpl", }, Expected: []entry{ - {input: "dir1{{.VAR1}}[[.TRUE_VAR]]/file1{{.VAR2}}[[.TRUE_VAR]].txt.tmpl", output: "dir1value1/file1value2.txt", render: true}, + {input: "dir1{{.VAR1}}[[.TRUE_VAR]]/file1{{.VAR2}}[[.TRUE_VAR]].txt.tmpl", output: "dir1value1/file1value2.txt", mode: TemplateMode}, }, }, { @@ -153,8 +153,8 @@ func TestGetEntries(t *testing.T) { "abcprojektdef.txt", }, Expected: []entry{ - {input: "ABC_PROJEKT_DEF.txt", output: "ABC_MYPROJECT_DEF.txt"}, - {input: "abcprojektdef.txt", output: "abcmyprojectdef.txt"}, + {input: "ABC_PROJEKT_DEF.txt", output: "ABC_MYPROJECT_DEF.txt", mode: CopyMode}, + {input: "abcprojektdef.txt", output: "abcmyprojectdef.txt", mode: CopyMode}, }, }, { @@ -164,7 +164,7 @@ func TestGetEntries(t *testing.T) { "dir3/[[.UNDEFINED_VAR]]/dir4/file2.txt", }, Expected: []entry{ - {input: "dir1/[[.TRUE_VAR]]/dir2/file1.txt", output: "dir1/dir2/file1.txt"}, + {input: "dir1/[[.TRUE_VAR]]/dir2/file1.txt", output: "dir1/dir2/file1.txt", mode: CopyMode}, }, }, } @@ -175,7 +175,7 @@ func TestGetEntries(t *testing.T) { results = append(results, entry{ input: filepath.Join(inputDir, ent.input), output: filepath.Join("/output", ent.output), - render: ent.render, + mode: ent.mode, }) } return results @@ -192,7 +192,7 @@ func TestGetEntries(t *testing.T) { createEmptyFile(inputFile) } - actual, err := getEntries(context, inputDir, outputDir, false) + actual, err := getEntries(context, inputDir, outputDir, CopyMode) expected := getExpected(f.Expected, inputDir) sort.SliceStable(actual, func(i, j int) bool { diff --git a/src/internal/evaluation/insertion_test.go b/src/internal/evaluation/insertion_test.go index 034bf04..5e7bd6f 100644 --- a/src/internal/evaluation/insertion_test.go +++ b/src/internal/evaluation/insertion_test.go @@ -196,6 +196,11 @@ body } func TestEval(t *testing.T) { + context := context{ + vars: varMap{ + "VAR1": "value1", + }, + } items := []struct { name string @@ -297,6 +302,28 @@ body 3 body 4 line 4`, }, + + // With templating + { + name: "with templating", + text: `line 1 +line 2`, + insert: Insert{ + sections: []Section{ + { + start: "^line 1", + body: "body 1\n{{.VAR1}}\nbody 2", + }, + }, + }, + expected: `line 1 +body 1 +value1 +body 2 +line 2`, + }, + + // Error cases { name: "no start match", text: `line 1 @@ -362,7 +389,7 @@ line 4`, assert := _assert.New(t) require := _require.New(t) - actual, err := item.insert.Eval(item.text) + actual, err := item.insert.Eval(context, item.text) if item.error != "" { require.Error(err) diff --git a/src/internal/evaluation/render.go b/src/internal/evaluation/render.go index c715f3d..3de16bc 100644 --- a/src/internal/evaluation/render.go +++ b/src/internal/evaluation/render.go @@ -6,7 +6,6 @@ import ( "os" "path/filepath" - "github.com/Samasource/jen/src/internal/insertion" "github.com/Samasource/jen/src/internal/logging" ) @@ -60,13 +59,13 @@ func getEntries(context Context, inputDir, outputDir string, parentMode RenderMo } // Mode defaults to parent's mode - if mode == DefaultRendering { + if mode == DefaultMode { mode = parentMode } // Directory? if info.IsDir() { - if mode == InsertRendering { + if mode == InsertMode { return nil, fmt.Errorf("the .insert extension is not supported for directories: %q", inputName) } children, err := getEntries(context, inputPath, outputPath, mode) @@ -96,15 +95,15 @@ func renderFile(context Context, inputPath, outputPath string, renderMode Render // Render input as template or copy as-is var outputText string - if renderMode == TemplateRendering { + if renderMode == TemplateMode { // Render file as template outputText, err = EvalTemplate(context, string(inputText)) if err != nil { return fmt.Errorf("failed to render template %q: %w", inputPath, err) } - } else if renderMode == InsertRendering { + } else if renderMode == InsertMode { // Parse insertion template - insert, err := insertion.NewInsert(string(inputText)) + insert, err := NewInsert(string(inputText)) if err != nil { return fmt.Errorf("failed to parse insertion template %q: %w", inputPath, err) } diff --git a/src/internal/evaluation/renderFile_test.go b/src/internal/evaluation/renderFile_test.go index 9e3dd78..42eb4c0 100644 --- a/src/internal/evaluation/renderFile_test.go +++ b/src/internal/evaluation/renderFile_test.go @@ -23,55 +23,55 @@ func TestRenderFile(t *testing.T) { fixtures := []struct { Name string Input string - Render bool + Mode RenderMode Expected string Error string }{ { Name: "plain text", - Render: true, + Mode: TemplateMode, Input: "abc\ndef", Expected: "abc\ndef", }, { Name: "variable with whitespace trimming", - Render: true, + Mode: TemplateMode, Input: "abc\n{{- .VAR1 -}}\ndef", Expected: "abcvalue1def", }, { Name: "if true", - Render: true, + Mode: TemplateMode, Input: "abc\n{{if .TRUE_VAR}}def\n{{end}}ghi", Expected: "abc\ndef\nghi", }, { Name: "if false", - Render: true, + Mode: TemplateMode, Input: "abc\n{{if .UNDEFINED_VAR}}def\n{{end}}ghi", Expected: "abc\nghi", }, { Name: "with sprig func", - Render: true, + Mode: TemplateMode, Input: "{{.VAR1 | upper}}", Expected: "VALUE1", }, { Name: "replacements", - Render: true, + Mode: TemplateMode, Input: "abcprojektdef {{.VAR1}} ABC_PROJEKT_DEF", Expected: "abcmyprojectdef value1 ABC_MYPROJECT_DEF", }, { Name: "variable without rendering", - Render: false, + Mode: CopyMode, Input: "abc\n{{- .UNDEFINED_VAR -}}\ndef", Expected: "abc\n{{- .UNDEFINED_VAR -}}\ndef", }, { Name: "replacements without rendering", - Render: false, + Mode: CopyMode, Input: "abcprojektdef ABC_PROJEKT_DEF", Expected: "abcprojektdef ABC_PROJEKT_DEF", }, @@ -83,7 +83,7 @@ func TestRenderFile(t *testing.T) { outputFile := getTempFile() defer deleteFile(inputFile) defer deleteFile(outputFile) - err := renderFile(context, inputFile, outputFile, f.Render) + err := renderFile(context, inputFile, outputFile, f.Mode) actual := readFile(outputFile) if f.Error != "" {