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

feat: add envPrefix nested env vars lists #4

Merged
merged 1 commit into from
Feb 5, 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
2 changes: 1 addition & 1 deletion _examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ There are few target files:
[`x_complex.md`](./x_complex.md) with `-env-prefix` argument;
and [`complex-nostyle.html`](./complex-nostyle.html) which is HTML documentation without built-in styles.
- [`envprefix.go`](./envprefix.go) showcases a nested config structure with the `envPrefix` tag for a structure field.
It generates [`envprefix.md`](./envprefix.md).
It generates [`envprefix.md`](./envprefix.md), [`envprefix.txt`](./envprefix.txt) and [`envprefix.html`](./envprefix.html).

The examples directory also contains helper script files:
- `build-examples.sh` - modify any example Go file and regenerate all documentation outputs by executing it via `./build-examples.sh`.
Expand Down
3 changes: 0 additions & 3 deletions _examples/complex-nostyle.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,18 @@ <h2>ComplexConfig</h2>
<li><code>HOSTS</code> (separated by "<code>:</code>", <strong>required</strong>) - Hosts is a list of hosts.</li>
<li><code>WORDS</code> (comma-separated, from-file, default: <code>one,two,three</code>) - Words is just a list of words.</li>
<li><code>COMMENT</code> (<strong>required</strong>, default: <code>This is a comment.</code>) - Just a comment.</li>

</ul>

<h2>NextConfig</h2>

<ul>
<li><code>MOUNT</code> (<strong>required</strong>) - Mount is a mount point.</li>

</ul>

<h2>FieldNames</h2>
<p>FieldNames uses field names as env names.</p>
<ul>
<li><code>QUUX</code> - Quux is a field with a tag.</li>

</ul>

</article>
Expand Down
3 changes: 0 additions & 3 deletions _examples/complex.html
Original file line number Diff line number Diff line change
Expand Up @@ -89,21 +89,18 @@ <h2>ComplexConfig</h2>
<li><code>HOSTS</code> (separated by "<code>:</code>", <strong>required</strong>) - Hosts is a list of hosts.</li>
<li><code>WORDS</code> (comma-separated, from-file, default: <code>one,two,three</code>) - Words is just a list of words.</li>
<li><code>COMMENT</code> (<strong>required</strong>, default: <code>This is a comment.</code>) - Just a comment.</li>

</ul>

<h2>NextConfig</h2>

<ul>
<li><code>MOUNT</code> (<strong>required</strong>) - Mount is a mount point.</li>

</ul>

<h2>FieldNames</h2>
<p>FieldNames uses field names as env names.</p>
<ul>
<li><code>QUUX</code> - Quux is a field with a tag.</li>

</ul>

</article>
Expand Down
1 change: 0 additions & 1 deletion _examples/config.html
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ <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>

</ul>

</article>
Expand Down
36 changes: 36 additions & 0 deletions _examples/envprefix.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,51 @@
package main

// Settings is the application settings.
//
//go:generate go run ../ -output envprefix.txt -format plaintext -type Settings
//go:generate go run ../ -output envprefix.md -type Settings
//go:generate go run ../ -output envprefix.html -format html -type Settings
type Settings struct {
// Database is the database settings
Database Database `envPrefix:"DB_"`

// Server is the server settings
Server ServerConfig `envPrefix:"SERVER_"`

// Debug is the debug flag
Debug bool `env:"DEBUG"`
}

// Database is the database settings.
type Database struct {
// Port is the port to connect to
Port Int `env:"PORT,required"`
// Host is the host to connect to
Host string `env:"HOST,notEmpty" envDefault:"localhost"`
// User is the user to connect as
User string `env:"USER"`
// Password is the password to use
Password string `env:"PASSWORD"`
// DisableTLS is the flag to disable TLS
DisableTLS bool `env:"DISABLE_TLS"`
}

// ServerConfig is the server settings.
type ServerConfig struct {
// Port is the port to listen on
Port Int `env:"PORT,required"`

// Host is the host to listen on
Host string `env:"HOST,notEmpty" envDefault:"localhost"`

// Timeout is the timeout settings
Timeout TimeoutConfig `envPrefix:"TIMEOUT_"`
}

