Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: render templates refactoring #12

Merged
merged 1 commit into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions _examples/complex-fields.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ FieldNames uses field names as env names.
- `QUUX` - Quux is a field with a tag.
- `FOO_BAR` (default: `quuux`) - FooBar is a field with a default value.
- `REQUIRED` (**required**) - Required is a required field.

3 changes: 2 additions & 1 deletion _examples/complex.env
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,5 @@
## FieldNames uses field names as env names.
#
## Quux is a field with a tag.
# QUUX=""
# QUUX=""

1 change: 1 addition & 0 deletions _examples/complex.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ It is trying to cover all the possible cases.
FieldNames uses field names as env names.

- `QUUX` - Quux is a field with a tag.

1 change: 1 addition & 0 deletions _examples/complex.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ It is trying to cover all the possible cases.
FieldNames uses field names as env names.

* `QUUX` - Quux is a field with a tag.

5 changes: 4 additions & 1 deletion _examples/config.env
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,7 @@
# PORT=""
## Debug mode enabled.
## (default: 'false')
# DEBUG="false"
# DEBUG="false"
## Prefix for something.
# PREFIX=""

3 changes: 3 additions & 0 deletions _examples/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,7 @@ type Config struct {

// Debug mode enabled.
Debug bool `env:"DEBUG" envDefault:"false"`

// Prefix for something.
Prefix string `env:"PREFIX"`
}
1 change: 1 addition & 0 deletions _examples/config.html
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ <h2>Config</h2>
<li><code>HOST</code> (separated by "<code>;</code>", <strong>required</strong>) - Hosts name of hosts to listen on.</li>
<li><code>PORT</code> (<strong>required</strong>, non-empty) - Port to listen on.</li>
<li><code>DEBUG</code> (default: <code>false</code>) - Debug mode enabled.</li>
<li><code>PREFIX</code> - Prefix for something.</li>
</ul>

</article>
Expand Down
2 changes: 2 additions & 0 deletions _examples/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ using the commands below.
- `HOST` (separated by `;`, **required**) - Hosts name of hosts to listen on.
- `PORT` (**required**, non-empty) - Port to listen on.
- `DEBUG` (default: `false`) - Debug mode enabled.
- `PREFIX` - Prefix for something.

2 changes: 2 additions & 0 deletions _examples/config.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ using the commands below.
* `HOST` (separated by `;`, required) - Hosts name of hosts to listen on.
* `PORT` (required, non-empty) - Port to listen on.
* `DEBUG` (default: `false`) - Debug mode enabled.
* `PREFIX` - Prefix for something.

3 changes: 2 additions & 1 deletion _examples/envprefix.env
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,5 @@
## (default: '30')
# SERVER_TIMEOUT_WRITE="30"
## Debug is the debug flag
# DEBUG=""
# DEBUG=""

1 change: 1 addition & 0 deletions _examples/envprefix.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ Settings is the application settings.
- `SERVER_TIMEOUT_READ` (default: `30`) - Read is the read timeout
- `SERVER_TIMEOUT_WRITE` (default: `30`) - Write is the write timeout
- `DEBUG` - Debug is the debug flag

1 change: 1 addition & 0 deletions _examples/envprefix.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ Settings is the application settings.
* `SERVER_TIMEOUT_READ` (default: `30`) - Read is the read timeout
* `SERVER_TIMEOUT_WRITE` (default: `30`) - Write is the write timeout
* `DEBUG` - Debug is the debug flag

1 change: 1 addition & 0 deletions _examples/x_complex.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ It is trying to cover all the possible cases.
FieldNames uses field names as env names.

- `X_QUUX` - Quux is a field with a tag.

24 changes: 15 additions & 9 deletions generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import (
)

type generator struct {
fileName string
execLine int
targetType string
all bool // generate documentation for all types in the file
tmpl template
prefix string
noStyles bool
fieldNames bool
fileName string
execLine int
targetType string
all bool // generate documentation for all types in the file
tmpl template
renderConfig renderConfig
prefix string
noStyles bool
fieldNames bool
}

