Util: Add formatting and type printing capability
Extending the type compatibility is done with an easy to use API.
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
# -*- yaml -*-
|
||||
|
||||
---
|
||||
# FIXME: Figure out why NOLINTBEGIN/NOLINTEND doesnt work
|
||||
Checks: -misc-unused-using-decls
|
||||
...
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Riyyi
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <cstddef> // size_t
|
||||
#include <iomanip> // setprecision
|
||||
#include <ios> // fixed
|
||||
#include <limits> // numeric_limits
|
||||
#include <sstream> // stringstream
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "util/format/builder.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Builder::putF32(float number, size_t precision) const
|
||||
{
|
||||
precision = (precision > std::numeric_limits<float>::digits10) ? m_precision : precision;
|
||||
|
||||
std::stringstream stream;
|
||||
stream
|
||||
<< std::fixed
|
||||
<< std::setprecision(precision)
|
||||
<< number;
|
||||
std::string string = stream.str();
|
||||
string = string.substr(0, string.find_first_of('0', string.find('.')));
|
||||
m_builder << string;
|
||||
}
|
||||
|
||||
void Builder::putF64(double number, size_t precision) const
|
||||
{
|
||||
precision = (precision > std::numeric_limits<double>::digits10) ? m_precision : precision;
|
||||
|
||||
std::stringstream stream;
|
||||
stream
|
||||
<< std::fixed
|
||||
<< std::setprecision(precision)
|
||||
<< number;
|
||||
std::string string = stream.str();
|
||||
string = string.substr(0, string.find_first_of('0', string.find('.')));
|
||||
m_builder << string;
|
||||
}
|
||||
|
||||
void Builder::resetSpecifiers()
|
||||
{
|
||||
setPrecision();
|
||||
}
|
||||
|
||||
} // namespace Util::Format
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Riyyi
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef UTIL_FORMAT_BUILDER_H
|
||||
#define UTIL_FORMAT_BUILDER_H
|
||||
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // int32_t, uint32_t, int64_t
|
||||
#include <sstream> // stringstream
|
||||
#include <string_view>
|
||||
|
||||
namespace Util::Format {
|
||||
|
||||
class Builder {
|
||||
public:
|
||||
explicit Builder(std::stringstream& builder)
|
||||
: m_precision(6)
|
||||
, m_builder(builder)
|
||||
{
|
||||
}
|
||||
|
||||
void putLiteral(std::string_view literal);
|
||||
|
||||
void putI32(int32_t number) const { m_builder << number; } // int
|
||||
void putU32(uint32_t number) const { m_builder << number; } // unsigned int
|
||||
void putI64(int64_t number) const { m_builder << number; } // long int
|
||||
void putU64(size_t number) const { m_builder << number; } // long unsigned int
|
||||
void putF32(float number, size_t precision = -1) const;
|
||||
void putF64(double number, size_t precision = -1) const;
|
||||
void putCharacter(char character) const { m_builder.write(&character, 1); }
|
||||
void putString(const std::string_view string) const { m_builder.write(string.data(), string.length()); }
|
||||
void putPointer(const void* pointer) const { m_builder << pointer; }
|
||||
|
||||
void resetSpecifiers();
|
||||
void setPrecision(size_t precision = 6) { m_precision = precision; };
|
||||
|
||||
const std::stringstream& builder() const { return m_builder; }
|
||||
std::stringstream& builder() { return m_builder; }
|
||||
|
||||
private:
|
||||
size_t m_precision { 6 };
|
||||
std::stringstream& m_builder;
|
||||
};
|
||||
|
||||
} // namespace Util::Format
|
||||
|
||||
#endif // UTIL_FORMAT_BUILDER_H
|
||||
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Riyyi
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <sstream> // stringstream
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "util/format/builder.h"
|
||||
#include "util/format/format.h"
|
||||
#include "util/format/parser.h"
|
||||
|
||||
namespace Util::Format {
|
||||
|
||||
void variadicFormatImpl(Builder& builder, Parser& parser, TypeErasedParameters& parameters)
|
||||
{
|
||||
const auto literal = parser.consumeLiteral();
|
||||
builder.putLiteral(literal);
|
||||
|
||||
if (!parameters.isEOF()) {
|
||||
std::string_view specifier;
|
||||
if (parser.consumeSpecifier(specifier)) {
|
||||
parser.applySpecifier(builder, specifier);
|
||||
}
|
||||
|
||||
auto& parameter = parameters.parameter(parameters.tell());
|
||||
parameter.format(builder, parameter.value);
|
||||
parameters.ignore();
|
||||
|
||||
builder.resetSpecifiers();
|
||||
}
|
||||
|
||||
if (!parser.isEOF()) {
|
||||
variadicFormatImpl(builder, parser, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
void variadicFormat(std::stringstream& stream, std::string_view format, TypeErasedParameters& parameters)
|
||||
{
|
||||
Builder builder { stream };
|
||||
Parser parser { format, parameters.size() };
|
||||
|
||||
variadicFormatImpl(builder, parser, parameters);
|
||||
}
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
void prettyVariadicFormat(Type type, bool bold, std::string_view format, TypeErasedParameters& parameters)
|
||||
{
|
||||
std::stringstream stream;
|
||||
|
||||
if (type != Type::None || bold) {
|
||||
stream << "\033[";
|
||||
switch (type) {
|
||||
case Type::Info:
|
||||
stream << "34";
|
||||
break;
|
||||
case Type::Warn:
|
||||
stream << "33";
|
||||
break;
|
||||
case Type::Danger:
|
||||
stream << "31";
|
||||
break;
|
||||
case Type::Success:
|
||||
stream << "32";
|
||||
break;
|
||||
case Type::Comment:
|
||||
stream << "37";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
||||
if (bold) {
|
||||
stream << ";1";
|
||||
}
|
||||
stream << "m";
|
||||
}
|
||||
|
||||
variadicFormat(stream, format, parameters);
|
||||
|
||||
if (type != Type::None || bold) {
|
||||
stream << "\033[0m";
|
||||
}
|
||||
|
||||
printf("%s\n", stream.str().c_str());
|
||||
}
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
Dbg::Dbg(Type type, bool bold)
|
||||
: m_type(type)
|
||||
, m_bold(bold)
|
||||
, m_stream()
|
||||
, m_builder(m_stream)
|
||||
{
|
||||
if (type != Type::None || m_bold) {
|
||||
m_stream << "\033[";
|
||||
switch (type) {
|
||||
case Type::Info:
|
||||
m_stream << "34";
|
||||
break;
|
||||
case Type::Warn:
|
||||
m_stream << "33";
|
||||
break;
|
||||
case Type::Danger:
|
||||
m_stream << "31";
|
||||
break;
|
||||
case Type::Success:
|
||||
m_stream << "32";
|
||||
break;
|
||||
case Type::Comment:
|
||||
m_stream << "37";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
||||
if (m_bold) {
|
||||
m_stream << ";1";
|
||||
}
|
||||
m_stream << "m";
|
||||
}
|
||||
}
|
||||
|
||||
Dbg::~Dbg()
|
||||
{
|
||||
if (m_type != Type::None || m_bold) {
|
||||
m_stream << "\033[0m";
|
||||
}
|
||||
|
||||
printf("%s", m_stream.str().c_str());
|
||||
}
|
||||
|
||||
Dbg dbg()
|
||||
{
|
||||
return Dbg(Type::None, false);
|
||||
}
|
||||
|
||||
Dbg dbgb()
|
||||
{
|
||||
return Dbg(Type::None, true);
|
||||
}
|
||||
|
||||
Dbg info()
|
||||
{
|
||||
return Dbg(Type::Info, false);
|
||||
}
|
||||
|
||||
Dbg infob()
|
||||
{
|
||||
return Dbg(Type::Info, true);
|
||||
}
|
||||
|
||||
Dbg warn()
|
||||
{
|
||||
return Dbg(Type::Warn, false);
|
||||
}
|
||||
|
||||
Dbg warnb()
|
||||
{
|
||||
return Dbg(Type::Warn, true);
|
||||
}
|
||||
|
||||
Dbg danger()
|
||||
{
|
||||
return Dbg(Type::Danger, false);
|
||||
}
|
||||
|
||||
Dbg dangerb()
|
||||
{
|
||||
return Dbg(Type::Danger, true);
|
||||
}
|
||||
|
||||
Dbg success()
|
||||
{
|
||||
return Dbg(Type::Success, false);
|
||||
}
|
||||
|
||||
Dbg successb()
|
||||
{
|
||||
return Dbg(Type::Success, true);
|
||||
}
|
||||
|
||||
Dbg comment()
|
||||
{
|
||||
return Dbg(Type::Comment, false);
|
||||
}
|
||||
|
||||
Dbg commentb()
|
||||
{
|
||||
return Dbg(Type::Comment, true);
|
||||
}
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
Str::Str(std::string& fill)
|
||||
: m_fill(fill)
|
||||
, m_stream()
|
||||
, m_builder(m_stream)
|
||||
{
|
||||
}
|
||||
|
||||
Str::~Str()
|
||||
{
|
||||
m_fill = m_stream.str();
|
||||
}
|
||||
|
||||
Str str(std::string& fill)
|
||||
{
|
||||
return Str(fill);
|
||||
}
|
||||
|
||||
} // namespace Util::Format
|
||||
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Riyyi
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef UTIL_FORMAT_FORMAT_H
|
||||
#define UTIL_FORMAT_FORMAT_H
|
||||
|
||||
#include <cstddef> // size_t
|
||||
#include <sstream> // stringstream
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "util/format/builder.h"
|
||||
#include "util/format/parser.h"
|
||||
#include "util/format/toformat.h"
|
||||
|
||||
namespace Util::Format {
|
||||
|
||||
struct Parameter {
|
||||
const void* value;
|
||||
void (*format)(Builder& builder, const void* value);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
void formatParameterValue(Builder& builder, const void* value)
|
||||
{
|
||||
format(builder, *static_cast<const T*>(value));
|
||||
}
|
||||
|
||||
class TypeErasedParameters {
|
||||
public:
|
||||
const Parameter parameter(size_t index) { return m_parameters[index]; }
|
||||
|
||||
size_t tell() const { return m_index; }
|
||||
size_t size() const { return m_parameters.size(); }
|
||||
bool isEOF() const { return m_index >= m_parameters.size(); }
|
||||
void ignore() { m_index++; }
|
||||
|
||||
protected:
|
||||
size_t m_index { 0 };
|
||||
std::vector<Parameter> m_parameters;
|
||||
};
|
||||
|
||||
template<typename... Parameters>
|
||||
class VariadicParameters final : public TypeErasedParameters {
|
||||
public:
|
||||
VariadicParameters(const Parameters&... parameters)
|
||||
{
|
||||
m_parameters = { { ¶meters, formatParameterValue<Parameters> }... };
|
||||
}
|
||||
};
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
void variadicFormatImpl(Builder& builder, Parser& parser, TypeErasedParameters& parameters);
|
||||
void variadicFormat(std::stringstream& stream, std::string_view format, TypeErasedParameters& parameters);
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
enum class Type {
|
||||
None, // Foreground
|
||||
Info, // Blue
|
||||
Warn, // Yellow
|
||||
Danger, // Red
|
||||
Success, // Green
|
||||
Comment, // White
|
||||
};
|
||||
|
||||
void prettyVariadicFormat(Type type, bool bold, std::string_view format, TypeErasedParameters& parameters);
|
||||
|
||||
#define FORMAT_FUNCTION(name, type, bold) \
|
||||
template<size_t N, typename... Parameters> \
|
||||
void name(const char(&format)[N] = "", const Parameters&... parameters) \
|
||||
{ \
|
||||
VariadicParameters variadicParameters { parameters... }; \
|
||||
prettyVariadicFormat(Type::type, bold, { format, N - 1 }, variadicParameters); \
|
||||
}
|
||||
|
||||
FORMAT_FUNCTION(dbgln, None, false);
|
||||
FORMAT_FUNCTION(dbgbln, None, true);
|
||||
FORMAT_FUNCTION(infoln, Info, false);
|
||||
FORMAT_FUNCTION(infobln, Info, true);
|
||||
FORMAT_FUNCTION(warnln, Warn, false);
|
||||
FORMAT_FUNCTION(warnbln, Warn, true);
|
||||
FORMAT_FUNCTION(dangerln, Danger, false);
|
||||
FORMAT_FUNCTION(dangerbln, Danger, true);
|
||||
FORMAT_FUNCTION(successln, Success, false);
|
||||
FORMAT_FUNCTION(successbln, Success, true);
|
||||
FORMAT_FUNCTION(commentln, Comment, false);
|
||||
FORMAT_FUNCTION(commentbln, Comment, true);
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
class Dbg {
|
||||
public:
|
||||
Dbg(Type type, bool bold);
|
||||
virtual ~Dbg();
|
||||
|
||||
Builder& builder() { return m_builder; }
|
||||
|
||||
private:
|
||||
Type m_type;
|
||||
bool m_bold;
|
||||
|
||||
std::stringstream m_stream;
|
||||
Builder m_builder;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
const Dbg& operator<<(const Dbg& debug, const T& value)
|
||||
{
|
||||
format(const_cast<Dbg&>(debug).builder(), value);
|
||||
return debug;
|
||||
}
|
||||
|
||||
Dbg dbg();
|
||||
Dbg dbgb();
|
||||
Dbg info();
|
||||
Dbg infob();
|
||||
Dbg warn();
|
||||
Dbg warnb();
|
||||
Dbg danger();
|
||||
Dbg dangerb();
|
||||
Dbg success();
|
||||
Dbg successb();
|
||||
Dbg comment();
|
||||
Dbg commentb();
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
template<typename... Parameters>
|
||||
void strln(std::string& fill, std::string_view format, const Parameters&... parameters)
|
||||
{
|
||||
std::stringstream stream;
|
||||
VariadicParameters variadicParameters { parameters... };
|
||||
variadicFormat(stream, format, variadicParameters);
|
||||
fill = stream.str();
|
||||
}
|
||||
|
||||
class Str {
|
||||
public:
|
||||
Str(std::string& fill);
|
||||
virtual ~Str();
|
||||
|
||||
Builder& builder() { return m_builder; }
|
||||
|
||||
private:
|
||||
std::string& m_fill;
|
||||
std::stringstream m_stream;
|
||||
Builder m_builder;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
const Str& operator<<(const Str& string, const T& value)
|
||||
{
|
||||
format(const_cast<Str&>(string).builder(), value);
|
||||
return string;
|
||||
}
|
||||
|
||||
Str str(std::string& fill);
|
||||
|
||||
} // namespace Util::Format
|
||||
|
||||
using Util::Format::commentbln;
|
||||
using Util::Format::commentln;
|
||||
using Util::Format::dangerbln;
|
||||
using Util::Format::dangerln;
|
||||
using Util::Format::dbgbln;
|
||||
using Util::Format::dbgln;
|
||||
using Util::Format::infobln;
|
||||
using Util::Format::infoln;
|
||||
using Util::Format::successbln;
|
||||
using Util::Format::successln;
|
||||
using Util::Format::warnbln;
|
||||
using Util::Format::warnln;
|
||||
|
||||
using Util::Format::comment;
|
||||
using Util::Format::commentb;
|
||||
using Util::Format::danger;
|
||||
using Util::Format::dangerb;
|
||||
using Util::Format::dbg;
|
||||
using Util::Format::dbgb;
|
||||
using Util::Format::info;
|
||||
using Util::Format::infob;
|
||||
using Util::Format::success;
|
||||
using Util::Format::successb;
|
||||
using Util::Format::warn;
|
||||
using Util::Format::warnb;
|
||||
|
||||
using Util::Format::str;
|
||||
using Util::Format::strln;
|
||||
|
||||
using FormatBuilder = Util::Format::Builder;
|
||||
|
||||
#endif // UTIL_FORMAT_FORMAT_H
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Riyyi
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <algorithm> // replace
|
||||
#include <cassert> // assert
|
||||
#include <cstddef> // size_t
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "util/format/builder.h"
|
||||
#include "util/format/parser.h"
|
||||
|
||||
namespace Util::Format {
|
||||
|
||||
Parser::Parser(std::string_view format, size_t parameterCount)
|
||||
: GenericLexer(format)
|
||||
, m_parameterCount(parameterCount)
|
||||
{
|
||||
checkFormatParameterConsistency();
|
||||
}
|
||||
|
||||
Parser::~Parser()
|
||||
{
|
||||
}
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
void Parser::checkFormatParameterConsistency()
|
||||
{
|
||||
size_t length = m_input.length();
|
||||
|
||||
// Format string does not reference all passed parameters
|
||||
assert(length >= m_parameterCount * 2);
|
||||
|
||||
size_t braceOpen = 0;
|
||||
size_t braceClose = 0;
|
||||
while (!isEOF()) {
|
||||
char peek0 = peek();
|
||||
char peek1 = peek(1);
|
||||
|
||||
if (peek0 == '{' && peek1 == '{') {
|
||||
ignore(2);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (peek0 == '}' && peek1 == '}') {
|
||||
ignore(2);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (peek0 == '{') {
|
||||
braceOpen++;
|
||||
|
||||
// Only empty references {} or specified references {:} are valid
|
||||
assert(peek1 == '}' || peek1 == ':');
|
||||
}
|
||||
if (peek0 == '}') {
|
||||
braceClose++;
|
||||
}
|
||||
|
||||
ignore();
|
||||
}
|
||||
m_index = 0;
|
||||
|
||||
// Extra open braces in format string
|
||||
assert(!(braceOpen < braceClose));
|
||||
|
||||
// Extra closing braces in format string
|
||||
assert(!(braceOpen > braceClose));
|
||||
|
||||
// Format string does not reference all passed parameters
|
||||
assert(!(braceOpen < m_parameterCount));
|
||||
|
||||
// Format string references nonexistent parameter
|
||||
assert(!(braceOpen > m_parameterCount));
|
||||
}
|
||||
|
||||
std::string_view Parser::consumeLiteral()
|
||||
{
|
||||
const auto begin = tell();
|
||||
|
||||
while (!isEOF()) {
|
||||
char peek0 = peek();
|
||||
char peek1 = peek(1);
|
||||
|
||||
if (peek0 == '{' && peek1 == '{') {
|
||||
ignore(2);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (peek0 == '}' && peek1 == '}') {
|
||||
ignore(2);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get literal before the specifier {}
|
||||
if (peek0 == '{' || peek0 == '}') {
|
||||
return m_input.substr(begin, tell() - begin);
|
||||
}
|
||||
|
||||
ignore();
|
||||
}
|
||||
|
||||
return m_input.substr(begin);
|
||||
}
|
||||
|
||||
bool Parser::consumeSpecifier(std::string_view& specifier)
|
||||
{
|
||||
if (!consumeSpecific('{')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!consumeSpecific(':')) {
|
||||
assert(consumeSpecific('}'));
|
||||
specifier = "";
|
||||
}
|
||||
else {
|
||||
const auto begin = tell();
|
||||
|
||||
// Go until AFTER the closing brace
|
||||
while (peek(-1) != '}') {
|
||||
consume();
|
||||
}
|
||||
|
||||
specifier = m_input.substr(begin, tell() - begin - 1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Parser::applySpecifier(Builder& builder, std::string_view specifier)
|
||||
{
|
||||
if (specifier[0] == '.') {
|
||||
size_t value = 0;
|
||||
|
||||
for (size_t i = 1; i < specifier.length(); ++i) {
|
||||
if (specifier[i] < '0' || specifier[i] > '9') {
|
||||
return;
|
||||
}
|
||||
value *= 10;
|
||||
value += specifier[i] - '0'; // Subtract ASCII 48 to get the number
|
||||
}
|
||||
|
||||
builder.setPrecision(value);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Util::Format
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Riyyi
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef UTIL_FORMAT_PARSER_H
|
||||
#define UTIL_FORMAT_PARSER_H
|
||||
|
||||
#include <cstddef> // size_t
|
||||
#include <string_view>
|
||||
|
||||
#include "util/genericlexer.h"
|
||||
|
||||
namespace Util::Format {
|
||||
|
||||
class Builder;
|
||||
|
||||
class Parser final : public GenericLexer {
|
||||
public:
|
||||
Parser(std::string_view format, size_t parameterCount);
|
||||
virtual ~Parser();
|
||||
|
||||
void checkFormatParameterConsistency();
|
||||
|
||||
std::string_view consumeLiteral();
|
||||
bool consumeSpecifier(std::string_view& specifier);
|
||||
|
||||
void applySpecifier(Builder& builder, std::string_view specifier);
|
||||
|
||||
private:
|
||||
size_t m_parameterCount { 0 };
|
||||
};
|
||||
|
||||
} // namespace Util::Format
|
||||
|
||||
#endif // UTIL_FORMAT_PARSER_H
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Riyyi
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef UTIL_TO_FORMAT_H
|
||||
#define UTIL_TO_FORMAT_H
|
||||
|
||||
#include <cstddef> // nullptr_t
|
||||
#include <cstdint> // int64_t
|
||||
#include <cstring> // strlen
|
||||
#include <iterator> // next
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <utility> // forward
|
||||
#include <vector>
|
||||
|
||||
#include "util/meta/odr.h"
|
||||
|
||||
namespace Util::Format {
|
||||
|
||||
namespace Detail {
|
||||
|
||||
template<typename FormatBuilder>
|
||||
void format(FormatBuilder& builder, std::nullptr_t)
|
||||
{
|
||||
builder.putString("(nil)");
|
||||
}
|
||||
|
||||
template<typename FormatBuilder, typename T>
|
||||
void format(FormatBuilder& builder, T* pointer)
|
||||
{
|
||||
builder.putPointer(static_cast<const void*>(pointer));
|
||||
}
|
||||
|
||||
template<typename FormatBuilder>
|
||||
void format(FormatBuilder& builder, bool boolean)
|
||||
{
|
||||
builder.putString(boolean ? "true" : "false");
|
||||
}
|
||||
|
||||
template<typename FormatBuilder>
|
||||
void format(FormatBuilder& builder, int32_t number) // int
|
||||
{
|
||||
builder.putI32(number);
|
||||
}
|
||||
|
||||
template<typename FormatBuilder>
|
||||
void format(FormatBuilder& builder, uint32_t number) // unsigned int
|
||||
{
|
||||
builder.putU32(number);
|
||||
}
|
||||
|
||||
template<typename FormatBuilder>
|
||||
void format(FormatBuilder& builder, int64_t number) // long int
|
||||
{
|
||||
builder.putI64(number);
|
||||
}
|
||||
|
||||
template<typename FormatBuilder> // uint64_t
|
||||
void format(FormatBuilder& builder, size_t number) // long unsigned int
|
||||
{
|
||||
builder.putU64(number);
|
||||
}
|
||||
|
||||
template<typename FormatBuilder>
|
||||
void format(FormatBuilder& builder, float number)
|
||||
{
|
||||
builder.putF32(number);
|
||||
}
|
||||
|
||||
template<typename FormatBuilder>
|
||||
void format(FormatBuilder& builder, double number)
|
||||
{
|
||||
builder.putF64(number);
|
||||
}
|
||||
|
||||
template<typename FormatBuilder>
|
||||
void format(FormatBuilder& builder, char character)
|
||||
{
|
||||
builder.putCharacter(character);
|
||||
}
|
||||
|
||||
template<typename FormatBuilder>
|
||||
void format(FormatBuilder& builder, const char* string)
|
||||
{
|
||||
builder.putString({ string, strlen(string) });
|
||||
}
|
||||
|
||||
template<typename FormatBuilder>
|
||||
void format(FormatBuilder& builder, const std::string& string)
|
||||
{
|
||||
builder.putString(string);
|
||||
}
|
||||
|
||||
template<typename FormatBuilder>
|
||||
void format(FormatBuilder& builder, std::string_view string)
|
||||
{
|
||||
builder.putString(string);
|
||||
}
|
||||
|
||||
template<typename FormatBuilder, typename T>
|
||||
void format(FormatBuilder& builder, const std::vector<T>& array)
|
||||
{
|
||||
builder.putString("{\n");
|
||||
for (auto it = array.cbegin(); it != array.cend(); ++it) {
|
||||
builder.putString(" ");
|
||||
format(builder, *it);
|
||||
|
||||
// Add comma, except after the last element
|
||||
if (it != std::prev(array.end(), 1)) {
|
||||
builder.putCharacter(',');
|
||||
}
|
||||
builder.putCharacter('\n');
|
||||
}
|
||||
builder.putCharacter('}');
|
||||
}
|
||||
|
||||
#define FORMAT_MAP \
|
||||
builder.putString("{\n"); \
|
||||
auto last = map.end(); \
|
||||
for (auto it = map.begin(); it != last; ++it) { \
|
||||
builder.putString(R"( ")"); \
|
||||
format(builder, it->first); \
|
||||
builder.putString(R"(": )"); \
|
||||
format(builder, it->second); \
|
||||
\
|
||||
/* Add comma, except after the last element */ \
|
||||
if (std::next(it) != last) { \
|
||||
builder.putCharacter(','); \
|
||||
} \
|
||||
\
|
||||
builder.putCharacter('\n'); \
|
||||
} \
|
||||
builder.putCharacter('}');
|
||||
|
||||
template<typename FormatBuilder, typename K, typename V>
|
||||
void format(FormatBuilder& builder, const std::map<K, V>& map)
|
||||
{
|
||||
FORMAT_MAP;
|
||||
}
|
||||
|
||||
template<typename FormatBuilder, typename K, typename V>
|
||||
void format(FormatBuilder& builder, const std::unordered_map<K, V>& map)
|
||||
{
|
||||
FORMAT_MAP;
|
||||
}
|
||||
|
||||
struct formatFunction {
|
||||
template<typename FormatBuilder, typename T>
|
||||
auto operator()(FormatBuilder& builder, T&& value) const
|
||||
{
|
||||
return format(builder, std::forward<T>(value));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Detail
|
||||
|
||||
namespace {
|
||||
constexpr const auto& format = Util::Detail::staticConst<Detail::formatFunction>; // NOLINT(misc-definitions-in-headers,clang-diagnostic-unused-variable)
|
||||
} // namespace
|
||||
|
||||
} // namespace Util::Format
|
||||
|
||||
#endif // UTIL_TO_FORMAT_H
|
||||
Reference in New Issue
Block a user