Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Close stale issues and PRs / stale (push) Successful in 13s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
226 lines
6.0 KiB
C++
226 lines
6.0 KiB
C++
#define GI_INLINE 1
|
|
#include <gio/gio.hpp>
|
|
|
|
#include <functional>
|
|
#include <iostream>
|
|
#include <memory>
|
|
|
|
#include <QCoreApplication>
|
|
|
|
#include <QFuture>
|
|
#include <QFutureWatcher>
|
|
#include <QObject>
|
|
|
|
namespace GLib = gi::repository::GLib;
|
|
namespace GObject_ = gi::repository::GObject;
|
|
namespace Gio = gi::repository::Gio;
|
|
|
|
template<typename T>
|
|
using ResultExtractor = std::function<void(
|
|
GObject_::Object, Gio::AsyncResult result, QFutureInterface<T> &fut)>;
|
|
|
|
template<typename T>
|
|
class qt_future
|
|
{
|
|
struct CallbackData
|
|
{
|
|
QFutureInterface<T> promise;
|
|
QFutureWatcher<T> watcher;
|
|
Gio::Cancellable cancel;
|
|
ResultExtractor<T> handler;
|
|
};
|
|
|
|
std::shared_ptr<CallbackData> data_;
|
|
|
|
public:
|
|
qt_future(ResultExtractor<T> h)
|
|
{
|
|
data_ = std::make_shared<CallbackData>();
|
|
data_->handler = h;
|
|
}
|
|
|
|
QFuture<T> future() { return data_->promise.future(); }
|
|
|
|
QFutureInterface<T> promise() { return data_->promise; }
|
|
|
|
operator Gio::AsyncReadyCallback()
|
|
{
|
|
auto d = data_;
|
|
return [d](GObject_::Object obj, Gio::AsyncResult result) {
|
|
assert(d->handler);
|
|
d->handler(obj, result, d->promise);
|
|
};
|
|
}
|
|
|
|
Gio::Cancellable cancellable()
|
|
{
|
|
if (!data_->cancel) {
|
|
auto cancel = data_->cancel = Gio::Cancellable::new_();
|
|
data_->watcher.setFuture(future());
|
|
auto h = [cancel]() mutable { cancel.cancel(); };
|
|
data_->watcher.connect(
|
|
&data_->watcher, &decltype(data_->watcher)::finished, std::move(h));
|
|
}
|
|
return data_->cancel;
|
|
}
|
|
|
|
// why not ... ??
|
|
operator Gio::Cancellable() { return cancellable(); }
|
|
};
|
|
|
|
#if GI_CONST_METHOD
|
|
#define CONST_METHOD const
|
|
#else
|
|
#define CONST_METHOD
|
|
#endif
|
|
|
|
template<typename Result, typename Object>
|
|
qt_future<Result>
|
|
make_future(Result (Object::*mf)(Gio::AsyncResult, GLib::Error *) CONST_METHOD)
|
|
{
|
|
ResultExtractor<Result> f;
|
|
f = [mf](GObject_::Object cbobj, Gio::AsyncResult result,
|
|
QFutureInterface<Result> &fut) mutable {
|
|
auto obj = gi::object_cast<Object>(cbobj);
|
|
GLib::Error error{};
|
|
auto res = (obj.*mf)(result, &error);
|
|
if (error.gobj_()) {
|
|
// FIXME use std::expected<Result> as a result type ??
|
|
// reportException might be an option, if enabled, but leads to throw
|
|
fut.reportFinished();
|
|
qWarning() << "error code " << error.code_();
|
|
qWarning() << "error message "
|
|
<< QString::fromUtf8(error.message_().c_str());
|
|
} else {
|
|
fut.reportFinished(&res);
|
|
}
|
|
};
|
|
return {f};
|
|
}
|
|
|
|
// ====
|
|
// some small future-centric API wrappers using above helper class
|
|
|
|
QFuture<Gio::FileInfo>
|
|
file_query_file_system_info(
|
|
Gio::File f, const std::string &attributes, gint io_priority)
|
|
{
|
|
auto fut = make_future(&Gio::File::query_filesystem_info_finish);
|
|
f.query_filesystem_info_async(attributes, io_priority, fut, fut);
|
|
return fut.future();
|
|
}
|
|
|
|
QFuture<bool>
|
|
file_copy(Gio::File src, Gio::File destination, Gio::FileCopyFlags flags,
|
|
gint io_priority)
|
|
{
|
|
auto fut = make_future(&Gio::File::copy_finish);
|
|
auto promise = fut.promise();
|
|
auto p = [promise](
|
|
goffset current_num_bytes, goffset total_num_bytes) mutable {
|
|
promise.setProgressRange(0, total_num_bytes);
|
|
promise.setProgressValue(current_num_bytes);
|
|
};
|
|
src.copy_async(destination, flags, io_priority, fut, p, fut);
|
|
return fut.future();
|
|
}
|
|
|
|
// ====
|
|
|
|
// save on typing elsewhere
|
|
// also makes it movable in a way
|
|
template<typename T>
|
|
std::shared_ptr<QFutureWatcher<T>>
|
|
make_watcher(QFuture<T> fut)
|
|
{
|
|
auto watcher = std::make_shared<QFutureWatcher<T>>();
|
|
watcher->setFuture(fut);
|
|
return watcher;
|
|
}
|
|
|
|
// type-erased owner/cleanup type
|
|
using Retainer = std::shared_ptr<std::nullptr_t>;
|
|
|
|
Retainer
|
|
do_query(QCoreApplication &app, const std::string &fpath)
|
|
{
|
|
auto file = Gio::File::new_for_commandline_arg(fpath);
|
|
auto fut = file_query_file_system_info(file, "*", GLib::PRIORITY_DEFAULT_);
|
|
auto watcher = make_watcher(fut);
|
|
auto h = [fut, &app]() {
|
|
Q_ASSERT(fut.isFinished());
|
|
std::cout << "query finished" << std::endl;
|
|
if (fut.resultCount()) {
|
|
auto finfo = fut.result();
|
|
for (auto attr : {Gio::FILE_ATTRIBUTE_FILESYSTEM_TYPE_,
|
|
Gio::FILE_ATTRIBUTE_FILESYSTEM_FREE_,
|
|
Gio::FILE_ATTRIBUTE_FILESYSTEM_SIZE_}) {
|
|
std::cout << attr << ": " << finfo.get_attribute_as_string(attr)
|
|
<< std::endl;
|
|
}
|
|
} else {
|
|
std::cout << "... but no results" << std::endl;
|
|
}
|
|
app.quit();
|
|
};
|
|
watcher->connect(
|
|
watcher.get(), &decltype(watcher)::element_type::finished, &app, h);
|
|
return {watcher, nullptr};
|
|
}
|
|
|
|
Retainer
|
|
do_copy(QCoreApplication &app, const std::string &src, const std::string &dest)
|
|
{
|
|
auto fsrc = Gio::File::new_for_commandline_arg(src);
|
|
auto fdest = Gio::File::new_for_commandline_arg(dest);
|
|
auto fut = file_copy(
|
|
fsrc, fdest, Gio::FileCopyFlags::ALL_METADATA_, GLib::PRIORITY_DEFAULT_);
|
|
auto watcher = make_watcher(fut);
|
|
auto h = [fut, &app]() {
|
|
Q_ASSERT(fut.isFinished());
|
|
std::cout << "copy finished" << std::endl;
|
|
if (fut.resultCount()) {
|
|
auto ok = fut.result();
|
|
if (ok) {
|
|
std::cout << "copy ok" << std::endl;
|
|
} else {
|
|
std::cout << "copy failed" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << "... but no results" << std::endl;
|
|
}
|
|
app.quit();
|
|
};
|
|
watcher->connect(
|
|
watcher.get(), &decltype(watcher)::element_type::finished, &app, h);
|
|
// also monitor progress
|
|
auto progress = [fut](int) {
|
|
auto max = fut.progressMaximum();
|
|
auto min = fut.progressMinimum();
|
|
auto current = fut.progressValue();
|
|
std::cout << "copy progress " << current << " (" << min << " -> " << max
|
|
<< ")" << std::endl;
|
|
};
|
|
watcher->connect(watcher.get(),
|
|
&decltype(watcher)::element_type::progressValueChanged, &app, progress);
|
|
return {watcher, nullptr};
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
QCoreApplication app(argc, argv);
|
|
|
|
if (argc < 2)
|
|
return -1;
|
|
|
|
Retainer r;
|
|
if (argc == 2) {
|
|
r = do_query(app, argv[1]);
|
|
} else if (argc == 3) {
|
|
r = do_copy(app, argv[1], argv[2]);
|
|
}
|
|
|
|
return app.exec();
|
|
}
|