diff --git a/CMakeLists.txt b/CMakeLists.txt index b24cb0c..c1e3a9a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,65 +1,65 @@ cmake_minimum_required(VERSION 3.0) project(itinerary VERSION 0.0.1) find_package(ECM 5.38 REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_SOURCE_DIR}/cmake) include(KDECompilerSettings NO_POLICY_SCOPE) include(ECMAddTests) include(ECMGenerateHeaders) include(ECMQtDeclareLoggingCategory) include(ECMSetupVersion) include(FeatureSummary) include(KDEInstallDirs) include(KDECMakeSettings) include(GenerateExportHeader) set(CMAKE_CXX_STANDARD 14) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) ecm_setup_version(PROJECT VARIABLE_PREFIX ITINERARY VERSION_HEADER itinerary_version.h) # build-time dependencies -find_package(Qt5 REQUIRED COMPONENTS Test Quick) +find_package(Qt5 5.10 REQUIRED COMPONENTS Test Quick) find_package(Qt5 CONFIG QUIET OPTIONAL_COMPONENTS QuickCompiler) find_package(KF5 REQUIRED COMPONENTS I18n) find_package(KF5Holidays 5.49.0 CONFIG QUIET) find_package(KF5Contacts CONFIG REQUIRED) find_package(KPimPkPass CONFIG REQUIRED) find_package(KPimItinerary 5.9.40 CONFIG REQUIRED) find_package(SharedMimeInfo 1.0 REQUIRED) find_package(ZLIB REQUIRED) set_package_properties("ZLIB" PROPERTIES PURPOSE "Needed for retrieving weather forecast data.") set_package_properties(KF5Solid PROPERTIES TYPE OPTIONAL TYPE RUNTIME PURPOSE "Used for controlling the screen brightness.") include(ECMQMLModules) ecm_find_qmlmodule(org.kde.prison 1.0) if (NOT ANDROID) ecm_find_qmlmodule(Qt.labs.platform 1.0) endif() # runtime dependencies are build-time dependencies on Android if (ANDROID) find_package(Qt5 REQUIRED COMPONENTS AndroidExtras Svg) find_package(KF5 REQUIRED COMPONENTS Archive Kirigami2 Prison) if (NOT DEFINED BREEZEICONS_DIR AND EXISTS ${CMAKE_SOURCE_DIR}/../breeze-icons) set(BREEZEICONS_DIR ${CMAKE_SOURCE_DIR}/../breeze-icons) endif() find_package(OpenSSL REQUIRED) else() find_package(Qt5 REQUIRED COMPONENTS Widgets Positioning DBus) find_package(KF5 REQUIRED COMPONENTS DBusAddons) find_package(KF5 COMPONENTS Solid) endif() add_definitions(-DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII -DQT_NO_URL_CAST_FROM_STRING) add_definitions(-DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT) add_definitions(-DQT_USE_QSTRINGBUILDER) add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x060000) add_subdirectory(src) add_subdirectory(autotests) add_subdirectory(tests) install(FILES org_kde_itinerary.categories DESTINATION ${KDE_INSTALL_CONFDIR}) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/src/publictransport/backends/abstractbackend.cpp b/src/publictransport/backends/abstractbackend.cpp index ad7483e..3ceef5b 100644 --- a/src/publictransport/backends/abstractbackend.cpp +++ b/src/publictransport/backends/abstractbackend.cpp @@ -1,37 +1,37 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "abstractbackend.h" using namespace KPublicTransport; AbstractBackend::AbstractBackend() = default; AbstractBackend::~AbstractBackend() = default; -bool AbstractBackend::queryDeparture(DepartureReply *reply, QNetworkAccessManager *nam) +bool AbstractBackend::queryDeparture(DepartureReply *reply, QNetworkAccessManager *nam) const { Q_UNUSED(reply); Q_UNUSED(nam); return false; } -bool AbstractBackend::queryJourney(JourneyReply *reply, QNetworkAccessManager *nam) +bool AbstractBackend::queryJourney(JourneyReply *reply, QNetworkAccessManager *nam) const { Q_UNUSED(reply); Q_UNUSED(nam); return false; } diff --git a/src/publictransport/backends/abstractbackend.h b/src/publictransport/backends/abstractbackend.h index 9a9e94d..9b8b074 100644 --- a/src/publictransport/backends/abstractbackend.h +++ b/src/publictransport/backends/abstractbackend.h @@ -1,54 +1,66 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef KPUBLICTRANSPORT_ABSTRACTBACKEND_H #define KPUBLICTRANSPORT_ABSTRACTBACKEND_H -#include +#include "reply.h" class QNetworkAccessManager; namespace KPublicTransport { class DepartureReply; class JourneyReply; /** Abstract base class for transport provider backends. */ class AbstractBackend { Q_GADGET public: AbstractBackend(); virtual ~AbstractBackend(); /** Perform a journey query. * @return @c true if performing an async operation, @c false otherwise. */ - virtual bool queryJourney(JourneyReply *reply, QNetworkAccessManager *nam); + virtual bool queryJourney(JourneyReply *reply, QNetworkAccessManager *nam) const; /** Perform a departure query. * @return @c true if performing an async operation, @c false otherwise. */ - virtual bool queryDeparture(DepartureReply *reply, QNetworkAccessManager *nam); + virtual bool queryDeparture(DepartureReply *reply, QNetworkAccessManager *nam) const; + +protected: + /** Helper function to call non-public Reply API. */ + template inline static void addResult(T *reply, Args&&... args) + { + reply->addResult(std::forward(args)...); + } + + inline static void addError(Reply *reply, Reply::Error error, const QString &errorMsg) + { + reply->addError(error, errorMsg); + } private: Q_DISABLE_COPY(AbstractBackend) }; } #endif // KPUBLICTRANSPORT_ABSTRACTBACKEND_H diff --git a/src/publictransport/backends/navitiabackend.cpp b/src/publictransport/backends/navitiabackend.cpp index 5006f3c..d68992e 100644 --- a/src/publictransport/backends/navitiabackend.cpp +++ b/src/publictransport/backends/navitiabackend.cpp @@ -1,32 +1,83 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "navitiabackend.h" +#include "logging.h" +#include "navitiaparser.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include using namespace KPublicTransport; NavitiaBackend::NavitiaBackend() = default; -bool NavitiaBackend::queryDeparture(DepartureReply *reply, QNetworkAccessManager *nam) +bool NavitiaBackend::queryJourney(JourneyReply *reply, QNetworkAccessManager *nam) const { return true; } -bool NavitiaBackend::queryJourney(JourneyReply *reply, QNetworkAccessManager *nam) +bool NavitiaBackend::queryDeparture(DepartureReply *reply, QNetworkAccessManager *nam) const { + const auto req = reply->request(); + QUrl url; + url.setScheme(QStringLiteral("https")); + url.setHost(m_endpoint); + url.setPath( + QStringLiteral("/v1/coverage/") + + (m_coverage.isEmpty() ? QString::number(req.stop().latitude()) + QLatin1Char(';') + QString::number(req.stop().longitude()) : m_coverage) + + QStringLiteral("/coord/") + QString::number(req.stop().latitude()) + QLatin1Char(';') + QString::number(req.stop().longitude()) + + QStringLiteral("/departures") + ); + + QUrlQuery query; + query.addQueryItem(QStringLiteral("from_datetime"), req.dateTime().toString(QStringLiteral("yyyyMMddThhmmss"))); + query.addQueryItem(QStringLiteral("disable_geojson"), QStringLiteral("true")); + query.addQueryItem(QStringLiteral("depth"), QStringLiteral("0")); + url.setQuery(query); + + QNetworkRequest netReq(url); + netReq.setRawHeader("Authorization", m_auth.toUtf8()); + + qCDebug(Log) << "GET:" << url; + auto netReply = nam->get(netReq); + QObject::connect(netReply, &QNetworkReply::finished, [netReply, reply] { + switch (netReply->error()) { + case QNetworkReply::NoError: + addResult(reply, NavitiaParser::parseDepartures(netReply->readAll())); + break; + case QNetworkReply::ContentNotFoundError: + addError(reply, Reply::NotFoundError, NavitiaParser::parseErrorMessage(netReply->readAll())); + break; + default: + addError(reply, Reply::NetworkError, netReply->errorString()); + qCDebug(Log) << netReply->error() << netReply->errorString(); + break; + } + netReply->deleteLater(); + }); + return true; } diff --git a/src/publictransport/backends/navitiabackend.h b/src/publictransport/backends/navitiabackend.h index 98f7c6d..594dad0 100644 --- a/src/publictransport/backends/navitiabackend.h +++ b/src/publictransport/backends/navitiabackend.h @@ -1,49 +1,49 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef KPUBLICTRANSPORT_NAVITIABACKEND_H #define KPUBLICTRANSPORT_NAVITIABACKEND_H #include "abstractbackend.h" #include namespace KPublicTransport { /** Backend for Navitia-based providers. */ class NavitiaBackend : public AbstractBackend { Q_GADGET Q_PROPERTY(QString endpoint MEMBER m_endpoint) Q_PROPERTY(QString coverage MEMBER m_coverage) Q_PROPERTY(QString authorization MEMBER m_auth) public: NavitiaBackend(); - bool queryJourney(JourneyReply *reply, QNetworkAccessManager *nam) override; - bool queryDeparture(DepartureReply *reply, QNetworkAccessManager *nam) override; + bool queryJourney(JourneyReply *reply, QNetworkAccessManager *nam) const override; + bool queryDeparture(DepartureReply *reply, QNetworkAccessManager *nam) const override; private: QString m_endpoint; QString m_coverage; QString m_auth; }; } #endif // KPUBLICTRANSPORT_NAVITIABACKEND_H diff --git a/src/publictransport/departurereply.cpp b/src/publictransport/departurereply.cpp index 6f65755..98ac7d8 100644 --- a/src/publictransport/departurereply.cpp +++ b/src/publictransport/departurereply.cpp @@ -1,80 +1,84 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "departurereply.h" #include "reply_p.h" #include "departurerequest.h" #include "logging.h" -#include "backends/navitiaclient.h" -#include "backends/navitiaparser.h" - #include -#include #include using namespace KPublicTransport; namespace KPublicTransport { class DepartureReplyPrivate : public ReplyPrivate { public: + void finalizeResult() override; + DepartureRequest request; std::vector departures; }; } -DepartureReply::DepartureReply(const DepartureRequest &req, QNetworkAccessManager *nam) +void DepartureReplyPrivate::finalizeResult() +{ + if (departures.empty()) { + return; + } + error = Reply::NoError; + errorMsg.clear(); + + std::sort(departures.begin(), departures.end(), [](const auto &lhs, const auto &rhs) { + return lhs.scheduledTime() < rhs.scheduledTime(); + }); +} + +DepartureReply::DepartureReply(const DepartureRequest &req) : Reply(new DepartureReplyPrivate) { Q_D(DepartureReply); d->request = req; - auto reply = NavitiaClient::queryDeparture(req, nam); - connect(reply, &QNetworkReply::finished, [reply, this] { - Q_D(DepartureReply); - switch (reply->error()) { - case QNetworkReply::NoError: - d->departures = NavitiaParser::parseDepartures(reply->readAll()); - break; - case QNetworkReply::ContentNotFoundError: - d->error = NotFoundError; - d->errorMsg = NavitiaParser::parseErrorMessage(reply->readAll()); - break; - default: - d->error = NetworkError; - d->errorMsg = reply->errorString(); - qCDebug(Log) << reply->error() << reply->errorString(); - } - - emit finished(); - deleteLater(); - }); } DepartureReply::~DepartureReply() = default; DepartureRequest DepartureReply::request() const { Q_D(const DepartureReply); return d->request; } std::vector DepartureReply::departures() const { Q_D(const DepartureReply); return d->departures; // TODO this copies } + +void DepartureReply::addResult(std::vector &&res) +{ + Q_D(DepartureReply); + if (d->departures.empty()) { + d->departures = std::move(res); + } else { + d->departures.insert(d->departures.end(), res.begin(), res.end()); + } + + d->pendingOps--; + d->emitFinishedIfDone(this); +} diff --git a/src/publictransport/departurereply.h b/src/publictransport/departurereply.h index e5cb457..03cc7b9 100644 --- a/src/publictransport/departurereply.h +++ b/src/publictransport/departurereply.h @@ -1,54 +1,57 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef KPUBLICTRANSPORT_DEPARTUREREPLY_H #define KPUBLICTRANSPORT_DEPARTUREREPLY_H #include "reply.h" #include -class QNetworkAccessManager; - namespace KPublicTransport { +class AbstractBackend; class Departure; class DepartureRequest; class DepartureReplyPrivate; /** Departure query reply. */ class DepartureReply : public Reply { Q_OBJECT public: ~DepartureReply(); /** The request this is the reply for. */ DepartureRequest request() const; /** Returns the found departure information. */ std::vector departures() const; private: friend class Manager; - explicit DepartureReply(const DepartureRequest &req, QNetworkAccessManager *nam); + explicit DepartureReply(const DepartureRequest &req); + + friend class AbstractBackend; + void addResult(std::vector &&res); + Q_DECLARE_PRIVATE(DepartureReply) }; } #endif // KPUBLICTRANSPORT_DEPARTUREREPLY_H diff --git a/src/publictransport/journeyreply.cpp b/src/publictransport/journeyreply.cpp index 37c55b1..ca26916 100644 --- a/src/publictransport/journeyreply.cpp +++ b/src/publictransport/journeyreply.cpp @@ -1,115 +1,126 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "journeyreply.h" #include "reply_p.h" #include "journeyrequest.h" #include "logging.h" #include "backends/navitiaclient.h" #include "backends/navitiaparser.h" #include #include #include #include #include using namespace KPublicTransport; namespace KPublicTransport { class JourneyReplyPrivate : public ReplyPrivate { public: + void finalizeResult() override; void postProcessJourneys(); JourneyRequest request; std::vector journeys; }; } +void JourneyReplyPrivate::finalizeResult() +{ + if (journeys.empty()) { + return; + } + + error = Reply::NoError; + errorMsg.clear(); +} + void JourneyReplyPrivate::postProcessJourneys() { // try to fill gaps in timezone data for (auto &journey : journeys) { auto sections = journey.takeSections(); for (auto §ion : sections) { if (section.mode() == JourneySection::Walking) { if (!section.from().timeZone().isValid() && section.to().timeZone().isValid()) { auto from = section.from(); from.setTimeZone(section.to().timeZone()); section.setFrom(from); auto dt = section.departureTime(); dt.setTimeZone(from.timeZone()); section.setDepartureTime(dt); } if (section.from().timeZone().isValid() && !section.to().timeZone().isValid()) { auto to = section.to(); to.setTimeZone(section.from().timeZone()); section.setTo(to); auto dt = section.arrivalTime(); dt.setTimeZone(to.timeZone()); section.setArrivalTime(dt); } } } journey.setSections(std::move(sections)); } } JourneyReply::JourneyReply(const JourneyRequest &req, QNetworkAccessManager *nam) : Reply(new JourneyReplyPrivate) { Q_D(JourneyReply); d->request = req; auto reply = NavitiaClient::findJourney(req, nam); connect(reply, &QNetworkReply::finished, [reply, this] { Q_D(JourneyReply); switch (reply->error()) { case QNetworkReply::NoError: d->journeys = NavitiaParser::parseJourneys(reply->readAll()); d->postProcessJourneys(); break; case QNetworkReply::ContentNotFoundError: d->error = NotFoundError; d->errorMsg = NavitiaParser::parseErrorMessage(reply->readAll()); break; default: d->error = NetworkError; d->errorMsg = reply->errorString(); qCDebug(Log) << reply->error() << reply->errorString(); } emit finished(); deleteLater(); }); } JourneyReply::~JourneyReply() = default; JourneyRequest JourneyReply::request() const { Q_D(const JourneyReply); return d->request; } std::vector JourneyReply::journeys() const { Q_D(const JourneyReply); // TODO avoid the copy here return d->journeys; } diff --git a/src/publictransport/manager.cpp b/src/publictransport/manager.cpp index bfd2dd9..90c1be5 100644 --- a/src/publictransport/manager.cpp +++ b/src/publictransport/manager.cpp @@ -1,129 +1,137 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "manager.h" #include "departurereply.h" #include "journeyreply.h" #include "logging.h" #include "backends/navitiabackend.h" #include #include #include #include #include using namespace KPublicTransport; static inline void initResources() { Q_INIT_RESOURCE(networks); } namespace KPublicTransport { class ManagerPrivate { public: QNetworkAccessManager* nam(); void loadNetworks(); std::unique_ptr loadNetwork(const QJsonObject &obj); template std::unique_ptr loadNetwork(const QJsonObject &obj); QNetworkAccessManager *m_nam = nullptr; std::vector> m_backends; }; } QNetworkAccessManager* ManagerPrivate::nam() { if (!m_nam) { m_nam = new QNetworkAccessManager; } return m_nam; } void ManagerPrivate::loadNetworks() { QDirIterator it(QStringLiteral(":/org.kde.pim/kpublictransport/networks")); while (it.hasNext()) { QFile f(it.next()); if (!f.open(QFile::ReadOnly)) { qCWarning(Log) << "Failed to open public transport network configuration:" << f.errorString(); continue; } const auto doc = QJsonDocument::fromJson(f.readAll()); auto net = loadNetwork(doc.object()); if (net) { m_backends.push_back(std::move(net)); } } qCDebug(Log) << m_backends.size() << "public transport network configurations loaded"; } std::unique_ptr ManagerPrivate::loadNetwork(const QJsonObject &obj) { const auto type = obj.value(QLatin1String("type")).toString(); if (type == QLatin1String("navitia")) { return loadNetwork(obj); } return {}; } static void applyBackendOptions(void *backend, const QMetaObject *mo, const QJsonObject &obj) { const auto opts = obj.value(QLatin1String("options")).toObject(); for (auto it = opts.begin(); it != opts.end(); ++it) { const auto idx = mo->indexOfProperty(it.key().toUtf8()); const auto mp = mo->property(idx); mp.writeOnGadget(backend, it.value().toVariant()); } } template std::unique_ptr ManagerPrivate::loadNetwork(const QJsonObject &obj) { std::unique_ptr backend(new T); applyBackendOptions(backend.get(), &T::staticMetaObject, obj); return backend; } Manager::Manager() : d(new ManagerPrivate) { initResources(); d->loadNetworks(); } Manager::Manager(Manager&&) noexcept = default; Manager::~Manager() = default; void Manager::setNetworkAccessManager(QNetworkAccessManager *nam) { // TODO delete d->nam if we created it ourselves d->m_nam = nam; } JourneyReply* Manager::findJourney(const JourneyRequest &req) const { return new JourneyReply(req, d->nam()); } DepartureReply* Manager::queryDeparture(const DepartureRequest &req) const { - return new DepartureReply(req, d->nam()); + auto reply = new DepartureReply(req); + int pendingOps = 0; + for (const auto &backend : d->m_backends) { + if (backend->queryDeparture(reply, d->nam())) { + ++pendingOps; + } + } + reply->setPendingOps(pendingOps); + return reply; } diff --git a/src/publictransport/reply.cpp b/src/publictransport/reply.cpp index cdf1bfd..fe74926 100644 --- a/src/publictransport/reply.cpp +++ b/src/publictransport/reply.cpp @@ -1,38 +1,64 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "reply.h" #include "reply_p.h" using namespace KPublicTransport; +void ReplyPrivate::emitFinishedIfDone(Reply *q) +{ + if (pendingOps <= 0) { + finalizeResult(); + emit q->finished(); + } +} + Reply::Reply(ReplyPrivate *dd) : d_ptr(dd) { } Reply::~Reply() = default; Reply::Error Reply::error() const { return d_ptr->error; } QString Reply::errorString() const { return d_ptr->errorMsg; } + +void Reply::addError(Reply::Error error, const QString &errorMsg) +{ + d_ptr->error = error; + d_ptr->errorMsg = errorMsg; + d_ptr->pendingOps--; + d_ptr->emitFinishedIfDone(this); +} + +void Reply::setPendingOps(int ops) +{ + Q_ASSERT(d_ptr->pendingOps == -1); + Q_ASSERT(ops >= 0); + d_ptr->pendingOps = ops; + if (ops == 0) { + QMetaObject::invokeMethod(this, &Reply::finished, Qt::QueuedConnection); + } +} diff --git a/src/publictransport/reply.h b/src/publictransport/reply.h index d767ef7..4d9cdec 100644 --- a/src/publictransport/reply.h +++ b/src/publictransport/reply.h @@ -1,62 +1,70 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef KPUBLICTRANSPORT_REPLY_H #define KPUBLICTRANSPORT_REPLY_H #include #include template static inline typename std::unique_ptr::pointer qGetPtrHelper(const std::unique_ptr &p) { return p.get(); } namespace KPublicTransport { +class AbstractBackend; +class Manager; class ReplyPrivate; /** Query response base class. */ class Reply : public QObject { Q_OBJECT public: ~Reply(); /** Error types. */ enum Error { NoError, ///< Nothing went wrong. NetworkError, ///< Error during network operations. NotFoundError, ///< The requested journey/departure/place could not be found. UnknownError ///< Anything else. }; /** Error code. */ Error error() const; /** Textual error message. */ QString errorString() const; Q_SIGNALS: /** Emitted whenever the journey search has been completed. */ void finished(); protected: explicit Reply(ReplyPrivate *dd); std::unique_ptr d_ptr; + + friend class AbstractBackend; + void addError(Error error, const QString &errorMsg); + + friend class Manager; + void setPendingOps(int ops); }; } #endif // KPUBLICTRANSPORT_JOURNEYREPLY_H diff --git a/src/publictransport/reply_p.h b/src/publictransport/reply_p.h index 73cc1d5..558e8ef 100644 --- a/src/publictransport/reply_p.h +++ b/src/publictransport/reply_p.h @@ -1,39 +1,43 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef KPUBLICTRANSPORT_REPLY_P_H #define KPUBLICTRANSPORT_REPLY_P_H #include "reply.h" #include namespace KPublicTransport { class ReplyPrivate { public: virtual ~ReplyPrivate() = default; + virtual void finalizeResult() = 0; + + void emitFinishedIfDone(Reply *q); QString errorMsg; Reply::Error error = Reply::NoError; + int pendingOps = -1; }; } #endif // KPUBLICTRANSPORT_REPLY_P_H