/* This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once #include "base/flat_set.h" class RPCError { public: RPCError(const MTPrpcError &error); int32 code() const { return _code; } const QString &type() const { return _type; } const QString &description() const { return _description; } enum { NoError, TimeoutError }; static RPCError Local(const QString &type, const QString &description) { return MTP_rpc_error( MTP_int(0), MTP_bytes( ("CLIENT_" + type + (description.length() ? (": " + description) : QString())).toUtf8())); } private: int32 _code; QString _type, _description; }; namespace MTP { inline bool isFloodError(const RPCError &error) { return error.type().startsWith(qstr("FLOOD_WAIT_")); } inline bool isTemporaryError(const RPCError &error) { return error.code() < 0 || error.code() >= 500 || isFloodError(error); } inline bool isDefaultHandledError(const RPCError &error) { return isTemporaryError(error); } } // namespace MTP class RPCAbstractDoneHandler { // abstract done public: [[nodiscard]] virtual bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) = 0; virtual ~RPCAbstractDoneHandler() { } }; using RPCDoneHandlerPtr = std::shared_ptr; class RPCAbstractFailHandler { // abstract fail public: virtual bool operator()(mtpRequestId requestId, const RPCError &e) = 0; virtual ~RPCAbstractFailHandler() { } }; using RPCFailHandlerPtr = std::shared_ptr; struct RPCResponseHandler { RPCResponseHandler() = default; RPCResponseHandler(RPCDoneHandlerPtr &&done, RPCFailHandlerPtr &&fail) : onDone(std::move(done)) , onFail(std::move(fail)) { } RPCDoneHandlerPtr onDone; RPCFailHandlerPtr onFail; }; class RPCDoneHandlerBare : public RPCAbstractDoneHandler { // done(from, end) using CallbackType = bool (*)(const mtpPrime *, const mtpPrime *); public: RPCDoneHandlerBare(CallbackType onDone) : _onDone(onDone) { } bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override { return (*_onDone)(from, end); } private: CallbackType _onDone; }; class RPCDoneHandlerBareReq : public RPCAbstractDoneHandler { // done(from, end, req_id) using CallbackType = bool (*)(const mtpPrime *, const mtpPrime *, mtpRequestId); public: RPCDoneHandlerBareReq(CallbackType onDone) : _onDone(onDone) { } bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override { return (*_onDone)(from, end, requestId); } private: CallbackType _onDone; }; template class RPCDoneHandlerPlain : public RPCAbstractDoneHandler { // done(result) using CallbackType = TReturn (*)(const TResponse &); public: RPCDoneHandlerPlain(CallbackType onDone) : _onDone(onDone) { } bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override { auto response = TResponse(); if (!response.read(from, end)) { return false; } (*_onDone)(std::move(response)); return true; } private: CallbackType _onDone; }; template class RPCDoneHandlerReq : public RPCAbstractDoneHandler { // done(result, req_id) using CallbackType = TReturn (*)(const TResponse &, mtpRequestId); public: RPCDoneHandlerReq(CallbackType onDone) : _onDone(onDone) { } bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override { auto response = TResponse(); if (!response.read(from, end)) { return false; } (*_onDone)(std::move(response), requestId); return true; } private: CallbackType _onDone; }; template class RPCDoneHandlerNo : public RPCAbstractDoneHandler { // done() using CallbackType = TReturn (*)(); public: RPCDoneHandlerNo(CallbackType onDone) : _onDone(onDone) { } bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override { (*_onDone)(); return true; } private: CallbackType _onDone; }; template class RPCDoneHandlerNoReq : public RPCAbstractDoneHandler { // done(req_id) using CallbackType = TReturn (*)(mtpRequestId); public: RPCDoneHandlerNoReq(CallbackType onDone) : _onDone(onDone) { } bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override { (*_onDone)(requestId); return true; } private: CallbackType _onDone; }; class RPCFailHandlerPlain : public RPCAbstractFailHandler { // fail(error) using CallbackType = bool (*)(const RPCError &); public: RPCFailHandlerPlain(CallbackType onFail) : _onFail(onFail) { } bool operator()(mtpRequestId requestId, const RPCError &e) override { return (*_onFail)(e); } private: CallbackType _onFail; }; class RPCFailHandlerReq : public RPCAbstractFailHandler { // fail(error, req_id) using CallbackType = bool (*)(const RPCError &, mtpRequestId); public: RPCFailHandlerReq(CallbackType onFail) : _onFail(onFail) { } bool operator()(mtpRequestId requestId, const RPCError &e) override { return (*_onFail)(e, requestId); } private: CallbackType _onFail; }; class RPCFailHandlerNo : public RPCAbstractFailHandler { // fail() using CallbackType = bool (*)(); public: RPCFailHandlerNo(CallbackType onFail) : _onFail(onFail) { } bool operator()(mtpRequestId requestId, const RPCError &e) override { return (*_onFail)(); } private: CallbackType _onFail; }; class RPCFailHandlerNoReq : public RPCAbstractFailHandler { // fail(req_id) using CallbackType = bool (*)(mtpRequestId); public: RPCFailHandlerNoReq(CallbackType onFail) : _onFail(onFail) { } bool operator()(mtpRequestId requestId, const RPCError &e) override { return (*_onFail)(requestId); } private: CallbackType _onFail; }; struct RPCCallbackClear { RPCCallbackClear(mtpRequestId id, int32 code = RPCError::NoError) : requestId(id) , errorCode(code) { } mtpRequestId requestId = 0; int32 errorCode = 0; }; inline RPCDoneHandlerPtr rpcDone(bool (*onDone)(const mtpPrime *, const mtpPrime *)) { // done(from, end) return RPCDoneHandlerPtr(new RPCDoneHandlerBare(onDone)); } inline RPCDoneHandlerPtr rpcDone(bool (*onDone)(const mtpPrime *, const mtpPrime *, mtpRequestId)) { // done(from, end, req_id) return RPCDoneHandlerPtr(new RPCDoneHandlerBareReq(onDone)); } template inline RPCDoneHandlerPtr rpcDone(TReturn (*onDone)(const TResponse &)) { // done(result) return RPCDoneHandlerPtr(new RPCDoneHandlerPlain(onDone)); } template inline RPCDoneHandlerPtr rpcDone(TReturn (*onDone)(const TResponse &, mtpRequestId)) { // done(result, req_id) return RPCDoneHandlerPtr(new RPCDoneHandlerReq(onDone)); } template inline RPCDoneHandlerPtr rpcDone(TReturn (*onDone)()) { // done() return RPCDoneHandlerPtr(new RPCDoneHandlerNo(onDone)); } template inline RPCDoneHandlerPtr rpcDone(TReturn (*onDone)(mtpRequestId)) { // done(req_id) return RPCDoneHandlerPtr(new RPCDoneHandlerNoReq(onDone)); } inline RPCFailHandlerPtr rpcFail(bool (*onFail)(const RPCError &)) { // fail(error) return RPCFailHandlerPtr(new RPCFailHandlerPlain(onFail)); } inline RPCFailHandlerPtr rpcFail(bool (*onFail)(const RPCError &, mtpRequestId)) { // fail(error, req_id) return RPCFailHandlerPtr(new RPCFailHandlerReq(onFail)); } inline RPCFailHandlerPtr rpcFail(bool (*onFail)()) { // fail() return RPCFailHandlerPtr(new RPCFailHandlerNo(onFail)); } inline RPCFailHandlerPtr rpcFail(bool (*onFail)(mtpRequestId)) { // fail(req_id) return RPCFailHandlerPtr(new RPCFailHandlerNoReq(onFail)); } using MTPStateChangedHandler = void (*)(int32 dcId, int32 state); using MTPSessionResetHandler = void (*)(int32 dcId); template class RPCHandlerImplementation : public Base { protected: using Lambda = FnMut; using Parent = RPCHandlerImplementation; public: RPCHandlerImplementation(Lambda handler) : _handler(std::move(handler)) { } protected: Lambda _handler; }; template using RPCDoneHandlerImplementation = RPCHandlerImplementation; class RPCDoneHandlerImplementationBare : public RPCDoneHandlerImplementation { // done(from, end) public: using RPCDoneHandlerImplementation::Parent::Parent; bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override { return this->_handler ? this->_handler(from, end) : true; } }; class RPCDoneHandlerImplementationBareReq : public RPCDoneHandlerImplementation { // done(from, end, req_id) public: using RPCDoneHandlerImplementation::Parent::Parent; bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override { return this->_handler ? this->_handler(from, end, requestId) : true; } }; template class RPCDoneHandlerImplementationPlain : public RPCDoneHandlerImplementation { // done(result) public: using RPCDoneHandlerImplementation::Parent::Parent; bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override { auto response = TResponse(); if (!response.read(from, end)) { return false; } if (this->_handler) { this->_handler(std::move(response)); } return true; } }; template class RPCDoneHandlerImplementationReq : public RPCDoneHandlerImplementation { // done(result, req_id) public: using RPCDoneHandlerImplementation::Parent::Parent; bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override { auto response = TResponse(); if (!response.read(from, end)) { return false; } if (this->_handler) { this->_handler(std::move(response), requestId); } return true; } }; template class RPCDoneHandlerImplementationNo : public RPCDoneHandlerImplementation { // done() public: using RPCDoneHandlerImplementation::Parent::Parent; bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override { if (this->_handler) { this->_handler(); } return true; } }; template class RPCDoneHandlerImplementationNoReq : public RPCDoneHandlerImplementation { // done(req_id) public: using RPCDoneHandlerImplementation::Parent::Parent; bool operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override { if (this->_handler) { this->_handler(requestId); } return true; } }; template constexpr bool rpcDone_canCallBare_v = rpl::details::is_callable_plain_v< Lambda, const mtpPrime*, const mtpPrime*>; template constexpr bool rpcDone_canCallBareReq_v = rpl::details::is_callable_plain_v< Lambda, const mtpPrime*, const mtpPrime*, mtpRequestId>; template constexpr bool rpcDone_canCallNo_v = rpl::details::is_callable_plain_v< Lambda>; template constexpr bool rpcDone_canCallNoReq_v = rpl::details::is_callable_plain_v< Lambda, mtpRequestId>; template struct rpcDone_canCallPlain : std::false_type { }; template struct rpcDone_canCallPlain : std::true_type { using Arg = T; }; template struct rpcDone_canCallPlain : rpcDone_canCallPlain { }; template constexpr bool rpcDone_canCallPlain_v = rpcDone_canCallPlain::value; template struct rpcDone_canCallReq : std::false_type { }; template struct rpcDone_canCallReq : std::true_type { using Arg = T; }; template struct rpcDone_canCallReq : rpcDone_canCallReq { }; template constexpr bool rpcDone_canCallReq_v = rpcDone_canCallReq::value; template struct rpcDone_returnType; template struct rpcDone_returnType { using type = Return; }; template struct rpcDone_returnType { using type = Return; }; template using rpcDone_returnType_t = typename rpcDone_returnType::type; template < typename Lambda, typename Function = crl::deduced_call_type> RPCDoneHandlerPtr rpcDone(Lambda lambda) { using R = rpcDone_returnType_t; if constexpr (rpcDone_canCallBare_v) { return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationBare(std::move(lambda))); } else if constexpr (rpcDone_canCallBareReq_v) { return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationBareReq(std::move(lambda))); } else if constexpr (rpcDone_canCallNo_v) { return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationNo(std::move(lambda))); } else if constexpr (rpcDone_canCallNoReq_v) { return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationNoReq(std::move(lambda))); } else if constexpr (rpcDone_canCallPlain_v) { using T = typename rpcDone_canCallPlain::Arg; return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationPlain(std::move(lambda))); } else if constexpr (rpcDone_canCallReq_v) { using T = typename rpcDone_canCallReq::Arg; return RPCDoneHandlerPtr(new RPCDoneHandlerImplementationReq(std::move(lambda))); } else { static_assert(false_t(lambda), "Unknown method."); } } template using RPCFailHandlerImplementation = RPCHandlerImplementation; class RPCFailHandlerImplementationPlain : public RPCFailHandlerImplementation { // fail(error) public: using Parent::Parent; bool operator()(mtpRequestId requestId, const RPCError &error) override { return _handler ? _handler(error) : true; } }; class RPCFailHandlerImplementationReq : public RPCFailHandlerImplementation { // fail(error, req_id) public: using Parent::Parent; bool operator()(mtpRequestId requestId, const RPCError &error) override { return this->_handler ? this->_handler(error, requestId) : true; } }; class RPCFailHandlerImplementationNo : public RPCFailHandlerImplementation { // fail() public: using Parent::Parent; bool operator()(mtpRequestId requestId, const RPCError &error) override { return this->_handler ? this->_handler() : true; } }; class RPCFailHandlerImplementationNoReq : public RPCFailHandlerImplementation { // fail(req_id) public: using Parent::Parent; bool operator()(mtpRequestId requestId, const RPCError &error) override { return this->_handler ? this->_handler(requestId) : true; } }; template constexpr bool rpcFail_canCallNo_v = rpl::details::is_callable_plain_v< Lambda>; template constexpr bool rpcFail_canCallNoReq_v = rpl::details::is_callable_plain_v< Lambda, mtpRequestId>; template constexpr bool rpcFail_canCallPlain_v = rpl::details::is_callable_plain_v< Lambda, const RPCError&>; template constexpr bool rpcFail_canCallReq_v = rpl::details::is_callable_plain_v< Lambda, const RPCError&, mtpRequestId>; template < typename Lambda, typename Function = crl::deduced_call_type> RPCFailHandlerPtr rpcFail(Lambda lambda) { if constexpr (rpcFail_canCallNo_v) { return RPCFailHandlerPtr(new RPCFailHandlerImplementationNo(std::move(lambda))); } else if constexpr (rpcFail_canCallNoReq_v) { return RPCFailHandlerPtr(new RPCFailHandlerImplementationNoReq(std::move(lambda))); } else if constexpr (rpcFail_canCallPlain_v) { return RPCFailHandlerPtr(new RPCFailHandlerImplementationPlain(std::move(lambda))); } else if constexpr (rpcFail_canCallReq_v) { return RPCFailHandlerPtr(new RPCFailHandlerImplementationReq(std::move(lambda))); } else { static_assert(false_t(lambda), "Unknown method."); } }