github.com/best4tires/kit@v1.0.5/env/dotenv.go (about)

     1  package env
     2  
     3  import (
     4  	"bufio"
     5  	"os"
     6  	"path/filepath"
     7  	"strings"
     8  
     9  	"github.com/BurntSushi/toml"
    10  )
    11  
    12  func loadDotenv(path string) (map[string]any, error) {
    13  	vs := map[string]any{}
    14  	f, err := os.Open(path)
    15  	if err != nil {
    16  		return nil, err
    17  	}
    18  	defer f.Close()
    19  	scanner := bufio.NewScanner(f)
    20  	for scanner.Scan() {
    21  		line := strings.TrimSpace(scanner.Text())
    22  		if line == "" || strings.HasPrefix(line, "#") {
    23  			continue
    24  		}
    25  		k, v, _ := strings.Cut(line, "=")
    26  		k = strings.TrimSpace(k)
    27  		if k == "" {
    28  			continue
    29  		}
    30  		v = unquote(strings.TrimSpace(v))
    31  		if v == "" {
    32  			vs[k] = true
    33  		} else {
    34  			vs[k] = v
    35  		}
    36  	}
    37  	return vs, nil
    38  }
    39  
    40  func loadDotenvToml(path string) (map[string]any, error) {
    41  	vs := map[string]any{}
    42  	_, err := toml.DecodeFile(path, &vs)
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  	return vs, nil
    47  }
    48  
    49  // LoadDotenv looks up .env and related files in the current directory and the in parent directories.
    50  // Existing values are not overwritten by higher level files.
    51  // Filenames considered are ".env" (usual .env files ), ".env.toml" (decoded as toml), where ".env" is evaluated before ".env.toml"
    52  func LoadDotenv() map[string]any {
    53  	// get working-dir as absolute path
    54  	wd, err := os.Getwd()
    55  	if err != nil {
    56  		return map[string]any{}
    57  	}
    58  	wd, err = filepath.Abs(wd)
    59  	if err != nil {
    60  		return map[string]any{}
    61  	}
    62  
    63  	// build slices of dirs
    64  	vol := filepath.VolumeName(wd)
    65  	wd = strings.TrimPrefix(wd, vol)
    66  	sep := string(filepath.Separator)
    67  	var dirSl []string
    68  	for _, e := range strings.Split(wd, sep) {
    69  		if e == "" {
    70  			continue
    71  		}
    72  		dirSl = append(dirSl, e)
    73  	}
    74  
    75  	const (
    76  		dotEnvFile     = ".env"
    77  		dotEnvFileToml = ".env.toml"
    78  	)
    79  	all := map[string]any{}
    80  	for {
    81  		currDir := vol + sep + filepath.Join(dirSl...)
    82  		if vs, err := loadDotenv(filepath.Join(currDir, dotEnvFile)); err == nil {
    83  			merge(vs, all)
    84  		}
    85  		if vs, err := loadDotenvToml(filepath.Join(currDir, dotEnvFileToml)); err == nil {
    86  			merge(vs, all)
    87  		}
    88  
    89  		if len(dirSl) > 0 {
    90  			dirSl = dirSl[:len(dirSl)-1]
    91  		}
    92  
    93  		if len(dirSl) == 0 {
    94  			//done
    95  			break
    96  		}
    97  	}
    98  	return all
    99  }