File indexing completed on 2025-01-05 04:46:51

0001 /*
0002     SPDX-FileCopyrightText: 2024 g10 Code GmbH
0003     SPDX-FileContributor: Daniel Vrátil <dvratil@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "akonadifull-version.h"
0009 #include "dbmigrator.h"
0010 
0011 #include <KAboutData>
0012 #include <KAboutLicense>
0013 #include <KLocalizedString>
0014 
0015 #include <QCommandLineOption>
0016 #include <QCommandLineParser>
0017 #include <QCoreApplication>
0018 
0019 #include <iostream>
0020 
0021 using namespace Akonadi::Server;
0022 
0023 class CLIDelegate : public UIDelegate
0024 {
0025 public:
0026     Result questionYesNo(const QString &question) override
0027     {
0028         std::cout << qUtf8Printable(question)
0029                   << qUtf8Printable(i18nc("Options for user to input to command line prompt, parenthesis indicate "
0030                                           "which letter to type, capitalized option is default",
0031                                           " [(y)es/(N)o] "));
0032         std::cout.flush();
0033         std::string answer;
0034         std::getline(std::cin, answer);
0035 
0036         const auto yes = i18nc("Letter for option \"(y)es\" prompted from command line", "y");
0037 
0038         if (answer.size() > 0 && std::tolower(answer[0]) == yes[0].toLatin1()) {
0039             return Result::Yes;
0040         }
0041         return Result::No;
0042     }
0043 
0044     Result questionYesNoSkip(const QString &question) override
0045     {
0046         std::cout << qUtf8Printable(question)
0047                   << qUtf8Printable(i18nc("Options for user to input to command line prompt, parenthesis indicate "
0048                                           "which letter to type, capitalized option is default",
0049                                           " [(y)es/(N)o/(s)kip] "));
0050 
0051         std::cout.flush();
0052         std::string answer;
0053         std::getline(std::cin, answer);
0054 
0055         const auto yes = i18nc("Letter for option \"(y)es\" prompted from command line", "y");
0056         const auto skip = i18nc("Letter for option \"(s)kip\" prompted from command line", "s");
0057 
0058         if (answer.size() > 0 && std::tolower(answer[0]) == yes[0].toLatin1()) {
0059             return Result::Yes;
0060         }
0061         if (answer.size() > 0 && std::tolower(answer[0]) == skip[0].toLatin1()) {
0062             return Result::Skip;
0063         }
0064         return Result::No;
0065     }
0066 };
0067 
0068 int main(int argc, char **argv)
0069 {
0070     Q_INIT_RESOURCE(akonadidb);
0071 
0072     QCoreApplication app(argc, argv);
0073     KLocalizedString::setApplicationDomain("akonadi-db-migrator");
0074     KAboutData about(QStringLiteral("akonadi-db-migrator"),
0075                      i18n("Akonadi DB Migrator"),
0076                      QStringLiteral(AKONADI_FULL_VERSION),
0077                      i18n("Akonadi DB Migrator"),
0078                      KAboutLicense::LGPL,
0079                      i18n("(c) 2024 g10 Code GmbH"));
0080     KAboutData::setApplicationData(about);
0081 
0082     QCommandLineParser parser;
0083     about.setupCommandLine(&parser);
0084     const QCommandLineOption engineOpt{QStringLiteral("newengine"),
0085                                        i18n("The new DB engine to use. Possible values are  \"sqlite\", \"mysql\" and \"postgres\""),
0086                                        QStringLiteral("ENGINE")};
0087     parser.addOption(engineOpt);
0088     parser.process(app);
0089 
0090     const auto targetEngine = parser.value(engineOpt).toLower();
0091     if (targetEngine != QLatin1StringView("sqlite") && targetEngine != QLatin1StringView("mysql") && targetEngine != QLatin1StringView("postgres")) {
0092         std::cerr << qUtf8Printable(i18nc("@info:shell", "Invalid target engine: %1.", targetEngine)) << std::endl;
0093         return 1;
0094     }
0095 
0096     CLIDelegate delegate;
0097     DbMigrator migrator(targetEngine, &delegate);
0098     QObject::connect(&migrator, &DbMigrator::info, &app, [](const QString &message) {
0099         std::cout << qUtf8Printable(message) << std::endl;
0100     });
0101     QObject::connect(&migrator, &DbMigrator::error, &app, [](const QString &message) {
0102         std::cerr << qUtf8Printable(message) << std::endl;
0103     });
0104     QObject::connect(&migrator, &DbMigrator::migrationCompleted, &app, [](bool success) {
0105         if (success) {
0106             std::cout << qUtf8Printable(i18nc("@info:status", "Migration completed successfully.")) << std::endl;
0107         } else {
0108             std::cout << qUtf8Printable(i18nc("@info:status", "Migration failed.")) << std::endl;
0109         }
0110         qApp->quit();
0111     });
0112     QObject::connect(&migrator, &DbMigrator::progress, &app, [](const QString &table, int tablesDone, int tablesTotal) {
0113         std::cout << qUtf8Printable(i18nc("@info:progress", "Migrating table %1 (%2/%3)...", table, tablesDone, tablesTotal)) << std::endl;
0114     });
0115     QString lastTable;
0116     int lastPerc = -1;
0117     QObject::connect(&migrator, &DbMigrator::tableProgress, &app, [&lastTable, &lastPerc](const QString &table, int rowsDone, int rowsTotal) {
0118         const int perc = rowsDone * 100 / rowsTotal;
0119         if (lastTable != table) {
0120             lastPerc = -1;
0121         }
0122         if (perc % 10 != 0 || perc == lastPerc) {
0123             return;
0124         }
0125         lastPerc = perc;
0126         lastTable = table;
0127         std::cout << qUtf8Printable(i18nc("@info:progress", "%1%...", perc)) << std::endl;
0128     });
0129 
0130     QMetaObject::invokeMethod(&migrator, &DbMigrator::startMigration, Qt::QueuedConnection);
0131 
0132     return app.exec();
0133 }