configurable table style: rounded, sharp, sharp-double, horizontal-only, ascii v_0
authorFrantišek Kučera <franta-hg@frantovo.cz>
Mon, 20 Jun 2022 00:55:56 +0200
branchv_0
changeset 38 2cc2d3f658f4
parent 37 5dcff3c35462
child 39 f33464965693
configurable table style: rounded, sharp, sharp-double, horizontal-only, ascii
bash-completion.sh
src/CLIParser.h
src/Configuration.h
src/TabularPrefetchingHandler.h
--- a/bash-completion.sh	Mon Jun 20 00:15:08 2022 +0200
+++ b/bash-completion.sh	Mon Jun 20 00:55:56 2022 +0200
@@ -27,16 +27,34 @@
 		"false"
 	)
 
+	TABLE_STYLES=(
+		"rounded"
+		"sharp"
+		"sharp-double"
+		"horizontal-only"
+		"ascii"
+	)
+
+	COLOR_SCHEMES=(
+		"green-screen"
+		"monochrome"
+		"midnight"
+	)
+
 	if   [[ "$w1" == "--relation"                      && "x$w0" == "x" ]];    then COMPREPLY=("'.+'")
 	elif [[ "$w1" == "--write-types"                                    ]];    then COMPREPLY=($(compgen -W "${BOOLEAN_VALUES[*]}" -- "$w0"))
 	elif [[ "$w1" == "--write-relation-name"                            ]];    then COMPREPLY=($(compgen -W "${BOOLEAN_VALUES[*]}" -- "$w0"))
 	elif [[ "$w1" == "--write-record-count"                             ]];    then COMPREPLY=($(compgen -W "${BOOLEAN_VALUES[*]}" -- "$w0"))
+	elif [[ "$w1" == "--table-style"                                    ]];    then COMPREPLY=($(compgen -W "${TABLE_STYLES[*]}" -- "$w0"))
+	elif [[ "$w1" == "--color-scheme"                                    ]];    then COMPREPLY=($(compgen -W "${COLOR_SCHEMES[*]}" -- "$w0"))
 	else
 		OPTIONS=(
 			"--relation"
 			"--write-types"
 			"--write-relation-name"
 			"--write-record-count"
+			"--table-style"
+			"--color-scheme"
 		)
 		COMPREPLY=($(compgen -W "${OPTIONS[*]}" -- "$w0"))
 	fi
--- a/src/CLIParser.h	Mon Jun 20 00:15:08 2022 +0200
+++ b/src/CLIParser.h	Mon Jun 20 00:55:56 2022 +0200
@@ -57,6 +57,22 @@
 		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"green-screen") return Configuration::ColorScheme::GreenScreen;
+		else if (value == L"monochrome") return Configuration::ColorScheme::Monochrome;
+		else if (value == L"midnight") return Configuration::ColorScheme::Midnight;
+		else throw relpipe::cli::RelpipeCLIException(L"Unable to parse ColorScheme value: " + value + L" (expecting green-screen, monochrome or midnight)", 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;
+		else if (value == L"sharp-double") return Configuration::TableStyle::SharpDouble;
+		else if (value == L"horizontal-only") return Configuration::TableStyle::HorizontalOnly;
+		else if (value == L"ascii") return Configuration::TableStyle::Ascii;
+		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);
+	}
 
 public:
 
@@ -64,10 +80,14 @@
 	static const relpipe::reader::string_t OPTION_WRITE_TYPES;
 	static const relpipe::reader::string_t OPTION_WRITE_RELATION_NAME;
 	static const relpipe::reader::string_t OPTION_WRITE_RECORD_COUNT;
+	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 c;
 		RelationConfiguration currentRelation;
+		
+		// TODO: global configuration of writeTypes, writeRelationName, writeRecordCount
 
 		for (int i = 0; i < arguments.size();) {
 			relpipe::reader::string_t option = readNext(arguments, i);
@@ -81,6 +101,10 @@
 				currentRelation.writeRelationName = parseBoolean(readNext(arguments, i));
 			} else if (option == OPTION_WRITE_RECORD_COUNT) {
 				currentRelation.writeRecordCount = parseBoolean(readNext(arguments, i));
+			} else if (option == OPTION_COLOR_SCHEME) {
+				c.colorScheme = parseColorScheme(readNext(arguments, i));
+			} else if (option == OPTION_TABLE_STYLE) {
+				c.tableStyle = parseTableStyle(readNext(arguments, i));
 			} else throw relpipe::cli::RelpipeCLIException(L"Unsupported CLI option: " + option, relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS);
 		}
 
