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

.env.* support and possible to check require field after all config loading #138

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
48 changes: 41 additions & 7 deletions cleanenv.go
Expand Up @@ -53,6 +53,10 @@ const (
TagEnvPrefix = "env-prefix"
)

var (
RequiredCheck = true
)

// Setter is an interface for a custom value setter.
//
// To implement a custom value setter you need to add a SetValue function to your type that will receive a string raw value:
Expand All @@ -75,6 +79,20 @@ type Updater interface {
Update() error
}

type unsupportedFileFormat struct {
ftype string
}

func (e *unsupportedFileFormat) Error() string {
return fmt.Sprintf("file format '%s' doesn't supported by the parser", e.ftype)
}

func newErrorUnsupportedFileFormat(ftype string) *unsupportedFileFormat {
return &unsupportedFileFormat{
ftype: ftype,
}
}

// ReadConfig reads configuration file and parses it depending on tags in structure provided.
// Then it reads and parses
//
Expand Down Expand Up @@ -134,8 +152,27 @@ func parseFile(path string, cfg interface{}) error {
}
defer f.Close()

ftype := "."
for ftype != "" {
ftype = strings.ToLower(filepath.Ext(path))
err = parseFileByType(ftype, f, cfg)
if err != nil {
if _, ok := err.(*unsupportedFileFormat); ok {
path = strings.TrimSuffix(path, filepath.Ext(path))
} else {
return fmt.Errorf("config file parsing error: %s", err.Error())
}
} else {
ftype = ""
}
}

return nil
}

func parseFileByType(ftype string, f *os.File, cfg interface{}) (err error) {
// parse the file depending on the file type
switch ext := strings.ToLower(filepath.Ext(path)); ext {
switch ftype {
case ".yaml", ".yml":
err = ParseYAML(f, cfg)
case ".json":
Expand All @@ -147,12 +184,9 @@ func parseFile(path string, cfg interface{}) error {
case ".env":
err = parseENV(f, cfg)
default:
return fmt.Errorf("file format '%s' doesn't supported by the parser", ext)
}
if err != nil {
return fmt.Errorf("config file parsing error: %s", err.Error())
return newErrorUnsupportedFileFormat(ftype)
}
return nil
return err
}

// ParseYAML parses YAML from reader to data structure
Expand Down Expand Up @@ -428,7 +462,7 @@ func readEnvVars(cfg interface{}, update bool) error {
}
}

if rawValue == nil && meta.required && meta.isFieldValueZero() {
if RequiredCheck && rawValue == nil && meta.required && meta.isFieldValueZero() {
return fmt.Errorf(
"field %q is required but the value is not provided",
meta.fieldName,
Expand Down
2 changes: 2 additions & 0 deletions example/parse_multiple_files/.env.local
@@ -0,0 +1,2 @@
DB_PORT=5435
DB_PASSWORD=password
2 changes: 1 addition & 1 deletion example/parse_multiple_files/README.md
@@ -1,5 +1,5 @@
# Parse multiple files for configuration

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

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.
1 change: 0 additions & 1 deletion example/parse_multiple_files/db_config.yaml
@@ -1,7 +1,6 @@
database:
host: "localhost"
user: "root"
password: "password"
name: "cleanenv"
port: "5432"
ssl_mode: "disable"
6 changes: 3 additions & 3 deletions example/parse_multiple_files/main.go
Expand Up @@ -15,10 +15,10 @@ type config struct {

type databaseConfig struct {
User string `yaml:"user"`
Password string `yaml:"password"`
Password string `env-required:"true" yaml:"password" env:"DB_PASSWORD"`
Name string `yaml:"name"`
Host string `yaml:"host"`
Port string `yaml:"port"`
Port string `yaml:"port" env:"DB_PORT"`
SSLMode string `yaml:"ssl_mode"`
}

Expand All @@ -28,7 +28,7 @@ type emailService struct {
}

func main() {
cfg, err := ParseConfigFiles("./db_config.yaml", "./email_config.yaml", "./general_config.yaml")
cfg, err := ParseConfigFiles("./db_config.yaml", "./email_config.yaml", "./general_config.yaml", "./.env.local")
if err != nil {
log.Printf("Error parsing config files: %v", err)
return
Expand Down