diff --git a/include/blt/std/format.h b/include/blt/std/format.h index d7ca0e6..d6556ab 100755 --- a/include/blt/std/format.h +++ b/include/blt/std/format.h @@ -13,40 +13,66 @@ #include #include -namespace blt::string { +namespace blt::string +{ template - static inline std::string withGrouping(T t, size_t group = 3){ + static inline std::string withGrouping(T t, size_t group = 3) + { // TODO: all this + make it faster static_assert(std::is_integral_v, "Must be integer type! (Floats currently not supported!)"); auto str = std::to_string(t); std::string ret; ret.reserve(str.size()); size_t count = 0; - for (int64_t i = str.size() - 1; i >= 0; i--){ + for (int64_t i = str.size() - 1; i >= 0; i--) + { ret += str[i]; - if (count++ % (group) == group-1 && i != 0) + if (count++ % (group) == group - 1 && i != 0) ret += ','; } std::reverse(ret.begin(), ret.end()); return ret; } - static inline std::string fromBytes(unsigned long bytes){ - if (bytes > 1073741824) { + static inline std::string fromBytes(unsigned long bytes) + { + if (bytes > 1073741824) + { // gigabyte return std::to_string(round_up<3>((double) bytes / 1024.0 / 1024.0 / 1024.0)) += "gb"; - } else if (bytes > 1048576) { + } else if (bytes > 1048576) + { // megabyte - return std::to_string(round_up<3>((double)bytes / 1024.0 / 1024.0)) += "mb"; - } else if (bytes > 1024) { + return std::to_string(round_up<3>((double) bytes / 1024.0 / 1024.0)) += "mb"; + } else if (bytes > 1024) + { // kilobyte - return std::to_string(round_up<3>((double)bytes / 1024.0)) += "kb"; - } else { + return std::to_string(round_up<3>((double) bytes / 1024.0)) += "kb"; + } else + { return std::to_string(bytes) += "b"; } } + // TODO: update table formatter to use these! + /** + * creates a line starting/ending with ending char filled between with spacing char + * @param totalLength total length to generate + * @param endingChar beginning and ending char to use + * @param spacingChar char to use for spacing + * @return a generated line string eg: +--------+ + */ + std::string createLine(size_t totalLength, char endingChar, char spacingChar); + + /** + * Create a padding string using length and spacing char + * @param length length of string to generate + * @param spacing char to use to generate padding + * @return a padding string + */ + std::string createPadding(size_t length, char spacing = ' '); + // TODO template the padding functions: /** @@ -55,13 +81,13 @@ namespace blt::string { * @param expectedLength expected length of the string. * @return a space padded string */ - static inline std::string postPadWithSpaces(const std::string& str, size_t expectedLength) { + static inline std::string postPadWithSpaces(const std::string& str, size_t expectedLength) + { auto currentSize = (int) (str.length() - 1); if ((int) expectedLength - currentSize <= 0) return str; auto paddedString = str; - for (size_t i = 0; i < expectedLength - currentSize; i++) - paddedString += " "; + paddedString += createPadding(expectedLength - currentSize); return paddedString; } @@ -71,26 +97,29 @@ namespace blt::string { * @param expectedLength expected length of the string. * @return a space padded string */ - static inline std::string prePadWithSpaces(const std::string& str, size_t expectedLength) { + static inline std::string prePadWithSpaces(const std::string& str, size_t expectedLength) + { auto currentSize = str.length() - 1; auto paddedString = std::string(); - for (unsigned int i = 0; i < expectedLength - currentSize; i++) - paddedString += " "; + paddedString += createPadding(expectedLength - currentSize); paddedString += str; return paddedString; } - struct utf8_string { + struct utf8_string + { char* characters; unsigned int size; }; // taken from java, adapted for c++. - static inline utf8_string createUTFString(const std::string& str) { + static inline utf8_string createUTFString(const std::string& str) + { const auto strlen = (unsigned int) str.size(); unsigned int utflen = strlen; - for (unsigned int i = 0; i < strlen; i++) { + for (unsigned int i = 0; i < strlen; i++) + { unsigned char c = str[i]; if (c >= 0x80 || c == 0) utflen += 1; @@ -108,21 +137,26 @@ namespace blt::string { chars.characters[count++] = (char) ((utflen >> 0) & 0xFF); unsigned int i = 0; - for (i = 0; i < strlen; i++) { // optimized for initial run of ASCII + for (i = 0; i < strlen; i++) + { // optimized for initial run of ASCII int c = (unsigned char) str[i]; if (c >= 0x80 || c == 0) break; chars.characters[count++] = (char) c; } - for (; i < strlen; i++) { + for (; i < strlen; i++) + { int c = (unsigned char) str[i]; - if (c < 0x80 && c != 0) { + if (c < 0x80 && c != 0) + { chars.characters[count++] = (char) c; - } else if (c >= 0x800) { + } else if (c >= 0x800) + { chars.characters[count++] = (char) (0xE0 | ((c >> 12) & 0x0F)); chars.characters[count++] = (char) (0x80 | ((c >> 6) & 0x3F)); chars.characters[count++] = (char) (0x80 | ((c >> 0) & 0x3F)); - } else { + } else + { chars.characters[count++] = (char) (0xC0 | ((c >> 6) & 0x1F)); chars.characters[count++] = (char) (0x80 | ((c >> 0) & 0x3F)); } @@ -130,7 +164,8 @@ namespace blt::string { return chars; } - static inline std::string getStringFromUTF8(const utf8_string& str) { + static inline std::string getStringFromUTF8(const utf8_string& str) + { int utflen = (int) str.size; int c, char2, char3; int count = 0; @@ -138,16 +173,19 @@ namespace blt::string { auto chararr = new char[utflen + 1]; - while (count < utflen) { + while (count < utflen) + { c = (int) str.characters[count] & 0xff; if (c > 127) break; count++; chararr[chararr_count++] = (char) c; } - while (count < utflen) { + while (count < utflen) + { c = (int) str.characters[count] & 0xff; - switch (c >> 4) { + switch (c >> 4) + { case 0: case 1: case 2: @@ -192,23 +230,27 @@ namespace blt::string { } } chararr[utflen] = '\0'; - std::string strs {chararr}; + std::string strs{chararr}; delete[] chararr; return strs; } - struct TableColumn { + struct TableColumn + { std::string columnName; size_t maxColumnLength = 0; - TableColumn(std::string columnName): columnName(std::move(columnName)) {} + TableColumn(std::string columnName): columnName(std::move(columnName)) + {} }; - struct TableRow { + struct TableRow + { std::vector rowValues; }; - class TableFormatter { + class TableFormatter + { private: std::string m_tableName; int m_columnPadding; @@ -224,19 +266,23 @@ namespace blt::string { void updateMaxColumnLengths(); - [[nodiscard]] inline size_t columnSize(const TableColumn& column) const { + [[nodiscard]] inline size_t columnSize(const TableColumn& column) const + { return column.columnName.size() + m_columnPadding * 2; } public: explicit TableFormatter(std::string tableName = "", int columnPadding = 2, int maxColumnWidth = 500): - m_tableName(std::move(tableName)), m_columnPadding(columnPadding), m_maxColumnWidth(maxColumnWidth) {} + m_tableName(std::move(tableName)), m_columnPadding(columnPadding), m_maxColumnWidth(maxColumnWidth) + {} - inline void addColumn(const TableColumn& column) { + inline void addColumn(const TableColumn& column) + { columns.push_back(column); } - inline void addRow(TableRow row) { + inline void addRow(TableRow row) + { if (row.rowValues.size() > columns.size()) throw "Cannot insert more rows than columns!\n"; // ensure every row populates every column. This is important as the table generator assumes that all rows are complete! @@ -246,7 +292,8 @@ namespace blt::string { rows.push_back(std::move(row)); } - inline void addRow(const std::initializer_list& values) { + inline void addRow(const std::initializer_list& values) + { TableRow row; for (const auto& value : values) row.rowValues.push_back(value); @@ -256,5 +303,65 @@ namespace blt::string { std::vector createTable(bool top = false, bool bottom = false); }; + class TreeFormatter + { + public: + struct Node + { + std::string data; + Node* left = nullptr; + Node* right = nullptr; + + explicit Node(std::string data): data(std::move(data)) + {} + + Node* with(Node* l, Node* r = nullptr) + { + left = l; + right = r; + return this; + } + + ~Node() + { + delete left; + delete right; + } + }; + + private: + // data classes + struct TreeFormat + { + int verticalSpacing; + int horizontalSpacing; + + int verticalPadding; + int horizontalPadding; + + TreeFormat(): verticalSpacing(2), horizontalSpacing(4), verticalPadding(1), horizontalPadding(4) + {} + } format; + + Node* root; + public: + explicit TreeFormatter(std::string rootData, TreeFormat format = {}): format(format), root(new Node(std::move(rootData))) + {} + + std::vector generateBox(Node* node) const; + + inline Node* getRoot() + { + return root; + } + + std::vector construct(); + + ~TreeFormatter() + { + delete root; + } + }; + } #endif //BLT_TESTS_FORMAT_H diff --git a/src/blt/std/format.cpp b/src/blt/std/format.cpp index f7e5082..9dd1c63 100755 --- a/src/blt/std/format.cpp +++ b/src/blt/std/format.cpp @@ -6,15 +6,19 @@ #include #include #include "blt/std/logging.h" +#include +#include -std::string createPadding(int padAmount) { +std::string createPadding(int padAmount) +{ std::string padStr; for (int i = 0; i < padAmount; i++) padStr += " "; return padStr; } -std::vector blt::string::TableFormatter::createTable(bool top, bool bottom) { +std::vector blt::string::TableFormatter::createTable(bool top, bool bottom) +{ std::vector table; const auto& tableHeader = generateColumnHeader(); const auto& topSeparator = generateTopSeparator(tableHeader.size()); @@ -26,16 +30,18 @@ std::vector blt::string::TableFormatter::createTable(bool top, bool table.push_back(tableHeader); table.push_back(lineSeparator); - for (const auto& row : rows) { + for (const auto& row : rows) + { std::string rowString = "|"; - for (unsigned int i = 0; i < row.rowValues.size(); i++) { + for (unsigned int i = 0; i < row.rowValues.size(); i++) + { const auto& rowValue = row.rowValues[i]; const auto& column = columns[i]; const int spaceLeft = int(column.maxColumnLength) - int(rowValue.size()); // we want to prefer putting the space on the right size, flooring left and ceiling right ensures this. - rowString += createPadding((int)std::floor(spaceLeft/2.0) + m_columnPadding); + rowString += createPadding((int) std::floor(spaceLeft / 2.0) + m_columnPadding); rowString += rowValue; - rowString += createPadding((int)std::ceil(spaceLeft/2.0) + m_columnPadding); + rowString += createPadding((int) std::ceil(spaceLeft / 2.0) + m_columnPadding); rowString += "|"; } table.push_back(rowString); @@ -47,19 +53,21 @@ std::vector blt::string::TableFormatter::createTable(bool top, bool return table; } -std::string blt::string::TableFormatter::generateColumnHeader() { +std::string blt::string::TableFormatter::generateColumnHeader() +{ updateMaxColumnLengths(); std::string header = "|"; - for (unsigned int i = 0; i < columns.size(); i++) { + for (unsigned int i = 0; i < columns.size(); i++) + { const auto& column = columns[i]; - auto columnPaddingLength = (int(column.maxColumnLength) - int(column.columnName.size()))/2.0; - header += createPadding(int(m_columnPadding + (int)std::floor(columnPaddingLength))); + auto columnPaddingLength = (int(column.maxColumnLength) - int(column.columnName.size())) / 2.0; + header += createPadding(int(m_columnPadding + (int) std::floor(columnPaddingLength))); header += column.columnName; - header += createPadding(int(m_columnPadding + (int)std::ceil(columnPaddingLength))); - if (i < columns.size()-1) + header += createPadding(int(m_columnPadding + (int) std::ceil(columnPaddingLength))); + if (i < columns.size() - 1) header += "|"; } @@ -67,15 +75,16 @@ std::string blt::string::TableFormatter::generateColumnHeader() { return header; } -std::string blt::string::TableFormatter::generateTopSeparator(size_t size) { +std::string blt::string::TableFormatter::generateTopSeparator(size_t size) +{ auto sizeOfName = m_tableName.empty() ? 0 : m_tableName.size() + 4; auto sizeNameRemoved = size - sizeOfName; std::string halfWidthLeftSeparator; std::string halfWidthRightSeparator; - auto sizeNameFloor = (size_t) std::floor((double)sizeNameRemoved/2.0); - auto sizeNameCeil = (size_t) std::ceil((double)sizeNameRemoved/2.0); + auto sizeNameFloor = (size_t) std::floor((double) sizeNameRemoved / 2.0); + auto sizeNameCeil = (size_t) std::ceil((double) sizeNameRemoved / 2.0); halfWidthLeftSeparator.reserve(sizeNameCeil); halfWidthRightSeparator.reserve(sizeNameFloor); @@ -92,7 +101,8 @@ std::string blt::string::TableFormatter::generateTopSeparator(size_t size) { std::string separator; separator += halfWidthLeftSeparator; - if (sizeOfName != 0) { + if (sizeOfName != 0) + { separator += "{ "; separator += m_tableName; separator += " }"; @@ -101,13 +111,16 @@ std::string blt::string::TableFormatter::generateTopSeparator(size_t size) { return separator; } -std::string blt::string::TableFormatter::generateSeparator(size_t size) { +std::string blt::string::TableFormatter::generateSeparator(size_t size) +{ size_t nextIndex = 0; size_t currentColumnIndex = 0; std::string wholeWidthSeparator; - for (unsigned int i = 0; i < size; i++) { - if (i == nextIndex) { - auto currentColumnSize = columns[currentColumnIndex++].maxColumnLength + m_columnPadding*2; + for (unsigned int i = 0; i < size; i++) + { + if (i == nextIndex) + { + auto currentColumnSize = columns[currentColumnIndex++].maxColumnLength + m_columnPadding * 2; nextIndex += currentColumnSize + 1; wholeWidthSeparator += "+"; } else @@ -117,13 +130,113 @@ std::string blt::string::TableFormatter::generateSeparator(size_t size) { return wholeWidthSeparator; } -void blt::string::TableFormatter::updateMaxColumnLengths() { - for (unsigned int i = 0; i < columns.size(); i++) { +void blt::string::TableFormatter::updateMaxColumnLengths() +{ + for (unsigned int i = 0; i < columns.size(); i++) + { auto& column = columns[i]; column.maxColumnLength = column.columnName.size(); - for (const auto& row : rows) { + for (const auto& row : rows) + { column.maxColumnLength = std::max(column.maxColumnLength, row.rowValues[i].size()); } } } +/* + * ----------------------- + * Tree Formatter + * ----------------------- + */ + +struct node_data +{ + blt::string::TreeFormatter::Node* node; + size_t level; + + node_data(blt::string::TreeFormatter::Node* node, size_t level): node(node), level(level) + {} +}; + +std::vector blt::string::TreeFormatter::construct() +{ + std::vector lines; + + std::stack nodes; + + std::queue bfs; + bfs.emplace(root, 0); + while (!bfs.empty()) + { + auto n = bfs.front(); + nodes.push(n); + std::cout << "Node at level " << n.level << " " << bfs.size() << "\n"; + if (n.node->left != nullptr) + bfs.emplace(n.node->left, n.level + 1); + if (n.node->right != nullptr) + bfs.emplace(n.node->right, n.level + 1); + bfs.pop(); + } + + return lines; +} + +std::vector blt::string::TreeFormatter::generateBox(blt::string::TreeFormatter::Node* node) const +{ + if (node == nullptr) + return {}; + std::vector lines; + + auto& data = node->data; + auto dataLength = data.length(); + auto paddingLeft = format.horizontalPadding; + auto paddingRight = format.horizontalPadding; + auto totalLength = paddingLeft + paddingRight + dataLength; + + if (totalLength % 2 != 0) + { + paddingRight += 1; + totalLength += 1; + } + + // create horizontal line based on the calculated length + std::string hline = createLine(totalLength, '+', '-'); + std::string paddedLine = createLine(totalLength, '|', ' '); + std::string dataStr; + dataStr.reserve(totalLength); + dataStr += '|'; + dataStr += createPadding(paddingLeft - 1); + dataStr += data; + dataStr += createPadding(paddingRight - 1); + dataStr += '|'; + + lines.push_back(hline); + for (int i = 0; i < format.verticalPadding; i++) + lines.push_back(paddedLine); + lines.push_back(std::move(dataStr)); + for (int i = 0; i < format.verticalPadding; i++) + lines.push_back(paddedLine); + + lines.push_back(std::move(hline)); + + return lines; +} + +std::string blt::string::createLine(size_t totalLength, char endingChar, char spacingChar) +{ + std::string line; + line.reserve(totalLength); + line += endingChar; + line += createPadding(totalLength - 2, spacingChar); + line += endingChar; + return line; +} + +std::string blt::string::createPadding(size_t length, char spacing) +{ + std::string padding; + padding.reserve(length); + for (size_t i = 0; i < length; i++) + padding += spacing; + return padding; +} diff --git a/tests/src/utility_test.cpp b/tests/src/utility_test.cpp index f7cafa4..f3758cd 100644 --- a/tests/src/utility_test.cpp +++ b/tests/src/utility_test.cpp @@ -16,6 +16,7 @@ * along with this program. If not, see . */ #include +#include #include #include #include @@ -23,12 +24,17 @@ #include #include - std::optional get() { return 10; } +void printLines(const std::vector& lines) +{ + for (const auto& v : lines) + std::cout << v << "\n"; +} + void blt::test::utility::run() { //std::vector temp; @@ -37,4 +43,54 @@ void blt::test::utility::run() { } + + blt::string::TableFormatter parkerLove("Intrinsic Action Value Table"); + parkerLove.addColumn({"Thing"}); + parkerLove.addColumn({"Value"}); + + parkerLove.addRow({"Cuddles", "1 / minute"}); + parkerLove.addRow({"Hand Job", "10"}); + parkerLove.addRow({"Head", "100"}); + parkerLove.addRow({"Sleeping Together (Non-Sexual)", "1,000"}); + parkerLove.addRow({"Actual Sex", "5,000"}); + parkerLove.addRow({"Sleeping Together (Sexual)", "10,000"}); + parkerLove.addRow({"Relationship (I would do anything for you)", "1,000,000,000,000"}); + + + printLines(parkerLove.createTable(true, true)); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + blt::string::TreeFormatter treeFormatter("I love Men"); + treeFormatter.getRoot()->with( + new string::TreeFormatter::Node("Guys"), + new string::TreeFormatter::Node("Femboys")); + printLines(treeFormatter.construct()); + + }