Skip to content

Commit b858674

Browse files
committed
add config
1 parent 737ea7e commit b858674

File tree

7 files changed

+219
-9
lines changed

7 files changed

+219
-9
lines changed

config/env.go

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,16 @@ import (
1414
// }
1515
//
1616
// if your envPrefix is XXX, it can be set in env like XXX_DSN
17-
func BindEnvs(v *viper.Viper, envPrefix string, iface any) { bindEnvs(v, envPrefix, "", iface) }
17+
func BindEnvs(v *viper.Viper, envPrefix string, iface any) {
18+
walkStruct(iface, "", func(fieldv reflect.Value, path, envKey string) error {
19+
if err := v.BindEnv(path, envPrefix+envKey); err != nil {
20+
panic(err)
21+
}
22+
return nil
23+
})
24+
}
1825

19-
func bindEnvs(v *viper.Viper, envPrefix string, prefix string, iface any) {
26+
func walkStruct(iface any, prefix string, fn func(fieldv reflect.Value, path, envKey string) error) error {
2027
ifv := reflect.Indirect(reflect.ValueOf(iface))
2128
ift := ifv.Type()
2229

@@ -34,14 +41,17 @@ func bindEnvs(v *viper.Viper, envPrefix string, prefix string, iface any) {
3441
path = prefix + "." + name
3542
}
3643

37-
switch fieldv.Kind() {
38-
case reflect.Struct:
39-
bindEnvs(v, envPrefix, path, fieldv.Addr().Interface())
40-
default:
41-
envKey := strings.ToUpper(strings.ReplaceAll(path, ".", "_"))
42-
if err := v.BindEnv(path, envPrefix+envKey); err != nil {
43-
panic(err)
44+
envKey := strings.ToUpper(strings.ReplaceAll(path, ".", "_"))
45+
46+
if fieldv.Kind() == reflect.Struct {
47+
if err := walkStruct(fieldv.Addr().Interface(), path, fn); err != nil {
48+
return err
49+
}
50+
} else {
51+
if err := fn(fieldv, path, envKey); err != nil {
52+
return err
4453
}
4554
}
4655
}
56+
return nil
4757
}

config/export.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package config
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"io"
7+
"reflect"
8+
"strings"
9+
10+
"gopkg.in/yaml.v3"
11+
)
12+
13+
// ENUM(yml,json,env)
14+
type ExportType uint8
15+
16+
func Export(w io.Writer, cfg any, exportType ExportType, envPrefix string) error {
17+
switch exportType {
18+
case ExportTypeJson:
19+
enc := json.NewEncoder(w)
20+
enc.SetIndent("", " ")
21+
return enc.Encode(cfg)
22+
case ExportTypeYml:
23+
return yaml.NewEncoder(w).Encode(cfg)
24+
case ExportTypeEnv:
25+
return exportEnv(w, cfg, envPrefix, "")
26+
default:
27+
return fmt.Errorf("unsupported export type: %v", exportType)
28+
}
29+
}
30+
31+
func exportEnv(w io.Writer, iface any, envPrefix string, prefix string) error {
32+
var sb strings.Builder
33+
if err := walkStruct(iface, prefix, func(fieldv reflect.Value, path, envKey string) error {
34+
35+
key := envPrefix + envKey
36+
37+
value := fmt.Sprintf("%v", fieldv.Interface())
38+
sb.WriteString(key)
39+
sb.WriteString("=")
40+
sb.WriteString(value)
41+
sb.WriteString("\n")
42+
43+
return nil
44+
}); err != nil {
45+
return err
46+
}
47+
_, err := w.Write([]byte(sb.String()))
48+
return err
49+
}

config/export_enum.go

Lines changed: 59 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/export_test.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package config
2+
3+
import (
4+
"bytes"
5+
"os"
6+
"path/filepath"
7+
"testing"
8+
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
type testConfig struct {
13+
Database struct {
14+
Host string `mapstructure:"host" json:"host"`
15+
Port int `mapstructure:"port" json:"port"`
16+
} `mapstructure:"database" json:"database"`
17+
API struct {
18+
Key string `mapstructure:"key" json:"key"`
19+
Secret string `mapstructure:"secret" json:"secret"`
20+
} `mapstructure:"api" json:"api"`
21+
Debug bool `mapstructure:"debug" json:"debug"`
22+
}
23+
24+
func TestExport(t *testing.T) {
25+
cfg := testConfig{}
26+
cfg.Database.Host = "localhost"
27+
cfg.Database.Port = 5432
28+
cfg.API.Key = "test-key"
29+
cfg.API.Secret = "secret123"
30+
cfg.Debug = true
31+
32+
envPrefix := "APP_"
33+
34+
tests := []struct {
35+
name string
36+
exportType ExportType
37+
expectedFile string
38+
}{
39+
{
40+
name: "json export",
41+
exportType: ExportTypeJson,
42+
expectedFile: "expected.json",
43+
},
44+
{
45+
name: "yml export",
46+
exportType: ExportTypeYml,
47+
expectedFile: "expected.yml",
48+
},
49+
{
50+
name: "env export",
51+
exportType: ExportTypeEnv,
52+
expectedFile: "expected.env",
53+
},
54+
}
55+
56+
for _, tt := range tests {
57+
t.Run(tt.name, func(t *testing.T) {
58+
var buf bytes.Buffer
59+
err := Export(&buf, &cfg, tt.exportType, envPrefix)
60+
require.NoError(t, err)
61+
62+
expectedPath := filepath.Join("testdata", tt.expectedFile)
63+
expected, err := os.ReadFile(expectedPath)
64+
require.NoError(t, err)
65+
66+
require.Equal(t, string(expected), buf.String())
67+
})
68+
}
69+
}

config/testdata/expected.env

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
APP_DATABASE_HOST=localhost
2+
APP_DATABASE_PORT=5432
3+
APP_API_KEY=test-key
4+
APP_API_SECRET=secret123
5+
APP_DEBUG=true

config/testdata/expected.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"database": {
3+
"host": "localhost",
4+
"port": 5432
5+
},
6+
"api": {
7+
"key": "test-key",
8+
"secret": "secret123"
9+
},
10+
"debug": true
11+
}

config/testdata/expected.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
database:
2+
host: localhost
3+
port: 5432
4+
api:
5+
key: test-key
6+
secret: secret123
7+
debug: true

0 commit comments

Comments
 (0)