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 <QTimer>
14
15#include "providerbase_p.h"
16#include "providercore.h"
17#include "providercore_p.h"
18#include "searchrequest.h"
19#include "searchrequest_p.h"
20
21using namespace KNSCore;
22
23class KNSCore::ResultsStreamPrivate
24{
25public:
27 EngineBase const *engine;
28 SearchRequest request;
29 bool finished = false;
30 int queuedFetch = 0;
31};
32
33#if KNEWSTUFFCORE_BUILD_DEPRECATED_SINCE(6, 9)
34ResultsStream::ResultsStream([[maybe_unused]] const Provider::SearchRequest &request, EngineBase *base)
35 : KNSCore::ResultsStream(SearchRequest(), base)
36{
37 // This ctor should not be used. It is private and we don't use. Nobody else should either. Here for ABI stability.
38 Q_ASSERT(false);
39 qFatal("Do not use private constructors!");
40}
41#endif
42
43ResultsStream::ResultsStream(const SearchRequest &request, EngineBase *base)
44 : d(new ResultsStreamPrivate{
45 .providers = base->d->providerCores.values(),
46 .engine = base,
47 .request = request,
48 })
49{
50 auto entriesLoaded = [this](const KNSCore::SearchRequest &request, const KNSCore::Entry::List &entries) {
51 if (request.d != d->request.d) {
52 return;
53 }
54 Q_EMIT entriesFound(entries);
55 };
56
57 auto done = [this](const KNSCore::SearchRequest &request) {
58 if (request.d != d->request.d) {
59 return;
60 }
61
62 qWarning() << this << "Finishing" << sender() << request.d->id;
63
64 auto base = qobject_cast<ProviderBase *>(sender());
65 Q_ASSERT_X(base, Q_FUNC_INFO, "Sender failed to cast to ProviderBase");
66 if (const auto coresRemoved = d->providers.removeIf([base](const auto &core) {
67 return core->d->base == base;
68 });
69 coresRemoved <= 0) {
70 qCWarning(KNEWSTUFFCORE) << "Request finished twice, check your provider" << sender() << d->engine;
71
72 Q_ASSERT(false);
73 return;
74 }
75
76 if (d->providers.isEmpty()) {
77 d->finished = true;
78 if (d->queuedFetch > 0) {
79 d->queuedFetch--;
80 fetchMore();
81 return;
82 }
83
84 d->request = {}; // prevent this stream from making more requests
85 d->finished = true;
86 finish();
87 }
88 };
89 auto failed = [this](const KNSCore::SearchRequest &request) {
90 if (request.d == d->request.d) {
91 finish();
92 }
93 };
94
95 auto seenProviders = d->providers;
96 seenProviders.clear();
97 for (const auto &provider : d->providers) {
98 Q_ASSERT(!seenProviders.contains(provider));
99 seenProviders.append(provider);
100
101 connect(provider->d->base, &ProviderBase::entriesLoaded, this, entriesLoaded);
102 connect(provider->d->base, &ProviderBase::loadingDone, this, done);
103 connect(provider->d->base, &ProviderBase::entryDetailsLoaded, this, [this](const KNSCore::Entry &entry) {
104 if (d->request.d->filter == KNSCore::Filter::ExactEntryId && d->request.d->searchTerm == entry.uniqueId()) {
105 if (entry.isValid()) {
106 Q_EMIT entriesFound({entry});
107 }
108 finish();
109 }
110 });
111 connect(provider->d->base, &ProviderBase::loadingFailed, this, failed);
112 }
113}
114
115ResultsStream::~ResultsStream() = default;
116
118{
119 if (d->finished) {
120 Q_ASSERT_X(false, Q_FUNC_INFO, "Called fetch on an already finished stream. Call fetchMore.");
121 return;
122 }
123
124 qDebug() << this << "fetching" << d->request;
125 if (d->request.d->filter != Filter::Installed) {
126 // when asking for installed entries, never use the cache
127 Entry::List cacheEntries = d->engine->d->cache->requestFromCache(d->request);
128 if (!cacheEntries.isEmpty()) {
129 Q_EMIT entriesFound(cacheEntries);
130 return;
131 }
132 }
133
134 for (const auto &providerCore : std::as_const(d->providers)) {
135 auto provider = providerCore->d->base;
136 qDebug() << this << "loading entries from provider" << provider;
137 if (provider->isInitialized()) {
138 QTimer::singleShot(0, this, [this, provider] {
139 provider->loadEntries(d->request);
140 });
141 } else {
142 connect(provider, &KNSCore::ProviderBase::providerInitialized, this, [this, provider] {
143 disconnect(provider, &KNSCore::ProviderBase::providerInitialized, this, nullptr);
144 provider->loadEntries(d->request);
145 });
146 }
147 }
148}
149
151{
152 // fetchMore requires some extra tinkering but this is worthwhile. By offering a fetchMore we can fully encapsulate
153 // a search state so the caller doesn't have to worry about persisting SearchRequests. Instead we'll do it for them.
154 if (!d->finished) {
155 d->queuedFetch++;
156 return;
157 }
158 d->finished = false;
159 const auto nextPage = d->request.d->page + 1;
160 d->request =
161 SearchRequest(d->request.d->sortMode, d->request.d->filter, d->request.d->searchTerm, d->request.d->categories, nextPage, d->request.d->pageSize);
162 d->providers = d->engine->d->providerCores.values();
163 fetch();
164}
165
166void ResultsStream::finish()
167{
168 Q_EMIT finished();
169 deleteLater();
170}
171
172#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-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:02:29 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.