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

Creating new method ReadConfigFS which allows users to pass use fs.FS for loading files #136

Open
wants to merge 3 commits 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
86 changes: 64 additions & 22 deletions cleanenv.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"flag"
"fmt"
"io"
"io/fs"
"net/url"
"os"
"path/filepath"
Expand Down Expand Up @@ -53,6 +54,9 @@ const (
TagEnvPrefix = "env-prefix"
)

// ParseFunc defines the type signature that parser functions follow
type ParseFunc func(io.Reader, interface{}) error

// 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 Down Expand Up @@ -95,7 +99,57 @@ type Updater interface {
// ...
// }
func ReadConfig(path string, cfg interface{}) error {
err := parseFile(path, cfg)
parseFunc, err := getParseFunc(strings.ToLower(filepath.Ext(path)))
if err != nil {
return err
}

f, err := os.OpenFile(path, os.O_RDONLY|os.O_SYNC, 0)
if err != nil {
return err
}
defer f.Close()

err = parseFunc(f, cfg)
if err != nil {
return err
}

return readEnvVars(cfg, false)
}

// ReadConfigFS reads configuration file from provided filesystem and parses it depending on tags in structure provided.
//
// Example:
//
// type ConfigDatabase struct {
// Port string `yaml:"port" env:"PORT" env-default:"5432"`
// Host string `yaml:"host" env:"HOST" env-default:"localhost"`
// Name string `yaml:"name" env:"NAME" env-default:"postgres"`
// User string `yaml:"user" env:"USER" env-default:"user"`
// Password string `yaml:"password" env:"PASSWORD"`
// }
//
// var cfg ConfigDatabase
//
// fsys := os.DirFS("/example/config/folder")
// err := cleanenv.ReadConfigFS(fsys, "config.yml", &cfg)
// if err != nil {
// ...
// }
func ReadConfigFS(fsys fs.FS, fname string, cfg interface{}) error {
parseFunc, err := getParseFunc(strings.ToLower(filepath.Ext(fname)))
if err != nil {
return err
}

f, err := fsys.Open(fname)
if err != nil {
return err
}
defer f.Close()

err = parseFunc(f, cfg)
if err != nil {
return err
}
Expand All @@ -113,7 +167,7 @@ func UpdateEnv(cfg interface{}) error {
return readEnvVars(cfg, true)
}

// parseFile parses configuration file according to it's extension
// getParseFunc returns the parsing function based on the file extension passed in.
//
// Currently following file extensions are supported:
//
Expand All @@ -126,33 +180,21 @@ func UpdateEnv(cfg interface{}) error {
// - env
//
// - edn
func parseFile(path string, cfg interface{}) error {
// open the configuration file
f, err := os.OpenFile(path, os.O_RDONLY|os.O_SYNC, 0)
if err != nil {
return err
}
defer f.Close()

// parse the file depending on the file type
switch ext := strings.ToLower(filepath.Ext(path)); ext {
func getParseFunc(ext string) (ParseFunc, error) {
switch ext {
case ".yaml", ".yml":
err = ParseYAML(f, cfg)
return ParseYAML, nil
case ".json":
err = ParseJSON(f, cfg)
return ParseJSON, nil
case ".toml":
err = ParseTOML(f, cfg)
return ParseTOML, nil
case ".edn":
err = parseEDN(f, cfg)
return parseEDN, nil
case ".env":
err = parseENV(f, cfg)
return parseENV, nil
default:
return fmt.Errorf("file format '%s' doesn't supported by the parser", ext)
return nil, fmt.Errorf("file format '%s' is not supported by the parser", ext)
}
if err != nil {
return fmt.Errorf("config file parsing error: %s", err.Error())
}
return nil
}

// ParseYAML parses YAML from reader to data structure
Expand Down