Moved over files from vieter repo
This commit is contained in:
commit
662d8cd2e6
4 changed files with 116 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
*.so
|
||||
4
README.md
Normal file
4
README.md
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Vconf
|
||||
|
||||
Vconf is a small V library for loading in configuration from a TOML config file
|
||||
and/or environment variables.
|
||||
7
v.mod
Normal file
7
v.mod
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
Module {
|
||||
name: 'vconf'
|
||||
description: 'A small library for reading configuration from a TOML file & environment variables'
|
||||
version: '0.0.0'
|
||||
license: 'MIT'
|
||||
dependencies: []
|
||||
}
|
||||
104
vconf.v
Normal file
104
vconf.v
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
module vconf
|
||||
|
||||
import os
|
||||
import toml
|
||||
|
||||
// get_env_var tries to read the contents of the given environment variable. It
|
||||
// looks for either `${env.prefix}${field_name.to_upper()}` or
|
||||
// `${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
|
||||
// function returns an error.
|
||||
fn get_env_var(prefix string, field_name string, file_suffix string) ?string {
|
||||
env_var_name := '$prefix$field_name.to_upper()'
|
||||
env_file_name := '$prefix$field_name.to_upper()$file_suffix'
|
||||
env_var := os.getenv(env_var_name)
|
||||
env_file := os.getenv(env_file_name)
|
||||
|
||||
// If both are missing, we return an empty string
|
||||
if env_var == '' && env_file == '' {
|
||||
return ''
|
||||
}
|
||||
|
||||
// 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.')
|
||||
}
|
||||
|
||||
// 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.
|
||||
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()}.')
|
||||
}
|
||||
}
|
||||
|
||||
[params]
|
||||
struct LoadConfig {
|
||||
prefix string
|
||||
file_suffix string = '_FILE'
|
||||
default_path string
|
||||
}
|
||||
|
||||
// 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.
|
||||
pub fn load<T>(conf LoadConfig) ?T {
|
||||
mut res := T{}
|
||||
|
||||
// Later, this could be read from an env var as well.
|
||||
path := conf.default_path
|
||||
|
||||
if os.exists(path) {
|
||||
// We don't use reflect here because reflect also sets any fields not
|
||||
// in the toml back to their zero value, which we don't want
|
||||
doc := toml.parse_file(path)?
|
||||
|
||||
$for field in T.fields {
|
||||
s := doc.value(field.name)
|
||||
|
||||
if s !is toml.Null {
|
||||
$if field.typ is string {
|
||||
res.$(field.name) = s.string()
|
||||
} $else $if field.typ is int {
|
||||
res.$(field.name) = s.int()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$for field in T.fields {
|
||||
env_value := get_env_var(conf.prefix, field.name, conf.file_suffix)?
|
||||
|
||||
// The value of an env var will always take precedence over the toml
|
||||
// file.
|
||||
if env_value != '' {
|
||||
$if field.typ is string {
|
||||
res.$(field.name) = env_value
|
||||
} $else $if field.typ is int {
|
||||
res.$(field.name) = env_value.int()
|
||||
}
|
||||
}
|
||||
|
||||
// Now, we check whether a value is present. If there isn't, that means
|
||||
// it isn't in the config file, nor is there a default or an env var.
|
||||
mut has_value := false
|
||||
|
||||
$if field.typ is string {
|
||||
has_value = res.$(field.name) != ''
|
||||
} $else $if field.typ is int {
|
||||
has_value = res.$(field.name) != 0
|
||||
}
|
||||
|
||||
if !has_value {
|
||||
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.")
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
Reference in a new issue