// TimeoutConfig is the timeout settings.
type TimeoutConfig struct {
// Read is the read timeout
Read Int `env:"READ" envDefault:"30"`
// Write is the write timeout
Write Int `env:"WRITE" envDefault:"30"`
}
109 changes: 109 additions & 0 deletions _examples/envprefix.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Environment Variables</title>
<style>
* {
box-sizing: border-box;
}
body {
font-family: sans-serif;
color: #1F2328;
}
article {
margin-left: auto;
margin-right: auto;
max-width: 1012px;
font-size: 16px;
line-height: 1.5;
word-wrap: break-word;
display: block;
padding: 32px;
}
article::first-child {
margin-top: 0 !important;
}
article::last-child {
margin-bottom: 0 !important;
}
section {
margin-top: 46px;
background-color: #ffffff;
border: 0px;
border-radius: 0px 0px 6px 6px;
padding: 0px;
min-width: 0px;
margin-top: 46px;
-moz-box-pack: center;
}
h1, h2 {
margin-top: 24px;
margin-bottom: 16px;
line-height: 1.25;
font-weight: 600;
padding-bottom: .3em;
border-bottom: 1px solid hsla(210,18%,87%,1);
}
h1 {
font-size: 2em;
}
h2 {
font-size: 1.5em;
}
li {
margin-top: .25em;
}
li code {
padding: .2em .4em;
margin: 0;
font-size: 85%;
white-space: break-sp#ffffffaces;
background-color: rgba(175,184,193,0.2);
border-radius: 6px;
}
li strong {
font-weight: 600;
}
p {
margin-top: 0;
margin-bottom: 16px;
}
</style>
</head>
<body>
<section>
<article>
<h1>Environment Variables</h1>

<h2>Settings</h2>
<p>Settings is the application settings.</p>
<ul>
<li>Database is the database settings.
<ul>
<li><code>DB_PORT</code> (<strong>required</strong>) - Port is the port to connect to</li>
<li><code>DB_HOST</code> (<strong>required</strong>, non-empty, default: <code>localhost</code>) - Host is the host to connect to</li>
<li><code>DB_USER</code> - User is the user to connect as</li>
<li><code>DB_PASSWORD</code> - Password is the password to use</li>
<li><code>DB_DISABLE_TLS</code> - DisableTLS is the flag to disable TLS</li>
</ul>
</li>
<li>ServerConfig is the server settings.
<ul>
<li><code>SERVER_PORT</code> (<strong>required</strong>) - Port is the port to listen on</li>
<li><code>SERVER_HOST</code> (<strong>required</strong>, non-empty, default: <code>localhost</code>) - Host is the host to listen on</li>
<li>TimeoutConfig is the timeout settings.
<ul>
<li><code>SERVER_TIMEOUT_READ</code> (default: <code>30</code>) - Read is the read timeout</li>
<li><code>SERVER_TIMEOUT_WRITE</code> (default: <code>30</code>) - Write is the write timeout</li>
</ul>
</li>
</ul>
</li>
<li><code>DEBUG</code> - Debug is the debug flag</li>
</ul>

</article>
</section>
</body>
</html>
15 changes: 14 additions & 1 deletion _examples/envprefix.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,18 @@

## Settings

- `DB_PORT` (**required**) - Port is the port to connect to
Settings is the application settings.

- Database is the database settings.
- `DB_PORT` (**required**) - Port is the port to connect to
- `DB_HOST` (**required**, non-empty, default: `localhost`) - Host is the host to connect to
- `DB_USER` - User is the user to connect as
- `DB_PASSWORD` - Password is the password to use
- `DB_DISABLE_TLS` - DisableTLS is the flag to disable TLS
- ServerConfig is the server settings.
- `SERVER_PORT` (**required**) - Port is the port to listen on
- `SERVER_HOST` (**required**, non-empty, default: `localhost`) - Host is the host to listen on
- TimeoutConfig is the timeout 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
19 changes: 19 additions & 0 deletions _examples/envprefix.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Environment Variables

## Settings

Settings is the application settings.

