allow setting some options through ENV variables (not only CLI arguments) v_0 tip
authorFrantišek Kučera <franta-hg@frantovo.cz>
Sat, 02 Jul 2022 19:45:18 +0200
branchv_0
changeset 42 ca216de56ef0
parent 41 e1339b8e838e
allow setting some options through ENV variables (not only CLI arguments)
src/CLIParser.h
--- a/src/CLIParser.h	Sat Jul 02 15:21:59 2022 +0200
+++ b/src/CLIParser.h	Sat Jul 02 19:45:18 2022 +0200
@@ -32,6 +32,8 @@
 class CLIParser {
 private:
 
+	std::wstring_convert<std::codecvt_utf8<wchar_t>> convertor; // TODO: support also other encodings.
+
 	relpipe::reader::string_t readNext(const std::vector<relpipe::reader::string_t>& arguments, int& i) {
 		if (i < arguments.size()) return arguments[i++];
 		else throw relpipe::cli::RelpipeCLIException(L"Missing CLI argument" + (i > 0 ? (L" after " + arguments[i - 1]) : L""), relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS);
@@ -57,7 +59,7 @@
 		else if (value == L"false") return false;
 		else throw relpipe::cli::RelpipeCLIException(L"Unable to parse boolean value: " + value + L" (expecting true or false)", relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS);
 	}
-	
+
 	Configuration::ColorScheme parseColorScheme(const relpipe::reader::string_t& value) {
 		if (value == L"greenish") return Configuration::ColorScheme::Greenish;
 		else if (value == L"amberish") return Configuration::ColorScheme::Amberish;
@@ -65,7 +67,7 @@
 		else if (value == L"midnight") return Configuration::ColorScheme::Midnight;
 		else throw relpipe::cli::RelpipeCLIException(L"Unable to parse ColorScheme value: " + value + L" (expecting greenish, amberish, midnight or black-and-white)", relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS);
 	}
-	
+
 	Configuration::TableStyle parseTableStyle(const relpipe::reader::string_t& value) {
 		if (value == L"rounded") return Configuration::TableStyle::Rounded;
 		else if (value == L"sharp") return Configuration::TableStyle::Sharp;
@@ -75,6 +77,54 @@
 		else throw relpipe::cli::RelpipeCLIException(L"Unable to parse TableStyle value: " + value + L" (expecting rounded, sharp, ascii)", relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS);
 	}
 
+	/**
+	 * TODO: move to common parent class in relpipe-lib-cli
+	 * 
+	 * @param cliArguments original CLI arguments
+	 * @param moduleName name of this program e.g. "relpipe-out-tabular"
+	 * @param supportedOptions options that could be specified also as environmental variables, not only CLI arguments, including the "--" prefix e.g. "--color-scheme"
+	 * @return defaults found in ENV + given CLI arguments
+	 */
+	const std::vector<relpipe::reader::string_t> addEnvDefaults(const std::vector<relpipe::reader::string_t>& cliArguments, const relpipe::reader::string_t moduleName, const std::vector<relpipe::reader::string_t>& supportedOptions) {
+		std::vector<relpipe::reader::string_t> allArguments;
+
+		auto toEnv = [](char ch) {
+			return ch == '-' ? '_' : ::toupper(ch);
+		};
+
+		std::string envPrefix = convertor.to_bytes(moduleName);
+		transform(envPrefix.begin(), envPrefix.end(), envPrefix.begin(), toEnv);
+		envPrefix.append("_");
+
+		for (auto o : supportedOptions) {
+			if (o.substr(0, 2) == L"--") {
+				std::string option = convertor.to_bytes(o);
+				option.erase(0, 2);
+				transform(option.begin(), option.end(), option.begin(), toEnv);
+
+				// FIXME: check argument counts (should be specified alongside with supportedOptions) to avoid injection of unwanted options through ENV variables
+				// TODO: allow repeated options?
+				for (int i = -1; i < 10; i++) {
+					std::string envName = i == -1 ? envPrefix + option : envPrefix + option + "_" + std::to_string(i);
+					const char* value = std::getenv(envName.c_str());
+					if (value) {
+						if (i == -1 || i == 0) allArguments.push_back(o);
+						allArguments.push_back(convertor.from_bytes(value));
+						if (i == -1) break;
+					} else {
+						if (i > -1) break;
+					}
+				}
+			} else {
+				throw std::logic_error("supportedOptions must start with '--'");
+			}
+		}
+
+		for (auto a : cliArguments) allArguments.push_back(a);
+
+		return allArguments;
+	}
+
 public:
 
 	static const relpipe::reader::string_t OPTION_RELATION;
@@ -84,10 +134,12 @@
 	static const relpipe::reader::string_t OPTION_COLOR_SCHEME;
 	static const relpipe::reader::string_t OPTION_TABLE_STYLE;
 
-	Configuration parse(const std::vector<relpipe::reader::string_t>& arguments) {
+	Configuration parse(const std::vector<relpipe::reader::string_t>& cliArguments) {
 		Configuration c;
 		RelationConfiguration currentRelation;
-		
+
+		const std::vector<relpipe::reader::string_t> arguments = addEnvDefaults(cliArguments, L"relpipe-out-tabular",{OPTION_COLOR_SCHEME, OPTION_TABLE_STYLE});
+
 		// TODO: global configuration of writeTypes, writeRelationName, writeRecordCount
 
 		for (int i = 0; i < arguments.size();) {