type generatorOption func(*generator) error
Expand Down Expand Up @@ -41,12 +42,16 @@ func withFormat(formatName string) generatorOption {
fallthrough
case "markdown":
g.tmpl = tmplMarkdown
g.renderConfig = renderMarkdown
case "plaintext":
g.tmpl = tmplPlaintext
g.renderConfig = renderPlaintext
case "html":
g.tmpl = tmplHTML
g.renderConfig = renderHTML
case "dotenv":
g.tmpl = tmplDotEnv
g.renderConfig = renderDotenv
default:
return fmt.Errorf("unknown format: %s", formatName)
}
Expand Down Expand Up @@ -84,6 +89,7 @@ func newGenerator(fileName string, execLine int, opts ...generatorOption) (*gene
}
if g.tmpl == nil {
g.tmpl = tmplMarkdown
g.renderConfig = renderMarkdown
}
return g, nil
}
Expand All @@ -95,7 +101,7 @@ func (g *generator) generate(out io.Writer) error {
return fmt.Errorf("inspect file: %w", err)
}
renderer := templateRenderer(g.tmpl)
rctx := newRenderContext(data, g.prefix, g.noStyles)
rctx := newRenderContext(data, g.renderConfig, g.prefix, g.noStyles)
if err := renderer(rctx, out); err != nil {
return err
}
Expand Down
90 changes: 65 additions & 25 deletions render.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
package main

import (
"embed"
"fmt"
"io"
"strings"

htmltmpl "html/template"
texttmpl "text/template"
)

