From: José Iván López González Date: Wed, 30 Oct 2019 23:13:47 +0000 (+0000) Subject: Add formatters X-Git-Tag: v0.8.6~10^2~11 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=90b63df519fd0457472a9fd095377fad83ca94ef;p=thirdparty%2Fsnapper.git Add formatters --- diff --git a/client/utils/CsvFormatter.cc b/client/utils/CsvFormatter.cc new file mode 100644 index 00000000..b566b89f --- /dev/null +++ b/client/utils/CsvFormatter.cc @@ -0,0 +1,125 @@ +/* + * Copyright (c) [2019] SUSE LLC + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * 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, contact Novell, Inc. + * + * To contact Novell about this file by physical or electronic mail, you may + * find current contact information at www.novell.com. + */ + +#include +#include +#include + +#include "client/utils/CsvFormatter.h" + +using namespace std; + +namespace snapper +{ + namespace cli + { + + namespace + { + + const string DEFAULT_SEPARATOR = ","; + + + string double_quotes(const string value) + { + return boost::algorithm::replace_all_copy(value, "\"", "\"\"" ); + } + + + string enclose_with_quotes(const string value) + { + return "\"" + value + "\""; + } + + } + + + const string CsvFormatter::default_separator() + { + return DEFAULT_SEPARATOR; + } + + + CsvFormatter::CsvFormatter( + vector columns, + vector> rows) : + _columns(columns), _rows(rows), _separator(default_separator()) + {} + + + CsvFormatter::CsvFormatter( + vector columns, + vector> rows, + const string separator) : + _columns(columns), _rows(rows), _separator(separator) + {} + + + string CsvFormatter::output() const + { + string cvs_output; + + cvs_output = csv_line(_columns); + + for (auto row : _rows) + cvs_output += csv_line(row); + + return cvs_output; + } + + + string CsvFormatter::csv_line(vector values) const + { + vector csv_values; + + for (auto value : values) + csv_values.push_back(csv_value(value)); + + return boost::algorithm::join(csv_values, _separator) + "\n"; + } + + + string CsvFormatter::csv_value(const string value) const + { + string fixed_value = boost::algorithm::trim_copy(value); + + if (has_special_chars(value)) + fixed_value = enclose_with_quotes(double_quotes(value)); + + return fixed_value; + } + + + bool CsvFormatter::has_special_chars(const string value) const + { + vector special_chars = { _separator, "\n", "\"" }; + + for (auto special_char : special_chars) + { + if (value.find(special_char) != string::npos) + return true; + } + + return false; + } + + } +} \ No newline at end of file diff --git a/client/utils/CsvFormatter.h b/client/utils/CsvFormatter.h new file mode 100644 index 00000000..24c8c327 --- /dev/null +++ b/client/utils/CsvFormatter.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) [2019] SUSE LLC + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * 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, contact Novell, Inc. + * + * To contact Novell about this file by physical or electronic mail, you may + * find current contact information at www.novell.com. + */ + +#ifndef SNAPPER_CLI_CSV_FORMATTER_H +#define SNAPPER_CLI_CSV_FORMATTER_H + +#include +#include + +namespace snapper +{ + namespace cli + { + + class CsvFormatter + { + + public: + + static const std::string default_separator(); + + CsvFormatter( + std::vector columns, + std::vector> rows); + + CsvFormatter( + std::vector columns, + std::vector> rows, + const std::string separator); + + std::string output() const; + + private: + + std::string csv_line(std::vector values) const; + + std::string csv_value(const std::string value) const; + + bool has_special_chars(const std::string value) const; + + std::vector _columns; + + std::vector> _rows; + + const std::string _separator; + + }; + + } +} + +#endif diff --git a/client/utils/JsonFormatter.cc b/client/utils/JsonFormatter.cc new file mode 100644 index 00000000..4fd81505 --- /dev/null +++ b/client/utils/JsonFormatter.cc @@ -0,0 +1,127 @@ +/* + * Copyright (c) [2019] SUSE LLC + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * 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, contact Novell, Inc. + * + * To contact Novell about this file by physical or electronic mail, you may + * find current contact information at www.novell.com. + */ + +#include +#include +#include +#include + +#include "client/utils/JsonFormatter.h" + +using namespace std; + +namespace snapper +{ + namespace cli + { + + namespace + { + + string indent(u_int indent_level) + { + return string(indent_level * 2, ' '); + } + + + string escape(const string& value) + { + string fixed_value = value; + + boost::algorithm::replace_all(fixed_value, "\\", "\\\\"); + boost::algorithm::replace_all(fixed_value, "\"", "\\\""); + + return fixed_value; + } + + + string quote(const string& value) + { + return "\"" + value + "\""; + } + + + string to_json(const string& value) + { + return quote(escape(boost::algorithm::trim_copy(value))); + } + + } + + + JsonFormatter::JsonFormatter(const Data& data) : + _data(data), _inline(false) + {} + + + string JsonFormatter::output(u_int indent_level) const + { + string first_indent = _inline ? "" : indent(indent_level); + + return first_indent + "{" + "\n" + + json_attributes(indent_level + 1) + "\n" + + indent(indent_level) + "}"; + } + + + string JsonFormatter::json_attributes(u_int indent_level) const + { + vector attributes; + + for (auto& data_pair : _data) + { + string value = data_pair.second; + + if (!skip_format_value(data_pair.first)) + value = to_json(value); + + attributes.push_back(indent(indent_level) + to_json(data_pair.first) + ": " + value); + } + + return boost::algorithm::join(attributes, ",\n"); + } + + + bool JsonFormatter::skip_format_value(const string& key) const + { + auto it = find(_skip_format_values.begin(), _skip_format_values.end(), key); + + if (it != _skip_format_values.end()) + return true; + + return false; + } + + + JsonFormatter::JsonFormatter::List::List(const vector& data) : + _data(data) + {} + + + string JsonFormatter::List::output(u_int indent_level) const + { + return "[\n" + + boost::algorithm::join(_data, ",\n") + "\n" + + indent(indent_level) + "]"; + } + + } +} \ No newline at end of file diff --git a/client/utils/JsonFormatter.h b/client/utils/JsonFormatter.h new file mode 100644 index 00000000..79f7672d --- /dev/null +++ b/client/utils/JsonFormatter.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) [2019] SUSE LLC + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * 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, contact Novell, Inc. + * + * To contact Novell about this file by physical or electronic mail, you may + * find current contact information at www.novell.com. + */ + +#ifndef SNAPPER_CLI_JSON_FORMATTER_H +#define SNAPPER_CLI_JSON_FORMATTER_H + +#include +#include +#include + +namespace snapper +{ + namespace cli + { + + /* Very simplistic JSON formatter, but enough for current requirements. + * + * If needed, this could be replaced by some modern library, for example: + * https://github.com/nlohmann/json + */ + class JsonFormatter + { + + public: + + class List; + + using Data = std::vector>; + + JsonFormatter(const Data& data); + + void skip_format_values(const std::vector& skip_format_values) + { + _skip_format_values = skip_format_values; + } + + void set_inline(bool is_inline) + { + _inline = is_inline; + } + + std::string output(u_int indent_level = 0) const; + + private: + + std::string json_attributes(u_int indent_level) const; + + bool skip_format_value(const std::string& key) const; + + const Data& _data; + + std::vector _skip_format_values; + + bool _inline; + }; + + + class JsonFormatter::List + { + + public: + + List(const std::vector& data); + + std::string output(u_int indent_level = 0) const; + + private: + + const std::vector& _data; + + }; + + } +} + +#endif diff --git a/client/utils/Makefile.am b/client/utils/Makefile.am index 7ebc87b5..092b3cfd 100644 --- a/client/utils/Makefile.am +++ b/client/utils/Makefile.am @@ -6,14 +6,18 @@ AM_CPPFLAGS = -I$(top_srcdir) noinst_LTLIBRARIES = libutils.la -libutils_la_SOURCES = \ - Table.cc Table.h \ - text.cc text.h \ - console.cc console.h \ - equal-date.cc equal-date.h \ - GetOpts.cc GetOpts.h \ - Range.cc Range.h \ - HumanString.cc HumanString.h +libutils_la_SOURCES = \ + Table.cc Table.h \ + text.cc text.h \ + console.cc console.h \ + equal-date.cc equal-date.h \ + GetOpts.cc GetOpts.h \ + Range.cc Range.h \ + HumanString.cc HumanString.h \ + TableFormatter.cc TableFormatter.h \ + CsvFormatter.cc CsvFormatter.h \ + JsonFormatter.cc JsonFormatter.h + libutils_la_LIBADD = ../../snapper/libsnapper.la diff --git a/client/utils/TableFormatter.cc b/client/utils/TableFormatter.cc new file mode 100644 index 00000000..330da3b2 --- /dev/null +++ b/client/utils/TableFormatter.cc @@ -0,0 +1,91 @@ +/* + * Copyright (c) [2019] SUSE LLC + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * 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, contact Novell, Inc. + * + * To contact Novell about this file by physical or electronic mail, you may + * find current contact information at www.novell.com. + */ + +#include + +#include "client/utils/TableFormatter.h" + +using namespace std; + +namespace snapper +{ + namespace cli + { + + namespace + { + const TableLineStyle DEFAULT_STYLE = Table::defaultStyle; + } + + + TableLineStyle TableFormatter::default_style() + { + return DEFAULT_STYLE; + } + + + TableFormatter::TableFormatter( + vector> columns, + vector> rows) : + _columns(columns), _rows(rows), _style(default_style()) + {} + + + TableFormatter::TableFormatter( + vector> columns, + vector> rows, + TableLineStyle style) : + _columns(columns), _rows(rows), _style(style) + {} + + + string TableFormatter::output() const + { + Table::defaultStyle = _style; + + Table table; + + TableHeader header; + + for (auto column : _columns) + header.add(column.first, column.second); + + table.setHeader(header); + + for (auto row : _rows) + { + TableRow table_row; + + for (auto value : row) + table_row.add(value); + + table.add(table_row); + } + + ostringstream stream; + + stream << table; + + return stream.str(); + } + + } +} \ No newline at end of file diff --git a/client/utils/TableFormatter.h b/client/utils/TableFormatter.h new file mode 100644 index 00000000..ed1a3db0 --- /dev/null +++ b/client/utils/TableFormatter.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) [2019] SUSE LLC + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * 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, contact Novell, Inc. + * + * To contact Novell about this file by physical or electronic mail, you may + * find current contact information at www.novell.com. + */ + +#ifndef SNAPPER_CLI_TABLE_FORMATTER_H +#define SNAPPER_CLI_TABLE_FORMATTER_H + +#include +#include +#include + +#include "client/utils/Table.h" + +namespace snapper +{ + namespace cli + { + + class TableFormatter + { + + public: + + static TableLineStyle default_style(); + + TableFormatter( + std::vector> columns, + std::vector> rows); + + TableFormatter( + std::vector> columns, + std::vector> rows, + TableLineStyle style); + + std::string output() const; + + private: + + std::vector> _columns; + + std::vector> _rows; + + TableLineStyle _style; + + }; + + } +} + +#endif diff --git a/testsuite/Makefile.am b/testsuite/Makefile.am index 770237ca..31ec8117 100644 --- a/testsuite/Makefile.am +++ b/testsuite/Makefile.am @@ -6,8 +6,9 @@ AM_CPPFLAGS = -I$(top_srcdir) $(DBUS_CFLAGS) LDADD = ../snapper/libsnapper.la ../dbus/libdbus.la -lboost_unit_test_framework -check_PROGRAMS = sysconfig-get1.test dirname1.test basename1.test \ - equal-date.test dbus-escape.test cmp-lt.test humanstring.test table.test +check_PROGRAMS = sysconfig-get1.test dirname1.test basename1.test \ + equal-date.test dbus-escape.test cmp-lt.test humanstring.test table.test \ + csv-formatter.test json-formatter.test if ENABLE_BTRFS_QUOTA check_PROGRAMS += qgroup1.test @@ -25,3 +26,6 @@ humanstring_test_LDADD = -lboost_unit_test_framework ../client/utils/libutils.la table_test_LDADD = -lboost_unit_test_framework ../client/utils/libutils.la +csv_formatter_test_LDADD = -lboost_unit_test_framework ../client/utils/libutils.la + +json_formatter_test_LDADD = -lboost_unit_test_framework ../client/utils/libutils.la diff --git a/testsuite/csv-formatter.cc b/testsuite/csv-formatter.cc new file mode 100644 index 00000000..8851bed1 --- /dev/null +++ b/testsuite/csv-formatter.cc @@ -0,0 +1,33 @@ + +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_MODULE snapper + +#include + +#include +#include + +#include "../client/utils/CsvFormatter.h" + +using namespace std; + +BOOST_AUTO_TEST_CASE(test1) +{ + vector columns = { "column1", "column2", "column3" };; + + vector> rows = { + { "value;1", "value\n2", "value\"3" }, + { "value1", "\"value2\"", "value3" } + }; + + string separator = ";"; + + snapper::cli::CsvFormatter formatter(columns, rows, separator); + + string result = + "column1;column2;column3\n" + "\"value;1\";\"value\n2\";\"value\"\"3\"\n" + "value1;\"\"\"value2\"\"\";value3\n"; + + BOOST_CHECK_EQUAL(formatter.output(), result); +} diff --git a/testsuite/json-formatter.cc b/testsuite/json-formatter.cc new file mode 100644 index 00000000..74801b5f --- /dev/null +++ b/testsuite/json-formatter.cc @@ -0,0 +1,116 @@ + +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_MODULE snapper + +#include + +#include +#include + +#include "../client/utils/JsonFormatter.h" + +using namespace std; + +BOOST_AUTO_TEST_CASE(test1_escape_values) +{ + snapper::cli::JsonFormatter::Data data = { + { "key1", "value1" }, + { "key2", "value\"2" }, + { "key3", "value\\3" }, + { "key4", "\"value4\"" } + }; + + string expected_result = + "{\n" + " \"key1\": \"value1\",\n" + " \"key2\": \"value\\\"2\",\n" + " \"key3\": \"value\\\\3\",\n" + " \"key4\": \"\\\"value4\\\"\"\n" + "}"; + + snapper::cli::JsonFormatter formatter(data); + + BOOST_CHECK_EQUAL(formatter.output(), expected_result); +} + + +BOOST_AUTO_TEST_CASE(test2_skip_format) +{ + snapper::cli::JsonFormatter::Data data = { + { "key1", "value1" }, + { "key2", "value2" } + }; + + string expected_result = + "{\n" + " \"key1\": value1,\n" + " \"key2\": \"value2\"\n" + "}"; + + snapper::cli::JsonFormatter formatter(data); + + formatter.skip_format_values({ "key1" }); + + BOOST_CHECK_EQUAL(formatter.output(), expected_result); +} + + +BOOST_AUTO_TEST_CASE(test3_indent) +{ + // Using an ident level + + snapper::cli::JsonFormatter::Data data = { + { "key1", "value1" }, + { "key2", "value2" } + }; + + string expected_result = + " {\n" + " \"key1\": \"value1\",\n" + " \"key2\": \"value2\"\n" + " }"; + + snapper::cli::JsonFormatter formatter(data); + + BOOST_CHECK_EQUAL(formatter.output(1), expected_result); +} + + +BOOST_AUTO_TEST_CASE(test4_inline) +{ + // Using inline + + snapper::cli::JsonFormatter::Data data = { + { "key1", "value1" }, + { "key2", "value2" } + }; + + string expected_result = + "{\n" + " \"key1\": \"value1\",\n" + " \"key2\": \"value2\"\n" + " }"; + + snapper::cli::JsonFormatter formatter(data); + + formatter.set_inline(true); + + BOOST_CHECK_EQUAL(formatter.output(1), expected_result); +} + + +BOOST_AUTO_TEST_CASE(test5_list) +{ + vector data = { "value1", "value2", "value3" }; + + snapper::cli::JsonFormatter::List formatter(data); + + string result = + "[\n" + "value1,\n" + "value2,\n" + "value3\n" + "]"; + + BOOST_CHECK_EQUAL(formatter.output(), result); +}