Skip to content

Commit

Permalink
Merge pull request #4 from g4s8/nested-lists
Browse files Browse the repository at this point in the history
feat: add envPrefix nested env vars lists
  • Loading branch information
g4s8 authored Feb 5, 2024
2 parents 75e50f6 + b797b00 commit caab31a
Show file tree
Hide file tree
Showing 20 changed files with 707 additions and 200 deletions.
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

0 comments on commit caab31a

Please sign in to comment.