type renderSection struct {
Expand Down Expand Up @@ -41,16 +36,31 @@ func (i renderItem) Children(indentInc int) []renderItem {
return res
}

type renderItemConfigt struct {
SeparatorFormat string
SeparatorDefault string
OptRequired string
OptExpand string
OptNonEmpty string
OptFromFile string
EnvDefaultFormat string
}
type renderConfig struct {
Item renderItemConfigt
}

type renderContext struct {
Title string
Sections []renderSection
Styles bool
Config renderConfig
}

func newRenderContext(scopes []*EnvScope, envPrefix string, noStyles bool) renderContext {
func newRenderContext(scopes []*EnvScope, cfg renderConfig, envPrefix string, noStyles bool) renderContext {
res := renderContext{
Sections: make([]renderSection, len(scopes)),
Styles: !noStyles,
Config: cfg,
}
res.Title = "Environment Variables"
for i, scope := range scopes {
Expand Down Expand Up @@ -90,26 +100,56 @@ func newRenderItem(item *EnvDocItem, envPrefix string) renderItem {
}
}

//go:embed templ
var templates embed.FS

var tplFuncs = map[string]any{
"repeat": strings.Repeat,
"split": strings.Split,
"strAppend": func(arr []string, item string) []string {
return append(arr, item)
},
"join": strings.Join,
"strSlice": func() []string {
return make([]string, 0)
},
}

var (
tmplMarkdown = texttmpl.Must(texttmpl.New("markdown.tmpl").Funcs(tplFuncs).ParseFS(templates, "templ/markdown.tmpl"))
tmplHTML = htmltmpl.Must(htmltmpl.New("html.tmpl").Funcs(tplFuncs).ParseFS(templates, "templ/html.tmpl"))
tmplPlaintext = texttmpl.Must(texttmpl.New("plaintext.tmpl").Funcs(tplFuncs).ParseFS(templates, "templ/plaintext.tmpl"))
tmplDotEnv = texttmpl.Must(texttmpl.New("dotenv.tmpl").Funcs(tplFuncs).ParseFS(templates, "templ/dotenv.tmpl"))
tmplMarkdown = newTmplText("markdown.tmpl")
tmplHTML = newTmplText("html.tmpl")
tmplPlaintext = newTmplText("plaintext.tmpl")
tmplDotEnv = newTmplText("dotenv.tmpl")

renderMarkdown = renderConfig{
Item: renderItemConfigt{
SeparatorFormat: "separated by `%s`",
SeparatorDefault: "comma-separated",
OptRequired: "**required**",
OptExpand: "expand",
OptFromFile: "from-file",
OptNonEmpty: "non-empty",
EnvDefaultFormat: "default: `%s`",
},
}
renderPlaintext = renderConfig{
Item: renderItemConfigt{
SeparatorFormat: "separated by `%s`",
SeparatorDefault: "comma-separated",
OptRequired: "required",
OptExpand: "expand",
OptFromFile: "from-file",
OptNonEmpty: "non-empty",
EnvDefaultFormat: "default: `%s`",
},
}
renderDotenv = renderConfig{
Item: renderItemConfigt{
SeparatorFormat: "separated by '%s'",
SeparatorDefault: "comma-separated",
OptRequired: "required",
OptExpand: "expand",
OptFromFile: "from-file",
OptNonEmpty: "non-empty",
EnvDefaultFormat: "default: '%s'",
},
}
renderHTML = renderConfig{
Item: renderItemConfigt{
SeparatorFormat: `separated by "<code>%s</code>"`,
SeparatorDefault: "comma-separated",
OptRequired: "<strong>required</strong>",
OptExpand: "expand",
OptFromFile: "from-file",
OptNonEmpty: "non-empty",
EnvDefaultFormat: "default: <code>%s</code>",
},
}
)

type template interface {
Expand Down
30 changes: 19 additions & 11 deletions render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ var testRenderSections = []renderSection{
func TestRender(t *testing.T) {
t.Run("simple", func(t *testing.T) {
rc := renderContext{Title: "Simple", Sections: []renderSection{{Items: testRenderItems}}}
t.Run("markdown", testRenderer(tmplMarkdown, rc,
t.Run("markdown", testRenderer(tmplMarkdown, rc, renderMarkdown,
"# Simple",
"- `TEST_ENV` - This is a test environment variable.",
"- `TEST_ENV2` (comma-separated, default: `default value`) - This is another test environment variable.",
Expand All @@ -87,8 +87,9 @@ func TestRender(t *testing.T) {
" - `NESTED_ENV2` - This is a second nested environment variable.",
" - Nested item level two",
" - `NESTED_ENV3` - This is a third nested environment variable.",
"",
))
t.Run("plaintext", testRenderer(tmplPlaintext, rc,
t.Run("plaintext", testRenderer(tmplPlaintext, rc, renderPlaintext,
"Simple",
" * `TEST_ENV` - This is a test environment variable.",
" * `TEST_ENV2` (comma-separated, default: `default value`) - This is another test environment variable.",
Expand All @@ -98,8 +99,9 @@ func TestRender(t *testing.T) {
" * `NESTED_ENV2` - This is a second nested environment variable.",
" * Nested item level two",
" * `NESTED_ENV3` - This is a third nested environment variable.",
"",
))
t.Run("html", testRenderer(tmplHTML, rc,
t.Run("html", testRenderer(tmplHTML, rc, renderHTML,
`<!DOCTYPE html>`,
`<html lang="en">`,
`<head>`,
Expand Down Expand Up @@ -132,21 +134,25 @@ func TestRender(t *testing.T) {
})
t.Run("sections", func(t *testing.T) {
rc := renderContext{Title: "Sections", Sections: testRenderSections, Styles: true}
t.Run("markdown", testRenderer(tmplMarkdown, rc,
t.Run("markdown", testRenderer(tmplMarkdown, rc, renderMarkdown,
"# Sections",
"## First",
" - `ONE` - First one",
" - `TWO` - First two",
"## Second",
" - `THREE` - Second three"))
t.Run("plaintext", testRenderer(tmplPlaintext, rc,
" - `THREE` - Second three",
"",
))
t.Run("plaintext", testRenderer(tmplPlaintext, rc, renderPlaintext,
"Sections",
"## First",
" * `ONE` - First one",
" * `TWO` - First two",
"## Second",
" * `THREE` - Second three"))
t.Run("html", testRenderer(tmplHTML, rc,
" * `THREE` - Second three",
"",
))
t.Run("html", testRenderer(tmplHTML, rc, renderHTML,
`<!DOCTYPE html>`,
`<html lang="en">`,
`<head>`,
Expand All @@ -169,7 +175,8 @@ func TestRender(t *testing.T) {
`</article>`,
`</section>`,
`</body>`,
`</html>`))
`</html>`,
))
})
}

Expand All @@ -194,7 +201,7 @@ func TestNewRenderContext(t *testing.T) {
},
},
}
rc := newRenderContext(src, "PREFIX_", false)
rc := newRenderContext(src, renderPlaintext, "PREFIX_", false)
const title = "Environment Variables"
if rc.Title != title {
t.Errorf("expected title %q, got %q", title, rc.Title)
Expand Down Expand Up @@ -235,8 +242,9 @@ func TestNewRenderContext(t *testing.T) {
}
}

func testRenderer(tmpl template, c renderContext, expectLines ...string) func(*testing.T) {
func testRenderer(tmpl template, c renderContext, cfg renderConfig, expectLines ...string) func(*testing.T) {
return func(t *testing.T) {
c.Config = cfg
var buf bytes.Buffer
r := templateRenderer(tmpl)
err := r(c, &buf)
Expand Down
Loading
Loading