Browse Source

Util+Test: Remove Util library

master
Riyyi 2 years ago
parent
commit
0e15bfe4bf
  1. 568
      src/util/argparser.cpp
  2. 120
      src/util/argparser.h
  3. 89
      src/util/file.cpp
  4. 34
      src/util/file.h
  5. 6
      src/util/format/.clang-tidy
  6. 221
      src/util/format/builder.cpp
  7. 71
      src/util/format/builder.h
  8. 176
      src/util/format/color.cpp
  9. 156
      src/util/format/color.h
  10. 76
      src/util/format/format.cpp
  11. 119
      src/util/format/format.h
  12. 91
      src/util/format/formatter.cpp
  13. 307
      src/util/format/formatter.h
  14. 109
      src/util/format/log.cpp
  15. 97
      src/util/format/log.h
  16. 411
      src/util/format/parser.cpp
  17. 61
      src/util/format/parser.h
  18. 52
      src/util/format/print.cpp
  19. 69
      src/util/format/print.h
  20. 71
      src/util/genericlexer.cpp
  21. 41
      src/util/genericlexer.h
  22. 26
      src/util/json/array.cpp
  23. 56
      src/util/json/array.h
  24. 133
      src/util/json/fromjson.h
  25. 114
      src/util/json/job.cpp
  26. 41
      src/util/json/job.h
  27. 15
      src/util/json/json.h
  28. 217
      src/util/json/lexer.cpp
  29. 65
      src/util/json/lexer.h
  30. 26
      src/util/json/object.cpp
  31. 52
      src/util/json/object.h
  32. 497
      src/util/json/parser.cpp
  33. 45
      src/util/json/parser.h
  34. 154
      src/util/json/serializer.cpp
  35. 34
      src/util/json/serializer.h
  36. 153
      src/util/json/tojson.h
  37. 340
      src/util/json/value.cpp
  38. 149
      src/util/json/value.h
  39. 96
      src/util/meta/assert.h
  40. 27
      src/util/meta/compiler.h
  41. 22
      src/util/meta/concepts.h
  42. 20
      src/util/meta/odr.h
  43. 53
      src/util/shell.cpp
  44. 33
      src/util/shell.h
  45. 53
      src/util/singleton.h
  46. 328
      src/util/system.cpp
  47. 68
      src/util/system.h
  48. 131
      src/util/timer.cpp
  49. 51
      src/util/timer.h
  50. 79
      test/macro.h
  51. 8
      test/main.cpp
  52. 45
      test/testcase.h
  53. 76
      test/testsuite.cpp
  54. 36
      test/testsuite.h
  55. 4
      test/unit/testdotfile.cpp
  56. 949
      test/unit/testutilargparser.cpp
  57. 501
      test/unit/testutilformat.cpp
  58. 570
      test/unit/testutiljson.cpp
  59. 39
      test/unit/testutilshell.cpp
  60. 98
      test/unit/testutilsystem.cpp

568
src/util/argparser.cpp

