/* * SPDX-FileCopyrightText: 2013 Aleix Pol Gonzalez * * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL */ #include "SnapBackend.h" #include "SnapResource.h" #include "SnapTransaction.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" DISCOVER_BACKEND_PLUGIN(SnapBackend) class SnapSourcesBackend : public AbstractSourcesBackend { public: explicit SnapSourcesBackend(AbstractResourcesBackend *parent) : AbstractSourcesBackend(parent) , m_model(new QStandardItemModel(this)) { auto it = new QStandardItem(i18n("Snap")); it->setData(QStringLiteral("Snap"), IdRole); m_model->appendRow(it); } QAbstractItemModel *sources() override { return m_model; } bool addSource(const QString & /*id*/) override { return false; } bool removeSource(const QString & /*id*/) override { return false; } QString idDescription() override { return QStringLiteral("Snap"); } QVariantList actions() const override { return {}; } bool supportsAdding() const override { return false; } bool canMoveSources() const override { return false; } private: QStandardItemModel *const m_model; }; SnapBackend::SnapBackend(QObject *parent) : AbstractResourcesBackend(parent) , m_updater(new StandardBackendUpdater(this)) , m_reviews(OdrsReviewsBackend::global()) { connect(m_updater, &StandardBackendUpdater::updatesCountChanged, this, &SnapBackend::updatesCountChanged); connect(m_reviews.data(), &OdrsReviewsBackend::ratingsReady, this, [this] { m_reviews->emitRatingFetched(this, kTransform>(m_resources.values(), [](AbstractResource *r) { return r; })); }); // make sure we populate the installed resources first refreshStates(); SourcesModel::global()->addSourcesBackend(new SnapSourcesBackend(this)); m_threadPool.setMaxThreadCount(1); } SnapBackend::~SnapBackend() { Q_EMIT shuttingDown(); m_threadPool.waitForDone(80000); m_threadPool.clear(); } int SnapBackend::updatesCount() const { return m_updater->updatesCount(); } static ResultsStream *voidStream() { return new ResultsStream(QStringLiteral("Snap-void"), {}); } ResultsStream *SnapBackend::search(const AbstractResourcesBackend::Filters &filters) { if (!filters.extends.isEmpty()) { return voidStream(); } else if (!filters.resourceUrl.isEmpty()) { return findResourceByPackageName(filters.resourceUrl); } else if (filters.category && filters.category->isAddons()) { return voidStream(); } else if (filters.state >= AbstractResource::Installed || filters.origin == QLatin1String("Snap")) { std::function &)> f = [filters](const QSharedPointer &s) { return filters.search.isEmpty() || s->name().contains(filters.search, Qt::CaseInsensitive) || s->description().contains(filters.search, Qt::CaseInsensitive); }; return populateWithFilter(m_client.getSnaps(), f); } else if (!filters.search.isEmpty()) { return populate(m_client.find(QSnapdClient::FindFlag::None, filters.search)); } return voidStream(); } ResultsStream *SnapBackend::findResourceByPackageName(const QUrl &search) { Q_ASSERT(!search.host().isEmpty() || !AppStreamUtils::appstreamIds(search).isEmpty()); return search.scheme() == QLatin1String("snap") ? populate(m_client.find(QSnapdClient::MatchName, search.host())) : search.scheme() == QLatin1String("appstream") ? populate(kTransform>(AppStreamUtils::appstreamIds(search), [this](const QString &id) { return m_client.find(QSnapdClient::MatchCommonId, id); })) : voidStream(); } template ResultsStream *SnapBackend::populate(T *job) { return populate(QVector{job}); } template ResultsStream *SnapBackend::populate(const QVector &jobs) { std::function &)> acceptAll = [](const QSharedPointer &) { return true; }; return populateJobsWithFilter(jobs, acceptAll); } template ResultsStream *SnapBackend::populateWithFilter(T *job, std::function &s)> &filter) { return populateJobsWithFilter({job}, filter); } template ResultsStream *SnapBackend::populateJobsWithFilter(const QVector &jobs, std::function &s)> &filter) { auto stream = new ResultsStream(QStringLiteral("Snap-populate")); auto future = QtConcurrent::run(&m_threadPool, [this, jobs]() { for (auto job : jobs) { connect(this, &SnapBackend::shuttingDown, job, &T::cancel); job->runSync(); } }); auto watcher = new QFutureWatcher(this); watcher->setFuture(future); connect(watcher, &QFutureWatcher::finished, watcher, &QObject::deleteLater); connect(watcher, &QFutureWatcher::finished, stream, [this, jobs, filter, stream] { QVector ret; for (auto job : jobs) { job->deleteLater(); if (job->error()) { qDebug() << "error:" << job->error() << job->errorString(); continue; } for (int i = 0, c = job->snapCount(); i < c; ++i) { QSharedPointer snap(job->snap(i)); if (!filter(snap)) continue; const auto snapname = snap->name(); SnapResource *&res = m_resources[snapname]; if (!res) { res = new SnapResource(snap, AbstractResource::None, this); Q_ASSERT(res->packageName() == snapname); } else { res->setSnap(snap); } ret += res; } } if (!ret.isEmpty()) Q_EMIT stream->resourcesFound(ret); stream->finish(); }); return stream; } void SnapBackend::setFetching(bool fetching) { if (m_fetching != fetching) { m_fetching = fetching; Q_EMIT fetchingChanged(); } else { qWarning() << "fetching already on state" << fetching; } } AbstractBackendUpdater *SnapBackend::backendUpdater() const { return m_updater; } AbstractReviewsBackend *SnapBackend::reviewsBackend() const { return m_reviews.data(); } Transaction *SnapBackend::installApplication(AbstractResource *app, const AddonList &addons) { Q_ASSERT(addons.isEmpty()); return installApplication(app); } Transaction *SnapBackend::installApplication(AbstractResource *_app) { auto app = qobject_cast(_app); return new SnapTransaction(&m_client, app, Transaction::InstallRole, AbstractResource::Installed); } Transaction *SnapBackend::removeApplication(AbstractResource *_app) { auto app = qobject_cast(_app); return new SnapTransaction(&m_client, app, Transaction::RemoveRole, AbstractResource::None); } void SnapBackend::checkForUpdates() { auto ret = new StoredResultsStream({populate(m_client.findRefreshable())}); connect(ret, &StoredResultsStream::finishedResources, this, [this](const QVector &resources) { for (SnapResource *res : std::as_const(m_resources)) { bool contained = kContains(resources, [res](const StreamResult &in) { return in.resource == res; }); if (contained) { res->setState(AbstractResource::Upgradeable); res->updateSizes(); } } }); } QString SnapBackend::displayName() const { return QStringLiteral("Snap"); } void SnapBackend::refreshStates() { auto ret = new StoredResultsStream({populate(m_client.getSnaps())}); connect(ret, &StoredResultsStream::finishedResources, this, [this](const QVector &resources) { for (auto res : std::as_const(m_resources)) { bool contained = kContains(resources, [res](const StreamResult &in) { return in.resource == res; }); res->updateSizes(); if (contained) res->setState(AbstractResource::Installed); else res->setState(AbstractResource::None); } checkForUpdates(); }); } #include "SnapBackend.moc" #include "moc_SnapBackend.cpp"