refactor(net): use std::expected for the sink interface (#6071)

This commit is contained in:
Alexandru Ionut Tripon authored and GitHub committed 2026-09-14 08:32:21 +00:00
commit df96e1c76f
13 files changed
+203 -255

No files matched your search

+19 -27
View File
@@ -14,6 +14,8 @@
*/
#include "BaseEntity.h"
#include <expected>
#include <utility>
#include "Exception.h"
#include "FileSystem.h"
@@ -26,55 +28,44 @@
#include "net/NetJob.h"
#include "Application.h"
#include "settings/SettingsObject.h"
#include "BuildConfig.h"
#include "settings/SettingsObject.h"
#include "tasks/Task.h"
namespace Meta {
namespace {
class ParsingValidator : public Net::Validator {
public: /* con/des */
ParsingValidator(BaseEntity* entity) : m_entity(entity) {};
virtual ~ParsingValidator() = default;
explicit ParsingValidator(Meta::BaseEntity* entity) : m_entity(entity) {};
~ParsingValidator() override = default;
public: /* methods */
bool init(QNetworkRequest&) override
{
m_data.clear();
return true;
}
bool write(QByteArray& data) override
{
this->m_data.append(data);
return true;
}
bool abort() override
{
m_data.clear();
return true;
}
bool validate(QNetworkReply&) override
void init() override { m_data.clear(); }
void write(const QByteArray& data) override { this->m_data.append(data); }
void abort() override { m_data.clear(); }
Result validate() override
{
auto fname = m_entity->localFilename();
try {
auto doc = Json::requireDocument(m_data, fname);
auto obj = Json::requireObject(doc, fname);
m_entity->parse(obj);
return true;
return {};
} catch (const Exception& e) {
qWarning() << "Unable to parse response:" << e.cause();
return false;
return std::unexpected<Error>("Unable to parse response:" + e.cause());
}
}
private: /* data */
QByteArray m_data;
BaseEntity* m_entity;
Meta::BaseEntity* m_entity;
};
} // namespace
namespace Meta {
QUrl BaseEntity::url() const
{
auto s = APPLICATION->settings();
auto* s = APPLICATION->settings();
QString metaOverride = s->get("MetaURLOverride").toString();
if (metaOverride.isEmpty()) {
return QUrl(BuildConfig.META_URL).resolved(localFilename());
@@ -99,7 +90,7 @@ bool BaseEntity::isLoaded() const
void BaseEntity::setSha256(QString sha256)
{
m_sha256 = sha256;
m_sha256 = std::move(sha256);
}
BaseEntity::LoadStatus BaseEntity::status() const
@@ -171,8 +162,9 @@ void BaseEntityLoadTask::executeTask()
* The validator parses the file and loads it into the object.
* If that fails, the file is not written to storage.
*/
if (!m_entity->m_sha256.isEmpty())
if (!m_entity->m_sha256.isEmpty()) {
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Algorithm::Sha256, m_entity->m_sha256));
}
dl->addValidator(new ParsingValidator(m_entity));
m_task->addNetAction(dl);
m_task->setAskRetry(false);
+8 -26
View File
@@ -48,41 +48,23 @@ class ByteArraySink : public Sink {
~ByteArraySink() override = default;
public:
auto init(QNetworkRequest& request) -> Task::State override
InitResult init(QNetworkRequest& /*request*/) override
{
m_output.clear();
if (initAllValidators(request)) {
return Task::State::Running;
}
m_fail_reason = "Failed to initialize validators";
return Task::State::Failed;
initAllValidators();
return InitType::Ok;
};
auto write(QByteArray& data) -> Task::State override
Result write(const QByteArray& data) override
{
m_output.append(data);
if (writeAllValidators(data)) {
return Task::State::Running;
}
m_fail_reason = "Failed to write validators";
return Task::State::Failed;
writeAllValidators(data);
return {};
}
auto abort() -> Task::State override
{
failAllValidators();
m_fail_reason = "Aborted";
return Task::State::Failed;
}
void abort() override { failAllValidators(); }
auto finalize(QNetworkReply& reply) -> Task::State override
{
if (finalizeAllValidators(reply)) {
return Task::State::Succeeded;
}
m_fail_reason = "Failed to finalize validators";
return Task::State::Failed;
}
Result finalize(QNetworkReply& /*reply*/) override { return finalizeAllValidators(); }
auto hasLocalData() -> bool override { return false; }
+13 -26
View File
@@ -38,48 +38,35 @@
#include "Validator.h"
#include <QCryptographicHash>
#include <expected>
#include <utility>
namespace Net {
class ChecksumValidator : public Validator {
public:
ChecksumValidator(QCryptographicHash::Algorithm algorithm, QString expectedHex)
ChecksumValidator(QCryptographicHash::Algorithm algorithm, const QString& expectedHex)
: Net::ChecksumValidator(algorithm, QByteArray::fromHex(expectedHex.toLatin1()))
{}
ChecksumValidator(QCryptographicHash::Algorithm algorithm, QByteArray expected = QByteArray())
: m_checksum(algorithm), m_expected(expected) {};
virtual ~ChecksumValidator() = default;
explicit ChecksumValidator(QCryptographicHash::Algorithm algorithm, QByteArray expected = QByteArray())
: m_checksum(algorithm), m_expected(std::move(expected)) {};
~ChecksumValidator() override = default;
public:
auto init(QNetworkRequest&) -> bool override
{
m_checksum.reset();
return true;
}
void init() override { m_checksum.reset(); }
void write(const QByteArray& data) override { m_checksum.addData(data); }
void abort() override { m_checksum.reset(); }
auto write(QByteArray& data) -> bool override
{
m_checksum.addData(data);
return true;
}
auto abort() -> bool override
{
m_checksum.reset();
return true;
}
auto validate(QNetworkReply& reply) -> bool override
Result validate() override
{
if (!m_expected.isEmpty() && m_expected != hash()) {
qWarning() << "Checksum mismatch for URL:" << reply.url().toString() << "expected:" << m_expected << "got:" << hash();
return false;
return std::unexpected<Error>(QString("Checksum mismatch: expected %1, got %2").arg(m_expected.toHex(), hash().toHex()));
}
return true;
return {};
}
auto hash() -> QByteArray { return m_checksum.result(); }
void setExpected(QByteArray expected) { m_expected = expected; }
void setExpected(QByteArray expected) { m_expected = std::move(expected); }
private:
QCryptographicHash m_checksum;
+4 -4
View File
@@ -25,10 +25,10 @@ class DummySink : public Sink {
public:
explicit DummySink() = default;
~DummySink() override = default;
auto init(QNetworkRequest& /*request*/) -> Task::State override { return Task::State::Running; }
auto write(QByteArray& /*data*/) -> Task::State override { return Task::State::Succeeded; }
auto abort() -> Task::State override { return Task::State::AbortedByUser; }
auto finalize(QNetworkReply& /*reply*/) -> Task::State override { return Task::State::Succeeded; }
InitResult init(QNetworkRequest& /*request*/) override { return InitType::Ok; }
Result write(const QByteArray& /*data*/) override { return {}; }
Result finalize(QNetworkReply& /*reply*/) override { return {}; }
void abort() override {}
auto hasLocalData() -> bool override { return false; }
};
+31 -46
View File
@@ -34,6 +34,7 @@
*/
#include "FileSink.h"
#include <expected>
#include "FileSystem.h"
@@ -41,66 +42,61 @@
namespace Net {
Task::State FileSink::init(QNetworkRequest& request)
auto FileSink::init(QNetworkRequest& request) -> InitResult
{
auto result = initCache(request);
if (result != Task::State::Running) {
if (!result || *result != InitType::Ok) {
return result;
}
// create a new save file and open it for writing
if (!FS::ensureFilePathExists(m_filename)) {
qCCritical(taskNetLogC) << "Could not create folder for " + m_filename;
m_fail_reason = "Could not create folder";
return Task::State::Failed;
return std::unexpected<Error>("Could not create folder");
}
m_wroteAnyData = false;
m_output_file.reset(new PSaveFile(m_filename));
if (!m_output_file->open(QIODevice::WriteOnly)) {
const auto error = QString("Could not open %1 for writing: %2").arg(m_filename).arg(m_output_file->errorString());
m_outputFile.reset(new PSaveFile(m_filename));
if (!m_outputFile->open(QIODevice::WriteOnly)) {
const auto error = QString("Could not open %1 for writing: %2").arg(m_filename).arg(m_outputFile->errorString());
qCCritical(taskNetLogC) << error;
m_fail_reason = error;
return Task::State::Failed;
return std::unexpected<Error>(error);
}
if (initAllValidators(request))
return Task::State::Running;
m_fail_reason = "Failed to initialize validators";
return Task::State::Failed;
initAllValidators();
return InitType::Ok;
}
Task::State FileSink::write(QByteArray& data)
auto FileSink::write(const QByteArray& data) -> Result
{
if (!writeAllValidators(data) || m_output_file->write(data) != data.size()) {
writeAllValidators(data);
if (m_outputFile->write(data) != data.size()) {
QString error = QString("Failed writing into %1: %2").arg(m_filename);
if (m_output_file->error() == QFileDevice::NoError) {
if (m_outputFile->error() == QFileDevice::NoError) {
error = error.arg("Validators failed");
} else {
error = error.arg(m_output_file->errorString());
error = error.arg(m_outputFile->errorString());
}
qCCritical(taskNetLogC) << error;
m_fail_reason = error;
m_output_file->cancelWriting();
m_output_file.reset();
m_outputFile->cancelWriting();
m_outputFile.reset();
m_wroteAnyData = false;
return Task::State::Failed;
return std::unexpected<Error>(error);
}
m_wroteAnyData = true;
return Task::State::Running;
return {};
}
Task::State FileSink::abort()
void FileSink::abort()
{
if (m_output_file) {
m_output_file->cancelWriting();
if (m_outputFile) {
m_outputFile->cancelWriting();
}
failAllValidators();
return Task::State::Failed;
}
Task::State FileSink::finalize(QNetworkReply& reply)
auto FileSink::finalize(QNetworkReply& reply) -> Result
{
bool gotFile = false;
QVariant statusCodeV = reply.attribute(QNetworkRequest::HttpStatusCodeAttribute);
@@ -116,37 +112,26 @@ Task::State FileSink::finalize(QNetworkReply& reply)
if (gotFile || m_wroteAnyData) {
// ask validators for data consistency
// we only do this for actual downloads, not 'your data is still the same' cache hits
if (!finalizeAllValidators(reply)) {
m_fail_reason = "Failed to finalize validators";
return Task::State::Failed;
auto result = finalizeAllValidators();
if (!result) {
return result;
}
// nothing went wrong...
if (!m_output_file->commit()) {
const auto error = QString("Failed to commit changes to %1: %2").arg(m_filename).arg(m_output_file->errorString());
if (!m_outputFile->commit()) {
const auto error = QString("Failed to commit changes to %1: %2").arg(m_filename).arg(m_outputFile->errorString());
qCCritical(taskNetLogC) << error;
m_fail_reason = error;
m_output_file->cancelWriting();
return Task::State::Failed;
m_outputFile->cancelWriting();
return std::unexpected<Error>(error);
}
}
// then get rid of the save file
m_output_file.reset();
m_outputFile.reset();
return finalizeCache(reply);
}
Task::State FileSink::initCache(QNetworkRequest&)
{
return Task::State::Running;
}
Task::State FileSink::finalizeCache(QNetworkReply&)
{
return Task::State::Succeeded;
}
bool FileSink::hasLocalData()
{
QFileInfo info(m_filename);
+11 -9
View File
@@ -38,27 +38,29 @@
#include "PSaveFile.h"
#include "Sink.h"
#include <utility>
namespace Net {
class FileSink : public Sink {
public:
FileSink(QString filename) : m_filename(filename) {};
virtual ~FileSink() = default;
explicit FileSink(QString filename) : m_filename(std::move(filename)) {};
~FileSink() override = default;
public:
auto init(QNetworkRequest& request) -> Task::State override;
auto write(QByteArray& data) -> Task::State override;
auto abort() -> Task::State override;
auto finalize(QNetworkReply& reply) -> Task::State override;
InitResult init(QNetworkRequest& request) override;
Result write(const QByteArray& data) override;
Result finalize(QNetworkReply& reply) override;
void abort() override;
auto hasLocalData() -> bool override;
protected:
virtual auto initCache(QNetworkRequest&) -> Task::State;
virtual auto finalizeCache(QNetworkReply& reply) -> Task::State;
virtual InitResult initCache(QNetworkRequest&) { return InitType::Ok; }
virtual Result finalizeCache(QNetworkReply& /*reply*/) { return {}; }
protected:
QString m_filename;
bool m_wroteAnyData = false;
std::unique_ptr<PSaveFile> m_output_file;
std::unique_ptr<PSaveFile> m_outputFile;
};
} // namespace Net
+25 -25
View File
@@ -46,37 +46,37 @@ namespace Net {
/** Maximum time to hold a cache entry
* = 1 week in seconds
*/
#define MAX_TIME_TO_EXPIRE 1 * 7 * 24 * 60 * 60
#define MAX_TIME_TO_EXPIRE (1 * 7 * 24 * 60 * 60)
MetaCacheSink::MetaCacheSink(MetaEntryPtr entry, ChecksumValidator* md5sum, bool is_eternal)
: Net::FileSink(entry->getFullPath()), m_entry(entry), m_md5Node(md5sum), m_is_eternal(is_eternal)
MetaCacheSink::MetaCacheSink(MetaEntryPtr entry, ChecksumValidator* md5sum, bool isEternal)
: Net::FileSink(entry->getFullPath()), m_entry(entry), m_md5Node(md5sum), m_isEternal(isEternal)
{
addValidator(md5sum);
}
Task::State MetaCacheSink::initCache(QNetworkRequest& request)
auto MetaCacheSink::initCache(QNetworkRequest& request) -> InitResult
{
if (!m_entry->isStale()) {
return Task::State::Succeeded;
return InitType::CacheHit;
}
// check if file exists, if it does, use its information for the request
QFile current(m_filename);
if (current.exists() && current.size() != 0) {
if (m_entry->getRemoteChangedTimestamp().size()) {
if (!m_entry->getRemoteChangedTimestamp().isEmpty()) {
request.setRawHeader(QString("If-Modified-Since").toLatin1(), m_entry->getRemoteChangedTimestamp().toLatin1());
}
if (m_entry->getETag().size()) {
if (!m_entry->getETag().isEmpty()) {
request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->getETag().toLatin1());
}
}
return Task::State::Running;
return InitType::Ok;
}
Task::State MetaCacheSink::finalizeCache(QNetworkReply& reply)
auto MetaCacheSink::finalizeCache(QNetworkReply& reply) -> Result
{
QFileInfo output_file_info(m_filename);
QFileInfo outputFileInfo(m_filename);
if (m_wroteAnyData) {
m_entry->setMD5Sum(m_md5Node->hash().toHex().constData());
@@ -88,36 +88,36 @@ Task::State MetaCacheSink::finalizeCache(QNetworkReply& reply)
m_entry->setRemoteChangedTimestamp(reply.rawHeader("Last-Modified").constData());
}
m_entry->setLocalChangedTimestamp(output_file_info.lastModified().toUTC().toMSecsSinceEpoch());
m_entry->setLocalChangedTimestamp(outputFileInfo.lastModified().toUTC().toMSecsSinceEpoch());
{ // Cache lifetime
if (m_is_eternal) {
if (m_isEternal) {
qCDebug(taskMetaCacheLogC) << "Adding eternal cache entry:" << m_entry->getFullPath();
m_entry->makeEternal(true);
} else if (reply.hasRawHeader("Cache-Control")) {
auto cache_control_header = reply.rawHeader("Cache-Control");
qCDebug(taskMetaCacheLogC) << "Parsing 'Cache-Control' header with" << cache_control_header;
auto cacheControlHeader = reply.rawHeader("Cache-Control");
qCDebug(taskMetaCacheLogC) << "Parsing 'Cache-Control' header with" << cacheControlHeader;
static const QRegularExpression s_maxAgeExpr("max-age=([0-9]+)");
qint64 max_age = s_maxAgeExpr.match(cache_control_header).captured(1).toLongLong();
m_entry->setMaximumAge(max_age);
qint64 maxAge = s_maxAgeExpr.match(cacheControlHeader).captured(1).toLongLong();
m_entry->setMaximumAge(maxAge);
} else if (reply.hasRawHeader("Expires")) {
auto expires_header = reply.rawHeader("Expires");
qCDebug(taskMetaCacheLogC) << "Parsing 'Expires' header with" << expires_header;
auto expiresHeader = reply.rawHeader("Expires");
qCDebug(taskMetaCacheLogC) << "Parsing 'Expires' header with" << expiresHeader;
qint64 max_age = QDateTime::fromString(expires_header).toSecsSinceEpoch() - QDateTime::currentSecsSinceEpoch();
m_entry->setMaximumAge(max_age);
qint64 maxAge = QDateTime::fromString(expiresHeader).toSecsSinceEpoch() - QDateTime::currentSecsSinceEpoch();
m_entry->setMaximumAge(maxAge);
} else {
m_entry->setMaximumAge(MAX_TIME_TO_EXPIRE);
}
if (reply.hasRawHeader("Age")) {
auto age_header = reply.rawHeader("Age");
qCDebug(taskMetaCacheLogC) << "Parsing 'Age' header with" << age_header;
auto ageHeader = reply.rawHeader("Age");
qCDebug(taskMetaCacheLogC) << "Parsing 'Age' header with" << ageHeader;
qint64 current_age = age_header.toLongLong();
m_entry->setCurrentAge(current_age);
qint64 currentAge = ageHeader.toLongLong();
m_entry->setCurrentAge(currentAge);
} else {
m_entry->setCurrentAge(0);
}
@@ -126,7 +126,7 @@ Task::State MetaCacheSink::finalizeCache(QNetworkReply& reply)
m_entry->setStale(false);
APPLICATION->metacache()->updateEntry(m_entry);
return Task::State::Succeeded;
return {};
}
bool MetaCacheSink::hasLocalData()
+5 -5
View File
@@ -42,18 +42,18 @@
namespace Net {
class MetaCacheSink : public FileSink {
public:
MetaCacheSink(MetaEntryPtr entry, ChecksumValidator* md5sum, bool is_eternal = false);
virtual ~MetaCacheSink() = default;
MetaCacheSink(MetaEntryPtr entry, ChecksumValidator* md5sum, bool isEternal = false);
~MetaCacheSink() override = default;
auto hasLocalData() -> bool override;
protected:
auto initCache(QNetworkRequest& request) -> Task::State override;
auto finalizeCache(QNetworkReply& reply) -> Task::State override;
InitResult initCache(QNetworkRequest& request) override;
Result finalizeCache(QNetworkReply& reply) override;
private:
MetaEntryPtr m_entry;
ChecksumValidator* m_md5Node;
bool m_is_eternal;
bool m_isEternal;
};
} // namespace Net
+17 -20
View File
@@ -35,28 +35,25 @@ class Sink : public ByteArraySink {
~Sink() override = default;
public:
auto finalize(QNetworkReply& reply) -> Task::State override
Result finalize(QNetworkReply& /*reply*/) override
{
if (finalizeAllValidators(reply)) {
try {
auto result = m_parseFunc(m_output);
if (!result.has_value()) {
m_fail_reason = result.error();
return Task::State::Failed;
}
m_result = *result;
} catch (const std::exception& e) {
m_fail_reason = QString::fromUtf8(e.what());
return Task::State::Failed;
// ToDo: make this suppport QJsonException
} catch (...) {
m_fail_reason = QObject::tr("Unknown error while parsing RPC response");
return Task::State::Failed;
}
return Task::State::Succeeded;
auto validatorResult = finalizeAllValidators();
if (!validatorResult) {
return validatorResult;
}
m_fail_reason = "Failed to finalize validators";
return Task::State::Failed;
try {
auto result = m_parseFunc(m_output);
if (!result.has_value()) {
return std::unexpected(result.error());
}
m_result = *result;
} catch (const std::exception& e) {
return std::unexpected<Error>(QString::fromUtf8(e.what()));
// ToDo: make this suppport QJsonException
} catch (...) {
return std::unexpected<Error>(QObject::tr("Unknown error while parsing RPC response"));
}
return {};
}
T* result() { return &m_result; }
+30 -27
View File
@@ -64,6 +64,7 @@
#include "net/ByteArraySink.h"
#include "net/FileSink.h"
#include "net/Logging.h"
#include "tasks/Task.h"
#include "MMCTime.h"
#include "StringUtils.h"
@@ -139,24 +140,22 @@ void Request::executeTask()
}
QNetworkRequest request(m_url);
m_state = m_sink->init(request);
switch (m_state) {
case State::Succeeded:
qCDebug(m_logCat) << getUid().toString() << "Request cache hit" << m_url.toString();
emit succeeded();
emit finished();
return;
case State::Running:
auto result = m_sink->init(request);
if (!result) {
m_state = Task::State::Failed;
m_failReason = result.error();
emit failed(m_failReason);
emit finished();
return;
}
switch (*result) {
case Sink::InitType::Ok:
qCDebug(m_logCat) << getUid().toString() << "Running" << m_url.toString();
break;
case State::Inactive:
case State::Failed:
m_failReason = m_sink->failReason();
emit failed(m_sink->failReason());
emit finished();
return;
case State::AbortedByUser:
emit aborted();
case Sink::InitType::CacheHit:
m_state = Task::State::Succeeded;
qCDebug(m_logCat) << getUid().toString() << "Request cache hit" << m_url.toString();
emit succeeded();
emit finished();
return;
}
@@ -404,24 +403,26 @@ void Request::downloadFinished()
auto data = m_reply->readAll();
if (!data.isEmpty()) {
qCDebug(m_logCat) << getUid().toString() << "Writing extra" << data.size() << "bytes";
m_state = m_sink->write(data);
if (m_state != State::Succeeded) {
auto result = m_sink->write(data);
if (!result) {
m_state = Task::State::Failed;
qCDebug(m_logCat) << getUid().toString() << "Request failed to write:" << m_url.toString();
m_sink->abort();
m_failReason = m_sink->failReason();
emit failed(m_sink->failReason());
m_failReason = result.error();
emit failed(m_failReason);
emit finished();
return;
}
}
// otherwise, finalize the whole graph
m_state = m_sink->finalize(*m_reply);
if (m_state != State::Succeeded) {
auto result = m_sink->finalize(*m_reply);
if (!result) {
m_state = Task::State::Failed;
qCDebug(m_logCat) << getUid().toString() << "Request failed to finalize:" << m_url.toString();
m_sink->abort();
m_failReason = m_sink->failReason();
emit failed(m_sink->failReason());
m_failReason = result.error();
emit failed(m_failReason);
emit finished();
return;
}
@@ -435,12 +436,14 @@ void Request::downloadReadyRead()
{
if (m_state == State::Running) {
auto data = m_reply->readAll();
m_state = m_sink->write(data);
auto result = m_sink->write(data);
if (replyStatusCode() >= 400) {
m_errorResponse.append(data);
}
if (m_state == State::Failed) {
qCCritical(m_logCat) << getUid().toString() << "Failed to process response chunk:" << m_sink->failReason();
if (!result) {
m_state = Task::State::Failed;
m_failReason = result.error();
qCCritical(m_logCat) << getUid().toString() << "Failed to process response chunk:" << m_failReason;
}
// qDebug() << "Request" << m_url.toString() << "gained" << data.size() << "bytes";
} else {
+2 -4
View File
@@ -53,11 +53,9 @@
#include "EnumWrapper.h"
#include "HeaderProxy.h"
#include "HttpMetaCache.h"
#include "QObjectPtr.h"
#include "Sink.h"
#include "Validator.h"
#include "QObjectPtr.h"
#include "tasks/Task.h"
class QIODevice;
@@ -195,7 +193,7 @@ class Request : public Task {
int m_redirectCount = 0;
HttpMethod m_httpMethod = HttpMethod::Get;
PostData m_postData{};
PostData m_postData;
};
} // namespace Net
+29 -30
View File
@@ -35,8 +35,8 @@
#pragma once
#include <expected>
#include "Validator.h"
#include "tasks/Task.h"
namespace Net {
class Sink {
@@ -44,59 +44,58 @@ class Sink {
Sink() = default;
virtual ~Sink() = default;
using Error = Validator::Error;
using Result = Validator::Result;
enum class InitType : std::uint8_t { Ok, CacheHit };
using InitResult = std::expected<InitType, QString>;
public:
virtual auto init(QNetworkRequest& request) -> Task::State = 0;
virtual auto write(QByteArray& data) -> Task::State = 0;
virtual auto abort() -> Task::State = 0;
virtual auto finalize(QNetworkReply& reply) -> Task::State = 0;
virtual InitResult init(QNetworkRequest& request) = 0;
virtual Result write(const QByteArray& data) = 0;
virtual Result finalize(QNetworkReply& reply) = 0;
virtual void abort() = 0;
virtual auto hasLocalData() -> bool = 0;
QString failReason() const { return m_fail_reason; }
void addValidator(Validator* validator)
{
if (validator) {
validators.push_back(std::shared_ptr<Validator>(validator));
m_validators.push_back(std::shared_ptr<Validator>(validator));
}
}
protected:
bool initAllValidators(QNetworkRequest& request)
void initAllValidators()
{
for (auto& validator : validators) {
if (!validator->init(request))
return false;
for (auto& validator : m_validators) {
validator->init();
}
return true;
}
bool finalizeAllValidators(QNetworkReply& reply)
void writeAllValidators(const QByteArray& data)
{
for (auto& validator : validators) {
if (!validator->validate(reply))
return false;
for (auto& validator : m_validators) {
validator->write(data);
}
return true;
}
bool failAllValidators()
void failAllValidators()
{
bool success = true;
for (auto& validator : validators) {
success &= validator->abort();
for (auto& validator : m_validators) {
validator->abort();
}
return success;
}
bool writeAllValidators(QByteArray& data)
Validator::Result finalizeAllValidators()
{
for (auto& validator : validators) {
if (!validator->write(data))
return false;
for (auto& validator : m_validators) {
auto result = validator->validate();
if (!result) {
return result;
}
}
return true;
return {};
}
protected:
std::vector<std::shared_ptr<Validator>> validators;
QString m_fail_reason;
std::vector<std::shared_ptr<Validator>> m_validators;
};
} // namespace Net
+9 -6
View File
@@ -35,17 +35,20 @@
#pragma once
#include <QNetworkReply>
#include <expected>
namespace Net {
class Validator {
public: /* con/des */
Validator() {}
virtual ~Validator() {}
Validator() = default;
virtual ~Validator() = default;
using Error = QString;
using Result = std::expected<void, Error>;
public: /* methods */
virtual bool init(QNetworkRequest& request) = 0;
virtual bool write(QByteArray& data) = 0;
virtual bool abort() = 0;
virtual bool validate(QNetworkReply& reply) = 0;
virtual void init() = 0;
virtual void write(const QByteArray& data) = 0;
virtual void abort() = 0;
virtual Result validate() = 0;
};
} // namespace Net