tom added tom about command

It also supports --json, --pretty, and --only= parameters.
This commit is contained in:
silverqx
2023-07-24 13:10:53 +02:00
parent 6f11fa6fc8
commit 650018bf99
14 changed files with 500 additions and 11 deletions

View File

@@ -291,6 +291,7 @@ function(tinytom_sources out_headers out_sources)
list(APPEND headers
application.hpp
commands/aboutcommand.hpp
commands/command.hpp
commands/completecommand.hpp
commands/database/seedcommand.hpp
@@ -363,6 +364,7 @@ function(tinytom_sources out_headers out_sources)
list(APPEND sources
application.cpp
commands/aboutcommand.cpp
commands/command.cpp
commands/completecommand.cpp
commands/database/seedcommand.cpp

View File

@@ -9,6 +9,7 @@ else: \
headersList += \
$$PWD/tom/application.hpp \
$$PWD/tom/commands/aboutcommand.hpp \
$$PWD/tom/commands/command.hpp \
$$PWD/tom/commands/completecommand.hpp \
$$PWD/tom/commands/database/seedcommand.hpp \

View File

@@ -34,6 +34,7 @@ namespace Tom {
namespace Commands
{
class AboutCommand;
class CompleteCommand;
class HelpCommand;
class ListCommand;
@@ -55,6 +56,8 @@ namespace Concerns
{
Q_DISABLE_COPY(Application)
// To access createVersionsSubsection()
friend Commands::AboutCommand;
// To access saveOptions()
friend Commands::Command;
// To access createCommand(), namespaceNames(), guessCommandXyz() related methods

View File

@@ -0,0 +1,91 @@
#pragma once
#ifndef TOM_COMMANDS_ABOUTCOMMAND_HPP
#define TOM_COMMANDS_ABOUTCOMMAND_HPP
#include <orm/macros/systemheader.hpp>
TINY_SYSTEM_HEADER
#include <unordered_set>
#include "tom/commands/command.hpp"
#include "tom/tomconstants.hpp"
TINYORM_BEGIN_COMMON_NAMESPACE
namespace Tom
{
struct SectionItem;
struct SubSectionItem;
namespace Commands
{
/*! Display basic information about the tom application. */
class AboutCommand : public Command
{
Q_DISABLE_COPY(AboutCommand)
public:
/*! Constructor. */
AboutCommand(Application &application, QCommandLineParser &parser);
/*! Virtual destructor. */
inline ~AboutCommand() override = default;
/*! The console command name. */
inline QString name() const override;
/*! The console command description. */
inline QString description() const override;
/*! The signature of the console command. */
QList<CommandLineOption> optionsSignature() const override;
/*! Execute the console command. */
int run() override;
protected:
/*! Display the application information. */
void display(const QVector<SectionItem> &sections);
/*! Display the application information as a detail view. */
void displayDetail(const QVector<SectionItem> &sections);
/*! Display the application information as JSON. */
void displayJson(const QVector<SectionItem> &sections);
/*! Determine whether the only values list contains the given section name. */
bool shouldSkipSection(const QString &sectionName) const;
/*! Get and prepare the only values set. */
std::unordered_set<QString> getOnlyValues() const;
/*! Prepare the section name for JSON output (map to a short section name). */
static QString prepareJsonSectionName(const QString &name);
/*! Prepare the about item name for JSON output (don't snake_case macro names). */
static QString prepareJsonAboutItemName(const QString &section,
const QString &aboutItem);
/*! Gather all information about the application. */
QVector<SectionItem> gatherAllAboutInformation() const;
/*! Gather environment-related information. */
QVector<SubSectionItem> gatherEnvironmentInformation() const;
/*! Gather C preprocessor macros-related information. */
static QVector<SubSectionItem> gatherMacrosInformation();
/*! Gather version-related information. */
static QVector<SubSectionItem> gatherVersionsInformation();
};
/* public */
QString AboutCommand::name() const
{
return Constants::About;
}
QString AboutCommand::description() const
{
return QStringLiteral("Display basic information about the tom application");
}
} // namespace Commands
} // namespace Tom
TINYORM_END_COMMON_NAMESPACE
#endif // TOM_COMMANDS_ABOUTCOMMAND_HPP

View File

@@ -5,6 +5,8 @@
#include <orm/macros/systemheader.hpp>
TINY_SYSTEM_HEADER
#include <QJsonDocument>
#include "tom/concerns/callscommands.hpp"
#include "tom/concerns/interactswithio.hpp"
#include "tom/types/commandlineoption.hpp"
@@ -132,6 +134,9 @@ namespace Commands
/*! Get a positional argument by the given name. */
QString argument(const QString &name, bool useDefault = true) const;
/*! Get the JsonFormat based on the --pretty option. */
QJsonDocument::JsonFormat jsonFormat() const;
/* Getters */
/*! Get a database connection. */
Orm::DatabaseConnection &connection(const QString &name) const;

View File

@@ -3,7 +3,6 @@
#define TOM_MIGRATIONREPOSITORY_HPP
#include <map>
#include <optional>
#include <vector>
#include <orm/connectionresolverinterface.hpp>

View File

@@ -53,6 +53,9 @@ namespace Tom::Constants
SHAREDLIB_EXPORT extern const QString seeder;
SHAREDLIB_EXPORT extern const QString step_;
SHAREDLIB_EXPORT extern const QString path_;
SHAREDLIB_EXPORT extern const QString json_;
SHAREDLIB_EXPORT extern const QString pretty;
SHAREDLIB_EXPORT extern const QString only_;
// Default value names
SHAREDLIB_EXPORT extern const QString env_up;
SHAREDLIB_EXPORT extern const QString class_up;
@@ -89,6 +92,7 @@ namespace Tom::Constants
SHAREDLIB_EXPORT extern const QString hidden_up;
SHAREDLIB_EXPORT extern const QString accessors_up;
SHAREDLIB_EXPORT extern const QString appends_up;
SHAREDLIB_EXPORT extern const QString only_up;
// complete
SHAREDLIB_EXPORT extern const QString commandline;
SHAREDLIB_EXPORT extern const QString position;
@@ -161,6 +165,7 @@ namespace Tom::Constants
SHAREDLIB_EXPORT extern const QString ShZsh;
// Command names
SHAREDLIB_EXPORT extern const QString About;
SHAREDLIB_EXPORT extern const QString Complete;
SHAREDLIB_EXPORT extern const QString DbSeed;
SHAREDLIB_EXPORT extern const QString DbWipe;

View File

@@ -52,6 +52,9 @@ namespace Tom::Constants
inline const QString seeder = QStringLiteral("seeder");
inline const QString step_ = QStringLiteral("step");
inline const QString path_ = QStringLiteral("path");
inline const QString json_ = QStringLiteral("json");
inline const QString pretty = QStringLiteral("pretty");
inline const QString only_ = QStringLiteral("only");
// Default value names
inline const QString env_up = QStringLiteral("ENV");
inline const QString class_up = QStringLiteral("CLASS");
@@ -88,6 +91,7 @@ namespace Tom::Constants
inline const QString hidden_up = QStringLiteral("HIDDEN");
inline const QString accessors_up = QStringLiteral("ACCESSORS");
inline const QString appends_up = QStringLiteral("APPENDS");
inline const QString only_up = QStringLiteral("ONLY");
// complete
inline const QString commandline = QStringLiteral("commandline");
inline const QString position = QStringLiteral("position");
@@ -161,6 +165,7 @@ namespace Tom::Constants
inline const QString ShZsh = QStringLiteral("zsh");
// Command names
inline const QString About = QStringLiteral("about");
inline const QString Complete = QStringLiteral("complete");
inline const QString DbSeed = QStringLiteral("db:seed");
inline const QString DbWipe = QStringLiteral("db:wipe");

View File

@@ -5,9 +5,9 @@
#include <orm/macros/systemheader.hpp>
TINY_SYSTEM_HEADER
#include <QString>
#include <QVector>
#include <memory>
#include <optional>
#include <orm/macros/commonnamespace.hpp>
@@ -52,6 +52,33 @@ namespace Tom
bool withinTransaction = false;
};
/*! About item type. */
struct AboutItem
{
/*! About item name. */
QString name;
/*! About item value. */
QString value;
};
/*! Subsection item type. */
struct SubSectionItem
{
/*! Subsection item name. */
std::optional<QString> name;
/*! About items list. */
QVector<AboutItem> abouts;
};
/*! Section item type. */
struct SectionItem
{
/*! Section item name. */
QString name;
/*! Subsection items list. */
QVector<SubSectionItem> subSections;
};
} // namespace Tom
TINYORM_END_COMMON_NAMESPACE

View File

@@ -4,6 +4,7 @@ extern_constants: \
sourcesList += \
$$PWD/tom/application.cpp \
$$PWD/tom/commands/aboutcommand.cpp \
$$PWD/tom/commands/command.cpp \
$$PWD/tom/commands/completecommand.cpp \
$$PWD/tom/commands/database/seedcommand.cpp \

View File

@@ -21,6 +21,7 @@
#include <orm/utils/type.hpp>
#include <orm/version.hpp>
#include "tom/commands/aboutcommand.hpp"
#include "tom/commands/completecommand.hpp"
#include "tom/commands/database/seedcommand.hpp"
#include "tom/commands/database/wipecommand.hpp"
@@ -67,6 +68,7 @@ using Orm::LibraryInfo;
using TypeUtils = Orm::Utils::Type;
using Tom::Commands::AboutCommand;
using Tom::Commands::Command;
using Tom::Commands::CompleteCommand;
using Tom::Commands::Database::SeedCommand;
@@ -89,6 +91,7 @@ using Tom::Commands::Migrations::RollbackCommand;
using Tom::Commands::Migrations::StatusCommand;
using Tom::Commands::Migrations::UninstallCommand;
using Tom::Constants::About;
using Tom::Constants::Complete;
using Tom::Constants::DbSeed;
using Tom::Constants::DbWipe;
@@ -529,6 +532,9 @@ Application::createCommand(const QString &command, const OptionalParserRef parse
// Use a custom parser if passed as the argument, needed by CallsCommands::call()
auto parserRef = parser ? *parser : std::ref(m_parser);
if (command == About)
return std::make_unique<AboutCommand>(*this, parserRef);
if (command == Complete)
return std::make_unique<CompleteCommand>(*this, parserRef);
@@ -683,7 +689,7 @@ Application::commandNames()
// Order is important here (shown by defined order by the list command)
static const std::vector<std::reference_wrapper<const QString>> cached {
// global namespace
Complete, Env, Help, Inspire, Integrate, List, Migrate,
About, Complete, Env, Help, Inspire, Integrate, List, Migrate,
// db
DbSeed, DbWipe,
// make
@@ -729,13 +735,13 @@ const std::vector<std::tuple<int, int>> &Application::commandsIndexes()
Order is important here - ziped with the namespaceNames(). */
static const std::vector<std::tuple<int, int>> cached {
{0, 7}, // "" - also global
{0, 7}, // global
{7, 9}, // db
{9, 12}, // make
{12, 19}, // migrate
{7, 19}, // namespaced
{0, 19}, // all
{0, 8}, // "" - also global
{0, 8}, // global
{8, 10}, // db
{10, 13}, // make
{13, 20}, // migrate
{8, 20}, // namespaced
{0, 20}, // all
};
return cached;

View File

@@ -0,0 +1,333 @@
#include "tom/commands/aboutcommand.hpp"
#include <QJsonObject>
#include <QJsonValue>
#include <range/v3/algorithm/contains.hpp>
#include <orm/constants.hpp>
#include <orm/utils/string.hpp>
#include "tom/application.hpp"
#include "tom/config.hpp"
#include "tom/tomtypes.hpp"
#include "tom/version.hpp"
TINYORM_BEGIN_COMMON_NAMESPACE
using Orm::Constants::NOSPACE;
using Orm::Constants::SPACE;
using StringUtils = Orm::Utils::String;
using Tom::Constants::json_;
using Tom::Constants::only_;
using Tom::Constants::only_up;
using Tom::Constants::pretty;
#define sl(str) QStringLiteral(str)
namespace Tom::Commands
{
/* public */
AboutCommand::AboutCommand(Application &application, QCommandLineParser &parser)
: Command(application, parser)
{}
QList<CommandLineOption> AboutCommand::optionsSignature() const
{
return {
{json_, sl("Output the information as JSON")},
{pretty, sl("Enable JSON human readable output")},
{only_, sl("Sections to display <gray>(partial match)</gray> "
"<comment>(multiple values allowed)</comment>"), only_up}, // Value
};
}
int AboutCommand::run()
{
Command::run();
display(gatherAllAboutInformation());
return EXIT_SUCCESS;
}
/* protected */
void AboutCommand::display(const QVector<SectionItem> &sections)
{
if (isSet(json_))
displayJson(sections);
else
displayDetail(sections);
}
void AboutCommand::displayDetail(const QVector<SectionItem> &sections)
{
auto firstSection = true;
// Print sections
for (const auto &[sectionName, subSections] : sections) {
// Nothing to do, skipping section
if (shouldSkipSection(sectionName))
continue;
// Main section title
if (firstSection)
firstSection = false;
else
newLine();
line(QStringLiteral("<blue>%1</blue>").arg(sectionName));
auto firstSubsection = true;
// Print subsections
for (const auto &[subsectionName, abouts] : subSections) {
if (firstSubsection)
firstSubsection = false;
else
// newline only if a subsection name was defined
if (subsectionName)
newLine();
// Subsection name is optional
if (subsectionName)
comment(*subsectionName);
// Print about items
for (const auto &[name, value] : abouts) {
note(NOSPACE.arg(name).arg(SPACE), false);
info(value);
}
}
}
}
void AboutCommand::displayJson(const QVector<SectionItem> &sections)
{
QJsonObject jsonSections;
// Serialize sections
for (const auto &[sectionName, subSections] : sections) {
// Nothing to do, skipping section
if (shouldSkipSection(sectionName))
continue;
// Prepare the section name for JSON output (map to a short section name)
const auto sectionNamePrepared = prepareJsonSectionName(sectionName);
QJsonObject jsonSubsections;
// Serialize subsections
for (const auto &[subsectionName, abouts] : subSections) {
QJsonObject jsonAboutItems;
// Serialize about items
for (const auto &[name, value] : abouts)
/* Subsection name is optional, if it's not defined then append directly
to the parent jsonSubsections. */
if (subsectionName)
jsonAboutItems.insert(StringUtils::snake(name.toLower()),
QJsonValue(value));
else
// Don't use snake_case for preprocessor macro names
jsonSubsections.insert(
prepareJsonAboutItemName(sectionNamePrepared, name),
QJsonValue(value));
/* Nothing to do, subsection name is the std::nullopt so the jsonAboutItems
were directly appended to the parent jsonSubsections. */
if (subsectionName)
jsonSubsections.insert(StringUtils::snake(subsectionName->toLower()),
jsonAboutItems);
}
jsonSections.insert(StringUtils::snake(sectionNamePrepared.toLower()),
jsonSubsections);
}
// Send to the stdout with disabled ANSI support
withoutAnsi([this, &jsonSections]
{
line(StringUtils::stripTags(
QJsonDocument(jsonSections).toJson(jsonFormat())));
});
}
bool AboutCommand::shouldSkipSection(const QString &sectionName) const
{
static const auto only = getOnlyValues();
const auto contains = ranges::contains(only, true,
[sectionName = sectionName.toLower()]
(const QString &onlyValue)
{
return sectionName.contains(onlyValue);
});
return !only.empty() && !contains;
}
std::unordered_set<QString> AboutCommand::getOnlyValues() const
{
const auto onlyList = values(only_, Qt::SkipEmptyParts);
std::unordered_set<QString> only;
only.reserve(static_cast<std::unordered_set<QString>::size_type>(onlyList.size()));
for (const auto &value : onlyList)
only.emplace(value.toLower());
return only;
}
QString AboutCommand::prepareJsonSectionName(const QString &name)
{
static const std::unordered_map<QString, QString> sectionNamesMap {
{sl("Macros <gray>C Preprocessor</gray>"), sl("Macros")},
};
if (!sectionNamesMap.contains(name))
return name;
return sectionNamesMap.at(name);
}
QString AboutCommand::prepareJsonAboutItemName(const QString &section,
const QString &aboutItem)
{
return section == sl("Macros") ? aboutItem
: StringUtils::snake(aboutItem.toLower());
}
QVector<SectionItem> AboutCommand::gatherAllAboutInformation() const
{
return {
{sl("Environment"), gatherEnvironmentInformation()},
{sl("Macros <gray>C Preprocessor</gray>"), gatherMacrosInformation()},
{sl("Versions"), gatherVersionsInformation()},
};
}
QVector<SubSectionItem> AboutCommand::gatherEnvironmentInformation() const
{
return {
{std::nullopt,
{
{sl("Application name"), QCoreApplication::applicationName()},
{sl("Organization name"), QCoreApplication::organizationName()},
{sl("Organization domain"), QCoreApplication::organizationDomain()},
{sl("tom version"), TINYTOM_VERSION_STR},
{sl("Environment"), application().environment()},
#ifdef TINYORM_DEBUG
{sl("Build type"), "Debug"},
#else
{sl("Build type"), "Release"},
#endif
}},
};
}
QVector<SubSectionItem> AboutCommand::gatherMacrosInformation()
{
static const auto ON = QStringLiteral("ON");
static const auto OFF = QStringLiteral("OFF");
return {
{std::nullopt,
{
#ifdef TINYORM_BUILDING_SHARED
{sl("TINYORM_BUILDING_SHARED"), ON},
#else
{sl("TINYORM_BUILDING_SHARED"), OFF},
#endif
#ifdef TINYORM_DEBUG
{sl("TINYORM_DEBUG"), ON},
#else
{sl("TINYORM_DEBUG"), OFF},
#endif
#ifdef TINYORM_DEBUG_SQL
{sl("TINYORM_DEBUG_SQL"), ON},
#else
{sl("TINYORM_DEBUG_SQL"), OFF},
#endif
#ifdef TINYORM_DISABLE_ORM
{sl("TINYORM_DISABLE_ORM"), ON},
#else
{sl("TINYORM_DISABLE_ORM"), OFF},
#endif
#ifdef TINYORM_DISABLE_THREAD_LOCAL
{sl("TINYORM_DISABLE_THREAD_LOCAL"), ON},
#else
{sl("TINYORM_DISABLE_THREAD_LOCAL"), OFF},
#endif
#ifdef TINYORM_EXTERN_CONSTANTS
{sl("TINYORM_EXTERN_CONSTANTS"), ON},
#else
{sl("TINYORM_EXTERN_CONSTANTS"), OFF},
#endif
#ifdef TINYORM_INLINE_CONSTANTS
{sl("TINYORM_INLINE_CONSTANTS"), ON},
#else
{sl("TINYORM_INLINE_CONSTANTS"), OFF},
#endif
#ifdef TINYORM_MYSQL_PING
{sl("TINYORM_MYSQL_PING"), ON},
#else
{sl("TINYORM_MYSQL_PING"), OFF},
#endif
#ifdef TINYORM_NO_DEBUG
{sl("TINYORM_NO_DEBUG"), ON},
#else
{sl("TINYORM_NO_DEBUG"), OFF},
#endif
#ifdef TINYORM_NO_DEBUG_SQL
{sl("TINYORM_NO_DEBUG_SQL"), ON},
#else
{sl("TINYORM_NO_DEBUG_SQL"), OFF},
#endif
#ifdef TINYORM_TESTS_CODE
{sl("TINYORM_TESTS_CODE"), ON},
#else
{sl("TINYORM_TESTS_CODE"), OFF},
#endif
#ifdef TINYORM_TOM_EXAMPLE
{sl("TINYORM_TOM_EXAMPLE"), ON},
#else
{sl("TINYORM_TOM_EXAMPLE"), OFF},
#endif
#ifdef TINYORM_USING_PCH
{sl("TINYORM_USING_PCH"), ON},
#else
{sl("TINYORM_USING_PCH"), OFF},
#endif
{sl("TINYTOM_MIGRATIONS_DIR"), TINYTOM_STRINGIFY(TINYTOM_MIGRATIONS_DIR)},
{sl("TINYTOM_MODELS_DIR"), TINYTOM_STRINGIFY(TINYTOM_MODELS_DIR)},
{sl("TINYTOM_SEEDERS_DIR"), TINYTOM_STRINGIFY(TINYTOM_SEEDERS_DIR)},
}},
};
}
QVector<SubSectionItem> AboutCommand::gatherVersionsInformation()
{
auto versions = Application::createVersionsSubsection();
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
versions.emplaceFront(std::nullopt, QVector<AboutItem> {
{QStringLiteral("tom"), TINYTOM_VERSION_STR},
});
#else
versions.push_front({std::nullopt, {
{QStringLiteral("tom"), TINYTOM_VERSION_STR},
}});
#endif
return versions;
}
} // namespace Tom::Commands
TINYORM_END_COMMON_NAMESPACE

View File

@@ -22,6 +22,7 @@ using Orm::Constants::SPACE;
using Tom::Constants::Help;
using Tom::Constants::LongOption;
using Tom::Constants::pretty;
using TomUtils = Tom::Utils;
@@ -312,6 +313,11 @@ QString Command::argument(const QString &name, const bool useDefault) const
return argument(m_positionalArguments.at(name), useDefault);
}
QJsonDocument::JsonFormat Command::jsonFormat() const
{
return isSet(pretty) ? QJsonDocument::Indented : QJsonDocument::Compact;
}
/* Getters */
Orm::DatabaseConnection &Command::connection(const QString &name) const

View File

@@ -41,6 +41,9 @@ namespace Tom::Constants
const QString seeder = QStringLiteral("seeder");
const QString step_ = QStringLiteral("step");
const QString path_ = QStringLiteral("path");
const QString json_ = QStringLiteral("json");
const QString pretty = QStringLiteral("pretty");
const QString only_ = QStringLiteral("only");
// Default value names
const QString env_up = QStringLiteral("ENV");
const QString class_up = QStringLiteral("CLASS");
@@ -77,6 +80,7 @@ namespace Tom::Constants
const QString hidden_up = QStringLiteral("HIDDEN");
const QString accessors_up = QStringLiteral("ACCESSORS");
const QString appends_up = QStringLiteral("APPENDS");
const QString only_up = QStringLiteral("ONLY");
// complete
const QString commandline = QStringLiteral("commandline");
const QString position = QStringLiteral("position");
@@ -150,6 +154,7 @@ namespace Tom::Constants
const QString ShZsh = QStringLiteral("zsh");
// Command names
const QString About = QStringLiteral("about");
const QString Complete = QStringLiteral("complete");
const QString DbSeed = QStringLiteral("db:seed");
const QString DbWipe = QStringLiteral("db:wipe");