Utils: Add std::expected implementation

Adds a std::expected implementation that is compatible with >= C++11.

FilePath::fileContents and FilePath::writeFileContents as well as
FilePath::copyFile are changed to return std::expected.

A couple of macros have been added to aid in using the expected types.

An auto test was added showing how to use the library.

Change-Id: Ibe3aecfc1029a0cf13b45bf5184ff03a04a2393b
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
Marcus Tillmanns
2022-11-24 08:52:47 +01:00
parent 13c7283c0e
commit eca7044361
35 changed files with 2986 additions and 265 deletions

View File

@@ -688,3 +688,15 @@ SQLite (https://www.sqlite.org) is in the Public Domain.
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
### TartanLlama/expected
Implementation of std::expected compatible with C++11/C++14/C++17.
https://github.com/TartanLlama/expected
To the extent possible under law, the author(s) have dedicated all
copyright and related and neighboring rights to this software to the
public domain worldwide. This software is distributed without any warranty.
http://creativecommons.org/publicdomain/zero/1.0/

View File

@@ -988,5 +988,19 @@
SOFTWARE. SOFTWARE.
\endcode \endcode
\li \b TartanLlama/expected
Implementation of std::expected compatible with C++11/C++14/C++17.
The sources can be found in:
\list
\li \c QtCreator/src/libs/3rdparty/tl_expected
\li \l https://github.com/TartanLlama/expected
\endlist
CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
https://creativecommons.org/publicdomain/zero/1.0/
\endlist \endlist
*/ */

121
src/libs/3rdparty/tl_expected/COPYING vendored Normal file
View File

@@ -0,0 +1,121 @@
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.

74
src/libs/3rdparty/tl_expected/README.md vendored Normal file
View File

@@ -0,0 +1,74 @@
# expected
Single header implementation of `std::expected` with functional-style extensions.
[![Documentation Status](https://readthedocs.org/projects/tl-docs/badge/?version=latest)](https://tl.tartanllama.xyz/en/latest/?badge=latest)
Clang + GCC: [![Linux Build Status](https://github.com/TartanLlama/expected/actions/workflows/cmake.yml/badge.svg)](https://github.com/TartanLlama/expected/actions/workflows/cmake.yml)
MSVC: [![Windows Build Status](https://ci.appveyor.com/api/projects/status/k5x00xa11y3s5wsg?svg=true)](https://ci.appveyor.com/project/TartanLlama/expected)
Available on [Vcpkg](https://github.com/microsoft/vcpkg/tree/master/ports/tl-expected) and [Conan](https://github.com/yipdw/conan-tl-expected).
[`std::expected`](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0323r3.pdf) is proposed as the preferred way to represent object which will either have an expected value, or an unexpected value giving information about why something failed. Unfortunately, chaining together many computations which may fail can be verbose, as error-checking code will be mixed in with the actual programming logic. This implementation provides a number of utilities to make coding with `expected` cleaner.
For example, instead of writing this code:
```cpp
std::expected<image,fail_reason> get_cute_cat (const image& img) {
auto cropped = crop_to_cat(img);
if (!cropped) {
return cropped;
}
auto with_tie = add_bow_tie(*cropped);
if (!with_tie) {
return with_tie;
}
auto with_sparkles = make_eyes_sparkle(*with_tie);
if (!with_sparkles) {
return with_sparkles;
}
return add_rainbow(make_smaller(*with_sparkles));
}
```
You can do this:
```cpp
tl::expected<image,fail_reason> get_cute_cat (const image& img) {
return crop_to_cat(img)
.and_then(add_bow_tie)
.and_then(make_eyes_sparkle)
.map(make_smaller)
.map(add_rainbow);
}
```
The interface is the same as `std::expected` as proposed in [p0323r3](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0323r3.pdf), but the following member functions are also defined. Explicit types are for clarity.
- `map`: carries out some operation on the stored object if there is one.
* `tl::expected<std::size_t,std::error_code> s = exp_string.map(&std::string::size);`
- `map_error`: carries out some operation on the unexpected object if there is one.
* `my_error_code translate_error (std::error_code);`
* `tl::expected<int,my_error_code> s = exp_int.map_error(translate_error);`
- `and_then`: like `map`, but for operations which return a `tl::expected`.
* `tl::expected<ast, fail_reason> parse (const std::string& s);`
* `tl::expected<ast, fail_reason> exp_ast = exp_string.and_then(parse);`
- `or_else`: calls some function if there is no value stored.
* `exp.or_else([] { throw std::runtime_error{"oh no"}; });`
### Compiler support
Tested on:
- Linux
* clang++ 3.5, 3.6, 3.7, 3.8, 3.9, 4, 5, 6, 7, 8, 9, 10, 11
* g++ 4.8, 4.9, 5.5, 6.4, 7.5, 8, 9, 10
- Windows
* MSVC 2015, 2017, 2019, 2022
----------
[![CC0](http://i.creativecommons.org/p/zero/1.0/88x31.png)]("http://creativecommons.org/publicdomain/zero/1.0/")
To the extent possible under law, [Sy Brand](https://twitter.com/TartanLlama) has waived all copyright and related or neighboring rights to the `expected` library. This work is published from: United Kingdom.

File diff suppressed because it is too large Load Diff

View File

@@ -875,7 +875,7 @@ static bool findNewQmlLibraryInPath(const Utils::FilePath &path,
} }
// found a new library! // found a new library!
const std::optional<QByteArray> contents = qmldirFile.fileContents(); const expected_str<QByteArray> contents = qmldirFile.fileContents();
if (!contents) if (!contents)
return false; return false;
QString qmldirData = QString::fromUtf8(*contents); QString qmldirData = QString::fromUtf8(*contents);
@@ -985,7 +985,7 @@ void ModelManagerInterface::parseLoop(QSet<Utils::FilePath> &scannedPaths,
contents = entry.first; contents = entry.first;
documentRevision = entry.second; documentRevision = entry.second;
} else { } else {
const std::optional<QByteArray> fileContents = fileName.fileContents(); const expected_str<QByteArray> fileContents = fileName.fileContents();
if (fileContents) { if (fileContents) {
QTextStream ins(*fileContents); QTextStream ins(*fileContents);
contents = ins.readAll(); contents = ins.readAll();

View File

@@ -42,6 +42,7 @@ add_qtc_library(Utils
environmentmodel.cpp environmentmodel.h environmentmodel.cpp environmentmodel.h
execmenu.cpp execmenu.h execmenu.cpp execmenu.h
executeondestruction.h executeondestruction.h
expected.h
fadingindicator.cpp fadingindicator.h fadingindicator.cpp fadingindicator.h
faketooltip.cpp faketooltip.h faketooltip.cpp faketooltip.h
fancylineedit.cpp fancylineedit.h fancylineedit.cpp fancylineedit.h

View File

@@ -6,8 +6,10 @@
#include "algorithm.h" #include "algorithm.h"
#include "commandline.h" #include "commandline.h"
#include "environment.h" #include "environment.h"
#include "expected.h"
#include "hostosinfo.h" #include "hostosinfo.h"
#include "qtcassert.h" #include "qtcassert.h"
#include "utilstr.h"
#include <QCoreApplication> #include <QCoreApplication>
#include <QOperatingSystemVersion> #include <QOperatingSystemVersion>
@@ -18,8 +20,8 @@
#ifdef QTCREATOR_PCH_H #ifdef QTCREATOR_PCH_H
#define CALLBACK WINAPI #define CALLBACK WINAPI
#endif #endif
#include <qt_windows.h>
#include <shlobj.h> #include <shlobj.h>
#include <qt_windows.h>
#else #else
#include <qplatformdefs.h> #include <qplatformdefs.h>
#endif #endif
@@ -137,12 +139,13 @@ bool DeviceFileAccess::removeRecursively(const FilePath &filePath, QString *erro
return false; return false;
} }
bool DeviceFileAccess::copyFile(const FilePath &filePath, const FilePath &target) const expected_str<void> DeviceFileAccess::copyFile(const FilePath &filePath, const FilePath &target) const
{ {
Q_UNUSED(filePath) Q_UNUSED(filePath)
Q_UNUSED(target) Q_UNUSED(target)
QTC_CHECK(false); QTC_CHECK(false);
return false; return make_unexpected(
Tr::tr("copyFile is not implemented for \"%1\"").arg(filePath.toUserOutput()));
} }
bool DeviceFileAccess::renameFile(const FilePath &filePath, const FilePath &target) const bool DeviceFileAccess::renameFile(const FilePath &filePath, const FilePath &target) const
@@ -166,8 +169,7 @@ FilePath DeviceFileAccess::symLinkTarget(const FilePath &filePath) const
return {}; return {};
} }
void DeviceFileAccess::iterateDirectory( void DeviceFileAccess::iterateDirectory(const FilePath &filePath,
const FilePath &filePath,
const FilePath::IterateDirCallback &callBack, const FilePath::IterateDirCallback &callBack,
const FileFilter &filter) const const FileFilter &filter) const
{ {
@@ -177,8 +179,7 @@ void DeviceFileAccess::iterateDirectory(
QTC_CHECK(false); QTC_CHECK(false);
} }
std::optional<QByteArray> DeviceFileAccess::fileContents( expected_str<QByteArray> DeviceFileAccess::fileContents(const FilePath &filePath,
const FilePath &filePath,
qint64 limit, qint64 limit,
qint64 offset) const qint64 offset) const
{ {
@@ -186,11 +187,11 @@ std::optional<QByteArray> DeviceFileAccess::fileContents(
Q_UNUSED(limit) Q_UNUSED(limit)
Q_UNUSED(offset) Q_UNUSED(offset)
QTC_CHECK(false); QTC_CHECK(false);
return {}; return make_unexpected(
Tr::tr("fileContents is not implemented for \"%1\"").arg(filePath.toUserOutput()));
} }
bool DeviceFileAccess::writeFileContents( expected_str<qint64> DeviceFileAccess::writeFileContents(const FilePath &filePath,
const FilePath &filePath,
const QByteArray &data, const QByteArray &data,
qint64 offset) const qint64 offset) const
{ {
@@ -198,7 +199,8 @@ bool DeviceFileAccess::writeFileContents(
Q_UNUSED(data) Q_UNUSED(data)
Q_UNUSED(offset) Q_UNUSED(offset)
QTC_CHECK(false); QTC_CHECK(false);
return false; return make_unexpected(
Tr::tr("writeFileContents is not implemented for \"%1\"").arg(filePath.toUserOutput()));
} }
FilePathInfo DeviceFileAccess::filePathInfo(const FilePath &filePath) const FilePathInfo DeviceFileAccess::filePathInfo(const FilePath &filePath) const
@@ -251,8 +253,7 @@ QByteArray DeviceFileAccess::fileId(const FilePath &filePath) const
} }
std::optional<FilePath> DeviceFileAccess::refersToExecutableFile( std::optional<FilePath> DeviceFileAccess::refersToExecutableFile(
const FilePath &filePath, const FilePath &filePath, FilePath::MatchScope matchScope) const
FilePath::MatchScope matchScope) const
{ {
Q_UNUSED(matchScope) Q_UNUSED(matchScope)
if (isExecutableFile(filePath)) if (isExecutableFile(filePath))
@@ -260,33 +261,29 @@ std::optional<FilePath> DeviceFileAccess::refersToExecutableFile(
return {}; return {};
} }
void DeviceFileAccess::asyncFileContents( void DeviceFileAccess::asyncFileContents(const FilePath &filePath,
const FilePath &filePath, const Continuation<expected_str<QByteArray>> &cont,
const Continuation<std::optional<QByteArray>> &cont,
qint64 limit, qint64 limit,
qint64 offset) const qint64 offset) const
{ {
cont(fileContents(filePath, limit, offset)); cont(fileContents(filePath, limit, offset));
} }
void DeviceFileAccess::asyncWriteFileContents( void DeviceFileAccess::asyncWriteFileContents(const FilePath &filePath,
const FilePath &filePath, const Continuation<expected_str<qint64>> &cont,
const Continuation<bool> &cont,
const QByteArray &data, const QByteArray &data,
qint64 offset) const qint64 offset) const
{ {
cont(writeFileContents(filePath, data, offset)); cont(writeFileContents(filePath, data, offset));
} }
void DeviceFileAccess::asyncCopyFile( void DeviceFileAccess::asyncCopyFile(const FilePath &filePath,
const FilePath &filePath, const Continuation<expected_str<void>> &cont,
const Continuation<bool> &cont,
const FilePath &target) const const FilePath &target) const
{ {
cont(copyFile(filePath, target)); cont(copyFile(filePath, target));
} }
// DesktopDeviceFileAccess // DesktopDeviceFileAccess
DesktopDeviceFileAccess::~DesktopDeviceFileAccess() = default; DesktopDeviceFileAccess::~DesktopDeviceFileAccess() = default;
@@ -303,8 +300,8 @@ bool DesktopDeviceFileAccess::isExecutableFile(const FilePath &filePath) const
return fi.isExecutable() && !fi.isDir(); return fi.isExecutable() && !fi.isDir();
} }
static std::optional<FilePath> isWindowsExecutableHelper static std::optional<FilePath> isWindowsExecutableHelper(const FilePath &filePath,
(const FilePath &filePath, const QStringView suffix) const QStringView suffix)
{ {
const QFileInfo fi(filePath.path().append(suffix)); const QFileInfo fi(filePath.path().append(suffix));
if (!fi.isExecutable() || fi.isDir()) if (!fi.isExecutable() || fi.isDir())
@@ -314,8 +311,7 @@ static std::optional<FilePath> isWindowsExecutableHelper
} }
std::optional<FilePath> DesktopDeviceFileAccess::refersToExecutableFile( std::optional<FilePath> DesktopDeviceFileAccess::refersToExecutableFile(
const FilePath &filePath, const FilePath &filePath, FilePath::MatchScope matchScope) const
FilePath::MatchScope matchScope) const
{ {
if (isExecutableFile(filePath)) if (isExecutableFile(filePath))
return filePath; return filePath;
@@ -444,15 +440,16 @@ bool DesktopDeviceFileAccess::removeRecursively(const FilePath &filePath, QStrin
return false; return false;
} }
const QStringList fileNames = dir.entryList( const QStringList fileNames = dir.entryList(QDir::Files | QDir::Hidden | QDir::System
QDir::Files | QDir::Hidden | QDir::System | QDir::Dirs | QDir::NoDotAndDotDot); | QDir::Dirs | QDir::NoDotAndDotDot);
for (const QString &fileName : fileNames) { for (const QString &fileName : fileNames) {
if (!removeRecursively(filePath / fileName, error)) if (!removeRecursively(filePath / fileName, error))
return false; return false;
} }
if (!QDir::root().rmdir(dir.path())) { if (!QDir::root().rmdir(dir.path())) {
if (error) { if (error) {
*error = QCoreApplication::translate("Utils::FileUtils", "Failed to remove directory \"%1\".") *error = QCoreApplication::translate("Utils::FileUtils",
"Failed to remove directory \"%1\".")
.arg(filePath.toUserOutput()); .arg(filePath.toUserOutput());
} }
return false; return false;
@@ -460,7 +457,8 @@ bool DesktopDeviceFileAccess::removeRecursively(const FilePath &filePath, QStrin
} else { } else {
if (!QFile::remove(filePath.path())) { if (!QFile::remove(filePath.path())) {
if (error) { if (error) {
*error = QCoreApplication::translate("Utils::FileUtils", "Failed to remove file \"%1\".") *error = QCoreApplication::translate("Utils::FileUtils",
"Failed to remove file \"%1\".")
.arg(filePath.toUserOutput()); .arg(filePath.toUserOutput());
} }
return false; return false;
@@ -469,9 +467,14 @@ bool DesktopDeviceFileAccess::removeRecursively(const FilePath &filePath, QStrin
return true; return true;
} }
bool DesktopDeviceFileAccess::copyFile(const FilePath &filePath, const FilePath &target) const expected_str<void> DesktopDeviceFileAccess::copyFile(const FilePath &filePath,
const FilePath &target) const
{ {
return QFile::copy(filePath.path(), target.path()); if (QFile::copy(filePath.path(), target.path()))
return {};
return make_unexpected(Tr::tr("Failed to copy file \"%1\" to \"%2\".")
.arg(filePath.toUserOutput())
.arg(target.toUserOutput()));
} }
bool DesktopDeviceFileAccess::renameFile(const FilePath &filePath, const FilePath &target) const bool DesktopDeviceFileAccess::renameFile(const FilePath &filePath, const FilePath &target) const
@@ -514,8 +517,7 @@ FilePath DesktopDeviceFileAccess::symLinkTarget(const FilePath &filePath) const
return FilePath::fromString(info.symLinkTarget()); return FilePath::fromString(info.symLinkTarget());
} }
void DesktopDeviceFileAccess::iterateDirectory( void DesktopDeviceFileAccess::iterateDirectory(const FilePath &filePath,
const FilePath &filePath,
const FilePath::IterateDirCallback &callBack, const FilePath::IterateDirCallback &callBack,
const FileFilter &filter) const const FileFilter &filter) const
{ {
@@ -532,18 +534,17 @@ void DesktopDeviceFileAccess::iterateDirectory(
} }
} }
std::optional<QByteArray> DesktopDeviceFileAccess::fileContents( expected_str<QByteArray> DesktopDeviceFileAccess::fileContents(const FilePath &filePath,
const FilePath &filePath,
qint64 limit, qint64 limit,
qint64 offset) const qint64 offset) const
{ {
const QString path = filePath.path(); const QString path = filePath.path();
QFile f(path); QFile f(path);
if (!f.exists()) if (!f.exists())
return {}; return make_unexpected(Tr::tr("File \"%1\" does not exist").arg(path));
if (!f.open(QFile::ReadOnly)) if (!f.open(QFile::ReadOnly))
return {}; return make_unexpected(Tr::tr("Could not open File \"%1\"").arg(path));
if (offset != 0) if (offset != 0)
f.seek(offset); f.seek(offset);
@@ -551,21 +552,35 @@ std::optional<QByteArray> DesktopDeviceFileAccess::fileContents(
if (limit != -1) if (limit != -1)
return f.read(limit); return f.read(limit);
return f.readAll(); const QByteArray data = f.readAll();
if (f.error() != QFile::NoError) {
return make_unexpected(
Tr::tr("Cannot read \"%1\": %2").arg(filePath.toUserOutput(), f.errorString()));
}
return data;
} }
bool DesktopDeviceFileAccess::writeFileContents( expected_str<qint64> DesktopDeviceFileAccess::writeFileContents(const FilePath &filePath,
const FilePath &filePath,
const QByteArray &data, const QByteArray &data,
qint64 offset) const qint64 offset) const
{ {
QFile file(filePath.path()); QFile file(filePath.path());
const bool isOpened = file.open(QFile::WriteOnly | QFile::Truncate); const bool isOpened = file.open(QFile::WriteOnly | QFile::Truncate);
QTC_ASSERT(isOpened, return false); if (!isOpened)
return make_unexpected(
Tr::tr("Could not open file \"%1\" for writing").arg(filePath.toUserOutput()));
if (offset != 0) if (offset != 0)
file.seek(offset); file.seek(offset);
qint64 res = file.write(data); qint64 res = file.write(data);
return res == data.size(); if (res != data.size())
return make_unexpected(
Tr::tr("Could not write to file \"%1\" (only %2 of %3 bytes written)")
.arg(filePath.toUserOutput())
.arg(res)
.arg(data.size()));
return res;
} }
QDateTime DesktopDeviceFileAccess::lastModified(const FilePath &filePath) const QDateTime DesktopDeviceFileAccess::lastModified(const FilePath &filePath) const
@@ -603,7 +618,9 @@ static inline QByteArray fileIdWin7(HANDLE handle)
BY_HANDLE_FILE_INFORMATION info; BY_HANDLE_FILE_INFORMATION info;
if (GetFileInformationByHandle(handle, &info)) { if (GetFileInformationByHandle(handle, &info)) {
char buffer[sizeof "01234567:0123456701234567\0"]; char buffer[sizeof "01234567:0123456701234567\0"];
qsnprintf(buffer, sizeof(buffer), "%lx:%08lx%08lx", qsnprintf(buffer,
sizeof(buffer),
"%lx:%08lx%08lx",
info.dwVolumeSerialNumber, info.dwVolumeSerialNumber,
info.nFileIndexHigh, info.nFileIndexHigh,
info.nFileIndexLow); info.nFileIndexLow);
@@ -618,20 +635,25 @@ static QByteArray fileIdWin8(HANDLE handle)
QByteArray result; QByteArray result;
FILE_ID_INFO infoEx; FILE_ID_INFO infoEx;
if (GetFileInformationByHandleEx(handle, if (GetFileInformationByHandleEx(handle,
static_cast<FILE_INFO_BY_HANDLE_CLASS>(18), // FileIdInfo in Windows 8 static_cast<FILE_INFO_BY_HANDLE_CLASS>(
&infoEx, sizeof(FILE_ID_INFO))) { 18), // FileIdInfo in Windows 8
&infoEx,
sizeof(FILE_ID_INFO))) {
result = QByteArray::number(infoEx.VolumeSerialNumber, 16); result = QByteArray::number(infoEx.VolumeSerialNumber, 16);
result += ':'; result += ':';
// Note: MinGW-64's definition of FILE_ID_128 differs from the MSVC one. // Note: MinGW-64's definition of FILE_ID_128 differs from the MSVC one.
result += QByteArray(reinterpret_cast<const char *>(&infoEx.FileId), int(sizeof(infoEx.FileId))).toHex(); result += QByteArray(reinterpret_cast<const char *>(&infoEx.FileId),
int(sizeof(infoEx.FileId)))
.toHex();
} }
return result; return result;
} }
static QByteArray fileIdWin(HANDLE fHandle) static QByteArray fileIdWin(HANDLE fHandle)
{ {
return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8 ? return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8
fileIdWin8(HANDLE(fHandle)) : fileIdWin7(HANDLE(fHandle)); ? fileIdWin8(HANDLE(fHandle))
: fileIdWin7(HANDLE(fHandle));
} }
#endif #endif
@@ -640,10 +662,13 @@ QByteArray DesktopDeviceFileAccess::fileId(const FilePath &filePath) const
QByteArray result; QByteArray result;
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
const HANDLE handle = const HANDLE handle = CreateFile((wchar_t *) filePath.toUserOutput().utf16(),
CreateFile((wchar_t*)filePath.toUserOutput().utf16(), 0, 0,
FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_SHARE_READ,
FILE_FLAG_BACKUP_SEMANTICS, NULL); NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL);
if (handle != INVALID_HANDLE_VALUE) { if (handle != INVALID_HANDLE_VALUE) {
result = fileIdWin(handle); result = fileIdWin(handle);
CloseHandle(handle); CloseHandle(handle);
@@ -668,7 +693,6 @@ OsType DesktopDeviceFileAccess::osType(const FilePath &filePath) const
return HostOsInfo::hostOs(); return HostOsInfo::hostOs();
} }
// UnixDeviceAccess // UnixDeviceAccess
UnixDeviceFileAccess::~UnixDeviceFileAccess() = default; UnixDeviceFileAccess::~UnixDeviceFileAccess() = default;
@@ -769,9 +793,19 @@ bool UnixDeviceFileAccess::removeRecursively(const FilePath &filePath, QString *
return result.exitCode == 0; return result.exitCode == 0;
} }
bool UnixDeviceFileAccess::copyFile(const FilePath &filePath, const FilePath &target) const expected_str<void> UnixDeviceFileAccess::copyFile(const FilePath &filePath,
const FilePath &target) const
{ {
return runInShellSuccess({"cp", {filePath.path(), target.path()}, OsType::OsTypeLinux}); const RunResult result = runInShell(
{"cp", {filePath.path(), target.path()}, OsType::OsTypeLinux});
if (result.exitCode != 0) {
return make_unexpected(Tr::tr("Failed to copy file \"%1\" to \"%2\": %3")
.arg(filePath.toUserOutput())
.arg(target.toUserOutput())
.arg(QString::fromUtf8(result.stdErr)));
}
return {};
} }
bool UnixDeviceFileAccess::renameFile(const FilePath &filePath, const FilePath &target) const bool UnixDeviceFileAccess::renameFile(const FilePath &filePath, const FilePath &target) const
@@ -781,13 +815,13 @@ bool UnixDeviceFileAccess::renameFile(const FilePath &filePath, const FilePath &
FilePath UnixDeviceFileAccess::symLinkTarget(const FilePath &filePath) const FilePath UnixDeviceFileAccess::symLinkTarget(const FilePath &filePath) const
{ {
const RunResult result = runInShell({"readlink", {"-n", "-e", filePath.path()}, OsType::OsTypeLinux}); const RunResult result = runInShell(
{"readlink", {"-n", "-e", filePath.path()}, OsType::OsTypeLinux});
const QString out = QString::fromUtf8(result.stdOut); const QString out = QString::fromUtf8(result.stdOut);
return out.isEmpty() ? FilePath() : filePath.withNewPath(out); return out.isEmpty() ? FilePath() : filePath.withNewPath(out);
} }
std::optional<QByteArray> UnixDeviceFileAccess::fileContents( expected_str<QByteArray> UnixDeviceFileAccess::fileContents(const FilePath &filePath,
const FilePath &filePath,
qint64 limit, qint64 limit,
qint64 offset) const qint64 offset) const
{ {
@@ -802,13 +836,14 @@ std::optional<QByteArray> UnixDeviceFileAccess::fileContents(
const RunResult r = runInShell({"dd", args, OsType::OsTypeLinux}); const RunResult r = runInShell({"dd", args, OsType::OsTypeLinux});
if (r.exitCode != 0) if (r.exitCode != 0)
return {}; return make_unexpected(Tr::tr("Failed reading file \"%1\": %2")
.arg(filePath.toUserOutput())
.arg(QString::fromUtf8(r.stdErr)));
return r.stdOut; return r.stdOut;
} }
bool UnixDeviceFileAccess::writeFileContents( expected_str<qint64> UnixDeviceFileAccess::writeFileContents(const FilePath &filePath,
const FilePath &filePath,
const QByteArray &data, const QByteArray &data,
qint64 offset) const qint64 offset) const
{ {
@@ -817,7 +852,14 @@ bool UnixDeviceFileAccess::writeFileContents(
args.append("bs=1"); args.append("bs=1");
args.append(QString("seek=%1").arg(offset)); args.append(QString("seek=%1").arg(offset));
} }
return runInShellSuccess({"dd", args, OsType::OsTypeLinux}, data); RunResult result = runInShell({"dd", args, OsType::OsTypeLinux}, data);
if (result.exitCode != 0) {
return make_unexpected(Tr::tr("Failed writing file \"%1\": %2")
.arg(filePath.toUserOutput())
.arg(QString::fromUtf8(result.stdErr)));
}
return data.size();
} }
OsType UnixDeviceFileAccess::osType(const FilePath &filePath) const OsType UnixDeviceFileAccess::osType(const FilePath &filePath) const
@@ -828,7 +870,8 @@ OsType UnixDeviceFileAccess::osType(const FilePath &filePath) const
QDateTime UnixDeviceFileAccess::lastModified(const FilePath &filePath) const QDateTime UnixDeviceFileAccess::lastModified(const FilePath &filePath) const
{ {
const RunResult result = runInShell({"stat", {"-L", "-c", "%Y", filePath.path()}, OsType::OsTypeLinux}); const RunResult result = runInShell(
{"stat", {"-L", "-c", "%Y", filePath.path()}, OsType::OsTypeLinux});
qint64 secs = result.stdOut.toLongLong(); qint64 secs = result.stdOut.toLongLong();
const QDateTime dt = QDateTime::fromSecsSinceEpoch(secs, Qt::UTC); const QDateTime dt = QDateTime::fromSecsSinceEpoch(secs, Qt::UTC);
return dt; return dt;
@@ -836,10 +879,13 @@ QDateTime UnixDeviceFileAccess::lastModified(const FilePath &filePath) const
QFile::Permissions UnixDeviceFileAccess::permissions(const FilePath &filePath) const QFile::Permissions UnixDeviceFileAccess::permissions(const FilePath &filePath) const
{ {
const RunResult result = runInShell({"stat", {"-L", "-c", "%a", filePath.path()}, OsType::OsTypeLinux}); const RunResult result = runInShell(
{"stat", {"-L", "-c", "%a", filePath.path()}, OsType::OsTypeLinux});
const uint bits = result.stdOut.toUInt(nullptr, 8); const uint bits = result.stdOut.toUInt(nullptr, 8);
QFileDevice::Permissions perm = {}; QFileDevice::Permissions perm = {};
#define BIT(n, p) if (bits & (1<<n)) perm |= QFileDevice::p #define BIT(n, p) \
if (bits & (1 << n)) \
perm |= QFileDevice::p
BIT(0, ExeOther); BIT(0, ExeOther);
BIT(1, WriteOther); BIT(1, WriteOther);
BIT(2, ReadOther); BIT(2, ReadOther);
@@ -856,12 +902,14 @@ QFile::Permissions UnixDeviceFileAccess::permissions(const FilePath &filePath) c
bool UnixDeviceFileAccess::setPermissions(const FilePath &filePath, QFile::Permissions perms) const bool UnixDeviceFileAccess::setPermissions(const FilePath &filePath, QFile::Permissions perms) const
{ {
const int flags = int(perms); const int flags = int(perms);
return runInShellSuccess({"chmod", {QString::number(flags, 16), filePath.path()}, OsType::OsTypeLinux}); return runInShellSuccess(
{"chmod", {QString::number(flags, 16), filePath.path()}, OsType::OsTypeLinux});
} }
qint64 UnixDeviceFileAccess::fileSize(const FilePath &filePath) const qint64 UnixDeviceFileAccess::fileSize(const FilePath &filePath) const
{ {
const RunResult result = runInShell({"stat", {"-L", "-c", "%s", filePath.path()}, OsType::OsTypeLinux}); const RunResult result = runInShell(
{"stat", {"-L", "-c", "%s", filePath.path()}, OsType::OsTypeLinux});
return result.stdOut.toLongLong(); return result.stdOut.toLongLong();
} }
@@ -873,7 +921,8 @@ qint64 UnixDeviceFileAccess::bytesAvailable(const FilePath &filePath) const
QByteArray UnixDeviceFileAccess::fileId(const FilePath &filePath) const QByteArray UnixDeviceFileAccess::fileId(const FilePath &filePath) const
{ {
const RunResult result = runInShell({"stat", {"-L", "-c", "%D:%i", filePath.path()}, OsType::OsTypeLinux}); const RunResult result = runInShell(
{"stat", {"-L", "-c", "%D:%i", filePath.path()}, OsType::OsTypeLinux});
if (result.exitCode != 0) if (result.exitCode != 0)
return {}; return {};
@@ -882,13 +931,13 @@ QByteArray UnixDeviceFileAccess::fileId(const FilePath &filePath) const
FilePathInfo UnixDeviceFileAccess::filePathInfo(const FilePath &filePath) const FilePathInfo UnixDeviceFileAccess::filePathInfo(const FilePath &filePath) const
{ {
const RunResult stat = runInShell({"stat", {"-L", "-c", "%f %Y %s", filePath.path()}, OsType::OsTypeLinux}); const RunResult stat = runInShell(
{"stat", {"-L", "-c", "%f %Y %s", filePath.path()}, OsType::OsTypeLinux});
return FileUtils::filePathInfoFromTriple(QString::fromLatin1(stat.stdOut)); return FileUtils::filePathInfoFromTriple(QString::fromLatin1(stat.stdOut));
} }
// returns whether 'find' could be used. // returns whether 'find' could be used.
bool UnixDeviceFileAccess::iterateWithFind( bool UnixDeviceFileAccess::iterateWithFind(const FilePath &filePath,
const FilePath &filePath,
const FileFilter &filter, const FileFilter &filter,
const FilePath::IterateDirCallback &callBack) const const FilePath::IterateDirCallback &callBack) const
{ {
@@ -899,7 +948,8 @@ bool UnixDeviceFileAccess::iterateWithFind(
// TODO: Using stat -L will always return the link target, not the link itself. // TODO: Using stat -L will always return the link target, not the link itself.
// We may wan't to add the information that it is a link at some point. // We may wan't to add the information that it is a link at some point.
if (callBack.index() == 1) if (callBack.index() == 1)
cmdLine.addArgs(R"(-exec echo -n \"{}\"" " \; -exec stat -L -c "%f %Y %s" "{}" \;)", CommandLine::Raw); cmdLine.addArgs(R"(-exec echo -n \"{}\"" " \; -exec stat -L -c "%f %Y %s" "{}" \;)",
CommandLine::Raw);
const RunResult result = runInShell(cmdLine); const RunResult result = runInShell(cmdLine);
const QString out = QString::fromUtf8(result.stdOut); const QString out = QString::fromUtf8(result.stdOut);
@@ -951,8 +1001,7 @@ bool UnixDeviceFileAccess::iterateWithFind(
return true; return true;
} }
void UnixDeviceFileAccess::findUsingLs( void UnixDeviceFileAccess::findUsingLs(const QString &current,
const QString &current,
const FileFilter &filter, const FileFilter &filter,
QStringList *found) const QStringList *found) const
{ {
@@ -975,8 +1024,8 @@ static void iterateLsOutput(const FilePath &base,
const FileFilter &filter, const FileFilter &filter,
const FilePath::IterateDirCallback &callBack) const FilePath::IterateDirCallback &callBack)
{ {
const QList<QRegularExpression> nameRegexps = const QList<QRegularExpression> nameRegexps
transform(filter.nameFilters, [](const QString &filter) { = transform(filter.nameFilters, [](const QString &filter) {
QRegularExpression re; QRegularExpression re;
re.setPattern(QRegularExpression::wildcardToRegularExpression(filter)); re.setPattern(QRegularExpression::wildcardToRegularExpression(filter));
QTC_CHECK(re.isValid()); QTC_CHECK(re.isValid());
@@ -1009,8 +1058,7 @@ static void iterateLsOutput(const FilePath &base,
} }
} }
void UnixDeviceFileAccess::iterateDirectory( void UnixDeviceFileAccess::iterateDirectory(const FilePath &filePath,
const FilePath &filePath,
const FilePath::IterateDirCallback &callBack, const FilePath::IterateDirCallback &callBack,
const FileFilter &filter) const const FileFilter &filter) const
{ {
@@ -1019,7 +1067,8 @@ void UnixDeviceFileAccess::iterateDirectory(
if (m_tryUseFind) { if (m_tryUseFind) {
if (iterateWithFind(filePath, filter, callBack)) if (iterateWithFind(filePath, filter, callBack))
return; return;
m_tryUseFind = false; // remember the failure for the next time and use the 'ls' fallback below. m_tryUseFind
= false; // remember the failure for the next time and use the 'ls' fallback below.
} }
// if we do not have find - use ls as fallback // if we do not have find - use ls as fallback
@@ -1028,4 +1077,4 @@ void UnixDeviceFileAccess::iterateDirectory(
iterateLsOutput(filePath, entries, filter, callBack); iterateLsOutput(filePath, entries, filter, callBack);
} }
} // Utils } // namespace Utils

View File

@@ -34,7 +34,7 @@ protected:
virtual bool exists(const FilePath &filePath) const; virtual bool exists(const FilePath &filePath) const;
virtual bool removeFile(const FilePath &filePath) const; virtual bool removeFile(const FilePath &filePath) const;
virtual bool removeRecursively(const FilePath &filePath, QString *error) const; virtual bool removeRecursively(const FilePath &filePath, QString *error) const;
virtual bool copyFile(const FilePath &filePath, const FilePath &target) const; virtual expected_str<void> copyFile(const FilePath &filePath, const FilePath &target) const;
virtual bool renameFile(const FilePath &filePath, const FilePath &target) const; virtual bool renameFile(const FilePath &filePath, const FilePath &target) const;
virtual OsType osType(const FilePath &filePath) const; virtual OsType osType(const FilePath &filePath) const;
@@ -56,30 +56,26 @@ protected:
const FilePath::IterateDirCallback &callBack, const FilePath::IterateDirCallback &callBack,
const FileFilter &filter) const; const FileFilter &filter) const;
virtual std::optional<QByteArray> fileContents( virtual expected_str<QByteArray> fileContents(const FilePath &filePath,
const FilePath &filePath,
qint64 limit,
qint64 offset) const;
virtual bool writeFileContents(
const FilePath &filePath,
const QByteArray &data,
qint64 offset) const;
virtual void asyncFileContents(
const FilePath &filePath,
const Continuation<std::optional<QByteArray>> &cont,
qint64 limit, qint64 limit,
qint64 offset) const; qint64 offset) const;
virtual void asyncWriteFileContents( virtual expected_str<qint64> writeFileContents(const FilePath &filePath,
const FilePath &filePath,
const Continuation<bool> &cont,
const QByteArray &data, const QByteArray &data,
qint64 offset) const; qint64 offset) const;
virtual void asyncCopyFile( virtual void asyncFileContents(const FilePath &filePath,
const FilePath &filePath, const Continuation<expected_str<QByteArray>> &cont,
const Continuation<bool> &cont, qint64 limit,
qint64 offset) const;
virtual void asyncWriteFileContents(const FilePath &filePath,
const Continuation<expected_str<qint64>> &cont,
const QByteArray &data,
qint64 offset) const;
virtual void asyncCopyFile(const FilePath &filePath,
const Continuation<expected_str<void>> &cont,
const FilePath &target) const; const FilePath &target) const;
}; };
@@ -105,7 +101,7 @@ protected:
bool exists(const FilePath &filePath) const override; bool exists(const FilePath &filePath) const override;
bool removeFile(const FilePath &filePath) const override; bool removeFile(const FilePath &filePath) const override;
bool removeRecursively(const FilePath &filePath, QString *error) const override; bool removeRecursively(const FilePath &filePath, QString *error) const override;
bool copyFile(const FilePath &filePath, const FilePath &target) const override; expected_str<void> copyFile(const FilePath &filePath, const FilePath &target) const override;
bool renameFile(const FilePath &filePath, const FilePath &target) const override; bool renameFile(const FilePath &filePath, const FilePath &target) const override;
OsType osType(const FilePath &filePath) const override; OsType osType(const FilePath &filePath) const override;
@@ -127,12 +123,10 @@ protected:
const FilePath::IterateDirCallback &callBack, const FilePath::IterateDirCallback &callBack,
const FileFilter &filter) const override; const FileFilter &filter) const override;
std::optional<QByteArray> fileContents( expected_str<QByteArray> fileContents(const FilePath &filePath,
const FilePath &filePath,
qint64 limit, qint64 limit,
qint64 offset) const override; qint64 offset) const override;
bool writeFileContents( expected_str<qint64> writeFileContents(const FilePath &filePath,
const FilePath &filePath,
const QByteArray &data, const QByteArray &data,
qint64 offset) const override; qint64 offset) const override;
}; };
@@ -160,7 +154,7 @@ protected:
bool exists(const FilePath &filePath) const override; bool exists(const FilePath &filePath) const override;
bool removeFile(const FilePath &filePath) const override; bool removeFile(const FilePath &filePath) const override;
bool removeRecursively(const FilePath &filePath, QString *error) const override; bool removeRecursively(const FilePath &filePath, QString *error) const override;
bool copyFile(const FilePath &filePath, const FilePath &target) const override; expected_str<void> copyFile(const FilePath &filePath, const FilePath &target) const override;
bool renameFile(const FilePath &filePath, const FilePath &target) const override; bool renameFile(const FilePath &filePath, const FilePath &target) const override;
FilePathInfo filePathInfo(const FilePath &filePath) const override; FilePathInfo filePathInfo(const FilePath &filePath) const override;
@@ -178,12 +172,10 @@ protected:
const FilePath::IterateDirCallback &callBack, const FilePath::IterateDirCallback &callBack,
const FileFilter &filter) const override; const FileFilter &filter) const override;
std::optional<QByteArray> fileContents( expected_str<QByteArray> fileContents(const FilePath &filePath,
const FilePath &filePath,
qint64 limit, qint64 limit,
qint64 offset) const override; qint64 offset) const override;
bool writeFileContents( expected_str<qint64> writeFileContents(const FilePath &filePath,
const FilePath &filePath,
const QByteArray &data, const QByteArray &data,
qint64 offset) const override; qint64 offset) const override;

View File

@@ -3,6 +3,7 @@
#include "elfreader.h" #include "elfreader.h"
#include "expected.h"
#include "qtcassert.h" #include "qtcassert.h"
#include <QDir> #include <QDir>
@@ -85,7 +86,9 @@ ElfMapper::ElfMapper(const ElfReader *reader)
bool ElfMapper::map() bool ElfMapper::map()
{ {
if (binary.needsDevice()) { if (binary.needsDevice()) {
raw = binary.fileContents().value_or(QByteArray()); const expected_str<QByteArray> contents = binary.fileContents();
QTC_CHECK(contents);
raw = contents.value_or(QByteArray());
start = raw.constData(); start = raw.constData();
fdlen = raw.size(); fdlen = raw.size();
return fdlen > 0; return fdlen > 0;

47
src/libs/utils/expected.h Normal file
View File

@@ -0,0 +1,47 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#pragma once
#include "qtcassert.h"
#include "../3rdparty/tl_expected/include/tl/expected.hpp"
namespace Utils {
template<class T, class E>
using expected = tl::expected<T, E>;
template<class T>
using expected_str = tl::expected<T, QString>;
template<class E>
using unexpected = tl::unexpected<E>;
using unexpect_t = tl::unexpect_t;
static constexpr unexpect_t unexpect{};
template<class E>
constexpr unexpected<std::decay_t<E>> make_unexpected(E &&e)
{
return tl::make_unexpected(e);
}
} // namespace Utils
//! If 'expected' has an error the error will be printed and the 'action' will be executed.
#define QTC_ASSERT_EXPECTED(expected, action) \
{ \
if (Q_LIKELY(expected)) { \
} else { \
::Utils::writeAssertLocation(QString("%1:%2: %3") \
.arg(__FILE__) \
.arg(__LINE__) \
.arg(expected.error()) \
.toUtf8() \
.data()); \
action; \
} \
do { \
} while (0); \
}

View File

@@ -9,15 +9,17 @@
#include "fileutils.h" #include "fileutils.h"
#include "hostosinfo.h" #include "hostosinfo.h"
#include "qtcassert.h" #include "qtcassert.h"
#include "utilstr.h"
#include <QtGlobal> #include <QByteArray>
#include <QDateTime> #include <QDateTime>
#include <QDebug> #include <QDebug>
#include <QDirIterator> #include <QDirIterator>
#include <QFileInfo> #include <QFileInfo>
#include <QRegularExpression> #include <QRegularExpression>
#include <QUrl>
#include <QStringView> #include <QStringView>
#include <QUrl>
#include <QtGlobal>
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#ifdef QTCREATOR_PCH_H #ifdef QTCREATOR_PCH_H
@@ -494,7 +496,7 @@ void FilePath::iterateDirectories(const FilePaths &dirs,
dir.iterateDirectory(callBack, filter); dir.iterateDirectory(callBack, filter);
} }
std::optional<QByteArray> FilePath::fileContents(qint64 maxSize, qint64 offset) const expected_str<QByteArray> FilePath::fileContents(qint64 maxSize, qint64 offset) const
{ {
return fileAccess()->fileContents(*this, maxSize, offset); return fileAccess()->fileContents(*this, maxSize, offset);
} }
@@ -510,15 +512,14 @@ bool FilePath::ensureReachable(const FilePath &other) const
return false; return false;
} }
void FilePath::asyncFileContents( void FilePath::asyncFileContents(const Continuation<const expected_str<QByteArray> &> &cont,
const Continuation<const std::optional<QByteArray> &> &cont,
qint64 maxSize, qint64 maxSize,
qint64 offset) const qint64 offset) const
{ {
return fileAccess()->asyncFileContents(*this, cont, maxSize, offset); return fileAccess()->asyncFileContents(*this, cont, maxSize, offset);
} }
bool FilePath::writeFileContents(const QByteArray &data, qint64 offset) const expected_str<qint64> FilePath::writeFileContents(const QByteArray &data, qint64 offset) const
{ {
return fileAccess()->writeFileContents(*this, data, offset); return fileAccess()->writeFileContents(*this, data, offset);
} }
@@ -528,8 +529,7 @@ FilePathInfo FilePath::filePathInfo() const
return fileAccess()->filePathInfo(*this); return fileAccess()->filePathInfo(*this);
} }
void FilePath::asyncWriteFileContents( void FilePath::asyncWriteFileContents(const Continuation<const expected_str<qint64> &> &cont,
const Continuation<bool> &cont,
const QByteArray &data, const QByteArray &data,
qint64 offset) const qint64 offset) const
{ {
@@ -1315,33 +1315,47 @@ bool FilePath::removeRecursively(QString *error) const
return fileAccess()->removeRecursively(*this, error); return fileAccess()->removeRecursively(*this, error);
} }
bool FilePath::copyFile(const FilePath &target) const expected_str<void> FilePath::copyFile(const FilePath &target) const
{ {
if (host() != target.host()) { if (host() != target.host()) {
// FIXME: This does not scale. // FIXME: This does not scale.
const std::optional<QByteArray> ba = fileContents(); const expected_str<QByteArray> contents = fileContents();
if (!ba) if (!contents) {
return false; return make_unexpected(
const auto perms = permissions(); Tr::tr("Error while trying to copy file: %1").arg(contents.error()));
if (!target.writeFileContents(*ba)) }
return false;
const QFile::Permissions perms = permissions();
const expected_str<qint64> copyResult = target.writeFileContents(*contents);
if (!copyResult)
return make_unexpected(Tr::tr("Could not copy file: %1").arg(copyResult.error()));
if (!target.setPermissions(perms)) { if (!target.setPermissions(perms)) {
target.removeFile(); target.removeFile();
return false; return make_unexpected(
Tr::tr("Could not set permissions on \"%1\"").arg(target.toString()));
} }
return true; return {};
} }
return fileAccess()->copyFile(*this, target); return fileAccess()->copyFile(*this, target);
} }
void FilePath::asyncCopyFile(const std::function<void(bool)> &cont, const FilePath &target) const void FilePath::asyncCopyFile(const Continuation<const expected_str<void> &> &cont,
const FilePath &target) const
{ {
if (host() != target.host()) { if (host() != target.host()) {
asyncFileContents([cont, target](const std::optional<QByteArray> &ba) { asyncFileContents([cont, target](const expected_str<QByteArray> &contents) {
if (ba) if (contents)
target.asyncWriteFileContents(cont, *ba); target.asyncWriteFileContents(
[cont](const expected_str<qint64> &result) {
if (result)
cont({});
else
cont(make_unexpected(result.error()));
},
*contents);
}); });
return; return;
} }

View File

@@ -5,6 +5,7 @@
#include "utils_global.h" #include "utils_global.h"
#include "expected.h"
#include "filepathinfo.h" #include "filepathinfo.h"
#include "osspecificaspects.h" #include "osspecificaspects.h"
@@ -119,15 +120,15 @@ public:
OsType osType() const; OsType osType() const;
bool removeFile() const; bool removeFile() const;
bool removeRecursively(QString *error = nullptr) const; bool removeRecursively(QString *error = nullptr) const;
bool copyFile(const FilePath &target) const; expected_str<void> copyFile(const FilePath &target) const;
bool renameFile(const FilePath &target) const; bool renameFile(const FilePath &target) const;
qint64 fileSize() const; qint64 fileSize() const;
qint64 bytesAvailable() const; qint64 bytesAvailable() const;
bool createDir() const; bool createDir() const;
FilePaths dirEntries(const FileFilter &filter, QDir::SortFlags sort = QDir::NoSort) const; FilePaths dirEntries(const FileFilter &filter, QDir::SortFlags sort = QDir::NoSort) const;
FilePaths dirEntries(QDir::Filters filters) const; FilePaths dirEntries(QDir::Filters filters) const;
std::optional<QByteArray> fileContents(qint64 maxSize = -1, qint64 offset = 0) const; expected_str<QByteArray> fileContents(qint64 maxSize = -1, qint64 offset = 0) const;
bool writeFileContents(const QByteArray &data, qint64 offset = 0) const; expected_str<qint64> writeFileContents(const QByteArray &data, qint64 offset = 0) const;
FilePathInfo filePathInfo() const; FilePathInfo filePathInfo() const;
bool operator==(const FilePath &other) const; bool operator==(const FilePath &other) const;
@@ -198,11 +199,12 @@ public:
static void sort(FilePaths &files); static void sort(FilePaths &files);
// Asynchronous interface // Asynchronous interface
void asyncCopyFile(const Continuation<bool> &cont, const FilePath &target) const; void asyncCopyFile(const Continuation<const expected_str<void> &> &cont,
void asyncFileContents(const Continuation<const std::optional<QByteArray> &> &cont, const FilePath &target) const;
void asyncFileContents(const Continuation<const expected_str<QByteArray> &> &cont,
qint64 maxSize = -1, qint64 maxSize = -1,
qint64 offset = 0) const; qint64 offset = 0) const;
void asyncWriteFileContents(const Continuation<bool> &cont, void asyncWriteFileContents(const Continuation<const expected_str<qint64> &> &cont,
const QByteArray &data, const QByteArray &data,
qint64 offset = 0) const; qint64 offset = 0) const;

View File

@@ -55,7 +55,7 @@ static bool getFileContent(const FilePath &filePath,
if (fileToContentsMap.contains(filePath)) { if (fileToContentsMap.contains(filePath)) {
*tempString = fileToContentsMap.value(filePath); *tempString = fileToContentsMap.value(filePath);
} else { } else {
const std::optional<QByteArray> content = filePath.fileContents(); const expected_str<QByteArray> content = filePath.fileContents();
if (!content) if (!content)
return false; return false;
*tempString = QTC_GUARD(encoding) ? encoding->toUnicode(*content) *tempString = QTC_GUARD(encoding) ? encoding->toUnicode(*content)

View File

@@ -55,29 +55,13 @@ bool FileReader::fetch(const FilePath &filePath, QIODevice::OpenMode mode)
{ {
QTC_ASSERT(!(mode & ~(QIODevice::ReadOnly | QIODevice::Text)), return false); QTC_ASSERT(!(mode & ~(QIODevice::ReadOnly | QIODevice::Text)), return false);
if (filePath.needsDevice()) { const expected_str<QByteArray> contents = filePath.fileContents();
const std::optional<QByteArray> contents = filePath.fileContents();
if (!contents) { if (!contents) {
m_errorString = tr("Cannot read %1").arg(filePath.toUserOutput()); m_errorString = contents.error();
return false; return false;
} }
m_data = *contents; m_data = *contents;
return true; return true;
}
QFile file(filePath.toString());
if (!file.open(QIODevice::ReadOnly | mode)) {
m_errorString = tr("Cannot open %1 for reading: %2").arg(
filePath.toUserOutput(), file.errorString());
return false;
}
m_data = file.readAll();
if (file.error() != QFile::NoError) {
m_errorString = tr("Cannot read %1: %2").arg(
filePath.toUserOutput(), file.errorString());
return false;
}
return true;
} }
bool FileReader::fetch(const FilePath &filePath, QIODevice::OpenMode mode, QString *errorString) bool FileReader::fetch(const FilePath &filePath, QIODevice::OpenMode mode, QString *errorString)
@@ -225,10 +209,10 @@ bool FileSaver::finalize()
m_file->close(); m_file->close();
m_file->open(QIODevice::ReadOnly); m_file->open(QIODevice::ReadOnly);
const QByteArray data = m_file->readAll(); const QByteArray data = m_file->readAll();
const bool res = m_filePath.writeFileContents(data); const expected_str<qint64> res = m_filePath.writeFileContents(data);
m_file->remove(); m_file->remove();
m_file.reset(); m_file.reset();
return res; return res.has_value();
} }
if (!m_isSafe) if (!m_isSafe)
@@ -703,15 +687,20 @@ bool FileUtils::copyIfDifferent(const FilePath &srcFilePath, const FilePath &tgt
const QDateTime srcModified = srcFilePath.lastModified(); const QDateTime srcModified = srcFilePath.lastModified();
const QDateTime tgtModified = tgtFilePath.lastModified(); const QDateTime tgtModified = tgtFilePath.lastModified();
if (srcModified == tgtModified) { if (srcModified == tgtModified) {
const std::optional<QByteArray> srcContents = srcFilePath.fileContents(); // TODO: Create FilePath::hashFromContents() and compare hashes.
const std::optional<QByteArray> tgtContents = srcFilePath.fileContents(); const expected_str<QByteArray> srcContents = srcFilePath.fileContents();
if (srcContents == tgtContents) const expected_str<QByteArray> tgtContents = srcFilePath.fileContents();
if (srcContents && srcContents == tgtContents)
return true; return true;
} }
tgtFilePath.removeFile(); tgtFilePath.removeFile();
} }
return srcFilePath.copyFile(tgtFilePath); const expected_str<void> copyResult = srcFilePath.copyFile(tgtFilePath);
// TODO forward error to caller instead of assert, since IO errors can always be expected
QTC_ASSERT_EXPECTED(copyResult, return false);
return true;
} }
QString FileUtils::fileSystemFriendlyName(const QString &name) QString FileUtils::fileSystemFriendlyName(const QString &name)

View File

@@ -4,6 +4,7 @@
#include "fsengine_impl.h" #include "fsengine_impl.h"
#include "diriterator.h" #include "diriterator.h"
#include "expected.h"
#include "filepathinfocache.h" #include "filepathinfocache.h"
#include "../filepath.h" #include "../filepath.h"
@@ -62,8 +63,11 @@ bool FSEngineImpl::open(QIODevice::OpenMode openMode)
return false; return false;
if (read || append) { if (read || append) {
const std::optional<QByteArray> contents = m_filePath.fileContents(); const expected_str<QByteArray> readResult = m_filePath.fileContents();
QTC_ASSERT(contents && m_tempStorage->write(*contents) >= 0, return false); QTC_ASSERT_EXPECTED(readResult, return false);
const expected_str<qint64> writeResult = m_tempStorage->write(*readResult);
QTC_ASSERT_EXPECTED(writeResult, return false);
if (!append) if (!append)
m_tempStorage->seek(0); m_tempStorage->seek(0);
@@ -133,7 +137,9 @@ bool FSEngineImpl::remove()
bool FSEngineImpl::copy(const QString &newName) bool FSEngineImpl::copy(const QString &newName)
{ {
return m_filePath.copyFile(FilePath::fromString(newName)); expected_str<void> result = m_filePath.copyFile(FilePath::fromString(newName));
QTC_ASSERT_EXPECTED(result, return false);
return true;
} }
bool FSEngineImpl::rename(const QString &newName) bool FSEngineImpl::rename(const QString &newName)

View File

@@ -680,7 +680,9 @@ static bool copyFileIfNewer(const FilePath &sourceFilePath,
if (!destinationFilePath.parentDir().ensureWritableDir()) if (!destinationFilePath.parentDir().ensureWritableDir())
return false; return false;
return sourceFilePath.copyFile(destinationFilePath); expected_str<void> result = sourceFilePath.copyFile(destinationFilePath);
QTC_ASSERT_EXPECTED(result, return false);
return true;
} }
void AndroidBuildApkStep::doRun() void AndroidBuildApkStep::doRun()

View File

@@ -75,7 +75,7 @@ static QJsonDocument readJsonFile(const FilePath &filePath)
qCDebug(cmakeFileApi) << "readJsonFile:" << filePath; qCDebug(cmakeFileApi) << "readJsonFile:" << filePath;
QTC_ASSERT(!filePath.isEmpty(), return {}); QTC_ASSERT(!filePath.isEmpty(), return {});
const std::optional<QByteArray> contents = filePath.fileContents(); const expected_str<QByteArray> contents = filePath.fileContents();
if (!contents) if (!contents)
return {}; return {};
const QJsonDocument doc = QJsonDocument::fromJson(*contents); const QJsonDocument doc = QJsonDocument::fromJson(*contents);

View File

@@ -2,10 +2,11 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include "presetsparser.h" #include "presetsparser.h"
#include "utils/algorithm.h"
#include "cmakeprojectmanagertr.h" #include "cmakeprojectmanagertr.h"
#include <utils/algorithm.h>
#include <QJsonArray> #include <QJsonArray>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
@@ -391,7 +392,7 @@ const PresetsData &PresetsParser::presetsData() const
bool PresetsParser::parse(const Utils::FilePath &jsonFile, QString &errorMessage, int &errorLine) bool PresetsParser::parse(const Utils::FilePath &jsonFile, QString &errorMessage, int &errorLine)
{ {
const std::optional<QByteArray> jsonContents = jsonFile.fileContents(); const Utils::expected_str<QByteArray> jsonContents = jsonFile.fileContents();
if (!jsonContents) { if (!jsonContents) {
errorMessage = Tr::tr("Failed to read %1 file").arg(jsonFile.fileName()); errorMessage = Tr::tr("Failed to read %1 file").arg(jsonFile.fileName());
return false; return false;

View File

@@ -652,7 +652,7 @@ void LoggingViewManagerWidget::loadAndUpdateFromPreset()
if (fp.isEmpty()) if (fp.isEmpty())
return; return;
// read file, update categories // read file, update categories
const std::optional<QByteArray> contents = fp.fileContents(); const Utils::expected_str<QByteArray> contents = fp.fileContents();
if (!contents) { if (!contents) {
QMessageBox::critical(ICore::dialogParent(), QMessageBox::critical(ICore::dialogParent(),
tr("Error"), tr("Error"),

View File

@@ -136,9 +136,8 @@ bool CppToolsJsExtension::hasQObjectParent(const QString &klassName) const
const WorkingCopy workingCopy = CppModelManager::instance()->workingCopy(); const WorkingCopy workingCopy = CppModelManager::instance()->workingCopy();
QByteArray source = workingCopy.source(item->filePath()); QByteArray source = workingCopy.source(item->filePath());
if (source.isEmpty()) { if (source.isEmpty()) {
std::optional<QByteArray> contents = item->filePath().fileContents(); const Utils::expected_str<QByteArray> contents = item->filePath().fileContents();
if (!contents) QTC_ASSERT_EXPECTED(contents, return false);
return false;
source = *contents; source = *contents;
} }
const auto doc = snapshot.preprocessedDocument(source, item->filePath()); const auto doc = snapshot.preprocessedDocument(source, item->filePath());

View File

@@ -15,12 +15,7 @@
#include <ctype.h> #include <ctype.h>
#include <utils/processhandle.h> #include <utils/processhandle.h>
#include <utils/qtcassert.h>
#define QTC_ASSERT_STRINGIFY_HELPER(x) #x
#define QTC_ASSERT_STRINGIFY(x) QTC_ASSERT_STRINGIFY_HELPER(x)
#define QTC_ASSERT_STRING(cond) qDebug("SOFT ASSERT: \"" cond"\" in file " __FILE__ ", line " QTC_ASSERT_STRINGIFY(__LINE__))
#define QTC_ASSERT(cond, action) if (cond) {} else { QTC_ASSERT_STRING(#cond); action; } do {} while (0)
#define QTC_CHECK(cond) if (cond) {} else { QTC_ASSERT_STRING(#cond); } do {} while (0)
namespace Debugger::Internal { namespace Debugger::Internal {

View File

@@ -129,7 +129,7 @@ static QList<GitLabServer> readTokensFile(const Utils::FilePath &filePath)
{ {
if (!filePath.exists()) if (!filePath.exists())
return {}; return {};
std::optional<QByteArray> contents = filePath.fileContents(); const Utils::expected_str<QByteArray> contents = filePath.fileContents();
if (!contents) if (!contents)
return {}; return {};
const QByteArray content = *contents; const QByteArray content = *contents;

View File

@@ -16,6 +16,7 @@
#include <texteditor/texteditorconstants.h> #include <texteditor/texteditorconstants.h>
#include <texteditor/fontsettings.h> #include <texteditor/fontsettings.h>
#include <utils/expected.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/qtcprocess.h> #include <utils/qtcprocess.h>
#include <utils/runextensions.h> #include <utils/runextensions.h>
@@ -98,7 +99,9 @@ ExtraCompiler::ExtraCompiler(const Project *project, const FilePath &source,
if (!d->compileTime.isValid() || d->compileTime > lastModified) if (!d->compileTime.isValid() || d->compileTime > lastModified)
d->compileTime = lastModified; d->compileTime = lastModified;
if (const std::optional<QByteArray> contents = target.fileContents()) const expected_str<QByteArray> contents = target.fileContents();
QTC_ASSERT_EXPECTED(contents, return);
setContent(target, *contents); setContent(target, *contents);
} }
} }
@@ -169,11 +172,12 @@ void ExtraCompiler::onTargetsBuilt(Project *project)
if (d->compileTime >= generateTime) if (d->compileTime >= generateTime)
return; return;
if (const std::optional<QByteArray> contents = target.fileContents()) { const expected_str<QByteArray> contents = target.fileContents();
QTC_ASSERT_EXPECTED(contents, return);
d->compileTime = generateTime; d->compileTime = generateTime;
setContent(target, *contents); setContent(target, *contents);
} }
}
}); });
} }

View File

@@ -224,7 +224,7 @@ void ContentLibraryBundleImporter::handleImportTimer()
QVariantHash ContentLibraryBundleImporter::loadAssetRefMap(const Utils::FilePath &bundlePath) QVariantHash ContentLibraryBundleImporter::loadAssetRefMap(const Utils::FilePath &bundlePath)
{ {
FilePath assetRefPath = bundlePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_ASSET_REF_FILE)); FilePath assetRefPath = bundlePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_ASSET_REF_FILE));
const std::optional<QByteArray> content = assetRefPath.fileContents(); const Utils::expected_str<QByteArray> content = assetRefPath.fileContents();
if (content) { if (content) {
QJsonParseError error; QJsonParseError error;
QJsonDocument bundleDataJsonDoc = QJsonDocument::fromJson(*content, &error); QJsonDocument bundleDataJsonDoc = QJsonDocument::fromJson(*content, &error);
@@ -266,7 +266,7 @@ QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile)
removedFiles.append(qmlFile); removedFiles.append(qmlFile);
FilePath qmldirPath = bundleImportPath.resolvePath(QStringLiteral("qmldir")); FilePath qmldirPath = bundleImportPath.resolvePath(QStringLiteral("qmldir"));
const std::optional<QByteArray> qmldirContent = qmldirPath.fileContents(); const expected_str<QByteArray> qmldirContent = qmldirPath.fileContents();
QByteArray newContent; QByteArray newContent;
QString qmlType = qmlFilePath.baseName(); QString qmlType = qmlFilePath.baseName();

View File

@@ -144,7 +144,9 @@ bool ScriptHelper::writeScriptFile(const Utils::FilePath &outScriptFile,
for (const QByteArray &line : functionFooter(m_language)) for (const QByteArray &line : functionFooter(m_language))
data.append(line).append('\n'); data.append(line).append('\n');
return outScriptFile.writeFileContents(data); const Utils::expected_str<qint64> result = outScriptFile.writeFileContents(data);
QTC_ASSERT_EXPECTED(result, return false);
return true;
} }
} // namespace Internal } // namespace Internal

View File

@@ -179,8 +179,8 @@ static bool copyScriptTemplates(const SuiteConf &suiteConf, const Utils::FilePat
const Utils::FilePath test = scripts.pathAppended(testStr + extension); const Utils::FilePath test = scripts.pathAppended(testStr + extension);
const Utils::FilePath testFile = destination.pathAppended("test" + extension); const Utils::FilePath testFile = destination.pathAppended("test" + extension);
QTC_ASSERT(!testFile.exists(), return false); QTC_ASSERT(!testFile.exists(), return false);
ok = test.copyFile(testFile); const Utils::expected_str<void> result = test.copyFile(testFile);
QTC_ASSERT(ok, return false); QTC_ASSERT_EXPECTED(result, return false)
if (scripted) if (scripted)
ok = suiteConf.ensureObjectMapExists(); ok = suiteConf.ensureObjectMapExists();

View File

@@ -80,7 +80,7 @@ static QMap<QString, QString> readSuiteConfContent(const Utils::FilePath &file)
if (!file.isReadableFile()) if (!file.isReadableFile())
return {}; return {};
std::optional<QByteArray> suiteConfContent = file.fileContents(); const Utils::expected_str<QByteArray> suiteConfContent = file.fileContents();
if (!suiteConfContent) if (!suiteConfContent)
return {}; return {};
@@ -112,7 +112,9 @@ static bool writeSuiteConfContent(const Utils::FilePath &file, const QMap<QStrin
else else
outData.append(it.key().toUtf8()).append('=').append(it.value().toUtf8()).append('\n'); outData.append(it.key().toUtf8()).append('=').append(it.value().toUtf8()).append('\n');
} }
return file.writeFileContents(outData); const Utils::expected_str<qint64> result = file.writeFileContents(outData);
QTC_ASSERT_EXPECTED(result, return false);
return true;
} }
bool SuiteConf::read() bool SuiteConf::read()
@@ -306,9 +308,9 @@ bool SuiteConf::ensureObjectMapExists() const
const Utils::FilePath objectMap = scripts.pathAppended("objectmap_template" + extension); const Utils::FilePath objectMap = scripts.pathAppended("objectmap_template" + extension);
bool ok = destinationObjectMap.parentDir().ensureWritableDir(); bool ok = destinationObjectMap.parentDir().ensureWritableDir();
QTC_ASSERT(ok, return false); QTC_ASSERT(ok, return false);
ok = objectMap.copyFile(destinationObjectMap); const Utils::expected_str<void> result = objectMap.copyFile(destinationObjectMap);
QTC_ASSERT(ok, return false); QTC_ASSERT_EXPECTED(result, return false);
return ok; return true;
} }
} // namespace Internal } // namespace Internal

View File

@@ -256,9 +256,8 @@ void CallgrindToolRunner::triggerParse()
m_hostOutputFile = FilePath::fromString(dataFile.fileName()); m_hostOutputFile = FilePath::fromString(dataFile.fileName());
} }
const auto afterCopy = [this](bool res) { const auto afterCopy = [this](expected_str<void> res) {
QTC_CHECK(res); QTC_ASSERT_EXPECTED(res, return);
QTC_ASSERT(m_hostOutputFile.exists(), return);
showStatusMessage(Tr::tr("Parsing Profile Data...")); showStatusMessage(Tr::tr("Parsing Profile Data..."));
m_parser.parse(m_hostOutputFile); m_parser.parse(m_hostOutputFile);
}; };

View File

@@ -2,6 +2,7 @@ add_subdirectory(ansiescapecodehandler)
add_subdirectory(asynctask) add_subdirectory(asynctask)
add_subdirectory(commandline) add_subdirectory(commandline)
add_subdirectory(deviceshell) add_subdirectory(deviceshell)
add_subdirectory(expected)
add_subdirectory(fileutils) add_subdirectory(fileutils)
add_subdirectory(fsengine) add_subdirectory(fsengine)
add_subdirectory(fuzzymatcher) add_subdirectory(fuzzymatcher)

View File

@@ -0,0 +1,4 @@
add_qtc_test(tst_utils_expected
DEPENDS Utils
SOURCES tst_expected.cpp
)

View File

@@ -0,0 +1,7 @@
import qbs
QtcAutotest {
name: "Expected autotest"
Depends { name: "Utils" }
files: "tst_expected.cpp"
}

View File

@@ -0,0 +1,38 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include <QtTest/qtestcase.h>
#include <QtTest>
#include <utils/expected.h>
#include <utils/filepath.h>
using namespace Utils;
class tst_expected : public QObject
{
Q_OBJECT
private slots:
void initTestCase() {}
void tryMonads()
{
FilePath p = "idontexists.ne";
auto result = p.fileContents()
.and_then([](auto) { return expected_str<QByteArray>{}; })
.or_else([](auto error) {
return expected_str<QByteArray>(
make_unexpected(QString("Error: " + error)));
})
.transform_or([](auto error) -> QString {
return QString(QString("More Info: ") + error);
});
QVERIFY(!result);
}
};
QTEST_GUILESS_MAIN(tst_expected)
#include "tst_expected.moc"

View File

@@ -1,9 +1,9 @@
// Copyright (C) 2016 The Qt Company Ltd. // Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include <QtTest>
#include <QDebug> #include <QDebug>
#include <QRandomGenerator> #include <QRandomGenerator>
#include <QtTest>
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <utils/hostosinfo.h> #include <utils/hostosinfo.h>
@@ -13,12 +13,12 @@
using namespace Utils; using namespace Utils;
namespace QTest { namespace QTest {
template<> template<>
char *toString(const FilePath &filePath) char *toString(const FilePath &filePath)
{ {
return qstrdup(filePath.toString().toLocal8Bit().constData()); return qstrdup(filePath.toString().toLocal8Bit().constData());
}
} }
} // namespace QTest
class tst_fileutils : public QObject class tst_fileutils : public QObject
{ {
@@ -1027,7 +1027,7 @@ void tst_fileutils::asyncLocalCopy()
const FilePath orig = FilePath::fromString(rootPath).pathAppended("x/y/fileToCopy.txt"); const FilePath orig = FilePath::fromString(rootPath).pathAppended("x/y/fileToCopy.txt");
QVERIFY(orig.exists()); QVERIFY(orig.exists());
const FilePath dest = FilePath::fromString(rootPath).pathAppended("x/fileToCopyDest.txt"); const FilePath dest = FilePath::fromString(rootPath).pathAppended("x/fileToCopyDest.txt");
auto afterCopy = [&orig, &dest, this] (bool result) { auto afterCopy = [&orig, &dest, this](expected_str<void> result) {
QVERIFY(result); QVERIFY(result);
// check existence, size and content // check existence, size and content
QVERIFY(dest.exists()); QVERIFY(dest.exists());

View File

@@ -7,6 +7,7 @@ Project {
"asynctask/asynctask.qbs", "asynctask/asynctask.qbs",
"commandline/commandline.qbs", "commandline/commandline.qbs",
"deviceshell/deviceshell.qbs", "deviceshell/deviceshell.qbs",
"expected/expected.qbs",
"fileutils/fileutils.qbs", "fileutils/fileutils.qbs",
"fsengine/fsengine.qbs", "fsengine/fsengine.qbs",
"fuzzymatcher/fuzzymatcher.qbs", "fuzzymatcher/fuzzymatcher.qbs",