File indexing completed on 2024-05-05 04:41:15

0001 // SPDX-FileCopyrightText: 2021 Carson Black <uhhadd@gmail.com>
0002 //
0003 // SPDX-License-Identifier: MIT
0004 
0005 #pragma once
0006 
0007 #include "futurebase.h"
0008 
0009 namespace Croutons
0010 {
0011 
0012 template<typename T>
0013 concept Variantable = requires(T a) {
0014     QVariant::fromValue(a);
0015 };
0016 
0017 template<typename T, typename Error>
0018 requires (!std::is_same_v<T, Error>)
0019 struct Result {
0020     std::variant<T, Error> it;
0021 
0022     T value() const {
0023         return std::get<T>(it);
0024     }
0025     Error error() const {
0026         return std::get<Error>(it);
0027     }
0028 
0029     bool ok() const {
0030         return std::holds_alternative<T>(it);
0031     }
0032 };
0033 
0034 template<typename T = void>
0035 requires (Variantable<T> || std::is_same_v<T, void>) class Future : public FutureBase
0036 {
0037 
0038 public:
0039     using Kind = T;
0040 
0041     Future() : FutureBase() {
0042     }
0043     Future(const FutureBase& other) : FutureBase(other) {
0044     }
0045     void succeed(const T& it) const {
0046         FutureBase::succeed(QVariant::fromValue(it));
0047     }
0048     void fail(const T& it) const {
0049         FutureBase::succeed(QVariant::fromValue(it));
0050     }
0051     T result() const {
0052         Q_ASSERT(settled());
0053 
0054         return FutureBase::result().template value<T>();
0055     }
0056     void then(std::function<void(T)> callback, std::function<void(T)> orElse = [](T){}) const {
0057         auto wrap1 = [callback](QVariant r) { callback(qvariant_cast<T>(r)); };
0058         auto wrap2 = [orElse](QVariant r) { orElse(qvariant_cast<T>(r)); };
0059         FutureBase::then(wrap1, wrap2);
0060     }
0061     template<typename Function>
0062     Future<typename std::invoke_result_t<Function, T>::Kind>
0063     flatMap(Function callback) const {
0064         using NewT = typename std::invoke_result_t<Function, T>::Kind;
0065 
0066         Future<NewT> ret;
0067 
0068         auto wrap1 = [callback, ret](QVariant r) {
0069             auto bret = callback(qvariant_cast<T>(r));
0070 
0071             bret.then([ret](NewT t) { ret.succeed(t); }, [ret](NewT t) { ret.fail(t); });
0072         };
0073 
0074         FutureBase::then(wrap1);
0075 
0076         return ret;
0077     }
0078     template<typename Function>
0079     Future<typename std::invoke_result_t<Function, T>>
0080     map(Function callback) const {
0081         using NewT = std::invoke_result_t<Function, T>;
0082         Future<NewT> ret;
0083 
0084         auto wrap1 = [callback, ret](QVariant r) {
0085             ret.succeed(callback(qvariant_cast<T>(r)));
0086         };
0087 
0088         FutureBase::then(wrap1);
0089 
0090         return ret;
0091     }
0092 };
0093 
0094 template<>
0095 class Future<void> : public FutureBase
0096 {
0097 
0098 public:
0099     void succeed() const {
0100         FutureBase::succeed(QVariant());
0101     }
0102     void fail() const {
0103         FutureBase::succeed(QVariant());
0104     }
0105     void then(std::function<void()> callback, std::function<void()> orElse = [](){}) const {
0106         auto wrap1 = [callback](QVariant) { callback(); };
0107         auto wrap2 = [orElse](QVariant) { orElse(); };
0108         FutureBase::then(wrap1, wrap2);
0109     }
0110 };
0111 
0112 struct Error {
0113     QString err;
0114 };
0115 
0116 struct Nil {};
0117 
0118 template<typename T = Nil, typename Error = Error>
0119     requires Variantable<T>&& Variantable<Error> && (!std::is_same_v<T, Error>) class FutureResult : public FutureBase
0120 {
0121 
0122 public:
0123     FutureResult() : FutureBase() {
0124     }
0125     FutureResult(const FutureBase& other) : FutureBase(other) {
0126     }
0127     void succeed(const T& it) const {
0128         FutureBase::succeed(QVariant::fromValue(it));
0129     }
0130     void fail(const Error& it) const {
0131         FutureBase::fail(QVariant::fromValue(it));
0132     }
0133     void finish(const Result<T, Error>& it) const {
0134         if (it.ok()) {
0135             succeed(it.value());
0136         } else {
0137             fail(it.error());
0138         }
0139     }
0140 
0141     Result<T, Error> result() const {
0142         Q_ASSERT(settled());
0143 
0144         Result<T, Error> res;
0145 
0146         if (!success()) {
0147             res = Result<T, Error> { .it = FutureBase::result().template value<Error>() };
0148         } else {
0149             res = Result<T, Error> { .it = FutureBase::result().template value<T>() };
0150         }
0151 
0152         return res;
0153     }
0154     void then(std::function<void(Result<T, Error>)> callback) const {
0155         auto wrap = [callback, *this](QVariant) { callback(this->result()); };
0156         FutureBase::then(wrap, wrap);
0157     }
0158     Future<T> toFutureT() {
0159         Future<T> ret;
0160 
0161         then([ret](Result<T, Error> res) mutable {
0162             if (res.ok()) {
0163                 ret.succeed(res.value());
0164             }
0165         });
0166 
0167         return ret;
0168     }
0169 };
0170 
0171 } // namespace Croutons
0172 
0173 Q_DECLARE_METATYPE(Croutons::Error)
0174 Q_DECLARE_METATYPE(Croutons::Nil)