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 }