diff --git a/internal/format/asciidoc_document.go b/internal/format/asciidoc_document.go index f003b3a..7625283 100644 --- a/internal/format/asciidoc_document.go +++ b/internal/format/asciidoc_document.go @@ -15,8 +15,8 @@ import ( gotemplate "text/template" "github.com/terraform-docs/terraform-docs/internal/print" - "github.com/terraform-docs/terraform-docs/internal/template" "github.com/terraform-docs/terraform-docs/internal/terraform" + "github.com/terraform-docs/terraform-docs/template" ) //go:embed templates/asciidoc_document*.tmpl diff --git a/internal/format/asciidoc_table.go b/internal/format/asciidoc_table.go index 8678a4b..26d1dec 100644 --- a/internal/format/asciidoc_table.go +++ b/internal/format/asciidoc_table.go @@ -15,8 +15,8 @@ import ( gotemplate "text/template" "github.com/terraform-docs/terraform-docs/internal/print" - "github.com/terraform-docs/terraform-docs/internal/template" "github.com/terraform-docs/terraform-docs/internal/terraform" + "github.com/terraform-docs/terraform-docs/template" ) //go:embed templates/asciidoc_table*.tmpl diff --git a/internal/format/markdown_document.go b/internal/format/markdown_document.go index 9c32443..4f49a75 100644 --- a/internal/format/markdown_document.go +++ b/internal/format/markdown_document.go @@ -15,8 +15,8 @@ import ( gotemplate "text/template" "github.com/terraform-docs/terraform-docs/internal/print" - "github.com/terraform-docs/terraform-docs/internal/template" "github.com/terraform-docs/terraform-docs/internal/terraform" + "github.com/terraform-docs/terraform-docs/template" ) //go:embed templates/markdown_document*.tmpl diff --git a/internal/format/markdown_table.go b/internal/format/markdown_table.go index 3758389..0baca66 100644 --- a/internal/format/markdown_table.go +++ b/internal/format/markdown_table.go @@ -15,8 +15,8 @@ import ( gotemplate "text/template" "github.com/terraform-docs/terraform-docs/internal/print" - "github.com/terraform-docs/terraform-docs/internal/template" "github.com/terraform-docs/terraform-docs/internal/terraform" + "github.com/terraform-docs/terraform-docs/template" ) //go:embed templates/markdown_table*.tmpl diff --git a/internal/format/pretty.go b/internal/format/pretty.go index e36b0c6..b6dc634 100644 --- a/internal/format/pretty.go +++ b/internal/format/pretty.go @@ -17,8 +17,8 @@ import ( gotemplate "text/template" "github.com/terraform-docs/terraform-docs/internal/print" - "github.com/terraform-docs/terraform-docs/internal/template" "github.com/terraform-docs/terraform-docs/internal/terraform" + "github.com/terraform-docs/terraform-docs/template" ) //go:embed templates/pretty.tmpl diff --git a/internal/format/tfvars_hcl.go b/internal/format/tfvars_hcl.go index 52b7445..da73c82 100644 --- a/internal/format/tfvars_hcl.go +++ b/internal/format/tfvars_hcl.go @@ -17,9 +17,9 @@ import ( gotemplate "text/template" "github.com/terraform-docs/terraform-docs/internal/print" - "github.com/terraform-docs/terraform-docs/internal/template" "github.com/terraform-docs/terraform-docs/internal/terraform" "github.com/terraform-docs/terraform-docs/internal/types" + "github.com/terraform-docs/terraform-docs/template" ) //go:embed templates/tfvars_hcl.tmpl diff --git a/internal/format/util.go b/internal/format/util.go index 991e880..9bcd7fc 100644 --- a/internal/format/util.go +++ b/internal/format/util.go @@ -17,7 +17,7 @@ import ( "regexp" "strings" - "github.com/terraform-docs/terraform-docs/internal/template" + "github.com/terraform-docs/terraform-docs/template" ) // sanitize cleans a Markdown document to soothe linters. diff --git a/internal/template/doc.go b/internal/template/doc.go deleted file mode 100644 index d8230c6..0000000 --- a/internal/template/doc.go +++ /dev/null @@ -1,12 +0,0 @@ -/* -Copyright 2021 The terraform-docs Authors. - -Licensed under the MIT license (the "License"); you may not -use this file except in compliance with the License. - -You may obtain a copy of the License at the LICENSE file in -the root directory of this source tree. -*/ - -// Package template provides templating functionality -package template diff --git a/internal/template/template.go b/internal/template/template.go deleted file mode 100644 index 17b9beb..0000000 --- a/internal/template/template.go +++ /dev/null @@ -1,89 +0,0 @@ -/* -Copyright 2021 The terraform-docs Authors. - -Licensed under the MIT license (the "License"); you may not -use this file except in compliance with the License. - -You may obtain a copy of the License at the LICENSE file in -the root directory of this source tree. -*/ - -package template - -import ( - gotemplate "text/template" - - templatesdk "github.com/terraform-docs/plugin-sdk/template" - "github.com/terraform-docs/terraform-docs/internal/print" - "github.com/terraform-docs/terraform-docs/internal/terraform" - "github.com/terraform-docs/terraform-docs/internal/types" -) - -// Item represents a named templated which can reference -// other named templated too. -type Item struct { - Name string - Text string -} - -// Template represents a new Template with given name and content -// to be rendered with provided settings with use of built-in and -// custom functions. -type Template struct { - engine *templatesdk.Template - settings *print.Settings -} - -// New returns new instance of Template. -func New(settings *print.Settings, items ...*Item) *Template { - ii := []*templatesdk.Item{} - for _, v := range items { - ii = append(ii, &templatesdk.Item{Name: v.Name, Text: v.Text}) - } - - engine := templatesdk.New(settings.Convert(), ii...) - engine.CustomFunc(gotemplate.FuncMap{ - "tostring": func(s types.String) string { - return string(s) - }, - "sanitizeSection": func(s string) string { - return sanitizeSection(s, settings) - }, - "sanitizeDoc": func(s string) string { - return sanitizeDocument(s, settings) - }, - "sanitizeMarkdownTbl": func(s string) string { - return sanitizeMarkdownTable(s, settings) - }, - "sanitizeAsciidocTbl": func(s string) string { - return sanitizeAsciidocTable(s, settings) - }, - "anchorNameMarkdown": func(s string, t string) string { - return createAnchorMarkdown(s, t, settings) - }, - "anchorNameAsciidoc": func(s string, t string) string { - return createAnchorAsciidoc(s, t, settings) - }, - }) - - return &Template{ - engine: engine, - settings: settings, - } -} - -// Funcs return available template out of the box and custom functions. -func (t Template) Funcs() gotemplate.FuncMap { - return t.engine.Funcs() -} - -// CustomFunc adds new custom functions to the template -// if functions with the same names didn't exist. -func (t Template) CustomFunc(funcs gotemplate.FuncMap) { - t.engine.CustomFunc(funcs) -} - -// Render template with given Module struct. -func (t Template) Render(name string, module *terraform.Module) (string, error) { - return t.engine.Render(name, module) -} diff --git a/internal/template/anchor.go b/template/anchor.go similarity index 65% rename from internal/template/anchor.go rename to template/anchor.go index e07e11a..74163c6 100644 --- a/internal/template/anchor.go +++ b/template/anchor.go @@ -16,13 +16,13 @@ import ( "github.com/terraform-docs/terraform-docs/internal/print" ) -// createAnchorMarkdown -func createAnchorMarkdown(t string, s string, settings *print.Settings) string { - sanitizedName := sanitizeName(s, settings) +// CreateAnchorMarkdown creates HTML anchor for Markdown format. +func CreateAnchorMarkdown(t string, s string, settings *print.Settings) string { + sanitizedName := SanitizeName(s, settings) if settings.ShowAnchor { anchorName := fmt.Sprintf("%s_%s", t, s) - sanitizedAnchorName := sanitizeName(anchorName, settings) + sanitizedAnchorName := SanitizeName(anchorName, settings) // the link is purposely not sanitized as this breaks markdown formatting return fmt.Sprintf(" [%s](#%s)", anchorName, sanitizedName, sanitizedAnchorName) } @@ -30,13 +30,13 @@ func createAnchorMarkdown(t string, s string, settings *print.Settings) string { return sanitizedName } -// createAnchorAsciidoc -func createAnchorAsciidoc(t string, s string, settings *print.Settings) string { - sanitizedName := sanitizeName(s, settings) +// CreateAnchorAsciidoc creates HTML anchor for AsciiDoc format. +func CreateAnchorAsciidoc(t string, s string, settings *print.Settings) string { + sanitizedName := SanitizeName(s, settings) if settings.ShowAnchor { anchorName := fmt.Sprintf("%s_%s", t, s) - sanitizedAnchorName := sanitizeName(anchorName, settings) + sanitizedAnchorName := SanitizeName(anchorName, settings) return fmt.Sprintf("[[%s]] <<%s,%s>>", sanitizedAnchorName, sanitizedAnchorName, sanitizedName) } diff --git a/internal/template/anchor_test.go b/template/anchor_test.go similarity index 95% rename from internal/template/anchor_test.go rename to template/anchor_test.go index fbfb7ab..418d5c8 100644 --- a/internal/template/anchor_test.go +++ b/template/anchor_test.go @@ -62,7 +62,7 @@ func TestAnchorMarkdown(t *testing.T) { ShowAnchor: tt.anchor, EscapeCharacters: tt.escape, } - actual := createAnchorMarkdown(tt.typeSection, tt.name, settings) + actual := CreateAnchorMarkdown(tt.typeSection, tt.name, settings) assert.Equal(tt.expected, actual) }) @@ -113,7 +113,7 @@ func TestAnchorAsciidoc(t *testing.T) { ShowAnchor: tt.anchor, EscapeCharacters: tt.escape, } - actual := createAnchorAsciidoc(tt.typeSection, tt.name, settings) + actual := CreateAnchorAsciidoc(tt.typeSection, tt.name, settings) assert.Equal(tt.expected, actual) }) diff --git a/template/doc.go b/template/doc.go new file mode 100644 index 0000000..a39f8bf --- /dev/null +++ b/template/doc.go @@ -0,0 +1,53 @@ +/* +Copyright 2021 The terraform-docs Authors. + +Licensed under the MIT license (the "License"); you may not +use this file except in compliance with the License. + +You may obtain a copy of the License at the LICENSE file in +the root directory of this source tree. +*/ + +// Package template provides templating functionality. +// +// Usage +// +// import ( +// "fmt" +// gotemplate "text/template" +// +// "github.com/terraform-docs/terraform-docs/internal/print" +// "github.com/terraform-docs/terraform-docs/internal/terraform" +// "github.com/terraform-docs/terraform-docs/template" +// ) +// +// const mainTpl =` +// {{- if .Settings.ShowHeader -}} +// {{- with .Module.Header -}} +// {{ colorize "\033[90m" . }} +// {{ end -}} +// {{- printf "\n\n" -}} +// {{ end -}}` +// +// func render(settings *print.Settings, module *terraform.Module) (string, error) { +// tt := template.New(settings, &template.Item{ +// Name: "main", +// Text: mainTpl, +// }) +// +// tt := template.New(settings, items...) +// tt.CustomFunc(gotemplate.FuncMap{ +// "colorize": func(c string, s string) string { +// r := "\033[0m" +// if !settings.ShowColor { +// c = "" +// r = "" +// } +// return fmt.Sprintf("%s%s%s", c, s, r) +// }, +// }) +// +// return tt.Render("main", module) +// } +// +package template diff --git a/internal/template/sanitizer.go b/template/sanitizer.go similarity index 80% rename from internal/template/sanitizer.go rename to template/sanitizer.go index 7bbbdb1..9ab836f 100644 --- a/internal/template/sanitizer.go +++ b/template/sanitizer.go @@ -22,8 +22,8 @@ import ( "github.com/terraform-docs/terraform-docs/internal/print" ) -// sanitizeName escapes underscore character which have special meaning in Markdown. -func sanitizeName(name string, settings *print.Settings) string { +// SanitizeName escapes underscore character which have special meaning in Markdown. +func SanitizeName(name string, settings *print.Settings) string { if settings.EscapeCharacters { // Escape underscore name = strings.ReplaceAll(name, "_", "\\_") @@ -31,13 +31,13 @@ func sanitizeName(name string, settings *print.Settings) string { return name } -// sanitizeSection converts passed 'string' to suitable Markdown or AsciiDoc +// SanitizeSection converts passed 'string' to suitable Markdown or AsciiDoc // representation for a document. (including line-break, illegal characters, // code blocks etc). This is in particular being used for header and footer. // -// IMPORTANT: sanitizeSection will never change the line-endings and preserve +// IMPORTANT: SanitizeSection will never change the line-endings and preserve // them as they are provided by the users. -func sanitizeSection(s string, settings *print.Settings) string { +func SanitizeSection(s string, settings *print.Settings) string { if s == "" { return "n/a" } @@ -45,9 +45,9 @@ func sanitizeSection(s string, settings *print.Settings) string { s, "```", func(segment string, first bool, last bool) string { - segment = escapeIllegalCharacters(segment, settings, false) - segment = convertMultiLineText(segment, false, true, settings.ShowHTML) - segment = normalizeURLs(segment, settings) + segment = EscapeCharacters(segment, settings, false) + segment = ConvertMultiLineText(segment, false, true, settings.ShowHTML) + segment = NormalizeURLs(segment, settings) return segment }, func(segment string, first bool, last bool) string { @@ -71,10 +71,10 @@ func sanitizeSection(s string, settings *print.Settings) string { return result } -// sanitizeDocument converts passed 'string' to suitable Markdown or AsciiDoc +// SanitizeDocument converts passed 'string' to suitable Markdown or AsciiDoc // representation for a document. (including line-break, illegal characters, // code blocks etc) -func sanitizeDocument(s string, settings *print.Settings) string { +func SanitizeDocument(s string, settings *print.Settings) string { if s == "" { return "n/a" } @@ -82,9 +82,9 @@ func sanitizeDocument(s string, settings *print.Settings) string { s, "```", func(segment string, first bool, last bool) string { - segment = escapeIllegalCharacters(segment, settings, false) - segment = convertMultiLineText(segment, false, false, settings.ShowHTML) - segment = normalizeURLs(segment, settings) + segment = EscapeCharacters(segment, settings, false) + segment = ConvertMultiLineText(segment, false, false, settings.ShowHTML) + segment = NormalizeURLs(segment, settings) return segment }, func(segment string, first bool, last bool) string { @@ -99,9 +99,9 @@ func sanitizeDocument(s string, settings *print.Settings) string { return result } -// sanitizeMarkdownTable converts passed 'string' to suitable Markdown representation +// SanitizeMarkdownTable converts passed 'string' to suitable Markdown representation // for a table. (including line-break, illegal characters, code blocks etc) -func sanitizeMarkdownTable(s string, settings *print.Settings) string { +func SanitizeMarkdownTable(s string, settings *print.Settings) string { if s == "" { return "n/a" } @@ -109,9 +109,9 @@ func sanitizeMarkdownTable(s string, settings *print.Settings) string { s, "```", func(segment string, first bool, last bool) string { - segment = escapeIllegalCharacters(segment, settings, true) - segment = convertMultiLineText(segment, true, false, settings.ShowHTML) - segment = normalizeURLs(segment, settings) + segment = EscapeCharacters(segment, settings, true) + segment = ConvertMultiLineText(segment, true, false, settings.ShowHTML) + segment = NormalizeURLs(segment, settings) return segment }, func(segment string, first bool, last bool) string { @@ -133,7 +133,7 @@ func sanitizeMarkdownTable(s string, settings *print.Settings) string { codeend = codeend[:3] } - segment = convertOneLineCodeBlock(segment) + segment = ConvertOneLineCodeBlock(segment) } segment = strings.ReplaceAll(segment, "\n", linebreak) @@ -145,9 +145,9 @@ func sanitizeMarkdownTable(s string, settings *print.Settings) string { return result } -// sanitizeAsciidocTable converts passed 'string' to suitable AsciiDoc representation +// SanitizeAsciidocTable converts passed 'string' to suitable AsciiDoc representation // for a table. (including line-break, illegal characters, code blocks etc) -func sanitizeAsciidocTable(s string, settings *print.Settings) string { +func SanitizeAsciidocTable(s string, settings *print.Settings) string { if s == "" { return "n/a" } @@ -155,8 +155,8 @@ func sanitizeAsciidocTable(s string, settings *print.Settings) string { s, "```", func(segment string, first bool, last bool) string { - segment = escapeIllegalCharacters(segment, settings, true) - segment = normalizeURLs(segment, settings) + segment = EscapeCharacters(segment, settings, true) + segment = NormalizeURLs(segment, settings) return segment }, func(segment string, first bool, last bool) string { @@ -168,8 +168,8 @@ func sanitizeAsciidocTable(s string, settings *print.Settings) string { return result } -// convertMultiLineText converts a multi-line text into a suitable Markdown representation. -func convertMultiLineText(s string, isTable bool, isHeader bool, showHTML bool) string { +// ConvertMultiLineText converts a multi-line text into a suitable Markdown representation. +func ConvertMultiLineText(s string, isTable bool, isHeader bool, showHTML bool) string { if isTable { s = strings.TrimSpace(s) } @@ -204,9 +204,9 @@ func convertMultiLineText(s string, isTable bool, isHeader bool, showHTML bool) return strings.ReplaceAll(s, "\n", linebreak) } -// convertOneLineCodeBlock converts a multi-line code block into a one-liner. +// ConvertOneLineCodeBlock converts a multi-line code block into a one-liner. // Line breaks are replaced with single space. -func convertOneLineCodeBlock(s string) string { +func ConvertOneLineCodeBlock(s string) string { splitted := strings.Split(s, "\n") result := []string{} for _, segment := range splitted { @@ -220,8 +220,8 @@ func convertOneLineCodeBlock(s string) string { return strings.Join(result, " ") } -// escapeIllegalCharacters escapes characters which have special meaning in Markdown into their corresponding literal. -func escapeIllegalCharacters(s string, settings *print.Settings, escapePipe bool) string { +// EscapeCharacters escapes characters which have special meaning in Markdown into their corresponding literal. +func EscapeCharacters(s string, settings *print.Settings, escapePipe bool) string { // Escape pipe (only for 'markdown table' or 'asciidoc table') if escapePipe { s = processSegments( @@ -285,10 +285,10 @@ func escapeIllegalCharacters(s string, settings *print.Settings, escapePipe bool return s } -// normalizeURLs runs after escape function and normalizes URL back +// NormalizeURLs runs after escape function and normalizes URL back // to the original state. For example any underscore in the URL which // got escaped by 'EscapeIllegalCharacters' will be reverted back. -func normalizeURLs(s string, settings *print.Settings) string { +func NormalizeURLs(s string, settings *print.Settings) string { if settings.EscapeCharacters { if urls := xurls.Strict().FindAllString(s, -1); len(urls) > 0 { for _, url := range urls { diff --git a/internal/template/sanitizer_test.go b/template/sanitizer_test.go similarity index 97% rename from internal/template/sanitizer_test.go rename to template/sanitizer_test.go index bbd1b04..8ca9749 100644 --- a/internal/template/sanitizer_test.go +++ b/template/sanitizer_test.go @@ -100,7 +100,7 @@ func TestSanitizeName(t *testing.T) { settings := &print.Settings{ EscapeCharacters: tt.escape, } - actual := sanitizeName(tt.input, settings) + actual := SanitizeName(tt.input, settings) assert.Equal(tt.expected, actual) }) @@ -139,7 +139,7 @@ func TestSanitizeSection(t *testing.T) { bytes, err := ioutil.ReadFile(filepath.Join("testdata", "section", tt.filename+".golden")) assert.Nil(err) - actual := sanitizeSection(string(bytes), settings) + actual := SanitizeSection(string(bytes), settings) expected, err := ioutil.ReadFile(filepath.Join("testdata", "section", tt.filename+".expected")) assert.Nil(err) @@ -181,7 +181,7 @@ func TestSanitizeDocument(t *testing.T) { bytes, err := ioutil.ReadFile(filepath.Join("testdata", "document", tt.filename+".golden")) assert.Nil(err) - actual := sanitizeDocument(string(bytes), settings) + actual := SanitizeDocument(string(bytes), settings) expected, err := ioutil.ReadFile(filepath.Join("testdata", "document", tt.filename+".expected")) assert.Nil(err) @@ -246,7 +246,7 @@ func TestSanitizeMarkdownTable(t *testing.T) { bytes, err := ioutil.ReadFile(filepath.Join("testdata", "table", tt.filename+".golden")) assert.Nil(err) - actual := sanitizeMarkdownTable(string(bytes), settings) + actual := SanitizeMarkdownTable(string(bytes), settings) expected, err := ioutil.ReadFile(filepath.Join("testdata", "table", tt.expected+".markdown.expected")) assert.Nil(err) @@ -288,7 +288,7 @@ func TestSanitizeAsciidocTable(t *testing.T) { bytes, err := ioutil.ReadFile(filepath.Join("testdata", "table", tt.filename+".golden")) assert.Nil(err) - actual := sanitizeAsciidocTable(string(bytes), settings) + actual := SanitizeAsciidocTable(string(bytes), settings) expected, err := ioutil.ReadFile(filepath.Join("testdata", "table", tt.filename+".asciidoc.expected")) assert.Nil(err) @@ -420,7 +420,7 @@ func TestConvertMultiLineText(t *testing.T) { bytes, err := ioutil.ReadFile(path) assert.Nil(err) - actual := convertMultiLineText(string(bytes), tt.isTable, false, tt.showHTML) + actual := ConvertMultiLineText(string(bytes), tt.isTable, false, tt.showHTML) assert.Equal(tt.expected, actual) }) } @@ -581,7 +581,7 @@ func TestEscapeIllegalCharacters(t *testing.T) { settings := &print.Settings{ EscapeCharacters: tt.escapeChars, } - actual := escapeIllegalCharacters(tt.input, settings, tt.escapePipe) + actual := EscapeCharacters(tt.input, settings, tt.escapePipe) assert.Equal(tt.expected, actual) }) @@ -638,7 +638,7 @@ func TestNormalizeURLs(t *testing.T) { settings := &print.Settings{ EscapeCharacters: tt.escape, } - actual := normalizeURLs(tt.input, settings) + actual := NormalizeURLs(tt.input, settings) assert.Equal(tt.expected, actual) }) diff --git a/template/template.go b/template/template.go new file mode 100644 index 0000000..00b4bfb --- /dev/null +++ b/template/template.go @@ -0,0 +1,244 @@ +/* +Copyright 2021 The terraform-docs Authors. + +Licensed under the MIT license (the "License"); you may not +use this file except in compliance with the License. + +You may obtain a copy of the License at the LICENSE file in +the root directory of this source tree. +*/ + +package template + +import ( + "bytes" + "fmt" + "strings" + gotemplate "text/template" + + "github.com/terraform-docs/terraform-docs/internal/print" + "github.com/terraform-docs/terraform-docs/internal/terraform" + "github.com/terraform-docs/terraform-docs/internal/types" +) + +// Item represents a named templated which can reference other named templated too. +type Item struct { + Name string + Text string +} + +// Template represents a new Template with given name and content to be rendered +// with provided settings with use of built-in and custom functions. +type Template struct { + items []*Item + settings *print.Settings + + funcMap gotemplate.FuncMap + customFunc gotemplate.FuncMap +} + +// New returns new instance of Template. +func New(settings *print.Settings, items ...*Item) *Template { + return &Template{ + items: items, + settings: settings, + funcMap: builtinFuncs(settings), + customFunc: make(gotemplate.FuncMap), + } +} + +// Funcs return available template out of the box and custom functions. +func (t Template) Funcs() gotemplate.FuncMap { + return t.funcMap +} + +// CustomFunc adds new custom functions to the template if functions with the same +// names didn't exist. +func (t *Template) CustomFunc(funcs gotemplate.FuncMap) { + for name, fn := range funcs { + if _, found := t.customFunc[name]; !found { + t.customFunc[name] = fn + } + } + t.applyCustomFunc() +} + +// applyCustomFunc is re-adding the custom functions to list of available functions. +func (t *Template) applyCustomFunc() { + for name, fn := range t.customFunc { + if _, found := t.funcMap[name]; !found { + t.funcMap[name] = fn + } + } +} + +// Render template with given Module struct. +func (t *Template) Render(name string, module *terraform.Module) (string, error) { + if len(t.items) < 1 { + return "", fmt.Errorf("base template not found") + } + + item := t.findByName(name) + if item == nil { + return "", fmt.Errorf("%s template not found", name) + } + + var buffer bytes.Buffer + + tmpl := gotemplate.New(item.Name) + tmpl.Funcs(t.funcMap) + gotemplate.Must(tmpl.Parse(normalize(item.Text))) + + for _, ii := range t.items { + tt := tmpl.New(ii.Name) + tt.Funcs(t.funcMap) + gotemplate.Must(tt.Parse(normalize(ii.Text))) + } + + if err := tmpl.ExecuteTemplate(&buffer, item.Name, struct { + Module *terraform.Module + Settings *print.Settings + }{ + Module: module, + Settings: t.settings, + }); err != nil { + return "", err + } + + return buffer.String(), nil +} + +func (t *Template) findByName(name string) *Item { + if name == "" { + if len(t.items) > 0 { + return t.items[0] + } + return nil + } + for _, i := range t.items { + if i.Name == name { + return i + } + } + return nil +} + +func builtinFuncs(settings *print.Settings) gotemplate.FuncMap { // nolint:gocyclo + return gotemplate.FuncMap{ + "default": func(d string, s string) string { + if s != "" { + return s + } + return d + }, + "indent": func(l int, char string) string { + return GenerateIndentation(l, char, settings) + }, + "name": func(n string) string { + return SanitizeName(n, settings) + }, + "ternary": func(condition interface{}, trueValue string, falseValue string) string { + var c bool + switch x := fmt.Sprintf("%T", condition); x { + case "string": + c = condition.(string) != "" + case "int": + c = condition.(int) != 0 + case "bool": + c = condition.(bool) + } + if c { + return trueValue + } + return falseValue + }, + "tostring": func(s types.String) string { + return string(s) + }, + + // trim + "trim": func(cut string, s string) string { + if s != "" { + return strings.Trim(s, cut) + } + return s + }, + "trimLeft": func(cut string, s string) string { + if s != "" { + return strings.TrimLeft(s, cut) + } + return s + }, + "trimRight": func(cut string, s string) string { + if s != "" { + return strings.TrimRight(s, cut) + } + return s + }, + "trimPrefix": func(prefix string, s string) string { + if s != "" { + return strings.TrimPrefix(s, prefix) + } + return s + }, + "trimSuffix": func(suffix string, s string) string { + if s != "" { + return strings.TrimSuffix(s, suffix) + } + return s + }, + + // sanitize + "sanitizeSection": func(s string) string { + return SanitizeSection(s, settings) + }, + "sanitizeDoc": func(s string) string { + return SanitizeDocument(s, settings) + }, + "sanitizeMarkdownTbl": func(s string) string { + return SanitizeMarkdownTable(s, settings) + }, + "sanitizeAsciidocTbl": func(s string) string { + return SanitizeAsciidocTable(s, settings) + }, + + // anchors + "anchorNameMarkdown": func(s string, t string) string { + return CreateAnchorMarkdown(s, t, settings) + }, + "anchorNameAsciidoc": func(s string, t string) string { + return CreateAnchorAsciidoc(s, t, settings) + }, + } +} + +// normalize the template and remove any space from all the lines. This makes +// it possible to have a indented, human-readable template which doesn't affect +// the rendering of them. +func normalize(s string) string { + segments := strings.Split(s, "\n") + buffer := bytes.NewBufferString("") + for _, segment := range segments { + buffer.WriteString(strings.TrimSpace(segment)) // nolint:gosec + buffer.WriteString("\n") // nolint:gosec + } + return buffer.String() +} + +// GenerateIndentation generates indentation of Markdown and AsciiDoc headers +// with base level of provided 'settings.IndentLevel' plus any extra level needed +// for subsection (e.g. 'Required Inputs' which is a subsection of 'Inputs' section) +func GenerateIndentation(extra int, char string, settings *print.Settings) string { + if char == "" { + return "" + } + var base = settings.IndentLevel + if base < 1 || base > 5 { + base = 2 + } + var indent string + for i := 0; i < base+extra; i++ { + indent += char + } + return indent +} diff --git a/internal/template/template_test.go b/template/template_test.go similarity index 92% rename from internal/template/template_test.go rename to template/template_test.go index 64c3eef..147d8ff 100644 --- a/internal/template/template_test.go +++ b/template/template_test.go @@ -420,8 +420,7 @@ func TestBuiltinFunc(t *testing.T) { assert := assert.New(t) settings := print.DefaultSettings() settings.EscapeCharacters = tt.escape - tmpl := New(settings) - funcs := tmpl.Funcs() + funcs := builtinFuncs(settings) fn, ok := funcs[tt.funcName] assert.Truef(ok, "function %s is not defined", tt.funcName) @@ -462,3 +461,51 @@ func TestBuiltinFunc(t *testing.T) { }) } } + +func TestGenerateIndentation(t *testing.T) { + tests := []struct { + name string + base int + extra int + expected string + }{ + { + name: "generate indentation", + base: 2, + extra: 1, + expected: "###", + }, + { + name: "generate indentation", + extra: 2, + expected: "####", + }, + { + name: "generate indentation", + base: 4, + extra: 3, + expected: "#######", + }, + { + name: "generate indentation", + base: 0, + extra: 0, + expected: "##", + }, + { + name: "generate indentation", + base: 6, + extra: 1, + expected: "###", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert := assert.New(t) + settings := &print.Settings{IndentLevel: tt.base} + actual := GenerateIndentation(tt.extra, "#", settings) + + assert.Equal(tt.expected, actual) + }) + } +} diff --git a/internal/template/testdata/document/codeblock.expected b/template/testdata/document/codeblock.expected similarity index 100% rename from internal/template/testdata/document/codeblock.expected rename to template/testdata/document/codeblock.expected diff --git a/internal/template/testdata/document/codeblock.golden b/template/testdata/document/codeblock.golden similarity index 100% rename from internal/template/testdata/document/codeblock.golden rename to template/testdata/document/codeblock.golden diff --git a/internal/template/testdata/document/complex.expected b/template/testdata/document/complex.expected similarity index 100% rename from internal/template/testdata/document/complex.expected rename to template/testdata/document/complex.expected diff --git a/internal/template/testdata/document/complex.golden b/template/testdata/document/complex.golden similarity index 100% rename from internal/template/testdata/document/complex.golden rename to template/testdata/document/complex.golden diff --git a/internal/template/testdata/document/empty.expected b/template/testdata/document/empty.expected similarity index 100% rename from internal/template/testdata/document/empty.expected rename to template/testdata/document/empty.expected diff --git a/internal/template/testdata/document/empty.golden b/template/testdata/document/empty.golden similarity index 100% rename from internal/template/testdata/document/empty.golden rename to template/testdata/document/empty.golden diff --git a/internal/template/testdata/multiline/indentations.golden b/template/testdata/multiline/indentations.golden similarity index 100% rename from internal/template/testdata/multiline/indentations.golden rename to template/testdata/multiline/indentations.golden diff --git a/internal/template/testdata/multiline/list.golden b/template/testdata/multiline/list.golden similarity index 100% rename from internal/template/testdata/multiline/list.golden rename to template/testdata/multiline/list.golden diff --git a/internal/template/testdata/multiline/newline-double.golden b/template/testdata/multiline/newline-double.golden similarity index 100% rename from internal/template/testdata/multiline/newline-double.golden rename to template/testdata/multiline/newline-double.golden diff --git a/internal/template/testdata/multiline/newline-single.golden b/template/testdata/multiline/newline-single.golden similarity index 100% rename from internal/template/testdata/multiline/newline-single.golden rename to template/testdata/multiline/newline-single.golden diff --git a/internal/template/testdata/multiline/paragraph.golden b/template/testdata/multiline/paragraph.golden similarity index 100% rename from internal/template/testdata/multiline/paragraph.golden rename to template/testdata/multiline/paragraph.golden diff --git a/internal/template/testdata/section/codeblock.expected b/template/testdata/section/codeblock.expected similarity index 100% rename from internal/template/testdata/section/codeblock.expected rename to template/testdata/section/codeblock.expected diff --git a/internal/template/testdata/section/codeblock.golden b/template/testdata/section/codeblock.golden similarity index 100% rename from internal/template/testdata/section/codeblock.golden rename to template/testdata/section/codeblock.golden diff --git a/internal/template/testdata/section/complex.expected b/template/testdata/section/complex.expected similarity index 100% rename from internal/template/testdata/section/complex.expected rename to template/testdata/section/complex.expected diff --git a/internal/template/testdata/section/complex.golden b/template/testdata/section/complex.golden similarity index 100% rename from internal/template/testdata/section/complex.golden rename to template/testdata/section/complex.golden diff --git a/internal/template/testdata/section/empty.expected b/template/testdata/section/empty.expected similarity index 100% rename from internal/template/testdata/section/empty.expected rename to template/testdata/section/empty.expected diff --git a/internal/template/testdata/section/empty.golden b/template/testdata/section/empty.golden similarity index 100% rename from internal/template/testdata/section/empty.golden rename to template/testdata/section/empty.golden diff --git a/internal/template/testdata/table/codeblock-html.markdown.expected b/template/testdata/table/codeblock-html.markdown.expected similarity index 100% rename from internal/template/testdata/table/codeblock-html.markdown.expected rename to template/testdata/table/codeblock-html.markdown.expected diff --git a/internal/template/testdata/table/codeblock-nohtml.markdown.expected b/template/testdata/table/codeblock-nohtml.markdown.expected similarity index 100% rename from internal/template/testdata/table/codeblock-nohtml.markdown.expected rename to template/testdata/table/codeblock-nohtml.markdown.expected diff --git a/internal/template/testdata/table/codeblock.asciidoc.expected b/template/testdata/table/codeblock.asciidoc.expected similarity index 100% rename from internal/template/testdata/table/codeblock.asciidoc.expected rename to template/testdata/table/codeblock.asciidoc.expected diff --git a/internal/template/testdata/table/codeblock.golden b/template/testdata/table/codeblock.golden similarity index 100% rename from internal/template/testdata/table/codeblock.golden rename to template/testdata/table/codeblock.golden diff --git a/internal/template/testdata/table/complex-html.markdown.expected b/template/testdata/table/complex-html.markdown.expected similarity index 100% rename from internal/template/testdata/table/complex-html.markdown.expected rename to template/testdata/table/complex-html.markdown.expected diff --git a/internal/template/testdata/table/complex-nohtml.markdown.expected b/template/testdata/table/complex-nohtml.markdown.expected similarity index 100% rename from internal/template/testdata/table/complex-nohtml.markdown.expected rename to template/testdata/table/complex-nohtml.markdown.expected diff --git a/internal/template/testdata/table/complex.asciidoc.expected b/template/testdata/table/complex.asciidoc.expected similarity index 100% rename from internal/template/testdata/table/complex.asciidoc.expected rename to template/testdata/table/complex.asciidoc.expected diff --git a/internal/template/testdata/table/complex.golden b/template/testdata/table/complex.golden similarity index 100% rename from internal/template/testdata/table/complex.golden rename to template/testdata/table/complex.golden diff --git a/internal/template/testdata/table/empty.asciidoc.expected b/template/testdata/table/empty.asciidoc.expected similarity index 100% rename from internal/template/testdata/table/empty.asciidoc.expected rename to template/testdata/table/empty.asciidoc.expected diff --git a/internal/template/testdata/table/empty.golden b/template/testdata/table/empty.golden similarity index 100% rename from internal/template/testdata/table/empty.golden rename to template/testdata/table/empty.golden diff --git a/internal/template/testdata/table/empty.markdown.expected b/template/testdata/table/empty.markdown.expected similarity index 100% rename from internal/template/testdata/table/empty.markdown.expected rename to template/testdata/table/empty.markdown.expected