mirror of
https://github.com/silverqx/TinyORM.git
synced 2026-02-22 03:38:44 -06:00
added migrations 🔥🚀✨
Almost everything is implemented, possible to migrate up, down (rollback), migrate by steps (works for up/down too), reset, refresh, fresh, wipe database, showing status, installing migration table. Command line interface is superb, it supports ansi coloring, verbosity, no-interactive mode, force option, env option to select current env. It has enhanced ansi coloring (vt100 terminal) detection, when ansi or no-ansi option is not passed it can detect whether terminal supports coloring. Ansi coloring is disabled when redirection to file is detected (can be overridden by ansi/no-ansi options). Supports NO_COLOR env. variable (https://no-color.org/) and can detect the ConEmu, Hyper, and xterm on Windows. Carefully implemented help and list commands, list command can print supported commands by namespace. Advanced make migration command offers great command interface for creating migration classes, supports options for generating empty, create, or update migration classes. Unfinished make project command, will support creating qmake, qmake subproject, and cmake, cmake subproject projects. Later will be extracted to own executable tomproject.exe for rapidly generating a new TinyORM projects. Other implemented commands are env that prints current env. and inspire that displays an inspiring quote 😁. Verbose supports 5 levels quiet, normal, verbose, very verbose, and debug. Possibility to disable compilation of the tom command related code using TINYORM_DISABLE_TOM c macro, for the qmake exists disable_tom CONFIG option and for the CMake exist TOM configuration option. Confirmable interface that ask Y/N confirmation during migrate when env. == production, can be overridden by --force option. Whole tom terminal application supports or is implemented with UTF-8 support, also implemented UTF-16 output methods but they are not needed. Input also supports UTF-8, currently only Y/N input is needed by the Confirmation concern. All migrate commands also support the --pretend option and the --env option, when env. is production then tom asks confirmation to migrate, it can be overridden by the --force option. Added the tom example project, it is a complete command-line migration application, it uses migrations from the tests. Implementing this was really fun 🙃😎. - added 14 functional tests to test migrations up/down, stepping, installing migration table, refresh, reset on MySQL database - added unit test to check version number in tom.exe executable - new tom exception classes - created dozens of a new todo tasks 😂🤪, currently 348 todos 😎 - added some info messages to the qmake build about building features - in the debug build and with the -vvv option enable debugging of sql queries - enhanced RC and manifest file generation, allowed to pass a custom basename for a RC/manifest file as the 3. argument and a custom replace token for the CMake genex as the 4. argument - bugfix disabled #pragma code_page(65001) // UTF-8 in RC files, it messes up the © character Output of tom exe without arguments and options: Common options: --ansi Force ANSI output --no-ansi Disable ANSI output --env The environment the command should run under -h, --help Display help for the given command. When no command is given display help for the list command -n, --no-interaction Do not ask any interactive question -q, --quiet Do not output any message -V, --version Display this application version -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug Available commands: env Display the current framework environment help Display help for a command inspire Display an inspiring quote list List commands migrate Run the database migrations db db:wipe Drop all tables, views, and types make make:migration Create a new migration file make:project Create a new Tom application project migrate migrate:fresh Drop all tables and re-run all migrations migrate:install Create the migration repository migrate:refresh Rollback and re-run all migrations migrate:reset Rollback all database migrations migrate:rollback Rollback the last database migration migrate:status Show the status of each migration
This commit is contained in:
@@ -53,8 +53,10 @@ set(minReqMsvcVersion 19.29)
|
||||
set(minRecGCCVersion 10.2)
|
||||
set(minRecClangVersion 12)
|
||||
# Packages
|
||||
set(minQtVersion 5.15.2)
|
||||
set(minRangeV3Version 0.11.0)
|
||||
set(minQtVersion 5.15.2)
|
||||
set(minRangeV3Version 0.11.0)
|
||||
# tabulate doesn't provide Package Version File
|
||||
#set(minTabulateVersion 1.4.0)
|
||||
|
||||
# Make minimum toolchain version a requirement
|
||||
tiny_toolchain_requirement(
|
||||
@@ -157,11 +159,25 @@ option is disabled, then only the query builder without ORM is compiled."
|
||||
DISABLED TINYORM_DISABLE_ORM
|
||||
)
|
||||
|
||||
target_optional_compile_definitions(${TinyOrm_target}
|
||||
PUBLIC
|
||||
FEATURE NAME TOM
|
||||
DEFAULT ON
|
||||
DESCRIPTION "Controls the compilation of all tom related source code (command \
|
||||
line interface)."
|
||||
DISABLED TINYORM_DISABLE_TOM
|
||||
)
|
||||
# Depends on the TOM option so defined here
|
||||
feature_option_dependent(TOM_EXAMPLE
|
||||
"Build the tom command-line application example" OFF
|
||||
"TOM" TOM_EXAMPLE-NOTFOUND
|
||||
)
|
||||
|
||||
# TinyORM library header and source files
|
||||
# ---
|
||||
|
||||
include(TinySources)
|
||||
tiny_sources(${TinyOrm_target}_headers ${TinyOrm_target}_sources)
|
||||
tinyorm_sources(${TinyOrm_target}_headers ${TinyOrm_target}_sources)
|
||||
|
||||
target_sources(${TinyOrm_target} PRIVATE
|
||||
${${TinyOrm_target}_headers}
|
||||
@@ -212,7 +228,6 @@ target_compile_definitions(${TinyOrm_target}
|
||||
$<$<NOT:$<CONFIG:Debug>>:TINYORM_NO_DEBUG>
|
||||
# Do not log queries
|
||||
$<$<NOT:$<CONFIG:Debug>>:TINYORM_NO_DEBUG_SQL>
|
||||
PRIVATE
|
||||
# Debug build
|
||||
$<$<CONFIG:Debug>:TINYORM_DEBUG>
|
||||
# Log queries with a time measurement
|
||||
@@ -237,10 +252,49 @@ else()
|
||||
endif()
|
||||
|
||||
# Enable code needed by tests, eg. connection overriding in the Model
|
||||
if(BUILD_TESTS)
|
||||
if(BUILD_TESTS AND ORM)
|
||||
target_compile_definitions(${TinyOrm_target} PUBLIC TINYORM_TESTS_CODE)
|
||||
endif()
|
||||
|
||||
# TinyTom related header and source files
|
||||
# ---
|
||||
|
||||
if(TOM)
|
||||
tinytom_sources(${TomExample_target}_headers ${TomExample_target}_sources)
|
||||
|
||||
target_sources(${TinyOrm_target} PRIVATE
|
||||
${${TomExample_target}_headers}
|
||||
${${TomExample_target}_sources}
|
||||
)
|
||||
endif()
|
||||
|
||||
# TinyTom related specific configuration
|
||||
# ---
|
||||
|
||||
if(TOM)
|
||||
target_include_directories(${TinyOrm_target} PUBLIC
|
||||
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/tom/include>"
|
||||
)
|
||||
endif()
|
||||
|
||||
# TinyTom related defines
|
||||
# ---
|
||||
|
||||
if(TOM)
|
||||
target_compile_definitions(${TinyOrm_target}
|
||||
PUBLIC
|
||||
# Release build
|
||||
$<$<NOT:$<CONFIG:Debug>>:TINYTOM_NO_DEBUG>
|
||||
# Debug build
|
||||
$<$<CONFIG:Debug>:TINYTOM_DEBUG>
|
||||
)
|
||||
|
||||
# Enable code needed by tests (modify the migrate:status command for tests need)
|
||||
if(BUILD_TESTS)
|
||||
target_compile_definitions(${TinyOrm_target} PUBLIC TINYTOM_TESTS_CODE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Windows resource and manifest files
|
||||
# ---
|
||||
|
||||
@@ -285,6 +339,12 @@ if(MYSQL_PING)
|
||||
target_link_libraries(${TinyOrm_target} PRIVATE MySQL::MySQL)
|
||||
endif()
|
||||
|
||||
if(TOM)
|
||||
# tabulate doesn't provide Package Version File
|
||||
tiny_find_package(tabulate CONFIG REQUIRED)
|
||||
target_link_libraries(${TinyOrm_target} PUBLIC tabulate::tabulate)
|
||||
endif()
|
||||
|
||||
# Build auto tests
|
||||
# ---
|
||||
|
||||
@@ -295,6 +355,13 @@ if(BUILD_TESTS)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
# Build examples
|
||||
# ---
|
||||
|
||||
if(TOM_EXAMPLE)
|
||||
add_subdirectory(examples)
|
||||
endif()
|
||||
|
||||
# Deployment
|
||||
# ---
|
||||
|
||||
@@ -355,10 +422,20 @@ set_package_properties(range-v3
|
||||
if(MYSQL_PING)
|
||||
set_package_properties(MySQL
|
||||
PROPERTIES
|
||||
# URL and DESCRIPTION are already set by Find-module Package (FindMySQL.cmake)
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Provides MySQL ping, enables MySqlConnection::pingDatabase()"
|
||||
)
|
||||
endif()
|
||||
if(TOM)
|
||||
set_package_properties(tabulate
|
||||
PROPERTIES
|
||||
URL "https://github.com/p-ranav/tabulate/"
|
||||
DESCRIPTION "Table Maker for Modern C++"
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Used by the tom in the migrate:status command"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(VERBOSE_CONFIGURE)
|
||||
if(NOT TINY_IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
|
||||
@@ -2,13 +2,15 @@ TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS = src
|
||||
|
||||
!disable_tom {
|
||||
SUBDIRS += tom
|
||||
tom.depends = src
|
||||
tom_example {
|
||||
SUBDIRS += examples
|
||||
examples.depends = src
|
||||
}
|
||||
|
||||
# Can be enabled by CONFIG += build_tests when the qmake.exe for the project is called
|
||||
build_tests {
|
||||
SUBDIRS += tests
|
||||
tests.depends = src
|
||||
|
||||
!build_pass: message("Build TinyORM unit tests.")
|
||||
}
|
||||
|
||||
@@ -143,10 +143,12 @@ macro(tiny_init_tiny_variables_pre)
|
||||
# a main package name
|
||||
set(TinyOrm_ns TinyOrm)
|
||||
set(TinyUtils_ns TinyUtils)
|
||||
set(TomExample_ns tom)
|
||||
# Target names
|
||||
set(CommonConfig_target CommonConfig)
|
||||
set(TinyOrm_target TinyOrm)
|
||||
set(TinyUtils_target TinyUtils)
|
||||
set(TomExample_target tom)
|
||||
|
||||
get_property(isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
set(TINY_IS_MULTI_CONFIG "${isMultiConfig}" CACHE INTERNAL
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# TinyORM library header and source files
|
||||
# Create header and source files lists and return them
|
||||
function(tiny_sources out_headers out_sources)
|
||||
function(tinyorm_sources out_headers out_sources)
|
||||
|
||||
# ORM headers section
|
||||
set(headers)
|
||||
|
||||
if(TINY_EXTERN_CONSTANTS)
|
||||
@@ -131,10 +132,17 @@ function(tiny_sources out_headers out_sources)
|
||||
tiny/types/connectionoverride.hpp
|
||||
tiny/types/syncchanges.hpp
|
||||
tiny/utils/attribute.hpp
|
||||
)
|
||||
endif()
|
||||
|
||||
# Headers used in both ORM and TOM
|
||||
if(ORM OR TOM)
|
||||
list(APPEND headers
|
||||
tiny/utils/string.hpp
|
||||
)
|
||||
endif()
|
||||
|
||||
# ORM sources section
|
||||
set(sources)
|
||||
|
||||
if(TINY_EXTERN_CONSTANTS)
|
||||
@@ -203,6 +211,12 @@ function(tiny_sources out_headers out_sources)
|
||||
tiny/tinytypes.cpp
|
||||
tiny/types/syncchanges.cpp
|
||||
tiny/utils/attribute.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
# Sources needed in both ORM and TOM
|
||||
if(ORM OR TOM)
|
||||
list(APPEND sources
|
||||
tiny/utils/string.cpp
|
||||
)
|
||||
endif()
|
||||
@@ -215,5 +229,91 @@ function(tiny_sources out_headers out_sources)
|
||||
|
||||
set(${out_headers} ${headers} PARENT_SCOPE)
|
||||
set(${out_sources} ${sources} PARENT_SCOPE)
|
||||
|
||||
endfunction()
|
||||
|
||||
# TinyTom application header and source files
|
||||
# Create header and source files lists and return them
|
||||
function(tinytom_sources out_headers out_sources)
|
||||
|
||||
# Tom headers section
|
||||
set(headers)
|
||||
|
||||
list(APPEND headers
|
||||
application.hpp
|
||||
commands/command.hpp
|
||||
commands/database/wipecommand.hpp
|
||||
commands/environmentcommand.hpp
|
||||
commands/helpcommand.hpp
|
||||
commands/inspirecommand.hpp
|
||||
commands/listcommand.hpp
|
||||
commands/make/migrationcommand.hpp
|
||||
# commands/make/projectcommand.hpp
|
||||
commands/make/stubs/migrationstubs.hpp
|
||||
commands/make/stubs/projectstubs.hpp
|
||||
commands/migrations/freshcommand.hpp
|
||||
commands/migrations/installcommand.hpp
|
||||
commands/migrations/migratecommand.hpp
|
||||
commands/migrations/refreshcommand.hpp
|
||||
commands/migrations/resetcommand.hpp
|
||||
commands/migrations/rollbackcommand.hpp
|
||||
commands/migrations/statuscommand.hpp
|
||||
concerns/callscommands.hpp
|
||||
concerns/confirmable.hpp
|
||||
concerns/interactswithio.hpp
|
||||
concerns/printsoptions.hpp
|
||||
config.hpp
|
||||
exceptions/invalidargumenterror.hpp
|
||||
exceptions/invalidtemplateargumenterror.hpp
|
||||
exceptions/logicerror.hpp
|
||||
exceptions/runtimeerror.hpp
|
||||
exceptions/tomerror.hpp
|
||||
migration.hpp
|
||||
migrationcreator.hpp
|
||||
migrationrepository.hpp
|
||||
migrator.hpp
|
||||
terminal.hpp
|
||||
tomtypes.hpp
|
||||
version.hpp
|
||||
)
|
||||
|
||||
# Tom sources section
|
||||
set(sources)
|
||||
|
||||
list(APPEND sources
|
||||
application.cpp
|
||||
commands/command.cpp
|
||||
commands/database/wipecommand.cpp
|
||||
commands/environmentcommand.cpp
|
||||
commands/helpcommand.cpp
|
||||
commands/inspirecommand.cpp
|
||||
commands/listcommand.cpp
|
||||
commands/make/migrationcommand.cpp
|
||||
# commands/make/projectcommand.cpp
|
||||
commands/migrations/freshcommand.cpp
|
||||
commands/migrations/installcommand.cpp
|
||||
commands/migrations/migratecommand.cpp
|
||||
commands/migrations/refreshcommand.cpp
|
||||
commands/migrations/resetcommand.cpp
|
||||
commands/migrations/rollbackcommand.cpp
|
||||
commands/migrations/statuscommand.cpp
|
||||
concerns/callscommands.cpp
|
||||
concerns/confirmable.cpp
|
||||
concerns/interactswithio.cpp
|
||||
concerns/printsoptions.cpp
|
||||
exceptions/tomlogicerror.cpp
|
||||
exceptions/tomruntimeerror.cpp
|
||||
migrationcreator.cpp
|
||||
migrationrepository.cpp
|
||||
migrator.cpp
|
||||
terminal.cpp
|
||||
)
|
||||
|
||||
list(SORT headers)
|
||||
list(SORT sources)
|
||||
|
||||
list(TRANSFORM headers PREPEND "${CMAKE_SOURCE_DIR}/tom/include/tom/")
|
||||
list(TRANSFORM sources PREPEND "${CMAKE_SOURCE_DIR}/tom/src/tom/")
|
||||
|
||||
set(${out_headers} ${headers} PARENT_SCOPE)
|
||||
set(${out_sources} ${sources} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
@@ -3,7 +3,7 @@ include(TinyResourceAndManifest)
|
||||
# Configure passed auto test
|
||||
function(tiny_configure_test name)
|
||||
|
||||
set(options INCLUDE_MODELS)
|
||||
set(options INCLUDE_MIGRATIONS INCLUDE_MODELS)
|
||||
cmake_parse_arguments(PARSE_ARGV 1 TINY "${options}" "" "")
|
||||
|
||||
if(DEFINED TINY_UNPARSED_ARGUMENTS)
|
||||
@@ -54,6 +54,12 @@ ${TINY_UNPARSED_ARGUMENTS}")
|
||||
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>"
|
||||
)
|
||||
|
||||
if(TINY_INCLUDE_MIGRATIONS)
|
||||
target_include_directories(${name} PRIVATE
|
||||
"$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/tests/database>"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(TINY_INCLUDE_MODELS)
|
||||
target_include_directories(${name} PRIVATE
|
||||
"$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/tests/models>"
|
||||
@@ -62,8 +68,6 @@ ${TINY_UNPARSED_ARGUMENTS}")
|
||||
|
||||
target_link_libraries(${name}
|
||||
PRIVATE
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Sql
|
||||
Qt${QT_VERSION_MAJOR}::Test
|
||||
${TinyOrm_ns}::${TinyUtils_target}
|
||||
${TinyOrm_ns}::${TinyOrm_target}
|
||||
|
||||
3
examples/CMakeLists.txt
Normal file
3
examples/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
if(TOM_EXAMPLE)
|
||||
add_subdirectory(tom)
|
||||
endif()
|
||||
7
examples/examples.pro
Normal file
7
examples/examples.pro
Normal file
@@ -0,0 +1,7 @@
|
||||
TEMPLATE = subdirs
|
||||
|
||||
tom_example:!disable_tom {
|
||||
SUBDIRS += tom
|
||||
|
||||
!build_pass: message("Build the tom example.")
|
||||
}
|
||||
74
examples/tom/CMakeLists.txt
Normal file
74
examples/tom/CMakeLists.txt
Normal file
@@ -0,0 +1,74 @@
|
||||
# Tom command-line application example
|
||||
# ---
|
||||
|
||||
# Initialize Project Version
|
||||
# ---
|
||||
|
||||
include(TinyHelpers)
|
||||
tiny_read_version(TINY_VERSION
|
||||
TINY_VERSION_MAJOR TINY_VERSION_MINOR TINY_VERSION_PATCH TINY_VERSION_TWEAK
|
||||
VERSION_HEADER "${CMAKE_SOURCE_DIR}/tom/include/tom/version.hpp"
|
||||
PREFIX TINYTOM
|
||||
HEADER_FOR "${TomExample_ns}"
|
||||
)
|
||||
|
||||
# Basic project
|
||||
# ---
|
||||
|
||||
project(${TomExample_ns}
|
||||
DESCRIPTION "Tom console for TinyORM"
|
||||
HOMEPAGE_URL "https://silverqx.github.io/TinyORM/"
|
||||
LANGUAGES CXX
|
||||
VERSION ${TINY_VERSION}
|
||||
)
|
||||
|
||||
# Tom command-line application
|
||||
# ---
|
||||
|
||||
add_executable(${TomExample_target}
|
||||
main.cpp
|
||||
)
|
||||
add_executable(${TomExample_ns}::${TomExample_target} ALIAS ${TomExample_target})
|
||||
|
||||
# Tom command-line application specific configuration
|
||||
# ---
|
||||
|
||||
set_target_properties(${TomExample_target}
|
||||
PROPERTIES
|
||||
C_VISIBILITY_PRESET "hidden"
|
||||
CXX_VISIBILITY_PRESET "hidden"
|
||||
VISIBILITY_INLINES_HIDDEN YES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
)
|
||||
|
||||
target_include_directories(${TomExample_target} PRIVATE
|
||||
"$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/tests/database>"
|
||||
)
|
||||
|
||||
# Tom command-line application defines
|
||||
# ---
|
||||
|
||||
target_compile_definitions(${TomExample_target}
|
||||
PRIVATE
|
||||
PROJECT_TOMEXAMPLE
|
||||
)
|
||||
|
||||
# Windows resource and manifest files
|
||||
# ---
|
||||
|
||||
# Find icons, tom/version.hpp, and Windows manifest file for MinGW
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
tiny_set_rc_flags("-I \"${CMAKE_SOURCE_DIR}/tom/resources\"")
|
||||
endif()
|
||||
|
||||
include(TinyResourceAndManifest)
|
||||
tiny_resource_and_manifest(${TomExample_target}
|
||||
OUTPUT_DIR "${TINY_BUILD_GENDIR}/tmp/"
|
||||
RESOURCES_DIR "${CMAKE_SOURCE_DIR}/tom/resources"
|
||||
)
|
||||
|
||||
# Resolve and link dependencies
|
||||
# ---
|
||||
|
||||
# Unconditional dependencies
|
||||
target_link_libraries(${TomExample_target} PRIVATE ${TinyOrm_ns}::${TinyOrm_target})
|
||||
39
examples/tom/conf.pri.example
Normal file
39
examples/tom/conf.pri.example
Normal file
@@ -0,0 +1,39 @@
|
||||
# Migrations header files
|
||||
# ---
|
||||
|
||||
# Tests' migrations as example migrations
|
||||
include($$TINYORM_SOURCE_TREE/tests/database/migrations.pri)
|
||||
# Or include yours migrations
|
||||
#include(/home/xyz/your_project/database/migrations.pri)
|
||||
|
||||
# Dependencies include and library paths
|
||||
# ---
|
||||
|
||||
# MinGW
|
||||
win32-g++|win32-clang-g++ {
|
||||
# Enable ccache wrapper
|
||||
CONFIG *= tiny_ccache
|
||||
|
||||
# Includes
|
||||
# tabulate
|
||||
INCLUDEPATH += $$quote(C:/msys64/home/xyz/vcpkg/installed/x64-mingw-dynamic/include/)
|
||||
QMAKE_CXXFLAGS += -isystem $$shell_quote(C:/msys64/home/xyz/vcpkg/installed/x64-mingw-dynamic/include/)
|
||||
|
||||
# Use faster linker
|
||||
# CONFIG *= use_lld_linker does not work on MinGW
|
||||
QMAKE_LFLAGS *= -fuse-ld=lld
|
||||
}
|
||||
else:win32-msvc {
|
||||
# Includes
|
||||
# range-v3 and tabulate
|
||||
INCLUDEPATH += $$quote(E:/xyz/vcpkg/installed/x64-windows/include/)
|
||||
}
|
||||
else:unix {
|
||||
# Includes
|
||||
# range-v3 and tabulate
|
||||
QMAKE_CXXFLAGS += -isystem $$shell_quote(/home/xyz/vcpkg/installed/x64-linux/include/)
|
||||
|
||||
# Use faster linkers
|
||||
clang: CONFIG *= use_lld_linker
|
||||
else: CONFIG *= use_gold_linker
|
||||
}
|
||||
99
examples/tom/main.cpp
Normal file
99
examples/tom/main.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
#include <orm/db.hpp>
|
||||
|
||||
#include <tom/application.hpp>
|
||||
|
||||
#include "migrations/2014_10_12_000000_create_posts_table.hpp"
|
||||
#include "migrations/2014_10_12_100000_add_factor_column_to_posts_table.hpp"
|
||||
#include "migrations/2014_10_12_200000_create_properties_table.hpp"
|
||||
#include "migrations/2014_10_12_300000_create_phones_table.hpp"
|
||||
|
||||
using Orm::Constants::H127001;
|
||||
using Orm::Constants::P3306;
|
||||
using Orm::Constants::QMYSQL;
|
||||
using Orm::Constants::SYSTEM;
|
||||
using Orm::Constants::UTF8MB4;
|
||||
using Orm::Constants::charset_;
|
||||
using Orm::Constants::collation_;
|
||||
using Orm::Constants::database_;
|
||||
using Orm::Constants::driver_;
|
||||
using Orm::Constants::engine_;
|
||||
using Orm::Constants::host_;
|
||||
using Orm::Constants::InnoDB;
|
||||
using Orm::Constants::isolation_level;
|
||||
using Orm::Constants::options_;
|
||||
using Orm::Constants::password_;
|
||||
using Orm::Constants::port_;
|
||||
using Orm::Constants::prefix_;
|
||||
using Orm::Constants::prefix_indexes;
|
||||
using Orm::Constants::strict_;
|
||||
using Orm::Constants::timezone_;
|
||||
using Orm::Constants::username_;
|
||||
|
||||
using Orm::DatabaseManager;
|
||||
using Orm::DB;
|
||||
|
||||
using TomApplication = Tom::Application;
|
||||
|
||||
using namespace Migrations; // NOLINT(google-build-using-namespace)
|
||||
|
||||
/*! Build the database manager instance and add a database connection. */
|
||||
std::shared_ptr<DatabaseManager> setupManager();
|
||||
|
||||
/*! c++ main function. */
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
try {
|
||||
// Ownership of the shared_ptr()
|
||||
auto db = setupManager();
|
||||
|
||||
return TomApplication(argc, argv, db, "TOM_EXAMPLE_ENV")
|
||||
.migrations<
|
||||
_2014_10_12_000000_create_posts_table,
|
||||
_2014_10_12_100000_add_factor_column_to_posts_table,
|
||||
_2014_10_12_200000_create_properties_table,
|
||||
_2014_10_12_300000_create_phones_table>()
|
||||
// Fire it up 🔥🚀✨
|
||||
.run();
|
||||
|
||||
} catch (const std::exception &e) {
|
||||
|
||||
TomApplication::logException(e);
|
||||
}
|
||||
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::shared_ptr<DatabaseManager> setupManager()
|
||||
{
|
||||
// Ownership of the shared_ptr()
|
||||
return DB::create({
|
||||
{driver_, QMYSQL},
|
||||
{host_, qEnvironmentVariable("DB_MYSQL_HOST", H127001)},
|
||||
{port_, qEnvironmentVariable("DB_MYSQL_PORT", P3306)},
|
||||
{database_, qEnvironmentVariable("DB_MYSQL_DATABASE", "")},
|
||||
{username_, qEnvironmentVariable("DB_MYSQL_USERNAME", "")},
|
||||
{password_, qEnvironmentVariable("DB_MYSQL_PASSWORD", "")},
|
||||
{charset_, qEnvironmentVariable("DB_MYSQL_CHARSET", UTF8MB4)},
|
||||
{collation_, qEnvironmentVariable("DB_MYSQL_COLLATION",
|
||||
QStringLiteral("utf8mb4_0900_ai_ci"))},
|
||||
{timezone_, SYSTEM},
|
||||
{prefix_, ""},
|
||||
{prefix_indexes, true},
|
||||
{strict_, true},
|
||||
{isolation_level, QStringLiteral("REPEATABLE READ")},
|
||||
{engine_, InnoDB},
|
||||
{options_, QVariantHash()},
|
||||
},
|
||||
QLatin1String("tinyorm_tom"));
|
||||
}
|
||||
|
||||
/* Alternative syntax to instantiate migration classes. */
|
||||
// return TomApplication(argc, argv, db, "TOM_EXAMPLE_ENV",
|
||||
// {
|
||||
// std::make_shared<_2014_10_12_000000_create_posts_table>(),
|
||||
// std::make_shared<_2014_10_12_100000_add_factor_column_to_posts_table>(),
|
||||
// std::make_shared<_2014_10_12_200000_create_properties_table>(),
|
||||
// std::make_shared<_2014_10_12_300000_create_phones_table>(),
|
||||
// })
|
||||
// // Fire it up 🔥🚀✨
|
||||
// .run();
|
||||
53
examples/tom/tom.pro
Normal file
53
examples/tom/tom.pro
Normal file
@@ -0,0 +1,53 @@
|
||||
QT *= core sql
|
||||
QT -= gui
|
||||
|
||||
TEMPLATE = app
|
||||
TARGET = tom
|
||||
|
||||
# TinyTom example application specific configuration
|
||||
# ---
|
||||
|
||||
CONFIG *= console
|
||||
|
||||
include($$TINYORM_SOURCE_TREE/qmake/tom.pri)
|
||||
|
||||
# TinyTom example application defines
|
||||
# ---
|
||||
|
||||
DEFINES += PROJECT_TOMEXAMPLE
|
||||
|
||||
# TinyTom defines
|
||||
# ---
|
||||
# this define is not provided in the qmake/tom.pri
|
||||
|
||||
# Enable code needed by tests (modify the migrate:status command for tests need)
|
||||
build_tests: \
|
||||
DEFINES *= TINYTOM_TESTS_CODE
|
||||
|
||||
# TinyTom example application header and source files
|
||||
# ---
|
||||
|
||||
SOURCES += $$PWD/main.cpp
|
||||
|
||||
# Deployment
|
||||
# ---
|
||||
|
||||
win32-msvc:CONFIG(debug, debug|release) {
|
||||
win32-msvc: target.path = C:/optx64/$${TARGET}
|
||||
# else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||
!isEmpty(target.path): INSTALLS += target
|
||||
}
|
||||
|
||||
# User Configuration
|
||||
# ---
|
||||
|
||||
exists(conf.pri): \
|
||||
include(conf.pri)
|
||||
|
||||
#else:is_vcpkg_build: \
|
||||
# include(../qmake/vcpkgconf.pri)
|
||||
|
||||
else: \
|
||||
error( "'conf.pri' for 'tom' example project does not exist. See an example\
|
||||
configuration in 'examples/tom/conf.pri.example' or call 'vcpkg install'\
|
||||
in the project's root." )
|
||||
@@ -126,6 +126,9 @@ headersList += \
|
||||
$$PWD/orm/tiny/types/connectionoverride.hpp \
|
||||
$$PWD/orm/tiny/types/syncchanges.hpp \
|
||||
$$PWD/orm/tiny/utils/attribute.hpp \
|
||||
|
||||
!disable_orm|!disable_tom: \
|
||||
headersList += \
|
||||
$$PWD/orm/tiny/utils/string.hpp \
|
||||
|
||||
HEADERS += $$sorted(headersList)
|
||||
|
||||
@@ -9,7 +9,7 @@ TINY_SYSTEM_HEADER
|
||||
|
||||
#include "orm/macros/commonnamespace.hpp"
|
||||
#include "orm/macros/export.hpp"
|
||||
// CUR check this on clean project silverqx
|
||||
// CUR tom, check this on clean project silverqx
|
||||
// Include the blueprint here so a user doesn't have to
|
||||
#include "orm/schema/blueprint.hpp"
|
||||
|
||||
|
||||
@@ -1338,3 +1338,13 @@ TINYORM_END_COMMON_NAMESPACE
|
||||
// CUR tom, add tabulate to comments where range-v3 is, all checked, only docs left silverqx
|
||||
// CUR tom, verify -isystem $$shell_quote() updated in docs silverqx
|
||||
// CUR tom, tom/conf.pri is used by who silverqx
|
||||
// CUR cmake, add messages about Building tom example, tests and ORM silverqx
|
||||
// CUR docs, remove target_link_libs() for transitive dependencies silverqx
|
||||
// CUR tests, move version test outside of the orm/ folder silverqx
|
||||
// CUR tom docs, write documentation silverqx
|
||||
// CUR tom, build on mingw, linux, build without pch and all common tasks that should run from time to time silverqx
|
||||
// CUR tom, update docs target_link_library() https://discourse.cmake.org/t/explicitly-link-against-public-interface-dependencies/5484/2 silverqx
|
||||
// CUR tom, provide somehow a custom path to migrations for the tom example, so the tom example can be used like real migration app silverqx
|
||||
// CUR tom, don't modify migrate:status command, rather extend it and add possibility to only call it through Application::runWithArguments() (this secure to not be able to call it from the cmd. line), do not show it in the list or help command output silverqx
|
||||
// CUR tom, think about remove TINYTOM_NO/_DEBUG and TINYTOM_TESTS_CODE and use TINYORM_ defines instead silverqx
|
||||
// BUG rc file © encoding silverqx
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#if defined __cplusplus
|
||||
/* Add C++ includes here */
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QHash>
|
||||
#include <QMap>
|
||||
#include <QSharedPointer>
|
||||
@@ -38,6 +39,7 @@
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <typeindex>
|
||||
#include <typeinfo>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
@@ -30,3 +30,11 @@ CONFIG(release, debug|release): \
|
||||
# Log queries with a time measurement in debug build
|
||||
CONFIG(release, debug|release): \
|
||||
DEFINES *= TINYORM_NO_DEBUG_SQL
|
||||
|
||||
# TinyTom related defines
|
||||
# ---
|
||||
|
||||
# Release build
|
||||
CONFIG(release, debug|release): DEFINES += TINYTOM_NO_DEBUG
|
||||
# Debug build
|
||||
CONFIG(debug, debug|release): DEFINES *= TINYTOM_DEBUG
|
||||
|
||||
@@ -30,11 +30,25 @@ DEFINES *= QT_NO_CAST_FROM_BYTEARRAY
|
||||
DEFINES *= QT_USE_QSTRINGBUILDER
|
||||
DEFINES *= QT_STRICT_ITERATORS
|
||||
|
||||
# TinyORM configuration
|
||||
# ---
|
||||
|
||||
# Use extern constants for shared build
|
||||
CONFIG(shared, dll|shared|static|staticlib) | \
|
||||
CONFIG(dll, dll|shared|static|staticlib): \
|
||||
# Support override because inline_constants can be used in the shared build too
|
||||
!inline_constants: \
|
||||
CONFIG += extern_constants
|
||||
|
||||
# Archive library build (static build)
|
||||
else: \
|
||||
CONFIG += inline_constants
|
||||
|
||||
# TinyORM defines
|
||||
# ---
|
||||
|
||||
# Release build
|
||||
CONFIG(release, debug|release): DEFINES *= TINYORM_NO_DEBUG
|
||||
CONFIG(release, debug|release): DEFINES += TINYORM_NO_DEBUG
|
||||
# Debug build
|
||||
CONFIG(debug, debug|release): DEFINES *= TINYORM_DEBUG
|
||||
|
||||
@@ -42,9 +56,27 @@ CONFIG(debug, debug|release): DEFINES *= TINYORM_DEBUG
|
||||
mysql_ping: DEFINES *= TINYORM_MYSQL_PING
|
||||
|
||||
# Log queries with a time measurement
|
||||
CONFIG(release, debug|release): DEFINES *= TINYORM_NO_DEBUG_SQL
|
||||
CONFIG(release, debug|release): DEFINES += TINYORM_NO_DEBUG_SQL
|
||||
CONFIG(debug, debug|release): DEFINES *= TINYORM_DEBUG_SQL
|
||||
|
||||
# Enable code needed by tests, eg. connection overriding in the Model
|
||||
!disable_orm:build_tests: \
|
||||
DEFINES *= TINYORM_TESTS_CODE
|
||||
|
||||
# TinyTom related defines
|
||||
# ---
|
||||
|
||||
!disable_tom {
|
||||
# Release build
|
||||
CONFIG(release, debug|release): DEFINES += TINYTOM_NO_DEBUG
|
||||
# Debug build
|
||||
CONFIG(debug, debug|release): DEFINES *= TINYTOM_DEBUG
|
||||
|
||||
# Enable code needed by tests (modify the migrate:status command for tests need)
|
||||
build_tests: \
|
||||
DEFINES *= TINYTOM_TESTS_CODE
|
||||
}
|
||||
|
||||
# Platform specific configuration
|
||||
# ---
|
||||
win32: include(winconf.pri)
|
||||
@@ -62,17 +94,3 @@ debug_and_release: {
|
||||
TINY_RELEASE_TYPE = $$quote(/debug)
|
||||
}
|
||||
else: TINY_RELEASE_TYPE =
|
||||
|
||||
# Other
|
||||
# ---
|
||||
|
||||
# Use extern constants for shared build
|
||||
CONFIG(shared, dll|shared|static|staticlib) | \
|
||||
CONFIG(dll, dll|shared|static|staticlib): \
|
||||
# Support override because inline_constants can be used in the shared build too
|
||||
!inline_constants: \
|
||||
CONFIG += extern_constants
|
||||
|
||||
# Archive library build (static build)
|
||||
else: \
|
||||
CONFIG += inline_constants
|
||||
|
||||
@@ -14,16 +14,12 @@ defineTest(tiny_resource_and_manifest) {
|
||||
isEmpty(2): resourcesFolder = $$absolute_path(resources, $$_PRO_FILE_PWD_)
|
||||
else: resourcesFolder = $$absolute_path($$2)
|
||||
|
||||
# Processing of RC file and manifest file for the test?
|
||||
defined(3, var):$$3: isTest = true
|
||||
else: isTest = false
|
||||
|
||||
# Target's extension
|
||||
contains(TEMPLATE, ".*app"): targetExt = ".exe"
|
||||
else:contains(TEMPLATE, ".*lib"): targetExt = ".dll"
|
||||
|
||||
# Windows Resource file
|
||||
rcFile = $$tiny_configure_cmake_rc($$resourcesFolder, $$targetExt, $$isTest)
|
||||
rcFile = $$tiny_configure_cmake_rc($$resourcesFolder, $$targetExt, $$3, $$4)
|
||||
|
||||
# Needed in the RC file, MinGW does not define _DEBUG macro
|
||||
mingw:CONFIG(debug, debug|release): DEFINES += _DEBUG
|
||||
@@ -34,8 +30,8 @@ defineTest(tiny_resource_and_manifest) {
|
||||
# Manifest file
|
||||
CONFIG -= embed_manifest_dll
|
||||
|
||||
# Use the same manifest file for all tests
|
||||
$$isTest: manifestBasename = TinyTest
|
||||
# Allow to pass RC/manifest file basename as 3. argument (default is a $$TARGET value)
|
||||
defined(3, var):!isEmpty(3): manifestBasename = $$3
|
||||
else: manifestBasename = $$TARGET
|
||||
|
||||
# On MSVC use EMBED and on MinGW injected through the RC file
|
||||
@@ -56,7 +52,7 @@ defineTest(tiny_resource_and_manifest) {
|
||||
# RC file than manage two practically the same files.
|
||||
defineReplace(tiny_configure_cmake_rc) {
|
||||
# All tests use the same test.rc.in file
|
||||
defined(3, var):$$3: rcBasename = TinyTest
|
||||
defined(3, var):!isEmpty(3): rcBasename = $$3
|
||||
else: rcBasename = $$TARGET
|
||||
|
||||
rcFile = $$absolute_path($$1/$${rcBasename}.rc.in)
|
||||
@@ -75,8 +71,9 @@ defineReplace(tiny_configure_cmake_rc) {
|
||||
|
||||
tiny_manifest_basename = $$rcBasename
|
||||
|
||||
# The same logic for the substitution token, is 'test' for tests and $$TARGET instead
|
||||
token = $$rcBasename
|
||||
# Allow to pass a custom token as 4. argument (default is a $$TARGET value)
|
||||
defined(4, var):!isEmpty(4): token = $$4
|
||||
else: token = $$rcBasename
|
||||
|
||||
rcFileContent = $$cat($$rcFile, blob)
|
||||
|
||||
|
||||
@@ -1,11 +1,29 @@
|
||||
# Find version numbers in the version header file and assign them to the
|
||||
# <TARGET>_VERSION_<MAJOR,MINOR,PATCH,TWEAK> and also to the VERSION variable.
|
||||
defineTest(tiny_version_numbers) {
|
||||
versionHeader = $$find(HEADERS, "(?:.*\/)?version\.h(?:pp)?$")
|
||||
exists(versionHeader)|isEmpty(versionHeader) {
|
||||
# version.hpp for the TinyORM library project
|
||||
contains(_PRO_FILE_, ".*\/src\/src\.pro$"): \
|
||||
versionHeader = $$absolute_path("../include/orm/version.hpp")
|
||||
|
||||
# version.hpp for the tom example project
|
||||
else:contains(_PRO_FILE_, ".*\/examples\/tom\/tom\.pro$"): \
|
||||
versionHeader = $$absolute_path("../tom/include/tom/version.hpp")
|
||||
|
||||
else {
|
||||
# Try to find in the HEADERS
|
||||
versionHeader = $$find(HEADERS, "(?:.*\/)?version\.h(?:pp)?$")
|
||||
|
||||
# Try to find on the INCLUDEPATH
|
||||
isEmpty(versionHeader) {
|
||||
versionHeaders = $$files("$$clean_path($$first(INCLUDEPATH))/version.hpp", \
|
||||
true)
|
||||
versionHeader = $$first(versionHeaders)
|
||||
}
|
||||
}
|
||||
|
||||
exists(versionHeader)|isEmpty(versionHeader): \
|
||||
error( "HEADERS does not contain a version header file version.hpp, needed\
|
||||
in the tiny_version_numbers.prf." )
|
||||
}
|
||||
|
||||
versionFileContent = $$cat($$quote($$absolute_path($$versionHeader)), lines)
|
||||
versionTokens = MAJOR MINOR BUGFIX BUILD STATUS
|
||||
@@ -38,9 +56,8 @@ defineTest(tiny_version_numbers) {
|
||||
$$hasStatus: versionStatus = $$take_last(versionList)
|
||||
|
||||
# Use 3 numbers version on other platforms
|
||||
!win32 {
|
||||
!win32: \
|
||||
$$take_last(versionList)
|
||||
}
|
||||
|
||||
VERSION = $$join(versionList, '.')$$versionStatus
|
||||
export(VERSION)
|
||||
|
||||
2
qmake/features/tom_example.prf
Normal file
2
qmake/features/tom_example.prf
Normal file
@@ -0,0 +1,2 @@
|
||||
CONFIG *= tom_example
|
||||
DEFINES *= TINYORM_TOM_EXAMPLE
|
||||
54
qmake/tom.pri
Normal file
54
qmake/tom.pri
Normal file
@@ -0,0 +1,54 @@
|
||||
TINYORM_SOURCE_TREE = $$clean_path($$quote($$PWD/..))
|
||||
TINYTOM_SOURCE_TREE = $$quote($$TINYORM_SOURCE_TREE/tom)
|
||||
|
||||
# Qt Common Configuration
|
||||
# ---
|
||||
|
||||
QT *= core sql
|
||||
|
||||
CONFIG *= link_prl
|
||||
|
||||
include($$TINYORM_SOURCE_TREE/qmake/common.pri)
|
||||
|
||||
# Configure TinyORM library
|
||||
# ---
|
||||
# everything other is defined in the qmake/common.pri
|
||||
|
||||
# Link with the shared library
|
||||
CONFIG(shared, dll|shared|static|staticlib) | \
|
||||
CONFIG(dll, dll|shared|static|staticlib): \
|
||||
DEFINES *= TINYORM_LINKING_SHARED
|
||||
|
||||
# File version
|
||||
# ---
|
||||
|
||||
# Find version numbers in the version header file and assign them to the
|
||||
# <TARGET>_VERSION_<MAJOR,MINOR,PATCH,TWEAK> and also to the VERSION variable.
|
||||
load(tiny_version_numbers)
|
||||
tiny_version_numbers()
|
||||
|
||||
# Windows resource and manifest files
|
||||
# ---
|
||||
|
||||
# Find version.hpp
|
||||
tinyRcIncludepath = $$quote($$TINYTOM_SOURCE_TREE/include/)
|
||||
# Find Windows manifest
|
||||
mingw: tinyRcIncludepath += $$quote($$TINYTOM_SOURCE_TREE/resources/)
|
||||
|
||||
load(tiny_resource_and_manifest)
|
||||
tiny_resource_and_manifest( \
|
||||
$$tinyRcIncludepath, $$TINYTOM_SOURCE_TREE/resources, tom, TomExample \
|
||||
)
|
||||
|
||||
# Link against TinyORM library
|
||||
# ---
|
||||
|
||||
INCLUDEPATH *= \
|
||||
$$quote($$TINYORM_SOURCE_TREE/include/) \
|
||||
$$quote($$TINYTOM_SOURCE_TREE/include/) \
|
||||
|
||||
!isEmpty(TINYORM_BUILD_TREE): \
|
||||
exists($$TINYORM_BUILD_TREE): {
|
||||
LIBS += $$quote(-L$$TINYORM_BUILD_TREE/src$${TINY_RELEASE_TYPE}/)
|
||||
LIBS += -lTinyOrm
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma code_page(65001) // UTF-8
|
||||
//#pragma code_page(65001) // UTF-8
|
||||
|
||||
IDI_ICON1 ICON "icons/@TinyOrm_target@.ico"
|
||||
|
||||
|
||||
@@ -61,6 +61,9 @@ sourcesList += \
|
||||
$$PWD/orm/tiny/tinytypes.cpp \
|
||||
$$PWD/orm/tiny/types/syncchanges.cpp \
|
||||
$$PWD/orm/tiny/utils/attribute.cpp \
|
||||
|
||||
!disable_orm|!disable_tom: \
|
||||
sourcesList += \
|
||||
$$PWD/orm/tiny/utils/string.cpp \
|
||||
|
||||
SOURCES += $$sorted(sourcesList)
|
||||
|
||||
24
src/src.pro
24
src/src.pro
@@ -39,10 +39,6 @@ CONFIG(shared, dll|shared|static|staticlib) | \
|
||||
CONFIG(dll, dll|shared|static|staticlib): \
|
||||
DEFINES *= TINYORM_BUILDING_SHARED
|
||||
|
||||
# Enable code needed by tests, eg. connection overriding in the Model
|
||||
build_tests: \
|
||||
DEFINES *= TINYORM_TESTS_CODE
|
||||
|
||||
# TinyORM library header and source files
|
||||
# ---
|
||||
|
||||
@@ -50,6 +46,14 @@ build_tests: \
|
||||
include(../include/include.pri)
|
||||
include(src.pri)
|
||||
|
||||
# TinyTom header and source files
|
||||
# ---
|
||||
|
||||
!disable_tom {
|
||||
include(../tom/include/include.pri)
|
||||
include(../tom/src/src.pri)
|
||||
}
|
||||
|
||||
# File version
|
||||
# ---
|
||||
|
||||
@@ -85,8 +89,16 @@ win32-msvc:CONFIG(debug, debug|release) {
|
||||
# Some info output
|
||||
# ---
|
||||
|
||||
CONFIG(debug, debug|release):!build_pass: message( "Project is built in DEBUG mode." )
|
||||
CONFIG(release, debug|release):!build_pass: message( "Project is built in RELEASE mode." )
|
||||
!build_pass {
|
||||
CONFIG(debug, debug|release): message( "Project is built in DEBUG mode." )
|
||||
CONFIG(release, debug|release): message( "Project is built in RELEASE mode." )
|
||||
|
||||
!disable_orm: message("Build ORM-related source code.")
|
||||
else: message("Disable ORM-related source code (build the query builder \
|
||||
only).")
|
||||
|
||||
mysql_ping: message("Enable MySQL ping on Orm::MySqlConnection.")
|
||||
}
|
||||
|
||||
# User Configuration
|
||||
# ---
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma code_page(65001) // UTF-8
|
||||
//#pragma code_page(65001) // UTF-8
|
||||
|
||||
//IDI_ICON1 ICON "icons/@TinyUtils_target@.ico"
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
/* Add C++ includes here */
|
||||
//#include <QCoreApplication>
|
||||
#include <QDateTime>
|
||||
//#include <QDebug>
|
||||
#include <QHash>
|
||||
#include <QMap>
|
||||
#include <QSharedPointer>
|
||||
@@ -38,7 +39,8 @@
|
||||
//#include <thread>
|
||||
#include <tuple>
|
||||
//#include <type_traits>
|
||||
#include <typeinfo>
|
||||
//#include <typeindex>
|
||||
//#include <typeinfo>
|
||||
//#include <unordered_map>
|
||||
//#include <vector>
|
||||
#endif
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
add_subdirectory(orm)
|
||||
|
||||
if(TOM)
|
||||
add_subdirectory(tom)
|
||||
endif()
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS = \
|
||||
subdirsList = \
|
||||
orm \
|
||||
|
||||
!disable_tom: \
|
||||
subdirsList += \
|
||||
tom \
|
||||
|
||||
SUBDIRS = $$sorted(subdirsList)
|
||||
|
||||
unset(subdirsList)
|
||||
|
||||
1
tests/auto/functional/tom/CMakeLists.txt
Normal file
1
tests/auto/functional/tom/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
||||
add_subdirectory(migrate)
|
||||
18
tests/auto/functional/tom/migrate/CMakeLists.txt
Normal file
18
tests/auto/functional/tom/migrate/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
# migrate auto test
|
||||
# ---
|
||||
|
||||
set(migrate_ns migrate)
|
||||
set(migrate_target ${migrate_ns})
|
||||
|
||||
project(${migrate_ns}
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
add_executable(${migrate_target}
|
||||
tst_migrate.cpp
|
||||
)
|
||||
|
||||
add_test(NAME ${migrate_target} COMMAND ${migrate_target})
|
||||
|
||||
include(TinyTestCommon)
|
||||
tiny_configure_test(${migrate_target} INCLUDE_MIGRATIONS)
|
||||
5
tests/auto/functional/tom/migrate/migrate.pro
Normal file
5
tests/auto/functional/tom/migrate/migrate.pro
Normal file
@@ -0,0 +1,5 @@
|
||||
include($$TINYORM_SOURCE_TREE/tests/qmake/common.pri)
|
||||
include($$TINYORM_SOURCE_TREE/tests/qmake/TinyUtils.pri)
|
||||
include($$TINYORM_SOURCE_TREE/tests/database/migrations.pri)
|
||||
|
||||
SOURCES += tst_migrate.cpp
|
||||
585
tests/auto/functional/tom/migrate/tst_migrate.cpp
Normal file
585
tests/auto/functional/tom/migrate/tst_migrate.cpp
Normal file
@@ -0,0 +1,585 @@
|
||||
#include <QCoreApplication>
|
||||
#include <QtTest>
|
||||
|
||||
#include "tom/application.hpp"
|
||||
#include "tom/commands/migrations/statuscommand.hpp"
|
||||
|
||||
#include "databases.hpp"
|
||||
|
||||
#include "migrations/2014_10_12_000000_create_posts_table.hpp"
|
||||
#include "migrations/2014_10_12_100000_add_factor_column_to_posts_table.hpp"
|
||||
#include "migrations/2014_10_12_200000_create_properties_table.hpp"
|
||||
#include "migrations/2014_10_12_300000_create_phones_table.hpp"
|
||||
|
||||
using namespace Migrations; // NOLINT(google-build-using-namespace)
|
||||
|
||||
using TomApplication = Tom::Application;
|
||||
|
||||
using Tom::Commands::Migrations::StatusCommand;
|
||||
|
||||
using TestUtils::Databases;
|
||||
|
||||
class tst_Migrate : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/*! Alias for the test output row. */
|
||||
using StatusRow = StatusCommand::StatusRow;
|
||||
/*! Type used for comparing results of the status command. */
|
||||
using Status = std::vector<StatusRow>;
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void cleanup() const;
|
||||
|
||||
void reset() const;
|
||||
|
||||
void migrate() const;
|
||||
void migrate_Step() const;
|
||||
|
||||
void rollback_OnMigrate() const;
|
||||
void rollback_OnMigrateWithStep() const;
|
||||
|
||||
void rollback_Step_OnMigrate() const;
|
||||
void rollback_Step_OnMigrateWithStep() const;
|
||||
|
||||
void refresh_OnMigrate() const;
|
||||
void refresh_OnMigrateWithStep() const;
|
||||
|
||||
void refresh_Step() const;
|
||||
void refresh_StepMigrate() const;
|
||||
void refresh_Step_StepMigrate() const;
|
||||
|
||||
// NOLINTNEXTLINE(readability-redundant-access-specifiers)
|
||||
private:
|
||||
/*! Prepare arguments and invoke runCommand(). */
|
||||
[[nodiscard]] int
|
||||
invokeCommand(const QString &name, std::vector<const char *> &&arguments = {}) const;
|
||||
/*! Create a tom application instance and invoke the given command. */
|
||||
int runCommand(int &argc, const std::vector<const char *> &argv) const;
|
||||
|
||||
/*! Invoke the status command to obtain results. */
|
||||
inline int invokeTestStatusCommand() const;
|
||||
/*! Get result of the last status command. */
|
||||
Status status() const;
|
||||
/*! Create a status object for comparing with the result of the status(). */
|
||||
Status createStatus(std::initializer_list<StatusRow> rows) const;
|
||||
/*! Create a status object to be equal after complete rollback. */
|
||||
Status createResetStatus() const;
|
||||
|
||||
/*! Connection name used in this test case. */
|
||||
QString m_connection {};
|
||||
};
|
||||
|
||||
/*! Alias for the test output row. */
|
||||
using Status = tst_Migrate::Status;
|
||||
/*! Type used for comparing results of the status command. */
|
||||
using StatusRow = tst_Migrate::StatusRow;
|
||||
|
||||
/* Extracted common code to re-use. */
|
||||
namespace
|
||||
{
|
||||
// Status
|
||||
inline const auto *Yes = "Yes";
|
||||
inline const auto *No = "No";
|
||||
|
||||
// Batches
|
||||
inline const auto *s_1 = "1";
|
||||
inline const auto *s_2 = "2";
|
||||
inline const auto *s_3 = "3";
|
||||
inline const auto *s_4 = "4";
|
||||
|
||||
// Migration names
|
||||
inline const auto *
|
||||
s_2014_10_12_000000_create_posts_table =
|
||||
"2014_10_12_000000_create_posts_table";
|
||||
inline const auto *
|
||||
s_2014_10_12_100000_add_factor_column_to_posts_table =
|
||||
"2014_10_12_100000_add_factor_column_to_posts_table";
|
||||
inline const auto *
|
||||
s_2014_10_12_200000_create_properties_table =
|
||||
"2014_10_12_200000_create_properties_table";
|
||||
inline const auto *
|
||||
s_2014_10_12_300000_create_phones_table =
|
||||
"2014_10_12_300000_create_phones_table";
|
||||
|
||||
// Fully migrated w/o --step
|
||||
inline const std::initializer_list<StatusRow>
|
||||
FullyMigrated = {
|
||||
{Yes, s_2014_10_12_000000_create_posts_table, s_1},
|
||||
{Yes, s_2014_10_12_100000_add_factor_column_to_posts_table, s_1},
|
||||
{Yes, s_2014_10_12_200000_create_properties_table, s_1},
|
||||
{Yes, s_2014_10_12_300000_create_phones_table, s_1},
|
||||
};
|
||||
|
||||
// Fully migrated with --step
|
||||
inline const std::initializer_list<StatusRow>
|
||||
FullyStepMigrated = {
|
||||
{Yes, s_2014_10_12_000000_create_posts_table, s_1},
|
||||
{Yes, s_2014_10_12_100000_add_factor_column_to_posts_table, s_2},
|
||||
{Yes, s_2014_10_12_200000_create_properties_table, s_3},
|
||||
{Yes, s_2014_10_12_300000_create_phones_table, s_4},
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
/* private slots */
|
||||
|
||||
void tst_Migrate::initTestCase()
|
||||
{
|
||||
m_connection = Databases::createConnection(Databases::MYSQL);
|
||||
|
||||
if (m_connection.isEmpty())
|
||||
QSKIP(QStringLiteral("%1 autotest skipped, environment variables "
|
||||
"for '%2' connection have not been defined.")
|
||||
.arg("tst_Migrate", Databases::MYSQL).toUtf8().constData(), );
|
||||
|
||||
/* Modify the migrate:status command to not output a status table to the console but
|
||||
instead return a result as the vector, this vector is then used for comparing
|
||||
results. */
|
||||
TomApplication::enableInUnitTests();
|
||||
}
|
||||
|
||||
void tst_Migrate::cleanup() const
|
||||
{
|
||||
/* All test methods need this except for two of them (reset and I don't remember
|
||||
second), I will not implement special logic to skip this for these two methods. */
|
||||
{
|
||||
auto exitCode = invokeCommand("migrate:reset");
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
auto exitCode = invokeTestStatusCommand();
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
QCOMPARE(createResetStatus(), status());
|
||||
}
|
||||
}
|
||||
|
||||
void tst_Migrate::reset() const
|
||||
{
|
||||
{
|
||||
auto exitCode = invokeCommand("migrate:reset");
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
auto exitCode = invokeTestStatusCommand();
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
QCOMPARE(createResetStatus(), status());
|
||||
}
|
||||
}
|
||||
|
||||
void tst_Migrate::migrate() const
|
||||
{
|
||||
{
|
||||
auto exitCode = invokeCommand("migrate");
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
auto exitCode = invokeTestStatusCommand();
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
QCOMPARE(createStatus(FullyMigrated), status());
|
||||
}
|
||||
}
|
||||
|
||||
void tst_Migrate::migrate_Step() const
|
||||
{
|
||||
{
|
||||
auto exitCode = invokeCommand("migrate", {"--step"});
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
auto exitCode = invokeTestStatusCommand();
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
QCOMPARE(createStatus(FullyStepMigrated), status());
|
||||
}
|
||||
}
|
||||
|
||||
void tst_Migrate::rollback_OnMigrate() const
|
||||
{
|
||||
{
|
||||
auto exitCode = invokeCommand("migrate");
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
auto exitCode = invokeTestStatusCommand();
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
QCOMPARE(createStatus(FullyMigrated), status());
|
||||
}
|
||||
|
||||
// rollback on previous migrate w/o --step
|
||||
{
|
||||
auto exitCode = invokeCommand("migrate:rollback");
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
auto exitCode = invokeTestStatusCommand();
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
QCOMPARE(createResetStatus(), status());
|
||||
}
|
||||
}
|
||||
|
||||
void tst_Migrate::rollback_OnMigrateWithStep() const
|
||||
{
|
||||
{
|
||||
auto exitCode = invokeCommand("migrate", {"--step"});
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
auto exitCode = invokeTestStatusCommand();
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
QCOMPARE(createStatus(FullyStepMigrated), status());
|
||||
}
|
||||
|
||||
// rollback on previous migrate with --step
|
||||
{
|
||||
auto exitCode = invokeCommand("migrate:rollback");
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
auto exitCode = invokeTestStatusCommand();
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
QCOMPARE(createStatus({
|
||||
{Yes, s_2014_10_12_000000_create_posts_table, s_1},
|
||||
{Yes, s_2014_10_12_100000_add_factor_column_to_posts_table, s_2},
|
||||
{Yes, s_2014_10_12_200000_create_properties_table, s_3},
|
||||
{No, s_2014_10_12_300000_create_phones_table},
|
||||
}), status());
|
||||
}
|
||||
}
|
||||
|
||||
void tst_Migrate::rollback_Step_OnMigrate() const
|
||||
{
|
||||
{
|
||||
auto exitCode = invokeCommand("migrate");
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
auto exitCode = invokeTestStatusCommand();
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
QCOMPARE(createStatus(FullyMigrated), status());
|
||||
}
|
||||
|
||||
// rollback on previous migrate w/o --step
|
||||
{
|
||||
auto exitCode = invokeCommand("migrate:rollback", {"--step=2"});
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
auto exitCode = invokeTestStatusCommand();
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
QCOMPARE(createStatus({
|
||||
{Yes, s_2014_10_12_000000_create_posts_table, s_1},
|
||||
{Yes, s_2014_10_12_100000_add_factor_column_to_posts_table, s_1},
|
||||
{No, s_2014_10_12_200000_create_properties_table},
|
||||
{No, s_2014_10_12_300000_create_phones_table},
|
||||
}), status());
|
||||
}
|
||||
}
|
||||
|
||||
void tst_Migrate::rollback_Step_OnMigrateWithStep() const
|
||||
{
|
||||
{
|
||||
auto exitCode = invokeCommand("migrate", {"--step"});
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
auto exitCode = invokeTestStatusCommand();
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
QCOMPARE(createStatus(FullyStepMigrated), status());
|
||||
}
|
||||
|
||||
// rollback on previous migrate with --step
|
||||
{
|
||||
auto exitCode = invokeCommand("migrate:rollback", {"--step=2"});
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
auto exitCode = invokeTestStatusCommand();
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
QCOMPARE(createStatus({
|
||||
{Yes, s_2014_10_12_000000_create_posts_table, s_1},
|
||||
{Yes, s_2014_10_12_100000_add_factor_column_to_posts_table, s_2},
|
||||
{No, s_2014_10_12_200000_create_properties_table},
|
||||
{No, s_2014_10_12_300000_create_phones_table},
|
||||
}), status());
|
||||
}
|
||||
}
|
||||
|
||||
void tst_Migrate::refresh_OnMigrate() const
|
||||
{
|
||||
{
|
||||
auto exitCode = invokeCommand("migrate");
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
auto exitCode = invokeTestStatusCommand();
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
QCOMPARE(createStatus(FullyMigrated), status());
|
||||
}
|
||||
|
||||
// refresh on previous migrate w/o --step
|
||||
{
|
||||
auto exitCode = invokeCommand("migrate:refresh");
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
auto exitCode = invokeTestStatusCommand();
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
QCOMPARE(createStatus(FullyMigrated), status());
|
||||
}
|
||||
}
|
||||
|
||||
void tst_Migrate::refresh_OnMigrateWithStep() const
|
||||
{
|
||||
{
|
||||
auto exitCode = invokeCommand("migrate", {"--step"});
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
auto exitCode = invokeTestStatusCommand();
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
QCOMPARE(createStatus(FullyStepMigrated), status());
|
||||
}
|
||||
|
||||
// refresh on previous migrate with --step
|
||||
{
|
||||
auto exitCode = invokeCommand("migrate:refresh");
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
auto exitCode = invokeTestStatusCommand();
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
QCOMPARE(createStatus(FullyMigrated), status());
|
||||
}
|
||||
}
|
||||
|
||||
void tst_Migrate::refresh_Step() const
|
||||
{
|
||||
{
|
||||
auto exitCode = invokeCommand("migrate");
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
auto exitCode = invokeTestStatusCommand();
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
QCOMPARE(createStatus(FullyMigrated), status());
|
||||
}
|
||||
|
||||
// refresh on previous migrate w/o --step
|
||||
{
|
||||
auto exitCode = invokeCommand("migrate:refresh", {"--step=2"});
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
auto exitCode = invokeTestStatusCommand();
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
QCOMPARE(createStatus({
|
||||
{Yes, s_2014_10_12_000000_create_posts_table, s_1},
|
||||
{Yes, s_2014_10_12_100000_add_factor_column_to_posts_table, s_1},
|
||||
{Yes, s_2014_10_12_200000_create_properties_table, s_2},
|
||||
{Yes, s_2014_10_12_300000_create_phones_table, s_2},
|
||||
}), status());
|
||||
}
|
||||
}
|
||||
|
||||
void tst_Migrate::refresh_StepMigrate() const
|
||||
{
|
||||
{
|
||||
auto exitCode = invokeCommand("migrate");
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
auto exitCode = invokeTestStatusCommand();
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
QCOMPARE(createStatus(FullyMigrated), status());
|
||||
}
|
||||
|
||||
// refresh on previous migrate w/o --step
|
||||
{
|
||||
auto exitCode = invokeCommand("migrate:refresh", {"--step-migrate"});
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
auto exitCode = invokeTestStatusCommand();
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
QCOMPARE(createStatus(FullyStepMigrated), status());
|
||||
}
|
||||
}
|
||||
|
||||
void tst_Migrate::refresh_Step_StepMigrate() const
|
||||
{
|
||||
{
|
||||
auto exitCode = invokeCommand("migrate");
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
auto exitCode = invokeTestStatusCommand();
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
QCOMPARE(createStatus(FullyMigrated), status());
|
||||
}
|
||||
|
||||
// refresh on previous migrate w/o --step
|
||||
{
|
||||
auto exitCode = invokeCommand("migrate:refresh", {"--step=2", "--step-migrate"});
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
{
|
||||
auto exitCode = invokeTestStatusCommand();
|
||||
|
||||
QVERIFY(exitCode == EXIT_SUCCESS);
|
||||
QCOMPARE(createStatus({
|
||||
{Yes, s_2014_10_12_000000_create_posts_table, s_1},
|
||||
{Yes, s_2014_10_12_100000_add_factor_column_to_posts_table, s_1},
|
||||
{Yes, s_2014_10_12_200000_create_properties_table, s_2},
|
||||
{Yes, s_2014_10_12_300000_create_phones_table, s_3},
|
||||
}), status());
|
||||
}
|
||||
}
|
||||
|
||||
/* private */
|
||||
|
||||
int tst_Migrate::invokeCommand(const QString &name,
|
||||
std::vector<const char *> &&arguments) const
|
||||
{
|
||||
static const auto connectionTmpl = QStringLiteral("--database=%1");
|
||||
|
||||
// Prepare fake argc and argv
|
||||
const auto nameArr = name.toUtf8();
|
||||
// FUTURE tests tom, when the schema builder will support more db drivers, I can run it on all supported connections, code will look like in the tst_querybuilder.cpp, then I will fetch connection name in every test method using QFETCH_GLOBAL() and I will pass this connection name to the invokeCommand(), so I will discard m_connection and will use method parameter connection here silverqx
|
||||
/* Schema builder is implemented only for the MySQL driver, so I can use m_connection
|
||||
here as the default connection. */
|
||||
// DB connection to use
|
||||
const auto connectionArr = connectionTmpl.arg(m_connection).toUtf8();
|
||||
|
||||
std::vector<const char *> argv {
|
||||
#ifdef _WIN32
|
||||
"tom.exe",
|
||||
#else
|
||||
"tom",
|
||||
#endif
|
||||
nameArr.constData(),
|
||||
connectionArr.constData(),
|
||||
};
|
||||
std::ranges::move(arguments, std::back_inserter(argv));
|
||||
|
||||
int argc = static_cast<int>(argv.size());
|
||||
|
||||
return runCommand(argc, argv);
|
||||
}
|
||||
|
||||
int tst_Migrate::runCommand(int &argc, const std::vector<const char *> &argv) const
|
||||
{
|
||||
try {
|
||||
// env. should be always development so passed {} for env. name
|
||||
return TomApplication(argc, const_cast<char **>(argv.data()),
|
||||
Databases::manager(), {})
|
||||
.migrations<
|
||||
_2014_10_12_000000_create_posts_table,
|
||||
_2014_10_12_100000_add_factor_column_to_posts_table,
|
||||
_2014_10_12_200000_create_properties_table,
|
||||
_2014_10_12_300000_create_phones_table>()
|
||||
// Fire it up 🔥🚀✨
|
||||
.runWithArguments({argv.cbegin(), argv.cend()});
|
||||
|
||||
} catch (const std::exception &e) {
|
||||
|
||||
TomApplication::logException(e, true);
|
||||
}
|
||||
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
int tst_Migrate::invokeTestStatusCommand() const
|
||||
{
|
||||
return invokeCommand("migrate:status");
|
||||
}
|
||||
|
||||
Status tst_Migrate::status() const
|
||||
{
|
||||
return TomApplication::status();
|
||||
}
|
||||
|
||||
Status tst_Migrate::createStatus(std::initializer_list<StatusRow> rows) const
|
||||
{
|
||||
return Status({rows});
|
||||
}
|
||||
|
||||
Status tst_Migrate::createResetStatus() const
|
||||
{
|
||||
return Status({
|
||||
{No, s_2014_10_12_000000_create_posts_table},
|
||||
{No, s_2014_10_12_100000_add_factor_column_to_posts_table},
|
||||
{No, s_2014_10_12_200000_create_properties_table},
|
||||
{No, s_2014_10_12_300000_create_phones_table},
|
||||
});
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_Migrate)
|
||||
|
||||
#include "tst_migrate.moc"
|
||||
4
tests/auto/functional/tom/tom.pro
Normal file
4
tests/auto/functional/tom/tom.pro
Normal file
@@ -0,0 +1,4 @@
|
||||
TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS = \
|
||||
migrate \
|
||||
@@ -21,6 +21,15 @@ if(BUILD_SHARED_LIBS)
|
||||
target_compile_definitions(version PRIVATE TINYTEST_VERSION_IS_SHARED_BUILD)
|
||||
endif()
|
||||
|
||||
if(TOM_EXAMPLE)
|
||||
target_compile_definitions(version PRIVATE TINYTOM_EXAMPLE)
|
||||
|
||||
# To find tom/include/version.hpp
|
||||
target_include_directories(version PRIVATE
|
||||
"$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/tom/include>"
|
||||
)
|
||||
endif()
|
||||
|
||||
target_include_directories(version PRIVATE
|
||||
"$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/${TINY_BUILD_GENDIR}/include>"
|
||||
)
|
||||
|
||||
@@ -4,5 +4,6 @@
|
||||
|
||||
#define TINYTEST_VERSION_TINYORM_PATH "$<TARGET_FILE:@TinyOrm_target@>"
|
||||
#define TINYTEST_VERSION_TINYUTILS_PATH "$<TARGET_FILE:@TinyUtils_target@>"
|
||||
#define TINYTEST_VERSION_TOMEXAMPLE_PATH "$<$<TARGET_EXISTS:@TomExample_target@>:$<TARGET_FILE:@TomExample_target@>>"
|
||||
|
||||
#endif // TINYTESTS_VERSIONDEBUG_CMAKE_HPP
|
||||
|
||||
@@ -4,5 +4,6 @@
|
||||
|
||||
#define TINYTEST_VERSION_TINYORM_PATH \"$${TINYTEST_VERSION_TINYORM_PATH}\"
|
||||
#define TINYTEST_VERSION_TINYUTILS_PATH \"$${TINYTEST_VERSION_TINYUTILS_PATH}\"
|
||||
#define TINYTEST_VERSION_TOMEXAMPLE_PATH \"$${TINYTEST_VERSION_TOMEXAMPLE_PATH}\"
|
||||
|
||||
#endif // TINYTEST_VERSIONDEBUG_QMAKE_HPP
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
#include "orm/version.hpp"
|
||||
// TinyUtils
|
||||
#include "version.hpp"
|
||||
// Tom example
|
||||
#ifdef TINYTOM_EXAMPLE
|
||||
# include "tom/version.hpp"
|
||||
#endif
|
||||
|
||||
// Used by checkFileVersion_*() tests
|
||||
#if defined(_WIN32) && defined(TINYTEST_VERSION_IS_SHARED_BUILD)
|
||||
@@ -22,6 +26,7 @@
|
||||
#else
|
||||
# define TINYTEST_VERSION_TINYORM_PATH
|
||||
# define TINYTEST_VERSION_TINYUTILS_PATH
|
||||
# define TINYTEST_VERSION_TOMEXAMPLE_PATH
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
@@ -35,9 +40,15 @@ class tst_Version : public QObject
|
||||
private slots:
|
||||
void versions_TinyOrm() const;
|
||||
void versions_TinyUtils() const;
|
||||
#ifdef TINYTOM_EXAMPLE
|
||||
void versions_TomExample() const;
|
||||
#endif
|
||||
|
||||
void checkFileVersion_TinyOrm() const;
|
||||
void checkFileVersion_TinyUtils() const;
|
||||
#ifdef TINYTOM_EXAMPLE
|
||||
void checkFileVersion_TomExample() const;
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && defined(TINYTEST_VERSION_IS_SHARED_BUILD)
|
||||
// NOLINTNEXTLINE(readability-redundant-access-specifiers)
|
||||
@@ -125,6 +136,43 @@ void tst_Version::versions_TinyUtils() const
|
||||
QCOMPARE(TINYUTILS_VERSION, version);
|
||||
}
|
||||
|
||||
#ifdef TINYTOM_EXAMPLE
|
||||
void tst_Version::versions_TomExample() const
|
||||
{
|
||||
// Test types
|
||||
QCOMPARE(typeid (TINYTOM_VERSION_MAJOR), typeid (int));
|
||||
QCOMPARE(typeid (TINYTOM_VERSION_MINOR), typeid (int));
|
||||
QCOMPARE(typeid (TINYTOM_VERSION_BUGFIX), typeid (int));
|
||||
QCOMPARE(typeid (TINYTOM_VERSION_BUILD), typeid (int));
|
||||
|
||||
// Individual version numbers have to be greater than zero
|
||||
QVERIFY(TINYTOM_VERSION_MAJOR >= 0);
|
||||
QVERIFY(TINYTOM_VERSION_MINOR >= 0);
|
||||
QVERIFY(TINYTOM_VERSION_BUGFIX >= 0);
|
||||
QVERIFY(TINYTOM_VERSION_BUILD >= 0);
|
||||
|
||||
// Project and File Version strings
|
||||
QString versionStr = QString::number(TINYTOM_VERSION_MAJOR) + QChar('.') +
|
||||
QString::number(TINYTOM_VERSION_MINOR) + QChar('.') +
|
||||
QString::number(TINYTOM_VERSION_BUGFIX);
|
||||
QString fileVersionStr = versionStr + QChar('.') +
|
||||
QString::number(TINYTOM_VERSION_BUILD);
|
||||
if constexpr (TINYTOM_VERSION_BUILD > 0)
|
||||
versionStr += QChar('.') + QString::number(TINYTOM_VERSION_BUILD);
|
||||
versionStr += TINYTOM_VERSION_STATUS;
|
||||
|
||||
QCOMPARE(TINYTOM_FILEVERSION_STR, fileVersionStr);
|
||||
QCOMPARE(TINYTOM_VERSION_STR, versionStr);
|
||||
QCOMPARE(TINYTOM_VERSION_STR_2, QChar('v') + versionStr);
|
||||
|
||||
// Project Version number, to check API compatibility
|
||||
const auto version = TINYTOM_VERSION_MAJOR * 10000 +
|
||||
TINYTOM_VERSION_MINOR * 100 +
|
||||
TINYTOM_VERSION_BUGFIX;
|
||||
QCOMPARE(TINYTOM_VERSION, version);
|
||||
}
|
||||
#endif
|
||||
|
||||
void tst_Version::checkFileVersion_TinyOrm() const
|
||||
{
|
||||
#if !defined(_WIN32)
|
||||
@@ -167,6 +215,29 @@ void tst_Version::checkFileVersion_TinyUtils() const
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef TINYTOM_EXAMPLE
|
||||
void tst_Version::checkFileVersion_TomExample() const
|
||||
{
|
||||
#if !defined(_WIN32)
|
||||
QSKIP("checkFileVersion_*() related tests are supported on MSVC only.", );
|
||||
#elif !defined(TINYTEST_VERSION_IS_SHARED_BUILD)
|
||||
QSKIP("checkFileVersion_*() related tests are enabled for shared builds only.", );
|
||||
#else
|
||||
const auto fileVersions =
|
||||
getExeVersionString(Fs::absolutePath(TINYTEST_VERSION_TOMEXAMPLE_PATH));
|
||||
|
||||
// Project and File Version strings
|
||||
const QString versionStr = QString::number(TINYTOM_VERSION_MAJOR) + QChar('.') +
|
||||
QString::number(TINYTOM_VERSION_MINOR) + QChar('.') +
|
||||
QString::number(TINYTOM_VERSION_BUGFIX) + QChar('.') +
|
||||
QString::number(TINYTOM_VERSION_BUILD);
|
||||
|
||||
QCOMPARE(fileVersions.productVersion, versionStr);
|
||||
QCOMPARE(fileVersions.fileVersion, fileVersions.productVersion);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && defined(TINYTEST_VERSION_IS_SHARED_BUILD)
|
||||
tst_Version::FileVersions
|
||||
tst_Version::getExeVersionString(const QString &fileName) const
|
||||
|
||||
@@ -7,6 +7,9 @@ SOURCES = tst_version.cpp
|
||||
win32 {
|
||||
DEFINES += TINYTEST_VERSION_IS_QMAKE
|
||||
|
||||
tom_example:!disable_tom: \
|
||||
DEFINES += TINYTOM_EXAMPLE
|
||||
|
||||
CONFIG(shared, dll|shared|static|staticlib) | \
|
||||
CONFIG(dll, dll|shared|static|staticlib): \
|
||||
DEFINES += TINYTEST_VERSION_IS_SHARED_BUILD
|
||||
@@ -15,6 +18,8 @@ win32 {
|
||||
$$quote($${TINYORM_BUILD_TREE}/src$${TINY_RELEASE_TYPE}/TinyOrm0.dll)
|
||||
TINYTEST_VERSION_TINYUTILS_PATH = \
|
||||
$$quote($${TINYORM_BUILD_TREE}/tests/TinyUtils$${TINY_RELEASE_TYPE}/TinyUtils0.dll)
|
||||
TINYTEST_VERSION_TOMEXAMPLE_PATH = \
|
||||
$$quote($${TINYORM_BUILD_TREE}/examples/tom$${TINY_RELEASE_TYPE}/tom.exe)
|
||||
|
||||
QMAKE_SUBSTITUTES += $$quote(include/versiondebug_qmake.hpp.in)
|
||||
|
||||
@@ -22,5 +27,9 @@ win32 {
|
||||
|
||||
INCLUDEPATH += $$quote($$OUT_PWD/include/)
|
||||
|
||||
# To find tom/include/version.hpp (don't need to include whole qmake/tom.pri)
|
||||
tom_example:!disable_tom: \
|
||||
INCLUDEPATH += $$quote($$TINYORM_SOURCE_TREE/tom/include/)
|
||||
|
||||
LIBS += -lVersion
|
||||
}
|
||||
|
||||
7
tests/database/migrations.pri
Normal file
7
tests/database/migrations.pri
Normal file
@@ -0,0 +1,7 @@
|
||||
INCLUDEPATH += $$PWD
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/migrations/2014_10_12_000000_create_posts_table.hpp \
|
||||
$$PWD/migrations/2014_10_12_100000_add_factor_column_to_posts_table.hpp \
|
||||
$$PWD/migrations/2014_10_12_200000_create_properties_table.hpp \
|
||||
$$PWD/migrations/2014_10_12_300000_create_phones_table.hpp \
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <tom/migration.hpp>
|
||||
|
||||
namespace Migrations
|
||||
{
|
||||
|
||||
struct _2014_10_12_000000_create_posts_table : Migration
|
||||
{
|
||||
/*! Run the migrations. */
|
||||
void up() const override
|
||||
{
|
||||
Schema::create("posts", [](Blueprint &table)
|
||||
{
|
||||
table.id();
|
||||
|
||||
table.string(NAME);
|
||||
table.timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/*! Reverse the migrations. */
|
||||
void down() const override
|
||||
{
|
||||
Schema::dropIfExists("posts");
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Migrations
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <tom/migration.hpp>
|
||||
|
||||
namespace Migrations
|
||||
{
|
||||
|
||||
struct _2014_10_12_100000_add_factor_column_to_posts_table : Migration
|
||||
{
|
||||
/*! Run the migrations. */
|
||||
void up() const override
|
||||
{
|
||||
Schema::table("posts", [](Blueprint &table)
|
||||
{
|
||||
table.integer("factor");
|
||||
});
|
||||
}
|
||||
|
||||
/*! Reverse the migrations. */
|
||||
void down() const override
|
||||
{
|
||||
Schema::table("posts", [](Blueprint &table)
|
||||
{
|
||||
table.dropColumn("factor");
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Migrations
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <tom/migration.hpp>
|
||||
|
||||
namespace Migrations
|
||||
{
|
||||
|
||||
struct _2014_10_12_200000_create_properties_table : Migration
|
||||
{
|
||||
/*! Run the migrations. */
|
||||
void up() const override
|
||||
{
|
||||
Schema::create("properties", [](Blueprint &table)
|
||||
{
|
||||
table.id();
|
||||
|
||||
table.string(NAME);
|
||||
table.timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/*! Reverse the migrations. */
|
||||
void down() const override
|
||||
{
|
||||
Schema::dropIfExists("properties");
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Migrations
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <tom/migration.hpp>
|
||||
|
||||
namespace Migrations
|
||||
{
|
||||
|
||||
struct _2014_10_12_300000_create_phones_table : Migration
|
||||
{
|
||||
/*! Run the migrations. */
|
||||
void up() const override
|
||||
{
|
||||
Schema::create("phones", [](Blueprint &table)
|
||||
{
|
||||
table.id();
|
||||
|
||||
table.string(NAME);
|
||||
table.timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/*! Reverse the migrations. */
|
||||
void down() const override
|
||||
{
|
||||
Schema::dropIfExists("phones");
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Migrations
|
||||
@@ -5,14 +5,17 @@ CONFIG(shared, dll|shared|static|staticlib) | \
|
||||
CONFIG(dll, dll|shared|static|staticlib): \
|
||||
DEFINES += TINYORM_LINKING_SHARED
|
||||
|
||||
# Enable code needed by tests, eg connection overriding in the Model
|
||||
DEFINES *= TINYORM_TESTS_CODE
|
||||
|
||||
# TinyORM library headers include path
|
||||
# ---
|
||||
|
||||
INCLUDEPATH += $$quote($$TINYORM_SOURCE_TREE/include/)
|
||||
|
||||
# TinyTom include path
|
||||
# ---
|
||||
|
||||
!disable_tom: \
|
||||
INCLUDEPATH += $$quote($$TINYORM_SOURCE_TREE/tom/include/)
|
||||
|
||||
# Link against TinyORM library
|
||||
# ---
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ mingw: tinyRcIncludepath += $$quote($$TINYORM_SOURCE_TREE/tests/resources/)
|
||||
load(tiny_resource_and_manifest)
|
||||
tiny_resource_and_manifest($$tinyRcIncludepath, \
|
||||
$$quote($$TINYORM_SOURCE_TREE/tests/resources), \
|
||||
true \
|
||||
TinyTest \
|
||||
)
|
||||
|
||||
unset(tinyRcIncludepath)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma code_page(65001) // UTF-8
|
||||
//#pragma code_page(65001) // UTF-8
|
||||
|
||||
//IDI_ICON1 ICON "icons/@TinyTest_icon@.ico"
|
||||
|
||||
|
||||
@@ -1,5 +1,38 @@
|
||||
INCLUDEPATH += $$PWD
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/tom/application.hpp \
|
||||
$$PWD/tom/commands/command.hpp \
|
||||
$$PWD/tom/commands/database/wipecommand.hpp \
|
||||
$$PWD/tom/commands/environmentcommand.hpp \
|
||||
$$PWD/tom/commands/helpcommand.hpp \
|
||||
$$PWD/tom/commands/inspirecommand.hpp \
|
||||
$$PWD/tom/commands/listcommand.hpp \
|
||||
$$PWD/tom/commands/make/migrationcommand.hpp \
|
||||
# $$PWD/tom/commands/make/projectcommand.hpp \
|
||||
$$PWD/tom/commands/make/stubs/migrationstubs.hpp \
|
||||
$$PWD/tom/commands/make/stubs/projectstubs.hpp \
|
||||
$$PWD/tom/commands/migrations/freshcommand.hpp \
|
||||
$$PWD/tom/commands/migrations/installcommand.hpp \
|
||||
$$PWD/tom/commands/migrations/migratecommand.hpp \
|
||||
$$PWD/tom/commands/migrations/refreshcommand.hpp \
|
||||
$$PWD/tom/commands/migrations/resetcommand.hpp \
|
||||
$$PWD/tom/commands/migrations/rollbackcommand.hpp \
|
||||
$$PWD/tom/commands/migrations/statuscommand.hpp \
|
||||
$$PWD/tom/concerns/callscommands.hpp \
|
||||
$$PWD/tom/concerns/confirmable.hpp \
|
||||
$$PWD/tom/concerns/interactswithio.hpp \
|
||||
$$PWD/tom/concerns/printsoptions.hpp \
|
||||
$$PWD/tom/config.hpp \
|
||||
$$PWD/tom/exceptions/invalidargumenterror.hpp \
|
||||
$$PWD/tom/exceptions/invalidtemplateargumenterror.hpp \
|
||||
$$PWD/tom/exceptions/logicerror.hpp \
|
||||
$$PWD/tom/exceptions/runtimeerror.hpp \
|
||||
$$PWD/tom/exceptions/tomerror.hpp \
|
||||
$$PWD/tom/migration.hpp \
|
||||
$$PWD/tom/migrationcreator.hpp \
|
||||
$$PWD/tom/migrationrepository.hpp \
|
||||
$$PWD/tom/migrator.hpp \
|
||||
$$PWD/tom/terminal.hpp \
|
||||
$$PWD/tom/tomtypes.hpp \
|
||||
$$PWD/tom/version.hpp \
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
/* This file can't be included in the project, it's for a precompiled header. */
|
||||
|
||||
/* Add C includes here */
|
||||
|
||||
#if defined __cplusplus
|
||||
/* Add C++ includes here */
|
||||
//#include <QDateTime>
|
||||
//#include <QHash>
|
||||
//#include <QMap>
|
||||
//#include <QSharedPointer>
|
||||
#include <QStringList>
|
||||
//#include <QTimer>
|
||||
//#include <QVariant>
|
||||
#include <QVector>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <cassert>
|
||||
#include <cfloat>
|
||||
#include <cmath>
|
||||
#include <complex>
|
||||
#include <condition_variable>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <numeric>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <span>
|
||||
#include <string>
|
||||
//#include <thread>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <typeinfo>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#endif
|
||||
@@ -1,8 +0,0 @@
|
||||
# Use Precompiled headers (PCH)
|
||||
# ---
|
||||
|
||||
PRECOMPILED_HEADER = $$quote($$PWD/pch.h)
|
||||
HEADERS += $$PRECOMPILED_HEADER
|
||||
|
||||
precompile_header: \
|
||||
DEFINES *= USING_PCH
|
||||
264
tom/include/tom/application.hpp
Normal file
264
tom/include/tom/application.hpp
Normal file
@@ -0,0 +1,264 @@
|
||||
#pragma once
|
||||
#ifndef TOM_APPLICATION_HPP
|
||||
#define TOM_APPLICATION_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include <QCommandLineParser>
|
||||
|
||||
#include "tom/config.hpp"
|
||||
|
||||
#include "tom/concerns/interactswithio.hpp"
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Orm
|
||||
{
|
||||
class DatabaseManager;
|
||||
}
|
||||
|
||||
namespace Tom {
|
||||
|
||||
namespace Commands
|
||||
{
|
||||
class Command;
|
||||
class HelpCommand;
|
||||
class ListCommand;
|
||||
} // namespace Commands
|
||||
namespace Concerns
|
||||
{
|
||||
class CallsCommands;
|
||||
class PrintsOptions;
|
||||
} // namespace Concerns
|
||||
|
||||
class Migration;
|
||||
class MigrationRepository;
|
||||
class Migrator;
|
||||
|
||||
/*! Tom application. */
|
||||
class SHAREDLIB_EXPORT Application : public Concerns::InteractsWithIO
|
||||
{
|
||||
Q_DISABLE_COPY(Application)
|
||||
|
||||
// To access saveOptions()
|
||||
friend Commands::Command;
|
||||
// To access createCommand()
|
||||
friend Commands::HelpCommand;
|
||||
// To access showVersion()
|
||||
friend Commands::ListCommand;
|
||||
// To access m_options
|
||||
friend Concerns::PrintsOptions;
|
||||
// To access initializeParser() and createCommand()
|
||||
friend Concerns::CallsCommands;
|
||||
|
||||
/*! Alias for the DatabaseManager. */
|
||||
using DatabaseManager = Orm::DatabaseManager;
|
||||
|
||||
public:
|
||||
/*! Constructor. */
|
||||
Application(int &argc, char **argv, std::shared_ptr<DatabaseManager> db,
|
||||
const char *environmentEnvName = "TOM_ENV",
|
||||
QString migrationTable = QLatin1String("migrations"),
|
||||
std::vector<std::shared_ptr<Migration>> migrations = {});
|
||||
/*! Default destructor. */
|
||||
inline ~Application() = default;
|
||||
|
||||
/*! Instantiate/initialize all migration classes. */
|
||||
template<typename ...M>
|
||||
Application &migrations();
|
||||
|
||||
/*! Run the tom application. */
|
||||
int run();
|
||||
|
||||
/*! Log exception caught in the main exception handler in a current thread. */
|
||||
static void logException(const std::exception &e, bool noAnsi = false);
|
||||
|
||||
/* Getters / Setters */
|
||||
/*! Get a current application environment. */
|
||||
inline const QString &environment() const noexcept;
|
||||
/*! Get database manager. */
|
||||
inline DatabaseManager &db() const noexcept;
|
||||
|
||||
/*! Get command-line parser. */
|
||||
inline const QCommandLineParser &parser() const noexcept;
|
||||
/*! Is the application running in an interactive mode? */
|
||||
inline bool isInteractive() const noexcept;
|
||||
/*! Obtain current command-line arguments. */
|
||||
QStringList arguments() const;
|
||||
|
||||
/*! Set the migration repository table name. */
|
||||
inline Application &migrationTable(QString table);
|
||||
|
||||
#ifdef TINYTOM_TESTS_CODE
|
||||
/*! Alias for the test output row from the status command. */
|
||||
using StatusRow = std::vector<std::string>;
|
||||
|
||||
/*! Get result of the status command (used in auto tests). */
|
||||
static std::vector<StatusRow> status() noexcept;
|
||||
/*! Enable logic for unit testing? */
|
||||
static void enableInUnitTests() noexcept;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
/*! Alias for the commands' base class. */
|
||||
using Command = Commands::Command;
|
||||
|
||||
/* Application initialization */
|
||||
#ifdef _WIN32
|
||||
/*! Prepare console input/output character encoding. */
|
||||
void initializeConsoleEncoding() const;
|
||||
#endif
|
||||
/*! Fix m_argc/m_argv data members if the argv is empty. */
|
||||
void fixEmptyArgv();
|
||||
/*! Processes the specified function at application's normal exit. */
|
||||
void initializeAtExit() const;
|
||||
|
||||
/*! Initialize the command-line parser. */
|
||||
void initializeParser(QCommandLineParser &parser);
|
||||
/*! Save a copy of application options passed to the Qt's parser. */
|
||||
const QList<QCommandLineOption> &
|
||||
saveOptions(QList<QCommandLineOption> &&options);
|
||||
|
||||
/*! Prepend command options before common options (used by the help command). */
|
||||
QList<QCommandLineOption>
|
||||
prependOptions(QList<QCommandLineOption> &&options);
|
||||
|
||||
/* Run command */
|
||||
/*! Parse current application's command line. */
|
||||
void parseCommandLine();
|
||||
|
||||
/*! Initialize environment value, order:
|
||||
development -> value from env. variable -> --env command-line argument. */
|
||||
void initializeEnvironment();
|
||||
/*! Obtain command name to run. */
|
||||
QString getCommandName();
|
||||
|
||||
/* Early exit during parse command-line */
|
||||
/*! Display the version information and exits. */
|
||||
Q_NORETURN void showVersion() const;
|
||||
/*! Display the version information. */
|
||||
void printVersion() const;
|
||||
/*! Invoke the list command. */
|
||||
Q_NORETURN void showCommandsList(int exitCode);
|
||||
/*! Exit the application with post routines. */
|
||||
Q_NORETURN void exitApplication(int exitCode) const;
|
||||
|
||||
/* Commands factory */
|
||||
/*! Alias for an optional command-line parser reference. */
|
||||
using OptionalParserRef =
|
||||
std::optional<std::reference_wrapper<QCommandLineParser>>;
|
||||
|
||||
/*! Create command by the given name. */
|
||||
std::unique_ptr<Command>
|
||||
createCommand(const QString &command, OptionalParserRef parser = std::nullopt,
|
||||
bool showHelp = true);
|
||||
|
||||
/*! Migration repository instance. */
|
||||
std::shared_ptr<MigrationRepository> createMigrationRepository();
|
||||
/*! Migrator instance. */
|
||||
std::shared_ptr<Migrator> createMigrator();
|
||||
|
||||
/* Others */
|
||||
/*! Get all supported commands list (used by the list command). */
|
||||
const std::vector<std::shared_ptr<Command>> &createCommandsVector();
|
||||
/*! Get all supported commands' names. */
|
||||
const std::vector<const char *> &commandNames() const;
|
||||
/*! Get arguments list from the m_argv array. */
|
||||
QStringList prepareArguments() const;
|
||||
|
||||
/*! Current application argc. */
|
||||
int &m_argc;
|
||||
/*! Current application argv. */
|
||||
char **m_argv;
|
||||
|
||||
/*! DatabaseManager instance. */
|
||||
std::shared_ptr<DatabaseManager> m_db;
|
||||
/*! The migration repository instance. */
|
||||
std::shared_ptr<MigrationRepository> m_repository = nullptr;
|
||||
/*! The migrator service instance. */
|
||||
std::shared_ptr<Migrator> m_migrator = nullptr;
|
||||
|
||||
/* Only one instance can exist in the whole application, auto tests create their
|
||||
own QCoreApplication instance so this has to be excluded. */
|
||||
#ifndef TINYTOM_TESTS_CODE
|
||||
/*! Qt's application instance. */
|
||||
QCoreApplication m_qtApplication;
|
||||
/*! Determine whether the TomApplication has its own QCoreApplication instance. */
|
||||
bool hasQtApplication = true;
|
||||
#else
|
||||
/*! Determine whether the TomApplication has its own QCoreApplication instance. */
|
||||
bool hasQtApplication = false;
|
||||
#endif
|
||||
/*! Command line parser. */
|
||||
QCommandLineParser m_parser {};
|
||||
|
||||
/*! Current environment. */
|
||||
QString m_environment = QStringLiteral("development");
|
||||
/*! Environment variable name that holds a current environment value. */
|
||||
const char *m_environmentEnvName;
|
||||
/*! Migration repository table name. */
|
||||
QString m_migrationTable;
|
||||
|
||||
/*! Migrations vector to process. */
|
||||
std::vector<std::shared_ptr<Migration>> m_migrations;
|
||||
/*! Is this input means interactive? */
|
||||
bool m_interactive = true;
|
||||
|
||||
/*! Application options. */
|
||||
QList<QCommandLineOption> m_options {};
|
||||
|
||||
/* Auto tests helpers */
|
||||
#ifdef TINYTOM_TESTS_CODE
|
||||
public:
|
||||
/*! Run the tom application with the given arguments (used in auto tests). */
|
||||
int runWithArguments(QStringList &&arguments);
|
||||
#endif
|
||||
};
|
||||
|
||||
/* public */
|
||||
|
||||
template<typename ...Migrations>
|
||||
Application &Application::migrations()
|
||||
{
|
||||
m_migrations = {std::make_shared<Migrations>()...};
|
||||
|
||||
// Correct sort order is checked in the Migrator::createMigrationNamesMap()
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/* Getters / Setters */
|
||||
|
||||
const QString &Application::environment() const noexcept
|
||||
{
|
||||
return m_environment;
|
||||
}
|
||||
|
||||
Application::DatabaseManager &Application::db() const noexcept
|
||||
{
|
||||
return *m_db;
|
||||
}
|
||||
|
||||
const QCommandLineParser &Application::parser() const noexcept
|
||||
{
|
||||
return m_parser;
|
||||
}
|
||||
|
||||
bool Application::isInteractive() const noexcept
|
||||
{
|
||||
return m_interactive;
|
||||
}
|
||||
|
||||
Application &Application::migrationTable(QString table)
|
||||
{
|
||||
m_migrationTable = std::move(table);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace Tom
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_APPLICATION_HPP
|
||||
153
tom/include/tom/commands/command.hpp
Normal file
153
tom/include/tom/commands/command.hpp
Normal file
@@ -0,0 +1,153 @@
|
||||
#pragma once
|
||||
#ifndef TOM_COMMANDS_COMMAND_HPP
|
||||
#define TOM_COMMANDS_COMMAND_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include "tom/concerns/callscommands.hpp"
|
||||
#include "tom/concerns/interactswithio.hpp"
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
class QCommandLineOption;
|
||||
|
||||
namespace Orm
|
||||
{
|
||||
class DatabaseConnection;
|
||||
}
|
||||
|
||||
namespace Tom::Commands
|
||||
{
|
||||
|
||||
/*! Positional argument item for a console command. */
|
||||
struct PositionalArgument
|
||||
{
|
||||
/*! Argument name. */
|
||||
QString name;
|
||||
/*! Argument description. */
|
||||
QString description;
|
||||
/*! Appended to the Usage line, if empty the name is used. */
|
||||
QString syntax {};
|
||||
/*! Is argument optional? */
|
||||
bool optional = false;
|
||||
/*! Argument's default value (optional argument only). */
|
||||
QString defaultValue {};
|
||||
};
|
||||
|
||||
/*! Abstract base class for the console command. */
|
||||
class Command : public Concerns::CallsCommands,
|
||||
public Concerns::InteractsWithIO
|
||||
{
|
||||
Q_DISABLE_COPY(Command)
|
||||
|
||||
public:
|
||||
/*! Constructor. */
|
||||
Command(Application &application, QCommandLineParser &parser);
|
||||
/*! Pure virtual destructor. */
|
||||
inline virtual ~Command() = 0;
|
||||
|
||||
/*! The console command name. */
|
||||
virtual QString name() const = 0;
|
||||
/*! The console command description. */
|
||||
virtual QString description() const = 0;
|
||||
|
||||
/*! The console command positional arguments signature. */
|
||||
inline virtual const std::vector<PositionalArgument> &
|
||||
positionalArguments() const;
|
||||
/*! The signature of the console command. */
|
||||
virtual QList<QCommandLineOption> optionsSignature() const;
|
||||
|
||||
/*! The console command help. */
|
||||
inline virtual QString help() const;
|
||||
|
||||
/*! Execute the console command. */
|
||||
virtual int run();
|
||||
/*! Execute the console command with the given arguments. */
|
||||
int runWithArguments(QStringList &&arguments);
|
||||
|
||||
/* Getters */
|
||||
/*! Get the tom application. */
|
||||
inline Application &application() const noexcept;
|
||||
/*! Determine whether a command has own positional arguments. */
|
||||
bool hasPositionalArguments() const;
|
||||
/*! Determine whether a command has own options. */
|
||||
bool hasOptions() const;
|
||||
|
||||
protected:
|
||||
/* Getters */
|
||||
/*! Obtain passed arguments to parse (can come from three sources). */
|
||||
QStringList passedArguments() const;
|
||||
|
||||
/* Parser helpers */
|
||||
/*! Check whether the option name was set in the parser. */
|
||||
bool isSet(const QString &name) const;
|
||||
/*! Returns the option value found for the given option name or empty string. */
|
||||
QString value(const QString &name) const;
|
||||
/*! Get a full command-line value option if value is set in the parser. */
|
||||
QString valueCmd(const QString &name, const QString &key = "") const;
|
||||
/*! Get a full command-line boolean option if it's set in the parser. */
|
||||
QString boolCmd(const QString &name, const QString &key = "") const;
|
||||
|
||||
/*! Check whether a positional argument at the given index was set. */
|
||||
bool hasArgument(QList<QString>::size_type index) const;
|
||||
/*! Get a list of positional arguments. */
|
||||
QStringList arguments() const;
|
||||
/*! Get a positional argument at the given index position. */
|
||||
QString argument(QList<QString>::size_type index) const;
|
||||
/*! Get a positional argument by the given name. */
|
||||
QString argument(const QString &name) const;
|
||||
|
||||
/*! Get a database connection. */
|
||||
Orm::DatabaseConnection &connection(const QString &name) const;
|
||||
/*! Get a command-line parser. */
|
||||
QCommandLineParser &parser() const noexcept;
|
||||
|
||||
/*! Reference to the tom application. */
|
||||
std::reference_wrapper<Application> m_application;
|
||||
/*! Command line parser. */
|
||||
std::reference_wrapper<QCommandLineParser> m_parser;
|
||||
|
||||
/*! Passed command's arguments. */
|
||||
QStringList m_arguments {};
|
||||
|
||||
/*! Alias for the QList command-line option size type. */
|
||||
using OptionsSizeType = QList<QCommandLineOption>::size_type;
|
||||
/*! Map positional argument names to the index for obtaining values. */
|
||||
std::unordered_map<QString, OptionsSizeType> m_positionalArguments {};
|
||||
|
||||
private:
|
||||
/*! Initialize positional arguments map. */
|
||||
void initializePositionalArguments();
|
||||
/*! Show help if --help argument was passed. */
|
||||
void checkHelpArgument() const;
|
||||
/*! Show the error wall and exit the application if the parser fails. */
|
||||
void showParserError(const QCommandLineParser &parser) const;
|
||||
};
|
||||
|
||||
/* public */
|
||||
|
||||
Command::~Command() = default;
|
||||
|
||||
const std::vector<PositionalArgument> &Command::positionalArguments() const
|
||||
{
|
||||
static const std::vector<PositionalArgument> cached;
|
||||
|
||||
return cached;
|
||||
}
|
||||
|
||||
QString Command::help() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
Application &Command::application() const noexcept
|
||||
{
|
||||
return m_application;
|
||||
}
|
||||
|
||||
} // namespace Tom::Commands
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_COMMANDS_COMMAND_HPP
|
||||
67
tom/include/tom/commands/database/wipecommand.hpp
Normal file
67
tom/include/tom/commands/database/wipecommand.hpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
#ifndef TOM_COMMANDS_DATABASE_WIPECOMMAND_HPP
|
||||
#define TOM_COMMANDS_DATABASE_WIPECOMMAND_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include "tom/commands/command.hpp"
|
||||
#include "tom/concerns/confirmable.hpp"
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom::Commands::Database
|
||||
{
|
||||
|
||||
/*! Drop all tables, views, and types. */
|
||||
class WipeCommand : public Command,
|
||||
public Concerns::Confirmable
|
||||
{
|
||||
Q_DISABLE_COPY(WipeCommand)
|
||||
|
||||
/*! Alias for the Command. */
|
||||
using Command = Commands::Command;
|
||||
|
||||
public:
|
||||
/*! Constructor. */
|
||||
WipeCommand(Application &application, QCommandLineParser &parser);
|
||||
/*! Virtual destructor. */
|
||||
inline ~WipeCommand() 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<QCommandLineOption> optionsSignature() const override;
|
||||
|
||||
/*! Execute the console command. */
|
||||
int run() override;
|
||||
|
||||
protected:
|
||||
/*! Drop all of the database tables. */
|
||||
void dropAllTables(const QString &database) const;
|
||||
/*! Drop all of the database views. */
|
||||
void dropAllViews(const QString &database) const;
|
||||
/*! Drop all of the database types. */
|
||||
void dropAllTypes(const QString &database) const;
|
||||
};
|
||||
|
||||
/* public */
|
||||
|
||||
QString WipeCommand::name() const
|
||||
{
|
||||
return QStringLiteral("db:wipe");
|
||||
}
|
||||
|
||||
QString WipeCommand::description() const
|
||||
{
|
||||
return QLatin1String("Drop all tables, views, and types");
|
||||
}
|
||||
|
||||
} // namespace Tom::Commands::Database
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_COMMANDS_DATABASE_WIPECOMMAND_HPP
|
||||
51
tom/include/tom/commands/environmentcommand.hpp
Normal file
51
tom/include/tom/commands/environmentcommand.hpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
#ifndef TOM_COMMANDS_ENVIRONMENTCOMMAND_HPP
|
||||
#define TOM_COMMANDS_ENVIRONMENTCOMMAND_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include "tom/commands/command.hpp"
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom::Commands
|
||||
{
|
||||
|
||||
/*! Display the current environment. */
|
||||
class EnvironmentCommand : public Command
|
||||
{
|
||||
Q_DISABLE_COPY(EnvironmentCommand)
|
||||
|
||||
public:
|
||||
/*! Constructor. */
|
||||
EnvironmentCommand(Application &application, QCommandLineParser &parser);
|
||||
/*! Virtual destructor. */
|
||||
inline ~EnvironmentCommand() override = default;
|
||||
|
||||
/*! The console command name. */
|
||||
inline QString name() const override;
|
||||
/*! The console command description. */
|
||||
inline QString description() const override;
|
||||
|
||||
/*! Execute the console command. */
|
||||
int run() override;
|
||||
};
|
||||
|
||||
/* public */
|
||||
|
||||
QString EnvironmentCommand::name() const
|
||||
{
|
||||
return QStringLiteral("env");
|
||||
}
|
||||
|
||||
QString EnvironmentCommand::description() const
|
||||
{
|
||||
return QLatin1String("Display the current framework environment");
|
||||
}
|
||||
|
||||
} // namespace Tom::Commands
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_COMMANDS_ENVIRONMENTCOMMAND_HPP
|
||||
85
tom/include/tom/commands/helpcommand.hpp
Normal file
85
tom/include/tom/commands/helpcommand.hpp
Normal file
@@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
#ifndef TOM_COMMANDS_HELPCOMMAND_HPP
|
||||
#define TOM_COMMANDS_HELPCOMMAND_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include "tom/commands/command.hpp"
|
||||
#include "tom/concerns/printsoptions.hpp"
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom::Commands
|
||||
{
|
||||
|
||||
/*! Display help for a command. */
|
||||
class HelpCommand : public Command,
|
||||
public Concerns::PrintsOptions
|
||||
{
|
||||
Q_DISABLE_COPY(HelpCommand)
|
||||
|
||||
public:
|
||||
/*! Constructor. */
|
||||
HelpCommand(Application &application, QCommandLineParser &parser);
|
||||
/*! Virtual destructor. */
|
||||
inline ~HelpCommand() override = default;
|
||||
|
||||
/*! The console command name. */
|
||||
inline QString name() const override;
|
||||
/*! The console command description. */
|
||||
inline QString description() const override;
|
||||
|
||||
/*! The console command positional arguments signature. */
|
||||
const std::vector<PositionalArgument> &positionalArguments() const override;
|
||||
/*! The console command help. */
|
||||
QString help() const override;
|
||||
|
||||
/*! Execute the console command. */
|
||||
int run() override;
|
||||
|
||||
protected:
|
||||
/*! Create command by the given name. */
|
||||
std::unique_ptr<Command> createCommand(const QString &name) const;
|
||||
/*! Validate if all required positional arguments are after optional arguments. */
|
||||
bool validateRequiredArguments(
|
||||
const std::vector<PositionalArgument> &arguments) const;
|
||||
|
||||
/*! Print description section. */
|
||||
void printDescriptionSection(const Command &command) const;
|
||||
/*! Print usage section. */
|
||||
void printUsageSection(
|
||||
const QString &commandNameArg, const Command &command,
|
||||
const std::vector<PositionalArgument> &arguments) const;
|
||||
|
||||
/*! Print positional arguments section. */
|
||||
void printArgumentsSection(
|
||||
const std::vector<PositionalArgument> &arguments) const;
|
||||
/*! Get max. positional argument size in all command arguments. */
|
||||
int argumentsMaxSize(const std::vector<PositionalArgument> &arguments) const;
|
||||
/*! Print a positional's argument default value part. */
|
||||
void printArgumentDefaultValue(const PositionalArgument &argument) const;
|
||||
|
||||
/*! Print options section. */
|
||||
int printOptionsSection(const Command &command) const;
|
||||
/*! Print help section. */
|
||||
void printHelpSection(const Command &command) const;
|
||||
};
|
||||
|
||||
/* public */
|
||||
|
||||
QString HelpCommand::name() const
|
||||
{
|
||||
return QStringLiteral("help");
|
||||
}
|
||||
|
||||
QString HelpCommand::description() const
|
||||
{
|
||||
return QLatin1String("Display help for a command");
|
||||
}
|
||||
|
||||
} // namespace Tom::Commands
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_COMMANDS_HELPCOMMAND_HPP
|
||||
51
tom/include/tom/commands/inspirecommand.hpp
Normal file
51
tom/include/tom/commands/inspirecommand.hpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
#ifndef TOM_COMMANDS_INSPIRECOMMAND_HPP
|
||||
#define TOM_COMMANDS_INSPIRECOMMAND_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include "tom/commands/command.hpp"
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom::Commands
|
||||
{
|
||||
|
||||
/*! Display an inspiring quote. */
|
||||
class InspireCommand : public Command
|
||||
{
|
||||
Q_DISABLE_COPY(InspireCommand)
|
||||
|
||||
public:
|
||||
/*! Constructor. */
|
||||
InspireCommand(Application &application, QCommandLineParser &parser);
|
||||
/*! Virtual destructor. */
|
||||
inline ~InspireCommand() override = default;
|
||||
|
||||
/*! The console command name. */
|
||||
inline QString name() const override;
|
||||
/*! The console command description. */
|
||||
inline QString description() const override;
|
||||
|
||||
/*! Execute the console command. */
|
||||
int run() override;
|
||||
};
|
||||
|
||||
/* public */
|
||||
|
||||
QString InspireCommand::name() const
|
||||
{
|
||||
return QStringLiteral("inspire");
|
||||
}
|
||||
|
||||
QString InspireCommand::description() const
|
||||
{
|
||||
return QLatin1String("Display an inspiring quote");
|
||||
}
|
||||
|
||||
} // namespace Tom::Commands
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_COMMANDS_INSPIRECOMMAND_HPP
|
||||
89
tom/include/tom/commands/listcommand.hpp
Normal file
89
tom/include/tom/commands/listcommand.hpp
Normal file
@@ -0,0 +1,89 @@
|
||||
#pragma once
|
||||
#ifndef TOM_COMMANDS_LISTCOMMAND_HPP
|
||||
#define TOM_COMMANDS_LISTCOMMAND_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include "tom/commands/command.hpp"
|
||||
#include "tom/concerns/printsoptions.hpp"
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom::Commands
|
||||
{
|
||||
|
||||
/*! List all available commands. */
|
||||
class ListCommand : public Command,
|
||||
public Concerns::PrintsOptions
|
||||
{
|
||||
Q_DISABLE_COPY(ListCommand)
|
||||
|
||||
public:
|
||||
/*! Constructor. */
|
||||
ListCommand(Application &application, QCommandLineParser &parser);
|
||||
/*! Virtual destructor. */
|
||||
inline ~ListCommand() override = default;
|
||||
|
||||
/*! The console command name. */
|
||||
inline QString name() const override;
|
||||
/*! The console command description. */
|
||||
inline QString description() const override;
|
||||
|
||||
/*! The console command positional arguments signature. */
|
||||
const std::vector<PositionalArgument> &positionalArguments() const override;
|
||||
/*! The signature of the console command. */
|
||||
QList<QCommandLineOption> optionsSignature() const override;
|
||||
|
||||
/*! The console command help. */
|
||||
QString help() const override;
|
||||
|
||||
/*! Execute the console command. */
|
||||
int run() override;
|
||||
|
||||
protected:
|
||||
/*! Output full commands list. */
|
||||
int full(const QString &namespaceArg);
|
||||
/*! Output raw commands list and nothing else (can be consumed by scripts). */
|
||||
int raw(const QString &namespaceArg);
|
||||
|
||||
/* Commands section */
|
||||
/*! Print commands section. */
|
||||
void printCommandsSection(const QString &namespaceArg, int optionsMaxSize) const;
|
||||
/*! Get max. command size in all command names. */
|
||||
int commandsMaxSize(const std::vector<std::shared_ptr<Command>> &commands,
|
||||
int optionsMaxSize) const;
|
||||
/*! Print commands to the console. */
|
||||
void printCommands(const std::vector<std::shared_ptr<Command>> &commands,
|
||||
int commandsMaxSize, bool hasNamespaceArg) const;
|
||||
/*! Print a new namespace section. */
|
||||
void tryBeginNsSection(QString &renderingNamespace,
|
||||
const QString &commandName, bool hasNamespaceArg) const;
|
||||
/*! Get command's namespace from a command name. */
|
||||
QString commandNamespace(const QString &commandName) const;
|
||||
|
||||
/*! Wrapper for the two methods below, helps to avoid one copy. */
|
||||
const std::vector<std::shared_ptr<Command>> &
|
||||
getCommandsByNamespace(const QString &name) const;
|
||||
/*! Obtain all commands in the given namespace. */
|
||||
std::vector<std::shared_ptr<Command>>
|
||||
getCommandsInNamespace(const QString &name) const;
|
||||
};
|
||||
|
||||
/* public */
|
||||
|
||||
QString ListCommand::name() const
|
||||
{
|
||||
return QStringLiteral("list");
|
||||
}
|
||||
|
||||
QString ListCommand::description() const
|
||||
{
|
||||
return QLatin1String("List commands");
|
||||
}
|
||||
|
||||
} // namespace Tom::Commands
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_COMMANDS_LISTCOMMAND_HPP
|
||||
80
tom/include/tom/commands/make/migrationcommand.hpp
Normal file
80
tom/include/tom/commands/make/migrationcommand.hpp
Normal file
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
#ifndef TOM_COMMANDS_MAKE_MIGRATIONCOMMAND_HPP
|
||||
#define TOM_COMMANDS_MAKE_MIGRATIONCOMMAND_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include "tom/commands/command.hpp"
|
||||
#include "tom/migrationcreator.hpp"
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom::Commands::Make
|
||||
{
|
||||
|
||||
/*! Create a new migration file. */
|
||||
class MigrationCommand : public Command
|
||||
{
|
||||
Q_DISABLE_COPY(MigrationCommand)
|
||||
|
||||
/*! Alias for the filesystem path. */
|
||||
using path = std::filesystem::path;
|
||||
|
||||
public:
|
||||
/*! Constructor. */
|
||||
MigrationCommand(Application &application, QCommandLineParser &parser);
|
||||
/*! Virtual destructor. */
|
||||
inline ~MigrationCommand() override = default;
|
||||
|
||||
/*! The console command name. */
|
||||
inline QString name() const override;
|
||||
/*! The console command description. */
|
||||
inline QString description() const override;
|
||||
|
||||
/*! The console command positional arguments signature. */
|
||||
const std::vector<PositionalArgument> &positionalArguments() const override;
|
||||
/*! The signature of the console command. */
|
||||
QList<QCommandLineOption> optionsSignature() const override;
|
||||
|
||||
/*! Execute the console command. */
|
||||
int run() override;
|
||||
|
||||
protected:
|
||||
/*! Write the migration file to disk. */
|
||||
void writeMigration(const QString &name, const QString &table,
|
||||
bool create) const;
|
||||
|
||||
/*! Get migration path (either specified by '--path' option or default
|
||||
location). */
|
||||
inline path getMigrationPath() const;
|
||||
|
||||
/*! The migration creator instance. */
|
||||
MigrationCreator m_creator {};
|
||||
};
|
||||
|
||||
/* public */
|
||||
|
||||
QString MigrationCommand::name() const
|
||||
{
|
||||
return QStringLiteral("make:migration");
|
||||
}
|
||||
|
||||
QString MigrationCommand::description() const
|
||||
{
|
||||
return QLatin1String("Create a new migration file");
|
||||
}
|
||||
|
||||
/* protected */
|
||||
|
||||
// CUR tom, finish --path/--realpath silverqx
|
||||
std::filesystem::path MigrationCommand::getMigrationPath() const
|
||||
{
|
||||
return path(__FILE__).parent_path().parent_path().parent_path() / "migrations";
|
||||
}
|
||||
|
||||
} // namespace Tom::Commands::Make
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_COMMANDS_MAKE_MIGRATIONCOMMAND_HPP
|
||||
78
tom/include/tom/commands/make/projectcommand.hpp
Normal file
78
tom/include/tom/commands/make/projectcommand.hpp
Normal file
@@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
#ifndef TOM_COMMANDS_MAKE_PROJECTCOMMAND_HPP
|
||||
#define TOM_COMMANDS_MAKE_PROJECTCOMMAND_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include "tom/commands/command.hpp"
|
||||
//#include "tom/migrationcreator.hpp"
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom::Commands::Make
|
||||
{
|
||||
|
||||
/*! Create a new Tom application project. */
|
||||
class ProjectCommand : public Command
|
||||
{
|
||||
Q_DISABLE_COPY(ProjectCommand)
|
||||
|
||||
/*! Alias for the filesystem path. */
|
||||
// using path = std::filesystem::path;
|
||||
|
||||
public:
|
||||
/*! Constructor. */
|
||||
ProjectCommand(Application &application, QCommandLineParser &parser);
|
||||
/*! Virtual destructor. */
|
||||
inline ~ProjectCommand() override = default;
|
||||
|
||||
/*! The console command name. */
|
||||
inline QString name() const override;
|
||||
/*! The console command description. */
|
||||
inline QString description() const override;
|
||||
|
||||
/*! The console command positional arguments signature. */
|
||||
const std::vector<PositionalArgument> &positionalArguments() const override;
|
||||
/*! The signature of the console command. */
|
||||
QList<QCommandLineOption> signature() const override;
|
||||
|
||||
/*! Execute the console command. */
|
||||
int run() override;
|
||||
|
||||
protected:
|
||||
/*! Write the migration file to disk. */
|
||||
// void writeMigration(const QString &name, const QString &table,
|
||||
// bool create) const;
|
||||
/*! Get migration path (either specified by '--path' option or default
|
||||
location). */
|
||||
// inline path getMigrationPath() const;
|
||||
|
||||
/*! The migration creator instance. */
|
||||
// MigrationCreator m_creator {};
|
||||
};
|
||||
|
||||
/* public */
|
||||
|
||||
QString ProjectCommand::name() const
|
||||
{
|
||||
return QStringLiteral("make:project");
|
||||
}
|
||||
|
||||
QString ProjectCommand::description() const
|
||||
{
|
||||
return QLatin1String("Create a new Tom application project");
|
||||
}
|
||||
|
||||
/* protected */
|
||||
|
||||
// std::filesystem::path ProjectCommand::getMigrationPath() const
|
||||
// {
|
||||
// return path(__FILE__).parent_path().parent_path().parent_path() / "migrations";
|
||||
// }
|
||||
|
||||
} // namespace Tom::Commands::Make
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_COMMANDS_MAKE_PROJECTCOMMAND_HPP
|
||||
110
tom/include/tom/commands/make/stubs/migrationstubs.hpp
Normal file
110
tom/include/tom/commands/make/stubs/migrationstubs.hpp
Normal file
@@ -0,0 +1,110 @@
|
||||
#pragma once
|
||||
#ifndef TOM_COMMANDS_MAKE_STUBS_MIGRATIONSTUBS_HPP
|
||||
#define TOM_COMMANDS_MAKE_STUBS_MIGRATIONSTUBS_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include <orm/macros/commonnamespace.hpp>
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom::Commands::Make::Stubs
|
||||
{
|
||||
|
||||
/*! Empty migration stub. */
|
||||
inline const auto *const MigrationStub = R"T(#pragma once
|
||||
|
||||
#include <tom/migration.hpp>
|
||||
|
||||
namespace Migrations
|
||||
{
|
||||
|
||||
struct {{ class }} : Migration
|
||||
{
|
||||
|
||||
/*! Run the migrations. */
|
||||
void up() const override
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/*! Reverse the migrations. */
|
||||
void down() const override
|
||||
{
|
||||
//
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Migrations
|
||||
)T";
|
||||
|
||||
/*! Migration stub for creating a new table. */
|
||||
inline const auto *const MigrationCreateStub = R"T(#pragma once
|
||||
|
||||
#include <tom/migration.hpp>
|
||||
|
||||
namespace Migrations
|
||||
{
|
||||
|
||||
struct {{ class }} : Migration
|
||||
{
|
||||
|
||||
/*! Run the migrations. */
|
||||
void up() const override
|
||||
{
|
||||
Schema::create("{{ table }}", [](Blueprint &table)
|
||||
{
|
||||
table.id();
|
||||
table.timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/*! Reverse the migrations. */
|
||||
void down() const override
|
||||
{
|
||||
Schema::dropIfExists("{{ table }}");
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Migrations
|
||||
)T";
|
||||
|
||||
/*! Migration stub for updating an existing table. */
|
||||
inline const auto *const MigrationUpdateStub = R"T(#pragma once
|
||||
|
||||
#include <tom/migration.hpp>
|
||||
|
||||
namespace Migrations
|
||||
{
|
||||
|
||||
struct {{ class }} : Migration
|
||||
{
|
||||
|
||||
/*! Run the migrations. */
|
||||
void up() const override
|
||||
{
|
||||
Schema::table("{{ table }}", [](Blueprint &table)
|
||||
{
|
||||
//
|
||||
});
|
||||
}
|
||||
|
||||
/*! Reverse the migrations. */
|
||||
void down() const override
|
||||
{
|
||||
Schema::table("{{ table }}", [](Blueprint &table)
|
||||
{
|
||||
//
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Migrations
|
||||
)T";
|
||||
|
||||
} // namespace Tom::Commands::Make::Stubs
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_COMMANDS_MAKE_STUBS_MIGRATIONSTUBS_HPP
|
||||
22
tom/include/tom/commands/make/stubs/projectstubs.hpp
Normal file
22
tom/include/tom/commands/make/stubs/projectstubs.hpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#ifndef TOM_COMMANDS_MAKE_STUBS_PROJECTSTUBS_HPP
|
||||
#define TOM_COMMANDS_MAKE_STUBS_PROJECTSTUBS_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include <orm/macros/commonnamespace.hpp>
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom::Commands::Make::Stubs
|
||||
{
|
||||
|
||||
inline const auto *const XyzStub = R"T(#pragma once
|
||||
)T";
|
||||
|
||||
} // namespace Tom::Commands::Make::Stubs
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_COMMANDS_MAKE_STUBS_PROJECTSTUBS_HPP
|
||||
74
tom/include/tom/commands/migrations/freshcommand.hpp
Normal file
74
tom/include/tom/commands/migrations/freshcommand.hpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
#ifndef TOM_COMMANDS_MIGRATIONS_FRESHCOMMAND_HPP
|
||||
#define TOM_COMMANDS_MIGRATIONS_FRESHCOMMAND_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include "tom/commands/command.hpp"
|
||||
#include "tom/concerns/confirmable.hpp"
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom
|
||||
{
|
||||
class Migrator;
|
||||
|
||||
namespace Commands::Migrations
|
||||
{
|
||||
|
||||
/*! Rollback the last database migration/s. */
|
||||
class FreshCommand : public Command,
|
||||
public Concerns::Confirmable
|
||||
{
|
||||
Q_DISABLE_COPY(FreshCommand)
|
||||
|
||||
/*! Alias for the Command. */
|
||||
using Command = Commands::Command;
|
||||
|
||||
public:
|
||||
/*! Constructor. */
|
||||
FreshCommand(Application &application, QCommandLineParser &parser,
|
||||
std::shared_ptr<Migrator> migrator);
|
||||
/*! Virtual destructor. */
|
||||
inline ~FreshCommand() 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<QCommandLineOption> optionsSignature() const override;
|
||||
|
||||
/*! Execute the console command. */
|
||||
int run() override;
|
||||
|
||||
protected:
|
||||
/*! Determine if the developer has requested database seeding. */
|
||||
bool needsSeeding() const;
|
||||
/*! Run the database seeder command. */
|
||||
void runSeeder(QString &&databaseCmd) const;
|
||||
|
||||
/*! The migrator service instance. */
|
||||
std::shared_ptr<Migrator> m_migrator;
|
||||
};
|
||||
|
||||
/* public */
|
||||
|
||||
QString FreshCommand::name() const
|
||||
{
|
||||
return QStringLiteral("migrate:fresh");
|
||||
}
|
||||
|
||||
QString FreshCommand::description() const
|
||||
{
|
||||
return QLatin1String("Drop all tables and re-run all migrations");
|
||||
}
|
||||
|
||||
} // namespace Commands::Migrations
|
||||
} // namespace Tom
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_COMMANDS_MIGRATIONS_FRESHCOMMAND_HPP
|
||||
64
tom/include/tom/commands/migrations/installcommand.hpp
Normal file
64
tom/include/tom/commands/migrations/installcommand.hpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
#ifndef TOM_COMMANDS_MIGRATIONS_INSTALLCOMMAND_HPP
|
||||
#define TOM_COMMANDS_MIGRATIONS_INSTALLCOMMAND_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include "tom/commands/command.hpp"
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom
|
||||
{
|
||||
class MigrationRepository;
|
||||
|
||||
namespace Commands::Migrations
|
||||
{
|
||||
|
||||
/*! Create the migration database repository. */
|
||||
class InstallCommand : public Command
|
||||
{
|
||||
Q_DISABLE_COPY(InstallCommand)
|
||||
|
||||
public:
|
||||
/*! Constructor. */
|
||||
InstallCommand(Application &application, QCommandLineParser &parser,
|
||||
std::shared_ptr<MigrationRepository> repository);
|
||||
/*! Virtual destructor. */
|
||||
inline ~InstallCommand() 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<QCommandLineOption> optionsSignature() const override;
|
||||
|
||||
/*! Execute the console command. */
|
||||
int run() override;
|
||||
|
||||
protected:
|
||||
/*! The repository instance. */
|
||||
std::shared_ptr<MigrationRepository> m_repository;
|
||||
};
|
||||
|
||||
/* public */
|
||||
|
||||
QString InstallCommand::name() const
|
||||
{
|
||||
return QStringLiteral("migrate:install");
|
||||
}
|
||||
|
||||
QString InstallCommand::description() const
|
||||
{
|
||||
return QLatin1String("Create the migration repository");
|
||||
}
|
||||
|
||||
} // namespace Commands::Migrations
|
||||
} // namespace Tom
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_COMMANDS_MIGRATIONS_INSTALLCOMMAND_HPP
|
||||
79
tom/include/tom/commands/migrations/migratecommand.hpp
Normal file
79
tom/include/tom/commands/migrations/migratecommand.hpp
Normal file
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
#ifndef TOM_COMMANDS_MIGRATIONS_MIGRATECOMMAND_HPP
|
||||
#define TOM_COMMANDS_MIGRATIONS_MIGRATECOMMAND_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include "tom/commands/command.hpp"
|
||||
#include "tom/concerns/confirmable.hpp"
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom
|
||||
{
|
||||
class Migrator;
|
||||
|
||||
namespace Commands::Migrations
|
||||
{
|
||||
|
||||
/*! Run the database migrations up/down. */
|
||||
class MigrateCommand : public Command,
|
||||
public Concerns::Confirmable
|
||||
{
|
||||
Q_DISABLE_COPY(MigrateCommand)
|
||||
|
||||
/*! Alias for the Command. */
|
||||
using Command = Commands::Command;
|
||||
|
||||
public:
|
||||
/*! Constructor. */
|
||||
MigrateCommand(Application &application, QCommandLineParser &parser,
|
||||
std::shared_ptr<Migrator> migrator);
|
||||
/*! Virtual destructor. */
|
||||
inline ~MigrateCommand() 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<QCommandLineOption> optionsSignature() const override;
|
||||
|
||||
/*! Execute the console command. */
|
||||
int run() override;
|
||||
|
||||
protected:
|
||||
/*! Prepare the migration database for running. */
|
||||
void prepareDatabase() const;
|
||||
/*! Load the schema state to seed the initial database schema structure. */
|
||||
void loadSchemaState() const;
|
||||
|
||||
/*! Determine if the developer has requested database seeding. */
|
||||
bool needsSeeding() const;
|
||||
/*! Run the database seeder command. */
|
||||
void runSeeder() const;
|
||||
|
||||
/*! The migrator service instance. */
|
||||
std::shared_ptr<Migrator> m_migrator;
|
||||
};
|
||||
|
||||
/* public */
|
||||
|
||||
QString MigrateCommand::name() const
|
||||
{
|
||||
return QStringLiteral("migrate");
|
||||
}
|
||||
|
||||
QString MigrateCommand::description() const
|
||||
{
|
||||
return QLatin1String("Run the database migrations");
|
||||
}
|
||||
|
||||
} // namespace Commands::Migrations
|
||||
} // namespace Tom
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_COMMANDS_MIGRATIONS_MIGRATECOMMAND_HPP
|
||||
74
tom/include/tom/commands/migrations/refreshcommand.hpp
Normal file
74
tom/include/tom/commands/migrations/refreshcommand.hpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
#ifndef TOM_COMMANDS_MIGRATIONS_REFRESHCOMMAND_HPP
|
||||
#define TOM_COMMANDS_MIGRATIONS_REFRESHCOMMAND_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include "tom/commands/command.hpp"
|
||||
#include "tom/concerns/confirmable.hpp"
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom
|
||||
{
|
||||
class Migrator;
|
||||
|
||||
namespace Commands::Migrations
|
||||
{
|
||||
|
||||
/*! Rollback the last database migration. */
|
||||
class RefreshCommand : public Command,
|
||||
public Concerns::Confirmable
|
||||
{
|
||||
Q_DISABLE_COPY(RefreshCommand)
|
||||
|
||||
/*! Alias for the Command. */
|
||||
using Command = Commands::Command;
|
||||
|
||||
public:
|
||||
/*! Constructor. */
|
||||
RefreshCommand(Application &application, QCommandLineParser &parser,
|
||||
std::shared_ptr<Migrator> migrator);
|
||||
/*! Virtual destructor. */
|
||||
inline ~RefreshCommand() 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<QCommandLineOption> optionsSignature() const override;
|
||||
|
||||
/*! Execute the console command. */
|
||||
int run() override;
|
||||
|
||||
protected:
|
||||
/*! Determine if the developer has requested database seeding. */
|
||||
bool needsSeeding() const;
|
||||
/*! Run the database seeder command. */
|
||||
void runSeeder(QString &&databaseCmd) const;
|
||||
|
||||
/*! The migrator service instance. */
|
||||
std::shared_ptr<Migrator> m_migrator;
|
||||
};
|
||||
|
||||
/* public */
|
||||
|
||||
QString RefreshCommand::name() const
|
||||
{
|
||||
return QStringLiteral("migrate:refresh");
|
||||
}
|
||||
|
||||
QString RefreshCommand::description() const
|
||||
{
|
||||
return QLatin1String("Rollback and re-run all migrations");
|
||||
}
|
||||
|
||||
} // namespace Commands::Migrations
|
||||
} // namespace Tom
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_COMMANDS_MIGRATIONS_REFRESHCOMMAND_HPP
|
||||
69
tom/include/tom/commands/migrations/resetcommand.hpp
Normal file
69
tom/include/tom/commands/migrations/resetcommand.hpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
#ifndef TOM_COMMANDS_MIGRATIONS_RESETCOMMAND_HPP
|
||||
#define TOM_COMMANDS_MIGRATIONS_RESETCOMMAND_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include "tom/commands/command.hpp"
|
||||
#include "tom/concerns/confirmable.hpp"
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom
|
||||
{
|
||||
class Migrator;
|
||||
|
||||
namespace Commands::Migrations
|
||||
{
|
||||
|
||||
/*! Rollback the last database migration. */
|
||||
class ResetCommand : public Command,
|
||||
public Concerns::Confirmable
|
||||
{
|
||||
Q_DISABLE_COPY(ResetCommand)
|
||||
|
||||
/*! Alias for the Command. */
|
||||
using Command = Commands::Command;
|
||||
|
||||
public:
|
||||
/*! Constructor. */
|
||||
ResetCommand(Application &application, QCommandLineParser &parser,
|
||||
std::shared_ptr<Migrator> migrator);
|
||||
/*! Virtual destructor. */
|
||||
inline ~ResetCommand() 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<QCommandLineOption> optionsSignature() const override;
|
||||
|
||||
/*! Execute the console command. */
|
||||
int run() override;
|
||||
|
||||
protected:
|
||||
/*! The migrator service instance. */
|
||||
std::shared_ptr<Migrator> m_migrator;
|
||||
};
|
||||
|
||||
/* public */
|
||||
|
||||
QString ResetCommand::name() const
|
||||
{
|
||||
return QStringLiteral("migrate:reset");
|
||||
}
|
||||
|
||||
QString ResetCommand::description() const
|
||||
{
|
||||
return QLatin1String("Rollback all database migrations");
|
||||
}
|
||||
|
||||
} // namespace Commands::Migrations
|
||||
} // namespace Tom
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_COMMANDS_MIGRATIONS_RESETCOMMAND_HPP
|
||||
69
tom/include/tom/commands/migrations/rollbackcommand.hpp
Normal file
69
tom/include/tom/commands/migrations/rollbackcommand.hpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
#ifndef TOM_COMMANDS_MIGRATIONS_ROLLBACKCOMMAND_HPP
|
||||
#define TOM_COMMANDS_MIGRATIONS_ROLLBACKCOMMAND_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include "tom/commands/command.hpp"
|
||||
#include "tom/concerns/confirmable.hpp"
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom
|
||||
{
|
||||
class Migrator;
|
||||
|
||||
namespace Commands::Migrations
|
||||
{
|
||||
|
||||
/*! Rollback the last database migration. */
|
||||
class RollbackCommand : public Command,
|
||||
public Concerns::Confirmable
|
||||
{
|
||||
Q_DISABLE_COPY(RollbackCommand)
|
||||
|
||||
/*! Alias for the Command. */
|
||||
using Command = Commands::Command;
|
||||
|
||||
public:
|
||||
/*! Constructor. */
|
||||
RollbackCommand(Application &application, QCommandLineParser &parser,
|
||||
std::shared_ptr<Migrator> migrator);
|
||||
/*! Virtual destructor. */
|
||||
inline ~RollbackCommand() 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<QCommandLineOption> optionsSignature() const override;
|
||||
|
||||
/*! Execute the console command. */
|
||||
int run() override;
|
||||
|
||||
protected:
|
||||
/*! The migrator service instance. */
|
||||
std::shared_ptr<Migrator> m_migrator;
|
||||
};
|
||||
|
||||
/* public */
|
||||
|
||||
QString RollbackCommand::name() const
|
||||
{
|
||||
return QStringLiteral("migrate:rollback");
|
||||
}
|
||||
|
||||
QString RollbackCommand::description() const
|
||||
{
|
||||
return QLatin1String("Rollback the last database migration");
|
||||
}
|
||||
|
||||
} // namespace Commands::Migrations
|
||||
} // namespace Tom
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_COMMANDS_MIGRATIONS_ROLLBACKCOMMAND_HPP
|
||||
118
tom/include/tom/commands/migrations/statuscommand.hpp
Normal file
118
tom/include/tom/commands/migrations/statuscommand.hpp
Normal file
@@ -0,0 +1,118 @@
|
||||
#pragma once
|
||||
#ifndef TOM_COMMANDS_MIGRATIONS_STATUSCOMMAND_HPP
|
||||
#define TOM_COMMANDS_MIGRATIONS_STATUSCOMMAND_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include <QVariant>
|
||||
|
||||
#include <tabulate/table.hpp>
|
||||
|
||||
#ifdef TINYTOM_TESTS_CODE
|
||||
#include <orm/macros/threadlocal.hpp>
|
||||
#endif
|
||||
|
||||
#include "tom/commands/command.hpp"
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom
|
||||
{
|
||||
class Migrator;
|
||||
|
||||
namespace Commands::Migrations
|
||||
{
|
||||
|
||||
/*! Show the status of each migration. */
|
||||
class StatusCommand : public Command
|
||||
{
|
||||
Q_DISABLE_COPY(StatusCommand)
|
||||
|
||||
/*! Alias for the tabulate cell. */
|
||||
using TableCell = InteractsWithIO::TableCell;
|
||||
/*! Alias for the tabulate row. */
|
||||
using TableRow = InteractsWithIO::TableRow;
|
||||
|
||||
public:
|
||||
/*! Constructor. */
|
||||
StatusCommand(Application &application, QCommandLineParser &parser,
|
||||
std::shared_ptr<Migrator> migrator);
|
||||
/*! Virtual destructor. */
|
||||
inline ~StatusCommand() 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<QCommandLineOption> optionsSignature() const override;
|
||||
|
||||
/*! Execute the console command. */
|
||||
int run() override;
|
||||
|
||||
#ifdef TINYTOM_TESTS_CODE
|
||||
/*! Alias for the test output row. */
|
||||
using StatusRow = std::vector<std::string>;
|
||||
|
||||
/*! Get result of the status command (used in auto tests). */
|
||||
inline static std::vector<StatusRow> status() noexcept;
|
||||
/*! Enable logic for unit testing? */
|
||||
inline static void setInUnitTests() noexcept;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
/*! Get the status for the given ran migrations. */
|
||||
std::vector<TableRow>
|
||||
getStatusFor(QVector<QVariant> &&ran,
|
||||
std::map<QString, QVariant> &&batches) const;
|
||||
#ifdef TINYTOM_TESTS_CODE
|
||||
/*! Transform migrations status for comparing in auto tests. */
|
||||
std::vector<StatusRow>
|
||||
statusForUnitTest(std::vector<TableRow> &&migrations) const;
|
||||
#endif
|
||||
|
||||
/*! The migrator service instance. */
|
||||
std::shared_ptr<Migrator> m_migrator;
|
||||
|
||||
#ifdef TINYTOM_TESTS_CODE
|
||||
/*! Result of the status command (used in auto tests). */
|
||||
T_THREAD_LOCAL
|
||||
inline static std::vector<StatusRow> m_status;
|
||||
/*! Is enabled logic for unit testing? */
|
||||
T_THREAD_LOCAL
|
||||
inline static auto m_inUnitTests = false;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* public */
|
||||
|
||||
QString StatusCommand::name() const
|
||||
{
|
||||
return QStringLiteral("migrate:status");
|
||||
}
|
||||
|
||||
QString StatusCommand::description() const
|
||||
{
|
||||
return QLatin1String("Show the status of each migration");
|
||||
}
|
||||
|
||||
#ifdef TINYTOM_TESTS_CODE
|
||||
std::vector<StatusCommand::StatusRow> StatusCommand::status() noexcept
|
||||
{
|
||||
return m_status;
|
||||
}
|
||||
|
||||
void StatusCommand::setInUnitTests() noexcept
|
||||
{
|
||||
m_inUnitTests = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace Commands::Migrations
|
||||
} // namespace Tom
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_COMMANDS_MIGRATIONS_STATUSCOMMAND_HPP
|
||||
67
tom/include/tom/concerns/callscommands.hpp
Normal file
67
tom/include/tom/concerns/callscommands.hpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
#ifndef TOM_CONCERNS_CALLSCOMMANDS_HPP
|
||||
#define TOM_CONCERNS_CALLSCOMMANDS_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
#include <orm/macros/commonnamespace.hpp>
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom
|
||||
{
|
||||
namespace Commands
|
||||
{
|
||||
class Command;
|
||||
}
|
||||
|
||||
namespace Concerns
|
||||
{
|
||||
|
||||
/*! Invoke another command by name and passed arguments. */
|
||||
class CallsCommands
|
||||
{
|
||||
Q_DISABLE_COPY(CallsCommands)
|
||||
|
||||
public:
|
||||
/*! Default constructor. */
|
||||
inline CallsCommands() = default;
|
||||
/*! Default destructor. */
|
||||
inline ~CallsCommands() = default;
|
||||
|
||||
/*! Call another console command. */
|
||||
inline int call(const QString &command, QStringList &&arguments = {}) const;
|
||||
|
||||
protected:
|
||||
/*! Run the given console command. */
|
||||
int runCommand(const QString &command, QStringList &&arguments) const;
|
||||
|
||||
/*! Create command-line arguments from the given arguments. */
|
||||
QStringList
|
||||
createCommandLineArguments(const QString &command, QStringList &&arguments,
|
||||
QStringList &¤tArguments) const;
|
||||
|
||||
/*! Get common command-line arguments from current command-line arguments. */
|
||||
QStringList getCommonArguments(QStringList &&arguments) const;
|
||||
|
||||
private:
|
||||
/*! Static cast *this to the Command & derived type, const version. */
|
||||
const Commands::Command &command() const;
|
||||
};
|
||||
|
||||
/* public */
|
||||
|
||||
int CallsCommands::call(const QString &command, QStringList &&arguments) const
|
||||
{
|
||||
return runCommand(std::move(command), std::move(arguments));
|
||||
}
|
||||
|
||||
} // namespace Concerns
|
||||
} // namespace Tom
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_CONCERNS_CALLSCOMMANDS_HPP
|
||||
58
tom/include/tom/concerns/confirmable.hpp
Normal file
58
tom/include/tom/concerns/confirmable.hpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
#ifndef TOM_CONCERNS_CONFIRMABLE_HPP
|
||||
#define TOM_CONCERNS_CONFIRMABLE_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <orm/macros/commonnamespace.hpp>
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom
|
||||
{
|
||||
namespace Commands
|
||||
{
|
||||
class Command;
|
||||
}
|
||||
|
||||
namespace Concerns
|
||||
{
|
||||
|
||||
/*! Prints alert and asks for the confirmation (Y/N). */
|
||||
class Confirmable
|
||||
{
|
||||
Q_DISABLE_COPY(Confirmable)
|
||||
|
||||
/*! Alias for the Command. */
|
||||
using Command = Commands::Command;
|
||||
|
||||
public:
|
||||
/*! Constructor (int param. to avoid interpret it as copy ctor). */
|
||||
Confirmable(Command &command, int);
|
||||
/*! Default destructor. */
|
||||
inline ~Confirmable() = default;
|
||||
|
||||
/*! Confirm before proceeding with the action (only in production environment). */
|
||||
bool confirmToProceed(
|
||||
const QString &warning = QLatin1String("Application In Production!"),
|
||||
const std::function<bool()> &callback = nullptr) const;
|
||||
|
||||
protected:
|
||||
/*! Get the default confirmation callback. */
|
||||
std::function<bool()> defaultConfirmCallback() const;
|
||||
|
||||
/*! Reference to a command that should be confimable. */
|
||||
std::reference_wrapper<Command> m_command;
|
||||
};
|
||||
|
||||
} // namespace Concerns
|
||||
} // namespace Tom
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_CONCERNS_CONFIRMABLE_HPP
|
||||
238
tom/include/tom/concerns/interactswithio.hpp
Normal file
238
tom/include/tom/concerns/interactswithio.hpp
Normal file
@@ -0,0 +1,238 @@
|
||||
#pragma once
|
||||
#ifndef TOM_CONCERNS_INTERACTSWITHIO_HPP
|
||||
#define TOM_CONCERNS_INTERACTSWITHIO_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
#include <tabulate/table.hpp>
|
||||
|
||||
#include <orm/macros/commonnamespace.hpp>
|
||||
#include <orm/macros/export.hpp>
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
class QCommandLineParser;
|
||||
|
||||
namespace Tom
|
||||
{
|
||||
class Application;
|
||||
class Terminal;
|
||||
|
||||
namespace Concerns
|
||||
{
|
||||
|
||||
/*! Set of methods for the console output/input. */
|
||||
class SHAREDLIB_EXPORT InteractsWithIO
|
||||
{
|
||||
Q_DISABLE_COPY(InteractsWithIO)
|
||||
|
||||
// To access private ctor and errorWallInternal() (used by logException())
|
||||
friend Tom::Application;
|
||||
|
||||
public:
|
||||
/*! Alias for the tabulate cell. */
|
||||
using TableCell = std::variant<std::string, tabulate::Table>;
|
||||
/*! Alias for the tabulate row. */
|
||||
using TableRow = std::vector<TableCell>;
|
||||
|
||||
/*! Constructor. */
|
||||
explicit InteractsWithIO(const QCommandLineParser &parser);
|
||||
/*! Default destructor. */
|
||||
~InteractsWithIO();
|
||||
|
||||
/*! Base enum for the verbosity levels. */
|
||||
enum struct Verbosity {
|
||||
Quiet = 0x0001,
|
||||
Normal = 0x0002,
|
||||
Verbose = 0x0004,
|
||||
VeryVerbose = 0x0008,
|
||||
Debug = 0x0010,
|
||||
};
|
||||
/*! Quiet verbosity. */
|
||||
static constexpr Verbosity Quiet = Verbosity::Quiet;
|
||||
/*! Normal verbosity (default). */
|
||||
static constexpr Verbosity Normal = Verbosity::Normal;
|
||||
/*! Verbose verbosity. */
|
||||
static constexpr Verbosity Verbose = Verbosity::Verbose;
|
||||
/*! Very verbose verbosity. */
|
||||
static constexpr Verbosity VeryVerbose = Verbosity::VeryVerbose;
|
||||
/*! Debug verbosity. */
|
||||
static constexpr Verbosity Debug = Verbosity::Debug;
|
||||
|
||||
/*! Write a string as standard output. */
|
||||
const InteractsWithIO &line(const QString &string, bool newline = true,
|
||||
Verbosity verbosity = Normal,
|
||||
QString &&style = "",
|
||||
std::ostream &cout = std::cout) const;
|
||||
/*! Write a string as note output. */
|
||||
const InteractsWithIO ¬e(const QString &string, bool newline = true,
|
||||
Verbosity verbosity = Normal) const;
|
||||
/*! Write a string as information output. */
|
||||
const InteractsWithIO &info(const QString &string, bool newline = true,
|
||||
Verbosity verbosity = Normal) const;
|
||||
/*! Write a string as error output. */
|
||||
const InteractsWithIO &error(const QString &string, bool newline = true,
|
||||
Verbosity verbosity = Normal) const;
|
||||
/*! Write a string as comment output. */
|
||||
const InteractsWithIO &comment(const QString &string, bool newline = true,
|
||||
Verbosity verbosity = Normal) const;
|
||||
/*! Write a string in an alert box. */
|
||||
const InteractsWithIO &alert(const QString &string,
|
||||
Verbosity verbosity = Normal) const;
|
||||
/*! Write a string as error output (red box with a white text). */
|
||||
const InteractsWithIO &errorWall(const QString &string,
|
||||
Verbosity verbosity = Normal) const;
|
||||
|
||||
/*! Write a string as standard output, wide version. */
|
||||
const InteractsWithIO &wline(const QString &string, bool newline = true,
|
||||
Verbosity verbosity = Normal,
|
||||
QString &&style = "",
|
||||
std::wostream &wcout = std::wcout) const;
|
||||
/*! Write a string as note output, wide version. */
|
||||
const InteractsWithIO &wnote(const QString &string, bool newline = true,
|
||||
Verbosity verbosity = Normal) const;
|
||||
/*! Write a string as information output, wide version. */
|
||||
const InteractsWithIO &winfo(const QString &string, bool newline = true,
|
||||
Verbosity verbosity = Normal) const;
|
||||
/*! Write a string as error output, wide version. */
|
||||
const InteractsWithIO &werror(const QString &string, bool newline = true,
|
||||
Verbosity verbosity = Normal) const;
|
||||
/*! Write a string as comment output, wide version. */
|
||||
const InteractsWithIO &wcomment(const QString &string, bool newline = true,
|
||||
Verbosity verbosity = Normal) const;
|
||||
/*! Write a string in an alert box, wide version. */
|
||||
const InteractsWithIO &walert(const QString &string,
|
||||
Verbosity verbosity = Normal) const;
|
||||
/*! Write a string as error output (red box with a white text). */
|
||||
const InteractsWithIO &werrorWall(const QString &string,
|
||||
Verbosity verbosity = Normal) const;
|
||||
|
||||
/*! Write a blank line. */
|
||||
const InteractsWithIO &newLine(int count = 1,
|
||||
Verbosity verbosity = Normal) const;
|
||||
/*! Write a blank line, wide version. */
|
||||
const InteractsWithIO &newLineErr(int count = 1,
|
||||
Verbosity verbosity = Normal) const;
|
||||
|
||||
/*! Format input to textual table. */
|
||||
const InteractsWithIO &
|
||||
table(const TableRow &headers, const std::vector<TableRow> &rows,
|
||||
Verbosity verbosity = Normal) const;
|
||||
|
||||
/*! Confirm a question with the user. */
|
||||
bool confirm(const QString &question, bool defaultAnswer = false) const;
|
||||
|
||||
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? */
|
||||
inline bool isQuietVerbosity() const noexcept;
|
||||
/*! Is normal verbosity level? */
|
||||
inline bool isNormalVerbosity() const noexcept;
|
||||
/*! Is verbose verbosity level? */
|
||||
inline bool isVerboseVerbosity() const noexcept;
|
||||
/*! Is very verbose verbosity level? */
|
||||
inline bool isVeryVerboseVerbosity() const noexcept;
|
||||
/*! Is debug verbosity level? */
|
||||
inline bool isDebugVerbosity() const noexcept;
|
||||
|
||||
private:
|
||||
/*! Constructor (used by TomApplication::logException()). */
|
||||
explicit InteractsWithIO(bool noAnsi);
|
||||
|
||||
/*! Repalce text tags with ANSI sequences. */
|
||||
QString parseOutput(QString string, bool isAnsi = true) const;
|
||||
/*! Remove tom ansi tags from the given string. */
|
||||
QString stripTags(QString string) const;
|
||||
|
||||
/*! Initialize verbosity by set options in the command-line parser. */
|
||||
Verbosity initializeVerbosity(const QCommandLineParser &parser) const;
|
||||
/*! Initialize ansi support by set options in the command-line parser. */
|
||||
std::optional<bool> initializeAnsi(const QCommandLineParser &parser) const;
|
||||
/*! Initialize ansi support by noAnsi passed to the Application::logException. */
|
||||
std::optional<bool> initializeNoAnsi(bool noAnsi) const;
|
||||
|
||||
/*! Number of the option name set on the command line (used by eg. -vvv). */
|
||||
QStringList::size_type
|
||||
countSetOption(const QString &optionName, const QCommandLineParser &parser) const;
|
||||
/*! 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 for non-tty). */
|
||||
bool isAnsiOutput(std::ostream &cout = std::cout) const;
|
||||
/*! Should the given output use ansi? (ansi is disabled for non-tty),
|
||||
wide version. */
|
||||
bool isAnsiWOutput(std::wostream &cout = std::wcout) const;
|
||||
|
||||
/*! Write a string as error output (red box with a white text). */
|
||||
QString errorWallInternal(const QString &string) const;
|
||||
|
||||
/*! 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 this input means interactive? */
|
||||
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. */
|
||||
std::optional<bool> m_ansi = std::nullopt;
|
||||
/*! Describes current terminal features. */
|
||||
std::unique_ptr<Terminal> m_terminal;
|
||||
};
|
||||
|
||||
/* protected */
|
||||
|
||||
InteractsWithIO::Verbosity InteractsWithIO::verbosity() const noexcept
|
||||
{
|
||||
return m_verbosity;
|
||||
}
|
||||
|
||||
bool InteractsWithIO::isQuietVerbosity() const noexcept
|
||||
{
|
||||
return m_verbosity == Quiet;
|
||||
}
|
||||
|
||||
bool InteractsWithIO::isNormalVerbosity() const noexcept
|
||||
{
|
||||
return m_verbosity == Normal;
|
||||
}
|
||||
|
||||
bool InteractsWithIO::isVerboseVerbosity() const noexcept
|
||||
{
|
||||
return m_verbosity == Verbose;
|
||||
}
|
||||
|
||||
bool InteractsWithIO::isVeryVerboseVerbosity() const noexcept
|
||||
{
|
||||
return m_verbosity == VeryVerbose;
|
||||
}
|
||||
|
||||
bool InteractsWithIO::isDebugVerbosity() const noexcept
|
||||
{
|
||||
return m_verbosity == Debug;
|
||||
}
|
||||
|
||||
} // namespace Concerns
|
||||
} // namespace Tom
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_CONCERNS_INTERACTSWITHIO_HPP
|
||||
53
tom/include/tom/concerns/printsoptions.hpp
Normal file
53
tom/include/tom/concerns/printsoptions.hpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
#ifndef TOM_CONCERNS_PRINTSOPTIONS_HPP
|
||||
#define TOM_CONCERNS_PRINTSOPTIONS_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
#include <orm/macros/commonnamespace.hpp>
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom
|
||||
{
|
||||
namespace Commands
|
||||
{
|
||||
class Command;
|
||||
}
|
||||
|
||||
namespace Concerns
|
||||
{
|
||||
|
||||
/*! Print options section. */
|
||||
class PrintsOptions
|
||||
{
|
||||
Q_DISABLE_COPY(PrintsOptions)
|
||||
|
||||
public:
|
||||
/*! Constructor (int param. to avoid interpret it as copy ctor). */
|
||||
PrintsOptions(const Commands::Command &command, int);
|
||||
/*! Default destructor. */
|
||||
inline ~PrintsOptions() = default;
|
||||
|
||||
/*! Print options section. */
|
||||
int printOptionsSection(bool commonOptions) const;
|
||||
|
||||
private:
|
||||
/*! Get max. option size in all options. */
|
||||
int optionsMaxSize() const;
|
||||
/*! Print options to the console. */
|
||||
void printOptions(int optionsMaxSize) const;
|
||||
|
||||
/*! Reference to the command. */
|
||||
std::reference_wrapper<const Commands::Command> m_command;
|
||||
};
|
||||
|
||||
} // namespace Concerns
|
||||
} // namespace Tom
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_CONCERNS_PRINTSOPTIONS_HPP
|
||||
26
tom/include/tom/exceptions/invalidargumenterror.hpp
Normal file
26
tom/include/tom/exceptions/invalidargumenterror.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#ifndef TOM_EXCEPTIONS_INVALIDARGUMENTERROR_HPP
|
||||
#define TOM_EXCEPTIONS_INVALIDARGUMENTERROR_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include "tom/exceptions/logicerror.hpp"
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom::Exceptions
|
||||
{
|
||||
|
||||
/*! Tom Invalid argument exception. */
|
||||
class InvalidArgumentError : public LogicError
|
||||
{
|
||||
/*! Inherit constructors. */
|
||||
using LogicError::LogicError;
|
||||
};
|
||||
|
||||
} // namespace Tom::Exceptions
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_EXCEPTIONS_INVALIDARGUMENTERROR_HPP
|
||||
26
tom/include/tom/exceptions/invalidtemplateargumenterror.hpp
Normal file
26
tom/include/tom/exceptions/invalidtemplateargumenterror.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#ifndef TOM_EXCEPTIONS_INVALIDTEMPLATEARGUMENTERROR_HPP
|
||||
#define TOM_EXCEPTIONS_INVALIDTEMPLATEARGUMENTERROR_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include "tom/exceptions/invalidargumenterror.hpp"
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom::Exceptions
|
||||
{
|
||||
|
||||
/*! Tom invalid template argument exception. */
|
||||
class InvalidTemplateArgumentError : public InvalidArgumentError
|
||||
{
|
||||
/*! Inherit constructors. */
|
||||
using InvalidArgumentError::InvalidArgumentError;
|
||||
};
|
||||
|
||||
} // namespace Tom::Exceptions
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_EXCEPTIONS_INVALIDTEMPLATEARGUMENTERROR_HPP
|
||||
48
tom/include/tom/exceptions/logicerror.hpp
Normal file
48
tom/include/tom/exceptions/logicerror.hpp
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
#ifndef TOM_EXCEPTIONS_LOGICERROR_HPP
|
||||
#define TOM_EXCEPTIONS_LOGICERROR_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "tom/exceptions/tomerror.hpp"
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom::Exceptions
|
||||
{
|
||||
|
||||
/*! Tom Logic exception. */
|
||||
class LogicError : public std::logic_error,
|
||||
public TomError
|
||||
{
|
||||
public:
|
||||
/*! const char * constructor. */
|
||||
explicit LogicError(const char *message);
|
||||
/*! QString constructor. */
|
||||
explicit LogicError(const QString &message);
|
||||
/*! std::string constructor. */
|
||||
explicit LogicError(const std::string &message);
|
||||
|
||||
/*! Return exception message as a QString. */
|
||||
inline const QString &message() const noexcept;
|
||||
|
||||
protected:
|
||||
/*! Exception message. */
|
||||
QString m_message = what();
|
||||
};
|
||||
|
||||
const QString &LogicError::message() const noexcept
|
||||
{
|
||||
return m_message;
|
||||
}
|
||||
|
||||
} // namespace Tom::Exceptions
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_EXCEPTIONS_LOGICERROR_HPP
|
||||
48
tom/include/tom/exceptions/runtimeerror.hpp
Normal file
48
tom/include/tom/exceptions/runtimeerror.hpp
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
#ifndef TOM_EXCEPTIONS_RUNTIMEERROR_HPP
|
||||
#define TOM_EXCEPTIONS_RUNTIMEERROR_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "tom/exceptions/tomerror.hpp"
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom::Exceptions
|
||||
{
|
||||
|
||||
/*! Tom Runtime exception. */
|
||||
class RuntimeError : public std::runtime_error,
|
||||
public TomError
|
||||
{
|
||||
public:
|
||||
/*! const char * constructor. */
|
||||
explicit RuntimeError(const char *message);
|
||||
/*! QString constructor. */
|
||||
explicit RuntimeError(const QString &message);
|
||||
/*! std::string constructor. */
|
||||
explicit RuntimeError(const std::string &message);
|
||||
|
||||
/*! Return exception message as a QString. */
|
||||
inline const QString &message() const noexcept;
|
||||
|
||||
protected:
|
||||
/*! Exception message. */
|
||||
QString m_message = what();
|
||||
};
|
||||
|
||||
const QString &RuntimeError::message() const noexcept
|
||||
{
|
||||
return m_message;
|
||||
}
|
||||
|
||||
} // namespace Tom::Exceptions
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_EXCEPTIONS_RUNTIMEERROR_HPP
|
||||
27
tom/include/tom/exceptions/tomerror.hpp
Normal file
27
tom/include/tom/exceptions/tomerror.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#ifndef TOM_EXCEPTIONS_TOMERROR_HPP
|
||||
#define TOM_EXCEPTIONS_TOMERROR_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include <orm/macros/commonnamespace.hpp>
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom::Exceptions
|
||||
{
|
||||
|
||||
/*! Tom exceptions tag, all Tom exceptions are derived from this class. */
|
||||
class TomError
|
||||
{
|
||||
public:
|
||||
/*! Virtual destructor. */
|
||||
inline virtual ~TomError() = default;
|
||||
};
|
||||
|
||||
} // namespace Tom::Exceptions
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_EXCEPTIONS_TOMERROR_HPP
|
||||
131
tom/include/tom/migration.hpp
Normal file
131
tom/include/tom/migration.hpp
Normal file
@@ -0,0 +1,131 @@
|
||||
#pragma once
|
||||
#ifndef TOM_MIGRATION_HPP
|
||||
#define TOM_MIGRATION_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include <orm/schema.hpp>
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom
|
||||
{
|
||||
|
||||
/*! Migrations base class. */
|
||||
class Migration
|
||||
{
|
||||
Q_DISABLE_COPY(Migration)
|
||||
|
||||
public:
|
||||
/*! Default constructor. */
|
||||
inline Migration() = default;
|
||||
/*! Pure virtual destructor. */
|
||||
inline virtual ~Migration() = 0;
|
||||
|
||||
/*! Run the migrations. */
|
||||
virtual void up() const = 0;
|
||||
/*! Reverse the migrations. */
|
||||
virtual void down() const = 0;
|
||||
|
||||
/*! The name of the database connection to use. */
|
||||
QString connection;
|
||||
|
||||
/*! Wrapping the migration within a transaction, if supported. */
|
||||
bool withinTransaction = true;
|
||||
};
|
||||
|
||||
Migration::~Migration() = default;
|
||||
|
||||
} // namespace Tom
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
// Predefine some aliases so the user doesn't have to
|
||||
namespace Migrations
|
||||
{
|
||||
/*! Alias for the Schema Blueprint. */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::SchemaNs::Blueprint; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the Tom Migration. */
|
||||
using TINYORM_COMMON_NAMESPACE::Tom::Migration; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the Orm Schema. */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Schema; // NOLINT(misc-unused-using-decls)
|
||||
|
||||
// Aliases for the most used string constants
|
||||
/*! Alias for the string constant "id". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::ID; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "name". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::NAME; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "size". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::SIZE_; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "created_at". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::CREATED_AT; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "updated_at". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::UPDATED_AT; // NOLINT(misc-unused-using-decls)
|
||||
|
||||
/*! Alias for the string constant "MySQL". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::MYSQL_; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "PostgreSQL". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::POSTGRESQL; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "SQLite". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::SQLITE; // NOLINT(misc-unused-using-decls)
|
||||
|
||||
/*! Alias for the string constant "driver". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::driver_; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "host". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::host_; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "port". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::port_; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "database". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::database_; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "schema". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::schema_; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "username". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::username_; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "password". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::password_; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "charset". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::charset_; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "collation". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::collation_; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "timezone". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::timezone_; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "prefix". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::prefix_; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "options". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::options_; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "strict". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::strict_; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "engine". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::engine_; // NOLINT(misc-unused-using-decls)
|
||||
|
||||
/*! Alias for the string constant "127.0.0.1". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::H127001; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "localhost". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::LOCALHOST; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "3306". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::P3306; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "5432". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::P5432; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "root". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::ROOT; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "UTC". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::UTC; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "LOCAL". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::LOCAL; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "SYSTEM". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::SYSTEM; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "public". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::PUBLIC; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "utf8". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::UTF8; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "utf8mb4". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::UTF8MB4; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "InnoDB". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::InnoDB; // NOLINT(misc-unused-using-decls)
|
||||
/*! Alias for the string constant "MyISAM". */
|
||||
using TINYORM_COMMON_NAMESPACE::Orm::Constants::MyISAM; // NOLINT(misc-unused-using-decls)
|
||||
|
||||
} // namespace Migrations
|
||||
|
||||
#endif // TOM_MIGRATION_HPP
|
||||
65
tom/include/tom/migrationcreator.hpp
Normal file
65
tom/include/tom/migrationcreator.hpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
#ifndef TOM_MIGRATIONCREATOR_HPP
|
||||
#define TOM_MIGRATIONCREATOR_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include <orm/macros/commonnamespace.hpp>
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom
|
||||
{
|
||||
|
||||
/*! Migration file generator (used by the make:migration command). */
|
||||
class MigrationCreator
|
||||
{
|
||||
Q_DISABLE_COPY(MigrationCreator)
|
||||
|
||||
/*! Alias for the filesystem path. */
|
||||
using fspath = std::filesystem::path;
|
||||
|
||||
public:
|
||||
/*! Default constructor. */
|
||||
inline MigrationCreator() = default;
|
||||
/*! Default destructor. */
|
||||
inline ~MigrationCreator() = default;
|
||||
|
||||
/*! Create a new migration at the given path. */
|
||||
fspath create(const QString &name, fspath &&migrationsPath,
|
||||
const QString &table = "", bool create = false) const;
|
||||
|
||||
protected:
|
||||
/*! Ensure that a migration with the given name doesn't already exist. */
|
||||
void throwIfMigrationAlreadyExists(const QString &name,
|
||||
const fspath &migrationsPath) const;
|
||||
|
||||
/*! Get the migration stub file. */
|
||||
QString getStub(const QString &table, bool create) const;
|
||||
|
||||
/*! Get the path to the stubs. */
|
||||
fspath stubPath() const;
|
||||
/*! Get the full path to the migration. */
|
||||
fspath getPath(const QString &name, const fspath &path) const;
|
||||
/*! Get the date prefix for the migration. */
|
||||
std::string getDatePrefix() const;
|
||||
|
||||
/*! Populate the place-holders in the migration stub. */
|
||||
std::string populateStub(const QString &name, QString &&stub,
|
||||
const QString &table) const;
|
||||
/*! Get the class name of a migration name. */
|
||||
QString getClassName(const QString &name) const;
|
||||
/*! Ensure a directory exists. */
|
||||
void ensureDirectoryExists(fspath &&path) const;
|
||||
};
|
||||
|
||||
} // namespace Tom
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_MIGRATIONCREATOR_HPP
|
||||
94
tom/include/tom/migrationrepository.hpp
Normal file
94
tom/include/tom/migrationrepository.hpp
Normal file
@@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
#ifndef TOM_MIGRATIONREPOSITORY_HPP
|
||||
#define TOM_MIGRATIONREPOSITORY_HPP
|
||||
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include <orm/connectionresolverinterface.hpp>
|
||||
|
||||
#include "tom/tomtypes.hpp"
|
||||
|
||||
class QSqlQuery;
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Orm::Query
|
||||
{
|
||||
class Builder;
|
||||
}
|
||||
|
||||
namespace Tom
|
||||
{
|
||||
|
||||
/*! Migrations database repository. */
|
||||
class MigrationRepository
|
||||
{
|
||||
Q_DISABLE_COPY(MigrationRepository)
|
||||
|
||||
/*! Alias for the ConnectionResolverInterface. */
|
||||
using ConnectionResolverInterface = Orm::ConnectionResolverInterface;
|
||||
/*! Alias for the DatabaseConnection. */
|
||||
using DatabaseConnection = Orm::DatabaseConnection;
|
||||
/*! Alias for the QueryBuilder. */
|
||||
using QueryBuilder = Orm::Query::Builder;
|
||||
|
||||
public:
|
||||
/*! Constructor. */
|
||||
MigrationRepository(std::shared_ptr<ConnectionResolverInterface> &&resolver,
|
||||
QString table);
|
||||
/*! Default destructor. */
|
||||
inline ~MigrationRepository() = default;
|
||||
|
||||
/*! Get the completed migrations (only migration names using pluck). */
|
||||
QVector<QVariant> getRanSimple() const;
|
||||
/*! Get the completed migrations. */
|
||||
std::vector<MigrationItem> getRan(const QString &order) const;
|
||||
/*! Get list of migrations. */
|
||||
std::vector<MigrationItem> getMigrations(int steps) const;
|
||||
/*! Get the last migration batch. */
|
||||
std::vector<MigrationItem> getLast() const;
|
||||
/*! Get the completed migrations with their batch numbers. */
|
||||
std::map<QString, QVariant> getMigrationBatches() const;
|
||||
/*! Log that a migration was run. */
|
||||
void log(const QString &file, int batch) const;
|
||||
/*! Remove a migration from the log. */
|
||||
void deleteMigration(quint64 id) const;
|
||||
/*! Get the next migration batch number. */
|
||||
int getNextBatchNumber() const;
|
||||
/*! Get the last migration batch number. */
|
||||
int getLastBatchNumber() const;
|
||||
/*! Create the migration repository data store. */
|
||||
void createRepository() const;
|
||||
/*! Determine if the migration repository exists. */
|
||||
bool repositoryExists() const;
|
||||
/*! Delete the migration repository data store. */
|
||||
void deleteRepository() const;
|
||||
|
||||
/*! Resolve the database connection instance. */
|
||||
DatabaseConnection &getConnection() const;
|
||||
/*! Set the connection name to use in the repository. */
|
||||
void setConnection(const QString &name, std::optional<bool> &&debugSql);
|
||||
|
||||
protected:
|
||||
/*! Get a query builder for the migration table. */
|
||||
QSharedPointer<QueryBuilder> table() const;
|
||||
|
||||
/*! Hydrate a vector of migration items from a raw QSqlQuery. */
|
||||
std::vector<MigrationItem> hydrateMigrations(QSqlQuery &query) const;
|
||||
|
||||
/*! Set the debug sql for the current repository connection. */
|
||||
void setConnectionDebugSql(std::optional<bool> &&debugSql) const;
|
||||
|
||||
/*! The database connection resolver instance. */
|
||||
std::shared_ptr<ConnectionResolverInterface> m_resolver;
|
||||
/*! The name of the migration table. */
|
||||
QString m_table;
|
||||
/*! The name of the database connection to use. */
|
||||
QString m_connection {};
|
||||
};
|
||||
|
||||
} // namespace Tom
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_MIGRATIONREPOSITORY_HPP
|
||||
178
tom/include/tom/migrator.hpp
Normal file
178
tom/include/tom/migrator.hpp
Normal file
@@ -0,0 +1,178 @@
|
||||
#pragma once
|
||||
#ifndef TOM_MIGRATOR_HPP
|
||||
#define TOM_MIGRATOR_HPP
|
||||
|
||||
#include <set>
|
||||
#include <typeindex>
|
||||
|
||||
#include <orm/connectionresolverinterface.hpp>
|
||||
#include <orm/types/log.hpp>
|
||||
|
||||
#include "tom/concerns/interactswithio.hpp"
|
||||
#include "tom/tomtypes.hpp"
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom
|
||||
{
|
||||
|
||||
class MigrationRepository;
|
||||
|
||||
/*! Migration service class. */
|
||||
class Migrator : public Concerns::InteractsWithIO
|
||||
{
|
||||
Q_DISABLE_COPY(Migrator)
|
||||
|
||||
/*! Alias for the ConnectionResolverInterface. */
|
||||
using ConnectionResolverInterface = Orm::ConnectionResolverInterface;
|
||||
/*! Alias for the DatabaseConnection. */
|
||||
using DatabaseConnection = Orm::DatabaseConnection;
|
||||
/*! Alias for the pretend Log. */
|
||||
using Log = Orm::Types::Log;
|
||||
|
||||
public:
|
||||
/*! Constructor. */
|
||||
Migrator(std::shared_ptr<MigrationRepository> &&repository,
|
||||
std::shared_ptr<ConnectionResolverInterface> &&resolver,
|
||||
const std::vector<std::shared_ptr<Migration>> &migrations,
|
||||
const QCommandLineParser &parser);
|
||||
/*! Default destructor. */
|
||||
inline ~Migrator() = default;
|
||||
|
||||
/* Main migrate operations */
|
||||
/*! Migrate options. */
|
||||
struct MigrateOptions
|
||||
{
|
||||
/*! Dump the SQL queries that would be run. */
|
||||
bool pretend = false;
|
||||
/*! Force the migrations to be run so they can be rolled back individually. */
|
||||
bool step = false;
|
||||
/*! The number of migrations to be reverted. */
|
||||
int stepValue = 0;
|
||||
};
|
||||
|
||||
/*! Run the pending migrations. */
|
||||
std::vector<std::shared_ptr<Migration>> run(MigrateOptions options) const;
|
||||
/*! Rollback the last migration operation. */
|
||||
std::vector<RollbackItem> rollback(MigrateOptions options) const;
|
||||
/*! Rolls all of the currently applied migrations back. */
|
||||
std::vector<RollbackItem> reset(bool pretend = false) const;
|
||||
|
||||
/* Database connection related */
|
||||
/*! Execute the given callback using the given connection as the default
|
||||
connection. */
|
||||
int usingConnection(QString &&name, bool debugSql,
|
||||
std::function<int()> &&callback);
|
||||
|
||||
/* Proxies to MigrationRepository */
|
||||
/*! Determine if the migration repository exists. */
|
||||
bool repositoryExists() const;
|
||||
/*! Determine if any migrations have been run. */
|
||||
bool hasRunAnyMigrations() const;
|
||||
|
||||
/* Getters / Setters */
|
||||
/*! Get the default connection name. */
|
||||
inline const QString &getConnection() const noexcept;
|
||||
/*! Set the default connection name. */
|
||||
void setConnection(QString &&name, std::optional<bool> &&debugSql);
|
||||
/*! Get the migration repository instance. */
|
||||
inline MigrationRepository &repository() const noexcept;
|
||||
/*! Get migration names list. */
|
||||
inline const std::set<QString> &migrationNames() const noexcept;
|
||||
|
||||
protected:
|
||||
/* Database connection related */
|
||||
/*! Resolve the database connection instance. */
|
||||
DatabaseConnection &resolveConnection(const QString &name = "") const;
|
||||
/*! Get the debug sql by the connection name. */
|
||||
std::optional<bool> getConnectionDebugSql(const QString &name) const;
|
||||
|
||||
/* Migration instances lists and hashes */
|
||||
/*! Create a map that maps migration names by migrations type-id (type_index). */
|
||||
void createMigrationNamesMap();
|
||||
/*! Get a migration name by a migration type-id. */
|
||||
QString getMigrationName(const Migration &migration) const;
|
||||
|
||||
/* Migrate */
|
||||
/*! Get the migration instances that have not yet run. */
|
||||
std::vector<std::shared_ptr<Migration>>
|
||||
pendingMigrations(const QVector<QVariant> &ran) const;
|
||||
/*! Run "up" a migration instance. */
|
||||
void runUp(const Migration &migration, int batch, bool pretend) const;
|
||||
|
||||
/* Rollback */
|
||||
/*! Get the migrations for a rollback operation (used by rollback). */
|
||||
std::vector<RollbackItem>
|
||||
getMigrationsForRollback(MigrateOptions options) const;
|
||||
/*! Get the migrations for a rollback operation (used by reset). */
|
||||
std::vector<RollbackItem>
|
||||
getMigrationsForRollback(std::vector<MigrationItem> &&ran) const;
|
||||
|
||||
/*! Rollback the given migrations. */
|
||||
std::vector<RollbackItem>
|
||||
rollbackMigrations(std::vector<RollbackItem> &&migrations, bool pretend) const;
|
||||
/*! Run "down" a migration instance. */
|
||||
void runDown(const RollbackItem &migrationToRollback, bool pretend) const;
|
||||
|
||||
/* Pretend */
|
||||
/*! Migrate type (up/down). */
|
||||
enum struct MigrateMethod { Up, Down };
|
||||
|
||||
/*! Pretend to run the migrations. */
|
||||
void pretendToRun(const Migration &migration, MigrateMethod method) const;
|
||||
/*! Get all of the queries that would be run for a migration. */
|
||||
QVector<Log> getQueries(const Migration &migration, MigrateMethod method) const;
|
||||
|
||||
/* Migrate up/down common */
|
||||
/*! Run a migration inside a transaction if the database supports it. */
|
||||
void runMigration(const Migration &migration, MigrateMethod method) const;
|
||||
/*! Migrate by the given method (up/down). */
|
||||
void migrateByMethod(const Migration &migration, MigrateMethod method) const;
|
||||
|
||||
/*! Throw if migrations passed to the TomApplication are not sorted
|
||||
alphabetically. */
|
||||
void throwIfMigrationsNotSorted(const QString &previousMigrationName,
|
||||
const QString &migrationName) const;
|
||||
|
||||
/*! The migration repository instance. */
|
||||
std::shared_ptr<MigrationRepository> m_repository;
|
||||
/*! The database connection resolver instance. */
|
||||
std::shared_ptr<ConnectionResolverInterface> m_resolver;
|
||||
/*! The name of the database connection to use. */
|
||||
QString m_connection;
|
||||
|
||||
/*! Reference to the migrations vector to process. */
|
||||
const std::vector<std::shared_ptr<Migration>> &m_migrations;
|
||||
/*! Map a migration names by migrations type-id (type_index)
|
||||
(used migrate, rollback, pretend). */
|
||||
std::unordered_map<std::type_index, QString> m_migrationNamesMap {};
|
||||
/*! Migration names list (used by status). */
|
||||
std::set<QString> m_migrationNames {};
|
||||
/*! Map a migration instances by migration names (used by reset). */
|
||||
std::unordered_map<QString,
|
||||
std::shared_ptr<Migration>> m_migrationInstancesMap {};
|
||||
};
|
||||
|
||||
/* public */
|
||||
|
||||
const QString &Migrator::getConnection() const noexcept
|
||||
{
|
||||
return m_connection;
|
||||
}
|
||||
|
||||
MigrationRepository &Migrator::repository() const noexcept
|
||||
{
|
||||
return *m_repository;
|
||||
}
|
||||
|
||||
const std::set<QString> &Migrator::migrationNames() const noexcept
|
||||
{
|
||||
return m_migrationNames;
|
||||
}
|
||||
|
||||
} // namespace Tom
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_MIGRATOR_HPP
|
||||
|
||||
129
tom/include/tom/terminal.hpp
Normal file
129
tom/include/tom/terminal.hpp
Normal file
@@ -0,0 +1,129 @@
|
||||
#pragma once
|
||||
#ifndef TOM_TERMINAL_HPP
|
||||
#define TOM_TERMINAL_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <orm/macros/commonnamespace.hpp>
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom
|
||||
{
|
||||
/*! Concept for the ostream and wostream. */
|
||||
template<typename T>
|
||||
concept OStreamConcept = std::convertible_to<T, const std::ostream &> ||
|
||||
std::convertible_to<T, const std::wostream &>;
|
||||
|
||||
/*! Describes current terminal features. */
|
||||
class Terminal
|
||||
{
|
||||
Q_DISABLE_COPY(Terminal)
|
||||
|
||||
public:
|
||||
/*! Default constructor. */
|
||||
inline Terminal() = default;
|
||||
/*! Default destructor. */
|
||||
inline ~Terminal() = default;
|
||||
|
||||
/*! Supports the given output ansi colors? (ansi is disabled for non-tty). */
|
||||
bool hasColorSupport(std::ostream &cout = std::cout) const;
|
||||
/*! Supports the given output ansi colors? (ansi is disabled for non-tty),
|
||||
wide version. */
|
||||
bool hasWColorSupport(std::wostream &wcout = std::wcout) const;
|
||||
|
||||
/*! Determines whether a file descriptor is associated with a character device. */
|
||||
bool isatty(FILE *stream) const;
|
||||
|
||||
/*! Obtain the current terminal width. */
|
||||
int width();
|
||||
/*! Obtain the current terminal height. */
|
||||
int height();
|
||||
|
||||
/*! Get the cached terminal width. */
|
||||
inline int lastWidth() const noexcept;
|
||||
/*! Get the cached terminal height. */
|
||||
inline int lastHeight() const noexcept;
|
||||
|
||||
/*! Terminal width and height. */
|
||||
struct TerminalSize
|
||||
{
|
||||
/*! Visible columns. */
|
||||
int columns;
|
||||
/*! Visible lines. */
|
||||
int lines;
|
||||
};
|
||||
|
||||
/*! Get terminal size of the visible area. */
|
||||
TerminalSize terminalSize() const;
|
||||
|
||||
private:
|
||||
/*! Supports the given output ansi colors? (common logic). */
|
||||
template<OStreamConcept O>
|
||||
bool hasColorSupportInternal(O &&cout, FILE *stream) const;
|
||||
|
||||
#ifdef _WIN32
|
||||
/*! Detect if c++ ostream has enabled virtual terminal processing. */
|
||||
bool hasVt100Support(std::ostream &cout) const;
|
||||
/*! Detect if c++ wostream has enabled virtual terminal processing,
|
||||
wide version. */
|
||||
bool hasVt100Support(std::wostream &wcout) const;
|
||||
#endif
|
||||
|
||||
/*! Cache for detected ansi output. */
|
||||
mutable std::unordered_map<std::ostream *, bool> m_isAnsiOutput;
|
||||
/*! Cache for detected ansi output, wide version. */
|
||||
mutable std::unordered_map<std::wostream *, bool> m_isAnsiWOutput;
|
||||
|
||||
/*! Current terminal width. */
|
||||
int m_lastWidth = 80;
|
||||
/*! Current terminal height. */
|
||||
int m_lastHeight = 50;
|
||||
};
|
||||
|
||||
/* public */
|
||||
|
||||
int Terminal::lastWidth() const noexcept
|
||||
{
|
||||
return m_lastWidth;
|
||||
}
|
||||
|
||||
int Terminal::lastHeight() const noexcept
|
||||
{
|
||||
return m_lastHeight;
|
||||
}
|
||||
|
||||
/* private */
|
||||
|
||||
template<OStreamConcept O>
|
||||
bool Terminal::hasColorSupportInternal(O &&cout, FILE *stream) const
|
||||
{
|
||||
// Follow https://no-color.org/
|
||||
if (qEnvironmentVariableIsSet("NO_COLOR"))
|
||||
return false;
|
||||
|
||||
if (qEnvironmentVariable("TERM_PROGRAM") == QLatin1String("Hyper"))
|
||||
return isatty(stream);
|
||||
|
||||
#ifdef _WIN32
|
||||
return isatty(stream) &&
|
||||
(hasVt100Support(std::forward<O>(cout)) ||
|
||||
qEnvironmentVariableIsSet("ANSICON") ||
|
||||
qEnvironmentVariable("ConEmuANSI") == QLatin1String("ON") ||
|
||||
qEnvironmentVariable("TERM") == QLatin1String("xterm"));
|
||||
#endif
|
||||
|
||||
// Detect character device, in most cases false when the output is redirected
|
||||
return isatty(stream);
|
||||
}
|
||||
|
||||
} // namespace Tom
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_TERMINAL_HPP
|
||||
46
tom/include/tom/tomtypes.hpp
Normal file
46
tom/include/tom/tomtypes.hpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
#ifndef TOM_TOMTYPES_HPP
|
||||
#define TOM_TOMTYPES_HPP
|
||||
|
||||
#include <orm/macros/systemheader.hpp>
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <orm/macros/commonnamespace.hpp>
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom
|
||||
{
|
||||
|
||||
/*! Hydrated migration item from the database. */
|
||||
struct MigrationItem
|
||||
{
|
||||
/*! Database ID. */
|
||||
quint64 id;
|
||||
/*! Migration name. */
|
||||
QString migration;
|
||||
int batch;
|
||||
};
|
||||
|
||||
class Migration;
|
||||
|
||||
/*! Migration item used and returned from the rollback. */
|
||||
struct RollbackItem
|
||||
{
|
||||
/*! Database ID. */
|
||||
quint64 id;
|
||||
/*! Migration name. */
|
||||
QString migrationName;
|
||||
/*! Migration instance. */
|
||||
std::shared_ptr<Migration> migration;
|
||||
};
|
||||
|
||||
} // namespace Tom
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
#endif // TOM_TOMTYPES_HPP
|
||||
@@ -1,6 +1,6 @@
|
||||
//#pragma code_page(65001) // UTF-8
|
||||
|
||||
//IDI_ICON1 ICON "icons/@tom_target@.ico"
|
||||
//IDI_ICON1 ICON "icons/@TomExample_target@.ico"
|
||||
|
||||
#include <windows.h>
|
||||
#include "tom/version.hpp"
|
||||
@@ -11,7 +11,7 @@
|
||||
#define VER_PRODUCTVERSION TINYTOM_VERSION_MAJOR,TINYTOM_VERSION_MINOR,TINYTOM_VERSION_BUGFIX,TINYTOM_VERSION_BUILD
|
||||
#define VER_PRODUCTVERSION_STR TINYTOM_VERSION_STR "\0"
|
||||
|
||||
#define VER_ORIGINALFILENAME_STR "$<TARGET_FILE_NAME:@tom_target@>\0"
|
||||
#define VER_ORIGINALFILENAME_STR "$<TARGET_FILE_NAME:@TomExample_target@>\0"
|
||||
|
||||
#ifdef TINYTOM_NO_DEBUG
|
||||
# define VER_DEBUG 0
|
||||
@@ -33,7 +33,7 @@ VS_VERSION_INFO VERSIONINFO
|
||||
BLOCK "040904B0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Crystal Studio\0"
|
||||
VALUE "FileDescription", "tom console for TinyORM\0"
|
||||
VALUE "FileDescription", "Tom console for TinyORM\0"
|
||||
VALUE "FileVersion", VER_FILEVERSION_STR
|
||||
VALUE "InternalName", "tom console\0"
|
||||
VALUE "LegalCopyright", "Copyright (©) 2022 Crystal Studio\0"
|
||||
@@ -51,5 +51,5 @@ VS_VERSION_INFO VERSIONINFO
|
||||
/* End of Version info */
|
||||
|
||||
#ifdef __MINGW32__
|
||||
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "@tom_target@$<TARGET_FILE_SUFFIX:@tom_target@>.manifest"
|
||||
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "@TomExample_target@$<TARGET_FILE_SUFFIX:@TomExample_target@>.manifest"
|
||||
#endif
|
||||
|
||||
@@ -1,2 +1,27 @@
|
||||
SOURCES += \
|
||||
$$PWD/tom/main.cpp \
|
||||
$$PWD/tom/application.cpp \
|
||||
$$PWD/tom/commands/command.cpp \
|
||||
$$PWD/tom/commands/database/wipecommand.cpp \
|
||||
$$PWD/tom/commands/environmentcommand.cpp \
|
||||
$$PWD/tom/commands/helpcommand.cpp \
|
||||
$$PWD/tom/commands/inspirecommand.cpp \
|
||||
$$PWD/tom/commands/listcommand.cpp \
|
||||
$$PWD/tom/commands/make/migrationcommand.cpp \
|
||||
# $$PWD/tom/commands/make/projectcommand.cpp \
|
||||
$$PWD/tom/commands/migrations/freshcommand.cpp \
|
||||
$$PWD/tom/commands/migrations/installcommand.cpp \
|
||||
$$PWD/tom/commands/migrations/migratecommand.cpp \
|
||||
$$PWD/tom/commands/migrations/refreshcommand.cpp \
|
||||
$$PWD/tom/commands/migrations/resetcommand.cpp \
|
||||
$$PWD/tom/commands/migrations/rollbackcommand.cpp \
|
||||
$$PWD/tom/commands/migrations/statuscommand.cpp \
|
||||
$$PWD/tom/concerns/callscommands.cpp \
|
||||
$$PWD/tom/concerns/confirmable.cpp \
|
||||
$$PWD/tom/concerns/interactswithio.cpp \
|
||||
$$PWD/tom/concerns/printsoptions.cpp \
|
||||
$$PWD/tom/exceptions/tomlogicerror.cpp \
|
||||
$$PWD/tom/exceptions/tomruntimeerror.cpp \
|
||||
$$PWD/tom/migrationcreator.cpp \
|
||||
$$PWD/tom/migrationrepository.cpp \
|
||||
$$PWD/tom/migrator.cpp \
|
||||
$$PWD/tom/terminal.cpp \
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
QT *= core sql
|
||||
QT -= gui
|
||||
|
||||
TEMPLATE = app
|
||||
TARGET = tom
|
||||
|
||||
# TinyTom application specific configuration
|
||||
# ---
|
||||
|
||||
CONFIG *= console
|
||||
|
||||
# Common Configuration
|
||||
# ---
|
||||
|
||||
include($$TINYORM_SOURCE_TREE/qmake/common.pri)
|
||||
|
||||
# TinyTom defines
|
||||
# ---
|
||||
|
||||
DEFINES += PROJECT_TINYTOM
|
||||
|
||||
# Release build
|
||||
CONFIG(release, debug|release): DEFINES += TINYTOM_NO_DEBUG
|
||||
# Debug build
|
||||
CONFIG(debug, debug|release): DEFINES *= TINYTOM_DEBUG
|
||||
|
||||
# Enable code needed by tests
|
||||
#build_tests: \
|
||||
# DEFINES *= TINYTOM_TESTS_CODE
|
||||
|
||||
# TinyTom library header and source files
|
||||
# ---
|
||||
|
||||
# tiny_version_numbers() depends on HEADERS (version.hpp)
|
||||
include(../include/include.pri)
|
||||
include(src.pri)
|
||||
|
||||
# File version
|
||||
# ---
|
||||
|
||||
# Find version numbers in the version header file and assign them to the
|
||||
# <TARGET>_VERSION_<MAJOR,MINOR,PATCH,TWEAK> and also to the VERSION variable.
|
||||
load(tiny_version_numbers)
|
||||
tiny_version_numbers()
|
||||
|
||||
# Windows resource and manifest files
|
||||
# ---
|
||||
|
||||
# Find version.hpp
|
||||
tinyRcIncludepath = $$quote($$PWD/../include/)
|
||||
# Find Windows manifest
|
||||
mingw: tinyRcIncludepath += $$quote($$PWD/../resources/)
|
||||
|
||||
load(tiny_resource_and_manifest)
|
||||
tiny_resource_and_manifest($$tinyRcIncludepath, ../resources)
|
||||
|
||||
# Use Precompiled headers (PCH)
|
||||
# ---
|
||||
|
||||
include(../include/pch.pri)
|
||||
|
||||
# Deployment
|
||||
# ---
|
||||
|
||||
win32-msvc:CONFIG(debug, debug|release) {
|
||||
win32-msvc: target.path = C:/optx64/$${TARGET}
|
||||
# else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||
!isEmpty(target.path): INSTALLS += target
|
||||
}
|
||||
|
||||
# Configure TinyORM library
|
||||
# ---
|
||||
|
||||
include($$TINYORM_SOURCE_TREE/qmake/TinyOrm.pri)
|
||||
|
||||
# User Configuration
|
||||
# ---
|
||||
|
||||
exists(../conf.pri): \
|
||||
include(../conf.pri)
|
||||
|
||||
#else:is_vcpkg_build: \
|
||||
# include(../qmake/vcpkgconf.pri)
|
||||
|
||||
else: \
|
||||
error( "'conf.pri' for 'tom' project does not exist. See an example configuration\
|
||||
in 'tom/conf.pri.example' or call 'vcpkg install' in the project's root." )
|
||||
|
||||
# Link against TinyORM library
|
||||
# ---
|
||||
|
||||
INCLUDEPATH += $$quote($$TINYORM_SOURCE_TREE/include/)
|
||||
|
||||
LIBS += $$quote(-L$$TINYORM_BUILD_TREE/src$${TINY_RELEASE_TYPE}/)
|
||||
LIBS += -lTinyOrm
|
||||
474
tom/src/tom/application.cpp
Normal file
474
tom/src/tom/application.cpp
Normal file
@@ -0,0 +1,474 @@
|
||||
#include "tom/application.hpp"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <qt_windows.h>
|
||||
|
||||
# include <fcntl.h>
|
||||
# include <io.h>
|
||||
#endif
|
||||
|
||||
#include <orm/databasemanager.hpp>
|
||||
#include <orm/utils/type.hpp>
|
||||
#include <orm/version.hpp>
|
||||
|
||||
#include "tom/commands/database/wipecommand.hpp"
|
||||
#include "tom/commands/environmentcommand.hpp"
|
||||
#include "tom/commands/helpcommand.hpp"
|
||||
#include "tom/commands/inspirecommand.hpp"
|
||||
#include "tom/commands/listcommand.hpp"
|
||||
#include "tom/commands/make/migrationcommand.hpp"
|
||||
//#include "tom/commands/make/projectcommand.hpp"
|
||||
#include "tom/commands/migrations/freshcommand.hpp"
|
||||
#include "tom/commands/migrations/installcommand.hpp"
|
||||
#include "tom/commands/migrations/migratecommand.hpp"
|
||||
#include "tom/commands/migrations/refreshcommand.hpp"
|
||||
#include "tom/commands/migrations/resetcommand.hpp"
|
||||
#include "tom/commands/migrations/rollbackcommand.hpp"
|
||||
#include "tom/commands/migrations/statuscommand.hpp"
|
||||
#include "tom/migrationrepository.hpp"
|
||||
#include "tom/migrator.hpp"
|
||||
#ifndef TINYTOM_TESTS_CODE
|
||||
# include "tom/version.hpp"
|
||||
#endif
|
||||
|
||||
using Orm::ConnectionResolverInterface;
|
||||
using Orm::Constants::NEWLINE;
|
||||
|
||||
using TypeUtils = Orm::Utils::Type;
|
||||
|
||||
using Tom::Commands::Command;
|
||||
using Tom::Commands::Database::WipeCommand;
|
||||
using Tom::Commands::EnvironmentCommand;
|
||||
using Tom::Commands::HelpCommand;
|
||||
using Tom::Commands::InspireCommand;
|
||||
using Tom::Commands::ListCommand;
|
||||
using Tom::Commands::Make::MigrationCommand;
|
||||
//using Tom::Commands::Make::ProjectCommand;
|
||||
using Tom::Commands::Migrations::FreshCommand;
|
||||
using Tom::Commands::Migrations::InstallCommand;
|
||||
using Tom::Commands::Migrations::MigrateCommand;
|
||||
using Tom::Commands::Migrations::RefreshCommand;
|
||||
using Tom::Commands::Migrations::ResetCommand;
|
||||
using Tom::Commands::Migrations::RollbackCommand;
|
||||
using Tom::Commands::Migrations::StatusCommand;
|
||||
|
||||
/*! Invoke Qt's global post routines. */
|
||||
extern void Q_DECL_IMPORT qt_call_post_routines();
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom {
|
||||
|
||||
/* public */
|
||||
|
||||
Application::Application(int &argc, char **argv, std::shared_ptr<DatabaseManager> db,
|
||||
const char *const environmentEnvName, QString migrationTable,
|
||||
std::vector<std::shared_ptr<Migration>> migrations)
|
||||
: m_argc(argc)
|
||||
, m_argv(argv)
|
||||
, m_db(std::move(db))
|
||||
#ifndef TINYTOM_TESTS_CODE
|
||||
, m_qtApplication(argc, argv)
|
||||
#endif
|
||||
, m_environmentEnvName(environmentEnvName)
|
||||
, m_migrationTable(std::move(migrationTable))
|
||||
, m_migrations(std::move(migrations))
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// Prepare console input/output character encoding
|
||||
initializeConsoleEncoding();
|
||||
#endif
|
||||
|
||||
// Following is not relevant in the auto test executables
|
||||
#ifndef TINYTOM_TESTS_CODE
|
||||
QCoreApplication::setOrganizationName("TinyORM");
|
||||
QCoreApplication::setOrganizationDomain("tinyorm.org");
|
||||
QCoreApplication::setApplicationName("tom");
|
||||
QCoreApplication::setApplicationVersion(TINYTOM_VERSION_STR);
|
||||
#endif
|
||||
|
||||
// Print a newline at application's normal exit
|
||||
// initializeAtExit();
|
||||
|
||||
// Fix m_argc/m_argv data members if the argv is empty
|
||||
fixEmptyArgv();
|
||||
|
||||
// Initialize the command-line parser
|
||||
initializeParser(m_parser);
|
||||
}
|
||||
|
||||
int Application::run()
|
||||
{
|
||||
// Process the actual command-line arguments given by the user
|
||||
parseCommandLine();
|
||||
|
||||
// Ownership of a unique_ptr()
|
||||
return createCommand(getCommandName())->run();
|
||||
}
|
||||
|
||||
void Application::logException(const std::exception &e, const bool noAnsi)
|
||||
{
|
||||
// TODO future decide how qCritical()/qFatal() really works, also regarding to the Qt Creator's settings 'Ignore first chance access violations' and similar silverqx
|
||||
// TODO future alse how to correctly setup this in prod/dev envs. silverqx
|
||||
auto message = QStringLiteral("Caught '%1' Exception:\n%2")
|
||||
.arg(TypeUtils::classPureBasename(e, true), e.what());
|
||||
|
||||
/* Want to have this method static, downside is that the InteractsWithIO has to be
|
||||
instantiated again. */
|
||||
Concerns::InteractsWithIO io(noAnsi);
|
||||
|
||||
static const auto tmpl = QStringLiteral("%1%2%1").arg(NEWLINE, "%1");
|
||||
|
||||
// No-ansi output
|
||||
if (noAnsi || !io.isAnsiOutput(std::cerr)) {
|
||||
qCritical().nospace().noquote() << tmpl.arg(std::move(message));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Print error wall (red box with a white text) */
|
||||
qCritical().nospace().noquote() << tmpl.arg(io.errorWallInternal(message));
|
||||
}
|
||||
|
||||
QStringList Application::arguments() const
|
||||
{
|
||||
// Never obtain arguments from the QCoreApplication instance in unit tests
|
||||
return hasQtApplication ? QCoreApplication::arguments()
|
||||
: prepareArguments();
|
||||
}
|
||||
|
||||
#ifdef TINYTOM_TESTS_CODE
|
||||
std::vector<Application::StatusRow> Application::status() noexcept
|
||||
{
|
||||
return StatusCommand::status();
|
||||
}
|
||||
|
||||
void Application::enableInUnitTests() noexcept
|
||||
{
|
||||
StatusCommand::setInUnitTests();
|
||||
}
|
||||
#endif
|
||||
|
||||
/* protected */
|
||||
|
||||
/* Application initialization */
|
||||
|
||||
#ifdef _WIN32
|
||||
void Application::initializeConsoleEncoding() const
|
||||
{
|
||||
// Set it here so the user doesn't have to deal with this
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
|
||||
/* UTF-8 encoding is corrupted for narrow input functions, needed to use wcin/wstring
|
||||
for an input, input will be in the unicode encoding then needed to translate
|
||||
unicode to utf8, eg. by QString::fromStdWString(), WideCharToMultiByte(), or
|
||||
std::codecvt(). It also works with msys2 ucrt64 gcc/clang. */
|
||||
SetConsoleCP(CP_UTF8);
|
||||
_setmode(_fileno(stdin), _O_WTEXT);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Application::fixEmptyArgv()
|
||||
{
|
||||
constexpr const auto *const empty = "";
|
||||
|
||||
if (m_argc == 0 || m_argv == nullptr) {
|
||||
m_argc = 0;
|
||||
m_argv = const_cast<char **>(&empty);
|
||||
}
|
||||
}
|
||||
|
||||
// CUR tom, remove? silverqx
|
||||
void Application::initializeAtExit() const
|
||||
{
|
||||
std::atexit([]
|
||||
{
|
||||
std::cout << std::endl;
|
||||
});
|
||||
}
|
||||
|
||||
void Application::initializeParser(QCommandLineParser &parser)
|
||||
{
|
||||
parser.setApplicationDescription(
|
||||
QStringLiteral("TinyORM %1").arg(TINYORM_VERSION_STR));
|
||||
|
||||
// Common options used in all commands
|
||||
parser.addOptions(saveOptions({
|
||||
{ "ansi", "Force ANSI output"},
|
||||
{ "no-ansi", "Disable ANSI output"},
|
||||
{ "env", "The environment the command should run under",
|
||||
"env"}, // Value
|
||||
{{"h", "help"}, "Display help for the given command. When no command "
|
||||
"is given display help for the <info>list</info> "
|
||||
"command"},
|
||||
{{"n", "no-interaction"}, "Do not ask any interactive question"},
|
||||
{{"q", "quiet"}, "Do not output any message"},
|
||||
{{"V", "version"}, "Display this application version"},
|
||||
{{"v", "verbose"}, "Increase the verbosity of messages: 1 for normal "
|
||||
"output, 2 for more verbose output and 3 for debug"},
|
||||
}));
|
||||
}
|
||||
|
||||
const QList<QCommandLineOption> &
|
||||
Application::saveOptions(QList<QCommandLineOption> &&options)
|
||||
{
|
||||
return m_options = std::move(options);
|
||||
}
|
||||
|
||||
QList<QCommandLineOption>
|
||||
Application::prependOptions(QList<QCommandLineOption> &&options)
|
||||
{
|
||||
auto commonOptions = m_options;
|
||||
|
||||
m_options = options;
|
||||
|
||||
m_options << std::move(commonOptions);
|
||||
|
||||
// Return only a new options because they are passed to the addOptions() method
|
||||
return std::move(options);
|
||||
}
|
||||
|
||||
/* Run command */
|
||||
|
||||
void Application::parseCommandLine()
|
||||
{
|
||||
/* Can not use QCommandLineParser::ParseAsPositionalArguments because I still need
|
||||
to check eg. --version and --no-interaction below, it doesn't matter as everything
|
||||
works just fine. */
|
||||
|
||||
/* Arguments have to be prepared manually in unit tests because is not possible
|
||||
to use the QCoreApplication::arguments() method. Also no error handling needed
|
||||
here as it is only some kind of pre-parse, whole command is parsed inside
|
||||
the command itself. */
|
||||
m_parser.parse(arguments());
|
||||
|
||||
initializeEnvironment();
|
||||
|
||||
/* Command line arguments are parsed now, 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);
|
||||
|
||||
if (m_parser.isSet("no-interaction"))
|
||||
m_interactive = false;
|
||||
|
||||
if (m_parser.isSet(QStringLiteral("version")))
|
||||
showVersion();
|
||||
}
|
||||
|
||||
void Application::initializeEnvironment()
|
||||
{
|
||||
/*! Order is as follow, the default value is development, can be overriden by
|
||||
a env. variable which name is in the m_environmentEnvName data member, highest
|
||||
priority has --env command-line argument. */
|
||||
if (auto environmentCmd = m_parser.value("env");
|
||||
!environmentCmd.isEmpty()
|
||||
)
|
||||
m_environment = std::move(environmentCmd);
|
||||
|
||||
else if (auto environmentEnv = QString::fromUtf8(m_environmentEnvName).isEmpty()
|
||||
? ""
|
||||
: qEnvironmentVariable(m_environmentEnvName);
|
||||
!environmentEnv.isEmpty()
|
||||
)
|
||||
m_environment = std::move(environmentEnv);
|
||||
}
|
||||
|
||||
QString Application::getCommandName()
|
||||
{
|
||||
const auto arguments = m_parser.positionalArguments();
|
||||
|
||||
if (arguments.isEmpty())
|
||||
return {};
|
||||
|
||||
return arguments.at(0);
|
||||
}
|
||||
|
||||
/* Early exit during parse command-line */
|
||||
|
||||
Q_NORETURN void Application::showVersion() const
|
||||
{
|
||||
printVersion();
|
||||
|
||||
exitApplication(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
/* Has to be divided into two methods because the printVersion() is called from
|
||||
the list command. */
|
||||
void Application::printVersion() const
|
||||
{
|
||||
note(QStringLiteral("TinyORM "), false);
|
||||
|
||||
info(TINYORM_VERSION_STR);
|
||||
}
|
||||
|
||||
Q_NORETURN void Application::showCommandsList(const int exitCode)
|
||||
{
|
||||
ListCommand(*this, m_parser).run();
|
||||
|
||||
exitApplication(exitCode);
|
||||
}
|
||||
|
||||
Q_NORETURN void Application::exitApplication(const int exitCode) const
|
||||
{
|
||||
/* Invoke post routines manually, it's safe as they will not be called twice even if
|
||||
the QCoreApplication's desctructor calls also this function. */
|
||||
qt_call_post_routines();
|
||||
|
||||
::exit(exitCode); // NOLINT(concurrency-mt-unsafe)
|
||||
}
|
||||
|
||||
/* Commands factory */
|
||||
|
||||
std::unique_ptr<Command>
|
||||
Application::createCommand(const QString &command, const OptionalParserRef parser,
|
||||
const bool showHelp)
|
||||
{
|
||||
// Use a custom parser if passed as the argument, needed by CallsCommands::call()
|
||||
auto parserRef = parser ? *parser : std::ref(m_parser);
|
||||
|
||||
if (command == QStringLiteral("db:wipe"))
|
||||
return std::make_unique<WipeCommand>(*this, parserRef);
|
||||
|
||||
if (command == QStringLiteral("env"))
|
||||
return std::make_unique<EnvironmentCommand>(*this, parserRef);
|
||||
|
||||
if (command == QStringLiteral("help"))
|
||||
return std::make_unique<HelpCommand>(*this, parserRef);
|
||||
|
||||
if (command == QStringLiteral("inspire"))
|
||||
return std::make_unique<InspireCommand>(*this, parserRef);
|
||||
|
||||
if (command == QStringLiteral("list"))
|
||||
return std::make_unique<ListCommand>(*this, parserRef);
|
||||
|
||||
if (command == QStringLiteral("make:migration"))
|
||||
return std::make_unique<MigrationCommand>(*this, parserRef);
|
||||
|
||||
// if (command == QStringLiteral("make:project"))
|
||||
// return std::make_unique<ProjectCommand>(*this, parserRef);
|
||||
|
||||
if (command == QStringLiteral("migrate"))
|
||||
return std::make_unique<MigrateCommand>(*this, parserRef, createMigrator());
|
||||
|
||||
if (command == QStringLiteral("migrate:fresh"))
|
||||
return std::make_unique<FreshCommand>(*this, parserRef, createMigrator());
|
||||
|
||||
if (command == QStringLiteral("migrate:install"))
|
||||
return std::make_unique<InstallCommand>(*this, parserRef,
|
||||
createMigrationRepository());
|
||||
|
||||
if (command == QStringLiteral("migrate:rollback"))
|
||||
return std::make_unique<RollbackCommand>(*this, parserRef, createMigrator());
|
||||
|
||||
if (command == QStringLiteral("migrate:refresh"))
|
||||
return std::make_unique<RefreshCommand>(*this, parserRef, createMigrator());
|
||||
|
||||
if (command == QStringLiteral("migrate:reset"))
|
||||
return std::make_unique<ResetCommand>(*this, parserRef, createMigrator());
|
||||
|
||||
if (command == QStringLiteral("migrate:status"))
|
||||
return std::make_unique<StatusCommand>(*this, parserRef, createMigrator());
|
||||
|
||||
// Used by the help command
|
||||
if (!showHelp)
|
||||
return nullptr;
|
||||
|
||||
// If passed non-existent command then show all commands list
|
||||
this->showCommandsList(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
std::shared_ptr<MigrationRepository> Application::createMigrationRepository()
|
||||
{
|
||||
if (m_repository)
|
||||
return m_repository;
|
||||
|
||||
return m_repository =
|
||||
std::make_shared<MigrationRepository>(
|
||||
std::dynamic_pointer_cast<ConnectionResolverInterface>(m_db),
|
||||
m_migrationTable);
|
||||
}
|
||||
|
||||
std::shared_ptr<Migrator> Application::createMigrator()
|
||||
{
|
||||
if (m_migrator)
|
||||
return m_migrator;
|
||||
|
||||
return m_migrator =
|
||||
std::make_shared<Migrator>(
|
||||
createMigrationRepository(),
|
||||
std::dynamic_pointer_cast<ConnectionResolverInterface>(m_db),
|
||||
m_migrations, m_parser);
|
||||
}
|
||||
|
||||
/* Others */
|
||||
|
||||
const std::vector<std::shared_ptr<Application::Command>> &
|
||||
Application::createCommandsVector()
|
||||
{
|
||||
static const std::vector<std::shared_ptr<Command>> cached = [this]
|
||||
{
|
||||
return commandNames()
|
||||
| ranges::views::transform([this](const char *const commandName)
|
||||
-> std::shared_ptr<Command>
|
||||
{
|
||||
return createCommand(commandName);
|
||||
})
|
||||
| ranges::to<std::vector<std::shared_ptr<Command>>>();
|
||||
}();
|
||||
|
||||
return cached;
|
||||
}
|
||||
|
||||
const std::vector<const char *> &Application::commandNames() const
|
||||
{
|
||||
// Order is important here
|
||||
static const std::vector<const char *> cached {
|
||||
// global namespace
|
||||
"env", "help", "inspire", "list", "migrate",
|
||||
// db
|
||||
"db:wipe",
|
||||
// make
|
||||
"make:migration", "make:project",
|
||||
// migrate
|
||||
"migrate:fresh", "migrate:install", "migrate:refresh", "migrate:reset",
|
||||
"migrate:rollback", "migrate:status",
|
||||
};
|
||||
|
||||
return cached;
|
||||
}
|
||||
|
||||
QStringList Application::prepareArguments() const
|
||||
{
|
||||
QStringList arguments;
|
||||
arguments.reserve(m_argc);
|
||||
|
||||
for (QStringList::size_type i = 0; i < m_argc; ++i)
|
||||
arguments << QString::fromUtf8(m_argv[i]);
|
||||
|
||||
return arguments;
|
||||
}
|
||||
|
||||
/* Auto tests helpers */
|
||||
|
||||
#ifdef TINYTOM_TESTS_CODE
|
||||
/* public */
|
||||
|
||||
int Application::runWithArguments(QStringList &&arguments)
|
||||
{
|
||||
// Process the actual command-line arguments given by the user
|
||||
parseCommandLine();
|
||||
|
||||
// Ownership of a unique_ptr()
|
||||
return createCommand(getCommandName())->runWithArguments(std::move(arguments));
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace Tom
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
// CUR tom, commands I want to implement; completion, test, db:seed, schema:dump silverqx
|
||||
// CUR tom, implement support for short command names, eg mig:st silverqx
|
||||
198
tom/src/tom/commands/command.cpp
Normal file
198
tom/src/tom/commands/command.cpp
Normal file
@@ -0,0 +1,198 @@
|
||||
#include "tom/commands/command.hpp"
|
||||
|
||||
#include <QCommandLineParser>
|
||||
|
||||
#include <range/v3/range/conversion.hpp>
|
||||
#include <range/v3/view/iota.hpp>
|
||||
#include <range/v3/view/zip_with.hpp>
|
||||
|
||||
#include <orm/databasemanager.hpp>
|
||||
|
||||
#include "tom/application.hpp"
|
||||
#include "tom/version.hpp"
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom::Commands
|
||||
{
|
||||
|
||||
/* public */
|
||||
|
||||
Command::Command(Application &application, QCommandLineParser &parser)
|
||||
: Concerns::InteractsWithIO(parser)
|
||||
, m_application(application)
|
||||
, m_parser(parser)
|
||||
{}
|
||||
|
||||
QList<QCommandLineOption> Command::optionsSignature() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
int Command::run()
|
||||
{
|
||||
initializePositionalArguments();
|
||||
|
||||
auto &parser = this->parser();
|
||||
|
||||
parser.clearPositionalArguments();
|
||||
|
||||
parser.addOptions(optionsSignature());
|
||||
|
||||
if (!parser.parse(passedArguments()))
|
||||
showParserError(parser);
|
||||
|
||||
// Show help if --help argument was passed
|
||||
checkHelpArgument();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int Command::runWithArguments(QStringList &&arguments)
|
||||
{
|
||||
m_arguments = std::move(arguments);
|
||||
|
||||
return run();
|
||||
}
|
||||
|
||||
/* Getters */
|
||||
|
||||
bool Command::hasPositionalArguments() const
|
||||
{
|
||||
return !positionalArguments().empty();
|
||||
}
|
||||
|
||||
bool Command::hasOptions() const
|
||||
{
|
||||
return !optionsSignature().isEmpty();
|
||||
}
|
||||
|
||||
/* protected */
|
||||
|
||||
/* Getters */
|
||||
|
||||
QStringList Command::passedArguments() const
|
||||
{
|
||||
if (!m_arguments.isEmpty())
|
||||
return m_arguments;
|
||||
|
||||
// Never obtain arguments from the QCoreApplication instance in tests
|
||||
return application().hasQtApplication ? QCoreApplication::arguments()
|
||||
: application().prepareArguments();
|
||||
}
|
||||
|
||||
/* Parser helpers */
|
||||
|
||||
bool Command::isSet(const QString &name) const
|
||||
{
|
||||
return parser().isSet(name);
|
||||
}
|
||||
|
||||
QString Command::value(const QString &name) const
|
||||
{
|
||||
return parser().value(name);
|
||||
}
|
||||
|
||||
QString Command::valueCmd(const QString &name, const QString &key) const
|
||||
{
|
||||
if (auto value = parser().value(name);
|
||||
!value.isEmpty()
|
||||
)
|
||||
return QStringLiteral("--%1=%2").arg(key.isEmpty() ? name : key,
|
||||
std::move(value));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
QString Command::boolCmd(const QString &name, const QString &key) const
|
||||
{
|
||||
if (!parser().isSet(name))
|
||||
return {};
|
||||
|
||||
return QStringLiteral("--%1").arg(key.isEmpty() ? name : key);
|
||||
}
|
||||
|
||||
bool Command::hasArgument(const QList<QString>::size_type index) const
|
||||
{
|
||||
/* Has to be isNull(), an argument passed on the command line still can be an empty
|
||||
value, like "", in this case it has to return a true value. */
|
||||
return !argument(index).isNull();
|
||||
}
|
||||
|
||||
QStringList Command::arguments() const
|
||||
{
|
||||
return parser().positionalArguments();
|
||||
}
|
||||
|
||||
QString Command::argument(const QList<QString>::size_type index) const
|
||||
{
|
||||
// Default value supported
|
||||
return parser().positionalArguments()
|
||||
.value(index,
|
||||
positionalArguments().at(
|
||||
static_cast<std::size_t>(index) - 1).defaultValue);
|
||||
}
|
||||
|
||||
QString Command::argument(const QString &name) const
|
||||
{
|
||||
// Default value supported
|
||||
return argument(m_positionalArguments.at(name));
|
||||
}
|
||||
|
||||
Orm::DatabaseConnection &Command::connection(const QString &name) const
|
||||
{
|
||||
return application().db().connection(name);
|
||||
}
|
||||
|
||||
QCommandLineParser &Command::parser() const noexcept
|
||||
{
|
||||
return m_parser;
|
||||
}
|
||||
|
||||
/* private */
|
||||
|
||||
void Command::initializePositionalArguments()
|
||||
{
|
||||
const auto &arguments = positionalArguments();
|
||||
|
||||
if (arguments.empty())
|
||||
return;
|
||||
|
||||
using ArgumentsSizeType = std::vector<PositionalArgument>::size_type;
|
||||
|
||||
m_positionalArguments =
|
||||
ranges::views::zip_with([](const auto &argument, auto &&index)
|
||||
-> std::pair<QString, OptionsSizeType>
|
||||
{
|
||||
return {argument.name, index};
|
||||
},
|
||||
arguments, ranges::views::closed_iota(static_cast<ArgumentsSizeType>(1),
|
||||
arguments.size())
|
||||
)
|
||||
| ranges::to<decltype (m_positionalArguments)>();
|
||||
|
||||
// The same as above, I leave above as I want to have one example with zip_with()
|
||||
// for (OptionsSizeType index = 0; const auto &argument : positionalArguments())
|
||||
// m_positionalArguments.emplace(argument.name, ++index);
|
||||
}
|
||||
|
||||
void Command::checkHelpArgument() const
|
||||
{
|
||||
if (!isSet("help"))
|
||||
return;
|
||||
|
||||
call("help", {name()});
|
||||
|
||||
application().exitApplication(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
void Command::showParserError(const QCommandLineParser &parser) const
|
||||
{
|
||||
errorWall(parser.errorText());
|
||||
|
||||
application().exitApplication(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
} // namespace Tom::Commands
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
80
tom/src/tom/commands/database/wipecommand.cpp
Normal file
80
tom/src/tom/commands/database/wipecommand.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
#include "tom/commands/database/wipecommand.hpp"
|
||||
|
||||
#include <QCommandLineParser>
|
||||
|
||||
#include <orm/databaseconnection.hpp>
|
||||
|
||||
using Orm::Constants::database_;
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom::Commands::Database
|
||||
{
|
||||
|
||||
/* public */
|
||||
|
||||
WipeCommand::WipeCommand(Application &application, QCommandLineParser &parser)
|
||||
: Command(application, parser)
|
||||
, Concerns::Confirmable(*this, 0)
|
||||
{}
|
||||
|
||||
QList<QCommandLineOption> WipeCommand::optionsSignature() const
|
||||
{
|
||||
return {
|
||||
{database_, "The database connection to use", database_}, // Value
|
||||
{"drop-views", "Drop all tables and views"},
|
||||
{"drop-types", "Drop all tables and types (Postgres only)"},
|
||||
{"force", "Force the operation to run when in production"},
|
||||
};
|
||||
}
|
||||
|
||||
int WipeCommand::run()
|
||||
{
|
||||
Command::run();
|
||||
|
||||
// Ask for confirmation in the production environment
|
||||
if (!confirmToProceed())
|
||||
return EXIT_FAILURE;
|
||||
|
||||
// Database connection to use
|
||||
const auto database = value(database_);
|
||||
|
||||
if (isSet("drop-views")) {
|
||||
dropAllViews(database);
|
||||
|
||||
info(QLatin1String("Dropped all views successfully."));
|
||||
}
|
||||
|
||||
dropAllTables(database);
|
||||
|
||||
info(QLatin1String("Dropped all tables successfully."));
|
||||
|
||||
if (isSet("drop-types")) {
|
||||
dropAllTypes(database);
|
||||
|
||||
info(QLatin1String("Dropped all types successfully."));
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* protected */
|
||||
|
||||
void WipeCommand::dropAllTables(const QString &database) const
|
||||
{
|
||||
connection(database).getSchemaBuilder()->dropAllTables();
|
||||
}
|
||||
|
||||
void WipeCommand::dropAllViews(const QString &database) const
|
||||
{
|
||||
connection(database).getSchemaBuilder()->dropAllViews();
|
||||
}
|
||||
|
||||
void WipeCommand::dropAllTypes(const QString &database) const
|
||||
{
|
||||
connection(database).getSchemaBuilder()->dropAllTypes();
|
||||
}
|
||||
|
||||
} // namespace Tom::Commands::Database
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
30
tom/src/tom/commands/environmentcommand.cpp
Normal file
30
tom/src/tom/commands/environmentcommand.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#include "tom/commands/environmentcommand.hpp"
|
||||
|
||||
#include "tom/application.hpp"
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom::Commands
|
||||
{
|
||||
|
||||
/* public */
|
||||
|
||||
EnvironmentCommand::EnvironmentCommand(Application &application,
|
||||
QCommandLineParser &parser)
|
||||
: Command(application, parser)
|
||||
{}
|
||||
|
||||
int EnvironmentCommand::run()
|
||||
{
|
||||
Command::run();
|
||||
|
||||
info(QLatin1String("Current application environment: "), false);
|
||||
|
||||
comment(application().environment());
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace Tom::Commands
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
240
tom/src/tom/commands/helpcommand.cpp
Normal file
240
tom/src/tom/commands/helpcommand.cpp
Normal file
@@ -0,0 +1,240 @@
|
||||
#include "tom/commands/helpcommand.hpp"
|
||||
|
||||
#include <QCommandLineOption>
|
||||
|
||||
#include <orm/constants.hpp>
|
||||
#include <orm/tiny/utils/string.hpp>
|
||||
|
||||
#include "tom/application.hpp"
|
||||
|
||||
using Orm::Constants::SPACE;
|
||||
|
||||
using StringUtils = Orm::Tiny::Utils::String;
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom::Commands
|
||||
{
|
||||
|
||||
/* public */
|
||||
|
||||
HelpCommand::HelpCommand(Application &application, QCommandLineParser &parser)
|
||||
: Command(application, parser)
|
||||
, Concerns::PrintsOptions(*this, 0)
|
||||
{}
|
||||
|
||||
const std::vector<PositionalArgument> &HelpCommand::positionalArguments() const
|
||||
{
|
||||
static const std::vector<PositionalArgument> cached {
|
||||
{"command_name", "The command name", {}, true, "help"},
|
||||
};
|
||||
|
||||
return cached;
|
||||
}
|
||||
|
||||
QString HelpCommand::help() const
|
||||
{
|
||||
return QLatin1String(
|
||||
" The <info>help</info> command displays help for a given command:\n\n"
|
||||
" <info>tom</info> help list\n\n"
|
||||
" To display the list of available commands, please use the <info>list</info> "
|
||||
"command.");
|
||||
}
|
||||
|
||||
int HelpCommand::run()
|
||||
{
|
||||
Command::run();
|
||||
|
||||
const auto commandNameArg = argument("command_name");
|
||||
|
||||
const auto command = createCommand(commandNameArg);
|
||||
const auto &arguments = command->positionalArguments();
|
||||
|
||||
if (!validateRequiredArguments(arguments))
|
||||
return EXIT_FAILURE;
|
||||
|
||||
printDescriptionSection(*command);
|
||||
printUsageSection(commandNameArg, *command, arguments);
|
||||
|
||||
printArgumentsSection(arguments);
|
||||
printOptionsSection(*command);
|
||||
|
||||
printHelpSection(*command);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* protected */
|
||||
|
||||
std::unique_ptr<Command> HelpCommand::createCommand(const QString &name) const
|
||||
{
|
||||
auto command = application().createCommand(name, std::nullopt, false);
|
||||
|
||||
if (command)
|
||||
return command;
|
||||
|
||||
errorWall(QLatin1String("Command '%1' is not defined.").arg(name));
|
||||
|
||||
application().exitApplication(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
bool HelpCommand::validateRequiredArguments(
|
||||
const std::vector<PositionalArgument> &arguments) const
|
||||
{
|
||||
// Fail on required argument after optional argument
|
||||
for (std::vector<PositionalArgument>::size_type i = 1;
|
||||
i < arguments.size() ; ++i
|
||||
) {
|
||||
const auto &left = arguments.at(i - 1);
|
||||
const auto &right = arguments.at(i);
|
||||
|
||||
if (left.optional && !right.optional) {
|
||||
errorWall(QLatin1String("Cannot add a required argument '%1' after "
|
||||
"an optional one '%2'.")
|
||||
.arg(right.name, left.name));
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Fail when required argument has a default value
|
||||
return std::ranges::none_of(arguments, [this](const auto &argument)
|
||||
{
|
||||
const auto requiredWithDefault = !argument.optional &&
|
||||
!argument.defaultValue.isEmpty();
|
||||
|
||||
if (requiredWithDefault)
|
||||
errorWall(QLatin1String("The required argument '%1' has a default value "
|
||||
"'%2'.")
|
||||
.arg(argument.name, argument.defaultValue));
|
||||
|
||||
return requiredWithDefault;
|
||||
});
|
||||
}
|
||||
|
||||
void HelpCommand::printDescriptionSection(const Command &command) const
|
||||
{
|
||||
comment(QLatin1String("Description:"));
|
||||
|
||||
note(QStringLiteral(" %1").arg(command.description()));
|
||||
}
|
||||
|
||||
void HelpCommand::printUsageSection(
|
||||
const QString &commandNameArg, const Command &command,
|
||||
const std::vector<PositionalArgument> &arguments) const
|
||||
{
|
||||
/* Everything after the option -- (double dash) is treated as positional arguments.
|
||||
[] means optional, <> means positional argument. If an argument is optional,
|
||||
all arguments after have to be optional. */
|
||||
|
||||
newLine();
|
||||
|
||||
comment(QLatin1String("Usage:"));
|
||||
|
||||
QString usage(2, SPACE);
|
||||
usage += commandNameArg;
|
||||
|
||||
if (command.hasOptions())
|
||||
usage += QLatin1String(" [options]");
|
||||
|
||||
if (command.hasPositionalArguments()) {
|
||||
usage += QLatin1String(" [--]");
|
||||
|
||||
auto optionalCounter = 0;
|
||||
|
||||
for (const auto &argument : arguments) {
|
||||
auto syntax = argument.syntax.isEmpty() ? argument.name : argument.syntax;
|
||||
|
||||
if (argument.optional) {
|
||||
usage += QStringLiteral(" [<%1>").arg(std::move(syntax));
|
||||
++optionalCounter;
|
||||
}
|
||||
else
|
||||
usage += QStringLiteral(" <%1>").arg(std::move(syntax));
|
||||
}
|
||||
|
||||
usage += QString(optionalCounter, QChar(']'));
|
||||
}
|
||||
|
||||
note(usage);
|
||||
}
|
||||
|
||||
void HelpCommand::printArgumentsSection(
|
||||
const std::vector<PositionalArgument> &arguments) const
|
||||
{
|
||||
if (arguments.empty())
|
||||
return;
|
||||
|
||||
newLine();
|
||||
|
||||
comment(QLatin1String("Arguments:"));
|
||||
|
||||
for (const auto argumentsMaxSize = this->argumentsMaxSize(arguments);
|
||||
const auto &argument : arguments
|
||||
) {
|
||||
// Compute indent
|
||||
auto indent = QString(argumentsMaxSize - argument.name.size(), SPACE);
|
||||
|
||||
info(QStringLiteral(" %1%2 ").arg(argument.name, std::move(indent)), false);
|
||||
|
||||
note(argument.description, false);
|
||||
|
||||
printArgumentDefaultValue(argument);
|
||||
}
|
||||
}
|
||||
|
||||
int HelpCommand::argumentsMaxSize(const std::vector<PositionalArgument> &arguments) const
|
||||
{
|
||||
const auto it = std::ranges::max_element(arguments, std::less {},
|
||||
[](const auto &argument)
|
||||
{
|
||||
return argument.name.size();
|
||||
});
|
||||
|
||||
return static_cast<int>((*it).name.size());
|
||||
}
|
||||
|
||||
void HelpCommand::printArgumentDefaultValue(const PositionalArgument &argument) const
|
||||
{
|
||||
// Empty default value, don't render
|
||||
const auto &defaultValueRef = argument.defaultValue;
|
||||
|
||||
if (defaultValueRef.isEmpty()) {
|
||||
newLine();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Quote string type
|
||||
auto defaultValue = StringUtils::isNumber(defaultValueRef, true)
|
||||
? defaultValueRef
|
||||
: QStringLiteral("\"%1\"").arg(defaultValueRef);
|
||||
|
||||
comment(QStringLiteral(" [default: %1]").arg(std::move(defaultValue)));
|
||||
}
|
||||
|
||||
int HelpCommand::printOptionsSection(const Command &command) const
|
||||
{
|
||||
// Prepare the command parser to show command options
|
||||
parser().addOptions(application().prependOptions(command.optionsSignature()));
|
||||
|
||||
return PrintsOptions::printOptionsSection(false);
|
||||
}
|
||||
|
||||
void HelpCommand::printHelpSection(const Command &command) const
|
||||
{
|
||||
const auto help = command.help();
|
||||
|
||||
if (help.isEmpty())
|
||||
return;
|
||||
|
||||
newLine();
|
||||
|
||||
comment(QLatin1String("Help:"));
|
||||
|
||||
note(help);
|
||||
}
|
||||
|
||||
} // namespace Tom::Commands
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
80
tom/src/tom/commands/inspirecommand.cpp
Normal file
80
tom/src/tom/commands/inspirecommand.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
#include "tom/commands/inspirecommand.hpp"
|
||||
|
||||
#include <random>
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom::Commands
|
||||
{
|
||||
|
||||
/* public */
|
||||
|
||||
InspireCommand::InspireCommand(Application &application, QCommandLineParser &parser)
|
||||
: Command(application, parser)
|
||||
{}
|
||||
|
||||
int InspireCommand::run()
|
||||
{
|
||||
Command::run();
|
||||
|
||||
static const std::vector inspires {
|
||||
"Act only according to that maxim whereby you can, at the same time, will that it should become a universal law. - Immanuel Kant",
|
||||
"An unexamined life is not worth living. - Socrates",
|
||||
"Be present above all else. - Naval Ravikant",
|
||||
"Do what you can, with what you have, where you are. - Theodore Roosevelt",
|
||||
"Happiness is not something readymade. It comes from your own actions. - Dalai Lama",
|
||||
"He who is contented is rich. - Laozi",
|
||||
"I begin to speak only when I am certain what I will say is not better left unsaid. - Cato the Younger",
|
||||
"I have not failed. I've just found 10,000 ways that won't work. - Thomas Edison",
|
||||
"If you do not have a consistent goal in life, you can not live it in a consistent way. - Marcus Aurelius",
|
||||
"It is never too late to be what you might have been. - George Eliot",
|
||||
"It is not the man who has too little, but the man who craves more, that is poor. - Seneca",
|
||||
"It is quality rather than quantity that matters. - Lucius Annaeus Seneca",
|
||||
"Knowing is not enough; we must apply. Being willing is not enough; we must do. - Leonardo da Vinci",
|
||||
"Let all your things have their places; let each part of your business have its time. - Benjamin Franklin",
|
||||
"Live as if you were to die tomorrow. Learn as if you were to live forever. - Mahatma Gandhi",
|
||||
"No surplus words or unnecessary actions. - Marcus Aurelius",
|
||||
"Nothing worth having comes easy. - Theodore Roosevelt",
|
||||
"Order your soul. Reduce your wants. - Augustine",
|
||||
"People find pleasure in different ways. I find it in keeping my mind clear. - Marcus Aurelius",
|
||||
"Simplicity is an acquired taste. - Katharine Gerould",
|
||||
"Simplicity is the consequence of refined emotions. - Jean D'Alembert",
|
||||
"Simplicity is the essence of happiness. - Cedric Bledsoe",
|
||||
"Simplicity is the ultimate sophistication. - Leonardo da Vinci",
|
||||
"Smile, breathe, and go slowly. - Thich Nhat Hanh",
|
||||
"The only way to do great work is to love what you do. - Steve Jobs",
|
||||
"The whole future lies in uncertainty: live immediately. - Seneca",
|
||||
"Very little is needed to make a happy life. - Marcus Aurelius",
|
||||
"Waste no more time arguing what a good man should be, be one. - Marcus Aurelius",
|
||||
"Well begun is half done. - Aristotle",
|
||||
"When there is no desire, all things are at peace. - Laozi",
|
||||
"Walk as if you are kissing the Earth with your feet. - Thich Nhat Hanh",
|
||||
"Because you are alive, everything is possible. - Thich Nhat Hanh",
|
||||
"Breathing in, I calm body and mind. Breathing out, I smile. - Thich Nhat Hanh",
|
||||
"Life is available only in the present moment. - Thich Nhat Hanh",
|
||||
"The best way to take care of the future is to take care of the present moment. - Thich Nhat Hanh",
|
||||
"Nothing in life is to be feared, it is only to be understood. Now is the time to understand more, so that we may fear less. - Marie Curie",
|
||||
};
|
||||
|
||||
static const auto size = inspires.size();
|
||||
|
||||
// Obtain a random number from hardware
|
||||
std::random_device rd;
|
||||
// Seed the generator
|
||||
std::default_random_engine generator(rd());
|
||||
// Define the range
|
||||
std::uniform_int_distribution<std::size_t>
|
||||
distribute(0, static_cast<decltype (inspires)::size_type>(size) - 1);
|
||||
|
||||
comment(inspires.at(distribute(generator)));
|
||||
|
||||
// Alternative implementation
|
||||
// std::srand(std::time(nullptr));
|
||||
// comment(inspires.at(std::rand() % size));
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace Tom::Commands
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
234
tom/src/tom/commands/listcommand.cpp
Normal file
234
tom/src/tom/commands/listcommand.cpp
Normal file
@@ -0,0 +1,234 @@
|
||||
#include "tom/commands/listcommand.hpp"
|
||||
|
||||
#include <QCommandLineOption>
|
||||
|
||||
#include <range/v3/range/conversion.hpp>
|
||||
#include <range/v3/view/slice.hpp>
|
||||
|
||||
#include <orm/constants.hpp>
|
||||
|
||||
#include "tom/application.hpp"
|
||||
|
||||
using Orm::Constants::COLON;
|
||||
using Orm::Constants::SPACE;
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom::Commands
|
||||
{
|
||||
|
||||
/* public */
|
||||
|
||||
ListCommand::ListCommand(Application &application, QCommandLineParser &parser)
|
||||
: Command(application, parser)
|
||||
, Concerns::PrintsOptions(*this, 0)
|
||||
{}
|
||||
|
||||
const std::vector<PositionalArgument> &ListCommand::positionalArguments() const
|
||||
{
|
||||
static const std::vector<PositionalArgument> cached {
|
||||
{"namespace", "The namespace name", {}, true},
|
||||
};
|
||||
|
||||
return cached;
|
||||
}
|
||||
|
||||
QList<QCommandLineOption> ListCommand::optionsSignature() const
|
||||
{
|
||||
return {
|
||||
{"raw", "To output raw command list"},
|
||||
};
|
||||
}
|
||||
|
||||
QString ListCommand::help() const
|
||||
{
|
||||
return QLatin1String(
|
||||
" The <info>list</info> command lists all commands:\n\n"
|
||||
" <info>tom list</info>\n\n"
|
||||
" You can also display the commands for a specific namespace:\n\n"
|
||||
" <info>tom list test</info>\n\n"
|
||||
" It's also possible to get raw list of commands (useful for embedding command "
|
||||
"runner):\n\n"
|
||||
" <info>tom list --raw</info>");
|
||||
}
|
||||
|
||||
int ListCommand::run()
|
||||
{
|
||||
Command::run();
|
||||
|
||||
const auto namespaceArg = argument("namespace");
|
||||
|
||||
return isSet("raw") ? raw(namespaceArg) : full(namespaceArg);
|
||||
}
|
||||
|
||||
/* protected */
|
||||
|
||||
int ListCommand::full(const QString &namespaceArg)
|
||||
{
|
||||
application().printVersion();
|
||||
|
||||
newLine();
|
||||
comment(QLatin1String("Usage:"));
|
||||
note(QLatin1String(" command [options] [--] [arguments]"));
|
||||
|
||||
// Options section
|
||||
const auto optionsMaxSize = printOptionsSection(true);
|
||||
|
||||
// Commands section
|
||||
printCommandsSection(namespaceArg, optionsMaxSize);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int ListCommand::raw(const QString &namespaceArg)
|
||||
{
|
||||
const auto &commands = getCommandsByNamespace(namespaceArg);
|
||||
|
||||
const auto it = std::ranges::max_element(commands, std::less {},
|
||||
[](const auto &command)
|
||||
{
|
||||
return command->name().size();
|
||||
});
|
||||
|
||||
const auto commandMaxSize = static_cast<int>((*it)->name().size());
|
||||
|
||||
for (const auto &command : commands) {
|
||||
auto commandName = command->name();
|
||||
auto indent = QString(commandMaxSize - commandName.size(), SPACE);
|
||||
|
||||
note(QStringLiteral("%1%2 %3").arg(std::move(commandName), std::move(indent),
|
||||
command->description()));
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* Commands section */
|
||||
|
||||
void ListCommand::printCommandsSection(const QString &namespaceArg,
|
||||
const int optionsMaxSize) const
|
||||
{
|
||||
const auto hasNamespaceArg = !namespaceArg.isNull();
|
||||
|
||||
newLine();
|
||||
|
||||
if (hasNamespaceArg)
|
||||
comment(QLatin1String("Available commands for the '%1' namespace:")
|
||||
.arg(namespaceArg.isEmpty() ? QLatin1String("global") : namespaceArg));
|
||||
else
|
||||
comment(QLatin1String("Available commands:"));
|
||||
|
||||
const auto &commands = getCommandsByNamespace(namespaceArg);
|
||||
|
||||
// Get max. command size in all command names
|
||||
const auto commandsMaxSize = this->commandsMaxSize(commands, optionsMaxSize);
|
||||
// Print commands to the console
|
||||
printCommands(commands, commandsMaxSize, hasNamespaceArg);
|
||||
}
|
||||
|
||||
int ListCommand::commandsMaxSize(
|
||||
const std::vector<std::shared_ptr<Command>> &commands,
|
||||
const int optionsMaxSize) const
|
||||
{
|
||||
const auto it = std::ranges::max_element(commands, std::less {},
|
||||
[](const auto &command)
|
||||
{
|
||||
return command->name().size();
|
||||
});
|
||||
|
||||
auto commandsMaxSize = static_cast<int>((*it)->name().size());
|
||||
|
||||
// Align commands' description to the same level as options' description
|
||||
if (commandsMaxSize < optionsMaxSize)
|
||||
commandsMaxSize = optionsMaxSize;
|
||||
|
||||
return commandsMaxSize;
|
||||
}
|
||||
|
||||
void ListCommand::printCommands(
|
||||
const std::vector<std::shared_ptr<Command>> &commands,
|
||||
const int commandsMaxSize, const bool hasNamespaceArg) const
|
||||
{
|
||||
// Currently rendering NS
|
||||
QString renderingNamespace;
|
||||
|
||||
for (const auto &command : commands) {
|
||||
auto commandName = command->name();
|
||||
|
||||
// Begin a new namespace section
|
||||
tryBeginNsSection(renderingNamespace, commandName, hasNamespaceArg);
|
||||
|
||||
auto indent = QString(commandsMaxSize - commandName.size(), SPACE);
|
||||
|
||||
info(QStringLiteral(" %1%2").arg(std::move(commandName), std::move(indent)),
|
||||
false);
|
||||
|
||||
note(QStringLiteral(" %1").arg(command->description()));
|
||||
}
|
||||
}
|
||||
|
||||
void ListCommand::tryBeginNsSection(
|
||||
QString &renderingNamespace, const QString &commandName,
|
||||
const bool hasNamespaceArg) const
|
||||
{
|
||||
const auto commandNamespace = this->commandNamespace(commandName);
|
||||
|
||||
if (hasNamespaceArg || commandNamespace == renderingNamespace)
|
||||
return;
|
||||
|
||||
// Update currently rendering NS section
|
||||
renderingNamespace = commandNamespace;
|
||||
|
||||
comment(QStringLiteral(" %1").arg(renderingNamespace));
|
||||
}
|
||||
|
||||
QString ListCommand::commandNamespace(const QString &commandName) const
|
||||
{
|
||||
if (!commandName.contains(COLON))
|
||||
return {};
|
||||
|
||||
return commandName.split(COLON).at(0);
|
||||
}
|
||||
|
||||
const std::vector<std::shared_ptr<Command>> &
|
||||
ListCommand::getCommandsByNamespace(const QString &name) const
|
||||
{
|
||||
// isNull() needed because still able to return global namespace for an empty string
|
||||
if (name.isNull())
|
||||
return application().createCommandsVector();
|
||||
|
||||
/* This avoids one copy that would be done if commands would be returned by a value,
|
||||
key thing is that it can be returned as a const reference, believe it it works. */
|
||||
static std::vector<std::shared_ptr<Command>> cached;
|
||||
|
||||
return cached = getCommandsInNamespace(name);
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<Command>>
|
||||
ListCommand::getCommandsInNamespace(const QString &name) const
|
||||
{
|
||||
/* First number is index where it starts (0-based), second the number where it ends.
|
||||
tuple is forwarded as args to the ranges::views::slice(). */
|
||||
static const std::unordered_map<QString, std::tuple<int, int>> cached {
|
||||
{"", std::make_tuple(0, 5)},
|
||||
{"global", std::make_tuple(0, 5)},
|
||||
{"db", std::make_tuple(5, 6)},
|
||||
{"make", std::make_tuple(6, 8)},
|
||||
{"migrate", std::make_tuple(8, 14)},
|
||||
};
|
||||
|
||||
if (!cached.contains(name)) {
|
||||
errorWall(QLatin1String("There are no commands defined in the '%1' namespace.")
|
||||
.arg(name));
|
||||
|
||||
application().exitApplication(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return application().createCommandsVector()
|
||||
| std::apply(ranges::views::slice, cached.at(name))
|
||||
| ranges::to<std::vector<std::shared_ptr<Command>>>();
|
||||
}
|
||||
|
||||
} // namespace Tom::Commands
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
99
tom/src/tom/commands/make/migrationcommand.cpp
Normal file
99
tom/src/tom/commands/make/migrationcommand.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
#include "tom/commands/make/migrationcommand.hpp"
|
||||
|
||||
#include <QCommandLineOption>
|
||||
|
||||
#include <orm/constants.hpp>
|
||||
#include <orm/tiny/utils/string.hpp>
|
||||
|
||||
using fspath = std::filesystem::path;
|
||||
|
||||
using Orm::Constants::NAME;
|
||||
|
||||
using StringUtils = Orm::Tiny::Utils::String;
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom::Commands::Make
|
||||
{
|
||||
|
||||
/* public */
|
||||
|
||||
MigrationCommand::MigrationCommand(Application &application, QCommandLineParser &parser)
|
||||
: Command(application, parser)
|
||||
{}
|
||||
|
||||
const std::vector<PositionalArgument> &MigrationCommand::positionalArguments() const
|
||||
{
|
||||
static const std::vector<PositionalArgument> cached {
|
||||
{NAME, "The name of the migration"},
|
||||
};
|
||||
|
||||
return cached;
|
||||
}
|
||||
|
||||
QList<QCommandLineOption> MigrationCommand::optionsSignature() const
|
||||
{
|
||||
return {
|
||||
{"create", "The table to be created", "create"},
|
||||
{"table", "The table to migrate", "table"},
|
||||
{"path", "The location where the migration file should be created", "path"},
|
||||
{"realpath", "Indicate any provided migration file paths are pre-resolved "
|
||||
"absolute paths"},
|
||||
{"fullpath", "Output the full path of the migration"},
|
||||
};
|
||||
}
|
||||
|
||||
int MigrationCommand::run()
|
||||
{
|
||||
Command::run();
|
||||
|
||||
/* It's possible for the developer to specify the tables to modify in this
|
||||
schema operation. The developer may also specify if this table needs
|
||||
to be freshly created so we can create the appropriate migrations. */
|
||||
const auto name = StringUtils::snake(argument(NAME).trimmed());
|
||||
|
||||
auto table = value(QStringLiteral("table"));
|
||||
|
||||
auto createArg = value(QStringLiteral("create"));
|
||||
auto create = isSet(QStringLiteral("create"));
|
||||
|
||||
/* If no table was given as an option but a create option is given then we
|
||||
will use the "create" option as the table name. This allows the devs
|
||||
to pass a table name into this option as a short-cut for creating. */
|
||||
if (table.isEmpty() && !createArg.isEmpty()) {
|
||||
table = createArg;
|
||||
|
||||
create = true;
|
||||
}
|
||||
|
||||
// CUR tom, finish TableGuesser, when --create/--table params are not passed silverqx
|
||||
/* Next, we will attempt to guess the table name if the migration name has
|
||||
"create" in the name. This will allow us to provide a convenient way
|
||||
of creating migrations that create new tables for the application. */
|
||||
// if (!table.isEmpty())
|
||||
// auto [table, create] = TableGuesser::guess(name);
|
||||
|
||||
/* Now we are ready to write the migration out to disk. Once we've written
|
||||
the migration out. */
|
||||
writeMigration(name, table, create);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
void MigrationCommand::writeMigration(const QString &name, const QString &table,
|
||||
const bool create) const
|
||||
{
|
||||
auto migrationFilePath = m_creator.create(name, getMigrationPath(), table, create);
|
||||
|
||||
// make_preferred() returns reference and filename() creates a new fs::path instance
|
||||
const auto migrationFile = isSet("fullpath") ? migrationFilePath.make_preferred()
|
||||
: migrationFilePath.filename();
|
||||
|
||||
info(QLatin1String("Created Migration: "), false);
|
||||
|
||||
note(QString::fromWCharArray(migrationFile.c_str()));
|
||||
}
|
||||
|
||||
} // namespace Tom::Commands::Make
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
95
tom/src/tom/commands/make/projectcommand.cpp
Normal file
95
tom/src/tom/commands/make/projectcommand.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
#include "tom/commands/make/projectcommand.hpp"
|
||||
|
||||
#include <QCommandLineOption>
|
||||
|
||||
#include <orm/constants.hpp>
|
||||
|
||||
//using fspath = std::filesystem::path;
|
||||
|
||||
using Orm::Constants::NAME;
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom::Commands::Make
|
||||
{
|
||||
|
||||
/* public */
|
||||
|
||||
ProjectCommand::ProjectCommand(Application &application, QCommandLineParser &parser)
|
||||
: Command(application, parser)
|
||||
{}
|
||||
|
||||
const std::vector<PositionalArgument> &ProjectCommand::positionalArguments() const
|
||||
{
|
||||
static const std::vector<PositionalArgument> cached {
|
||||
{NAME, "The name of the project"},
|
||||
};
|
||||
|
||||
return cached;
|
||||
}
|
||||
|
||||
QList<QCommandLineOption> ProjectCommand::signature() const
|
||||
{
|
||||
return {
|
||||
{"qmake", "Create qmake project"},
|
||||
{"cmake", "Create CMake project"},
|
||||
{"tinyorm", "Crete TinyORM project"},
|
||||
{"tom", "Crete tom project"},
|
||||
{"path", "The location where the project should be created", "path"},
|
||||
{"realpath", "Indicate the <info>path</info> argument is an absolute path"},
|
||||
};
|
||||
}
|
||||
|
||||
int ProjectCommand::run()
|
||||
{
|
||||
Command::run();
|
||||
|
||||
/* It's possible for the developer to specify the tables to modify in this
|
||||
schema operation. The developer may also specify if this table needs
|
||||
to be freshly created so we can create the appropriate migrations. */
|
||||
// const auto name = StringUtils::snake(argument(NAME).trimmed());
|
||||
|
||||
// auto table = value(QStringLiteral("table"));
|
||||
|
||||
// auto createArg = value(QStringLiteral("create"));
|
||||
// auto create = isSet(QStringLiteral("create"));
|
||||
|
||||
// /* If no table was given as an option but a create option is given then we
|
||||
// will use the "create" option as the table name. This allows the devs
|
||||
// to pass a table name into this option as a short-cut for creating. */
|
||||
// if (table.isEmpty() && !createArg.isEmpty()) {
|
||||
// table = createArg;
|
||||
|
||||
// create = true;
|
||||
// }
|
||||
|
||||
// // CUR tom, finish, when --create/--table params are not passed silverqx
|
||||
// /* Next, we will attempt to guess the table name if the migration name has
|
||||
// "create" in the name. This will allow us to provide a convenient way
|
||||
// of creating migrations that create new tables for the application. */
|
||||
//// if (!table.isEmpty())
|
||||
//// auto [table, create] = TableGuesser::guess(name);
|
||||
|
||||
// /* Now we are ready to write the migration out to disk. Once we've written
|
||||
// the migration out. */
|
||||
// writeMigration(name, table, create);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
//void ProjectCommand::writeMigration(const QString &name, const QString &table,
|
||||
// const bool create) const
|
||||
//{
|
||||
// auto migrationFilePath = m_creator.create(name, getMigrationPath(), table, create);
|
||||
|
||||
// // make_preferred() returns reference and filename() creates a new fs::path instance
|
||||
// const auto migrationFile = isSet("fullpath") ? migrationFilePath.make_preferred()
|
||||
// : migrationFilePath.filename();
|
||||
|
||||
// info(QLatin1String("Created Migration: "), false)
|
||||
// .note(QString::fromWCharArray(migrationFile.c_str()));
|
||||
//}
|
||||
|
||||
} // namespace Tom::Commands::Make
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
85
tom/src/tom/commands/migrations/freshcommand.cpp
Normal file
85
tom/src/tom/commands/migrations/freshcommand.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
#include "tom/commands/migrations/freshcommand.hpp"
|
||||
|
||||
#include <QCommandLineParser>
|
||||
|
||||
#include <orm/constants.hpp>
|
||||
|
||||
#include "tom/migrator.hpp"
|
||||
|
||||
using Orm::Constants::database_;
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom::Commands::Migrations
|
||||
{
|
||||
|
||||
/* public */
|
||||
|
||||
FreshCommand::FreshCommand(
|
||||
Application &application, QCommandLineParser &parser,
|
||||
std::shared_ptr<Migrator> migrator
|
||||
)
|
||||
: Command(application, parser)
|
||||
, Concerns::Confirmable(*this, 0)
|
||||
, m_migrator(std::move(migrator))
|
||||
{}
|
||||
|
||||
QList<QCommandLineOption> FreshCommand::optionsSignature() const
|
||||
{
|
||||
return {
|
||||
{database_, "The database connection to use", database_}, // Value
|
||||
{"drop-views", "Drop all tables and views"},
|
||||
{"drop-types", "Drop all tables and types (Postgres only)"},
|
||||
{"force", "Force the operation to run when in production"},
|
||||
// {"schema-path", "The path to a schema dump file"}, // Value
|
||||
// {"seed", "Indicates if the seed task should be re-run"},
|
||||
// {"seeder", "The class name of the root seeder", "seeded"}, // Value
|
||||
{"step", "Force the migrations to be run so they can be rolled back "
|
||||
"individually"},
|
||||
};
|
||||
}
|
||||
|
||||
int FreshCommand::run()
|
||||
{
|
||||
Command::run();
|
||||
|
||||
// Ask for confirmation in the production environment
|
||||
if (!confirmToProceed())
|
||||
return EXIT_FAILURE;
|
||||
|
||||
// Database connection to use
|
||||
auto databaseCmd = valueCmd(database_);
|
||||
|
||||
call("db:wipe", {databaseCmd,
|
||||
QStringLiteral("--force"),
|
||||
boolCmd("drop-views"),
|
||||
boolCmd("drop-types")});
|
||||
|
||||
call("migrate", {databaseCmd,
|
||||
QStringLiteral("--force"),
|
||||
boolCmd("step"),
|
||||
valueCmd("schema-path")});
|
||||
|
||||
// if (needsSeeding())
|
||||
// runSeeder(std::move(databaseCmd));
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* protected */
|
||||
|
||||
bool FreshCommand::needsSeeding() const
|
||||
{
|
||||
return isSet("seed") || !value("seeder").isEmpty();
|
||||
}
|
||||
|
||||
void FreshCommand::runSeeder(QString &&databaseCmd) const
|
||||
{
|
||||
call("db:seed", {std::move(databaseCmd),
|
||||
QStringLiteral("--force"),
|
||||
valueCmd("seeder", "class")});
|
||||
}
|
||||
|
||||
} // namespace Tom::Commands::Migrations
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
51
tom/src/tom/commands/migrations/installcommand.cpp
Normal file
51
tom/src/tom/commands/migrations/installcommand.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#include "tom/commands/migrations/installcommand.hpp"
|
||||
|
||||
#include <QCommandLineParser>
|
||||
|
||||
#include <orm/constants.hpp>
|
||||
|
||||
#include "tom/migrationrepository.hpp"
|
||||
|
||||
using Orm::Constants::database_;
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom::Commands::Migrations
|
||||
{
|
||||
|
||||
/* public */
|
||||
|
||||
InstallCommand::InstallCommand(
|
||||
Application &application, QCommandLineParser &parser,
|
||||
std::shared_ptr<MigrationRepository> repository
|
||||
)
|
||||
: Command(application, parser)
|
||||
, m_repository(std::move(repository))
|
||||
{}
|
||||
|
||||
QList<QCommandLineOption> InstallCommand::optionsSignature() const
|
||||
{
|
||||
return {
|
||||
{database_, "The database connection to use", database_}, // Value
|
||||
};
|
||||
}
|
||||
|
||||
int InstallCommand::run()
|
||||
{
|
||||
Command::run();
|
||||
|
||||
// Database connection to use
|
||||
m_repository->setConnection(value(database_), isDebugVerbosity());
|
||||
|
||||
m_repository->createRepository();
|
||||
|
||||
info(QLatin1String("Migration table created successfully."));
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace Tom::Commands::Migrations
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
// FUTURE tom, add migrate:uninstall or migrate:install --uninstall silverqx
|
||||
100
tom/src/tom/commands/migrations/migratecommand.cpp
Normal file
100
tom/src/tom/commands/migrations/migratecommand.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
#include "tom/commands/migrations/migratecommand.hpp"
|
||||
|
||||
#include <QCommandLineParser>
|
||||
|
||||
#include <orm/constants.hpp>
|
||||
|
||||
#include "tom/migrator.hpp"
|
||||
|
||||
using Orm::Constants::database_;
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom::Commands::Migrations
|
||||
{
|
||||
|
||||
/* public */
|
||||
|
||||
MigrateCommand::MigrateCommand(
|
||||
Application &application, QCommandLineParser &parser,
|
||||
std::shared_ptr<Migrator> migrator
|
||||
)
|
||||
: Command(application, parser)
|
||||
, Concerns::Confirmable(*this, 0)
|
||||
, m_migrator(std::move(migrator))
|
||||
{}
|
||||
|
||||
QList<QCommandLineOption> MigrateCommand::optionsSignature() const
|
||||
{
|
||||
return {
|
||||
{database_, "The database connection to use", database_}, // Value
|
||||
{"force", "Force the operation to run when in production"},
|
||||
{"pretend", "Dump the SQL queries that would be run"},
|
||||
{"schema-path", "The path to a schema dump file"}, // Value
|
||||
// {"seed", "Indicates if the seed task should be re-run"},
|
||||
// {"seeder", "The class name of the root seeder", "seeded"}, // Value
|
||||
{"step", "Force the migrations to be run so they can be rolled back "
|
||||
"individually"},
|
||||
};
|
||||
}
|
||||
|
||||
int MigrateCommand::run()
|
||||
{
|
||||
Command::run();
|
||||
|
||||
// Ask for confirmation in the production environment
|
||||
if (!confirmToProceed())
|
||||
return EXIT_FAILURE;
|
||||
|
||||
// Database connection to use
|
||||
return m_migrator->usingConnection(value(database_), isDebugVerbosity(), [this]
|
||||
{
|
||||
// Install db repository and load schema state
|
||||
prepareDatabase();
|
||||
|
||||
/* Next, we will check to see if a path option has been defined. If it has
|
||||
we will use the path relative to the root of this installation folder
|
||||
so that migrations may be run for any path within the applications. */
|
||||
m_migrator->run({isSet("pretend"), isSet("step")});
|
||||
|
||||
/* Finally, if the "seed" option has been given, we will re-run the database
|
||||
seed task to re-populate the database, which is convenient when adding
|
||||
a migration and a seed at the same time, as it is only this command. */
|
||||
// if (needsSeeding()
|
||||
// runSeeder();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
});
|
||||
}
|
||||
|
||||
/* protected */
|
||||
|
||||
void MigrateCommand::prepareDatabase() const
|
||||
{
|
||||
if (!m_migrator->repositoryExists())
|
||||
call(QStringLiteral("migrate:install"), {value(database_)});
|
||||
|
||||
if (!m_migrator->hasRunAnyMigrations() && !isSet("pretend"))
|
||||
loadSchemaState();
|
||||
}
|
||||
|
||||
void MigrateCommand::loadSchemaState() const
|
||||
{
|
||||
// CUR tom, finish load schema silverqx
|
||||
}
|
||||
|
||||
bool MigrateCommand::needsSeeding() const
|
||||
{
|
||||
return !isSet("pretend") && (isSet("seed") || !value("seeder").isEmpty());
|
||||
}
|
||||
|
||||
void MigrateCommand::runSeeder() const
|
||||
{
|
||||
call("db:seed", {valueCmd(database_),
|
||||
QStringLiteral("--force"),
|
||||
valueCmd("seeder", "class")});
|
||||
}
|
||||
|
||||
} // namespace Tom::Commands::Migrations
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
87
tom/src/tom/commands/migrations/refreshcommand.cpp
Normal file
87
tom/src/tom/commands/migrations/refreshcommand.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
#include "tom/commands/migrations/refreshcommand.hpp"
|
||||
|
||||
#include <QCommandLineParser>
|
||||
|
||||
#include <orm/constants.hpp>
|
||||
|
||||
#include "tom/migrator.hpp"
|
||||
|
||||
using Orm::Constants::database_;
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom::Commands::Migrations
|
||||
{
|
||||
|
||||
/* public */
|
||||
|
||||
RefreshCommand::RefreshCommand(
|
||||
Application &application, QCommandLineParser &parser,
|
||||
std::shared_ptr<Migrator> migrator
|
||||
)
|
||||
: Command(application, parser)
|
||||
, Concerns::Confirmable(*this, 0)
|
||||
, m_migrator(std::move(migrator))
|
||||
{}
|
||||
|
||||
QList<QCommandLineOption> RefreshCommand::optionsSignature() const
|
||||
{
|
||||
return {
|
||||
{database_, "The database connection to use", database_}, // Value
|
||||
{"force", "Force the operation to run when in production"},
|
||||
// {"seed", "Indicates if the seed task should be re-run"},
|
||||
// {"seeder", "The class name of the root seeder", "seeded"}, // Value
|
||||
{"step", "The number of migrations to be reverted & re-run", "step"}, // Value
|
||||
{"step-migrate", "Force the migrations to be run so they can be rolled back "
|
||||
"individually"},
|
||||
};
|
||||
}
|
||||
|
||||
int RefreshCommand::run()
|
||||
{
|
||||
Command::run();
|
||||
|
||||
// Ask for confirmation in the production environment
|
||||
if (!confirmToProceed())
|
||||
return EXIT_FAILURE;
|
||||
|
||||
// Database connection to use
|
||||
auto databaseCmd = valueCmd(database_);
|
||||
|
||||
/* If the "step" option is specified it means we only want to rollback a small
|
||||
number of migrations before migrating again. For example, the user might
|
||||
only rollback and remigrate the latest four migrations instead of all. */
|
||||
if (const auto step = value("step").toInt(); step > 0)
|
||||
call("migrate:rollback", {databaseCmd,
|
||||
QStringLiteral("--force"),
|
||||
valueCmd("step")});
|
||||
else
|
||||
call("migrate:reset", {databaseCmd, QStringLiteral("--force")});
|
||||
|
||||
call("migrate", {databaseCmd,
|
||||
QStringLiteral("--force"),
|
||||
boolCmd("step-migrate", "step")});
|
||||
|
||||
// if (needsSeeding())
|
||||
// runSeeder(std::move(databaseCmd));
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* protected */
|
||||
|
||||
bool RefreshCommand::needsSeeding() const
|
||||
{
|
||||
return isSet("seed") || !value("seeder").isEmpty();
|
||||
}
|
||||
|
||||
void RefreshCommand::runSeeder(QString &&databaseCmd) const
|
||||
{
|
||||
call("db:seed", {std::move(databaseCmd),
|
||||
QStringLiteral("--force"),
|
||||
valueCmd("seeder", "class")});
|
||||
}
|
||||
|
||||
} // namespace Tom::Commands::Migrations
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
61
tom/src/tom/commands/migrations/resetcommand.cpp
Normal file
61
tom/src/tom/commands/migrations/resetcommand.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#include "tom/commands/migrations/resetcommand.hpp"
|
||||
|
||||
#include <QCommandLineParser>
|
||||
|
||||
#include <orm/constants.hpp>
|
||||
|
||||
#include "tom/migrator.hpp"
|
||||
|
||||
using Orm::Constants::database_;
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom::Commands::Migrations
|
||||
{
|
||||
|
||||
/* public */
|
||||
|
||||
ResetCommand::ResetCommand(
|
||||
Application &application, QCommandLineParser &parser,
|
||||
std::shared_ptr<Migrator> migrator
|
||||
)
|
||||
: Command(application, parser)
|
||||
, Concerns::Confirmable(*this, 0)
|
||||
, m_migrator(std::move(migrator))
|
||||
{}
|
||||
|
||||
QList<QCommandLineOption> ResetCommand::optionsSignature() const
|
||||
{
|
||||
return {
|
||||
{database_, "The database connection to use", database_}, // Value
|
||||
{"force", "Force the operation to run when in production"},
|
||||
{"pretend", "Dump the SQL queries that would be run"},
|
||||
};
|
||||
}
|
||||
|
||||
int ResetCommand::run()
|
||||
{
|
||||
Command::run();
|
||||
|
||||
// Ask for confirmation in the production environment
|
||||
if (!confirmToProceed())
|
||||
return EXIT_FAILURE;
|
||||
|
||||
// Database connection to use
|
||||
return m_migrator->usingConnection(value(database_), isDebugVerbosity(), [this]
|
||||
{
|
||||
if (!m_migrator->repositoryExists()) {
|
||||
comment(QLatin1String("Migration table not found."));
|
||||
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
m_migrator->reset(isSet("pretend"));
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace Tom::Commands::Migrations
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
58
tom/src/tom/commands/migrations/rollbackcommand.cpp
Normal file
58
tom/src/tom/commands/migrations/rollbackcommand.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#include "tom/commands/migrations/rollbackcommand.hpp"
|
||||
|
||||
#include <QCommandLineParser>
|
||||
|
||||
#include <orm/constants.hpp>
|
||||
|
||||
#include "tom/migrator.hpp"
|
||||
|
||||
using Orm::Constants::database_;
|
||||
|
||||
TINYORM_BEGIN_COMMON_NAMESPACE
|
||||
|
||||
namespace Tom::Commands::Migrations
|
||||
{
|
||||
|
||||
/* public */
|
||||
|
||||
RollbackCommand::RollbackCommand(
|
||||
Application &application, QCommandLineParser &parser,
|
||||
std::shared_ptr<Migrator> migrator
|
||||
)
|
||||
: Command(application, parser)
|
||||
, Concerns::Confirmable(*this, 0)
|
||||
, m_migrator(std::move(migrator))
|
||||
{}
|
||||
|
||||
QList<QCommandLineOption> RollbackCommand::optionsSignature() const
|
||||
{
|
||||
return {
|
||||
{database_, "The database connection to use", database_}, // Value
|
||||
{"force", "Force the operation to run when in production"},
|
||||
{"pretend", "Dump the SQL queries that would be run"},
|
||||
{"step", "The number of migrations to be reverted", "step"}, // Value
|
||||
};
|
||||
}
|
||||
|
||||
int RollbackCommand::run()
|
||||
{
|
||||
Command::run();
|
||||
|
||||
// Ask for confirmation in the production environment
|
||||
if (!confirmToProceed())
|
||||
return EXIT_FAILURE;
|
||||
|
||||
// Database connection to use
|
||||
return m_migrator->usingConnection(value(database_), isDebugVerbosity(), [this]
|
||||
{
|
||||
// Validation not needed as the toInt() returns 0 if conversion fails, like it
|
||||
m_migrator->rollback({.pretend = isSet("pretend"),
|
||||
.stepValue = value("step").toInt()});
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace Tom::Commands::Migrations
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user