KNewStuff

resultsstream.cpp
1/*
2 SPDX-FileCopyrightText: 2023 Aleix Pol Gonzalez <aleixpol@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-or-later
5*/
6
7#include "resultsstream.h"
8#include "enginebase_p.h"
9#include "knewstuffcore_debug.h"
10
11#include <iostream>
12
13#include <QLoggingCategory>
14#include <QTimer>
15
16#include "providerbase_p.h"
17#include "providercore.h"
18#include "providercore_p.h"
19#include "searchrequest.h"
20#include "searchrequest_p.h"
21
22using namespace KNSCore;
23
24class KNSCore::ResultsStreamPrivate
25{
26public:
27 QList<QSharedPointer<KNSCore::ProviderCore>> providers;
28 EngineBase const *engine;
29 SearchRequest request;
30 bool finished = false;
31 int queuedFetch = 0;
32};
33
34#if KNEWSTUFFCORE_BUILD_DEPRECATED_SINCE(6, 9)
35ResultsStream::ResultsStream([[maybe_unused]] const Provider::SearchRequest &request, EngineBase *base)
36 : KNSCore::ResultsStream(SearchRequest(), base)
37{
38 // This ctor should not be used. It is private and we don't use. Nobody else should either. Here for ABI stability.
39 Q_ASSERT(false);
40 qCFatal(KNEWSTUFFCORE, "Do not use private constructors!");
41}
42#endif
43
44ResultsStream::ResultsStream(const SearchRequest &request, EngineBase *base)
45 : d(new ResultsStreamPrivate{
46 .providers = base->d->providerCores.values(),
47 .engine = base,
48 .request = request,
49 })
50{
51 auto entriesLoaded = [this](const KNSCore::SearchRequest &request, const KNSCore::Entry::List &entries) {
52 if (request.d != d->request.d) {
53 return;
54 }
55 Q_EMIT entriesFound(entries);
56 };
57
58 auto done = [this](const KNSCore::SearchRequest &request) {
59 if (request.d != d->request.d) {
60 return;
61 }
62
63 qCDebug(KNEWSTUFFCORE) << this << "Finishing" << sender() << request.d->id;
64
65 auto base = qobject_cast<ProviderBase *>(sender());
66 Q_ASSERT_X(base, Q_FUNC_INFO, "Sender failed to cast to ProviderBase");
67 if (const auto coresRemoved = d->providers.removeIf([base](const auto &core) {
68 return core->d->base == base;
69 });
70 coresRemoved <= 0) {
71 qCWarning(KNEWSTUFFCORE) << "Request finished twice, check your provider" << sender() << d->engine;
72
73 Q_ASSERT(false);
74 return;
75 }
76
77 if (d->providers.isEmpty()) {
78 d->finished = true;
79 if (d->queuedFetch > 0) {
80 d->queuedFetch--;
81 fetchMore();
82 return;
83 }
84
85 d->request = {}; // prevent this stream from making more requests
86 d->finished = true;
87 finish();
88 }
89 };
90 auto failed = [this](const KNSCore::SearchRequest &request) {
91 if (request.d == d->request.d) {
92 finish();
93 }
94 };
95
96 auto seenProviders = d->providers;
97 seenProviders.clear();
98 for (const auto &provider : d->providers) {
99 Q_ASSERT(!seenProviders.contains(provider));
100 seenProviders.append(provider);
101
102 connect(provider->d->base, &ProviderBase::entriesLoaded, this, entriesLoaded);
103 connect(provider->d->base, &ProviderBase::loadingDone, this, done);
104 connect(provider->d->base, &ProviderBase::entryDetailsLoaded, this, [this](const KNSCore::Entry &entry) {
105 if (d->request.d->filter == KNSCore::Filter::ExactEntryId && d->request.d->searchTerm == entry.uniqueId()) {
106 if (entry.isValid()) {
107 Q_EMIT entriesFound({entry});
108 }
109 finish();
110 }
111 });
112 connect(provider->d->base, &ProviderBase::loadingFailed, this, failed);
113 }
114}
115
116ResultsStream::~ResultsStream() = default;
117
119{
120 if (d->finished) {
121 Q_ASSERT_X(false, Q_FUNC_INFO, "Called fetch on an already finished stream. Call fetchMore.");
122 return;
123 }
124
125 qCDebug(KNEWSTUFFCORE) << this << "fetching" << d->request;
126 if (d->request.d->filter != Filter::Installed) {
127 // when asking for installed entries, never use the cache
128 Entry::List cacheEntries = d->engine->d->cache->requestFromCache(d->request);
129 if (!cacheEntries.isEmpty()) {
130 Q_EMIT entriesFound(cacheEntries);
131 return;
132 }
133 }
134
135 for (const auto &providerCore : std::as_const(d->providers)) {
136 auto provider = providerCore->d->base;
137 qCDebug(KNEWSTUFFCORE) << this << "loading entries from provider" << provider;
138 if (provider->isInitialized()) {
139 QTimer::singleShot(0, this, [this, provider] {
140 provider->loadEntries(d->request);
141 });
142 } else {
143 connect(provider, &KNSCore::ProviderBase::providerInitialized, this, [this, provider] {
144 disconnect(provider, &KNSCore::ProviderBase::providerInitialized, this, nullptr);
145 provider->loadEntries(d->request);
146 });
147 }
148 }
149}
150
152{
153 // fetchMore requires some extra tinkering but this is worthwhile. By offering a fetchMore we can fully encapsulate
154 // a search state so the caller doesn't have to worry about persisting SearchRequests. Instead we'll do it for them.
155 if (!d->finished) {
156 d->queuedFetch++;
157 return;
158 }
159 d->finished = false;
160 const auto nextPage = d->request.d->page + 1;
161 d->request =
162 SearchRequest(d->request.d->sortMode, d->request.d->filter, d->request.d->searchTerm, d->request.d->categories, nextPage, d->request.d->pageSize);
163 d->providers = d->engine->d->providerCores.values();
164 fetch();
165}
166
167void ResultsStream::finish()
168{
169 Q_EMIT finished();
170 deleteLater();
171}
172
173#include "moc_resultsstream.cpp"
KNewStuff engine.
Definition enginebase.h:56
The ResultsStream is returned by EngineBase::search.
void fetch()
Issues the search, make sure all signals are connected before calling.
void fetchMore()
Increments the requested page and issues another search.
A search request.
QCA_EXPORT ProviderList providers()
bool isEmpty() const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
bool disconnect(const QMetaObject::Connection &connection)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
used to keep track of a search
Definition provider.h:77
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Apr 4 2025 12:04:22 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.