@@ -97,6 +121,8 @@
 const relpipe::reader::string_t CLIParser::OPTION_WRITE_TYPES = L"--write-types";
 const relpipe::reader::string_t CLIParser::OPTION_WRITE_RELATION_NAME = L"--write-relation-name";
 const relpipe::reader::string_t CLIParser::OPTION_WRITE_RECORD_COUNT = L"--write-record-count";
+const relpipe::reader::string_t CLIParser::OPTION_COLOR_SCHEME = L"--color-scheme";
+const relpipe::reader::string_t CLIParser::OPTION_TABLE_STYLE = L"--table-style";
 
 }
 }
--- a/src/Configuration.h	Mon Jun 20 00:15:08 2022 +0200
+++ b/src/Configuration.h	Mon Jun 20 00:55:56 2022 +0200
@@ -40,10 +40,28 @@
 
 class Configuration {
 public:
+
+	enum class ColorScheme {
+		GreenScreen,
+		Monochrome,
+		Midnight
+	};
+
+	enum class TableStyle {
+		Rounded,
+		Sharp,
+		SharpDouble,
+		HorizontalOnly,
+		Ascii
+	};
+
+
 	relpipe::reader::boolean_t writeTypes = true;
 	relpipe::reader::boolean_t writeRecordCount = true;
 	relpipe::reader::boolean_t writeRelationName = true;
 	std::vector<RelationConfiguration> relationConfigurations;
+	ColorScheme colorScheme = ColorScheme::GreenScreen;
+	TableStyle tableStyle = TableStyle::Rounded;
 
 	virtual ~Configuration() {
 	}
--- a/src/TabularPrefetchingHandler.h	Mon Jun 20 00:15:08 2022 +0200
+++ b/src/TabularPrefetchingHandler.h	Mon Jun 20 00:55:56 2022 +0200
@@ -28,6 +28,7 @@
 #include <relpipe/reader/TypeId.h>
 #include <relpipe/reader/handlers/RelationalReaderStringHandler.h>
 #include <relpipe/reader/handlers/AttributeMetadata.h>
+#include <relpipe/reader/RelpipeReaderException.h>
 
 #include "Configuration.h"
 
@@ -138,13 +139,7 @@
 		return width;
 	}
 
