src/CLIParser.h
branchv_0
changeset 42 ca216de56ef0
parent 41 e1339b8e838e
equal deleted inserted replaced
41:e1339b8e838e 42:ca216de56ef0
    30 namespace tabular {
    30 namespace tabular {
    31 
    31 
    32 class CLIParser {
    32 class CLIParser {
    33 private:
    33 private:
    34 
    34 
       
    35 	std::wstring_convert<std::codecvt_utf8<wchar_t>> convertor; // TODO: support also other encodings.
       
    36 
    35 	relpipe::reader::string_t readNext(const std::vector<relpipe::reader::string_t>& arguments, int& i) {
    37 	relpipe::reader::string_t readNext(const std::vector<relpipe::reader::string_t>& arguments, int& i) {
    36 		if (i < arguments.size()) return arguments[i++];
    38 		if (i < arguments.size()) return arguments[i++];
    37 		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);
    39 		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);
    38 	}
    40 	}
    39 
    41 
    55 	bool parseBoolean(const relpipe::reader::string_t& value) {
    57 	bool parseBoolean(const relpipe::reader::string_t& value) {
    56 		if (value == L"true") return true;
    58 		if (value == L"true") return true;
    57 		else if (value == L"false") return false;
    59 		else if (value == L"false") return false;
    58 		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);
    60 		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);
    59 	}
    61 	}
    60 	
    62 
    61 	Configuration::ColorScheme parseColorScheme(const relpipe::reader::string_t& value) {
    63 	Configuration::ColorScheme parseColorScheme(const relpipe::reader::string_t& value) {
    62 		if (value == L"greenish") return Configuration::ColorScheme::Greenish;
    64 		if (value == L"greenish") return Configuration::ColorScheme::Greenish;
    63 		else if (value == L"amberish") return Configuration::ColorScheme::Amberish;
    65 		else if (value == L"amberish") return Configuration::ColorScheme::Amberish;
    64 		else if (value == L"black-and-white") return Configuration::ColorScheme::BlackAndWhite;
    66 		else if (value == L"black-and-white") return Configuration::ColorScheme::BlackAndWhite;
    65 		else if (value == L"midnight") return Configuration::ColorScheme::Midnight;
    67 		else if (value == L"midnight") return Configuration::ColorScheme::Midnight;
    66 		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);
    68 		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);
    67 	}
    69 	}
    68 	
    70 
    69 	Configuration::TableStyle parseTableStyle(const relpipe::reader::string_t& value) {
    71 	Configuration::TableStyle parseTableStyle(const relpipe::reader::string_t& value) {
    70 		if (value == L"rounded") return Configuration::TableStyle::Rounded;
    72 		if (value == L"rounded") return Configuration::TableStyle::Rounded;
    71 		else if (value == L"sharp") return Configuration::TableStyle::Sharp;
    73 		else if (value == L"sharp") return Configuration::TableStyle::Sharp;
    72 		else if (value == L"sharp-double") return Configuration::TableStyle::SharpDouble;
    74 		else if (value == L"sharp-double") return Configuration::TableStyle::SharpDouble;
    73 		else if (value == L"horizontal-only") return Configuration::TableStyle::HorizontalOnly;
    75 		else if (value == L"horizontal-only") return Configuration::TableStyle::HorizontalOnly;
    74 		else if (value == L"ascii") return Configuration::TableStyle::Ascii;
    76 		else if (value == L"ascii") return Configuration::TableStyle::Ascii;
    75 		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);
    77 		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);
       
    78 	}
       
    79 
       
    80 	/**
       
    81 	 * TODO: move to common parent class in relpipe-lib-cli
       
    82 	 * 
       
    83 	 * @param cliArguments original CLI arguments
       
    84 	 * @param moduleName name of this program e.g. "relpipe-out-tabular"
       
    85 	 * @param supportedOptions options that could be specified also as environmental variables, not only CLI arguments, including the "--" prefix e.g. "--color-scheme"
       
    86 	 * @return defaults found in ENV + given CLI arguments
       
    87 	 */
       
    88 	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) {
       
    89 		std::vector<relpipe::reader::string_t> allArguments;
       
    90 
       
    91 		auto toEnv = [](char ch) {
       
    92 			return ch == '-' ? '_' : ::toupper(ch);
       
    93 		};
       
    94 
       
    95 		std::string envPrefix = convertor.to_bytes(moduleName);
       
    96 		transform(envPrefix.begin(), envPrefix.end(), envPrefix.begin(), toEnv);
       
    97 		envPrefix.append("_");
       
    98 
       
    99 		for (auto o : supportedOptions) {
       
   100 			if (o.substr(0, 2) == L"--") {
       
   101 				std::string option = convertor.to_bytes(o);
       
   102 				option.erase(0, 2);
       
   103 				transform(option.begin(), option.end(), option.begin(), toEnv);
       
   104 
       
   105 				// FIXME: check argument counts (should be specified alongside with supportedOptions) to avoid injection of unwanted options through ENV variables
       
   106 				// TODO: allow repeated options?
       
   107 				for (int i = -1; i < 10; i++) {
       
   108 					std::string envName = i == -1 ? envPrefix + option : envPrefix + option + "_" + std::to_string(i);
       
   109 					const char* value = std::getenv(envName.c_str());
       
   110 					if (value) {
       
   111 						if (i == -1 || i == 0) allArguments.push_back(o);
       
   112 						allArguments.push_back(convertor.from_bytes(value));
       
   113 						if (i == -1) break;
       
   114 					} else {
       
   115 						if (i > -1) break;
       
   116 					}
       
   117 				}
       
   118 			} else {
       
   119 				throw std::logic_error("supportedOptions must start with '--'");
       
   120 			}
       
   121 		}
       
   122 
       
   123 		for (auto a : cliArguments) allArguments.push_back(a);
       
   124 
       
   125 		return allArguments;
    76 	}
   126 	}
    77 
   127 
    78 public:
   128 public:
    79 
   129 
    80 	static const relpipe::reader::string_t OPTION_RELATION;
   130 	static const relpipe::reader::string_t OPTION_RELATION;
    82 	static const relpipe::reader::string_t OPTION_WRITE_RELATION_NAME;
   132 	static const relpipe::reader::string_t OPTION_WRITE_RELATION_NAME;
    83 	static const relpipe::reader::string_t OPTION_WRITE_RECORD_COUNT;
   133 	static const relpipe::reader::string_t OPTION_WRITE_RECORD_COUNT;
    84 	static const relpipe::reader::string_t OPTION_COLOR_SCHEME;
   134 	static const relpipe::reader::string_t OPTION_COLOR_SCHEME;
    85 	static const relpipe::reader::string_t OPTION_TABLE_STYLE;
   135 	static const relpipe::reader::string_t OPTION_TABLE_STYLE;
    86 
   136 
    87 	Configuration parse(const std::vector<relpipe::reader::string_t>& arguments) {
   137 	Configuration parse(const std::vector<relpipe::reader::string_t>& cliArguments) {
    88 		Configuration c;
   138 		Configuration c;
    89 		RelationConfiguration currentRelation;
   139 		RelationConfiguration currentRelation;
    90 		
   140 
       
   141 		const std::vector<relpipe::reader::string_t> arguments = addEnvDefaults(cliArguments, L"relpipe-out-tabular",{OPTION_COLOR_SCHEME, OPTION_TABLE_STYLE});
       
   142 
    91 		// TODO: global configuration of writeTypes, writeRelationName, writeRecordCount
   143 		// TODO: global configuration of writeTypes, writeRelationName, writeRecordCount
    92 
   144 
    93 		for (int i = 0; i < arguments.size();) {
   145 		for (int i = 0; i < arguments.size();) {
    94 			relpipe::reader::string_t option = readNext(arguments, i);
   146 			relpipe::reader::string_t option = readNext(arguments, i);
    95 
   147