change CLI interface: options: --relation --print-types --print-relation-name --print-record-count v_0
authorFrantišek Kučera <franta-hg@frantovo.cz>
Thu, 24 Sep 2020 18:57:20 +0200
branchv_0
changeset 30 3c6374467a82
parent 29 249a66b6e34e
child 31 c22577615ce4
change CLI interface: options: --relation --print-types --print-relation-name --print-record-count
bash-completion.sh
src/CLIParser.h
src/Configuration.h
src/TabularPrefetchingHandler.h
src/relpipe-out-tabular.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bash-completion.sh	Thu Sep 24 18:57:20 2020 +0200
@@ -0,0 +1,45 @@
+# Relational pipes
+# Copyright © 2019 František Kučera (Frantovo.cz, GlobalCode.info)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, version 3 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+_relpipe_out_tabular_completion() {
+	local w0 w1 w2 w3
+
+	COMPREPLY=()
+	w0=${COMP_WORDS[COMP_CWORD]}
+	w1=${COMP_WORDS[COMP_CWORD-1]}
+	w2=${COMP_WORDS[COMP_CWORD-2]}
+	w3=${COMP_WORDS[COMP_CWORD-3]}
+
+	BOOLEAN_VALUES=(
+		"true"
+		"false"
+	)
+
+	if   [[ "$w1" == "--relation"                      && "x$w0" == "x" ]];    then COMPREPLY=("'.+'")
+	elif [[ "$w1" == "--print-types"                                    ]];    then COMPREPLY=($(compgen -W "${BOOLEAN_VALUES[*]}" -- "$w0"))
+	elif [[ "$w1" == "--print-relation-name"                            ]];    then COMPREPLY=($(compgen -W "${BOOLEAN_VALUES[*]}" -- "$w0"))
+	elif [[ "$w1" == "--print-record-count"                             ]];    then COMPREPLY=($(compgen -W "${BOOLEAN_VALUES[*]}" -- "$w0"))
+	else
+		OPTIONS=(
+			"--relation"
+			"--print-types"
+			"--print-relation-name"
+			"--print-record-count"
+		)
+		COMPREPLY=($(compgen -W "${OPTIONS[*]}" -- "$w0"))
+	fi
+}
+
+complete -F _relpipe_out_tabular_completion relpipe-out-tabular
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/CLIParser.h	Thu Sep 24 18:57:20 2020 +0200
@@ -0,0 +1,103 @@
+/**
+ * Relational pipes
+ * Copyright © 2020 František Kučera (Frantovo.cz, GlobalCode.info)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+
+#include <vector>
+#include <iostream>
+
+#include <relpipe/reader/typedefs.h>
+#include <relpipe/cli/CLI.h>
+#include <relpipe/cli/RelpipeCLIException.h>
+
+#include "Configuration.h"
+
+namespace relpipe {
+namespace out {
+namespace tabular {
+
+class CLIParser {
+private:
+
+	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);
+	}
+
+	void addRelation(Configuration& c, RelationConfiguration& currentRelation) {
+		if (currentRelation.relation.size()) {
+			c.relationConfigurations.push_back(currentRelation);
+		} else {
+			// no relation name given → global configuration
+			c.printTypes = currentRelation.printTypes;
+			c.printRelationName = currentRelation.printRelationName;
+			c.printRecordCount = currentRelation.printRecordCount;
+		}
+		currentRelation = RelationConfiguration();
+	}
+
+	/**
+	 * TODO: use a common method
+	 */
+	bool parseBoolean(const relpipe::reader::string_t& value) {
+		if (value == L"true") return true;
+		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);
+	}
+
+public:
+
+	static const relpipe::reader::string_t OPTION_RELATION;
+	static const relpipe::reader::string_t OPTION_PRINT_TYPES;
+	static const relpipe::reader::string_t OPTION_PRINT_RELATION_NAME;
+	static const relpipe::reader::string_t OPTION_PRINT_RECORD_COUNT;
+
+	Configuration parse(const std::vector<relpipe::reader::string_t>& arguments) {
+		Configuration c;
+		RelationConfiguration currentRelation;
+
+		for (int i = 0; i < arguments.size();) {
+			relpipe::reader::string_t option = readNext(arguments, i);
+
+			if (option == OPTION_RELATION) {
+				addRelation(c, currentRelation); // previous relation
+				currentRelation.relation = readNext(arguments, i);
+			} else if (option == OPTION_PRINT_TYPES) {
+				currentRelation.printTypes = parseBoolean(readNext(arguments, i));
+			} else if (option == OPTION_PRINT_RELATION_NAME) {
+				currentRelation.printRelationName = parseBoolean(readNext(arguments, i));
+			} else if (option == OPTION_PRINT_RECORD_COUNT) {
+				currentRelation.printRecordCount = parseBoolean(readNext(arguments, i));
+			} else throw relpipe::cli::RelpipeCLIException(L"Unsupported CLI option: " + option, relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS);
+		}
+
+		addRelation(c, currentRelation); // last relation
+
+		return c;
+	}
+
+	virtual ~CLIParser() {
+	}
+};
+
+const relpipe::reader::string_t CLIParser::OPTION_RELATION = L"--relation";
+const relpipe::reader::string_t CLIParser::OPTION_PRINT_TYPES = L"--print-types";
+const relpipe::reader::string_t CLIParser::OPTION_PRINT_RELATION_NAME = L"--print-relation-name";
+const relpipe::reader::string_t CLIParser::OPTION_PRINT_RECORD_COUNT = L"--print-record-count";
+
+}
+}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Configuration.h	Thu Sep 24 18:57:20 2020 +0200
@@ -0,0 +1,54 @@
+/**
+ * Relational pipes
+ * Copyright © 2020 František Kučera (Frantovo.cz, GlobalCode.info)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+
+#include <vector>
+#include <iostream>
+
+#include <relpipe/reader/typedefs.h>
+
+
+namespace relpipe {
+namespace out {
+namespace tabular {
+
+class RelationConfiguration {
+public:
+
+	virtual ~RelationConfiguration() {
+	}
+
+	relpipe::reader::string_t relation;
+	relpipe::reader::boolean_t printTypes = true;
+	relpipe::reader::boolean_t printRecordCount = true;
+	relpipe::reader::boolean_t printRelationName = true;
+};
+
+class Configuration {
+public:
+	relpipe::reader::boolean_t printTypes = true;
+	relpipe::reader::boolean_t printRecordCount = true;
+	relpipe::reader::boolean_t printRelationName = true;
+	std::vector<RelationConfiguration> relationConfigurations;
+
+	virtual ~Configuration() {
+	}
+};
+
+}
+}
+}
\ No newline at end of file
--- a/src/TabularPrefetchingHandler.h	Sat Jun 06 01:50:44 2020 +0200
+++ b/src/TabularPrefetchingHandler.h	Thu Sep 24 18:57:20 2020 +0200
@@ -29,6 +29,8 @@
 #include <relpipe/reader/handlers/RelationalReaderStringHandler.h>
 #include <relpipe/reader/handlers/AttributeMetadata.h>
 
+#include "Configuration.h"
+
 namespace relpipe {
 namespace out {
 namespace tabular {
@@ -52,7 +54,11 @@
 
 	const char* INDENT = " "; // table indent from the left
 
-	std::ostream &output;
+	std::ostream& output;
+	Configuration& configuration;
+	RelationConfiguration* currentRelationConfiguration;
+
+#define getConfiguration(option) (currentRelationConfiguration ? currentRelationConfiguration->option : configuration.option)
 
 	std::vector<TypeId> columnTypes;
 	std::vector<string_t> columnTypeCodes;
@@ -146,7 +152,7 @@
 		for (size_t i = 0; i < columnCount; i++) {
 			string_t typeCode = columnTypeCodes[i];
 			string_t columnName = columnNames[i];
-			integer_t minWidth = columnName.size() + typeCode.size() + 3; // 3 = " ()" in "columnName (typeCode)"
+			integer_t minWidth = columnName.size() + (getConfiguration(printTypes) ? typeCode.size() + 3 : 0); // 3 = " ()" in "columnName (typeCode)"
 			columnWidths[i] = max(columnWidths[i], minWidth);
 			paddings[i] = columnWidths[i] - minWidth;
 		}
@@ -160,7 +166,7 @@
 			for (integer_t p = 0; p < paddings[i]; p++) {
 				output << " ";
 			}
-			output << " (" << convertor.to_bytes(columnTypeCodes[i]) << ")";
+			if (getConfiguration(printTypes)) output << " (" << convertor.to_bytes(columnTypeCodes[i]) << ")";
 			output << ESC_BORDER << " │" << ESC_RESET;
 		}
 		output << std::endl;
@@ -182,21 +188,32 @@
 			if (columnIndex == (columnCount - 1)) output << std::endl;
 		}
 		printHorizontalLine(L"╰", L"┴", L"╯");
-		integer_t recordCount = values.size() / columnCount;
-		output << ESC_YELLOW << "Record count: " << ESC_RESET << recordCount << std::endl;
+
+		if (getConfiguration(printRecordCount)) {
+			integer_t recordCount = values.size() / columnCount;
+			output << ESC_YELLOW << "Record count: " << ESC_RESET << recordCount << std::endl;
+		}
 
 		values.clear();
 	}
 
 public:
 
-	TabularPrefetchingHandler(std::ostream& output) : output(output) {
+	TabularPrefetchingHandler(std::ostream& output, Configuration& configuration) : output(output), configuration(configuration) {
 	}
 
 	void startRelation(string_t name, std::vector<handlers::AttributeMetadata> attributes) override {
 		if (columnCount) printCachedData();
 
-		output << ESC_RED << convertor.to_bytes(name) << ":" << ESC_RESET << endl;
+		currentRelationConfiguration = nullptr;
+		for (int i = 0; i < configuration.relationConfigurations.size(); i++) {
+			if (std::regex_match(name, std::wregex(configuration.relationConfigurations[i].relation))) {
+				currentRelationConfiguration = &configuration.relationConfigurations[i];
+				break; // it there are multiple matches, only the first configuration is used
+			}
+		}
+
+		if (getConfiguration(printRelationName)) output << ESC_RED << convertor.to_bytes(name) << ":" << ESC_RESET << endl;
 		columnCount = attributes.size();
 		columnTypes.resize(columnCount);
 		columnTypeCodes.resize(columnCount);
@@ -222,6 +239,8 @@
 
 };
 
+#undef getConfiguration
+
 }
 }
 }
--- a/src/relpipe-out-tabular.cpp	Sat Jun 06 01:50:44 2020 +0200
+++ b/src/relpipe-out-tabular.cpp	Thu Sep 24 18:57:20 2020 +0200
@@ -24,6 +24,8 @@
 #include <relpipe/reader/RelpipeReaderException.h>
 
 #include "TabularPrefetchingHandler.h"
+#include "Configuration.h"
+#include "CLIParser.h"
 
 using namespace relpipe::cli;
 using namespace relpipe::reader;
@@ -37,16 +39,10 @@
 	int resultCode = CLI::EXIT_CODE_UNEXPECTED_ERROR;
 
 	try {
-		// if (cli.arguments().size() > 0) {
-
-		// const wstring commandName = cli.arguments()[0];
-		// vector<wstring> arguments(cli.arguments().size() - 1);
-		// for (int i = 1; i < cli.arguments().size(); i++) {
-		//	arguments[i - 1] = cli.arguments()[i];
-		// }
-
+		CLIParser cliParser;
+		Configuration configuration = cliParser.parse(cli.arguments());
 		std::shared_ptr<RelationalReader> reader(Factory::create(std::cin));
-		TabularPrefetchingHandler handler(std::cout);
+		TabularPrefetchingHandler handler(std::cout, configuration);
 		reader->addHandler(&handler);
 		reader->process();