95 lines
2.8 KiB
Odin
95 lines
2.8 KiB
Odin
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
|
|
} |