2022-02-21 20:40:13 +01:00
module env
import os
2022-04-06 17:51:06 +02:00
import toml
2022-02-21 20:40:13 +01:00
// The prefix that every environment variable should have
const prefix = ' VIETER_'
// The suffix an environment variable in order for it to be loaded from a file
// instead
const file_suffix = ' _ FILE'
pub struct ServerConfig {
pub :
2022-04-06 18:17:33 +02:00
log_level string = " WARN "
log_file string = " vieter.log "
2022-02-21 20:51:41 +01:00
pkg_dir string
2022-02-21 20:40:13 +01:00
download_dir string
2022-02-21 20:51:41 +01:00
api_key string
repo_dir string
2022-02-21 22:32:54 +01:00
repos_file string
2022-02-21 20:40:13 +01:00
}
pub struct BuildConfig {
pub :
2022-02-22 08:14:20 +01:00
api_key string
address string
2022-02-21 20:40:13 +01:00
}
fn get_env_var ( field_name string ) ? string {
2022-02-21 20:51:41 +01:00
env_var_name := ' $ env . prefix $ field_name . to_upper () '
env_file_name := ' $ env . prefix $ field_name . to_upper () $ env . file_suffix'
2022-02-21 20:40:13 +01:00
env_var := os . getenv ( env_var_name )
env_file := os . getenv ( env_file_name )
2022-04-06 17:51:06 +02:00
// If both are missing , we return an empty string
2022-02-21 20:40:13 +01:00
if env_var = = ' ' && env_file = = ' ' {
2022-04-06 17:51:06 +02:00
return ' '
2022-02-21 20:40:13 +01:00
}
// If they're both set , we report a conflict
if env_var != ' ' && env_file != ' ' {
return error ( ' Only one of $ env_var_name or $ env_file_name can be defined . ' )
}
2022-02-21 22:22:36 +01:00
// If it's the env var itself , we return it .
// I'm pretty sure this also prevents variable ending in _ FILE ( e . g .
// VIETER_LOG_FILE ) from being mistakingely read as an _ FILE suffixed env
// var .
2022-02-21 20:40:13 +01:00
if env_var != ' ' {
return env_var
}
// Otherwise , we process the file
return os . read_file ( env_file ) or {
error ( ' Failed to read file defined in $ env_file_name : $ { err . msg } . ' )
}
}
2022-04-06 18:02:57 +02:00
// load < T > attempts to create an object of type T from the given path to a toml
// file & environment variables . For each field , it will select either a value
// given from an environment variable , a value defined in the config file or a
// configured default if present , in that order .
2022-04-06 17:51:06 +02:00
pub fn load < T > ( path string ) ? T {
2022-04-06 16:52:31 +02:00
mut res := T { }
2022-04-06 17:51:06 +02:00
if os . exists ( path ) {
2022-04-06 18:17:33 +02:00
// We don't use reflect here because reflect also sets any fields not
// in the toml back to " empty " , which we don't want
doc := toml . parse_file ( path ) ?
$ for field in T . fields {
s := doc . value ( field . name )
// We currently only support strings
if s . type_name () = = " string " {
res . $ ( field . name ) = s . string ()
}
}
2022-04-06 17:51:06 +02:00
}
2022-04-06 16:52:31 +02:00
2022-04-06 17:51:06 +02:00
$ for field in T . fields {
$ if field . typ is string {
env_value := get_env_var ( field . name ) ?
// The value of the env var will always be chosen over the config
// file
if env_value != ' ' {
res . $ ( field . name ) = env_value
}
// If there's no value from the toml file either , we try to find a
// default value
else if res . $ ( field . name ) = = ' ' {
2022-04-06 18:17:33 +02:00
return error ( " Missing config variable '$field.name' with no provided default. Either add it to the config file or provide it using an environment variable. " )
2022-04-06 17:51:06 +02:00
}
}
}
2022-02-21 20:40:13 +01:00
return res
}