From 6777364257db8de2fa4a596ce0c85a7bb7b1ffe7 Mon Sep 17 00:00:00 2001 From: Khosrow Moossavi Date: Mon, 10 May 2021 18:23:09 -0400 Subject: [PATCH] Customize content with individual sections output Generated content can be customized further away with `content` in configuration. If the `content` is empty the default orders of section is used. `content` is a Go template with following additional variables: - `{{ .Header }}` - `{{ .Footer }}` - `{{ .Inputs }}` - `{{ .Modules }}` - `{{ .Outputs }}` - `{{ .Providers }}` - `{{ .Requirements }}` - `{{ .Resources }}` ```yaml content: |- Any arbitrary text can be placed anywhere in the content {{ .Header }} and even in between sections {{ .Providers }} and they don't even need to be in the default order {{ .Outputs }} {{ .Inputs }} ``` These variables are the generated output of individual sections in the selected formatter. For example `{{ .Inputs }}` is Markdown Table representation of inputs when formatter is set to `markdown table` and AsciiDoc Document representation when formatter is set to `asciidoc document` and so on. Compatible formats for customized content are: - `asciidoc document` - `asciidoc table` - `markdown document` - `markdown table` Signed-off-by: Khosrow Moossavi --- docs/user-guide/configuration.md | 72 ++++++ examples/.terraform-docs.yml | 27 ++- go.mod | 2 +- go.sum | 4 +- internal/cli/config.go | 2 + internal/cli/run.go | 10 +- internal/format/asciidoc_document.go | 37 ++- internal/format/asciidoc_document_test.go | 8 +- internal/format/asciidoc_table.go | 37 ++- internal/format/asciidoc_table_test.go | 8 +- internal/format/json.go | 23 +- internal/format/json_test.go | 8 +- internal/format/markdown_document.go | 36 ++- internal/format/markdown_document_test.go | 8 +- internal/format/markdown_table.go | 36 ++- internal/format/markdown_table_test.go | 8 +- internal/format/pretty.go | 16 +- internal/format/pretty_test.go | 8 +- .../format/templates/asciidoc_document.tmpl | 180 +------------- .../templates/asciidoc_document_footer.tmpl | 6 + .../templates/asciidoc_document_header.tmpl | 6 + .../templates/asciidoc_document_inputs.tmpl | 71 ++++++ .../templates/asciidoc_document_modules.tmpl | 16 ++ .../templates/asciidoc_document_outputs.tmpl | 23 ++ .../asciidoc_document_providers.tmpl | 12 + .../asciidoc_document_requirements.tmpl | 12 + .../asciidoc_document_resources.tmpl | 19 ++ internal/format/templates/asciidoc_table.tmpl | 133 +--------- .../templates/asciidoc_table_footer.tmpl | 6 + .../templates/asciidoc_table_header.tmpl | 6 + .../templates/asciidoc_table_inputs.tmpl | 21 ++ .../templates/asciidoc_table_modules.tmpl | 14 ++ .../templates/asciidoc_table_outputs.tmpl | 21 ++ .../templates/asciidoc_table_providers.tmpl | 14 ++ .../asciidoc_table_requirements.tmpl | 14 ++ .../templates/asciidoc_table_resources.tmpl | 22 ++ .../format/templates/markdown_document.tmpl | 181 +------------- .../templates/markdown_document_footer.tmpl | 6 + .../templates/markdown_document_header.tmpl | 6 + .../templates/markdown_document_inputs.tmpl | 71 ++++++ .../templates/markdown_document_modules.tmpl | 17 ++ .../templates/markdown_document_outputs.tmpl | 23 ++ .../markdown_document_providers.tmpl | 12 + .../markdown_document_requirements.tmpl | 12 + .../markdown_document_resources.tmpl | 19 ++ internal/format/templates/markdown_table.tmpl | 129 +--------- .../templates/markdown_table_footer.tmpl | 6 + .../templates/markdown_table_header.tmpl | 6 + .../templates/markdown_table_inputs.tmpl | 27 +++ .../templates/markdown_table_modules.tmpl | 12 + .../templates/markdown_table_outputs.tmpl | 19 ++ .../templates/markdown_table_providers.tmpl | 12 + .../markdown_table_requirements.tmpl | 12 + .../templates/markdown_table_resources.tmpl | 20 ++ internal/format/tfvars_hcl.go | 19 +- internal/format/tfvars_hcl_test.go | 8 +- internal/format/tfvars_json.go | 20 +- internal/format/tfvars_json_test.go | 8 +- internal/format/toml.go | 22 +- internal/format/toml_test.go | 8 +- internal/format/util.go | 37 +++ internal/format/xml.go | 21 +- internal/format/xml_test.go | 8 +- internal/format/yaml.go | 21 +- internal/format/yaml_test.go | 8 +- internal/print/engine.go | 4 +- internal/print/generator.go | 185 ++++++++++++++ internal/print/generator_test.go | 229 ++++++++++++++++++ internal/template/template.go | 4 +- internal/template/template_test.go | 2 +- scripts/docs/generate.go | 8 +- 71 files changed, 1425 insertions(+), 723 deletions(-) create mode 100644 internal/format/templates/asciidoc_document_footer.tmpl create mode 100644 internal/format/templates/asciidoc_document_header.tmpl create mode 100644 internal/format/templates/asciidoc_document_inputs.tmpl create mode 100644 internal/format/templates/asciidoc_document_modules.tmpl create mode 100644 internal/format/templates/asciidoc_document_outputs.tmpl create mode 100644 internal/format/templates/asciidoc_document_providers.tmpl create mode 100644 internal/format/templates/asciidoc_document_requirements.tmpl create mode 100644 internal/format/templates/asciidoc_document_resources.tmpl create mode 100644 internal/format/templates/asciidoc_table_footer.tmpl create mode 100644 internal/format/templates/asciidoc_table_header.tmpl create mode 100644 internal/format/templates/asciidoc_table_inputs.tmpl create mode 100644 internal/format/templates/asciidoc_table_modules.tmpl create mode 100644 internal/format/templates/asciidoc_table_outputs.tmpl create mode 100644 internal/format/templates/asciidoc_table_providers.tmpl create mode 100644 internal/format/templates/asciidoc_table_requirements.tmpl create mode 100644 internal/format/templates/asciidoc_table_resources.tmpl create mode 100644 internal/format/templates/markdown_document_footer.tmpl create mode 100644 internal/format/templates/markdown_document_header.tmpl create mode 100644 internal/format/templates/markdown_document_inputs.tmpl create mode 100644 internal/format/templates/markdown_document_modules.tmpl create mode 100644 internal/format/templates/markdown_document_outputs.tmpl create mode 100644 internal/format/templates/markdown_document_providers.tmpl create mode 100644 internal/format/templates/markdown_document_requirements.tmpl create mode 100644 internal/format/templates/markdown_document_resources.tmpl create mode 100644 internal/format/templates/markdown_table_footer.tmpl create mode 100644 internal/format/templates/markdown_table_header.tmpl create mode 100644 internal/format/templates/markdown_table_inputs.tmpl create mode 100644 internal/format/templates/markdown_table_modules.tmpl create mode 100644 internal/format/templates/markdown_table_outputs.tmpl create mode 100644 internal/format/templates/markdown_table_providers.tmpl create mode 100644 internal/format/templates/markdown_table_requirements.tmpl create mode 100644 internal/format/templates/markdown_table_resources.tmpl create mode 100644 internal/print/generator.go create mode 100644 internal/print/generator_test.go diff --git a/docs/user-guide/configuration.md b/docs/user-guide/configuration.md index cdf18ab..09508c4 100644 --- a/docs/user-guide/configuration.md +++ b/docs/user-guide/configuration.md @@ -72,6 +72,8 @@ sections: hide-all: false # deprecated in v0.13.0 show-all: true # deprecated in v0.13.0 +content: "" + output: file: "" mode: inject @@ -192,6 +194,76 @@ The following options are supported and can be used for `sections.show` and **Note:** As of `v0.13.0`, `sections.hide-all` and `sections.show-all` are deprecated and removed in favor of explicit use of `sections.hide` and `sections.show`. +## Content + +Since `v0.14.0` + +Generated content can be customized further away with `content` in configuration. +If the `content` is empty the default orders of section is used. `content` is a +Go template with following additional variables: + +- `{{ .Header }}` +- `{{ .Footer }}` +- `{{ .Inputs }}` +- `{{ .Modules }}` +- `{{ .Outputs }}` +- `{{ .Providers }}` +- `{{ .Requirements }}` +- `{{ .Resources }}` + +```yaml +content: |- + Any arbitrary text can be placed anywhere in the content + + {{ .Header }} + + and even in between sections + + {{ .Providers }} + + and they don't even need to be in the default order + + {{ .Outputs }} + + {{ .Inputs }} +``` + +These variables are the generated output of individual sections in the selected +formatter. For example `{{ .Inputs }}` is Markdown Table representation of _inputs_ +when formatter is set to `markdown table` and AsciiDoc Document representation +when formatter is set to `asciidoc document` and so on. + +Compatible formats for customized content are: + +- `asciidoc document` +- `asciidoc table` +- `markdown document` +- `markdown table` + +**Note:** Sections visibility (i.e. `sections.show` and `sections.hide`) takes +precedence over the `content`. In the following example although `{{ .Providers }}` +is used it won't be rendered because `providers` is not set to be shown in +`sections.show`. + +```yaml +sections: + show: + - header + - inputs + - outputs + +content: |- + {{ .Header }} + + Some more information can go here. + + {{ .Providers }} + + {{ .Inputs }} + + {{ .Outputs }} +``` + ## Output Since `v0.12.0` diff --git a/examples/.terraform-docs.yml b/examples/.terraform-docs.yml index 2cc4017..8adca6c 100644 --- a/examples/.terraform-docs.yml +++ b/examples/.terraform-docs.yml @@ -1,12 +1,13 @@ -# # terraform-docs version constraints for execution -# # more information: https://terraform-docs.io/user-guide/configuration/#version +# # see: https://terraform-docs.io/user-guide/configuration/#version # version: ">= 0.10, < 0.12" +# see: https://terraform-docs.io/user-guide/configuration/#formatters formatter: markdown table header-from: doc.txt footer-from: footer.md +# see: https://terraform-docs.io/user-guide/configuration/#sections sections: show: - header @@ -15,6 +16,23 @@ sections: - modules - footer +# # see: https://terraform-docs.io/user-guide/configuration/#content +# content: |- +# Any arbitrary text can be placed anywhere in the content +# +# {{ .Header }} +# +# and even in between sections +# +# {{ .Providers }} +# +# and they don't even need to be in the default order +# +# {{ .Outputs }} +# +# {{ .Inputs }} + +# # see: https://terraform-docs.io/user-guide/configuration/#output # output: # file: README.md # mode: inject @@ -23,12 +41,13 @@ sections: # The template can be customized with aribitrary markdown content. # For example this can be shown before the actual content generated # by formatters. - +# # {{ .Content }} - +# # You can also show something after it! # +# see: https://terraform-docs.io/user-guide/configuration/#sort sort: enabled: true by: required diff --git a/go.mod b/go.mod index 74cafbd..c228d8c 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.7.1 github.com/stretchr/testify v1.7.0 - github.com/terraform-docs/plugin-sdk v0.3.0 + github.com/terraform-docs/plugin-sdk v0.3.1-0.20210512170044-49b620c0a2da github.com/terraform-docs/terraform-config-inspect v0.0.0-20210318143659-b932ca5358a6 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b honnef.co/go/tools v0.1.2 diff --git a/go.sum b/go.sum index 41d0382..11b59ce 100644 --- a/go.sum +++ b/go.sum @@ -237,8 +237,8 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/terraform-docs/plugin-sdk v0.3.0 h1:+2NxYMXhyDJ4fQHW5UYfDlF5Dpip2xMo7STSvBy+zok= -github.com/terraform-docs/plugin-sdk v0.3.0/go.mod h1:3G+0nZTeaMF1c5CZh8cOEYeNq0kUL6+DlQOVcxK7eCQ= +github.com/terraform-docs/plugin-sdk v0.3.1-0.20210512170044-49b620c0a2da h1:WJXjngYRi9rtvzFFKjYuiWpax9R6tEiAA9givVSA4tw= +github.com/terraform-docs/plugin-sdk v0.3.1-0.20210512170044-49b620c0a2da/go.mod h1:3G+0nZTeaMF1c5CZh8cOEYeNq0kUL6+DlQOVcxK7eCQ= github.com/terraform-docs/terraform-config-inspect v0.0.0-20210318143659-b932ca5358a6 h1:chOGKLaX5wNawU8rcF6HFJL+N5uU1Km8SiUQ/Ggwu2I= github.com/terraform-docs/terraform-config-inspect v0.0.0-20210318143659-b932ca5358a6/go.mod h1:GtanFwTsRRXScYHOMb5h4K18XQBFeS2tXat9/LrPtPc= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= diff --git a/internal/cli/config.go b/internal/cli/config.go index 9be9195..155b353 100644 --- a/internal/cli/config.go +++ b/internal/cli/config.go @@ -56,6 +56,7 @@ type Config struct { Version string `mapstructure:"version"` HeaderFrom string `mapstructure:"header-from"` FooterFrom string `mapstructure:"footer-from"` + Content string `mapstructure:"content"` Sections sections `mapstructure:"sections"` Output output `mapstructure:"output"` OutputValues outputvalues `mapstructure:"output-values"` @@ -74,6 +75,7 @@ func DefaultConfig() *Config { Version: "", HeaderFrom: "main.tf", FooterFrom: "", + Content: "", Sections: defaultSections(), Output: defaultOutput(), OutputValues: defaultOutputValues(), diff --git a/internal/cli/run.go b/internal/cli/run.go index 46f3be3..a7b4d15 100644 --- a/internal/cli/run.go +++ b/internal/cli/run.go @@ -123,7 +123,7 @@ func RunEFunc(config *Config) func(*cobra.Command, []string) error { return err } - printer, err := format.Factory(config.Formatter, settings) + formatter, err := format.Factory(config.Formatter, settings) if err != nil { plugins, perr := plugin.Discover() if perr != nil { @@ -145,10 +145,16 @@ func RunEFunc(config *Config) func(*cobra.Command, []string) error { return writeContent(config, content) } - content, err := printer.Print(module, settings) + generator, err := formatter.Generate(module) if err != nil { return err } + + content, err := generator.ExecuteTemplate(config.Content) + if err != nil { + return err + } + return writeContent(config, content) } } diff --git a/internal/format/asciidoc_document.go b/internal/format/asciidoc_document.go index 397d312..f003b3a 100644 --- a/internal/format/asciidoc_document.go +++ b/internal/format/asciidoc_document.go @@ -11,7 +11,7 @@ the root directory of this source tree. package format import ( - _ "embed" //nolint + "embed" gotemplate "text/template" "github.com/terraform-docs/terraform-docs/internal/print" @@ -19,21 +19,22 @@ import ( "github.com/terraform-docs/terraform-docs/internal/terraform" ) -//go:embed templates/asciidoc_document.tmpl -var asciidocDocumentTpl []byte +//go:embed templates/asciidoc_document*.tmpl +var asciidocsDocumentFS embed.FS // AsciidocDocument represents AsciiDoc Document format. type AsciidocDocument struct { template *template.Template + settings *print.Settings } // NewAsciidocDocument returns new instance of AsciidocDocument. func NewAsciidocDocument(settings *print.Settings) print.Engine { + items := readTemplateItems(asciidocsDocumentFS, "asciidoc_document") + settings.EscapeCharacters = false - tt := template.New(settings, &template.Item{ - Name: "document", - Text: string(asciidocDocumentTpl), - }) + + tt := template.New(settings, items...) tt.CustomFunc(gotemplate.FuncMap{ "type": func(t string) string { result, extraline := printFencedAsciidocCodeBlock(t, "hcl") @@ -58,16 +59,28 @@ func NewAsciidocDocument(settings *print.Settings) print.Engine { }) return &AsciidocDocument{ template: tt, + settings: settings, } } -// Print a Terraform module as AsciiDoc document. -func (d *AsciidocDocument) Print(module *terraform.Module, settings *print.Settings) (string, error) { - rendered, err := d.template.Render(module) +// Generate a Terraform module as AsciiDoc document. +func (d *AsciidocDocument) Generate(module *terraform.Module) (*print.Generator, error) { + funcs := []print.GenerateFunc{} + + err := print.ForEach(func(name string, fn print.GeneratorCallback) error { + rendered, err := d.template.Render(name, module) + if err != nil { + return err + } + + funcs = append(funcs, fn(sanitize(rendered))) + return nil + }) if err != nil { - return "", err + return nil, err } - return sanitize(rendered), nil + + return print.NewGenerator("asciidoc document", funcs...), nil } func init() { diff --git a/internal/format/asciidoc_document_test.go b/internal/format/asciidoc_document_test.go index 2524daf..a58a020 100644 --- a/internal/format/asciidoc_document_test.go +++ b/internal/format/asciidoc_document_test.go @@ -178,8 +178,12 @@ func TestAsciidocDocument(t *testing.T) { module, err := testutil.GetModule(options) assert.Nil(err) - printer := NewAsciidocDocument(&tt.settings) - actual, err := printer.Print(module, &tt.settings) + formatter := NewAsciidocDocument(&tt.settings) + + generator, err := formatter.Generate(module) + assert.Nil(err) + + actual, err := generator.ExecuteTemplate("") assert.Nil(err) assert.Equal(expected, actual) diff --git a/internal/format/asciidoc_table.go b/internal/format/asciidoc_table.go index 023c774..8678a4b 100644 --- a/internal/format/asciidoc_table.go +++ b/internal/format/asciidoc_table.go @@ -11,7 +11,7 @@ the root directory of this source tree. package format import ( - _ "embed" //nolint + "embed" gotemplate "text/template" "github.com/terraform-docs/terraform-docs/internal/print" @@ -19,21 +19,22 @@ import ( "github.com/terraform-docs/terraform-docs/internal/terraform" ) -//go:embed templates/asciidoc_table.tmpl -var asciidocTableTpl []byte +//go:embed templates/asciidoc_table*.tmpl +var asciidocTableFS embed.FS // AsciidocTable represents AsciiDoc Table format. type AsciidocTable struct { template *template.Template + settings *print.Settings } // NewAsciidocTable returns new instance of AsciidocTable. func NewAsciidocTable(settings *print.Settings) print.Engine { + items := readTemplateItems(asciidocTableFS, "asciidoc_table") + settings.EscapeCharacters = false - tt := template.New(settings, &template.Item{ - Name: "table", - Text: string(asciidocTableTpl), - }) + + tt := template.New(settings, items...) tt.CustomFunc(gotemplate.FuncMap{ "type": func(t string) string { inputType, _ := printFencedCodeBlock(t, "") @@ -49,16 +50,28 @@ func NewAsciidocTable(settings *print.Settings) print.Engine { }) return &AsciidocTable{ template: tt, + settings: settings, } } -// Print a Terraform module as AsciiDoc tables. -func (t *AsciidocTable) Print(module *terraform.Module, settings *print.Settings) (string, error) { - rendered, err := t.template.Render(module) +// Generate a Terraform module as AsciiDoc tables. +func (t *AsciidocTable) Generate(module *terraform.Module) (*print.Generator, error) { + funcs := []print.GenerateFunc{} + + err := print.ForEach(func(name string, fn print.GeneratorCallback) error { + rendered, err := t.template.Render(name, module) + if err != nil { + return err + } + + funcs = append(funcs, fn(sanitize(rendered))) + return nil + }) if err != nil { - return "", err + return nil, err } - return sanitize(rendered), nil + + return print.NewGenerator("asciidoc table", funcs...), nil } func init() { diff --git a/internal/format/asciidoc_table_test.go b/internal/format/asciidoc_table_test.go index d451a52..5fee062 100644 --- a/internal/format/asciidoc_table_test.go +++ b/internal/format/asciidoc_table_test.go @@ -178,8 +178,12 @@ func TestAsciidocTable(t *testing.T) { module, err := testutil.GetModule(options) assert.Nil(err) - printer := NewAsciidocTable(&tt.settings) - actual, err := printer.Print(module, &tt.settings) + formatter := NewAsciidocTable(&tt.settings) + + generator, err := formatter.Generate(module) + assert.Nil(err) + + actual, err := generator.ExecuteTemplate("") assert.Nil(err) assert.Equal(expected, actual) diff --git a/internal/format/json.go b/internal/format/json.go index 2d0bd06..6d84246 100644 --- a/internal/format/json.go +++ b/internal/format/json.go @@ -20,15 +20,19 @@ import ( ) // JSON represents JSON format. -type JSON struct{} +type JSON struct { + settings *print.Settings +} // NewJSON returns new instance of JSON. func NewJSON(settings *print.Settings) print.Engine { - return &JSON{} + return &JSON{ + settings: settings, + } } -// Print a Terraform module as json. -func (j *JSON) Print(module *terraform.Module, settings *print.Settings) (string, error) { +// Generate a Terraform module as json. +func (j *JSON) Generate(module *terraform.Module) (*print.Generator, error) { copy := &terraform.Module{ Header: "", Footer: "", @@ -40,20 +44,23 @@ func (j *JSON) Print(module *terraform.Module, settings *print.Settings) (string Resources: make([]*terraform.Resource, 0), } - print.CopySections(settings, module, copy) + print.CopySections(j.settings, module, copy) buffer := new(bytes.Buffer) encoder := json.NewEncoder(buffer) encoder.SetIndent("", " ") - encoder.SetEscapeHTML(settings.EscapeCharacters) + encoder.SetEscapeHTML(j.settings.EscapeCharacters) err := encoder.Encode(copy) if err != nil { - return "", err + return nil, err } - return strings.TrimSuffix(buffer.String(), "\n"), nil + return print.NewGenerator( + "json", + print.WithContent(strings.TrimSuffix(buffer.String(), "\n")), + ), nil } func init() { diff --git a/internal/format/json_test.go b/internal/format/json_test.go index 74f95bd..a8c6cd8 100644 --- a/internal/format/json_test.go +++ b/internal/format/json_test.go @@ -125,8 +125,12 @@ func TestJson(t *testing.T) { module, err := testutil.GetModule(options) assert.Nil(err) - printer := NewJSON(&tt.settings) - actual, err := printer.Print(module, &tt.settings) + formatter := NewJSON(&tt.settings) + + generator, err := formatter.Generate(module) + assert.Nil(err) + + actual, err := generator.ExecuteTemplate("") assert.Nil(err) assert.Equal(expected, actual) diff --git a/internal/format/markdown_document.go b/internal/format/markdown_document.go index 196a39f..9c32443 100644 --- a/internal/format/markdown_document.go +++ b/internal/format/markdown_document.go @@ -11,7 +11,7 @@ the root directory of this source tree. package format import ( - _ "embed" //nolint + "embed" gotemplate "text/template" "github.com/terraform-docs/terraform-docs/internal/print" @@ -19,20 +19,20 @@ import ( "github.com/terraform-docs/terraform-docs/internal/terraform" ) -//go:embed templates/markdown_document.tmpl -var markdownDocumentTpl []byte +//go:embed templates/markdown_document*.tmpl +var markdownDocumentFS embed.FS // MarkdownDocument represents Markdown Document format. type MarkdownDocument struct { template *template.Template + settings *print.Settings } // NewMarkdownDocument returns new instance of Document. func NewMarkdownDocument(settings *print.Settings) print.Engine { - tt := template.New(settings, &template.Item{ - Name: "document", - Text: string(markdownDocumentTpl), - }) + items := readTemplateItems(markdownDocumentFS, "markdown_document") + + tt := template.New(settings, items...) tt.CustomFunc(gotemplate.FuncMap{ "type": func(t string) string { result, extraline := printFencedCodeBlock(t, "hcl") @@ -57,16 +57,28 @@ func NewMarkdownDocument(settings *print.Settings) print.Engine { }) return &MarkdownDocument{ template: tt, + settings: settings, } } -// Print a Terraform module as Markdown document. -func (d *MarkdownDocument) Print(module *terraform.Module, settings *print.Settings) (string, error) { - rendered, err := d.template.Render(module) +// Generate a Terraform module as Markdown document. +func (d *MarkdownDocument) Generate(module *terraform.Module) (*print.Generator, error) { + funcs := []print.GenerateFunc{} + + err := print.ForEach(func(name string, fn print.GeneratorCallback) error { + rendered, err := d.template.Render(name, module) + if err != nil { + return err + } + + funcs = append(funcs, fn(sanitize(rendered))) + return nil + }) if err != nil { - return "", err + return nil, err } - return sanitize(rendered), nil + + return print.NewGenerator("markdown document", funcs...), nil } func init() { diff --git a/internal/format/markdown_document_test.go b/internal/format/markdown_document_test.go index c50b4b8..0e288d6 100644 --- a/internal/format/markdown_document_test.go +++ b/internal/format/markdown_document_test.go @@ -189,8 +189,12 @@ func TestMarkdownDocument(t *testing.T) { module, err := testutil.GetModule(options) assert.Nil(err) - printer := NewMarkdownDocument(&tt.settings) - actual, err := printer.Print(module, &tt.settings) + formatter := NewMarkdownDocument(&tt.settings) + + generator, err := formatter.Generate(module) + assert.Nil(err) + + actual, err := generator.ExecuteTemplate("") assert.Nil(err) assert.Equal(expected, actual) diff --git a/internal/format/markdown_table.go b/internal/format/markdown_table.go index 15101d4..3758389 100644 --- a/internal/format/markdown_table.go +++ b/internal/format/markdown_table.go @@ -11,7 +11,7 @@ the root directory of this source tree. package format import ( - _ "embed" //nolint + "embed" gotemplate "text/template" "github.com/terraform-docs/terraform-docs/internal/print" @@ -19,20 +19,20 @@ import ( "github.com/terraform-docs/terraform-docs/internal/terraform" ) -//go:embed templates/markdown_table.tmpl -var markdownTableTpl []byte +//go:embed templates/markdown_table*.tmpl +var markdownTableFS embed.FS // MarkdownTable represents Markdown Table format. type MarkdownTable struct { template *template.Template + settings *print.Settings } // NewMarkdownTable returns new instance of Table. func NewMarkdownTable(settings *print.Settings) print.Engine { - tt := template.New(settings, &template.Item{ - Name: "table", - Text: string(markdownTableTpl), - }) + items := readTemplateItems(markdownTableFS, "markdown_table") + + tt := template.New(settings, items...) tt.CustomFunc(gotemplate.FuncMap{ "type": func(t string) string { inputType, _ := printFencedCodeBlock(t, "") @@ -48,16 +48,28 @@ func NewMarkdownTable(settings *print.Settings) print.Engine { }) return &MarkdownTable{ template: tt, + settings: settings, } } -// Print a Terraform module as Markdown tables. -func (t *MarkdownTable) Print(module *terraform.Module, settings *print.Settings) (string, error) { - rendered, err := t.template.Render(module) +// Generate a Terraform module as Markdown tables. +func (t *MarkdownTable) Generate(module *terraform.Module) (*print.Generator, error) { + funcs := []print.GenerateFunc{} + + err := print.ForEach(func(name string, fn print.GeneratorCallback) error { + rendered, err := t.template.Render(name, module) + if err != nil { + return err + } + + funcs = append(funcs, fn(sanitize(rendered))) + return nil + }) if err != nil { - return "", err + return nil, err } - return sanitize(rendered), nil + + return print.NewGenerator("markdown table", funcs...), nil } func init() { diff --git a/internal/format/markdown_table_test.go b/internal/format/markdown_table_test.go index 4eea4cd..8564729 100644 --- a/internal/format/markdown_table_test.go +++ b/internal/format/markdown_table_test.go @@ -189,8 +189,12 @@ func TestMarkdownTable(t *testing.T) { module, err := testutil.GetModule(options) assert.Nil(err) - printer := NewMarkdownTable(&tt.settings) - actual, err := printer.Print(module, &tt.settings) + formatter := NewMarkdownTable(&tt.settings) + + generator, err := formatter.Generate(module) + assert.Nil(err) + + actual, err := generator.ExecuteTemplate("") assert.Nil(err) assert.Equal(expected, actual) diff --git a/internal/format/pretty.go b/internal/format/pretty.go index 9a779db..e36b0c6 100644 --- a/internal/format/pretty.go +++ b/internal/format/pretty.go @@ -27,6 +27,7 @@ var prettyTpl []byte // Pretty represents colorized pretty format. type Pretty struct { template *template.Template + settings *print.Settings } // NewPretty returns new instance of Pretty. @@ -47,16 +48,21 @@ func NewPretty(settings *print.Settings) print.Engine { }) return &Pretty{ template: tt, + settings: settings, } } -// Print a Terraform module document. -func (p *Pretty) Print(module *terraform.Module, settings *print.Settings) (string, error) { - rendered, err := p.template.Render(module) +// Generate a Terraform module document. +func (p *Pretty) Generate(module *terraform.Module) (*print.Generator, error) { + rendered, err := p.template.Render("pretty", module) if err != nil { - return "", err + return nil, err } - return regexp.MustCompile(`(\r?\n)*$`).ReplaceAllString(rendered, ""), nil + + return print.NewGenerator( + "pretty", + print.WithContent(regexp.MustCompile(`(\r?\n)*$`).ReplaceAllString(rendered, "")), + ), nil } func init() { diff --git a/internal/format/pretty_test.go b/internal/format/pretty_test.go index 3b67df0..f2aef2f 100644 --- a/internal/format/pretty_test.go +++ b/internal/format/pretty_test.go @@ -125,8 +125,12 @@ func TestPretty(t *testing.T) { module, err := testutil.GetModule(options) assert.Nil(err) - printer := NewPretty(&tt.settings) - actual, err := printer.Print(module, &tt.settings) + formatter := NewPretty(&tt.settings) + + generator, err := formatter.Generate(module) + assert.Nil(err) + + actual, err := generator.ExecuteTemplate("") assert.Nil(err) assert.Equal(expected, actual) diff --git a/internal/format/templates/asciidoc_document.tmpl b/internal/format/templates/asciidoc_document.tmpl index 54f672d..690ebb8 100644 --- a/internal/format/templates/asciidoc_document.tmpl +++ b/internal/format/templates/asciidoc_document.tmpl @@ -1,172 +1,8 @@ -{{- if .Settings.ShowHeader -}} - {{- with .Module.Header -}} - {{ sanitizeSection . }} - {{ printf "\n" }} - {{- end -}} -{{ end -}} - -{{- if .Settings.ShowRequirements -}} - {{ indent 0 "=" }} Requirements - {{ if not .Module.Requirements }} - No requirements. - {{ else }} - The following requirements are needed by this module: - {{- range .Module.Requirements }} - {{ $version := ternary (tostring .Version) (printf " (%s)" .Version) "" }} - - {{ anchorNameAsciidoc "requirement" .Name }}{{ $version }} - {{- end }} - {{ end }} -{{ end -}} - -{{- if .Settings.ShowProviders -}} - {{ indent 0 "=" }} Providers - {{ if not .Module.Providers }} - No providers. - {{ else }} - The following providers are used by this module: - {{- range .Module.Providers }} - {{ $version := ternary (tostring .Version) (printf " (%s)" .Version) "" }} - - {{ anchorNameAsciidoc "provider" .FullName }}{{ $version }} - {{- end }} - {{ end }} -{{ end -}} - -{{- if .Settings.ShowModuleCalls -}} - {{ indent 0 "=" }} Modules - {{ if not .Module.ModuleCalls }} - No modules. - {{ else }} - The following Modules are called: - {{- range .Module.ModuleCalls }} - - {{ indent 1 "=" }} {{ anchorNameAsciidoc "module" .Name }} - - Source: {{ .Source }} - - Version: {{ .Version }} - {{- end }} - {{ end }} -{{ end -}} - -{{- if or .Settings.ShowResources .Settings.ShowDataSources -}} - {{ indent 0 "=" }} Resources - {{ if not .Module.Resources }} - No resources. - {{ else }} - The following resources are used by this module: - {{ range .Module.Resources }} - {{- $isResource := and $.Settings.ShowResources ( eq "resource" (printf "%s" .GetMode)) }} - {{- $isDataResource := and $.Settings.ShowDataSources ( eq "data source" (printf "%s" .GetMode)) }} - {{- if or $isResource $isDataResource }} - {{ if eq (len .URL) 0 }} - - {{ .Spec }} {{ printf "(%s)" .GetMode -}} - {{- else -}} - - {{ .URL }}[{{ .Spec }}] {{ printf "(%s)" .GetMode -}} - {{- end }} - {{- end }} - {{- end }} - {{ end }} -{{ end -}} - -{{- if .Settings.ShowInputs -}} - {{- if .Settings.ShowRequired -}} - {{ indent 0 "=" }} Required Inputs - {{ if not .Module.RequiredInputs }} - No required inputs. - {{ else }} - The following input variables are required: - {{- range .Module.RequiredInputs }} - {{ printf "\n" }} - {{ indent 1 "=" }} {{ anchorNameAsciidoc "input" .Name }} - - Description: {{ tostring .Description | sanitizeDoc }} - - {{ if $.Settings.ShowType -}} - Type: {{ tostring .Type | type }} - {{- end }} - - {{ if $.Settings.ShowDefault }} - {{ if or .HasDefault (not isRequired) }} - Default: {{ default "n/a" .GetValue | value }} - {{- end }} - {{- end }} - {{- end }} - {{- end }} - {{ indent 0 "=" }} Optional Inputs - {{ if not .Module.OptionalInputs }} - No optional inputs. - {{ else }} - The following input variables are optional (have default values): - {{- range .Module.OptionalInputs }} - {{ printf "\n" }} - {{ indent 1 "=" }} {{ anchorNameAsciidoc "input" .Name }} - - Description: {{ tostring .Description | sanitizeDoc }} - - {{ if $.Settings.ShowType -}} - Type: {{ tostring .Type | type }} - {{- end }} - - {{ if $.Settings.ShowDefault }} - {{ if or .HasDefault (not isRequired) }} - Default: {{ default "n/a" .GetValue | value }} - {{- end }} - {{- end }} - {{- end }} - {{ end }} - {{ else -}} - {{ indent 0 "=" }} Inputs - {{ if not .Module.Inputs }} - No inputs. - {{ else }} - The following input variables are supported: - {{- range .Module.Inputs }} - {{ printf "\n" }} - {{ indent 1 "=" }} {{ anchorNameAsciidoc "input" .Name }} - - Description: {{ tostring .Description | sanitizeDoc }} - - {{ if $.Settings.ShowType -}} - Type: {{ tostring .Type | type }} - {{- end }} - - {{ if $.Settings.ShowDefault }} - {{ if or .HasDefault (not isRequired) }} - Default: {{ default "n/a" .GetValue | value }} - {{- end }} - {{- end }} - {{- end }} - {{ end }} - {{- end }} -{{ end -}} - -{{- if .Settings.ShowOutputs -}} - {{ indent 0 "=" }} Outputs - {{ if not .Module.Outputs }} - No outputs. - {{ else }} - The following outputs are exported: - {{- range .Module.Outputs }} - - {{ indent 1 "=" }} {{ anchorNameAsciidoc "output" .Name }} - - Description: {{ tostring .Description | sanitizeDoc }} - - {{ if $.Settings.OutputValues }} - {{- $sensitive := ternary .Sensitive "" .GetValue -}} - Value: {{ value $sensitive | sanitizeDoc }} - - {{ if $.Settings.ShowSensitivity -}} - Sensitive: {{ ternary (.Sensitive) "yes" "no" }} - {{- end }} - {{ end }} - {{ end }} - {{ end }} -{{ end -}} - -{{- if .Settings.ShowFooter -}} - {{- with .Module.Footer -}} - {{ sanitizeSection . }} - {{ printf "\n" }} - {{- end -}} -{{ end -}} \ No newline at end of file +{{- template "header" . -}} +{{- template "requirements" . -}} +{{- template "providers" . -}} +{{- template "modules" . -}} +{{- template "resources" . -}} +{{- template "inputs" . -}} +{{- template "outputs" . -}} +{{- template "footer" . -}} \ No newline at end of file diff --git a/internal/format/templates/asciidoc_document_footer.tmpl b/internal/format/templates/asciidoc_document_footer.tmpl new file mode 100644 index 0000000..5f9cdcd --- /dev/null +++ b/internal/format/templates/asciidoc_document_footer.tmpl @@ -0,0 +1,6 @@ +{{- if .Settings.ShowFooter -}} + {{- with .Module.Footer -}} + {{ sanitizeSection . }} + {{ printf "\n" }} + {{- end -}} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/asciidoc_document_header.tmpl b/internal/format/templates/asciidoc_document_header.tmpl new file mode 100644 index 0000000..5e2d737 --- /dev/null +++ b/internal/format/templates/asciidoc_document_header.tmpl @@ -0,0 +1,6 @@ +{{- if .Settings.ShowHeader -}} + {{- with .Module.Header -}} + {{ sanitizeSection . }} + {{ printf "\n" }} + {{- end -}} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/asciidoc_document_inputs.tmpl b/internal/format/templates/asciidoc_document_inputs.tmpl new file mode 100644 index 0000000..1b31089 --- /dev/null +++ b/internal/format/templates/asciidoc_document_inputs.tmpl @@ -0,0 +1,71 @@ +{{- if .Settings.ShowInputs -}} + {{- if .Settings.ShowRequired -}} + {{ indent 0 "=" }} Required Inputs + {{ if not .Module.RequiredInputs }} + No required inputs. + {{ else }} + The following input variables are required: + {{- range .Module.RequiredInputs }} + {{ printf "\n" }} + {{ indent 1 "=" }} {{ anchorNameAsciidoc "input" .Name }} + + Description: {{ tostring .Description | sanitizeDoc }} + + {{ if $.Settings.ShowType -}} + Type: {{ tostring .Type | type }} + {{- end }} + + {{ if $.Settings.ShowDefault }} + {{ if or .HasDefault (not isRequired) }} + Default: {{ default "n/a" .GetValue | value }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{ indent 0 "=" }} Optional Inputs + {{ if not .Module.OptionalInputs }} + No optional inputs. + {{ else }} + The following input variables are optional (have default values): + {{- range .Module.OptionalInputs }} + {{ printf "\n" }} + {{ indent 1 "=" }} {{ anchorNameAsciidoc "input" .Name }} + + Description: {{ tostring .Description | sanitizeDoc }} + + {{ if $.Settings.ShowType -}} + Type: {{ tostring .Type | type }} + {{- end }} + + {{ if $.Settings.ShowDefault }} + {{ if or .HasDefault (not isRequired) }} + Default: {{ default "n/a" .GetValue | value }} + {{- end }} + {{- end }} + {{- end }} + {{ end }} + {{ else -}} + {{ indent 0 "=" }} Inputs + {{ if not .Module.Inputs }} + No inputs. + {{ else }} + The following input variables are supported: + {{- range .Module.Inputs }} + {{ printf "\n" }} + {{ indent 1 "=" }} {{ anchorNameAsciidoc "input" .Name }} + + Description: {{ tostring .Description | sanitizeDoc }} + + {{ if $.Settings.ShowType -}} + Type: {{ tostring .Type | type }} + {{- end }} + + {{ if $.Settings.ShowDefault }} + {{ if or .HasDefault (not isRequired) }} + Default: {{ default "n/a" .GetValue | value }} + {{- end }} + {{- end }} + {{- end }} + {{ end }} + {{- end }} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/asciidoc_document_modules.tmpl b/internal/format/templates/asciidoc_document_modules.tmpl new file mode 100644 index 0000000..80e9e1a --- /dev/null +++ b/internal/format/templates/asciidoc_document_modules.tmpl @@ -0,0 +1,16 @@ +{{- if .Settings.ShowModuleCalls -}} + {{ indent 0 "=" }} Modules + {{ if not .Module.ModuleCalls }} + No modules. + {{ else }} + The following Modules are called: + {{- range .Module.ModuleCalls }} + + {{ indent 1 "=" }} {{ anchorNameAsciidoc "module" .Name }} + + Source: {{ .Source }} + + Version: {{ .Version }} + {{- end }} + {{ end }} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/asciidoc_document_outputs.tmpl b/internal/format/templates/asciidoc_document_outputs.tmpl new file mode 100644 index 0000000..28913f2 --- /dev/null +++ b/internal/format/templates/asciidoc_document_outputs.tmpl @@ -0,0 +1,23 @@ +{{- if .Settings.ShowOutputs -}} + {{ indent 0 "=" }} Outputs + {{ if not .Module.Outputs }} + No outputs. + {{ else }} + The following outputs are exported: + {{- range .Module.Outputs }} + + {{ indent 1 "=" }} {{ anchorNameAsciidoc "output" .Name }} + + Description: {{ tostring .Description | sanitizeDoc }} + + {{ if $.Settings.OutputValues }} + {{- $sensitive := ternary .Sensitive "" .GetValue -}} + Value: {{ value $sensitive | sanitizeDoc }} + + {{ if $.Settings.ShowSensitivity -}} + Sensitive: {{ ternary (.Sensitive) "yes" "no" }} + {{- end }} + {{ end }} + {{ end }} + {{ end }} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/asciidoc_document_providers.tmpl b/internal/format/templates/asciidoc_document_providers.tmpl new file mode 100644 index 0000000..77b75ff --- /dev/null +++ b/internal/format/templates/asciidoc_document_providers.tmpl @@ -0,0 +1,12 @@ +{{- if .Settings.ShowProviders -}} + {{ indent 0 "=" }} Providers + {{ if not .Module.Providers }} + No providers. + {{ else }} + The following providers are used by this module: + {{- range .Module.Providers }} + {{ $version := ternary (tostring .Version) (printf " (%s)" .Version) "" }} + - {{ anchorNameAsciidoc "provider" .FullName }}{{ $version }} + {{- end }} + {{ end }} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/asciidoc_document_requirements.tmpl b/internal/format/templates/asciidoc_document_requirements.tmpl new file mode 100644 index 0000000..9c8b2dc --- /dev/null +++ b/internal/format/templates/asciidoc_document_requirements.tmpl @@ -0,0 +1,12 @@ +{{- if .Settings.ShowRequirements -}} + {{ indent 0 "=" }} Requirements + {{ if not .Module.Requirements }} + No requirements. + {{ else }} + The following requirements are needed by this module: + {{- range .Module.Requirements }} + {{ $version := ternary (tostring .Version) (printf " (%s)" .Version) "" }} + - {{ anchorNameAsciidoc "requirement" .Name }}{{ $version }} + {{- end }} + {{ end }} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/asciidoc_document_resources.tmpl b/internal/format/templates/asciidoc_document_resources.tmpl new file mode 100644 index 0000000..de53d66 --- /dev/null +++ b/internal/format/templates/asciidoc_document_resources.tmpl @@ -0,0 +1,19 @@ +{{- if or .Settings.ShowResources .Settings.ShowDataSources -}} + {{ indent 0 "=" }} Resources + {{ if not .Module.Resources }} + No resources. + {{ else }} + The following resources are used by this module: + {{ range .Module.Resources }} + {{- $isResource := and $.Settings.ShowResources ( eq "resource" (printf "%s" .GetMode)) }} + {{- $isDataResource := and $.Settings.ShowDataSources ( eq "data source" (printf "%s" .GetMode)) }} + {{- if or $isResource $isDataResource }} + {{ if eq (len .URL) 0 }} + - {{ .Spec }} {{ printf "(%s)" .GetMode -}} + {{- else -}} + - {{ .URL }}[{{ .Spec }}] {{ printf "(%s)" .GetMode -}} + {{- end }} + {{- end }} + {{- end }} + {{ end }} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/asciidoc_table.tmpl b/internal/format/templates/asciidoc_table.tmpl index c7f4c56..690ebb8 100644 --- a/internal/format/templates/asciidoc_table.tmpl +++ b/internal/format/templates/asciidoc_table.tmpl @@ -1,125 +1,8 @@ -{{- if .Settings.ShowHeader -}} - {{- with .Module.Header -}} - {{ sanitizeSection . }} - {{ printf "\n" }} - {{- end -}} -{{ end -}} - -{{- if .Settings.ShowRequirements -}} - {{ indent 0 "=" }} Requirements - {{ if not .Module.Requirements }} - No requirements. - {{ else }} - [cols="a,a",options="header,autowidth"] - |=== - |Name |Version - {{- range .Module.Requirements }} - |{{ anchorNameAsciidoc "requirement" .Name }} |{{ tostring .Version | default "n/a" }} - {{- end }} - |=== - {{ end }} -{{ end -}} - -{{- if .Settings.ShowProviders -}} - {{ indent 0 "=" }} Providers - {{ if not .Module.Providers }} - No providers. - {{ else }} - [cols="a,a",options="header,autowidth"] - |=== - |Name |Version - {{- range .Module.Providers }} - |{{ anchorNameAsciidoc "provider" .FullName }} |{{ tostring .Version | default "n/a" }} - {{- end }} - |=== - {{ end }} -{{ end -}} - -{{- if .Settings.ShowModuleCalls -}} - {{ indent 0 "=" }} Modules - {{ if not .Module.ModuleCalls }} - No modules. - {{ else }} - [cols="a,a,a",options="header,autowidth"] - |=== - |Name|Source|Version| - {{- range .Module.ModuleCalls }} - |{{ anchorNameAsciidoc "module" .Name }}|{{ .Source }}|{{ .Version }} - {{- end }} - |=== - {{ end }} -{{ end -}} - -{{- if or .Settings.ShowResources .Settings.ShowDataSources -}} - {{ indent 0 "=" }} Resources - {{ if not .Module.Resources }} - No resources. - {{ else }} - [cols="a,a",options="header,autowidth"] - |=== - |Name |Type - {{- range .Module.Resources }} - {{- $isResource := and $.Settings.ShowResources ( eq "resource" (printf "%s" .GetMode)) }} - {{- $isDataResource := and $.Settings.ShowDataSources ( eq "data source" (printf "%s" .GetMode)) }} - {{- if or $isResource $isDataResource }} - {{ if eq (len .URL) 0 }} - |{{ .Spec }} |{{ .GetMode }} - {{- else -}} - |{{ .URL }}[{{ .Spec }}] |{{ .GetMode }} - {{- end }} - {{- end }} - {{- end }} - |=== - {{ end }} -{{ end -}} - -{{- if .Settings.ShowInputs -}} - {{ indent 0 "=" }} Inputs - {{ if not .Module.Inputs }} - No inputs. - {{ else }} - [cols="a,a{{ if .Settings.ShowType }},a{{ end }}{{ if .Settings.ShowDefault }},a{{ end }}{{ if .Settings.ShowRequired }},a{{ end }}",options="header,autowidth"] - |=== - |Name |Description - {{- if .Settings.ShowType }} |Type{{ end }} - {{- if .Settings.ShowDefault }} |Default{{ end }} - {{- if .Settings.ShowRequired }} |Required{{ end }} - {{- range .Module.Inputs }} - |{{ anchorNameAsciidoc "input" .Name }} - |{{ tostring .Description | sanitizeAsciidocTbl }} - {{- if $.Settings.ShowType }}{{ printf "\n" }}|{{ tostring .Type | type | sanitizeAsciidocTbl }}{{ end }} - {{- if $.Settings.ShowDefault }}{{ printf "\n" }}|{{ value .GetValue | sanitizeAsciidocTbl }}{{ end }} - {{- if $.Settings.ShowRequired }}{{ printf "\n" }}|{{ ternary .Required "yes" "no" }}{{ end }} - {{ end }} - |=== - {{ end }} -{{ end -}} - -{{- if .Settings.ShowOutputs -}} - {{ indent 0 "=" }} Outputs - {{ if not .Module.Outputs }} - No outputs. - {{ else }} - [cols="a,a{{ if .Settings.OutputValues }},a{{ if $.Settings.ShowSensitivity }},a{{ end }}{{ end }}",options="header,autowidth"] - |=== - |Name |Description{{ if .Settings.OutputValues }} |Value{{ if $.Settings.ShowSensitivity }} |Sensitive{{ end }}{{ end }} - {{- range .Module.Outputs }} - |{{ anchorNameAsciidoc "output" .Name }} |{{ tostring .Description | sanitizeAsciidocTbl }} - {{- if $.Settings.OutputValues -}} - {{- $sensitive := ternary .Sensitive "" .GetValue -}} - {{ printf " " }}|{{ value $sensitive }} - {{- if $.Settings.ShowSensitivity -}} - {{ printf " " }}|{{ ternary .Sensitive "yes" "no" }} - {{- end -}} - {{- end -}} - {{- end }} - |=== - {{ end }} -{{ end -}} - -{{- if .Settings.ShowFooter -}} - {{- with .Module.Footer -}} - {{ sanitizeSection . }} - {{ printf "\n" }} - {{- end -}} -{{ end -}} \ No newline at end of file +{{- template "header" . -}} +{{- template "requirements" . -}} +{{- template "providers" . -}} +{{- template "modules" . -}} +{{- template "resources" . -}} +{{- template "inputs" . -}} +{{- template "outputs" . -}} +{{- template "footer" . -}} \ No newline at end of file diff --git a/internal/format/templates/asciidoc_table_footer.tmpl b/internal/format/templates/asciidoc_table_footer.tmpl new file mode 100644 index 0000000..5f9cdcd --- /dev/null +++ b/internal/format/templates/asciidoc_table_footer.tmpl @@ -0,0 +1,6 @@ +{{- if .Settings.ShowFooter -}} + {{- with .Module.Footer -}} + {{ sanitizeSection . }} + {{ printf "\n" }} + {{- end -}} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/asciidoc_table_header.tmpl b/internal/format/templates/asciidoc_table_header.tmpl new file mode 100644 index 0000000..5e2d737 --- /dev/null +++ b/internal/format/templates/asciidoc_table_header.tmpl @@ -0,0 +1,6 @@ +{{- if .Settings.ShowHeader -}} + {{- with .Module.Header -}} + {{ sanitizeSection . }} + {{ printf "\n" }} + {{- end -}} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/asciidoc_table_inputs.tmpl b/internal/format/templates/asciidoc_table_inputs.tmpl new file mode 100644 index 0000000..8efea78 --- /dev/null +++ b/internal/format/templates/asciidoc_table_inputs.tmpl @@ -0,0 +1,21 @@ +{{- if .Settings.ShowInputs -}} + {{ indent 0 "=" }} Inputs + {{ if not .Module.Inputs }} + No inputs. + {{ else }} + [cols="a,a{{ if .Settings.ShowType }},a{{ end }}{{ if .Settings.ShowDefault }},a{{ end }}{{ if .Settings.ShowRequired }},a{{ end }}",options="header,autowidth"] + |=== + |Name |Description + {{- if .Settings.ShowType }} |Type{{ end }} + {{- if .Settings.ShowDefault }} |Default{{ end }} + {{- if .Settings.ShowRequired }} |Required{{ end }} + {{- range .Module.Inputs }} + |{{ anchorNameAsciidoc "input" .Name }} + |{{ tostring .Description | sanitizeAsciidocTbl }} + {{- if $.Settings.ShowType }}{{ printf "\n" }}|{{ tostring .Type | type | sanitizeAsciidocTbl }}{{ end }} + {{- if $.Settings.ShowDefault }}{{ printf "\n" }}|{{ value .GetValue | sanitizeAsciidocTbl }}{{ end }} + {{- if $.Settings.ShowRequired }}{{ printf "\n" }}|{{ ternary .Required "yes" "no" }}{{ end }} + {{ end }} + |=== + {{ end }} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/asciidoc_table_modules.tmpl b/internal/format/templates/asciidoc_table_modules.tmpl new file mode 100644 index 0000000..2f5759a --- /dev/null +++ b/internal/format/templates/asciidoc_table_modules.tmpl @@ -0,0 +1,14 @@ +{{- if .Settings.ShowModuleCalls -}} + {{ indent 0 "=" }} Modules + {{ if not .Module.ModuleCalls }} + No modules. + {{ else }} + [cols="a,a,a",options="header,autowidth"] + |=== + |Name|Source|Version| + {{- range .Module.ModuleCalls }} + |{{ anchorNameAsciidoc "module" .Name }}|{{ .Source }}|{{ .Version }} + {{- end }} + |=== + {{ end }} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/asciidoc_table_outputs.tmpl b/internal/format/templates/asciidoc_table_outputs.tmpl new file mode 100644 index 0000000..b3a2bde --- /dev/null +++ b/internal/format/templates/asciidoc_table_outputs.tmpl @@ -0,0 +1,21 @@ +{{- if .Settings.ShowOutputs -}} + {{ indent 0 "=" }} Outputs + {{ if not .Module.Outputs }} + No outputs. + {{ else }} + [cols="a,a{{ if .Settings.OutputValues }},a{{ if $.Settings.ShowSensitivity }},a{{ end }}{{ end }}",options="header,autowidth"] + |=== + |Name |Description{{ if .Settings.OutputValues }} |Value{{ if $.Settings.ShowSensitivity }} |Sensitive{{ end }}{{ end }} + {{- range .Module.Outputs }} + |{{ anchorNameAsciidoc "output" .Name }} |{{ tostring .Description | sanitizeAsciidocTbl }} + {{- if $.Settings.OutputValues -}} + {{- $sensitive := ternary .Sensitive "" .GetValue -}} + {{ printf " " }}|{{ value $sensitive }} + {{- if $.Settings.ShowSensitivity -}} + {{ printf " " }}|{{ ternary .Sensitive "yes" "no" }} + {{- end -}} + {{- end -}} + {{- end }} + |=== + {{ end }} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/asciidoc_table_providers.tmpl b/internal/format/templates/asciidoc_table_providers.tmpl new file mode 100644 index 0000000..ca59b8a --- /dev/null +++ b/internal/format/templates/asciidoc_table_providers.tmpl @@ -0,0 +1,14 @@ +{{- if .Settings.ShowProviders -}} + {{ indent 0 "=" }} Providers + {{ if not .Module.Providers }} + No providers. + {{ else }} + [cols="a,a",options="header,autowidth"] + |=== + |Name |Version + {{- range .Module.Providers }} + |{{ anchorNameAsciidoc "provider" .FullName }} |{{ tostring .Version | default "n/a" }} + {{- end }} + |=== + {{ end }} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/asciidoc_table_requirements.tmpl b/internal/format/templates/asciidoc_table_requirements.tmpl new file mode 100644 index 0000000..6556cdb --- /dev/null +++ b/internal/format/templates/asciidoc_table_requirements.tmpl @@ -0,0 +1,14 @@ +{{- if .Settings.ShowRequirements -}} + {{ indent 0 "=" }} Requirements + {{ if not .Module.Requirements }} + No requirements. + {{ else }} + [cols="a,a",options="header,autowidth"] + |=== + |Name |Version + {{- range .Module.Requirements }} + |{{ anchorNameAsciidoc "requirement" .Name }} |{{ tostring .Version | default "n/a" }} + {{- end }} + |=== + {{ end }} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/asciidoc_table_resources.tmpl b/internal/format/templates/asciidoc_table_resources.tmpl new file mode 100644 index 0000000..50a78d1 --- /dev/null +++ b/internal/format/templates/asciidoc_table_resources.tmpl @@ -0,0 +1,22 @@ +{{- if or .Settings.ShowResources .Settings.ShowDataSources -}} + {{ indent 0 "=" }} Resources + {{ if not .Module.Resources }} + No resources. + {{ else }} + [cols="a,a",options="header,autowidth"] + |=== + |Name |Type + {{- range .Module.Resources }} + {{- $isResource := and $.Settings.ShowResources ( eq "resource" (printf "%s" .GetMode)) }} + {{- $isDataResource := and $.Settings.ShowDataSources ( eq "data source" (printf "%s" .GetMode)) }} + {{- if or $isResource $isDataResource }} + {{ if eq (len .URL) 0 }} + |{{ .Spec }} |{{ .GetMode }} + {{- else -}} + |{{ .URL }}[{{ .Spec }}] |{{ .GetMode }} + {{- end }} + {{- end }} + {{- end }} + |=== + {{ end }} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/markdown_document.tmpl b/internal/format/templates/markdown_document.tmpl index 915f8c9..690ebb8 100644 --- a/internal/format/templates/markdown_document.tmpl +++ b/internal/format/templates/markdown_document.tmpl @@ -1,173 +1,8 @@ -{{- if .Settings.ShowHeader -}} - {{- with .Module.Header -}} - {{ sanitizeSection . }} - {{ printf "\n" }} - {{- end -}} -{{ end -}} - -{{- if .Settings.ShowRequirements -}} - {{ indent 0 "#" }} Requirements - {{ if not .Module.Requirements }} - No requirements. - {{ else }} - The following requirements are needed by this module: - {{- range .Module.Requirements }} - {{ $version := ternary (tostring .Version) (printf " (%s)" .Version) "" }} - - {{ anchorName "requirement" .Name }}{{ $version }} - {{- end }} - {{ end }} -{{ end -}} - -{{- if .Settings.ShowProviders -}} - {{ indent 0 "#" }} Providers - {{ if not .Module.Providers }} - No providers. - {{ else }} - The following providers are used by this module: - {{- range .Module.Providers }} - {{ $version := ternary (tostring .Version) (printf " (%s)" .Version) "" }} - - {{ anchorName "provider" .FullName }}{{ $version }} - {{- end }} - {{ end }} -{{ end -}} - -{{- if .Settings.ShowModuleCalls -}} - {{ indent 0 "#" }} Modules - {{ if not .Module.ModuleCalls }} - No modules. - {{ else }} - The following Modules are called: - {{- range .Module.ModuleCalls }} - - {{ indent 1 "#" }} {{ anchorName "module" .Name }} - - Source: {{ .Source }} - - Version: {{ .Version }} - - {{ end }} - {{ end }} -{{ end -}} - -{{- if or .Settings.ShowResources .Settings.ShowDataSources -}} - {{ indent 0 "#" }} Resources - {{ if not .Module.Resources }} - No resources. - {{ else }} - The following resources are used by this module: - {{ range .Module.Resources }} - {{- $isResource := and $.Settings.ShowResources ( eq "resource" (printf "%s" .GetMode)) }} - {{- $isDataResource := and $.Settings.ShowDataSources ( eq "data source" (printf "%s" .GetMode)) }} - {{- if or $isResource $isDataResource }} - {{ if eq (len .URL) 0 }} - - {{ .Spec }} {{ printf "(%s)" .GetMode -}} - {{- else -}} - - [{{ .Spec }}]({{ .URL }}) {{ printf "(%s)" .GetMode -}} - {{- end }} - {{- end }} - {{- end }} - {{ end }} -{{ end -}} - -{{- if .Settings.ShowInputs -}} - {{- if .Settings.ShowRequired -}} - {{ indent 0 "#" }} Required Inputs - {{ if not .Module.RequiredInputs }} - No required inputs. - {{ else }} - The following input variables are required: - {{- range .Module.RequiredInputs }} - {{ printf "\n" }} - {{ indent 1 "#" }} {{ anchorName "input" .Name }} - - Description: {{ tostring .Description | sanitizeDoc }} - - {{ if $.Settings.ShowType -}} - Type: {{ tostring .Type | type }} - {{- end }} - - {{ if $.Settings.ShowDefault }} - {{ if or .HasDefault (not isRequired) }} - Default: {{ default "n/a" .GetValue | value }} - {{- end }} - {{- end }} - {{- end }} - {{- end }} - {{ indent 0 "#" }} Optional Inputs - {{ if not .Module.OptionalInputs }} - No optional inputs. - {{ else }} - The following input variables are optional (have default values): - {{- range .Module.OptionalInputs }} - {{ printf "\n" }} - {{ indent 1 "#" }} {{ anchorName "input" .Name }} - - Description: {{ tostring .Description | sanitizeDoc }} - - {{ if $.Settings.ShowType -}} - Type: {{ tostring .Type | type }} - {{- end }} - - {{ if $.Settings.ShowDefault }} - {{ if or .HasDefault (not isRequired) }} - Default: {{ default "n/a" .GetValue | value }} - {{- end }} - {{- end }} - {{- end }} - {{ end }} - {{ else -}} - {{ indent 0 "#" }} Inputs - {{ if not .Module.Inputs }} - No inputs. - {{ else }} - The following input variables are supported: - {{- range .Module.Inputs }} - {{ printf "\n" }} - {{ indent 1 "#" }} {{ anchorName "input" .Name }} - - Description: {{ tostring .Description | sanitizeDoc }} - - {{ if $.Settings.ShowType -}} - Type: {{ tostring .Type | type }} - {{- end }} - - {{ if $.Settings.ShowDefault }} - {{ if or .HasDefault (not isRequired) }} - Default: {{ default "n/a" .GetValue | value }} - {{- end }} - {{- end }} - {{- end }} - {{ end }} - {{- end }} -{{ end -}} - -{{- if .Settings.ShowOutputs -}} - {{ indent 0 "#" }} Outputs - {{ if not .Module.Outputs }} - No outputs. - {{ else }} - The following outputs are exported: - {{- range .Module.Outputs }} - - {{ indent 1 "#" }} {{ anchorName "output" .Name }} - - Description: {{ tostring .Description | sanitizeDoc }} - - {{ if $.Settings.OutputValues }} - {{- $sensitive := ternary .Sensitive "" .GetValue -}} - Value: {{ value $sensitive | sanitizeDoc }} - - {{ if $.Settings.ShowSensitivity -}} - Sensitive: {{ ternary (.Sensitive) "yes" "no" }} - {{- end }} - {{ end }} - {{ end }} - {{ end }} -{{ end -}} - -{{- if .Settings.ShowFooter -}} - {{- with .Module.Footer -}} - {{ sanitizeSection . }} - {{ printf "\n" }} - {{- end -}} -{{ end -}} \ No newline at end of file +{{- template "header" . -}} +{{- template "requirements" . -}} +{{- template "providers" . -}} +{{- template "modules" . -}} +{{- template "resources" . -}} +{{- template "inputs" . -}} +{{- template "outputs" . -}} +{{- template "footer" . -}} \ No newline at end of file diff --git a/internal/format/templates/markdown_document_footer.tmpl b/internal/format/templates/markdown_document_footer.tmpl new file mode 100644 index 0000000..5f9cdcd --- /dev/null +++ b/internal/format/templates/markdown_document_footer.tmpl @@ -0,0 +1,6 @@ +{{- if .Settings.ShowFooter -}} + {{- with .Module.Footer -}} + {{ sanitizeSection . }} + {{ printf "\n" }} + {{- end -}} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/markdown_document_header.tmpl b/internal/format/templates/markdown_document_header.tmpl new file mode 100644 index 0000000..5e2d737 --- /dev/null +++ b/internal/format/templates/markdown_document_header.tmpl @@ -0,0 +1,6 @@ +{{- if .Settings.ShowHeader -}} + {{- with .Module.Header -}} + {{ sanitizeSection . }} + {{ printf "\n" }} + {{- end -}} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/markdown_document_inputs.tmpl b/internal/format/templates/markdown_document_inputs.tmpl new file mode 100644 index 0000000..02ee8a0 --- /dev/null +++ b/internal/format/templates/markdown_document_inputs.tmpl @@ -0,0 +1,71 @@ +{{- if .Settings.ShowInputs -}} + {{- if .Settings.ShowRequired -}} + {{ indent 0 "#" }} Required Inputs + {{ if not .Module.RequiredInputs }} + No required inputs. + {{ else }} + The following input variables are required: + {{- range .Module.RequiredInputs }} + {{ printf "\n" }} + {{ indent 1 "#" }} {{ anchorName "input" .Name }} + + Description: {{ tostring .Description | sanitizeDoc }} + + {{ if $.Settings.ShowType -}} + Type: {{ tostring .Type | type }} + {{- end }} + + {{ if $.Settings.ShowDefault }} + {{ if or .HasDefault (not isRequired) }} + Default: {{ default "n/a" .GetValue | value }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{ indent 0 "#" }} Optional Inputs + {{ if not .Module.OptionalInputs }} + No optional inputs. + {{ else }} + The following input variables are optional (have default values): + {{- range .Module.OptionalInputs }} + {{ printf "\n" }} + {{ indent 1 "#" }} {{ anchorName "input" .Name }} + + Description: {{ tostring .Description | sanitizeDoc }} + + {{ if $.Settings.ShowType -}} + Type: {{ tostring .Type | type }} + {{- end }} + + {{ if $.Settings.ShowDefault }} + {{ if or .HasDefault (not isRequired) }} + Default: {{ default "n/a" .GetValue | value }} + {{- end }} + {{- end }} + {{- end }} + {{ end }} + {{ else -}} + {{ indent 0 "#" }} Inputs + {{ if not .Module.Inputs }} + No inputs. + {{ else }} + The following input variables are supported: + {{- range .Module.Inputs }} + {{ printf "\n" }} + {{ indent 1 "#" }} {{ anchorName "input" .Name }} + + Description: {{ tostring .Description | sanitizeDoc }} + + {{ if $.Settings.ShowType -}} + Type: {{ tostring .Type | type }} + {{- end }} + + {{ if $.Settings.ShowDefault }} + {{ if or .HasDefault (not isRequired) }} + Default: {{ default "n/a" .GetValue | value }} + {{- end }} + {{- end }} + {{- end }} + {{ end }} + {{- end }} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/markdown_document_modules.tmpl b/internal/format/templates/markdown_document_modules.tmpl new file mode 100644 index 0000000..ad7af24 --- /dev/null +++ b/internal/format/templates/markdown_document_modules.tmpl @@ -0,0 +1,17 @@ +{{- if .Settings.ShowModuleCalls -}} + {{ indent 0 "#" }} Modules + {{ if not .Module.ModuleCalls }} + No modules. + {{ else }} + The following Modules are called: + {{- range .Module.ModuleCalls }} + + {{ indent 1 "#" }} {{ anchorName "module" .Name }} + + Source: {{ .Source }} + + Version: {{ .Version }} + + {{ end }} + {{ end }} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/markdown_document_outputs.tmpl b/internal/format/templates/markdown_document_outputs.tmpl new file mode 100644 index 0000000..47d8cf5 --- /dev/null +++ b/internal/format/templates/markdown_document_outputs.tmpl @@ -0,0 +1,23 @@ +{{- if .Settings.ShowOutputs -}} + {{ indent 0 "#" }} Outputs + {{ if not .Module.Outputs }} + No outputs. + {{ else }} + The following outputs are exported: + {{- range .Module.Outputs }} + + {{ indent 1 "#" }} {{ anchorName "output" .Name }} + + Description: {{ tostring .Description | sanitizeDoc }} + + {{ if $.Settings.OutputValues }} + {{- $sensitive := ternary .Sensitive "" .GetValue -}} + Value: {{ value $sensitive | sanitizeDoc }} + + {{ if $.Settings.ShowSensitivity -}} + Sensitive: {{ ternary (.Sensitive) "yes" "no" }} + {{- end }} + {{ end }} + {{ end }} + {{ end }} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/markdown_document_providers.tmpl b/internal/format/templates/markdown_document_providers.tmpl new file mode 100644 index 0000000..d462d67 --- /dev/null +++ b/internal/format/templates/markdown_document_providers.tmpl @@ -0,0 +1,12 @@ +{{- if .Settings.ShowProviders -}} + {{ indent 0 "#" }} Providers + {{ if not .Module.Providers }} + No providers. + {{ else }} + The following providers are used by this module: + {{- range .Module.Providers }} + {{ $version := ternary (tostring .Version) (printf " (%s)" .Version) "" }} + - {{ anchorName "provider" .FullName }}{{ $version }} + {{- end }} + {{ end }} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/markdown_document_requirements.tmpl b/internal/format/templates/markdown_document_requirements.tmpl new file mode 100644 index 0000000..db01b64 --- /dev/null +++ b/internal/format/templates/markdown_document_requirements.tmpl @@ -0,0 +1,12 @@ +{{- if .Settings.ShowRequirements -}} + {{ indent 0 "#" }} Requirements + {{ if not .Module.Requirements }} + No requirements. + {{ else }} + The following requirements are needed by this module: + {{- range .Module.Requirements }} + {{ $version := ternary (tostring .Version) (printf " (%s)" .Version) "" }} + - {{ anchorName "requirement" .Name }}{{ $version }} + {{- end }} + {{ end }} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/markdown_document_resources.tmpl b/internal/format/templates/markdown_document_resources.tmpl new file mode 100644 index 0000000..caa187e --- /dev/null +++ b/internal/format/templates/markdown_document_resources.tmpl @@ -0,0 +1,19 @@ +{{- if or .Settings.ShowResources .Settings.ShowDataSources -}} + {{ indent 0 "#" }} Resources + {{ if not .Module.Resources }} + No resources. + {{ else }} + The following resources are used by this module: + {{ range .Module.Resources }} + {{- $isResource := and $.Settings.ShowResources ( eq "resource" (printf "%s" .GetMode)) }} + {{- $isDataResource := and $.Settings.ShowDataSources ( eq "data source" (printf "%s" .GetMode)) }} + {{- if or $isResource $isDataResource }} + {{ if eq (len .URL) 0 }} + - {{ .Spec }} {{ printf "(%s)" .GetMode -}} + {{- else -}} + - [{{ .Spec }}]({{ .URL }}) {{ printf "(%s)" .GetMode -}} + {{- end }} + {{- end }} + {{- end }} + {{ end }} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/markdown_table.tmpl b/internal/format/templates/markdown_table.tmpl index 9a628ad..690ebb8 100644 --- a/internal/format/templates/markdown_table.tmpl +++ b/internal/format/templates/markdown_table.tmpl @@ -1,121 +1,8 @@ -{{- if .Settings.ShowHeader -}} - {{- with .Module.Header -}} - {{ sanitizeSection . }} - {{ printf "\n" }} - {{- end -}} -{{ end -}} - -{{- if .Settings.ShowRequirements -}} - {{ indent 0 "#" }} Requirements - {{ if not .Module.Requirements }} - No requirements. - {{ else }} - | Name | Version | - |------|---------| - {{- range .Module.Requirements }} - | {{ anchorName "requirement" .Name }} | {{ tostring .Version | default "n/a" }} | - {{- end }} - {{ end }} -{{ end -}} - -{{- if .Settings.ShowProviders -}} - {{ indent 0 "#" }} Providers - {{ if not .Module.Providers }} - No providers. - {{ else }} - | Name | Version | - |------|---------| - {{- range .Module.Providers }} - | {{ anchorName "provider" .FullName }} | {{ tostring .Version | default "n/a" }} | - {{- end }} - {{ end }} -{{ end -}} - -{{- if .Settings.ShowModuleCalls -}} - {{ indent 0 "#" }} Modules - {{ if not .Module.ModuleCalls }} - No modules. - {{ else }} - | Name | Source | Version | - |------|--------|---------| - {{- range .Module.ModuleCalls }} - | {{ anchorName "module" .Name }} | {{ .Source }} | {{ .Version }} | - {{- end }} - {{ end }} -{{ end -}} - -{{- if or .Settings.ShowResources .Settings.ShowDataSources -}} - {{ indent 0 "#" }} Resources - {{ if not .Module.Resources }} - No resources. - {{ else }} - | Name | Type | - |------|------| - {{- range .Module.Resources }} - {{- $isResource := and $.Settings.ShowResources ( eq "resource" (printf "%s" .GetMode)) }} - {{- $isDataResource := and $.Settings.ShowDataSources ( eq "data source" (printf "%s" .GetMode)) }} - {{- if or $isResource $isDataResource }} - {{ if eq (len .URL) 0 }} - | {{ .Spec }} | {{ .GetMode }} | - {{- else -}} - | [{{ .Spec }}]({{ .URL }}) | {{ .GetMode }} | - {{- end }} - {{- end }} - {{- end }} - {{ end }} -{{ end -}} - -{{- if .Settings.ShowInputs -}} - {{ indent 0 "#" }} Inputs - {{ if not .Module.Inputs }} - No inputs. - {{ else }} - | Name | Description | - {{- if .Settings.ShowType }} Type |{{ end }} - {{- if .Settings.ShowDefault }} Default |{{ end }} - {{- if .Settings.ShowRequired }} Required |{{ end }} - |------|-------------| - {{- if .Settings.ShowType }}------|{{ end }} - {{- if .Settings.ShowDefault }}---------|{{ end }} - {{- if .Settings.ShowRequired }}:--------:|{{ end }} - {{- range .Module.Inputs }} - | {{ anchorName "input" .Name }} | {{ tostring .Description | sanitizeMarkdownTbl }} | - {{- if $.Settings.ShowType -}} - {{ printf " " }}{{ tostring .Type | type | sanitizeMarkdownTbl }} | - {{- end -}} - {{- if $.Settings.ShowDefault -}} - {{ printf " " }}{{ value .GetValue | sanitizeMarkdownTbl }} | - {{- end -}} - {{- if $.Settings.ShowRequired -}} - {{ printf " " }}{{ ternary .Required "yes" "no" }} | - {{- end -}} - {{- end }} - {{ end }} -{{ end -}} - -{{- if .Settings.ShowOutputs -}} - {{ indent 0 "#" }} Outputs - {{ if not .Module.Outputs }} - No outputs. - {{ else }} - | Name | Description |{{ if .Settings.OutputValues }} Value |{{ if $.Settings.ShowSensitivity }} Sensitive |{{ end }}{{ end }} - |------|-------------|{{ if .Settings.OutputValues }}-------|{{ if $.Settings.ShowSensitivity }}:---------:|{{ end }}{{ end }} - {{- range .Module.Outputs }} - | {{ anchorName "output" .Name }} | {{ tostring .Description | sanitizeMarkdownTbl }} | - {{- if $.Settings.OutputValues -}} - {{- $sensitive := ternary .Sensitive "" .GetValue -}} - {{ printf " " }}{{ value $sensitive | sanitizeMarkdownTbl }} | - {{- if $.Settings.ShowSensitivity -}} - {{ printf " " }}{{ ternary .Sensitive "yes" "no" }} | - {{- end -}} - {{- end -}} - {{- end }} - {{ end }} -{{ end -}} - -{{- if .Settings.ShowFooter -}} - {{- with .Module.Footer -}} - {{ sanitizeSection . }} - {{ printf "\n" }} - {{- end -}} -{{ end -}} \ No newline at end of file +{{- template "header" . -}} +{{- template "requirements" . -}} +{{- template "providers" . -}} +{{- template "modules" . -}} +{{- template "resources" . -}} +{{- template "inputs" . -}} +{{- template "outputs" . -}} +{{- template "footer" . -}} \ No newline at end of file diff --git a/internal/format/templates/markdown_table_footer.tmpl b/internal/format/templates/markdown_table_footer.tmpl new file mode 100644 index 0000000..5f9cdcd --- /dev/null +++ b/internal/format/templates/markdown_table_footer.tmpl @@ -0,0 +1,6 @@ +{{- if .Settings.ShowFooter -}} + {{- with .Module.Footer -}} + {{ sanitizeSection . }} + {{ printf "\n" }} + {{- end -}} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/markdown_table_header.tmpl b/internal/format/templates/markdown_table_header.tmpl new file mode 100644 index 0000000..5e2d737 --- /dev/null +++ b/internal/format/templates/markdown_table_header.tmpl @@ -0,0 +1,6 @@ +{{- if .Settings.ShowHeader -}} + {{- with .Module.Header -}} + {{ sanitizeSection . }} + {{ printf "\n" }} + {{- end -}} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/markdown_table_inputs.tmpl b/internal/format/templates/markdown_table_inputs.tmpl new file mode 100644 index 0000000..72bdde0 --- /dev/null +++ b/internal/format/templates/markdown_table_inputs.tmpl @@ -0,0 +1,27 @@ +{{- if .Settings.ShowInputs -}} + {{ indent 0 "#" }} Inputs + {{ if not .Module.Inputs }} + No inputs. + {{ else }} + | Name | Description | + {{- if .Settings.ShowType }} Type |{{ end }} + {{- if .Settings.ShowDefault }} Default |{{ end }} + {{- if .Settings.ShowRequired }} Required |{{ end }} + |------|-------------| + {{- if .Settings.ShowType }}------|{{ end }} + {{- if .Settings.ShowDefault }}---------|{{ end }} + {{- if .Settings.ShowRequired }}:--------:|{{ end }} + {{- range .Module.Inputs }} + | {{ anchorName "input" .Name }} | {{ tostring .Description | sanitizeMarkdownTbl }} | + {{- if $.Settings.ShowType -}} + {{ printf " " }}{{ tostring .Type | type | sanitizeMarkdownTbl }} | + {{- end -}} + {{- if $.Settings.ShowDefault -}} + {{ printf " " }}{{ value .GetValue | sanitizeMarkdownTbl }} | + {{- end -}} + {{- if $.Settings.ShowRequired -}} + {{ printf " " }}{{ ternary .Required "yes" "no" }} | + {{- end -}} + {{- end }} + {{ end }} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/markdown_table_modules.tmpl b/internal/format/templates/markdown_table_modules.tmpl new file mode 100644 index 0000000..2a7b300 --- /dev/null +++ b/internal/format/templates/markdown_table_modules.tmpl @@ -0,0 +1,12 @@ +{{- if .Settings.ShowModuleCalls -}} + {{ indent 0 "#" }} Modules + {{ if not .Module.ModuleCalls }} + No modules. + {{ else }} + | Name | Source | Version | + |------|--------|---------| + {{- range .Module.ModuleCalls }} + | {{ anchorName "module" .Name }} | {{ .Source }} | {{ .Version }} | + {{- end }} + {{ end }} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/markdown_table_outputs.tmpl b/internal/format/templates/markdown_table_outputs.tmpl new file mode 100644 index 0000000..7d42d01 --- /dev/null +++ b/internal/format/templates/markdown_table_outputs.tmpl @@ -0,0 +1,19 @@ +{{- if .Settings.ShowOutputs -}} + {{ indent 0 "#" }} Outputs + {{ if not .Module.Outputs }} + No outputs. + {{ else }} + | Name | Description |{{ if .Settings.OutputValues }} Value |{{ if $.Settings.ShowSensitivity }} Sensitive |{{ end }}{{ end }} + |------|-------------|{{ if .Settings.OutputValues }}-------|{{ if $.Settings.ShowSensitivity }}:---------:|{{ end }}{{ end }} + {{- range .Module.Outputs }} + | {{ anchorName "output" .Name }} | {{ tostring .Description | sanitizeMarkdownTbl }} | + {{- if $.Settings.OutputValues -}} + {{- $sensitive := ternary .Sensitive "" .GetValue -}} + {{ printf " " }}{{ value $sensitive | sanitizeMarkdownTbl }} | + {{- if $.Settings.ShowSensitivity -}} + {{ printf " " }}{{ ternary .Sensitive "yes" "no" }} | + {{- end -}} + {{- end -}} + {{- end }} + {{ end }} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/markdown_table_providers.tmpl b/internal/format/templates/markdown_table_providers.tmpl new file mode 100644 index 0000000..0b1c176 --- /dev/null +++ b/internal/format/templates/markdown_table_providers.tmpl @@ -0,0 +1,12 @@ +{{- if .Settings.ShowProviders -}} + {{ indent 0 "#" }} Providers + {{ if not .Module.Providers }} + No providers. + {{ else }} + | Name | Version | + |------|---------| + {{- range .Module.Providers }} + | {{ anchorName "provider" .FullName }} | {{ tostring .Version | default "n/a" }} | + {{- end }} + {{ end }} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/markdown_table_requirements.tmpl b/internal/format/templates/markdown_table_requirements.tmpl new file mode 100644 index 0000000..674b14f --- /dev/null +++ b/internal/format/templates/markdown_table_requirements.tmpl @@ -0,0 +1,12 @@ +{{- if .Settings.ShowRequirements -}} + {{ indent 0 "#" }} Requirements + {{ if not .Module.Requirements }} + No requirements. + {{ else }} + | Name | Version | + |------|---------| + {{- range .Module.Requirements }} + | {{ anchorName "requirement" .Name }} | {{ tostring .Version | default "n/a" }} | + {{- end }} + {{ end }} +{{ end -}} \ No newline at end of file diff --git a/internal/format/templates/markdown_table_resources.tmpl b/internal/format/templates/markdown_table_resources.tmpl new file mode 100644 index 0000000..4e15ed2 --- /dev/null +++ b/internal/format/templates/markdown_table_resources.tmpl @@ -0,0 +1,20 @@ +{{- if or .Settings.ShowResources .Settings.ShowDataSources -}} + {{ indent 0 "#" }} Resources + {{ if not .Module.Resources }} + No resources. + {{ else }} + | Name | Type | + |------|------| + {{- range .Module.Resources }} + {{- $isResource := and $.Settings.ShowResources ( eq "resource" (printf "%s" .GetMode)) }} + {{- $isDataResource := and $.Settings.ShowDataSources ( eq "data source" (printf "%s" .GetMode)) }} + {{- if or $isResource $isDataResource }} + {{ if eq (len .URL) 0 }} + | {{ .Spec }} | {{ .GetMode }} | + {{- else -}} + | [{{ .Spec }}]({{ .URL }}) | {{ .GetMode }} | + {{- end }} + {{- end }} + {{- end }} + {{ end }} +{{ end -}} \ No newline at end of file diff --git a/internal/format/tfvars_hcl.go b/internal/format/tfvars_hcl.go index 2d044d6..52b7445 100644 --- a/internal/format/tfvars_hcl.go +++ b/internal/format/tfvars_hcl.go @@ -28,6 +28,7 @@ var tfvarsHCLTpl []byte // TfvarsHCL represents Terraform tfvars HCL format. type TfvarsHCL struct { template *template.Template + settings *print.Settings } var padding []int @@ -57,17 +58,23 @@ func NewTfvarsHCL(settings *print.Settings) print.Engine { }) return &TfvarsHCL{ template: tt, + settings: settings, } } -// Print a Terraform module as Terraform tfvars HCL. -func (h *TfvarsHCL) Print(module *terraform.Module, settings *print.Settings) (string, error) { - alignments(module.Inputs, settings) - rendered, err := h.template.Render(module) +// Generate a Terraform module as Terraform tfvars HCL. +func (h *TfvarsHCL) Generate(module *terraform.Module) (*print.Generator, error) { + alignments(module.Inputs, h.settings) + + rendered, err := h.template.Render("tfvars", module) if err != nil { - return "", err + return nil, err } - return strings.TrimSuffix(sanitize(rendered), "\n"), nil + + return print.NewGenerator( + "tfvars hcl", + print.WithContent(strings.TrimSuffix(sanitize(rendered), "\n")), + ), nil } func isMultilineFormat(input *terraform.Input) bool { diff --git a/internal/format/tfvars_hcl_test.go b/internal/format/tfvars_hcl_test.go index 156d3f1..0129b26 100644 --- a/internal/format/tfvars_hcl_test.go +++ b/internal/format/tfvars_hcl_test.go @@ -104,8 +104,12 @@ func TestTfvarsHcl(t *testing.T) { module, err := testutil.GetModule(options) assert.Nil(err) - printer := NewTfvarsHCL(&tt.settings) - actual, err := printer.Print(module, &tt.settings) + formatter := NewTfvarsHCL(&tt.settings) + + generator, err := formatter.Generate(module) + assert.Nil(err) + + actual, err := generator.ExecuteTemplate("") assert.Nil(err) assert.Equal(expected, actual) diff --git a/internal/format/tfvars_json.go b/internal/format/tfvars_json.go index 9ecdb47..0f16d5a 100644 --- a/internal/format/tfvars_json.go +++ b/internal/format/tfvars_json.go @@ -22,15 +22,19 @@ import ( ) // TfvarsJSON represents Terraform tfvars JSON format. -type TfvarsJSON struct{} +type TfvarsJSON struct { + settings *print.Settings +} // NewTfvarsJSON returns new instance of TfvarsJSON. func NewTfvarsJSON(settings *print.Settings) print.Engine { - return &TfvarsJSON{} + return &TfvarsJSON{ + settings: settings, + } } -// Print a Terraform module as Terraform tfvars JSON. -func (j *TfvarsJSON) Print(module *terraform.Module, settings *print.Settings) (string, error) { +// Generate a Terraform module as Terraform tfvars JSON. +func (j *TfvarsJSON) Generate(module *terraform.Module) (*print.Generator, error) { copy := orderedmap.New() copy.SetEscapeHTML(false) for _, i := range module.Inputs { @@ -45,10 +49,14 @@ func (j *TfvarsJSON) Print(module *terraform.Module, settings *print.Settings) ( err := encoder.Encode(copy) if err != nil { - return "", err + return nil, err } - return strings.TrimSuffix(buffer.String(), "\n"), nil + return print.NewGenerator( + "tfvars json", + print.WithContent(strings.TrimSuffix(buffer.String(), "\n")), + ), nil + } func init() { diff --git a/internal/format/tfvars_json_test.go b/internal/format/tfvars_json_test.go index 1cb025f..a8277c7 100644 --- a/internal/format/tfvars_json_test.go +++ b/internal/format/tfvars_json_test.go @@ -91,8 +91,12 @@ func TestTfvarsJson(t *testing.T) { module, err := testutil.GetModule(options) assert.Nil(err) - printer := NewTfvarsJSON(&tt.settings) - actual, err := printer.Print(module, &tt.settings) + formatter := NewTfvarsJSON(&tt.settings) + + generator, err := formatter.Generate(module) + assert.Nil(err) + + actual, err := generator.ExecuteTemplate("") assert.Nil(err) assert.Equal(expected, actual) diff --git a/internal/format/toml.go b/internal/format/toml.go index 638f2c7..239d861 100644 --- a/internal/format/toml.go +++ b/internal/format/toml.go @@ -21,15 +21,19 @@ import ( ) // TOML represents TOML format. -type TOML struct{} +type TOML struct { + settings *print.Settings +} // NewTOML returns new instance of TOML. func NewTOML(settings *print.Settings) print.Engine { - return &TOML{} + return &TOML{ + settings: settings, + } } -// Print a Terraform module as toml. -func (t *TOML) Print(module *terraform.Module, settings *print.Settings) (string, error) { +// Generate a Terraform module as toml. +func (t *TOML) Generate(module *terraform.Module) (*print.Generator, error) { copy := &terraform.Module{ Header: "", Footer: "", @@ -41,16 +45,20 @@ func (t *TOML) Print(module *terraform.Module, settings *print.Settings) (string Resources: make([]*terraform.Resource, 0), } - print.CopySections(settings, module, copy) + print.CopySections(t.settings, module, copy) buffer := new(bytes.Buffer) encoder := toml.NewEncoder(buffer) err := encoder.Encode(copy) if err != nil { - return "", err + return nil, err } - return strings.TrimSuffix(buffer.String(), "\n"), nil + return print.NewGenerator( + "toml", + print.WithContent(strings.TrimSuffix(buffer.String(), "\n")), + ), nil + } func init() { diff --git a/internal/format/toml_test.go b/internal/format/toml_test.go index b35008f..3143d7c 100644 --- a/internal/format/toml_test.go +++ b/internal/format/toml_test.go @@ -114,8 +114,12 @@ func TestToml(t *testing.T) { module, err := testutil.GetModule(options) assert.Nil(err) - printer := NewTOML(&tt.settings) - actual, err := printer.Print(module, &tt.settings) + formatter := NewTOML(&tt.settings) + + generator, err := formatter.Generate(module) + assert.Nil(err) + + actual, err := generator.ExecuteTemplate("") assert.Nil(err) assert.Equal(expected, actual) diff --git a/internal/format/util.go b/internal/format/util.go index 89ad9c4..eb09a39 100644 --- a/internal/format/util.go +++ b/internal/format/util.go @@ -11,9 +11,14 @@ the root directory of this source tree. package format import ( + "embed" "fmt" + "io/fs" + "path/filepath" "regexp" "strings" + + "github.com/terraform-docs/terraform-docs/internal/template" ) // sanitize cleans a Markdown document to soothe linters. @@ -63,3 +68,35 @@ func printFencedAsciidocCodeBlock(code string, language string) (string, bool) { } return fmt.Sprintf("`%s`", code), false } + +// readTemplateItems reads all static formatter .tmpl files prefixed by specific string +// from an embed file system. +func readTemplateItems(efs embed.FS, prefix string) []*template.Item { + items := make([]*template.Item, 0) + + files, err := fs.ReadDir(efs, "templates") + if err != nil { + return items + } + + for _, f := range files { + content, err := efs.ReadFile(filepath.Join("templates", f.Name())) + if err != nil { + continue + } + + name := f.Name() + name = strings.ReplaceAll(name, prefix, "") + name = strings.ReplaceAll(name, "_", "") + name = strings.ReplaceAll(name, ".tmpl", "") + if name == "" { + name = "all" + } + + items = append(items, &template.Item{ + Name: name, + Text: string(content), + }) + } + return items +} diff --git a/internal/format/xml.go b/internal/format/xml.go index 77a2db0..53daba4 100644 --- a/internal/format/xml.go +++ b/internal/format/xml.go @@ -19,15 +19,19 @@ import ( ) // XML represents XML format. -type XML struct{} +type XML struct { + settings *print.Settings +} // NewXML returns new instance of XML. func NewXML(settings *print.Settings) print.Engine { - return &XML{} + return &XML{ + settings: settings, + } } -// Print a Terraform module as xml. -func (x *XML) Print(module *terraform.Module, settings *print.Settings) (string, error) { +// Generate a Terraform module as xml. +func (x *XML) Generate(module *terraform.Module) (*print.Generator, error) { copy := &terraform.Module{ Header: "", Footer: "", @@ -39,14 +43,17 @@ func (x *XML) Print(module *terraform.Module, settings *print.Settings) (string, Resources: make([]*terraform.Resource, 0), } - print.CopySections(settings, module, copy) + print.CopySections(x.settings, module, copy) out, err := xml.MarshalIndent(copy, "", " ") if err != nil { - return "", err + return nil, err } - return strings.TrimSuffix(string(out), "\n"), nil + return print.NewGenerator( + "xml", + print.WithContent(strings.TrimSuffix(string(out), "\n")), + ), nil } func init() { diff --git a/internal/format/xml_test.go b/internal/format/xml_test.go index 71311d7..c8c78c8 100644 --- a/internal/format/xml_test.go +++ b/internal/format/xml_test.go @@ -114,8 +114,12 @@ func TestXml(t *testing.T) { module, err := testutil.GetModule(options) assert.Nil(err) - printer := NewXML(&tt.settings) - actual, err := printer.Print(module, &tt.settings) + formatter := NewXML(&tt.settings) + + generator, err := formatter.Generate(module) + assert.Nil(err) + + actual, err := generator.ExecuteTemplate("") assert.Nil(err) assert.Equal(expected, actual) diff --git a/internal/format/yaml.go b/internal/format/yaml.go index 8cf608c..d5b1a6c 100644 --- a/internal/format/yaml.go +++ b/internal/format/yaml.go @@ -21,15 +21,19 @@ import ( ) // YAML represents YAML format. -type YAML struct{} +type YAML struct { + settings *print.Settings +} // NewYAML returns new instance of YAML. func NewYAML(settings *print.Settings) print.Engine { - return &YAML{} + return &YAML{ + settings: settings, + } } -// Print a Terraform module as yaml. -func (y *YAML) Print(module *terraform.Module, settings *print.Settings) (string, error) { +// Generate a Terraform module as yaml. +func (y *YAML) Generate(module *terraform.Module) (*print.Generator, error) { copy := &terraform.Module{ Header: "", Footer: "", @@ -41,7 +45,7 @@ func (y *YAML) Print(module *terraform.Module, settings *print.Settings) (string Resources: make([]*terraform.Resource, 0), } - print.CopySections(settings, module, copy) + print.CopySections(y.settings, module, copy) buffer := new(bytes.Buffer) @@ -50,10 +54,13 @@ func (y *YAML) Print(module *terraform.Module, settings *print.Settings) (string err := encoder.Encode(copy) if err != nil { - return "", err + return nil, err } - return strings.TrimSuffix(buffer.String(), "\n"), nil + return print.NewGenerator( + "yaml", + print.WithContent(strings.TrimSuffix(buffer.String(), "\n")), + ), nil } func init() { diff --git a/internal/format/yaml_test.go b/internal/format/yaml_test.go index ddcecd6..4d60c41 100644 --- a/internal/format/yaml_test.go +++ b/internal/format/yaml_test.go @@ -114,8 +114,12 @@ func TestYaml(t *testing.T) { module, err := testutil.GetModule(options) assert.Nil(err) - printer := NewYAML(&tt.settings) - actual, err := printer.Print(module, &tt.settings) + formatter := NewYAML(&tt.settings) + + generator, err := formatter.Generate(module) + assert.Nil(err) + + actual, err := generator.ExecuteTemplate("") assert.Nil(err) assert.Equal(expected, actual) diff --git a/internal/print/engine.go b/internal/print/engine.go index a57640d..661cd29 100644 --- a/internal/print/engine.go +++ b/internal/print/engine.go @@ -14,7 +14,7 @@ import ( "github.com/terraform-docs/terraform-docs/internal/terraform" ) -// Engine represents a printer format engine (e.g. json, table, yaml, ...) +// Engine represents a format engine (e.g. json, table, yaml, ...) type Engine interface { - Print(*terraform.Module, *Settings) (string, error) + Generate(*terraform.Module) (*Generator, error) } diff --git a/internal/print/generator.go b/internal/print/generator.go new file mode 100644 index 0000000..475c4c3 --- /dev/null +++ b/internal/print/generator.go @@ -0,0 +1,185 @@ +/* +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 print + +import ( + "bytes" + "text/template" +) + +// GenerateFunc configures Generator. +type GenerateFunc func(*Generator) + +// WithContent specifies how the Generator should add content. +func WithContent(content string) GenerateFunc { + return func(g *Generator) { + g.content = content + } +} + +// WithHeader specifies how the Generator should add Header. +func WithHeader(header string) GenerateFunc { + return func(g *Generator) { + g.Header = header + } +} + +// WithFooter specifies how the Generator should add Footer. +func WithFooter(footer string) GenerateFunc { + return func(g *Generator) { + g.Footer = footer + } +} + +// WithInputs specifies how the Generator should add Inputs. +func WithInputs(inputs string) GenerateFunc { + return func(g *Generator) { + g.Inputs = inputs + } +} + +// WithModules specifies how the Generator should add Modules. +func WithModules(modules string) GenerateFunc { + return func(g *Generator) { + g.Modules = modules + } +} + +// WithOutputs specifies how the Generator should add Outputs. +func WithOutputs(outputs string) GenerateFunc { + return func(g *Generator) { + g.Outputs = outputs + } +} + +// WithProviders specifies how the Generator should add Providers. +func WithProviders(providers string) GenerateFunc { + return func(g *Generator) { + g.Providers = providers + } +} + +// WithRequirements specifies how the Generator should add Requirements. +func WithRequirements(requirements string) GenerateFunc { + return func(g *Generator) { + g.Requirements = requirements + } +} + +// WithResources specifies how the Generator should add Resources. +func WithResources(resources string) GenerateFunc { + return func(g *Generator) { + g.Resources = resources + } +} + +// Generator represents all the sections that can be generated for a Terraform +// modules (e.g. header, footer, inputs, etc). All these sections are being +// generated individually and if no content template was passed they will be +// combined together with a predefined order. +// +// On the other hand these sections can individually be used in content template +// to form a custom format (and order). +// +// Note that the notion of custom content template will be ignored for incompatible +// formatters and custom plugins. Compatible formatters are: +// +// - asciidoc document +// - asciidoc table +// - markdown document +// - markdown table +type Generator struct { + Header string + Footer string + Inputs string + Modules string + Outputs string + Providers string + Requirements string + Resources string + + content string // all the content combined + formatter string // name of the formatter +} + +// NewGenerator returns a Generator for specific formatter name and with +// provided sets of GeneratorFunc functions to build and add individual +// sections. +func NewGenerator(name string, fns ...GenerateFunc) *Generator { + g := &Generator{ + formatter: name, + } + + for _, fn := range fns { + fn(g) + } + + return g +} + +// ExecuteTemplate applies the template with Generator known items. If template +// is empty Generator.content is returned as is. If template is not empty this +// still returns Generator.content for incompatible formatters. +func (g *Generator) ExecuteTemplate(contentTmpl string) (string, error) { + if !g.isCompatible() { + return g.content, nil + } + + if contentTmpl == "" { + return g.content, nil + } + + var buf bytes.Buffer + + tmpl := template.New("content") + template.Must(tmpl.Parse(contentTmpl)) + + if err := tmpl.ExecuteTemplate(&buf, "content", g); err != nil { + return "", err + } + + return buf.String(), nil +} + +func (g *Generator) isCompatible() bool { + switch g.formatter { + case "asciidoc document", "asciidoc table", "markdown document", "markdown table": + return true + } + return false +} + +// GeneratorCallback renders a Terraform module and creates a GenerateFunc. +type GeneratorCallback func(string) GenerateFunc + +// ForEach section executes GeneratorCallback to render the content for that +// section and create corresponding GeneratorFunc. If there is any error in +// the executing the template for the section ForEach function immediately +// returns it and exit. +func ForEach(callback func(string, GeneratorCallback) error) error { + mappings := map[string]GeneratorCallback{ + "all": WithContent, + "header": WithHeader, + "footer": WithFooter, + "inputs": WithInputs, + "modules": WithModules, + "outputs": WithOutputs, + "providers": WithProviders, + "requirements": WithRequirements, + "resources": WithResources, + } + for name, fn := range mappings { + if err := callback(name, fn); err != nil { + return err + } + } + return nil +} diff --git a/internal/print/generator_test.go b/internal/print/generator_test.go new file mode 100644 index 0000000..eb82ecc --- /dev/null +++ b/internal/print/generator_test.go @@ -0,0 +1,229 @@ +/* +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 print + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestIsCompatible(t *testing.T) { + tests := map[string]struct { + expected bool + }{ + "asciidoc document": { + expected: true, + }, + "asciidoc table": { + expected: true, + }, + "markdown document": { + expected: true, + }, + "markdown table": { + expected: true, + }, + "markdown": { + expected: false, + }, + "markdown-table": { + expected: false, + }, + "md": { + expected: false, + }, + "md tbl": { + expected: false, + }, + "md-tbl": { + expected: false, + }, + "json": { + expected: false, + }, + "yaml": { + expected: false, + }, + "xml": { + expected: false, + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + assert := assert.New(t) + + generator := NewGenerator(name) + actual := generator.isCompatible() + + assert.Equal(tt.expected, actual) + }) + } +} + +func TestExecuteTemplate(t *testing.T) { + header := "this is the header" + footer := "this is the footer" + tests := map[string]struct { + name string + content string + template string + expected string + wantErr bool + }{ + "Compatible without template": { + name: "markdown table", + content: "this is the header\nthis is the footer", + template: "", + expected: "this is the header\nthis is the footer", + wantErr: false, + }, + "Compatible with template not empty section": { + name: "markdown table", + content: "this is the header\nthis is the footer", + template: "{{ .Header }}", + expected: "this is the header", + wantErr: false, + }, + "Compatible with template empty section": { + name: "markdown table", + content: "this is the header\nthis is the footer", + template: "{{ .Inputs }}", + expected: "", + wantErr: false, + }, + "Compatible with template and unknown section": { + name: "markdown table", + content: "this is the header\nthis is the footer", + template: "{{ .Unknown }}", + expected: "", + wantErr: true, + }, + "Incompatible without template": { + name: "yaml", + content: "header: \"this is the header\"\nfooter: \"this is the footer\"", + template: "", + expected: "header: \"this is the header\"\nfooter: \"this is the footer\"", + wantErr: false, + }, + "Incompatible with template": { + name: "yaml", + content: "header: \"this is the header\"\nfooter: \"this is the footer\"", + template: "{{ .Header }}", + expected: "header: \"this is the header\"\nfooter: \"this is the footer\"", + wantErr: false, + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + assert := assert.New(t) + + generator := NewGenerator(tt.name) + generator.content = tt.content + generator.Header = header + generator.Footer = footer + + actual, err := generator.ExecuteTemplate(tt.template) + + if tt.wantErr { + assert.NotNil(err) + } else { + assert.Nil(err) + assert.Equal(tt.expected, actual) + } + }) + } +} + +func TestGeneratorFunc(t *testing.T) { + text := "foo" + tests := map[string]struct { + fn func(string) GenerateFunc + actual func(*Generator) string + }{ + "WithContent": { + fn: WithContent, + actual: func(g *Generator) string { return g.content }, + }, + "WithHeader": { + fn: WithHeader, + actual: func(g *Generator) string { return g.Header }, + }, + "WithFooter": { + fn: WithFooter, + actual: func(g *Generator) string { return g.Footer }, + }, + "WithInputs": { + fn: WithInputs, + actual: func(g *Generator) string { return g.Inputs }, + }, + "WithModules": { + fn: WithModules, + actual: func(g *Generator) string { return g.Modules }, + }, + "WithOutputs": { + fn: WithOutputs, + actual: func(g *Generator) string { return g.Outputs }, + }, + "WithProviders": { + fn: WithProviders, + actual: func(g *Generator) string { return g.Providers }, + }, + "WithRequirements": { + fn: WithRequirements, + actual: func(g *Generator) string { return g.Requirements }, + }, + "WithResources": { + fn: WithResources, + actual: func(g *Generator) string { return g.Resources }, + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + assert := assert.New(t) + + generator := NewGenerator(name, tt.fn(text)) + + assert.Equal(text, tt.actual(generator)) + }) + } +} + +func TestForEach(t *testing.T) { + // text := "foo" + fns := []GenerateFunc{} + ForEach(func(name string, fn GeneratorCallback) error { + fns = append(fns, fn(name)) + return nil + }) + + generator := NewGenerator("foo", fns...) + + tests := map[string]struct { + actual string + }{ + "all": {actual: generator.content}, + "header": {actual: generator.Header}, + "footer": {actual: generator.Footer}, + "inputs": {actual: generator.Inputs}, + "modules": {actual: generator.Modules}, + "outputs": {actual: generator.Outputs}, + "providers": {actual: generator.Providers}, + "requirements": {actual: generator.Requirements}, + "resources": {actual: generator.Resources}, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + assert := assert.New(t) + assert.Equal(name, tt.actual) + }) + } +} diff --git a/internal/template/template.go b/internal/template/template.go index 40c3a5c..d1e0681 100644 --- a/internal/template/template.go +++ b/internal/template/template.go @@ -84,6 +84,6 @@ func (t Template) CustomFunc(funcs gotemplate.FuncMap) { } // Render template with given Module struct. -func (t Template) Render(module *terraform.Module) (string, error) { - return t.engine.Render(module) +func (t Template) Render(name string, module *terraform.Module) (string, error) { + return t.engine.Render(name, module) } diff --git a/internal/template/template_test.go b/internal/template/template_test.go index 8469bd2..64c3eef 100644 --- a/internal/template/template_test.go +++ b/internal/template/template_test.go @@ -71,7 +71,7 @@ func TestTemplateRender(t *testing.T) { assert := assert.New(t) tpl := New(print.DefaultSettings(), tt.items...) tpl.CustomFunc(customFuncs) - rendered, err := tpl.Render(module) + rendered, err := tpl.Render("", module) if tt.wantErr { assert.NotNil(err) } else { diff --git a/scripts/docs/generate.go b/scripts/docs/generate.go index 4b174c2..bbf4058 100644 --- a/scripts/docs/generate.go +++ b/scripts/docs/generate.go @@ -185,7 +185,7 @@ func example(ref *reference) error { }, } - printer, err := format.Factory(ref.Name, settings) + formatter, err := format.Factory(ref.Name, settings) if err != nil { return err } @@ -195,7 +195,11 @@ func example(ref *reference) error { log.Fatal(err) } - output, err := printer.Print(tfmodule, settings) + generator, err := formatter.Generate(tfmodule) + if err != nil { + return err + } + output, err := generator.ExecuteTemplate("") if err != nil { return err }