-	void printHorizontalLine(const string_t &left, const string_t &middle, const string_t &right) {
-		const string_t bar = L"─";
-		// TODO: support also ASCII nostalgia:
-		// border = border.replaceAll("─", "-");
-		// border = border.replaceAll("│", "|");
-		// border = border.replaceAll("[╭┬╮├┼┤╰┴╯]", "+");
-
+	void printHorizontalLine(const string_t& left, const string_t& middle, const string_t& right, const string_t& bar) {
 		output << INDENT << ESC_BORDER;
 		output << convertor.to_bytes(left);
 		for (size_t c = 0; c < columnCount; c++) {
@@ -168,25 +163,43 @@
 			paddings[i] = columnWidths[i] - minWidth;
 		}
 
-		printHorizontalLine(L"╭", L"┬", L"╮");
+		if (configuration.tableStyle == Configuration::TableStyle::Rounded) printHorizontalLine(L"╭", L"┬", L"╮", L"─");
+		else if (configuration.tableStyle == Configuration::TableStyle::Sharp) printHorizontalLine(L"┌", L"┬", L"┐", L"─");
+		else if (configuration.tableStyle == Configuration::TableStyle::SharpDouble) printHorizontalLine(L"╔", L"╦", L"╗", L"═");
+		else if (configuration.tableStyle == Configuration::TableStyle::HorizontalOnly) printHorizontalLine(L"─", L"─", L"─", L"─");
+		else if (configuration.tableStyle == Configuration::TableStyle::Ascii) printHorizontalLine(L"+", L"+", L"+", L"-");
+		else throw RelpipeReaderException(L"Unsupported TableStyle: " + std::to_wstring((int) configuration.tableStyle));
+
+		std::string verticalSeparator;
+		if (configuration.tableStyle == Configuration::TableStyle::Rounded) verticalSeparator = "│";
+		else if (configuration.tableStyle == Configuration::TableStyle::Sharp) verticalSeparator = "│";
+		else if (configuration.tableStyle == Configuration::TableStyle::SharpDouble) verticalSeparator = "║";
+		else if (configuration.tableStyle == Configuration::TableStyle::HorizontalOnly) verticalSeparator = " ";
+		else if (configuration.tableStyle == Configuration::TableStyle::Ascii) verticalSeparator = "|";
+		else throw RelpipeReaderException(L"Unsupported TableStyle: " + std::to_wstring((int) configuration.tableStyle));
 
 		// Print column headers:
-		output << INDENT << ESC_BORDER << "│" << ESC_RESET;
+		output << INDENT << ESC_BORDER << verticalSeparator << ESC_RESET;
 		for (size_t i = 0; i < columnCount; i++) {
 			output << " " << convertor.to_bytes(formatValue(columnNames[i], ESC_HEADER));
 			for (integer_t p = 0; p < paddings[i]; p++) {
 				output << " ";
 			}
 			if (getConfiguration(writeTypes)) output << " (" << convertor.to_bytes(columnTypeCodes[i]) << ")";
-			output << ESC_BORDER << " │" << ESC_RESET;
+			output << ESC_BORDER << " " << verticalSeparator << ESC_RESET;
 		}
 		output << std::endl;
-		printHorizontalLine(L"├", L"┼", L"┤");
+		if (configuration.tableStyle == Configuration::TableStyle::Rounded) printHorizontalLine(L"├", L"┼", L"┤", L"─");
+		else if (configuration.tableStyle == Configuration::TableStyle::Sharp) printHorizontalLine(L"├", L"┼", L"┤", L"─");
+		else if (configuration.tableStyle == Configuration::TableStyle::SharpDouble) printHorizontalLine(L"╠", L"╬", L"╣", L"═");
+		else if (configuration.tableStyle == Configuration::TableStyle::HorizontalOnly) printHorizontalLine(L"─", L"─", L"─", L"─");
+		else if (configuration.tableStyle == Configuration::TableStyle::Ascii) printHorizontalLine(L"+", L"+", L"+", L"-");
+		else throw RelpipeReaderException(L"Unsupported TableStyle: " + std::to_wstring((int) configuration.tableStyle));
 
 		// Print particular rows:
 		for (size_t i = 0; i < values.size(); i++) {
 			integer_t columnIndex = i % columnCount;
-			if (columnIndex == 0) output << INDENT << ESC_BORDER << "│" << ESC_RESET;
+			if (columnIndex == 0) output << INDENT << ESC_BORDER << verticalSeparator << ESC_RESET;
 			string_t stringValue = values[i];
 			integer_t padding = columnWidths[columnIndex] - computeWidth(stringValue);
 			boolean_t alignRight = columnTypes[columnIndex] == TypeId::BOOLEAN || columnTypes[columnIndex] == TypeId::INTEGER;
@@ -195,10 +208,15 @@
 			output << " " << convertor.to_bytes(formatValue(stringValue, ESC_VALUE));
 			if (!alignRight) for (integer_t p = 0; p < padding; p++) output << " ";
 
-			output << ESC_BORDER << " │" << ESC_RESET;
+			output << ESC_BORDER << " " << verticalSeparator << ESC_RESET;
 			if (columnIndex == (columnCount - 1)) output << std::endl;
 		}
-		printHorizontalLine(L"╰", L"┴", L"╯");
+		if (configuration.tableStyle == Configuration::TableStyle::Rounded) printHorizontalLine(L"╰", L"┴", L"╯", L"─");
+		else if (configuration.tableStyle == Configuration::TableStyle::Sharp) printHorizontalLine(L"└", L"┴", L"┘", L"─");
+		else if (configuration.tableStyle == Configuration::TableStyle::SharpDouble) printHorizontalLine(L"╚", L"╩", L"╝", L"═");
+		else if (configuration.tableStyle == Configuration::TableStyle::HorizontalOnly) printHorizontalLine(L"─", L"─", L"─", L"─");
+		else if (configuration.tableStyle == Configuration::TableStyle::Ascii) printHorizontalLine(L"+", L"+", L"+", L"-");
+		else throw RelpipeReaderException(L"Unsupported TableStyle: " + std::to_wstring((int) configuration.tableStyle));
 
 		if (getConfiguration(writeRecordCount)) {
 			integer_t recordCount = values.size() / columnCount;