To make it nicer to use outside of HTTP results.
--- /dev/null
+/*
+ * Copyright (C) by Olivier Goffart <ogoffart@woboq.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License
+ * for more details.
+ */
+
+#pragma once
+
+#include "asserts.h"
+
+namespace OCC {
+
+/**
+ * A Result of type T, or an Error
+ */
+template <typename T, typename Error>
+class Result
+{
+ union {
+ T _result;
+ Error _error;
+ };
+ bool _isError;
+
+public:
+ Result(T value)
+ : _result(std::move(value))
+ , _isError(false)
+ {
+ }
+ // TODO: This doesn't work if T and Error are too similar
+ Result(Error error)
+ : _error(std::move(error))
+ , _isError(true)
+ {
+ }
+
+ ~Result()
+ {
+ if (_isError)
+ _error.~Error();
+ else
+ _result.~T();
+ }
+ explicit operator bool() const { return !_isError; }
+ const T &operator*() const &
+ {
+ ASSERT(!_isError);
+ return _result;
+ }
+ T operator*() &&
+ {
+ ASSERT(!_isError);
+ return std::move(_result);
+ }
+ const Error &error() const &
+ {
+ ASSERT(_isError);
+ return _error;
+ }
+ Error error() &&
+ {
+ ASSERT(_isError);
+ return std::move(_error);
+ }
+};
+
+namespace detail {
+struct OptionalNoErrorData{};
+}
+
+template <typename T>
+class Optional : public Result<T, detail::OptionalNoErrorData>
+{
+public:
+ using Result<T, detail::OptionalNoErrorData>::Result;
+
+ Optional()
+ : Optional(detail::OptionalNoErrorData{})
+ {
+ }
+};
+
+} // namespace OCC
// we need to make a request to the server to know that the original file is deleted on the server
_pendingAsyncJobs++;
auto job = new RequestEtagJob(_discoveryData->_account, originalPath, this);
- connect(job, &RequestEtagJob::finishedWithResult, this, [=](const Result<QString> &etag) mutable {
+ connect(job, &RequestEtagJob::finishedWithResult, this, [=](const HttpResult<QString> &etag) mutable {
_pendingAsyncJobs--;
QTimer::singleShot(0, _discoveryData, &DiscoveryPhase::scheduleMoreJobs);
- if (etag.errorCode() != 404 ||
+ if (etag || etag.error().code != 404 ||
// Somehow another item claimed this original path, consider as if it existed
_discoveryData->_renamedItems.contains(originalPath)) {
// If the file exist or if there is another error, consider it is a new file.
if (base.isVirtualFile() && isVfsWithSuffix())
chopVirtualFileSuffix(serverOriginalPath);
auto job = new RequestEtagJob(_discoveryData->_account, serverOriginalPath, this);
- connect(job, &RequestEtagJob::finishedWithResult, this, [=](const Result<QString> &etag) mutable {
+ connect(job, &RequestEtagJob::finishedWithResult, this, [=](const HttpResult<QString> &etag) mutable {
if (!etag || (*etag != base._etag && !item->isDirectory()) || _discoveryData->_renamedItems.contains(originalPath)) {
qCInfo(lcDisco) << "Can't rename because the etag has changed or the directory is gone" << originalPath;
// Can't be a rename, leave it as a new.
if (_localQueryDone)
process();
} else {
- if (results.errorCode() == 403) {
+ if (results.error().code == 403) {
// 403 Forbidden can be sent by the server if the file firewall is active.
// A file or directory should be ignored and sync must continue. See #3490
qCWarning(lcDisco, "Directory access Forbidden (File Firewall?)");
if (_dirItem) {
_dirItem->_instruction = CSYNC_INSTRUCTION_IGNORE;
- _dirItem->_errorString = results.errorMessage();
+ _dirItem->_errorString = results.error().message;
emit finished();
return;
}
- } else if (results.errorCode() == 503) {
+ } else if (results.error().code == 503) {
// The server usually replies with the custom "503 Storage not available"
// if some path is temporarily unavailable. But in some cases a standard 503
// is returned too. Thus we can't distinguish the two and will treat any
qCWarning(lcDisco(), "Storage was not available!");
if (_dirItem) {
_dirItem->_instruction = CSYNC_INSTRUCTION_IGNORE;
- _dirItem->_errorString = results.errorMessage();
+ _dirItem->_errorString = results.error().message;
emit finished();
return;
}
}
emit _discoveryData->fatalError(tr("Server replied with an error while reading directory '%1' : %2")
- .arg(_currentFolder._server, results.errorMessage()));
+ .arg(_currentFolder._server, results.error().message));
}
});
connect(serverJob, &DiscoverySingleDirectoryJob::firstDirectoryPermissions, this,
if (!_ignoredFirst) {
// This is a sanity check, if we haven't _ignoredFirst then it means we never received any directoryListingIteratedSlot
// which means somehow the server XML was bogus
- emit finished({ 0, tr("Server error: PROPFIND reply is not XML formatted!") });
+ emit finished(HttpError{ 0, tr("Server error: PROPFIND reply is not XML formatted!") });
deleteLater();
return;
} else if (!_error.isEmpty()) {
- emit finished({ 0, _error });
+ emit finished(HttpError{ 0, _error });
deleteLater();
return;
}
&& !contentType.contains("application/xml; charset=utf-8")) {
msg = tr("Server error: PROPFIND reply is not XML formatted!");
}
- emit finished({httpCode, msg});
+ emit finished(HttpError{ httpCode, msg });
deleteLater();
}
}
signals:
void firstDirectoryPermissions(RemotePermissions);
void etag(const QString &);
- void finished(const Result<QVector<RemoteInfo>> &result);
+ void finished(const HttpResult<QVector<RemoteInfo>> &result);
private slots:
void directoryListingIteratedSlot(QString, const QMap<QString, QString> &);
void lsJobFinishedWithoutErrorSlot();
emit etagRetrieved(etag);
emit finishedWithResult(etag);
} else {
- emit finishedWithResult({ httpCode, errorString() });
+ emit finishedWithResult(HttpError{ httpCode, errorString() });
}
return true;
}
#include "abstractnetworkjob.h"
-#include "result.h"
+#include "common/result.h"
#include <QBuffer>
#include <QUrlQuery>
namespace OCC {
+struct HttpError
+{
+ int code; // HTTP error code
+ QString message;
+};
+
+template <typename T>
+using HttpResult = Result<T, HttpError>;
+
/**
* @brief The EntityExistsJob class
* @ingroup libsync
signals:
void etagRetrieved(const QString &etag);
- void finishedWithResult(const Result<QString> &etag);
+ void finishedWithResult(const HttpResult<QString> &etag);
private slots:
bool finished() override;
+++ /dev/null
-/*
- * Copyright (C) by Olivier Goffart <ogoffart@woboq.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU 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 General Public License
- * for more details.
- */
-
-#pragma once
-
-namespace OCC {
-
-/**
- * A Result of type T, or an Error that contains a code and a string
- *
- * The code is an HTTP error code.
- **/
-template <typename T>
-class Result
-{
- struct Error
- {
- QString string;
- int code;
- };
- union {
- T _result;
- Error _error;
- };
- bool _isError;
-
-public:
- Result(T value)
- : _result(std::move(value))
- , _isError(false){};
- Result(int code, QString str)
- : _error({ std::move(str), code })
- , _isError(true)
- {
- }
- ~Result()
- {
- if (_isError)
- _error.~Error();
- else
- _result.~T();
- }
- explicit operator bool() const { return !_isError; }
- const T &operator*() const &
- {
- ASSERT(!_isError);
- return _result;
- }
- T operator*() &&
- {
- ASSERT(!_isError);
- return std::move(_result);
- }
- QString errorMessage() const
- {
- ASSERT(_isError);
- return _error.string;
- }
- int errorCode() const
- {
- return _isError ? _error.code : 0;
- }
-};
-
-} // namespace OCC