Skip to content

Commit 7b5831a

Browse files
committed
Environment variables parsing utility functions
Add utility functions for parsing UR environment variables. Format of env vars should follow examples: - getenv_to_map() ENV_VAR="param_1:value_1,value_2;param_2:value_1" - getenv_to_vec() ENV_VAR="1,4K,2M" Function getenv_to_map() returns an std::optional with a dictionary of parameters and values passed with an env var. Function getenv_to_vec() returns an std::optional with a vector of values passed with an env var.
1 parent 93322cb commit 7b5831a

File tree

1 file changed

+144
-11
lines changed

1 file changed

+144
-11
lines changed

source/common/ur_util.hpp

Lines changed: 144 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,13 @@
99
#ifndef UR_UTIL_H
1010
#define UR_UTIL_H 1
1111

12+
#include <iostream>
13+
#include <map>
14+
#include <optional>
15+
#include <sstream>
1216
#include <string.h>
1317
#include <string>
18+
#include <vector>
1419

1520
/* for compatibility with non-clang compilers */
1621
#if defined(__has_feature)
@@ -86,23 +91,151 @@ inline std::string create_library_path(const char *name, const char *path) {
8691
#endif
8792

8893
///////////////////////////////////////////////////////////////////////////////
89-
inline bool getenv_tobool(const char *name) {
90-
const char *env = nullptr;
91-
94+
inline std::optional<std::string> ur_getenv(const char *name) {
9295
#if defined(_WIN32)
93-
char buffer[8];
94-
auto rc = GetEnvironmentVariable(name, buffer, 8);
95-
if (0 != rc && rc <= 8) {
96-
env = buffer;
96+
constexpr int buffer_size = 1024;
97+
char buffer[buffer_size];
98+
auto rc = GetEnvironmentVariable(name, buffer, buffer_size);
99+
if (0 != rc && rc < buffer_size) {
100+
return std::string(buffer);
101+
} else if (rc >= buffer_size) {
102+
std::stringstream ex_ss;
103+
ex_ss << "Environment variable " << name << " value too long!"
104+
<< " Maximum length is " << buffer_size - 1 << " characters.";
105+
throw std::invalid_argument(ex_ss.str());
97106
}
107+
return std::nullopt;
98108
#else
99-
env = getenv(name);
109+
const char *tmp_env = getenv(name);
110+
if (tmp_env != nullptr) {
111+
return std::string(tmp_env);
112+
} else {
113+
return std::nullopt;
114+
}
100115
#endif
116+
}
117+
118+
inline bool getenv_tobool(const char *name) {
119+
auto env = ur_getenv(name);
120+
return env.has_value();
121+
}
122+
123+
static void throw_wrong_format_vec(const char *env_var_name) {
124+
std::stringstream ex_ss;
125+
ex_ss << "Wrong format of the " << env_var_name << " environment variable!"
126+
<< " Proper format is: ENV_VAR=\"value_1,value_2,value_3\"";
127+
throw std::invalid_argument(ex_ss.str());
128+
}
129+
130+
static void throw_wrong_format_map(const char *env_var_name) {
131+
std::stringstream ex_ss;
132+
ex_ss << "Wrong format of the " << env_var_name << " environment variable!"
133+
<< " Proper format is: ENV_VAR=\"param_1:value_1,value_2;param_2:value_1";
134+
throw std::invalid_argument(ex_ss.str());
135+
}
136+
137+
/// @brief Get a vector of values from an environment variable \p env_var_name
138+
/// A comma is a delimiter for extracting values from env var string.
139+
/// Colons and semicolons are not allowed to align with the similar
140+
/// getenv_to_map() util function and avoid confusion.
141+
/// A vector with a single value is allowed.
142+
/// Env var must consist of strings separated by commas, ie.:
143+
/// ENV_VAR=1,4K,2M
144+
/// @param env_var_name name of an environment variable to be parsed
145+
/// @return std::optional with a possible vector of strings containing parsed values
146+
/// and std::nullopt when the environment variable is not set or is empty
147+
/// @throws std::invalid_argument() when the parsed environment variable has wrong format
148+
inline std::optional<std::vector<std::string>> getenv_to_vec(const char *env_var_name) {
149+
char values_delim = ',';
150+
151+
auto env_var = ur_getenv(env_var_name);
152+
if (!env_var.has_value()) {
153+
return std::nullopt;
154+
}
155+
156+
if (env_var->find(':') == std::string::npos && env_var->find(';') == std::string::npos) {
157+
std::stringstream values_ss(*env_var);
158+
std::string value;
159+
std::vector<std::string> values_vec;
160+
while (std::getline(values_ss, value, values_delim)) {
161+
if (value.empty()) {
162+
throw_wrong_format_vec(env_var_name);
163+
}
164+
values_vec.push_back(value);
165+
}
166+
return values_vec;
167+
} else {
168+
throw_wrong_format_vec(env_var_name);
169+
}
170+
return std::nullopt;
171+
}
172+
173+
using EnvVarMap = std::map<std::string, std::vector<std::string>>;
174+
175+
/// @brief Get a map of parameters and their values from an environment variable
176+
/// \p env_var_name
177+
/// Semicolon is a delimiter for extracting key-values pairs from
178+
/// an env var string. Colon is a delimiter for splitting key-values pairs
179+
/// into keys and their values. Comma is a delimiter for values.
180+
/// All special characters in parameter and value strings are allowed except
181+
/// the delimiters.
182+
/// Env vars without parameter names are not allowed, use the getenv_to_vec()
183+
/// util function instead.
184+
/// Keys in a map are parsed parameters and values are vectors of strings
185+
/// containing parameters' values, ie.:
186+
/// ENV_VAR="param_1:value_1,value_2;param_2:value_1"
187+
/// result map:
188+
/// map[param_1] = [value_1, value_2]
189+
/// map[param_2] = [value_1]
190+
/// @param env_var_name name of an environment variable to be parsed
191+
/// @return std::optional with a possible map with parsed parameters as keys and
192+
/// vectors of strings containing parsed values as keys.
193+
/// Otherwise, optional is set to std::nullopt when the environment variable
194+
/// is not set or is empty.
195+
/// @throws std::invalid_argument() when the parsed environment variable has wrong format
196+
inline std::optional<EnvVarMap> getenv_to_map(const char *env_var_name) {
197+
char main_delim = ';';
198+
char key_value_delim = ':';
199+
char values_delim = ',';
200+
EnvVarMap map;
201+
202+
auto env_var = ur_getenv(env_var_name);
203+
if (!env_var.has_value()) {
204+
return std::nullopt;
205+
}
206+
207+
std::stringstream ss(*env_var);
208+
std::string key_value;
209+
while (std::getline(ss, key_value, main_delim)) {
210+
std::string key;
211+
std::string values;
212+
std::stringstream kv_ss(key_value);
213+
214+
if (key_value.find(':') == std::string::npos) {
215+
throw_wrong_format_map(env_var_name);
216+
}
217+
218+
std::getline(kv_ss, key, key_value_delim);
219+
std::getline(kv_ss, values);
220+
if (key.empty() || values.empty() || values.find(':') != std::string::npos || map.find(key) != map.end()) {
221+
throw_wrong_format_map(env_var_name);
222+
}
101223

102-
if (nullptr == env) {
103-
return false;
224+
std::vector<std::string> values_vec;
225+
std::stringstream values_ss(values);
226+
std::string value;
227+
while (std::getline(values_ss, value, values_delim)) {
228+
if (value.empty()) {
229+
throw_wrong_format_map(env_var_name);
230+
}
231+
values_vec.push_back(value);
232+
}
233+
if (values_vec.empty()) {
234+
throw_wrong_format_map(env_var_name);
235+
}
236+
map[key] = values_vec;
104237
}
105-
return 0 == strcmp("1", env);
238+
return map;
106239
}
107240

108241
#endif /* UR_UTIL_H */

0 commit comments

Comments
 (0)