mirror of
https://github.com/silverqx/TinyORM.git
synced 2026-05-06 16:49:19 -05:00
tom removed tabulate/table.hpp from headers 🙌
This commit can't be divided because everything is related. The tabulate/table.hpp #include is too expensive to compile and it was included everywhere and it also was exposed to the client code. Whole code was refactored to move this header into the .cpp files. The InteractsWithIO was also hidden in similar way, forward declared in the TomApplication, used std::unique_ptr<>, and #include moved to the .cpp file. The InteractsWithIO is now private because it's fully initialized after the TomApplication::run() method call and it would need more work. The next change is in the InteractsWithIO::table() method, the values passed to it aren't tabulate::Table::Row_t anymore (for header and body rows), but are are own types. The reason for this was to get rid of the Tabulate version number checks and based on these version checks define the correct Tabulate row/cell types. Now, when this type will change in the future it will not cause any problems. These our new table row/cell values/types are internally converted to the tabulate::Table::Row_t type. The last change was to extract the formatting code for the Tabulate table into the StatusCommand. These formatting code/rules were directly defined in the InteractsWithIO::table() method and that was a bug. I have added the FormatTableCallback that can be passed to this InteractsWithIO::table() method that allows to format Tabulate table. - updated all InteractsWithIO method calls through the io() getter - moved the Application::~Application() to the .cpp file (non-inline) - exposed the isAnsiW/Output() methods (made public) - removed the InteractsWithIO() constructor and initialize(const QCommandLineParser &) methods - moved the Tabulate table formatting logic to the StatusCommand - removed Tabulate version checks thx to our own types
This commit is contained in:
@@ -15,7 +15,6 @@ TINY_SYSTEM_HEADER
|
||||
#include "tom/config.hpp" // IWYU pragma: keep
|
||||
|
||||
#include "tom/concerns/guesscommandname.hpp"
|
||||
#include "tom/concerns/interactswithio.hpp"
|
||||
#include "tom/tomtypes.hpp"
|
||||
#include "tom/types/commandlineoption.hpp"
|
||||
|
||||
@@ -39,6 +38,7 @@ namespace Commands
|
||||
namespace Concerns
|
||||
{
|
||||
class CallsCommands;
|
||||
class InteractsWithIO;
|
||||
class PrintsOptions;
|
||||
} // namespace Concerns
|
||||
|
||||
@@ -48,8 +48,7 @@ namespace Concerns
|
||||
class Seeder;
|
||||
|
||||
/*! Tom application. */
|
||||
class TINYORM_EXPORT Application : public Concerns::InteractsWithIO,
|
||||
public Concerns::GuessCommandName
|
||||
class TINYORM_EXPORT Application : public Concerns::GuessCommandName
|
||||
{
|
||||
Q_DISABLE_COPY_MOVE(Application)
|
||||
|
||||
@@ -67,7 +66,7 @@ namespace Concerns
|
||||
friend Concerns::PrintsOptions;
|
||||
// To access initializeParser() and createCommand()
|
||||
friend Concerns::CallsCommands;
|
||||
// To access createCommandsVector(), errorWall(), exitApplication()
|
||||
// To access createCommandsVector(), io(), exitApplication()
|
||||
friend Concerns::GuessCommandName;
|
||||
|
||||
/*! Alias for the ConnectionResolverInterface. */
|
||||
@@ -83,7 +82,7 @@ namespace Concerns
|
||||
std::vector<std::shared_ptr<Migration>> migrations = {},
|
||||
std::vector<std::shared_ptr<Seeder>> seeders = {});
|
||||
/*! Virtual destructor. */
|
||||
~Application() override = default;
|
||||
~Application() override;
|
||||
|
||||
/*! Instantiate/initialize all migration classes. */
|
||||
template<typename ...Migrations>
|
||||
@@ -250,6 +249,8 @@ namespace Concerns
|
||||
|
||||
/*! Get database connection resolver. */
|
||||
std::shared_ptr<ConnectionResolverInterface> connectionResolver() const noexcept;
|
||||
/*! Get the IO aka InteractsWithIO (methods for the console output/input). */
|
||||
const Concerns::InteractsWithIO &io() const noexcept;
|
||||
|
||||
/*! Throw if no connection configuration is registered. */
|
||||
void throwIfNoConnectionConfig() const;
|
||||
@@ -302,6 +303,9 @@ namespace Concerns
|
||||
/*! Application options (more info at the cpp file beginning). */
|
||||
QList<CommandLineOption> m_options;
|
||||
|
||||
/*! IO aka InteractsWithIO instance (methods for the console output/input). */
|
||||
std::unique_ptr<Concerns::InteractsWithIO> m_io;
|
||||
|
||||
/* Auto tests helpers */
|
||||
#ifdef TINYTOM_TESTS_CODE
|
||||
public:
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Commands::Migrations
|
||||
{
|
||||
Q_DISABLE_COPY_MOVE(StatusCommand)
|
||||
|
||||
/*! Alias for the tabulate row. */
|
||||
/*! Alias for the table row. */
|
||||
using TableRow = InteractsWithIO::TableRow;
|
||||
|
||||
public:
|
||||
|
||||
@@ -7,30 +7,20 @@ TINY_SYSTEM_HEADER
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
#include <tabulate/table.hpp>
|
||||
/* This header exists from the tabulate v1.4.0, it has been added in the middle of v1.3.0
|
||||
and didn't exist at the v1.3.0 release. */
|
||||
#if __has_include(<tabulate/tabulate.hpp>)
|
||||
# include <tabulate/tabulate.hpp>
|
||||
#endif
|
||||
#include <iostream>
|
||||
|
||||
#include <orm/macros/commonnamespace.hpp>
|
||||
#include <orm/macros/export.hpp>
|
||||
|
||||
class QCommandLineParser;
|
||||
|
||||
namespace tabulate
|
||||
{
|
||||
class Table;
|
||||
}
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
// Defines to detect the tabulate version
|
||||
#ifndef TABULATE_VERSION_MAJOR
|
||||
# define TABULATE_VERSION_MAJOR 0
|
||||
# define TABULATE_VERSION_MINOR 0
|
||||
# define TABULATE_VERSION_PATCH 0
|
||||
#endif
|
||||
|
||||
#define TINY_TABULATE_VERSION QT_VERSION_CHECK(TABULATE_VERSION_MAJOR, \
|
||||
TABULATE_VERSION_MINOR, \
|
||||
TABULATE_VERSION_PATCH)
|
||||
namespace Tom
|
||||
{
|
||||
class Application;
|
||||
@@ -47,29 +37,10 @@ namespace Concerns
|
||||
// To access private constructor and errorWallInternal() (used by logException())
|
||||
friend Tom::Application;
|
||||
|
||||
/*! Constructor (used by TomApplication::logException()). */
|
||||
explicit InteractsWithIO(bool noAnsi);
|
||||
|
||||
public:
|
||||
#if TINY_TABULATE_VERSION >= QT_VERSION_CHECK(1, 5, 0)
|
||||
/*! Alias for the tabulate cell. */
|
||||
using TableCell = tabulate::Table::Row_t::value_type;
|
||||
#elif TINY_TABULATE_VERSION == QT_VERSION_CHECK(1, 4, 0)
|
||||
/*! Alias for the tabulate cell. */
|
||||
using TableCell = std::variant<std::string, const char *, tabulate::Table>;
|
||||
#else
|
||||
/*! Alias for the tabulate cell. */
|
||||
using TableCell = std::variant<std::string, tabulate::Table>;
|
||||
#endif
|
||||
|
||||
#if TINY_TABULATE_VERSION >= QT_VERSION_CHECK(1, 5, 0)
|
||||
/*! Alias for the tabulate row. */
|
||||
using TableRow = tabulate::Table::Row_t;
|
||||
// Check the type because we have no control over this type
|
||||
static_assert (std::is_same_v<TableRow, std::vector<TableCell>>,
|
||||
"The InteractsWithIO::TableRow must be the std::vector<TableCell> type.");
|
||||
#else
|
||||
/*! Alias for the tabulate row. */
|
||||
using TableRow = std::vector<TableCell>;
|
||||
#endif
|
||||
|
||||
/*! Constructor. */
|
||||
explicit InteractsWithIO(const QCommandLineParser &parser);
|
||||
/*! Virtual destructor. */
|
||||
@@ -153,9 +124,17 @@ namespace Concerns
|
||||
const InteractsWithIO &newLineErr(quint16 count = 1,
|
||||
Verbosity verbosity = Normal) const;
|
||||
|
||||
/*! Alias for the table cell. */
|
||||
using TableCell = std::variant<std::string, const char *, std::string_view>;
|
||||
/*! Alias for the table row. */
|
||||
using TableRow = std::vector<TableCell>;
|
||||
/*! Format the tabulate table callback type. */
|
||||
using FormatTableCallback = std::function<void(tabulate::Table &,
|
||||
const InteractsWithIO &)>;
|
||||
/*! Format input to textual table. */
|
||||
const InteractsWithIO &
|
||||
table(const TableRow &header, const std::vector<TableRow> &rows,
|
||||
const FormatTableCallback &formatCallback = nullptr,
|
||||
Verbosity verbosity = Normal) const;
|
||||
|
||||
/*! Confirm a question with the user. */
|
||||
@@ -165,6 +144,12 @@ namespace Concerns
|
||||
static QString stripAnsiTags(QString string);
|
||||
|
||||
/* Getters / Setters */
|
||||
/*! Should the given output use ANSI? (ANSI is disabled without TTY). */
|
||||
bool isAnsiOutput(const std::ostream &cout = std::cout) const;
|
||||
/*! Should the given output use ANSI? (ANSI is disabled without TTY),
|
||||
wide version. */
|
||||
bool isAnsiWOutput(const std::wostream &cout = std::wcout) const;
|
||||
|
||||
/*! Run the given callable with disabled ANSI output support. */
|
||||
void withoutAnsi(const std::function<void()> &callback);
|
||||
/*! Enable ANSI support. */
|
||||
@@ -179,13 +164,6 @@ namespace Concerns
|
||||
inline InteractsWithIO &setAnsi(bool value) noexcept;
|
||||
|
||||
protected:
|
||||
/*! Default constructor (used by the TomApplication, instance is initialized
|
||||
later in the TomApplication::parseCommandLine()). */
|
||||
InteractsWithIO();
|
||||
/*! Initialize instance like the second constructor do, allows to create
|
||||
an instance in two steps. */
|
||||
void initialize(const QCommandLineParser &parser);
|
||||
|
||||
/*! Get a current verbosity level. */
|
||||
inline Verbosity verbosity() const noexcept;
|
||||
/*! Is quiet verbosity level? */
|
||||
@@ -200,9 +178,6 @@ namespace Concerns
|
||||
inline bool isDebugVerbosity() const noexcept;
|
||||
|
||||
private:
|
||||
/*! Constructor (used by TomApplication::logException()). */
|
||||
explicit InteractsWithIO(bool noAnsi);
|
||||
|
||||
/*! Replace text tags with ANSI sequences. */
|
||||
static QString parseOutput(QString string, bool isAnsi = true);
|
||||
|
||||
@@ -219,35 +194,18 @@ namespace Concerns
|
||||
/*! Determine whether discard output by the current and the given verbosity. */
|
||||
bool dontOutput(Verbosity verbosity) const;
|
||||
|
||||
/*! Should the given output use ANSI? (ANSI is disabled without TTY). */
|
||||
bool isAnsiOutput(const std::ostream &cout = std::cout) const;
|
||||
/*! Should the given output use ANSI? (ANSI is disabled without TTY),
|
||||
wide version. */
|
||||
bool isAnsiWOutput(const std::wostream &cout = std::wcout) const;
|
||||
|
||||
/*! Write a string as error output (red box with a white text). */
|
||||
QString errorWallInternal(const QString &string) const;
|
||||
/*! Compute a reserve value for the QStringList lines. */
|
||||
static QStringList::size_type
|
||||
computeReserveForErrorWall(const QStringList &splitted, int maxLineWidth);
|
||||
|
||||
/*! Alias for the tabulate color. */
|
||||
using Color = tabulate::Color;
|
||||
/*! Default tabulate table colors. */
|
||||
struct TableColors
|
||||
{
|
||||
Color green = Color::green;
|
||||
Color red = Color::red;
|
||||
};
|
||||
/*! Initialize tabulate table colors by supported ANSI. */
|
||||
TableColors initializeTableColors() const;
|
||||
|
||||
/*! Is the input interactive? (don't ask any interactive question if false) */
|
||||
bool m_interactive = true;
|
||||
/*! Current application verbosity (defined by passed command-line options). */
|
||||
Verbosity m_verbosity = Normal;
|
||||
/*! Current application ANSI passed by command-line option (nullopt is auto). */
|
||||
std::optional<bool> m_ansi = std::nullopt;
|
||||
std::optional<bool> m_ansi;
|
||||
/*! Describes features of the current terminal. */
|
||||
std::unique_ptr<Terminal> m_terminal;
|
||||
};
|
||||
|
||||
+30
-15
@@ -50,6 +50,7 @@
|
||||
#include "tom/commands/migrations/rollbackcommand.hpp"
|
||||
#include "tom/commands/migrations/statuscommand.hpp"
|
||||
#include "tom/commands/migrations/uninstallcommand.hpp"
|
||||
#include "tom/concerns/interactswithio.hpp"
|
||||
#include "tom/exceptions/runtimeerror.hpp"
|
||||
#include "tom/migrationrepository.hpp"
|
||||
#include "tom/migrator.hpp"
|
||||
@@ -195,6 +196,7 @@ Application::Application(int &argc, char *argv[], std::shared_ptr<DatabaseManage
|
||||
, m_seedersPath(initializePath(TINY_STRINGIFY(TINYTOM_SEEDERS_DIR)))
|
||||
, m_migrations(std::move(migrations))
|
||||
, m_seeders(std::move(seeders))
|
||||
, m_io(nullptr) // Instantiated after the command-line is parsed
|
||||
{
|
||||
// Enable UTF-8 encoding and VT100 support
|
||||
Terminal::initialize();
|
||||
@@ -208,6 +210,9 @@ Application::Application(int &argc, char *argv[], std::shared_ptr<DatabaseManage
|
||||
initializeParser(m_parser);
|
||||
}
|
||||
|
||||
// Needed by a unique_ptr()
|
||||
Application::~Application() = default;
|
||||
|
||||
int Application::run()
|
||||
{
|
||||
// Throw if no database connection configuration is registered
|
||||
@@ -352,10 +357,10 @@ void Application::parseCommandLine()
|
||||
|
||||
initializeEnvironment();
|
||||
|
||||
/* Command-line arguments are parsed now, so the InteractsWithIO() base class can be
|
||||
initialized. Nothing bad to divide it into two steps as output to the console
|
||||
is not needed until here. */
|
||||
Concerns::InteractsWithIO::initialize(m_parser);
|
||||
/* Command-line arguments are parsed now, so the InteractsWithIO() class can be
|
||||
instantiated. There's nothing wrong with being so late as output to the console
|
||||
is not needed until now. */
|
||||
m_io = std::make_unique<Concerns::InteractsWithIO>(m_parser);
|
||||
|
||||
if (m_parser.isSet(nointeraction))
|
||||
m_interactive = false;
|
||||
@@ -423,7 +428,7 @@ void Application::handleEmptyCommandName(const QString &name,
|
||||
|
||||
T_UNLIKELY
|
||||
case ShowErrorWall:
|
||||
errorWall(u"Command '%1' is not defined."_s.arg(name));
|
||||
io().errorWall(u"Command '%1' is not defined."_s.arg(name));
|
||||
|
||||
exitApplication(EXIT_FAILURE);
|
||||
|
||||
@@ -448,23 +453,23 @@ void Application::showVersion() const
|
||||
|
||||
void Application::printVersion() const
|
||||
{
|
||||
note(u"TinyORM "_s, false);
|
||||
io().note(u"TinyORM "_s, false);
|
||||
|
||||
info(TINYORM_VERSION_STR);
|
||||
io().info(TINYORM_VERSION_STR);
|
||||
}
|
||||
|
||||
void Application::printFullVersions() const
|
||||
{
|
||||
note(u"tom "_s, false);
|
||||
info(TINYTOM_VERSION_STR);
|
||||
io().note(u"tom "_s, false);
|
||||
io().info(TINYTOM_VERSION_STR);
|
||||
|
||||
for (const auto versionsSubsection = createVersionsSubsection();
|
||||
const auto &[subsectionName, abouts] : versionsSubsection
|
||||
) {
|
||||
// Subsection name is optional
|
||||
if (subsectionName) {
|
||||
newLine();
|
||||
comment(*subsectionName);
|
||||
io().newLine();
|
||||
io().comment(*subsectionName);
|
||||
}
|
||||
|
||||
/*! Alias for the std::map<QString, AboutValue>. */
|
||||
@@ -473,14 +478,14 @@ void Application::printFullVersions() const
|
||||
Q_ASSERT(std::holds_alternative<AboutItemsType>(abouts));
|
||||
|
||||
for (const auto &[name, about] : std::get<AboutItemsType>(abouts)) {
|
||||
note(NOSPACE.arg(name).arg(SPACE), false);
|
||||
info(about.value, false);
|
||||
io().note(NOSPACE.arg(name).arg(SPACE), false);
|
||||
io().info(about.value, false);
|
||||
|
||||
// Item components
|
||||
if (const auto &components = about.components; components)
|
||||
muted(SPACE + PARENTH_ONE.arg(components->join(COMMA)), false);
|
||||
io().muted(SPACE + PARENTH_ONE.arg(components->join(COMMA)), false);
|
||||
|
||||
newLine();
|
||||
io().newLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -854,6 +859,16 @@ Application::connectionResolver() const noexcept
|
||||
return std::dynamic_pointer_cast<ConnectionResolverInterface>(m_db);
|
||||
}
|
||||
|
||||
const Concerns::InteractsWithIO &Application::io() const noexcept
|
||||
{
|
||||
/* This is our internal thing so the Q_ASSERT() is enough. I tried to make it public
|
||||
because the InteractsWithIO() class contains useful methods, but it's not fully
|
||||
ready until the TomApplication::run() method call and that's a problem.
|
||||
And that's why I made it protected. 🫤 */
|
||||
Q_ASSERT(m_io);
|
||||
return *m_io;
|
||||
}
|
||||
|
||||
void Application::throwIfNoConnectionConfig() const
|
||||
{
|
||||
// Nothing to do, some database connection configuration/s are already registered
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include <QCommandLineParser>
|
||||
|
||||
#include <tabulate/table.hpp>
|
||||
|
||||
#include <orm/db.hpp>
|
||||
|
||||
#include "tom/migrationrepository.hpp"
|
||||
@@ -9,6 +11,12 @@
|
||||
|
||||
#include <range/v3/view/remove_if.hpp>
|
||||
|
||||
/* This header exists from the tabulate v1.4.0, it has been added in the middle of v1.3.0
|
||||
and doesn't exist at the v1.3.0 release. */
|
||||
#if __has_include(<tabulate/tabulate.hpp>)
|
||||
# include <tabulate/tabulate.hpp>
|
||||
#endif
|
||||
|
||||
#ifdef TINYTOM_TESTS_CODE
|
||||
# include <range/v3/algorithm/transform.hpp>
|
||||
# include <range/v3/view/move.hpp>
|
||||
@@ -46,6 +54,67 @@ QList<CommandLineOption> StatusCommand::optionsSignature() const
|
||||
};
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
/*! Alias for the tabulate color. */
|
||||
using Color = tabulate::Color;
|
||||
/*! Default tabulate table colors. */
|
||||
struct TableColors
|
||||
{
|
||||
Color green = Color::green;
|
||||
Color red = Color::red;
|
||||
};
|
||||
|
||||
/*! Initialize tabulate table colors by supported ANSI. */
|
||||
inline TableColors initializeTableColors(const bool isAnsiOutput)
|
||||
{
|
||||
/* Even if I detect ANSI support as true, tabulate has its own detection logic,
|
||||
it only checks isatty(), it has some consequences, eg. no colors when output
|
||||
is redirected and --ansi was passed to the tom application, practically
|
||||
all logic in the isAnsiOutput() will be skipped because of this internal
|
||||
tabulate logic, not a big deal though. */
|
||||
if (isAnsiOutput)
|
||||
return {}; // Use default colors
|
||||
|
||||
// Disable ANSI colors if eg. --no-ansi (or not supported)
|
||||
return {Color::none, Color::none};
|
||||
}
|
||||
|
||||
/*! Callback function to format the tabulate table. */
|
||||
void formatStatusTable(tabulate::Table &table, const Concerns::InteractsWithIO &io)
|
||||
{
|
||||
// Initialize tabulate table colors by supported ANSI
|
||||
const auto [green, red] = initializeTableColors(io.isAnsiOutput());
|
||||
|
||||
// Format table
|
||||
// thead - green text for all columns
|
||||
table.row(0).format().font_color(green);
|
||||
|
||||
// tbody
|
||||
for (std::size_t i = 1; i < table.size() ; ++i) {
|
||||
auto &row = table.row(i);
|
||||
|
||||
// Remove all lines between rows in the tbody (leave only the first one)
|
||||
if (i > 1)
|
||||
row.format().hide_border_top();
|
||||
|
||||
// Ran? column : Yes - green, No - red
|
||||
{
|
||||
auto &cell0 = row.cell(0);
|
||||
auto &format = cell0.format();
|
||||
|
||||
if (cell0.get_text() == "Yes")
|
||||
format.font_color(green);
|
||||
else
|
||||
format.font_color(red);
|
||||
}
|
||||
|
||||
// Align the Batch column to the right (must be after the hide_border_top())
|
||||
row.cell(2).format().font_align(tabulate::FontAlign::right);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int StatusCommand::run()
|
||||
{
|
||||
Command::run();
|
||||
@@ -74,7 +143,7 @@ int StatusCommand::run()
|
||||
m_status = statusForUnitTest(std::move(migrations));
|
||||
else
|
||||
#endif
|
||||
table({"Ran?", "Migration", "Batch"}, migrations);
|
||||
table({"Ran?", "Migration", "Batch"}, migrations, formatStatusTable);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -117,6 +186,21 @@ StatusCommand::getStatusFor(const QList<QVariant> &ran,
|
||||
}
|
||||
|
||||
#ifdef TINYTOM_TESTS_CODE
|
||||
// Prepare the tabulate version
|
||||
#ifndef TABULATE_VERSION_MAJOR
|
||||
# define TABULATE_VERSION_MAJOR 0
|
||||
#endif
|
||||
#ifndef TABULATE_VERSION_MINOR
|
||||
# define TABULATE_VERSION_MINOR 0
|
||||
#endif
|
||||
#ifndef TABULATE_VERSION_PATCH
|
||||
# define TABULATE_VERSION_PATCH 0
|
||||
#endif
|
||||
|
||||
/*! Tabulate version suitable for the QT_VERSION_CHECK comparison. */
|
||||
#define TINY_TABULATE_VERSION QT_VERSION_CHECK(TABULATE_VERSION_MAJOR, \
|
||||
TABULATE_VERSION_MINOR, \
|
||||
TABULATE_VERSION_PATCH)
|
||||
namespace
|
||||
{
|
||||
#if TINY_TABULATE_VERSION >= QT_VERSION_CHECK(1, 3, 0)
|
||||
|
||||
@@ -97,7 +97,7 @@ void GuessCommandName::printAmbiguousCommands(
|
||||
})
|
||||
| ranges::to<QStringList>();
|
||||
|
||||
application().errorWall(
|
||||
application().io().errorWall(
|
||||
u"Command \"%1\" is ambiguous.\n\nDid you mean one of these?\n%2"_s
|
||||
.arg(commandName, formattedCommands.join(NEWLINE)));
|
||||
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
|
||||
#include <QCommandLineParser>
|
||||
|
||||
#include <cmath>
|
||||
#include <range/v3/range/conversion.hpp>
|
||||
#include <range/v3/view/transform.hpp>
|
||||
|
||||
#include <tabulate/table.hpp>
|
||||
|
||||
#include <orm/constants.hpp>
|
||||
#include <orm/utils/string.hpp>
|
||||
|
||||
#include "tom/terminal.hpp"
|
||||
@@ -31,6 +33,13 @@ using Tom::Constants::quiet;
|
||||
namespace Tom::Concerns
|
||||
{
|
||||
|
||||
/* private */
|
||||
|
||||
InteractsWithIO::InteractsWithIO(const bool noAnsi)
|
||||
: m_ansi(initializeNoAnsi(noAnsi))
|
||||
, m_terminal(std::make_unique<Terminal>())
|
||||
{}
|
||||
|
||||
/* public */
|
||||
|
||||
InteractsWithIO::InteractsWithIO(const QCommandLineParser &parser)
|
||||
@@ -43,28 +52,6 @@ InteractsWithIO::InteractsWithIO(const QCommandLineParser &parser)
|
||||
// Needed by a unique_ptr()
|
||||
InteractsWithIO::~InteractsWithIO() = default;
|
||||
|
||||
/* protected */
|
||||
|
||||
// Needed by a unique_ptr()
|
||||
InteractsWithIO::InteractsWithIO()
|
||||
: m_terminal(nullptr)
|
||||
{}
|
||||
|
||||
void InteractsWithIO::initialize(const QCommandLineParser &parser)
|
||||
{
|
||||
m_interactive = !parser.isSet(nointeraction);
|
||||
m_verbosity = initializeVerbosity(parser);
|
||||
m_ansi = initializeAnsi(parser);
|
||||
m_terminal = std::make_unique<Terminal>();
|
||||
}
|
||||
|
||||
/* private */
|
||||
|
||||
InteractsWithIO::InteractsWithIO(const bool noAnsi)
|
||||
: m_ansi(initializeNoAnsi(noAnsi))
|
||||
, m_terminal(std::make_unique<Terminal>())
|
||||
{}
|
||||
|
||||
/* public */
|
||||
|
||||
const InteractsWithIO &
|
||||
@@ -270,9 +257,38 @@ InteractsWithIO::newLineErr(const quint16 count, const Verbosity verbosity) cons
|
||||
return *this;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
/*! Alias for the tabulate cell. */
|
||||
using TabulateCell = Table::Row_t::value_type;
|
||||
/*! Alias for the tabulate row. */
|
||||
using TabulateRow = Table::Row_t;
|
||||
|
||||
/*! Convert our table row to a tabulate table row. */
|
||||
TabulateRow convertToTabulateRow(const InteractsWithIO::TableRow &row)
|
||||
{
|
||||
return row
|
||||
| ranges::views::transform([](const auto &cell) -> TabulateCell
|
||||
{
|
||||
if (std::holds_alternative<const char *>(cell))
|
||||
return std::get<const char *>(cell);
|
||||
|
||||
if (std::holds_alternative<std::string>(cell))
|
||||
return std::get<std::string>(cell);
|
||||
|
||||
if (std::holds_alternative<std::string_view>(cell))
|
||||
return std::string(std::get<std::string_view>(cell));
|
||||
|
||||
Q_UNREACHABLE();
|
||||
})
|
||||
| ranges::to<TabulateRow>();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
const InteractsWithIO &
|
||||
InteractsWithIO::table(const TableRow &header, const std::vector<TableRow> &rows,
|
||||
const Verbosity verbosity) const
|
||||
InteractsWithIO::table(
|
||||
const TableRow &header, const std::vector<TableRow> &rows,
|
||||
const FormatTableCallback &formatCallback, const Verbosity verbosity) const
|
||||
{
|
||||
if (dontOutput(verbosity))
|
||||
return *this;
|
||||
@@ -288,40 +304,15 @@ InteractsWithIO::table(const TableRow &header, const std::vector<TableRow> &rows
|
||||
#endif
|
||||
|
||||
// thead
|
||||
table.add_row(header);
|
||||
table.add_row(convertToTabulateRow(header));
|
||||
|
||||
// tbody
|
||||
for (const auto &row : rows)
|
||||
table.add_row(row);
|
||||
table.add_row(convertToTabulateRow(row));
|
||||
|
||||
// Initialize tabulate table colors by supported ANSI
|
||||
const auto [green, red] = initializeTableColors();
|
||||
|
||||
// Format table
|
||||
// Green text in header
|
||||
table.row(0).format().font_color(green);
|
||||
|
||||
for (std::size_t i = 1; i <= rows.size() ; ++i) {
|
||||
auto &row = table.row(i);
|
||||
|
||||
// Remove line between rows in the tbody
|
||||
if (i > 1)
|
||||
row.format().hide_border_top();
|
||||
|
||||
// Align the Batch column to the right (must be after the hide_border_top())
|
||||
row.cell(2).format().font_align(tabulate::FontAlign::right);
|
||||
|
||||
// Ran? column : Yes - green, No - red
|
||||
{
|
||||
auto &cell0 = row.cell(0);
|
||||
auto &format = cell0.format();
|
||||
|
||||
if (cell0.get_text() == "Yes")
|
||||
format.font_color(green);
|
||||
else
|
||||
format.font_color(red);
|
||||
}
|
||||
}
|
||||
// Format the tabulate table using the given callback
|
||||
if (formatCallback)
|
||||
std::invoke(formatCallback, table, *this);
|
||||
|
||||
std::cout << table << std::endl; // NOLINT(performance-avoid-endl)
|
||||
|
||||
@@ -392,6 +383,26 @@ QString InteractsWithIO::stripAnsiTags(QString string)
|
||||
|
||||
/* Getters / Setters */
|
||||
|
||||
bool InteractsWithIO::isAnsiOutput(const std::ostream &cout) const
|
||||
{
|
||||
// ANSI was explicitly set on the command-line, respect it
|
||||
if (m_ansi)
|
||||
return *m_ansi;
|
||||
|
||||
// Instead autodetect
|
||||
return m_terminal->hasColorSupport(cout);
|
||||
}
|
||||
|
||||
bool InteractsWithIO::isAnsiWOutput(const std::wostream &wcout) const
|
||||
{
|
||||
// ANSI was explicitly set on the command-line, respect it
|
||||
if (m_ansi)
|
||||
return *m_ansi;
|
||||
|
||||
// Instead autodetect
|
||||
return m_terminal->hasWColorSupport(wcout);
|
||||
}
|
||||
|
||||
void InteractsWithIO::withoutAnsi(const std::function<void()> &callback)
|
||||
{
|
||||
// Nothing to do, ANSI is already disabled
|
||||
@@ -505,26 +516,6 @@ bool InteractsWithIO::dontOutput(const Verbosity verbosity) const
|
||||
return verbosity > m_verbosity;
|
||||
}
|
||||
|
||||
bool InteractsWithIO::isAnsiOutput(const std::ostream &cout) const
|
||||
{
|
||||
// ANSI was explicitly set on the command-line, respect it
|
||||
if (m_ansi)
|
||||
return *m_ansi;
|
||||
|
||||
// Instead autodetect
|
||||
return m_terminal->hasColorSupport(cout);
|
||||
}
|
||||
|
||||
bool InteractsWithIO::isAnsiWOutput(const std::wostream &wcout) const
|
||||
{
|
||||
// ANSI was explicitly set on the command-line, respect it
|
||||
if (m_ansi)
|
||||
return *m_ansi;
|
||||
|
||||
// Instead autodetect
|
||||
return m_terminal->hasWColorSupport(wcout);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
/*! Get max. line size after the split with the newline in all rendered lines. */
|
||||
@@ -616,20 +607,6 @@ InteractsWithIO::computeReserveForErrorWall(const QStringList &splitted,
|
||||
return size;
|
||||
}
|
||||
|
||||
InteractsWithIO::TableColors InteractsWithIO::initializeTableColors() const
|
||||
{
|
||||
/* Even is I detect ANSI support as true, tabulate has it's own detection logic,
|
||||
it only check isatty(), it has some consequences, eg. no colors when output
|
||||
is redirected and --ansi was passed to the tom application, practically all the
|
||||
logic in the isAnsiOutput() will be skipped because of this tabulate internal
|
||||
logic, not a big deal though. */
|
||||
if (isAnsiOutput())
|
||||
return {};
|
||||
|
||||
// Disable coloring if no-ansi
|
||||
return {Color::none, Color::none};
|
||||
}
|
||||
|
||||
} // namespace Tom::Concerns
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
Reference in New Issue
Block a user