feat: allow setting empty env var value

main
Jef Roosens 2022-12-28 18:36:58 +01:00
parent 997e9611eb
commit 6678040d30
Signed by: Jef Roosens
GPG Key ID: B75D4F293C7052DB
4 changed files with 108 additions and 35 deletions

24
conf.v
View File

@ -17,20 +17,18 @@ pub struct LoadConfig {
// looks for either `${env.prefix}${field_name.to_upper()}` or // looks for either `${env.prefix}${field_name.to_upper()}` or
// `${env.prefix}${field_name.to_upper()}${env.file_suffix}`, returning the // `${env.prefix}${field_name.to_upper()}${env.file_suffix}`, returning the
// contents of the file instead if the latter. If both or neither exist, the // contents of the file instead if the latter. If both or neither exist, the
// function returns an error. // function returns an error. It returns two values, with the first indicating
fn (ld LoadConfig) get_env_var(field_name string) !string { // whether the env vars were actually present.
fn (ld LoadConfig) get_env_var(field_name string) !(bool, string) {
env_var_name := '$ld.prefix$field_name.to_upper()' env_var_name := '$ld.prefix$field_name.to_upper()'
env_file_name := '$ld.prefix$field_name.to_upper()$ld.file_suffix' env_file_name := '$ld.prefix$field_name.to_upper()$ld.file_suffix'
env_var := ld.env[env_var_name] or { '' }
env_file := ld.env[env_file_name] or { '' }
// If both are missing, we return an empty string if env_var_name !in ld.env && env_file_name !in ld.env {
if env_var == '' && env_file == '' { return false, ''
return ''
} }
// If they're both set, we report a conflict // If they're both set, we report a conflict
if env_var != '' && env_file != '' { if env_var_name in ld.env && env_file_name in ld.env {
return error('Only one of $env_var_name or $env_file_name can be defined.') return error('Only one of $env_var_name or $env_file_name can be defined.')
} }
@ -38,12 +36,12 @@ fn (ld LoadConfig) get_env_var(field_name string) !string {
// I'm pretty sure this also prevents variable ending in _FILE (e.g. // 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 // VIETER_LOG_FILE) from being mistakingely read as an _FILE suffixed env
// var. // var.
if env_var != '' { if env_var_name in ld.env {
return env_var return true, ld.env[env_var_name]
} }
// Otherwise, we process the file // Otherwise, we process the file
return os.read_file(env_file) or { return true, os.read_file(ld.env[env_file_name]) or {
error('Failed to read file defined in $env_file_name: ${err.msg()}.') error('Failed to read file defined in $env_file_name: ${err.msg()}.')
} }
} }
@ -86,11 +84,11 @@ pub fn load<T>(ld LoadConfig) !T {
} }
$for field in T.fields { $for field in T.fields {
env_value := ld.get_env_var(field.name)! env_present, env_value := ld.get_env_var(field.name)!
// The value of an env var will always take precedence over the toml // The value of an env var will always take precedence over the toml
// file. // file.
if env_value != '' { if env_present {
$if field.typ is string { $if field.typ is string {
res.$(field.name) = env_value res.$(field.name) = env_value
} $else $if field.typ is int { } $else $if field.typ is int {

View File

@ -8,29 +8,47 @@ struct SingleConfDefault {
some_string string = 'default' some_string string = 'default'
} }
const env = {
'SOME_STRING': 'env'
}
const prefix_env = {
'TEST_SOME_STRING': 'env'
}
fn test_string_present_no_default() { fn test_string_present_no_default() {
conf := load<SingleConf>(default_path: 'test/string.toml')! conf := load<SingleConf>(default_path: 'test/string.toml')!
assert conf == SingleConf{ assert conf == SingleConf{
some_string: 'hi' some_string: 'hi'
} }
conf2 := load<SingleConf>(default_path: 'test/string_empty.toml')!
assert conf2 == SingleConf{
some_string: ''
}
} }
fn test_string_present_no_default_env() { fn test_string_present_no_default_env() {
conf := load<SingleConf>(default_path: 'test/string.toml', env: .env)! mut conf := load<SingleConf>(
default_path: 'test/string.toml'
env: {
'SOME_STRING': 'env'
}
)!
assert conf == SingleConf{ assert conf == SingleConf{
some_string: 'env' some_string: 'env'
} }
conf2 := load<SingleConf>(default_path: 'test/string.toml', env: .prefix_env, prefix: 'TEST_')! conf = load<SingleConf>(
assert conf2 == SingleConf{ default_path: 'test/string.toml'
env: {
'SOME_STRING': ''
}
)!
assert conf == SingleConf{
some_string: ''
}
conf = load<SingleConf>(
default_path: 'test/string.toml'
env: {
'TEST_SOME_STRING': 'env'
}
prefix: 'TEST_'
)!
assert conf == SingleConf{
some_string: 'env' some_string: 'env'
} }
} }
@ -41,13 +59,34 @@ fn test_string_absent_no_default() {
} }
fn test_string_absent_no_default_env() { fn test_string_absent_no_default_env() {
conf := load<SingleConf>(default_path: 'test/string.toml', env: .env)! mut conf := load<SingleConf>(
default_path: 'test/string.toml'
env: {
'SOME_STRING': 'env'
}
)!
assert conf == SingleConf{ assert conf == SingleConf{
some_string: 'env' some_string: 'env'
} }
conf2 := load<SingleConf>(default_path: 'test/string.toml', env: .prefix_env, prefix: 'TEST_')! conf = load<SingleConf>(
assert conf2 == SingleConf{ default_path: 'test/string.toml'
env: {
'SOME_STRING': ''
}
)!
assert conf == SingleConf{
some_string: ''
}
conf = load<SingleConf>(
default_path: 'test/string.toml'
env: {
'TEST_SOME_STRING': 'env'
}
prefix: 'TEST_'
)!
assert conf == SingleConf{
some_string: 'env' some_string: 'env'
} }
} }
@ -60,17 +99,34 @@ fn test_string_present_default() {
} }
fn test_string_present_default_env() { fn test_string_present_default_env() {
conf := load<SingleConfDefault>(default_path: 'test/string.toml', env: .env)! mut conf := load<SingleConfDefault>(
default_path: 'test/string.toml'
env: {
'SOME_STRING': 'env'
}
)!
assert conf == SingleConfDefault{ assert conf == SingleConfDefault{
some_string: 'env' some_string: 'env'
} }
conf2 := load<SingleConfDefault>( conf = load<SingleConfDefault>(
default_path: 'test/string.toml' default_path: 'test/string.toml'
env: .prefix_env env: {
'SOME_STRING': ''
}
)!
assert conf == SingleConfDefault{
some_string: ''
}
conf = load<SingleConfDefault>(
default_path: 'test/string.toml'
env: {
'TEST_SOME_STRING': 'env'
}
prefix: 'TEST_' prefix: 'TEST_'
)! )!
assert conf2 == SingleConfDefault{ assert conf == SingleConfDefault{
some_string: 'env' some_string: 'env'
} }
} }
@ -83,17 +139,34 @@ fn test_string_absent_default() {
} }
fn test_string_absent_default_env() { fn test_string_absent_default_env() {
conf := load<SingleConfDefault>(default_path: 'test/empty.toml', env: .env)! mut conf := load<SingleConfDefault>(
default_path: 'test/empty.toml'
env: {
'SOME_STRING': 'env'
}
)!
assert conf == SingleConfDefault{ assert conf == SingleConfDefault{
some_string: 'env' some_string: 'env'
} }
conf2 := load<SingleConfDefault>( conf = load<SingleConfDefault>(
default_path: 'test/empty.toml' default_path: 'test/empty.toml'
env: .prefix_env env: {
'SOME_STRING': ''
}
)!
assert conf == SingleConfDefault{
some_string: ''
}
conf = load<SingleConfDefault>(
default_path: 'test/empty.toml'
env: {
'TEST_SOME_STRING': 'env'
}
prefix: 'TEST_' prefix: 'TEST_'
)! )!
assert conf2 == SingleConfDefault{ assert conf == SingleConfDefault{
some_string: 'env' some_string: 'env'
} }
} }

1
test/int.toml 100644
View File

@ -0,0 +1 @@
some_int = "hi"

View File

@ -0,0 +1 @@
some_string = ""