src/CLIParser.h
branchv_0
changeset 3 2b57c8683ffe
parent 0 e8f15f432efc
equal deleted inserted replaced
2:2665ab0bcf44 3:2b57c8683ffe
    16  */
    16  */
    17 #pragma once
    17 #pragma once
    18 
    18 
    19 #include <vector>
    19 #include <vector>
    20 #include <iostream>
    20 #include <iostream>
       
    21 #include <codecvt>
       
    22 #include <regex>
    21 
    23 
    22 #include <relpipe/writer/typedefs.h>
    24 #include <relpipe/common/type/typedefs.h>
    23 #include <relpipe/cli/CLI.h>
    25 #include <relpipe/cli/CLI.h>
    24 #include <relpipe/cli/RelpipeCLIException.h>
    26 #include <relpipe/cli/RelpipeCLIException.h>
    25 
    27 
    26 #include "Configuration.h"
    28 #include "Configuration.h"
    27 
    29 
    29 namespace in {
    31 namespace in {
    30 namespace socket {
    32 namespace socket {
    31 
    33 
    32 class CLIParser {
    34 class CLIParser {
    33 private:
    35 private:
       
    36 	std::wstring_convert<codecvt_utf8<wchar_t>> convertor; // TODO: support also other encodings.
    34 
    37 
    35 	relpipe::writer::string_t readNext(const std::vector<relpipe::writer::string_t>& arguments, int& i) {
    38 	relpipe::common::type::StringX readNext(const std::vector<relpipe::common::type::StringX>& arguments, int& i) {
    36 		if (i < arguments.size()) return arguments[i++];
    39 		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);
    40 		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 	}
    41 	}
    39 
    42 
    40 	/**
    43 	/**
    44 		if (value == L"true") return true;
    47 		if (value == L"true") return true;
    45 		else if (value == L"false") return false;
    48 		else if (value == L"false") return false;
    46 		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);
    49 		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);
    47 	}
    50 	}
    48 
    51 
       
    52 	void setIfMissing(std::vector<Configuration::SocketOption>& options, const std::string& name, const std::string& value, bool includeEmptyValues = true) {
       
    53 		auto n = convertor.from_bytes(name);
       
    54 		auto v = convertor.from_bytes(value);
       
    55 		for (auto o : options) if (o.name == n) return;
       
    56 		if (value.size() || includeEmptyValues) options.push_back({n, v});
       
    57 	}
       
    58 
    49 public:
    59 public:
    50 
    60 
    51 	static const relpipe::writer::string_t OPTION_RELATION;
    61 	static const relpipe::common::type::StringX OPTION_RELATION;
    52 	static const relpipe::writer::string_t OPTION_UNLINK_ON_CLOSE;
    62 	static const relpipe::common::type::StringX OPTION_CONNECTION_STRING;
    53 	static const relpipe::writer::string_t OPTION_QUEUE;
    63 	static const relpipe::common::type::StringX OPTION_CONNECTION_OPTION;
    54 	static const relpipe::writer::string_t OPTION_MESSAGE_COUNT;
    64 	static const relpipe::writer::string_t OPTION_MESSAGE_COUNT;
    55 
    65 
    56 	Configuration parse(const std::vector<relpipe::writer::string_t>& arguments) {
    66 	Configuration parse(const std::vector<relpipe::common::type::StringX>& arguments) {
    57 		Configuration c;
    67 		Configuration c;
       
    68 		relpipe::common::type::StringX connectionString;
    58 
    69 
    59 		for (int i = 0; i < arguments.size();) {
    70 		for (int i = 0; i < arguments.size();) {
    60 			relpipe::writer::string_t option = readNext(arguments, i);
    71 			relpipe::common::type::StringX option = readNext(arguments, i);
    61 
    72 
    62 			if (option == OPTION_RELATION) {
    73 			if (option == OPTION_RELATION) {
    63 				c.relation = readNext(arguments, i);
    74 				c.relation = readNext(arguments, i);
    64 			} else if (option == OPTION_UNLINK_ON_CLOSE) {
       
    65 				c.unlinkOnClose = parseBoolean(readNext(arguments, i));
       
    66 			} else if (option == OPTION_QUEUE) {
       
    67 				c.queue = readNext(arguments, i);
       
    68 			} else if (option == OPTION_MESSAGE_COUNT) {
    75 			} else if (option == OPTION_MESSAGE_COUNT) {
    69 				c.messageCount = std::stoull(readNext(arguments, i));
    76 				c.messageCount = std::stoull(readNext(arguments, i));
       
    77 			} else if (option == OPTION_CONNECTION_STRING) {
       
    78 				connectionString = readNext(arguments, i);
       
    79 			} else if (option == OPTION_CONNECTION_OPTION) {
       
    80 				auto name = readNext(arguments, i);
       
    81 				auto value = readNext(arguments, i);
       
    82 				c.options.push_back({name, value});
    70 			} else throw relpipe::cli::RelpipeCLIException(L"Unsupported CLI option: " + option, relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS);
    83 			} else throw relpipe::cli::RelpipeCLIException(L"Unsupported CLI option: " + option, relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS);
    71 		}
    84 		}
       
    85 
       
    86 		using namespace options;
       
    87 
       
    88 		// Parse the connection string and convert it to options:
       
    89 		if (connectionString.size()) {
       
    90 			std::string connectionStringBytes = convertor.to_bytes(connectionString);
       
    91 			std::regex pattern("(tcp|udp|sctp)://(([^:]+)|\\[([0-9a-fA-F:]+(%[a-zA-Z0-9]+)?)\\]):([0-9]+)|(uds)://(.*)");
       
    92 			//                  1                23          4             5                    6        7       8
       
    93 			std::smatch match;
       
    94 			if (std::regex_match(connectionStringBytes, match, pattern)) {
       
    95 				setIfMissing(c.options, OPTION_PROTOCOL, match[1], false);
       
    96 				setIfMissing(c.options, OPTION_PROTOCOL, match[7], false);
       
    97 				setIfMissing(c.options, OPTION_HOST, match[3], false);
       
    98 				setIfMissing(c.options, OPTION_HOST, match[4], false);
       
    99 				setIfMissing(c.options, OPTION_PORT, match[6], false);
       
   100 				setIfMissing(c.options, OPTION_PATH, match[8], false);
       
   101 				if (match[1] == PROTOCOL_TCP) setIfMissing(c.options, OPTION_MODE, MODE_STREAM);
       
   102 			} else {
       
   103 				throw relpipe::cli::RelpipeCLIException(L"Invalid connection string: " + connectionString, relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS);
       
   104 			}
       
   105 		}
       
   106 
       
   107 		// Set defaults when values are missing:
       
   108 		setIfMissing(c.options, OPTION_PROTOCOL, PROTOCOL_UDP);
       
   109 		setIfMissing(c.options, OPTION_ROLE, ROLE_SERVER);
       
   110 		setIfMissing(c.options, OPTION_MODE, MODE_DATAGRAM);
       
   111 		setIfMissing(c.options, OPTION_HOST, "127.0.0.1");
       
   112 		setIfMissing(c.options, OPTION_PORT, "64000");
    72 
   113 
    73 		return c;
   114 		return c;
    74 	}
   115 	}
    75 
   116 
    76 	virtual ~CLIParser() {
   117 	virtual ~CLIParser() {
    77 	}
   118 	}
    78 };
   119 };
    79 
   120 
    80 const relpipe::writer::string_t CLIParser::OPTION_RELATION = L"--relation";
   121 const relpipe::common::type::StringX CLIParser::OPTION_RELATION = L"--relation";
    81 const relpipe::writer::string_t CLIParser::OPTION_UNLINK_ON_CLOSE = L"--unlink-on-close";
   122 const relpipe::common::type::StringX CLIParser::OPTION_CONNECTION_STRING = L"--connection-string";
    82 const relpipe::writer::string_t CLIParser::OPTION_QUEUE = L"--queue";
   123 const relpipe::common::type::StringX CLIParser::OPTION_CONNECTION_OPTION = L"--connection-option";
    83 const relpipe::writer::string_t CLIParser::OPTION_MESSAGE_COUNT = L"--message-count";
   124 const relpipe::writer::string_t CLIParser::OPTION_MESSAGE_COUNT = L"--message-count";
    84 
   125 
    85 }
   126 }
    86 }
   127 }
    87 }
   128 }