diff --git a/src/mainconsole.cpp b/src/mainconsole.cpp --- a/src/mainconsole.cpp +++ b/src/mainconsole.cpp @@ -1,191 +1,218 @@ #include "exitcode.h" #include "mainconsole.h" #include "psettings.h" #include "updater.h" #include #include #include #include MainConsole::MainConsole(QObject* a_parent) : QObject(a_parent) , _exitCode(0) , _errorCount(0) , _os(stdout) , _updater(new DatabaseUpdater) { } MainConsole::~MainConsole() { delete _updater; } void MainConsole::run() { ProgramSettings pset = processArguments(); if (!pset.helpText.isEmpty()) { _os << pset.helpText; QTimer::singleShot(0, this, &MainConsole::quit); return; } -// connect(_updater, SIGNAL(progress(int)), _progress, SLOT(setValue(int))); + connect(_updater, SIGNAL(progress(int)), this, SLOT(progress(int))); connect(_updater, SIGNAL(error(const QString&)), SLOT(error(const QString&))); connect(_updater, SIGNAL(message(const QString&)), SLOT(message(const QString&))); connect(_updater, SIGNAL(sqlError(const QString&, const QString&, const QString&)), SLOT(sqlError(const QString&, const QString&, const QString&))); connect(_updater, SIGNAL(logConnectionParameters(const QString&, const QString&, const QString&)), SLOT(logConnectionParameters(const QString&, const QString&, const QString&))); connect(&_futureWatcher, SIGNAL(finished()), SLOT(finishExecution())); QFuture rc = QtConcurrent::run(_updater, &DatabaseUpdater::run, pset); _futureWatcher.setFuture(rc); } void MainConsole::quit() { emit finished(); } void MainConsole::finishExecution() { //проверяем число ошибок, т.к. при выполнении без транзакции run() всегда возвращает 0 if (_errorCount == 0) { ExitCode rc(_updater->revisionBefore(), _updater->revisionAfter()); message(rc.message()); _exitCode = 0; } else { message(QStringLiteral("Выполнение прервано в результате ошибки.\nБаза данных не изменилась.")); _exitCode = 1; } emit finished(); } ProgramSettings MainConsole::processArguments() { ProgramSettings result; QString USERNAME_VALUE = QStringLiteral("пользователь"); QString PASSWORD_VALUE = QStringLiteral("пароль"); QString DATABASE_VALUE = QStringLiteral("база_данных"); QString HOST_VALUE = QStringLiteral("адрес_сервера"); QString PORT_VALUE = QStringLiteral("номер_порта"); QString FILE_VALUE = QStringLiteral("file.dmv"); QString USERNAME_OPTION = QStringLiteral("U"); QString PASSWORD_OPTION = QStringLiteral("W"); QString DATABASE_OPTION = QStringLiteral("d"); QString DROPDB_OPTION = QStringLiteral("0"); QString HOST_OPTION = QStringLiteral("h"); QString PORT_OPTION = QStringLiteral("p"); QString FILE_OPTION = QStringLiteral("f"); QString HELP_OPTION = QStringLiteral("help"); //обработать аргументы командной строки QCommandLineParser parser; QCommandLineOption helpOption(HELP_OPTION, QStringLiteral("Показать справку") ); parser.addOption(helpOption); QCommandLineOption usernameOption(USERNAME_OPTION, QStringLiteral("Имя пользователя"), USERNAME_VALUE); parser.addOption(usernameOption); QCommandLineOption databaseOption(DATABASE_OPTION, QStringLiteral("Имя базы данных"), DATABASE_VALUE); parser.addOption(databaseOption); QCommandLineOption filenameOption(FILE_OPTION, QStringLiteral("Управляющий файл"), FILE_VALUE); parser.addOption(filenameOption); QCommandLineOption hostOption(HOST_OPTION, QStringLiteral("Адрес сервера"), HOST_VALUE); parser.addOption(hostOption); QCommandLineOption portOption(PORT_OPTION, QStringLiteral("Порт сервера"), PORT_VALUE); parser.addOption(portOption); QCommandLineOption passwordOption(PASSWORD_OPTION, QStringLiteral("Пароль"), PASSWORD_VALUE); parser.addOption(passwordOption); QCommandLineOption dropdbOption(DROPDB_OPTION, QStringLiteral("Сначала удалить базу данных") ); parser.addOption(dropdbOption); parser.addPositionalArgument(FILE_VALUE, QStringLiteral("Управляющий файл"), FILE_VALUE); parser.process(*qApp); //записать прочитанные значения в структуру ProgramSettings result.username = parser.value(usernameOption); result.password = parser.value(passwordOption); result.database = parser.value(databaseOption); result.host = parser.value(hostOption); result.port = parser.value(portOption); result.dropdb = parser.isSet(dropdbOption); if (parser.isSet(filenameOption)) result.controlFile = parser.value(filenameOption); else if (parser.positionalArguments().size() > 0) result.controlFile = parser.positionalArguments().at(0); if (parser.isSet(helpOption)) result.helpText = parser.helpText(); return result; } void MainConsole::error(const QString& a_error) { - static const auto CONSOLE_RED_BEGIN = QByteArray("\x1B[1m\x1B[31m"); - static const auto CONSOLE_RESET = QByteArray("\x1B(B\x1B[m"); + static const char* CONSOLE_RED_BEGIN = "\x1B[1m\x1B[31m"; + static const char* CONSOLE_RESET = "\x1B(B\x1B[m"; + hideProgress(); _os << CONSOLE_RED_BEGIN << QStringLiteral("ОШИБКА") << CONSOLE_RESET << endl; message(a_error); _os << CONSOLE_RESET << endl; ++_errorCount; } void MainConsole::message(const QString& a_message) { + hideProgress(); _os << toConsoleText(a_message) << endl; } +void MainConsole::progress(int a_value) +{ + char bytes[50 + 2 + 1]; + auto begin = std::begin(bytes); + auto end = std::end(bytes) - 1; + *begin++ = '['; + *end-- = '\0'; + *end = ']'; + std::fill(begin, end, '.'); + int done = a_value / 2; + std::fill(begin, begin + done, 'O'); + if (a_value & 1) + bytes[done + 1] = 'o'; + _os << '\r' << bytes << flush; +} + +void MainConsole::hideProgress() +{ + static const char* CONSOLE_ERASE_LINE = "\r\x1B[K"; + _os << CONSOLE_ERASE_LINE << flush; +} + QString MainConsole::toConsoleText(const QString& a_text) { QString result = a_text; static const QString BOLD = QStringLiteral("**"); static const QString CONSOLE_BOLD_BEGIN = QStringLiteral("\x1B[1m"); static const QString CONSOLE_RESET = QStringLiteral("\x1B(B\x1B[m"); static const QString CONSOLE_TAB = QStringLiteral(" "); static const QChar TAB('\t'); bool begin = true; int pos = result.indexOf(BOLD); while (pos >= 0) { result.replace(pos, BOLD.size(), begin ? CONSOLE_BOLD_BEGIN : CONSOLE_RESET); begin = !begin; pos = result.indexOf(BOLD, pos + 1); } if (result.contains(TAB)) result.replace(TAB, CONSOLE_TAB); + if (result.endsWith(QChar('\n'))) + result.chop(1); + return result; } void MainConsole::logConnectionParameters(const QString& a_host, const QString& a_database, const QString& a_username) { message(QStringLiteral("Подключение к базе данных\n" "\tСервер: **%1**\n\tБаза данных: **%2**\n" "\tПользователь: **%3**") .arg(a_host, a_database, a_username)); } void MainConsole::sqlError(const QString& a_dbError, const QString& a_commandDescription, const QString& a_command) { QString errorText; if (!a_commandDescription.isEmpty()) errorText.append(QStringLiteral("**Операция:**\n")).append(a_commandDescription).append("\n\n"); QString dbError = a_dbError; errorText.append(QStringLiteral("**Сообщение об ошибке:**\n")).append(dbError.trimmed()).append("\n\n"); if (!a_command.isEmpty()) errorText.append(QStringLiteral("**Текст команды:**\n")).append(a_command); error(errorText); } diff --git a/src/mainconsole.h b/src/mainconsole.h --- a/src/mainconsole.h +++ b/src/mainconsole.h @@ -1,43 +1,45 @@ #ifndef MAINCLASS_H #define MAINCLASS_H #include #include #include class ProgramSettings; class DatabaseUpdater; class QStringList; class MainConsole : public QObject { Q_OBJECT public: MainConsole(QObject* a_parent = nullptr); ~MainConsole() override; int exitCode() const { return _exitCode; } signals: void finished(); public slots: void run(); void quit(); void finishExecution(); void error(const QString& a_error); void message(const QString& a_message); + void progress(int a_value); void logConnectionParameters(const QString& a_host, const QString& a_database, const QString& a_username); void sqlError(const QString& a_dbError, const QString& a_commandDescription, const QString& a_command); private: int _exitCode; int _errorCount; QTextStream _os; DatabaseUpdater* _updater; QFutureWatcher _futureWatcher; ProgramSettings processArguments(); QString toConsoleText(const QString& a_text); + void hideProgress(); }; #endif // MAINCLASS_H