199 lines
4.1 KiB
Go
199 lines
4.1 KiB
Go
package envutil
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/gookit/goutil/internal/comfunc"
|
|
)
|
|
|
|
// DefaultEnvFile default file name
|
|
const DefaultEnvFile = ".env"
|
|
|
|
// Dotenv load and parse dotenv files
|
|
type Dotenv struct {
|
|
// Files dot env file paths, allow multi files.
|
|
// - filename support simple glob pattern. eg: ".env.*"
|
|
//
|
|
// default: [".env"]
|
|
Files []string
|
|
// BaseDir base dir for join files path
|
|
//
|
|
// default is workdir
|
|
BaseDir string
|
|
// UpperKey change key to upper on set ENV. default: true
|
|
UpperKey bool
|
|
// IgnoreNotExist only load exists.
|
|
//
|
|
// - default: false - will return error if not exists
|
|
IgnoreNotExist bool
|
|
// LoadFirstExist only load first exists env file on Files
|
|
LoadFirstExist bool
|
|
|
|
loadFiles []string
|
|
loadData map[string]string
|
|
}
|
|
|
|
// NewDotenv create a new dotenv config
|
|
func NewDotenv() *Dotenv {
|
|
return &Dotenv{
|
|
UpperKey: true,
|
|
Files: []string{DefaultEnvFile},
|
|
// init fields
|
|
loadData: make(map[string]string),
|
|
}
|
|
}
|
|
|
|
// LoadAndInit load dotenv files and parse to os.Environ
|
|
func (c *Dotenv) LoadAndInit() error {
|
|
return c.doLoadFiles(c.Files)
|
|
}
|
|
|
|
// LoadFiles append load dotenv files
|
|
// - filename support simple glob pattern. eg: ".env.*"
|
|
func (c *Dotenv) LoadFiles(files ...string) error {
|
|
return c.doLoadFiles(files)
|
|
}
|
|
|
|
// LoadText load dotenv contents and parse to os Env
|
|
func (c *Dotenv) LoadText(contents string) error {
|
|
return c.parseAndSetEnv(contents)
|
|
}
|
|
|
|
// do load dotenv files
|
|
func (c *Dotenv) doLoadFiles(files []string) error {
|
|
var filePath string
|
|
for _, file := range files {
|
|
filePath = strings.TrimSpace(file)
|
|
if c.BaseDir != "" && !filepath.IsAbs(file) {
|
|
filePath = filepath.Join(c.BaseDir, file)
|
|
}
|
|
|
|
// load and parse to ENV
|
|
if err := c.loadFile(filePath); err != nil {
|
|
return err
|
|
}
|
|
if c.LoadFirstExist && len(c.loadFiles) > 0 {
|
|
break
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *Dotenv) loadFile(filePath string) error {
|
|
// filename support simple glob pattern.
|
|
if strings.ContainsRune(filePath, '*') {
|
|
matches, err := filepath.Glob(filePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, matchFile := range matches {
|
|
if err = c.parseFile(matchFile); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return err
|
|
}
|
|
|
|
// Load single file
|
|
return c.parseFile(filePath)
|
|
}
|
|
|
|
// parseFile load single file and parse to ENV
|
|
func (c *Dotenv) parseFile(filePath string) error {
|
|
contents, err := os.ReadFile(filePath)
|
|
if err != nil {
|
|
// IgnoreNotExist: skip non-existent files
|
|
if c.IgnoreNotExist && os.IsNotExist(err) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
err = c.parseAndSetEnv(string(contents))
|
|
if err == nil {
|
|
c.loadFiles = append(c.loadFiles, filePath)
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (c *Dotenv) parseAndSetEnv(contents string) error {
|
|
// Parse ENV lines
|
|
envMp, err := comfunc.ParseEnvLines(contents, comfunc.ParseEnvLineOption{
|
|
SkipOnErrorLine: true,
|
|
})
|
|
|
|
// Set to ENV
|
|
for key, val := range envMp {
|
|
key = strings.ToUpper(key)
|
|
c.loadData[key] = val
|
|
_ = os.Setenv(key, val)
|
|
}
|
|
return err
|
|
}
|
|
|
|
// UnloadEnv remove loaded dotenv data from os.Environ
|
|
func (c *Dotenv) UnloadEnv() bool {
|
|
if len(c.loadData) == 0 {
|
|
return false
|
|
}
|
|
|
|
for key := range c.loadData {
|
|
_ = os.Unsetenv(key)
|
|
}
|
|
return true
|
|
}
|
|
|
|
// LoadedData get loaded dotenv data map
|
|
func (c *Dotenv) LoadedData() map[string]string {
|
|
return c.loadData
|
|
}
|
|
|
|
// LoadedFiles get loaded dotenv files
|
|
func (c *Dotenv) LoadedFiles() []string {
|
|
return c.loadFiles
|
|
}
|
|
|
|
// Reset unload all loaded ENV and reset data
|
|
func (c *Dotenv) Reset() {
|
|
c.UnloadEnv()
|
|
c.loadFiles = nil
|
|
c.loadData = make(map[string]string)
|
|
}
|
|
|
|
//
|
|
// region standard dotenv instance
|
|
//
|
|
|
|
var stdEnv = NewDotenv()
|
|
|
|
// StdDotenv get standard dotenv instance
|
|
func StdDotenv() *Dotenv { return stdEnv }
|
|
|
|
// DotenvLoad load dotenv file and parse to ENV
|
|
func DotenvLoad(fns ...func(cfg *Dotenv)) error {
|
|
for _, fn := range fns {
|
|
fn(stdEnv)
|
|
}
|
|
return stdEnv.LoadAndInit()
|
|
}
|
|
|
|
// LoadEnvFiles load dotenv files and parse to ENV
|
|
func LoadEnvFiles(baseDir string, files ...string) error {
|
|
return DotenvLoad(func(cfg *Dotenv) {
|
|
cfg.BaseDir = baseDir
|
|
if len(files) > 0 {
|
|
cfg.Files = files
|
|
}
|
|
})
|
|
}
|
|
|
|
// LoadedEnvFiles get loaded dotenv files
|
|
func LoadedEnvFiles() []string {
|
|
return stdEnv.LoadedFiles()
|
|
}
|
|
|