@ -1,568 +0,0 @@
/*
* Copyright (C) 2021-2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <algorithm> // find_if
#include <cstddef> // size_t
#include <cstdint> // uint8_t
#include <cstdio> // printf
#include <cstring> // strcmp
#include <limits> // numeric_limits
#include <string> // stod, stoi, stoul
#include <string_view>
#include <vector>
#include "util/argparser.h"
namespace Util {
ArgParser::ArgParser()
{
}
ArgParser::~ArgParser()
{
}
void ArgParser::printError(char parameter, Error error)
{
char tmp[] { parameter, '\0' };
printError(tmp, error, false);
}
void ArgParser::printError(const char* parameter, Error error, bool longName)
{
if (!m_errorReporting) {
return;
}
if (error == Error::OptionInvalid) {
printf("%s: invalid option -- '%s'\n", m_name, parameter);
}
else if (error == Error::OptionUnrecognized) {
printf("%s: unrecognized option -- '%s'\n", m_name, parameter);
}
else if (error == Error::OptionDoesntAllowArgument) {
printf("%s: option '--%s' doesn't allow an argument\n", m_name, parameter);
}
else if (error == Error::OptionRequiresArgument) {
if (longName) {
printf("%s: option '--%s' requires an argument\n", m_name, parameter);
}
else {
printf("%s: option requires an argument -- '%s'\n", m_name, parameter);
}
}
else if (error == Error::OptionInvalidArgumentType) {
if (longName) {
printf("%s: option '--%s' invalid type\n", m_name, parameter);
}
else {
printf("%s: option invalid type -- '%s'\n", m_name, parameter);
}
}
else if (error == Error::ArgumentExtraOperand) {
printf("%s: extra operand '%s'\n", m_name, parameter);
}
else if (error == Error::ArgumentRequired) {
printf("%s: missing required argument '%s'\n", m_name, parameter);
}
else if (error == Error::ArgumentInvalidType) {
printf("%s: invalid argument type '%s'\n", m_name, parameter);
}
// TODO: Print command usage, if it's enabled.
}
// Required: directly after || separated by space
// Optional: directly after
bool ArgParser::parseShortOption(std::string_view option, std::string_view next)
{
bool result = true;
#ifndef NDEBUG
printf("Parsing short option: '%s'\n", option.data());
#endif
char c;
std::string_view value;
for (size_t i = 0; i < option.size(); ++i) {
c = option.at(i);
#ifndef NDEBUG
printf("short '%c'\n", c);
#endif
auto foundOption = std::find_if(m_options.begin(), m_options.end(), [&c](Option& it) -> bool {
return it.shortName == c;
});
// Option does not exist
if (foundOption == m_options.cend()) {
printError(c, Error::OptionInvalid);
if (m_exitOnFirstError) {
return false;
}
}
else if (foundOption->requiresArgument == Required::No) {
// FIXME: Figure out why providing a nullptr breaks the lambda here.
result = foundOption->acceptValue("");
}
else if (foundOption->requiresArgument == Required::Yes) {
value = option.substr(i + 1);
if (value.empty() && next.empty()) {
foundOption->error = Error::OptionRequiresArgument;
printError(c, Error::OptionRequiresArgument);
result = false;
}
else if (!value.empty()) {
result = foundOption->acceptValue(value.data());
if (!result) {
printError(c, Error::OptionInvalidArgumentType);
}
}
else if (next[0] == '-') {
foundOption->error = Error::OptionRequiresArgument;
printError(c, Error::OptionRequiresArgument);
result = false;
}
else {
result = foundOption->acceptValue(next.data());
m_optionIndex++;
if (!result) {
printError(c, Error::OptionInvalidArgumentType);
}
}
break;
}
else if (foundOption->requiresArgument == Required::Optional) {
value = option.substr(i + 1);
if (!value.empty()) {
result = foundOption->acceptValue(value.data());
if (!result) {
printError(c, Error::OptionInvalidArgumentType);
}
break;
}
}
}
return result;
}
// Required: directly after, separated by '=' || separated by space
// Optional: directly after, separated by '='
bool ArgParser::parseLongOption(std::string_view option, std::string_view next)
{
std::string name = std::string(option.substr(0, option.find_first_of('=')));
std::string_view value = option.substr(option.find_first_of('=') + 1);
auto foundOption = std::find_if(m_options.begin(), m_options.end(), [&name](Option& it) -> bool {
return it.longName && it.longName == name;
});
if (foundOption == m_options.cend()) {
printError(name.data(), Error::OptionUnrecognized);
return false;
}
enum class ArgumentProvided : uint8_t {
No,
DirectlyAfter,
Seperated,
};
auto argument = ArgumentProvided::No;
if (name != value || option.find('=') != std::string_view::npos) {
argument = ArgumentProvided::DirectlyAfter;
}
else if (!next.empty() && next[0] != '-') {
argument = ArgumentProvided::Seperated;
value = next;
}
bool result = true;
if (foundOption->requiresArgument == Required::No) {
if (argument == ArgumentProvided::DirectlyAfter) {
foundOption->error = Error::OptionDoesntAllowArgument;
printError(name.data(), Error::OptionDoesntAllowArgument);
return false;
}
result = foundOption->acceptValue("");
}
else if (foundOption->requiresArgument == Required::Yes) {
if (argument == ArgumentProvided::No) {
foundOption->error = Error::OptionRequiresArgument;
printError(name.data(), Error::OptionRequiresArgument);
return false;
}
result = foundOption->acceptValue(value.data());
if (!result) {
printError(name.data(), Error::OptionInvalidArgumentType);
}
if (argument == ArgumentProvided::Seperated) {
m_optionIndex++;
}
}
else if (foundOption->requiresArgument == Required::Optional) {
if (argument == ArgumentProvided::DirectlyAfter) {
result = foundOption->acceptValue(value.data());
if (!result) {
printError(name.data(), Error::OptionInvalidArgumentType);
}
}
}
return result;
}
bool ArgParser::parseArgument(std::string_view argument)
{
bool result = true;
for (;;) {
// Run out of argument handlers
if (m_argumentIndex >= m_arguments.size()) {
printError(argument.data(), Error::ArgumentExtraOperand);
return false;
}
Argument& currentArgument = m_arguments.at(m_argumentIndex);
result = currentArgument.acceptValue(argument.data());
if (result) {
currentArgument.addedValues++;
if (currentArgument.addedValues >= currentArgument.maxValues) {
m_argumentIndex++;
}
}
else if (currentArgument.minValues == 0) {
m_argumentIndex++;
continue;
}
else {
printError(argument.data(), Error::ArgumentInvalidType);
}
break;
}
return result;
}
bool ArgParser::parse(int argc, const char* argv[])
{
bool result = true;
// Set looping indices
m_optionIndex = 1;
m_argumentIndex = 0;
// By default parse all '-' prefixed parameters as options
m_nonOptionMode = false;
// Get program name
m_name = argv[0] + std::string_view(argv[0]).find_last_of('/') + 1;
std::string_view argument;
std::string_view next;
for (; m_optionIndex < (size_t)argc; ++m_optionIndex) {
#ifndef NDEBUG
printf("argv[%zu]: %s\n", m_optionIndex, argv[m_optionIndex]);
#endif
// Get the current and next parameter
argument = argv[m_optionIndex];
if (m_optionIndex + 1 < (size_t)argc && argv[m_optionIndex + 1][0] != '-') {
next = argv[m_optionIndex + 1];
}
else {
next = {};
}
// Stop parsing '-' prefixed parameters as options afer '--'
if (argument.compare("--") == 0) {
m_nonOptionMode = true;
continue;
}
// Long Option
if (!m_nonOptionMode && argument[0] == '-' && argument[1] == '-') {
argument = argument.substr(argument.find_first_not_of('-'));
if (!parseLongOption(argument, next)) {
result = false;
}
}
// Short Option
else if (!m_nonOptionMode && argument[0] == '-') {
argument = argument.substr(argument.find_first_not_of('-'));
if (!parseShortOption(argument, next)) {
result = false;
}
}
// Argument
else {
if (m_stopParsingOnFirstNonOption) {
m_nonOptionMode = true;
}
if (!parseArgument(argument)) {
result = false;
}
}
if (m_exitOnFirstError && !result) {
break;
}
}
// Check any leftover arguments for required
for (; m_argumentIndex < m_arguments.size(); ++m_argumentIndex) {
Argument& currentArgument = m_arguments.at(m_argumentIndex);
if (currentArgument.minValues > currentArgument.addedValues) {
result = false;
printError(currentArgument.name ? currentArgument.name : "", Error::ArgumentRequired);
}
}
if (result) {
return true;
}
for (auto& option : m_options) {
if (option.longName && strcmp(option.longName, "help") == 0) {
printf("Try '%s --help' for more information.\n", m_name);
break;
}
if (option.shortName == 'h') {
printf("Try '%s -h' for more information.\n", m_name);
break;
}
}
return false;
}
// -----------------------------------------
void ArgParser::addOption(Option&& option)
{
m_options.push_back(option);
}
void ArgParser::addOption(bool& value, char shortName, const char* longName, const char* usageString, const char* manString)
{
addOption({ shortName, longName, nullptr, usageString, manString, Required::No, getAcceptFunction(value) });
}
void ArgParser::addOption(const char*& value, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName, Required requiresArgument)
{
addOption({ shortName, longName, argumentName, usageString, manString, requiresArgument, getAcceptFunction(value) });
}
void ArgParser::addOption(std::string& value, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName, Required requiresArgument)
{
addOption({ shortName, longName, argumentName, usageString, manString, requiresArgument, getAcceptFunction(value) });
}
void ArgParser::addOption(std::string_view& value, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName, Required requiresArgument)
{
addOption({ shortName, longName, argumentName, usageString, manString, requiresArgument, getAcceptFunction(value) });
}
void ArgParser::addOption(int& value, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName, Required requiresArgument)
{
addOption({ shortName, longName, argumentName, usageString, manString, requiresArgument, getAcceptFunction(value) });
}
void ArgParser::addOption(unsigned int& value, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName, Required requiresArgument)
{
addOption({ shortName, longName, argumentName, usageString, manString, requiresArgument, getAcceptFunction(value) });
}
void ArgParser::addOption(double& value, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName, Required requiresArgument)
{
addOption({ shortName, longName, argumentName, usageString, manString, requiresArgument, getAcceptFunction(value) });
}
void ArgParser::addOption(std::vector<std::string>& values, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName, Required requiresArgument)
{
addOption({ shortName, longName, argumentName, usageString, manString, requiresArgument, getAcceptFunction(values) });
}
// -----------------------------------------
void ArgParser::addArgument(Argument&& argument)
{
m_arguments.push_back(argument);
}
void ArgParser::addArgument(bool& value, const char* name, const char* usageString, const char* manString, Required required)
{
size_t minValues = required == Required::Yes ? 1 : 0;
addArgument({ name, usageString, manString, minValues, 1, 0, getAcceptFunction(value) });
}
void ArgParser::addArgument(const char*& value, const char* name, const char* usageString, const char* manString, Required required)
{
size_t minValues = required == Required::Yes ? 1 : 0;
addArgument({ name, usageString, manString, minValues, 1, 0, getAcceptFunction(value) });
}
void ArgParser::addArgument(std::string& value, const char* name, const char* usageString, const char* manString, Required required)
{
size_t minValues = required == Required::Yes ? 1 : 0;
addArgument({ name, usageString, manString, minValues, 1, 0, getAcceptFunction(value) });
}
void ArgParser::addArgument(std::string_view& value, const char* name, const char* usageString, const char* manString, Required required)
{
size_t minValues = required == Required::Yes ? 1 : 0;
addArgument({ name, usageString, manString, minValues, 1, 0, getAcceptFunction(value) });
}
void ArgParser::addArgument(int& value, const char* name, const char* usageString, const char* manString, Required required)
{
size_t minValues = required == Required::Yes ? 1 : 0;
addArgument({ name, usageString, manString, minValues, 1, 0, getAcceptFunction(value) });
}
void ArgParser::addArgument(unsigned int& value, const char* name, const char* usageString, const char* manString, Required required)
{
size_t minValues = required == Required::Yes ? 1 : 0;
addArgument({ name, usageString, manString, minValues, 1, 0, getAcceptFunction(value) });
}
void ArgParser::addArgument(double& value, const char* name, const char* usageString, const char* manString, Required required)
{
size_t minValues = required == Required::Yes ? 1 : 0;
addArgument({ name, usageString, manString, minValues, 1, 0, getAcceptFunction(value) });
}
void ArgParser::addArgument(std::vector<std::string>& values, const char* name, const char* usageString, const char* manString, Required required)
{
size_t minValues = required == Required::Yes ? 1 : 0;
addArgument({ name, usageString, manString, minValues, values.max_size(), 0, getAcceptFunction(values) });
}
// -----------------------------------------
AcceptFunction ArgParser::getAcceptFunction(bool& value)
{
return [&value](const char*) -> bool {
value = true;
return true;
};
}
AcceptFunction ArgParser::getAcceptFunction(const char*& value)
{
return [&value](const char* input) -> bool {
value = input;
return true;
};
}
AcceptFunction ArgParser::getAcceptFunction(std::string& value)
{
return [&value](const char* input) -> bool {
value = input;
return true;
};
}
AcceptFunction ArgParser::getAcceptFunction(std::string_view& value)
{
return [&value](const char* input) -> bool {
value = input;
return true;
};
}
AcceptFunction ArgParser::getAcceptFunction(int& value)
{
return [&value](const char* input) -> bool {
const char* validate = input;
for (; *validate != '\0'; ++validate) {
// - [0-9]
if (*validate != 45 && (*validate < 48 || *validate > 57)) {
return false;
}
}
try {
value = std::stoi(input);
return true;
}
catch (...) {
return false;
}
};
}
AcceptFunction ArgParser::getAcceptFunction(unsigned int& value)
{
return [&value](const char* input) -> bool {
const char* validate = input;
for (; *validate != '\0'; ++validate) {
// [0-9]
if (*validate < 48 || *validate > 57) {
return false;
}
}
unsigned long convert = 0;
try {
convert = std::stoul(input);
}
catch (...) {
return false;
}
if (convert <= std::numeric_limits<unsigned int>::max()) {
value = static_cast<unsigned int>(convert);
return true;
}
return false;
};
}
AcceptFunction ArgParser::getAcceptFunction(double& value)
{
return [&value](const char* input) -> bool {
const char* validate = input;
for (; *validate != '\0'; ++validate) {
// . [0-9]
if (*validate != 46 && (*validate < 48 || *validate > 57)) {
return false;
}
}
try {
value = std::stod(input);
return true;
}
catch (...) {
return false;
}
};
}
AcceptFunction ArgParser::getAcceptFunction(std::vector<std::string>& value)
{
return [&value](const char* input) -> bool {
value.push_back(input);
return true;
};
}
} // namespace Util

120
src/util/argparser.h

@ -1,120 +0,0 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <cstddef> // size_t
#include <cstdint> // uint8_t
#include <functional> // function
#include <string>
#include <string_view>
#include <vector>
namespace Util {
using AcceptFunction = std::function<bool(const char*)>;
class ArgParser final {
public:
ArgParser();
virtual ~ArgParser();
enum class Required : uint8_t {
No,
Yes,
Optional,
};
enum class Error : uint8_t {
None,
OptionInvalid, // For short options
OptionUnrecognized, // For long options
OptionDoesntAllowArgument,
OptionRequiresArgument,
OptionInvalidArgumentType,
ArgumentExtraOperand,
ArgumentRequired,
ArgumentInvalidType,
};
struct Option {
char shortName { 0 };
const char* longName { nullptr };
const char* argumentName { nullptr };
const char* usageString { nullptr };
const char* manString { nullptr };
Required requiresArgument;
AcceptFunction acceptValue;
Error error = Error::None;
};
struct Argument {
const char* name { nullptr };
const char* usageString { nullptr };
const char* manString { nullptr };
size_t minValues { 0 };
size_t maxValues { 1 };
size_t addedValues { 0 };
AcceptFunction acceptValue;
};
bool parse(int argc, const char* argv[]);
void addOption(Option&& option);
void addOption(bool& value, char shortName, const char* longName, const char* usageString, const char* manString);
void addOption(const char*& value, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName = "", Required requiresArgument = Required::No);
void addOption(std::string& value, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName = "", Required requiresArgument = Required::No);
void addOption(std::string_view& value, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName = "", Required requiresArgument = Required::No);
void addOption(int& value, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName = "", Required requiresArgument = Required::No);
void addOption(unsigned int& value, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName = "", Required requiresArgument = Required::No);
void addOption(double& value, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName = "", Required requiresArgument = Required::No);
void addOption(std::vector<std::string>& values, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName = "", Required requiresArgument = Required::No);
void addArgument(Argument&& argument);
void addArgument(bool& value, const char* name, const char* usageString, const char* manString, Required required = Required::No);
void addArgument(const char*& value, const char* name, const char* usageString, const char* manString, Required required = Required::No);
void addArgument(std::string& value, const char* name, const char* usageString, const char* manString, Required required = Required::No);
void addArgument(std::string_view& value, const char* name, const char* usageString, const char* manString, Required required = Required::No);
void addArgument(int& value, const char* name, const char* usageString, const char* manString, Required required = Required::No);
void addArgument(unsigned int& value, const char* name, const char* usageString, const char* manString, Required required = Required::No);
void addArgument(double& value, const char* name, const char* usageString, const char* manString, Required required = Required::No);
void addArgument(std::vector<std::string>& values, const char* name, const char* usageString, const char* manString, Required required = Required::No);
void setErrorReporting(bool state) { m_errorReporting = state; }
void setExitOnFirstError(bool state) { m_exitOnFirstError = state; }
void setStopParsingOnFirstNonOption(bool state) { m_stopParsingOnFirstNonOption = state; }
private:
void printError(char parameter, Error error);
void printError(const char* parameter, Error error, bool longName = true);
bool parseShortOption(std::string_view option, std::string_view next);
bool parseLongOption(std::string_view option, std::string_view next);
bool parseArgument(std::string_view argument);
AcceptFunction getAcceptFunction(bool& value);
AcceptFunction getAcceptFunction(const char*& value);
AcceptFunction getAcceptFunction(std::string& value);
AcceptFunction getAcceptFunction(std::string_view& value);
AcceptFunction getAcceptFunction(int& value);
AcceptFunction getAcceptFunction(unsigned int& value);
AcceptFunction getAcceptFunction(double& value);
AcceptFunction getAcceptFunction(std::vector<std::string>& value);
bool m_errorReporting { true };
bool m_exitOnFirstError { true };
bool m_stopParsingOnFirstNonOption { false };
size_t m_optionIndex { 1 };
size_t m_argumentIndex { 0 };
bool m_nonOptionMode { false };
const char* m_name { nullptr };
std::vector<Option> m_options;
std::vector<Argument> m_arguments;
};
} // namespace Util

89
src/util/file.cpp

@ -1,89 +0,0 @@
/*
* Copyright (C) 2021-2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <cstdint> // int32_t
#include <filesystem>
#include <fstream> // ifstream, ios, ofstream
#include <memory> // make_unique
#include <string>
#include "util/file.h"
#include "util/meta/assert.h"
namespace Util {
File::File(const std::string& path)
: m_path(path)
{
// Create input stream object and open file
std::ifstream file(path, std::ios::in);
VERIFY(file.is_open(), "failed to open file: '{}'", path);
// Get length of the file
file.seekg(0, std::ios::end);
int32_t size = file.tellg();
file.seekg(0, std::ios::beg);
VERIFY(size != -1, "failed to read file length: '{}', path");
// Allocate memory filled with zeros
auto buffer = std::make_unique<char[]>(size);
// Fill buffer with file contents
file.read(buffer.get(), size);
file.close();
m_data = std::string(buffer.get(), size);
}
File::~File()
{
}
// -----------------------------------------
File File::create(const std::string& path)
{
if (!std::filesystem::exists(path)) {
std::ofstream { path };
}
return Util::File(path);
}
// -----------------------------------------
void File::clear()
{
m_data.clear();
}
File& File::append(const std::string& data)
{
m_data.append(data);
return *this;
}
File& File::replace(size_t index, size_t length, const std::string& data)
{
m_data.replace(index, length, data);
return *this;
}
File& File::flush()
{
// Create output stream object and open file
std::ofstream file(m_path, std::ios::out | std::ios::trunc);
VERIFY(file.is_open(), "failed to open file: '{}'", m_path);
// Write data to disk
file.write(m_data.c_str(), m_data.size());
return *this;
}
} // namespace Util

34
src/util/file.h

@ -1,34 +0,0 @@
/*
* Copyright (C) 2021-2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <string>
namespace Util {
class File {
public:
File(const std::string& path);
virtual ~File();
static File create(const std::string& path);
void clear();
File& append(const std::string& data);
File& replace(size_t index, size_t length, const std::string& data);
File& flush();
const char* c_str() const { return m_data.c_str(); }
const std::string& data() const { return m_data; }
const std::string& path() const { return m_path; }
private:
std::string m_path;
std::string m_data;
};
} // namespace Util

6
src/util/format/.clang-tidy

@ -1,6 +0,0 @@
# -*- yaml -*-
---
# FIXME: Figure out why NOLINTBEGIN/NOLINTEND doesnt work
Checks: -misc-unused-using-decls
...

221
src/util/format/builder.cpp

@ -1,221 +0,0 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <algorithm> // min
#include <cstddef> // size_t
#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
#include <iomanip> // setprecision
#include <ios> // defaultfloat, fixed
#include <limits> // numeric_limits
#include <sstream> // stringstream
#include <string>
#include <string_view>
#include "util/format/builder.h"
#include "util/meta/assert.h"
namespace Util::Format {
void Builder::putLiteral(std::string_view literal)
{
for (size_t i = 0; i < literal.length(); ++i) {
putCharacter(literal[i]);
if (literal[i] == '{' || literal[i] == '}') {
++i;
}
}
}
static constexpr std::string numberToString(size_t value, uint8_t base, bool uppercase)
{
std::string result;
if (value > std::numeric_limits<uint32_t>::max()) {
result.reserve(64);
}
else if (value > std::numeric_limits<uint16_t>::max()) {
result.reserve(32);
}
constexpr const auto& lookupLowercase = "0123456789abcdef";
constexpr const auto& lookupUppercase = "0123456789ABCDEF";
if (value == 0) {
result = '0';
}
else if (uppercase) {
while (value > 0) {
result.insert(0, 1, lookupUppercase[value % base]);
value /= base;
}
}
else {
while (value > 0) {
result.insert(0, 1, lookupLowercase[value % base]);
value /= base;
}
}
return result;
}
void Builder::putU64(size_t value,
uint8_t base,
bool uppercase,
char fill,
Align align,
Sign sign,
bool alternativeForm,
bool zeroPadding,
size_t width,
bool isNegative) const
{
std::string number = numberToString(value, base, uppercase);
// Sign
std::string prefix = "";
switch (sign) {
case Sign::None:
case Sign::Negative:
if (isNegative) {
prefix = '-';
}
break;
case Sign::Both:
prefix = (isNegative) ? '-' : '+';
break;
case Sign::Space:
prefix = (isNegative) ? '-' : ' ';
break;
default:
VERIFY_NOT_REACHED();
};
// Alternative form
if (alternativeForm) {
switch (base) {
case 2:
prefix += (uppercase) ? "0B" : "0b";
break;
case 8:
prefix += '0';
break;
case 10:
break;
case 16:
prefix += (uppercase) ? "0X" : "0x";
break;
default:
VERIFY_NOT_REACHED();
}
}
if (!zeroPadding) {
number.insert(0, prefix);
}
else {
if (align != Builder::Align::None) {
number.insert(0, prefix);
}
fill = '0';
}
size_t length = number.length();
if (width < length) {
m_builder.write(number.data(), length);
return;
}
switch (align) {
case Align::Left:
m_builder.write(number.data(), length);
m_builder << std::string(width - length, fill);
break;
case Align::Center: {
size_t half = (width - length) / 2;
m_builder << std::string(half, fill);
m_builder.write(number.data(), length);
m_builder << std::string(width - half - length, fill);
break;
}
case Align::Right:
m_builder << std::string(width - length, fill);
m_builder.write(number.data(), length);
break;
case Align::None:
if (zeroPadding) {
m_builder << prefix;
m_builder << std::string(width - length - prefix.length(), fill);
}
else {
m_builder << std::string(width - length, fill);
}
m_builder.write(number.data(), length);
break;
default:
VERIFY_NOT_REACHED();
};
}
void Builder::putI64(int64_t value,
uint8_t base,
bool uppercase,
char fill,
Align align,
Sign sign,
bool alternativeForm,
bool zeroPadding,
size_t width) const
{
bool isNegative = value < 0;
value = isNegative ? -value : value;
putU64(static_cast<uint64_t>(value), base, uppercase, fill, align, sign, alternativeForm, zeroPadding, width, isNegative);
}
void Builder::putF64(double number, uint8_t precision) const
{
precision = std::min(precision, static_cast<uint8_t>(std::numeric_limits<double>::digits10));
std::stringstream stream;
stream
<< std::fixed << std::setprecision(precision)
<< number
<< std::defaultfloat << std::setprecision(6);
std::string string = stream.str();
m_builder << string;
}
void Builder::putString(std::string_view string, char fill, Align align, size_t width) const
{
size_t length = string.length();
if (width < length) {
m_builder.write(string.data(), length);
return;
}
switch (align) {
case Align::None:
case Align::Left:
m_builder.write(string.data(), length);
m_builder << std::string(width - length, fill);
break;
case Align::Center: {
size_t half = (width - length) / 2;
m_builder << std::string(half, fill);
m_builder.write(string.data(), length);
m_builder << std::string(width - half - length, fill);
break;
}
case Align::Right:
m_builder << std::string(width - length, fill);
m_builder.write(string.data(), length);
break;
default:
VERIFY_NOT_REACHED();
};
}
} // namespace Util::Format

71
src/util/format/builder.h

@ -1,71 +0,0 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <cstddef> // size_t
#include <cstdint> // int32_t, int64_t, uint8_t, uint32_t
#include <sstream> // stringstream
#include <string_view>
namespace Util::Format {
class Builder {
public:
enum class Align : uint8_t {
None,
Left = '<',
Right = '>',
Center = '^',
};
enum class Sign : uint8_t {
None,
Negative = '-',
Both = '+',
Space = ' ',
};
explicit Builder(std::stringstream& builder)
: m_builder(builder)
{
}
void putLiteral(std::string_view literal);
void putU64(size_t value,
uint8_t base = 10,
bool uppercase = false,
char fill = ' ',
Align align = Align::Right,
Sign sign = Sign::Negative,
bool alternativeForm = false,
bool zeroPadding = false,
size_t width = 0,
bool isNegative = false) const;
void putI64(int64_t value,
uint8_t base = 10,
bool uppercase = false,
char fill = ' ',
Align align = Align::Right,
Sign sign = Sign::Negative,
bool alternativeForm = false,
bool zeroPadding = false,
size_t width = 0) const;
void putF64(double number, uint8_t precision = 6) const;
void putCharacter(char character) const { m_builder.write(&character, 1); }
void putString(std::string_view string, char fill = ' ', Align align = Align::Left, size_t width = 0) const;
const std::stringstream& builder() const { return m_builder; }
std::stringstream& builder() { return m_builder; }
private:
std::stringstream& m_builder;
};
} // namespace Util::Format

176
src/util/format/color.cpp

@ -1,176 +0,0 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <cstdint> // uint8_t
#include <cstdio> // FILE, fputs, stdout
#include <sstream> // stringstream
#include <string>
#include <string_view>
#include "util/format/color.h"
#include "util/format/format.h"
#include "util/meta/assert.h"
namespace Util::Format {
TextStyle::TextStyle(Emphasis emphasis)
: m_emphasis(emphasis)
{
}
TextStyle::TextStyle(bool isForeground, TerminalColor color)
{
if (isForeground) {
m_foregroundColor = color;
}
else {
m_backgroundColor = color;
}
}
// -----------------------------------------
TextStyle& TextStyle::operator|=(const TextStyle& rhs)
{
if (m_foregroundColor == TerminalColor::None) {
m_foregroundColor = rhs.m_foregroundColor;
}
else {
VERIFY(rhs.m_foregroundColor == TerminalColor::None, "can't OR a terminal color");
}
if (m_backgroundColor == TerminalColor::None) {
m_backgroundColor = rhs.m_backgroundColor;
}
else {
VERIFY(rhs.m_backgroundColor == TerminalColor::None, "can't OR a terminal color");
}
m_emphasis = static_cast<Emphasis>(static_cast<uint8_t>(m_emphasis) | static_cast<uint8_t>(rhs.m_emphasis));
return *this;
}
TextStyle fg(TerminalColor foreground)
{
return TextStyle(true, foreground);
}
TextStyle bg(TerminalColor background)
{
return TextStyle(false, background);
}
TextStyle operator|(TextStyle lhs, const TextStyle& rhs)
{
return lhs |= rhs;
}
TextStyle operator|(Emphasis lhs, Emphasis rhs)
{
return TextStyle { lhs } | rhs;
}
bool operator&(Emphasis lhs, Emphasis rhs)
{
return static_cast<uint8_t>(lhs) & static_cast<uint8_t>(rhs);
}
// -----------------------------------------
void setDisplayAttributes(std::stringstream& stream, const TextStyle& style)
{
bool hasForeground = style.foregroundColor() != TerminalColor::None;
bool hasBackground = style.backgroundColor() != TerminalColor::None;
bool hasEmphasis = style.emphasis() != Emphasis::None;
if (!hasForeground && !hasBackground && !hasEmphasis) {
return;
}
stream.write("\033[", 2);
if (hasForeground) {
stream << format("{}", static_cast<uint8_t>(style.foregroundColor()));
}
if (hasBackground) {
if (hasForeground) {
stream.write(";", 1);
}
stream << format("{}", static_cast<uint8_t>(style.backgroundColor()) + 10);
}
stream.write("m", 1);
if (hasEmphasis) {
#define ESCAPE_ATTRIBUTE(escape, attribute) \
if (style.emphasis() & escape) { \
stream.write("\033[", 2); \
stream.write(attribute, 1); \
stream.write("m", 1); \
}
ESCAPE_ATTRIBUTE(Emphasis::Bold, "1");
ESCAPE_ATTRIBUTE(Emphasis::Faint, "2");
ESCAPE_ATTRIBUTE(Emphasis::Italic, "3");
ESCAPE_ATTRIBUTE(Emphasis::Underline, "4");
ESCAPE_ATTRIBUTE(Emphasis::Blink, "5");
ESCAPE_ATTRIBUTE(Emphasis::Reverse, "7");
ESCAPE_ATTRIBUTE(Emphasis::Conceal, "8");
ESCAPE_ATTRIBUTE(Emphasis::Strikethrough, "9");
}
}
void coloredVariadicFormat(std::stringstream& stream, const TextStyle& style, std::string_view format, TypeErasedParameters& parameters)
{
setDisplayAttributes(stream, style);
variadicFormat(stream, format, parameters);
stream.write("\033[0m", 4);
}
// -----------------------------------------
void coloredVariadicPrint(FILE* file, const TextStyle& style, std::string_view format, TypeErasedParameters& parameters)
{
std::stringstream stream;
setDisplayAttributes(stream, style);
variadicFormat(stream, format, parameters);
stream.write("\033[0m", 4);
std::string string = stream.str();
fputs(string.c_str(), file);
}
// -----------------------------------------
ColorPrintOperatorStyle::ColorPrintOperatorStyle(FILE* file, const TextStyle& style)
: m_file(file)
, m_style(style)
, m_stream()
, m_builder(m_stream)
{
setDisplayAttributes(m_stream, style);
}
ColorPrintOperatorStyle::~ColorPrintOperatorStyle()
{
m_stream.write("\033[0m", 4);
std::string string = m_stream.str();
fputs(string.c_str(), m_file);
}
ColorPrintOperatorStyle print(const TextStyle& style)
{
return ColorPrintOperatorStyle(stdout, style);
}
ColorPrintOperatorStyle print(FILE* file, const TextStyle& style)
{
return ColorPrintOperatorStyle(file, style);
}
} // namespace Util::Format

156
src/util/format/color.h

@ -1,156 +0,0 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <cstddef> // size_t
#include <cstdint> // uint8_t
#include <cstdio> // FILE, stdout
#include <sstream> // stringstream
#include <string_view>
#include "util/format/format.h"
namespace Util::Format {
enum class TerminalColor : uint8_t {
None = 0,
Black = 30,
Red,
Green,
Yellow,
Blue,
Magenta,
Cyan,
White,
BrightBlack = 90,
BrightRed,
BrightGreen,
BrightYellow,
BrightBlue,
BrightMagenta,
BrightCyan,
BrightWhite
};
// Bit field
enum class Emphasis : uint8_t {
None = 0, // Attribute 0
Bold = 1, // Attribute 1
Faint = 1 << 1, // Attribute 2
Italic = 1 << 2, // Attribute 3