Skip to content

Commit c2eda10

Browse files
committed
env.* support and possible to check require field after all config loading
1 parent 8ca6c6a commit c2eda10

File tree

5 files changed

+47
-12
lines changed

5 files changed

+47
-12
lines changed

cleanenv.go

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ const (
5353
TagEnvPrefix = "env-prefix"
5454
)
5555

56+
var (
57+
RequiredCheck = true
58+
)
59+
5660
// Setter is an interface for a custom value setter.
5761
//
5862
// To implement a custom value setter you need to add a SetValue function to your type that will receive a string raw value:
@@ -75,6 +79,20 @@ type Updater interface {
7579
Update() error
7680
}
7781

82+
type unsupportedFileFormat struct {
83+
ftype string
84+
}
85+
86+
func (e *unsupportedFileFormat) Error() string {
87+
return fmt.Sprintf("file format '%s' doesn't supported by the parser", e.ftype)
88+
}
89+
90+
func newErrorUnsupportedFileFormat(ftype string) *unsupportedFileFormat {
91+
return &unsupportedFileFormat{
92+
ftype: ftype,
93+
}
94+
}
95+
7896
// ReadConfig reads configuration file and parses it depending on tags in structure provided.
7997
// Then it reads and parses
8098
//
@@ -134,8 +152,27 @@ func parseFile(path string, cfg interface{}) error {
134152
}
135153
defer f.Close()
136154

155+
ftype := "."
156+
for ftype != "" {
157+
ftype = strings.ToLower(filepath.Ext(path))
158+
err = parseFileByType(ftype, f, cfg)
159+
if err != nil {
160+
if _, ok := err.(*unsupportedFileFormat); ok {
161+
path = strings.TrimSuffix(path, filepath.Ext(path))
162+
} else {
163+
return fmt.Errorf("config file parsing error: %s", err.Error())
164+
}
165+
} else {
166+
ftype = ""
167+
}
168+
}
169+
170+
return nil
171+
}
172+
173+
func parseFileByType(ftype string, f *os.File, cfg interface{}) (err error) {
137174
// parse the file depending on the file type
138-
switch ext := strings.ToLower(filepath.Ext(path)); ext {
175+
switch ftype {
139176
case ".yaml", ".yml":
140177
err = ParseYAML(f, cfg)
141178
case ".json":
@@ -147,12 +184,9 @@ func parseFile(path string, cfg interface{}) error {
147184
case ".env":
148185
err = parseENV(f, cfg)
149186
default:
150-
return fmt.Errorf("file format '%s' doesn't supported by the parser", ext)
151-
}
152-
if err != nil {
153-
return fmt.Errorf("config file parsing error: %s", err.Error())
187+
return newErrorUnsupportedFileFormat(ftype)
154188
}
155-
return nil
189+
return err
156190
}
157191

158192
// ParseYAML parses YAML from reader to data structure
@@ -428,7 +462,7 @@ func readEnvVars(cfg interface{}, update bool) error {
428462
}
429463
}
430464

431-
if rawValue == nil && meta.required && meta.isFieldValueZero() {
465+
if RequiredCheck && rawValue == nil && meta.required && meta.isFieldValueZero() {
432466
return fmt.Errorf(
433467
"field %q is required but the value is not provided",
434468
meta.fieldName,
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
DB_PORT=5435
2+
DB_PASSWORD=password
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Parse multiple files for configuration
22

3-
This example shows how the package can be used to read from mutiple configuration files and assign them to the same structure.
3+
This example shows how the package can be used to read from multiple configuration files and assign them to the same structure.
44

55
In this example, the configuration is read from ```db_config.yaml```,```email_config.yaml``` and ```general_config.yaml``` and the values are stored in the ```config``` struct.
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
database:
22
host: "localhost"
33
user: "root"
4-
password: "password"
54
name: "cleanenv"
65
port: "5432"
76
ssl_mode: "disable"

example/parse_multiple_files/main.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ type config struct {
1515

1616
type databaseConfig struct {
1717
User string `yaml:"user"`
18-
Password string `yaml:"password"`
18+
Password string `env-required:"true" yaml:"password" env:"DB_PASSWORD"`
1919
Name string `yaml:"name"`
2020
Host string `yaml:"host"`
21-
Port string `yaml:"port"`
21+
Port string `yaml:"port" env:"DB_PORT"`
2222
SSLMode string `yaml:"ssl_mode"`
2323
}
2424

@@ -28,7 +28,7 @@ type emailService struct {
2828
}
2929

3030
func main() {
31-
cfg, err := ParseConfigFiles("./db_config.yaml", "./email_config.yaml", "./general_config.yaml")
31+
cfg, err := ParseConfigFiles("./db_config.yaml", "./email_config.yaml", "./general_config.yaml", "./.env.local")
3232
if err != nil {
3333
log.Printf("Error parsing config files: %v", err)
3434
return

0 commit comments

Comments
 (0)