added support for dotenv files and config
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
package dotenv
|
||||
|
||||
import "core:os"
|
||||
import "core:strconv"
|
||||
import "core:strings"
|
||||
|
||||
// parse parses dotenv-format source (KEY=VALUE lines) into a map allocated
|
||||
// with allocator. Blank lines, lines starting with '#', and lines without a
|
||||
// '=' are skipped. Keys and values are trimmed; values may be wrapped in
|
||||
// double quotes. Precedence with the real process environment is handled by
|
||||
// the get_* accessors (real env vars win over the file).
|
||||
parse :: proc(src: string, allocator := context.allocator) -> map[string]string {
|
||||
result := make(map[string]string, allocator)
|
||||
|
||||
it := src
|
||||
for line in strings.split_lines_iterator(&it) {
|
||||
tr := strings.trim_space(line)
|
||||
if len(tr) == 0 || strings.has_prefix(tr, "#") {
|
||||
continue
|
||||
}
|
||||
|
||||
eq := strings.index_byte(tr, '=')
|
||||
if eq < 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
key := strings.trim_space(tr[:eq])
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
value := strings.trim_space(tr[eq + 1:])
|
||||
if len(value) >= 2 && value[0] == '"' && value[len(value) - 1] == '"' {
|
||||
value = value[1:len(value) - 1]
|
||||
}
|
||||
|
||||
// clone so the map outlives the source buffer (e.g. a freed file read)
|
||||
result[strings.clone(key, allocator)] = strings.clone(value, allocator)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// parse_file reads a dotenv file from disk and parses it. It returns
|
||||
// (nil, false) when the file cannot be read (e.g. it does not exist).
|
||||
parse_file :: proc(filename: string, allocator := context.allocator) -> (map[string]string, bool) {
|
||||
data, err := os.read_entire_file(filename, allocator)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
defer delete(data)
|
||||
|
||||
return parse(string(data), allocator), true
|
||||
}
|
||||
|
||||
// destroy frees the cloned keys/values and the map itself. Use it to release
|
||||
// a map returned by parse/parse_file (plain delete does not free the strings).
|
||||
destroy :: proc(env: map[string]string) {
|
||||
for key, value in env {
|
||||
delete(key)
|
||||
delete(value)
|
||||
}
|
||||
delete(env)
|
||||
}
|
||||
|
||||
// get_string returns the value for key from the real process environment if
|
||||
// set, otherwise from the parsed dotenv map (or "" if neither has it).
|
||||
get_string :: proc(env: map[string]string, key: string) -> string {
|
||||
if value, found := os.lookup_env(key, context.temp_allocator); found {
|
||||
return value
|
||||
}
|
||||
return env[key]
|
||||
}
|
||||
|
||||
// get_bool resolves key and parses it as a boolean (true/false, 1/0,
|
||||
// yes/no, on/off). Missing or unparsable values yield default.
|
||||
get_bool :: proc(env: map[string]string, key: string, default := false) -> bool {
|
||||
if v := get_string(env, key); v != "" {
|
||||
if parsed, ok := strconv.parse_bool(v); ok {
|
||||
return parsed
|
||||
}
|
||||
}
|
||||
return default
|
||||
}
|
||||
|
||||
// get_int resolves key and parses it as an integer. Missing or unparsable
|
||||
// values yield default.
|
||||
get_int :: proc(env: map[string]string, key: string, default := 0) -> int {
|
||||
if v := get_string(env, key); v != "" {
|
||||
if parsed, ok := strconv.parse_int(v); ok {
|
||||
return parsed
|
||||
}
|
||||
}
|
||||
return default
|
||||
}
|
||||
Reference in New Issue
Block a user