* Database is the database settings.
* `DB_PORT` (required) - Port is the port to connect to
* `DB_HOST` (required, non-empty, default: `localhost`) - Host is the host to connect to
* `DB_USER` - User is the user to connect as
* `DB_PASSWORD` - Password is the password to use
* `DB_DISABLE_TLS` - DisableTLS is the flag to disable TLS
* ServerConfig is the server settings.
* `SERVER_PORT` (required) - Port is the port to listen on
* `SERVER_HOST` (required, non-empty, default: `localhost`) - Host is the host to listen on
* TimeoutConfig is the timeout 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
12 changes: 12 additions & 0 deletions debug.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package main

import "fmt"

const debugLogs = false

func debug(f string, args ...any) {
if !debugLogs {
return
}
fmt.Printf("DEBUG: "+f+"\n", args...)
}
85 changes: 46 additions & 39 deletions inspector.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type envField struct {
doc string
opts EnvVarOptions
typeRef string
fieldName string
envPrefix string
}

Expand Down Expand Up @@ -206,6 +207,11 @@ func (i *inspector) parseField(f *ast.Field) (out []envField) {
item.kind = envFieldKindStruct
fieldType := f.Type.(*ast.Ident)
item.typeRef = fieldType.Name
fieldNames := make([]string, len(f.Names))
for i, name := range f.Names {
fieldNames[i] = name.Name
}
item.fieldName = strings.Join(fieldNames, ", ")
out = []envField{item}
return
}
Expand All @@ -229,6 +235,7 @@ func (i *inspector) parseField(f *ast.Field) (out []envField) {
} else {
return
}

docStr := strings.TrimSpace(f.Doc.Text())
if docStr == "" {
docStr = strings.TrimSpace(f.Comment.Text())
Expand Down Expand Up @@ -288,51 +295,51 @@ func (i *inspector) buildScopes() ([]*EnvScope, error) {
Doc: s.doc,
}
for _, f := range s.fields {
switch f.kind {
case envFieldKindPlain:
v := EnvDocItem{
Name: f.name,
Doc: f.doc,
Opts: f.opts,
}
debug("[p] add docItem: %s <- %s", scope.Name, v.Name)
scope.Vars = append(scope.Vars, v)
case envFieldKindStruct:
envPrefix := f.envPrefix
var base *envStruct
for _, s := range i.items {
if s.name == f.typeRef {
base = s
break
}
}
if base == nil {
return nil, fmt.Errorf("struct %q not found", f.typeRef)
}
for _, f := range base.fields {
name := fmt.Sprintf("%s%s", envPrefix, f.name)
v := EnvDocItem{
Name: name,
Doc: f.doc,
Opts: f.opts,
}
debug("[s] add docItem: %s <- %s (prefix: %s)", scope.Name, v.Name, envPrefix)
scope.Vars = append(scope.Vars, v)
}
default:
panic("unknown field kind")
item, err := i.buildItem(&f, "")
if err != nil {
return nil, err
}
scope.Vars = append(scope.Vars, item)
}
scopes = append(scopes, scope)
}
return scopes, nil
}

const debugLogs = false

func debug(f string, args ...any) {
if !debugLogs {
return
func (i *inspector) buildItem(f *envField, envPrefix string) (EnvDocItem, error) {
switch f.kind {
case envFieldKindPlain:
return EnvDocItem{
Name: fmt.Sprintf("%s%s", envPrefix, f.name),
Doc: f.doc,
Opts: f.opts,
debugName: f.name,
}, nil
case envFieldKindStruct:
envPrefix := fmt.Sprintf("%s%s", envPrefix, f.envPrefix)
var base *envStruct
for _, s := range i.items {
if s.name == f.typeRef {
base = s
break
}
}
if base == nil {
return EnvDocItem{}, fmt.Errorf("struct %q not found", f.typeRef)
}
parentItem := EnvDocItem{
Doc: base.doc,
debugName: base.name,
}
for _, f := range base.fields {
item, err := i.buildItem(&f, envPrefix)
if err != nil {
return EnvDocItem{}, fmt.Errorf("build item `%s`: %w", f.name, err)
}
parentItem.Children = append(parentItem.Children, item)
}
return parentItem, nil
default:
panic("unknown field kind")
}
fmt.Printf("DEBUG: "+f+"\n", args...)
}
Loading
Loading