diff --git a/CMakeLists.txt b/CMakeLists.txt index dffe8cfb..063cda0d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.7) +cmake_minimum_required(VERSION 3.21) project(SqMod) # This plug-in only works on 64-bit @@ -7,8 +7,7 @@ if(CMAKE_SIZEOF_VOID_P EQUAL 4) endif() # Tell CMake where to find our scripts -set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) -set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/vendor/POCO/cmake) +set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake ${PROJECT_SOURCE_DIR}/vendor/POCO/cmake) # Several plugin options option(ENABLE_API21 "Build for 2.1 API." OFF) @@ -20,85 +19,12 @@ option(ENABLE_BUILTIN_MYSQL_C "Enable built-in MySQL connector library" OFF) if(WIN32 AND MINGW) option(COPY_DEPENDENCIES "Copy dependent DLLs into the deps folder." OFF) endif() +# Discord suppport +option(ENABLE_DISCORD "Enable built-in Discord support." ON) -# C++14 is mandatory -set(CPP_STD_NUMBER 14) - -include(CheckCXXCompilerFlag) -# C++ standard availability check -if(${CMAKE_CXX_COMPILER_ID} MATCHES "(GNU)+") - # Specific flags - set(CPP_STD_COMPILER_FLAG "-std=c++14") - # Don't even bother with previous version - if(CPP_STD_NUMBER LESS 20 AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8.0) - check_cxx_compiler_flag(-std=c++20 HAVE_FLAG_STD_CXX20) - check_cxx_compiler_flag(-std=c++2a HAVE_FLAG_STD_CXX2A) - if(HAVE_FLAG_STD_CXX20 OR HAVE_FLAG_STD_CXX2A) - # We can use C++20 - set(CPP_STD_NUMBER 20) - # Specific flags - if (HAVE_FLAG_STD_CXX2A AND NOT HAVE_FLAG_STD_CXX20) - set(CPP_STD_COMPILER_FLAG "-std=c++2a") - else() - set(CPP_STD_COMPILER_FLAG "-std=c++20") - endif() - # Need these workarounds for older CMake - if(${CMAKE_VERSION} VERSION_LESS "3.8.0") - if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.0) - set(CMAKE_CXX20_STANDARD_COMPILE_OPTION "-std=c++20") - set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION "-std=gnu++20") - elseif (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8.0) - set(CMAKE_CXX20_STANDARD_COMPILE_OPTION "-std=c++2a") - set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION "-std=gnu++2a") - endif() - endif() - endif() - endif() - # Don't even bother with previous version - if(CPP_STD_NUMBER LESS 17 AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0) - check_cxx_compiler_flag(-std=c++17 HAVE_FLAG_STD_CXX17) - check_cxx_compiler_flag(-std=c++1z HAVE_FLAG_STD_CXX1Z) - if(HAVE_FLAG_STD_CXX17 OR HAVE_FLAG_STD_CXX1Z) - # We can use C++17 - set(CPP_STD_NUMBER 17) - # Specific flags - if (HAVE_FLAG_STD_CXX1Z AND NOT HAVE_FLAG_STD_CXX17) - set(CPP_STD_COMPILER_FLAG "-std=c++1z") - else() - set(CPP_STD_COMPILER_FLAG "-std=c++17") - endif() - # Need these workarounds for older CMake - if(${CMAKE_VERSION} VERSION_LESS "3.8.0") - if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8.0) - set(CMAKE_CXX17_STANDARD_COMPILE_OPTION "-std=c++17") - set(CMAKE_CXX17_EXTENSION_COMPILE_OPTION "-std=gnu++17") - elseif (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.1) - set(CMAKE_CXX17_STANDARD_COMPILE_OPTION "-std=c++1z") - set(CMAKE_CXX17_EXTENSION_COMPILE_OPTION "-std=gnu++1z") - endif() - endif() - endif() - endif() -else() - # C++14 is mandatory - set(CPP_STD_NUMBER 14) -endif() - -message(STATUS "SqMod: Using C++${CPP_STD_NUMBER} standard.") - -# Default to the identified standard -if(CMAKE_VERSION VERSION_LESS "3.1") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CPP_STD_COMPILER_FLAG}") -else() - # Apparently the above does not work with cmake from on debian 8 - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CPP_STD_COMPILER_FLAG}") - # F* you too Debian. What can I say. - if(CMAKE_VERSION VERSION_LESS "3.8.0" AND CPP_STD_NUMBER LESS 17) - # Try the standard method as well - set(CMAKE_CXX_STANDARD ${CPP_STD_NUMBER}) - set(CMAKE_CXX_STANDARD_REQUIRED ON) - endif() -endif() +# C++17 is mandatory (globally) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) # Strip binary set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -s -g") diff --git a/cmake/_FindOpus.cmake b/cmake/_FindOpus.cmake new file mode 100644 index 00000000..75de4f9a --- /dev/null +++ b/cmake/_FindOpus.cmake @@ -0,0 +1,92 @@ +############################################################################ +# FindOpus.cmake +# Copyright (C) 2014-2023 Belledonne Communications, Grenoble France +# +############################################################################ +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +############################################################################ +# +# Find the opus library. +# +# Targets +# ^^^^^^^ +# +# The following targets may be defined: +# +# opus - If the opus library has been found +# +# +# Result variables +# ^^^^^^^^^^^^^^^^ +# +# This module will set the following variables in your project: +# +# Opus_FOUND - The opus library has been found +# Opus_TARGET - The name of the CMake target for the opus library +# +# This module may set the following variable: +# +# Opus_USE_BUILD_INTERFACE - If the opus library is used from its build directory + + +include(FindPackageHandleStandardArgs) + +set(_Opus_REQUIRED_VARS Opus_TARGET) +set(_Opus_CACHE_VARS ${_Opus_REQUIRED_VARS}) + +if(TARGET opus) + + set(Opus_TARGET opus) + set(Opus_USE_BUILD_INTERFACE TRUE) + +else() + + find_path(_Opus_INCLUDE_DIRS + NAMES opus/opus.h + PATH_SUFFIXES include + ) + + find_library(_Opus_LIBRARY NAMES opus) + if(_Opus_LIBRARY) + find_library(_m_LIBRARY NAMES m) + endif() + + if(_Opus_INCLUDE_DIRS AND _Opus_LIBRARY) + add_library(opus UNKNOWN IMPORTED) + if(WIN32) + set_target_properties(opus PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${_Opus_INCLUDE_DIRS}" + IMPORTED_IMPLIB "${_Opus_LIBRARY}" + IMPORTED_LINK_INTERFACE_LIBRARIES "${_m_LIBRARY}" + ) + else() + set_target_properties(opus PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${_Opus_INCLUDE_DIRS}" + IMPORTED_LOCATION "${_Opus_LIBRARY}" + IMPORTED_LINK_INTERFACE_LIBRARIES "${_m_LIBRARY}" + ) + endif() + + set(Opus_TARGET opus) + endif() + +endif() + +find_package_handle_standard_args(Opus + REQUIRED_VARS ${_Opus_REQUIRED_VARS} +) +mark_as_advanced(${_Opus_CACHE_VARS}) diff --git a/module/CMakeLists.txt b/module/CMakeLists.txt index 2d77f0bf..0ee59246 100644 --- a/module/CMakeLists.txt +++ b/module/CMakeLists.txt @@ -92,6 +92,7 @@ add_library(SqModule MODULE SqBase.hpp Main.cpp Library/Utils.cpp Library/Utils.hpp Library/Utils/Announce.cpp Library/Utils/Announce.hpp Library/Utils/String.cpp Library/Utils/String.hpp + Library/Utils/Template.cpp Library/Utils/Template.hpp Library/Utils/Vector.cpp Library/Utils/Vector.hpp Library/XML.cpp Library/XML.hpp Library/ZMQ.cpp Library/ZMQ.hpp @@ -112,12 +113,14 @@ add_library(SqModule MODULE SqBase.hpp Main.cpp PocoLib/Register.cpp PocoLib/Register.hpp PocoLib/Time.cpp PocoLib/Time.hpp PocoLib/Util.cpp PocoLib/Util.hpp - # + # Core.cpp Core.hpp Logger.cpp Logger.hpp Register.cpp Exports.cpp ) +# The module requires C++ 17 +set_property(TARGET SqModule PROPERTY CXX_STANDARD 17) # Various definitions required by the plug-in target_compile_definitions(SqModule PRIVATE SCRAT_USE_EXCEPTIONS=1) # SDK targeting @@ -135,7 +138,7 @@ if(WIN32 OR MINGW) target_link_libraries(SqModule wsock32 ws2_32 shlwapi) endif() # Link to base libraries -target_link_libraries(SqModule RPMalloc Squirrel fmt::fmt SimpleINI TinyDir xxHash ConcurrentQueue SAJSON CPR UTF8Lib PUGIXML CivetWeb maxminddb libzmq-static) +target_link_libraries(SqModule RPMalloc Squirrel fmt::fmt SimpleINI TinyDir xxHash ConcurrentQueue SAJSON CPR UTF8Lib PUGIXML CivetWeb inja maxminddb libzmq-static) # Link to POCO libraries target_link_libraries(SqModule Poco::Foundation Poco::Crypto Poco::Data Poco::Net) # Does POCO have SQLite support? @@ -174,6 +177,31 @@ if(POSTGRESQL_FOUND) # Inform the plug-in that it can make use of this library target_compile_definitions(SqModule PRIVATE SQMOD_POCO_HAS_POSTGRESQL=1) endif() +# Is Discord support enabled? +if(ENABLE_DISCORD) +target_link_libraries(SqModule dpp) +target_sources(SqModule PRIVATE + Library/Discord.cpp Library/Discord.hpp + Library/Discord/Application.hpp Library/Discord/Application.cpp + Library/Discord/Automod.hpp Library/Discord/Automod.cpp + Library/Discord/Channel.hpp Library/Discord/Channel.cpp + Library/Discord/Client.hpp Library/Discord/Client.cpp + Library/Discord/Cluster.hpp Library/Discord/Cluster.cpp + Library/Discord/Command.hpp Library/Discord/Command.cpp + Library/Discord/Constants.hpp Library/Discord/Constants.cpp + Library/Discord/Events.hpp Library/Discord/Events.cpp + Library/Discord/Guild.hpp Library/Discord/Guild.cpp + Library/Discord/Integration.hpp Library/Discord/Integration.cpp + Library/Discord/Message.hpp Library/Discord/Message.cpp + Library/Discord/Misc.hpp Library/Discord/Misc.cpp + Library/Discord/Presence.hpp Library/Discord/Presence.cpp + Library/Discord/Role.hpp Library/Discord/Role.cpp + Library/Discord/User.hpp Library/Discord/User.cpp + Library/Discord/Utilities.hpp Library/Discord/Utilities.cpp +) +# Inform the plug-in that discord is enabled +target_compile_definitions(SqModule PRIVATE SQMOD_DISCORD=1) +endif() # Determine if build mode if(${CMAKE_BUILD_TYPE} MATCHES "(Release)+") target_compile_definitions(SqModule PRIVATE NDEBUG=1) @@ -218,6 +246,7 @@ else() endif() # Copy module into the plug-ins folder add_custom_command(TARGET SqModule POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ "${PROJECT_SOURCE_DIR}/bin/plugins") + # Copy several dependent DLLs on windows to make distribution easier (used mainly by people that distribute builds) if(WIN32 AND MINGW AND COPY_DEPENDENCIES) get_filename_component(MINGW_BIN_PATH ${CMAKE_C_COMPILER} DIRECTORY REALPATH) diff --git a/module/Core.cpp b/module/Core.cpp index e53fe1bb..47ef3df1 100644 --- a/module/Core.cpp +++ b/module/Core.cpp @@ -49,6 +49,9 @@ extern void TerminateRoutines(); extern void TerminateCommands(); extern void TerminateSignals(); extern void TerminateNet(); +#ifdef SQMOD_DISCORD + extern void TerminateDiscord(); +#endif extern void TerminatePocoNet(); extern void TerminatePocoData(); @@ -553,6 +556,11 @@ void Core::Terminate(bool shutdown) // Release network TerminateNet(); cLogDbg(m_Verbosity >= 1, "Network terminated"); + // Release DPP +#ifdef SQMOD_DISCORD + TerminateDiscord(); + cLogDbg(m_Verbosity >= 1, "Discord terminated"); +#endif // Release Poco statement results TerminatePocoNet(); TerminatePocoData(); diff --git a/module/Library/CURL.cpp b/module/Library/CURL.cpp index 18643f84..f5860e15 100644 --- a/module/Library/CURL.cpp +++ b/module/Library/CURL.cpp @@ -19,6 +19,19 @@ SQMOD_DECL_TYPENAME(SqCpProxies, _SC("SqCprProxies")) SQMOD_DECL_TYPENAME(SqCpRedirect, _SC("SqCprRedirect")) SQMOD_DECL_TYPENAME(SqCpSession, _SC("SqCprSession")) +// ------------------------------------------------------------------------------------------------ +struct CurlInit +{ + CurlInit() + { + curl_global_init(CURL_GLOBAL_ALL); + } + ~CurlInit() + { + curl_global_cleanup(); + } +}; + /* ------------------------------------------------------------------------------------------------ * Common session action implementation. */ @@ -658,13 +671,11 @@ void Register_CURL(HSQUIRRELVM vm) .Func(_SC("SetTimeout"), &CpSession::SetTimeout_) .Func(_SC("SetConnectTimeout"), &CpSession::SetConnectTimeout_) .FmtFunc(_SC("SetAuth"), &CpSession::SetAuth_) - .FmtFunc(_SC("SetDigest"), &CpSession::SetDigest_) .FmtFunc(_SC("SetUserAgent"), &CpSession::SetUserAgent_) .Func(_SC("SetPayload"), &CpSession::SetPayload_) .Func(_SC("YieldPayload"), &CpSession::YieldPayload) .Func(_SC("SetProxies"), &CpSession::SetProxies_) .Func(_SC("YieldProxies"), &CpSession::YieldProxies) - .FmtFunc(_SC("SetNTLM"), &CpSession::SetNTLM_) .Func(_SC("SetRedirect"), &CpSession::SetRedirect_) .Func(_SC("SetCookies"), &CpSession::SetCookies_) .FmtFunc(_SC("SetBody"), &CpSession::SetBody_) diff --git a/module/Library/CURL.hpp b/module/Library/CURL.hpp index 7451deb6..763e868e 100644 --- a/module/Library/CURL.hpp +++ b/module/Library/CURL.hpp @@ -507,7 +507,7 @@ struct CpCookies : public cpr::Cookies * Move constructor. */ explicit CpCookies(cpr::Cookies && e) : cpr::Cookies(std::move(e)) { } - + /* -------------------------------------------------------------------------------------------- * Copy constructor. */ @@ -538,7 +538,7 @@ struct CpCookies : public cpr::Cookies */ SQMOD_NODISCARD SQInteger Size() const { - return static_cast< SQInteger >(cpr::Cookies::map_.size()); + return static_cast< SQInteger >(cpr::Cookies::cookies_.size()); } /* -------------------------------------------------------------------------------------------- @@ -546,7 +546,7 @@ struct CpCookies : public cpr::Cookies */ SQMOD_NODISCARD bool Empty() const { - return cpr::Cookies::map_.empty(); + return cpr::Cookies::cookies_.empty(); } /* -------------------------------------------------------------------------------------------- @@ -554,7 +554,7 @@ struct CpCookies : public cpr::Cookies */ void Clear() { - cpr::Cookies::map_.clear(); + cpr::Cookies::cookies_.clear(); } /* -------------------------------------------------------------------------------------------- @@ -562,7 +562,12 @@ struct CpCookies : public cpr::Cookies */ SQMOD_NODISCARD SQInteger Count(StackStrF & key) const { - return static_cast< SQInteger >(cpr::Cookies::map_.count(key.ToStr())); + SQInteger cnt = 0; + for (const auto & c : cpr::Cookies::cookies_) + { + if (c.GetName().compare(0, static_cast(key.mLen), key.mPtr) == 0) ++cnt; + } + return cnt; } /* -------------------------------------------------------------------------------------------- @@ -570,14 +575,14 @@ struct CpCookies : public cpr::Cookies */ bool Erase(StackStrF & key) { - auto itr = cpr::Cookies::map_.find(key.ToStr()); - // Does it exist? - if (itr == cpr::Cookies::map_.end()) - { - return false; // Nope - } - // Erase it - cpr::Cookies::map_.erase(itr); + // auto itr = cpr::Cookies::cookies_.find(key.ToStr()); + // // Does it exist? + // if (itr == cpr::Cookies::cookies_.end()) + // { + // return false; // Nope + // } + // // Erase it + // cpr::Cookies::cookies_.erase(itr); // Erased return true; } @@ -587,7 +592,7 @@ struct CpCookies : public cpr::Cookies */ SQMOD_NODISCARD bool Has(StackStrF & key) const { - return cpr::Cookies::map_.find(key.ToStr()) != cpr::Cookies::map_.end(); + return false;//cpr::Cookies::cookies_.find(key.ToStr()) != cpr::Cookies::cookies_.end() } /* -------------------------------------------------------------------------------------------- @@ -595,14 +600,15 @@ struct CpCookies : public cpr::Cookies */ SQMOD_NODISCARD std::string & Get(StackStrF & key) { - auto itr = cpr::Cookies::map_.find(key.ToStr()); - // Does it exist? - if (itr == cpr::Cookies::map_.end()) - { - STHROWF("No cookie named: %s", key.mPtr); - } + // auto itr = cpr::Cookies::cookies_.find(key.ToStr()); + // // Does it exist? + // if (itr == cpr::Cookies::cookies_.end()) + // { + // STHROWF("No cookie named: %s", key.mPtr); + // } + static std::string s; // Return it - return itr->second; + return s;// itr->second; } /* -------------------------------------------------------------------------------------------- @@ -610,7 +616,7 @@ struct CpCookies : public cpr::Cookies */ void Set(StackStrF & key, StackStrF & val) { - cpr::Cookies::map_[key.ToStr()] = val.ToStr(); + //cpr::Cookies::cookies_[key.ToStr()] = val.ToStr(); } /* -------------------------------------------------------------------------------------------- @@ -618,9 +624,9 @@ struct CpCookies : public cpr::Cookies */ void Each(Function & fn) const { - for (const auto & p : cpr::Cookies::map_) + for (const auto & c : cpr::Cookies::cookies_) { - fn.Execute(p.first, p.second); + fn.Execute(c); } } @@ -629,9 +635,9 @@ struct CpCookies : public cpr::Cookies */ void While(Function & fn) const { - for (const auto & p : cpr::Cookies::map_) + for (const auto & c : cpr::Cookies::cookies_) { - auto ret = fn.Eval(p.first, p.second); + auto ret = fn.Eval(c); // (null || true) == continue & false == break if (!ret.IsNull() || !ret.Cast< bool >()) { @@ -664,7 +670,7 @@ struct CpHeader * Move constructor. */ explicit CpHeader(cpr::Header && e) : mMap(std::move(e)) { } - + /* -------------------------------------------------------------------------------------------- * Copy constructor. */ @@ -863,7 +869,7 @@ struct CpResponse : public cpr::Response STHROWF("Invalid response instance"); } // Retrieve the info vector - auto vec = cpr::Response::GetCertInfo(); + auto vec = cpr::Response::GetCertInfos(); // Create a script array Array arr(SqVM(), static_cast< SQInteger >(vec.size())); // Populate the array with vector elements @@ -1120,7 +1126,7 @@ struct CpParameters : public cpr::Parameters * Move constructor. */ explicit CpParameters(cpr::Parameters && e) : cpr::Parameters(std::move(e)) { } - + /* -------------------------------------------------------------------------------------------- * Copy constructor. */ @@ -1291,7 +1297,7 @@ struct CpPayload : public cpr::Payload * Move constructor. */ explicit CpPayload(cpr::Payload && e) : cpr::Payload(std::move(e)) { } - + /* -------------------------------------------------------------------------------------------- * Copy constructor. */ @@ -1470,7 +1476,7 @@ struct CpProxies : public cpr::Proxies * Move constructor. */ explicit CpProxies(cpr::Proxies && e) : cpr::Proxies(std::move(e)) { } - + /* -------------------------------------------------------------------------------------------- * Copy constructor. */ @@ -1619,7 +1625,7 @@ struct CpRedirect : public cpr::Redirect * Explicit constructor. */ explicit CpRedirect(SQInteger maximum) - : cpr::Redirect(static_cast< long >(maximum), true, cpr::PostRedirectFlags::POST_ALL) + : cpr::Redirect(static_cast< long >(maximum), true, false, cpr::PostRedirectFlags::POST_ALL) { } @@ -1627,15 +1633,23 @@ struct CpRedirect : public cpr::Redirect * Explicit constructor. */ CpRedirect(SQInteger maximum, bool follow) - : cpr::Redirect(static_cast< long >(maximum), follow, cpr::PostRedirectFlags::POST_ALL) + : cpr::Redirect(static_cast< long >(maximum), follow, false, cpr::PostRedirectFlags::POST_ALL) { } /* -------------------------------------------------------------------------------------------- * Explicit constructor. */ - CpRedirect(SQInteger maximum, bool follow, SQInteger post_flags) - : cpr::Redirect(static_cast< long >(maximum), follow, static_cast< cpr::PostRedirectFlags >(post_flags)) + CpRedirect(SQInteger maximum, bool follow, bool cont_send_cred) + : cpr::Redirect(static_cast< long >(maximum), follow, cont_send_cred, cpr::PostRedirectFlags::POST_ALL) + { + } + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + CpRedirect(SQInteger maximum, bool follow, bool cont_send_cred, SQInteger post_flags) + : cpr::Redirect(static_cast< long >(maximum), follow, cont_send_cred, static_cast< cpr::PostRedirectFlags >(post_flags)) { } @@ -1648,7 +1662,7 @@ struct CpRedirect : public cpr::Redirect * Move constructor. */ explicit CpRedirect(cpr::Redirect && e) : cpr::Redirect(e) { } - + /* -------------------------------------------------------------------------------------------- * Copy constructor. */ @@ -1879,20 +1893,10 @@ struct CpSession : public cpr::Session /* -------------------------------------------------------------------------------------------- * Modify auth option. */ - CpSession & SetAuth_(StackStrF & username, StackStrF & password) + CpSession & SetAuth_(StackStrF & username, StackStrF & password, SQInteger mode) { LockCheck(); - cpr::Session::SetAuth(cpr::Authentication(username.ToStr(), password.ToStr())); - return *this; // Allow chaining - } - - /* -------------------------------------------------------------------------------------------- - * Modify digest option. - */ - CpSession & SetDigest_(StackStrF & username, StackStrF & password) - { - LockCheck(); - cpr::Session::SetAuth(cpr::Digest(username.ToStr(), password.ToStr())); + cpr::Session::SetAuth(cpr::Authentication(username.ToStr(), password.ToStr(), static_cast(mode))); return *this; // Allow chaining } @@ -1960,16 +1964,6 @@ struct CpSession : public cpr::Session //{ //} - /* -------------------------------------------------------------------------------------------- - * Modify NTLM option. - */ - CpSession & SetNTLM_(StackStrF & username, StackStrF & password) - { - LockCheck(); - cpr::Session::SetNTLM(cpr::NTLM(username.ToStr(), password.ToStr())); - return *this; // Allow chaining - } - /* -------------------------------------------------------------------------------------------- * Modify redirect option. */ diff --git a/module/Library/Discord.cpp b/module/Library/Discord.cpp new file mode 100644 index 00000000..e004badd --- /dev/null +++ b/module/Library/Discord.cpp @@ -0,0 +1,51 @@ +// ------------------------------------------------------------------------------------------------ +#include "Library/Discord.hpp" +#include "Library/Discord/Cluster.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +// ------------------------------------------------------------------------------------------------ +void TerminateDiscord() +{ + // Go over all clusters and try to terminate them + for (DpCluster * inst = DpCluster::sHead; inst && inst->mNext != DpCluster::sHead; inst = inst->mNext) + { + inst->Terminate(); // Terminate the cluster + } +} + +// ------------------------------------------------------------------------------------------------ +void ProcessDiscord() +{ + // Go over all clusters and allow them to process data + for (DpCluster * inst = DpCluster::sHead; inst && inst->mNext != DpCluster::sHead; inst = inst->mNext) + { + inst->Process(); + } +} + +// ------------------------------------------------------------------------------------------------ +extern void Register_Discord_Constants(HSQUIRRELVM vm, Table & ns); +extern void Register_Discord_Events(HSQUIRRELVM vm, Table & ns); +extern void Register_Discord_Misc(HSQUIRRELVM vm, Table & ns); +extern void Register_Discord_Cluster(HSQUIRRELVM vm, Table & ns); + +// ================================================================================================ +void Register_Discord(HSQUIRRELVM vm) +{ + Table ns(vm); + // -------------------------------------------------------------------------------------------- + Register_Discord_Constants(vm, ns); + { + Table ens(vm); + Register_Discord_Events(vm, ens); + ns.Bind(_SC("Event"), ens); + } + Register_Discord_Misc(vm, ns); + Register_Discord_Cluster(vm, ns); + // -------------------------------------------------------------------------------------------- + RootTable(vm).Bind(_SC("SqDiscord"), ns); +} + +} // Namespace:: SqMod diff --git a/module/Library/Discord.hpp b/module/Library/Discord.hpp new file mode 100644 index 00000000..d1757830 --- /dev/null +++ b/module/Library/Discord.hpp @@ -0,0 +1,17 @@ +#pragma once + +// ------------------------------------------------------------------------------------------------ +#include "Library/IO/Buffer.hpp" + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +#include +#include +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +} // Namespace:: SqMod diff --git a/module/Library/Discord/Application.cpp b/module/Library/Discord/Application.cpp new file mode 100644 index 00000000..6abaa1bf --- /dev/null +++ b/module/Library/Discord/Application.cpp @@ -0,0 +1,9 @@ +// ------------------------------------------------------------------------------------------------ +#include "Library/Discord/Application.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + + + +} // Namespace:: SqMod diff --git a/module/Library/Discord/Application.hpp b/module/Library/Discord/Application.hpp new file mode 100644 index 00000000..ab7ce4d7 --- /dev/null +++ b/module/Library/Discord/Application.hpp @@ -0,0 +1,13 @@ +#pragma once + +// ------------------------------------------------------------------------------------------------ +#include "Core/Utility.hpp" + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + + +} // Namespace:: SqMod diff --git a/module/Library/Discord/Automod.cpp b/module/Library/Discord/Automod.cpp new file mode 100644 index 00000000..a1337e7f --- /dev/null +++ b/module/Library/Discord/Automod.cpp @@ -0,0 +1,9 @@ +// ------------------------------------------------------------------------------------------------ +#include "Library/Discord/Automod.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + + + +} // Namespace:: SqMod diff --git a/module/Library/Discord/Automod.hpp b/module/Library/Discord/Automod.hpp new file mode 100644 index 00000000..ab7ce4d7 --- /dev/null +++ b/module/Library/Discord/Automod.hpp @@ -0,0 +1,13 @@ +#pragma once + +// ------------------------------------------------------------------------------------------------ +#include "Core/Utility.hpp" + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + + +} // Namespace:: SqMod diff --git a/module/Library/Discord/Channel.cpp b/module/Library/Discord/Channel.cpp new file mode 100644 index 00000000..9b25380e --- /dev/null +++ b/module/Library/Discord/Channel.cpp @@ -0,0 +1,9 @@ +// ------------------------------------------------------------------------------------------------ +#include "Library/Discord/Channel.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + + + +} // Namespace:: SqMod diff --git a/module/Library/Discord/Channel.hpp b/module/Library/Discord/Channel.hpp new file mode 100644 index 00000000..ab7ce4d7 --- /dev/null +++ b/module/Library/Discord/Channel.hpp @@ -0,0 +1,13 @@ +#pragma once + +// ------------------------------------------------------------------------------------------------ +#include "Core/Utility.hpp" + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + + +} // Namespace:: SqMod diff --git a/module/Library/Discord/Client.cpp b/module/Library/Discord/Client.cpp new file mode 100644 index 00000000..e110c8a2 --- /dev/null +++ b/module/Library/Discord/Client.cpp @@ -0,0 +1,9 @@ +// ------------------------------------------------------------------------------------------------ +#include "Library/Discord/Client.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + + + +} // Namespace:: SqMod diff --git a/module/Library/Discord/Client.hpp b/module/Library/Discord/Client.hpp new file mode 100644 index 00000000..ab7ce4d7 --- /dev/null +++ b/module/Library/Discord/Client.hpp @@ -0,0 +1,13 @@ +#pragma once + +// ------------------------------------------------------------------------------------------------ +#include "Core/Utility.hpp" + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + + +} // Namespace:: SqMod diff --git a/module/Library/Discord/Cluster.cpp b/module/Library/Discord/Cluster.cpp new file mode 100644 index 00000000..5d82b5b5 --- /dev/null +++ b/module/Library/Discord/Cluster.cpp @@ -0,0 +1,496 @@ +// ------------------------------------------------------------------------------------------------ +#include "Library/Discord/Cluster.hpp" +#include "Library/Discord/Events.hpp" +#include "Logger.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +// ------------------------------------------------------------------------------------------------ +SQMOD_DECL_TYPENAME(SqDpClusterTypename, _SC("SqDiscordCluster")) +SQMOD_DECL_TYPENAME(SqDpClusterOptionsTypename, _SC("SqDiscordClusterOptions")) + +// ------------------------------------------------------------------------------------------------ +DpCluster::DpCluster(DpClusterOptions & o) + : Base(), mQueue(4096) + , mC(std::make_unique< dpp::cluster >(o.mToken, o.mIntents, o.mShards, o.mClusterID, o.mMaxClusters, o.mCompressed, o.mPolicy, o.mRequestThreads, o.mRequestThreadsRaw)) + , mSqEvents(), mEvents(), mEventsHandle() +{ + // Make sure all event handles are not valid + mEventsHandle.fill(0); + // Initialize event signals + InitEvents(); + // Proxy library logging to our logger + if (!o.mCustomLogging) + { + mC->on_log([](const dpp::log_t& event) { + switch (event.severity) + { + case dpp::ll_trace: Logger::Get().Send(LOGL_DBG, true, event.message.c_str(), event.message.size()); break; + case dpp::ll_debug: Logger::Get().Send(LOGL_DBG, true, event.message.c_str(), event.message.size()); break; + case dpp::ll_info: Logger::Get().Send(LOGL_INF, true, event.message.c_str(), event.message.size()); break; + case dpp::ll_warning: Logger::Get().Send(LOGL_WRN, true, event.message.c_str(), event.message.size()); break; + case dpp::ll_error: Logger::Get().Send(LOGL_ERR, true, event.message.c_str(), event.message.size()); break; + case dpp::ll_critical: Logger::Get().Send(LOGL_FTL, true, event.message.c_str(), event.message.size()); break; + default: break; + } + }); + } + // Remember this instance + ChainInstance(); +} + +// ------------------------------------------------------------------------------------------------ +SQMOD_NODISCARD LightObj EventToScriptObject(uint8_t type, uintptr_t data); +void EventInvokeCleanup(uint8_t type, uintptr_t data); + +// ------------------------------------------------------------------------------------------------ +void DpCluster::Process(bool force) +{ + // Is there a valid connection? + if (!mC && !force) + { + return; // No point in going forward + } + EventItem event; + // Retrieve each event individually and process it + for (size_t count = mQueue.size_approx(), n = 0; n <= count; ++n) + { + // Try to get an event from the queue + if (mQueue.try_dequeue(event)) + { + // Fetch the type of event + const auto id = static_cast< size_t >(event->GetEventID()); + // Is this a valid event and is anyone listening to it? + if (!(event->mFrom) || !(mEvents[id].first) || mEvents[id].first->IsEmpty()) + { + continue; // Move on + } + // Transform the event instance into a script object + LightObj obj = event->ToScriptObject(); + // Allow the script to take ownership of the event instance now + [[maybe_unused]] auto p = event.release(); + // Don't abort everything down the line for an error caused by a discord event handler + try { + (*mEvents[id].first)(obj); // Forward the call to the associated signal + } catch (const std::exception & e) { + LogErr("Squirrel exception caught in (%s) discord event", p->GetEventName().data()); + LogSInf("Message: %s", e.what()); + } + // Allow the event instance to clean itself (i.e. invalidate itself) + // User should not keep this event object for later use! + // Event data is accessible only during the event signal + p->Cleanup(); + } + } +} + +// ------------------------------------------------------------------------------------------------ +void DpCluster::Terminate() +{ + // Stop the cluster connection + if (mC) mC->shutdown(); + // Release associated script objects + mSqEvents.Release(); + // Release event signal objects + DropEvents(); + // Delete the cluster instance + mC.reset(); +} + +// ================================================================================================ +void Register_Discord_Cluster(HSQUIRRELVM vm, Table & ns) +{ + ns.Bind(_SC("ClusterOptions"), + Class< DpClusterOptions >(vm, SqDpClusterOptionsTypename::Str) + // Constructors + .Ctor< StackStrF & >() + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpClusterOptionsTypename::Fn) + ); + + ns.Bind(_SC("Cluster"), + Class< DpCluster, NoCopy< DpCluster > >(vm, SqDpClusterTypename::Str) + // Constructors + .Ctor< DpClusterOptions & >() + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpClusterTypename::Fn) + // Member Properties + .Prop(_SC("On"), &DpCluster::GetEvents) + // Member Methods + .Func(_SC("Start"), &DpCluster::Start) + .Func(_SC("Stop"), &DpCluster::Stop) + .Func(_SC("EnableEvent"), &DpCluster::EnableEvent) + .Func(_SC("DisableEvent"), &DpCluster::DisableEvent) + ); +} + +// ------------------------------------------------------------------------------------------------ +DpCluster & DpCluster::EnableEvent(SQInteger id) +{ + // Retrieve managed cluster instance + auto & c = Valid("enable event on a"); + // Assigned event handle + dpp::event_handle eh = 0; + // Make sure the specified event handle is valid + if (id >= 0 && id < static_cast< SQInteger >(DpEventID::Max)) + { + eh = mEventsHandle[static_cast< size_t >(id)]; // Get the real handle + } + // Is this event already enabled? + if (eh != 0) + { + return *this; // Job already done + } + // Identify event type + switch (id) + { + case DpEventID::VoiceStateUpdate: eh = c.on_voice_state_update.attach([this](const dpp::voice_state_update_t & e) { OnVoiceStateUpdate(e); }); break; + case DpEventID::VoiceClientDisconnect: eh = c.on_voice_client_disconnect.attach([this](const dpp::voice_client_disconnect_t & e) { OnVoiceClientDisconnect(e); }); break; + case DpEventID::VoiceClientSpeaking: eh = c.on_voice_client_speaking.attach([this](const dpp::voice_client_speaking_t & e) { OnVoiceClientSpeaking(e); }); break; + case DpEventID::Log: eh = c.on_log.attach([this](const dpp::log_t & e) { OnLog(e); }); break; + case DpEventID::GuildJoinRequestDelete: eh = c.on_guild_join_request_delete.attach([this](const dpp::guild_join_request_delete_t & e) { OnGuildJoinRequestDelete(e); }); break; + case DpEventID::InteractionCreate: eh = c.on_interaction_create.attach([this](const dpp::interaction_create_t & e) { OnInteractionCreate(e); }); break; + case DpEventID::SlashCommand: eh = c.on_slashcommand.attach([this](const dpp::slashcommand_t & e) { OnSlashCommand(e); }); break; + case DpEventID::ButtonClick: eh = c.on_button_click.attach([this](const dpp::button_click_t & e) { OnButtonClick(e); }); break; + case DpEventID::AutoComplete: eh = c.on_autocomplete.attach([this](const dpp::autocomplete_t & e) { OnAutoComplete(e); }); break; + case DpEventID::SelectClick: eh = c.on_select_click.attach([this](const dpp::select_click_t & e) { OnSelectClick(e); }); break; + case DpEventID::MessageContextMenu: eh = c.on_message_context_menu.attach([this](const dpp::message_context_menu_t & e) { OnMessageContextMenu(e); }); break; + case DpEventID::UserContextMenu: eh = c.on_user_context_menu.attach([this](const dpp::user_context_menu_t & e) { OnUserContextMenu(e); }); break; + case DpEventID::FormSubmit: eh = c.on_form_submit.attach([this](const dpp::form_submit_t & e) { OnFormSubmit(e); }); break; + case DpEventID::GuildDelete: eh = c.on_guild_delete.attach([this](const dpp::guild_delete_t & e) { OnGuildDelete(e); }); break; + case DpEventID::ChannelDelete: eh = c.on_channel_delete.attach([this](const dpp::channel_delete_t & e) { OnChannelDelete(e); }); break; + case DpEventID::ChannelUpdate: eh = c.on_channel_update.attach([this](const dpp::channel_update_t & e) { OnChannelUpdate(e); }); break; + case DpEventID::Ready: eh = c.on_ready.attach([this](const dpp::ready_t & e) { OnReady(e); }); break; + case DpEventID::MessageDelete: eh = c.on_message_delete.attach([this](const dpp::message_delete_t & e) { OnMessageDelete(e); }); break; + case DpEventID::GuildMemberRemove: eh = c.on_guild_member_remove.attach([this](const dpp::guild_member_remove_t & e) { OnGuildMemberRemove(e); }); break; + case DpEventID::Resumed: eh = c.on_resumed.attach([this](const dpp::resumed_t & e) { OnResumed(e); }); break; + case DpEventID::GuildRoleCreate: eh = c.on_guild_role_create.attach([this](const dpp::guild_role_create_t & e) { OnGuildRoleCreate(e); }); break; + case DpEventID::TypingStart: eh = c.on_typing_start.attach([this](const dpp::typing_start_t & e) { OnTypingStart(e); }); break; + case DpEventID::MessageReactionAdd: eh = c.on_message_reaction_add.attach([this](const dpp::message_reaction_add_t & e) { OnMessageReactionAdd(e); }); break; + case DpEventID::GuildMembersChunk: eh = c.on_guild_members_chunk.attach([this](const dpp::guild_members_chunk_t & e) { OnGuildMembersChunk(e); }); break; + case DpEventID::MessageReactionRemove: eh = c.on_message_reaction_remove.attach([this](const dpp::message_reaction_remove_t & e) { OnMessageReactionRemove(e); }); break; + case DpEventID::GuildCreate: eh = c.on_guild_create.attach([this](const dpp::guild_create_t & e) { OnGuildCreate(e); }); break; + case DpEventID::ChannelCreate: eh = c.on_channel_create.attach([this](const dpp::channel_create_t & e) { OnChannelCreate(e); }); break; + case DpEventID::MessageReactionRemoveEmoji: eh = c.on_message_reaction_remove_emoji.attach([this](const dpp::message_reaction_remove_emoji_t & e) { OnMessageReactionRemoveEmoji(e); }); break; + case DpEventID::MessageDeleteDulk: eh = c.on_message_delete_bulk.attach([this](const dpp::message_delete_bulk_t & e) { OnMessageDeleteDulk(e); }); break; + case DpEventID::GuildRoleUpdate: eh = c.on_guild_role_update.attach([this](const dpp::guild_role_update_t & e) { OnGuildRoleUpdate(e); }); break; + case DpEventID::GuildRoleDelete: eh = c.on_guild_role_delete.attach([this](const dpp::guild_role_delete_t & e) { OnGuildRoleDelete(e); }); break; + case DpEventID::ChannelPinsUpdate: eh = c.on_channel_pins_update.attach([this](const dpp::channel_pins_update_t & e) { OnChannelPinsUpdate(e); }); break; + case DpEventID::MessageReactionRemoveAll: eh = c.on_message_reaction_remove_all.attach([this](const dpp::message_reaction_remove_all_t & e) { OnMessageReactionRemoveAll(e); }); break; + case DpEventID::VoiceServerUpdate: eh = c.on_voice_server_update.attach([this](const dpp::voice_server_update_t & e) { OnVoiceServerUpdate(e); }); break; + case DpEventID::GuildEmojisUpdate: eh = c.on_guild_emojis_update.attach([this](const dpp::guild_emojis_update_t & e) { OnGuildEmojisUpdate(e); }); break; + case DpEventID::GuildStickersUpdate: eh = c.on_guild_stickers_update.attach([this](const dpp::guild_stickers_update_t & e) { OnGuildStickersUpdate(e); }); break; + case DpEventID::PresenceUpdate: eh = c.on_presence_update.attach([this](const dpp::presence_update_t & e) { OnPresenceUpdate(e); }); break; + case DpEventID::WebhooksUpdate: eh = c.on_webhooks_update.attach([this](const dpp::webhooks_update_t & e) { OnWebhooksUpdate(e); }); break; + case DpEventID::AutomodRuleCreate: eh = c.on_automod_rule_create.attach([this](const dpp::automod_rule_create_t & e) { OnAutomodRuleCreate(e); }); break; + case DpEventID::AutomodRuleUpdate: eh = c.on_automod_rule_update.attach([this](const dpp::automod_rule_update_t & e) { OnAutomodRuleUpdate(e); }); break; + case DpEventID::AutomodRuleDelete: eh = c.on_automod_rule_delete.attach([this](const dpp::automod_rule_delete_t & e) { OnAutomodRuleDelete(e); }); break; + case DpEventID::AutomodRuleExecute: eh = c.on_automod_rule_execute.attach([this](const dpp::automod_rule_execute_t & e) { OnAutomodRuleExecute(e); }); break; + case DpEventID::GuildMemberAdd: eh = c.on_guild_member_add.attach([this](const dpp::guild_member_add_t & e) { OnGuildMemberAdd(e); }); break; + case DpEventID::InviteDelete: eh = c.on_invite_delete.attach([this](const dpp::invite_delete_t & e) { OnInviteDelete(e); }); break; + case DpEventID::GuildUpdate: eh = c.on_guild_update.attach([this](const dpp::guild_update_t & e) { OnGuildUpdate(e); }); break; + case DpEventID::GuildIntegrationsUpdate: eh = c.on_guild_integrations_update.attach([this](const dpp::guild_integrations_update_t & e) { OnGuildIntegrationsUpdate(e); }); break; + case DpEventID::GuildMemberUpdate: eh = c.on_guild_member_update.attach([this](const dpp::guild_member_update_t & e) { OnGuildMemberUpdate(e); }); break; + case DpEventID::InviteCreate: eh = c.on_invite_create.attach([this](const dpp::invite_create_t & e) { OnInviteCreate(e); }); break; + case DpEventID::MessageUpdate: eh = c.on_message_update.attach([this](const dpp::message_update_t & e) { OnMessageUpdate(e); }); break; + case DpEventID::UserUpdate: eh = c.on_user_update.attach([this](const dpp::user_update_t & e) { OnUserUpdate(e); }); break; + case DpEventID::MessageCreate: eh = c.on_message_create.attach([this](const dpp::message_create_t & e) { OnMessageCreate(e); }); break; + case DpEventID::GuildAuditLogEntryCreate: eh = c.on_guild_audit_log_entry_create.attach([this](const dpp::guild_audit_log_entry_create_t & e) { OnGuildAuditLogEntryCreate(e); }); break; + case DpEventID::GuildBanAdd: eh = c.on_guild_ban_add.attach([this](const dpp::guild_ban_add_t & e) { OnGuildBanAdd(e); }); break; + case DpEventID::GuildBanRemove: eh = c.on_guild_ban_remove.attach([this](const dpp::guild_ban_remove_t & e) { OnGuildBanRemove(e); }); break; + case DpEventID::IntegrationCreate: eh = c.on_integration_create.attach([this](const dpp::integration_create_t & e) { OnIntegrationCreate(e); }); break; + case DpEventID::IntegrationUpdate: eh = c.on_integration_update.attach([this](const dpp::integration_update_t & e) { OnIntegrationUpdate(e); }); break; + case DpEventID::IntegrationDelete: eh = c.on_integration_delete.attach([this](const dpp::integration_delete_t & e) { OnIntegrationDelete(e); }); break; + case DpEventID::ThreadCreate: eh = c.on_thread_create.attach([this](const dpp::thread_create_t & e) { OnThreadCreate(e); }); break; + case DpEventID::ThreadUpdate: eh = c.on_thread_update.attach([this](const dpp::thread_update_t & e) { OnThreadUpdate(e); }); break; + case DpEventID::ThreadDelete: eh = c.on_thread_delete.attach([this](const dpp::thread_delete_t & e) { OnThreadDelete(e); }); break; + case DpEventID::ThreadListSync: eh = c.on_thread_list_sync.attach([this](const dpp::thread_list_sync_t & e) { OnThreadListSync(e); }); break; + case DpEventID::ThreadMemberUpdate: eh = c.on_thread_member_update.attach([this](const dpp::thread_member_update_t & e) { OnThreadMemberUpdate(e); }); break; + case DpEventID::ThreadMembersUpdate: eh = c.on_thread_members_update.attach([this](const dpp::thread_members_update_t & e) { OnThreadMembersUpdate(e); }); break; + case DpEventID::GuildScheduledEventCreate: eh = c.on_guild_scheduled_event_create.attach([this](const dpp::guild_scheduled_event_create_t & e) { OnGuildScheduledEventCreate(e); }); break; + case DpEventID::GuildScheduledEventUpdate: eh = c.on_guild_scheduled_event_update.attach([this](const dpp::guild_scheduled_event_update_t & e) { OnGuildScheduledEventUpdate(e); }); break; + case DpEventID::GuildScheduledEventDelete: eh = c.on_guild_scheduled_event_delete.attach([this](const dpp::guild_scheduled_event_delete_t & e) { OnGuildScheduledEventDelete(e); }); break; + case DpEventID::GuildScheduledEventUserAdd: eh = c.on_guild_scheduled_event_user_add.attach([this](const dpp::guild_scheduled_event_user_add_t & e) { OnGuildScheduledEventUserAdd(e); }); break; + case DpEventID::GuildScheduledEventUserRemove: eh = c.on_guild_scheduled_event_user_remove.attach([this](const dpp::guild_scheduled_event_user_remove_t & e) { OnGuildScheduledEventUserRemove(e); }); break; + case DpEventID::VoiceBufferSend: eh = c.on_voice_buffer_send.attach([this](const dpp::voice_buffer_send_t & e) { OnVoiceBufferSend(e); }); break; + case DpEventID::VoiceUserTalking: eh = c.on_voice_user_talking.attach([this](const dpp::voice_user_talking_t & e) { OnVoiceUserTalking(e); }); break; + case DpEventID::VoiceReady: eh = c.on_voice_ready.attach([this](const dpp::voice_ready_t & e) { OnVoiceReady(e); }); break; + case DpEventID::VoiceReceive: eh = c.on_voice_receive.attach([this](const dpp::voice_receive_t & e) { OnVoiceReceive(e); }); break; + case DpEventID::VoiceReceiveCombined: eh = c.on_voice_receive_combined.attach([this](const dpp::voice_receive_t & e) { OnVoiceReceiveCombined(e); }); break; + case DpEventID::VoiceTrackMarker: eh = c.on_voice_track_marker.attach([this](const dpp::voice_track_marker_t & e) { OnVoiceTrackMarker(e); }); break; + case DpEventID::StageInstanceCreate: eh = c.on_stage_instance_create.attach([this](const dpp::stage_instance_create_t & e) { OnStageInstanceCreate(e); }); break; + case DpEventID::StageInstanceUpdate: eh = c.on_stage_instance_update.attach([this](const dpp::stage_instance_update_t & e) { OnStageInstanceUpdate(e); }); break; + case DpEventID::StageInstanceDelete: eh = c.on_stage_instance_delete.attach([this](const dpp::stage_instance_delete_t & e) { OnStageInstanceDelete(e); }); break; + case DpEventID::Max: // Fall through + default: STHROWF("Invalid discord event identifier {}", id); + } + // Remember the designated event handle + mEventsHandle[static_cast< size_t >(id)] = eh; + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +DpCluster & DpCluster::DisableEvent(SQInteger id) +{ + // Retrieve managed cluster instance + auto & c = Valid("disable event on a"); + // Assigned event handle + dpp::event_handle eh = 0; + // Make sure the specified event handle is valid + if (id >= 0 && id < static_cast< SQInteger >(DpEventID::Max)) + { + eh = mEventsHandle[static_cast< size_t >(id)]; // Get the real handle + } + // Is there anything attached to event dispatched? + if (eh == 0) + { + return *this; // Nothing to detach + } + // Identify event type + switch (id) + { + case DpEventID::VoiceStateUpdate: c.on_voice_state_update.detach(eh); break; + case DpEventID::VoiceClientDisconnect: c.on_voice_client_disconnect.detach(eh); break; + case DpEventID::VoiceClientSpeaking: c.on_voice_client_speaking.detach(eh); break; + case DpEventID::Log: c.on_log.detach(eh); break; + case DpEventID::GuildJoinRequestDelete: c.on_guild_join_request_delete.detach(eh); break; + case DpEventID::InteractionCreate: c.on_interaction_create.detach(eh); break; + case DpEventID::SlashCommand: c.on_slashcommand.detach(eh); break; + case DpEventID::ButtonClick: c.on_button_click.detach(eh); break; + case DpEventID::AutoComplete: c.on_autocomplete.detach(eh); break; + case DpEventID::SelectClick: c.on_select_click.detach(eh); break; + case DpEventID::MessageContextMenu: c.on_message_context_menu.detach(eh); break; + case DpEventID::UserContextMenu: c.on_user_context_menu.detach(eh); break; + case DpEventID::FormSubmit: c.on_form_submit.detach(eh); break; + case DpEventID::GuildDelete: c.on_guild_delete.detach(eh); break; + case DpEventID::ChannelDelete: c.on_channel_delete.detach(eh); break; + case DpEventID::ChannelUpdate: c.on_channel_update.detach(eh); break; + case DpEventID::Ready: c.on_ready.detach(eh); break; + case DpEventID::MessageDelete: c.on_message_delete.detach(eh); break; + case DpEventID::GuildMemberRemove: c.on_guild_member_remove.detach(eh); break; + case DpEventID::Resumed: c.on_resumed.detach(eh); break; + case DpEventID::GuildRoleCreate: c.on_guild_role_create.detach(eh); break; + case DpEventID::TypingStart: c.on_typing_start.detach(eh); break; + case DpEventID::MessageReactionAdd: c.on_message_reaction_add.detach(eh); break; + case DpEventID::GuildMembersChunk: c.on_guild_members_chunk.detach(eh); break; + case DpEventID::MessageReactionRemove: c.on_message_reaction_remove.detach(eh); break; + case DpEventID::GuildCreate: c.on_guild_create.detach(eh); break; + case DpEventID::ChannelCreate: c.on_channel_create.detach(eh); break; + case DpEventID::MessageReactionRemoveEmoji: c.on_message_reaction_remove_emoji.detach(eh); break; + case DpEventID::MessageDeleteDulk: c.on_message_delete_bulk.detach(eh); break; + case DpEventID::GuildRoleUpdate: c.on_guild_role_update.detach(eh); break; + case DpEventID::GuildRoleDelete: c.on_guild_role_delete.detach(eh); break; + case DpEventID::ChannelPinsUpdate: c.on_channel_pins_update.detach(eh); break; + case DpEventID::MessageReactionRemoveAll: c.on_message_reaction_remove_all.detach(eh); break; + case DpEventID::VoiceServerUpdate: c.on_voice_server_update.detach(eh); break; + case DpEventID::GuildEmojisUpdate: c.on_guild_emojis_update.detach(eh); break; + case DpEventID::GuildStickersUpdate: c.on_guild_stickers_update.detach(eh); break; + case DpEventID::PresenceUpdate: c.on_presence_update.detach(eh); break; + case DpEventID::WebhooksUpdate: c.on_webhooks_update.detach(eh); break; + case DpEventID::AutomodRuleCreate: c.on_automod_rule_create.detach(eh); break; + case DpEventID::AutomodRuleUpdate: c.on_automod_rule_update.detach(eh); break; + case DpEventID::AutomodRuleDelete: c.on_automod_rule_delete.detach(eh); break; + case DpEventID::AutomodRuleExecute: c.on_automod_rule_execute.detach(eh); break; + case DpEventID::GuildMemberAdd: c.on_guild_member_add.detach(eh); break; + case DpEventID::InviteDelete: c.on_invite_delete.detach(eh); break; + case DpEventID::GuildUpdate: c.on_guild_update.detach(eh); break; + case DpEventID::GuildIntegrationsUpdate: c.on_guild_integrations_update.detach(eh); break; + case DpEventID::GuildMemberUpdate: c.on_guild_member_update.detach(eh); break; + case DpEventID::InviteCreate: c.on_invite_create.detach(eh); break; + case DpEventID::MessageUpdate: c.on_message_update.detach(eh); break; + case DpEventID::UserUpdate: c.on_user_update.detach(eh); break; + case DpEventID::MessageCreate: c.on_message_create.detach(eh); break; + case DpEventID::GuildAuditLogEntryCreate: c.on_guild_audit_log_entry_create.detach(eh); break; + case DpEventID::GuildBanAdd: c.on_guild_ban_add.detach(eh); break; + case DpEventID::GuildBanRemove: c.on_guild_ban_remove.detach(eh); break; + case DpEventID::IntegrationCreate: c.on_integration_create.detach(eh); break; + case DpEventID::IntegrationUpdate: c.on_integration_update.detach(eh); break; + case DpEventID::IntegrationDelete: c.on_integration_delete.detach(eh); break; + case DpEventID::ThreadCreate: c.on_thread_create.detach(eh); break; + case DpEventID::ThreadUpdate: c.on_thread_update.detach(eh); break; + case DpEventID::ThreadDelete: c.on_thread_delete.detach(eh); break; + case DpEventID::ThreadListSync: c.on_thread_list_sync.detach(eh); break; + case DpEventID::ThreadMemberUpdate: c.on_thread_member_update.detach(eh); break; + case DpEventID::ThreadMembersUpdate: c.on_thread_members_update.detach(eh); break; + case DpEventID::GuildScheduledEventCreate: c.on_guild_scheduled_event_create.detach(eh); break; + case DpEventID::GuildScheduledEventUpdate: c.on_guild_scheduled_event_update.detach(eh); break; + case DpEventID::GuildScheduledEventDelete: c.on_guild_scheduled_event_delete.detach(eh); break; + case DpEventID::GuildScheduledEventUserAdd: c.on_guild_scheduled_event_user_add.detach(eh); break; + case DpEventID::GuildScheduledEventUserRemove: c.on_guild_scheduled_event_user_remove.detach(eh); break; + case DpEventID::VoiceBufferSend: c.on_voice_buffer_send.detach(eh); break; + case DpEventID::VoiceUserTalking: c.on_voice_user_talking.detach(eh); break; + case DpEventID::VoiceReady: c.on_voice_ready.detach(eh); break; + case DpEventID::VoiceReceive: c.on_voice_receive.detach(eh); break; + case DpEventID::VoiceReceiveCombined: c.on_voice_receive_combined.detach(eh); break; + case DpEventID::VoiceTrackMarker: c.on_voice_track_marker.detach(eh); break; + case DpEventID::StageInstanceCreate: c.on_stage_instance_create.detach(eh); break; + case DpEventID::StageInstanceUpdate: c.on_stage_instance_update.detach(eh); break; + case DpEventID::StageInstanceDelete: c.on_stage_instance_delete.detach(eh); break; + case DpEventID::Max: // Fall through + default: STHROWF("Invalid discord event identifier {}", id); + } + // Forget about this event handler + mEventsHandle[static_cast< size_t >(id)] = 0; + // Allow chaining + return *this; +} + +// ------------------------------------------------------------------------------------------------ +void DpCluster::OnVoiceStateUpdate(const dpp::voice_state_update_t & ev) +{ mQueue.enqueue(EventItem(new DpVoiceStateUpdateEvent(ev))); } +void DpCluster::OnVoiceClientDisconnect(const dpp::voice_client_disconnect_t & ev) +{ mQueue.enqueue(EventItem(new DpVoiceClientDisconnectEvent(ev))); } +void DpCluster::OnVoiceClientSpeaking(const dpp::voice_client_speaking_t & ev) +{ mQueue.enqueue(EventItem(new DpVoiceClientSpeakingEvent(ev))); } +void DpCluster::OnLog(const dpp::log_t & ev) +{ mQueue.enqueue(EventItem(new DpLogEvent(ev))); } +void DpCluster::OnGuildJoinRequestDelete(const dpp::guild_join_request_delete_t & ev) +{ mQueue.enqueue(EventItem(new DpGuildJoinRequestDeleteEvent(ev))); } +void DpCluster::OnInteractionCreate(const dpp::interaction_create_t & ev) +{ mQueue.enqueue(EventItem(new DpInteractionCreateEvent(ev))); } +void DpCluster::OnSlashCommand(const dpp::slashcommand_t & ev) +{ mQueue.enqueue(EventItem(new DpSlashCommandEvent(ev))); } +void DpCluster::OnButtonClick(const dpp::button_click_t & ev) +{ mQueue.enqueue(EventItem(new DpButtonClickEvent(ev))); } +void DpCluster::OnAutoComplete(const dpp::autocomplete_t & ev) +{ mQueue.enqueue(EventItem(new DpAutoCompleteEvent(ev))); } +void DpCluster::OnSelectClick(const dpp::select_click_t & ev) +{ mQueue.enqueue(EventItem(new DpSelectClickEvent(ev))); } +void DpCluster::OnMessageContextMenu(const dpp::message_context_menu_t & ev) +{ mQueue.enqueue(EventItem(new DpMessageContextMenuEvent(ev))); } +void DpCluster::OnUserContextMenu(const dpp::user_context_menu_t & ev) +{ mQueue.enqueue(EventItem(new DpUserContextMenuEvent(ev))); } +void DpCluster::OnFormSubmit(const dpp::form_submit_t & ev) +{ mQueue.enqueue(EventItem(new DpFormSubmitEvent(ev))); } +void DpCluster::OnGuildDelete(const dpp::guild_delete_t & ev) +{ mQueue.enqueue(EventItem(new DpGuildDeleteEvent(ev))); } +void DpCluster::OnChannelDelete(const dpp::channel_delete_t & ev) +{ mQueue.enqueue(EventItem(new DpChannelDeleteEvent(ev))); } +void DpCluster::OnChannelUpdate(const dpp::channel_update_t & ev) +{ mQueue.enqueue(EventItem(new DpChannelUpdateEvent(ev))); } +void DpCluster::OnReady(const dpp::ready_t & ev) +{ mQueue.enqueue(EventItem(new DpReadyEvent(ev))); } +void DpCluster::OnMessageDelete(const dpp::message_delete_t & ev) +{ mQueue.enqueue(EventItem(new DpMessageDeleteEvent(ev))); } +void DpCluster::OnGuildMemberRemove(const dpp::guild_member_remove_t & ev) +{ mQueue.enqueue(EventItem(new DpGuildMemberRemoveEvent(ev))); } +void DpCluster::OnResumed(const dpp::resumed_t & ev) +{ mQueue.enqueue(EventItem(new DpResumedEvent(ev))); } +void DpCluster::OnGuildRoleCreate(const dpp::guild_role_create_t & ev) +{ mQueue.enqueue(EventItem(new DpGuildRoleCreateEvent(ev))); } +void DpCluster::OnTypingStart(const dpp::typing_start_t & ev) +{ mQueue.enqueue(EventItem(new DpTypingStartEvent(ev))); } +void DpCluster::OnMessageReactionAdd(const dpp::message_reaction_add_t & ev) +{ mQueue.enqueue(EventItem(new DpMessageReactionAddEvent(ev))); } +void DpCluster::OnGuildMembersChunk(const dpp::guild_members_chunk_t & ev) +{ mQueue.enqueue(EventItem(new DpGuildMembersChunkEvent(ev))); } +void DpCluster::OnMessageReactionRemove(const dpp::message_reaction_remove_t & ev) +{ mQueue.enqueue(EventItem(new DpMessageReactionRemoveEvent(ev))); } +void DpCluster::OnGuildCreate(const dpp::guild_create_t & ev) +{ mQueue.enqueue(EventItem(new DpGuildCreateEvent(ev))); } +void DpCluster::OnChannelCreate(const dpp::channel_create_t & ev) +{ mQueue.enqueue(EventItem(new DpChannelCreateEvent(ev))); } +void DpCluster::OnMessageReactionRemoveEmoji(const dpp::message_reaction_remove_emoji_t & ev) +{ mQueue.enqueue(EventItem(new DpMessageReactionRemoveEmojiEvent(ev))); } +void DpCluster::OnMessageDeleteDulk(const dpp::message_delete_bulk_t & ev) +{ mQueue.enqueue(EventItem(new DpMessageDeleteDulkEvent(ev))); } +void DpCluster::OnGuildRoleUpdate(const dpp::guild_role_update_t & ev) +{ mQueue.enqueue(EventItem(new DpGuildRoleUpdateEvent(ev))); } +void DpCluster::OnGuildRoleDelete(const dpp::guild_role_delete_t & ev) +{ mQueue.enqueue(EventItem(new DpGuildRoleDeleteEvent(ev))); } +void DpCluster::OnChannelPinsUpdate(const dpp::channel_pins_update_t & ev) +{ mQueue.enqueue(EventItem(new DpChannelPinsUpdateEvent(ev))); } +void DpCluster::OnMessageReactionRemoveAll(const dpp::message_reaction_remove_all_t & ev) +{ mQueue.enqueue(EventItem(new DpMessageReactionRemoveAllEvent(ev))); } +void DpCluster::OnVoiceServerUpdate(const dpp::voice_server_update_t & ev) +{ mQueue.enqueue(EventItem(new DpVoiceServerUpdateEvent(ev))); } +void DpCluster::OnGuildEmojisUpdate(const dpp::guild_emojis_update_t & ev) +{ mQueue.enqueue(EventItem(new DpGuildEmojisUpdateEvent(ev))); } +void DpCluster::OnGuildStickersUpdate(const dpp::guild_stickers_update_t & ev) +{ mQueue.enqueue(EventItem(new DpGuildStickersUpdateEvent(ev))); } +void DpCluster::OnPresenceUpdate(const dpp::presence_update_t & ev) +{ mQueue.enqueue(EventItem(new DpPresenceUpdateEvent(ev))); } +void DpCluster::OnWebhooksUpdate(const dpp::webhooks_update_t & ev) +{ mQueue.enqueue(EventItem(new DpWebhooksUpdateEvent(ev))); } +void DpCluster::OnAutomodRuleCreate(const dpp::automod_rule_create_t & ev) +{ mQueue.enqueue(EventItem(new DpAutomodRuleCreateEvent(ev))); } +void DpCluster::OnAutomodRuleUpdate(const dpp::automod_rule_update_t & ev) +{ mQueue.enqueue(EventItem(new DpAutomodRuleUpdateEvent(ev))); } +void DpCluster::OnAutomodRuleDelete(const dpp::automod_rule_delete_t & ev) +{ mQueue.enqueue(EventItem(new DpAutomodRuleDeleteEvent(ev))); } +void DpCluster::OnAutomodRuleExecute(const dpp::automod_rule_execute_t & ev) +{ mQueue.enqueue(EventItem(new DpAutomodRuleExecuteEvent(ev))); } +void DpCluster::OnGuildMemberAdd(const dpp::guild_member_add_t & ev) +{ mQueue.enqueue(EventItem(new DpGuildMemberAddEvent(ev))); } +void DpCluster::OnInviteDelete(const dpp::invite_delete_t & ev) +{ mQueue.enqueue(EventItem(new DpInviteDeleteEvent(ev))); } +void DpCluster::OnGuildUpdate(const dpp::guild_update_t & ev) +{ mQueue.enqueue(EventItem(new DpGuildUpdateEvent(ev))); } +void DpCluster::OnGuildIntegrationsUpdate(const dpp::guild_integrations_update_t & ev) +{ mQueue.enqueue(EventItem(new DpGuildIntegrationsUpdateEvent(ev))); } +void DpCluster::OnGuildMemberUpdate(const dpp::guild_member_update_t & ev) +{ mQueue.enqueue(EventItem(new DpGuildMemberUpdateEvent(ev))); } +void DpCluster::OnInviteCreate(const dpp::invite_create_t & ev) +{ mQueue.enqueue(EventItem(new DpInviteCreateEvent(ev))); } +void DpCluster::OnMessageUpdate(const dpp::message_update_t & ev) +{ mQueue.enqueue(EventItem(new DpMessageUpdateEvent(ev))); } +void DpCluster::OnUserUpdate(const dpp::user_update_t & ev) +{ mQueue.enqueue(EventItem(new DpUserUpdateEvent(ev))); } +void DpCluster::OnMessageCreate(const dpp::message_create_t & ev) +{ mQueue.enqueue(EventItem(new DpMessageCreateEvent(ev))); } +void DpCluster::OnGuildAuditLogEntryCreate(const dpp::guild_audit_log_entry_create_t & ev) +{ mQueue.enqueue(EventItem(new DpGuildAuditLogEntryCreateEvent(ev))); } +void DpCluster::OnGuildBanAdd(const dpp::guild_ban_add_t & ev) +{ mQueue.enqueue(EventItem(new DpGuildBanAddEvent(ev))); } +void DpCluster::OnGuildBanRemove(const dpp::guild_ban_remove_t & ev) +{ mQueue.enqueue(EventItem(new DpGuildBanRemoveEvent(ev))); } +void DpCluster::OnIntegrationCreate(const dpp::integration_create_t & ev) +{ mQueue.enqueue(EventItem(new DpIntegrationCreateEvent(ev))); } +void DpCluster::OnIntegrationUpdate(const dpp::integration_update_t & ev) +{ mQueue.enqueue(EventItem(new DpIntegrationUpdateEvent(ev))); } +void DpCluster::OnIntegrationDelete(const dpp::integration_delete_t & ev) +{ mQueue.enqueue(EventItem(new DpIntegrationDeleteEvent(ev))); } +void DpCluster::OnThreadCreate(const dpp::thread_create_t & ev) +{ mQueue.enqueue(EventItem(new DpThreadCreateEvent(ev))); } +void DpCluster::OnThreadUpdate(const dpp::thread_update_t & ev) +{ mQueue.enqueue(EventItem(new DpThreadUpdateEvent(ev))); } +void DpCluster::OnThreadDelete(const dpp::thread_delete_t & ev) +{ mQueue.enqueue(EventItem(new DpThreadDeleteEvent(ev))); } +void DpCluster::OnThreadListSync(const dpp::thread_list_sync_t & ev) +{ mQueue.enqueue(EventItem(new DpThreadListSyncEvent(ev))); } +void DpCluster::OnThreadMemberUpdate(const dpp::thread_member_update_t & ev) +{ mQueue.enqueue(EventItem(new DpThreadMemberUpdateEvent(ev))); } +void DpCluster::OnThreadMembersUpdate(const dpp::thread_members_update_t & ev) +{ mQueue.enqueue(EventItem(new DpThreadMembersUpdateEvent(ev))); } +void DpCluster::OnGuildScheduledEventCreate(const dpp::guild_scheduled_event_create_t & ev) +{ mQueue.enqueue(EventItem(new DpGuildScheduledEventCreateEvent(ev))); } +void DpCluster::OnGuildScheduledEventUpdate(const dpp::guild_scheduled_event_update_t & ev) +{ mQueue.enqueue(EventItem(new DpGuildScheduledEventUpdateEvent(ev))); } +void DpCluster::OnGuildScheduledEventDelete(const dpp::guild_scheduled_event_delete_t & ev) +{ mQueue.enqueue(EventItem(new DpGuildScheduledEventDeleteEvent(ev))); } +void DpCluster::OnGuildScheduledEventUserAdd(const dpp::guild_scheduled_event_user_add_t & ev) +{ mQueue.enqueue(EventItem(new DpGuildScheduledEventUserAddEvent(ev))); } +void DpCluster::OnGuildScheduledEventUserRemove(const dpp::guild_scheduled_event_user_remove_t & ev) +{ mQueue.enqueue(EventItem(new DpGuildScheduledEventUserRemoveEvent(ev))); } +void DpCluster::OnVoiceBufferSend(const dpp::voice_buffer_send_t & ev) +{ mQueue.enqueue(EventItem(new DpVoiceBufferSendEvent(ev))); } +void DpCluster::OnVoiceUserTalking(const dpp::voice_user_talking_t & ev) +{ mQueue.enqueue(EventItem(new DpVoiceUserTalkingEvent(ev))); } +void DpCluster::OnVoiceReady(const dpp::voice_ready_t & ev) +{ mQueue.enqueue(EventItem(new DpVoiceReadyEvent(ev))); } +void DpCluster::OnVoiceReceive(const dpp::voice_receive_t & ev) +{ mQueue.enqueue(EventItem(new DpVoiceReceiveEvent(ev))); } +void DpCluster::OnVoiceReceiveCombined(const dpp::voice_receive_t & ev) +{ mQueue.enqueue(EventItem(new DpVoiceReceiveCombinedEvent(ev))); } +void DpCluster::OnVoiceTrackMarker(const dpp::voice_track_marker_t & ev) +{ mQueue.enqueue(EventItem(new DpVoiceTrackMarkerEvent(ev))); } +void DpCluster::OnStageInstanceCreate(const dpp::stage_instance_create_t & ev) +{ mQueue.enqueue(EventItem(new DpStageInstanceCreateEvent(ev))); } +void DpCluster::OnStageInstanceUpdate(const dpp::stage_instance_update_t & ev) +{ mQueue.enqueue(EventItem(new DpStageInstanceUpdateEvent(ev))); } +void DpCluster::OnStageInstanceDelete(const dpp::stage_instance_delete_t & ev) +{ mQueue.enqueue(EventItem(new DpStageInstanceDeleteEvent(ev))); } + +} // Namespace:: SqMod diff --git a/module/Library/Discord/Cluster.hpp b/module/Library/Discord/Cluster.hpp new file mode 100644 index 00000000..ba49104d --- /dev/null +++ b/module/Library/Discord/Cluster.hpp @@ -0,0 +1,338 @@ +#pragma once + +// ------------------------------------------------------------------------------------------------ +#include "Core/Utility.hpp" +#include "Core/Signal.hpp" + +// ------------------------------------------------------------------------------------------------ +#include "Library/Discord/Constants.hpp" +#include "Library/Discord/Misc.hpp" + +// ------------------------------------------------------------------------------------------------ +#include +#include +#include +#include + +// ------------------------------------------------------------------------------------------------ +#include +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +// ------------------------------------------------------------------------------------------------ +struct DpEventBase; + +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpClusterOptions +{ + // The bot token to use for all HTTP commands and websocket connections. + std::string mToken{}; + // A bitmask of dpd::intents values for all shards on this cluster. This is required to be sent for all bots with over 100 servers. + uint32_t mIntents{dpp::i_default_intents}; + // The total number of shards on this bot. If there are multiple clusters, then (shards / clusters) actual shards will run on this cluster. + // If you omit this value, the library will attempt to query the Discord API for the correct number of shards to start. + uint32_t mShards{0}; + // The ID of this cluster, should be between 0 and MAXCLUSTERS-1 + uint32_t mClusterID{0}; + // The total number of clusters that are active, which may be on separate processes or even separate machines. + uint32_t mMaxClusters{1}; + // Whether or not to use compression for shards on this cluster. Saves a ton of bandwidth at the cost of some CPU + bool mCompressed{true}; + // Set the user caching policy for the cluster, either lazy (only cache users/members when they message the bot) or aggressive (request whole member lists on seeing new guilds too) + dpp::cache_policy_t mPolicy{dpp::cp_aggressive, dpp::cp_aggressive, dpp::cp_aggressive}; + // The number of threads to allocate for making HTTP requests to Discord. This defaults to 12. You can increase this at runtime via the object returned from get_rest(). + uint32_t mRequestThreads{12}; + // The number of threads to allocate for making HTTP requests to sites outside of Discord. This defaults to 1. You can increase this at runtime via the object returned from get_raw_rest(). + uint32_t mRequestThreadsRaw{1}; + // Disable automatic forwarding of logged messages to internal logging. Allows to handle logging manually without risking duplicate output (at the cost of some performance, ofc). + bool mCustomLogging{false}; + + /* -------------------------------------------------------------------------------------------- + * Base constructors. + */ + DpClusterOptions(StackStrF & token) + : mToken(token.ToStr()) + { + } + + /* -------------------------------------------------------------------------------------------- + * Copy/Move constructors. + */ + DpClusterOptions(const DpClusterOptions &) noexcept = default; + DpClusterOptions(DpClusterOptions &&) noexcept = default; + + /* -------------------------------------------------------------------------------------------- + * Copy/Move assignment operators. + */ + DpClusterOptions & operator = (const DpClusterOptions &) noexcept = default; + DpClusterOptions & operator = (DpClusterOptions &&) noexcept = default; +}; + +/* ------------------------------------------------------------------------------------------------ + * Primitive implementation of a discord client with basic functionality. +*/ +struct DpCluster : public SqChainedInstances< DpCluster > +{ + using Base = SqChainedInstances< DpCluster >; + + /* -------------------------------------------------------------------------------------------- + * Queue of events generated from other threads. + */ + using EventItem = std::unique_ptr< DpEventBase >; + using EventQueue = moodycamel::ConcurrentQueue< EventItem >; + + /* -------------------------------------------------------------------------------------------- + * Type of container for cluster signals. + */ + using Signals = std::array< SignalPair, static_cast< size_t >(DpEventID::Max) >; + + /* -------------------------------------------------------------------------------------------- + * Type of container for event handles. + */ + using EventHandle = std::array< dpp::event_handle, static_cast< size_t >(DpEventID::Max) >; + + /* -------------------------------------------------------------------------------------------- + * Event queue. + */ + EventQueue mQueue{4096}; + + /* -------------------------------------------------------------------------------------------- + * Managed cluster instance. + */ + std::unique_ptr< dpp::cluster > mC; + + /* -------------------------------------------------------------------------------------------- + * Table containing the emitted cluster events. + */ + LightObj mSqEvents{}; + + /* -------------------------------------------------------------------------------------------- + * Cluster signals. + */ + Signals mEvents{}; + + /* -------------------------------------------------------------------------------------------- + * Event handles for bound event event handlers so they can be stopped at any time. + */ + EventHandle mEventsHandle{}; + + /* -------------------------------------------------------------------------------------------- + * Base constructors. + */ + DpCluster(DpClusterOptions & o); + + /* -------------------------------------------------------------------------------------------- + * Copy/Move constructors (disabled). + */ + DpCluster(const DpCluster &) noexcept = delete; + DpCluster(DpCluster &&) noexcept = delete; + + /* -------------------------------------------------------------------------------------------- + * Destructor. + */ + ~DpCluster() + { + if (mC) Stop(); + // Forget about this instance + UnchainInstance(); + } + + /* -------------------------------------------------------------------------------------------- + * Copy/Move assignment operators (disabled). + */ + DpCluster & operator = (const DpCluster &) noexcept = delete; + DpCluster & operator = (DpCluster &&) noexcept = delete; + + /* -------------------------------------------------------------------------------------------- + * Check if the managed cluster instance is valid and throw an exception otherwise. + */ + void Validate() const + { + if (!mC) + { + STHROWF("Discord cluster instance is not valid anymore."); + } + } + + /* -------------------------------------------------------------------------------------------- + * Check if the managed cluster instance is valid and throw an exception otherwise. + */ + void Validate(const char * m) const + { + if (!mC) + { + STHROWF("Cannot {} a cluster instance that is not valid anymore.", fmt::detail::to_string_view(m)); + } + } + + /* -------------------------------------------------------------------------------------------- + * Check if the managed cluster instance is valid and throw an exception otherwise. + */ + dpp::cluster & Valid() const { Validate(); return *mC; } + + /* -------------------------------------------------------------------------------------------- + * Check if the managed cluster instance is valid and throw an exception otherwise. + */ + dpp::cluster & Valid(const char * m) const { Validate(m); return *mC; } + + /* -------------------------------------------------------------------------------------------- + * Process the cluster. This is used internally on each server frame. + */ + void Process(bool force = false); + + /* -------------------------------------------------------------------------------------------- + * Terminate the cluster. This is used internally when the VM is shutting down. + */ + void Terminate(); + + /* -------------------------------------------------------------------------------------------- + * Start the cluster. + */ + DpCluster & Start() { Valid("start").start(dpp::st_return); return *this; } + + /* -------------------------------------------------------------------------------------------- + * Stop the cluster. + */ + void Stop() { Valid("stop").shutdown(); } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the events table of this cluster. + */ + SQMOD_NODISCARD LightObj & GetEvents() + { + return mSqEvents; + } + + /* -------------------------------------------------------------------------------------------- + * Enable a certain event for the cluster. + */ + DpCluster & EnableEvent(SQInteger id); + + /* -------------------------------------------------------------------------------------------- + * Disable a certain event for the cluster. + */ + DpCluster & DisableEvent(SQInteger id); + +private: + + /* -------------------------------------------------------------------------------------------- + * Signal initialization. + */ + void InitEvents() + { + // Ignore the call if already initialized + if (!mSqEvents.IsNull()) + { + return; + } + // Create a new table on the stack with enough space pre-allocated + sq_newtableex(SqVM(), static_cast< SQInteger >(DpEventID::Max + 1)); + // Grab the table object from the stack + mSqEvents = LightObj(-1, SqVM()); + // Pop the table object from the stack + sq_pop(SqVM(), 1); + // Proceed to initializing the events + for (size_t i = 0; i < mEvents.size(); ++i) + { + InitSignalPair(mEvents[i], mSqEvents, DpEventID::NAME[i]); + } + } + + /* -------------------------------------------------------------------------------------------- + * Signal termination. + */ + void DropEvents() + { + for (auto & e : mEvents) + { + ResetSignalPair(e); + } + } + + /* -------------------------------------------------------------------------------------------- + * Event handlers. + */ + void OnVoiceStateUpdate(const dpp::voice_state_update_t & ev); + void OnVoiceClientDisconnect(const dpp::voice_client_disconnect_t & ev); + void OnVoiceClientSpeaking(const dpp::voice_client_speaking_t & ev); + void OnLog(const dpp::log_t & ev); + void OnGuildJoinRequestDelete(const dpp::guild_join_request_delete_t & ev); + void OnInteractionCreate(const dpp::interaction_create_t & ev); + void OnSlashCommand(const dpp::slashcommand_t & ev); + void OnButtonClick(const dpp::button_click_t & ev); + void OnAutoComplete(const dpp::autocomplete_t & ev); + void OnSelectClick(const dpp::select_click_t & ev); + void OnMessageContextMenu(const dpp::message_context_menu_t & ev); + void OnUserContextMenu(const dpp::user_context_menu_t & ev); + void OnFormSubmit(const dpp::form_submit_t & ev); + void OnGuildDelete(const dpp::guild_delete_t & ev); + void OnChannelDelete(const dpp::channel_delete_t & ev); + void OnChannelUpdate(const dpp::channel_update_t & ev); + void OnReady(const dpp::ready_t & ev); + void OnMessageDelete(const dpp::message_delete_t & ev); + void OnGuildMemberRemove(const dpp::guild_member_remove_t & ev); + void OnResumed(const dpp::resumed_t & ev); + void OnGuildRoleCreate(const dpp::guild_role_create_t & ev); + void OnTypingStart(const dpp::typing_start_t & ev); + void OnMessageReactionAdd(const dpp::message_reaction_add_t & ev); + void OnGuildMembersChunk(const dpp::guild_members_chunk_t & ev); + void OnMessageReactionRemove(const dpp::message_reaction_remove_t & ev); + void OnGuildCreate(const dpp::guild_create_t & ev); + void OnChannelCreate(const dpp::channel_create_t & ev); + void OnMessageReactionRemoveEmoji(const dpp::message_reaction_remove_emoji_t & ev); + void OnMessageDeleteDulk(const dpp::message_delete_bulk_t & ev); + void OnGuildRoleUpdate(const dpp::guild_role_update_t & ev); + void OnGuildRoleDelete(const dpp::guild_role_delete_t & ev); + void OnChannelPinsUpdate(const dpp::channel_pins_update_t & ev); + void OnMessageReactionRemoveAll(const dpp::message_reaction_remove_all_t & ev); + void OnVoiceServerUpdate(const dpp::voice_server_update_t & ev); + void OnGuildEmojisUpdate(const dpp::guild_emojis_update_t & ev); + void OnGuildStickersUpdate(const dpp::guild_stickers_update_t & ev); + void OnPresenceUpdate(const dpp::presence_update_t & ev); + void OnWebhooksUpdate(const dpp::webhooks_update_t & ev); + void OnAutomodRuleCreate(const dpp::automod_rule_create_t & ev); + void OnAutomodRuleUpdate(const dpp::automod_rule_update_t & ev); + void OnAutomodRuleDelete(const dpp::automod_rule_delete_t & ev); + void OnAutomodRuleExecute(const dpp::automod_rule_execute_t & ev); + void OnGuildMemberAdd(const dpp::guild_member_add_t & ev); + void OnInviteDelete(const dpp::invite_delete_t & ev); + void OnGuildUpdate(const dpp::guild_update_t & ev); + void OnGuildIntegrationsUpdate(const dpp::guild_integrations_update_t & ev); + void OnGuildMemberUpdate(const dpp::guild_member_update_t & ev); + void OnInviteCreate(const dpp::invite_create_t & ev); + void OnMessageUpdate(const dpp::message_update_t & ev); + void OnUserUpdate(const dpp::user_update_t & ev); + void OnMessageCreate(const dpp::message_create_t & ev); + void OnGuildAuditLogEntryCreate(const dpp::guild_audit_log_entry_create_t & ev); + void OnGuildBanAdd(const dpp::guild_ban_add_t & ev); + void OnGuildBanRemove(const dpp::guild_ban_remove_t & ev); + void OnIntegrationCreate(const dpp::integration_create_t & ev); + void OnIntegrationUpdate(const dpp::integration_update_t & ev); + void OnIntegrationDelete(const dpp::integration_delete_t & ev); + void OnThreadCreate(const dpp::thread_create_t & ev); + void OnThreadUpdate(const dpp::thread_update_t & ev); + void OnThreadDelete(const dpp::thread_delete_t & ev); + void OnThreadListSync(const dpp::thread_list_sync_t & ev); + void OnThreadMemberUpdate(const dpp::thread_member_update_t & ev); + void OnThreadMembersUpdate(const dpp::thread_members_update_t & ev); + void OnGuildScheduledEventCreate(const dpp::guild_scheduled_event_create_t & ev); + void OnGuildScheduledEventUpdate(const dpp::guild_scheduled_event_update_t & ev); + void OnGuildScheduledEventDelete(const dpp::guild_scheduled_event_delete_t & ev); + void OnGuildScheduledEventUserAdd(const dpp::guild_scheduled_event_user_add_t & ev); + void OnGuildScheduledEventUserRemove(const dpp::guild_scheduled_event_user_remove_t & ev); + void OnVoiceBufferSend(const dpp::voice_buffer_send_t & ev); + void OnVoiceUserTalking(const dpp::voice_user_talking_t & ev); + void OnVoiceReady(const dpp::voice_ready_t & ev); + void OnVoiceReceive(const dpp::voice_receive_t & ev); + void OnVoiceReceiveCombined(const dpp::voice_receive_t & ev); + void OnVoiceTrackMarker(const dpp::voice_track_marker_t & ev); + void OnStageInstanceCreate(const dpp::stage_instance_create_t & ev); + void OnStageInstanceUpdate(const dpp::stage_instance_update_t & ev); + void OnStageInstanceDelete(const dpp::stage_instance_delete_t & ev); +}; + +} // Namespace:: SqMod diff --git a/module/Library/Discord/Command.cpp b/module/Library/Discord/Command.cpp new file mode 100644 index 00000000..94b38781 --- /dev/null +++ b/module/Library/Discord/Command.cpp @@ -0,0 +1,9 @@ +// ------------------------------------------------------------------------------------------------ +#include "Library/Discord/Command.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + + + +} // Namespace:: SqMod diff --git a/module/Library/Discord/Command.hpp b/module/Library/Discord/Command.hpp new file mode 100644 index 00000000..ab7ce4d7 --- /dev/null +++ b/module/Library/Discord/Command.hpp @@ -0,0 +1,13 @@ +#pragma once + +// ------------------------------------------------------------------------------------------------ +#include "Core/Utility.hpp" + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + + +} // Namespace:: SqMod diff --git a/module/Library/Discord/Constants.cpp b/module/Library/Discord/Constants.cpp new file mode 100644 index 00000000..9b9d74c9 --- /dev/null +++ b/module/Library/Discord/Constants.cpp @@ -0,0 +1,283 @@ +// ------------------------------------------------------------------------------------------------ +#include "Library/Discord/Constants.hpp" + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +// ------------------------------------------------------------------------------------------------ +const std::array< const char *, static_cast< size_t >(DpEventID::Max) > DpEventID::NAME{ + "VoiceStateUpdate", + "VoiceClientDisconnect", + "VoiceClientSpeaking", + "Log", + "GuildJoinRequestDelete", + "InteractionCreate", + "SlashCommand", + "ButtonClick", + "AutoComplete", + "SelectClick", + "MessageContextMenu", + "UserContextMenu", + "FormSubmit", + "GuildDelete", + "ChannelDelete", + "ChannelUpdate", + "Ready", + "MessageDelete", + "GuildMemberRemove", + "Resumed", + "GuildRoleCreate", + "TypingStart", + "MessageReactionAdd", + "GuildMembersChunk", + "MessageReactionRemove", + "GuildCreate", + "ChannelCreate", + "MessageReactionRemoveEmoji", + "MessageDeleteDulk", + "GuildRoleUpdate", + "GuildRoleDelete", + "ChannelPinsUpdate", + "MessageReactionRemoveAll", + "VoiceServerUpdate", + "GuildEmojisUpdate", + "GuildStickersUpdate", + "PresenceUpdate", + "WebhooksUpdate", + "AutomodRuleCreate", + "AutomodRuleUpdate", + "AutomodRuleDelete", + "AutomodRuleExecute", + "GuildMemberAdd", + "InviteDelete", + "GuildUpdate", + "GuildIntegrationsUpdate", + "GuildMemberUpdate", + "InviteCreate", + "MessageUpdate", + "UserUpdate", + "MessageCreate", + "GuildAuditLogEntryCreate", + "GuildBanAdd", + "GuildBanRemove", + "IntegrationCreate", + "IntegrationUpdate", + "IntegrationDelete", + "ThreadCreate", + "ThreadUpdate", + "ThreadDelete", + "ThreadListSync", + "ThreadMemberUpdate", + "ThreadMembersUpdate", + "GuildScheduledEventCreate", + "GuildScheduledEventUpdate", + "GuildScheduledEventDelete", + "GuildScheduledEventUserAdd", + "GuildScheduledEventUserRemove", + "VoiceBufferSend", + "VoiceUserTalking", + "VoiceReady", + "VoiceReceive", + "VoiceReceiveCombined", + "VoiceTrackMarker", + "StageInstanceCreate", + "StageInstanceUpdate", + "StageInstanceDelete", +}; + +// ------------------------------------------------------------------------------------------------ +static const EnumElement g_DpLogLevelEnum[] = { + {_SC("Trace"), static_cast< SQInteger >(dpp::ll_trace)}, + {_SC("Debug"), static_cast< SQInteger >(dpp::ll_debug)}, + {_SC("Info"), static_cast< SQInteger >(dpp::ll_info)}, + {_SC("Warning"), static_cast< SQInteger >(dpp::ll_warning)}, + {_SC("Error"), static_cast< SQInteger >(dpp::ll_error)}, + {_SC("Critical"), static_cast< SQInteger >(dpp::ll_critical)}, +}; + +// ------------------------------------------------------------------------------------------------ +static const EnumElement g_DpVoiceStateFlagsEnum[] = { + {_SC("Deaf"), static_cast< SQInteger >(dpp::vs_deaf)}, + {_SC("Mute"), static_cast< SQInteger >(dpp::vs_mute)}, + {_SC("SelfMute"), static_cast< SQInteger >(dpp::vs_self_mute)}, + {_SC("SelfDeaf"), static_cast< SQInteger >(dpp::vs_self_deaf)}, + {_SC("SelfStream"), static_cast< SQInteger >(dpp::vs_self_stream)}, + {_SC("SelfVideo"), static_cast< SQInteger >(dpp::vs_self_video)}, + {_SC("Suppress"), static_cast< SQInteger >(dpp::vs_suppress)}, +}; + +// ------------------------------------------------------------------------------------------------ +static const EnumElement g_DpEmojiFlagsEnum[] = { + {_SC("RequireColons"), static_cast< SQInteger >(dpp::e_require_colons)}, + {_SC("Managed"), static_cast< SQInteger >(dpp::e_managed)}, + {_SC("Animated"), static_cast< SQInteger >(dpp::e_animated)}, + {_SC("Available"), static_cast< SQInteger >(dpp::e_available)}, +}; + +// ------------------------------------------------------------------------------------------------ +static const EnumElement g_DpPresenceFlagsEnum[] = { + {_SC("DesktopOnline"), static_cast< SQInteger >(dpp::p_desktop_online)}, + {_SC("DesktopDND"), static_cast< SQInteger >(dpp::p_desktop_dnd)}, + {_SC("DesktopIdle"), static_cast< SQInteger >(dpp::p_desktop_idle)}, + {_SC("WebOnline"), static_cast< SQInteger >(dpp::p_web_online)}, + {_SC("WebDND"), static_cast< SQInteger >(dpp::p_web_dnd)}, + {_SC("WebIdle"), static_cast< SQInteger >(dpp::p_web_idle)}, + {_SC("MobileOnline"), static_cast< SQInteger >(dpp::p_mobile_online)}, + {_SC("MobileDND"), static_cast< SQInteger >(dpp::p_mobile_dnd)}, + {_SC("MobileIdle"), static_cast< SQInteger >(dpp::p_mobile_idle)}, + {_SC("StatusOnline"), static_cast< SQInteger >(dpp::p_status_online)}, + {_SC("StatusDND"), static_cast< SQInteger >(dpp::p_status_dnd)}, + {_SC("StatusIdle"), static_cast< SQInteger >(dpp::p_status_idle)}, +}; + +// ------------------------------------------------------------------------------------------------ +static const EnumElement g_DpPresenceStatusEnum[] = { + {_SC("Offline"), static_cast< SQInteger >(dpp::ps_offline)}, + {_SC("Online"), static_cast< SQInteger >(dpp::ps_online)}, + {_SC("DND"), static_cast< SQInteger >(dpp::ps_dnd)}, + {_SC("Idle"), static_cast< SQInteger >(dpp::ps_idle)}, + // Bit shift for desktop status + {_SC("ShiftDesktop"), static_cast< SQInteger >(PF_SHIFT_DESKTOP)}, + {_SC("ShiftWeb"), static_cast< SQInteger >(PF_SHIFT_WEB)}, + {_SC("ShiftMobile"), static_cast< SQInteger >(PF_SHIFT_MOBILE)}, + {_SC("ShiftMain"), static_cast< SQInteger >(PF_SHIFT_MAIN)}, + {_SC("StatusMask"), static_cast< SQInteger >(PF_STATUS_MASK)}, + {_SC("ClearDesktop"), static_cast< SQInteger >(PF_CLEAR_DESKTOP)}, + {_SC("ClearWeb"), static_cast< SQInteger >(PF_CLEAR_WEB)}, + {_SC("ClearMobile"), static_cast< SQInteger >(PF_CLEAR_MOBILE)}, + {_SC("ClearStatus"), static_cast< SQInteger >(PF_CLEAR_STATUS)}, +}; + +// ------------------------------------------------------------------------------------------------ +static const EnumElement g_DpActivityTypeEnum[] = { + {_SC("Game"), static_cast< SQInteger >(dpp::at_game)}, + {_SC("Streaming"), static_cast< SQInteger >(dpp::at_streaming)}, + {_SC("Listening"), static_cast< SQInteger >(dpp::at_listening)}, + {_SC("Watching"), static_cast< SQInteger >(dpp::at_watching)}, + {_SC("Custom"), static_cast< SQInteger >(dpp::at_custom)}, + {_SC("Competing"), static_cast< SQInteger >(dpp::at_competing)}, +}; + +// ------------------------------------------------------------------------------------------------ +static const EnumElement g_DpActivityFlagsEnum[] = { + {_SC("Instance"), static_cast< SQInteger >(dpp::af_instance)}, + {_SC("Join"), static_cast< SQInteger >(dpp::af_join)}, + {_SC("Spectate"), static_cast< SQInteger >(dpp::af_spectate)}, + {_SC("JoinRequest"), static_cast< SQInteger >(dpp::af_join_request)}, + {_SC("Sync"), static_cast< SQInteger >(dpp::af_sync)}, + {_SC("Play"), static_cast< SQInteger >(dpp::af_play)}, + {_SC("PartyPrivacyFriends"), static_cast< SQInteger >(dpp::af_party_privacy_friends)}, + {_SC("PartyPrivacyVoiceChannel"), static_cast< SQInteger >(dpp::af_party_privacy_voice_channel)}, + {_SC("Embedded"), static_cast< SQInteger >(dpp::af_embedded)}, +}; + +// ------------------------------------------------------------------------------------------------ +static const EnumElement g_DpRegionEnum[] = { + {_SC("Brazil"), static_cast< SQInteger >(dpp::r_brazil)}, + {_SC("CentralEurope"), static_cast< SQInteger >(dpp::r_central_europe)}, + {_SC("HongKong"), static_cast< SQInteger >(dpp::r_hong_kong)}, + {_SC("India"), static_cast< SQInteger >(dpp::r_india)}, + {_SC("Japan"), static_cast< SQInteger >(dpp::r_japan)}, + {_SC("Russia"), static_cast< SQInteger >(dpp::r_russia)}, + {_SC("Singapore"), static_cast< SQInteger >(dpp::r_singapore)}, + {_SC("SouthAfrica"), static_cast< SQInteger >(dpp::r_south_africa)}, + {_SC("Sydney"), static_cast< SQInteger >(dpp::r_sydney)}, + {_SC("UsCentral"), static_cast< SQInteger >(dpp::r_us_central)}, + {_SC("UsEast"), static_cast< SQInteger >(dpp::r_us_east)}, + {_SC("UsSouth"), static_cast< SQInteger >(dpp::r_us_south)}, + {_SC("UsWest"), static_cast< SQInteger >(dpp::r_us_west)}, + {_SC("WesternEurope"), static_cast< SQInteger >(dpp::r_western_europe)}, +}; + +// ------------------------------------------------------------------------------------------------ +static const EnumElement g_DpGuildFlagsEnum[] = { + {_SC("Large"), static_cast< SQInteger >(dpp::g_large)}, + {_SC("Unavailable"), static_cast< SQInteger >(dpp::g_unavailable)}, + {_SC("WidgetEnabled"), static_cast< SQInteger >(dpp::g_widget_enabled)}, + {_SC("InviteSplash"), static_cast< SQInteger >(dpp::g_invite_splash)}, + {_SC("VipRegions"), static_cast< SQInteger >(dpp::g_vip_regions)}, + {_SC("VanityURL"), static_cast< SQInteger >(dpp::g_vanity_url)}, + {_SC("Verified"), static_cast< SQInteger >(dpp::g_verified)}, + {_SC("Partnered"), static_cast< SQInteger >(dpp::g_partnered)}, + {_SC("Community"), static_cast< SQInteger >(dpp::g_community)}, + {_SC("RoleSubscriptionEnabled"), static_cast< SQInteger >(dpp::g_role_subscription_enabled)}, + {_SC("News"), static_cast< SQInteger >(dpp::g_news)}, + {_SC("Discoverable"), static_cast< SQInteger >(dpp::g_discoverable)}, + {_SC("Featureable"), static_cast< SQInteger >(dpp::g_featureable)}, + {_SC("AnimatedIcon"), static_cast< SQInteger >(dpp::g_animated_icon)}, + {_SC("Banner"), static_cast< SQInteger >(dpp::g_banner)}, + {_SC("WelcomeScreenEnabled"), static_cast< SQInteger >(dpp::g_welcome_screen_enabled)}, + {_SC("MemberVerificationGate"), static_cast< SQInteger >(dpp::g_member_verification_gate)}, + {_SC("PreviewEnabled"), static_cast< SQInteger >(dpp::g_preview_enabled)}, + {_SC("NoJoinNotifications"), static_cast< SQInteger >(dpp::g_no_join_notifications)}, + {_SC("NoBoostNotifications"), static_cast< SQInteger >(dpp::g_no_boost_notifications)}, + {_SC("HasAnimatedIcon"), static_cast< SQInteger >(dpp::g_has_animated_icon)}, + {_SC("HasAnimatedBanner"), static_cast< SQInteger >(dpp::g_has_animated_banner)}, + {_SC("NoSetupTips"), static_cast< SQInteger >(dpp::g_no_setup_tips)}, + {_SC("NoStickerGreeting"), static_cast< SQInteger >(dpp::g_no_sticker_greeting)}, + {_SC("MonetizationEnabled"), static_cast< SQInteger >(dpp::g_monetization_enabled)}, + {_SC("MoreStickers"), static_cast< SQInteger >(dpp::g_more_stickers)}, + {_SC("CreatorStorePageEnabled"), static_cast< SQInteger >(dpp::g_creator_store_page_enabled)}, + {_SC("RoleIcons"), static_cast< SQInteger >(dpp::g_role_icons)}, + {_SC("SevenDayThreadArchive"), static_cast< SQInteger >(dpp::g_seven_day_thread_archive)}, + {_SC("ThreeDayThreadArchive"), static_cast< SQInteger >(dpp::g_three_day_thread_archive)}, + {_SC("TicketedEvents"), static_cast< SQInteger >(dpp::g_ticketed_events)}, + {_SC("ChannelBanners"), static_cast< SQInteger >(dpp::g_channel_banners)}, +}; + +// ------------------------------------------------------------------------------------------------ +static const EnumElement g_DpGuildFlagsExtraEnum[] = { + {_SC("PremiumProgressBarEnabled"), static_cast< SQInteger >(dpp::g_premium_progress_bar_enabled)}, + {_SC("AnimatedBanner"), static_cast< SQInteger >(dpp::g_animated_banner)}, + {_SC("AutoModeration"), static_cast< SQInteger >(dpp::g_auto_moderation)}, + {_SC("InvitesDisabled"), static_cast< SQInteger >(dpp::g_invites_disabled)}, + {_SC("DeveloperSupportServer"), static_cast< SQInteger >(dpp::g_developer_support_server)}, + {_SC("NoRoleSubscriptionNotifications"), static_cast< SQInteger >(dpp::g_no_role_subscription_notifications)}, + {_SC("NoRoleSubscriptionNotificationReplies"), static_cast< SQInteger >(dpp::g_no_role_subscription_notification_replies)}, + {_SC("RoleSubscriptionsAvailableForPurchase"), static_cast< SQInteger >(dpp::g_role_subscriptions_available_for_purchase)}, + +}; + +// ------------------------------------------------------------------------------------------------ +static const EnumElement g_DpGuildMemberFlagsEnum[] = { + {_SC("Deaf"), static_cast< SQInteger >(dpp::gm_deaf)}, + {_SC("Mute"), static_cast< SQInteger >(dpp::gm_mute)}, + {_SC("Pending"), static_cast< SQInteger >(dpp::gm_pending)}, + {_SC("AnimatedAvatar"), static_cast< SQInteger >(dpp::gm_animated_avatar)}, + {_SC("VoiceAction"), static_cast< SQInteger >(dpp::gm_voice_action)}, +}; + +// ------------------------------------------------------------------------------------------------ +static const EnumElements g_EnumList[] = { + {_SC("SqDiscordLogLevel"), g_DpLogLevelEnum}, + {_SC("SqDiscordVoiceStateFlags"), g_DpVoiceStateFlagsEnum}, + {_SC("SqDiscordEmojiFlags"), g_DpEmojiFlagsEnum}, + {_SC("SqDiscordPresenceFlags"), g_DpPresenceFlagsEnum}, + {_SC("SqDiscordPresenceStatus"), g_DpPresenceStatusEnum}, + {_SC("SqDiscordActivityType"), g_DpActivityTypeEnum}, + {_SC("SqDiscordActivityFlags"), g_DpActivityFlagsEnum}, + {_SC("SqDiscordRegion"), g_DpRegionEnum}, + {_SC("SqDiscordGuildFlags"), g_DpGuildFlagsEnum}, + {_SC("SqDiscordGuildFlagsExtra"), g_DpGuildFlagsExtraEnum}, + {_SC("SqDiscordGuildMemberFlags"), g_DpGuildMemberFlagsEnum}, +}; + +// ------------------------------------------------------------------------------------------------ +void Register_Discord_Constants(HSQUIRRELVM vm, Table & ns) +{ + RegisterEnumerations(vm, g_EnumList); + // -------------------------------------------------------------------------------------------- + Enumeration e(vm); + // Bind all events using their associated name + for (SQInteger i = 0; i < static_cast< SQInteger >(DpEventID::Max); ++i) + { + e.Const(DpEventID::NAME[i], i); + } + // Expose the constants + ConstTable(vm).Enum(_SC("SqDiscordEvent"), e); +} + +} // Namespace:: SqMod diff --git a/module/Library/Discord/Constants.hpp b/module/Library/Discord/Constants.hpp new file mode 100644 index 00000000..c9f1181e --- /dev/null +++ b/module/Library/Discord/Constants.hpp @@ -0,0 +1,663 @@ +#pragma once + +// ------------------------------------------------------------------------------------------------ +#include "Core/Utility.hpp" + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +#include +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +/* ------------------------------------------------------------------------------------------------ + * Unique ID for each event. +*/ +struct DpEventID +{ + /* -------------------------------------------------------------------------------------------- + * ID enumeration. + */ + enum Type + { + VoiceStateUpdate=0, + VoiceClientDisconnect, + VoiceClientSpeaking, + Log, + GuildJoinRequestDelete, + InteractionCreate, + SlashCommand, + ButtonClick, + AutoComplete, + SelectClick, + MessageContextMenu, + UserContextMenu, + FormSubmit, + GuildDelete, + ChannelDelete, + ChannelUpdate, + Ready, + MessageDelete, + GuildMemberRemove, + Resumed, + GuildRoleCreate, + TypingStart, + MessageReactionAdd, + GuildMembersChunk, + MessageReactionRemove, + GuildCreate, + ChannelCreate, + MessageReactionRemoveEmoji, + MessageDeleteDulk, + GuildRoleUpdate, + GuildRoleDelete, + ChannelPinsUpdate, + MessageReactionRemoveAll, + VoiceServerUpdate, + GuildEmojisUpdate, + GuildStickersUpdate, + PresenceUpdate, + WebhooksUpdate, + AutomodRuleCreate, + AutomodRuleUpdate, + AutomodRuleDelete, + AutomodRuleExecute, + GuildMemberAdd, + InviteDelete, + GuildUpdate, + GuildIntegrationsUpdate, + GuildMemberUpdate, + InviteCreate, + MessageUpdate, + UserUpdate, + MessageCreate, + GuildAuditLogEntryCreate, + GuildBanAdd, + GuildBanRemove, + IntegrationCreate, + IntegrationUpdate, + IntegrationDelete, + ThreadCreate, + ThreadUpdate, + ThreadDelete, + ThreadListSync, + ThreadMemberUpdate, + ThreadMembersUpdate, + GuildScheduledEventCreate, + GuildScheduledEventUpdate, + GuildScheduledEventDelete, + GuildScheduledEventUserAdd, + GuildScheduledEventUserRemove, + VoiceBufferSend, + VoiceUserTalking, + VoiceReady, + VoiceReceive, + VoiceReceiveCombined, + VoiceTrackMarker, + StageInstanceCreate, + StageInstanceUpdate, + StageInstanceDelete, + Max + }; + /* -------------------------------------------------------------------------------------------- + * String identification for each event ID. + */ + static const std::array< const char *, static_cast< size_t >(Max) > NAME; +}; + +/* ------------------------------------------------------------------------------------------------ + * Structures that hold compile-time type information for events. +*/ +struct DpVoiceStateUpdateEventInfo +{ + using Type = dpp::voice_state_update_t; + static inline constexpr size_t ID = DpEventID::VoiceStateUpdate; + static inline constexpr std::string_view Name = "VoiceStateUpdate"; +}; +struct DpVoiceClientDisconnectEventInfo +{ + using Type = dpp::voice_client_disconnect_t; + static inline constexpr size_t ID = DpEventID::VoiceClientDisconnect; + static inline constexpr std::string_view Name = "VoiceClientDisconnect"; +}; +struct DpVoiceClientSpeakingEventInfo +{ + using Type = dpp::voice_client_speaking_t; + static inline constexpr size_t ID = DpEventID::VoiceClientSpeaking; + static inline constexpr std::string_view Name = "VoiceClientSpeaking"; +}; +struct DpLogEventInfo +{ + using Type = dpp::log_t; + static inline constexpr size_t ID = DpEventID::Log; + static inline constexpr std::string_view Name = "Log"; +}; +struct DpGuildJoinRequestDeleteEventInfo +{ + using Type = dpp::guild_join_request_delete_t; + static inline constexpr size_t ID = DpEventID::GuildJoinRequestDelete; + static inline constexpr std::string_view Name = "GuildJoinRequestDelete"; +}; +struct DpInteractionCreateEventInfo +{ + using Type = dpp::interaction_create_t; + static inline constexpr size_t ID = DpEventID::InteractionCreate; + static inline constexpr std::string_view Name = "InteractionCreate"; +}; +struct DpSlashCommandEventInfo +{ + using Type = dpp::slashcommand_t; + static inline constexpr size_t ID = DpEventID::SlashCommand; + static inline constexpr std::string_view Name = "SlashCommand"; +}; +struct DpButtonClickEventInfo +{ + using Type = dpp::button_click_t; + static inline constexpr size_t ID = DpEventID::ButtonClick; + static inline constexpr std::string_view Name = "ButtonClick"; +}; +struct DpAutoCompleteEventInfo +{ + using Type = dpp::autocomplete_t; + static inline constexpr size_t ID = DpEventID::AutoComplete; + static inline constexpr std::string_view Name = "AutoComplete"; +}; +struct DpSelectClickEventInfo +{ + using Type = dpp::select_click_t; + static inline constexpr size_t ID = DpEventID::SelectClick; + static inline constexpr std::string_view Name = "SelectClick"; +}; +struct DpMessageContextMenuEventInfo +{ + using Type = dpp::message_context_menu_t; + static inline constexpr size_t ID = DpEventID::MessageContextMenu; + static inline constexpr std::string_view Name = "MessageContextMenu"; +}; +struct DpUserContextMenuEventInfo +{ + using Type = dpp::user_context_menu_t; + static inline constexpr size_t ID = DpEventID::UserContextMenu; + static inline constexpr std::string_view Name = "UserContextMenu"; +}; +struct DpFormSubmitEventInfo +{ + using Type = dpp::form_submit_t; + static inline constexpr size_t ID = DpEventID::FormSubmit; + static inline constexpr std::string_view Name = "FormSubmit"; +}; +struct DpGuildDeleteEventInfo +{ + using Type = dpp::guild_delete_t; + static inline constexpr size_t ID = DpEventID::GuildDelete; + static inline constexpr std::string_view Name = "GuildDelete"; +}; +struct DpChannelDeleteEventInfo +{ + using Type = dpp::channel_delete_t; + static inline constexpr size_t ID = DpEventID::ChannelDelete; + static inline constexpr std::string_view Name = "ChannelDelete"; +}; +struct DpChannelUpdateEventInfo +{ + using Type = dpp::channel_update_t; + static inline constexpr size_t ID = DpEventID::ChannelUpdate; + static inline constexpr std::string_view Name = "ChannelUpdate"; +}; +struct DpReadyEventInfo +{ + using Type = dpp::ready_t; + static inline constexpr size_t ID = DpEventID::Ready; + static inline constexpr std::string_view Name = "Ready"; +}; +struct DpMessageDeleteEventInfo +{ + using Type = dpp::message_delete_t; + static inline constexpr size_t ID = DpEventID::MessageDelete; + static inline constexpr std::string_view Name = "MessageDelete"; +}; +struct DpGuildMemberRemoveEventInfo +{ + using Type = dpp::guild_member_remove_t; + static inline constexpr size_t ID = DpEventID::GuildMemberRemove; + static inline constexpr std::string_view Name = "GuildMemberRemove"; +}; +struct DpResumedEventInfo +{ + using Type = dpp::resumed_t; + static inline constexpr size_t ID = DpEventID::Resumed; + static inline constexpr std::string_view Name = "Resumed"; +}; +struct DpGuildRoleCreateEventInfo +{ + using Type = dpp::guild_role_create_t; + static inline constexpr size_t ID = DpEventID::GuildRoleCreate; + static inline constexpr std::string_view Name = "GuildRoleCreate"; +}; +struct DpTypingStartEventInfo +{ + using Type = dpp::typing_start_t; + static inline constexpr size_t ID = DpEventID::TypingStart; + static inline constexpr std::string_view Name = "TypingStart"; +}; +struct DpMessageReactionAddEventInfo +{ + using Type = dpp::message_reaction_add_t; + static inline constexpr size_t ID = DpEventID::MessageReactionAdd; + static inline constexpr std::string_view Name = "MessageReactionAdd"; +}; +struct DpGuildMembersChunkEventInfo +{ + using Type = dpp::guild_members_chunk_t; + static inline constexpr size_t ID = DpEventID::GuildMembersChunk; + static inline constexpr std::string_view Name = "GuildMembersChunk"; +}; +struct DpMessageReactionRemoveEventInfo +{ + using Type = dpp::message_reaction_remove_t; + static inline constexpr size_t ID = DpEventID::MessageReactionRemove; + static inline constexpr std::string_view Name = "MessageReactionRemove"; +}; +struct DpGuildCreateEventInfo +{ + using Type = dpp::guild_create_t; + static inline constexpr size_t ID = DpEventID::GuildCreate; + static inline constexpr std::string_view Name = "GuildCreate"; +}; +struct DpChannelCreateEventInfo +{ + using Type = dpp::channel_create_t; + static inline constexpr size_t ID = DpEventID::ChannelCreate; + static inline constexpr std::string_view Name = "ChannelCreate"; +}; +struct DpMessageReactionRemoveEmojiEventInfo +{ + using Type = dpp::message_reaction_remove_emoji_t; + static inline constexpr size_t ID = DpEventID::MessageReactionRemoveEmoji; + static inline constexpr std::string_view Name = "MessageReactionRemoveEmoji"; +}; +struct DpMessageDeleteDulkEventInfo +{ + using Type = dpp::message_delete_bulk_t; + static inline constexpr size_t ID = DpEventID::MessageDeleteDulk; + static inline constexpr std::string_view Name = "MessageDeleteDulk"; +}; +struct DpGuildRoleUpdateEventInfo +{ + using Type = dpp::guild_role_update_t; + static inline constexpr size_t ID = DpEventID::GuildRoleUpdate; + static inline constexpr std::string_view Name = "GuildRoleUpdate"; +}; +struct DpGuildRoleDeleteEventInfo +{ + using Type = dpp::guild_role_delete_t; + static inline constexpr size_t ID = DpEventID::GuildRoleDelete; + static inline constexpr std::string_view Name = "GuildRoleDelete"; +}; +struct DpChannelPinsUpdateEventInfo +{ + using Type = dpp::channel_pins_update_t; + static inline constexpr size_t ID = DpEventID::ChannelPinsUpdate; + static inline constexpr std::string_view Name = "ChannelPinsUpdate"; +}; +struct DpMessageReactionRemoveAllEventInfo +{ + using Type = dpp::message_reaction_remove_all_t; + static inline constexpr size_t ID = DpEventID::MessageReactionRemoveAll; + static inline constexpr std::string_view Name = "MessageReactionRemoveAll"; +}; +struct DpVoiceServerUpdateEventInfo +{ + using Type = dpp::voice_server_update_t; + static inline constexpr size_t ID = DpEventID::VoiceServerUpdate; + static inline constexpr std::string_view Name = "VoiceServerUpdate"; +}; +struct DpGuildEmojisUpdateEventInfo +{ + using Type = dpp::guild_emojis_update_t; + static inline constexpr size_t ID = DpEventID::GuildEmojisUpdate; + static inline constexpr std::string_view Name = "GuildEmojisUpdate"; +}; +struct DpGuildStickersUpdateEventInfo +{ + using Type = dpp::guild_stickers_update_t; + static inline constexpr size_t ID = DpEventID::GuildStickersUpdate; + static inline constexpr std::string_view Name = "GuildStickersUpdate"; +}; +struct DpPresenceUpdateEventInfo +{ + using Type = dpp::presence_update_t; + static inline constexpr size_t ID = DpEventID::PresenceUpdate; + static inline constexpr std::string_view Name = "PresenceUpdate"; +}; +struct DpWebhooksUpdateEventInfo +{ + using Type = dpp::webhooks_update_t; + static inline constexpr size_t ID = DpEventID::WebhooksUpdate; + static inline constexpr std::string_view Name = "WebhooksUpdate"; +}; +struct DpAutomodRuleCreateEventInfo +{ + using Type = dpp::automod_rule_create_t; + static inline constexpr size_t ID = DpEventID::AutomodRuleCreate; + static inline constexpr std::string_view Name = "AutomodRuleCreate"; +}; +struct DpAutomodRuleUpdateEventInfo +{ + using Type = dpp::automod_rule_update_t; + static inline constexpr size_t ID = DpEventID::AutomodRuleUpdate; + static inline constexpr std::string_view Name = "AutomodRuleUpdate"; +}; +struct DpAutomodRuleDeleteEventInfo +{ + using Type = dpp::automod_rule_delete_t; + static inline constexpr size_t ID = DpEventID::AutomodRuleDelete; + static inline constexpr std::string_view Name = "AutomodRuleDelete"; +}; +struct DpAutomodRuleExecuteEventInfo +{ + using Type = dpp::automod_rule_execute_t; + static inline constexpr size_t ID = DpEventID::AutomodRuleExecute; + static inline constexpr std::string_view Name = "AutomodRuleExecute"; +}; +struct DpGuildMemberAddEventInfo +{ + using Type = dpp::guild_member_add_t; + static inline constexpr size_t ID = DpEventID::GuildMemberAdd; + static inline constexpr std::string_view Name = "GuildMemberAdd"; +}; +struct DpInviteDeleteEventInfo +{ + using Type = dpp::invite_delete_t; + static inline constexpr size_t ID = DpEventID::InviteDelete; + static inline constexpr std::string_view Name = "InviteDelete"; +}; +struct DpGuildUpdateEventInfo +{ + using Type = dpp::guild_update_t; + static inline constexpr size_t ID = DpEventID::GuildUpdate; + static inline constexpr std::string_view Name = "GuildUpdate"; +}; +struct DpGuildIntegrationsUpdateEventInfo +{ + using Type = dpp::guild_integrations_update_t; + static inline constexpr size_t ID = DpEventID::GuildIntegrationsUpdate; + static inline constexpr std::string_view Name = "GuildIntegrationsUpdate"; +}; +struct DpGuildMemberUpdateEventInfo +{ + using Type = dpp::guild_member_update_t; + static inline constexpr size_t ID = DpEventID::GuildMemberUpdate; + static inline constexpr std::string_view Name = "GuildMemberUpdate"; +}; +struct DpInviteCreateEventInfo +{ + using Type = dpp::invite_create_t; + static inline constexpr size_t ID = DpEventID::InviteCreate; + static inline constexpr std::string_view Name = "InviteCreate"; +}; +struct DpMessageUpdateEventInfo +{ + using Type = dpp::message_update_t; + static inline constexpr size_t ID = DpEventID::MessageUpdate; + static inline constexpr std::string_view Name = "MessageUpdate"; +}; +struct DpUserUpdateEventInfo +{ + using Type = dpp::user_update_t; + static inline constexpr size_t ID = DpEventID::UserUpdate; + static inline constexpr std::string_view Name = "UserUpdate"; +}; +struct DpMessageCreateEventInfo +{ + using Type = dpp::message_create_t; + static inline constexpr size_t ID = DpEventID::MessageCreate; + static inline constexpr std::string_view Name = "MessageCreate"; +}; +struct DpGuildAuditLogEntryCreateEventInfo +{ + using Type = dpp::guild_audit_log_entry_create_t; + static inline constexpr size_t ID = DpEventID::GuildAuditLogEntryCreate; + static inline constexpr std::string_view Name = "GuildAuditLogEntryCreate"; +}; +struct DpGuildBanAddEventInfo +{ + using Type = dpp::guild_ban_add_t; + static inline constexpr size_t ID = DpEventID::GuildBanAdd; + static inline constexpr std::string_view Name = "GuildBanAdd"; +}; +struct DpGuildBanRemoveEventInfo +{ + using Type = dpp::guild_ban_remove_t; + static inline constexpr size_t ID = DpEventID::GuildBanRemove; + static inline constexpr std::string_view Name = "GuildBanRemove"; +}; +struct DpIntegrationCreateEventInfo +{ + using Type = dpp::integration_create_t; + static inline constexpr size_t ID = DpEventID::IntegrationCreate; + static inline constexpr std::string_view Name = "IntegrationCreate"; +}; +struct DpIntegrationUpdateEventInfo +{ + using Type = dpp::integration_update_t; + static inline constexpr size_t ID = DpEventID::IntegrationUpdate; + static inline constexpr std::string_view Name = "IntegrationUpdate"; +}; +struct DpIntegrationDeleteEventInfo +{ + using Type = dpp::integration_delete_t; + static inline constexpr size_t ID = DpEventID::IntegrationDelete; + static inline constexpr std::string_view Name = "IntegrationDelete"; +}; +struct DpThreadCreateEventInfo +{ + using Type = dpp::thread_create_t; + static inline constexpr size_t ID = DpEventID::ThreadCreate; + static inline constexpr std::string_view Name = "ThreadCreate"; +}; +struct DpThreadUpdateEventInfo +{ + using Type = dpp::thread_update_t; + static inline constexpr size_t ID = DpEventID::ThreadUpdate; + static inline constexpr std::string_view Name = "ThreadUpdate"; +}; +struct DpThreadDeleteEventInfo +{ + using Type = dpp::thread_delete_t; + static inline constexpr size_t ID = DpEventID::ThreadDelete; + static inline constexpr std::string_view Name = "ThreadDelete"; +}; +struct DpThreadListSyncEventInfo +{ + using Type = dpp::thread_list_sync_t; + static inline constexpr size_t ID = DpEventID::ThreadListSync; + static inline constexpr std::string_view Name = "ThreadListSync"; +}; +struct DpThreadMemberUpdateEventInfo +{ + using Type = dpp::thread_member_update_t; + static inline constexpr size_t ID = DpEventID::ThreadMemberUpdate; + static inline constexpr std::string_view Name = "ThreadMemberUpdate"; +}; +struct DpThreadMembersUpdateEventInfo +{ + using Type = dpp::thread_members_update_t; + static inline constexpr size_t ID = DpEventID::ThreadMembersUpdate; + static inline constexpr std::string_view Name = "ThreadMembersUpdate"; +}; +struct DpGuildScheduledEventCreateEventInfo +{ + using Type = dpp::guild_scheduled_event_create_t; + static inline constexpr size_t ID = DpEventID::GuildScheduledEventCreate; + static inline constexpr std::string_view Name = "GuildScheduledEventCreate"; +}; +struct DpGuildScheduledEventUpdateEventInfo +{ + using Type = dpp::guild_scheduled_event_update_t; + static inline constexpr size_t ID = DpEventID::GuildScheduledEventUpdate; + static inline constexpr std::string_view Name = "GuildScheduledEventUpdate"; +}; +struct DpGuildScheduledEventDeleteEventInfo +{ + using Type = dpp::guild_scheduled_event_delete_t; + static inline constexpr size_t ID = DpEventID::GuildScheduledEventDelete; + static inline constexpr std::string_view Name = "GuildScheduledEventDelete"; +}; +struct DpGuildScheduledEventUserAddEventInfo +{ + using Type = dpp::guild_scheduled_event_user_add_t; + static inline constexpr size_t ID = DpEventID::GuildScheduledEventUserAdd; + static inline constexpr std::string_view Name = "GuildScheduledEventUserAdd"; +}; +struct DpGuildScheduledEventUserRemoveEventInfo +{ + using Type = dpp::guild_scheduled_event_user_remove_t; + static inline constexpr size_t ID = DpEventID::GuildScheduledEventUserRemove; + static inline constexpr std::string_view Name = "GuildScheduledEventUserRemove"; +}; +struct DpVoiceBufferSendEventInfo +{ + using Type = dpp::voice_buffer_send_t; + static inline constexpr size_t ID = DpEventID::VoiceBufferSend; + static inline constexpr std::string_view Name = "VoiceBufferSend"; +}; +struct DpVoiceUserTalkingEventInfo +{ + using Type = dpp::voice_user_talking_t; + static inline constexpr size_t ID = DpEventID::VoiceUserTalking; + static inline constexpr std::string_view Name = "VoiceUserTalking"; +}; +struct DpVoiceReadyEventInfo +{ + using Type = dpp::voice_ready_t; + static inline constexpr size_t ID = DpEventID::VoiceReady; + static inline constexpr std::string_view Name = "VoiceReady"; +}; +struct DpVoiceReceiveEventInfo +{ + using Type = dpp::voice_receive_t; + static inline constexpr size_t ID = DpEventID::VoiceReceive; + static inline constexpr std::string_view Name = "VoiceReceive"; +}; +struct DpVoiceReceiveCombinedEventInfo +{ + using Type = dpp::voice_receive_t; + static inline constexpr size_t ID = DpEventID::VoiceReceiveCombined; + static inline constexpr std::string_view Name = "VoiceReceiveCombined"; +}; +struct DpVoiceTrackMarkerEventInfo +{ + using Type = dpp::voice_track_marker_t; + static inline constexpr size_t ID = DpEventID::VoiceTrackMarker; + static inline constexpr std::string_view Name = "VoiceTrackMarker"; +}; +struct DpStageInstanceCreateEventInfo +{ + using Type = dpp::stage_instance_create_t; + static inline constexpr size_t ID = DpEventID::StageInstanceCreate; + static inline constexpr std::string_view Name = "StageInstanceCreate"; +}; +struct DpStageInstanceUpdateEventInfo +{ + using Type = dpp::stage_instance_update_t; + static inline constexpr size_t ID = DpEventID::StageInstanceUpdate; + static inline constexpr std::string_view Name = "StageInstanceUpdate"; +}; +struct DpStageInstanceDeleteEventInfo +{ + using Type = dpp::stage_instance_delete_t; + static inline constexpr size_t ID = DpEventID::StageInstanceDelete; + static inline constexpr std::string_view Name = "StageInstanceDelete"; +}; + +/* ------------------------------------------------------------------------------------------------ + * Utility used to acquire event information at compile time. +*/ +template < int > struct EventInfoID; + +/* ------------------------------------------------------------------------------------------------ + * Implementation. +*/ +template < > struct EventInfoID< DpEventID::VoiceStateUpdate > : public DpVoiceStateUpdateEventInfo { }; +template < > struct EventInfoID< DpEventID::VoiceClientDisconnect > : public DpVoiceClientDisconnectEventInfo { }; +template < > struct EventInfoID< DpEventID::VoiceClientSpeaking > : public DpVoiceClientSpeakingEventInfo { }; +template < > struct EventInfoID< DpEventID::Log > : public DpLogEventInfo { }; +template < > struct EventInfoID< DpEventID::GuildJoinRequestDelete > : public DpGuildJoinRequestDeleteEventInfo { }; +template < > struct EventInfoID< DpEventID::InteractionCreate > : public DpInteractionCreateEventInfo { }; +template < > struct EventInfoID< DpEventID::SlashCommand > : public DpSlashCommandEventInfo { }; +template < > struct EventInfoID< DpEventID::ButtonClick > : public DpButtonClickEventInfo { }; +template < > struct EventInfoID< DpEventID::AutoComplete > : public DpAutoCompleteEventInfo { }; +template < > struct EventInfoID< DpEventID::SelectClick > : public DpSelectClickEventInfo { }; +template < > struct EventInfoID< DpEventID::MessageContextMenu > : public DpMessageContextMenuEventInfo { }; +template < > struct EventInfoID< DpEventID::UserContextMenu > : public DpUserContextMenuEventInfo { }; +template < > struct EventInfoID< DpEventID::FormSubmit > : public DpFormSubmitEventInfo { }; +template < > struct EventInfoID< DpEventID::GuildDelete > : public DpGuildDeleteEventInfo { }; +template < > struct EventInfoID< DpEventID::ChannelDelete > : public DpChannelDeleteEventInfo { }; +template < > struct EventInfoID< DpEventID::ChannelUpdate > : public DpChannelUpdateEventInfo { }; +template < > struct EventInfoID< DpEventID::Ready > : public DpReadyEventInfo { }; +template < > struct EventInfoID< DpEventID::MessageDelete > : public DpMessageDeleteEventInfo { }; +template < > struct EventInfoID< DpEventID::GuildMemberRemove > : public DpGuildMemberRemoveEventInfo { }; +template < > struct EventInfoID< DpEventID::Resumed > : public DpResumedEventInfo { }; +template < > struct EventInfoID< DpEventID::GuildRoleCreate > : public DpGuildRoleCreateEventInfo { }; +template < > struct EventInfoID< DpEventID::TypingStart > : public DpTypingStartEventInfo { }; +template < > struct EventInfoID< DpEventID::MessageReactionAdd > : public DpMessageReactionAddEventInfo { }; +template < > struct EventInfoID< DpEventID::GuildMembersChunk > : public DpGuildMembersChunkEventInfo { }; +template < > struct EventInfoID< DpEventID::MessageReactionRemove > : public DpMessageReactionRemoveEventInfo { }; +template < > struct EventInfoID< DpEventID::GuildCreate > : public DpGuildCreateEventInfo { }; +template < > struct EventInfoID< DpEventID::ChannelCreate > : public DpChannelCreateEventInfo { }; +template < > struct EventInfoID< DpEventID::MessageReactionRemoveEmoji > : public DpMessageReactionRemoveEmojiEventInfo { }; +template < > struct EventInfoID< DpEventID::MessageDeleteDulk > : public DpMessageDeleteDulkEventInfo { }; +template < > struct EventInfoID< DpEventID::GuildRoleUpdate > : public DpGuildRoleUpdateEventInfo { }; +template < > struct EventInfoID< DpEventID::GuildRoleDelete > : public DpGuildRoleDeleteEventInfo { }; +template < > struct EventInfoID< DpEventID::ChannelPinsUpdate > : public DpChannelPinsUpdateEventInfo { }; +template < > struct EventInfoID< DpEventID::MessageReactionRemoveAll > : public DpMessageReactionRemoveAllEventInfo { }; +template < > struct EventInfoID< DpEventID::VoiceServerUpdate > : public DpVoiceServerUpdateEventInfo { }; +template < > struct EventInfoID< DpEventID::GuildEmojisUpdate > : public DpGuildEmojisUpdateEventInfo { }; +template < > struct EventInfoID< DpEventID::GuildStickersUpdate > : public DpGuildStickersUpdateEventInfo { }; +template < > struct EventInfoID< DpEventID::PresenceUpdate > : public DpPresenceUpdateEventInfo { }; +template < > struct EventInfoID< DpEventID::WebhooksUpdate > : public DpWebhooksUpdateEventInfo { }; +template < > struct EventInfoID< DpEventID::AutomodRuleCreate > : public DpAutomodRuleCreateEventInfo { }; +template < > struct EventInfoID< DpEventID::AutomodRuleUpdate > : public DpAutomodRuleUpdateEventInfo { }; +template < > struct EventInfoID< DpEventID::AutomodRuleDelete > : public DpAutomodRuleDeleteEventInfo { }; +template < > struct EventInfoID< DpEventID::AutomodRuleExecute > : public DpAutomodRuleExecuteEventInfo { }; +template < > struct EventInfoID< DpEventID::GuildMemberAdd > : public DpGuildMemberAddEventInfo { }; +template < > struct EventInfoID< DpEventID::InviteDelete > : public DpInviteDeleteEventInfo { }; +template < > struct EventInfoID< DpEventID::GuildUpdate > : public DpGuildUpdateEventInfo { }; +template < > struct EventInfoID< DpEventID::GuildIntegrationsUpdate > : public DpGuildIntegrationsUpdateEventInfo { }; +template < > struct EventInfoID< DpEventID::GuildMemberUpdate > : public DpGuildMemberUpdateEventInfo { }; +template < > struct EventInfoID< DpEventID::InviteCreate > : public DpInviteCreateEventInfo { }; +template < > struct EventInfoID< DpEventID::MessageUpdate > : public DpMessageUpdateEventInfo { }; +template < > struct EventInfoID< DpEventID::UserUpdate > : public DpUserUpdateEventInfo { }; +template < > struct EventInfoID< DpEventID::MessageCreate > : public DpMessageCreateEventInfo { }; +template < > struct EventInfoID< DpEventID::GuildAuditLogEntryCreate > : public DpGuildAuditLogEntryCreateEventInfo { }; +template < > struct EventInfoID< DpEventID::GuildBanAdd > : public DpGuildBanAddEventInfo { }; +template < > struct EventInfoID< DpEventID::GuildBanRemove > : public DpGuildBanRemoveEventInfo { }; +template < > struct EventInfoID< DpEventID::IntegrationCreate > : public DpIntegrationCreateEventInfo { }; +template < > struct EventInfoID< DpEventID::IntegrationUpdate > : public DpIntegrationUpdateEventInfo { }; +template < > struct EventInfoID< DpEventID::IntegrationDelete > : public DpIntegrationDeleteEventInfo { }; +template < > struct EventInfoID< DpEventID::ThreadCreate > : public DpThreadCreateEventInfo { }; +template < > struct EventInfoID< DpEventID::ThreadUpdate > : public DpThreadUpdateEventInfo { }; +template < > struct EventInfoID< DpEventID::ThreadDelete > : public DpThreadDeleteEventInfo { }; +template < > struct EventInfoID< DpEventID::ThreadListSync > : public DpThreadListSyncEventInfo { }; +template < > struct EventInfoID< DpEventID::ThreadMemberUpdate > : public DpThreadMemberUpdateEventInfo { }; +template < > struct EventInfoID< DpEventID::ThreadMembersUpdate > : public DpThreadMembersUpdateEventInfo { }; +template < > struct EventInfoID< DpEventID::GuildScheduledEventCreate > : public DpGuildScheduledEventCreateEventInfo { }; +template < > struct EventInfoID< DpEventID::GuildScheduledEventUpdate > : public DpGuildScheduledEventUpdateEventInfo { }; +template < > struct EventInfoID< DpEventID::GuildScheduledEventDelete > : public DpGuildScheduledEventDeleteEventInfo { }; +template < > struct EventInfoID< DpEventID::GuildScheduledEventUserAdd > : public DpGuildScheduledEventUserAddEventInfo { }; +template < > struct EventInfoID< DpEventID::GuildScheduledEventUserRemove > : public DpGuildScheduledEventUserRemoveEventInfo { }; +template < > struct EventInfoID< DpEventID::VoiceBufferSend > : public DpVoiceBufferSendEventInfo { }; +template < > struct EventInfoID< DpEventID::VoiceUserTalking > : public DpVoiceUserTalkingEventInfo { }; +template < > struct EventInfoID< DpEventID::VoiceReady > : public DpVoiceReadyEventInfo { }; +template < > struct EventInfoID< DpEventID::VoiceReceive > : public DpVoiceReceiveEventInfo { }; +template < > struct EventInfoID< DpEventID::VoiceReceiveCombined > : public DpVoiceReceiveCombinedEventInfo { }; +template < > struct EventInfoID< DpEventID::VoiceTrackMarker > : public DpVoiceTrackMarkerEventInfo { }; +template < > struct EventInfoID< DpEventID::StageInstanceCreate > : public DpStageInstanceCreateEventInfo { }; +template < > struct EventInfoID< DpEventID::StageInstanceUpdate > : public DpStageInstanceUpdateEventInfo { }; +template < > struct EventInfoID< DpEventID::StageInstanceDelete > : public DpStageInstanceDeleteEventInfo { }; + +} // Namespace:: SqMod diff --git a/module/Library/Discord/Events.cpp b/module/Library/Discord/Events.cpp new file mode 100644 index 00000000..529b235e --- /dev/null +++ b/module/Library/Discord/Events.cpp @@ -0,0 +1,787 @@ +// ------------------------------------------------------------------------------------------------ +#include "Library/Discord/Events.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +// ------------------------------------------------------------------------------------------------ +SQMOD_DECL_TYPENAME(SqDpVoiceStateUpdateEvent, _SC("SqDiscordVoiceStateUpdateEvent")) +SQMOD_DECL_TYPENAME(SqDpVoiceClientDisconnectEvent, _SC("SqDiscordVoiceClientDisconnectEvent")) +SQMOD_DECL_TYPENAME(SqDpVoiceClientSpeakingEvent, _SC("SqDiscordVoiceClientSpeakingEvent")) +SQMOD_DECL_TYPENAME(SqDpLogEvent, _SC("SqDiscordLogEvent")) +SQMOD_DECL_TYPENAME(SqDpGuildJoinRequestDeleteEvent, _SC("SqDiscordGuildJoinRequestDeleteEvent")) +SQMOD_DECL_TYPENAME(SqDpInteractionCreateEvent, _SC("SqDiscordInteractionCreateEvent")) +SQMOD_DECL_TYPENAME(SqDpSlashCommandEvent, _SC("SqDiscordSlashCommandEvent")) +SQMOD_DECL_TYPENAME(SqDpButtonClickEvent, _SC("SqDiscordButtonClickEvent")) +SQMOD_DECL_TYPENAME(SqDpAutoCompleteEvent, _SC("SqDiscordAutoCompleteEvent")) +SQMOD_DECL_TYPENAME(SqDpSelectClickEvent, _SC("SqDiscordSelectClickEvent")) +SQMOD_DECL_TYPENAME(SqDpMessageContextMenuEvent, _SC("SqDiscordMessageContextMenuEvent")) +SQMOD_DECL_TYPENAME(SqDpUserContextMenuEvent, _SC("SqDiscordUserContextMenuEvent")) +SQMOD_DECL_TYPENAME(SqDpFormSubmitEvent, _SC("SqDiscordFormSubmitEvent")) +SQMOD_DECL_TYPENAME(SqDpGuildDeleteEvent, _SC("SqDiscordGuildDeleteEvent")) +SQMOD_DECL_TYPENAME(SqDpChannelDeleteEvent, _SC("SqDiscordChannelDeleteEvent")) +SQMOD_DECL_TYPENAME(SqDpChannelUpdateEvent, _SC("SqDiscordChannelUpdateEvent")) +SQMOD_DECL_TYPENAME(SqDpReadyEvent, _SC("SqDiscordReadyEvent")) +SQMOD_DECL_TYPENAME(SqDpMessageDeleteEvent, _SC("SqDiscordMessageDeleteEvent")) +SQMOD_DECL_TYPENAME(SqDpGuildMemberRemoveEvent, _SC("SqDiscordGuildMemberRemoveEvent")) +SQMOD_DECL_TYPENAME(SqDpResumedEvent, _SC("SqDiscordResumedEvent")) +SQMOD_DECL_TYPENAME(SqDpGuildRoleCreateEvent, _SC("SqDiscordGuildRoleCreateEvent")) +SQMOD_DECL_TYPENAME(SqDpTypingStartEvent, _SC("SqDiscordTypingStartEvent")) +SQMOD_DECL_TYPENAME(SqDpMessageReactionAddEvent, _SC("SqDiscordMessageReactionAddEvent")) +SQMOD_DECL_TYPENAME(SqDpGuildMembersChunkEvent, _SC("SqDiscordGuildMembersChunkEvent")) +SQMOD_DECL_TYPENAME(SqDpMessageReactionRemoveEvent, _SC("SqDiscordMessageReactionRemoveEvent")) +SQMOD_DECL_TYPENAME(SqDpGuildCreateEvent, _SC("SqDiscordGuildCreateEvent")) +SQMOD_DECL_TYPENAME(SqDpChannelCreateEvent, _SC("SqDiscordChannelCreateEvent")) +SQMOD_DECL_TYPENAME(SqDpMessageReactionRemoveEmojiEvent, _SC("SqDiscordMessageReactionRemoveEmojiEvent")) +SQMOD_DECL_TYPENAME(SqDpMessageDeleteDulkEvent, _SC("SqDiscordMessageDeleteDulkEvent")) +SQMOD_DECL_TYPENAME(SqDpGuildRoleUpdateEvent, _SC("SqDiscordGuildRoleUpdateEvent")) +SQMOD_DECL_TYPENAME(SqDpGuildRoleDeleteEvent, _SC("SqDiscordGuildRoleDeleteEvent")) +SQMOD_DECL_TYPENAME(SqDpChannelPinsUpdateEvent, _SC("SqDiscordChannelPinsUpdateEvent")) +SQMOD_DECL_TYPENAME(SqDpMessageReactionRemoveAllEvent, _SC("SqDiscordMessageReactionRemoveAllEvent")) +SQMOD_DECL_TYPENAME(SqDpVoiceServerUpdateEvent, _SC("SqDiscordVoiceServerUpdateEvent")) +SQMOD_DECL_TYPENAME(SqDpGuildEmojisUpdateEvent, _SC("SqDiscordGuildEmojisUpdateEvent")) +SQMOD_DECL_TYPENAME(SqDpGuildStickersUpdateEvent, _SC("SqDiscordGuildStickersUpdateEvent")) +SQMOD_DECL_TYPENAME(SqDpPresenceUpdateEvent, _SC("SqDiscordPresenceUpdateEvent")) +SQMOD_DECL_TYPENAME(SqDpWebhooksUpdateEvent, _SC("SqDiscordWebhooksUpdateEvent")) +SQMOD_DECL_TYPENAME(SqDpAutomodRuleCreateEvent, _SC("SqDiscordAutomodRuleCreateEvent")) +SQMOD_DECL_TYPENAME(SqDpAutomodRuleUpdateEvent, _SC("SqDiscordAutomodRuleUpdateEvent")) +SQMOD_DECL_TYPENAME(SqDpAutomodRuleDeleteEvent, _SC("SqDiscordAutomodRuleDeleteEvent")) +SQMOD_DECL_TYPENAME(SqDpAutomodRuleExecuteEvent, _SC("SqDiscordAutomodRuleExecuteEvent")) +SQMOD_DECL_TYPENAME(SqDpGuildMemberAddEvent, _SC("SqDiscordGuildMemberAddEvent")) +SQMOD_DECL_TYPENAME(SqDpInviteDeleteEvent, _SC("SqDiscordInviteDeleteEvent")) +SQMOD_DECL_TYPENAME(SqDpGuildUpdateEvent, _SC("SqDiscordGuildUpdateEvent")) +SQMOD_DECL_TYPENAME(SqDpGuildIntegrationsUpdateEvent, _SC("SqDiscordGuildIntegrationsUpdateEvent")) +SQMOD_DECL_TYPENAME(SqDpGuildMemberUpdateEvent, _SC("SqDiscordGuildMemberUpdateEvent")) +SQMOD_DECL_TYPENAME(SqDpInviteCreateEvent, _SC("SqDiscordInviteCreateEvent")) +SQMOD_DECL_TYPENAME(SqDpMessageUpdateEvent, _SC("SqDiscordMessageUpdateEvent")) +SQMOD_DECL_TYPENAME(SqDpUserUpdateEvent, _SC("SqDiscordUserUpdateEvent")) +SQMOD_DECL_TYPENAME(SqDpMessageCreateEvent, _SC("SqDiscordMessageCreateEvent")) +SQMOD_DECL_TYPENAME(SqDpGuildAuditLogEntryCreateEvent, _SC("SqDiscordGuildAuditLogEntryCreateEvent")) +SQMOD_DECL_TYPENAME(SqDpGuildBanAddEvent, _SC("SqDiscordGuildBanAddEvent")) +SQMOD_DECL_TYPENAME(SqDpGuildBanRemoveEvent, _SC("SqDiscordGuildBanRemoveEvent")) +SQMOD_DECL_TYPENAME(SqDpIntegrationCreateEvent, _SC("SqDiscordIntegrationCreateEvent")) +SQMOD_DECL_TYPENAME(SqDpIntegrationUpdateEvent, _SC("SqDiscordIntegrationUpdateEvent")) +SQMOD_DECL_TYPENAME(SqDpIntegrationDeleteEvent, _SC("SqDiscordIntegrationDeleteEvent")) +SQMOD_DECL_TYPENAME(SqDpThreadCreateEvent, _SC("SqDiscordThreadCreateEvent")) +SQMOD_DECL_TYPENAME(SqDpThreadUpdateEvent, _SC("SqDiscordThreadUpdateEvent")) +SQMOD_DECL_TYPENAME(SqDpThreadDeleteEvent, _SC("SqDiscordThreadDeleteEvent")) +SQMOD_DECL_TYPENAME(SqDpThreadListSyncEvent, _SC("SqDiscordThreadListSyncEvent")) +SQMOD_DECL_TYPENAME(SqDpThreadMemberUpdateEvent, _SC("SqDiscordThreadMemberUpdateEvent")) +SQMOD_DECL_TYPENAME(SqDpThreadMembersUpdateEvent, _SC("SqDiscordThreadMembersUpdateEvent")) +SQMOD_DECL_TYPENAME(SqDpGuildScheduledEventCreateEvent, _SC("SqDiscordGuildScheduledEventCreateEvent")) +SQMOD_DECL_TYPENAME(SqDpGuildScheduledEventUpdateEvent, _SC("SqDiscordGuildScheduledEventUpdateEvent")) +SQMOD_DECL_TYPENAME(SqDpGuildScheduledEventDeleteEvent, _SC("SqDiscordGuildScheduledEventDeleteEvent")) +SQMOD_DECL_TYPENAME(SqDpGuildScheduledEventUserAddEvent, _SC("SqDiscordGuildScheduledEventUserAddEvent")) +SQMOD_DECL_TYPENAME(SqDpGuildScheduledEventUserRemoveEvent, _SC("SqDiscordGuildScheduledEventUserRemoveEvent")) +SQMOD_DECL_TYPENAME(SqDpVoiceBufferSendEvent, _SC("SqDiscordVoiceBufferSendEvent")) +SQMOD_DECL_TYPENAME(SqDpVoiceUserTalkingEvent, _SC("SqDiscordVoiceUserTalkingEvent")) +SQMOD_DECL_TYPENAME(SqDpVoiceReadyEvent, _SC("SqDiscordVoiceReadyEvent")) +SQMOD_DECL_TYPENAME(SqDpVoiceReceiveEvent, _SC("SqDiscordVoiceReceiveEvent")) +SQMOD_DECL_TYPENAME(SqDpVoiceReceiveCombinedEvent, _SC("SqDiscordVoiceReceiveCombinedEvent")) +SQMOD_DECL_TYPENAME(SqDpVoiceTrackMarkerEvent, _SC("SqDiscordVoiceTrackMarkerEvent")) +SQMOD_DECL_TYPENAME(SqDpStageInstanceCreateEvent, _SC("SqDiscordStageInstanceCreateEvent")) +SQMOD_DECL_TYPENAME(SqDpStageInstanceUpdateEvent, _SC("SqDiscordStageInstanceUpdateEvent")) +SQMOD_DECL_TYPENAME(SqDpStageInstanceDeleteEvent, _SC("SqDiscordStageInstanceDeleteEvent")) + +// ------------------------------------------------------------------------------------------------ +void Register_Discord_Events(HSQUIRRELVM vm, Table & ns) +{ + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("VoiceStateUpdate"), + Class< DpVoiceStateUpdateEvent, NoConstructor< DpVoiceStateUpdateEvent > >(vm, SqDpVoiceStateUpdateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpVoiceStateUpdateEvent::Fn) + .Func(_SC("_tostring"), &DpVoiceStateUpdateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpVoiceStateUpdateEvent::GetRawEvent) + .Prop(_SC("State"), &DpVoiceStateUpdateEvent::GetState) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("VoiceClientDisconnect"), + Class< DpVoiceClientDisconnectEvent, NoConstructor< DpVoiceClientDisconnectEvent > >(vm, SqDpVoiceClientDisconnectEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpVoiceClientDisconnectEvent::Fn) + .Func(_SC("_tostring"), &DpVoiceClientDisconnectEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpVoiceClientDisconnectEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("VoiceClientSpeaking"), + Class< DpVoiceClientSpeakingEvent, NoConstructor< DpVoiceClientSpeakingEvent > >(vm, SqDpVoiceClientSpeakingEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpVoiceClientSpeakingEvent::Fn) + .Func(_SC("_tostring"), &DpVoiceClientSpeakingEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpVoiceClientSpeakingEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("Log"), + Class< DpLogEvent, NoConstructor< DpLogEvent > >(vm, SqDpLogEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpLogEvent::Fn) + .Func(_SC("_tostring"), &DpLogEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpLogEvent::GetRawEvent) + .Prop(_SC("Severity"), &DpLogEvent::GetSeverity) + .Prop(_SC("Message"), &DpLogEvent::GetMessage) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("GuildJoinRequestDelete"), + Class< DpGuildJoinRequestDeleteEvent, NoConstructor< DpGuildJoinRequestDeleteEvent > >(vm, SqDpGuildJoinRequestDeleteEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpGuildJoinRequestDeleteEvent::Fn) + .Func(_SC("_tostring"), &DpGuildJoinRequestDeleteEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpGuildJoinRequestDeleteEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("InteractionCreate"), + Class< DpInteractionCreateEvent, NoConstructor< DpInteractionCreateEvent > >(vm, SqDpInteractionCreateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpInteractionCreateEvent::Fn) + .Func(_SC("_tostring"), &DpInteractionCreateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpInteractionCreateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("SlashCommand"), + Class< DpSlashCommandEvent, NoConstructor< DpSlashCommandEvent > >(vm, SqDpSlashCommandEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpSlashCommandEvent::Fn) + .Func(_SC("_tostring"), &DpSlashCommandEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpSlashCommandEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("ButtonClick"), + Class< DpButtonClickEvent, NoConstructor< DpButtonClickEvent > >(vm, SqDpButtonClickEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpButtonClickEvent::Fn) + .Func(_SC("_tostring"), &DpButtonClickEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpButtonClickEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("AutoComplete"), + Class< DpAutoCompleteEvent, NoConstructor< DpAutoCompleteEvent > >(vm, SqDpAutoCompleteEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpAutoCompleteEvent::Fn) + .Func(_SC("_tostring"), &DpAutoCompleteEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpAutoCompleteEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("SelectClick"), + Class< DpSelectClickEvent, NoConstructor< DpSelectClickEvent > >(vm, SqDpSelectClickEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpSelectClickEvent::Fn) + .Func(_SC("_tostring"), &DpSelectClickEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpSelectClickEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("MessageContextMenu"), + Class< DpMessageContextMenuEvent, NoConstructor< DpMessageContextMenuEvent > >(vm, SqDpMessageContextMenuEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpMessageContextMenuEvent::Fn) + .Func(_SC("_tostring"), &DpMessageContextMenuEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpMessageContextMenuEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("UserContextMenu"), + Class< DpUserContextMenuEvent, NoConstructor< DpUserContextMenuEvent > >(vm, SqDpUserContextMenuEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpUserContextMenuEvent::Fn) + .Func(_SC("_tostring"), &DpUserContextMenuEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpUserContextMenuEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("FormSubmit"), + Class< DpFormSubmitEvent, NoConstructor< DpFormSubmitEvent > >(vm, SqDpFormSubmitEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpFormSubmitEvent::Fn) + .Func(_SC("_tostring"), &DpFormSubmitEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpFormSubmitEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("GuildDelete"), + Class< DpGuildDeleteEvent, NoConstructor< DpGuildDeleteEvent > >(vm, SqDpGuildDeleteEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpGuildDeleteEvent::Fn) + .Func(_SC("_tostring"), &DpGuildDeleteEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpGuildDeleteEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("ChannelDelete"), + Class< DpChannelDeleteEvent, NoConstructor< DpChannelDeleteEvent > >(vm, SqDpChannelDeleteEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpChannelDeleteEvent::Fn) + .Func(_SC("_tostring"), &DpChannelDeleteEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpChannelDeleteEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("ChannelUpdate"), + Class< DpChannelUpdateEvent, NoConstructor< DpChannelUpdateEvent > >(vm, SqDpChannelUpdateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpChannelUpdateEvent::Fn) + .Func(_SC("_tostring"), &DpChannelUpdateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpChannelUpdateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("Ready"), + Class< DpReadyEvent, NoConstructor< DpReadyEvent > >(vm, SqDpReadyEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpReadyEvent::Fn) + .Func(_SC("_tostring"), &DpReadyEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpReadyEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("MessageDelete"), + Class< DpMessageDeleteEvent, NoConstructor< DpMessageDeleteEvent > >(vm, SqDpMessageDeleteEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpMessageDeleteEvent::Fn) + .Func(_SC("_tostring"), &DpMessageDeleteEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpMessageDeleteEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("GuildMemberRemove"), + Class< DpGuildMemberRemoveEvent, NoConstructor< DpGuildMemberRemoveEvent > >(vm, SqDpGuildMemberRemoveEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpGuildMemberRemoveEvent::Fn) + .Func(_SC("_tostring"), &DpGuildMemberRemoveEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpGuildMemberRemoveEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("Resumed"), + Class< DpResumedEvent, NoConstructor< DpResumedEvent > >(vm, SqDpResumedEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpResumedEvent::Fn) + .Func(_SC("_tostring"), &DpResumedEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpResumedEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("GuildRoleCreate"), + Class< DpGuildRoleCreateEvent, NoConstructor< DpGuildRoleCreateEvent > >(vm, SqDpGuildRoleCreateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpGuildRoleCreateEvent::Fn) + .Func(_SC("_tostring"), &DpGuildRoleCreateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpGuildRoleCreateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("TypingStart"), + Class< DpTypingStartEvent, NoConstructor< DpTypingStartEvent > >(vm, SqDpTypingStartEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpTypingStartEvent::Fn) + .Func(_SC("_tostring"), &DpTypingStartEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpTypingStartEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("MessageReactionAdd"), + Class< DpMessageReactionAddEvent, NoConstructor< DpMessageReactionAddEvent > >(vm, SqDpMessageReactionAddEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpMessageReactionAddEvent::Fn) + .Func(_SC("_tostring"), &DpMessageReactionAddEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpMessageReactionAddEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("GuildMembersChunk"), + Class< DpGuildMembersChunkEvent, NoConstructor< DpGuildMembersChunkEvent > >(vm, SqDpGuildMembersChunkEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpGuildMembersChunkEvent::Fn) + .Func(_SC("_tostring"), &DpGuildMembersChunkEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpGuildMembersChunkEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("MessageReactionRemove"), + Class< DpMessageReactionRemoveEvent, NoConstructor< DpMessageReactionRemoveEvent > >(vm, SqDpMessageReactionRemoveEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpMessageReactionRemoveEvent::Fn) + .Func(_SC("_tostring"), &DpMessageReactionRemoveEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpMessageReactionRemoveEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("GuildCreate"), + Class< DpGuildCreateEvent, NoConstructor< DpGuildCreateEvent > >(vm, SqDpGuildCreateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpGuildCreateEvent::Fn) + .Func(_SC("_tostring"), &DpGuildCreateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpGuildCreateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("ChannelCreate"), + Class< DpChannelCreateEvent, NoConstructor< DpChannelCreateEvent > >(vm, SqDpChannelCreateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpChannelCreateEvent::Fn) + .Func(_SC("_tostring"), &DpChannelCreateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpChannelCreateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("MessageReactionRemoveEmoji"), + Class< DpMessageReactionRemoveEmojiEvent, NoConstructor< DpMessageReactionRemoveEmojiEvent > >(vm, SqDpMessageReactionRemoveEmojiEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpMessageReactionRemoveEmojiEvent::Fn) + .Func(_SC("_tostring"), &DpMessageReactionRemoveEmojiEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpMessageReactionRemoveEmojiEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("MessageDeleteDulk"), + Class< DpMessageDeleteDulkEvent, NoConstructor< DpMessageDeleteDulkEvent > >(vm, SqDpMessageDeleteDulkEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpMessageDeleteDulkEvent::Fn) + .Func(_SC("_tostring"), &DpMessageDeleteDulkEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpMessageDeleteDulkEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("GuildRoleUpdate"), + Class< DpGuildRoleUpdateEvent, NoConstructor< DpGuildRoleUpdateEvent > >(vm, SqDpGuildRoleUpdateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpGuildRoleUpdateEvent::Fn) + .Func(_SC("_tostring"), &DpGuildRoleUpdateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpGuildRoleUpdateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("GuildRoleDelete"), + Class< DpGuildRoleDeleteEvent, NoConstructor< DpGuildRoleDeleteEvent > >(vm, SqDpGuildRoleDeleteEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpGuildRoleDeleteEvent::Fn) + .Func(_SC("_tostring"), &DpGuildRoleDeleteEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpGuildRoleDeleteEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("ChannelPinsUpdate"), + Class< DpChannelPinsUpdateEvent, NoConstructor< DpChannelPinsUpdateEvent > >(vm, SqDpChannelPinsUpdateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpChannelPinsUpdateEvent::Fn) + .Func(_SC("_tostring"), &DpChannelPinsUpdateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpChannelPinsUpdateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("MessageReactionRemoveAll"), + Class< DpMessageReactionRemoveAllEvent, NoConstructor< DpMessageReactionRemoveAllEvent > >(vm, SqDpMessageReactionRemoveAllEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpMessageReactionRemoveAllEvent::Fn) + .Func(_SC("_tostring"), &DpMessageReactionRemoveAllEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpMessageReactionRemoveAllEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("VoiceServerUpdate"), + Class< DpVoiceServerUpdateEvent, NoConstructor< DpVoiceServerUpdateEvent > >(vm, SqDpVoiceServerUpdateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpVoiceServerUpdateEvent::Fn) + .Func(_SC("_tostring"), &DpVoiceServerUpdateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpVoiceServerUpdateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("GuildEmojisUpdate"), + Class< DpGuildEmojisUpdateEvent, NoConstructor< DpGuildEmojisUpdateEvent > >(vm, SqDpGuildEmojisUpdateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpGuildEmojisUpdateEvent::Fn) + .Func(_SC("_tostring"), &DpGuildEmojisUpdateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpGuildEmojisUpdateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("GuildStickersUpdate"), + Class< DpGuildStickersUpdateEvent, NoConstructor< DpGuildStickersUpdateEvent > >(vm, SqDpGuildStickersUpdateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpGuildStickersUpdateEvent::Fn) + .Func(_SC("_tostring"), &DpGuildStickersUpdateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpGuildStickersUpdateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("PresenceUpdate"), + Class< DpPresenceUpdateEvent, NoConstructor< DpPresenceUpdateEvent > >(vm, SqDpPresenceUpdateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpPresenceUpdateEvent::Fn) + .Func(_SC("_tostring"), &DpPresenceUpdateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpPresenceUpdateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("WebhooksUpdate"), + Class< DpWebhooksUpdateEvent, NoConstructor< DpWebhooksUpdateEvent > >(vm, SqDpWebhooksUpdateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpWebhooksUpdateEvent::Fn) + .Func(_SC("_tostring"), &DpWebhooksUpdateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpWebhooksUpdateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("AutomodRuleCreate"), + Class< DpAutomodRuleCreateEvent, NoConstructor< DpAutomodRuleCreateEvent > >(vm, SqDpAutomodRuleCreateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpAutomodRuleCreateEvent::Fn) + .Func(_SC("_tostring"), &DpAutomodRuleCreateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpAutomodRuleCreateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("AutomodRuleUpdate"), + Class< DpAutomodRuleUpdateEvent, NoConstructor< DpAutomodRuleUpdateEvent > >(vm, SqDpAutomodRuleUpdateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpAutomodRuleUpdateEvent::Fn) + .Func(_SC("_tostring"), &DpAutomodRuleUpdateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpAutomodRuleUpdateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("AutomodRuleDelete"), + Class< DpAutomodRuleDeleteEvent, NoConstructor< DpAutomodRuleDeleteEvent > >(vm, SqDpAutomodRuleDeleteEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpAutomodRuleDeleteEvent::Fn) + .Func(_SC("_tostring"), &DpAutomodRuleDeleteEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpAutomodRuleDeleteEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("AutomodRuleExecute"), + Class< DpAutomodRuleExecuteEvent, NoConstructor< DpAutomodRuleExecuteEvent > >(vm, SqDpAutomodRuleExecuteEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpAutomodRuleExecuteEvent::Fn) + .Func(_SC("_tostring"), &DpAutomodRuleExecuteEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpAutomodRuleExecuteEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("GuildMemberAdd"), + Class< DpGuildMemberAddEvent, NoConstructor< DpGuildMemberAddEvent > >(vm, SqDpGuildMemberAddEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpGuildMemberAddEvent::Fn) + .Func(_SC("_tostring"), &DpGuildMemberAddEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpGuildMemberAddEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("InviteDelete"), + Class< DpInviteDeleteEvent, NoConstructor< DpInviteDeleteEvent > >(vm, SqDpInviteDeleteEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpInviteDeleteEvent::Fn) + .Func(_SC("_tostring"), &DpInviteDeleteEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpInviteDeleteEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("GuildUpdate"), + Class< DpGuildUpdateEvent, NoConstructor< DpGuildUpdateEvent > >(vm, SqDpGuildUpdateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpGuildUpdateEvent::Fn) + .Func(_SC("_tostring"), &DpGuildUpdateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpGuildUpdateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("GuildIntegrationsUpdate"), + Class< DpGuildIntegrationsUpdateEvent, NoConstructor< DpGuildIntegrationsUpdateEvent > >(vm, SqDpGuildIntegrationsUpdateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpGuildIntegrationsUpdateEvent::Fn) + .Func(_SC("_tostring"), &DpGuildIntegrationsUpdateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpGuildIntegrationsUpdateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("GuildMemberUpdate"), + Class< DpGuildMemberUpdateEvent, NoConstructor< DpGuildMemberUpdateEvent > >(vm, SqDpGuildMemberUpdateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpGuildMemberUpdateEvent::Fn) + .Func(_SC("_tostring"), &DpGuildMemberUpdateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpGuildMemberUpdateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("InviteCreate"), + Class< DpInviteCreateEvent, NoConstructor< DpInviteCreateEvent > >(vm, SqDpInviteCreateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpInviteCreateEvent::Fn) + .Func(_SC("_tostring"), &DpInviteCreateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpInviteCreateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("MessageUpdate"), + Class< DpMessageUpdateEvent, NoConstructor< DpMessageUpdateEvent > >(vm, SqDpMessageUpdateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpMessageUpdateEvent::Fn) + .Func(_SC("_tostring"), &DpMessageUpdateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpMessageUpdateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("UserUpdate"), + Class< DpUserUpdateEvent, NoConstructor< DpUserUpdateEvent > >(vm, SqDpUserUpdateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpUserUpdateEvent::Fn) + .Func(_SC("_tostring"), &DpUserUpdateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpUserUpdateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("MessageCreate"), + Class< DpMessageCreateEvent, NoConstructor< DpMessageCreateEvent > >(vm, SqDpMessageCreateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpMessageCreateEvent::Fn) + .Func(_SC("_tostring"), &DpMessageCreateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpMessageCreateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("GuildAuditLogEntryCreate"), + Class< DpGuildAuditLogEntryCreateEvent, NoConstructor< DpGuildAuditLogEntryCreateEvent > >(vm, SqDpGuildAuditLogEntryCreateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpGuildAuditLogEntryCreateEvent::Fn) + .Func(_SC("_tostring"), &DpGuildAuditLogEntryCreateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpGuildAuditLogEntryCreateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("GuildBanAdd"), + Class< DpGuildBanAddEvent, NoConstructor< DpGuildBanAddEvent > >(vm, SqDpGuildBanAddEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpGuildBanAddEvent::Fn) + .Func(_SC("_tostring"), &DpGuildBanAddEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpGuildBanAddEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("GuildBanRemove"), + Class< DpGuildBanRemoveEvent, NoConstructor< DpGuildBanRemoveEvent > >(vm, SqDpGuildBanRemoveEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpGuildBanRemoveEvent::Fn) + .Func(_SC("_tostring"), &DpGuildBanRemoveEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpGuildBanRemoveEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("IntegrationCreate"), + Class< DpIntegrationCreateEvent, NoConstructor< DpIntegrationCreateEvent > >(vm, SqDpIntegrationCreateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpIntegrationCreateEvent::Fn) + .Func(_SC("_tostring"), &DpIntegrationCreateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpIntegrationCreateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("IntegrationUpdate"), + Class< DpIntegrationUpdateEvent, NoConstructor< DpIntegrationUpdateEvent > >(vm, SqDpIntegrationUpdateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpIntegrationUpdateEvent::Fn) + .Func(_SC("_tostring"), &DpIntegrationUpdateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpIntegrationUpdateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("IntegrationDelete"), + Class< DpIntegrationDeleteEvent, NoConstructor< DpIntegrationDeleteEvent > >(vm, SqDpIntegrationDeleteEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpIntegrationDeleteEvent::Fn) + .Func(_SC("_tostring"), &DpIntegrationDeleteEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpIntegrationDeleteEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("ThreadCreate"), + Class< DpThreadCreateEvent, NoConstructor< DpThreadCreateEvent > >(vm, SqDpThreadCreateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpThreadCreateEvent::Fn) + .Func(_SC("_tostring"), &DpThreadCreateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpThreadCreateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("ThreadUpdate"), + Class< DpThreadUpdateEvent, NoConstructor< DpThreadUpdateEvent > >(vm, SqDpThreadUpdateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpThreadUpdateEvent::Fn) + .Func(_SC("_tostring"), &DpThreadUpdateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpThreadUpdateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("ThreadDelete"), + Class< DpThreadDeleteEvent, NoConstructor< DpThreadDeleteEvent > >(vm, SqDpThreadDeleteEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpThreadDeleteEvent::Fn) + .Func(_SC("_tostring"), &DpThreadDeleteEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpThreadDeleteEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("ThreadListSync"), + Class< DpThreadListSyncEvent, NoConstructor< DpThreadListSyncEvent > >(vm, SqDpThreadListSyncEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpThreadListSyncEvent::Fn) + .Func(_SC("_tostring"), &DpThreadListSyncEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpThreadListSyncEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("ThreadMemberUpdate"), + Class< DpThreadMemberUpdateEvent, NoConstructor< DpThreadMemberUpdateEvent > >(vm, SqDpThreadMemberUpdateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpThreadMemberUpdateEvent::Fn) + .Func(_SC("_tostring"), &DpThreadMemberUpdateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpThreadMemberUpdateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("ThreadMembersUpdate"), + Class< DpThreadMembersUpdateEvent, NoConstructor< DpThreadMembersUpdateEvent > >(vm, SqDpThreadMembersUpdateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpThreadMembersUpdateEvent::Fn) + .Func(_SC("_tostring"), &DpThreadMembersUpdateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpThreadMembersUpdateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("GuildScheduledEventCreate"), + Class< DpGuildScheduledEventCreateEvent, NoConstructor< DpGuildScheduledEventCreateEvent > >(vm, SqDpGuildScheduledEventCreateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpGuildScheduledEventCreateEvent::Fn) + .Func(_SC("_tostring"), &DpGuildScheduledEventCreateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpGuildScheduledEventCreateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("GuildScheduledEventUpdate"), + Class< DpGuildScheduledEventUpdateEvent, NoConstructor< DpGuildScheduledEventUpdateEvent > >(vm, SqDpGuildScheduledEventUpdateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpGuildScheduledEventUpdateEvent::Fn) + .Func(_SC("_tostring"), &DpGuildScheduledEventUpdateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpGuildScheduledEventUpdateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("GuildScheduledEventDelete"), + Class< DpGuildScheduledEventDeleteEvent, NoConstructor< DpGuildScheduledEventDeleteEvent > >(vm, SqDpGuildScheduledEventDeleteEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpGuildScheduledEventDeleteEvent::Fn) + .Func(_SC("_tostring"), &DpGuildScheduledEventDeleteEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpGuildScheduledEventDeleteEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("GuildScheduledEventUserAdd"), + Class< DpGuildScheduledEventUserAddEvent, NoConstructor< DpGuildScheduledEventUserAddEvent > >(vm, SqDpGuildScheduledEventUserAddEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpGuildScheduledEventUserAddEvent::Fn) + .Func(_SC("_tostring"), &DpGuildScheduledEventUserAddEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpGuildScheduledEventUserAddEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("GuildScheduledEventUserRemove"), + Class< DpGuildScheduledEventUserRemoveEvent, NoConstructor< DpGuildScheduledEventUserRemoveEvent > >(vm, SqDpGuildScheduledEventUserRemoveEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpGuildScheduledEventUserRemoveEvent::Fn) + .Func(_SC("_tostring"), &DpGuildScheduledEventUserRemoveEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpGuildScheduledEventUserRemoveEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("VoiceBufferSend"), + Class< DpVoiceBufferSendEvent, NoConstructor< DpVoiceBufferSendEvent > >(vm, SqDpVoiceBufferSendEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpVoiceBufferSendEvent::Fn) + .Func(_SC("_tostring"), &DpVoiceBufferSendEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpVoiceBufferSendEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("VoiceUserTalking"), + Class< DpVoiceUserTalkingEvent, NoConstructor< DpVoiceUserTalkingEvent > >(vm, SqDpVoiceUserTalkingEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpVoiceUserTalkingEvent::Fn) + .Func(_SC("_tostring"), &DpVoiceUserTalkingEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpVoiceUserTalkingEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("VoiceReady"), + Class< DpVoiceReadyEvent, NoConstructor< DpVoiceReadyEvent > >(vm, SqDpVoiceReadyEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpVoiceReadyEvent::Fn) + .Func(_SC("_tostring"), &DpVoiceReadyEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpVoiceReadyEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("VoiceReceive"), + Class< DpVoiceReceiveEvent, NoConstructor< DpVoiceReceiveEvent > >(vm, SqDpVoiceReceiveEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpVoiceReceiveEvent::Fn) + .Func(_SC("_tostring"), &DpVoiceReceiveEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpVoiceReceiveEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("VoiceReceive"), + Class< DpVoiceReceiveCombinedEvent, NoConstructor< DpVoiceReceiveCombinedEvent > >(vm, SqDpVoiceReceiveCombinedEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpVoiceReceiveCombinedEvent::Fn) + .Func(_SC("_tostring"), &DpVoiceReceiveCombinedEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpVoiceReceiveCombinedEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("VoiceTrackMarker"), + Class< DpVoiceTrackMarkerEvent, NoConstructor< DpVoiceTrackMarkerEvent > >(vm, SqDpVoiceTrackMarkerEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpVoiceTrackMarkerEvent::Fn) + .Func(_SC("_tostring"), &DpVoiceTrackMarkerEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpVoiceTrackMarkerEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("StageInstanceCreate"), + Class< DpStageInstanceCreateEvent, NoConstructor< DpStageInstanceCreateEvent > >(vm, SqDpStageInstanceCreateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpStageInstanceCreateEvent::Fn) + .Func(_SC("_tostring"), &DpStageInstanceCreateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpStageInstanceCreateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("StageInstanceUpdate"), + Class< DpStageInstanceUpdateEvent, NoConstructor< DpStageInstanceUpdateEvent > >(vm, SqDpStageInstanceUpdateEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpStageInstanceUpdateEvent::Fn) + .Func(_SC("_tostring"), &DpStageInstanceUpdateEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpStageInstanceUpdateEvent::GetRawEvent) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("StageInstanceDelete"), + Class< DpStageInstanceDeleteEvent, NoConstructor< DpStageInstanceDeleteEvent > >(vm, SqDpStageInstanceDeleteEvent::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpStageInstanceDeleteEvent::Fn) + .Func(_SC("_tostring"), &DpStageInstanceDeleteEvent::GetRawEvent) + // Member Properties + .Prop(_SC("Raw"), &DpStageInstanceDeleteEvent::GetRawEvent) + ); +} + +} // Namespace:: SqMod diff --git a/module/Library/Discord/Events.hpp b/module/Library/Discord/Events.hpp new file mode 100644 index 00000000..f006cf57 --- /dev/null +++ b/module/Library/Discord/Events.hpp @@ -0,0 +1,2716 @@ +#pragma once + +// ------------------------------------------------------------------------------------------------ +#include "Library/Discord/Constants.hpp" +#include "Library/Discord/Channel.hpp" +#include "Library/Discord/Client.hpp" +#include "Library/Discord/Command.hpp" +#include "Library/Discord/Guild.hpp" +#include "Library/Discord/Integration.hpp" +#include "Library/Discord/Message.hpp" +#include "Library/Discord/Misc.hpp" +#include "Library/Discord/Role.hpp" +#include "Library/Discord/User.hpp" + +// ------------------------------------------------------------------------------------------------ +#include +#include + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +/* ------------------------------------------------------------------------------------------------ + * Base class of an event handler. +*/ +struct DpEventBase +{ + /* -------------------------------------------------------------------------------------------- + * Raw event text. + */ + std::string mRaw{}; + + /* -------------------------------------------------------------------------------------------- + * Shard the event came from. + */ + dpp::discord_client * mFrom{nullptr}; + /* -------------------------------------------------------------------------------------------- + * Default constructor. + */ + DpEventBase() noexcept = default; + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpEventBase(const dpp::event_dispatch_t & d) noexcept + : mRaw(d.raw_event), mFrom(d.from) + { } + /* -------------------------------------------------------------------------------------------- + * Copy constructor (disabled). + */ + DpEventBase(const DpEventBase &) noexcept = delete; + /* -------------------------------------------------------------------------------------------- + * Move constructor (disabled). + */ + DpEventBase(DpEventBase &&) noexcept = delete; + /* -------------------------------------------------------------------------------------------- + * Destructor. + */ + virtual ~DpEventBase() { Cleanup(); } + /* -------------------------------------------------------------------------------------------- + * Copy assignment operator (disabled). + */ + DpEventBase & operator = (const DpEventBase &) noexcept = delete; + /* -------------------------------------------------------------------------------------------- + * Move assignment operator (disabled). + */ + DpEventBase & operator = (DpEventBase &&) noexcept = delete; + /* -------------------------------------------------------------------------------------------- + * Retrieve the associated event ID. + */ + SQMOD_NODISCARD virtual int GetEventID() const = 0; + /* -------------------------------------------------------------------------------------------- + * Retrieve the associated event name. + */ + SQMOD_NODISCARD virtual std::string_view GetEventName() const = 0; + /* -------------------------------------------------------------------------------------------- + * Transform the script object itself to a script object. + */ + SQMOD_NODISCARD virtual LightObj ToScriptObject() = 0; + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. Release associated resources. + */ + virtual void Cleanup() + { + mFrom = nullptr; + } +}; + +/* ------------------------------------------------------------------------------------------------ + * Middleman between event implementation and event base where common functionality that depends + * on the implementation type information can be added once. +*/ +template < class T > struct DpEvent : public DpEventBase +{ + using Info = T; + /* -------------------------------------------------------------------------------------------- + * Default constructor. + */ + DpEvent() noexcept = default; + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpEvent(const typename T::Type & d) noexcept + : DpEventBase(d) + { } + /* -------------------------------------------------------------------------------------------- + * Validate the managed handle. + */ + void Validate() const + { + if (!DpEvent::mFrom) + { + const auto & name = T::Name; + STHROWF("Invalid discord [{}] event handle", name); + } + } + // -------------------------------------------------------------------------------------------- + using DpEventBase::Cleanup; + /* -------------------------------------------------------------------------------------------- + * Retrieve the associated event ID. + */ + SQMOD_NODISCARD int GetEventID() const noexcept override + { + return T::ID; + } + /* -------------------------------------------------------------------------------------------- + * Retrieve the associated event name. + */ + SQMOD_NODISCARD std::string_view GetEventName() const noexcept override + { + return T::Name; + } +}; + +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpVoiceStateUpdateEvent : public DpEvent< DpVoiceStateUpdateEventInfo > +{ + using Type = dpp::voice_state_update_t; + using Base = DpEvent< DpVoiceStateUpdateEventInfo >; + // -------------------------------------------------------------------------------------------- + dpp::voicestate mState{}; + // -------------------------------------------------------------------------------------------- + LightObj mSqState{}; + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpVoiceStateUpdateEvent(const Type & d) noexcept + : Base(d), mState(d.state), mSqState() + { + } + /* -------------------------------------------------------------------------------------------- + * Validate the managed handle and retrieve a reference to it. + */ + SQMOD_NODISCARD DpVoiceStateUpdateEvent & Valid() { Base::Validate(); return *this; } + /* -------------------------------------------------------------------------------------------- + * Validate the managed handle and retrieve a const reference to it. + */ + SQMOD_NODISCARD const DpVoiceStateUpdateEvent & Valid() const { Base::Validate(); return *this; } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + if (!mSqState.IsNull()) + { + mSqState.CastI< DpVoiceState >()->Cleanup(); + // Release script resources + mSqState.Release(); + } + // Allow the base to cleanup as well + Base::Cleanup(); + } + /* -------------------------------------------------------------------------------------------- + * Retrieve the voice state. + */ + SQMOD_NODISCARD LightObj & GetState() + { + // Is the script object already cached? + if (Valid().mSqState.IsNull()) + { + mSqState = LightObj{SqTypeIdentity< DpVoiceState >{}, SqVM(), &mState, false}; + } + // Return the associated script object + return mSqState; + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpVoiceClientDisconnectEvent : public DpEvent< DpVoiceClientDisconnectEventInfo > +{ + using Type = dpp::voice_client_disconnect_t; + using Base = DpEvent< DpVoiceClientDisconnectEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpVoiceClientDisconnectEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpVoiceClientSpeakingEvent : public DpEvent< DpVoiceClientSpeakingEventInfo > +{ + using Type = dpp::voice_client_speaking_t; + using Base = DpEvent< DpVoiceClientSpeakingEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpVoiceClientSpeakingEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpLogEvent : public DpEvent< DpLogEventInfo > +{ + using Type = dpp::log_t; + using Base = DpEvent< DpLogEventInfo >; + // -------------------------------------------------------------------------------------------- + SQInteger mSeverity{0}; + std::string mMessage{}; + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpLogEvent(const Type & d) noexcept + : Base(d), mSeverity(d.severity), mMessage(d.message) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } + /* -------------------------------------------------------------------------------------------- + * Retrieve log severity. + */ + SQMOD_NODISCARD SQInteger GetSeverity() const { return mSeverity; } + /* -------------------------------------------------------------------------------------------- + * Retrieve log message. + */ + SQMOD_NODISCARD const std::string & GetMessage() const { return mMessage; } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpGuildJoinRequestDeleteEvent : public DpEvent< DpGuildJoinRequestDeleteEventInfo > +{ + using Type = dpp::guild_join_request_delete_t; + using Base = DpEvent< DpGuildJoinRequestDeleteEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpGuildJoinRequestDeleteEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpInteractionCreateEvent : public DpEvent< DpInteractionCreateEventInfo > +{ + using Type = dpp::interaction_create_t; + using Base = DpEvent< DpInteractionCreateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpInteractionCreateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpSlashCommandEvent : public DpEvent< DpSlashCommandEventInfo > +{ + using Type = dpp::slashcommand_t; + using Base = DpEvent< DpSlashCommandEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpSlashCommandEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpButtonClickEvent : public DpEvent< DpButtonClickEventInfo > +{ + using Type = dpp::button_click_t; + using Base = DpEvent< DpButtonClickEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpButtonClickEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpAutoCompleteEvent : public DpEvent< DpAutoCompleteEventInfo > +{ + using Type = dpp::autocomplete_t; + using Base = DpEvent< DpAutoCompleteEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpAutoCompleteEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpSelectClickEvent : public DpEvent< DpSelectClickEventInfo > +{ + using Type = dpp::select_click_t; + using Base = DpEvent< DpSelectClickEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpSelectClickEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpMessageContextMenuEvent : public DpEvent< DpMessageContextMenuEventInfo > +{ + using Type = dpp::message_context_menu_t; + using Base = DpEvent< DpMessageContextMenuEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpMessageContextMenuEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpUserContextMenuEvent : public DpEvent< DpUserContextMenuEventInfo > +{ + using Type = dpp::user_context_menu_t; + using Base = DpEvent< DpUserContextMenuEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpUserContextMenuEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpFormSubmitEvent : public DpEvent< DpFormSubmitEventInfo > +{ + using Type = dpp::form_submit_t; + using Base = DpEvent< DpFormSubmitEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpFormSubmitEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpGuildDeleteEvent : public DpEvent< DpGuildDeleteEventInfo > +{ + using Type = dpp::guild_delete_t; + using Base = DpEvent< DpGuildDeleteEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpGuildDeleteEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpChannelDeleteEvent : public DpEvent< DpChannelDeleteEventInfo > +{ + using Type = dpp::channel_delete_t; + using Base = DpEvent< DpChannelDeleteEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpChannelDeleteEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpChannelUpdateEvent : public DpEvent< DpChannelUpdateEventInfo > +{ + using Type = dpp::channel_update_t; + using Base = DpEvent< DpChannelUpdateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpChannelUpdateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpReadyEvent : public DpEvent< DpReadyEventInfo > +{ + using Type = dpp::ready_t; + using Base = DpEvent< DpReadyEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpReadyEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpMessageDeleteEvent : public DpEvent< DpMessageDeleteEventInfo > +{ + using Type = dpp::message_delete_t; + using Base = DpEvent< DpMessageDeleteEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpMessageDeleteEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpGuildMemberRemoveEvent : public DpEvent< DpGuildMemberRemoveEventInfo > +{ + using Type = dpp::guild_member_remove_t; + using Base = DpEvent< DpGuildMemberRemoveEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpGuildMemberRemoveEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpResumedEvent : public DpEvent< DpResumedEventInfo > +{ + using Type = dpp::resumed_t; + using Base = DpEvent< DpResumedEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpResumedEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpGuildRoleCreateEvent : public DpEvent< DpGuildRoleCreateEventInfo > +{ + using Type = dpp::guild_role_create_t; + using Base = DpEvent< DpGuildRoleCreateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpGuildRoleCreateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpTypingStartEvent : public DpEvent< DpTypingStartEventInfo > +{ + using Type = dpp::typing_start_t; + using Base = DpEvent< DpTypingStartEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpTypingStartEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpMessageReactionAddEvent : public DpEvent< DpMessageReactionAddEventInfo > +{ + using Type = dpp::message_reaction_add_t; + using Base = DpEvent< DpMessageReactionAddEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpMessageReactionAddEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpGuildMembersChunkEvent : public DpEvent< DpGuildMembersChunkEventInfo > +{ + using Type = dpp::guild_members_chunk_t; + using Base = DpEvent< DpGuildMembersChunkEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpGuildMembersChunkEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpMessageReactionRemoveEvent : public DpEvent< DpMessageReactionRemoveEventInfo > +{ + using Type = dpp::message_reaction_remove_t; + using Base = DpEvent< DpMessageReactionRemoveEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpMessageReactionRemoveEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpGuildCreateEvent : public DpEvent< DpGuildCreateEventInfo > +{ + using Type = dpp::guild_create_t; + using Base = DpEvent< DpGuildCreateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpGuildCreateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpChannelCreateEvent : public DpEvent< DpChannelCreateEventInfo > +{ + using Type = dpp::channel_create_t; + using Base = DpEvent< DpChannelCreateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpChannelCreateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpMessageReactionRemoveEmojiEvent : public DpEvent< DpMessageReactionRemoveEmojiEventInfo > +{ + using Type = dpp::message_reaction_remove_emoji_t; + using Base = DpEvent< DpMessageReactionRemoveEmojiEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpMessageReactionRemoveEmojiEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpMessageDeleteDulkEvent : public DpEvent< DpMessageDeleteDulkEventInfo > +{ + using Type = dpp::message_delete_bulk_t; + using Base = DpEvent< DpMessageDeleteDulkEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpMessageDeleteDulkEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpGuildRoleUpdateEvent : public DpEvent< DpGuildRoleUpdateEventInfo > +{ + using Type = dpp::guild_role_update_t; + using Base = DpEvent< DpGuildRoleUpdateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpGuildRoleUpdateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpGuildRoleDeleteEvent : public DpEvent< DpGuildRoleDeleteEventInfo > +{ + using Type = dpp::guild_role_delete_t; + using Base = DpEvent< DpGuildRoleDeleteEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpGuildRoleDeleteEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpChannelPinsUpdateEvent : public DpEvent< DpChannelPinsUpdateEventInfo > +{ + using Type = dpp::channel_pins_update_t; + using Base = DpEvent< DpChannelPinsUpdateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpChannelPinsUpdateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpMessageReactionRemoveAllEvent : public DpEvent< DpMessageReactionRemoveAllEventInfo > +{ + using Type = dpp::message_reaction_remove_all_t; + using Base = DpEvent< DpMessageReactionRemoveAllEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpMessageReactionRemoveAllEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpVoiceServerUpdateEvent : public DpEvent< DpVoiceServerUpdateEventInfo > +{ + using Type = dpp::voice_server_update_t; + using Base = DpEvent< DpVoiceServerUpdateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpVoiceServerUpdateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpGuildEmojisUpdateEvent : public DpEvent< DpGuildEmojisUpdateEventInfo > +{ + using Type = dpp::guild_emojis_update_t; + using Base = DpEvent< DpGuildEmojisUpdateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpGuildEmojisUpdateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpGuildStickersUpdateEvent : public DpEvent< DpGuildStickersUpdateEventInfo > +{ + using Type = dpp::guild_stickers_update_t; + using Base = DpEvent< DpGuildStickersUpdateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpGuildStickersUpdateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpPresenceUpdateEvent : public DpEvent< DpPresenceUpdateEventInfo > +{ + using Type = dpp::presence_update_t; + using Base = DpEvent< DpPresenceUpdateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpPresenceUpdateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpWebhooksUpdateEvent : public DpEvent< DpWebhooksUpdateEventInfo > +{ + using Type = dpp::webhooks_update_t; + using Base = DpEvent< DpWebhooksUpdateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpWebhooksUpdateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpAutomodRuleCreateEvent : public DpEvent< DpAutomodRuleCreateEventInfo > +{ + using Type = dpp::automod_rule_create_t; + using Base = DpEvent< DpAutomodRuleCreateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpAutomodRuleCreateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpAutomodRuleUpdateEvent : public DpEvent< DpAutomodRuleUpdateEventInfo > +{ + using Type = dpp::automod_rule_update_t; + using Base = DpEvent< DpAutomodRuleUpdateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpAutomodRuleUpdateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpAutomodRuleDeleteEvent : public DpEvent< DpAutomodRuleDeleteEventInfo > +{ + using Type = dpp::automod_rule_delete_t; + using Base = DpEvent< DpAutomodRuleDeleteEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpAutomodRuleDeleteEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpAutomodRuleExecuteEvent : public DpEvent< DpAutomodRuleExecuteEventInfo > +{ + using Type = dpp::automod_rule_execute_t; + using Base = DpEvent< DpAutomodRuleExecuteEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpAutomodRuleExecuteEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpGuildMemberAddEvent : public DpEvent< DpGuildMemberAddEventInfo > +{ + using Type = dpp::guild_member_add_t; + using Base = DpEvent< DpGuildMemberAddEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpGuildMemberAddEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpInviteDeleteEvent : public DpEvent< DpInviteDeleteEventInfo > +{ + using Type = dpp::invite_delete_t; + using Base = DpEvent< DpInviteDeleteEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpInviteDeleteEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpGuildUpdateEvent : public DpEvent< DpGuildUpdateEventInfo > +{ + using Type = dpp::guild_update_t; + using Base = DpEvent< DpGuildUpdateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpGuildUpdateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpGuildIntegrationsUpdateEvent : public DpEvent< DpGuildIntegrationsUpdateEventInfo > +{ + using Type = dpp::guild_integrations_update_t; + using Base = DpEvent< DpGuildIntegrationsUpdateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpGuildIntegrationsUpdateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpGuildMemberUpdateEvent : public DpEvent< DpGuildMemberUpdateEventInfo > +{ + using Type = dpp::guild_member_update_t; + using Base = DpEvent< DpGuildMemberUpdateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpGuildMemberUpdateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpInviteCreateEvent : public DpEvent< DpInviteCreateEventInfo > +{ + using Type = dpp::invite_create_t; + using Base = DpEvent< DpInviteCreateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpInviteCreateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpMessageUpdateEvent : public DpEvent< DpMessageUpdateEventInfo > +{ + using Type = dpp::message_update_t; + using Base = DpEvent< DpMessageUpdateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpMessageUpdateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpUserUpdateEvent : public DpEvent< DpUserUpdateEventInfo > +{ + using Type = dpp::user_update_t; + using Base = DpEvent< DpUserUpdateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpUserUpdateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpMessageCreateEvent : public DpEvent< DpMessageCreateEventInfo > +{ + using Type = dpp::message_create_t; + using Base = DpEvent< DpMessageCreateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpMessageCreateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpGuildAuditLogEntryCreateEvent : public DpEvent< DpGuildAuditLogEntryCreateEventInfo > +{ + using Type = dpp::guild_audit_log_entry_create_t; + using Base = DpEvent< DpGuildAuditLogEntryCreateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpGuildAuditLogEntryCreateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpGuildBanAddEvent : public DpEvent< DpGuildBanAddEventInfo > +{ + using Type = dpp::guild_ban_add_t; + using Base = DpEvent< DpGuildBanAddEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpGuildBanAddEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpGuildBanRemoveEvent : public DpEvent< DpGuildBanRemoveEventInfo > +{ + using Type = dpp::guild_ban_remove_t; + using Base = DpEvent< DpGuildBanRemoveEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpGuildBanRemoveEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpIntegrationCreateEvent : public DpEvent< DpIntegrationCreateEventInfo > +{ + using Type = dpp::integration_create_t; + using Base = DpEvent< DpIntegrationCreateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpIntegrationCreateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpIntegrationUpdateEvent : public DpEvent< DpIntegrationUpdateEventInfo > +{ + using Type = dpp::integration_update_t; + using Base = DpEvent< DpIntegrationUpdateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpIntegrationUpdateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpIntegrationDeleteEvent : public DpEvent< DpIntegrationDeleteEventInfo > +{ + using Type = dpp::integration_delete_t; + using Base = DpEvent< DpIntegrationDeleteEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpIntegrationDeleteEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpThreadCreateEvent : public DpEvent< DpThreadCreateEventInfo > +{ + using Type = dpp::thread_create_t; + using Base = DpEvent< DpThreadCreateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpThreadCreateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpThreadUpdateEvent : public DpEvent< DpThreadUpdateEventInfo > +{ + using Type = dpp::thread_update_t; + using Base = DpEvent< DpThreadUpdateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpThreadUpdateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpThreadDeleteEvent : public DpEvent< DpThreadDeleteEventInfo > +{ + using Type = dpp::thread_delete_t; + using Base = DpEvent< DpThreadDeleteEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpThreadDeleteEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpThreadListSyncEvent : public DpEvent< DpThreadListSyncEventInfo > +{ + using Type = dpp::thread_list_sync_t; + using Base = DpEvent< DpThreadListSyncEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpThreadListSyncEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpThreadMemberUpdateEvent : public DpEvent< DpThreadMemberUpdateEventInfo > +{ + using Type = dpp::thread_member_update_t; + using Base = DpEvent< DpThreadMemberUpdateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpThreadMemberUpdateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpThreadMembersUpdateEvent : public DpEvent< DpThreadMembersUpdateEventInfo > +{ + using Type = dpp::thread_members_update_t; + using Base = DpEvent< DpThreadMembersUpdateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpThreadMembersUpdateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpGuildScheduledEventCreateEvent : public DpEvent< DpGuildScheduledEventCreateEventInfo > +{ + using Type = dpp::guild_scheduled_event_create_t; + using Base = DpEvent< DpGuildScheduledEventCreateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpGuildScheduledEventCreateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpGuildScheduledEventUpdateEvent : public DpEvent< DpGuildScheduledEventUpdateEventInfo > +{ + using Type = dpp::guild_scheduled_event_update_t; + using Base = DpEvent< DpGuildScheduledEventUpdateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpGuildScheduledEventUpdateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpGuildScheduledEventDeleteEvent : public DpEvent< DpGuildScheduledEventDeleteEventInfo > +{ + using Type = dpp::guild_scheduled_event_delete_t; + using Base = DpEvent< DpGuildScheduledEventDeleteEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpGuildScheduledEventDeleteEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpGuildScheduledEventUserAddEvent : public DpEvent< DpGuildScheduledEventUserAddEventInfo > +{ + using Type = dpp::guild_scheduled_event_user_add_t; + using Base = DpEvent< DpGuildScheduledEventUserAddEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpGuildScheduledEventUserAddEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpGuildScheduledEventUserRemoveEvent : public DpEvent< DpGuildScheduledEventUserRemoveEventInfo > +{ + using Type = dpp::guild_scheduled_event_user_remove_t; + using Base = DpEvent< DpGuildScheduledEventUserRemoveEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpGuildScheduledEventUserRemoveEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpVoiceBufferSendEvent : public DpEvent< DpVoiceBufferSendEventInfo > +{ + using Type = dpp::voice_buffer_send_t; + using Base = DpEvent< DpVoiceBufferSendEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpVoiceBufferSendEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpVoiceUserTalkingEvent : public DpEvent< DpVoiceUserTalkingEventInfo > +{ + using Type = dpp::voice_user_talking_t; + using Base = DpEvent< DpVoiceUserTalkingEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpVoiceUserTalkingEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpVoiceReadyEvent : public DpEvent< DpVoiceReadyEventInfo > +{ + using Type = dpp::voice_ready_t; + using Base = DpEvent< DpVoiceReadyEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpVoiceReadyEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpVoiceReceiveEvent : public DpEvent< DpVoiceReceiveEventInfo > +{ + using Type = dpp::voice_receive_t; + using Base = DpEvent< DpVoiceReceiveEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpVoiceReceiveEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpVoiceReceiveCombinedEvent : public DpEvent< DpVoiceReceiveCombinedEventInfo > +{ + using Type = dpp::voice_receive_t; + using Base = DpEvent< DpVoiceReceiveCombinedEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpVoiceReceiveCombinedEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpVoiceTrackMarkerEvent : public DpEvent< DpVoiceTrackMarkerEventInfo > +{ + using Type = dpp::voice_track_marker_t; + using Base = DpEvent< DpVoiceTrackMarkerEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpVoiceTrackMarkerEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpStageInstanceCreateEvent : public DpEvent< DpStageInstanceCreateEventInfo > +{ + using Type = dpp::stage_instance_create_t; + using Base = DpEvent< DpStageInstanceCreateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpStageInstanceCreateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpStageInstanceUpdateEvent : public DpEvent< DpStageInstanceUpdateEventInfo > +{ + using Type = dpp::stage_instance_update_t; + using Base = DpEvent< DpStageInstanceUpdateEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpStageInstanceUpdateEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; +/* ------------------------------------------------------------------------------------------------ + * +*/ +struct DpStageInstanceDeleteEvent : public DpEvent< DpStageInstanceDeleteEventInfo > +{ + using Type = dpp::stage_instance_delete_t; + using Base = DpEvent< DpStageInstanceDeleteEventInfo >; + // -------------------------------------------------------------------------------------------- + + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpStageInstanceDeleteEvent(const Type & d) noexcept + : Base(d) + { + } + /* -------------------------------------------------------------------------------------------- + * Transform the event object itself to a script object. Used internally. + */ + SQMOD_NODISCARD LightObj ToScriptObject() override { return LightObj{this}; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the raw event. + */ + SQMOD_NODISCARD const std::string & GetRawEvent() const { return Base::mRaw; } + /* -------------------------------------------------------------------------------------------- + * Cleanup after the event was processed. + */ + void Cleanup() override + { + // Allow the base to cleanup as well + Base::Cleanup(); + } +}; + +} // Namespace:: SqMod diff --git a/module/Library/Discord/Guild.cpp b/module/Library/Discord/Guild.cpp new file mode 100644 index 00000000..a40c697d --- /dev/null +++ b/module/Library/Discord/Guild.cpp @@ -0,0 +1,65 @@ +// ------------------------------------------------------------------------------------------------ +#include "Library/Discord/Guild.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +// ------------------------------------------------------------------------------------------------ +SQMOD_DECL_TYPENAME(SqDpGuildMember, _SC("SqDiscordGuildMember")) +SQMOD_DECL_TYPENAME(SqDpGuild, _SC("SqDiscordGuild")) + +// ------------------------------------------------------------------------------------------------ +void Register_Discord_Guild(HSQUIRRELVM vm, Table & ns) +{ + ns.Bind(_SC("GuildMember"), + Class< DpGuildMember, NoConstructor< DpGuildMember > >(vm, SqDpGuildMember::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpGuildMember::Fn) + .Func(_SC("_cmp"), &DpGuildMember::SqCmp) + // Member Properties + .Prop(_SC("Valid"), &DpGuildMember::IsValid) + .Prop(_SC("JSON"), &DpGuildMember::BuildJSON) + .Prop(_SC("Nickname"), &DpGuildMember::GetNickname, &DpGuildMember::SetNickname) + .Prop(_SC("Roles"), &DpGuildMember::GetRoles) + .Prop(_SC("RolesCount"), &DpGuildMember::RolesCount) + .Prop(_SC("GuildID"), &DpGuildMember::GetGuildID, &DpGuildMember::SetGuildID) + .Prop(_SC("UserID"), &DpGuildMember::GetUserID, &DpGuildMember::SetUserID) + .Prop(_SC("Avatar"), &DpGuildMember::GetAvatar, &DpGuildMember::SetAvatar) + .Prop(_SC("CommunicationDisabledUntil"), &DpGuildMember::GetCommunicationDisabledUntil, &DpGuildMember::SetCommunicationDisabledUntil) + .Prop(_SC("JoinedAt"), &DpGuildMember::GetJoinedAt) + .Prop(_SC("PremiumSince"), &DpGuildMember::GetPremiumSince) + .Prop(_SC("Flags"), &DpGuildMember::GetFlags, &DpGuildMember::SetFlags) + .Prop(_SC("CommunicationDisabled"), &DpGuildMember::IsCommunicationDisabled) + .Prop(_SC("Deaf"), &DpGuildMember::GetDeaf, &DpGuildMember::SetDeaf) + .Prop(_SC("Muted"), &DpGuildMember::GetMuted, &DpGuildMember::SetMuted) + .Prop(_SC("IsPending"), &DpGuildMember::IsPending) + .Prop(_SC("HasAnimatedGuildAvatar"), &DpGuildMember::HasAnimatedGuildAvatar) + .Prop(_SC("Mention"), &DpGuildMember::GetMention) + // Member Methods + .Func(_SC("BuildJSON"), &DpGuildMember::BuildJSON_) + .Func(_SC("GetNickname"), &DpGuildMember::ApplyNickname) + .Func(_SC("AddRole"), &DpGuildMember::AddRole) + .Func(_SC("EachRole"), &DpGuildMember::EachRole) + .Func(_SC("ClearRoles"), &DpGuildMember::ClearRoles) + .Func(_SC("FilterRoles"), &DpGuildMember::FilterRoles) + .Func(_SC("GetFlags"), &DpGuildMember::ApplyFlags) + .Func(_SC("GetAvatarURL"), &DpGuildMember::GetAvatarURL) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("Guild"), + Class< DpGuild, NoConstructor< DpGuild > >(vm, SqDpGuild::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpGuild::Fn) + // Member Properties + .Prop(_SC("Valid"), &DpGuild::IsValid) + .Prop(_SC("JSON"), &DpGuild::BuildJSON) + .Prop(_SC("Name"), &DpGuild::GetName, &DpGuild::SetName) + .Prop(_SC("Description"), &DpGuild::GetDescription, &DpGuild::SetDescription) + // Member Methods + .Func(_SC("BuildJSON"), &DpGuild::BuildJSON_) + .Func(_SC("GetName"), &DpGuild::ApplyName) + .Func(_SC("GetDescription"), &DpGuild::ApplyDescription) + ); +} + +} // Namespace:: SqMod diff --git a/module/Library/Discord/Guild.hpp b/module/Library/Discord/Guild.hpp new file mode 100644 index 00000000..1d06cbac --- /dev/null +++ b/module/Library/Discord/Guild.hpp @@ -0,0 +1,421 @@ +#pragma once + +// ------------------------------------------------------------------------------------------------ +#include "Core/Utility.hpp" + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +/* ------------------------------------------------------------------------------------------------ + * Represents a guild on Discord (AKA a server). +*/ +struct DpGuildMember +{ + using Ptr = std::unique_ptr< dpp::guild_member >; + /* -------------------------------------------------------------------------------------------- + * Referenced guild member instance. + */ + Ptr mPtr{nullptr}; + /* -------------------------------------------------------------------------------------------- + * Whether the referenced pointer is owned. + */ + bool mOwned{false}; + /* -------------------------------------------------------------------------------------------- + * Default constructor. + */ + DpGuildMember() noexcept = default; + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpGuildMember(Ptr::pointer ptr, bool owned = false) noexcept + : mPtr(ptr), mOwned(owned) + { } + /* -------------------------------------------------------------------------------------------- + * Copy constructor. + */ + explicit DpGuildMember(const Ptr::element_type & o) noexcept + : DpGuildMember(new Ptr::element_type(o), true) + { } + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + explicit DpGuildMember(Ptr::element_type && o) noexcept + : DpGuildMember(new Ptr::element_type(std::forward< Ptr::element_type >(o)), true) + { } + /* -------------------------------------------------------------------------------------------- + * Copy constructor (disabled). + */ + DpGuildMember(const DpGuildMember & o) = delete; + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + DpGuildMember(DpGuildMember && o) noexcept = default; + /* -------------------------------------------------------------------------------------------- + * Destructor. + */ + ~DpGuildMember() noexcept { Cleanup(); } + /* -------------------------------------------------------------------------------------------- + * Copy assignment operator (disabled). + */ + DpGuildMember & operator = (const DpGuildMember & o) = delete; + /* -------------------------------------------------------------------------------------------- + * Move assignment operator. + */ + DpGuildMember & operator = (DpGuildMember && o) noexcept + { + if (this != &o) { + // Do we own this to try delete it? + Cleanup(); + // Transfer members values + mPtr = std::move(o.mPtr); + mOwned = o.mOwned; + } + return *this; + } + /* -------------------------------------------------------------------------------------------- + * Release any referenced resources and default to an empty/invalid state. + */ + void Cleanup() + { + // Do we own this to try delete it? + if (!mOwned && mPtr) { + // Not our job, simply forget about it + [[maybe_unused]] auto p = mPtr.release(); + } else mPtr.reset(); // We own this so delete the instance + } + /* -------------------------------------------------------------------------------------------- + * Validate the managed handle. + */ + void Validate() const { if (!mPtr) STHROWF("Invalid discord voice state handle"); } + /* -------------------------------------------------------------------------------------------- + * Validate the managed handle and retrieve a const reference to it. + */ + SQMOD_NODISCARD Ptr::element_type & Valid() const { Validate(); return *mPtr; } + /* -------------------------------------------------------------------------------------------- + * Check whether a valid instance is managed. + */ + SQMOD_NODISCARD bool IsValid() const { return static_cast< bool >(mPtr); } + /* -------------------------------------------------------------------------------------------- + * Build json representation of the object. + */ + SQMOD_NODISCARD std::string BuildJSON() const { return Valid().build_json(); } + SQMOD_NODISCARD std::string BuildJSON_(bool with_id) const { return Valid().build_json(with_id); } + /* -------------------------------------------------------------------------------------------- + * Check if this member is equal to another member object. + */ + SQMOD_NODISCARD SQInteger SqCmp(const DpGuildMember & o) const + { + if (Valid() == o.Valid()) { + return 0; + // Nonsense... + } else if (Valid().user_id > o.Valid().user_id) { + return 1; + } else { + return -1; + } + } + /* -------------------------------------------------------------------------------------------- + * Retrieve the nickname, or empty string if they don't have a nickname on this guild. + */ + SQMOD_NODISCARD const std::string & GetNickname() const { return Valid().nickname; } + /* -------------------------------------------------------------------------------------------- + * Modify the nickname. + */ + void SetNickname(StackStrF & name) const { Valid().set_nickname(name.ToStr()); } + /* -------------------------------------------------------------------------------------------- + * Modify the nickname. + */ + DpGuildMember & ApplyNickname(StackStrF & name) { SetNickname(name); return *this; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the list of roles this user has on this guild. + */ + SQMOD_NODISCARD Array GetRoles() const + { + return Array(SqVM()).Reserve(static_cast< SQInteger >(Valid().roles.size())) + .AppendFromVector(Valid().roles); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the number of roles. + */ + SQMOD_NODISCARD SQInteger RolesCount() const { return static_cast< SQInteger >(Valid().roles.size()); } + /* -------------------------------------------------------------------------------------------- + * Add a new role. + */ + DpGuildMember & AddRole(dpp::snowflake role) { Valid().roles.push_back(role); return *this; } + /* -------------------------------------------------------------------------------------------- + * Iterate all roles. + */ + DpGuildMember & EachRole(Function & fn) + { + for (const auto & a : Valid().roles) + { + fn.Execute(a); + } + return *this; + } + /* -------------------------------------------------------------------------------------------- + * Retrieve the number of roles. + */ + DpGuildMember & ClearRoles(dpp::snowflake role) { Valid().roles.clear(); return *this; } + /* -------------------------------------------------------------------------------------------- + * Filter roles. + */ + DpGuildMember & FilterRoles(Function & fn) + { + std::vector< dpp::snowflake > list; + // Reserve memory in advance + list.reserve(Valid().roles.size()); + // Process each role individually + for (const auto & role : Valid().roles) + { + auto ret = fn.Eval(role); + // (null || true) == keep & false == skip + if (!ret.IsNull() || !ret.template Cast< bool >()) + { + list.push_back(role); // Keep this role + } + } + // Use filtered roles + Valid().roles.swap(list); + // Allow chaining + return *this; + } + /* -------------------------------------------------------------------------------------------- + * Retrieve the guild id. + */ + SQMOD_NODISCARD dpp::snowflake GetGuildID() const { return Valid().guild_id; } + /* -------------------------------------------------------------------------------------------- + * Modify the guild id. + */ + void SetGuildID(dpp::snowflake id) { Valid().guild_id = id; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the user id. + */ + SQMOD_NODISCARD dpp::snowflake GetUserID() const { return Valid().user_id; } + /* -------------------------------------------------------------------------------------------- + * Modify the guild id. + */ + void SetUserID(dpp::snowflake id) { Valid().guild_id = id; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the user avatar (per-server avatar is a nitro only feature). + */ + SQMOD_NODISCARD const dpp::utility::iconhash & GetAvatar() const { return Valid().avatar; } + /* -------------------------------------------------------------------------------------------- + * Modify the user avatar (per-server avatar is a nitro only feature). + */ + void SetAvatar(const dpp::utility::iconhash & a) const { Valid().avatar = a; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the date and time when the time out will be removed; until then, they cannot interact with the guild. + */ + SQMOD_NODISCARD SQInteger GetCommunicationDisabledUntil() const + { + return std::chrono::time_point_cast< std::chrono::seconds >( + std::chrono::system_clock::from_time_t(Valid().communication_disabled_until) + ).time_since_epoch().count(); + } + /* -------------------------------------------------------------------------------------------- + * Assign a timestamp until communication is disabled. + */ + void SetCommunicationDisabledUntil(SQInteger ts) const + { + Valid().set_communication_disabled_until( + std::chrono::system_clock::to_time_t(std::chrono::time_point< std::chrono::system_clock >(std::chrono::seconds(ts))) + ); + } + /* -------------------------------------------------------------------------------------------- + * Retrieve the date and time the user joined the guild. + */ + SQMOD_NODISCARD SQInteger GetJoinedAt() const + { + return std::chrono::time_point_cast< std::chrono::seconds >( + std::chrono::system_clock::from_time_t(Valid().joined_at) + ).time_since_epoch().count(); + } + /* -------------------------------------------------------------------------------------------- + * Retrieve the date and time since the user is boosting. + */ + SQMOD_NODISCARD SQInteger GetPremiumSince() const + { + return std::chrono::time_point_cast< std::chrono::seconds >( + std::chrono::system_clock::from_time_t(Valid().premium_since) + ).time_since_epoch().count(); + } + /* -------------------------------------------------------------------------------------------- + * Retrieve the guild flags. Built from the bitmask defined by SqDiscordGuildMemberFlags. + */ + SQMOD_NODISCARD SQInteger GetFlags() const { return static_cast< SQInteger >(Valid().flags); } + /* -------------------------------------------------------------------------------------------- + * Modify the guild flags. + */ + void SetFlags(SQInteger f) const { Valid().flags = static_cast< uint8_t >(f); } + /* -------------------------------------------------------------------------------------------- + * Modify the guild flags. + */ + DpGuildMember & ApplyFlags(SQInteger f) { SetFlags(f); return *this; } + /* -------------------------------------------------------------------------------------------- + * Check whether the user is in time-out (communication disabled). + */ + SQMOD_NODISCARD bool IsCommunicationDisabled() const { return Valid().is_communication_disabled(); } + /* -------------------------------------------------------------------------------------------- + * Check whether the user is deafened. + */ + SQMOD_NODISCARD bool GetDeaf() const { return Valid().is_deaf(); } + /* -------------------------------------------------------------------------------------------- + * Check whether the user is deafened. + */ + void SetDeaf(bool is_deafened) const { Valid().set_deaf(is_deafened); } + /* -------------------------------------------------------------------------------------------- + * Check whether the user is muted. + */ + SQMOD_NODISCARD bool GetMuted() const { return Valid().is_muted(); } + /* -------------------------------------------------------------------------------------------- + * Check whether the user is muted. + */ + void SetMuted(bool is_muted) const { Valid().set_mute(is_muted); } + /* -------------------------------------------------------------------------------------------- + * Check whether the user is pending verification by membership screening. + */ + SQMOD_NODISCARD bool IsPending() const { return Valid().is_pending(); } + /* -------------------------------------------------------------------------------------------- + * Check whether the the user's per-guild custom avatar is animated. + */ + SQMOD_NODISCARD bool HasAnimatedGuildAvatar() const { return Valid().has_animated_guild_avatar(); } + /* -------------------------------------------------------------------------------------------- + * Check whether the the user's per-guild custom avatar is animated. + */ + SQMOD_NODISCARD std::string GetAvatarURL(SQInteger size, SQInteger format, bool animated) const + { return Valid().get_avatar_url(static_cast< uint16_t >(size), static_cast< dpp::image_type >(format), animated); } + /* -------------------------------------------------------------------------------------------- + * Retrieve a ping/mention for the user by nickname. + */ + SQMOD_NODISCARD std::string GetMention() const { return Valid().get_mention(); } +}; + +/* ------------------------------------------------------------------------------------------------ + * Represents a guild on Discord (AKA a server) +*/ +struct DpGuild +{ + using Ptr = std::unique_ptr< dpp::guild >; + /* -------------------------------------------------------------------------------------------- + * Referenced guild instance. + */ + Ptr mPtr{nullptr}; + /* -------------------------------------------------------------------------------------------- + * Whether the referenced pointer is owned. + */ + bool mOwned{false}; + /* -------------------------------------------------------------------------------------------- + * Default constructor. + */ + DpGuild() noexcept = default; + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpGuild(Ptr::pointer ptr, bool owned = false) noexcept + : mPtr(ptr), mOwned(owned) + { } + /* -------------------------------------------------------------------------------------------- + * Copy constructor. + */ + explicit DpGuild(const Ptr::element_type & o) noexcept + : DpGuild(new Ptr::element_type(o), true) + { } + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + explicit DpGuild(Ptr::element_type && o) noexcept + : DpGuild(new Ptr::element_type(std::forward< Ptr::element_type >(o)), true) + { } + /* -------------------------------------------------------------------------------------------- + * Copy constructor (disabled). + */ + DpGuild(const DpGuild & o) = delete; + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + DpGuild(DpGuild && o) noexcept = default; + /* -------------------------------------------------------------------------------------------- + * Destructor. + */ + ~DpGuild() noexcept { Cleanup(); } + /* -------------------------------------------------------------------------------------------- + * Copy assignment operator (disabled). + */ + DpGuild & operator = (const DpGuild & o) = delete; + /* -------------------------------------------------------------------------------------------- + * Move assignment operator. + */ + DpGuild & operator = (DpGuild && o) noexcept + { + if (this != &o) { + // Do we own this to try delete it? + Cleanup(); + // Transfer members values + mPtr = std::move(o.mPtr); + mOwned = o.mOwned; + } + return *this; + } + /* -------------------------------------------------------------------------------------------- + * Release any referenced resources and default to an empty/invalid state. + */ + void Cleanup() + { + // Do we own this to try delete it? + if (!mOwned && mPtr) { + // Not our job, simply forget about it + [[maybe_unused]] auto p = mPtr.release(); + } else mPtr.reset(); // We own this so delete the instance + } + /* -------------------------------------------------------------------------------------------- + * Validate the managed handle. + */ + void Validate() const { if (!mPtr) STHROWF("Invalid discord guild handle"); } + /* -------------------------------------------------------------------------------------------- + * Validate the managed handle and retrieve a const reference to it. + */ + SQMOD_NODISCARD Ptr::element_type & Valid() const { Validate(); return *mPtr; } + /* -------------------------------------------------------------------------------------------- + * Check whether a valid instance is managed. + */ + SQMOD_NODISCARD bool IsValid() const { return static_cast< bool >(mPtr); } + /* -------------------------------------------------------------------------------------------- + * Build json representation of the object. + */ + SQMOD_NODISCARD std::string BuildJSON() const { return Valid().build_json(); } + SQMOD_NODISCARD std::string BuildJSON_(bool with_id) const { return Valid().build_json(with_id); } + /* -------------------------------------------------------------------------------------------- + * Retrieve the guild name. Min length: 2, Max length: 100 (not including leading/trailing spaces) + */ + SQMOD_NODISCARD const std::string & GetName() const { return Valid().name; } + /* -------------------------------------------------------------------------------------------- + * Modify the guild name. Min length: 2, Max length: 100 (not including leading/trailing spaces) + */ + void SetName(StackStrF & name) const { Valid().set_name(name.ToStr()); } + /* -------------------------------------------------------------------------------------------- + * Modify the guild name. Min length: 2, Max length: 100 (not including leading/trailing spaces) + */ + DpGuild & ApplyName(StackStrF & name) { SetName(name); return *this; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the server description. + */ + SQMOD_NODISCARD const std::string & GetDescription() const { return Valid().description; } + /* -------------------------------------------------------------------------------------------- + * Modify the server description. + */ + void SetDescription(StackStrF & description) const { Valid().description = description.ToStr(); } + /* -------------------------------------------------------------------------------------------- + * Modify the server description. + */ + DpGuild & ApplyDescription(StackStrF & description) { SetDescription(description); return *this; } +}; + +} // Namespace:: SqMod diff --git a/module/Library/Discord/Integration.cpp b/module/Library/Discord/Integration.cpp new file mode 100644 index 00000000..cb81cbeb --- /dev/null +++ b/module/Library/Discord/Integration.cpp @@ -0,0 +1,9 @@ +// ------------------------------------------------------------------------------------------------ +#include "Library/Discord/Integration.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + + + +} // Namespace:: SqMod diff --git a/module/Library/Discord/Integration.hpp b/module/Library/Discord/Integration.hpp new file mode 100644 index 00000000..ab7ce4d7 --- /dev/null +++ b/module/Library/Discord/Integration.hpp @@ -0,0 +1,13 @@ +#pragma once + +// ------------------------------------------------------------------------------------------------ +#include "Core/Utility.hpp" + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + + +} // Namespace:: SqMod diff --git a/module/Library/Discord/Message.cpp b/module/Library/Discord/Message.cpp new file mode 100644 index 00000000..6e379222 --- /dev/null +++ b/module/Library/Discord/Message.cpp @@ -0,0 +1,9 @@ +// ------------------------------------------------------------------------------------------------ +#include "Library/Discord/Message.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + + + +} // Namespace:: SqMod diff --git a/module/Library/Discord/Message.hpp b/module/Library/Discord/Message.hpp new file mode 100644 index 00000000..ab7ce4d7 --- /dev/null +++ b/module/Library/Discord/Message.hpp @@ -0,0 +1,13 @@ +#pragma once + +// ------------------------------------------------------------------------------------------------ +#include "Core/Utility.hpp" + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + + +} // Namespace:: SqMod diff --git a/module/Library/Discord/Misc.cpp b/module/Library/Discord/Misc.cpp new file mode 100644 index 00000000..0b47aaa0 --- /dev/null +++ b/module/Library/Discord/Misc.cpp @@ -0,0 +1,117 @@ +// ------------------------------------------------------------------------------------------------ +#include "Library/Discord/Misc.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +// ------------------------------------------------------------------------------------------------ +SQMOD_DECL_TYPENAME(SqDpCachePolicy, _SC("SqDiscordCachePolicy")) +SQMOD_DECL_TYPENAME(SqDpUptime, _SC("SqDiscordUptime")) +SQMOD_DECL_TYPENAME(SqDpIconHash, _SC("SqDiscordIconHash")) +SQMOD_DECL_TYPENAME(SqDpVoiceState, _SC("SqDiscordVoiceState")) +SQMOD_DECL_TYPENAME(SqDpEmoji, _SC("SqDiscordEmoji")) + +// ------------------------------------------------------------------------------------------------ +void Register_Discord_Misc(HSQUIRRELVM vm, Table & ns) +{ + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("Uptime"), + Class< dpp::utility::uptime >(vm, SqDpUptime::Str) + // Constructors + .Ctor() + .Ctor< double >() + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpUptime::Fn) + .Func(_SC("_tostring"), &dpp::utility::uptime::to_string) + // Member Variables + .Var(_SC("Days"), &dpp::utility::uptime::days) + .Var(_SC("Hours"), &dpp::utility::uptime::hours) + .Var(_SC("Minutes"), &dpp::utility::uptime::mins) + .Var(_SC("Seconds"), &dpp::utility::uptime::secs) + // Member Methods + .Func(_SC("ToSeconds"), &dpp::utility::uptime::to_secs) + .Func(_SC("ToMilliseconds"), &dpp::utility::uptime::to_msecs) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("IconHash"), + Class< dpp::utility::iconhash >(vm, SqDpIconHash::Str) + // Constructors + .Ctor() + .Ctor< const std::string & >() + .Ctor< uint64_t, uint64_t >() + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpIconHash::Fn) + .Func(_SC("_tostring"), &dpp::utility::iconhash::to_string) + // Member Variables + .Var(_SC("High"), &dpp::utility::iconhash::first) + .Var(_SC("Low"), &dpp::utility::iconhash::second) + // Member Methods + .Func(_SC("Set"), &dpp::utility::iconhash::set) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("CachePolicy"), + Class< DpCachePolicy >(vm, SqDpCachePolicy::Str) + // Constructors + .Ctor() + .Ctor< SQInteger >() + .Ctor< SQInteger, SQInteger >() + .Ctor< SQInteger, SQInteger, SQInteger >() + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpCachePolicy::Fn) + // Member Variables + .Var(_SC("UserPolicy"), &DpCachePolicy::mUserPolicy) + .Var(_SC("EmojiPolicy"), &DpCachePolicy::mEmojiPolicy) + .Var(_SC("RolePolicy"), &DpCachePolicy::mRolePolicy) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("VoiceState"), + Class< DpVoiceState, NoConstructor< DpVoiceState > >(vm, SqDpVoiceState::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpVoiceState::Fn) + // Member Properties + .Prop(_SC("Valid"), &DpVoiceState::IsValid) + .Prop(_SC("JSON"), &DpVoiceState::BuildJSON) + .Prop(_SC("GuildID"), &DpVoiceState::GetGuildID) + .Prop(_SC("ChannelID"), &DpVoiceState::GetChannelID) + .Prop(_SC("UserID"), &DpVoiceState::GetUserID) + .Prop(_SC("SessionID"), &DpVoiceState::GetSessionID) + .Prop(_SC("Flags"), &DpVoiceState::GetFlags, &DpVoiceState::SetFlags) + .Prop(_SC("RequestToSpeak"), &DpVoiceState::GetRequestToSpeak) + .Prop(_SC("IsDeaf"), &DpVoiceState::IsDeaf) + .Prop(_SC("IsMute"), &DpVoiceState::IsMute) + .Prop(_SC("IsSelfMute"), &DpVoiceState::IsSelfMute) + .Prop(_SC("IsSelfDeaf"), &DpVoiceState::IsSelfDeaf) + .Prop(_SC("SelfStream"), &DpVoiceState::SelfStream) + .Prop(_SC("SelfVideo"), &DpVoiceState::SelfVideo) + .Prop(_SC("IsSuppressed"), &DpVoiceState::IsSuppressed) + // Member Methods + .Func(_SC("BuildJSON"), &DpVoiceState::BuildJSON_) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("Emoji"), + Class< DpEmoji, NoCopy< DpEmoji > >(vm, SqDpEmoji::Str) + // Constructors + .Ctor() + .Ctor< StackStrF &, dpp::snowflake, SQInteger >() + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpEmoji::Fn) + // Member Properties + .Prop(_SC("Valid"), &DpEmoji::IsValid) + .Prop(_SC("JSON"), &DpEmoji::BuildJSON) + .Prop(_SC("Name"), &DpEmoji::GetName, &DpEmoji::SetName) + .Prop(_SC("ImageData"), &DpEmoji::GetImageData) + .Prop(_SC("RequiresColons"), &DpEmoji::GetRequiresColons) + .Prop(_SC("IsManaged"), &DpEmoji::GetIsManaged) + .Prop(_SC("IsAnimated"), &DpEmoji::GetIsAnimated) + .Prop(_SC("IsAvailable"), &DpEmoji::GetIsAvailable) + .Prop(_SC("Format"), &DpEmoji::Format) + .Prop(_SC("Mention"), &DpEmoji::GetMention) + // Member Methods + .Func(_SC("SetName"), &DpEmoji::ApplyName) + .Func(_SC("GetMention"), &DpEmoji::GetMention_) + .Func(_SC("BuildJSON"), &DpEmoji::BuildJSON_) + .Func(_SC("LoadImage"), &DpEmoji::LoadImage) + ); +} + +} // Namespace:: SqMod diff --git a/module/Library/Discord/Misc.hpp b/module/Library/Discord/Misc.hpp new file mode 100644 index 00000000..1f632a3b --- /dev/null +++ b/module/Library/Discord/Misc.hpp @@ -0,0 +1,393 @@ +#pragma once + +// ------------------------------------------------------------------------------------------------ +#include "Library/Discord/Utilities.hpp" + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +/* ------------------------------------------------------------------------------------------------ + * Represents the caching policy of the cluster. +*/ +struct DpCachePolicy +{ + SQInteger mUserPolicy{dpp::cp_aggressive}; + SQInteger mEmojiPolicy{dpp::cp_aggressive}; + SQInteger mRolePolicy{dpp::cp_aggressive}; + /* -------------------------------------------------------------------------------------------- + * Default constructor. + */ + DpCachePolicy() noexcept = default; + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpCachePolicy(SQInteger user) noexcept + : mUserPolicy(user), mEmojiPolicy(dpp::cp_aggressive), mRolePolicy(dpp::cp_aggressive) + { } + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + DpCachePolicy(SQInteger user, SQInteger emoji) noexcept + : mUserPolicy(user), mEmojiPolicy(emoji), mRolePolicy(dpp::cp_aggressive) + { } + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + DpCachePolicy(SQInteger user, SQInteger emoji, SQInteger role) noexcept + : mUserPolicy(user), mEmojiPolicy(emoji), mRolePolicy(role) + { } + /* -------------------------------------------------------------------------------------------- + * Copy constructor. + */ + DpCachePolicy(const DpCachePolicy &) noexcept = default; + /* -------------------------------------------------------------------------------------------- + * Convert to native cache policy type. + */ + SQMOD_NODISCARD dpp::cache_policy_t ToNative() const noexcept + { + return dpp::cache_policy_t{ + static_cast< dpp::cache_policy_setting_t >(mUserPolicy), + static_cast< dpp::cache_policy_setting_t >(mEmojiPolicy), + static_cast< dpp::cache_policy_setting_t >(mRolePolicy) + }; + } +}; + +/* ------------------------------------------------------------------------------------------------ + * Represents the voice state of a user on a guild. + * These are stored in the DpGuild object, and accessible there, or via DpChannel::GetVoiceMembers. +*/ +struct DpVoiceState +{ + using Ptr = std::unique_ptr< dpp::voicestate >; + /* -------------------------------------------------------------------------------------------- + * Referenced voice state instance. + */ + Ptr mPtr{nullptr}; + /* -------------------------------------------------------------------------------------------- + * Whether the referenced pointer is owned. + */ + bool mOwned{false}; + /* -------------------------------------------------------------------------------------------- + * Default constructor. + */ + DpVoiceState() noexcept = default; + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpVoiceState(Ptr::pointer ptr, bool owned = false) noexcept + : mPtr(ptr), mOwned(owned) + { } + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpVoiceState(const Ptr::element_type & o) noexcept + : DpVoiceState(new Ptr::element_type(o), true) + { } + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + explicit DpVoiceState(Ptr::element_type && o) noexcept + : DpVoiceState(new Ptr::element_type(std::forward< Ptr::element_type >(o)), true) + { } + /* -------------------------------------------------------------------------------------------- + * Copy constructor (disabled). + */ + DpVoiceState(const DpVoiceState & o) = delete; + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + DpVoiceState(DpVoiceState && o) noexcept = default; + /* -------------------------------------------------------------------------------------------- + * Destructor. + */ + ~DpVoiceState() noexcept { Cleanup(); } + /* -------------------------------------------------------------------------------------------- + * Copy assignment operator (disabled). + */ + DpVoiceState & operator = (const DpVoiceState & o) = delete; + /* -------------------------------------------------------------------------------------------- + * Move assignment operator. + */ + DpVoiceState & operator = (DpVoiceState && o) noexcept + { + if (this != &o) { + Cleanup(); + // Transfer members values + mPtr = std::move(o.mPtr); + mOwned = o.mOwned; + } + return *this; + } + /* -------------------------------------------------------------------------------------------- + * Release any referenced resources and default to an empty/invalid state. + */ + void Cleanup() + { + // Do we own this to try delete it? + if (!mOwned && mPtr) { + // Not our job, simply forget about it + [[maybe_unused]] auto p = mPtr.release(); + } else mPtr.reset(); // We own this so delete the instance + } + /* -------------------------------------------------------------------------------------------- + * Validate the managed handle. + */ + void Validate() const { if (!mPtr) STHROWF("Invalid discord voice state handle"); } + /* -------------------------------------------------------------------------------------------- + * Validate the managed handle and retrieve a const reference to it. + */ + SQMOD_NODISCARD Ptr::element_type & Valid() const { Validate(); return *mPtr; } + /* -------------------------------------------------------------------------------------------- + * Check whether a valid instance is managed. + */ + SQMOD_NODISCARD bool IsValid() const { return static_cast< bool >(mPtr); } + /* -------------------------------------------------------------------------------------------- + * Retrieve the guild id this voice state is for (optional). + */ + SQMOD_NODISCARD dpp::snowflake GetGuildID() const { return Valid().guild_id; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the channel id this user is connected to (may be empty). + */ + SQMOD_NODISCARD dpp::snowflake GetChannelID() const { return Valid().channel_id; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the user id this voice state is for. + */ + SQMOD_NODISCARD dpp::snowflake GetUserID() const { return Valid().user_id; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the session id for this voice state. + */ + SQMOD_NODISCARD const std::string & GetSessionID() const { return Valid().session_id; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the voice state flags (see SqDiscordVoiceStateFlags). + */ + SQMOD_NODISCARD SQInteger GetFlags() const { return Valid().flags; } + /* -------------------------------------------------------------------------------------------- + * Modify the voice state flags (see SqDiscordVoiceStateFlags). + */ + void SetFlags(SQInteger flags) const { Valid().flags = flags; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the time at which the user requested to speak, or 0. + */ + SQMOD_NODISCARD SQInteger GetRequestToSpeak() const { + return static_cast< SQInteger >(std::chrono::duration_cast< std::chrono::seconds >( + std::chrono::system_clock::from_time_t(Valid().request_to_speak).time_since_epoch() + ).count()); + } + /* -------------------------------------------------------------------------------------------- + * Build json representation of the object. + */ + SQMOD_NODISCARD std::string BuildJSON() const { return Valid().build_json(); } + SQMOD_NODISCARD std::string BuildJSON_(bool with_id) const { return Valid().build_json(with_id); } + /* -------------------------------------------------------------------------------------------- + * Check if the user is deafened by the server. + */ + SQMOD_NODISCARD bool IsDeaf() const { return Valid().is_deaf(); } + /* -------------------------------------------------------------------------------------------- + * Check if the user is muted by the server. + */ + SQMOD_NODISCARD bool IsMute() const { return Valid().is_mute(); } + /* -------------------------------------------------------------------------------------------- + * Check if the user muted themselves. + */ + SQMOD_NODISCARD bool IsSelfMute() const { return Valid().is_self_mute(); } + /* -------------------------------------------------------------------------------------------- + * Check if the user deafened themselves. + */ + SQMOD_NODISCARD bool IsSelfDeaf() const { return Valid().is_self_deaf(); } + /* -------------------------------------------------------------------------------------------- + * Check if the user is streaming using "Go Live". + */ + SQMOD_NODISCARD bool SelfStream() const { return Valid().self_stream(); } + /* -------------------------------------------------------------------------------------------- + * Check if the user's camera is enabled. + */ + SQMOD_NODISCARD bool SelfVideo() const { return Valid().self_video(); } + /* -------------------------------------------------------------------------------------------- + * Check if user is suppressed. "HELP HELP I'M BEING SUPPRESSED!" + */ + SQMOD_NODISCARD bool IsSuppressed() const { return Valid().is_suppressed(); } +}; + + +/* ------------------------------------------------------------------------------------------------ + * Represents the voice state of a user on a guild. +*/ +struct DpEmoji +{ + using Ptr = std::unique_ptr< dpp::emoji >; + /* -------------------------------------------------------------------------------------------- + * Referenced voice state instance. + */ + Ptr mPtr{nullptr}; + /* -------------------------------------------------------------------------------------------- + * Whether the referenced pointer is owned. + */ + bool mOwned{false}; + /* -------------------------------------------------------------------------------------------- + * Default constructor. + */ + DpEmoji() noexcept = default; + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpEmoji(Ptr::pointer ptr, bool owned = false) noexcept + : mPtr(ptr), mOwned(owned) + { } + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpEmoji(const Ptr::element_type & o) noexcept + : DpEmoji(new Ptr::element_type(o), true) + { } + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + explicit DpEmoji(Ptr::element_type && o) noexcept + : DpEmoji(new Ptr::element_type(std::forward< Ptr::element_type >(o)), true) + { } + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + DpEmoji(StackStrF & name, dpp::snowflake id, SQInteger flags) + : DpEmoji(new Ptr::element_type(name.ToStr(), id, static_cast< uint8_t >(flags)), true) + { } + /* -------------------------------------------------------------------------------------------- + * Copy constructor (disabled). + */ + DpEmoji(const DpEmoji & o) = delete; + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + DpEmoji(DpEmoji && o) noexcept = default; + /* -------------------------------------------------------------------------------------------- + * Destructor. + */ + ~DpEmoji() noexcept { Cleanup(); } + /* -------------------------------------------------------------------------------------------- + * Copy assignment operator (disabled). + */ + DpEmoji & operator = (const DpEmoji & o) = delete; + /* -------------------------------------------------------------------------------------------- + * Move assignment operator. + */ + DpEmoji & operator = (DpEmoji && o) noexcept + { + if (this != &o) { + Cleanup(); + // Transfer members values + mPtr = std::move(o.mPtr); + mOwned = o.mOwned; + } + return *this; + } + /* -------------------------------------------------------------------------------------------- + * Release any referenced resources and default to an empty/invalid state. + */ + void Cleanup() + { + // Do we own this to try delete it? + if (!mOwned && mPtr) { + // Not our job, simply forget about it + [[maybe_unused]] auto p = mPtr.release(); + } else mPtr.reset(); // We own this so delete the instance + } + /* -------------------------------------------------------------------------------------------- + * Validate the managed handle. + */ + void Validate() const { if (!mPtr) STHROWF("Invalid discord voice state handle"); } + /* -------------------------------------------------------------------------------------------- + * Validate the managed handle and retrieve a const reference to it. + */ + SQMOD_NODISCARD Ptr::element_type & Valid() const { Validate(); return *mPtr; } + /* -------------------------------------------------------------------------------------------- + * Check whether a valid instance is managed. + */ + SQMOD_NODISCARD bool IsValid() const { return static_cast< bool >(mPtr); } + /* -------------------------------------------------------------------------------------------- + * Retrieve the name of the emoji. + */ + SQMOD_NODISCARD const std::string & GetName() const { return Valid().name; } + /* -------------------------------------------------------------------------------------------- + * Modify the name of the emoji. + */ + void SetName(StackStrF & name) const { Valid().name = name.ToStr(); } + /* -------------------------------------------------------------------------------------------- + * Modify the name of the emoji. + */ + DpEmoji & ApplyName(StackStrF & name) { SetName(name); return *this; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the user who uploaded the emoji. + */ + SQMOD_NODISCARD dpp::snowflake GetUserID() const { return Valid().user_id; } + /* -------------------------------------------------------------------------------------------- + * Modify the user who uploaded the emoji. + */ + void SetUserID(dpp::snowflake id) const { Valid().user_id = id; } + /* -------------------------------------------------------------------------------------------- + * Modify the user who uploaded the emoji. + */ + DpEmoji & ApplyUserID(dpp::snowflake id) { SetUserID(id); return *this; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the flags for the emoji from SqDiscordEmojiFlags. + */ + SQMOD_NODISCARD SQInteger GetFlags() const { return Valid().flags; } + /* -------------------------------------------------------------------------------------------- + * Modify the flags for the emoji from SqDiscordEmojiFlags. + */ + void SetFlags(SQInteger flags) const { Valid().flags = static_cast< uint8_t >(flags); } + /* -------------------------------------------------------------------------------------------- + * Modify the flags for the emoji from SqDiscordEmojiFlags. + */ + DpEmoji & ApplyFlags(SQInteger flags) { SetFlags(flags); return *this; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the image data for the emoji if uploading. + */ + SQMOD_NODISCARD LightObj GetImageData() const { return !Valid().image_data ? LightObj{} : LightObj(*Valid().image_data); } + /* -------------------------------------------------------------------------------------------- + * Create a mentionable emoji. + */ + SQMOD_NODISCARD std::string GetMention_(StackStrF & name, dpp::snowflake id, bool is_animated = false) const + { return Valid().get_mention(name.ToStr(), id, is_animated); } + /* -------------------------------------------------------------------------------------------- + * Build json representation of the object. + */ + SQMOD_NODISCARD std::string BuildJSON() const { return Valid().build_json(); } + SQMOD_NODISCARD std::string BuildJSON_(bool with_id) const { return Valid().build_json(with_id); } + /* -------------------------------------------------------------------------------------------- + * Retrieve whether the Emoji requires colons. + */ + SQMOD_NODISCARD bool GetRequiresColons() const { return Valid().requires_colons(); } + /* -------------------------------------------------------------------------------------------- + * Retrieve whether the Emoji is managed. + */ + SQMOD_NODISCARD bool GetIsManaged() const { return Valid().is_managed(); } + /* -------------------------------------------------------------------------------------------- + * Retrieve whether the Emoji is animated. + */ + SQMOD_NODISCARD bool GetIsAnimated() const { return Valid().is_animated(); } + /* -------------------------------------------------------------------------------------------- + * Retrieve whether the Emoji is available. + */ + SQMOD_NODISCARD bool GetIsAvailable() const { return Valid().is_available(); } + /* -------------------------------------------------------------------------------------------- + * Load an image into the object as base64. + */ + DpEmoji & LoadImage(StackStrF & data, SQInteger type) { Valid().load_image(data.ToStr(), static_cast< dpp::image_type >(type)); return *this; } + /* -------------------------------------------------------------------------------------------- + * Format to name if unicode, name:id if has id or a:name:id if animated. + */ + SQMOD_NODISCARD std::string Format() const { return Valid().format(); } + /* -------------------------------------------------------------------------------------------- + * Retrieve the mention/ping for the emoji. + */ + SQMOD_NODISCARD std::string GetMention() const { return Valid().get_mention(); } +}; + +} // Namespace:: SqMod diff --git a/module/Library/Discord/Presence.cpp b/module/Library/Discord/Presence.cpp new file mode 100644 index 00000000..b12510dd --- /dev/null +++ b/module/Library/Discord/Presence.cpp @@ -0,0 +1,143 @@ +// ------------------------------------------------------------------------------------------------ +#include "Library/Discord/Presence.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +// ------------------------------------------------------------------------------------------------ +SQMOD_DECL_TYPENAME(SqDpActivityButton, _SC("SqDiscordActivityButton")) +SQMOD_DECL_TYPENAME(SqDpActivityButtons, _SC("SqDiscordActivityButtons")) +SQMOD_DECL_TYPENAME(SqDpActivityAssets, _SC("SqDiscordActivityAssets")) +SQMOD_DECL_TYPENAME(SqDpActivitySecrets, _SC("SqDiscordActivitySecrets")) +SQMOD_DECL_TYPENAME(SqDpActivity, _SC("SqDiscordActivity")) +SQMOD_DECL_TYPENAME(SqDpActivities, _SC("SqDiscordActivities")) +SQMOD_DECL_TYPENAME(SqDpPresence, _SC("SqDiscordPresence")) + +// ------------------------------------------------------------------------------------------------ +void Register_Discord_Presence(HSQUIRRELVM vm, Table & ns) +{ + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("ActivityButton"), + Class< DpActivityButton, NoCopy< DpActivityButton > >(vm, SqDpActivityButton::Str) + // Constructors + .Ctor() + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpActivityButton::Fn) + // Member Properties + .Prop(_SC("Valid"), &DpActivityButton::IsValid) + .Prop(_SC("Label"), &DpActivityButton::GetLabel, &DpActivityButton::SetLabel) + .Prop(_SC("URL"), &DpActivityButton::GetURL, &DpActivityButton::SetURL) + // Member Methods + .Func(_SC("SetLabel"), &DpActivityButton::ApplyLabel) + .Func(_SC("SetURL"), &DpActivityButton::ApplyURL) + ); + // -------------------------------------------------------------------------------------------- + Register_Discord_VectorProxy< dpp::activity_button, DpActivityButton, SqDpActivityButton >(vm, ns, _SC("ActivityButtons")); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("ActivityAssets"), + Class< DpActivityAssets, NoCopy< DpActivityAssets > >(vm, SqDpActivityAssets::Str) + // Constructors + .Ctor() + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpActivityAssets::Fn) + // Member Properties + .Prop(_SC("Valid"), &DpActivityAssets::IsValid) + .Prop(_SC("LargeImage"), &DpActivityAssets::GetLargeImage, &DpActivityAssets::SetLargeImage) + .Prop(_SC("LargeText"), &DpActivityAssets::GetLargeText, &DpActivityAssets::SetLargeText) + .Prop(_SC("SmallImage"), &DpActivityAssets::GetSmallImage, &DpActivityAssets::SetSmallImage) + .Prop(_SC("SmallText"), &DpActivityAssets::GetSmallText, &DpActivityAssets::SetSmallText) + // Member Methods + .Func(_SC("SetLargeImage"), &DpActivityAssets::ApplyLargeImage) + .Func(_SC("SetLargeText"), &DpActivityAssets::ApplyLargeText) + .Func(_SC("SetSmallImage"), &DpActivityAssets::ApplySmallImage) + .Func(_SC("SetSmallText"), &DpActivityAssets::ApplySmallText) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("ActivitySecrets"), + Class< DpActivitySecrets, NoCopy< DpActivitySecrets > >(vm, SqDpActivitySecrets::Str) + // Constructors + .Ctor() + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpActivitySecrets::Fn) + // Member Properties + .Prop(_SC("Valid"), &DpActivitySecrets::IsValid) + .Prop(_SC("Join"), &DpActivitySecrets::GetJoin, &DpActivitySecrets::SetJoin) + .Prop(_SC("Spectate"), &DpActivitySecrets::GetSpectate, &DpActivitySecrets::SetSpectate) + .Prop(_SC("Match"), &DpActivitySecrets::GetMatch, &DpActivitySecrets::SetMatch) + // Member Methods + .Func(_SC("SetJoin"), &DpActivitySecrets::ApplyJoin) + .Func(_SC("SetSpectate"), &DpActivitySecrets::ApplySpectate) + .Func(_SC("SetMatch"), &DpActivitySecrets::ApplyMatch) + ); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("Activity"), + Class< DpActivity, NoCopy< DpActivity > >(vm, SqDpActivity::Str) + // Constructors + .Ctor() + .Ctor< SQInteger, StackStrF &, StackStrF &, StackStrF & >() + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpActivity::Fn) + // Member Properties + .Prop(_SC("Valid"), &DpActivity::IsValid) + .Prop(_SC("Name"), &DpActivity::GetName, &DpActivity::SetName) + .Prop(_SC("State"), &DpActivity::GetState, &DpActivity::SetState) + .Prop(_SC("Details"), &DpActivity::GetDetails, &DpActivity::SetDetails) + .Prop(_SC("Assets"), &DpActivity::GetAssets) + .Prop(_SC("Buttons"), &DpActivity::GetButtons) + .Prop(_SC("Emoji"), &DpActivity::GetEmoji) + .Prop(_SC("PartyID"), &DpActivity::GetPartyID) + .Prop(_SC("CurrentPartySize"), &DpActivity::GetCurrentPartySize) + .Prop(_SC("MaxPartySize"), &DpActivity::GetMaxPartySize) + .Prop(_SC("Secrets"), &DpActivity::GetSecrets) + .Prop(_SC("URL"), &DpActivity::GetURL, &DpActivity::SetURL) + .Prop(_SC("Type"), &DpActivity::GetType, &DpActivity::SetType) + .Prop(_SC("CreatedAt"), &DpActivity::GetCreatedAt, &DpActivity::SetCreatedAt) + .Prop(_SC("Start"), &DpActivity::GetStart, &DpActivity::SetStart) + .Prop(_SC("End"), &DpActivity::GetEnd, &DpActivity::SetEnd) + .Prop(_SC("ApplicationID"), &DpActivity::GetApplicationID) + .Prop(_SC("Flags"), &DpActivity::GetFlags, &DpActivity::SetFlags) + .Prop(_SC("Instance"), &DpActivity::IsInstance) + // Member Methods + .Func(_SC("SetName"), &DpActivity::ApplyName) + .Func(_SC("SetState"), &DpActivity::ApplyState) + .Func(_SC("SetDetails"), &DpActivity::ApplyDetails) + .Func(_SC("SetURL"), &DpActivity::ApplyURL) + .Func(_SC("SetType"), &DpActivity::ApplyType) + .Func(_SC("SetCreatedAt"), &DpActivity::ApplyCreatedAt) + .Func(_SC("SetStart"), &DpActivity::ApplyStart) + .Func(_SC("SetEnd"), &DpActivity::ApplyEnd) + .Func(_SC("SetFlags"), &DpActivity::ApplyFlags) + .Func(_SC("GetLargeAssetURL"), &DpActivity::GetLargeAssetURL) + .Func(_SC("GetSmallAssetURL"), &DpActivity::GetSmallAssetURL) + ); + // -------------------------------------------------------------------------------------------- + Register_Discord_VectorProxy< dpp::activity, DpActivity, SqDpActivities >(vm, ns, _SC("Activities")); + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("Presence"), + Class< DpPresence, NoCopy< DpPresence > >(vm, SqDpPresence::Str) + // Constructors + .Ctor() + .Ctor< SQInteger, DpActivity & >() + .Ctor< SQInteger, SQInteger, StackStrF & >() + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDpPresence::Fn) + // Member Properties + .Prop(_SC("Valid"), &DpPresence::IsValid) + .Prop(_SC("JSON"), &DpPresence::BuildJSON) + .Prop(_SC("UserID"), &DpPresence::GetUserID, &DpPresence::SetUserID) + .Prop(_SC("GuildID"), &DpPresence::GetGuildID, &DpPresence::SetGuildID) + .Prop(_SC("Flags"), &DpPresence::GetFlags, &DpPresence::SetFlags) + .Prop(_SC("Activities"), &DpPresence::GetActivities) + .Prop(_SC("DesktopStatus"), &DpPresence::GetDesktopStatus) + .Prop(_SC("WebStatus"), &DpPresence::GetWebStatus) + .Prop(_SC("MobileStatus"), &DpPresence::GetMobileStatus) + .Prop(_SC("Status"), &DpPresence::GetStatus) + // Member Methods + .Func(_SC("SetUserID"), &DpPresence::ApplyUserID) + .Func(_SC("SetGuildID"), &DpPresence::ApplyGuildID) + .Func(_SC("SetFlags"), &DpPresence::ApplyFlags) + .Func(_SC("BuildJSON"), &DpVoiceState::BuildJSON_) + ); +} + +} // Namespace:: SqMod diff --git a/module/Library/Discord/Presence.hpp b/module/Library/Discord/Presence.hpp new file mode 100644 index 00000000..0b5e8ddb --- /dev/null +++ b/module/Library/Discord/Presence.hpp @@ -0,0 +1,928 @@ +#pragma once + +// ------------------------------------------------------------------------------------------------ +#include "Library/Discord/Misc.hpp" + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +/* ------------------------------------------------------------------------------------------------ + * An activity button is a custom button shown in the rich presence. Can be to join a game or whatever. +*/ +struct DpActivityButton +{ + using Ptr = std::unique_ptr< dpp::activity_button >; + /* -------------------------------------------------------------------------------------------- + * Referenced activity instance. + */ + Ptr mPtr{nullptr}; + /* -------------------------------------------------------------------------------------------- + * Whether the referenced pointer is owned. + */ + bool mOwned{false}; + /* -------------------------------------------------------------------------------------------- + * Default constructor. + */ + DpActivityButton() noexcept + : DpActivityButton(new Ptr::element_type(), true) + { } + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpActivityButton(Ptr::pointer ptr, bool owned = false) noexcept + : mPtr(ptr), mOwned(owned) + { } + /* -------------------------------------------------------------------------------------------- + * Copy constructor. + */ + explicit DpActivityButton(const Ptr::element_type & o) noexcept + : DpActivityButton(new Ptr::element_type(o), true) + { } + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + explicit DpActivityButton(Ptr::element_type && o) noexcept + : DpActivityButton(new Ptr::element_type(std::forward< Ptr::element_type >(o)), true) + { } + /* -------------------------------------------------------------------------------------------- + * Copy constructor (disabled). + */ + DpActivityButton(const DpActivityButton & o) = delete; + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + DpActivityButton(DpActivityButton && o) noexcept = default; + /* -------------------------------------------------------------------------------------------- + * Destructor. + */ + ~DpActivityButton() noexcept { Cleanup(); } + /* -------------------------------------------------------------------------------------------- + * Copy assignment operator (disabled). + */ + DpActivityButton & operator = (const DpActivityButton & o) = delete; + /* -------------------------------------------------------------------------------------------- + * Move assignment operator. + */ + DpActivityButton & operator = (DpActivityButton && o) noexcept + { + if (this != &o) { + Cleanup(); + // Transfer members values + mPtr = std::move(o.mPtr); + mOwned = o.mOwned; + } + return *this; + } + /* -------------------------------------------------------------------------------------------- + * Release any referenced resources and default to an empty/invalid state. + */ + void Cleanup() + { + // Do we own this to try delete it? + if (!mOwned && mPtr) { + // Not our job, simply forget about it + [[maybe_unused]] auto p = mPtr.release(); + } else mPtr.reset(); // We own this so delete the instance + } + /* -------------------------------------------------------------------------------------------- + * Validate the managed handle. + */ + void Validate() const { if (!mPtr) STHROWF("Invalid discord activity button handle"); } + /* -------------------------------------------------------------------------------------------- + * Validate the managed handle and retrieve a const reference to it. + */ + SQMOD_NODISCARD Ptr::element_type & Valid() const { Validate(); return *mPtr; } + /* -------------------------------------------------------------------------------------------- + * Check whether a valid instance is managed. + */ + SQMOD_NODISCARD bool IsValid() const { return static_cast< bool >(mPtr); } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the text shown on the button (1-32 characters). + */ + SQMOD_NODISCARD const std::string & GetLabel() const { return Valid().label; } + /* -------------------------------------------------------------------------------------------- + * Modify the text shown on the button (1-32 characters). + */ + void SetLabel(StackStrF & label) const { Valid().label = label.ToStr(); } + /* -------------------------------------------------------------------------------------------- + * Modify the text shown on the button (1-32 characters). + */ + DpActivityButton & ApplyLabel(StackStrF & label) { SetLabel(label); return *this; } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the url opened when clicking the button (1-512 characters). It's may be empty. + */ + SQMOD_NODISCARD const std::string & GetURL() const { return Valid().url; } + /* -------------------------------------------------------------------------------------------- + * Modify the url opened when clicking the button (1-512 characters). + */ + void SetURL(StackStrF & url) const { Valid().url = url.ToStr(); } + /* -------------------------------------------------------------------------------------------- + * Modify the url opened when clicking the button (1-512 characters). + */ + DpActivityButton & ApplyURL(StackStrF & url) { SetURL(url); return *this; } +}; + +/* ------------------------------------------------------------------------------------------------ + * An activity asset are the images and the hover text displayed in the rich presence. +*/ +struct DpActivityAssets +{ + using Ptr = std::unique_ptr< dpp::activity_assets >; + /* -------------------------------------------------------------------------------------------- + * Referenced activity instance. + */ + Ptr mPtr{nullptr}; + /* -------------------------------------------------------------------------------------------- + * Whether the referenced pointer is owned. + */ + bool mOwned{false}; + /* -------------------------------------------------------------------------------------------- + * Default constructor. + */ + DpActivityAssets() noexcept + : DpActivityAssets(new Ptr::element_type(), true) + { } + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpActivityAssets(Ptr::pointer ptr, bool owned = false) noexcept + : mPtr(ptr), mOwned(owned) + { } + /* -------------------------------------------------------------------------------------------- + * Copy constructor. + */ + explicit DpActivityAssets(const Ptr::element_type & o) noexcept + : DpActivityAssets(new Ptr::element_type(o), true) + { } + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + explicit DpActivityAssets(Ptr::element_type && o) noexcept + : DpActivityAssets(new Ptr::element_type(std::forward< Ptr::element_type >(o)), true) + { } + /* -------------------------------------------------------------------------------------------- + * Copy constructor (disabled). + */ + DpActivityAssets(const DpActivityAssets & o) = delete; + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + DpActivityAssets(DpActivityAssets && o) noexcept = default; + /* -------------------------------------------------------------------------------------------- + * Destructor. + */ + ~DpActivityAssets() noexcept { Cleanup(); } + /* -------------------------------------------------------------------------------------------- + * Copy assignment operator (disabled). + */ + DpActivityAssets & operator = (const DpActivityAssets & o) = delete; + /* -------------------------------------------------------------------------------------------- + * Move assignment operator. + */ + DpActivityAssets & operator = (DpActivityAssets && o) noexcept + { + if (this != &o) { + Cleanup(); + // Transfer members values + mPtr = std::move(o.mPtr); + mOwned = o.mOwned; + } + return *this; + } + /* -------------------------------------------------------------------------------------------- + * Release any referenced resources and default to an empty/invalid state. + */ + void Cleanup() + { + // Do we own this to try delete it? + if (!mOwned && mPtr) { + // Not our job, simply forget about it + [[maybe_unused]] auto p = mPtr.release(); + } else mPtr.reset(); // We own this so delete the instance + } + /* -------------------------------------------------------------------------------------------- + * Validate the managed handle. + */ + void Validate() const { if (!mPtr) STHROWF("Invalid discord activity assets handle"); } + /* -------------------------------------------------------------------------------------------- + * Validate the managed handle and retrieve a const reference to it. + */ + SQMOD_NODISCARD Ptr::element_type & Valid() const { Validate(); return *mPtr; } + /* -------------------------------------------------------------------------------------------- + * Check whether a valid instance is managed. + */ + SQMOD_NODISCARD bool IsValid() const { return static_cast< bool >(mPtr); } + /* -------------------------------------------------------------------------------------------- + * Retrieve the large asset image which usually contain snowflake ID or prefixed image ID. + */ + SQMOD_NODISCARD const std::string & GetLargeImage() const { return Valid().large_image; } + /* -------------------------------------------------------------------------------------------- + * Modify the large asset image. + */ + void SetLargeImage(StackStrF & large_image) const { Valid().large_image = large_image.ToStr(); } + /* -------------------------------------------------------------------------------------------- + * Modify the large asset image. + */ + DpActivityAssets & ApplyLargeImage(StackStrF & large_image) { SetLargeImage(large_image); return *this; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the text displayed when hovering over the large image of the activity. + */ + SQMOD_NODISCARD const std::string & GetLargeText() const { return Valid().large_text; } + /* -------------------------------------------------------------------------------------------- + * Modify the text displayed when hovering over the large image of the activity. + */ + void SetLargeText(StackStrF & large_text) const { Valid().large_text = large_text.ToStr(); } + /* -------------------------------------------------------------------------------------------- + * Modify the text displayed when hovering over the large image of the activity. + */ + DpActivityAssets & ApplyLargeText(StackStrF & large_text) { SetLargeText(large_text); return *this; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the small asset image which usually contain snowflake ID or prefixed image ID. + */ + SQMOD_NODISCARD const std::string & GetSmallImage() const { return Valid().small_image; } + /* -------------------------------------------------------------------------------------------- + * Modify the small asset image. + */ + void SetSmallImage(StackStrF & small_image) const { Valid().small_image = small_image.ToStr(); } + /* -------------------------------------------------------------------------------------------- + * Modify the small asset image. + */ + DpActivityAssets & ApplySmallImage(StackStrF & small_image) { SetSmallImage(small_image); return *this; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the text displayed when hovering over the small image of the activity. + */ + SQMOD_NODISCARD const std::string & GetSmallText() const { return Valid().small_text; } + /* -------------------------------------------------------------------------------------------- + * Modify the text displayed when hovering over the small image of the activity. + */ + void SetSmallText(StackStrF & small_text) const { Valid().small_text = small_text.ToStr(); } + /* -------------------------------------------------------------------------------------------- + * Modify the text displayed when hovering over the small image of the activity. + */ + DpActivityAssets & ApplySmallText(StackStrF & small_text) { SetSmallText(small_text); return *this; } +}; + +/* ------------------------------------------------------------------------------------------------ + * Secrets for Rich Presence joining and spectating. +*/ +struct DpActivitySecrets +{ + using Ptr = std::unique_ptr< dpp::activity_secrets >; + /* -------------------------------------------------------------------------------------------- + * Referenced activity instance. + */ + Ptr mPtr{nullptr}; + /* -------------------------------------------------------------------------------------------- + * Whether the referenced pointer is owned. + */ + bool mOwned{false}; + /* -------------------------------------------------------------------------------------------- + * Default constructor. + */ + DpActivitySecrets() noexcept + : DpActivitySecrets(new Ptr::element_type(), true) + { } + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpActivitySecrets(Ptr::pointer ptr, bool owned = false) noexcept + : mPtr(ptr), mOwned(owned) + { } + /* -------------------------------------------------------------------------------------------- + * Copy constructor. + */ + explicit DpActivitySecrets(const Ptr::element_type & o) noexcept + : DpActivitySecrets(new Ptr::element_type(o), true) + { } + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + explicit DpActivitySecrets(Ptr::element_type && o) noexcept + : DpActivitySecrets(new Ptr::element_type(std::forward< Ptr::element_type >(o)), true) + { } + /* -------------------------------------------------------------------------------------------- + * Copy constructor (disabled). + */ + DpActivitySecrets(const DpActivitySecrets & o) = delete; + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + DpActivitySecrets(DpActivitySecrets && o) noexcept = default; + /* -------------------------------------------------------------------------------------------- + * Destructor. + */ + ~DpActivitySecrets() noexcept { Cleanup(); } + /* -------------------------------------------------------------------------------------------- + * Copy assignment operator (disabled). + */ + DpActivitySecrets & operator = (const DpActivitySecrets & o) = delete; + /* -------------------------------------------------------------------------------------------- + * Move assignment operator. + */ + DpActivitySecrets & operator = (DpActivitySecrets && o) noexcept + { + if (this != &o) { + Cleanup(); + // Transfer members values + mPtr = std::move(o.mPtr); + mOwned = o.mOwned; + } + return *this; + } + /* -------------------------------------------------------------------------------------------- + * Release any referenced resources and default to an empty/invalid state. + */ + void Cleanup() + { + // Do we own this to try delete it? + if (!mOwned && mPtr) { + // Not our job, simply forget about it + [[maybe_unused]] auto p = mPtr.release(); + } else mPtr.reset(); // We own this so delete the instance + } + /* -------------------------------------------------------------------------------------------- + * Validate the managed handle. + */ + void Validate() const { if (!mPtr) STHROWF("Invalid discord activity secrets handle"); } + /* -------------------------------------------------------------------------------------------- + * Validate the managed handle and retrieve a const reference to it. + */ + SQMOD_NODISCARD Ptr::element_type & Valid() const { Validate(); return *mPtr; } + /* -------------------------------------------------------------------------------------------- + * Check whether a valid instance is managed. + */ + SQMOD_NODISCARD bool IsValid() const { return static_cast< bool >(mPtr); } + /* -------------------------------------------------------------------------------------------- + * Retrieve the secret for joining a party. + */ + SQMOD_NODISCARD const std::string & GetJoin() const { return Valid().join; } + /* -------------------------------------------------------------------------------------------- + * Modify the secret for joining a party. + */ + void SetJoin(StackStrF & join) const { Valid().join = join.ToStr(); } + /* -------------------------------------------------------------------------------------------- + * Modify the secret for joining a party. + */ + DpActivitySecrets & ApplyJoin(StackStrF & join) { SetJoin(join); return *this; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the secret for spectating a game. + */ + SQMOD_NODISCARD const std::string & GetSpectate() const { return Valid().spectate; } + /* -------------------------------------------------------------------------------------------- + * Modify the secret for spectating a game. + */ + void SetSpectate(StackStrF & spectate) const { Valid().spectate = spectate.ToStr(); } + /* -------------------------------------------------------------------------------------------- + * Modify the secret for spectating a game. + */ + DpActivitySecrets & ApplySpectate(StackStrF & spectate) { SetSpectate(spectate); return *this; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the secret for a specific instanced match. + */ + SQMOD_NODISCARD const std::string & GetMatch() const { return Valid().match; } + /* -------------------------------------------------------------------------------------------- + * Modify the secret for a specific instanced match. + */ + void SetMatch(StackStrF & match) const { Valid().match = match.ToStr(); } + /* -------------------------------------------------------------------------------------------- + * Modify the secret for a specific instanced match. + */ + DpActivitySecrets & ApplyMatch(StackStrF & match) { SetMatch(match); return *this; } +}; + +/* ------------------------------------------------------------------------------------------------ + * An activity is a representation of what a user is doing. It might be a game, or a website, or a movie. Whatever. +*/ +struct DpActivity +{ + using Ptr = std::unique_ptr< dpp::activity >; + /* -------------------------------------------------------------------------------------------- + * Referenced activity instance. + */ + Ptr mPtr{nullptr}; + /* -------------------------------------------------------------------------------------------- + * Whether the referenced pointer is owned. + */ + bool mOwned{false}; + // -------------------------------------------------------------------------------------------- + using Buttons = DpVectorProxy< dpp::activity_button, DpActivityButton >; + // -------------------------------------------------------------------------------------------- + LightObj mSqAssets{}; + LightObj mSqButtons{}; + LightObj mSqEmoji{}; + LightObj mSqSecrets{}; + /* -------------------------------------------------------------------------------------------- + * Default constructor. + */ + DpActivity() noexcept + : DpActivity(new Ptr::element_type(), true) + { } + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpActivity(Ptr::pointer ptr, bool owned = false) noexcept + : mPtr(ptr), mOwned(owned) + { } + /* -------------------------------------------------------------------------------------------- + * Copy constructor. + */ + explicit DpActivity(const Ptr::element_type & o) noexcept + : DpActivity(new Ptr::element_type(o), true) + { } + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + explicit DpActivity(Ptr::element_type && o) noexcept + : DpActivity(new Ptr::element_type(std::forward< Ptr::element_type >(o)), true) + { } + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + DpActivity(SQInteger type, StackStrF & name, StackStrF & state, StackStrF & url) + : DpActivity(new Ptr::element_type(static_cast< dpp::activity_type >(type), name.ToStr(), state.ToStr(), url.ToStr()), true) + { } + /* -------------------------------------------------------------------------------------------- + * Copy constructor (disabled). + */ + DpActivity(const DpActivity & o) = delete; + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + DpActivity(DpActivity && o) noexcept = default; + /* -------------------------------------------------------------------------------------------- + * Destructor. + */ + ~DpActivity() noexcept { Cleanup(); } + /* -------------------------------------------------------------------------------------------- + * Copy assignment operator (disabled). + */ + DpActivity & operator = (const DpActivity & o) = delete; + /* -------------------------------------------------------------------------------------------- + * Move assignment operator. + */ + DpActivity & operator = (DpActivity && o) noexcept + { + if (this != &o) { + Cleanup(); + // Transfer members values + mPtr = std::move(o.mPtr); + mOwned = o.mOwned; + } + return *this; + } + /* -------------------------------------------------------------------------------------------- + * Release any referenced resources and default to an empty/invalid state. + */ + void Cleanup() + { + // Cleanup activity assets, if any + if (!mSqAssets.IsNull()) + { + mSqAssets.CastI< DpActivityAssets >()->Cleanup(); + // Release script resources + mSqAssets.Release(); + } + // Cleanup activity buttons, if any + if (!mSqButtons.IsNull()) + { + mSqButtons.CastI< Buttons >()->Cleanup(); + // Release script resources + mSqButtons.Release(); + } + // Cleanup activity emoji, if any + if (!mSqEmoji.IsNull()) + { + mSqEmoji.CastI< DpEmoji >()->Cleanup(); + // Release script resources + mSqEmoji.Release(); + } + // Cleanup activity secrets, if any + if (!mSqSecrets.IsNull()) + { + mSqSecrets.CastI< DpActivitySecrets >()->Cleanup(); + // Release script resources + mSqSecrets.Release(); + } + // Do we own this to try delete it? + if (!mOwned && mPtr) { + // Not our job, simply forget about it + [[maybe_unused]] auto p = mPtr.release(); + } else mPtr.reset(); // We own this so delete the instance + } + /* -------------------------------------------------------------------------------------------- + * Validate the managed handle. + */ + void Validate() const { if (!mPtr) STHROWF("Invalid discord activity handle"); } + /* -------------------------------------------------------------------------------------------- + * Validate the managed handle and retrieve a const reference to it. + */ + SQMOD_NODISCARD Ptr::element_type & Valid() const { Validate(); return *mPtr; } + /* -------------------------------------------------------------------------------------------- + * Check whether a valid instance is managed. + */ + SQMOD_NODISCARD bool IsValid() const { return static_cast< bool >(mPtr); } + /* -------------------------------------------------------------------------------------------- + * Retrieve the name of the activity. e.g. "Vice City" + */ + SQMOD_NODISCARD const std::string & GetName() const { return Valid().name; } + /* -------------------------------------------------------------------------------------------- + * Modify the name of the activity. + */ + void SetName(StackStrF & name) const { Valid().name = name.ToStr(); } + /* -------------------------------------------------------------------------------------------- + * Modify the name of the activity. + */ + DpActivity & ApplyName(StackStrF & name) { SetName(name); return *this; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the state of the activity. e.g. "Waiting in lobby" + */ + SQMOD_NODISCARD const std::string & GetState() const { return Valid().state; } + /* -------------------------------------------------------------------------------------------- + * Modify the state of the activity. + */ + void SetState(StackStrF & state) const { Valid().state = state.ToStr(); } + /* -------------------------------------------------------------------------------------------- + * Modify the state of the activity. + */ + DpActivity & ApplyState(StackStrF & state) { SetState(state); return *this; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the details of the activity. What the player is currently doing. + */ + SQMOD_NODISCARD const std::string & GetDetails() const { return Valid().details; } + /* -------------------------------------------------------------------------------------------- + * Modify the details of the activity. + */ + void SetDetails(StackStrF & details) const { Valid().details = details.ToStr(); } + /* -------------------------------------------------------------------------------------------- + * Modify the details of the activity. + */ + DpActivity & ApplyDetails(StackStrF & details) { SetDetails(details); return *this; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the images for the presence and their hover texts. + */ + SQMOD_NODISCARD LightObj & GetAssets() + { + if (mSqAssets.IsNull()) + { + mSqAssets = LightObj{SqTypeIdentity< DpActivityAssets >{}, SqVM(), &Valid().assets, false}; + } + // Return the associated script object + return mSqAssets; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the url of the activity. Only applicable for certain sites such a YouTube. + */ + SQMOD_NODISCARD const std::string & GetURL() const { return Valid().url; } + /* -------------------------------------------------------------------------------------------- + * Modify the url of the activity. + */ + void SetURL(StackStrF & url) const { Valid().url = url.ToStr(); } + /* -------------------------------------------------------------------------------------------- + * Modify the url of the activity. + */ + DpActivity & ApplyURL(StackStrF & url) { SetURL(url); return *this; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the custom buttons shown in the Rich Presence (max 2). + */ + SQMOD_NODISCARD LightObj & GetButtons() + { + if (mSqButtons.IsNull()) + { + mSqButtons = LightObj{SqTypeIdentity< Buttons >{}, SqVM(), &Valid().buttons, false}; + } + // Return the associated script object + return mSqButtons; + } + /* -------------------------------------------------------------------------------------------- + * Retrieve the emoji used for the custom status. + */ + SQMOD_NODISCARD LightObj & GetEmoji() + { + if (mSqEmoji.IsNull()) + { + mSqEmoji = LightObj{SqTypeIdentity< DpEmoji >{}, SqVM(), &Valid().emoji, false}; + } + // Return the associated script object + return mSqEmoji; + } + /* -------------------------------------------------------------------------------------------- + * Retrieve the ID of the party. + */ + SQMOD_NODISCARD dpp::snowflake GetPartyID() const { return Valid().party.id; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the party's current size. Used to show the party's current size. + */ + SQMOD_NODISCARD SQInteger GetCurrentPartySize() const { return Valid().party.current_size; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the party's maximum size. Used to show the party's maximum size. + */ + SQMOD_NODISCARD SQInteger GetMaxPartySize() const { return Valid().party.maximum_size; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the secrets for rich presence joining and spectating. + */ + SQMOD_NODISCARD LightObj & GetSecrets() + { + if (mSqSecrets.IsNull()) + { + mSqSecrets = LightObj{SqTypeIdentity< DpActivitySecrets >{}, SqVM(), &Valid().secrets, false}; + } + // Return the associated script object + return mSqSecrets; + } + /* -------------------------------------------------------------------------------------------- + * Retrieve the type for the activity from SqDiscordActivityType. + */ + SQMOD_NODISCARD SQInteger GetType() const { return Valid().type; } + /* -------------------------------------------------------------------------------------------- + * Modify the type for the activity from SqDiscordActivityType. + */ + void SetType(SQInteger type) const { Valid().type = static_cast< dpp::activity_type >(type); } + /* -------------------------------------------------------------------------------------------- + * Modify the type for the activity from SqDiscordActivityType. + */ + DpActivity & ApplyType(SQInteger type) { SetType(type); return *this; } + /* -------------------------------------------------------------------------------------------- + * Retrieve when the activity was created. + */ + SQMOD_NODISCARD SQInteger GetCreatedAt() const + { + return static_cast< SQInteger >(std::chrono::duration_cast< std::chrono::seconds >(std::chrono::system_clock::from_time_t(Valid().created_at).time_since_epoch()).count()); + } + /* -------------------------------------------------------------------------------------------- + * Modify when the activity was created. + */ + void SetCreatedAt(SQInteger s) const + { + Valid().created_at = std::chrono::system_clock::to_time_t(std::chrono::time_point< std::chrono::system_clock >{std::chrono::seconds{s}}); + } + /* -------------------------------------------------------------------------------------------- + * Modify when the activity was created. + */ + DpActivity & ApplyCreatedAt(SQInteger s) { SetCreatedAt(s); return *this; } + /* -------------------------------------------------------------------------------------------- + * Retrieve when the activity was started. + */ + SQMOD_NODISCARD SQInteger GetStart() const + { + return static_cast< SQInteger >(std::chrono::duration_cast< std::chrono::seconds >(std::chrono::system_clock::from_time_t(Valid().start).time_since_epoch()).count()); + } + /* -------------------------------------------------------------------------------------------- + * Modify when the activity was started. + */ + void SetStart(SQInteger s) const + { + Valid().start = std::chrono::system_clock::to_time_t(std::chrono::time_point< std::chrono::system_clock >{std::chrono::seconds{s}}); + } + /* -------------------------------------------------------------------------------------------- + * Modify when the activity was started. + */ + DpActivity & ApplyStart(SQInteger s) { SetStart(s); return *this; } + /* -------------------------------------------------------------------------------------------- + * Retrieve when the activity was stopped. + */ + SQMOD_NODISCARD SQInteger GetEnd() const + { + return static_cast< SQInteger >(std::chrono::duration_cast< std::chrono::seconds >(std::chrono::system_clock::from_time_t(Valid().end).time_since_epoch()).count()); + } + /* -------------------------------------------------------------------------------------------- + * Modify when the activity was stopped. + */ + void SetEnd(SQInteger s) const + { + Valid().end = std::chrono::system_clock::to_time_t(std::chrono::time_point< std::chrono::system_clock >{std::chrono::seconds{s}}); + } + /* -------------------------------------------------------------------------------------------- + * Modify when the activity was stopped. + */ + DpActivity & ApplyEnd(SQInteger s) { SetEnd(s); return *this; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the creating application (e.g. a linked account on the user's client) + */ + SQMOD_NODISCARD dpp::snowflake GetApplicationID() const { return Valid().application_id; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the flags for the activity from SqDiscordActivityFlags. + */ + SQMOD_NODISCARD SQInteger GetFlags() const { return Valid().flags; } + /* -------------------------------------------------------------------------------------------- + * Modify the flags for the activity from SqDiscordActivityFlags. + */ + void SetFlags(SQInteger flags) const { Valid().flags = static_cast< uint8_t >(flags); } + /* -------------------------------------------------------------------------------------------- + * Modify the flags for the activity from SqDiscordActivityFlags. + */ + DpActivity & ApplyFlags(SQInteger flags) { SetFlags(flags); return *this; } + /* -------------------------------------------------------------------------------------------- + * Retrieve whether or not the activity is an instanced game session. + */ + SQMOD_NODISCARD bool IsInstance() const { return Valid().is_instance; } + /* -------------------------------------------------------------------------------------------- + * Get the assets large image url if they have one, otherwise returns an empty string. + * In case of prefixed image IDs (mp:{image_id}) it returns an empty string. + * See: https://discord.com/developers/docs/topics/gateway-events#activity-object-activity-asset-image + */ + SQMOD_NODISCARD std::string GetLargeAssetURL(SQInteger size, SQInteger format) const + { return Valid().get_large_asset_url(static_cast< uint16_t >(size), static_cast< dpp::image_type >(format)); } + /* -------------------------------------------------------------------------------------------- + * Get the assets small image url if they have one, otherwise returns an empty string. + * In case of prefixed image IDs (mp:{image_id}) it returns an empty string. + * See: https://discord.com/developers/docs/topics/gateway-events#activity-object-activity-asset-image + */ + SQMOD_NODISCARD std::string GetSmallAssetURL(SQInteger size, SQInteger format) const + { return Valid().get_small_asset_url(static_cast< uint16_t >(size), static_cast< dpp::image_type >(format)); } +}; + +/* ------------------------------------------------------------------------------------------------ + * Represents user presence, e.g. what game they are playing and if they are online. +*/ +struct DpPresence +{ + using Ptr = std::unique_ptr< dpp::presence >; + /* -------------------------------------------------------------------------------------------- + * Referenced presence instance. + */ + Ptr mPtr{nullptr}; + /* -------------------------------------------------------------------------------------------- + * Whether the referenced pointer is owned. + */ + bool mOwned{false}; + // -------------------------------------------------------------------------------------------- + using Activities = DpVectorProxy< dpp::activity, DpActivity >; + // -------------------------------------------------------------------------------------------- + LightObj mSqActivities{}; + /* -------------------------------------------------------------------------------------------- + * Default constructor. + */ + DpPresence() noexcept + : DpPresence(new Ptr::element_type(), true) + { } + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpPresence(Ptr::pointer ptr, bool owned = false) noexcept + : mPtr(ptr), mOwned(owned) + { } + /* -------------------------------------------------------------------------------------------- + * Copy constructor. + */ + explicit DpPresence(const Ptr::element_type & o) noexcept + : DpPresence(new Ptr::element_type(o), true) + { } + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + explicit DpPresence(Ptr::element_type && o) noexcept + : DpPresence(new Ptr::element_type(std::forward< Ptr::element_type >(o)), true) + { } + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + DpPresence(SQInteger status, DpActivity & activity) + : DpPresence(new Ptr::element_type(static_cast< dpp::presence_status >(status), activity.Valid()), true) + { } + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + DpPresence(SQInteger status, SQInteger type, StackStrF & description) + : DpPresence(new Ptr::element_type(static_cast< dpp::presence_status >(status), static_cast< dpp::activity_type >(status), description.ToStr()), true) + { } + /* -------------------------------------------------------------------------------------------- + * Copy constructor (disabled). + */ + DpPresence(const DpPresence & o) = delete; + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + DpPresence(DpPresence && o) noexcept = default; + /* -------------------------------------------------------------------------------------------- + * Destructor. + */ + ~DpPresence() noexcept { Cleanup(); } + /* -------------------------------------------------------------------------------------------- + * Copy assignment operator (disabled). + */ + DpPresence & operator = (const DpPresence & o) = delete; + /* -------------------------------------------------------------------------------------------- + * Move assignment operator. + */ + DpPresence & operator = (DpPresence && o) noexcept + { + if (this != &o) { + Cleanup(); + // Transfer members values + mPtr = std::move(o.mPtr); + mOwned = o.mOwned; + } + return *this; + } + /* -------------------------------------------------------------------------------------------- + * Release any referenced resources and default to an empty/invalid state. + */ + void Cleanup() + { + // Cleanup presence activities, if any + if (!mSqActivities.IsNull()) + { + mSqActivities.CastI< Activities >()->Cleanup(); + // Release script resources + mSqActivities.Release(); + } + // Do we own this to try delete it? + if (!mOwned && mPtr) { + // Not our job, simply forget about it + [[maybe_unused]] auto p = mPtr.release(); + } else mPtr.reset(); // We own this so delete the instance + } + /* -------------------------------------------------------------------------------------------- + * Validate the managed handle. + */ + void Validate() const { if (!mPtr) STHROWF("Invalid discord presence handle"); } + /* -------------------------------------------------------------------------------------------- + * Validate the managed handle and retrieve a const reference to it. + */ + SQMOD_NODISCARD Ptr::element_type & Valid() const { Validate(); return *mPtr; } + /* -------------------------------------------------------------------------------------------- + * Check whether a valid instance is managed. + */ + SQMOD_NODISCARD bool IsValid() const { return static_cast< bool >(mPtr); } + /* -------------------------------------------------------------------------------------------- + * Retrieve the user that the presence applies to. + */ + SQMOD_NODISCARD dpp::snowflake GetUserID() const { return Valid().user_id; } + /* -------------------------------------------------------------------------------------------- + * Modify the user that the presence applies to. + */ + void SetUserID(dpp::snowflake id) const { Valid().user_id = id; } + /* -------------------------------------------------------------------------------------------- + * Modify the user that the presence applies to. + */ + DpPresence & ApplyUserID(dpp::snowflake id) { SetUserID(id); return *this; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the guild that the presence applies to. + */ + SQMOD_NODISCARD dpp::snowflake GetGuildID() const { return Valid().guild_id; } + /* -------------------------------------------------------------------------------------------- + * Modify the guild that the presence applies to. + */ + void SetGuildID(dpp::snowflake id) const { Valid().guild_id = id; } + /* -------------------------------------------------------------------------------------------- + * Modify the guild that the presence applies to. + */ + DpPresence & ApplyGuildID(dpp::snowflake id) { SetGuildID(id); return *this; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the presence bit-mask. + */ + SQMOD_NODISCARD SQInteger GetFlags() const { return static_cast< SQInteger >(Valid().flags); } + /* -------------------------------------------------------------------------------------------- + * Modify the presence bit-mask. + */ + void SetFlags(SQInteger f) const { Valid().flags = static_cast< uint8_t >(f); } + /* -------------------------------------------------------------------------------------------- + * Modify the presence bit-mask. + */ + DpPresence & ApplyFlags(SQInteger f) { SetFlags(f); return *this; } + /* -------------------------------------------------------------------------------------------- + * Retrieve the list of activities. + */ + SQMOD_NODISCARD LightObj & GetActivities() + { + if (mSqActivities.IsNull()) + { + mSqActivities = LightObj{SqTypeIdentity< Activities >{}, SqVM(), &Valid().activities, false}; + } + // Return the associated script object + return mSqActivities; + } + /* -------------------------------------------------------------------------------------------- + * Build JSON string from this object. + */ + SQMOD_NODISCARD std::string BuildJSON() const { return Valid().build_json(); } + SQMOD_NODISCARD std::string BuildJSON_(bool with_id) const { return Valid().build_json(with_id); } + /* -------------------------------------------------------------------------------------------- + * Retrieve the users status on desktop. + */ + SQMOD_NODISCARD SQInteger GetDesktopStatus() const { return static_cast< SQInteger >(Valid().desktop_status()); } + /* -------------------------------------------------------------------------------------------- + * Retrieve the user's status on web. + */ + SQMOD_NODISCARD SQInteger GetWebStatus() const { return static_cast< SQInteger >(Valid().web_status()); } + /* -------------------------------------------------------------------------------------------- + * Retrieve the user's status on mobile. + */ + SQMOD_NODISCARD SQInteger GetMobileStatus() const { return static_cast< SQInteger >(Valid().mobile_status()); } + /* -------------------------------------------------------------------------------------------- + * Retrieve the user's status as shown to other users. + */ + SQMOD_NODISCARD SQInteger GetStatus() const { return static_cast< SQInteger >(Valid().status()); } +}; + + +} // Namespace:: SqMod diff --git a/module/Library/Discord/Role.cpp b/module/Library/Discord/Role.cpp new file mode 100644 index 00000000..8b1d96ec --- /dev/null +++ b/module/Library/Discord/Role.cpp @@ -0,0 +1,9 @@ +// ------------------------------------------------------------------------------------------------ +#include "Library/Discord/Role.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + + + +} // Namespace:: SqMod diff --git a/module/Library/Discord/Role.hpp b/module/Library/Discord/Role.hpp new file mode 100644 index 00000000..ab7ce4d7 --- /dev/null +++ b/module/Library/Discord/Role.hpp @@ -0,0 +1,13 @@ +#pragma once + +// ------------------------------------------------------------------------------------------------ +#include "Core/Utility.hpp" + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + + +} // Namespace:: SqMod diff --git a/module/Library/Discord/User.cpp b/module/Library/Discord/User.cpp new file mode 100644 index 00000000..7c3e19b4 --- /dev/null +++ b/module/Library/Discord/User.cpp @@ -0,0 +1,9 @@ +// ------------------------------------------------------------------------------------------------ +#include "Library/Discord/User.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + + + +} // Namespace:: SqMod diff --git a/module/Library/Discord/User.hpp b/module/Library/Discord/User.hpp new file mode 100644 index 00000000..ab7ce4d7 --- /dev/null +++ b/module/Library/Discord/User.hpp @@ -0,0 +1,13 @@ +#pragma once + +// ------------------------------------------------------------------------------------------------ +#include "Core/Utility.hpp" + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + + +} // Namespace:: SqMod diff --git a/module/Library/Discord/Utilities.cpp b/module/Library/Discord/Utilities.cpp new file mode 100644 index 00000000..b5e7c77e --- /dev/null +++ b/module/Library/Discord/Utilities.cpp @@ -0,0 +1,9 @@ +// ------------------------------------------------------------------------------------------------ +#include "Library/Discord/Utilities.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + + + +} // Namespace:: SqMod diff --git a/module/Library/Discord/Utilities.hpp b/module/Library/Discord/Utilities.hpp new file mode 100644 index 00000000..1802c7aa --- /dev/null +++ b/module/Library/Discord/Utilities.hpp @@ -0,0 +1,462 @@ +#pragma once + +// ------------------------------------------------------------------------------------------------ +#include "Core/Utility.hpp" + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +namespace Sqrat { + +// Allow the VM to treat the dpp::snowflake type as a integer. +template<> struct Var +{ + dpp::snowflake value; + Var(HSQUIRRELVM vm, SQInteger idx) + { + sq_getinteger(vm, idx, reinterpret_cast(&static_cast(value))); + } + inline static void push(HSQUIRRELVM vm, const dpp::snowflake& value) noexcept + { + sq_pushinteger(vm, static_cast(static_cast(value))); + } +}; + +// Allow the VM to treat the dpp::snowflake type as a integer. +template<> struct Var +{ + dpp::snowflake value; + Var(HSQUIRRELVM vm, SQInteger idx) + { + sq_getinteger(vm, idx, reinterpret_cast(&static_cast(value))); + } + inline static void push(HSQUIRRELVM vm, const dpp::snowflake& value) noexcept + { + sq_pushinteger(vm, static_cast(static_cast(value))); + } +}; + +} + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +/* ------------------------------------------------------------------------------------------------ + * Wrapper around a std::vector of DPP values. +*/ +template < class T, class W > struct DpVectorProxy +{ + using Ptr = std::unique_ptr< std::vector< T > >; + using Vec = std::vector< std::pair< LightObj, W * > >; + /* -------------------------------------------------------------------------------------------- + * Referenced vector instance. + */ + Ptr mPtr{nullptr}; + /* -------------------------------------------------------------------------------------------- + * Cached script objects vector. + */ + Vec mVec{}; + /* -------------------------------------------------------------------------------------------- + * Whether the referenced pointer is owned. + */ + bool mOwned{false}; + /* -------------------------------------------------------------------------------------------- + * Default constructor. + */ + DpVectorProxy() noexcept = default; + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpVectorProxy(typename Ptr::pointer ptr, bool owned = false) + : mPtr(ptr), mVec(), mOwned(owned) + { if (mPtr) mVec.resize(mPtr->size()); } + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpVectorProxy(const typename Ptr::element_type & o) noexcept + : DpVectorProxy(new typename Ptr::element_type(o), true) + { } + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + explicit DpVectorProxy(typename Ptr::element_type && o) noexcept + : DpVectorProxy(new typename Ptr::element_type(std::forward< Ptr::element_type >(o)), true) + { } + /* -------------------------------------------------------------------------------------------- + * Copy constructor (disabled). + */ + DpVectorProxy(const DpVectorProxy & o) = delete; + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + DpVectorProxy(DpVectorProxy && o) noexcept = default; + /* -------------------------------------------------------------------------------------------- + * Destructor. + */ + ~DpVectorProxy() noexcept { Cleanup(); } + /* -------------------------------------------------------------------------------------------- + * Copy assignment operator (disabled). + */ + DpVectorProxy & operator = (const DpVectorProxy & o) = delete; + /* -------------------------------------------------------------------------------------------- + * Move assignment operator. + */ + DpVectorProxy & operator = (DpVectorProxy && o) noexcept + { + if (this != &o) { + Cleanup(); + // Transfer members values + mPtr = std::move(o.mPtr); + mVec = std::move(o.mVec); + mOwned = o.mOwned; + } + return *this; + } + /* -------------------------------------------------------------------------------------------- + * Release any referenced resources and default to an empty/invalid state. + */ + void Cleanup() + { + // Invalidate cached instances + ClearCache(); + // Do we own this to try delete it? + if (!mOwned && mPtr) { + // Not our job, simply forget about it + [[maybe_unused]] auto p = mPtr.release(); + } else mPtr.reset(); // We own this so delete the instance + } + /* -------------------------------------------------------------------------------------------- + * Validate the managed handle. + */ + void Validate() const { if (!mPtr) STHROWF("Invalid discord vector handle"); } + /* -------------------------------------------------------------------------------------------- + * Validate the managed handle and retrieve a const reference to it. + */ + SQMOD_NODISCARD typename Ptr::element_type & Valid() const { Validate(); return *mPtr; } + /* -------------------------------------------------------------------------------------------- + * Check whether a valid instance is managed. + */ + SQMOD_NODISCARD bool IsValid() const { return static_cast< bool >(mPtr); } + /* -------------------------------------------------------------------------------------------- + * Make sure an index is within range and return the container. Container must exist. + */ + void ValidIdx_(SQInteger i) + { + if (static_cast< size_t >(i) >= Valid().size()) + { + STHROWF("Invalid vector container index({})", i); + } + } + typename Ptr::element_type & ValidIdx(SQInteger i) { ValidIdx_(i); return *mPtr; } + /* -------------------------------------------------------------------------------------------- + * Make sure an index is within range and return the container. Container must exist. + */ + void ValidIdx_(SQInteger i) const + { + if (static_cast< size_t >(i) >= Valid().size()) + { + STHROWF("Invalid vector container index({})", i); + } + } + const typename Ptr::element_type & ValidIdx(SQInteger i) const { ValidIdx_(i); return *mPtr; } + /* -------------------------------------------------------------------------------------------- + * Make sure a container instance is referenced and is populated, then return it. + */ + void ValidPop_() + { + if (Valid().empty()) + { + STHROWF("Vector container is empty"); + } + } + typename Ptr::element_type & ValidPop() { ValidPop_(); return *mPtr; } + /* -------------------------------------------------------------------------------------------- + * Make sure a container instance is referenced and is populated, then return it. + */ + void ValidPop_() const + { + if (Valid().empty()) + { + STHROWF("Vector container is empty"); + } + } + const typename Ptr::element_type & ValidPop() const { ValidPop_(); return *mPtr; } + /* -------------------------------------------------------------------------------------------- + * Check if a container instance is referenced. + */ + SQMOD_NODISCARD bool IsNull() const { return !mPtr; } + /* -------------------------------------------------------------------------------------------- + * Retrieve a value from the container. + */ + SQMOD_NODISCARD LightObj & Get_(SQInteger i) + { + // Is the element cached? + if (mVec[static_cast< size_t >(i)].first.IsNull()) + { + mVec[static_cast< size_t >(i)] = Obj(&mPtr->at(static_cast< size_t >(i))); + } + // Return the object from the cache + return mVec[static_cast< size_t >(i)].first; + } + SQMOD_NODISCARD LightObj & Get(SQInteger i) + { + // Was the referenced vector modified? + if (mVec.size() < Valid().size()) + { + // Synchronize the size + mVec.resize(mPtr->size()); + // Synchronize the cache + CacheSync(); + } + // Validate index + ValidIdx_(i); + // Perform the request + return Get_(i); + } + /* -------------------------------------------------------------------------------------------- + * Modify a value from the container. + */ + void Set(SQInteger i, const W & v) { ValidIdx(i)[static_cast< size_t >(i)] = v.Valid(); } + /* -------------------------------------------------------------------------------------------- + * Check if the container has no elements. + */ + SQMOD_NODISCARD bool Empty() const { return Valid().empty(); } + /* -------------------------------------------------------------------------------------------- + * Retrieve the number of elements in the container. + */ + SQMOD_NODISCARD SQInteger Size() const { return static_cast< SQInteger >(Valid().size()); } + /* -------------------------------------------------------------------------------------------- + * Retrieve the number of elements that the container has currently allocated space for. + */ + SQMOD_NODISCARD SQInteger Capacity() const { return static_cast< SQInteger >(Valid().capacity()); } + /* -------------------------------------------------------------------------------------------- + * Synchronize cache container instances. + */ + void CacheSync() + { + // Invalidate cached instances, if any + for (size_t i = 0; i < mVec.size(); ++i) + { + // Is this element cached? + if (mVec[i].second != nullptr) + { + // Discard previous instance, if any + [[maybe_unused]] auto _ = mVec[i].second->mPtr.release(); + // Sync to new instance + mVec[i].second->mPtr.reset(&mPtr->at(i)); + } + } + } + /* -------------------------------------------------------------------------------------------- + * Increase the capacity of the container to a value that's greater or equal to the one specified. + */ + DpVectorProxy & Reserve(SQInteger n) + { + Valid().reserve(ClampL< SQInteger, size_t >(n)); + mVec.reserve(mPtr->size()); + CacheSync(); + return *this; + } + /* -------------------------------------------------------------------------------------------- + * Request the removal of unused capacity. + */ + void Compact() { Valid().shrink_to_fit(); CacheSync(); mVec.shrink_to_fit(); } + /* -------------------------------------------------------------------------------------------- + * Erase all elements from the cache container. + */ + void ClearCache() + { + // Invalidate cached instances, if any + for (auto & e : mVec) + { + // Is this element cached? + if (e.second != nullptr) + { + // Invalidate the instance + e.second->Cleanup(); + // Forget about it + e.second = nullptr; + // Release script object + e.first.Release(); + } + } + // Clear the cache vector + mVec.clear(); + } + /* -------------------------------------------------------------------------------------------- + * Erase all elements from the container. + */ + void Clear() { Validate(); ClearCache(); mPtr->clear(); } + /* -------------------------------------------------------------------------------------------- + * Push a value at the back of the container. + */ + void Push(const W & v) + { + Valid().push_back(v.Valid()); + mVec.emplace_back(); + CacheSync(); + } + /* -------------------------------------------------------------------------------------------- + * Extends the Container by appending all the items in the given container. + */ + void Extend(DpVectorProxy & v) + { + Valid().insert(Valid().end(), v.Valid().begin(), v.Valid().end()); + mVec.resize(mPtr->size()); + CacheSync(); + } + /* -------------------------------------------------------------------------------------------- + * Pop the last element in the container. + */ + void Pop() + { + Validate(); + // Is this element cached? + if (mVec.back().second != nullptr) + { + // Invalidate the instance + mVec.back().second->Cleanup(); + mVec.back().first.Release(); + } + // Safe to remove + mPtr->pop_back(); + mVec.pop_back(); + } + /* -------------------------------------------------------------------------------------------- + * Erase the element at a certain position. + */ + void EraseAt(SQInteger i) + { + ValidIdx_(i); + // Is this element cached? + if (mVec[static_cast< size_t >(i)].second != nullptr) + { + // Invalidate the instance + mVec[static_cast< size_t >(i)].second->Cleanup(); + mVec[static_cast< size_t >(i)].first.Release(); + } + // Safe to remove + mPtr->erase(mPtr->begin() + static_cast< size_t >(i)); + mVec.erase(mVec.begin() + static_cast< size_t >(i)); + // Synchronize cache + CacheSync(); + } + /* -------------------------------------------------------------------------------------------- + * Iterate all values through a functor. + */ + void Each(Function & fn) + { + Validate(); + // Iterate referenced vector elements + for (size_t i = 0; i < mVec.size(); ++i) + { + fn.Execute(Get(static_cast< SQInteger >(i))); + } + } + /* -------------------------------------------------------------------------------------------- + * Iterate values in range through a functor. + */ + void EachRange(SQInteger p, SQInteger n, Function & fn) + { + ValidIdx_(p); + ValidIdx_(p + n); + // Iterate specified range + for (n += p; p < n; ++p) + { + fn.Execute(Get(static_cast< SQInteger >(p))); + } + } + /* -------------------------------------------------------------------------------------------- + * Iterate all values through a functor until stopped (i.e false is returned). + */ + void While(Function & fn) + { + Validate(); + // Iterate referenced vector elements + for (size_t i = 0; i < mVec.size(); ++i) + { + auto ret = fn.Eval(Get(static_cast< SQInteger >(i))); + // (null || true) == continue & false == break + if (!ret.IsNull() || !ret.template Cast< bool >()) + { + break; + } + } + } + /* -------------------------------------------------------------------------------------------- + * Iterate values in range through a functor until stopped (i.e false is returned). + */ + void WhileRange(SQInteger p, SQInteger n, Function & fn) + { + ValidIdx_(p); + ValidIdx_(p + n); + // Iterate specified range + for (n += p; p < n; ++p) + { + auto ret = fn.Eval(Get(static_cast< SQInteger >(p))); + // (null || true) == continue & false == break + if (!ret.IsNull() || !ret.template Cast< bool >()) + { + break; + } + } + } + /* -------------------------------------------------------------------------------------------- + * Retrieve a wrapped instance as a script object. + */ + SQMOD_NODISCARD static std::pair< LightObj, W * > Obj(T * ptr, bool owned = false) + { + // Create the wrapper instance for given pointer + auto wp = std::make_unique< W >(ptr, false); + // Create script object for wrapper instance + std::pair< LightObj, W * > p{LightObj{wp.get()}, wp.get()}; + // Release ownership of the wrapper instance + [[maybe_unused]] auto _ = wp.release(); + // Return the script object and wrapper instance + return p; + } + /* -------------------------------------------------------------------------------------------- + * Retrieve a wrapped instance as a script object. + */ + SQMOD_NODISCARD static std::pair< LightObj, W * > Obj(const T * ptr, bool owned = false) + { + return Obj(const_cast< T * >(ptr), owned); + } +}; + +template < class T, class W, class U > inline void Register_Discord_VectorProxy(HSQUIRRELVM vm, Table & ns, const SQChar * name) +{ + using Container = DpVectorProxy< T, W >; + // -------------------------------------------------------------------------------------------- + ns.Bind(name, + Class< Container, NoConstructor< Container > >(vm, U::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &U::Fn) + // Properties + .Prop(_SC("Null"), &Container::IsNull) + .Prop(_SC("Empty"), &Container::Empty) + .Prop(_SC("Size"), &Container::Size) + .Prop(_SC("Capacity"), &Container::Capacity, &Container::Reserve) + // Member Methods + .Func(_SC("Get"), &Container::Get) + .Func(_SC("Set"), &Container::Set) + .Func(_SC("Reserve"), &Container::Reserve) + .Func(_SC("Compact"), &Container::Compact) + .Func(_SC("Clear"), &Container::Clear) + .Func(_SC("Push"), &Container::Push) + .Func(_SC("Append"), &Container::Push) + .Func(_SC("Extend"), &Container::Extend) + .Func(_SC("Pop"), &Container::Pop) + .Func(_SC("EraseAt"), &Container::EraseAt) + .Func(_SC("Each"), &Container::Each) + .Func(_SC("EachRange"), &Container::EachRange) + .Func(_SC("While"), &Container::While) + .Func(_SC("WhileRange"), &Container::WhileRange) + ); +} + +} // Namespace:: SqMod diff --git a/module/Library/Utils.cpp b/module/Library/Utils.cpp index 269db36c..e81157aa 100644 --- a/module/Library/Utils.cpp +++ b/module/Library/Utils.cpp @@ -88,6 +88,7 @@ static SQInteger SqExtractIPv4(HSQUIRRELVM vm) // ------------------------------------------------------------------------------------------------ extern void Register_IdPool(HSQUIRRELVM vm, Table & ns); extern void Register_Vector(HSQUIRRELVM vm, Table & ns); +extern void Register_Template(HSQUIRRELVM vm, Table & ns); extern void Register_Native_String(HSQUIRRELVM vm, Table & ns); extern void Register_ServerAnnouncer(HSQUIRRELVM vm, Table & ns); @@ -99,6 +100,7 @@ void Register_Utils(HSQUIRRELVM vm) Register_IdPool(vm, ns); Register_Vector(vm, ns); + Register_Template(vm, ns); Register_Native_String(vm, ns); Register_ServerAnnouncer(vm, ns); @@ -110,7 +112,6 @@ void Register_Utils(HSQUIRRELVM vm) // ------------------------------------------------------------------------------------------------ void Register_IdPool(HSQUIRRELVM vm, Table & ns) { - // -------------------------------------------------------------------------------------------- ns.Bind(_SC("IdPool"), Class< SqIdPool, NoCopy< SqIdPool > >(vm, SqIdPoolTypename::Str) // Constructors diff --git a/module/Main.cpp b/module/Main.cpp index 89ca3699..7dfa5f7c 100644 --- a/module/Main.cpp +++ b/module/Main.cpp @@ -10,6 +10,10 @@ #include #endif // SQMOD_OS_WINDOWS +// ------------------------------------------------------------------------------------------------ +#include +#include + // ------------------------------------------------------------------------------------------------ namespace SqMod { @@ -24,6 +28,9 @@ extern void ProcessRoutines(); extern void ProcessTasks(); extern void ProcessThreads(); extern void ProcessNet(); +#ifdef SQMOD_DISCORD + extern void ProcessDiscord(); +#endif /* ------------------------------------------------------------------------------------------------ * Will the scripts be reloaded at the end of the current event? @@ -147,6 +154,7 @@ static void OnServerShutdown() Core::Get().EmitServerShutdown(); // Deallocate and release everything obtained at startup Core::Get().Terminate(true); + curl_global_cleanup(); SQMOD_SV_EV_TRACEBACK("[TRACE>] OnServerShutdown") } SQMOD_CATCH_EVENT_EXCEPTION(OnServerShutdown) @@ -172,6 +180,10 @@ static void OnServerFrame(float elapsed_time) ProcessThreads(); // Process network ProcessNet(); + // Process Discord +#ifdef SQMOD_DISCORD + ProcessDiscord(); +#endif // Process log messages from other threads Logger::Get().ProcessQueue(); // See if a reload was requested @@ -994,6 +1006,8 @@ SQMOD_API_EXPORT unsigned int VcmpPluginInit(PluginFuncs * funcs, PluginCallback _Info->apiMinorVersion = PLUGIN_API_MINOR; // Assign the plug-in name std::snprintf(_Info->name, sizeof(_Info->name), "%s", SQMOD_HOST_NAME); + // Initialize CURL + curl_global_init(CURL_GLOBAL_DEFAULT); // Initialize third-party allocator gsRPMallocInit = std::make_unique< RPMallocInit >(); // Attempt to initialize the logger before anything else diff --git a/module/Register.cpp b/module/Register.cpp index 685d0677..67100bff 100644 --- a/module/Register.cpp +++ b/module/Register.cpp @@ -30,6 +30,9 @@ extern void Register_CVehicle(HSQUIRRELVM vm); // ------------------------------------------------------------------------------------------------ extern void Register_Chrono(HSQUIRRELVM vm); extern void Register_CURL(HSQUIRRELVM vm); +#ifdef SQMOD_DISCORD + extern void Register_Discord(HSQUIRRELVM vm); +#endif extern void Register_Format(HSQUIRRELVM vm); extern void Register_IO(HSQUIRRELVM vm); extern void Register_JSON(HSQUIRRELVM vm); @@ -98,6 +101,9 @@ bool RegisterAPI(HSQUIRRELVM vm) Register_Chrono(vm); Register_CURL(vm); +#ifdef SQMOD_DISCORD + Register_Discord(vm); +#endif Register_Format(vm); Register_IO(vm); Register_JSON(vm); diff --git a/vendor/CMakeLists.txt b/vendor/CMakeLists.txt index 4a62fcf3..279d7b3e 100644 --- a/vendor/CMakeLists.txt +++ b/vendor/CMakeLists.txt @@ -8,6 +8,8 @@ add_subdirectory(TinyDir) add_subdirectory(SAJSON) add_subdirectory(CPR) add_subdirectory(UTF8) +add_subdirectory(JSON) +add_subdirectory(Inja) add_subdirectory(PUGIXML) add_subdirectory(CivetWeb) if (ENABLE_BUILTIN_MYSQL_C) @@ -70,4 +72,29 @@ set(BUILD_STATIC ON CACHE INTERNAL "" FORCE) if (WIN32 OR MINGW) set(ZMQ_HAVE_IPC OFF CACHE INTERNAL "" FORCE) endif() +set(ENABLE_CURVE OFF CACHE INTERNAL "" FORCE) +set(WITH_LIBSODIUM OFF CACHE INTERNAL "" FORCE) add_subdirectory(ZMQ) +if(ENABLE_DISCORD) + set(BUILD_TESTING OFF CACHE INTERNAL "" FORCE) + set(BUILD_VOICE_SUPPORT OFF CACHE INTERNAL "" FORCE) + set(DPP_INSTALL OFF CACHE INTERNAL "" FORCE) + set(DPP_BUILD_TEST OFF CACHE INTERNAL "" FORCE) + set(DPP_NO_VCPKG ON CACHE INTERNAL "" FORCE) + set(DPP_CORO OFF CACHE INTERNAL "" FORCE) + set(DPP_USE_EXTERNAL_JSON ON CACHE INTERNAL "" FORCE) + if (WIN32 OR MINGW) + set(BUILD_SHARED_LIBS ON CACHE INTERNAL "" FORCE) + endif() + add_subdirectory(DPP) + target_link_libraries(dpp PRIVATE nlohmann_json) + set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "" FORCE) + # We don't care about DPP warnings + if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") + target_compile_options(dpp PRIVATE -w) + elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + target_compile_options(dpp PRIVATE -w) + elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + target_compile_options(dpp PRIVATE /w) + endif() +endif() diff --git a/vendor/CPR/CMakeLists.txt b/vendor/CPR/CMakeLists.txt index dcf56b96..5c1a0c5c 100644 --- a/vendor/CPR/CMakeLists.txt +++ b/vendor/CPR/CMakeLists.txt @@ -1,52 +1,84 @@ # Create the CPR library add_library(CPR STATIC # Source files - cpr/auth.cpp - cpr/bearer.cpp - cpr/cookies.cpp - cpr/cprtypes.cpp - cpr/curl_container.cpp - cpr/curlholder.cpp cpr/error.cpp + cpr/file.cpp + cpr/interceptor.cpp cpr/multipart.cpp + cpr/multiperform.cpp cpr/parameters.cpp cpr/payload.cpp cpr/proxies.cpp cpr/proxyauth.cpp cpr/redirect.cpp + cpr/response.cpp cpr/session.cpp + cpr/ssl_ctx.cpp + cpr/threadpool.cpp cpr/timeout.cpp cpr/unix_socket.cpp cpr/util.cpp - cpr/response.cpp - # Header files - include/cpr/api.h - include/cpr/auth.h + cpr/accept_encoding.cpp + cpr/async.cpp + cpr/auth.cpp + cpr/bearer.cpp + cpr/callback.cpp + cpr/cert_info.cpp + cpr/cookies.cpp + cpr/cprtypes.cpp + cpr/curl_container.cpp + cpr/curlholder.cpp + cpr/curlmultiholder.cpp + # Header Files include/cpr/bearer.h include/cpr/body.h + include/cpr/buffer.h + include/cpr/callback.h + include/cpr/cert_info.h + include/cpr/connect_timeout.h include/cpr/cookies.h include/cpr/cpr.h include/cpr/cprtypes.h + include/cpr/curl_container.h include/cpr/curlholder.h - include/cpr/curlholder.h - include/cpr/digest.h + include/cpr/curlmultiholder.h include/cpr/error.h + include/cpr/file.h + include/cpr/filesystem.h + include/cpr/http_version.h + include/cpr/interceptor.h include/cpr/interface.h include/cpr/limit_rate.h + include/cpr/local_port.h + include/cpr/local_port_range.h + include/cpr/low_speed.h include/cpr/multipart.h - include/cpr/ntlm.h + include/cpr/multiperform.h include/cpr/parameters.h include/cpr/payload.h include/cpr/proxies.h include/cpr/proxyauth.h + include/cpr/range.h include/cpr/redirect.h + include/cpr/reserve_size.h + include/cpr/resolve.h include/cpr/response.h include/cpr/session.h + include/cpr/singleton.h + include/cpr/ssl_ctx.h include/cpr/ssl_options.h + include/cpr/status_codes.h + include/cpr/threadpool.h include/cpr/timeout.h include/cpr/unix_socket.h + include/cpr/user_agent.h include/cpr/util.h include/cpr/verbose.h + include/cpr/accept_encoding.h + include/cpr/api.h + include/cpr/async.h + include/cpr/async_wrapper.h + include/cpr/auth.h ) # Library includes target_include_directories(CPR PRIVATE ${CMAKE_CURRENT_LIST_DIR}/cpr) @@ -55,7 +87,7 @@ target_include_directories(CPR PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) if (NOT MSVC) target_compile_options(CPR PRIVATE -Wno-deprecated-declarations) endif() -# Find CURK +# Find CURL find_package(CURL REQUIRED) if (CURL_FOUND) set(SSL_ENABLED ON CACHE INTERNAL "" FORCE) diff --git a/vendor/CPR/cpr/accept_encoding.cpp b/vendor/CPR/cpr/accept_encoding.cpp new file mode 100644 index 00000000..b7576688 --- /dev/null +++ b/vendor/CPR/cpr/accept_encoding.cpp @@ -0,0 +1,37 @@ +#include "cpr/accept_encoding.h" + +#include +#include +#include +#include +#include +#include + +namespace cpr { + +AcceptEncoding::AcceptEncoding(const std::initializer_list& methods) { + methods_.clear(); + std::transform(methods.begin(), methods.end(), std::inserter(methods_, methods_.begin()), [&](cpr::AcceptEncodingMethods method) { return cpr::AcceptEncodingMethodsStringMap.at(method); }); +} + +AcceptEncoding::AcceptEncoding(const std::initializer_list& string_methods) : methods_{string_methods} {} + +bool AcceptEncoding::empty() const noexcept { + return methods_.empty(); +} + +const std::string AcceptEncoding::getString() const { + return std::accumulate(std::next(methods_.begin()), methods_.end(), *methods_.begin(), [](std::string a, std::string b) { return std::move(a) + ", " + std::move(b); }); +} + +[[nodiscard]] bool AcceptEncoding::disabled() const { + if (methods_.find(cpr::AcceptEncodingMethodsStringMap.at(AcceptEncodingMethods::disabled)) != methods_.end()) { + if (methods_.size() != 1) { + throw std::invalid_argument("AcceptEncoding does not accept any other values if 'disabled' is present. You set the following encodings: " + getString()); + } + return true; + } + return false; +} + +} // namespace cpr diff --git a/vendor/CPR/cpr/async.cpp b/vendor/CPR/cpr/async.cpp new file mode 100644 index 00000000..e10d09e1 --- /dev/null +++ b/vendor/CPR/cpr/async.cpp @@ -0,0 +1,8 @@ +#include "cpr/async.h" + +namespace cpr { + +// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables) +CPR_SINGLETON_IMPL(GlobalThreadPool) + +} // namespace cpr diff --git a/vendor/CPR/cpr/auth.cpp b/vendor/CPR/cpr/auth.cpp index 7d55d4b9..b3576f5c 100644 --- a/vendor/CPR/cpr/auth.cpp +++ b/vendor/CPR/cpr/auth.cpp @@ -1,7 +1,16 @@ #include "cpr/auth.h" +#include "cpr/util.h" namespace cpr { +Authentication::~Authentication() noexcept { + util::secureStringClear(auth_string_); +} + const char* Authentication::GetAuthString() const noexcept { return auth_string_.c_str(); } + +AuthMode Authentication::GetAuthMode() const noexcept { + return auth_mode_; +} } // namespace cpr diff --git a/vendor/CPR/cpr/bearer.cpp b/vendor/CPR/cpr/bearer.cpp index 5b7eb930..02bd728b 100644 --- a/vendor/CPR/cpr/bearer.cpp +++ b/vendor/CPR/cpr/bearer.cpp @@ -1,9 +1,14 @@ #include "cpr/bearer.h" +#include "cpr/util.h" namespace cpr { // Only supported with libcurl >= 7.61.0. // As an alternative use SetHeader and add the token manually. #if LIBCURL_VERSION_NUM >= 0x073D00 +Bearer::~Bearer() noexcept { + util::secureStringClear(token_string_); +} + const char* Bearer::GetToken() const noexcept { return token_string_.c_str(); } diff --git a/vendor/CPR/cpr/callback.cpp b/vendor/CPR/cpr/callback.cpp new file mode 100644 index 00000000..3af230b8 --- /dev/null +++ b/vendor/CPR/cpr/callback.cpp @@ -0,0 +1,14 @@ +#include +#include +#include + +namespace cpr { + +void CancellationCallback::SetProgressCallback(ProgressCallback& u_cb) { + user_cb.emplace(std::reference_wrapper{u_cb}); +} +bool CancellationCallback::operator()(cpr_pf_arg_t dltotal, cpr_pf_arg_t dlnow, cpr_pf_arg_t ultotal, cpr_pf_arg_t ulnow) const { + const bool cont_operation{!cancellation_state->load()}; + return user_cb ? (cont_operation && (*user_cb)(dltotal, dlnow, ultotal, ulnow)) : cont_operation; +} +} // namespace cpr diff --git a/vendor/CPR/cpr/cert_info.cpp b/vendor/CPR/cpr/cert_info.cpp new file mode 100644 index 00000000..a77a0277 --- /dev/null +++ b/vendor/CPR/cpr/cert_info.cpp @@ -0,0 +1,43 @@ +#include "cpr/cert_info.h" + +namespace cpr { + +std::string& CertInfo::operator[](const size_t& pos) { + return cert_info_[pos]; +} + +CertInfo::iterator CertInfo::begin() { + return cert_info_.begin(); +} +CertInfo::iterator CertInfo::end() { + return cert_info_.end(); +} + +CertInfo::const_iterator CertInfo::begin() const { + return cert_info_.begin(); +} + +CertInfo::const_iterator CertInfo::end() const { + return cert_info_.end(); +} + +CertInfo::const_iterator CertInfo::cbegin() const { + return cert_info_.cbegin(); +} + +CertInfo::const_iterator CertInfo::cend() const { + return cert_info_.cend(); +} + +void CertInfo::emplace_back(const std::string& str) { + cert_info_.emplace_back(str); +} + +void CertInfo::push_back(const std::string& str) { + cert_info_.push_back(str); +} + +void CertInfo::pop_back() { + cert_info_.pop_back(); +} +} // namespace cpr diff --git a/vendor/CPR/cpr/cookies.cpp b/vendor/CPR/cpr/cookies.cpp index 4bd58555..41e12469 100644 --- a/vendor/CPR/cpr/cookies.cpp +++ b/vendor/CPR/cpr/cookies.cpp @@ -1,51 +1,106 @@ #include "cpr/cookies.h" +#include +#include namespace cpr { -std::string Cookies::GetEncoded(const CurlHolder& holder) const { +const std::string Cookie::GetDomain() const { + return domain_; +} + +bool Cookie::IsIncludingSubdomains() const { + return includeSubdomains_; +} + +const std::string Cookie::GetPath() const { + return path_; +} + +bool Cookie::IsHttpsOnly() const { + return httpsOnly_; +} + +const std::chrono::system_clock::time_point Cookie::GetExpires() const { + return expires_; +} + +const std::string Cookie::GetExpiresString() const { + std::stringstream ss; + std::tm tm{}; + const std::time_t tt = std::chrono::system_clock::to_time_t(expires_); +#ifdef _WIN32 + gmtime_s(&tm, &tt); +#else + gmtime_r(&tt, &tm); +#endif + ss << std::put_time(&tm, "%a, %d %b %Y %H:%M:%S GMT"); + return ss.str(); +} + +const std::string Cookie::GetName() const { + return name_; +} + +const std::string Cookie::GetValue() const { + return value_; +} + +const std::string Cookies::GetEncoded(const CurlHolder& holder) const { std::stringstream stream; - for (const std::pair& item : map_) { + for (const cpr::Cookie& item : cookies_) { // Depending on if encoding is set to "true", we will URL-encode cookies - stream << (encode ? holder.urlEncode(item.first) : item.first) << "="; + stream << (encode ? holder.urlEncode(item.GetName()) : item.GetName()) << "="; // special case version 1 cookies, which can be distinguished by // beginning and trailing quotes - if (!item.second.empty() && item.second.front() == '"' && item.second.back() == '"') { - stream << item.second; + if (!item.GetValue().empty() && item.GetValue().front() == '"' && item.GetValue().back() == '"') { + stream << item.GetValue(); } else { // Depending on if encoding is set to "true", we will URL-encode cookies - stream << (encode ? holder.urlEncode(item.second) : item.second); + stream << (encode ? holder.urlEncode(item.GetValue()) : item.GetValue()); } stream << "; "; } return stream.str(); } -std::string& Cookies::operator[](const std::string& key) { - return map_[key]; +cpr::Cookie& Cookies::operator[](size_t pos) { + return cookies_[pos]; } Cookies::iterator Cookies::begin() { - return map_.begin(); + return cookies_.begin(); } Cookies::iterator Cookies::end() { - return map_.end(); + return cookies_.end(); } Cookies::const_iterator Cookies::begin() const { - return map_.begin(); + return cookies_.begin(); } Cookies::const_iterator Cookies::end() const { - return map_.end(); + return cookies_.end(); } Cookies::const_iterator Cookies::cbegin() const { - return map_.cbegin(); + return cookies_.cbegin(); } Cookies::const_iterator Cookies::cend() const { - return map_.cend(); + return cookies_.cend(); +} + +void Cookies::emplace_back(const Cookie& str) { + cookies_.emplace_back(str); +} + +void Cookies::push_back(const Cookie& str) { + cookies_.push_back(str); +} + +void Cookies::pop_back() { + cookies_.pop_back(); } } // namespace cpr diff --git a/vendor/CPR/cpr/cprtypes.cpp b/vendor/CPR/cpr/cprtypes.cpp index c618b3f1..7927b03b 100644 --- a/vendor/CPR/cpr/cprtypes.cpp +++ b/vendor/CPR/cpr/cprtypes.cpp @@ -5,8 +5,6 @@ namespace cpr { bool CaseInsensitiveCompare::operator()(const std::string& a, const std::string& b) const noexcept { - return std::lexicographical_compare( - a.begin(), a.end(), b.begin(), b.end(), - [](unsigned char ac, unsigned char bc) { return std::tolower(ac) < std::tolower(bc); }); + return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end(), [](unsigned char ac, unsigned char bc) { return std::tolower(ac) < std::tolower(bc); }); } } // namespace cpr diff --git a/vendor/CPR/cpr/curl_container.cpp b/vendor/CPR/cpr/curl_container.cpp index 12764110..c90b777a 100644 --- a/vendor/CPR/cpr/curl_container.cpp +++ b/vendor/CPR/cpr/curl_container.cpp @@ -1,16 +1,15 @@ #include "cpr/curl_container.h" +#include +#include namespace cpr { template -CurlContainer::CurlContainer(const std::initializer_list& containerList) - : containerList_(containerList) {} +CurlContainer::CurlContainer(const std::initializer_list& containerList) : containerList_(containerList) {} template void CurlContainer::Add(const std::initializer_list& containerList) { - for (const T& element : containerList) { - containerList_.push_back(std::move(element)); - } + std::transform(containerList.begin(), containerList.end(), std::back_inserter(containerList_), [](const T& elem) { return std::move(elem); }); } template @@ -26,15 +25,15 @@ const std::string CurlContainer::GetContent(const CurlHolder& holder) content += "&"; } - std::string escapedKey = encode ? holder.urlEncode(parameter.key) : parameter.key; + const std::string escapedKey = encode ? holder.urlEncode(parameter.key) : parameter.key; if (parameter.value.empty()) { content += escapedKey; } else { - std::string escapedValue = encode ? holder.urlEncode(parameter.value) : parameter.value; + const std::string escapedValue = encode ? holder.urlEncode(parameter.value) : parameter.value; content += escapedKey + "="; content += escapedValue; } - }; + } return content; } @@ -46,7 +45,7 @@ const std::string CurlContainer::GetContent(const CurlHolder& holder) cons if (!content.empty()) { content += "&"; } - std::string escaped = encode ? holder.urlEncode(element.value) : element.value; + const std::string escaped = encode ? holder.urlEncode(element.value) : element.value; content += element.key + "=" + escaped; } diff --git a/vendor/CPR/cpr/curlholder.cpp b/vendor/CPR/cpr/curlholder.cpp index 4596ed7f..0dcd6d76 100644 --- a/vendor/CPR/cpr/curlholder.cpp +++ b/vendor/CPR/cpr/curlholder.cpp @@ -2,10 +2,6 @@ #include namespace cpr { -// It does not make sense to make a std::mutex const. -// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables) -std::mutex CurlHolder::curl_easy_init_mutex_{}; - CurlHolder::CurlHolder() { /** * Allow multithreaded access to CPR by locking curl_easy_init(). @@ -14,22 +10,24 @@ CurlHolder::CurlHolder() { * https://curl.haxx.se/libcurl/c/curl_easy_init.html * https://curl.haxx.se/libcurl/c/threadsafe.html **/ - curl_easy_init_mutex_.lock(); + curl_easy_init_mutex_().lock(); + // NOLINTNEXTLINE (cppcoreguidelines-prefer-member-initializer) since we need it to happen inside the lock handle = curl_easy_init(); - curl_easy_init_mutex_.unlock(); + curl_easy_init_mutex_().unlock(); assert(handle); } // namespace cpr CurlHolder::~CurlHolder() { curl_slist_free_all(chunk); - curl_formfree(formpost); + curl_slist_free_all(resolveCurlList); + curl_mime_free(multipart); curl_easy_cleanup(handle); } std::string CurlHolder::urlEncode(const std::string& s) const { assert(handle); - char* output = curl_easy_escape(handle, s.c_str(), s.length()); + char* output = curl_easy_escape(handle, s.c_str(), static_cast(s.length())); if (output) { std::string result = output; curl_free(output); @@ -40,7 +38,7 @@ std::string CurlHolder::urlEncode(const std::string& s) const { std::string CurlHolder::urlDecode(const std::string& s) const { assert(handle); - char* output = curl_easy_unescape(handle, s.c_str(), s.length(), nullptr); + char* output = curl_easy_unescape(handle, s.c_str(), static_cast(s.length()), nullptr); if (output) { std::string result = output; curl_free(output); diff --git a/vendor/CPR/cpr/curlmultiholder.cpp b/vendor/CPR/cpr/curlmultiholder.cpp new file mode 100644 index 00000000..efe86ab2 --- /dev/null +++ b/vendor/CPR/cpr/curlmultiholder.cpp @@ -0,0 +1,15 @@ +#include "cpr/curlmultiholder.h" + +#include + +namespace cpr { + +CurlMultiHolder::CurlMultiHolder() : handle{curl_multi_init()} { + assert(handle); +} + +CurlMultiHolder::~CurlMultiHolder() { + curl_multi_cleanup(handle); +} + +} // namespace cpr diff --git a/vendor/CPR/cpr/error.cpp b/vendor/CPR/cpr/error.cpp index 7e713fd1..f085051f 100644 --- a/vendor/CPR/cpr/error.cpp +++ b/vendor/CPR/cpr/error.cpp @@ -59,7 +59,7 @@ ErrorCode Error::getErrorCodeForCurlError(std::int32_t curl_code) { case CURLE_SSL_ISSUER_ERROR: return ErrorCode::SSL_CACERT_ERROR; case CURLE_TOO_MANY_REDIRECTS: - return ErrorCode::OK; + return ErrorCode::TOO_MANY_REDIRECTS; default: return ErrorCode::INTERNAL_ERROR; } diff --git a/vendor/CPR/cpr/file.cpp b/vendor/CPR/cpr/file.cpp new file mode 100644 index 00000000..eadb3774 --- /dev/null +++ b/vendor/CPR/cpr/file.cpp @@ -0,0 +1,60 @@ +#include "cpr/file.h" + +namespace cpr { + +Files::Files(const std::initializer_list& p_filepaths) { + for (const std::string& filepath : p_filepaths) { + files.emplace_back(filepath); + } +} + +Files::iterator Files::begin() { + return files.begin(); +} + +Files::iterator Files::end() { + return files.end(); +} + +Files::const_iterator Files::begin() const { + return files.begin(); +} + +Files::const_iterator Files::end() const { + return files.end(); +} + +Files::const_iterator Files::cbegin() const { + return files.cbegin(); +} + +Files::const_iterator Files::cend() const { + return files.cend(); +} + +void Files::emplace_back(const File& file) { + files.emplace_back(file); +} + +void Files::push_back(const File& file) { + files.push_back(file); +} + +void Files::pop_back() { + files.pop_back(); +} + +Files& Files::operator=(const Files& other) { + if (&other != this) { + files = other.files; + } + return *this; +} + +Files& Files::operator=(Files&& old) noexcept { + if (&old != this) { + files = std::move(old.files); + } + return *this; +} +} // namespace cpr diff --git a/vendor/CPR/cpr/interceptor.cpp b/vendor/CPR/cpr/interceptor.cpp new file mode 100644 index 00000000..92ed80fb --- /dev/null +++ b/vendor/CPR/cpr/interceptor.cpp @@ -0,0 +1,53 @@ +#include "cpr/interceptor.h" + +#include + +namespace cpr { + +Response Interceptor::proceed(Session& session) { + return session.proceed(); +} + +Response Interceptor::proceed(Session& session, ProceedHttpMethod httpMethod) { + switch (httpMethod) { + case ProceedHttpMethod::DELETE_REQUEST: + return session.Delete(); + case ProceedHttpMethod::GET_REQUEST: + return session.Get(); + case ProceedHttpMethod::HEAD_REQUEST: + return session.Head(); + case ProceedHttpMethod::OPTIONS_REQUEST: + return session.Options(); + case ProceedHttpMethod::PATCH_REQUEST: + return session.Patch(); + case ProceedHttpMethod::POST_REQUEST: + return session.Post(); + case ProceedHttpMethod::PUT_REQUEST: + return session.Put(); + default: + throw std::invalid_argument{"Can't proceed the session with the provided http method!"}; + } +} + +Response Interceptor::proceed(Session& session, ProceedHttpMethod httpMethod, std::ofstream& file) { + if (httpMethod == ProceedHttpMethod::DOWNLOAD_FILE_REQUEST) { + return session.Download(file); + } + throw std::invalid_argument{"std::ofstream argument is only valid for ProceedHttpMethod::DOWNLOAD_FILE!"}; +} + +Response Interceptor::proceed(Session& session, ProceedHttpMethod httpMethod, const WriteCallback& write) { + if (httpMethod == ProceedHttpMethod::DOWNLOAD_CALLBACK_REQUEST) { + return session.Download(write); + } + throw std::invalid_argument{"WriteCallback argument is only valid for ProceedHttpMethod::DOWNLOAD_CALLBACK!"}; +} + +std::vector InterceptorMulti::proceed(MultiPerform& multi) { + return multi.proceed(); +} + +void InterceptorMulti::PrepareDownloadSession(MultiPerform& multi, size_t sessions_index, const WriteCallback& write) { + multi.PrepareDownloadSessions(sessions_index, write); +} +} // namespace cpr diff --git a/vendor/CPR/cpr/multipart.cpp b/vendor/CPR/cpr/multipart.cpp index d82d9a41..81854f9f 100644 --- a/vendor/CPR/cpr/multipart.cpp +++ b/vendor/CPR/cpr/multipart.cpp @@ -2,4 +2,6 @@ namespace cpr { Multipart::Multipart(const std::initializer_list& p_parts) : parts{p_parts} {} +Multipart::Multipart(const std::vector& p_parts) : parts{p_parts} {} +Multipart::Multipart(const std::vector&& p_parts) : parts{p_parts} {} } // namespace cpr diff --git a/vendor/CPR/cpr/multiperform.cpp b/vendor/CPR/cpr/multiperform.cpp new file mode 100644 index 00000000..051f2d68 --- /dev/null +++ b/vendor/CPR/cpr/multiperform.cpp @@ -0,0 +1,323 @@ +#include "cpr/multiperform.h" + +#include "cpr/interceptor.h" +#include "cpr/response.h" +#include "cpr/session.h" +#include +#include +#include +#include + +namespace cpr { + +MultiPerform::MultiPerform() : multicurl_(new CurlMultiHolder()) {} + +MultiPerform::~MultiPerform() { + // Unlock all sessions + for (const std::pair, HttpMethod>& pair : sessions_) { + pair.first->isUsedInMultiPerform = false; + } +} + +void MultiPerform::AddSession(std::shared_ptr& session, HttpMethod method) { + // Check if this multiperform is download only + if (((method != HttpMethod::DOWNLOAD_REQUEST && is_download_multi_perform) && method != HttpMethod::UNDEFINED) || (method == HttpMethod::DOWNLOAD_REQUEST && !is_download_multi_perform && !sessions_.empty())) { + // Currently it is not possible to mix download and non-download methods, as download needs additional parameters + throw std::invalid_argument("Failed to add session: Cannot mix download and non-download methods!"); + } + + // Set download only if neccessary + if (method == HttpMethod::DOWNLOAD_REQUEST) { + is_download_multi_perform = true; + } + + // Add easy handle to multi handle + const CURLMcode error_code = curl_multi_add_handle(multicurl_->handle, session->curl_->handle); + if (error_code) { + std::cerr << "curl_multi_add_handle() failed, code " << static_cast(error_code) << std::endl; + return; + } + + // Lock session to the multihandle + session->isUsedInMultiPerform = true; + + // Add session to sessions_ + sessions_.emplace_back(session, method); +} + +void MultiPerform::RemoveSession(const std::shared_ptr& session) { + // Remove easy handle from multihandle + const CURLMcode error_code = curl_multi_remove_handle(multicurl_->handle, session->curl_->handle); + if (error_code) { + std::cerr << "curl_multi_remove_handle() failed, code " << static_cast(error_code) << std::endl; + return; + } + + // Unock session + session->isUsedInMultiPerform = false; + + // Remove session from sessions_ + auto it = std::find_if(sessions_.begin(), sessions_.end(), [&session](const std::pair, HttpMethod>& pair) { return session->curl_->handle == pair.first->curl_->handle; }); + if (it == sessions_.end()) { + throw std::invalid_argument("Failed to find session!"); + } + sessions_.erase(it); + + // Reset download only if empty + if (sessions_.empty()) { + is_download_multi_perform = false; + } +} + +std::vector, MultiPerform::HttpMethod>>& MultiPerform::GetSessions() { + return sessions_; +} + +const std::vector, MultiPerform::HttpMethod>>& MultiPerform::GetSessions() const { + return sessions_; +} + +void MultiPerform::DoMultiPerform() { + // Do multi perform until every handle has finished + int still_running{0}; + do { + CURLMcode error_code = curl_multi_perform(multicurl_->handle, &still_running); + if (error_code) { + std::cerr << "curl_multi_perform() failed, code " << static_cast(error_code) << std::endl; + break; + } + + if (still_running) { + const int timeout_ms{250}; + error_code = curl_multi_poll(multicurl_->handle, nullptr, 0, timeout_ms, nullptr); + if (error_code) { + std::cerr << "curl_multi_poll() failed, code " << static_cast(error_code) << std::endl; + break; + } + } + } while (still_running); +} + +std::vector MultiPerform::ReadMultiInfo(std::function&& complete_function) { + // Get infos and create Response objects + std::vector responses; + struct CURLMsg* info{nullptr}; + do { + int msgq = 0; + + // Read info from multihandle + info = curl_multi_info_read(multicurl_->handle, &msgq); + + if (info) { + // Find current session + auto it = std::find_if(sessions_.begin(), sessions_.end(), [&info](const std::pair, HttpMethod>& pair) { return pair.first->curl_->handle == info->easy_handle; }); + if (it == sessions_.end()) { + std::cerr << "Failed to find current session!" << std::endl; + break; + } + const std::shared_ptr current_session = (*it).first; + + // Add response object + // NOLINTNEXTLINE (cppcoreguidelines-pro-type-union-access) + responses.push_back(complete_function(*current_session, info->data.result)); + } + } while (info); + + // Sort response objects to match order of added sessions + std::vector sorted_responses; + for (const std::pair, HttpMethod>& pair : sessions_) { + Session& current_session = *(pair.first); + auto it = std::find_if(responses.begin(), responses.end(), [¤t_session](const Response& response) { return current_session.curl_->handle == response.curl_->handle; }); + const Response current_response = *it; + // Erase response from original vector to increase future search speed + responses.erase(it); + sorted_responses.push_back(current_response); + } + + return sorted_responses; +} + +std::vector MultiPerform::MakeRequest() { + if (!interceptors_.empty()) { + return intercept(); + } + + DoMultiPerform(); + return ReadMultiInfo([](Session& session, CURLcode curl_error) -> Response { return session.Complete(curl_error); }); +} + +std::vector MultiPerform::MakeDownloadRequest() { + if (!interceptors_.empty()) { + return intercept(); + } + + DoMultiPerform(); + return ReadMultiInfo([](Session& session, CURLcode curl_error) -> Response { return session.CompleteDownload(curl_error); }); +} + +void MultiPerform::PrepareSessions() { + for (const std::pair, HttpMethod>& pair : sessions_) { + switch (pair.second) { + case HttpMethod::GET_REQUEST: + pair.first->PrepareGet(); + break; + case HttpMethod::POST_REQUEST: + pair.first->PreparePost(); + break; + case HttpMethod::PUT_REQUEST: + pair.first->PreparePut(); + break; + case HttpMethod::DELETE_REQUEST: + pair.first->PrepareDelete(); + break; + case HttpMethod::PATCH_REQUEST: + pair.first->PreparePatch(); + break; + case HttpMethod::HEAD_REQUEST: + pair.first->PrepareHead(); + break; + case HttpMethod::OPTIONS_REQUEST: + pair.first->PrepareOptions(); + break; + default: + std::cerr << "PrepareSessions failed: Undefined HttpMethod or download without arguments!" << std::endl; + return; + } + } +} + +void MultiPerform::PrepareDownloadSession(size_t sessions_index, const WriteCallback& write) { + const std::pair, HttpMethod>& pair = sessions_[sessions_index]; + switch (pair.second) { + case HttpMethod::DOWNLOAD_REQUEST: + pair.first->PrepareDownload(write); + break; + default: + std::cerr << "PrepareSessions failed: Undefined HttpMethod or non download method with arguments!" << std::endl; + return; + } +} + +void MultiPerform::PrepareDownloadSession(size_t sessions_index, std::ofstream& file) { + const std::pair, HttpMethod>& pair = sessions_[sessions_index]; + switch (pair.second) { + case HttpMethod::DOWNLOAD_REQUEST: + pair.first->PrepareDownload(file); + break; + default: + std::cerr << "PrepareSessions failed: Undefined HttpMethod or non download method with arguments!" << std::endl; + return; + } +} + +void MultiPerform::SetHttpMethod(HttpMethod method) { + for (std::pair, HttpMethod>& pair : sessions_) { + pair.second = method; + } +} + +void MultiPerform::PrepareGet() { + SetHttpMethod(HttpMethod::GET_REQUEST); + PrepareSessions(); +} + +void MultiPerform::PrepareDelete() { + SetHttpMethod(HttpMethod::DELETE_REQUEST); + PrepareSessions(); +} + +void MultiPerform::PreparePut() { + SetHttpMethod(HttpMethod::PUT_REQUEST); + PrepareSessions(); +} + +void MultiPerform::PreparePatch() { + SetHttpMethod(HttpMethod::PATCH_REQUEST); + PrepareSessions(); +} + +void MultiPerform::PrepareHead() { + SetHttpMethod(HttpMethod::HEAD_REQUEST); + PrepareSessions(); +} + +void MultiPerform::PrepareOptions() { + SetHttpMethod(HttpMethod::OPTIONS_REQUEST); + PrepareSessions(); +} + +void MultiPerform::PreparePost() { + SetHttpMethod(HttpMethod::POST_REQUEST); + PrepareSessions(); +} + +std::vector MultiPerform::Get() { + PrepareGet(); + return MakeRequest(); +} + +std::vector MultiPerform::Delete() { + PrepareDelete(); + return MakeRequest(); +} + +std::vector MultiPerform::Put() { + PreparePut(); + return MakeRequest(); +} + +std::vector MultiPerform::Head() { + PrepareHead(); + return MakeRequest(); +} + +std::vector MultiPerform::Options() { + PrepareOptions(); + return MakeRequest(); +} + +std::vector MultiPerform::Patch() { + PreparePatch(); + return MakeRequest(); +} + +std::vector MultiPerform::Post() { + PreparePost(); + return MakeRequest(); +} + +std::vector MultiPerform::Perform() { + PrepareSessions(); + return MakeRequest(); +} + +std::vector MultiPerform::proceed() { + // Check if this multiperform mixes download and non download requests + if (!sessions_.empty()) { + const bool new_is_download_multi_perform = sessions_.front().second == HttpMethod::DOWNLOAD_REQUEST; + + for (const std::pair, HttpMethod>& s : sessions_) { + const HttpMethod method = s.second; + if ((new_is_download_multi_perform && method != HttpMethod::DOWNLOAD_REQUEST) || (!new_is_download_multi_perform && method == HttpMethod::DOWNLOAD_REQUEST)) { + throw std::invalid_argument("Failed to proceed with session: Cannot mix download and non-download methods!"); + } + } + is_download_multi_perform = new_is_download_multi_perform; + } + + PrepareSessions(); + return MakeRequest(); +} + +std::vector MultiPerform::intercept() { + // At least one interceptor exists -> Execute its intercept function + const std::shared_ptr interceptor = interceptors_.front(); + interceptors_.pop(); + return interceptor->intercept(*this); +} + +void MultiPerform::AddInterceptor(const std::shared_ptr& pinterceptor) { + interceptors_.push(pinterceptor); +} + +} // namespace cpr diff --git a/vendor/CPR/cpr/parameters.cpp b/vendor/CPR/cpr/parameters.cpp index a24c3936..ed62d2ba 100644 --- a/vendor/CPR/cpr/parameters.cpp +++ b/vendor/CPR/cpr/parameters.cpp @@ -1,10 +1,4 @@ #include "cpr/parameters.h" -#include -#include - -#include "cpr/util.h" - namespace cpr { -Parameters::Parameters(const std::initializer_list& parameters) : CurlContainer(parameters) {} } // namespace cpr diff --git a/vendor/CPR/cpr/payload.cpp b/vendor/CPR/cpr/payload.cpp index 78373fa3..c3265c1a 100644 --- a/vendor/CPR/cpr/payload.cpp +++ b/vendor/CPR/cpr/payload.cpp @@ -1,10 +1,4 @@ #include "cpr/payload.h" -#include -#include - -#include "cpr/util.h" - namespace cpr { -Payload::Payload(const std::initializer_list& pairs) : CurlContainer(pairs) {} } // namespace cpr diff --git a/vendor/CPR/cpr/proxies.cpp b/vendor/CPR/cpr/proxies.cpp index 446f7d76..0d3fe989 100644 --- a/vendor/CPR/cpr/proxies.cpp +++ b/vendor/CPR/cpr/proxies.cpp @@ -7,8 +7,8 @@ namespace cpr { -Proxies::Proxies(const std::initializer_list>& hosts) - : hosts_{hosts} {} +Proxies::Proxies(const std::initializer_list>& hosts) : hosts_{hosts} {} +Proxies::Proxies(const std::map& hosts) : hosts_{hosts} {} bool Proxies::has(const std::string& protocol) const { return hosts_.count(protocol) > 0; diff --git a/vendor/CPR/cpr/proxyauth.cpp b/vendor/CPR/cpr/proxyauth.cpp index 47d914de..bfb0f555 100644 --- a/vendor/CPR/cpr/proxyauth.cpp +++ b/vendor/CPR/cpr/proxyauth.cpp @@ -1,16 +1,30 @@ #include "cpr/proxyauth.h" +#include "cpr/util.h" namespace cpr { -const char* EncodedAuthentication::GetAuthString() const noexcept { - return auth_string_.c_str(); +EncodedAuthentication::~EncodedAuthentication() noexcept { + util::secureStringClear(username); + util::secureStringClear(password); +} + +const std::string& EncodedAuthentication::GetUsername() const { + return username; +} + +const std::string& EncodedAuthentication::GetPassword() const { + return password; } bool ProxyAuthentication::has(const std::string& protocol) const { return proxyAuth_.count(protocol) > 0; } -const char* ProxyAuthentication::operator[](const std::string& protocol) { - return proxyAuth_[protocol].GetAuthString(); +const char* ProxyAuthentication::GetUsername(const std::string& protocol) { + return proxyAuth_[protocol].username.c_str(); +} + +const char* ProxyAuthentication::GetPassword(const std::string& protocol) { + return proxyAuth_[protocol].password.c_str(); } } // namespace cpr diff --git a/vendor/CPR/cpr/redirect.cpp b/vendor/CPR/cpr/redirect.cpp index fade1304..f95dc756 100644 --- a/vendor/CPR/cpr/redirect.cpp +++ b/vendor/CPR/cpr/redirect.cpp @@ -19,7 +19,7 @@ PostRedirectFlags operator~(PostRedirectFlags flag) { PostRedirectFlags& operator|=(PostRedirectFlags& lhs, PostRedirectFlags rhs) { lhs = static_cast(static_cast(lhs) | static_cast(rhs)); - uint8_t tmp = static_cast(lhs); + const uint8_t tmp = static_cast(lhs); lhs = static_cast(tmp); return lhs; } diff --git a/vendor/CPR/cpr/response.cpp b/vendor/CPR/cpr/response.cpp index 366bff5e..c9c73a24 100644 --- a/vendor/CPR/cpr/response.cpp +++ b/vendor/CPR/cpr/response.cpp @@ -1,12 +1,9 @@ #include "cpr/response.h" namespace cpr { -Response::Response(std::shared_ptr curl, std::string&& p_text, - std::string&& p_header_string, Cookies&& p_cookies = Cookies{}, - Error&& p_error = Error{}) - : curl_(std::move(curl)), text(std::move(p_text)), cookies(std::move(p_cookies)), - error(std::move(p_error)) { - header = cpr::util::parseHeader(p_header_string, &status_line, &reason); + +Response::Response(std::shared_ptr curl, std::string&& p_text, std::string&& p_header_string, Cookies&& p_cookies = Cookies{}, Error&& p_error = Error{}) : curl_(std::move(curl)), text(std::move(p_text)), cookies(std::move(p_cookies)), error(std::move(p_error)), raw_header(std::move(p_header_string)) { + header = cpr::util::parseHeader(raw_header, &status_line, &reason); assert(curl_); assert(curl_->handle); curl_easy_getinfo(curl_->handle, CURLINFO_RESPONSE_CODE, &status_code); @@ -14,7 +11,7 @@ Response::Response(std::shared_ptr curl, std::string&& p_text, char* url_string{nullptr}; curl_easy_getinfo(curl_->handle, CURLINFO_EFFECTIVE_URL, &url_string); url = Url(url_string); -#if LIBCURL_VERSION_NUM >= 0x073700 +#if LIBCURL_VERSION_NUM >= 0x073700 // 7.55.0 curl_easy_getinfo(curl_->handle, CURLINFO_SIZE_DOWNLOAD_T, &downloaded_bytes); curl_easy_getinfo(curl_->handle, CURLINFO_SIZE_UPLOAD_T, &uploaded_bytes); #else @@ -27,20 +24,21 @@ Response::Response(std::shared_ptr curl, std::string&& p_text, curl_easy_getinfo(curl_->handle, CURLINFO_REDIRECT_COUNT, &redirect_count); } -std::vector Response::GetCertInfo() { +std::vector Response::GetCertInfos() { assert(curl_); assert(curl_->handle); curl_certinfo* ci{nullptr}; curl_easy_getinfo(curl_->handle, CURLINFO_CERTINFO, &ci); - std::vector info; - info.resize(ci->num_of_certs); + std::vector cert_infos; for (int i = 0; i < ci->num_of_certs; i++) { - // No way around here. + CertInfo cert_info; // NOLINTNEXTLINE (cppcoreguidelines-pro-bounds-pointer-arithmetic) - info[i] = std::string{ci->certinfo[i]->data}; + for (curl_slist* slist = ci->certinfo[i]; slist; slist = slist->next) { + cert_info.emplace_back(std::string{slist->data}); + } + cert_infos.emplace_back(cert_info); } - - return info; + return cert_infos; } } // namespace cpr diff --git a/vendor/CPR/cpr/session.cpp b/vendor/CPR/cpr/session.cpp index 1994ecb6..2b4bdcc4 100644 --- a/vendor/CPR/cpr/session.cpp +++ b/vendor/CPR/cpr/session.cpp @@ -2,18 +2,26 @@ #include #include +#include #include #include +#include +#include #include #include +#include "cpr/async.h" #include "cpr/cprtypes.h" +#include "cpr/interceptor.h" #include "cpr/util.h" +#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION +#include "cpr/ssl_ctx.h" +#endif + namespace cpr { - // Ignored here since libcurl reqires a long: // NOLINTNEXTLINE(google-runtime-int) constexpr long ON = 1L; @@ -21,137 +29,18 @@ constexpr long ON = 1L; // NOLINTNEXTLINE(google-runtime-int) constexpr long OFF = 0L; -class Session::Impl { - public: - Impl(); - - void SetUrl(const Url& url); - void SetParameters(const Parameters& parameters); - void SetParameters(Parameters&& parameters); - void SetHeader(const Header& header); - void UpdateHeader(const Header& header); - void SetTimeout(const Timeout& timeout); - void SetConnectTimeout(const ConnectTimeout& timeout); - void SetAuth(const Authentication& auth); -// Only supported with libcurl >= 7.61.0. -// As an alternative use SetHeader and add the token manually. -#if LIBCURL_VERSION_NUM >= 0x073D00 - void SetBearer(const Bearer& token); -#endif - void SetDigest(const Digest& auth); - void SetUserAgent(const UserAgent& ua); - void SetPayload(Payload&& payload); - void SetPayload(const Payload& payload); - void SetProxies(Proxies&& proxies); - void SetProxies(const Proxies& proxies); - void SetProxyAuth(ProxyAuthentication&& proxy_auth); - void SetProxyAuth(const ProxyAuthentication& proxy_auth); - void SetMultipart(Multipart&& multipart); - void SetMultipart(const Multipart& multipart); - void SetNTLM(const NTLM& auth); - void SetRedirect(const Redirect& redirect); - void SetCookies(const Cookies& cookies); - void SetBody(Body&& body); - void SetBody(const Body& body); - void SetReadCallback(const ReadCallback& read); - void SetHeaderCallback(const HeaderCallback& header); - void SetWriteCallback(const WriteCallback& write); - void SetProgressCallback(const ProgressCallback& progress); - void SetDebugCallback(const DebugCallback& debug); - void SetLowSpeed(const LowSpeed& low_speed); - void SetVerifySsl(const VerifySsl& verify); - void SetLimitRate(const LimitRate& limit_rate); - void SetUnixSocket(const UnixSocket& unix_socket); - void SetVerbose(const Verbose& verbose); - void SetSslOptions(const SslOptions& options); - void SetInterface(const Interface& iface); - - cpr_off_t GetDownloadFileLength(); - Response Delete(); - Response Download(const WriteCallback& write); - Response Download(std::ofstream& file); - Response Get(); - Response Head(); - Response Options(); - Response Patch(); - Response Post(); - Response Put(); - - std::shared_ptr GetCurlHolder(); - - void PrepareDelete(); - void PrepareGet(); - void PrepareHead(); - void PrepareOptions(); - void PreparePatch(); - void PreparePost(); - void PreparePut(); - Response Complete(CURLcode curl_error); - - private: - void SetHeaderInternal(); - bool hasBodyOrPayload_{false}; - - std::shared_ptr curl_; - Url url_; - Parameters parameters_; - Proxies proxies_; - ProxyAuthentication proxyAuth_; - Header header_; - /** - * Will be set by the read callback. - * Ensures that the "Transfer-Encoding" is set to "chunked", if not overriden in header_. - **/ - bool chunkedTransferEncoding{false}; - - ReadCallback readcb_; - HeaderCallback headercb_; - WriteCallback writecb_; - ProgressCallback progresscb_; - DebugCallback debugcb_; - std::string response_string_; - std::string header_string_; - - Response makeDownloadRequest(); - Response makeRequest(); - void prepareCommon(); -}; - -Session::Impl::Impl() : curl_(new CurlHolder()) { - // Set up some sensible defaults - curl_version_info_data* version_info = curl_version_info(CURLVERSION_NOW); - std::string version = "curl/" + std::string{version_info->version}; - curl_easy_setopt(curl_->handle, CURLOPT_USERAGENT, version.c_str()); - SetRedirect(Redirect()); - curl_easy_setopt(curl_->handle, CURLOPT_NOPROGRESS, 1L); - curl_easy_setopt(curl_->handle, CURLOPT_ERRORBUFFER, curl_->error.data()); - curl_easy_setopt(curl_->handle, CURLOPT_COOKIEFILE, ""); -#ifdef CPR_CURL_NOSIGNAL - curl_easy_setopt(curl_->handle, CURLOPT_NOSIGNAL, 1L); -#endif - -#if LIBCURL_VERSION_MAJOR >= 7 -#if LIBCURL_VERSION_MINOR >= 25 -#if LIBCURL_VERSION_PATCH >= 0 - curl_easy_setopt(curl_->handle, CURLOPT_TCP_KEEPALIVE, 1L); -#endif -#endif -#endif +CURLcode Session::DoEasyPerform() { + if (isUsedInMultiPerform) { + std::cerr << "curl_easy_perform cannot be executed if the CURL handle is used in a MultiPerform." << std::endl; + return CURLcode::CURLE_FAILED_INIT; + } + puts("about to do easy perform..."); + auto r = curl_easy_perform(curl_->handle); + puts("done with easy perform..."); + return r; } -void Session::Impl::SetUrl(const Url& url) { - url_ = url; -} - -void Session::Impl::SetParameters(const Parameters& parameters) { - parameters_ = parameters; -} - -void Session::Impl::SetParameters(Parameters&& parameters) { - parameters_ = std::move(parameters); -} - -void Session::Impl::SetHeaderInternal() { +void Session::SetHeaderInternal() { curl_slist* chunk = nullptr; for (const std::pair& item : header_) { std::string header_string = item.first; @@ -168,181 +57,381 @@ void Session::Impl::SetHeaderInternal() { } // Set the chunked transfer encoding in case it does not already exist: - if (chunkedTransferEncoding && header_.find("Transfer-Encoding") == header_.end()) { + if (chunkedTransferEncoding_ && header_.find("Transfer-Encoding") == header_.end()) { curl_slist* temp = curl_slist_append(chunk, "Transfer-Encoding:chunked"); if (temp) { chunk = temp; } } + // libcurl would prepare the header "Expect: 100-continue" by default when uploading files larger than 1 MB. + // Here we would like to disable this feature: + curl_slist* temp = curl_slist_append(chunk, "Expect:"); + if (temp) { + chunk = temp; + } + curl_easy_setopt(curl_->handle, CURLOPT_HTTPHEADER, chunk); curl_slist_free_all(curl_->chunk); curl_->chunk = chunk; } -void Session::Impl::SetHeader(const Header& header) { - header_ = header; -} - -void Session::Impl::UpdateHeader(const Header& header) { - for (const std::pair& item : header) { - header_[item.first] = item.second; - } -} - -void Session::Impl::SetTimeout(const Timeout& timeout) { - curl_easy_setopt(curl_->handle, CURLOPT_TIMEOUT_MS, timeout.Milliseconds()); -} - -void Session::Impl::SetConnectTimeout(const ConnectTimeout& timeout) { - curl_easy_setopt(curl_->handle, CURLOPT_CONNECTTIMEOUT_MS, timeout.Milliseconds()); -} - -void Session::Impl::SetVerbose(const Verbose& verbose) { - curl_easy_setopt(curl_->handle, CURLOPT_VERBOSE, verbose.verbose ? ON : OFF); -} - -void Session::Impl::SetAuth(const Authentication& auth) { - // Ignore here since this has been defined by libcurl. - curl_easy_setopt(curl_->handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); - curl_easy_setopt(curl_->handle, CURLOPT_USERPWD, auth.GetAuthString()); -} - -void Session::Impl::SetInterface(const Interface& iface) { - if (iface.str().empty()) { - curl_easy_setopt(curl_->handle, CURLOPT_INTERFACE, nullptr); - } else { - curl_easy_setopt(curl_->handle, CURLOPT_INTERFACE, iface.c_str()); - } -} - // Only supported with libcurl >= 7.61.0. // As an alternative use SetHeader and add the token manually. #if LIBCURL_VERSION_NUM >= 0x073D00 -void Session::Impl::SetBearer(const Bearer& token) { +void Session::SetBearer(const Bearer& token) { // Ignore here since this has been defined by libcurl. curl_easy_setopt(curl_->handle, CURLOPT_HTTPAUTH, CURLAUTH_BEARER); curl_easy_setopt(curl_->handle, CURLOPT_XOAUTH2_BEARER, token.GetToken()); } #endif -void Session::Impl::SetDigest(const Digest& auth) { - // Ignore here since this has been defined by libcurl. - curl_easy_setopt(curl_->handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); - curl_easy_setopt(curl_->handle, CURLOPT_USERPWD, auth.GetAuthString()); +Session::Session() : curl_(new CurlHolder()) { + // Set up some sensible defaults + curl_version_info_data* version_info = curl_version_info(CURLVERSION_NOW); + const std::string version = "curl/" + std::string{version_info->version}; + curl_easy_setopt(curl_->handle, CURLOPT_USERAGENT, version.c_str()); + SetRedirect(Redirect()); + curl_easy_setopt(curl_->handle, CURLOPT_NOPROGRESS, 1L); + curl_easy_setopt(curl_->handle, CURLOPT_ERRORBUFFER, curl_->error.data()); + curl_easy_setopt(curl_->handle, CURLOPT_COOKIEFILE, ""); +#ifdef CPR_CURL_NOSIGNAL + curl_easy_setopt(curl_->handle, CURLOPT_NOSIGNAL, 1L); +#endif + +#if LIBCURL_VERSION_NUM >= 0x071900 // 7.25.0 + curl_easy_setopt(curl_->handle, CURLOPT_TCP_KEEPALIVE, 1L); +#endif } -void Session::Impl::SetUserAgent(const UserAgent& ua) { - curl_easy_setopt(curl_->handle, CURLOPT_USERAGENT, ua.c_str()); -} - -void Session::Impl::SetPayload(Payload&& payload) { - hasBodyOrPayload_ = true; - const std::string content = payload.GetContent(*curl_); - curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, static_cast(content.length())); - curl_easy_setopt(curl_->handle, CURLOPT_COPYPOSTFIELDS, content.c_str()); -} - -void Session::Impl::SetPayload(const Payload& payload) { - hasBodyOrPayload_ = true; - const std::string content = payload.GetContent(*curl_); - curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, static_cast(content.length())); - curl_easy_setopt(curl_->handle, CURLOPT_COPYPOSTFIELDS, content.c_str()); -} - -void Session::Impl::SetProxies(const Proxies& proxies) { - proxies_ = proxies; -} - -void Session::Impl::SetProxies(Proxies&& proxies) { - proxies_ = std::move(proxies); -} - -void Session::Impl::SetProxyAuth(ProxyAuthentication&& proxy_auth) { - proxyAuth_ = std::move(proxy_auth); -} - -void Session::Impl::SetProxyAuth(const ProxyAuthentication& proxy_auth) { - proxyAuth_ = proxy_auth; -} - -void Session::Impl::SetMultipart(Multipart&& multipart) { - curl_httppost* formpost = nullptr; - curl_httppost* lastptr = nullptr; - - for (const Part& part : multipart.parts) { - std::vector formdata; - if (part.is_buffer) { - // Do not use formdata, to prevent having to use reinterpreter_cast: - curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, part.name.c_str(), CURLFORM_BUFFER, part.value.c_str(), CURLFORM_BUFFERPTR, part.data, CURLFORM_BUFFERLENGTH, part.datalen, CURLFORM_END); - } else { - formdata.push_back({CURLFORM_COPYNAME, part.name.c_str()}); - if (part.is_file) { - formdata.push_back({CURLFORM_FILE, part.value.c_str()}); - } else { - formdata.push_back({CURLFORM_COPYCONTENTS, part.value.c_str()}); - } - } - if (!part.content_type.empty()) { - formdata.push_back({CURLFORM_CONTENTTYPE, part.content_type.c_str()}); - } - - formdata.push_back({CURLFORM_END, nullptr}); - curl_formadd(&formpost, &lastptr, CURLFORM_ARRAY, formdata.data(), CURLFORM_END); +Response Session::makeDownloadRequest() { + if (!interceptors_.empty()) { + return intercept(); } - curl_easy_setopt(curl_->handle, CURLOPT_HTTPPOST, formpost); - hasBodyOrPayload_ = true; - curl_formfree(curl_->formpost); - curl_->formpost = formpost; + const CURLcode curl_error = DoEasyPerform(); + + return CompleteDownload(curl_error); } -void Session::Impl::SetMultipart(const Multipart& multipart) { - curl_httppost* formpost = nullptr; - curl_httppost* lastptr = nullptr; +void Session::prepareCommon() { + assert(curl_->handle); - for (const Part& part : multipart.parts) { - std::vector formdata; - if (part.is_buffer) { - // Do not use formdata, to prevent having to use reinterpreter_cast: - curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, part.name.c_str(), CURLFORM_BUFFER, part.value.c_str(), CURLFORM_BUFFERPTR, part.data, CURLFORM_BUFFERLENGTH, part.datalen, CURLFORM_END); - } else { - formdata.push_back({CURLFORM_COPYNAME, part.name.c_str()}); - if (part.is_file) { - formdata.push_back({CURLFORM_FILE, part.value.c_str()}); - } else { - formdata.push_back({CURLFORM_COPYCONTENTS, part.value.c_str()}); - } - } - if (!part.content_type.empty()) { - formdata.push_back({CURLFORM_CONTENTTYPE, part.content_type.c_str()}); - } + // Set Header: + SetHeaderInternal(); - formdata.push_back({CURLFORM_END, nullptr}); - curl_formadd(&formpost, &lastptr, CURLFORM_ARRAY, formdata.data(), CURLFORM_END); + const std::string parametersContent = parameters_.GetContent(*curl_); + if (!parametersContent.empty()) { + const Url new_url{url_ + "?" + parametersContent}; + curl_easy_setopt(curl_->handle, CURLOPT_URL, new_url.c_str()); + } else { + curl_easy_setopt(curl_->handle, CURLOPT_URL, url_.c_str()); } - curl_easy_setopt(curl_->handle, CURLOPT_HTTPPOST, formpost); - hasBodyOrPayload_ = true; - curl_formfree(curl_->formpost); - curl_->formpost = formpost; + // Proxy: + const std::string protocol = url_.str().substr(0, url_.str().find(':')); + if (proxies_.has(protocol)) { + curl_easy_setopt(curl_->handle, CURLOPT_PROXY, proxies_[protocol].c_str()); + if (proxyAuth_.has(protocol)) { + curl_easy_setopt(curl_->handle, CURLOPT_PROXYUSERNAME, proxyAuth_.GetUsername(protocol)); + curl_easy_setopt(curl_->handle, CURLOPT_PROXYPASSWORD, proxyAuth_.GetPassword(protocol)); + } + } + +#if LIBCURL_VERSION_NUM >= 0x071506 // 7.21.6 + if (acceptEncoding_.empty()) { + // Enable all supported built-in compressions + curl_easy_setopt(curl_->handle, CURLOPT_ACCEPT_ENCODING, ""); + } else if (acceptEncoding_.disabled()) { + // Disable curl adding the 'Accept-Encoding' header + curl_easy_setopt(curl_->handle, CURLOPT_ACCEPT_ENCODING, nullptr); + } else { + curl_easy_setopt(curl_->handle, CURLOPT_ACCEPT_ENCODING, acceptEncoding_.getString().c_str()); + } +#endif + +#if LIBCURL_VERSION_NUM >= 0x071900 // 7.25.0 +#if SUPPORT_SSL_NO_REVOKE + // NOLINTNEXTLINE (google-runtime-int) + long bitmask{0}; + curl_easy_setopt(curl_->handle, CURLOPT_SSL_OPTIONS, &bitmask); + const bool noRevoke = bitmask & CURLSSLOPT_NO_REVOKE; +#endif + + // Fix loading certs from Windows cert store when using OpenSSL: + curl_easy_setopt(curl_->handle, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA); + +// Ensure SSL no revoke is still set +#if SUPPORT_SSL_NO_REVOKE + if (noRevoke) { + curl_easy_setopt(curl_->handle, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE); + } +#endif +#endif + + curl_->error[0] = '\0'; + + response_string_.clear(); + if (response_string_reserve_size_ > 0) { + response_string_.reserve(response_string_reserve_size_); + } + header_string_.clear(); + if (!this->writecb_.callback) { + curl_easy_setopt(curl_->handle, CURLOPT_WRITEFUNCTION, cpr::util::writeFunction); + curl_easy_setopt(curl_->handle, CURLOPT_WRITEDATA, &response_string_); + } + if (!this->headercb_.callback) { + curl_easy_setopt(curl_->handle, CURLOPT_HEADERFUNCTION, cpr::util::writeFunction); + curl_easy_setopt(curl_->handle, CURLOPT_HEADERDATA, &header_string_); + } + + // Enable so we are able to retrive certificate information: + curl_easy_setopt(curl_->handle, CURLOPT_CERTINFO, 1L); } -void Session::Impl::SetLimitRate(const LimitRate& limit_rate) { +void Session::prepareCommonDownload() { + assert(curl_->handle); + + // Set Header: + SetHeaderInternal(); + + const std::string parametersContent = parameters_.GetContent(*curl_); + if (!parametersContent.empty()) { + const Url new_url{url_ + "?" + parametersContent}; + curl_easy_setopt(curl_->handle, CURLOPT_URL, new_url.c_str()); + } else { + curl_easy_setopt(curl_->handle, CURLOPT_URL, url_.c_str()); + } + + const std::string protocol = url_.str().substr(0, url_.str().find(':')); + if (proxies_.has(protocol)) { + curl_easy_setopt(curl_->handle, CURLOPT_PROXY, proxies_[protocol].c_str()); + if (proxyAuth_.has(protocol)) { + curl_easy_setopt(curl_->handle, CURLOPT_PROXYUSERNAME, proxyAuth_.GetUsername(protocol)); + curl_easy_setopt(curl_->handle, CURLOPT_PROXYPASSWORD, proxyAuth_.GetPassword(protocol)); + } + } + + curl_->error[0] = '\0'; + + header_string_.clear(); + if (headercb_.callback) { + curl_easy_setopt(curl_->handle, CURLOPT_HEADERFUNCTION, cpr::util::headerUserFunction); + curl_easy_setopt(curl_->handle, CURLOPT_HEADERDATA, &headercb_); + } else { + curl_easy_setopt(curl_->handle, CURLOPT_HEADERFUNCTION, cpr::util::writeFunction); + curl_easy_setopt(curl_->handle, CURLOPT_HEADERDATA, &header_string_); + } +} + +Response Session::makeRequest() { + if (!interceptors_.empty()) { + return intercept(); + } + + const CURLcode curl_error = DoEasyPerform(); + return Complete(curl_error); +} + +void Session::SetLimitRate(const LimitRate& limit_rate) { curl_easy_setopt(curl_->handle, CURLOPT_MAX_RECV_SPEED_LARGE, limit_rate.downrate); curl_easy_setopt(curl_->handle, CURLOPT_MAX_SEND_SPEED_LARGE, limit_rate.uprate); } -void Session::Impl::SetNTLM(const NTLM& auth) { - // Ignore here since this has been defined by libcurl. - curl_easy_setopt(curl_->handle, CURLOPT_HTTPAUTH, CURLAUTH_NTLM); - curl_easy_setopt(curl_->handle, CURLOPT_USERPWD, auth.GetAuthString()); +void Session::SetReadCallback(const ReadCallback& read) { + readcb_ = read; + curl_easy_setopt(curl_->handle, CURLOPT_INFILESIZE_LARGE, read.size); + curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, read.size); + curl_easy_setopt(curl_->handle, CURLOPT_READFUNCTION, cpr::util::readUserFunction); + curl_easy_setopt(curl_->handle, CURLOPT_READDATA, &readcb_); + chunkedTransferEncoding_ = read.size == -1; } -void Session::Impl::SetRedirect(const Redirect& redirect) { +void Session::SetHeaderCallback(const HeaderCallback& header) { + curl_easy_setopt(curl_->handle, CURLOPT_HEADERFUNCTION, cpr::util::headerUserFunction); + headercb_ = header; + curl_easy_setopt(curl_->handle, CURLOPT_HEADERDATA, &headercb_); +} + +void Session::SetWriteCallback(const WriteCallback& write) { + curl_easy_setopt(curl_->handle, CURLOPT_WRITEFUNCTION, cpr::util::writeUserFunction); + writecb_ = write; + curl_easy_setopt(curl_->handle, CURLOPT_WRITEDATA, &writecb_); +} + +void Session::SetProgressCallback(const ProgressCallback& progress) { + progresscb_ = progress; + if (isCancellable) { + cancellationcb_.SetProgressCallback(progresscb_); + return; + } +#if LIBCURL_VERSION_NUM < 0x072000 // 7.32.0 + curl_easy_setopt(curl_->handle, CURLOPT_PROGRESSFUNCTION, cpr::util::progressUserFunction); + curl_easy_setopt(curl_->handle, CURLOPT_PROGRESSDATA, &progresscb_); +#else + curl_easy_setopt(curl_->handle, CURLOPT_XFERINFOFUNCTION, cpr::util::progressUserFunction); + curl_easy_setopt(curl_->handle, CURLOPT_XFERINFODATA, &progresscb_); +#endif + curl_easy_setopt(curl_->handle, CURLOPT_NOPROGRESS, 0L); +} + +void Session::SetDebugCallback(const DebugCallback& debug) { + curl_easy_setopt(curl_->handle, CURLOPT_DEBUGFUNCTION, cpr::util::debugUserFunction); + debugcb_ = debug; + curl_easy_setopt(curl_->handle, CURLOPT_DEBUGDATA, &debugcb_); + curl_easy_setopt(curl_->handle, CURLOPT_VERBOSE, 1L); +} + +void Session::SetUrl(const Url& url) { + url_ = url; +} + +void Session::SetResolve(const Resolve& resolve) { + SetResolves({resolve}); +} + +void Session::SetResolves(const std::vector& resolves) { + curl_slist_free_all(curl_->resolveCurlList); + curl_->resolveCurlList = nullptr; + for (const Resolve& resolve : resolves) { + for (const uint16_t port : resolve.ports) { + curl_->resolveCurlList = curl_slist_append(curl_->resolveCurlList, (resolve.host + ":" + std::to_string(port) + ":" + resolve.addr).c_str()); + } + } + curl_easy_setopt(curl_->handle, CURLOPT_RESOLVE, curl_->resolveCurlList); +} + +void Session::SetParameters(const Parameters& parameters) { + parameters_ = parameters; +} + +void Session::SetParameters(Parameters&& parameters) { + parameters_ = std::move(parameters); +} + +void Session::SetHeader(const Header& header) { + header_ = header; +} + +void Session::UpdateHeader(const Header& header) { + for (const std::pair& item : header) { + header_[item.first] = item.second; + } +} + +void Session::SetTimeout(const Timeout& timeout) { + curl_easy_setopt(curl_->handle, CURLOPT_TIMEOUT_MS, timeout.Milliseconds()); +} + +void Session::SetConnectTimeout(const ConnectTimeout& timeout) { + curl_easy_setopt(curl_->handle, CURLOPT_CONNECTTIMEOUT_MS, timeout.Milliseconds()); +} + +void Session::SetAuth(const Authentication& auth) { + // Ignore here since this has been defined by libcurl. + switch (auth.GetAuthMode()) { + case AuthMode::BASIC: + curl_easy_setopt(curl_->handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); + curl_easy_setopt(curl_->handle, CURLOPT_USERPWD, auth.GetAuthString()); + break; + case AuthMode::DIGEST: + curl_easy_setopt(curl_->handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); + curl_easy_setopt(curl_->handle, CURLOPT_USERPWD, auth.GetAuthString()); + break; + case AuthMode::NTLM: + curl_easy_setopt(curl_->handle, CURLOPT_HTTPAUTH, CURLAUTH_NTLM); + curl_easy_setopt(curl_->handle, CURLOPT_USERPWD, auth.GetAuthString()); + break; + } +} + +void Session::SetUserAgent(const UserAgent& ua) { + curl_easy_setopt(curl_->handle, CURLOPT_USERAGENT, ua.c_str()); +} + +void Session::SetPayload(const Payload& payload) { + hasBodyOrPayload_ = true; + const std::string content = payload.GetContent(*curl_); + curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, static_cast(content.length())); + curl_easy_setopt(curl_->handle, CURLOPT_COPYPOSTFIELDS, content.c_str()); +} + +void Session::SetPayload(Payload&& payload) { + hasBodyOrPayload_ = true; + const std::string content = payload.GetContent(*curl_); + curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, static_cast(content.length())); + curl_easy_setopt(curl_->handle, CURLOPT_COPYPOSTFIELDS, content.c_str()); +} + +void Session::SetProxies(const Proxies& proxies) { + proxies_ = proxies; +} + +void Session::SetProxies(Proxies&& proxies) { + proxies_ = std::move(proxies); +} + +void Session::SetProxyAuth(ProxyAuthentication&& proxy_auth) { + proxyAuth_ = std::move(proxy_auth); +} + +void Session::SetProxyAuth(const ProxyAuthentication& proxy_auth) { + proxyAuth_ = proxy_auth; +} + +void Session::SetMultipart(const Multipart& multipart) { + // Make sure, we have a empty multipart to start with: + if (curl_->multipart) { + curl_mime_free(curl_->multipart); + } + curl_->multipart = curl_mime_init(curl_->handle); + + // Add all multipart pieces: + for (const Part& part : multipart.parts) { + if (part.is_file) { + for (const File& file : part.files) { + curl_mimepart* mimePart = curl_mime_addpart(curl_->multipart); + if (!part.content_type.empty()) { + curl_mime_type(mimePart, part.content_type.c_str()); + } + + curl_mime_filedata(mimePart, file.filepath.c_str()); + curl_mime_name(mimePart, part.name.c_str()); + + if (file.hasOverridenFilename()) { + curl_mime_filename(mimePart, file.overriden_filename.c_str()); + } + } + } else { + curl_mimepart* mimePart = curl_mime_addpart(curl_->multipart); + if (!part.content_type.empty()) { + curl_mime_type(mimePart, part.content_type.c_str()); + } + if (part.is_buffer) { + // Do not use formdata, to prevent having to use reinterpreter_cast: + curl_mime_name(mimePart, part.name.c_str()); + curl_mime_data(mimePart, part.data, part.datalen); + curl_mime_filename(mimePart, part.value.c_str()); + } else { + curl_mime_name(mimePart, part.name.c_str()); + curl_mime_data(mimePart, part.value.c_str(), CURL_ZERO_TERMINATED); + } + } + } + + curl_easy_setopt(curl_->handle, CURLOPT_MIMEPOST, curl_->multipart); + hasBodyOrPayload_ = true; +} + +void Session::SetMultipart(Multipart&& multipart) { + SetMultipart(multipart); +} + +void Session::SetRedirect(const Redirect& redirect) { curl_easy_setopt(curl_->handle, CURLOPT_FOLLOWLOCATION, redirect.follow ? 1L : 0L); curl_easy_setopt(curl_->handle, CURLOPT_MAXREDIRS, redirect.maximum); + curl_easy_setopt(curl_->handle, CURLOPT_UNRESTRICTED_AUTH, redirect.cont_send_cred ? 1L : 0L); // NOLINTNEXTLINE (google-runtime-int) long mask = 0; @@ -358,78 +447,38 @@ void Session::Impl::SetRedirect(const Redirect& redirect) { curl_easy_setopt(curl_->handle, CURLOPT_POSTREDIR, mask); } -void Session::Impl::SetCookies(const Cookies& cookies) { +void Session::SetCookies(const Cookies& cookies) { curl_easy_setopt(curl_->handle, CURLOPT_COOKIELIST, "ALL"); curl_easy_setopt(curl_->handle, CURLOPT_COOKIE, cookies.GetEncoded(*curl_).c_str()); } -void Session::Impl::SetBody(Body&& body) { - hasBodyOrPayload_ = true; - curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, static_cast(body.str().length())); - curl_easy_setopt(curl_->handle, CURLOPT_COPYPOSTFIELDS, body.c_str()); -} - -void Session::Impl::SetBody(const Body& body) { +void Session::SetBody(const Body& body) { hasBodyOrPayload_ = true; curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, static_cast(body.str().length())); curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDS, body.c_str()); } -void Session::Impl::SetReadCallback(const ReadCallback& read) { - readcb_ = read; - curl_easy_setopt(curl_->handle, CURLOPT_INFILESIZE_LARGE, read.size); - curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, read.size); - curl_easy_setopt(curl_->handle, CURLOPT_READFUNCTION, cpr::util::readUserFunction); - curl_easy_setopt(curl_->handle, CURLOPT_READDATA, &readcb_); - chunkedTransferEncoding = read.size == -1; +void Session::SetBody(Body&& body) { + hasBodyOrPayload_ = true; + curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDSIZE_LARGE, static_cast(body.str().length())); + curl_easy_setopt(curl_->handle, CURLOPT_COPYPOSTFIELDS, body.c_str()); } -void Session::Impl::SetHeaderCallback(const HeaderCallback& header) { - curl_easy_setopt(curl_->handle, CURLOPT_HEADERFUNCTION, cpr::util::headerUserFunction); - headercb_ = header; - curl_easy_setopt(curl_->handle, CURLOPT_HEADERDATA, &headercb_); -} - -void Session::Impl::SetWriteCallback(const WriteCallback& write) { - curl_easy_setopt(curl_->handle, CURLOPT_WRITEFUNCTION, cpr::util::writeUserFunction); - writecb_ = write; - curl_easy_setopt(curl_->handle, CURLOPT_WRITEDATA, &writecb_); -} - -void Session::Impl::SetProgressCallback(const ProgressCallback& progress) { - progresscb_ = progress; -#if LIBCURL_VERSION_NUM < 0x072000 - curl_easy_setopt(curl_->handle, CURLOPT_PROGRESSFUNCTION, cpr::util::progressUserFunction); - curl_easy_setopt(curl_->handle, CURLOPT_PROGRESSDATA, &progresscb_); -#else - curl_easy_setopt(curl_->handle, CURLOPT_XFERINFOFUNCTION, cpr::util::progressUserFunction); - curl_easy_setopt(curl_->handle, CURLOPT_XFERINFODATA, &progresscb_); -#endif - curl_easy_setopt(curl_->handle, CURLOPT_NOPROGRESS, 0L); -} - -void Session::Impl::SetDebugCallback(const DebugCallback& debug) { - curl_easy_setopt(curl_->handle, CURLOPT_DEBUGFUNCTION, cpr::util::debugUserFunction); - debugcb_ = debug; - curl_easy_setopt(curl_->handle, CURLOPT_DEBUGDATA, &debugcb_); - curl_easy_setopt(curl_->handle, CURLOPT_VERBOSE, 1L); -} - -void Session::Impl::SetLowSpeed(const LowSpeed& low_speed) { +void Session::SetLowSpeed(const LowSpeed& low_speed) { curl_easy_setopt(curl_->handle, CURLOPT_LOW_SPEED_LIMIT, low_speed.limit); curl_easy_setopt(curl_->handle, CURLOPT_LOW_SPEED_TIME, low_speed.time); } -void Session::Impl::SetVerifySsl(const VerifySsl& verify) { +void Session::SetVerifySsl(const VerifySsl& verify) { curl_easy_setopt(curl_->handle, CURLOPT_SSL_VERIFYPEER, verify ? ON : OFF); curl_easy_setopt(curl_->handle, CURLOPT_SSL_VERIFYHOST, verify ? 2L : 0L); } -void Session::Impl::SetUnixSocket(const UnixSocket& unix_socket) { +void Session::SetUnixSocket(const UnixSocket& unix_socket) { curl_easy_setopt(curl_->handle, CURLOPT_UNIX_SOCKET_PATH, unix_socket.GetUnixSocketString()); } -void Session::Impl::SetSslOptions(const SslOptions& options) { +void Session::SetSslOptions(const SslOptions& options) { if (!options.cert_file.empty()) { curl_easy_setopt(curl_->handle, CURLOPT_SSLCERT, options.cert_file.c_str()); if (!options.cert_type.empty()) { @@ -444,6 +493,21 @@ void Session::Impl::SetSslOptions(const SslOptions& options) { if (!options.key_pass.empty()) { curl_easy_setopt(curl_->handle, CURLOPT_KEYPASSWD, options.key_pass.c_str()); } +#if SUPPORT_CURLOPT_SSLKEY_BLOB + } else if (!options.key_blob.empty()) { + std::string key_blob(options.key_blob); + curl_blob blob{}; + // NOLINTNEXTLINE (readability-container-data-pointer) + blob.data = &key_blob[0]; + blob.len = key_blob.length(); + curl_easy_setopt(curl_->handle, CURLOPT_SSLKEY_BLOB, &blob); + if (!options.key_type.empty()) { + curl_easy_setopt(curl_->handle, CURLOPT_SSLKEYTYPE, options.key_type.c_str()); + } + if (!options.key_pass.empty()) { + curl_easy_setopt(curl_->handle, CURLOPT_KEYPASSWD, options.key_pass.c_str()); + } +#endif } if (!options.pinned_public_key.empty()) { curl_easy_setopt(curl_->handle, CURLOPT_PINNEDPUBLICKEY, options.pinned_public_key.c_str()); @@ -456,7 +520,7 @@ void Session::Impl::SetSslOptions(const SslOptions& options) { #endif curl_easy_setopt(curl_->handle, CURLOPT_SSL_VERIFYPEER, options.verify_peer ? ON : OFF); curl_easy_setopt(curl_->handle, CURLOPT_SSL_VERIFYHOST, options.verify_host ? 2L : 0L); -#if LIBCURL_VERSION_NUM >= 0x072900 +#if LIBCURL_VERSION_NUM >= 0x072900 // 7.41.0 curl_easy_setopt(curl_->handle, CURLOPT_SSL_VERIFYSTATUS, options.verify_status ? ON : OFF); #endif @@ -479,6 +543,14 @@ void Session::Impl::SetSslOptions(const SslOptions& options) { if (!options.ca_path.empty()) { curl_easy_setopt(curl_->handle, CURLOPT_CAPATH, options.ca_path.c_str()); } +#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION +#ifdef OPENSSL_BACKEND_USED + if (!options.ca_buffer.empty()) { + curl_easy_setopt(curl_->handle, CURLOPT_SSL_CTX_FUNCTION, sslctx_function_load_ca_cert_from_buffer); + curl_easy_setopt(curl_->handle, CURLOPT_SSL_CTX_DATA, options.ca_buffer.c_str()); + } +#endif +#endif if (!options.crl_file.empty()) { curl_easy_setopt(curl_->handle, CURLOPT_CRLFILE, options.crl_file.c_str()); } @@ -495,58 +567,228 @@ void Session::Impl::SetSslOptions(const SslOptions& options) { #endif } -void Session::Impl::PrepareDelete() { +void Session::SetVerbose(const Verbose& verbose) { + curl_easy_setopt(curl_->handle, CURLOPT_VERBOSE, verbose.verbose ? ON : OFF); +} + +void Session::SetInterface(const Interface& iface) { + if (iface.str().empty()) { + curl_easy_setopt(curl_->handle, CURLOPT_INTERFACE, nullptr); + } else { + curl_easy_setopt(curl_->handle, CURLOPT_INTERFACE, iface.c_str()); + } +} + +void Session::SetLocalPort(const LocalPort& local_port) { + curl_easy_setopt(curl_->handle, CURLOPT_LOCALPORT, static_cast(static_cast(local_port))); +} + +void Session::SetLocalPortRange(const LocalPortRange& local_port_range) { + curl_easy_setopt(curl_->handle, CURLOPT_LOCALPORTRANGE, static_cast(static_cast(local_port_range))); +} + +void Session::SetHttpVersion(const HttpVersion& version) { + switch (version.code) { + case HttpVersionCode::VERSION_NONE: + curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_NONE); + break; + + case HttpVersionCode::VERSION_1_0: + curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); + break; + + case HttpVersionCode::VERSION_1_1: + curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + break; + +#if LIBCURL_VERSION_NUM >= 0x072100 // 7.33.0 + case HttpVersionCode::VERSION_2_0: + curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); + break; +#endif + +#if LIBCURL_VERSION_NUM >= 0x072F00 // 7.47.0 + case HttpVersionCode::VERSION_2_0_TLS: + curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS); + break; +#endif + +#if LIBCURL_VERSION_NUM >= 0x073100 // 7.49.0 + case HttpVersionCode::VERSION_2_0_PRIOR_KNOWLEDGE: + curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE); + break; +#endif + +#if LIBCURL_VERSION_NUM >= 0x074200 // 7.66.0 + case HttpVersionCode::VERSION_3_0: + curl_easy_setopt(curl_->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_3); + break; +#endif + + default: // Should not happen + throw std::invalid_argument("Invalid/Unknown HTTP version type."); + } +} + +void Session::SetRange(const Range& range) { + const std::string range_str = range.str(); + curl_easy_setopt(curl_->handle, CURLOPT_RANGE, range_str.c_str()); +} + +void Session::SetMultiRange(const MultiRange& multi_range) { + const std::string multi_range_str = multi_range.str(); + curl_easy_setopt(curl_->handle, CURLOPT_RANGE, multi_range_str.c_str()); +} + +void Session::SetReserveSize(const ReserveSize& reserve_size) { + ResponseStringReserve(reserve_size.size); +} + +void Session::SetAcceptEncoding(const AcceptEncoding& accept_encoding) { + acceptEncoding_ = accept_encoding; +} + +void Session::SetAcceptEncoding(AcceptEncoding&& accept_encoding) { + acceptEncoding_ = std::move(accept_encoding); +} + +cpr_off_t Session::GetDownloadFileLength() { + cpr_off_t downloadFileLenth = -1; + curl_easy_setopt(curl_->handle, CURLOPT_URL, url_.c_str()); + + const std::string protocol = url_.str().substr(0, url_.str().find(':')); + if (proxies_.has(protocol)) { + curl_easy_setopt(curl_->handle, CURLOPT_PROXY, proxies_[protocol].c_str()); + if (proxyAuth_.has(protocol)) { + curl_easy_setopt(curl_->handle, CURLOPT_PROXYUSERNAME, proxyAuth_.GetUsername(protocol)); + curl_easy_setopt(curl_->handle, CURLOPT_PROXYPASSWORD, proxyAuth_.GetPassword(protocol)); + } + } + + curl_easy_setopt(curl_->handle, CURLOPT_HTTPGET, 1); + curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 1); + if (DoEasyPerform() == CURLE_OK) { + // NOLINTNEXTLINE (google-runtime-int) + long status_code{}; + curl_easy_getinfo(curl_->handle, CURLINFO_RESPONSE_CODE, &status_code); + if (200 == status_code) { + curl_easy_getinfo(curl_->handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &downloadFileLenth); + } + } + return downloadFileLenth; +} + +void Session::ResponseStringReserve(size_t size) { + response_string_reserve_size_ = size; +} + +Response Session::Delete() { + PrepareDelete(); + return makeRequest(); +} + +Response Session::Download(const WriteCallback& write) { + PrepareDownload(write); + return makeDownloadRequest(); +} + +Response Session::Download(std::ofstream& file) { + PrepareDownload(file); + return makeDownloadRequest(); +} + +Response Session::Get() { + PrepareGet(); + return makeRequest(); +} + +Response Session::Head() { + PrepareHead(); + return makeRequest(); +} + +Response Session::Options() { + PrepareOptions(); + return makeRequest(); +} + +Response Session::Patch() { + PreparePatch(); + return makeRequest(); +} + +Response Session::Post() { + PreparePost(); + return makeRequest(); +} + +Response Session::Put() { + PreparePut(); + return makeRequest(); +} + +std::shared_ptr Session::GetSharedPtrFromThis() { + try { + return shared_from_this(); + } catch (std::bad_weak_ptr&) { + throw std::runtime_error("Failed to get a shared pointer from this. The reason is probably that the session object is not managed by a shared pointer, which is required to use this functionality."); + } +} + +AsyncResponse Session::GetAsync() { + auto shared_this = shared_from_this(); + return async([shared_this]() { return shared_this->Get(); }); +} + +AsyncResponse Session::DeleteAsync() { + return async([shared_this = GetSharedPtrFromThis()]() { return shared_this->Delete(); }); +} + +AsyncResponse Session::DownloadAsync(const WriteCallback& write) { + return async([shared_this = GetSharedPtrFromThis(), write]() { return shared_this->Download(write); }); +} + +AsyncResponse Session::DownloadAsync(std::ofstream& file) { + return async([shared_this = GetSharedPtrFromThis(), &file]() { return shared_this->Download(file); }); +} + +AsyncResponse Session::HeadAsync() { + return async([shared_this = GetSharedPtrFromThis()]() { return shared_this->Head(); }); +} + +AsyncResponse Session::OptionsAsync() { + return async([shared_this = GetSharedPtrFromThis()]() { return shared_this->Options(); }); +} + +AsyncResponse Session::PatchAsync() { + return async([shared_this = GetSharedPtrFromThis()]() { return shared_this->Patch(); }); +} + +AsyncResponse Session::PostAsync() { + return async([shared_this = GetSharedPtrFromThis()]() { return shared_this->Post(); }); +} + +AsyncResponse Session::PutAsync() { + return async([shared_this = GetSharedPtrFromThis()]() { return shared_this->Put(); }); +} + +std::shared_ptr Session::GetCurlHolder() { + return curl_; +} + +std::string Session::GetFullRequestUrl() { + const std::string parametersContent = parameters_.GetContent(*curl_); + return url_.str() + (parametersContent.empty() ? "" : "?") + parametersContent; +} + +void Session::PrepareDelete() { curl_easy_setopt(curl_->handle, CURLOPT_HTTPGET, 0L); curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L); curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, "DELETE"); prepareCommon(); } -cpr_off_t Session::Impl::GetDownloadFileLength() { - cpr_off_t downloadFileLenth = -1; - curl_easy_setopt(curl_->handle, CURLOPT_URL, url_.c_str()); - - std::string protocol = url_.str().substr(0, url_.str().find(':')); - if (proxies_.has(protocol)) { - curl_easy_setopt(curl_->handle, CURLOPT_PROXY, proxies_[protocol].c_str()); - if (proxyAuth_.has(protocol)) { - curl_easy_setopt(curl_->handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY); - curl_easy_setopt(curl_->handle, CURLOPT_PROXYUSERPWD, proxyAuth_[protocol]); - } - } - - curl_easy_setopt(curl_->handle, CURLOPT_HTTPGET, 1); - curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 1); - if (curl_easy_perform(curl_->handle) == CURLE_OK) { - curl_easy_getinfo(curl_->handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &downloadFileLenth); - } - return downloadFileLenth; -} - -Response Session::Impl::Delete() { - PrepareDelete(); - return makeRequest(); -} - -Response Session::Impl::Download(const WriteCallback& write) { - curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L); - curl_easy_setopt(curl_->handle, CURLOPT_HTTPGET, 1); - - SetWriteCallback(write); - - return makeDownloadRequest(); -} - -Response Session::Impl::Download(std::ofstream& file) { - curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L); - curl_easy_setopt(curl_->handle, CURLOPT_HTTPGET, 1); - curl_easy_setopt(curl_->handle, CURLOPT_WRITEFUNCTION, cpr::util::writeFileFunction); - curl_easy_setopt(curl_->handle, CURLOPT_WRITEDATA, &file); - - return makeDownloadRequest(); -} - -void Session::Impl::PrepareGet() { +void Session::PrepareGet() { // In case there is a body or payload for this request, we create a custom GET-Request since a // GET-Request with body is based on the HTTP RFC **not** a leagal request. if (hasBodyOrPayload_) { @@ -560,45 +802,25 @@ void Session::Impl::PrepareGet() { prepareCommon(); } -Response Session::Impl::Get() { - PrepareGet(); - return makeRequest(); -} - -void Session::Impl::PrepareHead() { +void Session::PrepareHead() { curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 1L); curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, nullptr); prepareCommon(); } -Response Session::Impl::Head() { - PrepareHead(); - return makeRequest(); -} - -void Session::Impl::PrepareOptions() { +void Session::PrepareOptions() { curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L); curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, "OPTIONS"); prepareCommon(); } -Response Session::Impl::Options() { - PrepareOptions(); - return makeRequest(); -} - -void Session::Impl::PreparePatch() { +void Session::PreparePatch() { curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L); curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, "PATCH"); prepareCommon(); } -Response Session::Impl::Patch() { - PreparePatch(); - return makeRequest(); -} - -void Session::Impl::PreparePost() { +void Session::PreparePost() { curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L); // In case there is no body or payload set it to an empty post: @@ -611,133 +833,42 @@ void Session::Impl::PreparePost() { prepareCommon(); } -Response Session::Impl::Post() { - PreparePost(); - return makeRequest(); -} - -void Session::Impl::PreparePut() { +void Session::PreparePut() { curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L); + if (!hasBodyOrPayload_ && readcb_.callback) { + /** + * Yes, this one has to be CURLOPT_POSTFIELDS even if we are performing a PUT request. + * In case we don't set this one, performing a POST-request with PUT won't work. + * It in theory this only enforces the usage of the readcallback for POST requests, but works here as well. + **/ + curl_easy_setopt(curl_->handle, CURLOPT_POSTFIELDS, nullptr); + } curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, "PUT"); + curl_easy_setopt(curl_->handle, CURLOPT_RANGE, nullptr); prepareCommon(); } -Response Session::Impl::Put() { - PreparePut(); - return makeRequest(); +void Session::PrepareDownload(std::ofstream& file) { + curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L); + curl_easy_setopt(curl_->handle, CURLOPT_HTTPGET, 1); + curl_easy_setopt(curl_->handle, CURLOPT_WRITEFUNCTION, cpr::util::writeFileFunction); + curl_easy_setopt(curl_->handle, CURLOPT_WRITEDATA, &file); + curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, nullptr); + + prepareCommonDownload(); } -std::shared_ptr Session::Impl::GetCurlHolder() { - return curl_; +void Session::PrepareDownload(const WriteCallback& write) { + curl_easy_setopt(curl_->handle, CURLOPT_NOBODY, 0L); + curl_easy_setopt(curl_->handle, CURLOPT_HTTPGET, 1); + curl_easy_setopt(curl_->handle, CURLOPT_CUSTOMREQUEST, nullptr); + + SetWriteCallback(write); + + prepareCommonDownload(); } -Response Session::Impl::makeDownloadRequest() { - assert(curl_->handle); - const std::string parametersContent = parameters_.GetContent(*curl_); - if (!parametersContent.empty()) { - Url new_url{url_ + "?" + parametersContent}; - curl_easy_setopt(curl_->handle, CURLOPT_URL, new_url.c_str()); - } else { - curl_easy_setopt(curl_->handle, CURLOPT_URL, url_.c_str()); - } - - std::string protocol = url_.str().substr(0, url_.str().find(':')); - if (proxies_.has(protocol)) { - curl_easy_setopt(curl_->handle, CURLOPT_PROXY, proxies_[protocol].c_str()); - if (proxyAuth_.has(protocol)) { - curl_easy_setopt(curl_->handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY); - curl_easy_setopt(curl_->handle, CURLOPT_PROXYUSERPWD, proxyAuth_[protocol]); - } - } - - curl_->error[0] = '\0'; - - std::string header_string; - if (headercb_.callback) { - curl_easy_setopt(curl_->handle, CURLOPT_HEADERFUNCTION, cpr::util::headerUserFunction); - curl_easy_setopt(curl_->handle, CURLOPT_HEADERDATA, &headercb_); - } else { - curl_easy_setopt(curl_->handle, CURLOPT_HEADERFUNCTION, cpr::util::writeFunction); - curl_easy_setopt(curl_->handle, CURLOPT_HEADERDATA, &header_string); - } - - CURLcode curl_error = curl_easy_perform(curl_->handle); - - if (!headercb_.callback) { - curl_easy_setopt(curl_->handle, CURLOPT_HEADERFUNCTION, nullptr); - curl_easy_setopt(curl_->handle, CURLOPT_HEADERDATA, 0); - } - - curl_slist* raw_cookies{nullptr}; - curl_easy_getinfo(curl_->handle, CURLINFO_COOKIELIST, &raw_cookies); - Cookies cookies = util::parseCookies(raw_cookies); - curl_slist_free_all(raw_cookies); - std::string errorMsg = curl_->error.data(); - - return Response(curl_, "", std::move(header_string), std::move(cookies), Error(curl_error, std::move(errorMsg))); -} - -void Session::Impl::prepareCommon() { - assert(curl_->handle); - - // Set Header: - SetHeaderInternal(); - - const std::string parametersContent = parameters_.GetContent(*curl_); - if (!parametersContent.empty()) { - Url new_url{url_ + "?" + parametersContent}; - curl_easy_setopt(curl_->handle, CURLOPT_URL, new_url.c_str()); - } else { - curl_easy_setopt(curl_->handle, CURLOPT_URL, url_.c_str()); - } - - // Proxy: - std::string protocol = url_.str().substr(0, url_.str().find(':')); - if (proxies_.has(protocol)) { - curl_easy_setopt(curl_->handle, CURLOPT_PROXY, proxies_[protocol].c_str()); - if (proxyAuth_.has(protocol)) { - curl_easy_setopt(curl_->handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY); - curl_easy_setopt(curl_->handle, CURLOPT_PROXYUSERPWD, proxyAuth_[protocol]); - } - } - -#if LIBCURL_VERSION_MAJOR >= 7 -#if LIBCURL_VERSION_MINOR >= 21 - /* enable all supported built-in compressions */ - curl_easy_setopt(curl_->handle, CURLOPT_ACCEPT_ENCODING, ""); -#endif -#endif - -#if LIBCURL_VERSION_MAJOR >= 7 -#if LIBCURL_VERSION_MINOR >= 71 - // Fix loading certs from Windows cert store when using OpenSSL: - curl_easy_setopt(curl_->handle, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA); -#endif -#endif - - curl_->error[0] = '\0'; - - response_string_.clear(); - header_string_.clear(); - if (!this->writecb_.callback) { - curl_easy_setopt(curl_->handle, CURLOPT_WRITEFUNCTION, cpr::util::writeFunction); - curl_easy_setopt(curl_->handle, CURLOPT_WRITEDATA, &response_string_); - } - if (!this->headercb_.callback) { - curl_easy_setopt(curl_->handle, CURLOPT_HEADERFUNCTION, cpr::util::writeFunction); - curl_easy_setopt(curl_->handle, CURLOPT_HEADERDATA, &header_string_); - } - - // Enable so we are able to retrive certificate information: - curl_easy_setopt(curl_->handle, CURLOPT_CERTINFO, 1L); -} - -Response Session::Impl::makeRequest() { - CURLcode curl_error = curl_easy_perform(curl_->handle); - return Complete(curl_error); -} - -Response Session::Impl::Complete(CURLcode curl_error) { +Response Session::Complete(CURLcode curl_error) { curl_slist* raw_cookies{nullptr}; curl_easy_getinfo(curl_->handle, CURLINFO_COOKIELIST, &raw_cookies); Cookies cookies = util::parseCookies(raw_cookies); @@ -750,106 +881,97 @@ Response Session::Impl::Complete(CURLcode curl_error) { return Response(curl_, std::move(response_string_), std::move(header_string_), std::move(cookies), Error(curl_error, std::move(errorMsg))); } +Response Session::CompleteDownload(CURLcode curl_error) { + if (!headercb_.callback) { + curl_easy_setopt(curl_->handle, CURLOPT_HEADERFUNCTION, nullptr); + curl_easy_setopt(curl_->handle, CURLOPT_HEADERDATA, 0); + } + + curl_slist* raw_cookies{nullptr}; + curl_easy_getinfo(curl_->handle, CURLINFO_COOKIELIST, &raw_cookies); + Cookies cookies = util::parseCookies(raw_cookies); + curl_slist_free_all(raw_cookies); + std::string errorMsg = curl_->error.data(); + + return Response(curl_, "", std::move(header_string_), std::move(cookies), Error(curl_error, std::move(errorMsg))); +} + +void Session::AddInterceptor(const std::shared_ptr& pinterceptor) { + interceptors_.push(pinterceptor); +} + +Response Session::proceed() { + prepareCommon(); + return makeRequest(); +} + +Response Session::intercept() { + // At least one interceptor exists -> Execute its intercept function + const std::shared_ptr interceptor = interceptors_.front(); + interceptors_.pop(); + return interceptor->intercept(*this); +} + // clang-format off -Session::Session() : pimpl_(new Impl()) {} -Session::Session(Session&& /*old*/) noexcept = default; -Session::~Session() = default; -Session& Session::operator=(Session&& old) noexcept = default; -void Session::SetReadCallback(const ReadCallback& read) { pimpl_->SetReadCallback(read); } -void Session::SetHeaderCallback(const HeaderCallback& header) { pimpl_->SetHeaderCallback(header); } -void Session::SetWriteCallback(const WriteCallback& write) { pimpl_->SetWriteCallback(write); } -void Session::SetProgressCallback(const ProgressCallback& progress) { pimpl_->SetProgressCallback(progress); } -void Session::SetDebugCallback(const DebugCallback& debug) { pimpl_->SetDebugCallback(debug); } -void Session::SetUrl(const Url& url) { pimpl_->SetUrl(url); } -void Session::SetParameters(const Parameters& parameters) { pimpl_->SetParameters(parameters); } -void Session::SetParameters(Parameters&& parameters) { pimpl_->SetParameters(std::move(parameters)); } -void Session::SetHeader(const Header& header) { pimpl_->SetHeader(header); } -void Session::UpdateHeader(const Header& header) { pimpl_->UpdateHeader(header); } -void Session::SetTimeout(const Timeout& timeout) { pimpl_->SetTimeout(timeout); } -void Session::SetConnectTimeout(const ConnectTimeout& timeout) { pimpl_->SetConnectTimeout(timeout); } -void Session::SetAuth(const Authentication& auth) { pimpl_->SetAuth(auth); } -void Session::SetDigest(const Digest& auth) { pimpl_->SetDigest(auth); } -void Session::SetUserAgent(const UserAgent& ua) { pimpl_->SetUserAgent(ua); } -void Session::SetPayload(const Payload& payload) { pimpl_->SetPayload(payload); } -void Session::SetPayload(Payload&& payload) { pimpl_->SetPayload(std::move(payload)); } -void Session::SetProxies(const Proxies& proxies) { pimpl_->SetProxies(proxies); } -void Session::SetProxies(Proxies&& proxies) { pimpl_->SetProxies(std::move(proxies)); } -void Session::SetProxyAuth(ProxyAuthentication&& proxy_auth) { pimpl_->SetProxyAuth(std::move(proxy_auth)); } -void Session::SetProxyAuth(const ProxyAuthentication& proxy_auth) { pimpl_->SetProxyAuth(proxy_auth); } -void Session::SetMultipart(const Multipart& multipart) { pimpl_->SetMultipart(multipart); } -void Session::SetMultipart(Multipart&& multipart) { pimpl_->SetMultipart(std::move(multipart)); } -void Session::SetNTLM(const NTLM& auth) { pimpl_->SetNTLM(auth); } -void Session::SetRedirect(const Redirect& redirect) { pimpl_->SetRedirect(redirect); } -void Session::SetCookies(const Cookies& cookies) { pimpl_->SetCookies(cookies); } -void Session::SetBody(const Body& body) { pimpl_->SetBody(body); } -void Session::SetBody(Body&& body) { pimpl_->SetBody(std::move(body)); } -void Session::SetLowSpeed(const LowSpeed& low_speed) { pimpl_->SetLowSpeed(low_speed); } -void Session::SetVerifySsl(const VerifySsl& verify) { pimpl_->SetVerifySsl(verify); } -void Session::SetUnixSocket(const UnixSocket& unix_socket) { pimpl_->SetUnixSocket(unix_socket); } -void Session::SetSslOptions(const SslOptions& options) { pimpl_->SetSslOptions(options); } -void Session::SetVerbose(const Verbose& verbose) { pimpl_->SetVerbose(verbose); } -void Session::SetInterface(const Interface& iface) { pimpl_->SetInterface(iface); } -void Session::SetOption(const ReadCallback& read) { pimpl_->SetReadCallback(read); } -void Session::SetOption(const HeaderCallback& header) { pimpl_->SetHeaderCallback(header); } -void Session::SetOption(const WriteCallback& write) { pimpl_->SetWriteCallback(write); } -void Session::SetOption(const ProgressCallback& progress) { pimpl_->SetProgressCallback(progress); } -void Session::SetOption(const DebugCallback& debug) { pimpl_->SetDebugCallback(debug); } -void Session::SetOption(const Url& url) { pimpl_->SetUrl(url); } -void Session::SetOption(const Parameters& parameters) { pimpl_->SetParameters(parameters); } -void Session::SetOption(Parameters&& parameters) { pimpl_->SetParameters(std::move(parameters)); } -void Session::SetOption(const Header& header) { pimpl_->SetHeader(header); } -void Session::SetOption(const Timeout& timeout) { pimpl_->SetTimeout(timeout); } -void Session::SetOption(const ConnectTimeout& timeout) { pimpl_->SetConnectTimeout(timeout); } -void Session::SetOption(const Authentication& auth) { pimpl_->SetAuth(auth); } -void Session::SetOption(const LimitRate& limit_rate) { pimpl_->SetLimitRate(limit_rate); } +void Session::SetOption(const Resolve& resolve) { SetResolve(resolve); } +void Session::SetOption(const std::vector& resolves) { SetResolves(resolves); } +void Session::SetOption(const ReadCallback& read) { SetReadCallback(read); } +void Session::SetOption(const HeaderCallback& header) { SetHeaderCallback(header); } +void Session::SetOption(const WriteCallback& write) { SetWriteCallback(write); } +void Session::SetOption(const ProgressCallback& progress) { SetProgressCallback(progress); } +void Session::SetOption(const DebugCallback& debug) { SetDebugCallback(debug); } +void Session::SetOption(const Url& url) { SetUrl(url); } +void Session::SetOption(const Parameters& parameters) { SetParameters(parameters); } +void Session::SetOption(Parameters&& parameters) { SetParameters(std::move(parameters)); } +void Session::SetOption(const Header& header) { SetHeader(header); } +void Session::SetOption(const Timeout& timeout) { SetTimeout(timeout); } +void Session::SetOption(const ConnectTimeout& timeout) { SetConnectTimeout(timeout); } +void Session::SetOption(const Authentication& auth) { SetAuth(auth); } +void Session::SetOption(const LimitRate& limit_rate) { SetLimitRate(limit_rate); } // Only supported with libcurl >= 7.61.0. // As an alternative use SetHeader and add the token manually. #if LIBCURL_VERSION_NUM >= 0x073D00 -void Session::SetOption(const Bearer& auth) { pimpl_->SetBearer(auth); } +void Session::SetOption(const Bearer& auth) { SetBearer(auth); } #endif -void Session::SetOption(const Digest& auth) { pimpl_->SetDigest(auth); } -void Session::SetOption(const UserAgent& ua) { pimpl_->SetUserAgent(ua); } -void Session::SetOption(const Payload& payload) { pimpl_->SetPayload(payload); } -void Session::SetOption(Payload&& payload) { pimpl_->SetPayload(std::move(payload)); } -void Session::SetOption(const Proxies& proxies) { pimpl_->SetProxies(proxies); } -void Session::SetOption(Proxies&& proxies) { pimpl_->SetProxies(std::move(proxies)); } -void Session::SetOption(ProxyAuthentication&& proxy_auth) { pimpl_->SetProxyAuth(std::move(proxy_auth)); } -void Session::SetOption(const ProxyAuthentication& proxy_auth) { pimpl_->SetProxyAuth(proxy_auth); } -void Session::SetOption(const Multipart& multipart) { pimpl_->SetMultipart(multipart); } -void Session::SetOption(Multipart&& multipart) { pimpl_->SetMultipart(std::move(multipart)); } -void Session::SetOption(const NTLM& auth) { pimpl_->SetNTLM(auth); } -void Session::SetOption(const Redirect& redirect) { pimpl_->SetRedirect(redirect); } -void Session::SetOption(const Cookies& cookies) { pimpl_->SetCookies(cookies); } -void Session::SetOption(const Body& body) { pimpl_->SetBody(body); } -void Session::SetOption(Body&& body) { pimpl_->SetBody(std::move(body)); } -void Session::SetOption(const LowSpeed& low_speed) { pimpl_->SetLowSpeed(low_speed); } -void Session::SetOption(const VerifySsl& verify) { pimpl_->SetVerifySsl(verify); } -void Session::SetOption(const Verbose& verbose) { pimpl_->SetVerbose(verbose); } -void Session::SetOption(const UnixSocket& unix_socket) { pimpl_->SetUnixSocket(unix_socket); } -void Session::SetOption(const SslOptions& options) { pimpl_->SetSslOptions(options); } -void Session::SetOption(const Interface& iface) { pimpl_->SetInterface(iface); } - -cpr_off_t Session::GetDownloadFileLength() { return pimpl_->GetDownloadFileLength(); } -Response Session::Delete() { return pimpl_->Delete(); } -Response Session::Download(const WriteCallback& write) { return pimpl_->Download(write); } -Response Session::Download(std::ofstream& file) { return pimpl_->Download(file); } -Response Session::Get() { return pimpl_->Get(); } -Response Session::Head() { return pimpl_->Head(); } -Response Session::Options() { return pimpl_->Options(); } -Response Session::Patch() { return pimpl_->Patch(); } -Response Session::Post() { return pimpl_->Post(); } -Response Session::Put() { return pimpl_->Put(); } - -std::shared_ptr Session::GetCurlHolder() { return pimpl_->GetCurlHolder(); } - -void Session::PrepareDelete() { return pimpl_->PrepareDelete(); } -void Session::PrepareGet() { return pimpl_->PrepareGet(); } -void Session::PrepareHead() { return pimpl_->PrepareHead(); } -void Session::PrepareOptions() { return pimpl_->PrepareOptions(); } -void Session::PreparePatch() { return pimpl_->PreparePatch(); } -void Session::PreparePost() { return pimpl_->PreparePost(); } -void Session::PreparePut() { return pimpl_->PreparePut(); } -Response Session::Complete( CURLcode curl_error ) { return pimpl_->Complete(curl_error); } - +void Session::SetOption(const UserAgent& ua) { SetUserAgent(ua); } +void Session::SetOption(const Payload& payload) { SetPayload(payload); } +void Session::SetOption(Payload&& payload) { SetPayload(std::move(payload)); } +void Session::SetOption(const Proxies& proxies) { SetProxies(proxies); } +void Session::SetOption(Proxies&& proxies) { SetProxies(std::move(proxies)); } +void Session::SetOption(ProxyAuthentication&& proxy_auth) { SetProxyAuth(std::move(proxy_auth)); } +void Session::SetOption(const ProxyAuthentication& proxy_auth) { SetProxyAuth(proxy_auth); } +void Session::SetOption(const Multipart& multipart) { SetMultipart(multipart); } +void Session::SetOption(Multipart&& multipart) { SetMultipart(std::move(multipart)); } +void Session::SetOption(const Redirect& redirect) { SetRedirect(redirect); } +void Session::SetOption(const Cookies& cookies) { SetCookies(cookies); } +void Session::SetOption(const Body& body) { SetBody(body); } +void Session::SetOption(Body&& body) { SetBody(std::move(body)); } +void Session::SetOption(const LowSpeed& low_speed) { SetLowSpeed(low_speed); } +void Session::SetOption(const VerifySsl& verify) { SetVerifySsl(verify); } +void Session::SetOption(const Verbose& verbose) { SetVerbose(verbose); } +void Session::SetOption(const UnixSocket& unix_socket) { SetUnixSocket(unix_socket); } +void Session::SetOption(const SslOptions& options) { SetSslOptions(options); } +void Session::SetOption(const Interface& iface) { SetInterface(iface); } +void Session::SetOption(const LocalPort& local_port) { SetLocalPort(local_port); } +void Session::SetOption(const LocalPortRange& local_port_range) { SetLocalPortRange(local_port_range); } +void Session::SetOption(const HttpVersion& version) { SetHttpVersion(version); } +void Session::SetOption(const Range& range) { SetRange(range); } +void Session::SetOption(const MultiRange& multi_range) { SetMultiRange(multi_range); } +void Session::SetOption(const ReserveSize& reserve_size) { SetReserveSize(reserve_size.size); } +void Session::SetOption(const AcceptEncoding& accept_encoding) { SetAcceptEncoding(accept_encoding); } +void Session::SetOption(AcceptEncoding&& accept_encoding) { SetAcceptEncoding(accept_encoding); } // clang-format on + +void Session::SetCancellationParam(std::shared_ptr param) { + cancellationcb_ = CancellationCallback{std::move(param)}; + isCancellable = true; +#if LIBCURL_VERSION_NUM < 0x072000 // 7.32.0 + curl_easy_setopt(curl_->handle, CURLOPT_PROGRESSFUNCTION, cpr::util::progressUserFunction); + curl_easy_setopt(curl_->handle, CURLOPT_PROGRESSDATA, &cancellationcb_); +#else + curl_easy_setopt(curl_->handle, CURLOPT_XFERINFOFUNCTION, cpr::util::progressUserFunction); + curl_easy_setopt(curl_->handle, CURLOPT_XFERINFODATA, &cancellationcb_); +#endif + curl_easy_setopt(curl_->handle, CURLOPT_NOPROGRESS, 0L); +} } // namespace cpr diff --git a/vendor/CPR/cpr/ssl_ctx.cpp b/vendor/CPR/cpr/ssl_ctx.cpp new file mode 100644 index 00000000..a8d14eb8 --- /dev/null +++ b/vendor/CPR/cpr/ssl_ctx.cpp @@ -0,0 +1,70 @@ + +#include "cpr/ssl_ctx.h" + +#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION + +#ifdef OPENSSL_BACKEND_USED + +#include +#include +#include + +namespace cpr { + +/** + * The ssl_ctx parameter is actually a pointer to the SSL library's SSL_CTX for OpenSSL. + * If an error is returned from the callback no attempt to establish a connection is made and + * the perform operation will return the callback's error code. + * + * Sources: https://curl.se/libcurl/c/CURLOPT_SSL_CTX_FUNCTION.html + * https://curl.se/libcurl/c/CURLOPT_SSL_CTX_DATA.html + */ +CURLcode sslctx_function_load_ca_cert_from_buffer(CURL* /*curl*/, void* sslctx, void* raw_cert_buf) { + // Check arguments + if (raw_cert_buf == nullptr || sslctx == nullptr) { + printf("Invalid callback arguments\n"); + return CURLE_ABORTED_BY_CALLBACK; + } + + // Setup pointer + X509_STORE* store = nullptr; + X509* cert = nullptr; + BIO* bio = nullptr; + char* cert_buf = static_cast(raw_cert_buf); + + // Create a memory BIO using the data of cert_buf. + // Note: It is assumed, that cert_buf is nul terminated and its length is determined by strlen. + bio = BIO_new_mem_buf(cert_buf, -1); + + // Load the PEM formatted certicifate into an X509 structure which OpenSSL can use. + PEM_read_bio_X509(bio, &cert, nullptr, nullptr); + if (cert == nullptr) { + printf("PEM_read_bio_X509 failed\n"); + return CURLE_ABORTED_BY_CALLBACK; + } + + // Get a pointer to the current certificate verification storage + store = SSL_CTX_get_cert_store(static_cast(sslctx)); + + // Add the loaded certificate to the verification storage + const int status = X509_STORE_add_cert(store, cert); + if (status == 0) { + printf("Error adding certificate\n"); + return CURLE_ABORTED_BY_CALLBACK; + } + + // Decrement the reference count of the X509 structure cert and frees it up + X509_free(cert); + + // Free the entire bio chain + BIO_free(bio); + + // The CA certificate was loaded successfully into the verification storage + return CURLE_OK; +} + +} // namespace cpr + +#endif // OPENSSL_BACKEND_USED + +#endif // SUPPORT_CURLOPT_SSL_CTX_FUNCTION diff --git a/vendor/CPR/cpr/threadpool.cpp b/vendor/CPR/cpr/threadpool.cpp new file mode 100644 index 00000000..8ab1ee82 --- /dev/null +++ b/vendor/CPR/cpr/threadpool.cpp @@ -0,0 +1,148 @@ +#include "cpr/threadpool.h" + +namespace cpr { + +ThreadPool::ThreadPool(size_t min_threads, size_t max_threads, std::chrono::milliseconds max_idle_ms) : min_thread_num(min_threads), max_thread_num(max_threads), max_idle_time(max_idle_ms), status(STOP), cur_thread_num(0), idle_thread_num(0) {} + +ThreadPool::~ThreadPool() { + Stop(); +} + +int ThreadPool::Start(size_t start_threads) { + if (status != STOP) { + return -1; + } + status = RUNNING; + if (start_threads < min_thread_num) { + start_threads = min_thread_num; + } + if (start_threads > max_thread_num) { + start_threads = max_thread_num; + } + for (size_t i = 0; i < start_threads; ++i) { + CreateThread(); + } + return 0; +} + +int ThreadPool::Stop() { + if (status == STOP) { + return -1; + } + status = STOP; + task_cond.notify_all(); + for (auto& i : threads) { + if (i.thread->joinable()) { + i.thread->join(); + } + } + threads.clear(); + cur_thread_num = 0; + idle_thread_num = 0; + return 0; +} + +int ThreadPool::Pause() { + if (status == RUNNING) { + status = PAUSE; + } + return 0; +} + +int ThreadPool::Resume() { + if (status == PAUSE) { + status = RUNNING; + } + return 0; +} + +int ThreadPool::Wait() { + while (true) { + if (status == STOP || (tasks.empty() && idle_thread_num == cur_thread_num)) { + break; + } + std::this_thread::yield(); + } + return 0; +} + +bool ThreadPool::CreateThread() { + if (cur_thread_num >= max_thread_num) { + return false; + } + std::thread* thread = new std::thread([this] { + bool initialRun = true; + while (status != STOP) { + while (status == PAUSE) { + std::this_thread::yield(); + } + + Task task; + { + std::unique_lock locker(task_mutex); + task_cond.wait_for(locker, std::chrono::milliseconds(max_idle_time), [this]() { return status == STOP || !tasks.empty(); }); + if (status == STOP) { + return; + } + if (tasks.empty()) { + if (cur_thread_num > min_thread_num) { + DelThread(std::this_thread::get_id()); + return; + } + continue; + } + if (!initialRun) { + --idle_thread_num; + } + task = std::move(tasks.front()); + tasks.pop(); + } + if (task) { + task(); + ++idle_thread_num; + if (initialRun) { + initialRun = false; + } + } + } + }); + AddThread(thread); + return true; +} + +void ThreadPool::AddThread(std::thread* thread) { + thread_mutex.lock(); + ++cur_thread_num; + ThreadData data; + data.thread = std::shared_ptr(thread); + data.id = thread->get_id(); + data.status = RUNNING; + data.start_time = time(nullptr); + data.stop_time = 0; + threads.emplace_back(data); + thread_mutex.unlock(); +} + +void ThreadPool::DelThread(std::thread::id id) { + const time_t now = time(nullptr); + thread_mutex.lock(); + --cur_thread_num; + --idle_thread_num; + auto iter = threads.begin(); + while (iter != threads.end()) { + if (iter->status == STOP && now > iter->stop_time) { + if (iter->thread->joinable()) { + iter->thread->join(); + iter = threads.erase(iter); + continue; + } + } else if (iter->id == id) { + iter->status = STOP; + iter->stop_time = time(nullptr); + } + ++iter; + } + thread_mutex.unlock(); +} + +} // namespace cpr diff --git a/vendor/CPR/cpr/timeout.cpp b/vendor/CPR/cpr/timeout.cpp index 986e3e71..5bcd73b2 100644 --- a/vendor/CPR/cpr/timeout.cpp +++ b/vendor/CPR/cpr/timeout.cpp @@ -14,12 +14,12 @@ long Timeout::Milliseconds() const { // No way around since curl uses a long here. // NOLINTNEXTLINE(google-runtime-int) - if (ms.count() > std::numeric_limits::max()) { + if (ms.count() > static_cast(std::numeric_limits::max())) { throw std::overflow_error("cpr::Timeout: timeout value overflow: " + std::to_string(ms.count()) + " ms."); } // No way around since curl uses a long here. // NOLINTNEXTLINE(google-runtime-int) - if (ms.count() < std::numeric_limits::min()) { + if (ms.count() < static_cast(std::numeric_limits::min())) { throw std::underflow_error("cpr::Timeout: timeout value underflow: " + std::to_string(ms.count()) + " ms."); } diff --git a/vendor/CPR/cpr/util.cpp b/vendor/CPR/cpr/util.cpp index 9cf425ed..ff0b6c28 100644 --- a/vendor/CPR/cpr/util.cpp +++ b/vendor/CPR/cpr/util.cpp @@ -3,23 +3,64 @@ #include #include #include +#include #include #include #include +#include #include #include #include -namespace cpr { -namespace util { +#if defined(_Win32) +#include +#else +#ifdef __clang__ +#pragma clang diagnostic push +#if __has_warning("-Wreserved-macro-identifier") // Not all versions of clang support this flag like the one used on Ubuntu 18.04 +#pragma clang diagnostic ignored "-Wreserved-macro-identifier" +#endif +#pragma clang diagnostic ignored "-Wunused-macros" +#endif +// https://en.cppreference.com/w/c/string/byte/memset +// NOLINTNEXTLINE(bugprone-reserved-identifier, cert-dcl37-c, cert-dcl51-cpp, cppcoreguidelines-macro-usage) +#define __STDC_WANT_LIB_EXT1__ 1 +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#include +#endif + +namespace cpr::util { + +enum class CurlHTTPCookieField : size_t { + Domain = 0, + IncludeSubdomains, + Path, + HttpsOnly, + Expires, + Name, + Value, +}; Cookies parseCookies(curl_slist* raw_cookies) { + const int CURL_HTTP_COOKIE_SIZE = static_cast(CurlHTTPCookieField::Value) + 1; Cookies cookies; for (curl_slist* nc = raw_cookies; nc; nc = nc->next) { std::vector tokens = cpr::util::split(nc->data, '\t'); - std::string value = tokens.back(); - tokens.pop_back(); - cookies[tokens.back()] = value; + while (tokens.size() < CURL_HTTP_COOKIE_SIZE) { + tokens.emplace_back(""); + } + const std::time_t expires = static_cast(std::stoul(tokens.at(static_cast(CurlHTTPCookieField::Expires)))); + cookies.emplace_back(Cookie{ + tokens.at(static_cast(CurlHTTPCookieField::Name)), + tokens.at(static_cast(CurlHTTPCookieField::Value)), + tokens.at(static_cast(CurlHTTPCookieField::Domain)), + isTrue(tokens.at(static_cast(CurlHTTPCookieField::IncludeSubdomains))), + tokens.at(static_cast(CurlHTTPCookieField::Path)), + isTrue(tokens.at(static_cast(CurlHTTPCookieField::HttpsOnly))), + std::chrono::system_clock::from_time_t(expires), + }); } return cookies; } @@ -36,7 +77,6 @@ Header parseHeader(const std::string& headers, std::string* status_line, std::st } for (std::string& line : lines) { - // NOLINTNEXTLINE (cppcoreguidelines-avoid-magic-numbers) if (line.substr(0, 5) == "HTTP/") { // set the status_line if it was given if ((status_line != nullptr) || (reason != nullptr)) { @@ -47,7 +87,7 @@ Header parseHeader(const std::string& headers, std::string* status_line, std::st // set the reason if it was given if (reason != nullptr) { - size_t pos1 = line.find_first_of("\t "); + const size_t pos1 = line.find_first_of("\t "); size_t pos2 = std::string::npos; if (pos1 != std::string::npos) { pos2 = line.find_first_of("\t ", pos1 + 1); @@ -62,7 +102,7 @@ Header parseHeader(const std::string& headers, std::string* status_line, std::st } if (line.length() > 0) { - size_t found = line.find(':'); + const size_t found = line.find(':'); if (found != std::string::npos) { std::string value = line.substr(found + 1); value.erase(0, value.find_first_not_of("\t ")); @@ -105,7 +145,7 @@ size_t writeFunction(char* ptr, size_t size, size_t nmemb, std::string* data) { size_t writeFileFunction(char* ptr, size_t size, size_t nmemb, std::ofstream* file) { size *= nmemb; - file->write(ptr, size); + file->write(ptr, static_cast(size)); return size; } @@ -114,19 +154,8 @@ size_t writeUserFunction(char* ptr, size_t size, size_t nmemb, const WriteCallba return (*write)({ptr, size}) ? size : 0; } -#if LIBCURL_VERSION_NUM < 0x072000 -int progressUserFunction(const ProgressCallback* progress, double dltotal, double dlnow, - double ultotal, double ulnow) { -#else -int progressUserFunction(const ProgressCallback* progress, curl_off_t dltotal, curl_off_t dlnow, - curl_off_t ultotal, curl_off_t ulnow) { -#endif - return (*progress)(dltotal, dlnow, ultotal, ulnow) ? 0 : 1; -} - -int debugUserFunction(CURL* /*handle*/, curl_infotype type, char* data, size_t size, - const DebugCallback* debug) { - (*debug)(DebugCallback::InfoType(type), std::string(data, size)); +int debugUserFunction(CURL* /*handle*/, curl_infotype type, char* data, size_t size, const DebugCallback* debug) { + (*debug)(static_cast(type), std::string(data, size)); return 0; } @@ -141,7 +170,7 @@ int debugUserFunction(CURL* /*handle*/, curl_infotype type, char* data, size_t s * std::string result = holder.urlEncode(input); **/ std::string urlEncode(const std::string& s) { - CurlHolder holder; // Create a temporary new holder for URL encoding + const CurlHolder holder; // Create a temporary new holder for URL encoding return holder.urlEncode(s); } @@ -156,9 +185,54 @@ std::string urlEncode(const std::string& s) { * std::string result = holder.urlDecode(input); **/ std::string urlDecode(const std::string& s) { - CurlHolder holder; // Create a temporary new holder for URL decoding + const CurlHolder holder; // Create a temporary new holder for URL decoding return holder.urlDecode(s); } -} // namespace util -} // namespace cpr +#if defined(__STDC_LIB_EXT1__) +void secureStringClear(std::string& s) { + if (s.empty()) { + return; + } + memset_s(&s.front(), s.length(), 0, s.length()); + s.clear(); +} +#elif defined(_WIN32) +void secureStringClear(std::string& s) { + if (s.empty()) { + return; + } + SecureZeroMemory(&s.front(), s.length()); + s.clear(); +} +#else +#if defined(__clang__) +#pragma clang optimize off // clang +#elif defined(__GNUC__) || defined(__MINGW32__) || defined(__MINGW32__) || defined(__MINGW64__) +#pragma GCC push_options // g++ +#pragma GCC optimize("O0") // g++ +#endif +void secureStringClear(std::string& s) { + if (s.empty()) { + return; + } + // NOLINTNEXTLINE (readability-container-data-pointer) + char* ptr = &(s[0]); + memset(ptr, '\0', s.length()); + s.clear(); +} + +#if defined(__clang__) +#pragma clang optimize on // clang +#elif defined(__GNUC__) || defined(__MINGW32__) || defined(__MINGW32__) || defined(__MINGW64__) +#pragma GCC pop_options // g++ +#endif +#endif + +bool isTrue(const std::string& s) { + std::string temp_string{s}; + std::transform(temp_string.begin(), temp_string.end(), temp_string.begin(), [](unsigned char c) { return std::tolower(c); }); + return temp_string == "true"; +} + +} // namespace cpr::util diff --git a/vendor/CPR/include/cpr/accept_encoding.h b/vendor/CPR/include/cpr/accept_encoding.h new file mode 100644 index 00000000..167d7c2c --- /dev/null +++ b/vendor/CPR/include/cpr/accept_encoding.h @@ -0,0 +1,41 @@ +#ifndef CPR_ACCEPT_ENCODING_H +#define CPR_ACCEPT_ENCODING_H + +#include +#include +#include +#include +#include + +namespace cpr { + +enum class AcceptEncodingMethods { + identity, + deflate, + zlib, + gzip, + disabled, +}; + +// NOLINTNEXTLINE(cert-err58-cpp) +static const std::map AcceptEncodingMethodsStringMap{{AcceptEncodingMethods::identity, "identity"}, {AcceptEncodingMethods::deflate, "deflate"}, {AcceptEncodingMethods::zlib, "zlib"}, {AcceptEncodingMethods::gzip, "gzip"}, {AcceptEncodingMethods::disabled, "disabled"}}; + +class AcceptEncoding { + public: + AcceptEncoding() = default; + // NOLINTNEXTLINE(google-explicit-constructor) + AcceptEncoding(const std::initializer_list& methods); + // NOLINTNEXTLINE(google-explicit-constructor) + AcceptEncoding(const std::initializer_list& methods); + + [[nodiscard]] bool empty() const noexcept; + [[nodiscard]] const std::string getString() const; + [[nodiscard]] bool disabled() const; + + private: + std::unordered_set methods_; +}; + +} // namespace cpr + +#endif diff --git a/vendor/CPR/include/cpr/api.h b/vendor/CPR/include/cpr/api.h index 089924b3..ba9c64e6 100644 --- a/vendor/CPR/include/cpr/api.h +++ b/vendor/CPR/include/cpr/api.h @@ -7,27 +7,108 @@ #include #include +#include "cpr/async.h" +#include "cpr/async_wrapper.h" #include "cpr/auth.h" #include "cpr/bearer.h" #include "cpr/cprtypes.h" -#include "cpr/digest.h" #include "cpr/multipart.h" -#include "cpr/ntlm.h" +#include "cpr/multiperform.h" #include "cpr/payload.h" #include "cpr/response.h" #include "cpr/session.h" -#include +#include namespace cpr { -using AsyncResponse = std::future; +using AsyncResponse = AsyncWrapper; namespace priv { +template +void set_option_internal(Session& session, CurrentType&& current_option) { + session.SetOption(std::forward(current_option)); +} + +template <> +inline void set_option_internal(Session& session, Header&& current_option) { + // Header option was already provided -> Update previous header + session.UpdateHeader(std::forward
(current_option)); +} + +template +void set_option_internal(Session& session, CurrentType&& current_option, Ts&&... ts) { + set_option_internal(session, std::forward(current_option)); + + if (std::is_same::value) { + set_option_internal(session, std::forward(ts)...); + } else { + set_option_internal(session, std::forward(ts)...); + } +} + template void set_option(Session& session, Ts&&... ts) { - std::initializer_list ignore = { (session.SetOption(std::forward(ts)), 0)... }; - (void)ignore; + set_option_internal(session, std::forward(ts)...); +} + +// Idea: https://stackoverflow.com/a/19060157 +template +void apply_set_option_internal(Session& session, Tuple&& t, std::index_sequence) { + set_option(session, std::get(std::forward(t))...); +} + +// Idea: https://stackoverflow.com/a/19060157 +template +void apply_set_option(Session& session, Tuple&& t) { + using Indices = std::make_index_sequence>::value>; + apply_set_option_internal(session, std::forward(t), Indices()); +} + +template +void setup_multiperform_internal(MultiPerform& multiperform, T&& t) { + std::shared_ptr session = std::make_shared(); + apply_set_option(*session, t); + multiperform.AddSession(session); +} + +template +void setup_multiperform_internal(MultiPerform& multiperform, T&& t, Ts&&... ts) { + std::shared_ptr session = std::make_shared(); + apply_set_option(*session, t); + multiperform.AddSession(session); + setup_multiperform_internal(multiperform, std::forward(ts)...); +} + +template +void setup_multiperform(MultiPerform& multiperform, Ts&&... ts) { + setup_multiperform_internal(multiperform, std::forward(ts)...); +} + +using session_action_t = cpr::Response (cpr::Session::*)(); + +template +void setup_multiasync(std::vector>& responses, T&& parameters) { + std::shared_ptr cancellation_state = std::make_shared(false); + + std::function execFn{[cancellation_state](T params) { + if (cancellation_state->load()) { + return Response{}; + } + cpr::Session s{}; + s.SetCancellationParam(cancellation_state); + apply_set_option(s, std::forward(params)); + return std::invoke(SessionAction, s); + }}; + responses.emplace_back(GlobalThreadPool::GetInstance()->Submit(std::move(execFn), std::forward(parameters)), std::move(cancellation_state)); +} + +template +void setup_multiasync(std::vector>& responses, T&& head, Ts&&... tail) { + setup_multiasync(responses, std::forward(head)); + if constexpr (sizeof...(Ts) > 0) { + setup_multiasync(responses, std::forward(tail)...); + } } } // namespace priv @@ -43,17 +124,14 @@ Response Get(Ts&&... ts) { // Get async methods template AsyncResponse GetAsync(Ts... ts) { - return std::async( - std::launch::async, [](Ts... ts_inner) { return Get(std::move(ts_inner)...); }, std::move(ts)...); + return cpr::async([](Ts... ts_inner) { return Get(std::move(ts_inner)...); }, std::move(ts)...); } // Get callback methods template // NOLINTNEXTLINE(fuchsia-trailing-return) -auto GetCallback(Then then, Ts... ts) -> std::future { - return std::async( - std::launch::async, [](Then then_inner, Ts... ts_inner) { return then_inner(Get(std::move(ts_inner)...)); }, - std::move(then), std::move(ts)...); +auto GetCallback(Then then, Ts... ts) { + return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Get(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...); } // Post methods @@ -67,17 +145,14 @@ Response Post(Ts&&... ts) { // Post async methods template AsyncResponse PostAsync(Ts... ts) { - return std::async( - std::launch::async, [](Ts... ts_inner) { return Post(std::move(ts_inner)...); }, std::move(ts)...); + return cpr::async([](Ts... ts_inner) { return Post(std::move(ts_inner)...); }, std::move(ts)...); } // Post callback methods template // NOLINTNEXTLINE(fuchsia-trailing-return) -auto PostCallback(Then then, Ts... ts) -> std::future { - return std::async( - std::launch::async, [](Then then_inner, Ts... ts_inner) { return then_inner(Post(std::move(ts_inner)...)); }, - std::move(then), std::move(ts)...); +auto PostCallback(Then then, Ts... ts) { + return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Post(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...); } // Put methods @@ -91,17 +166,14 @@ Response Put(Ts&&... ts) { // Put async methods template AsyncResponse PutAsync(Ts... ts) { - return std::async( - std::launch::async, [](Ts... ts_inner) { return Put(std::move(ts_inner)...); }, std::move(ts)...); + return cpr::async([](Ts... ts_inner) { return Put(std::move(ts_inner)...); }, std::move(ts)...); } // Put callback methods template // NOLINTNEXTLINE(fuchsia-trailing-return) -auto PutCallback(Then then, Ts... ts) -> std::future { - return std::async( - std::launch::async, [](Then then_inner, Ts... ts_inner) { return then_inner(Put(std::move(ts_inner)...)); }, - std::move(then), std::move(ts)...); +auto PutCallback(Then then, Ts... ts) { + return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Put(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...); } // Head methods @@ -115,17 +187,14 @@ Response Head(Ts&&... ts) { // Head async methods template AsyncResponse HeadAsync(Ts... ts) { - return std::async( - std::launch::async, [](Ts... ts_inner) { return Head(std::move(ts_inner)...); }, std::move(ts)...); + return cpr::async([](Ts... ts_inner) { return Head(std::move(ts_inner)...); }, std::move(ts)...); } // Head callback methods template // NOLINTNEXTLINE(fuchsia-trailing-return) -auto HeadCallback(Then then, Ts... ts) -> std::future { - return std::async( - std::launch::async, [](Then then_inner, Ts... ts_inner) { return then_inner(Head(std::move(ts_inner)...)); }, - std::move(then), std::move(ts)...); +auto HeadCallback(Then then, Ts... ts) { + return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Head(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...); } // Delete methods @@ -139,18 +208,14 @@ Response Delete(Ts&&... ts) { // Delete async methods template AsyncResponse DeleteAsync(Ts... ts) { - return std::async( - std::launch::async, [](Ts... ts_inner) { return Delete(std::move(ts_inner)...); }, - std::move(ts)...); + return cpr::async([](Ts... ts_inner) { return Delete(std::move(ts_inner)...); }, std::move(ts)...); } // Delete callback methods template // NOLINTNEXTLINE(fuchsia-trailing-return) -auto DeleteCallback(Then then, Ts... ts) -> std::future { - return std::async( - std::launch::async, [](Then then_inner, Ts... ts_inner) { return then_inner(Delete(std::move(ts_inner)...)); }, - std::move(then), std::move(ts)...); +auto DeleteCallback(Then then, Ts... ts) { + return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Delete(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...); } // Options methods @@ -164,19 +229,14 @@ Response Options(Ts&&... ts) { // Options async methods template AsyncResponse OptionsAsync(Ts... ts) { - return std::async( - std::launch::async, [](Ts... ts_inner) { return Options(std::move(ts_inner)...); }, - std::move(ts)...); + return cpr::async([](Ts... ts_inner) { return Options(std::move(ts_inner)...); }, std::move(ts)...); } // Options callback methods template // NOLINTNEXTLINE(fuchsia-trailing-return) -auto OptionsCallback(Then then, Ts... ts) - -> std::future { - return std::async( - std::launch::async, [](Then then_inner, Ts... ts_inner) { return then_inner(Options(std::move(ts_inner)...)); }, - std::move(then), std::move(ts)...); +auto OptionsCallback(Then then, Ts... ts) { + return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Options(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...); } // Patch methods @@ -190,17 +250,14 @@ Response Patch(Ts&&... ts) { // Patch async methods template AsyncResponse PatchAsync(Ts... ts) { - return std::async( - std::launch::async, [](Ts... ts_inner) { return Patch(std::move(ts_inner)...); }, std::move(ts)...); + return cpr::async([](Ts... ts_inner) { return Patch(std::move(ts_inner)...); }, std::move(ts)...); } // Patch callback methods template // NOLINTNEXTLINE(fuchsia-trailing-return) -auto PatchCallback(Then then, Ts... ts) -> std::future { - return std::async( - std::launch::async, [](Then then_inner, Ts... ts_inner) { return then_inner(Patch(std::move(ts_inner)...)); }, - std::move(then), std::move(ts)...); +auto PatchCallback(Then then, Ts... ts) { + return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Patch(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...); } // Download methods @@ -211,6 +268,18 @@ Response Download(std::ofstream& file, Ts&&... ts) { return session.Download(file); } +// Download async method +template +AsyncResponse DownloadAsync(fs::path local_path, Ts... ts) { + return AsyncWrapper{std::async( + std::launch::async, + [](fs::path local_path_, Ts... ts_) { + std::ofstream f(local_path_.c_str()); + return Download(f, std::move(ts_)...); + }, + std::move(local_path), std::move(ts)...)}; +} + // Download with user callback template Response Download(const WriteCallback& write, Ts&&... ts) { @@ -219,6 +288,105 @@ Response Download(const WriteCallback& write, Ts&&... ts) { return session.Download(write); } +// Multi requests +template +std::vector MultiGet(Ts&&... ts) { + MultiPerform multiperform; + priv::setup_multiperform(multiperform, std::forward(ts)...); + return multiperform.Get(); +} + +template +std::vector MultiDelete(Ts&&... ts) { + MultiPerform multiperform; + priv::setup_multiperform(multiperform, std::forward(ts)...); + return multiperform.Delete(); +} + +template +std::vector MultiPut(Ts&&... ts) { + MultiPerform multiperform; + priv::setup_multiperform(multiperform, std::forward(ts)...); + return multiperform.Put(); +} + +template +std::vector MultiHead(Ts&&... ts) { + MultiPerform multiperform; + priv::setup_multiperform(multiperform, std::forward(ts)...); + return multiperform.Head(); +} + +template +std::vector MultiOptions(Ts&&... ts) { + MultiPerform multiperform; + priv::setup_multiperform(multiperform, std::forward(ts)...); + return multiperform.Options(); +} + +template +std::vector MultiPatch(Ts&&... ts) { + MultiPerform multiperform; + priv::setup_multiperform(multiperform, std::forward(ts)...); + return multiperform.Patch(); +} + +template +std::vector MultiPost(Ts&&... ts) { + MultiPerform multiperform; + priv::setup_multiperform(multiperform, std::forward(ts)...); + return multiperform.Post(); +} + +template +std::vector> MultiGetAsync(Ts&&... ts) { + std::vector> ret{}; + priv::setup_multiasync<&cpr::Session::Get>(ret, std::forward(ts)...); + return ret; +} + +template +std::vector> MultiDeleteAsync(Ts&&... ts) { + std::vector> ret{}; + priv::setup_multiasync<&cpr::Session::Delete>(ret, std::forward(ts)...); + return ret; +} + +template +std::vector> MultiHeadAsync(Ts&&... ts) { + std::vector> ret{}; + priv::setup_multiasync<&cpr::Session::Head>(ret, std::forward(ts)...); + return ret; +} +template +std::vector> MultiOptionsAsync(Ts&&... ts) { + std::vector> ret{}; + priv::setup_multiasync<&cpr::Session::Options>(ret, std::forward(ts)...); + return ret; +} + +template +std::vector> MultiPatchAsync(Ts&&... ts) { + std::vector> ret{}; + priv::setup_multiasync<&cpr::Session::Patch>(ret, std::forward(ts)...); + return ret; +} + +template +std::vector> MultiPostAsync(Ts&&... ts) { + std::vector> ret{}; + priv::setup_multiasync<&cpr::Session::Post>(ret, std::forward(ts)...); + return ret; +} + +template +std::vector> MultiPutAsync(Ts&&... ts) { + std::vector> ret{}; + priv::setup_multiasync<&cpr::Session::Put>(ret, std::forward(ts)...); + return ret; +} + + } // namespace cpr #endif diff --git a/vendor/CPR/include/cpr/async.h b/vendor/CPR/include/cpr/async.h new file mode 100644 index 00000000..1305834f --- /dev/null +++ b/vendor/CPR/include/cpr/async.h @@ -0,0 +1,50 @@ +#ifndef CPR_ASYNC_H +#define CPR_ASYNC_H + +#include "async_wrapper.h" +#include "singleton.h" +#include "threadpool.h" + +namespace cpr { + +class GlobalThreadPool : public ThreadPool { + CPR_SINGLETON_DECL(GlobalThreadPool) + protected: + GlobalThreadPool() = default; + + public: + ~GlobalThreadPool() override = default; +}; + +/** + * Return a wrapper for a future, calling future.get() will wait until the task is done and return RetType. + * async(fn, args...) + * async(std::bind(&Class::mem_fn, &obj)) + * async(std::mem_fn(&Class::mem_fn, &obj)) + **/ +template +auto async(Fn&& fn, Args&&... args) { + return AsyncWrapper{GlobalThreadPool::GetInstance()->Submit(std::forward(fn), std::forward(args)...)}; +} + +class async { + public: + static void startup(size_t min_threads = CPR_DEFAULT_THREAD_POOL_MIN_THREAD_NUM, size_t max_threads = CPR_DEFAULT_THREAD_POOL_MAX_THREAD_NUM, std::chrono::milliseconds max_idle_ms = CPR_DEFAULT_THREAD_POOL_MAX_IDLE_TIME) { + GlobalThreadPool* gtp = GlobalThreadPool::GetInstance(); + if (gtp->IsStarted()) { + return; + } + gtp->SetMinThreadNum(min_threads); + gtp->SetMaxThreadNum(max_threads); + gtp->SetMaxIdleTime(max_idle_ms); + gtp->Start(); + } + + static void cleanup() { + GlobalThreadPool::ExitInstance(); + } +}; + +} // namespace cpr + +#endif diff --git a/vendor/CPR/include/cpr/async_wrapper.h b/vendor/CPR/include/cpr/async_wrapper.h new file mode 100644 index 00000000..bb46bf9d --- /dev/null +++ b/vendor/CPR/include/cpr/async_wrapper.h @@ -0,0 +1,140 @@ +#ifndef CPR_ASYNC_WRAPPER_H +#define CPR_ASYNC_WRAPPER_H + +#include +#include +#include + +#include "cpr/response.h" + +namespace cpr { +enum class [[nodiscard]] CancellationResult { failure, success, invalid_operation }; + +/** + * A class template intended to wrap results of async operations (instances of std::future) + * and also provide extended capablilities relaed to these requests, for example cancellation. + * + * The RAII semantics are the same as std::future - moveable, not copyable. + */ +template +class AsyncWrapper { + private: + std::future future; + std::shared_ptr is_cancelled; + + public: + // Constructors + explicit AsyncWrapper(std::future&& f) : future{std::move(f)} {} + AsyncWrapper(std::future&& f, std::shared_ptr&& cancelledState) : future{std::move(f)}, is_cancelled{std::move(cancelledState)} {} + + // Copy Semantics + AsyncWrapper(const AsyncWrapper&) = delete; + AsyncWrapper& operator=(const AsyncWrapper&) = delete; + + // Move Semantics + AsyncWrapper(AsyncWrapper&&) noexcept = default; + AsyncWrapper& operator=(AsyncWrapper&&) noexcept = default; + + // Destructor + ~AsyncWrapper() { + if constexpr (isCancellable) { + if(is_cancelled) { + is_cancelled->store(true); + } + } + } + // These methods replicate the behaviour of std::future + [[nodiscard]] T get() { + if constexpr (isCancellable) { + if (IsCancelled()) { + throw std::logic_error{"Calling AsyncWrapper::get on a cancelled request!"}; + } + } + if (!future.valid()) { + throw std::logic_error{"Calling AsyncWrapper::get when the associated future instance is invalid!"}; + } + return future.get(); + } + + [[nodiscard]] bool valid() const noexcept { + if constexpr (isCancellable) { + return !is_cancelled->load() && future.valid(); + } else { + return future.valid(); + } + } + + void wait() const { + if constexpr (isCancellable) { + if (is_cancelled->load()) { + throw std::logic_error{"Calling AsyncWrapper::wait when the associated future is invalid or cancelled!"}; + } + } + if (!future.valid()) { + throw std::logic_error{"Calling AsyncWrapper::wait_until when the associated future is invalid!"}; + } + future.wait(); + } + + template + std::future_status wait_for(const std::chrono::duration& timeout_duration) const { + if constexpr (isCancellable) { + if (IsCancelled()) { + throw std::logic_error{"Calling AsyncWrapper::wait_for when the associated future is cancelled!"}; + } + } + if (!future.valid()) { + throw std::logic_error{"Calling AsyncWrapper::wait_until when the associated future is invalid!"}; + } + return future.wait_for(timeout_duration); + } + + template + std::future_status wait_until(const std::chrono::time_point& timeout_time) const { + if constexpr (isCancellable) { + if (IsCancelled()) { + throw std::logic_error{"Calling AsyncWrapper::wait_until when the associated future is cancelled!"}; + } + } + if (!future.valid()) { + throw std::logic_error{"Calling AsyncWrapper::wait_until when the associated future is invalid!"}; + } + return future.wait_until(timeout_time); + } + + std::shared_future share() noexcept { + return future.share(); + } + + // Cancellation-related methods + CancellationResult Cancel() { + if constexpr (!isCancellable) { + return CancellationResult::invalid_operation; + } + if (!future.valid() || is_cancelled->load()) { + return CancellationResult::invalid_operation; + } + is_cancelled->store(true); + return CancellationResult::success; + } + + [[nodiscard]] bool IsCancelled() const { + if constexpr (isCancellable) { + return is_cancelled->load(); + } else { + return false; + } + } +}; + +// Deduction guides +template +AsyncWrapper(std::future&&) -> AsyncWrapper; + +template +AsyncWrapper(std::future&&, std::shared_ptr&&) -> AsyncWrapper; + +} // namespace cpr + + +#endif diff --git a/vendor/CPR/include/cpr/auth.h b/vendor/CPR/include/cpr/auth.h index 0da65e39..e7839694 100644 --- a/vendor/CPR/include/cpr/auth.h +++ b/vendor/CPR/include/cpr/auth.h @@ -7,23 +7,24 @@ namespace cpr { +enum class AuthMode { BASIC, DIGEST, NTLM }; + class Authentication { public: - Authentication(const std::string& username, const std::string& password) - : auth_string_{username + ":" + password} {} - Authentication(std::string&& username, std::string&& password) - : auth_string_{std::move(username) + ":" + std::move(password)} {} + Authentication(std::string username, std::string password, AuthMode auth_mode) : auth_string_{std::move(username) + ":" + std::move(password)}, auth_mode_{std::move(auth_mode)} {} Authentication(const Authentication& other) = default; Authentication(Authentication&& old) noexcept = default; - virtual ~Authentication() noexcept = default; + ~Authentication() noexcept; Authentication& operator=(Authentication&& old) noexcept = default; Authentication& operator=(const Authentication& other) = default; - virtual const char* GetAuthString() const noexcept; + const char* GetAuthString() const noexcept; + AuthMode GetAuthMode() const noexcept; - protected: + private: std::string auth_string_; + AuthMode auth_mode_; }; } // namespace cpr diff --git a/vendor/CPR/include/cpr/bearer.h b/vendor/CPR/include/cpr/bearer.h index c7e5d35a..5e58a7d9 100644 --- a/vendor/CPR/include/cpr/bearer.h +++ b/vendor/CPR/include/cpr/bearer.h @@ -1,8 +1,8 @@ #ifndef CPR_BEARER_H #define CPR_BEARER_H -#include #include +#include #include @@ -14,12 +14,10 @@ namespace cpr { class Bearer { public: // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) - Bearer(const std::string& token) : token_string_{token} {} - // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) - Bearer(std::string&& token) : token_string_{std::move(token)} {} + Bearer(std::string token) : token_string_{std::move(token)} {} Bearer(const Bearer& other) = default; Bearer(Bearer&& old) noexcept = default; - virtual ~Bearer() noexcept = default; + virtual ~Bearer() noexcept; Bearer& operator=(Bearer&& old) noexcept = default; Bearer& operator=(const Bearer& other) = default; diff --git a/vendor/CPR/include/cpr/body.h b/vendor/CPR/include/cpr/body.h index bafc89f9..f691d9c6 100644 --- a/vendor/CPR/include/cpr/body.h +++ b/vendor/CPR/include/cpr/body.h @@ -1,24 +1,46 @@ #ifndef CPR_BODY_H #define CPR_BODY_H +#include +#include #include #include +#include +#include "cpr/buffer.h" #include "cpr/cprtypes.h" +#include "cpr/file.h" namespace cpr { class Body : public StringHolder { public: - Body() : StringHolder() {} + Body() = default; // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) - Body(const std::string& body) : StringHolder(body) {} + Body(std::string body) : StringHolder(std::move(body)) {} // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) - Body(std::string&& body) : StringHolder(std::move(body)) {} + Body(std::string_view body) : StringHolder(body) {} // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) Body(const char* body) : StringHolder(body) {} Body(const char* str, size_t len) : StringHolder(str, len) {} Body(const std::initializer_list args) : StringHolder(args) {} + // NOLINTNEXTLINE(google-explicit-constructor, cppcoreguidelines-pro-type-reinterpret-cast) + Body(const Buffer& buffer) : StringHolder(reinterpret_cast(buffer.data), static_cast(buffer.datalen)) {} + // NOLINTNEXTLINE(google-explicit-constructor) + Body(const File& file) { + std::ifstream is(file.filepath, std::ifstream::binary); + if (!is) { + throw std::invalid_argument("Can't open the file for HTTP request body!"); + } + + is.seekg(0, std::ios::end); + const std::streampos length = is.tellg(); + is.seekg(0, std::ios::beg); + std::string buffer; + buffer.resize(static_cast(length)); + is.read(buffer.data(), length); + str_ = std::move(buffer); + } Body(const Body& other) = default; Body(Body&& old) noexcept = default; ~Body() override = default; diff --git a/vendor/CPR/include/cpr/buffer.h b/vendor/CPR/include/cpr/buffer.h new file mode 100644 index 00000000..5665faa0 --- /dev/null +++ b/vendor/CPR/include/cpr/buffer.h @@ -0,0 +1,33 @@ +#ifndef CPR_BUFFER_H +#define CPR_BUFFER_H + +#include + +#include + +namespace cpr { + +struct Buffer { + using data_t = const char*; + + template + Buffer(Iterator begin, Iterator end, fs::path&& p_filename) + // Ignored here since libcurl reqires a long. + // There is also no way around the reinterpret_cast. + // NOLINTNEXTLINE(google-runtime-int, cppcoreguidelines-pro-type-reinterpret-cast) + : data{reinterpret_cast(&(*begin))}, datalen{static_cast(std::distance(begin, end))}, filename(std::move(p_filename)) { + is_random_access_iterator(begin, end); + static_assert(sizeof(*begin) == 1, "Only byte buffers can be used"); + } + + template + typename std::enable_if::iterator_category, std::random_access_iterator_tag>::value>::type is_random_access_iterator(Iterator /* begin */, Iterator /* end */) {} + + data_t data; + size_t datalen; + const fs::path filename; +}; + +} // namespace cpr + +#endif diff --git a/vendor/CPR/include/cpr/callback.h b/vendor/CPR/include/cpr/callback.h index b90c1bbd..dc1c6eeb 100644 --- a/vendor/CPR/include/cpr/callback.h +++ b/vendor/CPR/include/cpr/callback.h @@ -3,7 +3,9 @@ #include "cprtypes.h" +#include #include +#include #include namespace cpr { @@ -14,11 +16,11 @@ class ReadCallback { // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) ReadCallback(std::function p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), size{-1}, callback{std::move(p_callback)} {} ReadCallback(cpr_off_t p_size, std::function p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), size{p_size}, callback{std::move(p_callback)} {} - bool operator()(char* buffer, size_t& size) const { - return callback(buffer, size, userdata); + bool operator()(char* buffer, size_t& buffer_size) const { + return callback(buffer, buffer_size, userdata); } - intptr_t userdata; + intptr_t userdata{}; cpr_off_t size{}; std::function callback; }; @@ -32,7 +34,7 @@ class HeaderCallback { return callback(std::move(header), userdata); } - intptr_t userdata; + intptr_t userdata{}; std::function callback; }; @@ -45,7 +47,7 @@ class WriteCallback { return callback(std::move(data), userdata); } - intptr_t userdata; + intptr_t userdata{}; std::function callback; }; @@ -53,13 +55,13 @@ class ProgressCallback { public: ProgressCallback() = default; // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) - ProgressCallback(std::function p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), callback(std::move(p_callback)) {} - bool operator()(cpr_off_t downloadTotal, cpr_off_t downloadNow, cpr_off_t uploadTotal, cpr_off_t uploadNow) const { + ProgressCallback(std::function p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), callback(std::move(p_callback)) {} + bool operator()(cpr_pf_arg_t downloadTotal, cpr_pf_arg_t downloadNow, cpr_pf_arg_t uploadTotal, cpr_pf_arg_t uploadNow) const { return callback(downloadTotal, downloadNow, uploadTotal, uploadNow, userdata); } - intptr_t userdata; - std::function callback; + intptr_t userdata{}; + std::function callback; }; class DebugCallback { @@ -80,10 +82,30 @@ class DebugCallback { callback(type, std::move(data), userdata); } - intptr_t userdata; + intptr_t userdata{}; std::function callback; }; +/** + * Functor class for progress functions that will be used in cancellable requests. + */ +class CancellationCallback { + public: + CancellationCallback() = default; + explicit CancellationCallback(std::shared_ptr&& cs) : cancellation_state{std::move(cs)} {} + + CancellationCallback(std::shared_ptr&& cs, ProgressCallback& u_cb) : cancellation_state{std::move(cs)}, user_cb{std::reference_wrapper{u_cb}} {} + + bool operator()(cpr_pf_arg_t dltotal, cpr_pf_arg_t dlnow, cpr_pf_arg_t ultotal, cpr_pf_arg_t ulnow) const; + + void SetProgressCallback(ProgressCallback& u_cb); + + private: + std::shared_ptr cancellation_state; + std::optional> user_cb; +}; + + } // namespace cpr #endif diff --git a/vendor/CPR/include/cpr/cert_info.h b/vendor/CPR/include/cpr/cert_info.h new file mode 100644 index 00000000..4d957b02 --- /dev/null +++ b/vendor/CPR/include/cpr/cert_info.h @@ -0,0 +1,37 @@ +#ifndef CPR_CERT_INFO_H +#define CPR_CERT_INFO_H + +#include +#include +#include + +namespace cpr { + +class CertInfo { + private: + std::vector cert_info_; + + public: + CertInfo() = default; + CertInfo(const CertInfo& other) = default; + CertInfo(CertInfo&& old) = default; + CertInfo(const std::initializer_list& entry) : cert_info_{entry} {} + ~CertInfo() noexcept = default; + + using iterator = std::vector::iterator; + using const_iterator = std::vector::const_iterator; + + std::string& operator[](const size_t& pos); + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + const_iterator cbegin() const; + const_iterator cend() const; + void emplace_back(const std::string& str); + void push_back(const std::string& str); + void pop_back(); +}; +} // namespace cpr + +#endif diff --git a/vendor/CPR/include/cpr/cookies.h b/vendor/CPR/include/cpr/cookies.h index ca1d8f75..4200ed84 100644 --- a/vendor/CPR/include/cpr/cookies.h +++ b/vendor/CPR/include/cpr/cookies.h @@ -2,12 +2,49 @@ #define CPR_COOKIES_H #include "cpr/curlholder.h" +#include #include -#include #include #include +#include namespace cpr { +/** + * EXPIRES_STRING_SIZE is an explicitly static and const variable that could be only accessed within the same namespace and is immutable. + * To be used for "std::array", the expression must have a constant value, so EXPIRES_STRING_SIZE must be a const value. + **/ +static const std::size_t EXPIRES_STRING_SIZE = 100; + +class Cookie { + public: + Cookie() = default; + /** + * Some notes for the default value used by expires: + * std::chrono::system_clock::time_point::min() won't work on Windows due to the min, max clash there. + * So we fall back to std::chrono::system_clock::from_time_t(0) for the minimum value here. + **/ + Cookie(const std::string& name, const std::string& value, const std::string& domain = "", bool p_isIncludingSubdomains = false, const std::string& path = "/", bool p_isHttpsOnly = false, std::chrono::system_clock::time_point expires = std::chrono::system_clock::from_time_t(0)) : name_{name}, value_{value}, domain_{domain}, includeSubdomains_{p_isIncludingSubdomains}, path_{path}, httpsOnly_{p_isHttpsOnly}, expires_{expires} {} + const std::string GetDomain() const; + bool IsIncludingSubdomains() const; + const std::string GetPath() const; + bool IsHttpsOnly() const; + const std::chrono::system_clock::time_point GetExpires() const; + const std::string GetExpiresString() const; + const std::string GetName() const; + const std::string GetValue() const; + + private: + std::string name_; + std::string value_; + std::string domain_; + bool includeSubdomains_{}; + std::string path_; + bool httpsOnly_{}; + /** + * TODO: Update the implementation using `std::chrono::utc_clock` of C++20 + **/ + std::chrono::system_clock::time_point expires_{}; +}; class Cookies { public: @@ -25,19 +62,16 @@ class Cookies { bool encode{true}; // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) - Cookies(bool p_encode = true) : encode(p_encode) {} - Cookies(const std::initializer_list>& pairs, - bool p_encode = true) - : encode(p_encode), map_{pairs} {} + Cookies(bool p_encode = true) : encode{p_encode} {} + Cookies(const std::initializer_list& cookies, bool p_encode = true) : encode{p_encode}, cookies_{cookies} {} // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) - Cookies(const std::map& map, bool p_encode = true) - : encode(p_encode), map_{map} {} + Cookies(const cpr::Cookie& cookie, bool p_encode = true) : encode{p_encode}, cookies_{cookie} {} - std::string& operator[](const std::string& key); - std::string GetEncoded(const CurlHolder& holder) const; + cpr::Cookie& operator[](size_t pos); + const std::string GetEncoded(const CurlHolder& holder) const; - using iterator = std::map::iterator; - using const_iterator = std::map::const_iterator; + using iterator = std::vector::iterator; + using const_iterator = std::vector::const_iterator; iterator begin(); iterator end(); @@ -45,9 +79,12 @@ class Cookies { const_iterator end() const; const_iterator cbegin() const; const_iterator cend() const; + void emplace_back(const Cookie& str); + void push_back(const Cookie& str); + void pop_back(); protected: - std::map map_; + std::vector cookies_; }; } // namespace cpr diff --git a/vendor/CPR/include/cpr/cpr.h b/vendor/CPR/include/cpr/cpr.h index 82330d2c..fbad1726 100644 --- a/vendor/CPR/include/cpr/cpr.h +++ b/vendor/CPR/include/cpr/cpr.h @@ -3,12 +3,43 @@ #include "cpr/api.h" #include "cpr/auth.h" +#include "cpr/bearer.h" +#include "cpr/callback.h" +#include "cpr/cert_info.h" +#include "cpr/connect_timeout.h" +#include "cpr/cookies.h" #include "cpr/cprtypes.h" +#include "cpr/cprver.h" +#include "cpr/curl_container.h" +#include "cpr/curlholder.h" +#include "cpr/error.h" +#include "cpr/http_version.h" +#include "cpr/interceptor.h" #include "cpr/interface.h" +#include "cpr/limit_rate.h" +#include "cpr/local_port.h" +#include "cpr/local_port_range.h" +#include "cpr/low_speed.h" +#include "cpr/multipart.h" +#include "cpr/multiperform.h" +#include "cpr/parameters.h" +#include "cpr/payload.h" +#include "cpr/proxies.h" +#include "cpr/proxyauth.h" +#include "cpr/range.h" #include "cpr/redirect.h" +#include "cpr/reserve_size.h" +#include "cpr/resolve.h" #include "cpr/response.h" #include "cpr/session.h" +#include "cpr/ssl_ctx.h" +#include "cpr/ssl_options.h" #include "cpr/status_codes.h" +#include "cpr/timeout.h" +#include "cpr/unix_socket.h" +#include "cpr/user_agent.h" +#include "cpr/util.h" +#include "cpr/verbose.h" #define CPR_LIBCURL_VERSION_NUM LIBCURL_VERSION_NUM diff --git a/vendor/CPR/include/cpr/cprtypes.h b/vendor/CPR/include/cpr/cprtypes.h index abd8dca3..65da7386 100644 --- a/vendor/CPR/include/cpr/cprtypes.h +++ b/vendor/CPR/include/cpr/cprtypes.h @@ -2,11 +2,13 @@ #define CPR_CPR_TYPES_H #include +#include #include #include #include #include #include +#include namespace cpr { @@ -15,12 +17,21 @@ namespace cpr { **/ using cpr_off_t = curl_off_t; +/** + * The argument type for progress functions, dependent on libcurl version + **/ +#if LIBCURL_VERSION_NUM < 0x072000 +using cpr_pf_arg_t = double; +#else +using cpr_pf_arg_t = cpr_off_t; +#endif + template class StringHolder { public: StringHolder() = default; - explicit StringHolder(const std::string& str) : str_(str) {} - explicit StringHolder(std::string&& str) : str_(std::move(str)) {} + explicit StringHolder(std::string str) : str_(std::move(str)) {} + explicit StringHolder(std::string_view str) : str_(str) {} explicit StringHolder(const char* str) : str_(str) {} StringHolder(const char* str, size_t len) : str_(str, len) {} StringHolder(const std::initializer_list args) { @@ -107,9 +118,9 @@ class Url : public StringHolder { public: Url() = default; // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) - Url(const std::string& url) : StringHolder(url) {} + Url(std::string url) : StringHolder(std::move(url)) {} // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) - Url(std::string&& url) : StringHolder(std::move(url)) {} + Url(std::string_view url) : StringHolder(url) {} // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) Url(const char* url) : StringHolder(url) {} Url(const char* str, size_t len) : StringHolder(std::string(str, len)) {} diff --git a/vendor/CPR/include/cpr/cprver.h b/vendor/CPR/include/cpr/cprver.h new file mode 100644 index 00000000..21106d11 --- /dev/null +++ b/vendor/CPR/include/cpr/cprver.h @@ -0,0 +1,30 @@ +#ifndef CPR_CPRVER_H +#define CPR_CPRVER_H + +/** + * CPR version as a string. + **/ +#define CPR_VERSION "1.10.4" + +/** + * CPR version split up into parts. + **/ +#define CPR_VERSION_MAJOR 1 +#define CPR_VERSION_MINOR 10 +#define CPR_VERSION_PATCH 4 + +/** + * CPR version as a single hex digit. + * it can be split up into three parts: + * 0xAABBCC + * AA: The current CPR major version number in a hex format. + * BB: The current CPR minor version number in a hex format. + * CC: The current CPR patch version number in a hex format. + * + * Examples: + * '0x010702' -> 01.07.02 -> CPR_VERSION: 1.7.2 + * '0xA13722' -> A1.37.22 -> CPR_VERSION: 161.55.34 + **/ +#define CPR_VERSION_NUM 0x011004 + +#endif diff --git a/vendor/CPR/include/cpr/curl_container.h b/vendor/CPR/include/cpr/curl_container.h index 691d6cba..c2409b22 100644 --- a/vendor/CPR/include/cpr/curl_container.h +++ b/vendor/CPR/include/cpr/curl_container.h @@ -12,18 +12,14 @@ namespace cpr { struct Parameter { - Parameter(const std::string& p_key, const std::string& p_value) : key{p_key}, value{p_value} {} - Parameter(std::string&& p_key, std::string&& p_value) - : key{std::move(p_key)}, value{std::move(p_value)} {} + Parameter(std::string p_key, std::string p_value) : key{std::move(p_key)}, value{std::move(p_value)} {} std::string key; std::string value; }; struct Pair { - Pair(const std::string& p_key, const std::string& p_value) : key(p_key), value(p_value) {} - Pair(std::string&& p_key, std::string&& p_value) - : key(std::move(p_key)), value(std::move(p_value)) {} + Pair(std::string p_key, std::string p_value) : key(std::move(p_key)), value(std::move(p_value)) {} std::string key; std::string value; diff --git a/vendor/CPR/include/cpr/curlholder.h b/vendor/CPR/include/cpr/curlholder.h index a8a6e07e..a9e1dc82 100644 --- a/vendor/CPR/include/cpr/curlholder.h +++ b/vendor/CPR/include/cpr/curlholder.h @@ -17,14 +17,18 @@ struct CurlHolder { * https://curl.haxx.se/libcurl/c/curl_easy_init.html * https://curl.haxx.se/libcurl/c/threadsafe.html **/ - // It does not make sense to make a std::mutex const. - // NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables) - static std::mutex curl_easy_init_mutex_; + + // Avoids initalization order problems in a static build + static std::mutex& curl_easy_init_mutex_() { + static std::mutex curl_easy_init_mutex_; + return curl_easy_init_mutex_; + } public: CURL* handle{nullptr}; struct curl_slist* chunk{nullptr}; - struct curl_httppost* formpost{nullptr}; + struct curl_slist* resolveCurlList{nullptr}; + curl_mime* multipart{nullptr}; std::array error{}; CurlHolder(); diff --git a/vendor/CPR/include/cpr/curlmultiholder.h b/vendor/CPR/include/cpr/curlmultiholder.h new file mode 100644 index 00000000..401df0eb --- /dev/null +++ b/vendor/CPR/include/cpr/curlmultiholder.h @@ -0,0 +1,18 @@ +#ifndef CPR_CURLMULTIHOLDER_H +#define CPR_CURLMULTIHOLDER_H + +#include + +namespace cpr { + +class CurlMultiHolder { + public: + CurlMultiHolder(); + ~CurlMultiHolder(); + + CURLM* handle{nullptr}; +}; + +} // namespace cpr + +#endif diff --git a/vendor/CPR/include/cpr/digest.h b/vendor/CPR/include/cpr/digest.h deleted file mode 100644 index 2a7ff38d..00000000 --- a/vendor/CPR/include/cpr/digest.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef CPR_DIGEST_H -#define CPR_DIGEST_H - -#include "cpr/auth.h" - -namespace cpr { -class Digest : public Authentication { - public: - Digest(const std::string& username, const std::string& password) - : Authentication{username, password} {} -}; - -} // namespace cpr - -#endif diff --git a/vendor/CPR/include/cpr/error.h b/vendor/CPR/include/cpr/error.h index 6a2be06e..bb59a4cc 100644 --- a/vendor/CPR/include/cpr/error.h +++ b/vendor/CPR/include/cpr/error.h @@ -27,6 +27,7 @@ enum class ErrorCode { GENERIC_SSL_ERROR, UNSUPPORTED_PROTOCOL, REQUEST_CANCELLED, + TOO_MANY_REDIRECTS, UNKNOWN_ERROR = 1000, }; @@ -37,9 +38,7 @@ class Error { Error() = default; - Error(const std::int32_t& curl_code, std::string&& p_error_message) - : code{getErrorCodeForCurlError(curl_code)}, - message(std::move(p_error_message)) {} + Error(const std::int32_t& curl_code, std::string&& p_error_message) : code{getErrorCodeForCurlError(curl_code)}, message(std::move(p_error_message)) {} explicit operator bool() const { return code != ErrorCode::OK; diff --git a/vendor/CPR/include/cpr/file.h b/vendor/CPR/include/cpr/file.h new file mode 100644 index 00000000..9aa2cf46 --- /dev/null +++ b/vendor/CPR/include/cpr/file.h @@ -0,0 +1,59 @@ +#ifndef CPR_FILE_H +#define CPR_FILE_H + +#include +#include +#include + +#include + +namespace cpr { + +struct File { + explicit File(std::string p_filepath, const std::string& p_overriden_filename = {}) : filepath(std::move(p_filepath)), overriden_filename(p_overriden_filename) {} + + std::string filepath; + std::string overriden_filename; + + [[nodiscard]] bool hasOverridenFilename() const noexcept { + return !overriden_filename.empty(); + } +}; + +class Files { + public: + Files() = default; + // NOLINTNEXTLINE(google-explicit-constructor) + Files(const File& p_file) : files{p_file} {} + + Files(const Files& other) = default; + Files(Files&& old) noexcept = default; + + Files(const std::initializer_list& p_files) : files{p_files} {} + Files(const std::initializer_list& p_filepaths); + + ~Files() noexcept = default; + + Files& operator=(const Files& other); + Files& operator=(Files&& old) noexcept; + + using iterator = std::vector::iterator; + using const_iterator = std::vector::const_iterator; + + iterator begin(); + iterator end(); + [[nodiscard]] const_iterator begin() const; + [[nodiscard]] const_iterator end() const; + [[nodiscard]] const_iterator cbegin() const; + [[nodiscard]] const_iterator cend() const; + void emplace_back(const File& file); + void push_back(const File& file); + void pop_back(); + + private: + std::vector files; +}; + +} // namespace cpr + +#endif diff --git a/vendor/CPR/include/cpr/filesystem.h b/vendor/CPR/include/cpr/filesystem.h new file mode 100644 index 00000000..f296770c --- /dev/null +++ b/vendor/CPR/include/cpr/filesystem.h @@ -0,0 +1,26 @@ +#ifndef CPR_FILESYSTEM_H +#define CPR_FILESYSTEM_H + +// Include filesystem into the namespace "fs" from either "filesystem" or "experimental/filesystem" or "boost/filesystem" +#ifdef CPR_USE_BOOST_FILESYSTEM +#define BOOST_FILESYSTEM_VERSION 4 // Use the latest, with the closest behavior to std::filesystem. +#include +namespace cpr { +namespace fs = boost::filesystem; +} +// cppcheck-suppress preprocessorErrorDirective +#elif __has_include() +#include +namespace cpr { +namespace fs = std::filesystem; +} +#elif __has_include("experimental/filesystem") +#include +namespace cpr { +namespace fs = std::experimental::filesystem; +} +#else +#error "Failed to include header!" +#endif + +#endif diff --git a/vendor/CPR/include/cpr/http_version.h b/vendor/CPR/include/cpr/http_version.h new file mode 100644 index 00000000..45b50287 --- /dev/null +++ b/vendor/CPR/include/cpr/http_version.h @@ -0,0 +1,67 @@ +#ifndef CPR_HTTP_VERSION_H +#define CPR_HTTP_VERSION_H + +#include + +namespace cpr { +enum class HttpVersionCode { + /** + * Let libcurl decide which version is the best. + **/ + VERSION_NONE, + /** + * Enforce HTTP 1.0 requests. + **/ + VERSION_1_0, + /** + * Enforce HTTP 1.1 requests. + **/ + VERSION_1_1, +#if LIBCURL_VERSION_NUM >= 0x072100 // 7.33.0 + /** + * Attempt HTTP 2.0 requests. + * Fallback to HTTP 1.1 if negotiation fails. + **/ + VERSION_2_0, +#endif +#if LIBCURL_VERSION_NUM >= 0x072F00 // 7.47.0 + /** + * Attempt HTTP 2.0 for HTTPS requests only. + * Fallback to HTTP 1.1 if negotiation fails. + * HTTP 1.1 will be used for HTTP connections. + **/ + VERSION_2_0_TLS, +#endif +#if LIBCURL_VERSION_NUM >= 0x073100 // 7.49.0 + /** + * Start HTTP 2.0 for HTTP requests. + * Requires prior knowledge that the server supports HTTP 2.0. + * For HTTPS requests we will negotiate the protocol version in the TLS handshake. + **/ + VERSION_2_0_PRIOR_KNOWLEDGE, +#endif +#if LIBCURL_VERSION_NUM >= 0x074200 // 7.66.0 + /** + * Attempt HTTP 3.0 requests. + * Requires prior knowledge that the server supports HTTP 3.0 since there is no gracefully downgrade. + * Fallback to HTTP 1.1 if negotiation fails. + **/ + VERSION_3_0 +#endif +}; + +class HttpVersion { + public: + /** + * The HTTP version that should be used by libcurl when initiating a HTTP(S) connection. + * Default: HttpVersionCode::VERSION_NONE + **/ + HttpVersionCode code = HttpVersionCode::VERSION_NONE; + + HttpVersion() = default; + explicit HttpVersion(HttpVersionCode _code) : code(_code) {} +}; + +} // namespace cpr + +#endif diff --git a/vendor/CPR/include/cpr/interceptor.h b/vendor/CPR/include/cpr/interceptor.h new file mode 100644 index 00000000..d05f4c9c --- /dev/null +++ b/vendor/CPR/include/cpr/interceptor.h @@ -0,0 +1,74 @@ +#ifndef CPR_INTERCEPTOR_H +#define CPR_INTERCEPTOR_H + +#include "cpr/multiperform.h" +#include "cpr/response.h" +#include "cpr/session.h" +#include + +namespace cpr { +class Interceptor { + public: + enum class ProceedHttpMethod { + GET_REQUEST = 0, + POST_REQUEST, + PUT_REQUEST, + DELETE_REQUEST, + PATCH_REQUEST, + HEAD_REQUEST, + OPTIONS_REQUEST, + DOWNLOAD_CALLBACK_REQUEST, + DOWNLOAD_FILE_REQUEST, + }; + + Interceptor() = default; + Interceptor(const Interceptor& other) = default; + Interceptor(Interceptor&& old) = default; + virtual ~Interceptor() = default; + + Interceptor& operator=(const Interceptor& other) = default; + Interceptor& operator=(Interceptor&& old) = default; + + virtual Response intercept(Session& session) = 0; + + protected: + static Response proceed(Session& session); + static Response proceed(Session& session, ProceedHttpMethod httpMethod); + static Response proceed(Session& session, ProceedHttpMethod httpMethod, std::ofstream& file); + static Response proceed(Session& session, ProceedHttpMethod httpMethod, const WriteCallback& write); +}; + +class InterceptorMulti { + public: + enum class ProceedHttpMethod { + GET_REQUEST = 0, + POST_REQUEST, + PUT_REQUEST, + DELETE_REQUEST, + PATCH_REQUEST, + HEAD_REQUEST, + OPTIONS_REQUEST, + DOWNLOAD_CALLBACK_REQUEST, + DOWNLOAD_FILE_REQUEST, + }; + + InterceptorMulti() = default; + InterceptorMulti(const InterceptorMulti& other) = default; + InterceptorMulti(InterceptorMulti&& old) = default; + virtual ~InterceptorMulti() = default; + + InterceptorMulti& operator=(const InterceptorMulti& other) = default; + InterceptorMulti& operator=(InterceptorMulti&& old) = default; + + virtual std::vector intercept(MultiPerform& multi) = 0; + + protected: + static std::vector proceed(MultiPerform& multi); + + static void PrepareDownloadSession(MultiPerform& multi, size_t sessions_index, const WriteCallback& write); +}; + +} // namespace cpr + + +#endif diff --git a/vendor/CPR/include/cpr/interface.h b/vendor/CPR/include/cpr/interface.h index ea8225d1..b98940ec 100644 --- a/vendor/CPR/include/cpr/interface.h +++ b/vendor/CPR/include/cpr/interface.h @@ -10,11 +10,11 @@ namespace cpr { class Interface : public StringHolder { public: - Interface() : StringHolder() {} + Interface() = default; // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) - Interface(const std::string& iface) : StringHolder(iface) {} + Interface(std::string iface) : StringHolder(std::move(iface)) {} // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) - Interface(std::string&& iface) : StringHolder(std::move(iface)) {} + Interface(std::string_view iface) : StringHolder(iface) {} // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) Interface(const char* iface) : StringHolder(iface) {} Interface(const char* str, size_t len) : StringHolder(str, len) {} @@ -29,4 +29,4 @@ class Interface : public StringHolder { } // namespace cpr -#endif \ No newline at end of file +#endif diff --git a/vendor/CPR/include/cpr/limit_rate.h b/vendor/CPR/include/cpr/limit_rate.h index 23bd0322..f25c09e5 100644 --- a/vendor/CPR/include/cpr/limit_rate.h +++ b/vendor/CPR/include/cpr/limit_rate.h @@ -7,8 +7,7 @@ namespace cpr { class LimitRate { public: - LimitRate(const std::int64_t p_downrate, const std::int64_t p_uprate) - : downrate(p_downrate), uprate(p_uprate) {} + LimitRate(const std::int64_t p_downrate, const std::int64_t p_uprate) : downrate(p_downrate), uprate(p_uprate) {} std::int64_t downrate = 0; std::int64_t uprate = 0; @@ -16,4 +15,4 @@ class LimitRate { } // namespace cpr -#endif \ No newline at end of file +#endif diff --git a/vendor/CPR/include/cpr/local_port.h b/vendor/CPR/include/cpr/local_port.h new file mode 100644 index 00000000..a6efe7ec --- /dev/null +++ b/vendor/CPR/include/cpr/local_port.h @@ -0,0 +1,23 @@ +#ifndef CPR_LOCAL_PORT_H +#define CPR_LOCAL_PORT_H + +#include + +namespace cpr { + +class LocalPort { + public: + // NOLINTNEXTLINE(google-explicit-constructor) + LocalPort(const std::uint16_t p_localport) : localport_(p_localport) {} + + operator std::uint16_t() const { + return localport_; + } + + private: + std::uint16_t localport_ = 0; +}; + +} // namespace cpr + +#endif diff --git a/vendor/CPR/include/cpr/local_port_range.h b/vendor/CPR/include/cpr/local_port_range.h new file mode 100644 index 00000000..e048b6e9 --- /dev/null +++ b/vendor/CPR/include/cpr/local_port_range.h @@ -0,0 +1,23 @@ +#ifndef CPR_LOCAL_PORT_RANGE_H +#define CPR_LOCAL_PORT_RANGE_H + +#include + +namespace cpr { + +class LocalPortRange { + public: + // NOLINTNEXTLINE(google-explicit-constructor) + LocalPortRange(const std::uint16_t p_localportrange) : localportrange_(p_localportrange) {} + + operator std::uint16_t() const { + return localportrange_; + } + + private: + std::uint16_t localportrange_ = 0; +}; + +} // namespace cpr + +#endif diff --git a/vendor/CPR/include/cpr/multipart.h b/vendor/CPR/include/cpr/multipart.h index 08ef541f..9ee62d5a 100644 --- a/vendor/CPR/include/cpr/multipart.h +++ b/vendor/CPR/include/cpr/multipart.h @@ -7,69 +7,35 @@ #include #include +#include "buffer.h" +#include "file.h" + namespace cpr { -struct File { - explicit File(std::string&& p_filepath) : filepath(std::move(p_filepath)) {} - explicit File(const std::string& p_filepath) : filepath(p_filepath) {} - const std::string filepath; -}; - -struct Buffer { - using data_t = const unsigned char*; - - template - Buffer(Iterator begin, Iterator end, std::string&& p_filename) - // Ignored here since libcurl reqires a long. - // There is also no way around the reinterpret_cast. - // NOLINTNEXTLINE(google-runtime-int, cppcoreguidelines-pro-type-reinterpret-cast) - : data{reinterpret_cast(&(*begin))}, datalen{static_cast( - std::distance(begin, end))}, - filename(std::move(p_filename)) { - is_random_access_iterator(begin, end); - static_assert(sizeof(*begin) == 1, "only byte buffers can be used"); - } - - template - typename std::enable_if::iterator_category, - std::random_access_iterator_tag>::value>::type - is_random_access_iterator(Iterator /* begin */, Iterator /* end */) {} - - data_t data; - // Ignored here since libcurl reqires a long: - // NOLINTNEXTLINE(google-runtime-int) - long datalen; - const std::string filename; -}; - struct Part { - Part(const std::string& p_name, const std::string& p_value, const std::string& p_content_type = {}) - : name{p_name}, value{p_value}, - content_type{p_content_type}, is_file{false}, is_buffer{false} {} - Part(const std::string& p_name, const std::int32_t& p_value, const std::string& p_content_type = {}) - : name{p_name}, value{std::to_string(p_value)}, - content_type{p_content_type}, is_file{false}, is_buffer{false} {} - Part(const std::string& p_name, const File& file, const std::string& p_content_type = {}) - : name{p_name}, value{file.filepath}, - content_type{p_content_type}, is_file{true}, is_buffer{false} {} - Part(const std::string& p_name, const Buffer& buffer, const std::string& p_content_type = {}) - : name{p_name}, value{buffer.filename}, content_type{p_content_type}, data{buffer.data}, - datalen{buffer.datalen}, is_file{false}, is_buffer{true} {} + Part(const std::string& p_name, const std::string& p_value, const std::string& p_content_type = {}) : name{p_name}, value{p_value}, content_type{p_content_type}, is_file{false}, is_buffer{false} {} + Part(const std::string& p_name, const std::int32_t& p_value, const std::string& p_content_type = {}) : name{p_name}, value{std::to_string(p_value)}, content_type{p_content_type}, is_file{false}, is_buffer{false} {} + Part(const std::string& p_name, const Files& p_files, const std::string& p_content_type = {}) : name{p_name}, content_type{p_content_type}, is_file{true}, is_buffer{false}, files{p_files} {} + Part(const std::string& p_name, Files&& p_files, const std::string& p_content_type = {}) : name{p_name}, content_type{p_content_type}, is_file{true}, is_buffer{false}, files{p_files} {} + Part(const std::string& p_name, const Buffer& buffer, const std::string& p_content_type = {}) : name{p_name}, value{buffer.filename.string()}, content_type{p_content_type}, data{buffer.data}, datalen{buffer.datalen}, is_file{false}, is_buffer{true} {} std::string name; + // We don't use fs::path here, as this leads to problems using windows std::string value; std::string content_type; Buffer::data_t data{nullptr}; - // Ignored here since libcurl reqires a long: - // NOLINTNEXTLINE(google-runtime-int) - long datalen{0}; + size_t datalen{0}; bool is_file; bool is_buffer; + + Files files; }; class Multipart { public: Multipart(const std::initializer_list& parts); + Multipart(const std::vector& parts); + Multipart(const std::vector&& parts); std::vector parts; }; diff --git a/vendor/CPR/include/cpr/multiperform.h b/vendor/CPR/include/cpr/multiperform.h new file mode 100644 index 00000000..3e39aaca --- /dev/null +++ b/vendor/CPR/include/cpr/multiperform.h @@ -0,0 +1,137 @@ +#ifndef CPR_MULTIPERFORM_H +#define CPR_MULTIPERFORM_H + +#include "cpr/curlmultiholder.h" +#include "cpr/response.h" +#include "cpr/session.h" +#include +#include +#include +#include +#include + +namespace cpr { + +class InterceptorMulti; + +class MultiPerform { + public: + enum class HttpMethod { + UNDEFINED = 0, + GET_REQUEST, + POST_REQUEST, + PUT_REQUEST, + DELETE_REQUEST, + PATCH_REQUEST, + HEAD_REQUEST, + OPTIONS_REQUEST, + DOWNLOAD_REQUEST, + }; + + MultiPerform(); + MultiPerform(const MultiPerform& other) = delete; + MultiPerform(MultiPerform&& old) = default; + ~MultiPerform(); + + MultiPerform& operator=(const MultiPerform& other) = delete; + MultiPerform& operator=(MultiPerform&& old) noexcept = default; + + std::vector Get(); + std::vector Delete(); + template + std::vector Download(DownloadArgTypes... args); + std::vector Put(); + std::vector Head(); + std::vector Options(); + std::vector Patch(); + std::vector Post(); + + std::vector Perform(); + template + std::vector PerformDownload(DownloadArgTypes... args); + + void AddSession(std::shared_ptr& session, HttpMethod method = HttpMethod::UNDEFINED); + void RemoveSession(const std::shared_ptr& session); + std::vector, HttpMethod>>& GetSessions(); + [[nodiscard]] const std::vector, HttpMethod>>& GetSessions() const; + + void AddInterceptor(const std::shared_ptr& pinterceptor); + + private: + // Interceptors should be able to call the private proceed() and PrepareDownloadSessions() functions + friend InterceptorMulti; + + void SetHttpMethod(HttpMethod method); + + void PrepareSessions(); + template + void PrepareDownloadSessions(size_t sessions_index, CurrentDownloadArgType current_arg, DownloadArgTypes... args); + template + void PrepareDownloadSessions(size_t sessions_index, CurrentDownloadArgType current_arg); + void PrepareDownloadSession(size_t sessions_index, std::ofstream& file); + void PrepareDownloadSession(size_t sessions_index, const WriteCallback& write); + + void PrepareGet(); + void PrepareDelete(); + void PreparePut(); + void PreparePatch(); + void PrepareHead(); + void PrepareOptions(); + void PreparePost(); + template + void PrepareDownload(DownloadArgTypes... args); + + std::vector intercept(); + std::vector proceed(); + std::vector MakeRequest(); + std::vector MakeDownloadRequest(); + + void DoMultiPerform(); + std::vector ReadMultiInfo(std::function&& complete_function); + + std::vector, HttpMethod>> sessions_; + std::unique_ptr multicurl_; + bool is_download_multi_perform{false}; + + std::queue> interceptors_; +}; + +template +void MultiPerform::PrepareDownloadSessions(size_t sessions_index, CurrentDownloadArgType current_arg) { + PrepareDownloadSession(sessions_index, current_arg); +} + +template +void MultiPerform::PrepareDownloadSessions(size_t sessions_index, CurrentDownloadArgType current_arg, DownloadArgTypes... args) { + PrepareDownloadSession(sessions_index, current_arg); + PrepareDownloadSessions(sessions_index + 1, args...); +} + + +template +void MultiPerform::PrepareDownload(DownloadArgTypes... args) { + SetHttpMethod(HttpMethod::DOWNLOAD_REQUEST); + PrepareDownloadSessions(0, args...); +} + +template +std::vector MultiPerform::Download(DownloadArgTypes... args) { + if (sizeof...(args) != sessions_.size()) { + throw std::invalid_argument("Number of download arguments has to match the number of sessions added to the multiperform!"); + } + PrepareDownload(args...); + return MakeDownloadRequest(); +} + +template +std::vector MultiPerform::PerformDownload(DownloadArgTypes... args) { + if (sizeof...(args) != sessions_.size()) { + throw std::invalid_argument("Number of download arguments has to match the number of sessions added to the multiperform!"); + } + PrepareDownloadSessions(0, args...); + return MakeDownloadRequest(); +} + +} // namespace cpr + +#endif diff --git a/vendor/CPR/include/cpr/ntlm.h b/vendor/CPR/include/cpr/ntlm.h deleted file mode 100644 index e15833d8..00000000 --- a/vendor/CPR/include/cpr/ntlm.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef CPR_NTLM_H -#define CPR_NTLM_H - -#include "cpr/auth.h" - -namespace cpr { -class NTLM : public Authentication { - public: - NTLM(const std::string& username, const std::string& password) - : Authentication{username, password} {} -}; - -} // namespace cpr - -#endif diff --git a/vendor/CPR/include/cpr/parameters.h b/vendor/CPR/include/cpr/parameters.h index 0e34d4d7..62096277 100644 --- a/vendor/CPR/include/cpr/parameters.h +++ b/vendor/CPR/include/cpr/parameters.h @@ -10,7 +10,7 @@ namespace cpr { class Parameters : public CurlContainer { public: Parameters() = default; - Parameters(const std::initializer_list& parameters); + Parameters(const std::initializer_list& parameters) : CurlContainer(parameters) {} }; } // namespace cpr diff --git a/vendor/CPR/include/cpr/payload.h b/vendor/CPR/include/cpr/payload.h index 686b540e..0741a88f 100644 --- a/vendor/CPR/include/cpr/payload.h +++ b/vendor/CPR/include/cpr/payload.h @@ -15,7 +15,7 @@ class Payload : public CurlContainer { Add(*pair); } } - Payload(const std::initializer_list& pairs); + Payload(const std::initializer_list& pairs) : CurlContainer(pairs) {} }; } // namespace cpr diff --git a/vendor/CPR/include/cpr/proxies.h b/vendor/CPR/include/cpr/proxies.h index 565a63ef..2ec47174 100644 --- a/vendor/CPR/include/cpr/proxies.h +++ b/vendor/CPR/include/cpr/proxies.h @@ -10,6 +10,7 @@ class Proxies { public: Proxies() = default; Proxies(const std::initializer_list>& hosts); + Proxies(const std::map& hosts); bool has(const std::string& protocol) const; const std::string& operator[](const std::string& protocol); diff --git a/vendor/CPR/include/cpr/proxyauth.h b/vendor/CPR/include/cpr/proxyauth.h index da468d59..521a0c58 100644 --- a/vendor/CPR/include/cpr/proxyauth.h +++ b/vendor/CPR/include/cpr/proxyauth.h @@ -3,36 +3,44 @@ #include #include +#include #include "cpr/auth.h" #include "cpr/util.h" namespace cpr { +class ProxyAuthentication; + class EncodedAuthentication { + friend ProxyAuthentication; + public: - EncodedAuthentication() : auth_string_{""} {} - EncodedAuthentication(const std::string& username, const std::string& password) : auth_string_{cpr::util::urlEncode(username) + ":" + cpr::util::urlEncode(password)} {} - EncodedAuthentication(std::string&& username, std::string&& password) : auth_string_{cpr::util::urlEncode(std::move(username)) + ":" + cpr::util::urlEncode(std::move(password))} {} + EncodedAuthentication() = default; + EncodedAuthentication(const std::string& p_username, const std::string& p_password) : username(util::urlEncode(p_username)), password(util::urlEncode(p_password)) {} EncodedAuthentication(const EncodedAuthentication& other) = default; EncodedAuthentication(EncodedAuthentication&& old) noexcept = default; - virtual ~EncodedAuthentication() noexcept = default; + virtual ~EncodedAuthentication() noexcept; EncodedAuthentication& operator=(EncodedAuthentication&& old) noexcept = default; EncodedAuthentication& operator=(const EncodedAuthentication& other) = default; - const char* GetAuthString() const noexcept; + [[nodiscard]] const std::string& GetUsername() const; + [[nodiscard]] const std::string& GetPassword() const; - protected: - std::string auth_string_; + private: + std::string username; + std::string password; }; class ProxyAuthentication { public: ProxyAuthentication() = default; ProxyAuthentication(const std::initializer_list>& auths) : proxyAuth_{auths} {} + explicit ProxyAuthentication(const std::map& auths) : proxyAuth_{auths} {} - bool has(const std::string& protocol) const; - const char* operator[](const std::string& protocol); + [[nodiscard]] bool has(const std::string& protocol) const; + const char* GetUsername(const std::string& protocol); + const char* GetPassword(const std::string& protocol); private: std::map proxyAuth_; diff --git a/vendor/CPR/include/cpr/range.h b/vendor/CPR/include/cpr/range.h new file mode 100644 index 00000000..2c5a145d --- /dev/null +++ b/vendor/CPR/include/cpr/range.h @@ -0,0 +1,44 @@ +#ifndef CPR_RANGE_H +#define CPR_RANGE_H + +#include +#include + +namespace cpr { + +class Range { + public: + Range(const std::optional p_resume_from = std::nullopt, const std::optional p_finish_at = std::nullopt) { + resume_from = p_resume_from.value_or(0); + finish_at = p_finish_at.value_or(-1); + } + + std::int64_t resume_from; + std::int64_t finish_at; + + const std::string str() const { + std::string from_str = (resume_from < 0U) ? "" : std::to_string(resume_from); + std::string to_str = (finish_at < 0U) ? "" : std::to_string(finish_at); + return from_str + "-" + to_str; + } +}; + +class MultiRange { + public: + MultiRange(std::initializer_list rs) : ranges{rs} {} + + const std::string str() const { + std::string multi_range_string{}; + for (Range range : ranges) { + multi_range_string += ((multi_range_string.empty()) ? "" : ", ") + range.str(); + } + return multi_range_string; + } + + private: + std::vector ranges; +}; // namespace cpr + +} // namespace cpr + +#endif diff --git a/vendor/CPR/include/cpr/redirect.h b/vendor/CPR/include/cpr/redirect.h index 4ce34777..f57687cb 100644 --- a/vendor/CPR/include/cpr/redirect.h +++ b/vendor/CPR/include/cpr/redirect.h @@ -50,7 +50,7 @@ class Redirect { * Default: 50 * https://curl.se/libcurl/c/CURLOPT_MAXREDIRS.html **/ - // NOLINTNEXTLINE (cppcoreguidelines-avoid-magic-numbers, google-runtime-int) + // NOLINTNEXTLINE (google-runtime-int) long maximum{50L}; /** * Follow 3xx redirects. @@ -58,6 +58,12 @@ class Redirect { * https://curl.se/libcurl/c/CURLOPT_FOLLOWLOCATION.html **/ bool follow{true}; + /** + * Continue to send authentication (user+password) credentials when following locations, even when hostname changed. + * Default: false + * https://curl.se/libcurl/c/CURLOPT_UNRESTRICTED_AUTH.html + **/ + bool cont_send_cred{false}; /** * Flags to control how to act after a redirect for a post request. * Default: POST_ALL @@ -66,11 +72,12 @@ class Redirect { Redirect() = default; // NOLINTNEXTLINE (google-runtime-int) - Redirect(long p_maximum, bool p_follow, PostRedirectFlags p_post_flags) : maximum(p_maximum), follow(p_follow), post_flags(p_post_flags){}; + Redirect(long p_maximum, bool p_follow, bool p_cont_send_cred, PostRedirectFlags p_post_flags) : maximum(p_maximum), follow(p_follow), cont_send_cred(p_cont_send_cred), post_flags(p_post_flags){} // NOLINTNEXTLINE (google-runtime-int) - explicit Redirect(long p_maximum) : maximum(p_maximum){}; - explicit Redirect(bool p_follow) : follow(p_follow){}; - explicit Redirect(PostRedirectFlags p_post_flags) : post_flags(p_post_flags){}; + explicit Redirect(long p_maximum) : maximum(p_maximum){} + explicit Redirect(bool p_follow) : follow(p_follow){} + Redirect(bool p_follow, bool p_cont_send_cred) : follow(p_follow), cont_send_cred(p_cont_send_cred){} + explicit Redirect(PostRedirectFlags p_post_flags) : post_flags(p_post_flags){} }; } // namespace cpr diff --git a/vendor/CPR/include/cpr/reserve_size.h b/vendor/CPR/include/cpr/reserve_size.h new file mode 100644 index 00000000..5eae4c80 --- /dev/null +++ b/vendor/CPR/include/cpr/reserve_size.h @@ -0,0 +1,17 @@ +#ifndef CPR_RESERVE_SIZE_H +#define CPR_RESERVE_SIZE_H + +#include + +namespace cpr { + +class ReserveSize { + public: + ReserveSize(const size_t _size) : size(_size) {} + + size_t size = 0; +}; + +} // namespace cpr + +#endif diff --git a/vendor/CPR/include/cpr/resolve.h b/vendor/CPR/include/cpr/resolve.h new file mode 100644 index 00000000..86a7c892 --- /dev/null +++ b/vendor/CPR/include/cpr/resolve.h @@ -0,0 +1,23 @@ +#ifndef CPR_RESOLVE_H +#define CPR_RESOLVE_H + +#include +#include + +namespace cpr { + class Resolve { + public: + std::string host; + std::string addr; + std::set ports; + + Resolve(const std::string& host_param, const std::string& addr_param, const std::set& ports_param = std::set{80U, 443U}): host(host_param), addr(addr_param), ports(ports_param) { + if (this->ports.empty()) { + this->ports.insert(80U); + this->ports.insert(443U); + } + } + }; +} // namespace cpr + +#endif diff --git a/vendor/CPR/include/cpr/response.h b/vendor/CPR/include/cpr/response.h index bd3f25d4..c268601a 100644 --- a/vendor/CPR/include/cpr/response.h +++ b/vendor/CPR/include/cpr/response.h @@ -3,12 +3,12 @@ #include #include -#include #include #include #include #include +#include "cpr/cert_info.h" #include "cpr/cookies.h" #include "cpr/cprtypes.h" #include "cpr/error.h" @@ -17,8 +17,11 @@ namespace cpr { +class MultiPerform; + class Response { protected: + friend MultiPerform; std::shared_ptr curl_{nullptr}; public: @@ -41,9 +44,8 @@ class Response { long redirect_count{}; Response() = default; - Response(std::shared_ptr curl, std::string&& p_text, std::string&& p_header_string, - Cookies&& p_cookies, Error&& p_error); - std::vector GetCertInfo(); + Response(std::shared_ptr curl, std::string&& p_text, std::string&& p_header_string, Cookies&& p_cookies, Error&& p_error); + std::vector GetCertInfos(); Response(const Response& other) = default; Response(Response&& old) noexcept = default; ~Response() noexcept = default; diff --git a/vendor/CPR/include/cpr/session.h b/vendor/CPR/include/cpr/session.h index d272a2af..6b2b93e4 100644 --- a/vendor/CPR/include/cpr/session.h +++ b/vendor/CPR/include/cpr/session.h @@ -3,8 +3,13 @@ #include #include +#include +#include #include +#include +#include "cpr/accept_encoding.h" +#include "cpr/async_wrapper.h" #include "cpr/auth.h" #include "cpr/bearer.h" #include "cpr/body.h" @@ -13,35 +18,45 @@ #include "cpr/cookies.h" #include "cpr/cprtypes.h" #include "cpr/curlholder.h" -#include "cpr/digest.h" +#include "cpr/http_version.h" #include "cpr/interface.h" #include "cpr/limit_rate.h" +#include "cpr/local_port.h" +#include "cpr/local_port_range.h" #include "cpr/low_speed.h" #include "cpr/multipart.h" -#include "cpr/ntlm.h" #include "cpr/parameters.h" #include "cpr/payload.h" #include "cpr/proxies.h" #include "cpr/proxyauth.h" +#include "cpr/range.h" #include "cpr/redirect.h" +#include "cpr/reserve_size.h" +#include "cpr/resolve.h" #include "cpr/response.h" #include "cpr/ssl_options.h" #include "cpr/timeout.h" #include "cpr/unix_socket.h" #include "cpr/user_agent.h" +#include "cpr/util.h" #include "cpr/verbose.h" namespace cpr { -class Session { +using AsyncResponse = AsyncWrapper; + +class Interceptor; +class MultiPerform; + +class Session : public std::enable_shared_from_this { public: Session(); - Session(Session&& old) noexcept; Session(const Session& other) = delete; + Session(Session&& old) = default; - ~Session(); + ~Session() = default; - Session& operator=(Session&& old) noexcept; + Session& operator=(Session&& old) noexcept = default; Session& operator=(const Session& other) = delete; void SetUrl(const Url& url); @@ -52,7 +67,11 @@ class Session { void SetTimeout(const Timeout& timeout); void SetConnectTimeout(const ConnectTimeout& timeout); void SetAuth(const Authentication& auth); - void SetDigest(const Digest& auth); +// Only supported with libcurl >= 7.61.0. +// As an alternative use SetHeader and add the token manually. +#if LIBCURL_VERSION_NUM >= 0x073D00 + void SetBearer(const Bearer& token); +#endif void SetUserAgent(const UserAgent& ua); void SetPayload(Payload&& payload); void SetPayload(const Payload& payload); @@ -62,7 +81,6 @@ class Session { void SetProxyAuth(const ProxyAuthentication& proxy_auth); void SetMultipart(Multipart&& multipart); void SetMultipart(const Multipart& multipart); - void SetNTLM(const NTLM& auth); void SetRedirect(const Redirect& redirect); void SetCookies(const Cookies& cookies); void SetBody(Body&& body); @@ -78,6 +96,20 @@ class Session { void SetDebugCallback(const DebugCallback& debug); void SetVerbose(const Verbose& verbose); void SetInterface(const Interface& iface); + void SetLocalPort(const LocalPort& local_port); + void SetLocalPortRange(const LocalPortRange& local_port_range); + void SetHttpVersion(const HttpVersion& version); + void SetRange(const Range& range); + void SetResolve(const Resolve& resolve); + void SetResolves(const std::vector& resolves); + void SetMultiRange(const MultiRange& multi_range); + void SetReserveSize(const ReserveSize& reserve_size); + void SetAcceptEncoding(const AcceptEncoding& accept_encoding); + void SetAcceptEncoding(AcceptEncoding&& accept_encoding); + void SetLimitRate(const LimitRate& limit_rate); + + // For cancellable requests + void SetCancellationParam(std::shared_ptr param); // Used in templated functions void SetOption(const Url& url); @@ -92,7 +124,6 @@ class Session { #if LIBCURL_VERSION_NUM >= 0x073D00 void SetOption(const Bearer& auth); #endif - void SetOption(const Digest& auth); void SetOption(const UserAgent& ua); void SetOption(Payload&& payload); void SetOption(const Payload& payload); @@ -103,7 +134,6 @@ class Session { void SetOption(const ProxyAuthentication& proxy_auth); void SetOption(Multipart&& multipart); void SetOption(const Multipart& multipart); - void SetOption(const NTLM& auth); void SetOption(const Redirect& redirect); void SetOption(const Cookies& cookies); void SetOption(Body&& body); @@ -119,8 +149,29 @@ class Session { void SetOption(const UnixSocket& unix_socket); void SetOption(const SslOptions& options); void SetOption(const Interface& iface); + void SetOption(const LocalPort& local_port); + void SetOption(const LocalPortRange& local_port_range); + void SetOption(const HttpVersion& version); + void SetOption(const Range& range); + void SetOption(const MultiRange& multi_range); + void SetOption(const ReserveSize& reserve_size); + void SetOption(const AcceptEncoding& accept_encoding); + void SetOption(AcceptEncoding&& accept_encoding); + void SetOption(const Resolve& resolve); + void SetOption(const std::vector& resolves); cpr_off_t GetDownloadFileLength(); + /** + * Attempt to preallocate enough memory for specified number of characters in the response string. + * Pass 0 to disable this behavior and let the response string be allocated dynamically on demand. + * + * Example: + * cpr::Session session; + * session.SetUrl(cpr::Url{"http://xxx/file"}); + * session.ResponseStringReserve(1024 * 512); // Reserve space for at least 1024 * 512 characters + * cpr::Response r = session.Get(); + **/ + void ResponseStringReserve(size_t size); Response Delete(); Response Download(const WriteCallback& write); Response Download(std::ofstream& file); @@ -131,7 +182,33 @@ class Session { Response Post(); Response Put(); + AsyncResponse GetAsync(); + AsyncResponse DeleteAsync(); + AsyncResponse DownloadAsync(const WriteCallback& write); + AsyncResponse DownloadAsync(std::ofstream& file); + AsyncResponse HeadAsync(); + AsyncResponse OptionsAsync(); + AsyncResponse PatchAsync(); + AsyncResponse PostAsync(); + AsyncResponse PutAsync(); + + template + auto GetCallback(Then then); + template + auto PostCallback(Then then); + template + auto PutCallback(Then then); + template + auto HeadCallback(Then then); + template + auto DeleteCallback(Then then); + template + auto OptionsCallback(Then then); + template + auto PatchCallback(Then then); + std::shared_ptr GetCurlHolder(); + std::string GetFullRequestUrl(); void PrepareDelete(); void PrepareGet(); @@ -140,13 +217,92 @@ class Session { void PreparePatch(); void PreparePost(); void PreparePut(); + void PrepareDownload(const WriteCallback& write); + void PrepareDownload(std::ofstream& file); Response Complete(CURLcode curl_error); + Response CompleteDownload(CURLcode curl_error); - protected: - class Impl; - std::unique_ptr pimpl_; + void AddInterceptor(const std::shared_ptr& pinterceptor); + + private: + // Interceptors should be able to call the private proceed() function + friend Interceptor; + friend MultiPerform; + + + bool hasBodyOrPayload_{false}; + bool chunkedTransferEncoding_{false}; + std::shared_ptr curl_; + Url url_; + Parameters parameters_; + Proxies proxies_; + ProxyAuthentication proxyAuth_; + Header header_; + AcceptEncoding acceptEncoding_; + /** + * Will be set by the read callback. + * Ensures that the "Transfer-Encoding" is set to "chunked", if not overriden in header_. + **/ + ReadCallback readcb_; + HeaderCallback headercb_; + WriteCallback writecb_; + ProgressCallback progresscb_; + DebugCallback debugcb_; + CancellationCallback cancellationcb_; + + size_t response_string_reserve_size_{0}; + std::string response_string_; + std::string header_string_; + std::queue> interceptors_; + bool isUsedInMultiPerform{false}; + bool isCancellable{false}; + + Response makeDownloadRequest(); + Response makeRequest(); + Response proceed(); + Response intercept(); + void prepareCommon(); + void prepareCommonDownload(); + void SetHeaderInternal(); + std::shared_ptr GetSharedPtrFromThis(); + CURLcode DoEasyPerform(); }; +template +auto Session::GetCallback(Then then) { + return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Get()); }, std::move(then)); +} + +template +auto Session::PostCallback(Then then) { + return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Post()); }, std::move(then)); +} + +template +auto Session::PutCallback(Then then) { + return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Put()); }, std::move(then)); +} + +template +auto Session::HeadCallback(Then then) { + return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Head()); }, std::move(then)); +} + +template +auto Session::DeleteCallback(Then then) { + return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Delete()); }, std::move(then)); +} + +template +auto Session::OptionsCallback(Then then) { + return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Options()); }, std::move(then)); +} + +template +auto Session::PatchCallback(Then then) { + return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Patch()); }, std::move(then)); +} + } // namespace cpr #endif diff --git a/vendor/CPR/include/cpr/singleton.h b/vendor/CPR/include/cpr/singleton.h new file mode 100644 index 00000000..e2ea13bb --- /dev/null +++ b/vendor/CPR/include/cpr/singleton.h @@ -0,0 +1,47 @@ +#ifndef CPR_SINGLETON_H +#define CPR_SINGLETON_H + +#include + +#ifndef CPR_DISABLE_COPY +#define CPR_DISABLE_COPY(Class) \ + Class(const Class&) = delete; \ + Class& operator=(const Class&) = delete; +#endif + +#ifndef CPR_SINGLETON_DECL +#define CPR_SINGLETON_DECL(Class) \ + public: \ + static Class* GetInstance(); \ + static void ExitInstance(); \ + private: \ + CPR_DISABLE_COPY(Class) \ + static Class* s_pInstance; \ + static std::mutex s_mutex; +#endif + +#ifndef CPR_SINGLETON_IMPL +#define CPR_SINGLETON_IMPL(Class) \ + Class* Class::s_pInstance = nullptr; \ + std::mutex Class::s_mutex; \ + Class* Class::GetInstance() { \ + if (s_pInstance == nullptr) { \ + s_mutex.lock(); \ + if (s_pInstance == nullptr) { \ + s_pInstance = new Class; \ + } \ + s_mutex.unlock(); \ + } \ + return s_pInstance; \ + } \ + void Class::ExitInstance() { \ + s_mutex.lock(); \ + if (s_pInstance) { \ + delete s_pInstance; \ + s_pInstance = nullptr; \ + } \ + s_mutex.unlock(); \ + } +#endif + +#endif diff --git a/vendor/CPR/include/cpr/ssl_ctx.h b/vendor/CPR/include/cpr/ssl_ctx.h new file mode 100644 index 00000000..b6bc8119 --- /dev/null +++ b/vendor/CPR/include/cpr/ssl_ctx.h @@ -0,0 +1,26 @@ +#ifndef CPR_SSL_CTX_H +#define CPR_SSL_CTX_H + +#include "cpr/ssl_options.h" +#include + +#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION + +namespace cpr { + +/** + * This callback function loads a CA certificate from raw_cert_buf and gets called by libcurl + * just before the initialization of an SSL connection. + * The raw_cert_buf argument is set with the CURLOPT_SSL_CTX_DATA option and has to be a nul + * terminated buffer. + * + * Sources: https://curl.se/libcurl/c/CURLOPT_SSL_CTX_FUNCTION.html + * https://curl.se/libcurl/c/CURLOPT_SSL_CTX_DATA.html + */ +CURLcode sslctx_function_load_ca_cert_from_buffer(CURL* curl, void* sslctx, void* raw_cert_buf); + +} // Namespace cpr + +#endif + +#endif diff --git a/vendor/CPR/include/cpr/ssl_options.h b/vendor/CPR/include/cpr/ssl_options.h index 21f7d935..ce30cd9a 100644 --- a/vendor/CPR/include/cpr/ssl_options.h +++ b/vendor/CPR/include/cpr/ssl_options.h @@ -1,67 +1,70 @@ #ifndef CPR_SSLOPTIONS_H #define CPR_SSLOPTIONS_H +#include #include +#include +#include #include +#include "cpr/util.h" #include -#define __LIBCURL_VERSION_GTE(major, minor) \ - ((LIBCURL_VERSION_MAJOR > (major)) || \ - ((LIBCURL_VERSION_MAJOR == (major)) && (LIBCURL_VERSION_MINOR >= (minor)))) -#define __LIBCURL_VERSION_LT(major, minor) \ - ((LIBCURL_VERSION_MAJOR < (major)) || \ - ((LIBCURL_VERSION_MAJOR == (major)) && (LIBCURL_VERSION_MINOR < (minor)))) - #ifndef SUPPORT_ALPN -#define SUPPORT_ALPN __LIBCURL_VERSION_GTE(7, 36) +#define SUPPORT_ALPN LIBCURL_VERSION_NUM >= 0x072400 // 7.36.0 #endif #ifndef SUPPORT_NPN -#define SUPPORT_NPN __LIBCURL_VERSION_GTE(7, 36) +#define SUPPORT_NPN LIBCURL_VERSION_NUM >= 0x072400 && LIBCURL_VERSION_NUM <= 0x078600 // 7.36.0 - 7.86.0 #endif #ifndef SUPPORT_SSLv2 -#define SUPPORT_SSLv2 __LIBCURL_VERSION_LT(7, 19) +#define SUPPORT_SSLv2 LIBCURL_VERSION_NUM <= 0x071300 // 7.19.0 #endif #ifndef SUPPORT_SSLv3 -#define SUPPORT_SSLv3 __LIBCURL_VERSION_LT(7, 39) +#define SUPPORT_SSLv3 LIBCURL_VERSION_NUM <= 0x072700 // 7.39.0 #endif #ifndef SUPPORT_TLSv1_0 -#define SUPPORT_TLSv1_0 __LIBCURL_VERSION_GTE(7, 34) +#define SUPPORT_TLSv1_0 LIBCURL_VERSION_NUM >= 0x072200 // 7.34.0 #endif #ifndef SUPPORT_TLSv1_1 -#define SUPPORT_TLSv1_1 __LIBCURL_VERSION_GTE(7, 34) +#define SUPPORT_TLSv1_1 LIBCURL_VERSION_NUM >= 0x072200 // 7.34.0 #endif #ifndef SUPPORT_TLSv1_2 -#define SUPPORT_TLSv1_2 __LIBCURL_VERSION_GTE(7, 34) +#define SUPPORT_TLSv1_2 LIBCURL_VERSION_NUM >= 0x072200 // 7.34.0 #endif #ifndef SUPPORT_TLSv1_3 -#define SUPPORT_TLSv1_3 __LIBCURL_VERSION_GTE(7, 52) +#define SUPPORT_TLSv1_3 LIBCURL_VERSION_NUM >= 0x073400 // 7.52.0 #endif #ifndef SUPPORT_MAX_TLS_VERSION -#define SUPPORT_MAX_TLS_VERSION __LIBCURL_VERSION_GTE(7, 54) +#define SUPPORT_MAX_TLS_VERSION LIBCURL_VERSION_NUM >= 0x073600 // 7.54.0 #endif #ifndef SUPPORT_MAX_TLSv1_1 -#define SUPPORT_MAX_TLSv1_1 __LIBCURL_VERSION_GTE(7, 54) +#define SUPPORT_MAX_TLSv1_1 LIBCURL_VERSION_NUM >= 0x073600 // 7.54.0 #endif #ifndef SUPPORT_MAX_TLSv1_2 -#define SUPPORT_MAX_TLSv1_2 __LIBCURL_VERSION_GTE(7, 54) +#define SUPPORT_MAX_TLSv1_2 LIBCURL_VERSION_NUM >= 0x073600 // 7.54.0 #endif #ifndef SUPPORT_MAX_TLSv1_3 -#define SUPPORT_MAX_TLSv1_3 __LIBCURL_VERSION_GTE(7, 54) +#define SUPPORT_MAX_TLSv1_3 LIBCURL_VERSION_NUM >= 0x073600 // 7.54.0 #endif #ifndef SUPPORT_TLSv13_CIPHERS -#define SUPPORT_TLSv13_CIPHERS __LIBCURL_VERSION_GTE(7, 61) +#define SUPPORT_TLSv13_CIPHERS LIBCURL_VERSION_NUM >= 0x073D00 // 7.61.0 #endif #ifndef SUPPORT_SESSIONID_CACHE -#define SUPPORT_SESSIONID_CACHE __LIBCURL_VERSION_GTE(7, 16) +#define SUPPORT_SESSIONID_CACHE LIBCURL_VERSION_NUM >= 0x071000 // 7.16.0 #endif #ifndef SUPPORT_SSL_FALSESTART -#define SUPPORT_SSL_FALSESTART __LIBCURL_VERSION_GTE(7, 42) +#define SUPPORT_SSL_FALSESTART LIBCURL_VERSION_NUM >= 0x072A00 // 7.42.0 #endif #ifndef SUPPORT_SSL_NO_REVOKE -#define SUPPORT_SSL_NO_REVOKE __LIBCURL_VERSION_GTE(7, 44) +#define SUPPORT_SSL_NO_REVOKE LIBCURL_VERSION_NUM >= 0x072C00 // 7.44.0 +#endif +#ifndef SUPPORT_CURLOPT_SSLKEY_BLOB +#define SUPPORT_CURLOPT_SSLKEY_BLOB LIBCURL_VERSION_NUM >= 0x074700 // 7.71.0 +#endif +#ifndef SUPPORT_CURLOPT_SSL_CTX_FUNCTION +#define SUPPORT_CURLOPT_SSL_CTX_FUNCTION LIBCURL_VERSION_NUM >= 0x070B00 // 7.11.0 #endif namespace cpr { @@ -85,9 +88,11 @@ namespace ssl { class CertFile { public: // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) - CertFile(std::string&& p_filename) : filename(std::move(p_filename)) {} + CertFile(fs::path&& p_filename) : filename(std::move(p_filename)) {} - const std::string filename; + virtual ~CertFile() = default; + + const fs::path filename; virtual const char* GetCertType() const { return "PEM"; @@ -99,7 +104,9 @@ using PemCert = CertFile; class DerCert : public CertFile { public: // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) - DerCert(std::string&& p_filename) : CertFile(std::move(p_filename)) {} + DerCert(fs::path&& p_filename) : CertFile(std::move(p_filename)) {} + + ~DerCert() override = default; const char* GetCertType() const override { return "DER"; @@ -110,13 +117,16 @@ class DerCert : public CertFile { class KeyFile { public: // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) - KeyFile(std::string&& p_filename) : filename(std::move(p_filename)) {} + KeyFile(fs::path&& p_filename) : filename(std::move(p_filename)) {} template - KeyFile(FileType&& p_filename, PassType p_password) - : filename(std::forward(p_filename)), password(std::move(p_password)) {} + KeyFile(FileType&& p_filename, PassType p_password) : filename(std::forward(p_filename)), password(std::move(p_password)) {} - std::string filename; + virtual ~KeyFile() { + util::secureStringClear(password); + } + + fs::path filename; std::string password; virtual const char* GetKeyType() const { @@ -124,16 +134,39 @@ class KeyFile { } }; +#if SUPPORT_CURLOPT_SSLKEY_BLOB +class KeyBlob { + public: + // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) + KeyBlob(std::string&& p_blob) : blob(std::move(p_blob)) {} + + template + KeyBlob(BlobType&& p_blob, PassType p_password) : blob(std::forward(p_blob)), password(std::move(p_password)) {} + + virtual ~KeyBlob() { + util::secureStringClear(password); + } + + std::string blob; + std::string password; + + virtual const char* GetKeyType() const { + return "PEM"; + } +}; +#endif + using PemKey = KeyFile; class DerKey : public KeyFile { public: // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) - DerKey(std::string&& p_filename) : KeyFile(std::move(p_filename)) {} + DerKey(fs::path&& p_filename) : KeyFile(std::move(p_filename)) {} template - DerKey(FileType&& p_filename, PassType p_password) - : KeyFile(std::forward(p_filename), std::move(p_password)) {} + DerKey(FileType&& p_filename, PassType p_password) : KeyFile(std::forward(p_filename), std::move(p_password)) {} + + ~DerKey() override = default; const char* GetKeyType() const override { return "DER"; @@ -277,27 +310,37 @@ struct MaxTLSv1_3 {}; class CaInfo { public: // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) - CaInfo(std::string&& p_filename) : filename(std::move(p_filename)) {} + CaInfo(fs::path&& p_filename) : filename(std::move(p_filename)) {} - std::string filename; + fs::path filename; }; // specify directory holding CA certificates class CaPath { public: // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) - CaPath(std::string&& p_filename) : filename(std::move(p_filename)) {} + CaPath(fs::path&& p_filename) : filename(std::move(p_filename)) {} - std::string filename; + fs::path filename; }; +#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION +class CaBuffer { + public: + // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) + CaBuffer(std::string&& p_buffer) : buffer(std::move(p_buffer)) {} + + const std::string buffer; +}; +#endif + // specify a Certificate Revocation List file class Crl { public: // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) - Crl(std::string&& p_filename) : filename(std::move(p_filename)) {} + Crl(fs::path&& p_filename) : filename(std::move(p_filename)) {} - std::string filename; + fs::path filename; }; // specify ciphers to use for TLS @@ -352,7 +395,7 @@ class SslFastStart { #endif class NoRevoke { -public: + public: NoRevoke() = default; // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) NoRevoke(bool p_enabled) : enabled(p_enabled) {} @@ -367,9 +410,14 @@ public: } // namespace ssl struct SslOptions { + // We don't use fs::path here, as this leads to problems using windows std::string cert_file; std::string cert_type; + // We don't use fs::path here, as this leads to problems using windows std::string key_file; +#if SUPPORT_CURLOPT_SSLKEY_BLOB + std::string key_blob; +#endif std::string key_type; std::string key_pass; std::string pinned_public_key; @@ -389,8 +437,14 @@ struct SslOptions { #if SUPPORT_MAX_TLS_VERSION int max_version = CURL_SSLVERSION_MAX_DEFAULT; #endif + // We don't use fs::path here, as this leads to problems using windows std::string ca_info; + // We don't use fs::path here, as this leads to problems using windows std::string ca_path; +#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION + std::string ca_buffer; +#endif + // We don't use fs::path here, as this leads to problems using windows std::string crl_file; std::string ciphers; #if SUPPORT_TLSv13_CIPHERS @@ -400,15 +454,29 @@ struct SslOptions { bool session_id_cache = true; #endif + ~SslOptions() noexcept { +#if SUPPORT_CURLOPT_SSLKEY_BLOB + util::secureStringClear(key_blob); +#endif + util::secureStringClear(key_pass); + } + void SetOption(const ssl::CertFile& opt) { - cert_file = opt.filename; + cert_file = opt.filename.string(); cert_type = opt.GetCertType(); } void SetOption(const ssl::KeyFile& opt) { - key_file = opt.filename; + key_file = opt.filename.string(); key_type = opt.GetKeyType(); key_pass = opt.password; } +#if SUPPORT_CURLOPT_SSLKEY_BLOB + void SetOption(const ssl::KeyBlob& opt) { + key_blob = opt.blob; + key_type = opt.GetKeyType(); + key_pass = opt.password; + } +#endif void SetOption(const ssl::PinnedPublicKey& opt) { pinned_public_key = opt.pinned_public_key; } @@ -496,13 +564,18 @@ struct SslOptions { } #endif void SetOption(const ssl::CaInfo& opt) { - ca_info = opt.filename; + ca_info = opt.filename.string(); } void SetOption(const ssl::CaPath& opt) { - ca_path = opt.filename; + ca_path = opt.filename.string(); } +#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION + void SetOption(const ssl::CaBuffer& opt) { + ca_buffer = opt.buffer; + } +#endif void SetOption(const ssl::Crl& opt) { - crl_file = opt.filename; + crl_file = opt.filename.string(); } void SetOption(const ssl::Ciphers& opt) { ciphers = opt.ciphers; diff --git a/vendor/CPR/include/cpr/status_codes.h b/vendor/CPR/include/cpr/status_codes.h index 6c7acd6b..3a2e9cbf 100644 --- a/vendor/CPR/include/cpr/status_codes.h +++ b/vendor/CPR/include/cpr/status_codes.h @@ -1,5 +1,5 @@ -#ifndef _CPR_STATUS_CODES -#define _CPR_STATUS_CODES +#ifndef CPR_STATUS_CODES +#define CPR_STATUS_CODES #include namespace cpr { namespace status { diff --git a/vendor/CPR/include/cpr/threadpool.h b/vendor/CPR/include/cpr/threadpool.h new file mode 100644 index 00000000..bb7e7f21 --- /dev/null +++ b/vendor/CPR/include/cpr/threadpool.h @@ -0,0 +1,122 @@ +#ifndef CPR_THREAD_POOL_H +#define CPR_THREAD_POOL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CPR_DEFAULT_THREAD_POOL_MAX_THREAD_NUM std::thread::hardware_concurrency() + +constexpr size_t CPR_DEFAULT_THREAD_POOL_MIN_THREAD_NUM = 1; +constexpr std::chrono::milliseconds CPR_DEFAULT_THREAD_POOL_MAX_IDLE_TIME{60000}; + +namespace cpr { + +class ThreadPool { + public: + using Task = std::function; + + explicit ThreadPool(size_t min_threads = CPR_DEFAULT_THREAD_POOL_MIN_THREAD_NUM, size_t max_threads = CPR_DEFAULT_THREAD_POOL_MAX_THREAD_NUM, std::chrono::milliseconds max_idle_ms = CPR_DEFAULT_THREAD_POOL_MAX_IDLE_TIME); + + virtual ~ThreadPool(); + + void SetMinThreadNum(size_t min_threads) { + min_thread_num = min_threads; + } + void SetMaxThreadNum(size_t max_threads) { + max_thread_num = max_threads; + } + void SetMaxIdleTime(std::chrono::milliseconds ms) { + max_idle_time = ms; + } + size_t GetCurrentThreadNum() { + return cur_thread_num; + } + size_t GetIdleThreadNum() { + return idle_thread_num; + } + bool IsStarted() { + return status != STOP; + } + bool IsStopped() { + return status == STOP; + } + + int Start(size_t start_threads = 0); + int Stop(); + int Pause(); + int Resume(); + int Wait(); + + /** + * Return a future, calling future.get() will wait task done and return RetType. + * Submit(fn, args...) + * Submit(std::bind(&Class::mem_fn, &obj)) + * Submit(std::mem_fn(&Class::mem_fn, &obj)) + **/ + template + auto Submit(Fn&& fn, Args&&... args) { + if (status == STOP) { + Start(); + } + if (idle_thread_num <= 0 && cur_thread_num < max_thread_num) { + CreateThread(); + } + using RetType = decltype(fn(args...)); + auto task = std::make_shared >(std::bind(std::forward(fn), std::forward(args)...)); + std::future future = task->get_future(); + { + std::lock_guard locker(task_mutex); + tasks.emplace([task] { (*task)(); }); + } + + task_cond.notify_one(); + return future; + } + + private: + bool CreateThread(); + void AddThread(std::thread* thread); + void DelThread(std::thread::id id); + + public: + size_t min_thread_num; + size_t max_thread_num; + std::chrono::milliseconds max_idle_time; + + private: + enum Status { + STOP, + RUNNING, + PAUSE, + }; + + struct ThreadData { + std::shared_ptr thread; + std::thread::id id; + Status status; + time_t start_time; + time_t stop_time; + }; + + std::atomic status; + std::atomic cur_thread_num; + std::atomic idle_thread_num; + std::list threads; + std::mutex thread_mutex; + std::queue tasks; + std::mutex task_mutex; + std::condition_variable task_cond; +}; + +} // namespace cpr + +#endif diff --git a/vendor/CPR/include/cpr/timeout.h b/vendor/CPR/include/cpr/timeout.h index a01da439..83b3e68f 100644 --- a/vendor/CPR/include/cpr/timeout.h +++ b/vendor/CPR/include/cpr/timeout.h @@ -12,6 +12,8 @@ class Timeout { Timeout(const std::chrono::milliseconds& duration) : ms{duration} {} // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) Timeout(const std::int32_t& milliseconds) : Timeout{std::chrono::milliseconds(milliseconds)} {} + // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) + Timeout(const std::chrono::seconds& duration) : ms{std::chrono::milliseconds(duration).count()} {} // No way around since curl uses a long here. // NOLINTNEXTLINE(google-runtime-int) diff --git a/vendor/CPR/include/cpr/unix_socket.h b/vendor/CPR/include/cpr/unix_socket.h index 9d4d77c0..152caf0c 100644 --- a/vendor/CPR/include/cpr/unix_socket.h +++ b/vendor/CPR/include/cpr/unix_socket.h @@ -8,7 +8,7 @@ namespace cpr { class UnixSocket { public: // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) - UnixSocket(std::string&& unix_socket) : unix_socket_(std::move(unix_socket)) {} + UnixSocket(std::string unix_socket) : unix_socket_(std::move(unix_socket)) {} const char* GetUnixSocketString() const noexcept; diff --git a/vendor/CPR/include/cpr/user_agent.h b/vendor/CPR/include/cpr/user_agent.h index 71787c5f..a3cc1293 100644 --- a/vendor/CPR/include/cpr/user_agent.h +++ b/vendor/CPR/include/cpr/user_agent.h @@ -9,11 +9,11 @@ namespace cpr { class UserAgent : public StringHolder { public: - UserAgent() : StringHolder() {} + UserAgent() = default; // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) - UserAgent(const std::string& useragent) : StringHolder(useragent) {} + UserAgent(std::string useragent) : StringHolder(std::move(useragent)) {} // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) - UserAgent(std::string&& useragent) : StringHolder(std::move(useragent)) {} + UserAgent(std::string_view useragent) : StringHolder(useragent) {} // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) UserAgent(const char* useragent) : StringHolder(useragent) {} UserAgent(const char* str, size_t len) : StringHolder(str, len) {} diff --git a/vendor/CPR/include/cpr/util.h b/vendor/CPR/include/cpr/util.h index 1c2e1610..d851e23a 100644 --- a/vendor/CPR/include/cpr/util.h +++ b/vendor/CPR/include/cpr/util.h @@ -10,31 +10,36 @@ #include "cpr/cprtypes.h" #include "cpr/curlholder.h" -namespace cpr { -namespace util { +namespace cpr::util { -Header parseHeader(const std::string& headers, std::string* status_line = nullptr, - std::string* reason = nullptr); +Header parseHeader(const std::string& headers, std::string* status_line = nullptr, std::string* reason = nullptr); Cookies parseCookies(curl_slist* raw_cookies); size_t readUserFunction(char* ptr, size_t size, size_t nitems, const ReadCallback* read); size_t headerUserFunction(char* ptr, size_t size, size_t nmemb, const HeaderCallback* header); size_t writeFunction(char* ptr, size_t size, size_t nmemb, std::string* data); size_t writeFileFunction(char* ptr, size_t size, size_t nmemb, std::ofstream* file); size_t writeUserFunction(char* ptr, size_t size, size_t nmemb, const WriteCallback* write); -#if LIBCURL_VERSION_NUM < 0x072000 -int progressUserFunction(const ProgressCallback* progress, double dltotal, double dlnow, - double ultotal, double ulnow); -#else -int progressUserFunction(const ProgressCallback* progress, curl_off_t dltotal, curl_off_t dlnow, - curl_off_t ultotal, curl_off_t ulnow); -#endif -int debugUserFunction(CURL* handle, curl_infotype type, char* data, size_t size, - const DebugCallback* debug); + +template +int progressUserFunction(const T* progress, cpr_pf_arg_t dltotal, cpr_pf_arg_t dlnow, cpr_pf_arg_t ultotal, cpr_pf_arg_t ulnow) { + const int cancel_retval{1}; + static_assert(cancel_retval != CURL_PROGRESSFUNC_CONTINUE); + return (*progress)(dltotal, dlnow, ultotal, ulnow) ? 0 : cancel_retval; +} +int debugUserFunction(CURL* handle, curl_infotype type, char* data, size_t size, const DebugCallback* debug); std::vector split(const std::string& to_split, char delimiter); std::string urlEncode(const std::string& s); std::string urlDecode(const std::string& s); -} // namespace util -} // namespace cpr +/** + * Override the content of the provided string to hide sensitive data. The + * string content after invocation is undefined. The string size is reset to zero. + * impl. based on: + * https://github.com/ojeda/secure_clear/blob/master/example-implementation/secure_clear.h + **/ +void secureStringClear(std::string& s); +bool isTrue(const std::string& s); + +} // namespace cpr::util #endif diff --git a/vendor/DPP/CMakeLists.txt b/vendor/DPP/CMakeLists.txt index 41c1f85a..aabbc42a 100644 --- a/vendor/DPP/CMakeLists.txt +++ b/vendor/DPP/CMakeLists.txt @@ -31,7 +31,9 @@ include(CheckCXXSymbolExists) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) add_compile_definitions(DPP_BUILD) -file(READ "${CMAKE_CURRENT_SOURCE_DIR}/include/dpp/version.h" version_h) +set(DPP_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) + +file(READ "${DPP_ROOT_PATH}/include/dpp/version.h" version_h) if(NOT version_h MATCHES "DPP_VERSION_SHORT ([0-9][0-9])([0-9][0-9])([0-9][0-9])") message(FATAL_ERROR "Cannot get DPP_VERSION_SHORT from version.h") @@ -43,7 +45,7 @@ math(EXPR DPP_VERSION_PATCH "${CMAKE_MATCH_3}") string(CONCAT DPP_VERSION "${DPP_VERSION_MAJOR}.${DPP_VERSION_MINOR}.${DPP_VERSION_PATCH}") -set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${CMAKE_SOURCE_DIR}/cmake/") +set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${DPP_ROOT_PATH}/cmake/") if (DPP_NO_VCPKG) message("-- INFO: Explicitly disabling VCPKG as running inside the CI action.") @@ -53,7 +55,7 @@ endif() if (WIN32 AND NOT MINGW AND BUILD_SHARED_LIBS) message("-- INFO: Configuring .rc resource script") - configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/dpp/dpp.rc.in" "${CMAKE_CURRENT_SOURCE_DIR}/src/dpp/dpp.rc" NEWLINE_STYLE WIN32) + configure_file("${DPP_ROOT_PATH}/src/dpp/dpp.rc.in" "${DPP_ROOT_PATH}/src/dpp/dpp.rc" NEWLINE_STYLE WIN32) endif() if (NOT DPP_NO_VCPKG AND EXISTS "${_VCPKG_ROOT_DIR}") @@ -65,7 +67,7 @@ if (NOT DPP_NO_VCPKG AND EXISTS "${_VCPKG_ROOT_DIR}") HOMEPAGE_URL "https://dpp.dev/" DESCRIPTION "An incredibly lightweight C++ Discord library." ) - add_subdirectory(library-vcpkg) + add_subdirectory(library-vcpkg) else() set(PROJECT_NAME "libdpp") project( diff --git a/vendor/DPP/buildtools/changelog.php b/vendor/DPP/buildtools/changelog.php index a8553d78..654515fb 100644 --- a/vendor/DPP/buildtools/changelog.php +++ b/vendor/DPP/buildtools/changelog.php @@ -22,6 +22,7 @@ $categories = [ 'improvement' => 'â™»ï¸ Refactoring', 'refactor' => 'â™»ï¸ Refactoring', 'refactored' => 'â™»ï¸ Refactoring', + 'refactoring' => 'â™»ï¸ Refactoring', 'deprecated' => 'â™»ï¸ Refactoring', 'deprecate' => 'â™»ï¸ Refactoring', 'remove' => 'â™»ï¸ Refactoring', diff --git a/vendor/DPP/buildtools/classes/Packager/Vcpkg.php b/vendor/DPP/buildtools/classes/Packager/Vcpkg.php new file mode 100644 index 00000000..87523ab0 --- /dev/null +++ b/vendor/DPP/buildtools/classes/Packager/Vcpkg.php @@ -0,0 +1,287 @@ +latestTag = preg_replace("/\n/", "", shell_exec("git describe --tags `git rev-list --tags --max-count=1`")); + $this->version = preg_replace('/^v/', '', $this->getTag()); + echo GREEN . "Latest tag: " . $this->getTag() . " version: " . $this->getVersion() . "\n" . WHITE; + + $this->git = trim(`which git`); + $this->sudo = trim(`which sudo`); + } + + /** + * Get semver version + * + * @return string + */ + public function getVersion(): string + { + return $this->version; + } + + /** + * Get the git tag we are building + * + * @return string + */ + public function getTag(): string + { + return $this->latestTag; + } + + private function git(string $parameters, bool $sudo = false): void + { + system(($sudo ? $this->sudo . ' ' : '') . $this->git . ' ' . $parameters); + } + + private function sudo(string $command): void + { + system($this->sudo . ' ' . $command); + } + + /** + * Check out a repository by tag or branch name to ~/dpp, + * using the personal access token and username passed in as command line parameters. + * + * @param string $tag Tag to clone + * @return bool false if the repository could not be cloned + */ + function checkoutRepository(string $tag = ""): bool + { + global $argv; + + if (empty($tag)) { + /* Empty tag means use the main branch */ + $tag = `{$this->git} config --get init.defaultBranch || echo master`; + } + $repositoryUrl = 'https://' . urlencode($argv[1]) . ':' . urlencode($argv[2]) . '@github.com/brainboxdotcc/DPP'; + + echo GREEN . "Check out repository: $tag (user: ". $argv[1] . " branch: " . $tag . ")\n" . WHITE; + + chdir(getenv('HOME')); + system('rm -rf ./dpp'); + $this->git('config --global user.email "noreply@dpp.dev"'); + $this->git('config --global user.name "DPP VCPKG Bot"'); + $this->git('clone ' . escapeshellarg($repositoryUrl) . ' ./dpp --depth=1'); + + /* This is noisy, silence it */ + $status = chdir(getenv("HOME") . '/dpp'); + $this->git('fetch -at 2>/dev/null'); + $this->git('checkout ' . escapeshellarg($tag) . ' 2>/dev/null'); + + return $status; + } + + /** + * Create ./vcpkg/ports/dpp/vcpkg.json and return the portfile contents to + * build the branch that is cloned at ~/dpp + * + * @param string $sha512 The SHA512 sum of the tagged download, or initially + * zero, which means that the vcpkg install command should obtain it the + * second time around. + * @return string The portfile content + */ + function constructPortAndVersionFile(string $sha512 = "0"): string + { + echo GREEN . "Construct portfile for " . $this->getVersion() . ", sha512: $sha512\n" . WHITE; + chdir(getenv("HOME") . '/dpp'); + + $portFileContent = 'vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO brainboxdotcc/DPP + REF "v${VERSION}" + # Auto-generated by release CI action at brainboxdotcc/DPP + SHA512 ' . $sha512 . ' +) + +vcpkg_cmake_configure( + SOURCE_PATH "${SOURCE_PATH}" + DISABLE_PARALLEL_CONFIGURE +) + +vcpkg_cmake_install() + +vcpkg_cmake_config_fixup(NO_PREFIX_CORRECTION) + +file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/share/dpp") +file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include") + +if(VCPKG_LIBRARY_LINKAGE STREQUAL "static") + file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/bin" "${CURRENT_PACKAGES_DIR}/debug/bin") +endif() + +file( + INSTALL "${SOURCE_PATH}/LICENSE" + DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" + RENAME copyright +) + +file(COPY "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}") +'; + // ./vcpkg/ports/dpp/vcpkg.json + $versionFileContent = '{ + "name": "dpp", + "version": ' . json_encode($this->getVersion()) . ', + "description": "D++ Extremely Lightweight C++ Discord Library.", + "homepage": "https://dpp.dev/", + "license": "Apache-2.0", + "supports": "((windows & !static & !uwp) | linux | osx)", + "dependencies": [ + "libsodium", + "nlohmann-json", + "openssl", + "opus", + "zlib", + { + "name": "vcpkg-cmake", + "host": true + }, + { + "name": "vcpkg-cmake-config", + "host": true + } + ] +}'; + echo GREEN . "Writing portfile...\n" . WHITE; + file_put_contents('./vcpkg/ports/dpp/vcpkg.json', $versionFileContent); + return $portFileContent; + } + + /** + * Attempt the first build of the vcpkg port. This will always fail, as it is given + * an SHA512 sum of 0. When it fails the output contains the SHA512 sum, which is then + * extracted from the error output using a regular expression, and saved for a second + * attempt. + * @param string $portFileContent Portfile content from constructPortAndVersionFile() + * with an SHA512 sum of 0 passed in. + * @return string SHA512 sum of build output + */ + function firstBuild(string $portFileContent): string + { + echo GREEN . "Starting first build\n" . WHITE; + + chdir(getenv("HOME") . '/dpp'); + echo GREEN . "Create /usr/local/share/vcpkg/ports/dpp/\n" . WHITE; + $this->sudo('mkdir -p /usr/local/share/vcpkg/ports/dpp/'); + echo GREEN . "Copy vcpkg.json to /usr/local/share/vcpkg/ports/dpp/vcpkg.json\n" . WHITE; + $this->sudo('cp -v -R ./vcpkg/ports/dpp/vcpkg.json /usr/local/share/vcpkg/ports/dpp/vcpkg.json'); + file_put_contents('/tmp/portfile', $portFileContent); + $this->sudo('cp -v -R /tmp/portfile /usr/local/share/vcpkg/ports/dpp/portfile.cmake'); + unlink('/tmp/portfile'); + $buildResults = shell_exec($this->sudo . ' /usr/local/share/vcpkg/vcpkg install dpp:x64-linux'); + $matches = []; + if (preg_match('/Actual hash:\s+([0-9a-fA-F]+)/', $buildResults, $matches)) { + echo GREEN . "Obtained SHA512 for first build: " . $matches[1] . "\n" . WHITE; + $this->firstBuildComplete = true; + return $matches[1]; + } + echo RED . "No SHA512 found during first build :(\n" . WHITE; + return ''; + } + + /** + * Second build using a valid SHA512 sum. This attempt should succeed, allowing us to push + * the changed vcpkg portfiles into the master branch, where they can be used in a PR to + * microsoft/vcpkg repository later. + * + * @param string $portFileContent the contents of the portfile, containing a valid SHA512 + * sum from the first build attempt. + * @return bool False if the build failed + */ + function secondBuild(string $portFileContent): bool + { + + if (!$this->firstBuildComplete) { + throw new RuntimeException("No SHA512 sum is available, first build has not been run!"); + } + + echo GREEN . "Executing second build\n" . WHITE; + echo GREEN . "Copy local port files to /usr/local/share...\n" . WHITE; + chdir(getenv("HOME") . '/dpp'); + file_put_contents('./vcpkg/ports/dpp/portfile.cmake', $portFileContent); + $this->sudo('cp -v -R ./vcpkg/ports/dpp/vcpkg.json /usr/local/share/vcpkg/ports/dpp/vcpkg.json'); + $this->sudo('cp -v -R ./vcpkg/ports/dpp/portfile.cmake /usr/local/share/vcpkg/ports/dpp/portfile.cmake'); + $this->sudo('cp -v -R ./vcpkg/ports/* /usr/local/share/vcpkg/ports/'); + + echo GREEN . "vcpkg x-add-version...\n" . WHITE; + chdir('/usr/local/share/vcpkg'); + $this->sudo('./vcpkg format-manifest ./ports/dpp/vcpkg.json'); + /* Note: We commit this in /usr/local, but we never push it (we can't) */ + $this->git('add .', true); + $this->git('commit -m "[bot] VCPKG info update"', true); + $this->sudo('/usr/local/share/vcpkg/vcpkg x-add-version dpp'); + + echo GREEN . "Copy back port files from /usr/local/share...\n" . WHITE; + chdir(getenv('HOME') . '/dpp'); + system('cp -v -R /usr/local/share/vcpkg/ports/dpp/vcpkg.json ./vcpkg/ports/dpp/vcpkg.json'); + system('cp -v -R /usr/local/share/vcpkg/versions/baseline.json ./vcpkg/versions/baseline.json'); + system('cp -v -R /usr/local/share/vcpkg/versions/d-/dpp.json ./vcpkg/versions/d-/dpp.json'); + + echo GREEN . "Commit and push changes to master branch\n" . WHITE; + $this->git('add .'); + $this->git('commit -m "[bot] VCPKG info update [skip ci]"'); + $this->git('config pull.rebase false'); + $this->git('pull'); + $this->git('push origin master'); + + echo GREEN . "vcpkg install...\n" . WHITE; + $resultCode = 0; + $output = []; + exec($this->sudo . ' /usr/local/share/vcpkg/vcpkg install dpp:x64-linux', $output, $resultCode); + + if ($resultCode != 0) { + echo RED . "There were build errors!\n\nBuild log:\n" . WHITE; + readfile("/usr/local/share/vcpkg/buildtrees/dpp/install-x64-linux-dbg-out.log"); + } + + return $resultCode == 0; + } +}; diff --git a/vendor/DPP/buildtools/make_struct.php b/vendor/DPP/buildtools/make_struct.php index 61ff1f36..78c0fce4 100644 --- a/vendor/DPP/buildtools/make_struct.php +++ b/vendor/DPP/buildtools/make_struct.php @@ -42,6 +42,8 @@ $forcedReturn = [ 'message_create' => 'message', 'message_edit' => 'message', 'thread_create_in_forum' => 'thread', + 'threads_get_active' => 'active_threads', + 'user_get_cached' => 'user_identified', ]; /* Get the contents of cluster.h into an array */ @@ -61,8 +63,11 @@ if (!$generator->checkForChanges()) { exit(0); } +$lastFunc = ''; +$l = 0; /* Scan every line of the C++ source */ foreach ($clustercpp as $cpp) { + $l++; /* Look for declaration of function body */ if ($state == STATE_SEARCH_FOR_FUNCTION && preg_match('/^\s*void\s+cluster::([^(]+)\s*\((.*)command_completion_event_t\s*callback\s*\)/', $cpp, $matches)) { @@ -111,10 +116,15 @@ foreach ($clustercpp as $cpp) { $content .= $generator->generateHeaderDef($returnType, $currentFunction, $parameters, $noDefaults, $parameterNames); $cppcontent .= $generator->generateCppDef($returnType, $currentFunction, $parameters, $noDefaults, $parameterNames); } + $lastFunc = $currentFunction; $currentFunction = $parameters = $returnType = ''; $state = STATE_SEARCH_FOR_FUNCTION; } } +if ($state != STATE_SEARCH_FOR_FUNCTION) { + die("\n\n\nBuilding headers is broken ($l) - state machine finished in the middle of function $currentFunction (previous $lastFunc) with parameters $parameters rv $returnType state=$state\n\n\n"); +} + $content .= <<checkoutRepository($vcpkg->getTag())) { + exit(1); +} + +/* First run with SHA512 of 0 to gather actual value and save it */ +$sha512 = $vcpkg->firstBuild( + $vcpkg->constructPortAndVersionFile() +); +if (!empty($sha512)) { + /* Now check out master */ + if (!$vcpkg->checkoutRepository()) { + exit(1); + } + + /* Attempt second build with the valid SHA512 sum. Program exit + * status is the exit status of `vcpkg install` + */ + exit( + $vcpkg->secondBuild( + $vcpkg->constructPortAndVersionFile($sha512) + ) + ); + +} + +/* Error if no SHA sum could be generated */ +exit(1); \ No newline at end of file diff --git a/vendor/DPP/cmake/ARM64ToolChain.cmake b/vendor/DPP/cmake/ARM64ToolChain.cmake index 01d524a5..7c09aa45 100644 --- a/vendor/DPP/cmake/ARM64ToolChain.cmake +++ b/vendor/DPP/cmake/ARM64ToolChain.cmake @@ -10,7 +10,7 @@ SET(CMAKE_LIBRARY_ARCHITECTURE aarch64-linux-gnu) SET(CPACK_DEBIAN_PACKAGE_ARCHITECTURE aarch64) SET(CPACK_RPM_PACKAGE_ARCHITECTURE aarch64) -SET(RASPBERRY_ROOT_PATH ${CMAKE_SOURCE_DIR}/arm_raspberry) +SET(RASPBERRY_ROOT_PATH ${DPP_ROOT_PATH}/arm_raspberry) SET(RASPBERRY_KINETIC_PATH ${RASPBERRY_ROOT_PATH}/opt/ros/kinetic) SET(CMAKE_FIND_ROOT_PATH ${RASPBERRY_ROOT_PATH} ${CATKIN_DEVEL_PREFIX}) diff --git a/vendor/DPP/cmake/ARMv6ToolChain.cmake b/vendor/DPP/cmake/ARMv6ToolChain.cmake index 6ae4d0a2..b1afe157 100644 --- a/vendor/DPP/cmake/ARMv6ToolChain.cmake +++ b/vendor/DPP/cmake/ARMv6ToolChain.cmake @@ -3,7 +3,7 @@ SET(CMAKE_SYSTEM_NAME Linux) #SET(CMAKE_SYSTEM_PROCESSOR armhf) #If you have installed cross compiler to somewhere else, please specify that path. -SET(COMPILER_ROOT /opt/cross-pi-gcc) +SET(COMPILER_ROOT /opt/cross-pi-gcc) SET(CMAKE_C_COMPILER ${COMPILER_ROOT}/bin/arm-linux-gnueabihf-gcc-8.3.0) SET(CMAKE_CXX_COMPILER ${COMPILER_ROOT}/bin/arm-linux-gnueabihf-g++) @@ -34,17 +34,17 @@ UNSET(CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES) SET(CMAKE_INCLUDE_DIRECTORIES_BEFORE ON) INCLUDE_DIRECTORIES( ${COMPILER_ROOT}/arm-linux-gnueabihf/libc/usr/include - ${COMPILER_ROOT}/arm-linux-gnueabihf/include + ${COMPILER_ROOT}/arm-linux-gnueabihf/include ${COMPILER_ROOT}/arm-linux-gnueabihf/include/c++/8.3.0 ${COMPILER_ROOT}/arm-linux-gnueabihf/include/c++/8.3.0/arm-linux-gnueabihf ${COMPILER_ROOT}/lib/gcc/arm-linux-gnueabihf/8.3.0/include ${COMPILER_ROOT}/lib/gcc/arm-linux-gnueabihf/8.3.0/include-fixed - ${CMAKE_SOURCE_DIR}/rootfs/usr/include/arm-linux-gnueabihf) + ${DPP_ROOT_PATH}/rootfs/usr/include/arm-linux-gnueabihf) SET(CMAKE_INCLUDE_DIRECTORIES_BEFORE OFF) -SET(ZLIB_LIBRARY ${CMAKE_SOURCE_DIR}/rootfs/lib/arm-linux-gnueabihf/libz.so.1.2.11) -SET(OPENSSL_CRYPTO_LIBRARY ${CMAKE_SOURCE_DIR}/rootfs/usr/lib/arm-linux-gnueabihf/libcrypto.so.1.1) -SET(OPENSSL_SSL_LIBRARY ${CMAKE_SOURCE_DIR}/rootfs/usr/lib/arm-linux-gnueabihf/libssl.so.1.1) +SET(ZLIB_LIBRARY ${DPP_ROOT_PATH}/rootfs/lib/arm-linux-gnueabihf/libz.so.1.2.11) +SET(OPENSSL_CRYPTO_LIBRARY ${DPP_ROOT_PATH}/rootfs/usr/lib/arm-linux-gnueabihf/libcrypto.so.1.1) +SET(OPENSSL_SSL_LIBRARY ${DPP_ROOT_PATH}/rootfs/usr/lib/arm-linux-gnueabihf/libssl.so.1.1) SET(CMAKE_CXX_COMPILER_WORKS 1) SET(CMAKE_C_FLAGS " ${CMAKE_C_FLAGS} -nostdinc --sysroot=${RASPBERRY_ROOT_PATH} -Wno-psabi " CACHE INTERNAL "" FORCE) @@ -54,11 +54,11 @@ SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} --sysroot=${RASPBERRY_ROOT_PAT SET(LD_LIBRARY_PATH ${RASPBERRY_KINETIC_PATH}/lib) -EXECUTE_PROCESS(COMMAND wget -P ${CMAKE_SOURCE_DIR}/rootfs -q http://content.dpp.dev/zlib1g_1.2.11.dfsg-1_armhf.deb http://content.dpp.dev/zlib1g-dev_1.2.11.dfsg-1_armhf.deb http://content.dpp.dev/libssl1.1_1.1.1m-1_armhf.deb http://content.dpp.dev/libssl-dev_1.1.1m-1_armhf.deb https://content.dpp.dev/raspi-toolchain.tar.gz) +EXECUTE_PROCESS(COMMAND wget -P ${DPP_ROOT_PATH}/rootfs -q http://content.dpp.dev/zlib1g_1.2.11.dfsg-1_armhf.deb http://content.dpp.dev/zlib1g-dev_1.2.11.dfsg-1_armhf.deb http://content.dpp.dev/libssl1.1_1.1.1m-1_armhf.deb http://content.dpp.dev/libssl-dev_1.1.1m-1_armhf.deb https://content.dpp.dev/raspi-toolchain.tar.gz) EXECUTE_PROCESS( - COMMAND tar -xzf ${CMAKE_SOURCE_DIR}/rootfs/raspi-toolchain.tar.gz -C /opt - COMMAND sudo dpkg-deb -x ${CMAKE_SOURCE_DIR}/rootfs/zlib1g-dev_1.2.11.dfsg-1_armhf.deb ${CMAKE_SOURCE_DIR}/rootfs - COMMAND sudo dpkg-deb -x ${CMAKE_SOURCE_DIR}/rootfs/zlib1g_1.2.11.dfsg-1_armhf.deb ${CMAKE_SOURCE_DIR}/rootfs - COMMAND sudo dpkg-deb -x ${CMAKE_SOURCE_DIR}/rootfs/libssl-dev_1.1.1m-1_armhf.deb ${CMAKE_SOURCE_DIR}/rootfs - COMMAND sudo dpkg-deb -x ${CMAKE_SOURCE_DIR}/rootfs/libssl1.1_1.1.1m-1_armhf.deb ${CMAKE_SOURCE_DIR}/rootfs) + COMMAND tar -xzf ${DPP_ROOT_PATH}/rootfs/raspi-toolchain.tar.gz -C /opt + COMMAND sudo dpkg-deb -x ${DPP_ROOT_PATH}/rootfs/zlib1g-dev_1.2.11.dfsg-1_armhf.deb ${DPP_ROOT_PATH}/rootfs + COMMAND sudo dpkg-deb -x ${DPP_ROOT_PATH}/rootfs/zlib1g_1.2.11.dfsg-1_armhf.deb ${DPP_ROOT_PATH}/rootfs + COMMAND sudo dpkg-deb -x ${DPP_ROOT_PATH}/rootfs/libssl-dev_1.1.1m-1_armhf.deb ${DPP_ROOT_PATH}/rootfs + COMMAND sudo dpkg-deb -x ${DPP_ROOT_PATH}/rootfs/libssl1.1_1.1.1m-1_armhf.deb ${DPP_ROOT_PATH}/rootfs) diff --git a/vendor/DPP/cmake/ARMv7ToolChain.cmake b/vendor/DPP/cmake/ARMv7ToolChain.cmake index 309138be..e5daa0ca 100644 --- a/vendor/DPP/cmake/ARMv7ToolChain.cmake +++ b/vendor/DPP/cmake/ARMv7ToolChain.cmake @@ -10,13 +10,13 @@ SET(CMAKE_LIBRARY_ARCHITECTURE arm-linux-gnueabihf) SET(CPACK_DEBIAN_PACKAGE_ARCHITECTURE armhf) SET(CPACK_RPM_PACKAGE_ARCHITECTURE armhf) -SET(RASPBERRY_ROOT_PATH ${CMAKE_SOURCE_DIR}/arm_raspberry) +SET(RASPBERRY_ROOT_PATH ${DPP_ROOT_PATH}/arm_raspberry) SET(RASPBERRY_KINETIC_PATH ${RASPBERRY_ROOT_PATH}/opt/ros/kinetic) SET(CMAKE_FIND_ROOT_PATH ${RASPBERRY_ROOT_PATH} ${CATKIN_DEVEL_PREFIX}) #If you have installed cross compiler to somewhere else, please specify that path. -SET(COMPILER_ROOT /usr/bin) +SET(COMPILER_ROOT /usr/bin) #Have to set this one to BOTH, to allow CMake to find rospack #This set of variables controls whether the CMAKE_FIND_ROOT_PATH and CMAKE_SYSROOT are used for find_xxx() operations. @@ -41,7 +41,7 @@ SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} --sysroot=${RASPBERRY_ROOT_PAT SET(LD_LIBRARY_PATH ${RASPBERRY_KINETIC_PATH}/lib) -EXECUTE_PROCESS(COMMAND printf "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ focal main multiverse restricted universe\ndeb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports/ focal main multiverse restricted universe\ndeb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports/ focal-updates main multiverse restricted universe\ndeb [arch=amd64] http://archive.ubuntu.com/ubuntu/ focal-updates main multiverse restricted universe\ndeb [arch=amd64] http://security.ubuntu.com/ubuntu/ focal-security main multiverse restricted universe" +EXECUTE_PROCESS(COMMAND printf "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ focal main multiverse restricted universe\ndeb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports/ focal main multiverse restricted universe\ndeb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports/ focal-updates main multiverse restricted universe\ndeb [arch=amd64] http://archive.ubuntu.com/ubuntu/ focal-updates main multiverse restricted universe\ndeb [arch=amd64] http://security.ubuntu.com/ubuntu/ focal-security main multiverse restricted universe" OUTPUT_FILE TMPFILE) EXECUTE_PROCESS(COMMAND sudo mv TMPFILE /etc/apt/sources.list) EXECUTE_PROCESS(COMMAND sudo dpkg --add-architecture armhf) diff --git a/vendor/DPP/cmake/CPackSetup.cmake b/vendor/DPP/cmake/CPackSetup.cmake index 75e914db..42fc924f 100644 --- a/vendor/DPP/cmake/CPackSetup.cmake +++ b/vendor/DPP/cmake/CPackSetup.cmake @@ -17,7 +17,7 @@ if (WIN32) ARCHIVE DESTINATION ${DPP_INSTALL_LIBRARY_DIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} INCLUDES DESTINATION ${DPP_INSTALL_INCLUDE_DIR}) - install(DIRECTORY "${CMAKE_SOURCE_DIR}/include/" DESTINATION "${DPP_INSTALL_INCLUDE_DIR}") + install(DIRECTORY "${DPP_ROOT_PATH}/include/" DESTINATION "${DPP_INSTALL_INCLUDE_DIR}") else() install(TARGETS dpp EXPORT ${DPP_EXPORT_NAME} @@ -34,7 +34,7 @@ write_basic_package_version_file(${DPP_VERSION_FILE} COMPATIBILITY SameMajorVersion) ## Include the file which allows `find_package(dpp)` to function. -install(FILES "${CMAKE_SOURCE_DIR}/cmake/dpp-config.cmake" "${DPP_VERSION_FILE}" DESTINATION "${DPP_CMAKE_DIR}") +install(FILES "${DPP_ROOT_PATH}/cmake/dpp-config.cmake" "${DPP_VERSION_FILE}" DESTINATION "${DPP_CMAKE_DIR}") ## Export the targets to allow other projects to easily include this project install(EXPORT "${DPP_EXPORT_NAME}" DESTINATION "${DPP_CMAKE_DIR}" NAMESPACE dpp::) diff --git a/vendor/DPP/cmake/LINUXx86ToolChain.cmake b/vendor/DPP/cmake/LINUXx86ToolChain.cmake index 9f941ea4..93994a35 100644 --- a/vendor/DPP/cmake/LINUXx86ToolChain.cmake +++ b/vendor/DPP/cmake/LINUXx86ToolChain.cmake @@ -24,7 +24,9 @@ SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32 " CACHE INTERNAL "" FORCE) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32 " CACHE INTERNAL "" FORCE) EXECUTE_PROCESS(COMMAND sudo dpkg --add-architecture i386) -EXECUTE_PROCESS(COMMAND sudo apt update) -EXECUTE_PROCESS(COMMAND sudo apt install -y g++-10 gcc-10-multilib glibc-*:i386 libc6-dev-i386 g++-10-multilib zlib1g-dev:i386 libssl-dev:i386 libopus-dev:i386 libsodium-dev:i386) -EXECUTE_PROCESS(COMMAND sudo mv /usr/lib/i386-linux-gnu/pkgconfig/libsodium.pc /usr/lib/pkgconfig/) +EXECUTE_PROCESS(COMMAND sudo apt-get update) +EXECUTE_PROCESS(COMMAND sudo apt-get install -qq -y g++-10 gcc-10-multilib glibc-*:i386 libc6-dev-i386 g++-10-multilib zlib1g-dev:i386 libssl-dev:i386 libopus-dev:i386 libsodium-dev:i386) +EXECUTE_PROCESS(COMMAND export PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig/) + +set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY") diff --git a/vendor/DPP/cmake/Raw-Files/Bottom-Of-Portfile.cmake b/vendor/DPP/cmake/Raw-Files/Bottom-Of-Portfile.cmake index cf861f79..9370e0ce 100644 --- a/vendor/DPP/cmake/Raw-Files/Bottom-Of-Portfile.cmake +++ b/vendor/DPP/cmake/Raw-Files/Bottom-Of-Portfile.cmake @@ -1,6 +1,4 @@ HEAD_REF master - PATCHES - make-pkgconfig-required.patch ) vcpkg_cmake_configure( diff --git a/vendor/DPP/docpages/01_frequently_asked_questions.md b/vendor/DPP/docpages/01_frequently_asked_questions.md deleted file mode 100644 index 2d27c8ef..00000000 --- a/vendor/DPP/docpages/01_frequently_asked_questions.md +++ /dev/null @@ -1,109 +0,0 @@ -# Frequently Asked Questions (FAQ) - -[TOC] - -## Is this library in production use? -This library powers the bot [TriviaBot](https://triviabot.co.uk) which has over **151,000 servers**, and [Sporks](https://sporks.gg) which has over **3,500 severs**. The library's use in these bots shows that the library is production ready for bots of all sizes. - -## How much RAM does this library use? -In production on TriviaBot, the bot takes approximately 2gb of ram to run 18 separate processes (this is approximately **140mb** per process) on a production bot with 36 million users and 151,000 guilds. Each process takes under 1% CPU. This is less than a quarter of the memory of a similar C++ Discord library, **Aegis.cpp** (version 2). - -For a very small bot, you can get the memory usage as low as **6 megabytes** on a Raspberry Pi. - -## How do I use this library in Windows? -The easiest way is to use our [template project](https://github.com/brainboxdotcc/windows-bot-template). If you are unable to do this, download the precompiled latest release from our GitHub releases, and take the dlls, .lib file, and header files (`bin`, `lib` and `include` directories), placing them in a easily accessible place on your computer. Go into Visual Studio project settings in a new project, and point the project directories (notably the library directories and and include directories) at the correct locations. Add the `include` folder you extracted to your include directories, and add `dpp.lib` to your library directories. Ensure the project is set to C++17 standard in the settings. You should then be able to compile example programs within that project. When you run the program you have compiled you must ensure that all the dll files from the `bin` directory exist in the same directory as your executable. - -## Does this library support Visual Studio 2022? -Yes! The master branch comes with pre-built binaries for Visual Studio 2022 and our windows bot template has a [vs2022 branch](https://github.com/brainboxdotcc/windows-bot-template/tree/vs2022) which you can clone to get Visual Studio 2022 specific code. For the time being we support both Visual Studio 2019 and 2022. At some point in the future only 2022 may be supported as 2019 becomes outdated. - -## How much of the library is completed? -All REST calls (outbound commands) are completed including all currently available interactions, and all Discord events are available. The library also has voice support. - -## How do I chat with the developers or get help? -The best place to do this is on the [Discord server](https://discord.gg/dpp). You most likely won't get an answer immediately (we have lives, and need to sleep sometimes), but we will be willing to help! - -## How can I contribute to development? -Just star and fork a copy of the repository, and submit a Pull Request! We won't bite! Authors of accepted pull requests get a special role on our [Discord server](https://discord.gg/dpp). - -## Whats the best way to learn C++? -A simple search can find some learning tools, however not all are good. Here is a list of some (good) learning resources: - -* [CodeAcademy](https://www.codecademy.com/learn/c-plus-plus) -* [Learn CPP](https://www.learncpp.com/) -* [Learn CPP (Very Basic)](https://www.learn-cpp.org/) - -If you don't understand something then feel free to ask in the [Discord server](https://discord.gg/dpp) ...*we don't bite!* - -## Do I need to be an expert in C++ to use this library? -NO! Definitely not! We have tried to keep things as simple as possible. We only use language features where they make sense, not just because they exist. Take a look at the example program (`test.cpp` and you'll see just how simple it is to get up and running quickly). We use a small subset of C++17 and C++14 features. - -## Why is D++ also called DPP -DPP is short for *D Plus Plus* (D++), a play on the Discord and C++ names. You'll see the library referred to as `dpp` within source code as `d++` is not a valid symbol so we couldn't exactly use that as our namespace name. - -## Is D++ a single header library? -No, D++ is a classically designed library which installs itself to your library directory/system directory as a shared object or dll. You must link to its .lib file and include its header files to make use of it. We have no plans for a single-header build. - -## Does this library support slash commands/interactions? -Yes! This library supports slash commands and interactions. For more information please see \ref slashcommands "Using Slash Commands and Interactions". - -## Does this library support buttons/drop down menus (message components)? -Yes! This library supports button message components, e.g. interactive buttons on the bottom of messages. For more information please see our \ref components "Using component interactions" and \ref components2 "Using component interactions (advanced)" examples. - -## Is the library asynchronous? -All functions within D++ are multi-threaded. You should still avoid doing long operations within event handlers or within callbacks, to prevent starvation of the threads managing each shard. Various blocking operations such as running files and making HTTP requests are offered as library functions (for example dpp::utility::exec) - -## Does this library support voice? -Yes! This library supports voice and will automatically enable voice if your system has the libopus and libsodium libraries. When running `cmake` the script will identify if these libraries are found. See the example programs for information on how to send audio. - -## Does this library support sharding? -Yes! D++ supports sharding and also clustering (grouping of shards into one process) to ensure it is scalable for small and large bots alike. - -## How do I contribute to the documentation and website? -The documentation and website are built using Doxygen. To contribute to site pages, submit a Pull Request to the main repository. The site pages can be found within the `docpages` directory. Details of classes, variables, namespaces etc are auto generated from Doxygen comments within the library source code in the `include` and `src` folders. - -## What version of the Discord API does this library support? -D++ only supports Discord API v10, the latest version. D++ major version numbers match the supported Discord API version. - -## Does this Discord library support the threads feature? -Yes! D++ supports Discord threads. You can create, edit and delete threads and also attach events watching for messages within threads. - -## Does D++ require C++20 support? -No, at the current time we do not use any C++20 features. Some C++17 features are used, which are available in all recent compilers. - -## When I start my bot i get an error: "error while loading shared libraries: libdpp.so: cannot open shared object file: No such file or directory" -To fix this issue, run `ldconfig`: `sudo ldconfig` as root. Log out if your SSH session and log back in, and the bot should be able to find the library. - -## When compiling with voice support, i get an error: "No rule to make target 'sodium_LIBRARY_DEBUG-NOTFOUND', needed by 'libdpp.so'. Stop." -The libsodium package requires pkg-config, but does not check for it when installed. Install it as root, e.g. `sudo apt install pkg-config`. Rerun cmake, and rebuild the library. - -## When I try to instantiate a dpp::cluster in windows, a std::bad_alloc exception is thrown -If this happens, ensure you are using the correct precompiled build of the library. Our precompiled binaries are built in two forms, **release mode** and **debug mode** for Visual Studio 2019/2022. These two versions of the library are not cross-compatible due to differences in the debug and release libstdc++. You should not need to build your own copy, but please see the section about \ref buildwindows for more information on how to build your own copy, if needed. - -## Does this library build/run on Raspberry Pi? -Yes! This project will build and run on Raspberry Pi and is very much suited to this kind of system. It may take some time (read: hours) to compile the project on your Raspberry Pi unless you build it using a cross compiler. We offer pre-built `.deb` files for arm6, arm7 and arm64, you should use these where possible to avoid having to compile it by hand, or you can use a cross-compiler to build it on your PC then transfer the compiled binaries across. - -## There are so many versions! Which deb file do i need for my Raspberry Pi? -Depending on which Raspberry Pi version you have, you will need to download a different release binary: - - - - - - - - - - -
Raspberry Pi ModelDeb file to installArch
Raspberry Pi Zero/Zero W`libdpp-x.x.x-linux-rpi-arm6.deb`ARMv6
Raspberry Pi 3`libdpp-x.x.x-linux-rpi-arm7hf.deb`ARMv7HF
Raspberry Pi 4`libdpp-x.x.x-linux-rpi-arm7hf.deb`ARMv7HF
Raspberry Pi 4 with 64 Bit Linux`libdpp-x.x.x-linux-rpi-arm64.deb`ARM64
- -## Are other ARM devices supported? -Yes! We have confirmed that the D++ deb file will successfully install and operate on various forms of cellphone or non-pi ARM devices. If you get it working on any other devices please let us know and we can build a compatibility chart. - -## Can I run a D++ bot in repl.it? -Yes! You can indeed run your bot in a repl.it container. [You can find a ready to go demo repl here](https://replit.com/@braindigitalis/dpp-demo-bot). We also have a [guide showing how to do this](https://dpp.dev/building-a-cpp-discord-bot-in-repl.html). - -## Why do the "get" functions like "messages_get" return void rather than what I'm after? -All the functions that obtain data directly from Discord (as opposed to the cache) perform HTTPS requests and may have to wait, either for the request itself or for their turn in a queue to respect rate limits. As such, it does not make sense that they should return a value, as this would mean they block execution of your event. Instead, each has a lambda, a function handler which receives the result of the request, which you can then read from within that function to get the data you are interested in. Note that this result will arrive on a different thread to the one which made the request. If you instead want the function to return a value, use the methods ending with `_sync` that will block until they have a response. Note that these forms of call will throw an exception on failure. - -## Can i use a user token with this library (as opposed to a bot token)? -No. This feature is not supported as it is against the Discord Terms Of Service, and therefore we have no plans to ever support it. You should not automate any user token. Some libraries used to support this but it is a legacy feature of those libraries (where still available) dating back to before Discord offered an official public API. Please be aware that if Discord ever catch you automating a user token (or making a user client that uses a bot token) they can and do ban people for this. diff --git a/vendor/DPP/docpages/01_installing.md b/vendor/DPP/docpages/01_installing.md deleted file mode 100644 index 0687858c..00000000 --- a/vendor/DPP/docpages/01_installing.md +++ /dev/null @@ -1,11 +0,0 @@ -# Installing D++ - -There are many ways to install D++, either from a package manager, or from source. Please choose your desired option from the list below: - -* \subpage install-linux-deb -* \subpage install-linux-rpm -* \subpage install-vcpkg -* \subpage install-arch-aur -* \subpage install-windows-zip -* \subpage install-xmake -* \subpage install-from-source diff --git a/vendor/DPP/docpages/02_building_a_bot.md b/vendor/DPP/docpages/02_building_a_bot.md deleted file mode 100644 index 5cd1c626..00000000 --- a/vendor/DPP/docpages/02_building_a_bot.md +++ /dev/null @@ -1,14 +0,0 @@ -# Creating a Discord Bot - -If you are wanting to build a bot using C++, you're in the right place! The fast and easy tutorials below will guide you through how to build a bot using the D++ library on either a UNIX-like (e.g. Linux) system with CMake or with Windows using Visual Studio 2019. - -Click on a link below for a guide specifically for your system: - -* \subpage creating-a-bot-application "Creating a Bot Token" -* \subpage build-a-discord-bot-windows-visual-studio "Building a discord bot in Windows using Visual Studio" -* \subpage build-a-discord-bot-windows-wsl "Building a discord bot in Windows using WSL (Windows Subsystem for Linux)" -* \subpage build-a-discord-bot-linux-clion "Building a discord bot in Linux using CLion" -* \subpage buildcmake "Building a Discord Bot using CMake/UNIX" -* \subpage buildmeson "Building a Discord Bot using Meson" -* \subpage building-a-cpp-discord-bot-in-repl "Creating a Discord bot in Repl.it" - diff --git a/vendor/DPP/docpages/03_example_programs.md b/vendor/DPP/docpages/03_example_programs.md deleted file mode 100644 index f2a1a72e..00000000 --- a/vendor/DPP/docpages/03_example_programs.md +++ /dev/null @@ -1,10 +0,0 @@ -# Example Programs - -There are example programs here for all skill levels demonstrating a great many features of the bot. New examples are added frequently, please check regularly for new content. - -* \subpage the-basics -* \subpage interactions-and-components -* \subpage music-and-audio -* \subpage misc - -Is the example you are looking for missing from these sections? Pop over to our [discord server](https://discord.com/dpp) and let us know what you need... Or, even better, if you know how to do something and want to contribute an example of your own, just submit a PR! \ No newline at end of file diff --git a/vendor/DPP/docpages/04_advanced_reference.md b/vendor/DPP/docpages/04_advanced_reference.md deleted file mode 100644 index 1ad0dea4..00000000 --- a/vendor/DPP/docpages/04_advanced_reference.md +++ /dev/null @@ -1,7 +0,0 @@ -# Advanced Reference - -* \subpage clusters-shards-guilds "Clusters, Shards and Guilds" -* \subpage thread-model "Thread Model" -* \subpage coding-standards "Coding Style Standards" -* \subpage unit-tests "Unit Tests" -* \subpage lambdas-and-locals "Ownership of local variables and safely transferring into a lambda" diff --git a/vendor/DPP/docpages/DPP-markdown-logo.png b/vendor/DPP/docpages/DPP-markdown-logo.png deleted file mode 100644 index 156b0de7..00000000 Binary files a/vendor/DPP/docpages/DPP-markdown-logo.png and /dev/null differ diff --git a/vendor/DPP/docpages/INDEX.md b/vendor/DPP/docpages/INDEX.md deleted file mode 100644 index 54923355..00000000 --- a/vendor/DPP/docpages/INDEX.md +++ /dev/null @@ -1,77 +0,0 @@ -# D++: A C++ Discord API Library for Bots - -## What is D++ (DPP)? - -D++ is a lightweight and simple library for Discord written in modern C++. It is designed to cover as much of the API specification as possible and to have a incredibly small memory footprint, even when caching large amounts of data. - -It is created by the developer of [TriviaBot](https://triviabot.co.uk) and contributed to by a dedicated team of developers. - -*This project is in stable development and accepting PRs and feature requests — Don't be a stranger! If you want to contribute, just get in touch via [GitHub](https://github.com/brainboxdotcc/DPP) or our official [Discord server](https://discord.gg/dpp)!* - -
- -## Downloads - -The following downloads are for the most recent version: - -* [Source Code](https://github.com/brainboxdotcc/DPP) -* [x64 Linux .deb (64 bit Debian, Ubuntu etc)](https://dl.dpp.dev/latest) -* [x86 Linux .deb (32 bit Debian, Ubuntu etc)](https://dl.dpp.dev/latest/linux-i386) -* [x64 Linux .rpm (64 bit Redhat, CentOS etc)](https://dl.dpp.dev/latest/linux-x64/rpm) -* [x86 Linux .rpm (32 bit Redhat, CentOS etc)](https://dl.dpp.dev/latest/linux-i386/rpm) -* [x64 Windows (64 bit vs2019 release build)](https://dl.dpp.dev/latest/win64-release-vs2019) -* [x64 Windows (64 bit vs2022 release build)](https://dl.dpp.dev/latest/win64-release-vs2022) -* [x64 Windows (64 bit vs2019 debug build)](https://dl.dpp.dev/latest/win64-debug-vs2019) -* [x64 Windows (64 bit vs2022 debug build)](https://dl.dpp.dev/latest/win64-debug-vs2022) -* [ARM6 Linux .deb (32 bit Raspberry Pi 1, 2)](https://dl.dpp.dev/latest/linux-rpi-arm6) -* [ARM7 Linux .deb (32 bit Raspberry Pi 3, 4)](https://dl.dpp.dev/latest/linux-rpi-arm7hf) -* [ARM64 Linux .deb (64 bit Raspberry Pi 4, Smartphones)](https://dl.dpp.dev/latest/linux-rpi-arm64) - -You can find further releases in other architectures and formats or the source code on the [GitHub Repository](https://github.com/brainboxdotcc/DPP/releases). For a realtime JSON format list of all download links, click [here](https://dl.dpp.dev/json) - -## Library features - -* Support for Discord API v10 -* Really small memory footprint -* Efficient caching system for guilds, channels, guild members, roles, users -* Sharding and clustering (Many shards, one process: specify the number of shards, or let the library decide) -* Highly optimised ETF (Erlang Term Format) support for very fast websocket throughput (*no other C++ Discord library has this!*) -* [Slash Commands/Interactions support](https://dpp.dev/slashcommands.html) -* [Voice support](https://dpp.dev/soundboard.html) (sending **and** receiving audio) -* The entire Discord API is available for use in the library -* Stable [Windows support](https://dpp.dev/buildwindows.html) -* Ready-made compiled packages for Windows, Raspberry Pi (ARM64/ARM7/ARMv6), Debian x86/x64 and RPM based distributions -* Highly scalable for large amounts of guilds and users - -## Supported Operating Systems - -### Linux -The library runs ideally on **Linux**. - -### Mac OS X and FreeBSD -The library is well-functional and stable on **Mac OS X** and **FreeBSD** too. - -### Raspberry Pi -For running your bot on a **Raspberry Pi**, we offer a prebuilt .deb package for ARM64, ARM6, and ARM7 so that you do not have to wait for it to compile. - -### Windows -**Windows** is well-supported with ready-made compiled DLL and LIB files, please check out our [Windows Bot Template repository](https://github.com/brainboxdotcc/windows-bot-template). The Windows Bot repository can be cloned and integrated immediately into any Visual Studio 2019 and 2022 project in a matter of minutes. - -### Other OS -The library should work fine on other operating systems as well, and if you run a D++ bot on something not listed here, please let us know! - -## Getting started -* [GitHub Repository](https://github.com/brainboxdotcc/DPP) -* [Discord Server](https://discord.gg/dpp) -* [Frequently Asked Questions](/md_docpages_01_frequently_asked_questions.html) -* [Installing D++](/md_docpages_01_installing.html) -* [Example Programs](/md_docpages_03_example_programs.html) - -## Architecture -* \ref clusters-shards-guilds -* \ref thread-model - -## Learning Resources -* [C++ for JavaScript Developers](https://pawelgrzybek.com/cpp-for-javascript-developers/) -* [C++ In Four Hours](https://www.youtube.com/watch?v=vLnPwxZdW4Y&vl=en) - diff --git a/vendor/DPP/docpages/advanced_reference/clusters_shards_and_guilds.md b/vendor/DPP/docpages/advanced_reference/clusters_shards_and_guilds.md deleted file mode 100644 index fd9a2419..00000000 --- a/vendor/DPP/docpages/advanced_reference/clusters_shards_and_guilds.md +++ /dev/null @@ -1,178 +0,0 @@ -\page clusters-shards-guilds Clusters, Shards and Guilds - -D++ takes a three-tiered highly scalable approach to bots, with three levels known as Clusters, Shards and Guilds as documented below. - -\dot -digraph "Clusters, Shards and Guilds" { - node [colorscheme="blues9",fontname="helvetica"]; - subgraph Bot { - node [style=filled, color=1]; - label = "Bot" - "Bot" [shape=folder, label="Bot", bordercolor=black]; - }; - subgraph Processes { - node [style=filled, color=2]; - label = "Processes" - "Bot" -> "Process 1" - "Bot" -> "Process 2" - "Process 1" [shape=record, label="Process"]; - "Process 2" [shape=record, label="Process"]; - }; - subgraph Clusters { - node [style=filled, color=3]; - label = "Clusters" - "Process 1" -> "Cluster 1" - "Process 2" -> "Cluster 2" - "Cluster 1" [shape=record, label="Cluster"]; - "Cluster 2" [shape=record, label="Cluster"]; - }; - subgraph Shards { - node [style=filled, color=4]; - label = "Shards" - "Shard 1" [shape=record, label="Shard"]; - "Shard 2" [shape=record, label="Shard"]; - "Shard 3" [shape=record, label="Shard"]; - "Shard 4" [shape=record, label="Shard"]; - "Shard 5" [shape=record, label="Shard"]; - "Shard 6" [shape=record, label="Shard"]; - "Shard 7" [shape=record, label="Shard"]; - "Shard 8" [shape=record, label="Shard"]; - "Cluster 1" -> "Shard 1" - "Cluster 1" -> "Shard 3" - "Cluster 2" -> "Shard 2" - "Cluster 2" -> "Shard 4" - "Cluster 1" -> "Shard 5" - "Cluster 1" -> "Shard 7" - "Cluster 2" -> "Shard 6" - "Cluster 2" -> "Shard 8" - }; - subgraph Guilds { - node [style=filled, color=5]; - label = "Guilds"; - "Guild 1" [shape=record, label="Guild"]; - "Guild 2" [shape=record, label="Guild"]; - "Guild 3" [shape=record, label="Guild"]; - "Guild 4" [shape=record, label="Guild"]; - "Guild 5" [shape=record, label="Guild"]; - "Guild 6" [shape=record, label="Guild"]; - "Guild 7" [shape=record, label="Guild"]; - "Guild 8" [shape=record, label="Guild"]; - "Guild 9" [shape=record, label="Guild"]; - "Guild 10" [shape=record, label="Guild"]; - "Guild 11" [shape=record, label="Guild"]; - "Guild 12" [shape=record, label="Guild"]; - "Guild 13" [shape=record, label="Guild"]; - "Guild 14" [shape=record, label="Guild"]; - "Guild 15" [shape=record, label="Guild"]; - "Guild 16" [shape=record, label="Guild"]; - "Shard 1" -> "Guild 1" - "Shard 1" -> "Guild 5" - "Shard 2" -> "Guild 2" - "Shard 2" -> "Guild 6" - "Shard 3" -> "Guild 3" - "Shard 3" -> "Guild 7" - "Shard 4" -> "Guild 4" - "Shard 4" -> "Guild 8" - "Shard 5" -> "Guild 9" - "Shard 5" -> "Guild 11" - "Shard 6" -> "Guild 10" - "Shard 6" -> "Guild 12" - "Shard 7" -> "Guild 13" - "Shard 7" -> "Guild 15" - "Shard 8" -> "Guild 14" - "Shard 8" -> "Guild 16" - }; -} -\enddot - -## Clusters - -A bot may be made of one or more clusters. Each cluster maintains a queue of commands waiting to be sent to Discord, a queue of replies from Discord for all commands executed, and zero or more **shards**. Usually, each process has one cluster, but the D++ library does not enforce this as a restriction. Small bots will require just one cluster. Clusters will split the required number of shards equally across themselves. There is no communication between clusters unless you add some yourself, they all remain independent without any central "controller" process. This ensures that there is no single point of failure in the design. Whenever you instantiate the library, you generally instantiate a cluster: - -```cpp -#include - -int main() -{ - /* This is a cluster */ - dpp::cluster bot("Token goes here"); -} -``` - -## Shards - -A cluster contains zero or more shards. Each shard maintains a persistent websocket connection to Discord via a websocket, which receives all events the bot is made aware of, e.g. messages, channel edits, etc. Requests to the API on the other hand go out to Discord as separate HTTP requests. - -Small bots will require only one shard and this is the default when you instantiate a cluster. The library will automatically determine and create the correct number of shards needed, if you do not configure it by hand. If you do want to specify a number of shards, you can specify this when creating a cluster: - -```cpp -#include - -int main() -{ - /* This is a cluster */ - int total_shards = 10; - dpp::cluster bot("Token goes here", dpp::i_default_intents, total_shards); -} -``` - -Remember that if there are multiple clusters, the number of shards you request will be split equally across these clusters! - -@note To spawn multiple clusters, you can specify this as the 4th and 5th parameter of the dpp::cluster constructor. You must do this, if you want this functionality. The library will not create additional clusters for you, as what you require is dependent upon your system specifications. It is your responsibility to somehow get the cluster id and total clusters into the process, e.g. via a command line argument. An example of this is shown below based on the cluster setup code of **TriviaBot**: -```cpp -#include -#include -#include -#include -#include - -int main(int argc, char** argv) -{ - int total_shards = 64; - int index; - char arg; - bool clusters_defined = false; - uint32_t clusterid = 0; - uint32_t maxclusters = 1; - - /* Parse command line parameters using getopt() */ - struct option longopts[] = - { - { "clusterid", required_argument, NULL, 'c' }, - { "maxclusters", required_argument, NULL, 'm' }, - { 0, 0, 0, 0 } - }; - opterr = 0; - while ((arg = getopt_long_only(argc, argv, "", longopts, &index)) != -1) { - switch (arg) { - case 'c': - clusterid = std::stoul(optarg); - clusters_defined = true; - break; - case 'm': - maxclusters = std::stoul(optarg); - break; - default: - std::cerr << "Unknown parameter '" << argv[optind - 1] << "'\n"; - exit(1); - break; - } - } - - if (clusters_defined && maxclusters == 0) { - std::cerr << "ERROR: You have defined a cluster id with -clusterid but no cluster count with -maxclusters.\n"; - exit(2); - } - - dpp::cluster bot("Token goes here", dpp::default_intents, total_shards, clusterid, maxclusters); -} -``` - -### Large Bot Sharding - -Discord restricts how many shards you can connect to at any one time to one per five seconds, unless your bot is in at least 150,000 guilds. Once you reach 150,000 guilds, Discord allow your bot to connect to more guilds concurrently, and your number of shards must divide cleanly into this value. By default, at 150,000 guilds this concurrency value is 16 meaning D++ will attempt to connect 16 shards in parallel, then wait for all these to connect and then connect another 16, until all shards are connected. In practice, this means a large bot with many shards (read: hundreds!) will connect significantly faster after a full restart. **You do not need to manually configure large bot sharding and connection concurrency, the D++ library will handle this for you if you are able to use it**. - - -## Guilds - -Guilds are what servers are known as to the Discord API. There can be up to **2500** of these per shard. Once you reach 2500 guilds on your bot, Discord force your bot to shard, the D++ library will automatically create additional shards to accomodate if not explicitly configured with a larger number. Discord *does not restrict sharding* to bots on 2500 guilds or above. You can shard at any size of bot, although it would be a waste of resources to do so unless it is required. diff --git a/vendor/DPP/docpages/advanced_reference/coding_style_standards.md b/vendor/DPP/docpages/advanced_reference/coding_style_standards.md deleted file mode 100644 index c4fc4529..00000000 --- a/vendor/DPP/docpages/advanced_reference/coding_style_standards.md +++ /dev/null @@ -1,116 +0,0 @@ -\page coding-standards Coding Style Standards - -This page lists the coding style we stick to when maintaining the D++ library. If you are submitting a pull request or other code contribution to the library, you should stick to the styles listed below. If something is not covered here, ask on the [official discord server](https://discord.gg/dpp)! - -## Class names, function names and method names -All class, variable/member, function and method names should use `snake_case`, similar to the style of the C++ standard library. - -## Enums -Enums and their values should be `snake_case` as with class, function and method names. You do not need to use `enum class`, so make sure that enum values are prefixed with a prefix to make them unique and grouped within the IDE, e.g. `ll_debug`, `ll_trace` etc. - - -## Curly Braces, Brackets etc - -Open curly braces on the same line as the keyword, for example: - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} -if (a == b) { - c(); -} -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Use a space after the comma in parameter lists, and after opening brackets and before closing brackets except when calling a function, e.g.: - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} -std::vector clowns = { "pennywise", "bobo" }; - -evaluate_clown(clowns[0], evilness(2.5, factor)); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -## Indentation -Indentation should always be tab characters. It is up to you how wide you set tab characters in your editor for your personal tastes. All code blocks delimited within curly braces should be indented neatly and uniformly. - -## Constants and \#define macros -Constants and macros should be all `UPPERCASE` with `SNAKE_CASE` to separate words. Macros should not have any unexpected side effects. - -## Comments -All comments should be in `doxygen` format (similar to javadoc). Please see existing class definitions for an example. You should use doxygen style comments in a class definition inside a header file, and can use any other comment types within the .cpp file. Be liberal with comments, especially if your code makes any assumptions! - -## Symbol exporting -If you export a class which is to be accessible to users, be sure to prefix it with the `DPP_EXPORT` macro, for example: - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} -class DPP_EXPORT my_new_class { -public: - int hats; - int clowns; -}; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The `DPP_EXPORT` macro ensures that on certain platforms (notably Windows) the symbol is exported to be available to the library user. - -## Public vs private vs protected -It is a design philosophy of D++ that everything possible in a class should be public, unless the user really does not need it (you should consider justifying in comments why) or user adjustment of the variable could badly break the functioning of the library. Avoid the use of accessors for setting/getting values in a class, except for bit fields, where you should provide accessors for setting and getting individual bits (for example, see `user.h`), or in the event you want to provide a "fluent" interface. The exception to this is where you want to provide a logic validation of a field, for example if you have a string field with a minimum and maximum length, you can provide a setter the user can *optionally use* which will validate their input. - -## Exceptions -All exceptions thrown should derive from dpp::exception (see dpp/exception.h) - when validating string lengths, a string which is too long should be truncated using dpp::utility::utf8substr and any strings that are too short should throw a dpp::length_exception. - -## Inheritance -Keep levels of inheritance low. If you need to inherit more than 3 levels deep, it is probable that the design could be simplified. Remember that at scale, there can be tens of millions of certain classes and each level of virtual nesting adds to the `vtable` of that object's instance in RAM. - -## Bit field packing -Where discord provides boolean flags, if the user is expected to store many of the object in RAM, or in cache, you should pack all these booleans into bit fields (see `user.h` and `channel.h` for examples). In the event that the object is transient, such as an interaction or a message, packing the data into bit fields is counter intuitive. Remember that you should provide specific accessors for bit field values! - -## Keep dependencies internal! -Where you are making use of an external dependency such as `opus` or `libssl`, do not place references to the types/structs, or the header files of these external libraries within the header files of D++. Doing so adds that library as a public dependency to the project (which is bad!). Instead make an opaque class, and/or forward-declare the structs (for examples see `sslclient.h` and `discordvoiceclient.h`). - -## API type names -Where discord provide a name in PascalCase we should stick as closely to that name as possible but convert it to `snake_case`. For example, GuildMember would become `guild_member`. - -## Don't introduce any platform-specific code -Do not introduce platform specific (e.g. windows only) code or libc functions. If you really must use these functions safely wrap them e.g. in `#ifdef _WIN32` and provide a cross-platform alternative so that it works for everyone. - -## Select the right size type for numeric types -If a value will only hold values up to 255, use `uint8_t`. If a value cannot hold over 65536, use `uint16_t`. These types can help use a lot less ram at scale. - -## Fluent design -Where possible, if you are adding methods to a class you should consider fluent design. Fluent design is the use of class methods tha return a reference to self (via `return *this`), so that you can chain object method calls together (in the way `dpp::message` and `dpp::embed` do). For example: - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} -class DPP_EXPORT my_new_class { -public: - int hats; - int clowns; - - my_new_class& set_hats(int new_hats); - my_new_class& set_clowns(int new_clowns); -}; - -my_new_class& my_new_class::set_hats(int new_hats) { - hats = new_hats; - return *this; -} - -my_new_class& my_new_class::set_clowns(int new_clowns) { - clowns = new_clowns; - return *this; -} - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This would allow the user to do this: - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} -dpp::my_new_class nc; -nc.set_hats(3).set_clowns(9001); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -## Keep all D++ related types in the dpp namespace - -All types for the library should be within the `dpp` namespace. There are a couple of additional namespaces, e.g. `dpp::utility` for static standalone helper functions and helper classes, and `dpp::events` for internal websocket event handlers. - -## Commit messages and Git - -All pull requests ("PRs") should be submitted against the `dev` branch in GitHub. It’s good to have descriptive commit messages, or PR titles so that other contributors can understand about your commit or the PR Created. Read [conventional commits](https://www.conventionalcommits.org/en/v1.0.0-beta.3/) for information on how we like to format commit messages. - -All PRs must pass the [GitHub Actions](https://github.com/brainboxdotcc/DPP/actions) tests before being allowed to be merged. This is to ensure that no code committed into the project fails to compile on any of our officially supported platforms or architectures. diff --git a/vendor/DPP/docpages/advanced_reference/lambdas_and_locals.md b/vendor/DPP/docpages/advanced_reference/lambdas_and_locals.md deleted file mode 100644 index 0179b190..00000000 --- a/vendor/DPP/docpages/advanced_reference/lambdas_and_locals.md +++ /dev/null @@ -1,56 +0,0 @@ -\page lambdas-and-locals Ownership of local variables and safely transferring into a lambda - -If you are reading this page, you have likely been sent here by someone helping you diagnose why your bot is crashing or why seemingly invalid values are being passed into lambdas within your program that uses D++. - -It is important to remember that when you put a lambda callback onto a function in D++, that this lambda will execute at some point in the **future**. As with all things in the future and as 80s Sci Fi movies will tell you, when you reach the future things may well have changed! - -\image html delorean-time-travel.gif - -To explain this situation and how it causes issues i'd like you to imagine the age old magic trick, where a magician sets a fine table full of cutlery, pots, pans and wine. He indicates to the audience that this is authentic, then with a whip of his wrist, he whips the tablecloth away, leaving the cutlery and other tableware in place (if he is any good as a magician!) - -Now imagine the following code scenario. We will describe this code scenario as the magic trick above, in the steps below: - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} -bot.on_message_create([&bot](const dpp::message_create_t & event) { - int myvar = 0; - bot.message_create(dpp::message(event.msg.channel_id, "foobar"), [&](const auto & cc) { - myvar = 42; - }); -}); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In this scenario, the outer event, `on_message_create` is your tablecloth. The lambda inside the `bot.message_create` is the tableware and cutlery. The following chain of events happens in this code: - -* The magician executes his magic trick (D++ the `bot.on_message_create entering` the outer lambda) -* Your code executes `bot.message_create()` inside this outer lambda -* D++ inserts your request to send a message into its queue, in another thread. The inner lambda, where you might later set `myvar = 42` is safely copied into the queue for later calling. -* The tablecloth is whipped away... in other words, `bot.on_message_create` ends, and all local variables including `myvar` become invalid -* At a later time (usually 80ms through to anything up to 4 seconds depending on rate limits!) the message is sent, and your inner lambda which was saved at the earlier step is called. -* Your inner lambda attempts to set `myvar` to 42... but `myvar` no longer exists, as the outer lambda has been destroyed.... -* The table wobbles... the cutlery shakes... and... -* Best case scenario: you access invalid RAM no longer owned by your program by trying to write to `myvar`, and [your bot outright crashes horribly](https://www.youtube.com/watch?v=sm8qb2kP-fQ)! -* Worse case scenario: you silently corrupt ram and end up spending days trying to track down a bug that subtly breaks your bot... - -The situation i am trying to describe here is one of object and variable ownership. When you call a lambda, **always assume that every non global reference outside of that lambda will be invalid when the lambda is called**! For any non-global variable always take a **copy** of the variable (not reference, or pointer). Global variables or those declared directly in `main()` are safe to pass as references. - -For example, if we were to fix the broken code above, we could rewrite it like this: - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} -bot.on_message_create([&bot](const dpp::message_create_t & event) { - int myvar = 0; - bot.message_create(dpp::message(event.msg.channel_id, "foobar"), [myvar](const auto & cc) { - myvar = 42; - }); - std::cout << "here\n"; -}); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Note however that when you set myvar within the inner lambda, this does **not effect** the value of the var outside it. Lambdas should be considered self-contained silos, and as they execute in other threads should not be relied upon to set anything that exists **outside of that lambda**. - -\warning Always avoid just using `[&]` in a lambda to access all in the scope above. It is unlikely that half of this scope will still even be valid by the time you get a look at it! - -Similarly, and important to note, your program **will not wait for bot.message_create to send its message and call its lambda** before continuing on to print `here`. It will instantly insert the request into its queue and bail straight back out (see the steps above) and immediately print the text. - -If you do want to get variables out of your lambda, create a class, or call a separate function, and pass what you need into that function from the lambda **by value** or alternatively, you can use `std::bind` to bind a lambda directly to an object's method instead (this is great for modular bots). - -If you are stuck, as this is a complex subject please do feel free to ask on the [official support server](https://discord.gg/dpp)! diff --git a/vendor/DPP/docpages/advanced_reference/thread_model.md b/vendor/DPP/docpages/advanced_reference/thread_model.md deleted file mode 100644 index 274fdde0..00000000 --- a/vendor/DPP/docpages/advanced_reference/thread_model.md +++ /dev/null @@ -1,81 +0,0 @@ -\page thread-model Thread Model - -\dot -digraph "Thread Model" { - graph [ranksep=1]; - node [colorscheme="blues9",fontname="helvetica"]; - "Discord Events" -> "Your Program" - - "Your Program" [style=filled, color=1, shape=rect] - "Cluster" [style=filled, color=1, shape=rect] - - subgraph cluster_4 { - style=filled; - color=lightgrey; - node [style=filled,color=2] - "Your Program" - "Cluster" - label = "User Code"; - } - - subgraph cluster_0 { - style=filled; - color=lightgrey; - node [style=filled,color=4] - "Shard 1" [style=filled, color=4] - "Shard 2" - "Shard 3..." - label = "Shards (Each is a thread, one per 2500 discord guilds)"; - } - - subgraph cluster_1 { - style=filled - color=lightgrey; - node [style=filled,color=4] - "REST Requests" - "Request In Queue 1" - "Request In Queue 2" - "Request In Queue 3..." - "Request Out Queue" - label = "REST Requests (Each in queue, and the out queue, are threads)" - } - - subgraph cluster_3 { - style=filled - color=lightgrey; - node [style=filled,color=4] - "Discord Events" [style=filled,color=4] - "User Callback Functions" - label = "Events and Callbacks" - } - - "Cluster" [shape=rect] - "REST Requests" [shape=rect] - "Request In Queue 1" [shape=rect] - "Request In Queue 2" [shape=rect] - "Request In Queue 3..." [shape=rect] - "Shard 1" [shape=rect] - "Shard 2" [shape=rect] - "Shard 3..." [shape=rect] - "Request Out Queue" [shape=rect] - "Discord Events" [shape=rect] - "User Callback Functions" [shape=rect] - - "Cluster" -> "REST Requests" - "Shard 1" -> "Discord Events" - "Shard 2" -> "Discord Events" - "Shard 3..." -> "Discord Events" - "Your Program" -> "Cluster" - "Cluster" -> "Shard 1" - "Cluster" -> "Shard 2" - "Cluster" -> "Shard 3..." - "REST Requests" -> "Request In Queue 1" - "REST Requests" -> "Request In Queue 2" - "REST Requests" -> "Request In Queue 3..." - "Request In Queue 1" -> "Request Out Queue" - "Request In Queue 2" -> "Request Out Queue" - "Request In Queue 3..." -> "Request Out Queue" - "Request Out Queue" -> "User Callback Functions" - "User Callback Functions" -> "Your Program" -} -\enddot diff --git a/vendor/DPP/docpages/advanced_reference/unit_tests.md b/vendor/DPP/docpages/advanced_reference/unit_tests.md deleted file mode 100644 index bdd2a0b6..00000000 --- a/vendor/DPP/docpages/advanced_reference/unit_tests.md +++ /dev/null @@ -1,28 +0,0 @@ -\page unit-tests Unit Tests - -## Running Unit Tests - -If you are adding functionality to DPP, make sure to run unit tests. This makes sure that the changes do not break anything. All pull requests must pass all unit tests before merging. - -Before running test cases, create a test server for your test bot. You should: - -* Make sure that the server only has you and your test bot, and no one else -* Give your bot the administrator permission -* Enable community for the server -* Make an event -* Create at least one voice channel -* Create at least one text channel - -Then, set the following variables to the appropriate values. (Below is a fake token, don't bother trying to use it) - - export DPP_UNIT_TEST_TOKEN="ODI2ZSQ4CFYyMzgxUzkzzACy.HPL5PA.9qKR4uh8po63-pjYVrPAvQQO4ln" - export TEST_GUILD_ID="907951970017480704" - export TEST_TEXT_CHANNEL_ID="907951970017480707" - export TEST_VC_ID="907951970017480708" - export TEST_USER_ID="826535422381391913" - export TEST_EVENT_ID="909928577951203360" - -Then, after cloning and building DPP, run `cd build && ctest -VV` for unit test cases. - -If you do not specify the `DPP_UNIT_TEST_TOKEN` environment variable, a subset of the tests will run which do not require discord connectivity. - diff --git a/vendor/DPP/docpages/building/02_build.md b/vendor/DPP/docpages/building/02_build.md deleted file mode 100644 index 5ff74290..00000000 --- a/vendor/DPP/docpages/building/02_build.md +++ /dev/null @@ -1,10 +0,0 @@ -\page install-from-source Building D++ From Source - -The way you build D++ varies from system to system. Please follow the guide below for your OS: - -* \subpage buildlinux "Building on Linux" -* \subpage buildwindows "Building on Windows" -* \subpage buildosx "Building on OSX" -* \subpage buildfreebsd "Building on FreeBSD" - -@warning Note that you most likely don't need to build D++ from source if you're on Linux or Windows. We offer prebuilt binaries for these platforms and are listed in package managers! Check the downloads in the releases section on github. diff --git a/vendor/DPP/docpages/building/freebsd.md b/vendor/DPP/docpages/building/freebsd.md deleted file mode 100644 index 0d64b263..00000000 --- a/vendor/DPP/docpages/building/freebsd.md +++ /dev/null @@ -1,49 +0,0 @@ -\page buildfreebsd Building on FreeBSD - -## 1. Toolchain -This project uses CMake. Install it with `pkg install cmake` - -## 2. Install External Dependencies -Your FreeBSD base system should have all the required dependencies installed by default. - -For voice support, additional dependencies are required - - pkg install libsodium opus pkgconf - -## 3. Build Source Code - - cmake -B ./build - cmake --build ./build -j8 - -Replace the number after -j with a number suitable for your setup, usually the same as the number of cores on your machine. `cmake` will fetch any dependencies that are required for you and ensure they are compiled alongside the library. - -## 4. Install globally - - cd build; make install - -## 5. Installation to a different directory - -If you want to install the library, its dependencies and header files to a different directory, specify this directory when running `cmake`: - - cmake .. -DCMAKE_INSTALL_PREFIX=/path/to/install - -Then once the build is complete, run `make install` to install to the location you specified. - -## 7. Using the library - -Once installed, you can make use of the library in standalone programs simply by including it and linking to it: - - clang++ -std=c++17 -ldpp mydppbot.cpp -o dppbot - -The important flags in this command-line are: - - * `-std=c++17` - Required to compile the headers - * `-ldpp` - Link to libdpp.dylib - * `mydppbot.cpp` - Your source code - * `dppbot` - The name of the executable to make - -Of course, this is just a proof of concept - you should really use a more robust build system like [`cmake`](@ref buildcmake). - -If you are having trouble setting up CMake, you can try [our template bot](https://github.com/brainboxdotcc/templatebot). - -**Have fun!** diff --git a/vendor/DPP/docpages/building/linux.md b/vendor/DPP/docpages/building/linux.md deleted file mode 100644 index ad0d8277..00000000 --- a/vendor/DPP/docpages/building/linux.md +++ /dev/null @@ -1,42 +0,0 @@ -\page buildlinux Building on Linux - -\note You might not need to build a copy of the library for Linux - precompiled deb files for 64 bit and 32 bit Debian and Ubuntu are provided in the GitHub version releases. Unless you are on a different Linux distribution which does not support the installation of deb files, or wish to submit fixes and enhancements to the library itself you should have an easier time installing the precompiled version instead. - -## 1. Build Source Code - - cmake -B ./build - cmake --build ./build -j8 - -Replace the number after -j with a number suitable for your setup, usually the same as the number of cores on your machine. `cmake` will fetch any dependencies that are required for you and ensure they are compiled alongside the library. - -## 2. Install to /usr/local/include and /usr/local/lib - - cd build; sudo make install - -## 3. Installation to a different directory - -If you want to install the library, its dependencies and header files to a different directory, specify this directory when running `cmake`: - - cmake .. -DCMAKE_INSTALL_PREFIX=/path/to/install - -Then once the build is complete, run `make install` to install to the location you specified. - -## 4. Using the library - -Once installed to the /usr/local directory, you can make use of the library in standalone programs simply by including it and linking to it: - - g++ -std=c++17 mydppbot.cpp -o dppbot -ldpp - -The important flags in this command-line are: - - * `-std=c++17` - Required to compile the headers - * `mydppbot.cpp` - Your source code - * `dppbot` - The name of the executable to make - -Of course, this is just a proof of concept — you should really use a more robust build system like GNU `make` or [`cmake`](@ref buildcmake). - -If you are having trouble setting up CMake, you can try [our template bot](https://github.com/brainboxdotcc/templatebot). - -**Have fun!** - - diff --git a/vendor/DPP/docpages/building/osx.md b/vendor/DPP/docpages/building/osx.md deleted file mode 100644 index a835ca0a..00000000 --- a/vendor/DPP/docpages/building/osx.md +++ /dev/null @@ -1,55 +0,0 @@ -\page buildosx Building on OSX - -## 1. Toolchain -Before compiling make sure you have all the tools installed. - -1. To install the dependencies, this guide will use homebrew which has [installation instructions on their project page](https://brew.sh/). - -2. This project uses CMake to generate the makefiles. Install it with `brew install cmake`. - -## 2. Install External Dependencies - - brew install openssl - -For voice support, additional dependencies are required: - - brew install libsodium opus - -## 3. Build Source Code - - cmake -B ./build - cmake --build ./build -j8 - -Replace the number after -j with a number suitable for your setup, usually the same as the number of cores on your machine. `cmake` will fetch any dependencies that are required for you and ensure they are compiled alongside the library. - -## 4. Install globally - - cd build; sudo make install - -## 5. Installation to a different directory - -If you want to install the library, its dependencies and header files to a different directory, specify this directory when running `cmake`: - - cmake .. -DCMAKE_INSTALL_PREFIX=/path/to/install - -Then once the build is complete, run `make install` to install to the location you specified. - -## 6. Using the library - -Once installed, you can make use of the library in standalone programs simply by including it and linking to it: - - clang++ -std=c++17 -ldpp mydppbot.cpp -o dppbot - -The important flags in this command-line are: - - * `-std=c++17` - Required to compile the headers - * `-ldpp` - Link to libdpp.dylib - * `mydppbot.cpp` - Your source code - * `dppbot` - The name of the executable to make - -Of course, this is just a proof of concept - you should really use a more robust build system like GNU `make` or [`cmake`](@ref buildcmake). - -If you are having trouble setting up CMake, you can try [our template bot](https://github.com/brainboxdotcc/templatebot). - -**Have fun!** - diff --git a/vendor/DPP/docpages/building/windows.md b/vendor/DPP/docpages/building/windows.md deleted file mode 100644 index f1aa6e0d..00000000 --- a/vendor/DPP/docpages/building/windows.md +++ /dev/null @@ -1,29 +0,0 @@ -\page buildwindows Building on Windows - -To build on windows follow these steps *exactly*. The build process depends on specific libraries being installed on your system in specific locations. - -## Wait a minute! Read this first! - -\warning **You do not need to follow this tutorial unless you plan to contribute to or modify the library itself**. Unless you consider yourself an **advanced user** with a specific **requirement to build from source** you should [obtain a pre-made visual studio template containing the latest D++ build (for 32 and 64 bit, release and debug profiles) by clicking here](https://github.com/brainboxdotcc/windows-bot-template/) and completely skip this guide! Instead, read \ref build-a-discord-bot-windows-visual-studio. - -## If you are absolutely sure you need this guide, read on: - -1. Make sure you have Visual Studio 2019 or Visual Studio 2022. The Community, Professional or Enterprise versions all work, however you will probably want to install Community. You do **NOT** want to use *Visual Studio Code* for this. You can [download the correct version here](https://visualstudio.microsoft.com/downloads/). -2. Check out the DPP project source using git -3. From within Visual Studio 2019, click the "File" menu, choose "Open" then "CMake", and select the CMakeLists.txt within the project folder - \image html winbuild_1.png - \image html winbuild_2.png -4. Go to the "Build" menu and choose "Build all" or just press F7 - \image html winbuild_3.png -5. Check that compilation succeeded. You may now use the library in your projects! - \image html winbuild_4.png - -## Troubleshooting - -* If you do not have an option to open the CMakeLists.txt, ensure that you have installed the C++ development portions of visual studio (not just web development portions) with at least the default options. -* If the project does not build, please ask for help on the [official discord server](https://discord.gg/dpp). - -## After compiling - -After compilation you can directly reference the compiled project in your own CMakeLists.txt as a library or use the lib/dll/headers as you wish. Note that `openssl` and `zlib` will also be an indirect dependency of your program (as `DLL` files) and should be copied alongside `dpp.dll`. - diff --git a/vendor/DPP/docpages/dl.dpp.dev/dlcount.php b/vendor/DPP/docpages/dl.dpp.dev/dlcount.php deleted file mode 100644 index e5e12ee2..00000000 --- a/vendor/DPP/docpages/dl.dpp.dev/dlcount.php +++ /dev/null @@ -1,47 +0,0 @@ - [ - "method" => "GET", - "header" => "User-Agent: DPP/Website" - ] - ] - ) - ) -); -$downloads = 0; -foreach ($json as $index => $release) { - $releaseDownloads = 0; - foreach ($release->assets as $asset) { - $releaseDownloads += $asset->download_count; - } - $downloads += $releaseDownloads; -} - -header("Content-Type: image/svg+xml"); -echo << - downloads: {$downloads} - - - - - - - - - - - - - downloads - - {$downloads} - - -IMG; diff --git a/vendor/DPP/docpages/dl.dpp.dev/index.php b/vendor/DPP/docpages/dl.dpp.dev/index.php deleted file mode 100644 index 31b38c68..00000000 --- a/vendor/DPP/docpages/dl.dpp.dev/index.php +++ /dev/null @@ -1,68 +0,0 @@ -tag_name; -} - -// Build search filename -$searchName = 'libdpp-' . preg_replace('/^v/', '', $version) . '-' . $arch . '.' . $type; - -// Iterate list of release artifacts across all releases -foreach ($json as $index => $release) { - foreach ($release->assets as $index2 => $asset) { - $url = $asset->browser_download_url; - $name = $asset->name; - $thisVersion = $release->tag_name; - // We found a matching file, stream it to the user - if (strtoupper($searchName) == strtoupper($name)) { - header('Content-Type: application/octet-stream'); - header('Content-Disposition: attachment; filename="' . $name . '"'); - readfile($url); - exit; - } - $urls[] = [ - 'name' => $name, - 'url' => $url, - 'version' => $thisVersion, - ]; - } -} - - -if ($version === 'json') { - header('Content-Type: application/json'); - echo json_encode($urls); -} else { - // Nothing found, offer up some useful info - foreach ($urls as $thisUrl) { - printf("%s - %s
", $thisUrl['version'], $thisUrl['url'], $thisUrl['name']); - } -} - diff --git a/vendor/DPP/docpages/example_programs/interactions_and_components.md b/vendor/DPP/docpages/example_programs/interactions_and_components.md deleted file mode 100644 index aed7d544..00000000 --- a/vendor/DPP/docpages/example_programs/interactions_and_components.md +++ /dev/null @@ -1,14 +0,0 @@ -\page interactions-and-components Interactions And Components - -The example programs listed here demonstrate lots of things to do with interactions, application commands (slash commands) and message components. If you're looking to make your bot **modern and user friendly** these examples are what you need. - -* \subpage slashcommands "Using Slash Commands and Interactions" -* \subpage context-menu "Context Menus" -* \subpage subcommands "Slash command sub-commands" -* \subpage components "Using button components" -* \subpage components3 "Using select menu components" -* \subpage components2 "Advanced components" -* \subpage modal-dialog-interactions "Modal Dialogs" -* \subpage commandhandler "Unified message/slash command handler" -* \subpage application-command-autocomplete "Slash command auto completion" -* \subpage discord-application-command-file-upload "Using file parameters in slash commands" diff --git a/vendor/DPP/docpages/example_programs/interactions_and_components/autocomplete.md b/vendor/DPP/docpages/example_programs/interactions_and_components/autocomplete.md deleted file mode 100644 index b5031335..00000000 --- a/vendor/DPP/docpages/example_programs/interactions_and_components/autocomplete.md +++ /dev/null @@ -1,72 +0,0 @@ -\page application-command-autocomplete Slash command auto completion - -Discord now supports sending auto completion lists for slash command choices. To use this feature you can use code such as the example below: - -~~~~~~~~~~{.cpp} -#include - -int main() -{ - dpp::cluster bot("token"); - - bot.on_log(dpp::utility::cout_logger()); - - bot.on_ready([&bot](const dpp::ready_t & event) { - if (dpp::run_once()) { - /* Create a new global command once on ready event */ - bot.global_command_create(dpp::slashcommand("blep", "Send a random adorable animal photo", bot.me.id) - .add_option( - /* If you set the auto complete setting on a command option, it will trigger the on_autocomplete - * event whenever discord needs to fill information for the choices. You cannot set any choices - * here if you set the auto complete value to true. - */ - dpp::command_option(dpp::co_string, "animal", "The type of animal").set_auto_complete(true) - ) - ); - } - }); - - /* The interaction create event is fired when someone issues your commands */ - bot.on_slashcommand([&bot](const dpp::slashcommand_t & event) { - /* Check which command they ran */ - if (event.command.get_command_name() == "blep") { - /* Fetch a parameter value from the command parameters */ - std::string animal = std::get(event.get_parameter("animal")); - /* Reply to the command. There is an overloaded version of this - * call that accepts a dpp::message so you can send embeds. - */ - event.reply("Blep! You chose " + animal); - } - }); - - /* The on_autocomplete event is fired whenever discord needs information to fill in a command options's choices. - * You must reply with a REST event within 500ms, so make it snappy! - */ - bot.on_autocomplete([&bot](const dpp::autocomplete_t & event) { - for (auto & opt : event.options) { - /* The option which has focused set to true is the one the user is typing in */ - if (opt.focused) { - /* In a real world usage of this function you should return values that loosely match - * opt.value, which contains what the user has typed so far. The opt.value is a variant - * and will contain the type identical to that of the slash command parameter. - * Here we can safely know it is string. - */ - std::string uservalue = std::get(opt.value); - bot.interaction_response_create(event.command.id, event.command.token, dpp::interaction_response(dpp::ir_autocomplete_reply) - .add_autocomplete_choice(dpp::command_option_choice("squids", "lots of squids")) - .add_autocomplete_choice(dpp::command_option_choice("cats", "a few cats")) - .add_autocomplete_choice(dpp::command_option_choice("dogs", "bucket of dogs")) - .add_autocomplete_choice(dpp::command_option_choice("elephants", "bottle of elephants")) - ); - bot.log(dpp::ll_debug, "Autocomplete " + opt.name + " with value '" + uservalue + "' in field " + event.name); - break; - } - } - }); - - bot.start(dpp::st_wait); - - return 0; -} -~~~~~~~~~~ - diff --git a/vendor/DPP/docpages/example_programs/interactions_and_components/commandhandler.md b/vendor/DPP/docpages/example_programs/interactions_and_components/commandhandler.md deleted file mode 100644 index 2a8de1b8..00000000 --- a/vendor/DPP/docpages/example_programs/interactions_and_components/commandhandler.md +++ /dev/null @@ -1,68 +0,0 @@ -\page commandhandler Using a command handler object - -If you have many commands in your bot, and want to handle commands from multiple sources (for example modern slash commands, and more regular -prefixed channel messages) you should consider instantiating a dpp::commandhandler object. This object can be used to automatically route -commands and their parameters to functions in your program. A simple example of using this object to route commands is shown below, and will -route both the /ping (global slash command) and .ping (prefixed channel message command) to a lambda where a reply can be generated. - -\note This example automatically hooks the dpp::cluster::on_message_create and dpp::cluster::on_slashcommand events. This can be overridden if needed to allow you to still make use of these functions for your own code, if you need to do this please see the constructor documentation for dpp::commandhandler. - -Note that because the dpp::commandhandler::add_command method accepts a std::function as the command handler, you may point a command handler -at a simple lambda (as shown in this example), a function pointer, or an instantiated class method of an object. This is extremely flexible -and allows you to decide how and where commands should be routed, either to an object oriented system or to a lambda based system. - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} -#include - -int main() -{ - dpp::cluster bot("token"); - - bot.on_log(dpp::utility::cout_logger()); - - /* Create command handler, and specify prefixes */ - dpp::commandhandler command_handler(&bot); - /* Specifying a prefix of "/" tells the command handler it should also expect slash commands */ - command_handler.add_prefix(".").add_prefix("/"); - - bot.on_ready([&command_handler](const dpp::ready_t &event) { - - command_handler.add_command( - /* Command name */ - "ping", - - /* Parameters */ - { - {"testparameter", dpp::param_info(dpp::pt_string, true, "Optional test parameter") } - }, - - /* Command handler */ - [&command_handler](const std::string& command, const dpp::parameter_list_t& parameters, dpp::command_source src) { - std::string got_param; - if (!parameters.empty()) { - got_param = std::get(parameters[0].second); - } - command_handler.reply(dpp::message("Pong! -> " + got_param), src); - }, - - /* Command description */ - "A test ping command", - - /* Guild id (omit for a global command) */ - 819556414099554344 - ); - - /* NOTE: We must call this to ensure slash commands are registered. - * This does a bulk register, which will replace other commands - * that are registered already! - */ - command_handler.register_commands(); - - }); - - bot.start(dpp::st_wait); - - return 0; -} -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - diff --git a/vendor/DPP/docpages/example_programs/interactions_and_components/components.md b/vendor/DPP/docpages/example_programs/interactions_and_components/components.md deleted file mode 100644 index 407c4928..00000000 --- a/vendor/DPP/docpages/example_programs/interactions_and_components/components.md +++ /dev/null @@ -1,54 +0,0 @@ -\page components Using button components - -Discord's newest features support sending buttons alongside messages, which when clicked by the user trigger an interaction which is routed by -D++ as an on_button_click event. To make use of this, use code as in this example. - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} -#include -#include -#include - -int main() { - - dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content); - - bot.on_log(dpp::utility::cout_logger()); - - /* Message handler to look for a command called !button */ - bot.on_message_create([&bot](const dpp::message_create_t & event) { - if (event.msg.content == "!button") { - /* Create a message containing an action row, and a button within the action row. */ - bot.message_create( - dpp::message(event.msg.channel_id, "this text has buttons").add_component( - dpp::component().add_component( - dpp::component().set_label("Click me!"). - set_type(dpp::cot_button). - set_emoji(u8"😄"). - set_style(dpp::cos_danger). - set_id("myid") - ) - ) - ); - } - }); - - /* When a user clicks your button, the on_button_click event will fire, - * containing the custom_id you defined in your button. - */ - bot.on_button_click([&bot](const dpp::button_click_t & event) { - /* Button clicks are still interactions, and must be replied to in some form to - * prevent the "this interaction has failed" message from Discord to the user. - */ - event.reply("You clicked: " + event.custom_id); - }); - - bot.start(dpp::st_wait); - - return 0; -} -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When the feature is functioning, the code below will produce buttons on the reply message like in the image below: - -\image html button.png - diff --git a/vendor/DPP/docpages/example_programs/interactions_and_components/components2.md b/vendor/DPP/docpages/example_programs/interactions_and_components/components2.md deleted file mode 100644 index 4ad17dd3..00000000 --- a/vendor/DPP/docpages/example_programs/interactions_and_components/components2.md +++ /dev/null @@ -1,55 +0,0 @@ -\page components2 Advanced components - -This example demonstrates receiving button clicks and sending response messages. - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} -#include - -using json = nlohmann::json; - -int main() { - - dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content); // Privileged intent required to receive message content - - bot.on_log(dpp::utility::cout_logger()); - - bot.on_button_click([&bot](const dpp::button_click_t & event) { - if (event.custom_id == "10") { - event.reply(dpp::message("Correct").set_flags(dpp::m_ephemeral)); - } else { - event.reply(dpp::message("Incorrect").set_flags(dpp::m_ephemeral)); - } - }); - - bot.on_message_create([&bot](const dpp::message_create_t & event) { - if (event.msg.content == "!ping2") { - bot.message_create( - dpp::message(event.msg.channel_id, "What is 5+5?").add_component( - dpp::component().add_component( - dpp::component().set_label("9"). - set_style(dpp::cos_primary). - set_id("9") - ).add_component( - dpp::component().set_label("10"). - set_style(dpp::cos_primary). - set_id("10") - ).add_component( - dpp::component().set_label("11"). - set_style(dpp::cos_primary). - set_id("11") - ) - ) - ); - } - }); - - bot.start(dpp::st_wait); - - return 0; -} -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This code will send a different message for correct and incorrect answers. - -\image html button_2.png - diff --git a/vendor/DPP/docpages/example_programs/interactions_and_components/components3.md b/vendor/DPP/docpages/example_programs/interactions_and_components/components3.md deleted file mode 100644 index df98eb3b..00000000 --- a/vendor/DPP/docpages/example_programs/interactions_and_components/components3.md +++ /dev/null @@ -1,47 +0,0 @@ -\page components3 Using select menu components - -This example demonstrates receiving select menu clicks and sending response messages. - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} -#include - -using json = nlohmann::json; - -int main() { - - dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content); - - bot.on_log(dpp::utility::cout_logger()); - - /* Message handler to look for a command called !select */ - bot.on_message_create([&bot](const dpp::message_create_t & event) { - if (event.msg.content == "!select") { - /* Create a message containing an action row, and a select menu within the action row. */ - dpp::message m(event.msg.channel_id, "this text has a select menu"); - m.add_component( - dpp::component().add_component( - dpp::component().set_type(dpp::cot_selectmenu). - set_placeholder("Pick something"). - add_select_option(dpp::select_option("label1","value1","description1").set_emoji(u8"😄")). - add_select_option(dpp::select_option("label2","value2","description2").set_emoji(u8"🙂")). - set_id("myselid") - ) - ); - bot.message_create(m); - } - }); - /* When a user clicks your select menu , the on_select_click event will fire, - * containing the custom_id you defined in your select menu. - */ - bot.on_select_click([&bot](const dpp::select_click_t & event) { - /* Select clicks are still interactions, and must be replied to in some form to - * prevent the "this interaction has failed" message from Discord to the user. - */ - event.reply("You clicked " + event.custom_id + " and chose: " + event.values[0]); - }); - - bot.start(dpp::st_wait); - - return 0; -} -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/vendor/DPP/docpages/example_programs/interactions_and_components/context_menus.md b/vendor/DPP/docpages/example_programs/interactions_and_components/context_menus.md deleted file mode 100644 index 5da0ed88..00000000 --- a/vendor/DPP/docpages/example_programs/interactions_and_components/context_menus.md +++ /dev/null @@ -1,51 +0,0 @@ -\page context-menu Context Menus - -Context menus are application commands that appear on the context menu (right click or tap) of users or messages to perform context-specific actions. They can be created using `dpp::slashcommand`. Once you create a context menu, try right-clicking either a user or message to see it in your server! - -\image html context_menu_user_command.png - -The following example shows how to create and handle **user context menus**. - -~~~~~~~~~~{.cpp} -#include -#include - -int main() -{ - dpp::cluster bot("token"); - - bot.on_log(dpp::utility::cout_logger()); - - bot.on_ready([&bot](const dpp::ready_t &event) { - if (dpp::run_once()) { - /* Register the command */ - bot.guild_command_create( - dpp::slashcommand() - .set_type(dpp::ctxm_user) - .set_name("High Five") - .set_application_id(bot.me.id), - 857692897221033129 // you need to put your guild-id in here - ); - } - }); - - /* Use the on_user_context_menu event to look for user context menu actions */ - bot.on_user_context_menu([&](const dpp::user_context_menu_t &event) { - /* check if the context menu name is High Five */ - if (event.command.get_command_name() == "High Five") { - dpp::user user = event.get_user(); // the user who the command has been issued on - dpp::user author = event.command.get_issuing_user(); // the user who clicked on the context menu - event.reply(author.get_mention() + " slapped " + user.get_mention()); - } - }); - - /* Start bot */ - bot.start(dpp::st_wait); - - return 0; -} -~~~~~~~~~~ - -It registers a guild command that can be called by right-click a user and click on the created menu. - -\image html context_menu_user_command_showcase.png diff --git a/vendor/DPP/docpages/example_programs/interactions_and_components/modal_dialog_interactions.md b/vendor/DPP/docpages/example_programs/interactions_and_components/modal_dialog_interactions.md deleted file mode 100644 index d388a72c..00000000 --- a/vendor/DPP/docpages/example_programs/interactions_and_components/modal_dialog_interactions.md +++ /dev/null @@ -1,78 +0,0 @@ -\page modal-dialog-interactions Modal Dialog Interactions - -Modal dialog interactions are a new Discord API feature that allow you to have pop-up windows which prompt the user to input information. Once the user has filled in this information, your program will receive an `on_form_submit` event which will contain the data which was input. You must use a slash command interaction response to submit your modal form data to Discord, via the `on_slashcommand` event. From here calling the `dialog` method of the `interaction_create_t` event object will trigger the dialog to appear. - -Each dialog box may have up to five rows of input fields. The example below demonstrates a simple setup with just one text input: - -~~~~~~~~~~{.cpp} -#include -#include - -int main(int argc, char const *argv[]) -{ - dpp::cluster bot("token"); - - bot.on_log(dpp::utility::cout_logger()); - - bot.on_ready([&](const dpp::ready_t & event) { - if (dpp::run_once()) { - /* Create a slash command and register it as a global command */ - bot.global_command_create(dpp::slashcommand("dialog", "Make a modal dialog box", bot.me.id)); - } - }); - - bot.on_slashcommand([&bot](const dpp::slashcommand_t & event) { - /* Check for our /dialog command */ - if (event.command.get_command_name() == "dialog") { - /* Instantiate an interaction_modal_response object */ - dpp::interaction_modal_response modal("my_modal", "Please enter stuff"); - /* Add a text component */ - modal.add_component( - dpp::component(). - set_label("Short type rammel"). - set_id("field_id"). - set_type(dpp::cot_text). - set_placeholder("gumd"). - set_min_length(5). - set_max_length(50). - set_text_style(dpp::text_short) - ); - /* Add another text component in the next row, as required by Discord */ - modal.add_row(); - modal.add_component( - dpp::component(). - set_label("Type rammel"). - set_id("field_id2"). - set_type(dpp::cot_text). - set_placeholder("gumf"). - set_min_length(1). - set_max_length(2000). - set_text_style(dpp::text_paragraph) - ); - /* Trigger the dialog box. All dialog boxes are ephemeral */ - event.dialog(modal); - } - }); - - /* This event handles form submission for the modal dialog we create above */ - bot.on_form_submit([&](const dpp::form_submit_t & event) { - /* For this simple example we know the first element of the first row ([0][0]) is value type string. - * In the real world it may not be safe to make such assumptions! - */ - std::string v = std::get(event.components[0].components[0].value); - dpp::message m; - m.set_content("You entered: " + v).set_flags(dpp::m_ephemeral); - /* Emit a reply. Form submission is still an interaction and must generate some form of reply! */ - event.reply(m); - }); - - /* Start bot */ - bot.start(dpp::st_wait); - return 0; -} -~~~~~~~~~~ - -If you compile and run this program and wait for the global command to register, typing `/dialog` will present you with a dialog box like the one below: - -\image html modal_dialog.png - diff --git a/vendor/DPP/docpages/example_programs/interactions_and_components/slashcommands.md b/vendor/DPP/docpages/example_programs/interactions_and_components/slashcommands.md deleted file mode 100644 index f849f236..00000000 --- a/vendor/DPP/docpages/example_programs/interactions_and_components/slashcommands.md +++ /dev/null @@ -1,61 +0,0 @@ -\page slashcommands Using Slash Commands and Interactions - -Slash commands and interactions are a newer feature of Discord which allow bot's commands to be registered centrally within the system and for users to easily explore and get help with available commands through the client itself. - -To add a slash command you should use the dpp::cluster::global_command_create method for global commands (available to all guilds) or dpp::cluster::guild_command_create to create a local command (available only to one guild). - -When a user issues these commands the reply will arrive via the `on_slashcommand` event which you can hook, and take action when you see your commands. It is possible to reply to an interaction by using either the dpp::interaction_create_t::reply method, or by manually instantiating an object of type dpp::interaction_response and attaching a dpp::message object to it. - -dpp::interaction_create_t::reply has two overloaded versions of the method, one of which accepts simple std::string replies, for basic text-only messages (if your message is 'ephemeral' you must use this) and one which accepts a dpp::message for more advanced replies. Please note that at present, Discord only supports a small subset of message and embed features within an interaction response object. - -\note You can also use the unified command handler, which lets you combine channel based message commands and slash commands under the same lambda with the same code like they were one and the same. Note that after August of 2022 Discord will be discouraging bots from using commands that are prefixed messages via means of a privileged message intent. It is advised that you exclusively use slash commands, or the unified handler with only a prefix of "/" going forward for any new bots you create and look to migrating existing bots to this setup. - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} -#include - -int main() -{ - dpp::cluster bot("token"); - - bot.on_log(dpp::utility::cout_logger()); - - /* The event is fired when someone issues your commands */ - bot.on_slashcommand([&bot](const dpp::slashcommand_t & event) { - /* Check which command they ran */ - if (event.command.get_command_name() == "blep") { - /* Fetch a parameter value from the command parameters */ - std::string animal = std::get(event.get_parameter("animal")); - /* Reply to the command. There is an overloaded version of this - * call that accepts a dpp::message so you can send embeds. - */ - event.reply(std::string("Blep! You chose") + animal); - } - }); - - bot.on_ready([&bot](const dpp::ready_t & event) { - if (dpp::run_once()) { - - /* Create a new global command on ready event */ - dpp::slashcommand newcommand("blep", "Send a random adorable animal photo", bot.me.id); - newcommand.add_option( - dpp::command_option(dpp::co_string, "animal", "The type of animal", true). - add_choice(dpp::command_option_choice("Dog", std::string("animal_dog"))). - add_choice(dpp::command_option_choice("Cat", std::string("animal_cat"))). - add_choice(dpp::command_option_choice("Penguin", std::string("animal_penguin") - ) - ) - ); - - /* Register the command */ - bot.global_command_create(newcommand); - } - }); - - bot.start(dpp::st_wait); - - return 0; -} -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -\note For demonstration purposes, and small bots, this code is OK, but in the real world once your bot gets big, it's not recommended to create slash commands in the `on_ready` event because it gets called often (discord forces reconnections and sometimes these do not resume). You could for example add a commandline parameter to your bot (`argc`, `argv`) so that if you want the bot to register commands it must be launched with a specific command line argument. - diff --git a/vendor/DPP/docpages/example_programs/interactions_and_components/subcommands.md b/vendor/DPP/docpages/example_programs/interactions_and_components/subcommands.md deleted file mode 100644 index 25046333..00000000 --- a/vendor/DPP/docpages/example_programs/interactions_and_components/subcommands.md +++ /dev/null @@ -1,78 +0,0 @@ -\page subcommands Using sub-commands in slash commands - -This demonstrates how to use sub-commands within slash commands. Also shown below is an example of how to get a "resolved" parameter without having to use the cache or an extra API call. - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} -#include -#include - -int main() { - - dpp::cluster bot("token"); - - bot.on_log(dpp::utility::cout_logger()); - - /* Executes on ready. */ - bot.on_ready([&bot](const dpp::ready_t & event) { - if (dpp::run_once()) { - /* Define a slash command. */ - dpp::slashcommand image("image", "Send a specific image.", bot.me.id); - image.add_option( - /* Create a subcommand type option for "dog". */ - dpp::command_option(dpp::co_sub_command, "dog", "Send a picture of a dog."). - add_option(dpp::command_option(dpp::co_user, "user", "User to turn into a dog.", false)) - ); - image.add_option( - /* Create another subcommand type option for "cat". */ - dpp::command_option(dpp::co_sub_command, "cat", "Send a picture of a cat."). - add_option(dpp::command_option(dpp::co_user, "user", "User to turn into a cat.", false)) - ); - /* Create command */ - bot.global_command_create(image); - } - }); - - /* Use the on_slashcommand event to look for commands */ - bot.on_slashcommand([&bot](const dpp::slashcommand_t & event) { - dpp::interaction interaction = event.command; - dpp::command_interaction cmd_data = interaction.get_command_interaction(); - /* Check if the command is the image command. */ - if (interaction.get_command_name() == "image") { - /* Get the sub command */ - auto subcommand = cmd_data.options[0]; - /* Check if the subcommand is "dog" */ - if (subcommand.name == "dog") { - /* Checks if the subcommand has any options. */ - if (!subcommand.options.empty()) { - /* Get the user from the parameter */ - dpp::user user = interaction.get_resolved_user( - subcommand.get_value(0) - ); - event.reply(user.get_mention() + " has now been turned into a dog."); - } else { - /* Reply if there were no options.. */ - event.reply("No user specified"); - } - } - /* Check if the subcommand is "cat" */ - if (subcommand.name == "cat") { - /* Checks if the subcommand has any options. */ - if (!subcommand.options.empty()) { - /* Get the user from the parameter */ - dpp::user user = interaction.get_resolved_user( - subcommand.get_value(0) - ); - event.reply(user.get_mention() + " has now been turned into a cat."); - } else { - /* Reply if there were no options.. */ - event.reply("No user specified"); - } - } - } - }); - - bot.start(dpp::st_wait); - - return 0; -} -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/vendor/DPP/docpages/example_programs/interactions_and_components/upload_parameter.md b/vendor/DPP/docpages/example_programs/interactions_and_components/upload_parameter.md deleted file mode 100644 index 27b107cf..00000000 --- a/vendor/DPP/docpages/example_programs/interactions_and_components/upload_parameter.md +++ /dev/null @@ -1,48 +0,0 @@ -\page discord-application-command-file-upload Using file parameters for application commands (slash commands) - -The program below demonstrates how to use the 'file' type parameter to an application command (slash command). -You must first get the file_id via std::get, and then you can find the attachment details in the 'resolved' -section, `event.command.resolved`. - -The file is uploaded to Discord's CDN so if you need it locally you should fetch the `.url` value, e.g. by using -something like `dpp::cluster::request()`. - -~~~~~~~~~~~~~~~~{.cpp} -#include - -int main() -{ - dpp::cluster bot("token"); - - bot.on_log(dpp::utility::cout_logger()); - - bot.on_slashcommand([&bot](const dpp::slashcommand_t & event) { - if (event.command.type == dpp::it_application_command) { - dpp::command_interaction cmd_data = std::get(event.command.data); - if (cmd_data.name == "show") { - dpp::snowflake file_id = std::get(event.get_parameter("file")); - auto iter = event.command.resolved.attachments.find(file_id); - if (iter != event.command.resolved.attachments.end()) { - const dpp::attachment& att = iter->second; - event.reply(att.url); - } - } - } - }); - - bot.on_ready([&bot](const dpp::ready_t & event) { - - if (dpp::run_once()) { - dpp::slashcommand newcommand("show", "Show an uploaded file", bot.me.id); - - newcommand.add_option(dpp::command_option(dpp::co_attachment, "file", "Select an image")); - - bot.global_command_create(newcommand); - } - }); - - bot.start(dpp::st_wait); - - return 0; -} -~~~~~~~~~~~~~~~~ diff --git a/vendor/DPP/docpages/example_programs/misc.md b/vendor/DPP/docpages/example_programs/misc.md deleted file mode 100644 index 1d7afcdf..00000000 --- a/vendor/DPP/docpages/example_programs/misc.md +++ /dev/null @@ -1,9 +0,0 @@ -\page misc Miscellaneous Examples - -This section lists examples that do not fit neatly into any of the categories above. - -* \subpage making_a_http_request "Making arbitrary HTTP requests using D++" -* \subpage spdlog "Integrating with spdlog" -* \subpage caching-messages "Caching messages" -* \subpage collecting-reactions "Collecting Reactions" -* \subpage cpp-eval-command-discord "Making an eval command in C++" diff --git a/vendor/DPP/docpages/example_programs/misc/cache_messages.md b/vendor/DPP/docpages/example_programs/misc/cache_messages.md deleted file mode 100644 index 77cfdbff..00000000 --- a/vendor/DPP/docpages/example_programs/misc/cache_messages.md +++ /dev/null @@ -1,61 +0,0 @@ -\page caching-messages Caching Messages - -By default D++ does not cache messages. The example program below demonstrates how to instantiate a custom cache using dpp::cache which will allow you to cache messages and query the cache for messages by ID. - -This can be adjusted to cache any type derived from dpp::managed including types you define yourself. - -@note This example will cache and hold onto messages forever! In a real world situation this would be bad. If you do use this, -you should use the dpp::cache::remove() method periodically to remove stale items. This is left out of this example as a learning -exercise to the reader. For further reading please see the documentation of dpp::cache - -~~~~~~~~~~{.cpp} -#include -#include - -int main() { - /* Create bot */ - dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content); - - /* Create a cache to contain types of dpp::message */ - dpp::cache message_cache; - - bot.on_log(dpp::utility::cout_logger()); - - /* Message handler */ - bot.on_message_create([&](const dpp::message_create_t &event) { - - /* Make a permanent pointer using new, for each message to be cached */ - dpp::message* m = new dpp::message(); - /* Store the message into the pointer by copying it */ - *m = event.msg; - /* Store the new pointer to the cache using the store() method */ - message_cache.store(m); - - /* Simple ghetto command handler. In the real world, use slashcommand or commandhandler here. */ - std::stringstream ss(event.msg.content); - std::string cmd; - dpp::snowflake msg_id; - ss >> cmd; - - /* Look for our command */ - if (cmd == "!get") { - ss >> msg_id; - /* Search our cache for a cached message */ - dpp::message* find_msg = message_cache.find(msg_id); - if (find_msg != nullptr) { - /* Found a cached message, echo it out */ - bot.message_create(dpp::message(event.msg.channel_id, "This message had the following content: " + find_msg->content)); - } else { - /* Nothing like that here. Have you checked under the carpet? */ - bot.message_create(dpp::message(event.msg.channel_id, "There is no message cached with this ID")); - } - } - }); - - /* Start bot */ - bot.start(dpp::st_wait); - - return 0; -} -~~~~~~~~~~ - diff --git a/vendor/DPP/docpages/example_programs/misc/collect_reactions.md b/vendor/DPP/docpages/example_programs/misc/collect_reactions.md deleted file mode 100644 index 6e7911b9..00000000 --- a/vendor/DPP/docpages/example_programs/misc/collect_reactions.md +++ /dev/null @@ -1,55 +0,0 @@ -\page collecting-reactions Collecting Reactions - -D++ comes with many useful helper classes, but amongst these is something called dpp::collector. Collector is a template which can be specialised to automatically collect objects of a pre-determined type from events for a specific interval of time. Once this time period is up, or the class is otherwise signalled, a method is called with the complete set of collected objects. - -In the example below we will use it to collect all reactions on a message. - -~~~~~~~~~~{.cpp} -#include - -/* To create a collector we must derive from dpp::collector. As dpp::collector is a complicated template, - * various pre-made forms exist such as this one, reaction_collector. - */ -class react_collector : public dpp::reaction_collector { -public: - /* Collector will run for 20 seconds */ - react_collector(dpp::cluster* cl, snowflake id) : dpp::message_collector(cl, 20, id) { } - - /* On completion just output number of collected reactions to as a message. */ - virtual void completed(const std::vector& list) { - if (list.size()) { - owner->message_create(dpp::message(list[0].channel_id, "I collected " + std::to_string(list.size()) + " reactions!")); - } else { - owner->message_create(dpp::message("... I got nothin'.")); - } - } -}; - - -int main() { - /* Create bot */ - dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content); - - /* Pointer to reaction collector */ - react_collector* r = nullptr; - - bot.on_log(dpp::utility::cout_logger()); - - /* Message handler */ - bot.on_message_create([&](const dpp::message_create_t &event) { - - /* If someone sends a message that has the text 'collect reactions!' start a reaction collector */ - if (event.msg.content == "collect reactions!" && r == nullptr) { - /* Create a new reaction collector to collect reactions */ - r = new react_collector(&bot, event.msg.id); - } - - }); - - /* Start bot */ - bot.start(dpp::st_wait); - - return 0; -} -~~~~~~~~~~ - diff --git a/vendor/DPP/docpages/example_programs/misc/eval.md b/vendor/DPP/docpages/example_programs/misc/eval.md deleted file mode 100644 index 369e7498..00000000 --- a/vendor/DPP/docpages/example_programs/misc/eval.md +++ /dev/null @@ -1,244 +0,0 @@ -\page cpp-eval-command-discord Making an eval command in C++ - -### What is an eval command anyway? - -Many times people will ask: "how do i make a command like 'eval' in C++". For the uninitiated, an `eval` command is a command often found in interpreted languages such as Javascript and Python, which allows the developer to pass in raw interpreter statements which are then executed within the context of the running program, without any sandboxing. Eval commands are plain **evil**, if not properly coded in. - -Needless to say, this is very dangerous. If you are asking how to do this, and want to put this into your bot, we trust that you have a very good reason to do so and have considered alternatives before resorting to this. The code below is for educational purposes only and makes several assumptions: - -1. This code will only operate on UNIX-like systems such as Linux (not **Darwin**) -2. It assumes you use GCC, and have `g++` installed on your server and in your $PATH -3. The program will attempt to write to the current directory -4. No security checks will be done against the code, except for to check that it is being run by the bot's developer by snowflake id. It is entirely possible to send an `!eval exit(0);` and make the bot quit, for example, or delete files from the host operating system, if misused or misconfigured. -5. You're willing to wait a few seconds for compilation before your evaluated code runs. There isn't a way around this, as C++ is a compiled language. - -To create this program you must create two files, `eval.h` and `eval.cpp`. The header file lists forward declarations of functions that you will be able to use directly within your `eval` code. As well as this the entire of D++ will be available to the eval command via the local variable `bot`, and the entire `on_message_create` event variable via a local variable called `event`. - -The evaluated code will run within its own thread, so can execute for as long as it needs (but use common sense, don't go spawning a tight `while` loop that runs forever, you'll lock a thread at 100% CPU that won't ever end!). - -### Implementation details - -This code operates by outputting your provided code to be evaluated into a simple boilerplate program which can be compiled to a -shared object library (.so file). This .so file is then compiled with g++, using the `-shared` and `-fPIC` flags. If the program can be successfully compiled, it is then loaded using `dlopen()`, and the symbol `so_exec()` searched for within it, and called. This `so_exec()` function will contain the body of the code given to the eval command. Once this has been called and it has returned, -the `dlclose()` function is called to unload the library, and finally any temporary files (such as the .so file and its corresponding .cpp file) are cleaned up. -Docker is definitely recommended if you code on Windows/Mac OS, because docker desktop still uses a linux VM, so your code can easily use `.so` file and your code runs the same on your vps (if it also uses Linux distro) - -### Source code - -\warning If you manage to get your system, network, or anything else harmed by use or misuse of this code, we are not responsible. Don't say we didn't warn you! Find another way to solve your problem! - -#### eval.h - -Remember that eval.h contains forward-declarations of any functions you want to expose to the eval command. It is included both by the bot itself, and by any shared object files compiled for evaluation. - -~~~~~~~~~~~~~~~~{.cpp} -#pragma once - -/* This is the snowflake ID of the bot's developer. - * The eval command will be restricted to this user. - */ -#define MY_DEVELOPER 189759562910400512 - -/* Any functions you want to be usable from within an eval, - * that are not part of D++ itself or the message event, you - * can put here as forward declarations. The test_function() - * serves as an example. - */ - -int test_function(); -~~~~~~~~~~~~~~~~ - -#### eval.cpp - -This is the main body of the example program. - -~~~~~~~~~~~~~~~~{.cpp} -/** - * D++ eval command example. - * This is dangerous and for educational use only, here be dragons! - */ - -#include -#include -#include -#include -/* We have to define this to make certain functions visible */ -#ifndef _GNU_SOURCE - #define _GNU_SOURCE -#endif -#include -#include -#include "eval.h" - -/* This is an example function you can expose to your eval command */ -int test_function() { - return 42; -} - -/* Important: This code is for UNIX-like systems only, e.g. - * Linux, BSD, OSX. It will NOT work on Windows! - * Note for OSX you'll probably have to change all references - * from .so to .dylib. - */ -int main() -{ - dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content); - - bot.on_log(dpp::utility::cout_logger()); - - /* This won't work in a slash command very well yet, as there is not yet - * a multi-line slash command input type. - */ - bot.on_message_create([&bot](const auto & event) { - if (dpp::utility::utf8substr(event.msg.content, 0, 5) == "!eval") { - - /** - * THIS IS CRITICALLY IMPORTANT! - * Never EVER make an eval command that isn't restricted to a specific developer by user id. - * With access to this command the person who invokes it has at best full control over - * your bot's user account and at worst, full control over your entire network!!! - * Eval commands are Evil (pun intended) and could even be considered a security - * vulnerability. YOU HAVE BEEN WARNED! - */ - if (event.msg.author.id != MY_DEVELOPER) { - bot.message_create(dpp::message(event.msg.channel_id, "On the day i do this for you, Satan will be ice skating to work.")); - return; - } - - /* We start by creating a string that contains a cpp program for a simple library. - * The library will contain one exported function called so_exec() that is called - * containing the raw C++ code to eval. - */ - std::string code = "#include \n\ - #include \n\ - #include \n\ - #include \n\ - #include \n\ - #include \n\ - #include \n\ - #include \n\ - #include \"eval.h\"\n\ - extern \"C\" void so_exec(dpp::cluster& bot, dpp::message_create_t event) {\n\ - " + dpp::utility::utf8substr( - event.msg.content, - 6, - dpp::utility::utf8len(event.msg.content) - ) + ";\n\ - return;\n\ - }"; - - /* Next we output this string full of C++ to a cpp file on disk. - * This code assumes the current directory is writeable. The file will have a - * unique name made from the user's id and the message id. - */ - std::string source_filename = std::to_string(event.msg.author.id) + "_" + std::to_string(event.msg.id) + ".cpp"; - std::fstream code_file(source_filename, std::fstream::binary | std::fstream::out); - if (!code_file.is_open()) { - bot.message_create(dpp::message(event.msg.channel_id, "Unable to create source file for `eval`")); - return; - } - code_file << code; - code_file.close(); - - /* Now to actually compile the file. We use dpp::utility::exec to - * invoke a compiler. This assumes you are using g++, and it is in your path. - */ - double compile_start = dpp::utility::time_f(); - dpp::utility::exec("g++", { - "-std=c++17", - "-shared", /* Build the output as a .so file */ - "-fPIC", - std::string("-o") + std::to_string(event.msg.author.id) + "_" + std::to_string(event.msg.id) + ".so", - std::to_string(event.msg.author.id) + "_" + std::to_string(event.msg.id) + ".cpp", - "-ldpp", - "-ldl" - }, [event, &bot, source_filename, compile_start](const std::string &output) { - - /* After g++ is ran we end up inside this lambda with the output as a string */ - double compile_time = dpp::utility::time_f() - compile_start; - - /* Delete our cpp file, we don't need it any more */ - std::string del_file = std::string(getenv("PWD")) + std::to_string(event.msg.author.id) + "_" + std::to_string(event.msg.id) + ".cpp"; - unlink(del_file.c_str()); - - /* On successful compilation g++ outputs nothing, so any output here is error output */ - if (output.length()) { - bot.message_create(dpp::message(event.msg.channel_id, "Compile error: ```\n" + output + "\n```")); - } else { - - /* Now for the meat of the function. To actually load - * our shared object we use dlopen() to load it into the - * memory space of our bot. If dlopen() returns a nullptr, - * the shared object could not be loaded. The user probably - * did something odd with the symbols inside their eval. - */ - std::string dl = std::string(getenv("PWD")) + std::to_string(event.msg.author.id) + "_" + std::to_string(event.msg.id) + ".so"; - auto shared_object_handle = dlopen(dl.c_str(), RTLD_NOW); - if (!shared_object_handle) { - const char *dlsym_error = dlerror(); - bot.message_create(dpp::message(event.msg.channel_id, "Shared object load error: ```\n" + - std::string(dlsym_error ? dlsym_error : "Unknown error") +"\n```")); - return; - } - - /* This type represents the "void so_exec()" function inside - * the shared object library file. - */ - using function_pointer = void(*)(dpp::cluster&, dpp::message_create_t); - - /* Attempt to find the function called so_exec() inside the - * library we just loaded. If we can't find it, then the user - * did something really strange in their eval. Also note it's - * important we call dlerror() here to reset it before trying - * to use it a second time. It's weird-ass C code and is just - * like that. - */ - dlerror(); - function_pointer exec_run = (function_pointer)dlsym(shared_object_handle, "so_exec"); - const char *dlsym_error = dlerror(); - if (dlsym_error) { - bot.message_create(dpp::message(event.msg.channel_id, "Shared object load error: ```\n" + std::string(dlsym_error) +"\n```")); - dlclose(shared_object_handle); - return; - } - - /* Now we have a function pointer to our actual exec code in - * 'exec_run', so lets call it, and pass it a reference to - * the cluster, and also a copy of the message_create_t. - */ - double run_start = dpp::utility::time_f(); - exec_run(bot, event); - double run_time = dpp::utility::time_f() - run_start; - - /* When we're done with a .so file we must always dlclose() it */ - dlclose(shared_object_handle); - - /* We are now done with the compiled code too */ - unlink(dl.c_str()); - - /* Output some statistics */ - bot.message_create(dpp::message(event.msg.channel_id, - "Execution completed. Compile time: " + std::to_string(compile_time) + - "s, execution time " + std::to_string(run_time) + "s")); - } - }); - } - }); - - bot.start(dpp::st_wait); - return 0; -} -~~~~~~~~~~~~~~~~ - -### Compilation - -To compile this program you must link against `libdl`. It is also critically important to include the `-rdynamic` flag. For example: - -``` -g++ -std=c++17 -rdynamic -oeval eval.cpp -ldpp -ldl -``` - -### Example usage - -\image html eval_example.png - diff --git a/vendor/DPP/docpages/example_programs/misc/http_request.md b/vendor/DPP/docpages/example_programs/misc/http_request.md deleted file mode 100644 index ff01ba5e..00000000 --- a/vendor/DPP/docpages/example_programs/misc/http_request.md +++ /dev/null @@ -1,33 +0,0 @@ -\page making_a_http_request Making arbitrary HTTP requests using D++ - -If you wish to make arbitrary HTTP(S) requests to websites and APIs, e.g. to update statistics on bot lists, you can use code similar to the code below. You may pass any arbitrary POST data: - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} -#include - -int main() { - dpp::cluster bot("TOKEN GOES HERE"); - - bot.on_log(dpp::utility::cout_logger()); - - bot.on_ready([&bot](const dpp::ready_t& event) { - // Arbitrary post data as a string - std::string mypostdata = "{\"value\": 42}"; - // Make a HTTP POST request. HTTP and HTTPS are supported here. - bot.request( - "http://www.somebotlist.com/api/servers", dpp::m_post, [](const dpp::http_request_completion_t & cc) { - // This callback is called when the HTTP request completes. See documentation of - // dpp::http_request_completion_t for information on the fields in the parameter. - std::cout << "I got reply: " << cc.body << " with HTTP status code: " << cc.status << "\n"; - }, - mypostdata, - "application/json", - { - {"Authorization", "Bearer tokengoeshere"} - } - ); - }); - - bot.start(dpp::st_wait); -} -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/vendor/DPP/docpages/example_programs/misc/spdlog.md b/vendor/DPP/docpages/example_programs/misc/spdlog.md deleted file mode 100644 index 9edeb7a2..00000000 --- a/vendor/DPP/docpages/example_programs/misc/spdlog.md +++ /dev/null @@ -1,63 +0,0 @@ -\page spdlog Integrating with spdlog - -If you want to make your bot use spdlog, like aegis does, you can attach it to the on_log event. You can do this as follows: - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} -#include -#include -#include -#include -#include -#include -#include - -int main(int argc, char const *argv[]) -{ - dpp::cluster bot("token"); - - const std::string log_name = "mybot.log"; - - /* Set up spdlog logger */ - std::shared_ptr log; - spdlog::init_thread_pool(8192, 2); - std::vector sinks; - auto stdout_sink = std::make_shared(); - auto rotating = std::make_shared(log_name, 1024 * 1024 * 5, 10); - sinks.push_back(stdout_sink); - sinks.push_back(rotating); - log = std::make_shared("logs", sinks.begin(), sinks.end(), spdlog::thread_pool(), spdlog::async_overflow_policy::block); - spdlog::register_logger(log); - log->set_pattern("%^%Y-%m-%d %H:%M:%S.%e [%L] [th#%t]%$ : %v"); - log->set_level(spdlog::level::level_enum::debug); - - /* Integrate spdlog logger to D++ log events */ - bot.on_log([&bot, &log](const dpp::log_t & event) { - switch (event.severity) { - case dpp::ll_trace: - log->trace("{}", event.message); - break; - case dpp::ll_debug: - log->debug("{}", event.message); - break; - case dpp::ll_info: - log->info("{}", event.message); - break; - case dpp::ll_warning: - log->warn("{}", event.message); - break; - case dpp::ll_error: - log->error("{}", event.message); - break; - case dpp::ll_critical: - default: - log->critical("{}", event.message); - break; - } - }); - - /* Add the rest of your events */ - - bot.start(dpp::st_wait); - return 0; -} -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/vendor/DPP/docpages/example_programs/music_and_audio.md b/vendor/DPP/docpages/example_programs/music_and_audio.md deleted file mode 100644 index 5c222df1..00000000 --- a/vendor/DPP/docpages/example_programs/music_and_audio.md +++ /dev/null @@ -1,9 +0,0 @@ -\page music-and-audio Music and Audio - -If you want to make noise, or capture noise, you're in the right place. You'll find examples here for creating basic music bots, or recording voice, amongst other things. - -* \subpage soundboard "Creating a Sound Board" -* \subpage oggopus "Streaming Ogg Opus file" -* \subpage stream-mp3-discord-bot "Streaming MP3 files" -* \subpage record-user "Record yourself in a VC" -* \subpage joinvc "Join or switch to the voice channel of the user issuing a command" diff --git a/vendor/DPP/docpages/example_programs/music_and_audio/join_voice.md b/vendor/DPP/docpages/example_programs/music_and_audio/join_voice.md deleted file mode 100644 index 65d069a4..00000000 --- a/vendor/DPP/docpages/example_programs/music_and_audio/join_voice.md +++ /dev/null @@ -1,75 +0,0 @@ -\page joinvc Join or switch to the voice channel of the user issuing a command - -When a user issues a command you may want to join their voice channel, e.g. in a music bot. If you are already on the same voice channel, the bot should do nothing (but be ready to instantly play audio) and if the user is on a different voice channel, the bot should switch to it. If the user is on no voice channel at all, this should be considered an error. This example shows how to do this. - -\note Please be aware this example sends no audio, but indicates clearly in the comments where and how you should do so. - -~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} -#include -#include -#include - -int main(int argc, char const *argv[]) -{ - /* Setup the bot */ - dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content); // Privileged intent required to receive message content - - bot.on_log(dpp::utility::cout_logger()); - - /* Use the on_message_create event to look for commands */ - bot.on_message_create([&bot, robot, robot_size](const dpp::message_create_t & event) { - - std::stringstream ss(event.msg.content); - std::string command; - - ss >> command; - - /* Switch to or join the vc the user is on. Syntax: .join */ - if (command == ".join") { - dpp::guild * g = dpp::find_guild(event.msg.guild_id); - auto current_vc = event.from->get_voice(event.msg.guild_id); - bool join_vc = true; - /* Check if we are currently on any vc */ - if (current_vc) { - /* Find the channel id that the user is currently on */ - auto users_vc = g->voice_members.find(event.msg.author.id); - /* See if we currently share a channel with the user */ - if (users_vc != g->voice_members.end() && current_vc->channel_id == users_vc->second.channel_id) { - join_vc = false; - /* We are on this voice channel, at this point we can send any audio instantly to vc: - - * current_vc->send_audio_raw(...) - */ - } else { - /* We are on a different voice channel. Leave it, then join the new one - * by falling through to the join_vc branch below. - */ - event.from->disconnect_voice(event.msg.guild_id); - join_vc = true; - } - } - /* If we need to join a vc at all, join it here if join_vc == true */ - if (join_vc) { - if (!g->connect_member_voice(event.msg.author.id)) { - /* The user issuing the command is not on any voice channel, we can't do anything */ - bot.message_create(dpp::message(event.msg.channel_id, "You don't seem to be on a voice channel! :(")); - } else { - /* We are now connecting to a vc. Wait for on_voice_ready - * event, and then send the audio within that event: - * - * event.voice_client->send_audio_raw(...); - * - * NOTE: We can't instantly send audio, as we have to wait for - * the connection to the voice server to be established! - */ - } - } - } - }); - - /* Start bot */ - bot.start(dpp::st_wait); - return 0; -} - -~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/vendor/DPP/docpages/example_programs/music_and_audio/mp3.md b/vendor/DPP/docpages/example_programs/music_and_audio/mp3.md deleted file mode 100644 index 0c80cafe..00000000 --- a/vendor/DPP/docpages/example_programs/music_and_audio/mp3.md +++ /dev/null @@ -1,107 +0,0 @@ -\page stream-mp3-discord-bot Streaming MP3 files - -To stream MP3 files via D++ you need to link an additional dependency to your bot, namely `libmpg123`. It is relatively simple when linking this library to your bot to then decode audio to PCM and send it to the dpp::discord_voice_client::send_audio_raw function as shown below: - - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -/* For an example we will hardcode a path to some awesome music here */ -#define MUSIC_FILE "/media/music/Rick Astley/Whenever You Need Somebody/Never Gonna Give You Up.mp3" - -int main(int argc, char const *argv[]) -{ - /* This will hold the decoded MP3. - * The D++ library expects PCM format, which are raw sound - * data, 2 channel stereo, 16 bit signed 48000Hz. - */ - std::vector pcmdata; - - mpg123_init(); - - int err = 0; - unsigned char* buffer; - size_t buffer_size, done; - int channels, encoding; - long rate; - - /* Note it is important to force the frequency to 48000 for Discord compatibility */ - mpg123_handle *mh = mpg123_new(NULL, &err); - mpg123_param(mh, MPG123_FORCE_RATE, 48000, 48000.0); - - /* Decode entire file into a vector. You could do this on the fly, but if you do that - * you may get timing issues if your CPU is busy at the time and you are streaming to - * a lot of channels/guilds. - */ - buffer_size = mpg123_outblock(mh); - buffer = new unsigned char[buffer_size]; - - /* Note: In a real world bot, this should have some error logging */ - mpg123_open(mh, MUSIC_FILE); - mpg123_getformat(mh, &rate, &channels, &encoding); - - unsigned int counter = 0; - for (int totalBytes = 0; mpg123_read(mh, buffer, buffer_size, &done) == MPG123_OK; ) { - for (auto i = 0; i < buffer_size; i++) { - pcmdata.push_back(buffer[i]); - } - counter += buffer_size; - totalBytes += done; - } - delete buffer; - mpg123_close(mh); - mpg123_delete(mh); - - /* Setup the bot */ - dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content); - - bot.on_log(dpp::utility::cout_logger()); - - /* Use the on_message_create event to look for commands */ - bot.on_message_create([&bot, &pcmdata](const dpp::message_create_t & event) { - std::stringstream ss(event.msg.content); - std::string command; - ss >> command; - - /* Tell the bot to join the discord voice channel the user is on. Syntax: .join */ - if (command == ".join") { - dpp::guild * g = dpp::find_guild(event.msg.guild_id); - if (!g->connect_member_voice(event.msg.author.id)) { - bot.message_create(dpp::message(event.msg.channel_id, "You don't seem to be on a voice channel! :(")); - } - } - - /* Tell the bot to play the mp3 file. Syntax: .mp3 */ - if (command == ".mp3") { - dpp::voiceconn* v = event.from->get_voice(event.msg.guild_id); - if (v && v->voiceclient && v->voiceclient->is_ready()) { - /* Stream the already decoded MP3 file. This passes the PCM data to the library to be encoded to OPUS */ - v->voiceclient->send_audio_raw((uint16_t*)pcmdata.data(), pcmdata.size()); - } - } - }); - - /* Start bot */ - bot.start(dpp::st_wait); - - /* Clean up */ - mpg123_exit(); - - return 0; -} -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To compile this program you must remember to specify `libmpg123` alongside `libdpp` in the build command, for example: - -` g++ -std=c++17 -o musictest musictest.cpp -lmpg123 -ldpp` - diff --git a/vendor/DPP/docpages/example_programs/music_and_audio/oggopus.md b/vendor/DPP/docpages/example_programs/music_and_audio/oggopus.md deleted file mode 100644 index fd1228d7..00000000 --- a/vendor/DPP/docpages/example_programs/music_and_audio/oggopus.md +++ /dev/null @@ -1,268 +0,0 @@ -\page oggopus Streaming Ogg Opus file - -This example shows how to stream an Ogg Opus file to a voice channel. This example requires some additional dependencies, namely `libogg` and `opusfile`. - -~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} -#include -#include -#include - -#include -#include -#include -#include -#include - -int main(int argc, char const *argv[]) -{ - /* Load an ogg opus file into memory. - * The bot expects opus packets to be 2 channel stereo, 48000Hz. - * - * You may use ffmpeg to encode songs to ogg opus: - * ffmpeg -i /path/to/song -c:a libopus -ar 48000 -ac 2 -vn -b:a 96K /path/to/opus.ogg - */ - dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content); - - bot.on_log(dpp::utility::cout_logger()); - - /* Use the on_message_create event to look for commands */ - bot.on_message_create([&bot](const dpp::message_create_t & event) { - std::stringstream ss(event.msg.content); - std::string command; - ss >> command; - - /* Tell the bot to join the discord voice channel the user is on. Syntax: .join */ - if (command == ".join") { - dpp::guild * g = dpp::find_guild(event.msg.guild_id); - if (!g->connect_member_voice(event.msg.author.id)) { - bot.message_create(dpp::message(event.msg.channel_id, "You don't seem to be on a voice channel! :(")); - } - } - - /* Tell the bot to play the sound file */ - if (command == ".play") { - dpp::voiceconn* v = event.from->get_voice(event.msg.guild_id); - if (v && v->voiceclient && v->voiceclient->is_ready()) { - ogg_sync_state oy; - ogg_stream_state os; - ogg_page og; - ogg_packet op; - OpusHead header; - char *buffer; - - FILE *fd; - - fd = fopen("/path/to/opus.ogg", "rb"); - - fseek(fd, 0L, SEEK_END); - size_t sz = ftell(fd); - rewind(fd); - - ogg_sync_init(&oy); - - int eos = 0; - int i; - - buffer = ogg_sync_buffer(&oy, sz); - fread(buffer, 1, sz, fd); - - ogg_sync_wrote(&oy, sz); - - /** - * We must first verify that the stream is indeed ogg opus - * by reading the header and parsing it - */ - if (ogg_sync_pageout(&oy, &og) != 1) - { - fprintf(stderr,"Does not appear to be ogg stream.\n"); - exit(1); - } - - ogg_stream_init(&os, ogg_page_serialno(&og)); - - if (ogg_stream_pagein(&os,&og) < 0) { - fprintf(stderr,"Error reading initial page of ogg stream.\n"); - exit(1); - } - - if (ogg_stream_packetout(&os,&op) != 1) - { - fprintf(stderr,"Error reading header packet of ogg stream.\n"); - exit(1); - } - - /* We must ensure that the ogg stream actually contains opus data */ - if (!(op.bytes > 8 && !memcmp("OpusHead", op.packet, 8))) - { - fprintf(stderr,"Not an ogg opus stream.\n"); - exit(1); - } - - /* Parse the header to get stream info */ - int err = opus_head_parse(&header, op.packet, op.bytes); - if (err) - { - fprintf(stderr,"Not a ogg opus stream\n"); - exit(1); - } - /* Now we ensure the encoding is correct for Discord */ - if (header.channel_count != 2 && header.input_sample_rate != 48000) - { - fprintf(stderr,"Wrong encoding for Discord, must be 48000Hz sample rate with 2 channels.\n"); - exit(1); - } - - /* Now loop though all the pages and send the packets to the vc */ - while (ogg_sync_pageout(&oy, &og) == 1){ - ogg_stream_init(&os, ogg_page_serialno(&og)); - - if(ogg_stream_pagein(&os,&og)<0){ - fprintf(stderr,"Error reading page of Ogg bitstream data.\n"); - exit(1); - } - - while (ogg_stream_packetout(&os,&op) != 0) - { - /* Read remaining headers */ - if (op.bytes > 8 && !memcmp("OpusHead", op.packet, 8)) - { - int err = opus_head_parse(&header, op.packet, op.bytes); - if (err) - { - fprintf(stderr,"Not a ogg opus stream\n"); - exit(1); - } - if (header.channel_count != 2 && header.input_sample_rate != 48000) - { - fprintf(stderr,"Wrong encoding for Discord, must be 48000Hz sample rate with 2 channels.\n"); - exit(1); - } - continue; - } - /* Skip the opus tags */ - if (op.bytes > 8 && !memcmp("OpusTags", op.packet, 8)) - continue; - - /* Send the audio */ - int samples = opus_packet_get_samples_per_frame(op.packet, 48000); - - v->voiceclient->send_audio_opus(op.packet, op.bytes, samples / 48); - } - } - - /* Cleanup */ - ogg_stream_clear(&os); - ogg_sync_clear(&oy); - } - } - }); - - /* Start bot */ - bot.start(dpp::st_wait); - return 0; -} -~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can compile this example using the following command - - c++ /path/to/source.cc -ldpp -lopus -lopusfile -logg -I/usr/include/opus - -## Using `liboggz` - -You can use `liboggz` to stream an Ogg Opus file to discord voice channel. -`liboggz` provides higher level abstraction and useful APIs. Some API `liboggz` provide includes seeking and timestamp interpretation. -Read more on the [documentation](https://www.xiph.org/oggz/doc/). - -~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} -#include -#include -#include - -#include -#include -#include -#include - -int main(int argc, char const *argv[]) -{ - /* Load an ogg opus file into memory. - * The bot expects opus packets to be 2 channel stereo, 48000Hz. - * - * You may use ffmpeg to encode songs to ogg opus: - * ffmpeg -i /path/to/song -c:a libopus -ar 48000 -ac 2 -vn -b:a 96K /path/to/opus.ogg - */ - dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content); - - bot.on_log(dpp::utility::cout_logger()); - - /* Use the on_message_create event to look for commands */ - bot.on_message_create([&bot](const dpp::message_create_t & event) { - std::stringstream ss(event.msg.content); - std::string command; - ss >> command; - - /* Tell the bot to join the discord voice channel the user is on. Syntax: .join */ - if (command == ".join") { - dpp::guild * g = dpp::find_guild(event.msg.guild_id); - if (!g->connect_member_voice(event.msg.author.id)) { - bot.message_create(dpp::message(event.msg.channel_id, "You don't seem to be on a voice channel! :(")); - } - } - - /* Tell the bot to play the sound file */ - if (command == ".play") { - dpp::voiceconn* v = event.from->get_voice(event.msg.guild_id); - if (v && v->voiceclient && v->voiceclient->is_ready()) { - // load the audio file with oggz - OGGZ *track_og = oggz_open("/path/to/opus.ogg", OGGZ_READ); - - if (track_og) { - // set read callback, this callback will be called on packets with the serialno, - // -1 means every packet will be handled with this callback - oggz_set_read_callback( - track_og, -1, - [](OGGZ *oggz, oggz_packet *packet, long serialno, - void *user_data) { - dpp::voiceconn *voiceconn = (dpp::voiceconn *)user_data; - - // send the audio - voiceconn->voiceclient->send_audio_opus(packet->op.packet, - packet->op.bytes); - - // make sure to always return 0 here, read more on oggz documentation - return 0; - }, - // this will be the value of void *user_data - (void *)v); - - // read loop - while (v && v->voiceclient && !v->voiceclient->terminating) { - // you can tweak this to whatever. Here I use BUFSIZ, defined in - // stdio.h as 8192 - static const constexpr long CHUNK_READ = BUFSIZ * 2; - - const long read_bytes = oggz_read(track_og, CHUNK_READ); - - // break on eof - if (!read_bytes) - break; - } - } else { - fprintf(stderr, "Error opening file\n"); - } - - // don't forget to free the memory - oggz_close(track_og); - } - } - }); - - /* Start bot */ - bot.start(dpp::st_wait); - return 0; -} -~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can compile this example using the following command - - c++ /path/to/source.cc -ldpp -loggz diff --git a/vendor/DPP/docpages/example_programs/music_and_audio/record_user.md b/vendor/DPP/docpages/example_programs/music_and_audio/record_user.md deleted file mode 100644 index bf37e151..00000000 --- a/vendor/DPP/docpages/example_programs/music_and_audio/record_user.md +++ /dev/null @@ -1,67 +0,0 @@ -\page record-user Record yourself in a VC - -DPP supports receiving audio. This examples show how to use it to record some user in a VC. - -\note Voice receiving by bots is not officially supported by the Discord API. We cannot guarantee that this feature will work in the future. - -~~~~~~~~~~{.cpp} -#include -#include -#include - -int main(int argc, char const *argv[]) -{ - /* Example to record a user in a VC - * - * Recording is output as './me.pcm' and you can play it via the soundboard example - * or use ffmpeg 'ffplay -f s16le -ar 48000 -ac 2 -i ./me.pcm' - */ - - /* Replace with the user's id you wish to record */ - dpp::snowflake user_id = 407877550216314882; - - dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content); - - FILE *fd; - fd = fopen("./me.pcm", "wb"); - - bot.on_log(dpp::utility::cout_logger()); - - /* Use the on_message_create event to look for commands */ - bot.on_message_create([&bot, &fd](const dpp::message_create_t & event) { - - std::stringstream ss(event.msg.content); - std::string command; - - ss >> command; - - /* Tell the bot to record */ - if (command == ".record") { - dpp::guild * g = dpp::find_guild(event.msg.guild_id); - if (!g->connect_member_voice(event.msg.author.id)) { - bot.message_create(dpp::message( - event.msg.channel_id, - "You don't seem to be on a voice channel! :(" - )); - } - } - - /* Tell the bot to stop recording */ - if (command == ".stop") { - event.from->disconnect_voice(event.msg.guild_id); - fclose(fd); - } - }); - - bot.on_voice_receive([&bot, &fd, &user_id](const dpp::voice_receive_t &event) { - if (event.user_id == user_id) { - fwrite((char *)event.audio, 1, event.audio_size, fd); - } - }); - - /* Start bot */ - bot.start(dpp::st_wait); - return 0; -} -~~~~~~~~~~ - diff --git a/vendor/DPP/docpages/example_programs/music_and_audio/soundboard.md b/vendor/DPP/docpages/example_programs/music_and_audio/soundboard.md deleted file mode 100644 index 624af153..00000000 --- a/vendor/DPP/docpages/example_programs/music_and_audio/soundboard.md +++ /dev/null @@ -1,68 +0,0 @@ -\page soundboard Creating a Sound Board - -This example script shows how to send a sound file to a voice channel. A few shortcuts are taken here, for more advanced techniques for connecting to a voice channel see the tutorial \ref joinvc - -~~~~~~~~~~~~~~~~~~~~~~~{.cpp} -#include -#include -#include - -int main(int argc, char const *argv[]) -{ - /* Load a sound file called Robot.pcm into memory. - * The bot expects PCM format, which are raw sound data, - * 2 channel stereo, 16 bit signed 48000Hz. - * - * You can use audacity to export these from WAV or MP3 etc. - * - * If you wanted to send a more complicated format, you could - * use a separate library to decode that audio to PCM. For - * example purposes, a raw PCM will suffice. This PCM file can - * be found within the bot's github repo. - */ - uint8_t* robot = nullptr; - size_t robot_size = 0; - std::ifstream input ("../testdata/Robot.pcm", std::ios::in|std::ios::binary|std::ios::ate); - if (input.is_open()) { - robot_size = input.tellg(); - robot = new uint8_t[robot_size]; - input.seekg (0, std::ios::beg); - input.read ((char*)robot, robot_size); - input.close(); - } - - /* Setup the bot */ - dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content); // Privileged intent required to receive message content - - bot.on_log(dpp::utility::cout_logger()); - - /* Use the on_message_create event to look for commands */ - bot.on_message_create([&bot, robot, robot_size](const dpp::message_create_t & event) { - - std::stringstream ss(event.msg.content); - std::string command; - - ss >> command; - - /* Tell the bot to join the discord voice channel the user is on. Syntax: .join */ - if (command == ".join") { - dpp::guild * g = dpp::find_guild(event.msg.guild_id); - if (!g->connect_member_voice(event.msg.author.id)) { - bot.message_create(dpp::message(event.msg.channel_id, "You don't seem to be on a voice channel! :(")); - } - } - - /* Tell the bot to play the sound file 'Robot.pcm'. Syntax: .robot */ - if (command == ".robot") { - dpp::voiceconn* v = event.from->get_voice(event.msg.guild_id); - if (v && v->voiceclient && v->voiceclient->is_ready()) { - v->voiceclient->send_audio_raw((uint16_t*)robot, robot_size); - } - } - }); - - /* Start bot */ - bot.start(dpp::st_wait); - return 0; -} -~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/vendor/DPP/docpages/example_programs/the_basics.md b/vendor/DPP/docpages/example_programs/the_basics.md deleted file mode 100644 index c5c43097..00000000 --- a/vendor/DPP/docpages/example_programs/the_basics.md +++ /dev/null @@ -1,8 +0,0 @@ -\page the-basics The Basics - -These example programs are great to get started with simple things in the D++ library, ideal for beginners to the language or to the Discord API. - -* \subpage firstbot "Creating Your First Bot" -* \subpage embed-message "Sending Embeds" -* \subpage attach-file "Attaching a file" -* \subpage webhooks "Webhooks" diff --git a/vendor/DPP/docpages/example_programs/the_basics/attachments.md b/vendor/DPP/docpages/example_programs/the_basics/attachments.md deleted file mode 100644 index 31ca63d7..00000000 --- a/vendor/DPP/docpages/example_programs/the_basics/attachments.md +++ /dev/null @@ -1,112 +0,0 @@ -\page attach-file Attaching a file to a message - -Attached files must be locally stored. - -To attach a file to a message, you can upload a local image. - -D++ has this helper function to read a file: `dpp::utility::read_file`. - -An example program: - -@note Because these examples utilizes message content, they require the message content privileged intent. - -~~~~~~~~~~{.cpp} -#include - -int main() { - dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content); - - bot.on_log(dpp::utility::cout_logger()); - - /* Message handler to look for a command called !file */ - bot.on_message_create([&bot](const dpp::message_create_t &event) { - if (event.msg.content == "!file") { - // create a message - dpp::message msg(event.msg.channel_id, "Hey there, i've got a new file!"); - - // attach the file to the message - msg.add_file("foobar.txt", dpp::utility::read_file("path_to_your_file.txt")); - - // send the message - bot.message_create(msg); - } - }); - - bot.start(dpp::st_wait); - return 0; -} -~~~~~~~~~~ - -Attachments via an url aren't possible. But there's a workaround for. You can download the file and then attach it to the message. - -To make requests, D++ also has a helper function: `dpp::cluster::request`. - -The following example program shows how to request a file and attach it to a message. - -~~~~~~~~~~{.cpp} -#include - -int main() { - dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content); - - bot.on_log(dpp::utility::cout_logger()); - - /* Message handler to look for a command called !file */ - bot.on_message_create([&bot](const dpp::message_create_t &event) { - if (event.msg.content == "!file") { - // request an image - bot.request("https://dpp.dev/DPP-Logo.png", dpp::m_get, [&bot, channel_id = event.msg.channel_id](const dpp::http_request_completion_t & httpRequestCompletion) { - - // create a message - dpp::message msg(channel_id, "This is my new attachment:"); - - // attach the image on success - if (httpRequestCompletion.status == 200) { - msg.add_file("logo.png", httpRequestCompletion.body); - } - - // send the message - bot.message_create(msg); - }); - } - }); - - bot.start(dpp::st_wait); - return 0; -} -~~~~~~~~~~ - -Here's another example of how to add a local image to an embed. - -Upload the image in the same message as the embed and then reference it in the embed. - -~~~~~~~~~~{.cpp} -#include - -int main() { - dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content); - - bot.on_log(dpp::utility::cout_logger()); - - /* Message handler to look for a command called !file */ - bot.on_message_create([&bot](const dpp::message_create_t &event) { - if (event.msg.content == "!file") { - // create a message - dpp::message msg(event.msg.channel_id, ""); - - // attach the image to the message - msg.add_file("image.jpg", dpp::utility::read_file("path_to_your_image.jpg")); - - dpp::embed embed; - embed.set_image("attachment://image.jpg"); // reference to the attached file - msg.add_embed(embed); - - // send the message - bot.message_create(msg); - } - }); - - bot.start(dpp::st_wait); - return 0; -} -~~~~~~~~~~ diff --git a/vendor/DPP/docpages/example_programs/the_basics/embeds.md b/vendor/DPP/docpages/example_programs/the_basics/embeds.md deleted file mode 100644 index 837a5199..00000000 --- a/vendor/DPP/docpages/example_programs/the_basics/embeds.md +++ /dev/null @@ -1,56 +0,0 @@ -\page embed-message Sending Embeds - -You might have seen these special messages, often sent by bots. In this section, we will show how to create an embed. - -@note Because this example utilizes message content, it requires the message content privileged intent. - -~~~~~~~~~~{.cpp} -#include - -int main() { - /* Setup the bot */ - dpp::cluster bot("token", dpp::i_default_intents | dpp::i_message_content); - - /* Message handler to look for a command called !embed */ - bot.on_message_create([&bot](const dpp::message_create_t & event) { - if (event.msg.content == "!embed") { - - /* create the embed */ - dpp::embed embed = dpp::embed(). - set_color(dpp::colors::sti_blue). - set_title("Some name"). - set_url("https://dpp.dev/"). - set_author("Some name", "https://dpp.dev/", "https://dpp.dev/DPP-Logo.png"). - set_description("Some description here"). - set_thumbnail("https://dpp.dev/DPP-Logo.png"). - add_field( - "Regular field title", - "Some value here" - ). - add_field( - "Inline field title", - "Some value here", - true - ). - add_field( - "Inline field title", - "Some value here", - true - ). - set_image("https://dpp.dev/DPP-Logo.png"). - set_footer(dpp::embed_footer().set_text("Some footer text here").set_icon("https://dpp.dev/DPP-Logo.png")). - set_timestamp(time(0)); - - /* reply with the created embed */ - bot.message_create(dpp::message(event.msg.channel_id, embed).set_reference(event.msg.id)); - } - }); - - bot.start(dpp::st_wait); - return 0; -} -~~~~~~~~~~ - -The code will send the following message. - -\image html embed.png diff --git a/vendor/DPP/docpages/example_programs/the_basics/firstbot.md b/vendor/DPP/docpages/example_programs/the_basics/firstbot.md deleted file mode 100644 index 97e2dca5..00000000 --- a/vendor/DPP/docpages/example_programs/the_basics/firstbot.md +++ /dev/null @@ -1,244 +0,0 @@ -\page firstbot Creating Your First Bot - -In this example we will create a C++ version of the [discord.js](https://discord.js.org/#/) example program. - -The two programs can be seen side by side below: - - - - - - - - - -
D++Discord.js -
- - -~~~~~~~~~~~~~~~{.cpp} -#include - -const std::string BOT_TOKEN = "add your token here"; - -int main() { - dpp::cluster bot(BOT_TOKEN); - - bot.on_log(dpp::utility::cout_logger()); - - bot.on_slashcommand([](const dpp::slashcommand_t& event) { - if (event.command.get_command_name() == "ping") { - event.reply("Pong!"); - } - }); - - bot.on_ready([&bot](const dpp::ready_t& event) { - if (dpp::run_once()) { - bot.global_command_create( - dpp::slashcommand("ping", "Ping pong!", bot.me.id) - ); - } - }); - - bot.start(dpp::st_wait); -} -~~~~~~~~~~~~~~~ - - - - - -~~~~~~~~~~~~~~~{.cpp} -let Discord = require('discord.js'); - - -let BOT_TOKEN = 'add your token here'; - - -let bot = new Discord.Client({ intents: [] }); - - -bot.on('interactionCreate', (interaction) => { - if (interaction.isCommand() && interaction.commandName === 'ping') { - interaction.reply({content: 'Pong!'}); - } -}); - - -bot.once('ready', async () => { - await client.commands.create({ - name: 'ping', - description: "Ping pong!" - }); -}); - - -bot.login(BOT_TOKEN);†-~~~~~~~~~~~~~~~ - - -
- -Let's break this program down step by step: - -### 1. Start with an empty C++ program - -Make sure to include the header file for the D++ library with the instruction \#include ``! - -~~~~~~~~~~~~~~{.cpp} -#include - -int main() { -} -~~~~~~~~~~~~~~ - -### 2. Create an instance of dpp::cluster - -To make use of the library you must create a dpp::cluster object. This object is the main object in your program like the `Discord.Client` object in Discord.js. - -You can instantiate this class as shown below. Remember to put your bot token in the constant! - -~~~~~~~~~~~~~~~{.cpp} -#include - -const std::string BOT_TOKEN = "add your token here"; - -int main() { - dpp::cluster bot(BOT_TOKEN); -} -~~~~~~~~~~~~~~~ - -### 3. Attach to an event - -To have a bot that does something, you should attach to some events. Let's start by attaching to the `on_ready` event (dpp::cluster::on_ready) which will notify your program when the bot is connected. In this event, we will register a slash -command called 'ping'. Note that we must wrap our registration of the command in a template called `dpp::run_once` which prevents it from being re-run -every time your bot does a full reconnection (e.g. if the connection fails). - -~~~~~~~~~~~~~~~~{.cpp} -#include - -const std::string BOT_TOKEN = "add your token here"; - -int main() { - dpp::cluster bot(BOT_TOKEN); - - bot.on_ready([&bot](const dpp::ready_t& event) { - if (dpp::run_once()) { - bot.global_command_create(dpp::slashcommand("ping", "Ping pong!", bot.me.id)); - } - }); -} -~~~~~~~~~~~~~~~~ - -### 4. Attach to another event to receive slash commands - -If you want to handle a slash command, you should also attach your program to the `on_slashcommand` event (dpp::cluster::on_slashcommand) which is basically the same as the Discord.js `interactionCreate` event. Lets add this to the program before the `on_ready` event: - -~~~~~~~~~~~~~~{.cpp} -#include - -const std::string BOT_TOKEN = "add your token here"; - -int main() { - dpp::cluster bot(BOT_TOKEN); - - bot.on_slashcommand([](const dpp::slashcommand_t& event) { - }); - - bot.on_ready([&bot](const dpp::ready_t& event) { - if (dpp::run_once()) { - bot.global_command_create(dpp::slashcommand("ping", "Ping pong!", bot.me.id)); - } - }); -} -~~~~~~~~~~~~~~ - -### 5 . Add some content to the events - -Attaching to an event is a good start, but to make a bot you should actually put some program code into the interaction event. We will add some code to the `on_slashcommand` to look for our slash command '/ping' and reply with `Pong!`: - -~~~~~~~~~~~~~~~~~~~~~~~{.cpp} -#include - -const std::string BOT_TOKEN = "add your token here"; - -int main() { - dpp::cluster bot(BOT_TOKEN); - - bot.on_slashcommand([](const dpp::slashcommand_t& event) { - if (event.command.get_command_name() == "ping") { - event.reply("Pong!"); - } - }); - - bot.on_ready([&bot](const dpp::ready_t& event) { - if (dpp::run_once()) { - bot.global_command_create(dpp::slashcommand("ping", "Ping pong!", bot.me.id)); - } - }); - -} -~~~~~~~~~~~~~~~~~~~~~~~ - -Let's break down the code in the `on_slashcommand` event so that we can discuss what it is doing: - -~~~~~~~~~~~~~~~~~~~~~~~{.cpp} - bot.on_slashcommand([](const dpp::slashcommand_t& event) { - if (event.command.get_command_name() == "ping") { - event.reply("Pong!"); - } - }); -~~~~~~~~~~~~~~~~~~~~~~~ - -This code is simply comparing the command name `event.command.get_command_name()` (dpp::interaction::get_command_name) against the value in a constant string value `"ping"`. If they match, then the `event.reply` method is called. - -The `event.reply` function (dpp::slashcommand_t::reply) replies to a slash command with a message. There are many ways to call this function to send embed messages, upload files, and more, but for this simple demonstration we will just send some message text. - -### 6. Add code to start the bot! - -To make the bot start, we must call the cluster::start method, e.g. in our program by using `bot.start(dpp::st_wait)`. - -We also add a line to tell the library to output all its log information to the console, `bot.on_log(dpp::utility::cout_logger());` - if you wanted to do something more advanced, you can replace this parameter with a lambda just like all other events. - -The parameter which we set to false indicates if the function should return once all shards are created. Passing `false` here tells the program you do not need to do anything once `bot.start` is called. - -~~~~~~~~~~~~~~{.cpp} -#include - -const std::string BOT_TOKEN = "add your token here"; - -int main() { - dpp::cluster bot(BOT_TOKEN); - - bot.on_log(dpp::utility::cout_logger()); - - bot.on_slashcommand([](const dpp::slashcommand_t& event) { - if (event.command.get_command_name() == "ping") { - event.reply("Pong!"); - } - }); - - bot.on_ready([&bot](const dpp::ready_t& event) { - if (dpp::run_once()) { - bot.global_command_create(dpp::slashcommand("ping", "Ping pong!", bot.me.id)); - } - }); - - bot.start(dpp::st_wait); -} -~~~~~~~~~~~~~~ - -### 7. Compile and run your bot - -Compile your bot using `g++ -std=c++17 -o bot bot.cpp -ldpp` (if your .cpp file is called `bot.cpp`) and run it with `./bot`. - -### 8. Inviting your bot to your server - -When you invite your bot, you must use the `applications.commands` and `bots` scopes to ensure your bot can create guild slash commands. For example: - -`https://discord.com/oauth2/authorize?client_id=YOUR-BOTS-ID-HERE&scope=bot+applications.commands&permissions=BOT-PERMISSIONS-HERE` - -Replace `YOUR-BOTS-ID-HERE` with your bot's user ID, and `BOT-PERMISSIONS-HERE` with the permissions your bot requires. - -**Congratulations** - you now have a working bot written using the D++ library! - diff --git a/vendor/DPP/docpages/example_programs/the_basics/webhooks.md b/vendor/DPP/docpages/example_programs/the_basics/webhooks.md deleted file mode 100644 index 02e1c8c4..00000000 --- a/vendor/DPP/docpages/example_programs/the_basics/webhooks.md +++ /dev/null @@ -1,26 +0,0 @@ -\page webhooks Webhooks - -Webhooks are a simple way to post messages from other apps and websites into Discord. They allow getting automated messages and data updates sent to a text channel in your server. [Read more](https://support.discord.com/hc/en-us/articles/228383668) in this article about Webhooks. - -The following code shows how to send messages in a channel using a webhook. - -~~~~~~~~~~{.cpp} -#include - -int main() -{ - dpp::cluster bot(""); // normally, you put your bot token in here. But to just run a webhook its not required - - bot.on_log(dpp::utility::cout_logger()); - - /* construct a webhook object using the URL you got from Discord */ - dpp::webhook wh("https://discord.com/api/webhooks/833047646548133537/ntCHEYYIoHSLy_GOxPx6pmM0sUoLbP101ct-WI6F-S4beAV2vaIcl_Id5loAMyQwxqhE"); - - /* send a message with this webhook */ - bot.execute_webhook_sync(wh, dpp::message("Have a great time here :smile:")); - - return 0; -} -~~~~~~~~~~ - -The above is just a very simple example. You can also send embed messages. All you have to do is to add an embed to the message you want to send. If you want to, you can also send it into a thread. diff --git a/vendor/DPP/docpages/footer.html b/vendor/DPP/docpages/footer.html deleted file mode 100644 index bcd33470..00000000 --- a/vendor/DPP/docpages/footer.html +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/vendor/DPP/docpages/footer.template.html b/vendor/DPP/docpages/footer.template.html deleted file mode 100644 index ded134a1..00000000 --- a/vendor/DPP/docpages/footer.template.html +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - -
- - ###PREV### -
- - diff --git a/vendor/DPP/docpages/header.html b/vendor/DPP/docpages/header.html deleted file mode 100644 index 6da47a56..00000000 --- a/vendor/DPP/docpages/header.html +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - - - -$title - D++ - The lightweight C++ Discord API Library -$title - D++ - The lightweight C++ Discord API Library - - - -$treeview -$search -$mathjax - -$extrastylesheet - - -
- - -
- - - - - - - - - - - - - - - - - - - - - -
-
$projectname -  $projectnumber -
-
$projectbrief
-
-
$projectbrief
-
$searchbox
-
- - - diff --git a/vendor/DPP/docpages/header.template.html b/vendor/DPP/docpages/header.template.html deleted file mode 100644 index 548b77b7..00000000 --- a/vendor/DPP/docpages/header.template.html +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - - - -$title - D++ - The lightweight C++ Discord API Library -$title - D++ - The lightweight C++ Discord API Library - - - -$treeview -$search -$mathjax - -$extrastylesheet - - -
- - -
- - - - - - - - - - - - - - - - - - - - - -
-
$projectname -  $projectnumber -
-
$projectbrief
-
-
$projectbrief
-
$searchbox
-
- - - diff --git a/vendor/DPP/docpages/images/DPP-Logo.png b/vendor/DPP/docpages/images/DPP-Logo.png deleted file mode 100644 index ac9fbd2c..00000000 Binary files a/vendor/DPP/docpages/images/DPP-Logo.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/DPP-Small.png b/vendor/DPP/docpages/images/DPP-Small.png deleted file mode 100644 index 6c6006e6..00000000 Binary files a/vendor/DPP/docpages/images/DPP-Small.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/DPP_Architecture.dia b/vendor/DPP/docpages/images/DPP_Architecture.dia deleted file mode 100644 index ff95980a..00000000 Binary files a/vendor/DPP/docpages/images/DPP_Architecture.dia and /dev/null differ diff --git a/vendor/DPP/docpages/images/DPP_Architecture.svg b/vendor/DPP/docpages/images/DPP_Architecture.svg deleted file mode 100644 index 73b47cdf..00000000 --- a/vendor/DPP/docpages/images/DPP_Architecture.svg +++ /dev/null @@ -1,499 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vendor/DPP/docpages/images/build-clion-project-structure.png b/vendor/DPP/docpages/images/build-clion-project-structure.png deleted file mode 100644 index 3bc0d5cb..00000000 Binary files a/vendor/DPP/docpages/images/build-clion-project-structure.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/build-clion-reload-cmake-project.png b/vendor/DPP/docpages/images/build-clion-reload-cmake-project.png deleted file mode 100644 index b925f43e..00000000 Binary files a/vendor/DPP/docpages/images/build-clion-reload-cmake-project.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/button.png b/vendor/DPP/docpages/images/button.png deleted file mode 100644 index ad1a40d9..00000000 Binary files a/vendor/DPP/docpages/images/button.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/button_2.png b/vendor/DPP/docpages/images/button_2.png deleted file mode 100644 index 39472011..00000000 Binary files a/vendor/DPP/docpages/images/button_2.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/code_editor.png b/vendor/DPP/docpages/images/code_editor.png deleted file mode 100644 index 3e45a52e..00000000 Binary files a/vendor/DPP/docpages/images/code_editor.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/context_menu_user_command.png b/vendor/DPP/docpages/images/context_menu_user_command.png deleted file mode 100644 index 31b0d933..00000000 Binary files a/vendor/DPP/docpages/images/context_menu_user_command.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/context_menu_user_command_showcase.png b/vendor/DPP/docpages/images/context_menu_user_command_showcase.png deleted file mode 100644 index 9d212759..00000000 Binary files a/vendor/DPP/docpages/images/context_menu_user_command_showcase.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/cprog.png b/vendor/DPP/docpages/images/cprog.png deleted file mode 100644 index 83aaf616..00000000 Binary files a/vendor/DPP/docpages/images/cprog.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/create_application_add_bot.png b/vendor/DPP/docpages/images/create_application_add_bot.png deleted file mode 100644 index fdf5f31a..00000000 Binary files a/vendor/DPP/docpages/images/create_application_add_bot.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/create_application_bot_overview.png b/vendor/DPP/docpages/images/create_application_bot_overview.png deleted file mode 100644 index 2ebf6708..00000000 Binary files a/vendor/DPP/docpages/images/create_application_bot_overview.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/create_application_confirm_popup.png b/vendor/DPP/docpages/images/create_application_confirm_popup.png deleted file mode 100644 index ac2c736f..00000000 Binary files a/vendor/DPP/docpages/images/create_application_confirm_popup.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/create_application_navigate_to_url_generator.png b/vendor/DPP/docpages/images/create_application_navigate_to_url_generator.png deleted file mode 100644 index db0b5493..00000000 Binary files a/vendor/DPP/docpages/images/create_application_navigate_to_url_generator.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/delorean-time-travel.gif b/vendor/DPP/docpages/images/delorean-time-travel.gif deleted file mode 100644 index 33bccf56..00000000 Binary files a/vendor/DPP/docpages/images/delorean-time-travel.gif and /dev/null differ diff --git a/vendor/DPP/docpages/images/embed.png b/vendor/DPP/docpages/images/embed.png deleted file mode 100644 index a8a6e334..00000000 Binary files a/vendor/DPP/docpages/images/embed.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/eval_example.png b/vendor/DPP/docpages/images/eval_example.png deleted file mode 100644 index 45704dc3..00000000 Binary files a/vendor/DPP/docpages/images/eval_example.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/jsprog.png b/vendor/DPP/docpages/images/jsprog.png deleted file mode 100644 index dfe6a8cc..00000000 Binary files a/vendor/DPP/docpages/images/jsprog.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/modal_dialog.png b/vendor/DPP/docpages/images/modal_dialog.png deleted file mode 100644 index bded5027..00000000 Binary files a/vendor/DPP/docpages/images/modal_dialog.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/progs.png b/vendor/DPP/docpages/images/progs.png deleted file mode 100644 index 0031f5d6..00000000 Binary files a/vendor/DPP/docpages/images/progs.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/repl.png b/vendor/DPP/docpages/images/repl.png deleted file mode 100644 index 622f38bb..00000000 Binary files a/vendor/DPP/docpages/images/repl.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/runbot.png b/vendor/DPP/docpages/images/runbot.png deleted file mode 100644 index 98a37da6..00000000 Binary files a/vendor/DPP/docpages/images/runbot.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/uptimerobot.png b/vendor/DPP/docpages/images/uptimerobot.png deleted file mode 100644 index e3dcb7b1..00000000 Binary files a/vendor/DPP/docpages/images/uptimerobot.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/vcpkg.png b/vendor/DPP/docpages/images/vcpkg.png deleted file mode 100644 index f3928581..00000000 Binary files a/vendor/DPP/docpages/images/vcpkg.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/vsproj_1.png b/vendor/DPP/docpages/images/vsproj_1.png deleted file mode 100644 index 3616125b..00000000 Binary files a/vendor/DPP/docpages/images/vsproj_1.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/vsproj_10.png b/vendor/DPP/docpages/images/vsproj_10.png deleted file mode 100644 index 0db504bd..00000000 Binary files a/vendor/DPP/docpages/images/vsproj_10.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/vsproj_11.png b/vendor/DPP/docpages/images/vsproj_11.png deleted file mode 100644 index 8c513c51..00000000 Binary files a/vendor/DPP/docpages/images/vsproj_11.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/vsproj_12.png b/vendor/DPP/docpages/images/vsproj_12.png deleted file mode 100644 index 01d6fbc3..00000000 Binary files a/vendor/DPP/docpages/images/vsproj_12.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/vsproj_13.png b/vendor/DPP/docpages/images/vsproj_13.png deleted file mode 100644 index 54d9682f..00000000 Binary files a/vendor/DPP/docpages/images/vsproj_13.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/vsproj_14.png b/vendor/DPP/docpages/images/vsproj_14.png deleted file mode 100644 index a01b72f7..00000000 Binary files a/vendor/DPP/docpages/images/vsproj_14.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/vsproj_2.png b/vendor/DPP/docpages/images/vsproj_2.png deleted file mode 100644 index 2b0603ed..00000000 Binary files a/vendor/DPP/docpages/images/vsproj_2.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/vsproj_3.png b/vendor/DPP/docpages/images/vsproj_3.png deleted file mode 100644 index eaeb1d5b..00000000 Binary files a/vendor/DPP/docpages/images/vsproj_3.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/vsproj_4.png b/vendor/DPP/docpages/images/vsproj_4.png deleted file mode 100644 index 9888d29e..00000000 Binary files a/vendor/DPP/docpages/images/vsproj_4.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/vsproj_5.png b/vendor/DPP/docpages/images/vsproj_5.png deleted file mode 100644 index 6a4db9ab..00000000 Binary files a/vendor/DPP/docpages/images/vsproj_5.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/vsproj_6.png b/vendor/DPP/docpages/images/vsproj_6.png deleted file mode 100644 index da793836..00000000 Binary files a/vendor/DPP/docpages/images/vsproj_6.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/vsproj_7.png b/vendor/DPP/docpages/images/vsproj_7.png deleted file mode 100644 index 66c08b89..00000000 Binary files a/vendor/DPP/docpages/images/vsproj_7.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/vsproj_8.png b/vendor/DPP/docpages/images/vsproj_8.png deleted file mode 100644 index a373c7d9..00000000 Binary files a/vendor/DPP/docpages/images/vsproj_8.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/vsproj_9.png b/vendor/DPP/docpages/images/vsproj_9.png deleted file mode 100644 index 2a5f78b7..00000000 Binary files a/vendor/DPP/docpages/images/vsproj_9.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/winbuild_0.png b/vendor/DPP/docpages/images/winbuild_0.png deleted file mode 100644 index a3e55ce3..00000000 Binary files a/vendor/DPP/docpages/images/winbuild_0.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/winbuild_1.png b/vendor/DPP/docpages/images/winbuild_1.png deleted file mode 100644 index 7180f425..00000000 Binary files a/vendor/DPP/docpages/images/winbuild_1.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/winbuild_2.png b/vendor/DPP/docpages/images/winbuild_2.png deleted file mode 100644 index dced2c3a..00000000 Binary files a/vendor/DPP/docpages/images/winbuild_2.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/winbuild_3.png b/vendor/DPP/docpages/images/winbuild_3.png deleted file mode 100644 index c10d716f..00000000 Binary files a/vendor/DPP/docpages/images/winbuild_3.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/winbuild_4.png b/vendor/DPP/docpages/images/winbuild_4.png deleted file mode 100644 index b66bfe81..00000000 Binary files a/vendor/DPP/docpages/images/winbuild_4.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/zip_vsproj_1.png b/vendor/DPP/docpages/images/zip_vsproj_1.png deleted file mode 100644 index f19a17af..00000000 Binary files a/vendor/DPP/docpages/images/zip_vsproj_1.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/zip_vsproj_10.png b/vendor/DPP/docpages/images/zip_vsproj_10.png deleted file mode 100644 index 0db504bd..00000000 Binary files a/vendor/DPP/docpages/images/zip_vsproj_10.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/zip_vsproj_11.png b/vendor/DPP/docpages/images/zip_vsproj_11.png deleted file mode 100644 index 8c513c51..00000000 Binary files a/vendor/DPP/docpages/images/zip_vsproj_11.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/zip_vsproj_12.png b/vendor/DPP/docpages/images/zip_vsproj_12.png deleted file mode 100644 index ad7f87ee..00000000 Binary files a/vendor/DPP/docpages/images/zip_vsproj_12.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/zip_vsproj_13.png b/vendor/DPP/docpages/images/zip_vsproj_13.png deleted file mode 100644 index 54d9682f..00000000 Binary files a/vendor/DPP/docpages/images/zip_vsproj_13.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/zip_vsproj_14.png b/vendor/DPP/docpages/images/zip_vsproj_14.png deleted file mode 100644 index a01b72f7..00000000 Binary files a/vendor/DPP/docpages/images/zip_vsproj_14.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/zip_vsproj_2.png b/vendor/DPP/docpages/images/zip_vsproj_2.png deleted file mode 100644 index aac43728..00000000 Binary files a/vendor/DPP/docpages/images/zip_vsproj_2.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/zip_vsproj_3.png b/vendor/DPP/docpages/images/zip_vsproj_3.png deleted file mode 100644 index ffc73f0d..00000000 Binary files a/vendor/DPP/docpages/images/zip_vsproj_3.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/zip_vsproj_4.png b/vendor/DPP/docpages/images/zip_vsproj_4.png deleted file mode 100644 index 9888d29e..00000000 Binary files a/vendor/DPP/docpages/images/zip_vsproj_4.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/zip_vsproj_5.png b/vendor/DPP/docpages/images/zip_vsproj_5.png deleted file mode 100644 index 6a4db9ab..00000000 Binary files a/vendor/DPP/docpages/images/zip_vsproj_5.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/zip_vsproj_6.png b/vendor/DPP/docpages/images/zip_vsproj_6.png deleted file mode 100644 index da793836..00000000 Binary files a/vendor/DPP/docpages/images/zip_vsproj_6.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/zip_vsproj_7.png b/vendor/DPP/docpages/images/zip_vsproj_7.png deleted file mode 100644 index 66c08b89..00000000 Binary files a/vendor/DPP/docpages/images/zip_vsproj_7.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/zip_vsproj_8.png b/vendor/DPP/docpages/images/zip_vsproj_8.png deleted file mode 100644 index a373c7d9..00000000 Binary files a/vendor/DPP/docpages/images/zip_vsproj_8.png and /dev/null differ diff --git a/vendor/DPP/docpages/images/zip_vsproj_9.png b/vendor/DPP/docpages/images/zip_vsproj_9.png deleted file mode 100644 index 2a5f78b7..00000000 Binary files a/vendor/DPP/docpages/images/zip_vsproj_9.png and /dev/null differ diff --git a/vendor/DPP/docpages/install/install-arch-aur.md b/vendor/DPP/docpages/install/install-arch-aur.md deleted file mode 100644 index 8450dc8a..00000000 --- a/vendor/DPP/docpages/install/install-arch-aur.md +++ /dev/null @@ -1,28 +0,0 @@ -\page install-arch-aur Installing from AUR (Arch Linux) - -To install [D++ from AUR](https://aur.archlinux.org/packages/dpp), follow the steps below (as root): - -``` -git clone https://aur.archlinux.org/dpp.git -cd dpp -makepkg -si -``` - -or use your favorite package manager: - -```sh -# example with `yay` (without root) -yay -Sy dpp -``` - -This will do the following three things: - -- Clone the D++ AUR repository to a directory called `dpp` -- Change into the directory `dpp` -- Make a pacman package from the AUR repository for D++, and install it - -You will now be able to use D++ by including its library on the command line: - -``` -g++ mybot.cpp -o mybot -ldpp -``` diff --git a/vendor/DPP/docpages/install/install-linux-deb.md b/vendor/DPP/docpages/install/install-linux-deb.md deleted file mode 100644 index d0ddb79a..00000000 --- a/vendor/DPP/docpages/install/install-linux-deb.md +++ /dev/null @@ -1,21 +0,0 @@ -\page install-linux-deb Installing from a .deb file (Debian, Ubuntu, Derivatives) - -To install D++ on a system from .deb using dpkg (as root): - -``` -apt install wget -wget -O dpp.deb https://dl.dpp.dev/ -dpkg -i dpp.deb -``` - -This will do the following three things: - -- Install `wget` -- Use `wget` to download the latest release of D++ to `dpp.deb` -- Install `dpp.deb` to /usr - -You will now be able to use D++ by including its library on the command line: - -``` -g++ mybot.cpp -o mybot -ldpp -``` \ No newline at end of file diff --git a/vendor/DPP/docpages/install/install-linux-rpm.md b/vendor/DPP/docpages/install/install-linux-rpm.md deleted file mode 100644 index e42e40e2..00000000 --- a/vendor/DPP/docpages/install/install-linux-rpm.md +++ /dev/null @@ -1,21 +0,0 @@ -\page install-linux-rpm Installing from a .rpm file (RedHat, CentOS and derivatives) - -To install D++ on a system from .rpm using `yum` (as root): - -``` -yum install wget -wget -O dpp.rpm https://dl.dpp.dev/latest/linux-x64/rpm -yum localinstall dpp.rpm -``` - -This will do the following three things: - -- Install `wget` -- Use `wget` to download the latest release of D++ to `dpp.rpm` -- Install `dpp.rpm` to /usr - -You will now be able to use D++ by including its library on the command line: - -``` -g++ mybot.cpp -o mybot -ldpp -``` \ No newline at end of file diff --git a/vendor/DPP/docpages/install/install-vcpkg.md b/vendor/DPP/docpages/install/install-vcpkg.md deleted file mode 100644 index 2faf3347..00000000 --- a/vendor/DPP/docpages/install/install-vcpkg.md +++ /dev/null @@ -1,19 +0,0 @@ -\page install-vcpkg Installing from VCPKG (Windows, Linux, OSX) - -To install D++ on a system from VCPKG: - -- Ensure VCPKG is correctly installed, and run `vcpkg integrate install` to integrate it with your preferred IDE. This has been reported to work with Visual Studio, vscode, and JetBrains CLion. -- From a command line, type `vcpkg install dpp:x64-windows` (replace `x64-windows` with whichever OS and architecture you want the library to be built for) -\image html vcpkg.png -- VCPKG will install the library and dependencies for you! Once completed you will receive a message indicating success: - -- Use `vcpkg list dpp` to check that the package is installed: -``` -c:\vcpkg>vcpkg list dpp -dpp:x64-windows 10.0.15 D++ Extremely Lightweight C++ Discord Library. -``` -- You may now use the library within a `cmake` based project by adding instructions such as these to your `CmakeLists.txt`: -```cmake - find_package(dpp CONFIG REQUIRED) - target_link_libraries(your_target_name PRIVATE dpp::dpp) -``` diff --git a/vendor/DPP/docpages/install/install-windows-zip.md b/vendor/DPP/docpages/install/install-windows-zip.md deleted file mode 100644 index ad36a3ec..00000000 --- a/vendor/DPP/docpages/install/install-windows-zip.md +++ /dev/null @@ -1,44 +0,0 @@ -\page install-windows-zip Installing from zip (Windows) - -To add D++ to a Visual Studio project, using **Visual Studio 2019** or **Visual Studio 2022**, follow the steps below. The steps below assume an empty project, if you are adding to an existing project simply skip steps 1 through 4, and step 13. - -\note It is possible to skip this entire tutorial, and obtain a [pre-made visual studio template containing the latest D++ build (for 32 and 64 bit, release and debug profiles) by clicking here](https://github.com/brainboxdotcc/windows-bot-template/). - -1. Make sure you have Visual Studio 2019 or 2022. Community, Professional or Enterprise work fine. These instructions are not for Visual Studio Code. You can [download the correct version here](https://visualstudio.microsoft.com/downloads/). Note that older versions of Visual Studio will not work as they do not support enough of the C++17 standard. -2. Start visual studio and choose to create a new project - \image html zip_vsproj_1.png -3. Choose the project type "Console Project" and click next - \image html zip_vsproj_2.png -4. Name your bot project. In this example i just chose the name 'MyBot'. You can have any name you like here. - \image html zip_vsproj_3.png -5. Open the zip file you downloaded which contains the D++ dlls, include files and lib file. Drag and drop this to your local machine and make note of where you placed it. This location will be important in later steps. - \image html zip_vsproj_4.png -6. Back inside visual studio, right click on the project (not solution!) in the tree in your visual studio window. choose 'Properties'. - \image html zip_vsproj_5.png -7. The next step is to populate the include directories and library directories sections with the paths to the D++ library and include files. The next steps will guide you through how to do this. - \image html zip_vsproj_6.png -8. Click 'edit' when prompted to edit the include paths section. Add the path to the include folder you extracted to your machine, which we took note of earlier. Note that it is important to add the dpp-10.0 folder, not any other folder, to this list: - \image html zip_vsproj_7.png -9. Going back to the previous window, now edit the library paths. Again click 'edit' when prompted to edit the library paths section. Add the path to the library folder you extracted to your machine, which we took note of earlier. Note that once more it is important to add the dpp-9.0 folder within it, not any other folder, to this list. Also be aware this is a **different folder** than the one you just added for includes! - \image html zip_vsproj_8.png -10. Double check at this point that all the directories are filled in correctly. They should look generally like the ones in the screenshot below: - \image html zip_vsproj_9.png -11. Go to the general section in the same window now, and look for the drop down list laballed "C++ Language Standard". Make sure the selected option is **C++17 Standard (/std:c++17)** - \image html zip_vsproj_10.png -12. Again within the same window, go to the input section, under the linker category, and add '**dpp.lib;**' to the start of the libraries to include, as shown below: - \image html zip_vsproj_11.png -13. Now you can paste some code into the editor, completely replacing the 'hello world' application that visual studio made for you. The example code here is the basic bot from the first example on this site. You should at this point also double check that the architecture you have selected (in this case x86) matches the version of the dll/lib files you downloaded from the website. This is **important** as if you mismatch them the compilation will just fail. - \image html zip_vsproj_12.png -14. Go to the build menu and choose Build Solution (A handy shortcut for this is to just press **F7**): - \image html zip_vsproj_13.png -15. Observe the build output. There may be warnings, but so long as the build output ends with "1 succeeded" then the process has worked. You may now run your bot! - \image html zip_vsproj_14.png - -## Troubleshooting - -- If you get an error that a dll is missing (e.g. `dpp.dll` or `opus.dll`) when starting your bot, then simply copy all dlls from the **bin** directory of where you extracted the DPP zip file to, into the same directory where your bot's executable is. You only need to do this once. There should be several of these dll files: `dpp.dll`, `zlib.dll`, `openssl.dll` and `libcrypto.dll` (or similarly named SSL related files), `libsodium.dll` and `opus.dll`. -- Please note that if you change the architecture (step 13) you must reconfigure all of steps 7 through 12 again as these configurations are specific to each architecture. This is to allow for different sets of precompiled libs, e.g. for `x86`, `x64`, etc. -- You should run your bot from a command prompt. If you do not, and it exits, you will not be able to see any output as the window will immediately close. -- If you need to update the `opus.dll` or `zlib.dll` (or any other prebuilt dll) these can be obtained by requesting them to be installed via `vcpkg` then copying the dlls, libraries and headers from the vcpkg `install` folder. -- Stuck? You can find us on the [official discord server](https://discord.gg/dpp) - ask away! We don't bite! - diff --git a/vendor/DPP/docpages/install/install-xmake.md b/vendor/DPP/docpages/install/install-xmake.md deleted file mode 100644 index d46091cc..00000000 --- a/vendor/DPP/docpages/install/install-xmake.md +++ /dev/null @@ -1,21 +0,0 @@ -\page install-xmake Installing from xmake - -To install D++ on a project from XMake: - -- Ensure XMake [is correctly installed](https://xmake.io/#/guide/installation) -- Create a new xmake project if you haven't already one, using `xmake init ` -- Update the `xmake.lua` file by adding the `dpp` package, below the minimum configuration: - -~~~~~~~~~~~{.cmake} -add_rules("mode.debug", "mode.release") - -add_requires("dpp") - -target("test-bot") - set_kind("binary") - add_files("src/*.cpp") - - add_packages("dpp") -~~~~~~~~~~~ - -- Finally, run `xmake build` to download dependencies and build the project \ No newline at end of file diff --git a/vendor/DPP/docpages/make_a_bot/clion.md b/vendor/DPP/docpages/make_a_bot/clion.md deleted file mode 100644 index 3d5ceead..00000000 --- a/vendor/DPP/docpages/make_a_bot/clion.md +++ /dev/null @@ -1,115 +0,0 @@ -\page build-a-discord-bot-linux-clion Building a discord bot in Linux using CLion - -This tutorial teaches you how to create a _working skeleton project you can build upon_, using the JetBrains-IDE **[CLion](https://www.jetbrains.com/clion/)**. - -\note This tutorial will use **Ubuntu**! You might use other Distros if you prefer, but keep in mind the setup process might be different! - -Make sure you have CLion installed and works fine (run a _hello-world program_). You can [download CLion here](https://www.jetbrains.com/de-de/clion/download/). - -## Setup a project - -Create a new project. Select C++17 as the Language standard, or C++20 if you want something more recent. - -We'll use the following file structure as a _skeleton project you can build upon_: - - - your_project/ - |-- libs/ - |-- src/ - |-- main.cpp - |-- CMakeLists.txt - - -Create the directories in your project and move the by CLion generated _hello-world main.cpp_ in the `src/` directory. - -In the `libs/` directory, clone D++ with: `git clone https://github.com/brainboxdotcc/DPP.git`. You can also clone [spdlog](https://github.com/gabime/spdlog) into it if you need a logger. - -Your project directory should look like this: - -\image html build-clion-project-structure.png - -### Configure CMake file - -Paste this CMake configuration in the `CMakeLists.txt` and adapt it according to your needs: - -~~~~~~~~~~~~~~{.cmake} -# minimum CMake version required -cmake_minimum_required(VERSION 3.15) -# Project name, version and description -project(discord-bot VERSION 1.0 DESCRIPTION "A discord bot") - -# Add DPP as dependency -add_subdirectory(libs/DPP) -add_subdirectory(libs/spdlog) # if you need a logger. Don't forget to clone sources - # in the `libs/` directory - -# Create an executable -add_executable(${PROJECT_NAME} - src/main.cpp - # your others files... -) - -# Linking libraries -target_link_libraries(${PROJECT_NAME} - dpp - spdlog # Like before, if you need spdlog -) - -# Specify includes -target_include_directories(${PROJECT_NAME} PRIVATE - libs/DPP/include - libs/spdlog/include # Like before, if you need spdlog -) - -# Set C++ version -set_target_properties(${PROJECT_NAME} PROPERTIES - CXX_STANDARD 17 # or 20 if you want something more recent - CXX_STANDARD_REQUIRED ON -) -~~~~~~~~~~~~~~ - -Then open the "File" menu and click on "Reload CMake Project" to reload the CMake configuration. - -\image html build-clion-reload-cmake-project.png - -### Add an example program - -The next step is to write the bot. Copy and paste the following [example program](https://dpp.dev/firstbot.html) in the `main.cpp` and set your bot token (see [Creating a Bot Token](https://dpp.dev/creating-a-bot-application.html)): - - -~~~~~~~~~~~~~~~{.cpp} -#include - -const std::string BOT_TOKEN = "add your token here"; - -int main() { - dpp::cluster bot(BOT_TOKEN); - - bot.on_log(dpp::utility::cout_logger()); - - bot.on_slashcommand([](const dpp::slashcommand_t& event) { - if (event.command.get_command_name() == "ping") { - event.reply("Pong!"); - } - }); - - bot.on_ready([&bot](const dpp::ready_t& event) { - if (dpp::run_once()) { - bot.global_command_create( - dpp::slashcommand("ping", "Ping pong!", bot.me.id) - ); - } - }); - - bot.start(dpp::st_wait); -} -~~~~~~~~~~~~~~~ - - -Hit the green "Run" button in the top-right to run the bot. - -**Congratulations, you've successfully set up a bot!** - -## Troubleshooting - -- Stuck? You can find us on the [official discord server](https://discord.gg/dpp) - ask away! We don't bite! - diff --git a/vendor/DPP/docpages/make_a_bot/cmake.md b/vendor/DPP/docpages/make_a_bot/cmake.md deleted file mode 100644 index 127fd448..00000000 --- a/vendor/DPP/docpages/make_a_bot/cmake.md +++ /dev/null @@ -1,74 +0,0 @@ -\page buildcmake Building a Discord Bot using CMake/UNIX - -## 1. Toolchain -Before compiling, you will need to install `cmake` on your system. -To be sure that `cmake` is installed, you can type the following command: - - $ cmake --version - cmake version 3.20.4 - - -## 2. Create a CMake project - -In an empty directory, create the following files and directories: - - - your_project/ - |-- libs/ - |-- src/ - |-- main.cpp - |-- CMakeLists.txt - - -In the `libs/` directory, clone D++ with: `git clone https://github.com/brainboxdotcc/DPP.git` - -## 3. Configure CMake - -Here is an example CMake configuration, adapt it according to your needs: - -~~~~~~~~~~~~~~{.cmake} -# minimum CMake version required -cmake_minimum_required(VERSION 3.15) -# Project name, version and description -project(discord-bot VERSION 1.0 DESCRIPTION "A discord bot") - -# Add DPP as dependency -add_subdirectory(libs/DPP) -# You can also add any other libs you want to use - -# Create an executable -add_executable(${PROJECT_NAME} - src/main.cpp - # your other files... -) - -# Linking libraries -target_link_libraries(${PROJECT_NAME} - dpp - # Add any other libs you want to use here -) - -# Specify includes -target_include_directories(${PROJECT_NAME} PRIVATE - libs/DPP/include - # Remember to add the include directories of any other libraries too -) - -# Set C++ version -set_target_properties(${PROJECT_NAME} PROPERTIES - CXX_STANDARD 17 - CXX_STANDARD_REQUIRED ON -) -~~~~~~~~~~~~~~ - -Your project directory should look like this: - - - your_project/ - |-- libs/ - |-- DPP - |-- src/ - |-- main.cpp - |-- CMakeLists.txt - - -**Have fun!** - diff --git a/vendor/DPP/docpages/make_a_bot/meson.md b/vendor/DPP/docpages/make_a_bot/meson.md deleted file mode 100644 index ff4a7d85..00000000 --- a/vendor/DPP/docpages/make_a_bot/meson.md +++ /dev/null @@ -1,57 +0,0 @@ -\page buildmeson Build a Discord Bot using Meson - -## 1. Toolchain - -Before compiling, you will need to install `meson` on your system. -To be sure that `meson` is installed, you can type the following command: - - $ meson --version - 0.63.2 - -## 2. Create a Meson project - -In an empty directory. - - - your project/ - -run the command - - $ meson init -l cpp - -## 3. Configuring your Meson project - -add the following line after the project() line in your meson.build file. - - dpp = dependency('dpp') - -add the following line in the executable section of your meson.build file. - - dependencies: [dpp] - -change the cpp_std value in the project() to c++17 - -your meson.build should look like this. -~~~~~~~~~~~~~~ -project('discord-bot', 'cpp', - version : '0.1', - default_options : ['warning_level=3', - 'cpp_std=c++14']) - -dpp = dependency('dpp') - - -exe = executable('discord', 'discord_bot.cpp', - install : true, dependencies: [dpp]) - -test('basic', exe) - -~~~~~~~~~~~~~~ - -Meson automatically generates a cpp for your project. And a test suite. - -## 4. Building - -To build a meson project run - - $ meson setup builddir - $ meson compile -C builddir \ No newline at end of file diff --git a/vendor/DPP/docpages/make_a_bot/replit.md b/vendor/DPP/docpages/make_a_bot/replit.md deleted file mode 100644 index ae51a86b..00000000 --- a/vendor/DPP/docpages/make_a_bot/replit.md +++ /dev/null @@ -1,42 +0,0 @@ -\page building-a-cpp-discord-bot-in-repl Creating a Discord bot in Repl.it - -@note There is a premade repl, ready for use which was built using the steps above. If you wish to use this repl simply [visit this github repository](https://github.com/alanlichen/dpp-on-repl) and click the "Run on Replit" button. Then, follow the steps in the README file. - -To build a D++ bot in a repl.it instance, follow these steps. These steps are slightly more convoluted than installing D++ into a standard container as we don't have access to root in the conventional way or write access to any files outside of our home directory in a repl. This guide sidesteps the issue by locally extracting a libdpp deb file installer, and referencing the local dependencies from the command-line. - -1. Use wget, or the upload button, to get the precompiled x64 release into your repl as a file, e.g. `wget -O libdpp.deb https://dl.dpp.dev/latest` -2. Extract this deb file using `dpkg`: -``` -dpkg -x libdpp.deb . -``` -3. Compile your bot, note that you should be sure to include the `pthread` library explicitly and reference the extracted dpp installation you just put into the repl: -``` -g++ -o bot main.cpp -ldpp -lpthread -L./usr/lib -I./usr/include -std=c++17 -``` -4. Run your bot! Note that you will need to set `LD_PRELOAD` to reference `libdpp.so` as it will be located in `$HOME` and not `/usr/lib`: -``` -LD_PRELOAD=./usr/lib/libdpp.so ./bot -``` - -Now that your bot is running, you have to keep it online. Replit automatically puts repls to sleep after some time, so you will need to ping a webserver. Unfortunately, Replit is sometimes limiting, and this is one of the only free workarounds to this issue. - -1. Start a http server. This can be through any webserver, but as a simple solution, use python's built in http.server: -``` -python3 -m http.server -``` -2. Create an index.html file with anything inside it for the server to serve. -3. Go to [uptimerobot.com](https://uptimerobot.com/) and create an account if you dont have one. -4. After verifying your account, click "Add New Monitor". -+ For Monitor Type, select "HTTP(s)" -+ In Friendly Name, put the name of your bot -+ For your url, copy the url of the new website that repl is serving for you -+ Select any alert contacts you want, then click "Create Monitor" -Here is an example of a possible uptimerobot configuration: - -\image html uptimerobot.png - -## Troubleshooting - -If the bot fails to start and instead you receive an error message about being banned from the Discord API, there is little to be done about this. These bans are temporary but because repl.it is a shared platform, you share an IP address with many thousands of bots, some abusive and some badly written. This will happen often and is outside of the control of yourself and us. However, you can try to migitate this by typing `kill 1` in the shell. This is not guaranteed to work, and you might need to try it a few times. If it still does not work, then we recommend instead you obtain some affordable non-free hosting instead. - -If your bot continues to fall asleep even though you have a server, we advise you to double check that no errors are happening, and if the server is being pinged. If that still does not work, we again recommend you to obtain some affordable non-free hosting. diff --git a/vendor/DPP/docpages/make_a_bot/token.md b/vendor/DPP/docpages/make_a_bot/token.md deleted file mode 100644 index 9f60f408..00000000 --- a/vendor/DPP/docpages/make_a_bot/token.md +++ /dev/null @@ -1,45 +0,0 @@ -\page creating-a-bot-application Creating a Bot Token - -Before you start coding, you need to create and register your bot in the Discord developer portal. You can then add this bot to your Discord-server. - -## Creating a new bot - -To create a new application, take the steps as follows: - -1. Sign in to the [Discord developer portal](https://discord.com/developers/applications) and click on "New Application" on the top right. -2. Next, enter a name for the application in the pop-up and press the "Create" button. -\image html create_application_confirm_popup.png -In this example we named it "D++ Test Bot". -3. Move on by click the "Bot" tab in the left-hand side of the screen. Now click the "Add Bot" button on the right and confirm that you want to add the bot to your application. - -\image html create_application_add_bot.png - -On the resulting screen, you’ll note a page with information regarding your new bot. You can edit your bot name, description, and avatar here if you want to. If you wish to read the message content from messages, you need to enable the message content intent in the "Privileged Gateway Intents" section. - -\image html create_application_bot_overview.png - -In this panel, you can get your bot token by clicking "Reset Token". A bot token looks like this: `OTAyOTMxODU1NTU1MzE3ODUw.YXlm0g.9oYCt-XHXVH_z9qAytzmVRzKWTg` - -\warning **Do not share this token** with anybody! If you ever somehow compromise your current bot token or see your bot in danger, you can regenerate the token in the panel. - -## Adding the bot to your server - -Once you've created your bot in the discord developer portal, you may wonder: -> Where is my bot now, I can't see him on my server?! - -That's because you've created a bot application, but it's not on any server right now. So, to invite the bot to your server, you must create an invitation URL. - -1. go again into the [Applications page](https://discord.com/developers/applications) and click on your bot. -2. Go to the "OAuth2" tab and click on the subpage "URL Generator". -\image html create_application_navigate_to_url_generator.png -3. Select the `bot` scope. If your bot uses slash commands, also select `applications.commands`. You can read more about scopes and which you need for your application [here](https://discord.com/developers/docs/topics/oauth2#shared-resources-oauth2-scopes). -4. Choose the permissions required for your bot to function in the "Bot Permissions" section. -5. Copy and paste the resulting URL in your browser. Choose a server to invite the bot to, and click "Authorize". - - -\note For bots with elevated permissions, Discord enforces two-factor authentication on the bot owner's account when added to servers that have server-wide 2FA enabled. - -## Troubleshooting - -- Stuck? You can find us on the [official discord server](https://discord.gg/dpp) - ask away! We don't bite! - diff --git a/vendor/DPP/docpages/make_a_bot/windows_vs.md b/vendor/DPP/docpages/make_a_bot/windows_vs.md deleted file mode 100644 index 535c8477..00000000 --- a/vendor/DPP/docpages/make_a_bot/windows_vs.md +++ /dev/null @@ -1,20 +0,0 @@ -\page build-a-discord-bot-windows-visual-studio Building a discord bot in Windows using Visual Studio - -To create a basic bot using **Visual Studio 2019** or **Visual Studio 2022**, follow the steps below to create a *working skeleton project you can build upon*. - -1. Make sure you have Visual Studio 2019 or 2022. Community, Professional or Enterprise work fine. These instructions are not for Visual Studio Code. You can [download the correct version here](https://visualstudio.microsoft.com/downloads/). Note that older versions of Visual Studio will not work as they do not support enough of the C++17 standard. -2. Clone the [template project](https://github.com/brainboxdotcc/windows-bot-template/). Be sure to clone the entire project and not just copy and paste the cpp file. -3. Double click on the MyBot.sln file in the folder you just cloned - \image html vsproj_1.png -4. Add your bot token (see \ref creating-a-bot-application) and guild ID to the example program - \image html vsproj_2.png -5. Click "Local windows debugger" to compile and run your bot! - \image html vsproj_3.png -6. Observe the build output. There may be warnings, but so long as the build output ends with "1 succeeded" then the process has worked. You may now run your bot! - \image html vsproj_14.png - -## Troubleshooting - -- If you get an error that a dll is missing (e.g. `dpp.dll` or `opus.dll`) when starting your bot, then simply copy all dlls from the **bin** directory of where you extracted the DPP zip file to, into the same directory where your bot's executable is. You only need to do this once. There should be several of these dll files: `dpp.dll`, `zlib.dll`, `openssl.dll` and `libcrypto.dll` (or similarly named SSL related files), `libsodium.dll` and `opus.dll`. Note the template project does this for you, so you should never encounter this issue. -- Stuck? You can find us on the [official discord server](https://discord.gg/dpp) - ask away! We don't bite! - diff --git a/vendor/DPP/docpages/make_a_bot/windows_wsl.md b/vendor/DPP/docpages/make_a_bot/windows_wsl.md deleted file mode 100644 index 1ae42e1e..00000000 --- a/vendor/DPP/docpages/make_a_bot/windows_wsl.md +++ /dev/null @@ -1,18 +0,0 @@ -\page build-a-discord-bot-windows-wsl Building a discord bot in Windows using WSL (Windows Subsystem for Linux) - -This tutorial teaches you how to create a lightweight environment for D++-development using **WSL** and **Visual Studio Code** - -This Tutorial will use WSL's default distribution, **Ubuntu**! You might use other Distros if you prefer, but keep in mind the setup process might be different! - -1. Make sure you have installed your WSL 2 environment properly using [this guide to setup up WSL](https://docs.microsoft.com/en-us/windows/wsl/install) and [this guide to connect to Visual Studio Code](https://docs.microsoft.com/en-us/windows/wsl/tutorials/wsl-vscode). -2. Now open PowerShell as an Admin and type `wsl` to start up your subsystem. If you want to set up a CMake project (recommended for production bots) now, consider continuing your path of becoming the master of all Discord bots [here](https://dpp.dev/buildcmake.html), otherwise keep following this guide! -3. Go to your home directory using `cd ~` -4. Download the latest build for your Distro using `wget [url here]`. In this guide we will use the latest build for 64 bit Ubuntu: `wget -O libdpp.deb https://dl.dpp.dev/latest` -5. Finally install all required deps and the library using `sudo apt-get install libopus0 && sudo apt-get install -y libopus-dev && sudo apt-get install -y libsodium-dev && sudo dpkg -i libdpp.deb && rm libdpp.deb` -6. Congratulations, you've successfully installed all dependencies! Now comes the real fun: Setting up the environment! For this tutorial we'll use a as small as possible setup, so you might create a more advanced one for production bots. -7. Navigate to a folder of your choice using `cd your/path/here` or create a new directory using `mkdir MyBot && cd MyBot` -8. Now that you've a folder to work in type `> mybot.cxx` to create a file you can work in! -9. Now you can open this file in Visual Studio Code by pressing `CTRL+SHIFT+P` and typing `Remote-WSL: New WSL Window`. This will bring up a new window. In the new window, choose `open folder` and choose the folder you've created prior. Press OK and now you have your Folder opened as a Workspace! -10. Add code to your CXX file and compile it by running `g++ -std=c++17 *.cxx -o bot -ldpp` in the same folder as your cxx file. -11. start your bot by typing `./bot`! - diff --git a/vendor/DPP/docpages/makedocs-gh-single.php b/vendor/DPP/docpages/makedocs-gh-single.php deleted file mode 100644 index 8c341c82..00000000 --- a/vendor/DPP/docpages/makedocs-gh-single.php +++ /dev/null @@ -1,42 +0,0 @@ -/dev/null"); -chdir("DPP"); -system("git fetch --tags -a >/dev/null"); -system("git checkout tags/$orig_tag >/dev/null"); -system("git checkout tags/v$orig_tag >/dev/null"); -/* Older versions of the docs before 9.0.7 don't have these. Force them into the tree so old versions get current styling */ -system("cp -r /home/runner/work/DPP/DPP/docpages/images docpages"); -system("cp -r /home/runner/work/DPP/DPP/docpages/style.css docpages/style.css"); -system("cp -r /home/runner/work/DPP/DPP/docpages/*.html docpages/"); -system("cp -r /home/runner/work/DPP/DPP/doxygen-awesome-css doxygen-awesome-css"); -/* Always make sure that the version is using the latest doxygen, - * but rewrite version number (project number) - */ -$doxy = file_get_contents("/home/runner/work/DPP/DPP/Doxyfile"); -$doxy = str_replace("PROJECT_NUMBER =", "PROJECT_NUMBER = $tag", $doxy); -file_put_contents("Doxyfile", $doxy); -/* Rewrite selected version number so that each page has a new default selected in the drop down */ -$hdr = file_get_contents("/home/runner/work/DPP/DPP/docpages/header.html"); -$hdr = str_replace("option value='/$tag/'", "option selected value='/$tag/'", $hdr); -/* Rewrite version info in header */ -file_put_contents("docpages/header.html", $hdr); -shell_exec("/usr/local/bin/doxygen >/dev/null"); -if (!file_exists("/home/runner/dpp-web/$tag")) { - mkdir("/home/runner/dpp-web/$tag"); -} -chdir("docs"); -/* Add giscus inside the actual content as postprocess */ -system('perl -p -i -e \'s/(<.div><.-- contents -->)/$1 + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+

API Reference¶

+

The {fmt} library API consists of the following parts:

+ +

All functions and types provided by the library reside in namespace fmt and +macros have prefix FMT_.

+
+

Core API¶

+

fmt/core.h defines the core API which provides main formatting functions +for char/UTF-8 with C++20 compile-time checks. It has minimal include +dependencies for better compile times. This header is only beneficial when +using {fmt} as a library (the default) and not in the header-only mode. +It also provides formatter specializations for built-in and string types.

+

The following functions use format string syntax +similar to that of Python’s str.format. +They take fmt and args as arguments.

+

fmt is a format string that contains literal text and replacement fields +surrounded by braces {}. The fields are replaced with formatted arguments +in the resulting string. format_string is a format string which can be +implicitly constructed from a string literal or a constexpr string and is +checked at compile time in C++20. To pass a runtime format string wrap it in +fmt::runtime().

+

args is an argument list representing objects to be formatted.

+
+
+template<typename ...T>
auto fmt::format(format_string<T...> fmt, T&&... args) -> std::string¶
+

Formats args according to specifications in fmt and returns the result +as a string.

+

Example:

+
#include <fmt/core.h>
+std::string message = fmt::format("The answer is {}.", 42);
+
+
+

+

+
+ +
+
+auto fmt::vformat(string_view fmt, format_args args) -> std::string¶
+
+ +
+
+template<typename OutputIt, typename ...T>
auto fmt::format_to(OutputIt out, format_string<T...> fmt, T&&... args) -> OutputIt¶
+

Formats args according to specifications in fmt, writes the result to +the output iterator out and returns the iterator past the end of the output +range. format_to() does not append a terminating null character.

+

Example:

+
auto out = std::vector<char>();
+fmt::format_to(std::back_inserter(out), "{}", 42);
+
+
+

+

+
+ +
+
+template<typename OutputIt, typename ...T>
auto fmt::format_to_n(OutputIt out, size_t n, format_string<T...> fmt, T&&... args) -> format_to_n_result<OutputIt>¶
+

Formats args according to specifications in fmt, writes up to n +characters of the result to the output iterator out and returns the total +(not truncated) output size and the iterator past the end of the output range. +format_to_n() does not append a terminating null character.

+

+

+
+ +
+
+template<typename ...T>
auto fmt::formatted_size(format_string<T...> fmt, T&&... args) -> size_t¶
+

Returns the number of chars in the output of format(fmt, args...).

+
+ +
+
+template<typename OutputIt>
struct fmt::format_to_n_result¶
+
+

Public Members

+
+
+OutputIt out¶
+

Iterator past the end of the output range.

+
+ +
+
+size_t size¶
+

Total (not truncated) output size.

+
+ +
+
+ +
+
+template<typename ...T>
void fmt::print(format_string<T...> fmt, T&&... args)¶
+

Formats args according to specifications in fmt and writes the output +to stdout.

+

Example:

+
fmt::print("Elapsed time: {0:.2f} seconds", 1.23);
+
+
+

+

+
+ +
+
+void fmt::vprint(string_view fmt, format_args args)¶
+
+ +
+
+template<typename ...T>
void fmt::print(std::FILE *f, format_string<T...> fmt, T&&... args)¶
+

Formats args according to specifications in fmt and writes the +output to the file f.

+

Example:

+
fmt::print(stderr, "Don't {}!", "panic");
+
+
+

+

+
+ +
+
+void fmt::vprint(std::FILE *f, string_view fmt, format_args args)¶
+
+ +
+

Compile-Time Format String Checks¶

+

Compile-time format string checks are enabled by default on compilers +that support C++20 consteval. On older compilers you can use the +FMT_STRING: macro defined in fmt/format.h instead.

+

Unused arguments are allowed as in Python’s str.format and ordinary functions.

+
+
+template<typename Char, typename ...Args>
class basic_format_string¶
+

A compile-time format string.

+
+ +
+
+template<typename ...Args>
using fmt::format_string = basic_format_string<char, type_identity_t<Args>...>¶
+
+ +
+
+auto fmt::runtime(string_view s) -> runtime_format_string<>¶
+

Creates a runtime format string.

+

Example:

+
// Check format string at runtime instead of compile-time.
+fmt::print(fmt::runtime("{:d}"), "I am not a number");
+
+
+

+

+
+ +
+
+

Formatting User-Defined Types¶

+

The {fmt} library provides formatters for many standard C++ types. +See fmt/ranges.h for ranges and tuples including standard +containers such as std::vector, fmt/chrono.h for date +and time formatting and fmt/std.h for other standard library +types.

+

To make a user-defined type formattable, specialize the formatter<T> struct +template and implement parse and format methods:

+
#include <fmt/core.h>
+
+struct point {
+  double x, y;
+};
+
+template <> struct fmt::formatter<point> {
+  // Presentation format: 'f' - fixed, 'e' - exponential.
+  char presentation = 'f';
+
+  // Parses format specifications of the form ['f' | 'e'].
+  constexpr auto parse(format_parse_context& ctx) -> format_parse_context::iterator {
+    // [ctx.begin(), ctx.end()) is a character range that contains a part of
+    // the format string starting from the format specifications to be parsed,
+    // e.g. in
+    //
+    //   fmt::format("{:f} - point of interest", point{1, 2});
+    //
+    // the range will contain "f} - point of interest". The formatter should
+    // parse specifiers until '}' or the end of the range. In this example
+    // the formatter should parse the 'f' specifier and return an iterator
+    // pointing to '}'.
+
+    // Please also note that this character range may be empty, in case of
+    // the "{}" format string, so therefore you should check ctx.begin()
+    // for equality with ctx.end().
+
+    // Parse the presentation format and store it in the formatter:
+    auto it = ctx.begin(), end = ctx.end();
+    if (it != end && (*it == 'f' || *it == 'e')) presentation = *it++;
+
+    // Check if reached the end of the range:
+    if (it != end && *it != '}') ctx.on_error("invalid format");
+
+    // Return an iterator past the end of the parsed range:
+    return it;
+  }
+
+  // Formats the point p using the parsed format specification (presentation)
+  // stored in this formatter.
+  auto format(const point& p, format_context& ctx) const -> format_context::iterator {
+    // ctx.out() is an output iterator to write to.
+    return presentation == 'f'
+              ? fmt::format_to(ctx.out(), "({:.1f}, {:.1f})", p.x, p.y)
+              : fmt::format_to(ctx.out(), "({:.1e}, {:.1e})", p.x, p.y);
+  }
+};
+
+
+

Then you can pass objects of type point to any formatting function:

+
point p = {1, 2};
+std::string s = fmt::format("{:f}", p);
+// s == "(1.0, 2.0)"
+
+
+

You can also reuse existing formatters via inheritance or composition, for +example:

+
// color.h:
+#include <fmt/core.h>
+
+enum class color {red, green, blue};
+
+template <> struct fmt::formatter<color>: formatter<string_view> {
+  // parse is inherited from formatter<string_view>.
+
+  auto format(color c, format_context& ctx) const;
+};
+
+// color.cc:
+#include "color.h"
+#include <fmt/format.h>
+
+auto fmt::formatter<color>::format(color c, format_context& ctx) const {
+  string_view name = "unknown";
+  switch (c) {
+  case color::red:   name = "red"; break;
+  case color::green: name = "green"; break;
+  case color::blue:  name = "blue"; break;
+  }
+  return formatter<string_view>::format(name, ctx);
+}
+
+
+

Note that formatter<string_view>::format is defined in fmt/format.h so +it has to be included in the source file. +Since parse is inherited from formatter<string_view> it will recognize +all string format specifications, for example

+
fmt::format("{:>10}", color::blue)
+
+
+

will return "      blue".

+

You can also write a formatter for a hierarchy of classes:

+
// demo.h:
+#include <type_traits>
+#include <fmt/core.h>
+
+struct A {
+  virtual ~A() {}
+  virtual std::string name() const { return "A"; }
+};
+
+struct B : A {
+  virtual std::string name() const { return "B"; }
+};
+
+template <typename T>
+struct fmt::formatter<T, std::enable_if_t<std::is_base_of<A, T>::value, char>> :
+    fmt::formatter<std::string> {
+  auto format(const A& a, format_context& ctx) const {
+    return fmt::formatter<std::string>::format(a.name(), ctx);
+  }
+};
+
+// demo.cc:
+#include "demo.h"
+#include <fmt/format.h>
+
+int main() {
+  B b;
+  A& a = b;
+  fmt::print("{}", a); // prints "B"
+}
+
+
+

If a type provides both a formatter specialization and an implicit +conversion to a formattable type, the specialization takes precedence over the +conversion.

+

For enums {fmt} also provides the format_as extension API. To format an enum +via this API define format_as that takes this enum and converts it to the +underlying type. format_as should be defined in the same namespace as the +enum.

+

Example (https://godbolt.org/z/r7vvGE1v7):

+
#include <fmt/format.h>
+
+namespace kevin_namespacy {
+enum class film {
+  house_of_cards, american_beauty, se7en = 7
+};
+auto format_as(film f) { return fmt::underlying(f); }
+}
+
+int main() {
+  fmt::print("{}\n", kevin_namespacy::film::se7en); // prints "7"
+}
+
+
+
+
+

Named Arguments¶

+
+
+template<typename Char, typename T>
auto fmt::arg(const Char *name, const T &arg) -> detail::named_arg<Char, T>¶
+

Returns a named argument to be used in a formatting function. +It should only be used in a call to a formatting function or +dynamic_format_arg_store::push_back.

+

Example:

+
fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23));
+
+
+

+

+
+ +

Named arguments are not supported in compile-time checks at the moment.

+
+
+

Argument Lists¶

+

You can create your own formatting function with compile-time checks and small +binary footprint, for example (https://godbolt.org/z/vajfWEG4b):

+
#include <fmt/core.h>
+
+void vlog(const char* file, int line, fmt::string_view format,
+          fmt::format_args args) {
+  fmt::print("{}: {}: ", file, line);
+  fmt::vprint(format, args);
+}
+
+template <typename... T>
+void log(const char* file, int line, fmt::format_string<T...> format, T&&... args) {
+  vlog(file, line, format, fmt::make_format_args(args...));
+}
+
+#define MY_LOG(format, ...) log(__FILE__, __LINE__, format, __VA_ARGS__)
+
+MY_LOG("invalid squishiness: {}", 42);
+
+
+

Note that vlog is not parameterized on argument types which improves compile +times and reduces binary code size compared to a fully parameterized version.

+
+
+template<typename Context = format_context, typename ...T>
constexpr auto fmt::make_format_args(T&&... args) -> format_arg_store<Context, remove_cvref_t<T>...>¶
+

Constructs a format_arg_store object that contains references to +arguments and can be implicitly converted to format_args. Context +can be omitted in which case it defaults to context. +See arg() for lifetime considerations.

+

+

+
+ +
+
+template<typename Context, typename ...Args>
class format_arg_store¶
+

An array of references to arguments. It can be implicitly converted into +basic_format_args for passing into type-erased formatting functions +such as vformat().

+

+

+
+ +
+
+template<typename Context>
class dynamic_format_arg_store¶
+
+ +
+
+template<typename Context>
class fmt::basic_format_args¶
+

A view of a collection of formatting arguments. To avoid lifetime issues it +should only be used as a parameter type in type-erased functions such as +vformat:

+
void vlog(string_view format_str, format_args args);  // OK
+format_args args = make_format_args(42);  // Error: dangling reference
+
+
+

+

+
+

Public Functions

+
+
+template<typename ...Args>
constexpr basic_format_args(const format_arg_store<Context, Args...> &store)¶
+

Constructs a basic_format_args() object from format_arg_store.

+

+

+
+ +
+
+constexpr basic_format_args(const dynamic_format_arg_store<Context> &store)¶
+

Constructs a basic_format_args() object from +dynamic_format_arg_store.

+

+

+
+ +
+
+constexpr basic_format_args(const format_arg *args, int count)¶
+

Constructs a basic_format_args() object from a dynamic set of arguments.

+

+

+
+ +
+
+auto get(int id) const -> format_arg¶
+

Returns the argument with the specified id.

+
+ +
+
+ +
+
+using fmt::format_args = basic_format_args<format_context>¶
+

An alias to basic_format_args<format_context>.

+
+ +
+
+template<typename Context>
class basic_format_arg¶
+
+ +
+
+template<typename Char>
class fmt::basic_format_parse_context¶
+

Parsing context consisting of a format string range being parsed and an +argument counter for automatic indexing. +You can use the format_parse_context type alias for char instead.

+

+

+

Subclassed by fmt::basic_printf_parse_context< Char >, fmt::detail::compile_parse_context< Char >

+
+

Public Functions

+
+
+constexpr auto begin() const noexcept -> iterator¶
+

Returns an iterator to the beginning of the format string range being parsed.

+
+ +
+
+constexpr auto end() const noexcept -> iterator¶
+

Returns an iterator past the end of the format string range being parsed.

+
+ +
+
+void advance_to(iterator it)¶
+

Advances the begin iterator to it.

+
+ +
+
+auto next_arg_id() -> int¶
+

Reports an error if using the manual argument indexing; otherwise returns the next argument index and switches to the automatic indexing.

+
+ +
+
+void check_arg_id(int id)¶
+

Reports an error if using the automatic argument indexing; otherwise switches to the manual indexing.

+
+ +
+
+ +
+
+template<typename OutputIt, typename Char>
class fmt::basic_format_context¶
+
+

Public Types

+
+
+using char_type = Char¶
+

The character type for the output.

+
+ +
+
+

Public Functions

+
+
+constexpr basic_format_context(OutputIt out, format_args ctx_args, detail::locale_ref loc = {})¶
+

Constructs a basic_format_context object.

+

References to the arguments are stored in the object so make sure they have appropriate lifetimes.

+
+ +
+
+ +
+
+using fmt::format_context = buffer_context<char>¶
+
+ +
+
+

Compatibility¶

+
+
+template<typename Char>
class fmt::basic_string_view¶
+

An implementation of std::basic_string_view for pre-C++17.

+

It provides a subset of the API. fmt::basic_string_view is used for format strings even if std::string_view is available to prevent issues when a library is compiled with a different -std option than the client code (which is not recommended).

+
+

Public Functions

+
+
+constexpr basic_string_view(const Char *s, size_t count) noexcept¶
+

Constructs a string reference object from a C string and a size.

+
+ +
+
+basic_string_view(const Char *s)¶
+

Constructs a string reference object from a C string computing +the size with std::char_traits<Char>::length.

+

+

+
+ +
+
+template<typename Traits, typename Alloc>
basic_string_view(const std::basic_string<Char, Traits, Alloc> &s) noexcept¶
+

Constructs a string reference from a std::basic_string object.

+
+ +
+
+constexpr auto data() const noexcept -> const Char*¶
+

Returns a pointer to the string data.

+
+ +
+
+constexpr auto size() const noexcept -> size_t¶
+

Returns the string size.

+
+ +
+
+ +
+
+using fmt::string_view = basic_string_view<char>¶
+
+ +
+
+
+

Format API¶

+

fmt/format.h defines the full format API providing additional formatting +functions and locale support.

+
+

Literal-Based API¶

+

The following user-defined literals are defined in fmt/format.h.

+
+
+template<detail_exported::fixed_string Str>
constexpr auto fmt::operator""_a()¶
+

User-defined literal equivalent of fmt::arg().

+

Example:

+
using namespace fmt::literals;
+fmt::print("Elapsed time: {s:.2f} seconds", "s"_a=1.23);
+
+
+

+

+
+ +
+
+

Utilities¶

+
+
+template<typename T>
auto fmt::ptr(T p) -> const void*¶
+

Converts p to const void* for pointer formatting.

+

Example:

+
auto s = fmt::format("{}", fmt::ptr(p));
+
+
+

+

+
+ +
+
+template<typename T, typename Deleter>
auto fmt::ptr(const std::unique_ptr<T, Deleter> &p) -> const void*¶
+
+ +
+
+template<typename T>
auto fmt::ptr(const std::shared_ptr<T> &p) -> const void*¶
+
+ +
+
+template<typename Enum>
constexpr auto fmt::underlying(Enum e) noexcept -> underlying_t<Enum>¶
+

Converts e to the underlying type.

+

Example:

+
enum class color { red, green, blue };
+auto s = fmt::format("{}", fmt::underlying(color::red));
+
+
+

+

+
+ +
+
+template<typename T>
auto fmt::to_string(const T &value) -> std::string¶
+

Converts value to std::string using the default format for type T.

+

Example:

+
#include <fmt/format.h>
+
+std::string answer = fmt::to_string(42);
+
+
+

+

+
+ +
+
+template<typename Range>
auto fmt::join(Range &&range, string_view sep) -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>>¶
+

Returns a view that formats range with elements separated by sep.

+

Example:

+
std::vector<int> v = {1, 2, 3};
+fmt::print("{}", fmt::join(v, ", "));
+// Output: "1, 2, 3"
+
+
+

fmt::join applies passed format specifiers to the range elements:

+
fmt::print("{:02}", fmt::join(v, ", "));
+// Output: "01, 02, 03"
+
+
+

+

+
+ +
+
+template<typename It, typename Sentinel>
auto fmt::join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel>¶
+

Returns a view that formats the iterator range [begin, end) with elements separated by sep.

+
+ +
+
+template<typename T>
auto fmt::group_digits(T value) -> group_digits_view<T>¶
+

Returns a view that formats an integer value using ‘,’ as a locale-independent +thousands separator.

+

Example:

+
fmt::print("{}", fmt::group_digits(12345));
+// Output: "12,345"
+
+
+

+

+
+ +
+
+template<typename T>
class fmt::detail::buffer¶
+

A contiguous memory buffer with an optional growing ability. It is an internal +class and shouldn’t be used directly, only via basic_memory_buffer.

+

+

+

Subclassed by fmt::basic_memory_buffer< bigit, bigits_capacity >, fmt::basic_memory_buffer< wchar_t >, fmt::basic_memory_buffer< T, SIZE, Allocator >, fmt::detail::iterator_buffer< OutputIt, T, Traits >, fmt::detail::iterator_buffer< T *, T >, fmt::detail::iterator_buffer< T *, T, fixed_buffer_traits >

+
+

Public Functions

+
+
+constexpr auto size() const noexcept -> size_t¶
+

Returns the size of this buffer.

+
+ +
+
+constexpr auto capacity() const noexcept -> size_t¶
+

Returns the capacity of this buffer.

+
+ +
+
+auto data() noexcept -> T*¶
+

Returns a pointer to the buffer data.

+
+ +
+
+auto data() const noexcept -> const T*¶
+

Returns a pointer to the buffer data.

+
+ +
+
+void clear()¶
+

Clears this buffer.

+
+ +
+
+template<typename U>
void append(const U *begin, const U *end)¶
+

Appends data to the end of the buffer.

+
+ +
+
+ +
+
+template<typename T, size_t SIZE = inline_buffer_size, typename Allocator = std::allocator<T>>
class fmt::basic_memory_buffer : public fmt::detail::buffer<T>¶
+

A dynamically growing memory buffer for trivially copyable/constructible types +with the first SIZE elements stored in the object itself.

+

You can use the memory_buffer type alias for char instead.

+

Example:

+
auto out = fmt::memory_buffer();
+format_to(std::back_inserter(out), "The answer is {}.", 42);
+
+
+

This will append the following output to the out object:

+
The answer is 42.
+
+
+

The output can be converted to an std::string with to_string(out).

+

+

+
+

Public Functions

+
+
+basic_memory_buffer(basic_memory_buffer &&other) noexcept¶
+

Constructs a fmt::basic_memory_buffer object moving the content +of the other object to it.

+

+

+
+ +
+
+auto operator=(basic_memory_buffer &&other) noexcept -> basic_memory_buffer&¶
+

Moves the content of the other basic_memory_buffer object to this one.

+

+

+
+ +
+
+void resize(size_t count)¶
+

Resizes the buffer to contain count elements.

+

If T is a POD type new elements may not be initialized.

+
+ +
+
+void reserve(size_t new_capacity)¶
+

Increases the buffer capacity to new_capacity.

+
+ +
+
+

Protected Functions

+
+
+void grow(size_t size) override¶
+

Increases the buffer capacity to hold at least capacity elements.

+
+ +
+
+ +
+
+

System Errors¶

+

{fmt} does not use errno to communicate errors to the user, but it may call +system functions which set errno. Users should not make any assumptions +about the value of errno being preserved by library functions.

+
+
+template<typename ...T>
auto fmt::system_error(int error_code, format_string<T...> fmt, T&&... args) -> std::system_error¶
+

Constructs std::system_error with a message formatted with +fmt::format(fmt, args...).

+
+

error_code is a system error code as given by errno.

+
+

Example:

+
// This throws std::system_error with the description
+//   cannot open file 'madeup': No such file or directory
+// or similar (system message may vary).
+const char* filename = "madeup";
+std::FILE* file = std::fopen(filename, "r");
+if (!file)
+  throw fmt::system_error(errno, "cannot open file '{}'", filename);
+
+
+

+

+
+ +
+
+void fmt::format_system_error(detail::buffer<char> &out, int error_code, const char *message) noexcept¶
+

Formats an error message for an error returned by an operating system or a +language runtime, for example a file opening error, and writes it to out. +The format is the same as the one used by std::system_error(ec, message) +where ec is std::error_code(error_code, std::generic_category()}). +It is implementation-defined but normally looks like:

+
<message>: <system-message>
+

where <message> is the passed message and <system-message> is the system +message corresponding to the error code. +error_code is a system error code as given by errno.

+

+

+
+ +
+
+

Custom Allocators¶

+

The {fmt} library supports custom dynamic memory allocators. +A custom allocator class can be specified as a template argument to +fmt::basic_memory_buffer:

+
using custom_memory_buffer =
+  fmt::basic_memory_buffer<char, fmt::inline_buffer_size, custom_allocator>;
+
+
+

It is also possible to write a formatting function that uses a custom +allocator:

+
using custom_string =
+  std::basic_string<char, std::char_traits<char>, custom_allocator>;
+
+custom_string vformat(custom_allocator alloc, fmt::string_view format_str,
+                      fmt::format_args args) {
+  auto buf = custom_memory_buffer(alloc);
+  fmt::vformat_to(std::back_inserter(buf), format_str, args);
+  return custom_string(buf.data(), buf.size(), alloc);
+}
+
+template <typename ...Args>
+inline custom_string format(custom_allocator alloc,
+                            fmt::string_view format_str,
+                            const Args& ... args) {
+  return vformat(alloc, format_str, fmt::make_format_args(args...));
+}
+
+
+

The allocator will be used for the output container only. Formatting functions +normally don’t do any allocations for built-in and string types except for +non-default floating-point formatting that occasionally falls back on +sprintf.

+
+
+

Locale¶

+

All formatting is locale-independent by default. Use the 'L' format +specifier to insert the appropriate number separator characters from the +locale:

+
#include <fmt/core.h>
+#include <locale>
+
+std::locale::global(std::locale("en_US.UTF-8"));
+auto s = fmt::format("{:L}", 1000000);  // s == "1,000,000"
+
+
+

fmt/format.h provides the following overloads of formatting functions that +take std::locale as a parameter. The locale type is a template parameter to +avoid the expensive <locale> include.

+
+
+template<typename Locale, typename ...T>
auto fmt::format(const Locale &loc, format_string<T...> fmt, T&&... args) -> std::string¶
+
+ +
+
+template<typename OutputIt, typename Locale, typename ...T>
auto fmt::format_to(OutputIt out, const Locale &loc, format_string<T...> fmt, T&&... args) -> OutputIt¶
+
+ +
+
+template<typename Locale, typename ...T>
auto fmt::formatted_size(const Locale &loc, format_string<T...> fmt, T&&... args) -> size_t¶
+
+ +
+
+

Legacy Compile-Time Format String Checks¶

+

FMT_STRING enables compile-time checks on older compilers. It requires C++14 +or later and is a no-op in C++11.

+
+
+FMT_STRING(s)¶
+

Constructs a compile-time format string from a string literal s.

+

Example:

+
// A compile-time error because 'd' is an invalid specifier for strings.
+std::string s = fmt::format(FMT_STRING("{:d}"), "foo");
+
+
+

+

+
+ +

To force the use of legacy compile-time checks, define the preprocessor variable +FMT_ENFORCE_COMPILE_STRING. When set, functions accepting FMT_STRING +will fail to compile with regular strings.

+
+
+
+

Range and Tuple Formatting¶

+

The library also supports convenient formatting of ranges and tuples:

+
#include <fmt/ranges.h>
+
+std::tuple<char, int, float> t{'a', 1, 2.0f};
+// Prints "('a', 1, 2.0)"
+fmt::print("{}", t);
+
+
+

NOTE: currently, the overload of fmt::join for iterables exists in the main +format.h header, but expect this to change in the future.

+

Using fmt::join, you can separate tuple elements with a custom separator:

+
#include <fmt/ranges.h>
+
+std::tuple<int, char> t = {1, 'a'};
+// Prints "1, a"
+fmt::print("{}", fmt::join(t, ", "));
+
+
+
+
+

Date and Time Formatting¶

+

fmt/chrono.h provides formatters for

+ +

The format syntax is described in Chrono Format Specifications.

+

Example:

+
#include <fmt/chrono.h>
+
+int main() {
+  std::time_t t = std::time(nullptr);
+
+  // Prints "The date is 2020-11-07." (with the current date):
+  fmt::print("The date is {:%Y-%m-%d}.", fmt::localtime(t));
+
+  using namespace std::literals::chrono_literals;
+
+  // Prints "Default format: 42s 100ms":
+  fmt::print("Default format: {} {}\n", 42s, 100ms);
+
+  // Prints "strftime-like format: 03:15:30":
+  fmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s);
+}
+
+
+
+
+std::tm fmt::localtime(std::time_t time)¶
+

Converts given time since epoch as std::time_t value into calendar time, expressed in local time.

+

Unlike std::localtime, this function is thread-safe on most platforms.

+
+ +
+
+std::tm fmt::gmtime(std::time_t time)¶
+

Converts given time since epoch as std::time_t value into calendar time, expressed in Coordinated Universal Time (UTC).

+

Unlike std::gmtime, this function is thread-safe on most platforms.

+
+ +
+
+

Standard Library Types Formatting¶

+

fmt/std.h provides formatters for:

+ +
+

Formatting Variants¶

+

A std::variant is only formattable if every variant alternative is formattable, and requires the +__cpp_lib_variant library feature.

+

Example:

+
#include <fmt/std.h>
+
+std::variant<char, float> v0{'x'};
+// Prints "variant('x')"
+fmt::print("{}", v0);
+
+std::variant<std::monostate, char> v1;
+// Prints "variant(monostate)"
+
+
+
+
+
+

Format String Compilation¶

+

fmt/compile.h provides format string compilation enabled via the +FMT_COMPILE macro or the _cf user-defined literal. Format strings +marked with FMT_COMPILE or _cf are parsed, checked and converted into +efficient formatting code at compile-time. This supports arguments of built-in +and string types as well as user-defined types with constexpr parse +functions in their formatter specializations. Format string compilation can +generate more binary code compared to the default API and is only recommended in +places where formatting is a performance bottleneck.

+
+
+FMT_COMPILE(s)¶
+

Converts a string literal s into a format string that will be parsed at +compile time and converted into efficient formatting code. Requires C++17 +constexpr if compiler support.

+

Example:

+
// Converts 42 into std::string using the most efficient method and no
+// runtime format string processing.
+std::string s = fmt::format(FMT_COMPILE("{}"), 42);
+
+
+

+

+
+ +
+
+template<detail_exported::fixed_string Str>
constexpr auto fmt::operator""_cf()¶
+
+ +
+
+

Terminal Color and Text Style¶

+

fmt/color.h provides support for terminal color and text style output.

+
+
+template<typename S, typename ...Args>
void fmt::print(const text_style &ts, const S &format_str, const Args&... args)¶
+

Formats a string and prints it to stdout using ANSI escape sequences to +specify text formatting.

+

Example:

+
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
+           "Elapsed time: {0:.2f} seconds", 1.23);
+
+
+

+

+
+ +
+
+text_style fmt::fg(detail::color_type foreground) noexcept¶
+

Creates a text style from the foreground (text) color.

+
+ +
+
+text_style fmt::bg(detail::color_type background) noexcept¶
+

Creates a text style from the background color.

+
+ +
+
+template<typename T>
auto fmt::styled(const T &value, text_style ts) -> detail::styled_arg<remove_cvref_t<T>>¶
+

Returns an argument that will be formatted using ANSI escape sequences, +to be used in a formatting function.

+

Example:

+
fmt::print("Elapsed time: {0:.2f} seconds",
+           fmt::styled(1.23, fmt::fg(fmt::color::green) |
+                             fmt::bg(fmt::color::blue)));
+
+
+

+

+
+ +
+
+

System APIs¶

+
+
+class fmt::ostream¶
+

A fast output stream which is not thread-safe.

+
+

Public Functions

+
+
+template<typename ...T>
void print(format_string<T...> fmt, T&&... args)¶
+

Formats args according to specifications in fmt and writes the output to the file.

+
+ +
+
+

Friends

+
+
+template<typename ...T>
ostream output_file(cstring_view path, T... params)¶
+

Opens a file for writing. Supported parameters passed in params:

+
    +
  • <integer>: Flags passed to open +(file::WRONLY | file::CREATE | file::TRUNC by default)

  • +
  • buffer_size=<integer>: Output buffer size

  • +
+

Example:

+
auto out = fmt::output_file("guide.txt");
+out.print("Don't {}", "Panic");
+
+
+

+

+
+ +
+
+ +
+
+

std::ostream Support¶

+

fmt/ostream.h provides std::ostream support including formatting of +user-defined types that have an overloaded insertion operator (operator<<). +In order to make a type formattable via std::ostream you should provide a +formatter specialization inherited from ostream_formatter:

+
#include <fmt/ostream.h>
+
+struct date {
+  int year, month, day;
+
+  friend std::ostream& operator<<(std::ostream& os, const date& d) {
+    return os << d.year << '-' << d.month << '-' << d.day;
+  }
+};
+
+template <> struct fmt::formatter<date> : ostream_formatter {};
+
+std::string s = fmt::format("The date is {}", date{2012, 12, 9});
+// s == "The date is 2012-12-9"
+
+
+
+
+template<typename T>
auto fmt::streamed(const T &value) -> detail::streamed_view<T>¶
+

Returns a view that formats value via an ostream operator<<.

+

Example:

+
fmt::print("Current thread id: {}\n",
+           fmt::streamed(std::this_thread::get_id()));
+
+
+

+

+
+ +
+
+template<typename ...T>
void fmt::print(std::ostream &os, format_string<T...> fmt, T&&... args)¶
+

Prints formatted data to the stream os.

+

Example:

+
fmt::print(cerr, "Don't {}!", "panic");
+
+
+

+

+
+ +
+
+

printf Formatting¶

+

The header fmt/printf.h provides printf-like formatting functionality. +The following functions use printf format string syntax with +the POSIX extension for positional arguments. Unlike their standard +counterparts, the fmt functions are type-safe and throw an exception if an +argument type doesn’t match its format specification.

+
+
+template<typename S, typename ...T>
auto fmt::printf(const S &fmt, const T&... args) -> int¶
+

Prints formatted data to stdout.

+

Example:

+
fmt::printf("Elapsed time: %.2f seconds", 1.23);
+
+
+

+

+
+ +
+
+template<typename S, typename ...T, typename Char = char_t<S>>
auto fmt::fprintf(std::FILE *f, const S &fmt, const T&... args) -> int¶
+

Prints formatted data to the file f.

+

Example:

+
fmt::fprintf(stderr, "Don't %s!", "panic");
+
+
+

+

+
+ +
+
+template<typename S, typename ...T, typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
auto fmt::sprintf(const S &fmt, const T&... args) -> std::basic_string<Char>¶
+

Formats arguments and returns the result as a string.

+

Example:

+
std::string message = fmt::sprintf("The answer is %d", 42);
+
+
+

+

+
+ +
+
+

wchar_t Support¶

+

The optional header fmt/xchar.h provides support for wchar_t and exotic +character types.

+
+
+template<typename T>
struct is_char : public std::false_type¶
+

Specifies if T is a character type.

+

Can be specialized by users.

+
+ +
+
+using fmt::wstring_view = basic_string_view<wchar_t>¶
+
+ +
+
+using fmt::wformat_context = buffer_context<wchar_t>¶
+
+ +
+
+template<typename T>
auto fmt::to_wstring(const T &value) -> std::wstring¶
+

Converts value to std::wstring using the default format for type T.

+
+ +
+
+

Compatibility with C++20 std::format¶

+

{fmt} implements nearly all of the C++20 formatting library with the following +differences:

+
    +
  • Names are defined in the fmt namespace instead of std to avoid +collisions with standard library implementations.

  • +
  • Width calculation doesn’t use grapheme clusterization. The latter has been +implemented in a separate branch but hasn’t been integrated yet.

  • +
  • Most C++20 chrono types are not supported yet.

  • +
+
+
+ + +
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/vendor/Fmt/doc/html/contents.html b/vendor/Fmt/doc/html/contents.html new file mode 100644 index 00000000..c01136d0 --- /dev/null +++ b/vendor/Fmt/doc/html/contents.html @@ -0,0 +1,189 @@ + + + + + + + + + + + + Contents — fmt 10.0.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vendor/Fmt/doc/html/genindex.html b/vendor/Fmt/doc/html/genindex.html new file mode 100644 index 00000000..6a18e934 --- /dev/null +++ b/vendor/Fmt/doc/html/genindex.html @@ -0,0 +1,312 @@ + + + + + + + + + + + Index — fmt 10.0.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ + +

Index

+ +
+ F + +
+

F

+ + + +
+ + + +
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/vendor/Fmt/doc/html/index.html b/vendor/Fmt/doc/html/index.html new file mode 100644 index 00000000..981ba3b0 --- /dev/null +++ b/vendor/Fmt/doc/html/index.html @@ -0,0 +1,325 @@ + + + + + + + + + + + + Overview — fmt 10.0.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+

{fmt}

+

A modern formatting library

+ +
+
+ + + +
+
+ + +
+ +
+

Overview¶

+

{fmt} is an open-source formatting library providing a fast and safe +alternative to C stdio and C++ iostreams.

+
+
What users say:
+
+ Thanks for creating this library. It’s been a hole in C++ for + a long time. I’ve used both boost::format and + loki::SPrintf, and neither felt like the right answer. + This does. +
+
+

Format API¶

+

The format API is similar in spirit to the C printf family of function but +is safer, simpler and several times faster +than common standard library implementations. +The format string syntax is similar to the one used by +str.format in +Python:

+
std::string s = fmt::format("The answer is {}.", 42);
+
+
+

The fmt::format function returns a string “The answer is 42.â€. You can use +fmt::memory_buffer to avoid constructing std::string:

+
auto out = fmt::memory_buffer();
+fmt::format_to(std::back_inserter(out),
+          "For a moment, {} happened.", "nothing");
+auto data = out.data(); // pointer to the formatted data
+auto size = out.size(); // size of the formatted data
+
+
+

The fmt::print function performs formatting and writes the result to a stream:

+
fmt::print(stderr, "System error code = {}\n", errno);
+
+
+

If you omit the file argument the function will print to stdout:

+
fmt::print("Don't {}\n", "panic");
+
+
+

The format API also supports positional arguments useful for localization:

+
fmt::print("I'd rather be {1} than {0}.", "right", "happy");
+
+
+

You can pass named arguments with fmt::arg:

+
fmt::print("Hello, {name}! The answer is {number}. Goodbye, {name}.",
+           fmt::arg("name", "World"), fmt::arg("number", 42));
+
+
+

If your compiler supports C++11 user-defined literals, the suffix _a offers +an alternative, slightly terser syntax for named arguments:

+
using namespace fmt::literals;
+fmt::print("Hello, {name}! The answer is {number}. Goodbye, {name}.",
+           "name"_a="World", "number"_a=42);
+
+
+
+
+

Safety¶

+

The library is fully type safe, automatic memory management prevents buffer +overflow, errors in format strings are reported using exceptions or at compile +time. For example, the code

+
fmt::format("The answer is {:d}", "forty-two");
+
+
+

throws the format_error exception because the argument "forty-two" is a +string while the format code d only applies to integers.

+

The code

+
format(FMT_STRING("The answer is {:d}"), "forty-two");
+
+
+

reports a compile-time error on compilers that support relaxed constexpr. +See here for details.

+

The following code

+
fmt::format("Cyrillic letter {}", L'\x42e');
+
+
+

produces a compile-time error because wide character L'\x42e' cannot be +formatted into a narrow string. For comparison, writing a wide character to +std::ostream results in its numeric value being written to the stream +(i.e. 1070 instead of letter ‘ю’ which is represented by L'\x42e' if we +use Unicode) which is rarely desirable.

+
+
+

Compact Binary Code¶

+

The library produces compact per-call compiled code. For example +(godbolt),

+
#include <fmt/core.h>
+
+int main() {
+  fmt::print("The answer is {}.", 42);
+}
+
+
+

compiles to just

+
main: # @main
+  sub rsp, 24
+  mov qword ptr [rsp], 42
+  mov rcx, rsp
+  mov edi, offset .L.str
+  mov esi, 17
+  mov edx, 1
+  call fmt::v7::vprint(fmt::v7::basic_string_view<char>, fmt::v7::format_args)
+  xor eax, eax
+  add rsp, 24
+  ret
+.L.str:
+  .asciz "The answer is {}."
+
+
+
+
+

Portability¶

+

The library is highly portable and relies only on a small set of C++11 features:

+
    +
  • variadic templates

  • +
  • type traits

  • +
  • rvalue references

  • +
  • decltype

  • +
  • trailing return types

  • +
  • deleted functions

  • +
  • alias templates

  • +
+

These are available in GCC 4.8, Clang 3.4, MSVC 19.0 (2015) and more recent +compiler version. For older compilers use {fmt} version 4.x which is maintained and +only requires C++98.

+

The output of all formatting functions is consistent across platforms. +For example,

+
fmt::print("{}", std::numeric_limits<double>::infinity());
+
+
+

always prints inf while the output of printf is platform-dependent.

+
+
+

Ease of Use¶

+

{fmt} has a small self-contained code base with the core library consisting of +just three header files and no external dependencies. +A permissive MIT license allows +using the library both in open-source and commercial projects.

+

Learn more…

+GitHub Repository + +
+
+ + +
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/vendor/Fmt/doc/html/objects.inv b/vendor/Fmt/doc/html/objects.inv new file mode 100644 index 00000000..f36fdb88 Binary files /dev/null and b/vendor/Fmt/doc/html/objects.inv differ diff --git a/vendor/Fmt/doc/html/search.html b/vendor/Fmt/doc/html/search.html new file mode 100644 index 00000000..743a4680 --- /dev/null +++ b/vendor/Fmt/doc/html/search.html @@ -0,0 +1,171 @@ + + + + + + + + + + + Search — fmt 10.0.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +

Search

+
+ +

+ Please activate JavaScript to enable the search + functionality. +

+
+

+ From here you can search these documents. Enter your search + words into the box below and click "search". Note that the search + function will automatically search for all of the words. Pages + containing fewer words won't appear in the result list. +

+ + + +
+ +
+ +
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/vendor/Fmt/doc/html/searchindex.js b/vendor/Fmt/doc/html/searchindex.js new file mode 100644 index 00000000..750cd14f --- /dev/null +++ b/vendor/Fmt/doc/html/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({docnames:["api","contents","index","syntax","usage"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":3,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":2,"sphinx.domains.rst":2,"sphinx.domains.std":1,sphinx:56},filenames:["api.rst","contents.rst","index.rst","syntax.rst","usage.rst"],objects:{"":{"fmt::arg":[0,1,1,"_CPPv4I00EN3fmt3argEN6detail9named_argI4Char1TEEPK4CharRK1T"],"fmt::arg::Char":[0,2,1,"_CPPv4I00EN3fmt3argEN6detail9named_argI4Char1TEEPK4CharRK1T"],"fmt::arg::T":[0,2,1,"_CPPv4I00EN3fmt3argEN6detail9named_argI4Char1TEEPK4CharRK1T"],"fmt::arg::arg":[0,3,1,"_CPPv4I00EN3fmt3argEN6detail9named_argI4Char1TEEPK4CharRK1T"],"fmt::arg::name":[0,3,1,"_CPPv4I00EN3fmt3argEN6detail9named_argI4Char1TEEPK4CharRK1T"],"fmt::basic_format_arg":[0,4,1,"_CPPv4I0EN3fmt16basic_format_argE"],"fmt::basic_format_arg::Context":[0,2,1,"_CPPv4I0EN3fmt16basic_format_argE"],"fmt::basic_format_args":[0,4,1,"_CPPv4I0EN3fmt17basic_format_argsE"],"fmt::basic_format_args::Context":[0,2,1,"_CPPv4I0EN3fmt17basic_format_argsE"],"fmt::basic_format_args::basic_format_args":[0,1,1,"_CPPv4N3fmt17basic_format_args17basic_format_argsERK24dynamic_format_arg_storeI7ContextE"],"fmt::basic_format_args::basic_format_args::Args":[0,2,1,"_CPPv4IDpEN3fmt17basic_format_args17basic_format_argsERK16format_arg_storeI7ContextDp4ArgsE"],"fmt::basic_format_args::basic_format_args::args":[0,3,1,"_CPPv4N3fmt17basic_format_args17basic_format_argsEPK10format_argi"],"fmt::basic_format_args::basic_format_args::count":[0,3,1,"_CPPv4N3fmt17basic_format_args17basic_format_argsEPK10format_argi"],"fmt::basic_format_args::basic_format_args::store":[0,3,1,"_CPPv4N3fmt17basic_format_args17basic_format_argsERK24dynamic_format_arg_storeI7ContextE"],"fmt::basic_format_args::get":[0,1,1,"_CPPv4NK3fmt17basic_format_args3getEi"],"fmt::basic_format_args::get::id":[0,3,1,"_CPPv4NK3fmt17basic_format_args3getEi"],"fmt::basic_format_context":[0,4,1,"_CPPv4I00EN3fmt20basic_format_contextE"],"fmt::basic_format_context::Char":[0,2,1,"_CPPv4I00EN3fmt20basic_format_contextE"],"fmt::basic_format_context::OutputIt":[0,2,1,"_CPPv4I00EN3fmt20basic_format_contextE"],"fmt::basic_format_context::basic_format_context":[0,1,1,"_CPPv4N3fmt20basic_format_context20basic_format_contextE8OutputIt11format_argsN6detail10locale_refE"],"fmt::basic_format_context::basic_format_context::ctx_args":[0,3,1,"_CPPv4N3fmt20basic_format_context20basic_format_contextE8OutputIt11format_argsN6detail10locale_refE"],"fmt::basic_format_context::basic_format_context::loc":[0,3,1,"_CPPv4N3fmt20basic_format_context20basic_format_contextE8OutputIt11format_argsN6detail10locale_refE"],"fmt::basic_format_context::basic_format_context::out":[0,3,1,"_CPPv4N3fmt20basic_format_context20basic_format_contextE8OutputIt11format_argsN6detail10locale_refE"],"fmt::basic_format_context::char_type":[0,5,1,"_CPPv4N3fmt20basic_format_context9char_typeE"],"fmt::basic_format_parse_context":[0,4,1,"_CPPv4I0EN3fmt26basic_format_parse_contextE"],"fmt::basic_format_parse_context::Char":[0,2,1,"_CPPv4I0EN3fmt26basic_format_parse_contextE"],"fmt::basic_format_parse_context::advance_to":[0,1,1,"_CPPv4N3fmt26basic_format_parse_context10advance_toE8iterator"],"fmt::basic_format_parse_context::advance_to::it":[0,3,1,"_CPPv4N3fmt26basic_format_parse_context10advance_toE8iterator"],"fmt::basic_format_parse_context::begin":[0,1,1,"_CPPv4NK3fmt26basic_format_parse_context5beginEv"],"fmt::basic_format_parse_context::check_arg_id":[0,1,1,"_CPPv4N3fmt26basic_format_parse_context12check_arg_idEi"],"fmt::basic_format_parse_context::check_arg_id::id":[0,3,1,"_CPPv4N3fmt26basic_format_parse_context12check_arg_idEi"],"fmt::basic_format_parse_context::end":[0,1,1,"_CPPv4NK3fmt26basic_format_parse_context3endEv"],"fmt::basic_format_parse_context::next_arg_id":[0,1,1,"_CPPv4N3fmt26basic_format_parse_context11next_arg_idEv"],"fmt::basic_format_string":[0,4,1,"_CPPv4I0DpEN3fmt19basic_format_stringE"],"fmt::basic_format_string::Args":[0,2,1,"_CPPv4I0DpEN3fmt19basic_format_stringE"],"fmt::basic_format_string::Char":[0,2,1,"_CPPv4I0DpEN3fmt19basic_format_stringE"],"fmt::basic_memory_buffer":[0,4,1,"_CPPv4I0_6size_t0EN3fmt19basic_memory_bufferE"],"fmt::basic_memory_buffer::Allocator":[0,2,1,"_CPPv4I0_6size_t0EN3fmt19basic_memory_bufferE"],"fmt::basic_memory_buffer::SIZE":[0,2,1,"_CPPv4I0_6size_t0EN3fmt19basic_memory_bufferE"],"fmt::basic_memory_buffer::T":[0,2,1,"_CPPv4I0_6size_t0EN3fmt19basic_memory_bufferE"],"fmt::basic_memory_buffer::basic_memory_buffer":[0,1,1,"_CPPv4N3fmt19basic_memory_buffer19basic_memory_bufferERR19basic_memory_buffer"],"fmt::basic_memory_buffer::basic_memory_buffer::other":[0,3,1,"_CPPv4N3fmt19basic_memory_buffer19basic_memory_bufferERR19basic_memory_buffer"],"fmt::basic_memory_buffer::grow":[0,1,1,"_CPPv4N3fmt19basic_memory_buffer4growE6size_t"],"fmt::basic_memory_buffer::grow::size":[0,3,1,"_CPPv4N3fmt19basic_memory_buffer4growE6size_t"],"fmt::basic_memory_buffer::operator=":[0,1,1,"_CPPv4N3fmt19basic_memory_bufferaSERR19basic_memory_buffer"],"fmt::basic_memory_buffer::operator=::other":[0,3,1,"_CPPv4N3fmt19basic_memory_bufferaSERR19basic_memory_buffer"],"fmt::basic_memory_buffer::reserve":[0,1,1,"_CPPv4N3fmt19basic_memory_buffer7reserveE6size_t"],"fmt::basic_memory_buffer::reserve::new_capacity":[0,3,1,"_CPPv4N3fmt19basic_memory_buffer7reserveE6size_t"],"fmt::basic_memory_buffer::resize":[0,1,1,"_CPPv4N3fmt19basic_memory_buffer6resizeE6size_t"],"fmt::basic_memory_buffer::resize::count":[0,3,1,"_CPPv4N3fmt19basic_memory_buffer6resizeE6size_t"],"fmt::basic_string_view":[0,4,1,"_CPPv4I0EN3fmt17basic_string_viewE"],"fmt::basic_string_view::Char":[0,2,1,"_CPPv4I0EN3fmt17basic_string_viewE"],"fmt::basic_string_view::basic_string_view":[0,1,1,"_CPPv4N3fmt17basic_string_view17basic_string_viewEPK4Char6size_t"],"fmt::basic_string_view::basic_string_view::Alloc":[0,2,1,"_CPPv4I00EN3fmt17basic_string_view17basic_string_viewERKNSt12basic_stringI4Char6Traits5AllocEE"],"fmt::basic_string_view::basic_string_view::Traits":[0,2,1,"_CPPv4I00EN3fmt17basic_string_view17basic_string_viewERKNSt12basic_stringI4Char6Traits5AllocEE"],"fmt::basic_string_view::basic_string_view::count":[0,3,1,"_CPPv4N3fmt17basic_string_view17basic_string_viewEPK4Char6size_t"],"fmt::basic_string_view::basic_string_view::s":[0,3,1,"_CPPv4N3fmt17basic_string_view17basic_string_viewEPK4Char6size_t"],"fmt::basic_string_view::data":[0,1,1,"_CPPv4NK3fmt17basic_string_view4dataEv"],"fmt::basic_string_view::size":[0,1,1,"_CPPv4NK3fmt17basic_string_view4sizeEv"],"fmt::bg":[0,1,1,"_CPPv4N3fmt2bgEN6detail10color_typeE"],"fmt::bg::background":[0,3,1,"_CPPv4N3fmt2bgEN6detail10color_typeE"],"fmt::detail::buffer":[0,4,1,"_CPPv4I0EN3fmt6detail6bufferE"],"fmt::detail::buffer::T":[0,2,1,"_CPPv4I0EN3fmt6detail6bufferE"],"fmt::detail::buffer::append":[0,1,1,"_CPPv4I0EN3fmt6detail6buffer6appendEvPK1UPK1U"],"fmt::detail::buffer::append::U":[0,2,1,"_CPPv4I0EN3fmt6detail6buffer6appendEvPK1UPK1U"],"fmt::detail::buffer::append::begin":[0,3,1,"_CPPv4I0EN3fmt6detail6buffer6appendEvPK1UPK1U"],"fmt::detail::buffer::append::end":[0,3,1,"_CPPv4I0EN3fmt6detail6buffer6appendEvPK1UPK1U"],"fmt::detail::buffer::capacity":[0,1,1,"_CPPv4NK3fmt6detail6buffer8capacityEv"],"fmt::detail::buffer::clear":[0,1,1,"_CPPv4N3fmt6detail6buffer5clearEv"],"fmt::detail::buffer::data":[0,1,1,"_CPPv4NK3fmt6detail6buffer4dataEv"],"fmt::detail::buffer::size":[0,1,1,"_CPPv4NK3fmt6detail6buffer4sizeEv"],"fmt::dynamic_format_arg_store":[0,4,1,"_CPPv4I0EN3fmt24dynamic_format_arg_storeE"],"fmt::dynamic_format_arg_store::Context":[0,2,1,"_CPPv4I0EN3fmt24dynamic_format_arg_storeE"],"fmt::fg":[0,1,1,"_CPPv4N3fmt2fgEN6detail10color_typeE"],"fmt::fg::foreground":[0,3,1,"_CPPv4N3fmt2fgEN6detail10color_typeE"],"fmt::format":[0,1,1,"_CPPv4IDpEN3fmt6formatENSt6stringE13format_stringIDp1TEDpRR1T"],"fmt::format::Locale":[0,2,1,"_CPPv4I0DpEN3fmt6formatENSt6stringERK6Locale13format_stringIDp1TEDpRR1T"],"fmt::format::T":[0,2,1,"_CPPv4IDpEN3fmt6formatENSt6stringE13format_stringIDp1TEDpRR1T"],"fmt::format::args":[0,3,1,"_CPPv4IDpEN3fmt6formatENSt6stringE13format_stringIDp1TEDpRR1T"],"fmt::format::fmt":[0,3,1,"_CPPv4IDpEN3fmt6formatENSt6stringE13format_stringIDp1TEDpRR1T"],"fmt::format::loc":[0,3,1,"_CPPv4I0DpEN3fmt6formatENSt6stringERK6Locale13format_stringIDp1TEDpRR1T"],"fmt::format_arg_store":[0,4,1,"_CPPv4I0DpEN3fmt16format_arg_storeE"],"fmt::format_arg_store::Args":[0,2,1,"_CPPv4I0DpEN3fmt16format_arg_storeE"],"fmt::format_arg_store::Context":[0,2,1,"_CPPv4I0DpEN3fmt16format_arg_storeE"],"fmt::format_args":[0,5,1,"_CPPv4N3fmt11format_argsE"],"fmt::format_context":[0,5,1,"_CPPv4N3fmt14format_contextE"],"fmt::format_string":[0,5,1,"_CPPv4IDpEN3fmt13format_stringE"],"fmt::format_string::Args":[0,2,1,"_CPPv4IDpEN3fmt13format_stringE"],"fmt::format_system_error":[0,1,1,"_CPPv4N3fmt19format_system_errorERN6detail6bufferIcEEiPKc"],"fmt::format_system_error::error_code":[0,3,1,"_CPPv4N3fmt19format_system_errorERN6detail6bufferIcEEiPKc"],"fmt::format_system_error::message":[0,3,1,"_CPPv4N3fmt19format_system_errorERN6detail6bufferIcEEiPKc"],"fmt::format_system_error::out":[0,3,1,"_CPPv4N3fmt19format_system_errorERN6detail6bufferIcEEiPKc"],"fmt::format_to":[0,1,1,"_CPPv4I0DpEN3fmt9format_toE8OutputIt8OutputIt13format_stringIDp1TEDpRR1T"],"fmt::format_to::Locale":[0,2,1,"_CPPv4I00DpEN3fmt9format_toE8OutputIt8OutputItRK6Locale13format_stringIDp1TEDpRR1T"],"fmt::format_to::OutputIt":[0,2,1,"_CPPv4I0DpEN3fmt9format_toE8OutputIt8OutputIt13format_stringIDp1TEDpRR1T"],"fmt::format_to::T":[0,2,1,"_CPPv4I0DpEN3fmt9format_toE8OutputIt8OutputIt13format_stringIDp1TEDpRR1T"],"fmt::format_to::args":[0,3,1,"_CPPv4I0DpEN3fmt9format_toE8OutputIt8OutputIt13format_stringIDp1TEDpRR1T"],"fmt::format_to::fmt":[0,3,1,"_CPPv4I0DpEN3fmt9format_toE8OutputIt8OutputIt13format_stringIDp1TEDpRR1T"],"fmt::format_to::loc":[0,3,1,"_CPPv4I00DpEN3fmt9format_toE8OutputIt8OutputItRK6Locale13format_stringIDp1TEDpRR1T"],"fmt::format_to::out":[0,3,1,"_CPPv4I0DpEN3fmt9format_toE8OutputIt8OutputIt13format_stringIDp1TEDpRR1T"],"fmt::format_to_n":[0,1,1,"_CPPv4I0DpEN3fmt11format_to_nE18format_to_n_resultI8OutputItE8OutputIt6size_t13format_stringIDp1TEDpRR1T"],"fmt::format_to_n::OutputIt":[0,2,1,"_CPPv4I0DpEN3fmt11format_to_nE18format_to_n_resultI8OutputItE8OutputIt6size_t13format_stringIDp1TEDpRR1T"],"fmt::format_to_n::T":[0,2,1,"_CPPv4I0DpEN3fmt11format_to_nE18format_to_n_resultI8OutputItE8OutputIt6size_t13format_stringIDp1TEDpRR1T"],"fmt::format_to_n::args":[0,3,1,"_CPPv4I0DpEN3fmt11format_to_nE18format_to_n_resultI8OutputItE8OutputIt6size_t13format_stringIDp1TEDpRR1T"],"fmt::format_to_n::fmt":[0,3,1,"_CPPv4I0DpEN3fmt11format_to_nE18format_to_n_resultI8OutputItE8OutputIt6size_t13format_stringIDp1TEDpRR1T"],"fmt::format_to_n::n":[0,3,1,"_CPPv4I0DpEN3fmt11format_to_nE18format_to_n_resultI8OutputItE8OutputIt6size_t13format_stringIDp1TEDpRR1T"],"fmt::format_to_n::out":[0,3,1,"_CPPv4I0DpEN3fmt11format_to_nE18format_to_n_resultI8OutputItE8OutputIt6size_t13format_stringIDp1TEDpRR1T"],"fmt::format_to_n_result":[0,4,1,"_CPPv4I0EN3fmt18format_to_n_resultE"],"fmt::format_to_n_result::OutputIt":[0,2,1,"_CPPv4I0EN3fmt18format_to_n_resultE"],"fmt::format_to_n_result::out":[0,6,1,"_CPPv4N3fmt18format_to_n_result3outE"],"fmt::format_to_n_result::size":[0,6,1,"_CPPv4N3fmt18format_to_n_result4sizeE"],"fmt::formatted_size":[0,1,1,"_CPPv4IDpEN3fmt14formatted_sizeE6size_t13format_stringIDp1TEDpRR1T"],"fmt::formatted_size::Locale":[0,2,1,"_CPPv4I0DpEN3fmt14formatted_sizeE6size_tRK6Locale13format_stringIDp1TEDpRR1T"],"fmt::formatted_size::T":[0,2,1,"_CPPv4IDpEN3fmt14formatted_sizeE6size_t13format_stringIDp1TEDpRR1T"],"fmt::formatted_size::args":[0,3,1,"_CPPv4IDpEN3fmt14formatted_sizeE6size_t13format_stringIDp1TEDpRR1T"],"fmt::formatted_size::fmt":[0,3,1,"_CPPv4IDpEN3fmt14formatted_sizeE6size_t13format_stringIDp1TEDpRR1T"],"fmt::formatted_size::loc":[0,3,1,"_CPPv4I0DpEN3fmt14formatted_sizeE6size_tRK6Locale13format_stringIDp1TEDpRR1T"],"fmt::fprintf":[0,1,1,"_CPPv4I0Dp0EN3fmt7fprintfEiPNSt4FILEERK1SDpRK1T"],"fmt::fprintf::Char":[0,2,1,"_CPPv4I0Dp0EN3fmt7fprintfEiPNSt4FILEERK1SDpRK1T"],"fmt::fprintf::S":[0,2,1,"_CPPv4I0Dp0EN3fmt7fprintfEiPNSt4FILEERK1SDpRK1T"],"fmt::fprintf::T":[0,2,1,"_CPPv4I0Dp0EN3fmt7fprintfEiPNSt4FILEERK1SDpRK1T"],"fmt::fprintf::args":[0,3,1,"_CPPv4I0Dp0EN3fmt7fprintfEiPNSt4FILEERK1SDpRK1T"],"fmt::fprintf::f":[0,3,1,"_CPPv4I0Dp0EN3fmt7fprintfEiPNSt4FILEERK1SDpRK1T"],"fmt::fprintf::fmt":[0,3,1,"_CPPv4I0Dp0EN3fmt7fprintfEiPNSt4FILEERK1SDpRK1T"],"fmt::gmtime":[0,1,1,"_CPPv4N3fmt6gmtimeENSt6time_tE"],"fmt::gmtime::time":[0,3,1,"_CPPv4N3fmt6gmtimeENSt6time_tE"],"fmt::group_digits":[0,1,1,"_CPPv4I0EN3fmt12group_digitsE17group_digits_viewI1TE1T"],"fmt::group_digits::T":[0,2,1,"_CPPv4I0EN3fmt12group_digitsE17group_digits_viewI1TE1T"],"fmt::group_digits::value":[0,3,1,"_CPPv4I0EN3fmt12group_digitsE17group_digits_viewI1TE1T"],"fmt::is_char":[0,4,1,"_CPPv4I0EN3fmt7is_charE"],"fmt::is_char::T":[0,2,1,"_CPPv4I0EN3fmt7is_charE"],"fmt::join":[0,1,1,"_CPPv4I0EN3fmt4joinE9join_viewIN6detail10iterator_tI5RangeEEN6detail10sentinel_tI5RangeEEERR5Range11string_view"],"fmt::join::It":[0,2,1,"_CPPv4I00EN3fmt4joinE9join_viewI2It8SentinelE2It8Sentinel11string_view"],"fmt::join::Range":[0,2,1,"_CPPv4I0EN3fmt4joinE9join_viewIN6detail10iterator_tI5RangeEEN6detail10sentinel_tI5RangeEEERR5Range11string_view"],"fmt::join::Sentinel":[0,2,1,"_CPPv4I00EN3fmt4joinE9join_viewI2It8SentinelE2It8Sentinel11string_view"],"fmt::join::begin":[0,3,1,"_CPPv4I00EN3fmt4joinE9join_viewI2It8SentinelE2It8Sentinel11string_view"],"fmt::join::end":[0,3,1,"_CPPv4I00EN3fmt4joinE9join_viewI2It8SentinelE2It8Sentinel11string_view"],"fmt::join::range":[0,3,1,"_CPPv4I0EN3fmt4joinE9join_viewIN6detail10iterator_tI5RangeEEN6detail10sentinel_tI5RangeEEERR5Range11string_view"],"fmt::join::sep":[0,3,1,"_CPPv4I0EN3fmt4joinE9join_viewIN6detail10iterator_tI5RangeEEN6detail10sentinel_tI5RangeEEERR5Range11string_view"],"fmt::localtime":[0,1,1,"_CPPv4N3fmt9localtimeENSt6time_tE"],"fmt::localtime::time":[0,3,1,"_CPPv4N3fmt9localtimeENSt6time_tE"],"fmt::make_format_args":[0,1,1,"_CPPv4I0DpEN3fmt16make_format_argsE16format_arg_storeI7ContextDp14remove_cvref_tI1TEEDpRR1T"],"fmt::make_format_args::Context":[0,2,1,"_CPPv4I0DpEN3fmt16make_format_argsE16format_arg_storeI7ContextDp14remove_cvref_tI1TEEDpRR1T"],"fmt::make_format_args::T":[0,2,1,"_CPPv4I0DpEN3fmt16make_format_argsE16format_arg_storeI7ContextDp14remove_cvref_tI1TEEDpRR1T"],"fmt::make_format_args::args":[0,3,1,"_CPPv4I0DpEN3fmt16make_format_argsE16format_arg_storeI7ContextDp14remove_cvref_tI1TEEDpRR1T"],"fmt::operator""_a":[0,1,1,"_CPPv4I_N15detail_exported12fixed_stringEEN3fmtli2_aEDav"],"fmt::operator""_a::Str":[0,2,1,"_CPPv4I_N15detail_exported12fixed_stringEEN3fmtli2_aEDav"],"fmt::operator""_cf":[0,1,1,"_CPPv4I_N15detail_exported12fixed_stringEEN3fmtli3_cfEDav"],"fmt::operator""_cf::Str":[0,2,1,"_CPPv4I_N15detail_exported12fixed_stringEEN3fmtli3_cfEDav"],"fmt::ostream":[0,4,1,"_CPPv4N3fmt7ostreamE"],"fmt::ostream::output_file":[0,1,1,"_CPPv4IDpEN3fmt7ostream11output_fileE7ostream12cstring_viewDp1T"],"fmt::ostream::output_file::T":[0,2,1,"_CPPv4IDpEN3fmt7ostream11output_fileE7ostream12cstring_viewDp1T"],"fmt::ostream::output_file::params":[0,3,1,"_CPPv4IDpEN3fmt7ostream11output_fileE7ostream12cstring_viewDp1T"],"fmt::ostream::output_file::path":[0,3,1,"_CPPv4IDpEN3fmt7ostream11output_fileE7ostream12cstring_viewDp1T"],"fmt::ostream::print":[0,1,1,"_CPPv4IDpEN3fmt7ostream5printEv13format_stringIDp1TEDpRR1T"],"fmt::ostream::print::T":[0,2,1,"_CPPv4IDpEN3fmt7ostream5printEv13format_stringIDp1TEDpRR1T"],"fmt::ostream::print::args":[0,3,1,"_CPPv4IDpEN3fmt7ostream5printEv13format_stringIDp1TEDpRR1T"],"fmt::ostream::print::fmt":[0,3,1,"_CPPv4IDpEN3fmt7ostream5printEv13format_stringIDp1TEDpRR1T"],"fmt::print":[0,1,1,"_CPPv4IDpEN3fmt5printEvRNSt7ostreamE13format_stringIDp1TEDpRR1T"],"fmt::print::Args":[0,2,1,"_CPPv4I0DpEN3fmt5printEvRK10text_styleRK1SDpRK4Args"],"fmt::print::S":[0,2,1,"_CPPv4I0DpEN3fmt5printEvRK10text_styleRK1SDpRK4Args"],"fmt::print::T":[0,2,1,"_CPPv4IDpEN3fmt5printEvRNSt7ostreamE13format_stringIDp1TEDpRR1T"],"fmt::print::args":[0,3,1,"_CPPv4IDpEN3fmt5printEvRNSt7ostreamE13format_stringIDp1TEDpRR1T"],"fmt::print::f":[0,3,1,"_CPPv4IDpEN3fmt5printEvPNSt4FILEE13format_stringIDp1TEDpRR1T"],"fmt::print::fmt":[0,3,1,"_CPPv4IDpEN3fmt5printEvRNSt7ostreamE13format_stringIDp1TEDpRR1T"],"fmt::print::format_str":[0,3,1,"_CPPv4I0DpEN3fmt5printEvRK10text_styleRK1SDpRK4Args"],"fmt::print::os":[0,3,1,"_CPPv4IDpEN3fmt5printEvRNSt7ostreamE13format_stringIDp1TEDpRR1T"],"fmt::print::ts":[0,3,1,"_CPPv4I0DpEN3fmt5printEvRK10text_styleRK1SDpRK4Args"],"fmt::printf":[0,1,1,"_CPPv4I0DpEN3fmt6printfEiRK1SDpRK1T"],"fmt::printf::S":[0,2,1,"_CPPv4I0DpEN3fmt6printfEiRK1SDpRK1T"],"fmt::printf::T":[0,2,1,"_CPPv4I0DpEN3fmt6printfEiRK1SDpRK1T"],"fmt::printf::args":[0,3,1,"_CPPv4I0DpEN3fmt6printfEiRK1SDpRK1T"],"fmt::printf::fmt":[0,3,1,"_CPPv4I0DpEN3fmt6printfEiRK1SDpRK1T"],"fmt::ptr":[0,1,1,"_CPPv4I0EN3fmt3ptrEPKvRKNSt10shared_ptrI1TEE"],"fmt::ptr::Deleter":[0,2,1,"_CPPv4I00EN3fmt3ptrEPKvRKNSt10unique_ptrI1T7DeleterEE"],"fmt::ptr::T":[0,2,1,"_CPPv4I0EN3fmt3ptrEPKvRKNSt10shared_ptrI1TEE"],"fmt::ptr::p":[0,3,1,"_CPPv4I0EN3fmt3ptrEPKvRKNSt10shared_ptrI1TEE"],"fmt::runtime":[0,1,1,"_CPPv4N3fmt7runtimeE11string_view"],"fmt::runtime::s":[0,3,1,"_CPPv4N3fmt7runtimeE11string_view"],"fmt::sprintf":[0,1,1,"_CPPv4I0Dp0EN3fmt7sprintfENSt12basic_stringI4CharEERK1SDpRK1T"],"fmt::sprintf::Char":[0,2,1,"_CPPv4I0Dp0EN3fmt7sprintfENSt12basic_stringI4CharEERK1SDpRK1T"],"fmt::sprintf::S":[0,2,1,"_CPPv4I0Dp0EN3fmt7sprintfENSt12basic_stringI4CharEERK1SDpRK1T"],"fmt::sprintf::T":[0,2,1,"_CPPv4I0Dp0EN3fmt7sprintfENSt12basic_stringI4CharEERK1SDpRK1T"],"fmt::sprintf::args":[0,3,1,"_CPPv4I0Dp0EN3fmt7sprintfENSt12basic_stringI4CharEERK1SDpRK1T"],"fmt::sprintf::fmt":[0,3,1,"_CPPv4I0Dp0EN3fmt7sprintfENSt12basic_stringI4CharEERK1SDpRK1T"],"fmt::streamed":[0,1,1,"_CPPv4I0EN3fmt8streamedEN6detail13streamed_viewI1TEERK1T"],"fmt::streamed::T":[0,2,1,"_CPPv4I0EN3fmt8streamedEN6detail13streamed_viewI1TEERK1T"],"fmt::streamed::value":[0,3,1,"_CPPv4I0EN3fmt8streamedEN6detail13streamed_viewI1TEERK1T"],"fmt::string_view":[0,5,1,"_CPPv4N3fmt11string_viewE"],"fmt::styled":[0,1,1,"_CPPv4I0EN3fmt6styledEN6detail10styled_argI14remove_cvref_tI1TEEERK1T10text_style"],"fmt::styled::T":[0,2,1,"_CPPv4I0EN3fmt6styledEN6detail10styled_argI14remove_cvref_tI1TEEERK1T10text_style"],"fmt::styled::ts":[0,3,1,"_CPPv4I0EN3fmt6styledEN6detail10styled_argI14remove_cvref_tI1TEEERK1T10text_style"],"fmt::styled::value":[0,3,1,"_CPPv4I0EN3fmt6styledEN6detail10styled_argI14remove_cvref_tI1TEEERK1T10text_style"],"fmt::system_error":[0,1,1,"_CPPv4IDpEN3fmt12system_errorENSt12system_errorEi13format_stringIDp1TEDpRR1T"],"fmt::system_error::T":[0,2,1,"_CPPv4IDpEN3fmt12system_errorENSt12system_errorEi13format_stringIDp1TEDpRR1T"],"fmt::system_error::args":[0,3,1,"_CPPv4IDpEN3fmt12system_errorENSt12system_errorEi13format_stringIDp1TEDpRR1T"],"fmt::system_error::error_code":[0,3,1,"_CPPv4IDpEN3fmt12system_errorENSt12system_errorEi13format_stringIDp1TEDpRR1T"],"fmt::system_error::fmt":[0,3,1,"_CPPv4IDpEN3fmt12system_errorENSt12system_errorEi13format_stringIDp1TEDpRR1T"],"fmt::to_string":[0,1,1,"_CPPv4I0EN3fmt9to_stringENSt6stringERK1T"],"fmt::to_string::T":[0,2,1,"_CPPv4I0EN3fmt9to_stringENSt6stringERK1T"],"fmt::to_string::value":[0,3,1,"_CPPv4I0EN3fmt9to_stringENSt6stringERK1T"],"fmt::to_wstring":[0,1,1,"_CPPv4I0EN3fmt10to_wstringENSt7wstringERK1T"],"fmt::to_wstring::T":[0,2,1,"_CPPv4I0EN3fmt10to_wstringENSt7wstringERK1T"],"fmt::to_wstring::value":[0,3,1,"_CPPv4I0EN3fmt10to_wstringENSt7wstringERK1T"],"fmt::underlying":[0,1,1,"_CPPv4I0EN3fmt10underlyingE12underlying_tI4EnumE4Enum"],"fmt::underlying::Enum":[0,2,1,"_CPPv4I0EN3fmt10underlyingE12underlying_tI4EnumE4Enum"],"fmt::underlying::e":[0,3,1,"_CPPv4I0EN3fmt10underlyingE12underlying_tI4EnumE4Enum"],"fmt::vformat":[0,1,1,"_CPPv4N3fmt7vformatE11string_view11format_args"],"fmt::vformat::args":[0,3,1,"_CPPv4N3fmt7vformatE11string_view11format_args"],"fmt::vformat::fmt":[0,3,1,"_CPPv4N3fmt7vformatE11string_view11format_args"],"fmt::vprint":[0,1,1,"_CPPv4N3fmt6vprintEPNSt4FILEE11string_view11format_args"],"fmt::vprint::args":[0,3,1,"_CPPv4N3fmt6vprintEPNSt4FILEE11string_view11format_args"],"fmt::vprint::f":[0,3,1,"_CPPv4N3fmt6vprintEPNSt4FILEE11string_view11format_args"],"fmt::vprint::fmt":[0,3,1,"_CPPv4N3fmt6vprintEPNSt4FILEE11string_view11format_args"],"fmt::wformat_context":[0,5,1,"_CPPv4N3fmt15wformat_contextE"],"fmt::wstring_view":[0,5,1,"_CPPv4N3fmt12wstring_viewE"],FMT_COMPILE:[0,0,1,"c.FMT_COMPILE"],FMT_STRING:[0,0,1,"c.FMT_STRING"]}},objnames:{"0":["c","macro","C macro"],"1":["cpp","function","C++ function"],"2":["cpp","templateParam","templateParam"],"3":["cpp","functionParam","functionParam"],"4":["cpp","class","C++ class"],"5":["cpp","type","C++ type"],"6":["cpp","member","C++ member"]},objtypes:{"0":"c:macro","1":"cpp:function","2":"cpp:templateParam","3":"cpp:functionParam","4":"cpp:class","5":"cpp:type","6":"cpp:member"},terms:{"000":0,"0000":3,"000000120":3,"001":3,"0430":3,"04x":3,"052":3,"0b101010":3,"0x00":3,"0x14":3,"0x1e":3,"0x2a":3,"0xa":3,"100":3,"1000000":0,"100m":0,"101":3,"101010":3,"104":3,"1070":2,"108":3,"111":3,"12345":0,"1234567890":3,"140000":3,"15min":0,"1900":3,"1955":3,"2004":3,"2010":3,"2012":0,"2015":2,"2020":0,"234":3,"30s":0,"345":0,"42s":0,"567":3,"8601":3,"890":3,"\u044e":2,"boolean":3,"break":0,"byte":3,"case":[0,3,4],"char":[0,2,3],"class":0,"const":0,"default":[0,3],"enum":0,"final":3,"float":[0,3],"function":[0,2,3],"import":4,"int":[0,2,3],"long":2,"new":[0,3],"null":[0,3],"public":0,"return":[0,2],"static":4,"switch":[0,3],"throw":[0,2],"true":[3,4],"void":0,"while":2,But:3,For:[0,2,3,4],The:[0,2,3,4],Then:[0,4],These:[2,3],Use:0,Uses:3,Using:[0,3],__cpp_lib_vari:0,__file__:0,__line__:0,__va_args__:0,_cf:0,abbrevi:3,abil:0,about:0,abov:3,abra:3,abracadabra:3,absent:3,accept:0,access:3,accord:[0,3],across:2,activ:4,add:[2,3,4],add_subdirectori:4,addit:[0,3],advanc:0,advance_to:0,after:[3,4],alia:[0,2],align:3,all:[0,2,3,4],allow:[0,2,3],alreadi:4,also:[0,2,3],altern:[0,2,3,4],although:3,alwai:[2,3],american_beauti:0,android:1,ani:[0,3],ansi:0,answer:[0,2],anyth:3,api:1,appear:3,append:0,appli:[0,2],appropri:[0,3,4],apt:4,archiv:4,arg:[0,2],arg_id:3,argument:[2,3],arrai:0,asciz:2,associ:3,assum:3,assumpt:0,auto:[0,2,3],automat:[0,2,3],avail:[0,2,3,4],avoid:[0,2],awar:3,back:0,back_insert:[0,2],background:0,base:[2,3],basic_format_arg:0,basic_format_context:0,basic_format_parse_context:0,basic_format_str:0,basic_memory_buff:0,basic_printf_parse_context:0,basic_str:0,basic_string_view:[0,2],bdep:4,becaus:[0,2],been:[0,2,4],befor:3,begin:0,behavior:3,behind:3,being:[0,2,3],benefici:0,better:0,between:3,bigit:0,bigits_capac:0,bin:3,binari:[0,3],blue:0,bold:0,boost:2,bootstrap:4,both:[0,2,3,4],bottleneck:0,box:3,brace:[0,3],branch:0,brew:4,bring:3,buf:0,buffer:[0,2],buffer_context:0,buffer_s:0,build2:1,build:1,build_shared_lib:4,buildfil:4,built:[0,3,4],cad:3,calcul:0,calendar:[0,3],call:[0,2],can:[0,2,3,4],cannot:[0,2,3],capac:0,caus:3,center:3,centuri:3,cerr:0,certain:3,chang:0,char_t:0,char_trait:0,char_typ:0,charact:[0,2,3],check_arg_id:0,choic:4,chrono:[0,1],chrono_format_spec:3,chrono_liter:0,chrono_spec:3,chrono_typ:3,clang:2,clean:4,clear:0,client:0,clock:3,clone:4,cluster:0,cmake:1,cmake_position_independent_cod:4,cmakelist:4,code:[0,3,4],collect:0,collis:0,colon:3,color:1,color_typ:0,com:4,combin:4,comma:3,command:3,commerci:2,common:[2,3],commun:[0,4],compar:0,comparison:[2,3],compat:1,compil:[1,2,4],compile_parse_context:0,compon:3,composit:0,comput:0,conda:1,configur:4,consid:3,consider:0,consist:[0,2],constev:0,constexpr:[0,2],construct:[0,2],consum:4,contain:[0,2,3],content:[0,3],context:0,contigu:0,contributor:4,control:4,conveni:0,convers:[0,3],conversion_spec:3,convert:[0,3],coordin:0,copi:3,copyabl:0,core:[1,2,4],correspond:0,count:[0,3],counter:0,counterpart:0,cppget:4,creat:[0,2,4],css:4,cstring_view:0,ctx:0,ctx_arg:0,curli:3,current:[0,3,4],custom:4,custom_alloc:0,custom_memory_buff:0,custom_str:0,cxx:4,cyril:2,dai:[0,3],dangl:0,data:[0,2,3],date:[1,3,4],dbuild_shared_lib:4,dcmake_position_independent_cod:4,decim:3,decltyp:2,defin:[2,3],delet:[0,2],demo:0,depend:[0,2,3,4],describ:[0,3,4],descript:0,design:3,desir:2,detail:[0,2,3,4],detail_export:0,detect:4,determin:3,differ:[0,3],digit:3,directli:0,directori:[0,4],displai:3,divid:3,divis:3,doc:4,document:1,doe:[0,2,3],doesn:[0,4],don:[0,2,4],doubl:[0,2,3],download:4,doxygen:4,draw:3,durat:[0,3],dynam:[0,3],dynamic_format_arg_stor:0,each:3,easi:4,eax:2,edi:2,edx:2,effect:3,effici:0,either:3,elaps:0,element:0,emphasi:0,empti:[0,3],en_u:[0,3],enabl:[0,3],enable_if_t:0,end:0,environ:4,epoch:0,equal:[0,4],equival:[0,3],eras:0,errno:[0,2],error:2,error_cod:0,escap:[0,3],esi:2,even:[0,3],everi:0,exactli:3,exampl:[0,1,2,4],except:[0,2,3],exclud:4,exclude_from_al:4,exist:0,exot:0,expect:0,expens:0,expon:3,exponenti:0,express:0,extens:[0,4],extern:2,extract:3,fail:0,fall:0,fals:3,false_typ:0,famili:2,fast:[0,2],faster:2,featur:[0,2],felt:2,field:[0,3],file:[0,2,4],filenam:0,filesystem:0,fill:3,film:0,find_packag:4,first:[0,3,4],fix:[0,3],fixed_buffer_trait:0,fixed_str:0,flag:0,floor:3,fmt:[0,2,3,4],fmt_:0,fmt_compil:0,fmt_enforce_compile_str:0,fmt_string:[0,2],fmt_test:4,follow:[0,2,3,4],foo:0,footprint:0,fopen:0,forc:[0,3],foreground:0,forg:4,form:[0,3],formal:3,format:[1,4],format_a:0,format_arg:[0,2],format_arg_stor:0,format_context:0,format_error:[2,3],format_parse_context:0,format_spec:3,format_str:0,format_system_error:0,format_to:[0,2],format_to_n:0,format_to_n_result:0,formatt:[0,3],formatted_s:0,forti:2,four:3,fprintf:0,fraction:3,freeli:4,friend:0,from:[0,3,4],full:[0,3],fulli:[0,2],futur:0,gcc:2,gener:[0,3,4],generic_categori:0,get:[0,3,4],get_id:0,git:4,github:[2,4],given:[0,3],global:0,gmtime:0,godbolt:[0,2],goodby:2,grammar:3,graphem:0,green:0,group_digit:0,group_digits_view:0,grow:0,guid:0,happen:2,happi:2,has:[0,2,3,4],hasn:0,have:[0,3,4],header:[0,2,4],hello:[2,3],here:2,hex:3,hexadecim:3,hierarchi:0,high:3,highli:2,hold:[0,4],hole:2,homebrew:1,horizont:3,hour:3,house_of_card:0,how:3,html:4,http:[0,4],id_continu:3,id_start:3,identifi:3,implement:[0,2,3],implicit:0,implicitli:[0,3],improv:0,includ:[0,2,3,4],increas:0,independ:[0,4],index:0,indic:3,individu:3,inf:[2,3],infin:[2,3],inform:3,inherit:0,initi:0,inl:4,inlin:0,inline_buffer_s:0,input:3,insert:[0,3],instal:1,instead:[0,2,3,4],integ:[0,2,3],integr:[0,4],interest:0,intern:0,interpret:3,introduct:4,invalid:0,invok:4,iostream:2,is_base_of:0,is_char:0,is_str:0,iso:3,issu:[0,4],iter:0,iterator_buff:0,iterator_t:0,its:[0,2,3,4],itself:0,jan:3,join:0,join_view:0,just:[2,3],kept:4,kevin_namespaci:0,languag:[0,1],larg:3,last:3,later:0,latter:0,lead:3,learn:2,least:0,left:3,length:0,less:[3,4],letter:[2,3],lhelper:1,lib:4,librari:[1,2],licens:2,lifetim:0,like:[0,2,4],line:[0,3],linux:4,liter:[2,3],literal_char:3,loc:0,local:[2,3],locale_ref:0,localtim:0,locat:4,log:0,loki:2,look:0,lower:3,mac:4,maco:4,macro:0,made:3,madeup:0,magnitud:3,mai:[0,3],main:[0,2,4],maintain:2,make:[0,4],make_format_arg:0,makefil:4,manag:[2,4],mani:[0,3],manifest:4,manual:0,mark:0,match:[0,3],maximum:3,mean:3,member:[0,4],memori:[0,2],memory_buff:[0,2],messag:0,method:0,microsecond:3,microsoft:4,mini:1,minim:0,minimum:3,minu:3,minut:3,mit:2,mkdir:4,mode:0,modifi:3,moment:[0,2],mondai:3,monost:0,month:[0,3],more:[0,2,4],most:[0,3],mov:2,move:0,msbuild:4,msvc:2,must:3,my_log:0,mylib:4,name:[2,3,4],named_arg:0,namespac:[0,2],nan:3,narrow:2,nativ:4,ndk:1,nearli:0,need:[3,4],neg:3,neither:2,nest:3,new_capac:0,newcom:4,next:[0,3],next_arg_id:0,nix:4,noexcept:0,non:[0,3],none:3,nonneg:3,normal:[0,3],notat:3,note:[0,3],noth:2,nov:3,novemb:3,now:4,npm:4,nullptr:0,number:[0,2,3],numer:[2,3],numeric_limit:2,object:[0,3],occasion:0,oct:3,octal:3,offer:2,offset:[2,3],older:[0,2],omit:[0,2,3],on_error:0,onc:4,one:[0,2,3,4],onli:[0,2,3,4],open:[0,2],oper:0,option:[0,3,4],order:[0,3],ordinari:0,org:[0,4],ostream:[1,2],ostream_formatt:0,other:[0,3,4],otherwis:[0,3],out:[0,2,4],output:[0,2,3,4],output_fil:0,outputit:0,over:0,overflow:2,overload:0,overrid:0,own:[0,3,4],packag:4,pad:3,panic:[0,2],param:0,paramet:0,parameter:0,pars:[0,3],part:0,particular:3,pass:[0,2],past:0,path:[0,4],per:2,perform:[0,2],permiss:2,pip:4,pkg:4,place:[0,3],platform:[0,2,4],pleas:[0,4],plugin:4,pod:0,point:[0,3],pointer:[0,2,3],port:4,posit:[0,2,3,4],posix:0,possibl:0,pre:0,preced:[0,3],precis:3,prefix:[0,3],preprocessor:0,prerequisit:4,presenc:3,present:[0,3],preserv:0,prevent:[0,2],previou:4,print:[0,2,3],printf:[1,2,3],prior:3,privat:4,process:0,produc:[2,3],project:[2,4],protect:0,provid:[0,2,3,4],ptr:[0,2],publish:4,pull:4,push_back:0,python:[0,2,4],quot:3,qword:2,r7vvge1v7:0,rang:[1,4],rare:2,rather:2,rcx:2,reach:0,read:4,recent:2,recip:4,recogn:0,recommend:0,red:0,reduc:0,refer:[1,2,3],regular:0,relax:2,releas:4,reli:2,remov:3,remove_cvref_t:0,repeat:3,replac:[0,3],report:[0,2],repositori:[2,4],repres:[0,2,3],represent:3,request:4,requir:[0,2,4],reserv:0,resid:0,resiz:0,respect:3,result:[0,2,3],ret:2,reus:0,revis:4,right:[2,3],role:4,round:3,rsp:2,run:4,runtim:0,runtime_format_str:0,rvalu:2,safe:[0,2],safer:2,sai:2,same:[0,3],sat:3,saturdai:3,scientif:3,script:4,se7en:0,second:[0,3],section:[3,4],see:[0,2,3,4],self:2,sentinel:0,sentinel_t:0,sep:0,separ:[0,3],sequenc:[0,3],set:[0,2,3,4],sever:[2,4],shalt:3,share:4,shared_ptr:0,should:[0,3,4],shouldn:0,show:3,shown:3,sign:3,signal:3,signific:3,similar:[0,2,3],simpl:3,simpler:2,sinc:0,singl:3,size:[0,2,3],size_t:0,slightli:2,sln:4,small:[0,2],softwar:4,some:[3,4],sourc:[0,2,4],space:3,special:[0,3],specif:[0,1],specifi:[0,3],spirit:2,sprintf:[0,2],squishi:0,src:4,stabl:4,standard:[1,2,3],start:[0,3,4],std:[1,2,3],stderr:[0,2],stdio:2,stdout:[0,2],store:0,str:[0,2],stream:[0,2],streamed_view:0,strftime:0,string:[1,2],string_view:0,struct:0,studio:4,style:1,styled_arg:0,sub:2,subclass:0,subdirectori:4,subset:0,sudo:4,suffix:[2,3],sundai:3,support:[1,2,3],sure:0,surround:[0,3],syntax:[0,1,2],system:[1,2,4],system_error:0,tab:3,take:0,target:4,target_link_librari:4,team:4,templat:[0,2],term:3,termin:[1,3],terser:2,test:4,text:[1,3],text_styl:0,textual:3,than:[0,2,3],thank:2,thei:[0,3],them:4,therefor:0,thi:[0,2,3,4],this_thread:0,thou:3,thousand:[0,3],thread:0,three:[2,3],thrown:3,time:[1,2,3],time_point:0,time_t:0,tm_hour:3,tm_mdai:3,tm_min:3,tm_mon:3,tm_sec:3,tm_year:3,to_str:0,to_wstr:0,too:3,toolchain:4,total:0,trail:[2,3],trait:[0,2],translat:3,treat:3,trivial:0,trunc:0,truncat:0,tupl:1,two:[2,3],txt:[0,4],type:[1,2,3],type_identity_t:0,type_trait:0,typenam:0,typic:4,ubuntu:4,unchang:3,underli:0,underlying_spec:3,underlying_t:0,unicod:[2,3],unique_ptr:0,unit:3,univers:0,unix:4,unknown:0,unless:3,unlik:0,unreleas:4,until:0,unus:0,updat:4,upper:3,uppercas:3,usag:1,use:[0,2,3,4],used:[0,2,3,4],useful:[2,4],user:2,uses:[0,3],using:[0,2,3,4],usual:4,utc:[0,3],utf:[0,3],vajfweg4b:0,valid:3,valu:[0,2,3],vari:0,variabl:[0,4],variad:2,variou:3,vcpkg:1,vcproj:4,vector:[0,3],version:[0,2,4],vformat:0,vformat_to:0,via:[0,3],view:0,virtual:0,virtualenv:4,visual:4,vlog:0,vprint:[0,2],want:4,wchar_t:1,week:3,weekdai:3,well:[0,3],wformat_context:0,what:2,when:[0,3],where:[0,3,4],whether:3,which:[0,2,3],whose:3,wide:[2,4],width:[0,3],window:4,within:3,without:3,word:3,work:4,workflow:4,world:[2,3],wrap:0,write:[0,2],written:2,wronli:0,wstring:0,wstring_view:0,www:4,x42e:2,xchar:0,xcode:4,xcodeproj:4,xor:2,year:[0,3],yet:0,you:[0,2,3,4],your:[0,2,4],zero:3,zone:3},titles:["API Reference","Contents","Overview","Format String Syntax","Usage"],titleterms:{Use:2,alloc:0,android:4,api:[0,2],argument:0,base:0,binari:2,build2:4,build:4,check:0,chrono:3,cmake:4,code:2,color:0,compact:2,compat:0,compil:0,conda:4,content:1,core:0,custom:0,date:0,defin:0,document:4,eas:2,error:0,exampl:3,format:[0,2,3],homebrew:4,instal:4,languag:3,legaci:0,lhelper:4,librari:[0,4],list:0,liter:0,local:0,mini:3,name:0,ndk:4,ostream:0,overview:2,portabl:2,printf:0,rang:[0,3],refer:0,safeti:2,specif:3,standard:0,std:0,string:[0,3],style:0,support:0,syntax:3,system:0,termin:0,text:0,time:0,tupl:0,type:0,usag:4,user:0,util:0,variant:0,vcpkg:4,wchar_t:0}}) \ No newline at end of file diff --git a/vendor/Fmt/doc/html/syntax.html b/vendor/Fmt/doc/html/syntax.html new file mode 100644 index 00000000..0011b842 --- /dev/null +++ b/vendor/Fmt/doc/html/syntax.html @@ -0,0 +1,819 @@ + + + + + + + + + + + + Format String Syntax — fmt 10.0.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+

Format String Syntax¶

+

Formatting functions such as fmt::format() and +fmt::print() use the same format string syntax described in this +section.

+

Format strings contain “replacement fields†surrounded by curly braces {}. +Anything that is not contained in braces is considered literal text, which is +copied unchanged to the output. If you need to include a brace character in the +literal text, it can be escaped by doubling: {{ and }}.

+

The grammar for a replacement field is as follows:

+
+replacement_field ::=  "{" [arg_id] [":" (format_spec | chrono_format_spec)] "}"
+arg_id            ::=  integer | identifier
+integer           ::=  digit+
+digit             ::=  "0"..."9"
+identifier        ::=  id_start id_continue*
+id_start          ::=  "a"..."z" | "A"..."Z" | "_"
+id_continue       ::=  id_start | digit
+
+

In less formal terms, the replacement field can start with an arg_id +that specifies the argument whose value is to be formatted and inserted into +the output instead of the replacement field. +The arg_id is optionally followed by a format_spec, which is preceded by a +colon ':'. These specify a non-default format for the replacement value.

+

See also the Format Specification Mini-Language section.

+

If the numerical arg_ids in a format string are 0, 1, 2, … in sequence, +they can all be omitted (not just some) and the numbers 0, 1, 2, … will be +automatically inserted in that order.

+

Named arguments can be referred to by their names or indices.

+

Some simple format string examples:

+
"First, thou shalt count to {0}" // References the first argument
+"Bring me a {}"                  // Implicitly references the first argument
+"From {} to {}"                  // Same as "From {0} to {1}"
+
+
+

The format_spec field contains a specification of how the value should be +presented, including such details as field width, alignment, padding, decimal +precision and so on. Each value type can define its own “formatting +mini-language†or interpretation of the format_spec.

+

Most built-in types support a common formatting mini-language, which is +described in the next section.

+

A format_spec field can also include nested replacement fields in certain +positions within it. These nested replacement fields can contain only an +argument id; format specifications are not allowed. This allows the formatting +of a value to be dynamically specified.

+

See the Format Examples section for some examples.

+
+

Format Specification Mini-Language¶

+

“Format specifications†are used within replacement fields contained within a +format string to define how individual values are presented (see +Format String Syntax). Each formattable type may define how the format +specification is to be interpreted.

+

Most built-in types implement the following options for format specifications, +although some of the formatting options are only supported by the numeric types.

+

The general form of a standard format specifier is:

+
+format_spec ::=  [[fill]align][sign]["#"]["0"][width]["." precision]["L"][type]
+fill        ::=  <a character other than '{' or '}'>
+align       ::=  "<" | ">" | "^"
+sign        ::=  "+" | "-" | " "
+width       ::=  integer | "{" [arg_id] "}"
+precision   ::=  integer | "{" [arg_id] "}"
+type        ::=  "a" | "A" | "b" | "B" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" |
+                 "o" | "p" | "s" | "x" | "X"
+
+

The fill character can be any Unicode code point other than '{' or +'}'. The presence of a fill character is signaled by the character following +it, which must be one of the alignment options. If the second character of +format_spec is not a valid alignment option, then it is assumed that both the +fill character and the alignment option are absent.

+

The meaning of the various alignment options is as follows:

+ ++++ + + + + + + + + + + + + + + + + +

Option

Meaning

'<'

Forces the field to be left-aligned within the available +space (this is the default for most objects).

'>'

Forces the field to be right-aligned within the +available space (this is the default for numbers).

'^'

Forces the field to be centered within the available +space.

+

Note that unless a minimum field width is defined, the field width will always +be the same size as the data to fill it, so that the alignment option has no +meaning in this case.

+

The sign option is only valid for floating point and signed integer types, +and can be one of the following:

+ ++++ + + + + + + + + + + + + + + + + +

Option

Meaning

'+'

indicates that a sign should be used for both +nonnegative as well as negative numbers.

'-'

indicates that a sign should be used only for negative +numbers (this is the default behavior).

space

indicates that a leading space should be used on +nonnegative numbers, and a minus sign on negative numbers.

+

The '#' option causes the “alternate form†to be used for the +conversion. The alternate form is defined differently for different +types. This option is only valid for integer and floating-point types. +For integers, when binary, octal, or hexadecimal output is used, this +option adds the prefix respective "0b" ("0B"), "0", or +"0x" ("0X") to the output value. Whether the prefix is +lower-case or upper-case is determined by the case of the type +specifier, for example, the prefix "0x" is used for the type 'x' +and "0X" is used for 'X'. For floating-point numbers the +alternate form causes the result of the conversion to always contain a +decimal-point character, even if no digits follow it. Normally, a +decimal-point character appears in the result of these conversions +only if a digit follows it. In addition, for 'g' and 'G' +conversions, trailing zeros are not removed from the result.

+

width is a decimal integer defining the minimum field width. If not +specified, then the field width will be determined by the content.

+

Preceding the width field by a zero ('0') character enables sign-aware +zero-padding for numeric types. It forces the padding to be placed after the +sign or base (if any) but before the digits. This is used for printing fields in +the form ‘+000000120’. This option is only valid for numeric types and it has no +effect on formatting of infinity and NaN.

+

The precision is a decimal number indicating how many digits should be +displayed after the decimal point for a floating-point value formatted with +'f' and 'F', or before and after the decimal point for a floating-point +value formatted with 'g' or 'G'. For non-number types the field +indicates the maximum field size - in other words, how many characters will be +used from the field content. The precision is not allowed for integer, +character, Boolean, and pointer values. Note that a C string must be +null-terminated even if precision is specified.

+

The 'L' option uses the current locale setting to insert the appropriate +number separator characters. This option is only valid for numeric types.

+

Finally, the type determines how the data should be presented.

+

The available string presentation types are:

+ ++++ + + + + + + + + + + + + + +

Type

Meaning

's'

String format. This is the default type for strings and +may be omitted.

none

The same as 's'.

+

The available character presentation types are:

+ ++++ + + + + + + + + + + + + + +

Type

Meaning

'c'

Character format. This is the default type for +characters and may be omitted.

none

The same as 'c'.

+

The available integer presentation types are:

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Type

Meaning

'b'

Binary format. Outputs the number in base 2. Using the +'#' option with this type adds the prefix "0b" +to the output value.

'B'

Binary format. Outputs the number in base 2. Using the +'#' option with this type adds the prefix "0B" +to the output value.

'c'

Character format. Outputs the number as a character.

'd'

Decimal integer. Outputs the number in base 10.

'o'

Octal format. Outputs the number in base 8.

'x'

Hex format. Outputs the number in base 16, using +lower-case letters for the digits above 9. Using the +'#' option with this type adds the prefix "0x" +to the output value.

'X'

Hex format. Outputs the number in base 16, using +upper-case letters for the digits above 9. Using the +'#' option with this type adds the prefix "0X" +to the output value.

none

The same as 'd'.

+

Integer presentation types can also be used with character and Boolean values. +Boolean values are formatted using textual representation, either true or +false, if the presentation type is not specified.

+

The available presentation types for floating-point values are:

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Type

Meaning

'a'

Hexadecimal floating point format. Prints the number in +base 16 with prefix "0x" and lower-case letters for +digits above 9. Uses 'p' to indicate the exponent.

'A'

Same as 'a' except it uses upper-case letters for +the prefix, digits above 9 and to indicate the exponent.

'e'

Exponent notation. Prints the number in scientific +notation using the letter ‘e’ to indicate the exponent.

'E'

Exponent notation. Same as 'e' except it uses an +upper-case 'E' as the separator character.

'f'

Fixed point. Displays the number as a fixed-point +number.

'F'

Fixed point. Same as 'f', but converts nan to +NAN and inf to INF.

'g'

General format. For a given precision p >= 1, +this rounds the number to p significant digits and +then formats the result in either fixed-point format +or in scientific notation, depending on its magnitude.

+

A precision of 0 is treated as equivalent to a +precision of 1.

+

'G'

General format. Same as 'g' except switches to +'E' if the number gets too large. The +representations of infinity and NaN are uppercased, too.

none

Similar to 'g', except that the default precision is +as high as needed to represent the particular value.

+

The available presentation types for pointers are:

+ ++++ + + + + + + + + + + + + + +

Type

Meaning

'p'

Pointer format. This is the default type for +pointers and may be omitted.

none

The same as 'p'.

+
+
+

Chrono Format Specifications¶

+

Format specifications for chrono duration and time point types as well as +std::tm have the following syntax:

+
+chrono_format_spec ::=  [[fill]align][width]["." precision][chrono_specs]
+chrono_specs       ::=  [chrono_specs] conversion_spec | chrono_specs literal_char
+conversion_spec    ::=  "%" [modifier] chrono_type
+literal_char       ::=  <a character other than '{', '}' or '%'>
+modifier           ::=  "E" | "O"
+chrono_type        ::=  "a" | "A" | "b" | "B" | "c" | "C" | "d" | "D" | "e" | "F" |
+                        "g" | "G" | "h" | "H" | "I" | "j" | "m" | "M" | "n" | "p" |
+                        "q" | "Q" | "r" | "R" | "S" | "t" | "T" | "u" | "U" | "V" |
+                        "w" | "W" | "x" | "X" | "y" | "Y" | "z" | "Z" | "%"
+
+

Literal chars are copied unchanged to the output. Precision is valid only for +std::chrono::duration types with a floating-point representation type.

+

The available presentation types (chrono_type) are:

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Type

Meaning

'a'

The abbreviated weekday name, e.g. “Satâ€. If the value does not +contain a valid weekday, an exception of type format_error is +thrown.

'A'

The full weekday name, e.g. “Saturdayâ€. If the value does not +contain a valid weekday, an exception of type format_error is +thrown.

'b'

The abbreviated month name, e.g. “Novâ€. If the value does not +contain a valid month, an exception of type format_error is +thrown.

'B'

The full month name, e.g. “Novemberâ€. If the value does not +contain a valid month, an exception of type format_error is +thrown.

'c'

The date and time representation, e.g. “Sat Nov 12 22:04:00 1955â€. +The modified command %Ec produces the locale’s alternate date +and time representation.

'C'

The year divided by 100 using floored division, e.g. “55â€. If the +result is a single decimal digit, it is prefixed with 0. +The modified command %EC produces the locale’s alternative +representation of the century.

'd'

The day of month as a decimal number. If the result is a single +decimal digit, it is prefixed with 0. The modified command %Od +produces the locale’s alternative representation.

'D'

Equivalent to %m/%d/%y, e.g. “11/12/55â€.

'e'

The day of month as a decimal number. If the result is a single +decimal digit, it is prefixed with a space. The modified command +%Oe produces the locale’s alternative representation.

'F'

Equivalent to %Y-%m-%d, e.g. “1955-11-12â€.

'g'

The last two decimal digits of the ISO week-based year. If the +result is a single digit it is prefixed by 0.

'G'

The ISO week-based year as a decimal number. If the result is less +than four digits it is left-padded with 0 to four digits.

'h'

Equivalent to %b, e.g. “Novâ€.

'H'

The hour (24-hour clock) as a decimal number. If the result is a +single digit, it is prefixed with 0. The modified command %OH +produces the locale’s alternative representation.

'I'

The hour (12-hour clock) as a decimal number. If the result is a +single digit, it is prefixed with 0. The modified command %OI +produces the locale’s alternative representation.

'j'

If the type being formatted is a specialization of duration, the +decimal number of days without padding. Otherwise, the day of the +year as a decimal number. Jan 1 is 001. If the result is less than +three digits, it is left-padded with 0 to three digits.

'm'

The month as a decimal number. Jan is 01. If the result is a +single digit, it is prefixed with 0. The modified command %Om +produces the locale’s alternative representation.

'M'

The minute as a decimal number. If the result is a single digit, +it is prefixed with 0. The modified command %OM produces the +locale’s alternative representation.

'n'

A new-line character.

'p'

The AM/PM designations associated with a 12-hour clock.

'q'

The duration’s unit suffix.

'Q'

The duration’s numeric value (as if extracted via .count()).

'r'

The 12-hour clock time, e.g. “10:04:00 PMâ€.

'R'

Equivalent to %H:%M, e.g. “22:04â€.

'S'

Seconds as a decimal number. If the number of seconds is less than +10, the result is prefixed with 0. If the precision of the input +cannot be exactly represented with seconds, then the format is a +decimal floating-point number with a fixed format and a precision +matching that of the precision of the input (or to a microseconds +precision if the conversion to floating-point decimal seconds +cannot be made within 18 fractional digits). The character for the +decimal point is localized according to the locale. The modified +command %OS produces the locale’s alternative representation.

't'

A horizontal-tab character.

'T'

Equivalent to %H:%M:%S.

'u'

The ISO weekday as a decimal number (1-7), where Monday is 1. The +modified command %Ou produces the locale’s alternative +representation.

'U'

The week number of the year as a decimal number. The first Sunday +of the year is the first day of week 01. Days of the same year +prior to that are in week 00. If the result is a single digit, it +is prefixed with 0. The modified command %OU produces the +locale’s alternative representation.

'V'

The ISO week-based week number as a decimal number. If the result +is a single digit, it is prefixed with 0. The modified command +%OV produces the locale’s alternative representation.

'w'

The weekday as a decimal number (0-6), where Sunday is 0. +The modified command %Ow produces the locale’s alternative +representation.

'W'

The week number of the year as a decimal number. The first Monday +of the year is the first day of week 01. Days of the same year +prior to that are in week 00. If the result is a single digit, it +is prefixed with 0. The modified command %OW produces the +locale’s alternative representation.

'x'

The date representation, e.g. “11/12/55â€. The modified command +%Ex produces the locale’s alternate date representation.

'X'

The time representation, e.g. “10:04:00â€. The modified command +%EX produces the locale’s alternate time representation.

'y'

The last two decimal digits of the year. If the result is a single +digit it is prefixed by 0. The modified command %Oy produces +the locale’s alternative representation. The modified command +%Ey produces the locale’s alternative representation of offset +from %EC (year only).

'Y'

The year as a decimal number. If the result is less than four +digits it is left-padded with 0 to four digits. The modified +command %EY produces the locale’s alternative full year +representation.

'z'

The offset from UTC in the ISO 8601:2004 format. For example -0430 +refers to 4 hours 30 minutes behind UTC. If the offset is zero, ++0000 is used. The modified commands %Ez and %Oz insert a +: between the hours and minutes: -04:30. If the offset +information is not available, an exception of type +format_error is thrown.

'Z'

The time zone abbreviation. If the time zone abbreviation is not +available, an exception of type format_error is thrown.

'%'

A % character.

+

Specifiers that have a calendaric component such as 'd' (the day of month) +are valid only for std::tm and time points but not durations.

+
+
+

Range Format Specifications¶

+

Format specifications for range types have the following syntax:

+
+range_format_spec ::=  [":" [underlying_spec]]
+
+

The underlying_spec is parsed based on the formatter of the range’s +reference type.

+

By default, a range of characters or strings is printed escaped and quoted. But +if any underlying_spec is provided (even if it is empty), then the characters +or strings are printed according to the provided specification.

+

Examples:

+
fmt::format("{}", std::vector{10, 20, 30});
+// Result: [10, 20, 30]
+fmt::format("{::#x}", std::vector{10, 20, 30});
+// Result: [0xa, 0x14, 0x1e]
+fmt::format("{}", vector{'h', 'e', 'l', 'l', 'o'});
+// Result: ['h', 'e', 'l', 'l', 'o']
+fmt::format("{::}", vector{'h', 'e', 'l', 'l', 'o'});
+// Result: [h, e, l, l, o]
+fmt::format("{::d}", vector{'h', 'e', 'l', 'l', 'o'});
+// Result: [104, 101, 108, 108, 111]
+
+
+
+
+

Format Examples¶

+

This section contains examples of the format syntax and comparison with +the printf formatting.

+

In most of the cases the syntax is similar to the printf formatting, with the +addition of the {} and with : used instead of %. +For example, "%03.2f" can be translated to "{:03.2f}".

+

The new format syntax also supports new and different options, shown in the +following examples.

+

Accessing arguments by position:

+
fmt::format("{0}, {1}, {2}", 'a', 'b', 'c');
+// Result: "a, b, c"
+fmt::format("{}, {}, {}", 'a', 'b', 'c');
+// Result: "a, b, c"
+fmt::format("{2}, {1}, {0}", 'a', 'b', 'c');
+// Result: "c, b, a"
+fmt::format("{0}{1}{0}", "abra", "cad");  // arguments' indices can be repeated
+// Result: "abracadabra"
+
+
+

Aligning the text and specifying a width:

+
fmt::format("{:<30}", "left aligned");
+// Result: "left aligned                  "
+fmt::format("{:>30}", "right aligned");
+// Result: "                 right aligned"
+fmt::format("{:^30}", "centered");
+// Result: "           centered           "
+fmt::format("{:*^30}", "centered");  // use '*' as a fill char
+// Result: "***********centered***********"
+
+
+

Dynamic width:

+
fmt::format("{:<{}}", "left aligned", 30);
+// Result: "left aligned                  "
+
+
+

Dynamic precision:

+
fmt::format("{:.{}f}", 3.14, 1);
+// Result: "3.1"
+
+
+

Replacing %+f, %-f, and % f and specifying a sign:

+
fmt::format("{:+f}; {:+f}", 3.14, -3.14);  // show it always
+// Result: "+3.140000; -3.140000"
+fmt::format("{: f}; {: f}", 3.14, -3.14);  // show a space for positive numbers
+// Result: " 3.140000; -3.140000"
+fmt::format("{:-f}; {:-f}", 3.14, -3.14);  // show only the minus -- same as '{:f}; {:f}'
+// Result: "3.140000; -3.140000"
+
+
+

Replacing %x and %o and converting the value to different bases:

+
fmt::format("int: {0:d};  hex: {0:x};  oct: {0:o}; bin: {0:b}", 42);
+// Result: "int: 42;  hex: 2a;  oct: 52; bin: 101010"
+// with 0x or 0 or 0b as prefix:
+fmt::format("int: {0:d};  hex: {0:#x};  oct: {0:#o};  bin: {0:#b}", 42);
+// Result: "int: 42;  hex: 0x2a;  oct: 052;  bin: 0b101010"
+
+
+

Padded hex byte with prefix and always prints both hex characters:

+
fmt::format("{:#04x}", 0);
+// Result: "0x00"
+
+
+

Box drawing using Unicode fill:

+
fmt::print(
+  "┌{0:─^{2}}â”\n"
+  "│{1: ^{2}}│\n"
+  "└{0:─^{2}}┘\n", "", "Hello, world!", 20);
+
+
+

prints:

+
┌────────────────────â”
+│   Hello, world!    │
+└────────────────────┘
+
+
+

Using type-specific formatting:

+
#include <fmt/chrono.h>
+
+auto t = tm();
+t.tm_year = 2010 - 1900;
+t.tm_mon = 7;
+t.tm_mday = 4;
+t.tm_hour = 12;
+t.tm_min = 15;
+t.tm_sec = 58;
+fmt::print("{:%Y-%m-%d %H:%M:%S}", t);
+// Prints: 2010-08-04 12:15:58
+
+
+

Using the comma as a thousands separator:

+
#include <fmt/format.h>
+
+auto s = fmt::format(std::locale("en_US.UTF-8"), "{:L}", 1234567890);
+// s == "1,234,567,890"
+
+
+
+
+ + +
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/vendor/Fmt/doc/html/usage.html b/vendor/Fmt/doc/html/usage.html new file mode 100644 index 00000000..8596e95f --- /dev/null +++ b/vendor/Fmt/doc/html/usage.html @@ -0,0 +1,335 @@ + + + + + + + + + + + + Usage — fmt 10.0.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ +
+

Usage¶

+

To use the {fmt} library, add fmt/core.h, fmt/format.h, +fmt/format-inl.h, src/format.cc and optionally other headers +from a release archive or +the Git repository to your project. +Alternatively, you can build the library with CMake.

+
+

Building the Library¶

+

The included CMake build script can be used to build the fmt +library on a wide range of platforms. CMake is freely available for +download from https://www.cmake.org/download/.

+

CMake works by generating native makefiles or project files that can +be used in the compiler environment of your choice. The typical +workflow starts with:

+
mkdir build          # Create a directory to hold the build output.
+cd build
+cmake ..  # Generate native build scripts.
+
+
+

where <path/to/fmt> is a path to the fmt repository.

+

If you are on a *nix system, you should now see a Makefile in the +current directory. Now you can build the library by running make.

+

Once the library has been built you can invoke make test to run +the tests.

+

You can control generation of the make test target with the FMT_TEST +CMake option. This can be useful if you include fmt as a subdirectory in +your project but don’t want to add fmt’s tests to your test target.

+

If you use Windows and have Visual Studio installed, a FMT.sln +file and several .vcproj files will be created. You can then build them +using Visual Studio or msbuild.

+

On Mac OS X with Xcode installed, an .xcodeproj file will be generated.

+

To build a shared library set the BUILD_SHARED_LIBS CMake variable to +TRUE:

+
cmake -DBUILD_SHARED_LIBS=TRUE ...
+
+
+

To build a static library with position independent code (required if the main +consumer of the fmt library is a shared library i.e. a Python extension) set the +CMAKE_POSITION_INDEPENDENT_CODE CMake variable to TRUE:

+
cmake -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE ...
+
+
+
+
+

Installing the Library¶

+

After building the library you can install it on a Unix-like system by running +sudo make install.

+
+
+

Usage with CMake¶

+

You can add the fmt library directory into your project and include it in +your CMakeLists.txt file:

+
add_subdirectory(fmt)
+
+
+

or

+
add_subdirectory(fmt EXCLUDE_FROM_ALL)
+
+
+

to exclude it from make, make all, or cmake --build ..

+

You can detect and use an installed version of {fmt} as follows:

+
find_package(fmt)
+target_link_libraries(<your-target> fmt::fmt)
+
+
+

Setting up your target to use a header-only version of fmt is equally easy:

+
target_link_libraries(<your-target> PRIVATE fmt::fmt-header-only)
+
+
+
+
+

Usage with build2¶

+

You can use build2, a dependency manager and a +build-system combined, to use fmt.

+

Currently this package is available in these package repositories:

+ +

Usage:

+
    +
  • build2 package name: fmt

  • +
  • Library target name : lib{fmt}

  • +
+

For example, to make your build2 project depend on fmt:

+
    +
  • Add one of the repositories to your configurations, or in your +repositories.manifest, if not already there:

    +
    :
    +role: prerequisite
    +location: https://pkg.cppget.org/1/stable
    +
    +
    +
  • +
  • Add this package as a dependency to your ./manifest file +(example for v7.0.x):

    +
    depends: fmt ~7.0.0
    +
    +
    +
  • +
  • Import the target and use it as a prerequisite to your own target +using fmt in the appropriate buildfile:

    +
    import fmt = fmt%lib{fmt}
    +lib{mylib} : cxx{**} ... $fmt
    +
    +
    +
  • +
+

Then build your project as usual with b or bdep update.

+

For build2 newcomers or to get more details and use cases, you can read the +build2 +toolchain introduction.

+
+
+

Building the Documentation¶

+

To build the documentation you need the following software installed on your +system:

+
    +
  • Python with pip and virtualenv

  • +
  • Doxygen

  • +
  • Less with less-plugin-clean-css. +Ubuntu doesn’t package the clean-css plugin so you should use npm +instead of apt to install both less and the plugin:

    +
    sudo npm install -g less less-plugin-clean-css.
    +
    +
    +
  • +
+

First generate makefiles or project files using CMake as described in +the previous section. Then compile the doc target/project, for example:

+
make doc
+
+
+

This will generate the HTML documentation in doc/html.

+
+
+

Conda¶

+

fmt can be installed on Linux, macOS and Windows with +Conda, using its +conda-forge +package, as follows:

+
conda install -c conda-forge fmt
+
+
+
+
+

Vcpkg¶

+

You can download and install fmt using the vcpkg dependency manager:

+
git clone https://github.com/Microsoft/vcpkg.git
+cd vcpkg
+./bootstrap-vcpkg.sh
+./vcpkg integrate install
+./vcpkg install fmt
+
+
+

The fmt port in vcpkg is kept up to date by Microsoft team members and community +contributors. If the version is out of date, please create an issue or pull +request on the vcpkg repository.

+
+
+

LHelper¶

+

You can download and install fmt using +lhelper dependency manager:

+
lhelper activate <some-environment>
+lhelper install fmt
+
+
+

All the recipes for lhelper are kept in the +lhelper’s recipe repository.

+
+
+

Android NDK¶

+

fmt provides Android.mk file that can be used to build the library +with Android NDK. +For an example of using fmt with Android NDK, see the +android-ndk-example +repository.

+
+
+

Homebrew¶

+

fmt can be installed on OS X using Homebrew:

+
brew install fmt
+
+
+
+
+ + +
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/vendor/Fmt/doc/index.rst b/vendor/Fmt/doc/index.rst index d5c4fa5f..8d55c7a1 100644 --- a/vendor/Fmt/doc/index.rst +++ b/vendor/Fmt/doc/index.rst @@ -39,7 +39,7 @@ The ``fmt::format`` function returns a string "The answer is 42.". You can use .. code:: c++ auto out = fmt::memory_buffer(); - format_to(std::back_inserter(out), + fmt::format_to(std::back_inserter(out), "For a moment, {} happened.", "nothing"); auto data = out.data(); // pointer to the formatted data auto size = out.size(); // size of the formatted data diff --git a/vendor/Fmt/doc/syntax.rst b/vendor/Fmt/doc/syntax.rst index 9bf8dba7..74b64c5a 100644 --- a/vendor/Fmt/doc/syntax.rst +++ b/vendor/Fmt/doc/syntax.rst @@ -109,8 +109,8 @@ Note that unless a minimum field width is defined, the field width will always be the same size as the data to fill it, so that the alignment option has no meaning in this case. -The *sign* option is only valid for number types, and can be one of the -following: +The *sign* option is only valid for floating point and signed integer types, +and can be one of the following: +---------+------------------------------------------------------------+ | Option | Meaning | @@ -304,8 +304,8 @@ The available presentation types for pointers are: Chrono Format Specifications ============================ -Format specifications for chrono types and ``std::tm`` have the following -syntax: +Format specifications for chrono duration and time point types as well as +``std::tm`` have the following syntax: .. productionlist:: sf chrono_format_spec: [[`fill`]`align`][`width`]["." `precision`][`chrono_specs`] @@ -321,20 +321,89 @@ syntax: Literal chars are copied unchanged to the output. Precision is valid only for ``std::chrono::duration`` types with a floating-point representation type. -The available presentation types (*chrono_type*) for chrono durations and time -points are: +The available presentation types (*chrono_type*) are: +---------+--------------------------------------------------------------------+ | Type | Meaning | +=========+====================================================================+ +| ``'a'`` | The abbreviated weekday name, e.g. "Sat". If the value does not | +| | contain a valid weekday, an exception of type ``format_error`` is | +| | thrown. | ++---------+--------------------------------------------------------------------+ +| ``'A'`` | The full weekday name, e.g. "Saturday". If the value does not | +| | contain a valid weekday, an exception of type ``format_error`` is | +| | thrown. | ++---------+--------------------------------------------------------------------+ +| ``'b'`` | The abbreviated month name, e.g. "Nov". If the value does not | +| | contain a valid month, an exception of type ``format_error`` is | +| | thrown. | ++---------+--------------------------------------------------------------------+ +| ``'B'`` | The full month name, e.g. "November". If the value does not | +| | contain a valid month, an exception of type ``format_error`` is | +| | thrown. | ++---------+--------------------------------------------------------------------+ +| ``'c'`` | The date and time representation, e.g. "Sat Nov 12 22:04:00 1955". | +| | The modified command ``%Ec`` produces the locale's alternate date | +| | and time representation. | ++---------+--------------------------------------------------------------------+ +| ``'C'`` | The year divided by 100 using floored division, e.g. "55". If the | +| | result is a single decimal digit, it is prefixed with 0. | +| | The modified command ``%EC`` produces the locale's alternative | +| | representation of the century. | ++---------+--------------------------------------------------------------------+ +| ``'d'`` | The day of month as a decimal number. If the result is a single | +| | decimal digit, it is prefixed with 0. The modified command ``%Od`` | +| | produces the locale's alternative representation. | ++---------+--------------------------------------------------------------------+ +| ``'D'`` | Equivalent to ``%m/%d/%y``, e.g. "11/12/55". | ++---------+--------------------------------------------------------------------+ +| ``'e'`` | The day of month as a decimal number. If the result is a single | +| | decimal digit, it is prefixed with a space. The modified command | +| | ``%Oe`` produces the locale's alternative representation. | ++---------+--------------------------------------------------------------------+ +| ``'F'`` | Equivalent to ``%Y-%m-%d``, e.g. "1955-11-12". | ++---------+--------------------------------------------------------------------+ +| ``'g'`` | The last two decimal digits of the ISO week-based year. If the | +| | result is a single digit it is prefixed by 0. | ++---------+--------------------------------------------------------------------+ +| ``'G'`` | The ISO week-based year as a decimal number. If the result is less | +| | than four digits it is left-padded with 0 to four digits. | ++---------+--------------------------------------------------------------------+ +| ``'h'`` | Equivalent to ``%b``, e.g. "Nov". | ++---------+--------------------------------------------------------------------+ | ``'H'`` | The hour (24-hour clock) as a decimal number. If the result is a | | | single digit, it is prefixed with 0. The modified command ``%OH`` | | | produces the locale's alternative representation. | +---------+--------------------------------------------------------------------+ +| ``'I'`` | The hour (12-hour clock) as a decimal number. If the result is a | +| | single digit, it is prefixed with 0. The modified command ``%OI`` | +| | produces the locale's alternative representation. | ++---------+--------------------------------------------------------------------+ +| ``'j'`` | If the type being formatted is a specialization of duration, the | +| | decimal number of days without padding. Otherwise, the day of the | +| | year as a decimal number. Jan 1 is 001. If the result is less than | +| | three digits, it is left-padded with 0 to three digits. | ++---------+--------------------------------------------------------------------+ +| ``'m'`` | The month as a decimal number. Jan is 01. If the result is a | +| | single digit, it is prefixed with 0. The modified command ``%Om`` | +| | produces the locale's alternative representation. | ++---------+--------------------------------------------------------------------+ | ``'M'`` | The minute as a decimal number. If the result is a single digit, | | | it is prefixed with 0. The modified command ``%OM`` produces the | | | locale's alternative representation. | +---------+--------------------------------------------------------------------+ +| ``'n'`` | A new-line character. | ++---------+--------------------------------------------------------------------+ +| ``'p'`` | The AM/PM designations associated with a 12-hour clock. | ++---------+--------------------------------------------------------------------+ +| ``'q'`` | The duration's unit suffix. | ++---------+--------------------------------------------------------------------+ +| ``'Q'`` | The duration's numeric value (as if extracted via ``.count()``). | ++---------+--------------------------------------------------------------------+ +| ``'r'`` | The 12-hour clock time, e.g. "10:04:00 PM". | ++---------+--------------------------------------------------------------------+ +| ``'R'`` | Equivalent to ``%H:%M``, e.g. "22:04". | ++---------+--------------------------------------------------------------------+ | ``'S'`` | Seconds as a decimal number. If the number of seconds is less than | | | 10, the result is prefixed with 0. If the precision of the input | | | cannot be exactly represented with seconds, then the format is a | @@ -345,9 +414,66 @@ points are: | | decimal point is localized according to the locale. The modified | | | command ``%OS`` produces the locale's alternative representation. | +---------+--------------------------------------------------------------------+ +| ``'t'`` | A horizontal-tab character. | ++---------+--------------------------------------------------------------------+ +| ``'T'`` | Equivalent to ``%H:%M:%S``. | ++---------+--------------------------------------------------------------------+ +| ``'u'`` | The ISO weekday as a decimal number (1-7), where Monday is 1. The | +| | modified command ``%Ou`` produces the locale's alternative | +| | representation. | ++---------+--------------------------------------------------------------------+ +| ``'U'`` | The week number of the year as a decimal number. The first Sunday | +| | of the year is the first day of week 01. Days of the same year | +| | prior to that are in week 00. If the result is a single digit, it | +| | is prefixed with 0. The modified command ``%OU`` produces the | +| | locale's alternative representation. | ++---------+--------------------------------------------------------------------+ +| ``'V'`` | The ISO week-based week number as a decimal number. If the result | +| | is a single digit, it is prefixed with 0. The modified command | +| | ``%OV`` produces the locale's alternative representation. | ++---------+--------------------------------------------------------------------+ +| ``'w'`` | The weekday as a decimal number (0-6), where Sunday is 0. | +| | The modified command ``%Ow`` produces the locale's alternative | +| | representation. | ++---------+--------------------------------------------------------------------+ +| ``'W'`` | The week number of the year as a decimal number. The first Monday | +| | of the year is the first day of week 01. Days of the same year | +| | prior to that are in week 00. If the result is a single digit, it | +| | is prefixed with 0. The modified command ``%OW`` produces the | +| | locale's alternative representation. | ++---------+--------------------------------------------------------------------+ +| ``'x'`` | The date representation, e.g. "11/12/55". The modified command | +| | ``%Ex`` produces the locale's alternate date representation. | ++---------+--------------------------------------------------------------------+ +| ``'X'`` | The time representation, e.g. "10:04:00". The modified command | +| | ``%EX`` produces the locale's alternate time representation. | ++---------+--------------------------------------------------------------------+ +| ``'y'`` | The last two decimal digits of the year. If the result is a single | +| | digit it is prefixed by 0. The modified command ``%Oy`` produces | +| | the locale's alternative representation. The modified command | +| | ``%Ey`` produces the locale's alternative representation of offset | +| | from ``%EC`` (year only). | ++---------+--------------------------------------------------------------------+ +| ``'Y'`` | The year as a decimal number. If the result is less than four | +| | digits it is left-padded with 0 to four digits. The modified | +| | command ``%EY`` produces the locale's alternative full year | +| | representation. | ++---------+--------------------------------------------------------------------+ +| ``'z'`` | The offset from UTC in the ISO 8601:2004 format. For example -0430 | +| | refers to 4 hours 30 minutes behind UTC. If the offset is zero, | +| | +0000 is used. The modified commands ``%Ez`` and ``%Oz`` insert a | +| | ``:`` between the hours and minutes: -04:30. If the offset | +| | information is not available, an exception of type | +| | ``format_error`` is thrown. | ++---------+--------------------------------------------------------------------+ +| ``'Z'`` | The time zone abbreviation. If the time zone abbreviation is not | +| | available, an exception of type ``format_error`` is thrown. | ++---------+--------------------------------------------------------------------+ +| ``'%'`` | A % character. | ++---------+--------------------------------------------------------------------+ -Specifiers that have a calendaric component such as `'d'` (the day of month) -are valid only for ``std::tm`` and not durations or time points. +Specifiers that have a calendaric component such as ``'d'`` (the day of month) +are valid only for ``std::tm`` and time points but not durations. .. range-specs: @@ -356,8 +482,8 @@ Range Format Specifications Format specifications for range types have the following syntax: -..productionlist:: sf - range_format_spec: [":" [`underlying_spec`]] +.. productionlist:: sf + range_format_spec: [":" [`underlying_spec`]] The `underlying_spec` is parsed based on the formatter of the range's reference type. @@ -366,12 +492,12 @@ By default, a range of characters or strings is printed escaped and quoted. But if any `underlying_spec` is provided (even if it is empty), then the characters or strings are printed according to the provided specification. -Examples: +Examples:: fmt::format("{}", std::vector{10, 20, 30}); // Result: [10, 20, 30] fmt::format("{::#x}", std::vector{10, 20, 30}); - // Result: [0xa, 0x14, 0x13] + // Result: [0xa, 0x14, 0x1e] fmt::format("{}", vector{'h', 'e', 'l', 'l', 'o'}); // Result: ['h', 'e', 'l', 'l', 'o'] fmt::format("{::}", vector{'h', 'e', 'l', 'l', 'o'}); diff --git a/vendor/Fmt/include/fmt/chrono.h b/vendor/Fmt/include/fmt/chrono.h index 7872fb4b..55e8a506 100644 --- a/vendor/Fmt/include/fmt/chrono.h +++ b/vendor/Fmt/include/fmt/chrono.h @@ -22,6 +22,24 @@ FMT_BEGIN_NAMESPACE +// Check if std::chrono::local_t is available. +#ifndef FMT_USE_LOCAL_TIME +# ifdef __cpp_lib_chrono +# define FMT_USE_LOCAL_TIME (__cpp_lib_chrono >= 201907L) +# else +# define FMT_USE_LOCAL_TIME 0 +# endif +#endif + +// Check if std::chrono::utc_timestamp is available. +#ifndef FMT_USE_UTC_TIME +# ifdef __cpp_lib_chrono +# define FMT_USE_UTC_TIME (__cpp_lib_chrono >= 201907L) +# else +# define FMT_USE_UTC_TIME 0 +# endif +#endif + // Enable tzset. #ifndef FMT_USE_TZSET // UWP doesn't provide _tzset. @@ -203,7 +221,8 @@ To safe_duration_cast(std::chrono::duration from, } const auto min1 = (std::numeric_limits::min)() / Factor::num; - if (count < min1) { + if (detail::const_check(!std::is_unsigned::value) && + count < min1) { ec = 1; return {}; } @@ -345,7 +364,7 @@ auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc) if (detail::is_utf8() && loc != get_classic_locale()) { // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and // gcc-4. -#if FMT_MSC_VER != 0 || \ +#if FMT_MSC_VERSION != 0 || \ (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI)) // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5 // and newer. @@ -358,37 +377,11 @@ auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc) unit_t unit; write_codecvt(unit, in, loc); // In UTF-8 is used one to four one-byte code units. - auto&& buf = basic_memory_buffer(); - for (code_unit* p = unit.buf; p != unit.end; ++p) { - uint32_t c = static_cast(*p); - if (sizeof(code_unit) == 2 && c >= 0xd800 && c <= 0xdfff) { - // surrogate pair - ++p; - if (p == unit.end || (c & 0xfc00) != 0xd800 || - (*p & 0xfc00) != 0xdc00) { - FMT_THROW(format_error("failed to format time")); - } - c = (c << 10) + static_cast(*p) - 0x35fdc00; - } - if (c < 0x80) { - buf.push_back(static_cast(c)); - } else if (c < 0x800) { - buf.push_back(static_cast(0xc0 | (c >> 6))); - buf.push_back(static_cast(0x80 | (c & 0x3f))); - } else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) { - buf.push_back(static_cast(0xe0 | (c >> 12))); - buf.push_back(static_cast(0x80 | ((c & 0xfff) >> 6))); - buf.push_back(static_cast(0x80 | (c & 0x3f))); - } else if (c >= 0x10000 && c <= 0x10ffff) { - buf.push_back(static_cast(0xf0 | (c >> 18))); - buf.push_back(static_cast(0x80 | ((c & 0x3ffff) >> 12))); - buf.push_back(static_cast(0x80 | ((c & 0xfff) >> 6))); - buf.push_back(static_cast(0x80 | (c & 0x3f))); - } else { - FMT_THROW(format_error("failed to format time")); - } - } - return copy_str(buf.data(), buf.data() + buf.size(), out); + unicode_to_utf8> + u; + if (!u.convert({unit.buf, to_unsigned(unit.end - unit.buf)})) + FMT_THROW(format_error("failed to format time")); + return copy_str(u.c_str(), u.c_str() + u.size(), out); } return copy_str(in.data(), in.data() + in.size(), out); } @@ -427,7 +420,7 @@ auto write(OutputIt out, const std::tm& time, const std::locale& loc, char format, char modifier = 0) -> OutputIt { auto&& buf = get_buffer(out); do_write(buf, time, loc, format, modifier); - return buf.out(); + return get_iterator(buf, out); } template ) { using namespace fmt::detail; std::tm* tm = std::localtime(&time_); @@ -484,10 +477,13 @@ inline std::tm localtime(std::time_t time) { return lt.tm_; } -inline std::tm localtime( - std::chrono::time_point time_point) { - return localtime(std::chrono::system_clock::to_time_t(time_point)); +#if FMT_USE_LOCAL_TIME +template +inline auto localtime(std::chrono::local_time time) -> std::tm { + return localtime(std::chrono::system_clock::to_time_t( + std::chrono::current_zone()->to_sys(time))); } +#endif /** Converts given time since epoch as ``std::time_t`` value into calendar time, @@ -515,7 +511,7 @@ inline std::tm gmtime(std::time_t time) { bool fallback(int res) { return res == 0; } -#if !FMT_MSC_VER +#if !FMT_MSC_VERSION bool fallback(detail::null<>) { std::tm* tm = std::gmtime(&time_); if (tm) tm_ = *tm; @@ -536,6 +532,49 @@ inline std::tm gmtime( FMT_BEGIN_DETAIL_NAMESPACE +// DEPRECATED! +template +FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end, + format_specs& specs) -> const Char* { + FMT_ASSERT(begin != end, ""); + auto align = align::none; + auto p = begin + code_point_length(begin); + if (end - p <= 0) p = begin; + for (;;) { + switch (to_ascii(*p)) { + case '<': + align = align::left; + break; + case '>': + align = align::right; + break; + case '^': + align = align::center; + break; + } + if (align != align::none) { + if (p != begin) { + auto c = *begin; + if (c == '}') return begin; + if (c == '{') { + throw_format_error("invalid fill character '{'"); + return begin; + } + specs.fill = {begin, to_unsigned(p - begin)}; + begin = p + 1; + } else { + ++begin; + } + break; + } else if (p == begin) { + break; + } + p = begin; + } + specs.align = align; + return begin; +} + // Writes two-digit numbers a, b and c separated by sep to buf. // The method by Pavel Novikov based on // https://johnnylee-sde.github.io/Fast-unsigned-integer-to-time-string/. @@ -599,12 +638,39 @@ enum class numeric_system { alternative }; +// Glibc extensions for formatting numeric values. +enum class pad_type { + unspecified, + // Do not pad a numeric result string. + none, + // Pad a numeric result string with zeros even if the conversion specifier + // character uses space-padding by default. + zero, + // Pad a numeric result string with spaces. + space, +}; + +template +auto write_padding(OutputIt out, pad_type pad, int width) -> OutputIt { + if (pad == pad_type::none) return out; + return std::fill_n(out, width, pad == pad_type::space ? ' ' : '0'); +} + +template +auto write_padding(OutputIt out, pad_type pad) -> OutputIt { + if (pad != pad_type::none) *out++ = pad == pad_type::space ? ' ' : '0'; + return out; +} + // Parses a put_time-like format string and invokes handler actions. template FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, const Char* end, Handler&& handler) { + if (begin == end || *begin == '}') return begin; + if (*begin != '%') FMT_THROW(format_error("invalid format")); auto ptr = begin; + pad_type pad = pad_type::unspecified; while (ptr != end) { auto c = *ptr; if (c == '}') break; @@ -615,6 +681,22 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, if (begin != ptr) handler.on_text(begin, ptr); ++ptr; // consume '%' if (ptr == end) FMT_THROW(format_error("invalid format")); + c = *ptr; + switch (c) { + case '_': + pad = pad_type::space; + ++ptr; + break; + case '-': + pad = pad_type::none; + ++ptr; + break; + case '0': + pad = pad_type::zero; + ++ptr; + break; + } + if (ptr == end) FMT_THROW(format_error("invalid format")); c = *ptr++; switch (c) { case '%': @@ -691,16 +773,16 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, break; // Hour, minute, second: case 'H': - handler.on_24_hour(numeric_system::standard); + handler.on_24_hour(numeric_system::standard, pad); break; case 'I': - handler.on_12_hour(numeric_system::standard); + handler.on_12_hour(numeric_system::standard, pad); break; case 'M': - handler.on_minute(numeric_system::standard); + handler.on_minute(numeric_system::standard, pad); break; case 'S': - handler.on_second(numeric_system::standard); + handler.on_second(numeric_system::standard, pad); break; // Other: case 'c': @@ -737,7 +819,7 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, handler.on_duration_unit(); break; case 'z': - handler.on_utc_offset(); + handler.on_utc_offset(numeric_system::standard); break; case 'Z': handler.on_tz_name(); @@ -765,6 +847,9 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, case 'X': handler.on_loc_time(numeric_system::alternative); break; + case 'z': + handler.on_utc_offset(numeric_system::alternative); + break; default: FMT_THROW(format_error("invalid format")); } @@ -802,16 +887,19 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, handler.on_dec1_weekday(numeric_system::alternative); break; case 'H': - handler.on_24_hour(numeric_system::alternative); + handler.on_24_hour(numeric_system::alternative, pad); break; case 'I': - handler.on_12_hour(numeric_system::alternative); + handler.on_12_hour(numeric_system::alternative, pad); break; case 'M': - handler.on_minute(numeric_system::alternative); + handler.on_minute(numeric_system::alternative, pad); break; case 'S': - handler.on_second(numeric_system::alternative); + handler.on_second(numeric_system::alternative, pad); + break; + case 'z': + handler.on_utc_offset(numeric_system::alternative); break; default: FMT_THROW(format_error("invalid format")); @@ -864,7 +952,7 @@ template struct null_chrono_spec_handler { FMT_CONSTEXPR void on_am_pm() { unsupported(); } FMT_CONSTEXPR void on_duration_value() { unsupported(); } FMT_CONSTEXPR void on_duration_unit() { unsupported(); } - FMT_CONSTEXPR void on_utc_offset() { unsupported(); } + FMT_CONSTEXPR void on_utc_offset(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_tz_name() { unsupported(); } }; @@ -892,10 +980,10 @@ struct tm_format_checker : null_chrono_spec_handler { FMT_CONSTEXPR void on_day_of_year() {} FMT_CONSTEXPR void on_day_of_month(numeric_system) {} FMT_CONSTEXPR void on_day_of_month_space(numeric_system) {} - FMT_CONSTEXPR void on_24_hour(numeric_system) {} - FMT_CONSTEXPR void on_12_hour(numeric_system) {} - FMT_CONSTEXPR void on_minute(numeric_system) {} - FMT_CONSTEXPR void on_second(numeric_system) {} + FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_second(numeric_system, pad_type) {} FMT_CONSTEXPR void on_datetime(numeric_system) {} FMT_CONSTEXPR void on_loc_date(numeric_system) {} FMT_CONSTEXPR void on_loc_time(numeric_system) {} @@ -905,7 +993,7 @@ struct tm_format_checker : null_chrono_spec_handler { FMT_CONSTEXPR void on_24_hour_time() {} FMT_CONSTEXPR void on_iso_time() {} FMT_CONSTEXPR void on_am_pm() {} - FMT_CONSTEXPR void on_utc_offset() {} + FMT_CONSTEXPR void on_utc_offset(numeric_system) {} FMT_CONSTEXPR void on_tz_name() {} }; @@ -957,13 +1045,130 @@ inline void tzset_once() { } #endif -template class tm_writer { +// Converts value to Int and checks that it's in the range [0, upper). +template ::value)> +inline Int to_nonnegative_int(T value, Int upper) { + FMT_ASSERT(std::is_unsigned::value || + (value >= 0 && to_unsigned(value) <= to_unsigned(upper)), + "invalid value"); + (void)upper; + return static_cast(value); +} +template ::value)> +inline Int to_nonnegative_int(T value, Int upper) { + if (value < 0 || value > static_cast(upper)) + FMT_THROW(format_error("invalid value")); + return static_cast(value); +} + +constexpr long long pow10(std::uint32_t n) { + return n == 0 ? 1 : 10 * pow10(n - 1); +} + +// Counts the number of fractional digits in the range [0, 18] according to the +// C++20 spec. If more than 18 fractional digits are required then returns 6 for +// microseconds precision. +template () / 10)> +struct count_fractional_digits { + static constexpr int value = + Num % Den == 0 ? N : count_fractional_digits::value; +}; + +// Base case that doesn't instantiate any more templates +// in order to avoid overflow. +template +struct count_fractional_digits { + static constexpr int value = (Num % Den == 0) ? N : 6; +}; + +// Format subseconds which are given as an integer type with an appropriate +// number of digits. +template +void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) { + constexpr auto num_fractional_digits = + count_fractional_digits::value; + + using subsecond_precision = std::chrono::duration< + typename std::common_type::type, + std::ratio<1, detail::pow10(num_fractional_digits)>>; + + const auto fractional = + d - std::chrono::duration_cast(d); + const auto subseconds = + std::chrono::treat_as_floating_point< + typename subsecond_precision::rep>::value + ? fractional.count() + : std::chrono::duration_cast(fractional).count(); + auto n = static_cast>(subseconds); + const int num_digits = detail::count_digits(n); + + int leading_zeroes = (std::max)(0, num_fractional_digits - num_digits); + if (precision < 0) { + FMT_ASSERT(!std::is_floating_point::value, ""); + if (std::ratio_less::value) { + *out++ = '.'; + out = std::fill_n(out, leading_zeroes, '0'); + out = format_decimal(out, n, num_digits).end; + } + } else { + *out++ = '.'; + leading_zeroes = (std::min)(leading_zeroes, precision); + out = std::fill_n(out, leading_zeroes, '0'); + int remaining = precision - leading_zeroes; + if (remaining != 0 && remaining < num_digits) { + n /= to_unsigned(detail::pow10(to_unsigned(num_digits - remaining))); + out = format_decimal(out, n, remaining).end; + return; + } + out = format_decimal(out, n, num_digits).end; + remaining -= num_digits; + out = std::fill_n(out, remaining, '0'); + } +} + +// Format subseconds which are given as a floating point type with an +// appropriate number of digits. We cannot pass the Duration here, as we +// explicitly need to pass the Rep value in the chrono_formatter. +template +void write_floating_seconds(memory_buffer& buf, Duration duration, + int num_fractional_digits = -1) { + using rep = typename Duration::rep; + FMT_ASSERT(std::is_floating_point::value, ""); + + auto val = duration.count(); + + if (num_fractional_digits < 0) { + // For `std::round` with fallback to `round`: + // On some toolchains `std::round` is not available (e.g. GCC 6). + using namespace std; + num_fractional_digits = + count_fractional_digits::value; + if (num_fractional_digits < 6 && static_cast(round(val)) != val) + num_fractional_digits = 6; + } + + format_to(std::back_inserter(buf), FMT_STRING("{:.{}f}"), + std::fmod(val * static_cast(Duration::period::num) / + static_cast(Duration::period::den), + static_cast(60)), + num_fractional_digits); +} + +template +class tm_writer { private: static constexpr int days_per_week = 7; const std::locale& loc_; const bool is_classic_; OutputIt out_; + const Duration* subsecs_; const std::tm& tm_; auto tm_sec() const noexcept -> int { @@ -1051,6 +1256,17 @@ template class tm_writer { *out_++ = *d++; *out_++ = *d; } + void write2(int value, pad_type pad) { + unsigned int v = to_unsigned(value) % 100; + if (v >= 10) { + const char* d = digits2(v); + *out_++ = *d++; + *out_++ = *d; + } else { + out_ = detail::write_padding(out_, pad); + *out_++ = static_cast('0' + v); + } + } void write_year_extended(long long year) { // At least 4 characters. @@ -1074,7 +1290,7 @@ template class tm_writer { } } - void write_utc_offset(long offset) { + void write_utc_offset(long offset, numeric_system ns) { if (offset < 0) { *out_++ = '-'; offset = -offset; @@ -1083,14 +1299,15 @@ template class tm_writer { } offset /= 60; write2(static_cast(offset / 60)); + if (ns != numeric_system::standard) *out_++ = ':'; write2(static_cast(offset % 60)); } template ::value)> - void format_utc_offset_impl(const T& tm) { - write_utc_offset(tm.tm_gmtoff); + void format_utc_offset_impl(const T& tm, numeric_system ns) { + write_utc_offset(tm.tm_gmtoff, ns); } template ::value)> - void format_utc_offset_impl(const T& tm) { + void format_utc_offset_impl(const T& tm, numeric_system ns) { #if defined(_WIN32) && defined(_UCRT) # if FMT_USE_TZSET tzset_once(); @@ -1102,10 +1319,17 @@ template class tm_writer { _get_dstbias(&dstbias); offset += dstbias; } - write_utc_offset(-offset); + write_utc_offset(-offset, ns); #else - ignore_unused(tm); - format_localized('z'); + if (ns == numeric_system::standard) return format_localized('z'); + + // Extract timezone offset from timezone conversion functions. + std::tm gtm = tm; + std::time_t gt = std::mktime(>m); + std::tm ltm = gmtime(gt); + std::time_t lt = std::mktime(<m); + long offset = gt - lt; + write_utc_offset(offset, ns); #endif } @@ -1126,10 +1350,12 @@ template class tm_writer { } public: - tm_writer(const std::locale& loc, OutputIt out, const std::tm& tm) + tm_writer(const std::locale& loc, OutputIt out, const std::tm& tm, + const Duration* subsecs = nullptr) : loc_(loc), is_classic_(loc_ == get_classic_locale()), out_(out), + subsecs_(subsecs), tm_(tm) {} OutputIt out() const { return out_; } @@ -1227,7 +1453,7 @@ template class tm_writer { out_ = copy_str(std::begin(buf) + offset, std::end(buf), out_); } - void on_utc_offset() { format_utc_offset_impl(tm_); } + void on_utc_offset(numeric_system ns) { format_utc_offset_impl(tm_, ns); } void on_tz_name() { format_tz_name_impl(tm_); } void on_year(numeric_system ns) { @@ -1315,22 +1541,41 @@ template class tm_writer { } } - void on_24_hour(numeric_system ns) { - if (is_classic_ || ns == numeric_system::standard) return write2(tm_hour()); + void on_24_hour(numeric_system ns, pad_type pad) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_hour(), pad); format_localized('H', 'O'); } - void on_12_hour(numeric_system ns) { + void on_12_hour(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) - return write2(tm_hour12()); + return write2(tm_hour12(), pad); format_localized('I', 'O'); } - void on_minute(numeric_system ns) { - if (is_classic_ || ns == numeric_system::standard) return write2(tm_min()); + void on_minute(numeric_system ns, pad_type pad) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_min(), pad); format_localized('M', 'O'); } - void on_second(numeric_system ns) { - if (is_classic_ || ns == numeric_system::standard) return write2(tm_sec()); - format_localized('S', 'O'); + + void on_second(numeric_system ns, pad_type pad) { + if (is_classic_ || ns == numeric_system::standard) { + write2(tm_sec(), pad); + if (subsecs_) { + if (std::is_floating_point::value) { + auto buf = memory_buffer(); + write_floating_seconds(buf, *subsecs_); + if (buf.size() > 1) { + // Remove the leading "0", write something like ".123". + out_ = std::copy(buf.begin() + 1, buf.end(), out_); + } + } else { + write_fractional_seconds(out_, *subsecs_); + } + } + } else { + // Currently no formatting of subseconds when a locale is set. + format_localized('S', 'O'); + } } void on_12_hour_time() { @@ -1351,10 +1596,9 @@ template class tm_writer { write2(tm_min()); } void on_iso_time() { - char buf[8]; - write_digit2_separated(buf, to_unsigned(tm_hour()), to_unsigned(tm_min()), - to_unsigned(tm_sec()), ':'); - out_ = copy_str(std::begin(buf), std::end(buf), out_); + on_24_hour_time(); + *out_++ = ':'; + on_second(numeric_system::standard, pad_type::unspecified); } void on_am_pm() { @@ -1372,42 +1616,34 @@ template class tm_writer { }; struct chrono_format_checker : null_chrono_spec_handler { + bool has_precision_integral = false; + FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); } template FMT_CONSTEXPR void on_text(const Char*, const Char*) {} - FMT_CONSTEXPR void on_24_hour(numeric_system) {} - FMT_CONSTEXPR void on_12_hour(numeric_system) {} - FMT_CONSTEXPR void on_minute(numeric_system) {} - FMT_CONSTEXPR void on_second(numeric_system) {} + FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_second(numeric_system, pad_type) {} FMT_CONSTEXPR void on_12_hour_time() {} FMT_CONSTEXPR void on_24_hour_time() {} FMT_CONSTEXPR void on_iso_time() {} FMT_CONSTEXPR void on_am_pm() {} - FMT_CONSTEXPR void on_duration_value() {} + FMT_CONSTEXPR void on_duration_value() const { + if (has_precision_integral) { + FMT_THROW(format_error("precision not allowed for this argument type")); + } + } FMT_CONSTEXPR void on_duration_unit() {} }; -template ::value)> +template ::value&& has_isfinite::value)> inline bool isfinite(T) { return true; } -// Converts value to Int and checks that it's in the range [0, upper). -template ::value)> -inline Int to_nonnegative_int(T value, Int upper) { - FMT_ASSERT(value >= 0 && to_unsigned(value) <= to_unsigned(upper), - "invalid value"); - (void)upper; - return static_cast(value); -} -template ::value)> -inline Int to_nonnegative_int(T value, Int upper) { - if (value < 0 || value > static_cast(upper)) - FMT_THROW(format_error("invalid value")); - return static_cast(value); -} - template ::value)> inline T mod(T x, int y) { return x % static_cast(y); @@ -1462,48 +1698,6 @@ inline std::chrono::duration get_milliseconds( #endif } -// Counts the number of fractional digits in the range [0, 18] according to the -// C++20 spec. If more than 18 fractional digits are required then returns 6 for -// microseconds precision. -template ::max() / 10)> -struct count_fractional_digits { - static constexpr int value = - Num % Den == 0 ? N : count_fractional_digits::value; -}; - -// Base case that doesn't instantiate any more templates -// in order to avoid overflow. -template -struct count_fractional_digits { - static constexpr int value = (Num % Den == 0) ? N : 6; -}; - -constexpr long long pow10(std::uint32_t n) { - return n == 0 ? 1 : 10 * pow10(n - 1); -} - -template ::is_signed)> -constexpr std::chrono::duration abs( - std::chrono::duration d) { - // We need to compare the duration using the count() method directly - // due to a compiler bug in clang-11 regarding the spaceship operator, - // when -Wzero-as-null-pointer-constant is enabled. - // In clang-12 the bug has been fixed. See - // https://bugs.llvm.org/show_bug.cgi?id=46235 and the reproducible example: - // https://www.godbolt.org/z/Knbb5joYx. - return d.count() >= d.zero().count() ? d : -d; -} - -template ::is_signed)> -constexpr std::chrono::duration abs( - std::chrono::duration d) { - return d; -} - template ::value)> OutputIt format_duration_value(OutputIt out, Rep val, int) { @@ -1513,7 +1707,7 @@ OutputIt format_duration_value(OutputIt out, Rep val, int) { template ::value)> OutputIt format_duration_value(OutputIt out, Rep val, int precision) { - auto specs = basic_format_specs(); + auto specs = format_specs(); specs.precision = precision; specs.type = precision >= 0 ? presentation_type::fixed_lower : presentation_type::general_lower; @@ -1654,44 +1848,16 @@ struct chrono_formatter { } } - void write(Rep value, int width) { + void write(Rep value, int width, pad_type pad = pad_type::unspecified) { write_sign(); if (isnan(value)) return write_nan(); uint32_or_64_or_128_t n = to_unsigned(to_nonnegative_int(value, max_value())); int num_digits = detail::count_digits(n); - if (width > num_digits) out = std::fill_n(out, width - num_digits, '0'); - out = format_decimal(out, n, num_digits).end; - } - - template void write_fractional_seconds(Duration d) { - FMT_ASSERT(!std::is_floating_point::value, ""); - constexpr auto num_fractional_digits = - count_fractional_digits::value; - - using subsecond_precision = std::chrono::duration< - typename std::common_type::type, - std::ratio<1, detail::pow10(num_fractional_digits)>>; - if (std::ratio_less::value) { - *out++ = '.'; - auto fractional = - detail::abs(d) - std::chrono::duration_cast(d); - auto subseconds = - std::chrono::treat_as_floating_point< - typename subsecond_precision::rep>::value - ? fractional.count() - : std::chrono::duration_cast(fractional) - .count(); - uint32_or_64_or_128_t n = - to_unsigned(to_nonnegative_int(subseconds, max_value())); - int num_digits = detail::count_digits(n); - if (num_fractional_digits > num_digits) - out = std::fill_n(out, num_fractional_digits - num_digits, '0'); - out = format_decimal(out, n, num_digits).end; + if (width > num_digits) { + out = detail::write_padding(out, pad, width - num_digits); } + out = format_decimal(out, n, num_digits).end; } void write_nan() { std::copy_n("nan", 3, out); } @@ -1723,7 +1889,7 @@ struct chrono_formatter { void on_loc_time(numeric_system) {} void on_us_date() {} void on_iso_date() {} - void on_utc_offset() {} + void on_utc_offset(numeric_system) {} void on_tz_name() {} void on_year(numeric_system) {} void on_short_year(numeric_system) {} @@ -1739,58 +1905,56 @@ struct chrono_formatter { void on_day_of_month(numeric_system) {} void on_day_of_month_space(numeric_system) {} - void on_24_hour(numeric_system ns) { + void on_24_hour(numeric_system ns, pad_type pad) { if (handle_nan_inf()) return; - if (ns == numeric_system::standard) return write(hour(), 2); + if (ns == numeric_system::standard) return write(hour(), 2, pad); auto time = tm(); time.tm_hour = to_nonnegative_int(hour(), 24); - format_tm(time, &tm_writer_type::on_24_hour, ns); + format_tm(time, &tm_writer_type::on_24_hour, ns, pad); } - void on_12_hour(numeric_system ns) { + void on_12_hour(numeric_system ns, pad_type pad) { if (handle_nan_inf()) return; - if (ns == numeric_system::standard) return write(hour12(), 2); + if (ns == numeric_system::standard) return write(hour12(), 2, pad); auto time = tm(); time.tm_hour = to_nonnegative_int(hour12(), 12); - format_tm(time, &tm_writer_type::on_12_hour, ns); + format_tm(time, &tm_writer_type::on_12_hour, ns, pad); } - void on_minute(numeric_system ns) { + void on_minute(numeric_system ns, pad_type pad) { if (handle_nan_inf()) return; - if (ns == numeric_system::standard) return write(minute(), 2); + if (ns == numeric_system::standard) return write(minute(), 2, pad); auto time = tm(); time.tm_min = to_nonnegative_int(minute(), 60); - format_tm(time, &tm_writer_type::on_minute, ns); + format_tm(time, &tm_writer_type::on_minute, ns, pad); } - void on_second(numeric_system ns) { + void on_second(numeric_system ns, pad_type pad) { if (handle_nan_inf()) return; if (ns == numeric_system::standard) { if (std::is_floating_point::value) { - constexpr auto num_fractional_digits = - count_fractional_digits::value; auto buf = memory_buffer(); - format_to(std::back_inserter(buf), runtime("{:.{}f}"), - std::fmod(val * static_cast(Period::num) / - static_cast(Period::den), - 60), - num_fractional_digits); + write_floating_seconds(buf, std::chrono::duration(val), + precision); if (negative) *out++ = '-'; - if (buf.size() < 2 || buf[1] == '.') *out++ = '0'; + if (buf.size() < 2 || buf[1] == '.') { + out = detail::write_padding(out, pad); + } out = std::copy(buf.begin(), buf.end(), out); } else { - write(second(), 2); - write_fractional_seconds(std::chrono::duration(val)); + write(second(), 2, pad); + write_fractional_seconds( + out, std::chrono::duration(val), precision); } return; } auto time = tm(); time.tm_sec = to_nonnegative_int(second(), 60); - format_tm(time, &tm_writer_type::on_second, ns); + format_tm(time, &tm_writer_type::on_second, ns, pad); } void on_12_hour_time() { @@ -1814,7 +1978,7 @@ struct chrono_formatter { on_24_hour_time(); *out++ = ':'; if (handle_nan_inf()) return; - on_second(numeric_system::standard); + on_second(numeric_system::standard, pad_type::unspecified); } void on_am_pm() { @@ -1883,7 +2047,7 @@ template struct formatter { template struct formatter, Char> { private: - basic_format_specs specs; + format_specs specs; int precision = -1; using arg_ref_type = detail::arg_ref; arg_ref_type width_ref; @@ -1892,45 +2056,6 @@ struct formatter, Char> { basic_string_view format_str; using duration = std::chrono::duration; - struct spec_handler { - formatter& f; - basic_format_parse_context& context; - basic_string_view format_str; - - template FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) { - context.check_arg_id(arg_id); - return arg_ref_type(arg_id); - } - - FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view arg_id) { - context.check_arg_id(arg_id); - return arg_ref_type(arg_id); - } - - FMT_CONSTEXPR arg_ref_type make_arg_ref(detail::auto_id) { - return arg_ref_type(context.next_arg_id()); - } - - void on_error(const char* msg) { FMT_THROW(format_error(msg)); } - FMT_CONSTEXPR void on_fill(basic_string_view fill) { - f.specs.fill = fill; - } - FMT_CONSTEXPR void on_align(align_t align) { f.specs.align = align; } - FMT_CONSTEXPR void on_width(int width) { f.specs.width = width; } - FMT_CONSTEXPR void on_precision(int _precision) { - f.precision = _precision; - } - FMT_CONSTEXPR void end_precision() {} - - template FMT_CONSTEXPR void on_dynamic_width(Id arg_id) { - f.width_ref = make_arg_ref(arg_id); - } - - template FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) { - f.precision_ref = make_arg_ref(arg_id); - } - }; - using iterator = typename basic_format_parse_context::iterator; struct parse_range { iterator begin; @@ -1940,23 +2065,24 @@ struct formatter, Char> { FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context& ctx) { auto begin = ctx.begin(), end = ctx.end(); if (begin == end || *begin == '}') return {begin, begin}; - spec_handler handler{*this, ctx, format_str}; - begin = detail::parse_align(begin, end, handler); + + begin = detail::parse_align(begin, end, specs); if (begin == end) return {begin, begin}; - begin = detail::parse_width(begin, end, handler); + + begin = detail::parse_dynamic_spec(begin, end, specs.width, width_ref, ctx); if (begin == end) return {begin, begin}; + + auto checker = detail::chrono_format_checker(); if (*begin == '.') { - if (std::is_floating_point::value) - begin = detail::parse_precision(begin, end, handler); - else - handler.on_error("precision not allowed for this argument type"); + checker.has_precision_integral = !std::is_floating_point::value; + begin = + detail::parse_precision(begin, end, precision, precision_ref, ctx); } if (begin != end && *begin == 'L') { ++begin; localized = true; } - end = detail::parse_chrono_format(begin, end, - detail::chrono_format_checker()); + end = detail::parse_chrono_format(begin, end, checker); return {begin, end}; } @@ -2002,80 +2128,140 @@ template struct formatter, Char> : formatter { FMT_CONSTEXPR formatter() { - this->do_parse(default_specs, - default_specs + sizeof(default_specs) / sizeof(Char)); - } - - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - return this->do_parse(ctx.begin(), ctx.end(), true); + this->format_str = detail::string_literal{}; } template - auto format(std::chrono::time_point val, + auto format(std::chrono::time_point val, FormatContext& ctx) const -> decltype(ctx.out()) { - return formatter::format(localtime(val), ctx); - } + using period = typename Duration::period; + if (period::num != 1 || period::den != 1 || + std::is_floating_point::value) { + const auto epoch = val.time_since_epoch(); + auto subsecs = std::chrono::duration_cast( + epoch - std::chrono::duration_cast(epoch)); - static constexpr const Char default_specs[] = {'%', 'F', ' ', '%', 'T'}; + if (subsecs.count() < 0) { + auto second = std::chrono::seconds(1); + if (epoch.count() < ((Duration::min)() + second).count()) + FMT_THROW(format_error("duration is too small")); + subsecs += second; + val -= second; + } + + return formatter::do_format( + gmtime(std::chrono::time_point_cast(val)), ctx, + &subsecs); + } + + return formatter::format( + gmtime(std::chrono::time_point_cast(val)), ctx); + } }; +#if FMT_USE_LOCAL_TIME template -constexpr const Char - formatter, - Char>::default_specs[]; +struct formatter, Char> + : formatter { + FMT_CONSTEXPR formatter() { + this->format_str = detail::string_literal{}; + } + + template + auto format(std::chrono::local_time val, FormatContext& ctx) const + -> decltype(ctx.out()) { + using period = typename Duration::period; + if (period::num != 1 || period::den != 1 || + std::is_floating_point::value) { + const auto epoch = val.time_since_epoch(); + const auto subsecs = std::chrono::duration_cast( + epoch - std::chrono::duration_cast(epoch)); + + return formatter::do_format( + localtime(std::chrono::time_point_cast(val)), + ctx, &subsecs); + } + + return formatter::format( + localtime(std::chrono::time_point_cast(val)), + ctx); + } +}; +#endif + +#if FMT_USE_UTC_TIME +template +struct formatter, + Char> + : formatter, + Char> { + template + auto format(std::chrono::time_point val, + FormatContext& ctx) const -> decltype(ctx.out()) { + return formatter< + std::chrono::time_point, + Char>::format(std::chrono::utc_clock::to_sys(val), ctx); + } +}; +#endif template struct formatter { private: - enum class spec { - unknown, - year_month_day, - hh_mm_ss, - }; - spec spec_ = spec::unknown; - basic_string_view specs; + format_specs specs; + detail::arg_ref width_ref; protected: - template - FMT_CONSTEXPR auto do_parse(It begin, It end, bool with_default = false) - -> It { - if (begin != end && *begin == ':') ++begin; + basic_string_view format_str; + + FMT_CONSTEXPR auto do_parse(basic_format_parse_context& ctx) + -> decltype(ctx.begin()) { + auto begin = ctx.begin(), end = ctx.end(); + if (begin == end || *begin == '}') return begin; + + begin = detail::parse_align(begin, end, specs); + if (begin == end) return end; + + begin = detail::parse_dynamic_spec(begin, end, specs.width, width_ref, ctx); + if (begin == end) return end; + end = detail::parse_chrono_format(begin, end, detail::tm_format_checker()); - if (!with_default || end != begin) - specs = {begin, detail::to_unsigned(end - begin)}; - // basic_string_view<>::compare isn't constexpr before C++17. - if (specs.size() == 2 && specs[0] == Char('%')) { - if (specs[1] == Char('F')) - spec_ = spec::year_month_day; - else if (specs[1] == Char('T')) - spec_ = spec::hh_mm_ss; - } + // Replace default format_str only if the new spec is not empty. + if (end != begin) format_str = {begin, detail::to_unsigned(end - begin)}; return end; } + template + auto do_format(const std::tm& tm, FormatContext& ctx, + const Duration* subsecs) const -> decltype(ctx.out()) { + auto specs_copy = specs; + basic_memory_buffer buf; + auto out = std::back_inserter(buf); + detail::handle_dynamic_spec(specs_copy.width, + width_ref, ctx); + + const auto loc_ref = ctx.locale(); + detail::get_locale loc(static_cast(loc_ref), loc_ref); + auto w = + detail::tm_writer(loc, out, tm, subsecs); + detail::parse_chrono_format(format_str.begin(), format_str.end(), w); + return detail::write( + ctx.out(), basic_string_view(buf.data(), buf.size()), specs_copy); + } + public: - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - return this->do_parse(ctx.begin(), ctx.end()); + FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) + -> decltype(ctx.begin()) { + return this->do_parse(ctx); } template auto format(const std::tm& tm, FormatContext& ctx) const -> decltype(ctx.out()) { - const auto loc_ref = ctx.locale(); - detail::get_locale loc(static_cast(loc_ref), loc_ref); - auto w = detail::tm_writer(loc, ctx.out(), tm); - if (spec_ == spec::year_month_day) - w.on_iso_date(); - else if (spec_ == spec::hh_mm_ss) - w.on_iso_time(); - else - detail::parse_chrono_format(specs.begin(), specs.end(), w); - return w.out(); + return do_format(tm, ctx, nullptr); } }; -FMT_MODULE_EXPORT_END +FMT_END_EXPORT FMT_END_NAMESPACE #endif // FMT_CHRONO_H_ diff --git a/vendor/Fmt/include/fmt/color.h b/vendor/Fmt/include/fmt/color.h index 8e26dfa3..d175448a 100644 --- a/vendor/Fmt/include/fmt/color.h +++ b/vendor/Fmt/include/fmt/color.h @@ -10,15 +10,8 @@ #include "format.h" -// __declspec(deprecated) is broken in some MSVC versions. -#if FMT_MSC_VER -# define FMT_DEPRECATED_NONMSVC -#else -# define FMT_DEPRECATED_NONMSVC FMT_DEPRECATED -#endif - FMT_BEGIN_NAMESPACE -FMT_MODULE_EXPORT_BEGIN +FMT_BEGIN_EXPORT enum class color : uint32_t { alice_blue = 0xF0F8FF, // rgb(240,248,255) @@ -270,16 +263,6 @@ class text_style { return lhs |= rhs; } - FMT_DEPRECATED_NONMSVC FMT_CONSTEXPR text_style& operator&=( - const text_style& rhs) { - return and_assign(rhs); - } - - FMT_DEPRECATED_NONMSVC friend FMT_CONSTEXPR text_style - operator&(text_style lhs, const text_style& rhs) { - return lhs.and_assign(rhs); - } - FMT_CONSTEXPR bool has_foreground() const noexcept { return set_foreground_color; } @@ -315,36 +298,9 @@ class text_style { } } - // DEPRECATED! - FMT_CONSTEXPR text_style& and_assign(const text_style& rhs) { - if (!set_foreground_color) { - set_foreground_color = rhs.set_foreground_color; - foreground_color = rhs.foreground_color; - } else if (rhs.set_foreground_color) { - if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) - FMT_THROW(format_error("can't AND a terminal color")); - foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color; - } + friend FMT_CONSTEXPR text_style fg(detail::color_type foreground) noexcept; - if (!set_background_color) { - set_background_color = rhs.set_background_color; - background_color = rhs.background_color; - } else if (rhs.set_background_color) { - if (!background_color.is_rgb || !rhs.background_color.is_rgb) - FMT_THROW(format_error("can't AND a terminal color")); - background_color.value.rgb_color &= rhs.background_color.value.rgb_color; - } - - ems = static_cast(static_cast(ems) & - static_cast(rhs.ems)); - return *this; - } - - friend FMT_CONSTEXPR_DECL text_style - fg(detail::color_type foreground) noexcept; - - friend FMT_CONSTEXPR_DECL text_style - bg(detail::color_type background) noexcept; + friend FMT_CONSTEXPR text_style bg(detail::color_type background) noexcept; detail::color_type foreground_color; detail::color_type background_color; @@ -467,26 +423,6 @@ FMT_CONSTEXPR ansi_color_escape make_emphasis(emphasis em) noexcept { return ansi_color_escape(em); } -template inline void fputs(const Char* chars, FILE* stream) { - int result = std::fputs(chars, stream); - if (result < 0) - FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); -} - -template <> inline void fputs(const wchar_t* chars, FILE* stream) { - int result = std::fputws(chars, stream); - if (result < 0) - FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); -} - -template inline void reset_color(FILE* stream) { - fputs("\x1b[0m", stream); -} - -template <> inline void reset_color(FILE* stream) { - fputs(L"\x1b[0m", stream); -} - template inline void reset_color(buffer& buffer) { auto reset_color = string_view("\x1b[0m"); buffer.append(reset_color.begin(), reset_color.end()); @@ -523,17 +459,19 @@ void vformat_to(buffer& buf, const text_style& ts, FMT_END_DETAIL_NAMESPACE -template > -void vprint(std::FILE* f, const text_style& ts, const S& format, - basic_format_args>> args) { - basic_memory_buffer buf; - detail::vformat_to(buf, ts, to_string_view(format), args); +inline void vprint(std::FILE* f, const text_style& ts, string_view fmt, + format_args args) { + // Legacy wide streams are not supported. + auto buf = memory_buffer(); + detail::vformat_to(buf, ts, fmt, args); if (detail::is_utf8()) { - detail::print(f, basic_string_view(buf.begin(), buf.size())); - } else { - buf.push_back(Char(0)); - detail::fputs(buf.data(), f); + detail::print(f, string_view(buf.begin(), buf.size())); + return; } + buf.push_back('\0'); + int result = std::fputs(buf.data(), f); + if (result < 0) + FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); } /** @@ -577,7 +515,7 @@ inline std::basic_string vformat( const text_style& ts, const S& format_str, basic_format_args>> args) { basic_memory_buffer buf; - detail::vformat_to(buf, ts, to_string_view(format_str), args); + detail::vformat_to(buf, ts, detail::to_string_view(format_str), args); return fmt::to_string(buf); } @@ -596,7 +534,7 @@ inline std::basic_string vformat( template > inline std::basic_string format(const text_style& ts, const S& format_str, const Args&... args) { - return fmt::vformat(ts, to_string_view(format_str), + return fmt::vformat(ts, detail::to_string_view(format_str), fmt::make_format_args>(args...)); } @@ -610,7 +548,7 @@ OutputIt vformat_to( basic_format_args>> args) { auto&& buf = detail::get_buffer(out); detail::vformat_to(buf, ts, format_str, args); - return detail::get_iterator(buf); + return detail::get_iterator(buf, out); } /** @@ -631,7 +569,7 @@ template typename std::enable_if::type { - return vformat_to(out, ts, to_string_view(format_str), + return vformat_to(out, ts, detail::to_string_view(format_str), fmt::make_format_args>>(args...)); } @@ -678,8 +616,9 @@ struct formatter, Char> : formatter { **Example**:: - fmt::print("Elapsed time: {s:.2f} seconds", - fmt::styled(1.23, fmt::fg(fmt::colors::green) | fmt::bg(fmt::color::blue))); + fmt::print("Elapsed time: {0:.2f} seconds", + fmt::styled(1.23, fmt::fg(fmt::color::green) | + fmt::bg(fmt::color::blue))); \endrst */ template @@ -688,7 +627,7 @@ FMT_CONSTEXPR auto styled(const T& value, text_style ts) return detail::styled_arg>{value, ts}; } -FMT_MODULE_EXPORT_END +FMT_END_EXPORT FMT_END_NAMESPACE #endif // FMT_COLOR_H_ diff --git a/vendor/Fmt/include/fmt/compile.h b/vendor/Fmt/include/fmt/compile.h index b2bf1ba1..94e13c02 100644 --- a/vendor/Fmt/include/fmt/compile.h +++ b/vendor/Fmt/include/fmt/compile.h @@ -14,8 +14,8 @@ FMT_BEGIN_NAMESPACE namespace detail { template -inline counting_iterator copy_str(InputIt begin, InputIt end, - counting_iterator it) { +FMT_CONSTEXPR inline counting_iterator copy_str(InputIt begin, InputIt end, + counting_iterator it) { return it + (end - begin); } @@ -36,8 +36,7 @@ template class truncating_iterator_base { using difference_type = std::ptrdiff_t; using pointer = void; using reference = void; - using _Unchecked_type = - truncating_iterator_base; // Mark iterator as checked. + FMT_UNCHECKED_ITERATOR(truncating_iterator_base); OutputIt base() const { return out_; } size_t count() const { return count_; } @@ -124,7 +123,7 @@ struct is_compiled_string : std::is_base_of {}; # define FMT_COMPILE(s) FMT_STRING(s) #endif -#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +#if FMT_USE_NONTYPE_TEMPLATE_ARGS template Str> struct udl_compiled_string : compiled_string { @@ -332,38 +331,35 @@ template struct parse_specs_result { int next_arg_id; }; -constexpr int manual_indexing_id = -1; +enum { manual_indexing_id = -1 }; template constexpr parse_specs_result parse_specs(basic_string_view str, size_t pos, int next_arg_id) { str.remove_prefix(pos); - auto ctx = basic_format_parse_context(str, {}, next_arg_id); + auto ctx = + compile_parse_context(str, max_value(), nullptr, next_arg_id); auto f = formatter(); auto end = f.parse(ctx); - return {f, pos + fmt::detail::to_unsigned(end - str.data()) + 1, + return {f, pos + fmt::detail::to_unsigned(end - str.data()), next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()}; } template struct arg_id_handler { arg_ref arg_id; - constexpr int operator()() { + constexpr int on_auto() { FMT_ASSERT(false, "handler cannot be used with automatic indexing"); return 0; } - constexpr int operator()(int id) { + constexpr int on_index(int id) { arg_id = arg_ref(id); return 0; } - constexpr int operator()(basic_string_view id) { + constexpr int on_name(basic_string_view id) { arg_id = arg_ref(id); return 0; } - - constexpr void on_error(const char* message) { - FMT_THROW(format_error(message)); - } }; template struct parse_arg_id_result { @@ -397,13 +393,20 @@ constexpr auto parse_replacement_field_then_tail(S format_str) { return parse_tail( field::type, ARG_INDEX>(), format_str); - } else if constexpr (c == ':') { + } else if constexpr (c != ':') { + FMT_THROW(format_error("expected ':'")); + } else { constexpr auto result = parse_specs::type>( str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID); - return parse_tail( - spec_field::type, ARG_INDEX>{ - result.fmt}, - format_str); + if constexpr (result.end >= str.size() || str[result.end] != '}') { + FMT_THROW(format_error("expected '}'")); + return 0; + } else { + return parse_tail( + spec_field::type, ARG_INDEX>{ + result.fmt}, + format_str); + } } } @@ -494,7 +497,7 @@ constexpr auto compile(S format_str) { #endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) } // namespace detail -FMT_MODULE_EXPORT_BEGIN +FMT_BEGIN_EXPORT #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) @@ -568,7 +571,8 @@ format_to_n_result format_to_n(OutputIt out, size_t n, template ::value)> -size_t formatted_size(const S& format_str, const Args&... args) { +FMT_CONSTEXPR20 size_t formatted_size(const S& format_str, + const Args&... args) { return fmt::format_to(detail::counting_iterator(), format_str, args...) .count(); } @@ -587,7 +591,7 @@ void print(const S& format_str, const Args&... args) { print(stdout, format_str, args...); } -#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +#if FMT_USE_NONTYPE_TEMPLATE_ARGS inline namespace literals { template constexpr auto operator""_cf() { using char_t = remove_cvref_t; @@ -597,7 +601,7 @@ template constexpr auto operator""_cf() { } // namespace literals #endif -FMT_MODULE_EXPORT_END +FMT_END_EXPORT FMT_END_NAMESPACE #endif // FMT_COMPILE_H_ diff --git a/vendor/Fmt/include/fmt/core.h b/vendor/Fmt/include/fmt/core.h index 1c81bbd2..46723d59 100644 --- a/vendor/Fmt/include/fmt/core.h +++ b/vendor/Fmt/include/fmt/core.h @@ -8,6 +8,7 @@ #ifndef FMT_CORE_H_ #define FMT_CORE_H_ +#include // std::byte #include // std::FILE #include // std::strlen #include @@ -16,7 +17,7 @@ #include // The fmt library version in the form major * 10000 + minor * 100 + patch. -#define FMT_VERSION 80102 +#define FMT_VERSION 100000 #if defined(__clang__) && !defined(__ibmxl__) # define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) @@ -48,36 +49,27 @@ # define FMT_ICC_VERSION 0 #endif -#ifdef __NVCOMPILER -# define FMT_NVCOMPILER_VERSION \ - (__NVCOMPILER_MAJOR__ * 100 + __NVCOMPILER_MINOR__) -#else -# define FMT_NVCOMPILER_VERSION 0 -#endif - -#ifdef __NVCC__ -# define FMT_NVCC __NVCC__ -#else -# define FMT_NVCC 0 -#endif - #ifdef _MSC_VER -# define FMT_MSC_VER _MSC_VER +# define FMT_MSC_VERSION _MSC_VER # define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__)) #else -# define FMT_MSC_VER 0 +# define FMT_MSC_VERSION 0 # define FMT_MSC_WARNING(...) #endif +#ifdef _MSVC_LANG +# define FMT_CPLUSPLUS _MSVC_LANG +#else +# define FMT_CPLUSPLUS __cplusplus +#endif + #ifdef __has_feature # define FMT_HAS_FEATURE(x) __has_feature(x) #else # define FMT_HAS_FEATURE(x) 0 #endif -#if defined(__has_include) && \ - (!defined(__INTELLISENSE__) || FMT_MSC_VER > 1900) && \ - (!FMT_ICC_VERSION || FMT_ICC_VERSION >= 1600) +#if defined(__has_include) || FMT_ICC_VERSION >= 1600 || FMT_MSC_VERSION > 1900 # define FMT_HAS_INCLUDE(x) __has_include(x) #else # define FMT_HAS_INCLUDE(x) 0 @@ -89,12 +81,6 @@ # define FMT_HAS_CPP_ATTRIBUTE(x) 0 #endif -#ifdef _MSVC_LANG -# define FMT_CPLUSPLUS _MSVC_LANG -#else -# define FMT_CPLUSPLUS __cplusplus -#endif - #define FMT_HAS_CPP14_ATTRIBUTE(attribute) \ (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute)) @@ -104,37 +90,38 @@ // Check if relaxed C++14 constexpr is supported. // GCC doesn't allow throw in constexpr until version 6 (bug 67371). #ifndef FMT_USE_CONSTEXPR -# define FMT_USE_CONSTEXPR \ - (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1912 || \ - (FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) && \ - !FMT_NVCC && !FMT_ICC_VERSION +# if (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912 || \ + (FMT_GCC_VERSION >= 600 && FMT_CPLUSPLUS >= 201402L)) && \ + !FMT_ICC_VERSION && !defined(__NVCC__) +# define FMT_USE_CONSTEXPR 1 +# else +# define FMT_USE_CONSTEXPR 0 +# endif #endif #if FMT_USE_CONSTEXPR # define FMT_CONSTEXPR constexpr -# define FMT_CONSTEXPR_DECL constexpr #else # define FMT_CONSTEXPR -# define FMT_CONSTEXPR_DECL #endif -#if ((__cplusplus >= 202002L) && \ +#if ((FMT_CPLUSPLUS >= 202002L) && \ (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9)) || \ - (__cplusplus >= 201709L && FMT_GCC_VERSION >= 1002) + (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002) # define FMT_CONSTEXPR20 constexpr #else # define FMT_CONSTEXPR20 #endif -// Check if constexpr std::char_traits<>::compare,length is supported. +// Check if constexpr std::char_traits<>::{compare,length} are supported. #if defined(__GLIBCXX__) -# if __cplusplus >= 201703L && defined(_GLIBCXX_RELEASE) && \ +# if FMT_CPLUSPLUS >= 201703L && defined(_GLIBCXX_RELEASE) && \ _GLIBCXX_RELEASE >= 7 // GCC 7+ libstdc++ has _GLIBCXX_RELEASE. # define FMT_CONSTEXPR_CHAR_TRAITS constexpr # endif -#elif defined(_LIBCPP_VERSION) && __cplusplus >= 201703L && \ +#elif defined(_LIBCPP_VERSION) && FMT_CPLUSPLUS >= 201703L && \ _LIBCPP_VERSION >= 4000 # define FMT_CONSTEXPR_CHAR_TRAITS constexpr -#elif FMT_MSC_VER >= 1914 && _MSVC_LANG >= 201703L +#elif FMT_MSC_VERSION >= 1914 && FMT_CPLUSPLUS >= 201703L # define FMT_CONSTEXPR_CHAR_TRAITS constexpr #endif #ifndef FMT_CONSTEXPR_CHAR_TRAITS @@ -144,39 +131,21 @@ // Check if exceptions are disabled. #ifndef FMT_EXCEPTIONS # if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \ - FMT_MSC_VER && !_HAS_EXCEPTIONS + (FMT_MSC_VERSION && !_HAS_EXCEPTIONS) # define FMT_EXCEPTIONS 0 # else # define FMT_EXCEPTIONS 1 # endif #endif -// [[noreturn]] is disabled on MSVC and NVCC because of bogus unreachable code -// warnings. -#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER && \ - !FMT_NVCC +// Disable [[noreturn]] on MSVC/NVCC because of bogus unreachable code warnings. +#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VERSION && \ + !defined(__NVCC__) # define FMT_NORETURN [[noreturn]] #else # define FMT_NORETURN #endif -#if __cplusplus == 201103L || __cplusplus == 201402L -# if defined(__INTEL_COMPILER) || defined(__PGI) -# define FMT_FALLTHROUGH -# elif defined(__clang__) -# define FMT_FALLTHROUGH [[clang::fallthrough]] -# elif FMT_GCC_VERSION >= 700 && \ - (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520) -# define FMT_FALLTHROUGH [[gnu::fallthrough]] -# else -# define FMT_FALLTHROUGH -# endif -#elif FMT_HAS_CPP17_ATTRIBUTE(fallthrough) -# define FMT_FALLTHROUGH [[fallthrough]] -#else -# define FMT_FALLTHROUGH -#endif - #ifndef FMT_NODISCARD # if FMT_HAS_CPP17_ATTRIBUTE(nodiscard) # define FMT_NODISCARD [[nodiscard]] @@ -185,16 +154,6 @@ # endif #endif -#ifndef FMT_USE_FLOAT -# define FMT_USE_FLOAT 1 -#endif -#ifndef FMT_USE_DOUBLE -# define FMT_USE_DOUBLE 1 -#endif -#ifndef FMT_USE_LONG_DOUBLE -# define FMT_USE_LONG_DOUBLE 1 -#endif - #ifndef FMT_INLINE # if FMT_GCC_VERSION || FMT_CLANG_VERSION # define FMT_INLINE inline __attribute__((always_inline)) @@ -203,24 +162,20 @@ # endif #endif -#ifndef FMT_DEPRECATED -# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900 -# define FMT_DEPRECATED [[deprecated]] -# else -# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__) -# define FMT_DEPRECATED __attribute__((deprecated)) -# elif FMT_MSC_VER -# define FMT_DEPRECATED __declspec(deprecated) -# else -# define FMT_DEPRECATED /* deprecated */ -# endif -# endif +// An inline std::forward replacement. +#define FMT_FORWARD(...) static_cast(__VA_ARGS__) + +#ifdef _MSC_VER +# define FMT_UNCHECKED_ITERATOR(It) \ + using _Unchecked_type = It // Mark iterator as checked. +#else +# define FMT_UNCHECKED_ITERATOR(It) using unchecked_type = It #endif #ifndef FMT_BEGIN_NAMESPACE # define FMT_BEGIN_NAMESPACE \ namespace fmt { \ - inline namespace v8 { + inline namespace v10 { # define FMT_END_NAMESPACE \ } \ } @@ -228,22 +183,18 @@ #ifndef FMT_MODULE_EXPORT # define FMT_MODULE_EXPORT -# define FMT_MODULE_EXPORT_BEGIN -# define FMT_MODULE_EXPORT_END -# define FMT_BEGIN_DETAIL_NAMESPACE namespace detail { -# define FMT_END_DETAIL_NAMESPACE } +# define FMT_BEGIN_EXPORT +# define FMT_END_EXPORT #endif #if !defined(FMT_HEADER_ONLY) && defined(_WIN32) -# define FMT_CLASS_API FMT_MSC_WARNING(suppress : 4275) -# ifdef FMT_EXPORT +# ifdef FMT_LIB_EXPORT # define FMT_API __declspec(dllexport) # elif defined(FMT_SHARED) # define FMT_API __declspec(dllimport) # endif #else -# define FMT_CLASS_API -# if defined(FMT_EXPORT) || defined(FMT_SHARED) +# if defined(FMT_LIB_EXPORT) || defined(FMT_SHARED) # if defined(__GNUC__) || defined(__clang__) # define FMT_API __attribute__((visibility("default"))) # endif @@ -254,26 +205,27 @@ #endif // libc++ supports string_view in pre-c++17. -#if (FMT_HAS_INCLUDE() && \ - (__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \ - (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910) +#if FMT_HAS_INCLUDE() && \ + (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION)) # include # define FMT_USE_STRING_VIEW -#elif FMT_HAS_INCLUDE("experimental/string_view") && __cplusplus >= 201402L +#elif FMT_HAS_INCLUDE("experimental/string_view") && FMT_CPLUSPLUS >= 201402L # include # define FMT_USE_EXPERIMENTAL_STRING_VIEW #endif #ifndef FMT_UNICODE -# define FMT_UNICODE !FMT_MSC_VER +# define FMT_UNICODE !FMT_MSC_VERSION #endif #ifndef FMT_CONSTEVAL -# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \ - __cplusplus > 201703L && !defined(__apple_build_version__)) || \ - (defined(__cpp_consteval) && \ - (!FMT_MSC_VER || _MSC_FULL_VER >= 193030704)) -// consteval is broken in MSVC before VS2022 and Apple clang 13. +# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \ + (!defined(__apple_build_version__) || \ + __apple_build_version__ >= 14000029L) && \ + FMT_CPLUSPLUS >= 202002L) || \ + (defined(__cpp_consteval) && \ + (!FMT_MSC_VERSION || _MSC_FULL_VER >= 193030704)) +// consteval is broken in MSVC before VS2022 and Apple clang before 14. # define FMT_CONSTEVAL consteval # define FMT_HAS_CONSTEVAL # else @@ -281,24 +233,31 @@ # endif #endif -#ifndef FMT_USE_NONTYPE_TEMPLATE_PARAMETERS -# if defined(__cpp_nontype_template_args) && \ - ((FMT_GCC_VERSION >= 903 && __cplusplus >= 201709L) || \ - __cpp_nontype_template_args >= 201911L) -# define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 1 +#ifndef FMT_USE_NONTYPE_TEMPLATE_ARGS +# if defined(__cpp_nontype_template_args) && \ + ((FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L) || \ + __cpp_nontype_template_args >= 201911L) && \ + !defined(__NVCOMPILER) && !defined(__LCC__) +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 # else -# define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 0 +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0 # endif #endif +#if defined __cpp_inline_variables && __cpp_inline_variables >= 201606L +# define FMT_INLINE_VARIABLE inline +#else +# define FMT_INLINE_VARIABLE +#endif + // Enable minimal optimizations for more compact code in debug mode. FMT_GCC_PRAGMA("GCC push_options") -#if !defined(__OPTIMIZE__) && !FMT_NVCOMPILER_VERSION +#if !defined(__OPTIMIZE__) && !defined(__NVCOMPILER) && !defined(__LCC__) && \ + !defined(__CUDACC__) FMT_GCC_PRAGMA("GCC optimize(\"Og\")") #endif FMT_BEGIN_NAMESPACE -FMT_MODULE_EXPORT_BEGIN // Implementations of enable_if_t and other metafunctions for older systems. template @@ -327,19 +286,32 @@ struct monostate { #ifdef FMT_DOC # define FMT_ENABLE_IF(...) #else -# define FMT_ENABLE_IF(...) enable_if_t<(__VA_ARGS__), int> = 0 +# define FMT_ENABLE_IF(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0 #endif -FMT_BEGIN_DETAIL_NAMESPACE +#ifdef __cpp_lib_byte +inline auto format_as(std::byte b) -> unsigned char { + return static_cast(b); +} +#endif -// Suppress "unused variable" warnings with the method described in +namespace detail { +// Suppresses "unused variable" warnings with the method described in // https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/. // (void)var does not work on many Intel compilers. template FMT_CONSTEXPR void ignore_unused(const T&...) {} constexpr FMT_INLINE auto is_constant_evaluated( bool default_value = false) noexcept -> bool { -#ifdef __cpp_lib_is_constant_evaluated +// Workaround for incompatibility between libstdc++ consteval-based +// std::is_constant_evaluated() implementation and clang-14. +// https://github.com/fmtlib/fmt/issues/3247 +#if FMT_CPLUSPLUS >= 202002L && defined(_GLIBCXX_RELEASE) && \ + _GLIBCXX_RELEASE >= 12 && \ + (FMT_CLANG_VERSION >= 1400 && FMT_CLANG_VERSION < 1500) + ignore_unused(default_value); + return __builtin_is_constant_evaluated(); +#elif defined(__cpp_lib_is_constant_evaluated) ignore_unused(default_value); return std::is_constant_evaluated(); #else @@ -347,7 +319,7 @@ constexpr FMT_INLINE auto is_constant_evaluated( #endif } -// A function to suppress "conditional expression is constant" warnings. +// Suppresses "conditional expression is constant" warnings. template constexpr FMT_INLINE auto const_check(T value) -> T { return value; } @@ -357,14 +329,14 @@ FMT_NORETURN FMT_API void assert_fail(const char* file, int line, #ifndef FMT_ASSERT # ifdef NDEBUG -// FMT_ASSERT is not empty to avoid -Werror=empty-body. +// FMT_ASSERT is not empty to avoid -Wempty-body. # define FMT_ASSERT(condition, message) \ - ::fmt::detail::ignore_unused((condition), (message)) + fmt::detail::ignore_unused((condition), (message)) # else # define FMT_ASSERT(condition, message) \ ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \ ? (void)0 \ - : ::fmt::detail::assert_fail(__FILE__, __LINE__, (message))) + : fmt::detail::assert_fail(__FILE__, __LINE__, (message))) # endif #endif @@ -379,10 +351,10 @@ template struct std_string_view {}; #ifdef FMT_USE_INT128 // Do nothing. -#elif defined(__SIZEOF_INT128__) && !FMT_NVCC && \ - !(FMT_CLANG_VERSION && FMT_MSC_VER) +#elif defined(__SIZEOF_INT128__) && !defined(__NVCC__) && \ + !(FMT_CLANG_VERSION && FMT_MSC_VERSION) # define FMT_USE_INT128 1 -using int128_opt = __int128_t; // An optional 128-bit integer. +using int128_opt = __int128_t; // An optional native 128-bit integer. using uint128_opt = __uint128_t; template inline auto convert_for_visit(T value) -> T { return value; @@ -401,20 +373,19 @@ template auto convert_for_visit(T) -> monostate { return {}; } template FMT_CONSTEXPR auto to_unsigned(Int value) -> typename std::make_unsigned::type { - FMT_ASSERT(value >= 0, "negative value"); + FMT_ASSERT(std::is_unsigned::value || value >= 0, "negative value"); return static_cast::type>(value); } -FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char micro[] = "\u00B5"; +FMT_CONSTEXPR inline auto is_utf8() -> bool { + FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char section[] = "\u00A7"; -constexpr auto is_utf8() -> bool { - // Avoid buggy sign extensions in MSVC's constant evaluation mode. - // https://developercommunity.visualstudio.com/t/C-difference-in-behavior-for-unsigned/1233612 + // Avoid buggy sign extensions in MSVC's constant evaluation mode (#2297). using uchar = unsigned char; - return FMT_UNICODE || (sizeof(micro) == 3 && uchar(micro[0]) == 0xC2 && - uchar(micro[1]) == 0xB5); + return FMT_UNICODE || (sizeof(section) == 3 && uchar(section[0]) == 0xC2 && + uchar(section[1]) == 0xA7); } -FMT_END_DETAIL_NAMESPACE +} // namespace detail /** An implementation of ``std::basic_string_view`` for pre-C++17. It provides a @@ -423,6 +394,7 @@ FMT_END_DETAIL_NAMESPACE compiled with a different ``-std`` option than the client code (which is not recommended). */ +FMT_MODULE_EXPORT template class basic_string_view { private: const Char* data_; @@ -482,6 +454,18 @@ template class basic_string_view { size_ -= n; } + FMT_CONSTEXPR_CHAR_TRAITS bool starts_with( + basic_string_view sv) const noexcept { + return size_ >= sv.size_ && + std::char_traits::compare(data_, sv.data_, sv.size_) == 0; + } + FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(Char c) const noexcept { + return size_ >= 1 && std::char_traits::eq(*data_, c); + } + FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(const Char* s) const { + return starts_with(basic_string_view(s)); + } + // Lexicographically compare this string reference to other. FMT_CONSTEXPR_CHAR_TRAITS auto compare(basic_string_view other) const -> int { size_t str_size = size_ < other.size_ ? size_ : other.size_; @@ -513,13 +497,22 @@ template class basic_string_view { } }; +FMT_MODULE_EXPORT using string_view = basic_string_view; /** Specifies if ``T`` is a character type. Can be specialized by users. */ +FMT_MODULE_EXPORT template struct is_char : std::false_type {}; template <> struct is_char : std::true_type {}; -// Returns a string view of `s`. +namespace detail { + +// A base class for compile-time strings. +struct compile_string {}; + +template +struct is_compile_string : std::is_base_of {}; + template ::value)> FMT_INLINE auto to_string_view(const Char* s) -> basic_string_view { return s; @@ -535,36 +528,24 @@ constexpr auto to_string_view(basic_string_view s) return s; } template >::value)> -inline auto to_string_view(detail::std_string_view s) - -> basic_string_view { + FMT_ENABLE_IF(!std::is_empty>::value)> +inline auto to_string_view(std_string_view s) -> basic_string_view { return s; } - -// A base class for compile-time strings. It is defined in the fmt namespace to -// make formatting functions visible via ADL, e.g. format(FMT_STRING("{}"), 42). -struct compile_string {}; - -template -struct is_compile_string : std::is_base_of {}; - template ::value)> constexpr auto to_string_view(const S& s) -> basic_string_view { return basic_string_view(s); } - -FMT_BEGIN_DETAIL_NAMESPACE - void to_string_view(...); -using fmt::to_string_view; // Specifies whether S is a string type convertible to fmt::basic_string_view. // It should be a constexpr function but MSVC 2017 fails to compile it in // enable_if and MSVC 2015 fails to compile it as an alias template. +// ADL is intentionally disabled as to_string_view is not an extension point. template -struct is_string : std::is_class()))> { -}; +struct is_string + : std::is_class()))> {}; template struct char_t_impl {}; template struct char_t_impl::value>> { @@ -572,28 +553,91 @@ template struct char_t_impl::value>> { using type = typename result::value_type; }; -// Reports a compile-time error if S is not a valid format string. -template ::value)> -FMT_INLINE void check_format_string(const S&) { -#ifdef FMT_ENFORCE_COMPILE_STRING - static_assert(is_compile_string::value, - "FMT_ENFORCE_COMPILE_STRING requires all format strings to use " - "FMT_STRING."); -#endif +enum class type { + none_type, + // Integer types should go first, + int_type, + uint_type, + long_long_type, + ulong_long_type, + int128_type, + uint128_type, + bool_type, + char_type, + last_integer_type = char_type, + // followed by floating-point types. + float_type, + double_type, + long_double_type, + last_numeric_type = long_double_type, + cstring_type, + string_type, + pointer_type, + custom_type +}; + +// Maps core type T to the corresponding type enum constant. +template +struct type_constant : std::integral_constant {}; + +#define FMT_TYPE_CONSTANT(Type, constant) \ + template \ + struct type_constant \ + : std::integral_constant {} + +FMT_TYPE_CONSTANT(int, int_type); +FMT_TYPE_CONSTANT(unsigned, uint_type); +FMT_TYPE_CONSTANT(long long, long_long_type); +FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type); +FMT_TYPE_CONSTANT(int128_opt, int128_type); +FMT_TYPE_CONSTANT(uint128_opt, uint128_type); +FMT_TYPE_CONSTANT(bool, bool_type); +FMT_TYPE_CONSTANT(Char, char_type); +FMT_TYPE_CONSTANT(float, float_type); +FMT_TYPE_CONSTANT(double, double_type); +FMT_TYPE_CONSTANT(long double, long_double_type); +FMT_TYPE_CONSTANT(const Char*, cstring_type); +FMT_TYPE_CONSTANT(basic_string_view, string_type); +FMT_TYPE_CONSTANT(const void*, pointer_type); + +constexpr bool is_integral_type(type t) { + return t > type::none_type && t <= type::last_integer_type; } -template ::value)> -void check_format_string(S); +constexpr bool is_arithmetic_type(type t) { + return t > type::none_type && t <= type::last_numeric_type; +} + +constexpr auto set(type rhs) -> int { return 1 << static_cast(rhs); } +constexpr auto in(type t, int set) -> bool { + return ((set >> static_cast(t)) & 1) != 0; +} + +// Bitsets of types. +enum { + sint_set = + set(type::int_type) | set(type::long_long_type) | set(type::int128_type), + uint_set = set(type::uint_type) | set(type::ulong_long_type) | + set(type::uint128_type), + bool_set = set(type::bool_type), + char_set = set(type::char_type), + float_set = set(type::float_type) | set(type::double_type) | + set(type::long_double_type), + string_set = set(type::string_type), + cstring_set = set(type::cstring_type), + pointer_set = set(type::pointer_type) +}; FMT_NORETURN FMT_API void throw_format_error(const char* message); struct error_handler { constexpr error_handler() = default; - constexpr error_handler(const error_handler&) = default; // This function is intentionally not constexpr to give a compile-time error. - FMT_NORETURN FMT_API void on_error(const char* message); + FMT_NORETURN void on_error(const char* message) { + throw_format_error(message); + } }; -FMT_END_DETAIL_NAMESPACE +} // namespace detail /** String's character type. */ template using char_t = typename detail::char_t_impl::type; @@ -605,20 +649,21 @@ template using char_t = typename detail::char_t_impl::type; You can use the ``format_parse_context`` type alias for ``char`` instead. \endrst */ -template -class basic_format_parse_context : private ErrorHandler { +FMT_MODULE_EXPORT +template class basic_format_parse_context { private: basic_string_view format_str_; int next_arg_id_; + FMT_CONSTEXPR void do_check_arg_id(int id); + public: using char_type = Char; - using iterator = typename basic_string_view::iterator; + using iterator = const Char*; explicit constexpr basic_format_parse_context( - basic_string_view format_str, ErrorHandler eh = {}, - int next_arg_id = 0) - : ErrorHandler(eh), format_str_(format_str), next_arg_id_(next_arg_id) {} + basic_string_view format_str, int next_arg_id = 0) + : format_str_(format_str), next_arg_id_(next_arg_id) {} /** Returns an iterator to the beginning of the format string range being @@ -643,40 +688,104 @@ class basic_format_parse_context : private ErrorHandler { the next argument index and switches to the automatic indexing. */ FMT_CONSTEXPR auto next_arg_id() -> int { - // Don't check if the argument id is valid to avoid overhead and because it - // will be checked during formatting anyway. - if (next_arg_id_ >= 0) return next_arg_id_++; - on_error("cannot switch from manual to automatic argument indexing"); - return 0; + if (next_arg_id_ < 0) { + detail::throw_format_error( + "cannot switch from manual to automatic argument indexing"); + return 0; + } + int id = next_arg_id_++; + do_check_arg_id(id); + return id; } /** Reports an error if using the automatic argument indexing; otherwise switches to the manual indexing. */ - FMT_CONSTEXPR void check_arg_id(int) { - if (next_arg_id_ > 0) - on_error("cannot switch from automatic to manual argument indexing"); - else - next_arg_id_ = -1; + FMT_CONSTEXPR void check_arg_id(int id) { + if (next_arg_id_ > 0) { + detail::throw_format_error( + "cannot switch from automatic to manual argument indexing"); + return; + } + next_arg_id_ = -1; + do_check_arg_id(id); } - FMT_CONSTEXPR void check_arg_id(basic_string_view) {} - - FMT_CONSTEXPR void on_error(const char* message) { - ErrorHandler::on_error(message); - } - - constexpr auto error_handler() const -> ErrorHandler { return *this; } + FMT_CONSTEXPR void check_dynamic_spec(int arg_id); }; +FMT_MODULE_EXPORT using format_parse_context = basic_format_parse_context; -template class basic_format_arg; -template class basic_format_args; -template class dynamic_format_arg_store; +namespace detail { +// A parse context with extra data used only in compile-time checks. +template +class compile_parse_context : public basic_format_parse_context { + private: + int num_args_; + const type* types_; + using base = basic_format_parse_context; + + public: + explicit FMT_CONSTEXPR compile_parse_context( + basic_string_view format_str, int num_args, const type* types, + int next_arg_id = 0) + : base(format_str, next_arg_id), num_args_(num_args), types_(types) {} + + constexpr auto num_args() const -> int { return num_args_; } + constexpr auto arg_type(int id) const -> type { return types_[id]; } + + FMT_CONSTEXPR auto next_arg_id() -> int { + int id = base::next_arg_id(); + if (id >= num_args_) throw_format_error("argument not found"); + return id; + } + + FMT_CONSTEXPR void check_arg_id(int id) { + base::check_arg_id(id); + if (id >= num_args_) throw_format_error("argument not found"); + } + using base::check_arg_id; + + FMT_CONSTEXPR void check_dynamic_spec(int arg_id) { + detail::ignore_unused(arg_id); +#if !defined(__LCC__) + if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id])) + throw_format_error("width/precision is not integer"); +#endif + } +}; +} // namespace detail + +template +FMT_CONSTEXPR void basic_format_parse_context::do_check_arg_id(int id) { + // Argument id is only checked at compile-time during parsing because + // formatting has its own validation. + if (detail::is_constant_evaluated() && + (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) { + using context = detail::compile_parse_context; + if (id >= static_cast(this)->num_args()) + detail::throw_format_error("argument not found"); + } +} + +template +FMT_CONSTEXPR void basic_format_parse_context::check_dynamic_spec( + int arg_id) { + if (detail::is_constant_evaluated() && + (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) { + using context = detail::compile_parse_context; + static_cast(this)->check_dynamic_spec(arg_id); + } +} + +FMT_MODULE_EXPORT template class basic_format_arg; +FMT_MODULE_EXPORT template class basic_format_args; +FMT_MODULE_EXPORT template class dynamic_format_arg_store; // A formatter for objects of type T. +FMT_MODULE_EXPORT template struct formatter { // A deleted default constructor indicates a disabled formatter. @@ -696,7 +805,7 @@ struct is_contiguous> : std::true_type {}; class appender; -FMT_BEGIN_DETAIL_NAMESPACE +namespace detail { template constexpr auto has_const_formatter_impl(T*) @@ -718,10 +827,10 @@ constexpr auto has_const_formatter() -> bool { template inline auto get_container(std::back_insert_iterator it) -> Container& { - using bi_iterator = std::back_insert_iterator; - struct accessor : bi_iterator { - accessor(bi_iterator iter) : bi_iterator(iter) {} - using bi_iterator::container; + using base = std::back_insert_iterator; + struct accessor : base { + accessor(base b) : base(b) {} + using base::container; }; return *accessor(it).container; } @@ -739,7 +848,7 @@ template U* { if (is_constant_evaluated()) return copy_str(begin, end, out); auto size = to_unsigned(end - begin); - memcpy(out, begin, size * sizeof(U)); + if (size > 0) memcpy(out, begin, size * sizeof(U)); return out + size; } @@ -782,11 +891,11 @@ template class buffer { buffer(const buffer&) = delete; void operator=(const buffer&) = delete; - auto begin() noexcept -> T* { return ptr_; } - auto end() noexcept -> T* { return ptr_ + size_; } + FMT_INLINE auto begin() noexcept -> T* { return ptr_; } + FMT_INLINE auto end() noexcept -> T* { return ptr_ + size_; } - auto begin() const noexcept -> const T* { return ptr_; } - auto end() const noexcept -> const T* { return ptr_ + size_; } + FMT_INLINE auto begin() const noexcept -> const T* { return ptr_; } + FMT_INLINE auto end() const noexcept -> const T* { return ptr_ + size_; } /** Returns the size of this buffer. */ constexpr auto size() const noexcept -> size_t { return size_; } @@ -826,11 +935,11 @@ template class buffer { /** Appends data to the end of the buffer. */ template void append(const U* begin, const U* end); - template FMT_CONSTEXPR auto operator[](I index) -> T& { + template FMT_CONSTEXPR auto operator[](Idx index) -> T& { return ptr_[index]; } - template - FMT_CONSTEXPR auto operator[](I index) const -> const T& { + template + FMT_CONSTEXPR auto operator[](Idx index) const -> const T& { return ptr_[index]; } }; @@ -965,6 +1074,7 @@ class iterator_buffer, : buffer(c.size()), container_(c) {} explicit iterator_buffer(std::back_insert_iterator out, size_t = 0) : iterator_buffer(get_container(out)) {} + auto out() -> std::back_insert_iterator { return std::back_inserter(container_); } @@ -999,29 +1109,21 @@ template auto get_buffer(OutputIt out) -> iterator_buffer { return iterator_buffer(out); } +template , Buf>::value)> +auto get_buffer(std::back_insert_iterator out) -> buffer& { + return get_container(out); +} -template -auto get_iterator(Buffer& buf) -> decltype(buf.out()) { +template +FMT_INLINE auto get_iterator(Buf& buf, OutputIt) -> decltype(buf.out()) { return buf.out(); } -template auto get_iterator(buffer& buf) -> buffer_appender { - return buffer_appender(buf); +template +auto get_iterator(buffer&, OutputIt out) -> OutputIt { + return out; } -template -struct fallback_formatter { - fallback_formatter() = delete; -}; - -// Specifies if T has an enabled fallback_formatter specialization. -template -using has_fallback_formatter = -#ifdef FMT_DEPRECATED_OSTREAM - std::is_constructible>; -#else - std::false_type; -#endif - struct view {}; template struct named_arg : view { @@ -1104,64 +1206,8 @@ constexpr auto count_statically_named_args() -> size_t { return count::value...>(); } -enum class type { - none_type, - // Integer types should go first, - int_type, - uint_type, - long_long_type, - ulong_long_type, - int128_type, - uint128_type, - bool_type, - char_type, - last_integer_type = char_type, - // followed by floating-point types. - float_type, - double_type, - long_double_type, - last_numeric_type = long_double_type, - cstring_type, - string_type, - pointer_type, - custom_type -}; - -// Maps core type T to the corresponding type enum constant. -template -struct type_constant : std::integral_constant {}; - -#define FMT_TYPE_CONSTANT(Type, constant) \ - template \ - struct type_constant \ - : std::integral_constant {} - -FMT_TYPE_CONSTANT(int, int_type); -FMT_TYPE_CONSTANT(unsigned, uint_type); -FMT_TYPE_CONSTANT(long long, long_long_type); -FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type); -FMT_TYPE_CONSTANT(int128_opt, int128_type); -FMT_TYPE_CONSTANT(uint128_opt, uint128_type); -FMT_TYPE_CONSTANT(bool, bool_type); -FMT_TYPE_CONSTANT(Char, char_type); -FMT_TYPE_CONSTANT(float, float_type); -FMT_TYPE_CONSTANT(double, double_type); -FMT_TYPE_CONSTANT(long double, long_double_type); -FMT_TYPE_CONSTANT(const Char*, cstring_type); -FMT_TYPE_CONSTANT(basic_string_view, string_type); -FMT_TYPE_CONSTANT(const void*, pointer_type); - -constexpr bool is_integral_type(type t) { - return t > type::none_type && t <= type::last_integer_type; -} - -constexpr bool is_arithmetic_type(type t) { - return t > type::none_type && t <= type::last_numeric_type; -} - struct unformattable {}; struct unformattable_char : unformattable {}; -struct unformattable_const : unformattable {}; struct unformattable_pointer : unformattable {}; template struct string_value { @@ -1235,14 +1281,10 @@ template class value { // have different extension points, e.g. `formatter` for `format` and // `printf_formatter` for `printf`. custom.format = format_custom_arg< - value_type, - conditional_t::value, - typename Context::template formatter_type, - fallback_formatter>>; + value_type, typename Context::template formatter_type>; } value(unformattable); value(unformattable_char); - value(unformattable_const); value(unformattable_pointer); private: @@ -1260,7 +1302,7 @@ template class value { }; template -FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg; +FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg; // To minimize the number of types we need to deal with, long is translated // either to int or to long long depending on its size. @@ -1268,6 +1310,20 @@ enum { long_short = sizeof(long) == sizeof(int) }; using long_type = conditional_t; using ulong_type = conditional_t; +template struct format_as_result { + template ::value || std::is_class::value)> + static auto map(U*) -> decltype(format_as(std::declval())); + static auto map(...) -> void; + + using type = decltype(map(static_cast(nullptr))); +}; +template using format_as_t = typename format_as_result::type; + +template +struct has_format_as + : bool_constant, void>::value> {}; + // Maps formatting arguments to core types. // arg_mapper reports errors by returning unformattable instead of using // static_assert because it's used in the is_formattable trait. @@ -1343,45 +1399,6 @@ template struct arg_mapper { FMT_CONSTEXPR FMT_INLINE auto map(const T&) -> unformattable_char { return {}; } - template >::value && - !is_string::value && !has_formatter::value && - !has_fallback_formatter::value)> - FMT_CONSTEXPR FMT_INLINE auto map(const T& val) - -> basic_string_view { - return basic_string_view(val); - } - template >::value && - !std::is_convertible>::value && - !is_string::value && !has_formatter::value && - !has_fallback_formatter::value)> - FMT_CONSTEXPR FMT_INLINE auto map(const T& val) - -> basic_string_view { - return std_string_view(val); - } - - using cstring_result = conditional_t::value, - const char*, unformattable_pointer>; - - FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const signed char* val) - -> cstring_result { - return map(reinterpret_cast(val)); - } - FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const unsigned char* val) - -> cstring_result { - return map(reinterpret_cast(val)); - } - FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(signed char* val) - -> cstring_result { - return map(reinterpret_cast(val)); - } - FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(unsigned char* val) - -> cstring_result { - return map(reinterpret_cast(val)); - } FMT_CONSTEXPR FMT_INLINE auto map(void* val) -> const void* { return val; } FMT_CONSTEXPR FMT_INLINE auto map(const void* val) -> const void* { @@ -1391,8 +1408,8 @@ template struct arg_mapper { return val; } - // We use SFINAE instead of a const T* parameter to avoid conflicting with - // the C array overload. + // Use SFINAE instead of a const T* parameter to avoid a conflict with the + // array overload. template < typename T, FMT_ENABLE_IF( @@ -1411,52 +1428,34 @@ template struct arg_mapper { return values; } - template ::value&& std::is_convertible::value && - !has_formatter::value && - !has_fallback_formatter::value)> - FMT_CONSTEXPR FMT_INLINE auto map(const T& val) - -> decltype(std::declval().map( - static_cast>(val))) { - return map(static_cast>(val)); - } - - template ::value&& std::is_integral::value)> - FMT_CONSTEXPR FMT_INLINE auto map(const T& val) - -> decltype(std::declval().map(U())) { + // Only map owning types because mapping views can be unsafe. + template , + FMT_ENABLE_IF(std::is_arithmetic::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> decltype(this->map(U())) { return map(format_as(val)); } template > struct formattable : bool_constant() || - !std::is_const>::value || - has_fallback_formatter::value> {}; + (has_formatter::value && + !std::is_const>::value)> {}; -#if (FMT_MSC_VER != 0 && FMT_MSC_VER < 1910) || FMT_ICC_VERSION != 0 || \ - FMT_NVCC != 0 - // Workaround a bug in MSVC and Intel (Issue 2746). - template FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& { - return val; - } -#else template ::value)> FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& { return val; } template ::value)> - FMT_CONSTEXPR FMT_INLINE auto do_map(T&&) -> unformattable_const { + FMT_CONSTEXPR FMT_INLINE auto do_map(T&&) -> unformattable { return {}; } -#endif template , - FMT_ENABLE_IF(!is_string::value && !is_char::value && - !std::is_array::value && - (has_formatter::value || - has_fallback_formatter::value))> + FMT_ENABLE_IF((std::is_class::value || std::is_enum::value || + std::is_union::value) && + !is_string::value && !is_char::value && + !is_named_arg::value && + !std::is_arithmetic>::value)> FMT_CONSTEXPR FMT_INLINE auto map(T&& val) -> decltype(this->do_map(std::forward(val))) { return do_map(std::forward(val)); @@ -1464,7 +1463,7 @@ template struct arg_mapper { template ::value)> FMT_CONSTEXPR FMT_INLINE auto map(const T& named_arg) - -> decltype(std::declval().map(named_arg.value)) { + -> decltype(this->map(named_arg.value)) { return map(named_arg.value); } @@ -1482,26 +1481,19 @@ enum { packed_arg_bits = 4 }; enum { max_packed_args = 62 / packed_arg_bits }; enum : unsigned long long { is_unpacked_bit = 1ULL << 63 }; enum : unsigned long long { has_named_args_bit = 1ULL << 62 }; - -FMT_END_DETAIL_NAMESPACE +} // namespace detail // An output iterator that appends to a buffer. // It is used to reduce symbol sizes for the common case. class appender : public std::back_insert_iterator> { using base = std::back_insert_iterator>; - template - friend auto get_buffer(appender out) -> detail::buffer& { - return detail::get_container(out); - } - public: using std::back_insert_iterator>::back_insert_iterator; appender(base it) noexcept : base(it) {} - using _Unchecked_type = appender; // Mark iterator as checked. + FMT_UNCHECKED_ITERATOR(appender); auto operator++() noexcept -> appender& { return *this; } - auto operator++(int) noexcept -> appender { return *this; } }; @@ -1513,7 +1505,7 @@ template class basic_format_arg { detail::type type_; template - friend FMT_CONSTEXPR auto detail::make_arg(const T& value) + friend FMT_CONSTEXPR auto detail::make_arg(T&& value) -> basic_format_arg; template @@ -1567,6 +1559,7 @@ template class basic_format_arg { ``vis(value)`` will be called with the value of type ``double``. \endrst */ +FMT_MODULE_EXPORT template FMT_CONSTEXPR FMT_INLINE auto visit_format_arg( Visitor&& vis, const basic_format_arg& arg) -> decltype(vis(0)) { @@ -1608,7 +1601,7 @@ FMT_CONSTEXPR FMT_INLINE auto visit_format_arg( return vis(monostate()); } -FMT_BEGIN_DETAIL_NAMESPACE +namespace detail { template auto copy_str(InputIt begin, InputIt end, appender out) -> appender { @@ -1616,11 +1609,15 @@ auto copy_str(InputIt begin, InputIt end, appender out) -> appender { return out; } +template +FMT_CONSTEXPR auto copy_str(R&& rng, OutputIt out) -> OutputIt { + return detail::copy_str(rng.begin(), rng.end(), out); +} + #if FMT_GCC_VERSION && FMT_GCC_VERSION < 500 // A workaround for gcc 4.8 to make void_t work in a SFINAE context. -template struct void_t_impl { using type = void; }; -template -using void_t = typename detail::void_t_impl::type; +template struct void_t_impl { using type = void; }; +template using void_t = typename void_t_impl::type; #else template using void_t = void; #endif @@ -1635,13 +1632,12 @@ struct is_output_iterator< decltype(*std::declval() = std::declval())>> : std::true_type {}; -template -struct is_back_insert_iterator : std::false_type {}; +template struct is_back_insert_iterator : std::false_type {}; template struct is_back_insert_iterator> : std::true_type {}; -template +template struct is_contiguous_back_insert_iterator : std::false_type {}; template struct is_contiguous_back_insert_iterator> @@ -1649,13 +1645,13 @@ struct is_contiguous_back_insert_iterator> template <> struct is_contiguous_back_insert_iterator : std::true_type {}; -// A type-erased reference to an std::locale to avoid heavy include. +// A type-erased reference to an std::locale to avoid a heavy include. class locale_ref { private: const void* locale_; // A type-erased pointer to std::locale. public: - constexpr locale_ref() : locale_(nullptr) {} + constexpr FMT_INLINE locale_ref() : locale_(nullptr) {} template explicit locale_ref(const Locale& loc); explicit operator bool() const noexcept { return locale_ != nullptr; } @@ -1674,40 +1670,23 @@ constexpr auto encode_types() -> unsigned long long { } template -FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg { - basic_format_arg arg; - arg.type_ = mapped_type_constant::value; - arg.value_ = arg_mapper().map(value); - return arg; -} - -// The type template parameter is there to avoid an ODR violation when using -// a fallback formatter in one translation unit and an implicit conversion in -// another (not recommended). -template -FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value { - const auto& arg = arg_mapper().map(std::forward(val)); +FMT_CONSTEXPR FMT_INLINE auto make_value(T&& val) -> value { + auto&& arg = arg_mapper().map(FMT_FORWARD(val)); + using arg_type = remove_cvref_t; constexpr bool formattable_char = - !std::is_same::value; + !std::is_same::value; static_assert(formattable_char, "Mixing character types is disallowed."); - constexpr bool formattable_const = - !std::is_same::value; - static_assert(formattable_const, "Cannot format a const argument."); - - // Formatting of arbitrary pointers is disallowed. If you want to output - // a pointer cast it to "void *" or "const void *". In particular, this - // forbids formatting of "[const] volatile char *" which is printed as bool - // by iostreams. + // Formatting of arbitrary pointers is disallowed. If you want to format a + // pointer cast it to `void*` or `const void*`. In particular, this forbids + // formatting of `[const] volatile char*` printed as bool by iostreams. constexpr bool formattable_pointer = - !std::is_same::value; + !std::is_same::value; static_assert(formattable_pointer, "Formatting of non-void pointers is disallowed."); - constexpr bool formattable = - !std::is_same::value; + constexpr bool formattable = !std::is_same::value; static_assert( formattable, "Cannot format an argument. To make type T formattable provide a " @@ -1715,19 +1694,33 @@ FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value { return {arg}; } +template +FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg { + auto arg = basic_format_arg(); + arg.type_ = mapped_type_constant::value; + arg.value_ = make_value(value); + return arg; +} + +// The DEPRECATED type template parameter is there to avoid an ODR violation +// when using a fallback formatter in one translation unit and an implicit +// conversion in another (not recommended). +template +FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value { + return make_value(val); +} + template -inline auto make_arg(const T& value) -> basic_format_arg { +FMT_CONSTEXPR inline auto make_arg(T&& value) -> basic_format_arg { return make_arg(value); } -FMT_END_DETAIL_NAMESPACE +} // namespace detail +FMT_BEGIN_EXPORT // Formatting context. template class basic_format_context { - public: - /** The character type for the output. */ - using char_type = Char; - private: OutputIt out_; basic_format_args args_; @@ -1736,31 +1729,32 @@ template class basic_format_context { public: using iterator = OutputIt; using format_arg = basic_format_arg; + using format_args = basic_format_args; using parse_context_type = basic_format_parse_context; - template using formatter_type = formatter; + template using formatter_type = formatter; + + /** The character type for the output. */ + using char_type = Char; basic_format_context(basic_format_context&&) = default; basic_format_context(const basic_format_context&) = delete; void operator=(const basic_format_context&) = delete; /** - Constructs a ``basic_format_context`` object. References to the arguments are - stored in the object so make sure they have appropriate lifetimes. + Constructs a ``basic_format_context`` object. References to the arguments + are stored in the object so make sure they have appropriate lifetimes. */ - constexpr basic_format_context( - OutputIt out, basic_format_args ctx_args, - detail::locale_ref loc = detail::locale_ref()) + constexpr basic_format_context(OutputIt out, format_args ctx_args, + detail::locale_ref loc = {}) : out_(out), args_(ctx_args), loc_(loc) {} constexpr auto arg(int id) const -> format_arg { return args_.get(id); } - FMT_CONSTEXPR auto arg(basic_string_view name) -> format_arg { + FMT_CONSTEXPR auto arg(basic_string_view name) -> format_arg { return args_.get(name); } - FMT_CONSTEXPR auto arg_id(basic_string_view name) -> int { + FMT_CONSTEXPR auto arg_id(basic_string_view name) -> int { return args_.get_id(name); } - auto args() const -> const basic_format_args& { - return args_; - } + auto args() const -> const format_args& { return args_; } FMT_CONSTEXPR auto error_handler() -> detail::error_handler { return {}; } void on_error(const char* message) { error_handler().on_error(message); } @@ -1781,16 +1775,10 @@ using buffer_context = basic_format_context, Char>; using format_context = buffer_context; -// Workaround an alias issue: https://stackoverflow.com/q/62767544/471164. -#define FMT_BUFFER_CONTEXT(Char) \ - basic_format_context, Char> - template -using is_formattable = bool_constant< - !std::is_base_of>().map( - std::declval()))>::value && - !detail::has_fallback_formatter::value>; +using is_formattable = bool_constant>() + .map(std::declval()))>::value>; /** \rst @@ -1837,7 +1825,7 @@ class format_arg_store data_{detail::make_arg< is_packed, Context, detail::mapped_type_constant, Context>::value>( - std::forward(args))...} { + FMT_FORWARD(args))...} { detail::init_named_args(data_.named_args(), 0, 0, args...); } }; @@ -1850,10 +1838,10 @@ class format_arg_store See `~fmt::arg` for lifetime considerations. \endrst */ -template -constexpr auto make_format_args(Args&&... args) - -> format_arg_store...> { - return {std::forward(args)...}; +template +constexpr auto make_format_args(T&&... args) + -> format_arg_store...> { + return {FMT_FORWARD(args)...}; } /** @@ -1872,6 +1860,7 @@ inline auto arg(const Char* name, const T& arg) -> detail::named_arg { static_assert(!detail::is_named_arg(), "nested named arguments"); return {name, arg}; } +FMT_END_EXPORT /** \rst @@ -1997,20 +1986,28 @@ template class basic_format_args { /** An alias to ``basic_format_args``. */ // A separate type would result in shorter symbols but break ABI compatibility // between clang and gcc on ARM (#1919). -using format_args = basic_format_args; +FMT_MODULE_EXPORT using format_args = basic_format_args; -// We cannot use enum classes as bit fields because of a gcc bug -// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414. +// We cannot use enum classes as bit fields because of a gcc bug, so we put them +// in namespaces instead (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414). +// Additionally, if an underlying type is specified, older gcc incorrectly warns +// that the type is too small. Both bugs are fixed in gcc 9.3. +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 903 +# define FMT_ENUM_UNDERLYING_TYPE(type) +#else +# define FMT_ENUM_UNDERLYING_TYPE(type) : type +#endif namespace align { -enum type { none, left, right, center, numeric }; +enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, left, right, center, + numeric}; } using align_t = align::type; namespace sign { -enum type { none, minus, plus, space }; +enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, minus, plus, space}; } using sign_t = sign::type; -FMT_BEGIN_DETAIL_NAMESPACE +namespace detail { // Workaround an array initialization issue in gcc 4.8. template struct fill_t { @@ -2022,7 +2019,7 @@ template struct fill_t { public: FMT_CONSTEXPR void operator=(basic_string_view s) { auto size = s.size(); - if (size > max_size) return throw_format_error("invalid fill"); + FMT_ASSERT(size <= max_size, "invalid fill"); for (size_t i = 0; i < size; ++i) data_[i] = s[i]; size_ = static_cast(size); } @@ -2035,11 +2032,10 @@ template struct fill_t { return data_[index]; } }; -FMT_END_DETAIL_NAMESPACE +} // namespace detail enum class presentation_type : unsigned char { none, - // Integer types should go first, dec, // 'd' oct, // 'o' hex_lower, // 'x' @@ -2061,7 +2057,7 @@ enum class presentation_type : unsigned char { }; // Format specifiers for built-in and string types. -template struct basic_format_specs { +template struct format_specs { int width; int precision; presentation_type type; @@ -2071,7 +2067,7 @@ template struct basic_format_specs { bool localized : 1; detail::fill_t fill; - constexpr basic_format_specs() + constexpr format_specs() : width(0), precision(-1), type(presentation_type::none), @@ -2081,9 +2077,7 @@ template struct basic_format_specs { localized(false) {} }; -using format_specs = basic_format_specs; - -FMT_BEGIN_DETAIL_NAMESPACE +namespace detail { enum class arg_id_kind { none, index, name }; @@ -2104,7 +2098,7 @@ template struct arg_ref { arg_id_kind kind; union value { - FMT_CONSTEXPR value(int id = 0) : index{id} {} + FMT_CONSTEXPR value(int idx = 0) : index(idx) {} FMT_CONSTEXPR value(basic_string_view n) : name(n) {} int index; @@ -2113,128 +2107,30 @@ template struct arg_ref { }; // Format specifiers with width and precision resolved at formatting rather -// than parsing time to allow re-using the same parsed specifiers with +// than parsing time to allow reusing the same parsed specifiers with // different sets of arguments (precompilation of format strings). -template -struct dynamic_format_specs : basic_format_specs { +template +struct dynamic_format_specs : format_specs { arg_ref width_ref; arg_ref precision_ref; }; -struct auto_id {}; - -// A format specifier handler that sets fields in basic_format_specs. -template class specs_setter { - protected: - basic_format_specs& specs_; - - public: - explicit FMT_CONSTEXPR specs_setter(basic_format_specs& specs) - : specs_(specs) {} - - FMT_CONSTEXPR specs_setter(const specs_setter& other) - : specs_(other.specs_) {} - - FMT_CONSTEXPR void on_align(align_t align) { specs_.align = align; } - FMT_CONSTEXPR void on_fill(basic_string_view fill) { - specs_.fill = fill; - } - FMT_CONSTEXPR void on_sign(sign_t s) { specs_.sign = s; } - FMT_CONSTEXPR void on_hash() { specs_.alt = true; } - FMT_CONSTEXPR void on_localized() { specs_.localized = true; } - - FMT_CONSTEXPR void on_zero() { - if (specs_.align == align::none) specs_.align = align::numeric; - specs_.fill[0] = Char('0'); - } - - FMT_CONSTEXPR void on_width(int width) { specs_.width = width; } - FMT_CONSTEXPR void on_precision(int precision) { - specs_.precision = precision; - } - FMT_CONSTEXPR void end_precision() {} - - FMT_CONSTEXPR void on_type(presentation_type type) { specs_.type = type; } -}; - -// Format spec handler that saves references to arguments representing dynamic -// width and precision to be resolved at formatting time. -template -class dynamic_specs_handler - : public specs_setter { - public: - using char_type = typename ParseContext::char_type; - - FMT_CONSTEXPR dynamic_specs_handler(dynamic_format_specs& specs, - ParseContext& ctx) - : specs_setter(specs), specs_(specs), context_(ctx) {} - - FMT_CONSTEXPR dynamic_specs_handler(const dynamic_specs_handler& other) - : specs_setter(other), - specs_(other.specs_), - context_(other.context_) {} - - template FMT_CONSTEXPR void on_dynamic_width(Id arg_id) { - specs_.width_ref = make_arg_ref(arg_id); - } - - template FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) { - specs_.precision_ref = make_arg_ref(arg_id); - } - - FMT_CONSTEXPR void on_error(const char* message) { - context_.on_error(message); - } - - private: - dynamic_format_specs& specs_; - ParseContext& context_; - - using arg_ref_type = arg_ref; - - FMT_CONSTEXPR auto make_arg_ref(int arg_id) -> arg_ref_type { - context_.check_arg_id(arg_id); - return arg_ref_type(arg_id); - } - - FMT_CONSTEXPR auto make_arg_ref(auto_id) -> arg_ref_type { - return arg_ref_type(context_.next_arg_id()); - } - - FMT_CONSTEXPR auto make_arg_ref(basic_string_view arg_id) - -> arg_ref_type { - context_.check_arg_id(arg_id); - basic_string_view format_str( - context_.begin(), to_unsigned(context_.end() - context_.begin())); - return arg_ref_type(arg_id); - } -}; - -template constexpr bool is_ascii_letter(Char c) { - return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); -} - -// Converts a character to ASCII. Returns a number > 127 on conversion failure. +// Converts a character to ASCII. Returns '\0' on conversion failure. template ::value)> -constexpr auto to_ascii(Char c) -> Char { - return c; +constexpr auto to_ascii(Char c) -> char { + return c <= 0xff ? static_cast(c) : '\0'; } template ::value)> -constexpr auto to_ascii(Char c) -> underlying_t { - return c; +constexpr auto to_ascii(Char c) -> char { + return c <= 0xff ? static_cast(c) : '\0'; } +// Returns the number of code units in a code point or 1 on error. template FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int { if (const_check(sizeof(Char) != 1)) return 1; - auto lengths = - "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4"; - int len = lengths[static_cast(*begin) >> 3]; - - // Compute the pointer to the next character early so that the next - // iteration can start working on the next character. Neither Clang - // nor GCC figure out this reordering on their own. - return len + !len; + auto c = static_cast(*begin); + return static_cast((0x3a55000000000000ull >> (2 * (c >> 3))) & 0x3) + 1; } // Return the result via the out param to workaround gcc bug 77539. @@ -2279,279 +2175,284 @@ FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end, : error_value; } -// Parses fill and alignment. -template -FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end, - Handler&& handler) -> const Char* { - FMT_ASSERT(begin != end, ""); - auto align = align::none; - auto p = begin + code_point_length(begin); - if (end - p <= 0) p = begin; - for (;;) { - switch (to_ascii(*p)) { - case '<': - align = align::left; - break; - case '>': - align = align::right; - break; - case '^': - align = align::center; - break; - default: - break; - } - if (align != align::none) { - if (p != begin) { - auto c = *begin; - if (c == '{') - return handler.on_error("invalid fill character '{'"), begin; - handler.on_fill(basic_string_view(begin, to_unsigned(p - begin))); - begin = p + 1; - } else - ++begin; - handler.on_align(align); - break; - } else if (p == begin) { - break; - } - p = begin; +FMT_CONSTEXPR inline auto parse_align(char c) -> align_t { + switch (c) { + case '<': + return align::left; + case '>': + return align::right; + case '^': + return align::center; } - return begin; + return align::none; } -template FMT_CONSTEXPR bool is_name_start(Char c) { - return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c; +template constexpr auto is_name_start(Char c) -> bool { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_'; } -template +template FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end, - IDHandler&& handler) -> const Char* { - FMT_ASSERT(begin != end, ""); + Handler&& handler) -> const Char* { Char c = *begin; if (c >= '0' && c <= '9') { int index = 0; + constexpr int max = (std::numeric_limits::max)(); if (c != '0') - index = - parse_nonnegative_int(begin, end, (std::numeric_limits::max)()); + index = parse_nonnegative_int(begin, end, max); else ++begin; if (begin == end || (*begin != '}' && *begin != ':')) - handler.on_error("invalid format string"); + throw_format_error("invalid format string"); else - handler(index); + handler.on_index(index); return begin; } if (!is_name_start(c)) { - handler.on_error("invalid format string"); + throw_format_error("invalid format string"); return begin; } auto it = begin; do { ++it; - } while (it != end && (is_name_start(c = *it) || ('0' <= c && c <= '9'))); - handler(basic_string_view(begin, to_unsigned(it - begin))); + } while (it != end && (is_name_start(*it) || ('0' <= *it && *it <= '9'))); + handler.on_name({begin, to_unsigned(it - begin)}); return it; } -template +template FMT_CONSTEXPR FMT_INLINE auto parse_arg_id(const Char* begin, const Char* end, - IDHandler&& handler) -> const Char* { + Handler&& handler) -> const Char* { + FMT_ASSERT(begin != end, ""); Char c = *begin; if (c != '}' && c != ':') return do_parse_arg_id(begin, end, handler); - handler(); + handler.on_auto(); return begin; } -template -FMT_CONSTEXPR auto parse_width(const Char* begin, const Char* end, - Handler&& handler) -> const Char* { - using detail::auto_id; - struct width_adapter { - Handler& handler; +template struct dynamic_spec_id_handler { + basic_format_parse_context& ctx; + arg_ref& ref; - FMT_CONSTEXPR void operator()() { handler.on_dynamic_width(auto_id()); } - FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_width(id); } - FMT_CONSTEXPR void operator()(basic_string_view id) { - handler.on_dynamic_width(id); - } - FMT_CONSTEXPR void on_error(const char* message) { - if (message) handler.on_error(message); - } - }; + FMT_CONSTEXPR void on_auto() { + int id = ctx.next_arg_id(); + ref = arg_ref(id); + ctx.check_dynamic_spec(id); + } + FMT_CONSTEXPR void on_index(int id) { + ref = arg_ref(id); + ctx.check_arg_id(id); + ctx.check_dynamic_spec(id); + } + FMT_CONSTEXPR void on_name(basic_string_view id) { + ref = arg_ref(id); + ctx.check_arg_id(id); + } +}; +// Parses [integer | "{" [arg_id] "}"]. +template +FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end, + int& value, arg_ref& ref, + basic_format_parse_context& ctx) + -> const Char* { FMT_ASSERT(begin != end, ""); if ('0' <= *begin && *begin <= '9') { - int width = parse_nonnegative_int(begin, end, -1); - if (width != -1) - handler.on_width(width); + int val = parse_nonnegative_int(begin, end, -1); + if (val != -1) + value = val; else - handler.on_error("number is too big"); + throw_format_error("number is too big"); } else if (*begin == '{') { ++begin; - if (begin != end) begin = parse_arg_id(begin, end, width_adapter{handler}); - if (begin == end || *begin != '}') - return handler.on_error("invalid format string"), begin; - ++begin; + auto handler = dynamic_spec_id_handler{ctx, ref}; + if (begin != end) begin = parse_arg_id(begin, end, handler); + if (begin != end && *begin == '}') return ++begin; + throw_format_error("invalid format string"); } return begin; } -template -FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end, - Handler&& handler) -> const Char* { - using detail::auto_id; - struct precision_adapter { - Handler& handler; - - FMT_CONSTEXPR void operator()() { handler.on_dynamic_precision(auto_id()); } - FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_precision(id); } - FMT_CONSTEXPR void operator()(basic_string_view id) { - handler.on_dynamic_precision(id); - } - FMT_CONSTEXPR void on_error(const char* message) { - if (message) handler.on_error(message); - } - }; - - ++begin; - auto c = begin != end ? *begin : Char(); - if ('0' <= c && c <= '9') { - auto precision = parse_nonnegative_int(begin, end, -1); - if (precision != -1) - handler.on_precision(precision); - else - handler.on_error("number is too big"); - } else if (c == '{') { - ++begin; - if (begin != end) - begin = parse_arg_id(begin, end, precision_adapter{handler}); - if (begin == end || *begin++ != '}') - return handler.on_error("invalid format string"), begin; - } else { - return handler.on_error("missing precision specifier"), begin; - } - handler.end_precision(); - return begin; -} - template -FMT_CONSTEXPR auto parse_presentation_type(Char type) -> presentation_type { - switch (to_ascii(type)) { - case 'd': - return presentation_type::dec; - case 'o': - return presentation_type::oct; - case 'x': - return presentation_type::hex_lower; - case 'X': - return presentation_type::hex_upper; - case 'b': - return presentation_type::bin_lower; - case 'B': - return presentation_type::bin_upper; - case 'a': - return presentation_type::hexfloat_lower; - case 'A': - return presentation_type::hexfloat_upper; - case 'e': - return presentation_type::exp_lower; - case 'E': - return presentation_type::exp_upper; - case 'f': - return presentation_type::fixed_lower; - case 'F': - return presentation_type::fixed_upper; - case 'g': - return presentation_type::general_lower; - case 'G': - return presentation_type::general_upper; - case 'c': - return presentation_type::chr; - case 's': - return presentation_type::string; - case 'p': - return presentation_type::pointer; - case '?': - return presentation_type::debug; - default: - return presentation_type::none; - } -} - -// Parses standard format specifiers and sends notifications about parsed -// components to handler. -template -FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(const Char* begin, - const Char* end, - SpecHandler&& handler) +FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end, + int& value, arg_ref& ref, + basic_format_parse_context& ctx) -> const Char* { - if (1 < end - begin && begin[1] == '}' && is_ascii_letter(*begin) && - *begin != 'L') { - presentation_type type = parse_presentation_type(*begin++); - if (type == presentation_type::none) - handler.on_error("invalid type specifier"); - handler.on_type(type); + ++begin; + if (begin == end || *begin == '}') { + throw_format_error("invalid precision"); return begin; } + return parse_dynamic_spec(begin, end, value, ref, ctx); +} - if (begin == end) return begin; +enum class state { start, align, sign, hash, zero, width, precision, locale }; - begin = parse_align(begin, end, handler); - if (begin == end) return begin; - - // Parse sign. - switch (to_ascii(*begin)) { - case '+': - handler.on_sign(sign::plus); - ++begin; - break; - case '-': - handler.on_sign(sign::minus); - ++begin; - break; - case ' ': - handler.on_sign(sign::space); - ++begin; - break; - default: - break; - } - if (begin == end) return begin; - - if (*begin == '#') { - handler.on_hash(); - if (++begin == end) return begin; - } - - // Parse zero flag. - if (*begin == '0') { - handler.on_zero(); - if (++begin == end) return begin; - } - - begin = parse_width(begin, end, handler); - if (begin == end) return begin; - - // Parse precision. - if (*begin == '.') { - begin = parse_precision(begin, end, handler); +// Parses standard format specifiers. +template +FMT_CONSTEXPR FMT_INLINE auto parse_format_specs( + const Char* begin, const Char* end, dynamic_format_specs& specs, + basic_format_parse_context& ctx, type arg_type) -> const Char* { + auto c = '\0'; + if (end - begin > 1) { + auto next = to_ascii(begin[1]); + c = parse_align(next) == align::none ? to_ascii(*begin) : '\0'; + } else { if (begin == end) return begin; + c = to_ascii(*begin); } - if (*begin == 'L') { - handler.on_localized(); - ++begin; - } + struct { + state current_state = state::start; + FMT_CONSTEXPR void operator()(state s, bool valid = true) { + if (current_state >= s || !valid) + throw_format_error("invalid format specifier"); + current_state = s; + } + } enter_state; - // Parse type. - if (begin != end && *begin != '}') { - presentation_type type = parse_presentation_type(*begin++); - if (type == presentation_type::none) - handler.on_error("invalid type specifier"); - handler.on_type(type); + using pres = presentation_type; + constexpr auto integral_set = sint_set | uint_set | bool_set | char_set; + struct { + const Char*& begin; + dynamic_format_specs& specs; + type arg_type; + + FMT_CONSTEXPR auto operator()(pres type, int set) -> const Char* { + if (!in(arg_type, set)) throw_format_error("invalid format specifier"); + specs.type = type; + return begin + 1; + } + } parse_presentation_type{begin, specs, arg_type}; + + for (;;) { + switch (c) { + case '<': + case '>': + case '^': + enter_state(state::align); + specs.align = parse_align(c); + ++begin; + break; + case '+': + case '-': + case ' ': + enter_state(state::sign, in(arg_type, sint_set | float_set)); + switch (c) { + case '+': + specs.sign = sign::plus; + break; + case '-': + specs.sign = sign::minus; + break; + case ' ': + specs.sign = sign::space; + break; + } + ++begin; + break; + case '#': + enter_state(state::hash, is_arithmetic_type(arg_type)); + specs.alt = true; + ++begin; + break; + case '0': + enter_state(state::zero); + if (!is_arithmetic_type(arg_type)) + throw_format_error("format specifier requires numeric argument"); + if (specs.align == align::none) { + // Ignore 0 if align is specified for compatibility with std::format. + specs.align = align::numeric; + specs.fill[0] = Char('0'); + } + ++begin; + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '{': + enter_state(state::width); + begin = parse_dynamic_spec(begin, end, specs.width, specs.width_ref, ctx); + break; + case '.': + enter_state(state::precision, + in(arg_type, float_set | string_set | cstring_set)); + begin = parse_precision(begin, end, specs.precision, specs.precision_ref, + ctx); + break; + case 'L': + enter_state(state::locale, is_arithmetic_type(arg_type)); + specs.localized = true; + ++begin; + break; + case 'd': + return parse_presentation_type(pres::dec, integral_set); + case 'o': + return parse_presentation_type(pres::oct, integral_set); + case 'x': + return parse_presentation_type(pres::hex_lower, integral_set); + case 'X': + return parse_presentation_type(pres::hex_upper, integral_set); + case 'b': + return parse_presentation_type(pres::bin_lower, integral_set); + case 'B': + return parse_presentation_type(pres::bin_upper, integral_set); + case 'a': + return parse_presentation_type(pres::hexfloat_lower, float_set); + case 'A': + return parse_presentation_type(pres::hexfloat_upper, float_set); + case 'e': + return parse_presentation_type(pres::exp_lower, float_set); + case 'E': + return parse_presentation_type(pres::exp_upper, float_set); + case 'f': + return parse_presentation_type(pres::fixed_lower, float_set); + case 'F': + return parse_presentation_type(pres::fixed_upper, float_set); + case 'g': + return parse_presentation_type(pres::general_lower, float_set); + case 'G': + return parse_presentation_type(pres::general_upper, float_set); + case 'c': + return parse_presentation_type(pres::chr, integral_set); + case 's': + return parse_presentation_type(pres::string, + bool_set | string_set | cstring_set); + case 'p': + return parse_presentation_type(pres::pointer, pointer_set | cstring_set); + case '?': + return parse_presentation_type(pres::debug, + char_set | string_set | cstring_set); + case '}': + return begin; + default: { + if (*begin == '}') return begin; + // Parse fill and alignment. + auto fill_end = begin + code_point_length(begin); + if (end - fill_end <= 0) { + throw_format_error("invalid format specifier"); + return begin; + } + if (*begin == '{') { + throw_format_error("invalid fill character '{'"); + return begin; + } + auto align = parse_align(to_ascii(*fill_end)); + enter_state(state::align, align != align::none); + specs.fill = {begin, to_unsigned(fill_end - begin)}; + specs.align = align; + begin = fill_end + 1; + } + } + if (begin == end) return begin; + c = to_ascii(*begin); } - return begin; } template @@ -2561,14 +2462,11 @@ FMT_CONSTEXPR auto parse_replacement_field(const Char* begin, const Char* end, Handler& handler; int arg_id; - FMT_CONSTEXPR void operator()() { arg_id = handler.on_arg_id(); } - FMT_CONSTEXPR void operator()(int id) { arg_id = handler.on_arg_id(id); } - FMT_CONSTEXPR void operator()(basic_string_view id) { + FMT_CONSTEXPR void on_auto() { arg_id = handler.on_arg_id(); } + FMT_CONSTEXPR void on_index(int id) { arg_id = handler.on_arg_id(id); } + FMT_CONSTEXPR void on_name(basic_string_view id) { arg_id = handler.on_arg_id(id); } - FMT_CONSTEXPR void on_error(const char* message) { - if (message) handler.on_error(message); - } }; ++begin; @@ -2597,9 +2495,6 @@ FMT_CONSTEXPR auto parse_replacement_field(const Char* begin, const Char* end, template FMT_CONSTEXPR FMT_INLINE void parse_format_string( basic_string_view format_str, Handler&& handler) { - // Workaround a name-lookup bug in MSVC's modules implementation. - using detail::find; - auto begin = format_str.data(); auto end = begin + format_str.size(); if (end - begin < 32) { @@ -2621,21 +2516,21 @@ FMT_CONSTEXPR FMT_INLINE void parse_format_string( return; } struct writer { - FMT_CONSTEXPR void operator()(const Char* pbegin, const Char* pend) { - if (pbegin == pend) return; + FMT_CONSTEXPR void operator()(const Char* from, const Char* to) { + if (from == to) return; for (;;) { const Char* p = nullptr; - if (!find(pbegin, pend, Char('}'), p)) - return handler_.on_text(pbegin, pend); + if (!find(from, to, Char('}'), p)) + return handler_.on_text(from, to); ++p; - if (p == pend || *p != '}') + if (p == to || *p != '}') return handler_.on_error("unmatched '}' in format string"); - handler_.on_text(pbegin, p); - pbegin = p + 1; + handler_.on_text(from, p); + from = p + 1; } } Handler& handler_; - } write{handler}; + } write = {handler}; while (begin != end) { // Doing two passes with memchr (one for '{' and another for '}') is up to // 2.5x faster than the naive one-pass implementation on big format strings. @@ -2647,6 +2542,13 @@ FMT_CONSTEXPR FMT_INLINE void parse_format_string( } } +template ::value> struct strip_named_arg { + using type = T; +}; +template struct strip_named_arg { + using type = remove_cvref_t; +}; + template FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx) -> decltype(ctx.begin()) { @@ -2654,208 +2556,30 @@ FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx) using context = buffer_context; using mapped_type = conditional_t< mapped_type_constant::value != type::custom_type, - decltype(arg_mapper().map(std::declval())), T>; - auto f = conditional_t::value, - formatter, - fallback_formatter>(); - return f.parse(ctx); + decltype(arg_mapper().map(std::declval())), + typename strip_named_arg::type>; + return formatter().parse(ctx); } -// A parse context with extra argument id checks. It is only used at compile -// time because adding checks at runtime would introduce substantial overhead -// and would be redundant since argument ids are checked when arguments are -// retrieved anyway. -template -class compile_parse_context - : public basic_format_parse_context { - private: - int num_args_; - using base = basic_format_parse_context; - - public: - explicit FMT_CONSTEXPR compile_parse_context( - basic_string_view format_str, - int num_args = (std::numeric_limits::max)(), ErrorHandler eh = {}) - : base(format_str, eh), num_args_(num_args) {} - - FMT_CONSTEXPR auto next_arg_id() -> int { - int id = base::next_arg_id(); - if (id >= num_args_) this->on_error("argument not found"); - return id; - } - - FMT_CONSTEXPR void check_arg_id(int id) { - base::check_arg_id(id); - if (id >= num_args_) this->on_error("argument not found"); - } - using base::check_arg_id; -}; - -template -FMT_CONSTEXPR void check_int_type_spec(presentation_type type, - ErrorHandler&& eh) { - if (type > presentation_type::bin_upper && type != presentation_type::chr) - eh.on_error("invalid type specifier"); -} - -// Checks char specs and returns true if the type spec is char (and not int). -template -FMT_CONSTEXPR auto check_char_specs(const basic_format_specs& specs, - ErrorHandler&& eh = {}) -> bool { +// Checks char specs and returns true iff the presentation type is char-like. +template +FMT_CONSTEXPR auto check_char_specs(const format_specs& specs) -> bool { if (specs.type != presentation_type::none && specs.type != presentation_type::chr && specs.type != presentation_type::debug) { - check_int_type_spec(specs.type, eh); return false; } if (specs.align == align::numeric || specs.sign != sign::none || specs.alt) - eh.on_error("invalid format specifier for char"); + throw_format_error("invalid format specifier for char"); return true; } -// A floating-point presentation format. -enum class float_format : unsigned char { - general, // General: exponent notation or fixed point based on magnitude. - exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3. - fixed, // Fixed point with the default precision of 6, e.g. 0.0012. - hex -}; +constexpr FMT_INLINE_VARIABLE int invalid_arg_index = -1; -struct float_specs { - int precision; - float_format format : 8; - sign_t sign : 8; - bool upper : 1; - bool locale : 1; - bool binary32 : 1; - bool showpoint : 1; -}; - -template -FMT_CONSTEXPR auto parse_float_type_spec(const basic_format_specs& specs, - ErrorHandler&& eh = {}) - -> float_specs { - auto result = float_specs(); - result.showpoint = specs.alt; - result.locale = specs.localized; - switch (specs.type) { - case presentation_type::none: - result.format = float_format::general; - break; - case presentation_type::general_upper: - result.upper = true; - FMT_FALLTHROUGH; - case presentation_type::general_lower: - result.format = float_format::general; - break; - case presentation_type::exp_upper: - result.upper = true; - FMT_FALLTHROUGH; - case presentation_type::exp_lower: - result.format = float_format::exp; - result.showpoint |= specs.precision != 0; - break; - case presentation_type::fixed_upper: - result.upper = true; - FMT_FALLTHROUGH; - case presentation_type::fixed_lower: - result.format = float_format::fixed; - result.showpoint |= specs.precision != 0; - break; - case presentation_type::hexfloat_upper: - result.upper = true; - FMT_FALLTHROUGH; - case presentation_type::hexfloat_lower: - result.format = float_format::hex; - break; - default: - eh.on_error("invalid type specifier"); - break; - } - return result; -} - -template -FMT_CONSTEXPR auto check_cstring_type_spec(presentation_type type, - ErrorHandler&& eh = {}) -> bool { - if (type == presentation_type::none || type == presentation_type::string) - return true; - if (type != presentation_type::pointer) eh.on_error("invalid type specifier"); - return false; -} - -template -FMT_CONSTEXPR void check_string_type_spec(presentation_type type, - ErrorHandler&& eh = {}) { - if (type != presentation_type::none && type != presentation_type::string && - type != presentation_type::debug) - eh.on_error("invalid type specifier"); -} - -template -FMT_CONSTEXPR void check_pointer_type_spec(presentation_type type, - ErrorHandler&& eh) { - if (type != presentation_type::none && type != presentation_type::pointer) - eh.on_error("invalid type specifier"); -} - -// A parse_format_specs handler that checks if specifiers are consistent with -// the argument type. -template class specs_checker : public Handler { - private: - detail::type arg_type_; - - FMT_CONSTEXPR void require_numeric_argument() { - if (!is_arithmetic_type(arg_type_)) - this->on_error("format specifier requires numeric argument"); - } - - public: - FMT_CONSTEXPR specs_checker(const Handler& handler, detail::type arg_type) - : Handler(handler), arg_type_(arg_type) {} - - FMT_CONSTEXPR void on_align(align_t align) { - if (align == align::numeric) require_numeric_argument(); - Handler::on_align(align); - } - - FMT_CONSTEXPR void on_sign(sign_t s) { - require_numeric_argument(); - if (is_integral_type(arg_type_) && arg_type_ != type::int_type && - arg_type_ != type::long_long_type && arg_type_ != type::int128_type && - arg_type_ != type::char_type) { - this->on_error("format specifier requires signed argument"); - } - Handler::on_sign(s); - } - - FMT_CONSTEXPR void on_hash() { - require_numeric_argument(); - Handler::on_hash(); - } - - FMT_CONSTEXPR void on_localized() { - require_numeric_argument(); - Handler::on_localized(); - } - - FMT_CONSTEXPR void on_zero() { - require_numeric_argument(); - Handler::on_zero(); - } - - FMT_CONSTEXPR void end_precision() { - if (is_integral_type(arg_type_) || arg_type_ == type::pointer_type) - this->on_error("precision not allowed for this argument type"); - } -}; - -constexpr int invalid_arg_index = -1; - -#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +#if FMT_USE_NONTYPE_TEMPLATE_ARGS template constexpr auto get_arg_index_by_name(basic_string_view name) -> int { - if constexpr (detail::is_statically_named_arg()) { + if constexpr (is_statically_named_arg()) { if (name == T::name) return N; } if constexpr (sizeof...(Args) > 0) @@ -2867,7 +2591,7 @@ constexpr auto get_arg_index_by_name(basic_string_view name) -> int { template FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view name) -> int { -#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +#if FMT_USE_NONTYPE_TEMPLATE_ARGS if constexpr (sizeof...(Args) > 0) return get_arg_index_by_name<0, Args...>(name); #endif @@ -2875,23 +2599,26 @@ FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view name) -> int { return invalid_arg_index; } -template -class format_string_checker { +template class format_string_checker { private: - using parse_context_type = compile_parse_context; - enum { num_args = sizeof...(Args) }; + using parse_context_type = compile_parse_context; + static constexpr int num_args = sizeof...(Args); // Format specifier parsing function. + // In the future basic_format_parse_context will replace compile_parse_context + // here and will use is_constant_evaluated and downcasting to access the data + // needed for compile-time checks: https://godbolt.org/z/GvWzcTjh1. using parse_func = const Char* (*)(parse_context_type&); parse_context_type context_; - parse_func parse_funcs_[num_args > 0 ? num_args : 1]; + parse_func parse_funcs_[num_args > 0 ? static_cast(num_args) : 1]; + type types_[num_args > 0 ? static_cast(num_args) : 1]; public: - explicit FMT_CONSTEXPR format_string_checker( - basic_string_view format_str, ErrorHandler eh) - : context_(format_str, num_args, eh), - parse_funcs_{&parse_format_specs...} {} + explicit FMT_CONSTEXPR format_string_checker(basic_string_view fmt) + : context_(fmt, num_args, types_), + parse_funcs_{&parse_format_specs...}, + types_{mapped_type_constant>::value...} {} FMT_CONSTEXPR void on_text(const Char*, const Char*) {} @@ -2900,10 +2627,10 @@ class format_string_checker { return context_.check_arg_id(id), id; } FMT_CONSTEXPR auto on_arg_id(basic_string_view id) -> int { -#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +#if FMT_USE_NONTYPE_TEMPLATE_ARGS auto index = get_arg_index_by_name(id); if (index == invalid_arg_index) on_error("named argument is not found"); - return context_.check_arg_id(index), index; + return index; #else (void)id; on_error("compile-time checks for named arguments require C++20 support"); @@ -2915,41 +2642,55 @@ class format_string_checker { FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char*) -> const Char* { - context_.advance_to(context_.begin() + (begin - &*context_.begin())); + context_.advance_to(begin); // id >= 0 check is a workaround for gcc 10 bug (#2065). return id >= 0 && id < num_args ? parse_funcs_[id](context_) : begin; } FMT_CONSTEXPR void on_error(const char* message) { - context_.on_error(message); + throw_format_error(message); } }; +// Reports a compile-time error if S is not a valid format string. +template ::value)> +FMT_INLINE void check_format_string(const S&) { +#ifdef FMT_ENFORCE_COMPILE_STRING + static_assert(is_compile_string::value, + "FMT_ENFORCE_COMPILE_STRING requires all format strings to use " + "FMT_STRING."); +#endif +} template ::value), int>> + FMT_ENABLE_IF(is_compile_string::value)> void check_format_string(S format_str) { - FMT_CONSTEXPR auto s = to_string_view(format_str); - using checker = format_string_checker...>; - FMT_CONSTEXPR bool invalid_format = - (parse_format_string(s, checker(s, {})), true); - ignore_unused(invalid_format); + using char_t = typename S::char_type; + FMT_CONSTEXPR auto s = basic_string_view(format_str); + using checker = format_string_checker...>; + FMT_CONSTEXPR bool error = (parse_format_string(s, checker(s)), true); + ignore_unused(error); } +template struct vformat_args { + using type = basic_format_args< + basic_format_context>, Char>>; +}; +template <> struct vformat_args { using type = format_args; }; + +// Use vformat_args and avoid type_identity to keep symbols short. template -void vformat_to( - buffer& buf, basic_string_view fmt, - basic_format_args)> args, - locale_ref loc = {}); +void vformat_to(buffer& buf, basic_string_view fmt, + typename vformat_args::type args, locale_ref loc = {}); FMT_API void vprint_mojibake(std::FILE*, string_view, format_args); #ifndef _WIN32 inline void vprint_mojibake(std::FILE*, string_view, format_args) {} #endif -FMT_END_DETAIL_NAMESPACE +} // namespace detail -// A formatter specialization for the core types corresponding to detail::type -// constants. +FMT_BEGIN_EXPORT + +// A formatter specialization for natively supported types. template struct formatter::value != @@ -2958,72 +2699,21 @@ struct formatter specs_; public: - // Parses format specifiers stopping either at the end of the range or at the - // terminating '}'. template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - auto begin = ctx.begin(), end = ctx.end(); - if (begin == end) return begin; - using handler_type = detail::dynamic_specs_handler; + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* { auto type = detail::type_constant::value; - auto checker = - detail::specs_checker(handler_type(specs_, ctx), type); - auto it = detail::parse_format_specs(begin, end, checker); - auto eh = ctx.error_handler(); - switch (type) { - case detail::type::none_type: - FMT_ASSERT(false, "invalid argument type"); - break; - case detail::type::bool_type: - if (specs_.type == presentation_type::none || - specs_.type == presentation_type::string) { - break; - } - FMT_FALLTHROUGH; - case detail::type::int_type: - case detail::type::uint_type: - case detail::type::long_long_type: - case detail::type::ulong_long_type: - case detail::type::int128_type: - case detail::type::uint128_type: - detail::check_int_type_spec(specs_.type, eh); - break; - case detail::type::char_type: - detail::check_char_specs(specs_, eh); - break; - case detail::type::float_type: - if (detail::const_check(FMT_USE_FLOAT)) - detail::parse_float_type_spec(specs_, eh); - else - FMT_ASSERT(false, "float support disabled"); - break; - case detail::type::double_type: - if (detail::const_check(FMT_USE_DOUBLE)) - detail::parse_float_type_spec(specs_, eh); - else - FMT_ASSERT(false, "double support disabled"); - break; - case detail::type::long_double_type: - if (detail::const_check(FMT_USE_LONG_DOUBLE)) - detail::parse_float_type_spec(specs_, eh); - else - FMT_ASSERT(false, "long double support disabled"); - break; - case detail::type::cstring_type: - detail::check_cstring_type_spec(specs_.type, eh); - break; - case detail::type::string_type: - detail::check_string_type_spec(specs_.type, eh); - break; - case detail::type::pointer_type: - detail::check_pointer_type_spec(specs_.type, eh); - break; - case detail::type::custom_type: - // Custom format specifiers are checked in parse functions of - // formatter specializations. - break; - } - return it; + auto end = + detail::parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, type); + if (type == detail::type::char_type) detail::check_char_specs(specs_); + return end; + } + + template ::value, + FMT_ENABLE_IF(U == detail::type::string_type || + U == detail::type::cstring_type || + U == detail::type::char_type)> + FMT_CONSTEXPR void set_debug_format(bool set = true) { + specs_.type = set ? presentation_type::debug : presentation_type::none; } template @@ -3035,7 +2725,7 @@ struct formatter \ struct formatter : formatter { \ template \ - auto format(Type const& val, FormatContext& ctx) const \ + auto format(const Type& val, FormatContext& ctx) const \ -> decltype(ctx.out()) { \ return formatter::format(static_cast(val), ctx); \ } \ @@ -3052,7 +2742,9 @@ FMT_FORMAT_AS(std::basic_string, basic_string_view); FMT_FORMAT_AS(std::nullptr_t, const void*); FMT_FORMAT_AS(detail::std_string_view, basic_string_view); -template struct basic_runtime { basic_string_view str; }; +template struct runtime_format_string { + basic_string_view str; +}; /** A compile-time format string. */ template class basic_format_string { @@ -3072,25 +2764,24 @@ template class basic_format_string { #ifdef FMT_HAS_CONSTEVAL if constexpr (detail::count_named_args() == detail::count_statically_named_args()) { - using checker = detail::format_string_checker...>; - detail::parse_format_string(str_, checker(s, {})); + using checker = + detail::format_string_checker...>; + detail::parse_format_string(str_, checker(s)); } #else detail::check_format_string(s); #endif } - basic_format_string(basic_runtime r) : str_(r.str) {} + basic_format_string(runtime_format_string fmt) : str_(fmt.str) {} FMT_INLINE operator basic_string_view() const { return str_; } + FMT_INLINE auto get() const -> basic_string_view { return str_; } }; #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 // Workaround broken conversion on older gcc. -template using format_string = string_view; -template auto runtime(const S& s) -> basic_string_view> { - return s; -} +template using format_string = string_view; +inline auto runtime(string_view s) -> string_view { return s; } #else template using format_string = basic_format_string...>; @@ -3104,9 +2795,7 @@ using format_string = basic_format_string...>; fmt::print(fmt::runtime("{:d}"), "I am not a number"); \endrst */ -template auto runtime(const S& s) -> basic_runtime> { - return {{s}}; -} +inline auto runtime(string_view s) -> runtime_format_string<> { return {{s}}; } #endif FMT_API auto vformat(string_view fmt, format_args args) -> std::string; @@ -3132,10 +2821,9 @@ FMT_NODISCARD FMT_INLINE auto format(format_string fmt, T&&... args) template ::value)> auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt { - using detail::get_buffer; - auto&& buf = get_buffer(out); + auto&& buf = detail::get_buffer(out); detail::vformat_to(buf, fmt, args, {}); - return detail::get_iterator(buf); + return detail::get_iterator(buf, out); } /** @@ -3194,7 +2882,7 @@ template FMT_NODISCARD FMT_INLINE auto formatted_size(format_string fmt, T&&... args) -> size_t { auto buf = detail::counting_buffer<>(); - detail::vformat_to(buf, string_view(fmt), fmt::make_format_args(args...), {}); + detail::vformat_to(buf, fmt, fmt::make_format_args(args...), {}); return buf.count(); } @@ -3235,7 +2923,25 @@ FMT_INLINE void print(std::FILE* f, format_string fmt, T&&... args) { : detail::vprint_mojibake(f, fmt, vargs); } -FMT_MODULE_EXPORT_END +/** + Formats ``args`` according to specifications in ``fmt`` and writes the + output to the file ``f`` followed by a newline. + */ +template +FMT_INLINE void println(std::FILE* f, format_string fmt, T&&... args) { + return fmt::print(f, "{}\n", fmt::format(fmt, std::forward(args)...)); +} + +/** + Formats ``args`` according to specifications in ``fmt`` and writes the output + to ``stdout`` followed by a newline. + */ +template +FMT_INLINE void println(format_string fmt, T&&... args) { + return fmt::println(stdout, fmt, std::forward(args)...); +} + +FMT_END_EXPORT FMT_GCC_PRAGMA("GCC pop_options") FMT_END_NAMESPACE diff --git a/vendor/Fmt/include/fmt/format-inl.h b/vendor/Fmt/include/fmt/format-inl.h index e1010a90..5bae3c7b 100644 --- a/vendor/Fmt/include/fmt/format-inl.h +++ b/vendor/Fmt/include/fmt/format-inl.h @@ -9,13 +9,9 @@ #define FMT_FORMAT_INL_H_ #include -#include #include // errno #include #include -#include -#include // std::memmove -#include #include #ifndef FMT_STATIC_THOUSANDS_SEPARATOR @@ -44,19 +40,6 @@ FMT_FUNC void throw_format_error(const char* message) { FMT_THROW(format_error(message)); } -#ifndef _MSC_VER -# define FMT_SNPRINTF snprintf -#else // _MSC_VER -inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) { - va_list args; - va_start(args, format); - int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args); - va_end(args); - return result; -} -# define FMT_SNPRINTF fmt_snprintf -#endif // _MSC_VER - FMT_FUNC void format_error_code(detail::buffer& out, int error_code, string_view message) noexcept { // Report error code making sure that the output fits into @@ -93,7 +76,8 @@ FMT_FUNC void report_error(format_func func, int error_code, inline void fwrite_fully(const void* ptr, size_t size, size_t count, FILE* stream) { size_t written = std::fwrite(ptr, size, count, stream); - if (written < count) FMT_THROW(system_error(errno, "cannot write to file")); + if (written < count) + FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); } #ifndef FMT_STATIC_THOUSANDS_SEPARATOR @@ -127,657 +111,51 @@ template FMT_FUNC Char decimal_point_impl(locale_ref) { return '.'; } #endif + +FMT_FUNC auto write_loc(appender out, loc_value value, + const format_specs<>& specs, locale_ref loc) -> bool { +#ifndef FMT_STATIC_THOUSANDS_SEPARATOR + auto locale = loc.get(); + // We cannot use the num_put facet because it may produce output in + // a wrong encoding. + using facet = format_facet; + if (std::has_facet(locale)) + return std::use_facet(locale).put(out, value, specs); + return facet(locale).put(out, value, specs); +#endif + return false; +} } // namespace detail -#if !FMT_MSC_VER -FMT_API FMT_FUNC format_error::~format_error() noexcept = default; +template typename Locale::id format_facet::id; + +#ifndef FMT_STATIC_THOUSANDS_SEPARATOR +template format_facet::format_facet(Locale& loc) { + auto& numpunct = std::use_facet>(loc); + grouping_ = numpunct.grouping(); + if (!grouping_.empty()) separator_ = std::string(1, numpunct.thousands_sep()); +} + +template <> +FMT_API FMT_FUNC auto format_facet::do_put( + appender out, loc_value val, const format_specs<>& specs) const -> bool { + return val.visit( + detail::loc_writer<>{out, specs, separator_, grouping_, decimal_point_}); +} #endif -FMT_FUNC std::system_error vsystem_error(int error_code, string_view format_str, +FMT_FUNC std::system_error vsystem_error(int error_code, string_view fmt, format_args args) { auto ec = std::error_code(error_code, std::generic_category()); - return std::system_error(ec, vformat(format_str, args)); + return std::system_error(ec, vformat(fmt, args)); } namespace detail { -template struct basic_impl_data { - // Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340. - // These are generated by support/compute-powers.py. - static constexpr uint64_t pow10_significands[87] = { - 0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76, - 0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df, - 0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c, - 0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5, - 0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57, - 0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7, - 0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e, - 0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996, - 0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126, - 0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053, - 0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f, - 0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b, - 0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06, - 0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb, - 0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000, - 0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984, - 0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068, - 0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8, - 0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758, - 0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85, - 0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d, - 0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25, - 0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2, - 0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a, - 0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410, - 0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129, - 0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85, - 0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841, - 0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b, - }; - -#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wnarrowing" -#endif - // Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding - // to significands above. - static constexpr int16_t pow10_exponents[87] = { - -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954, - -927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661, - -635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369, - -343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77, - -50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216, - 242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508, - 534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800, - 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066}; -#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 -# pragma GCC diagnostic pop -#endif - - static constexpr uint64_t power_of_10_64[20] = { - 1, FMT_POWERS_OF_10(1ULL), FMT_POWERS_OF_10(1000000000ULL), - 10000000000000000000ULL}; -}; - -// This is a struct rather than an alias to avoid shadowing warnings in gcc. -struct impl_data : basic_impl_data<> {}; - -#if __cplusplus < 201703L -template -constexpr uint64_t basic_impl_data::pow10_significands[]; -template constexpr int16_t basic_impl_data::pow10_exponents[]; -template constexpr uint64_t basic_impl_data::power_of_10_64[]; -#endif - -template struct bits { - static FMT_CONSTEXPR_DECL const int value = - static_cast(sizeof(T) * std::numeric_limits::digits); -}; - -// A floating-point number f * pow(2, e). -template struct basic_fp { - F f; - int e; - - static constexpr const int num_significand_bits = bits::value; - - constexpr basic_fp() : f(0), e(0) {} - constexpr basic_fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {} - - // Constructs fp from an IEEE754 floating-point number. It is a template to - // prevent compile errors on systems where n is not IEEE754. - template explicit FMT_CONSTEXPR basic_fp(Float n) { - assign(n); - } - - template - using is_supported = bool_constant::is_iec559 && - std::numeric_limits::digits <= 113>; - - // Assigns d to this and return true iff predecessor is closer than successor. - template ::value)> - FMT_CONSTEXPR bool assign(Float n) { - // Assume float is in the format [sign][exponent][significand]. - using carrier_uint = typename dragonbox::float_info::carrier_uint; - const carrier_uint implicit_bit = carrier_uint(1) - << detail::num_significand_bits(); - const carrier_uint significand_mask = implicit_bit - 1; - auto u = bit_cast(n); - f = static_cast(u & significand_mask); - int biased_e = static_cast((u & exponent_mask()) >> - detail::num_significand_bits()); - // The predecessor is closer if n is a normalized power of 2 (f == 0) other - // than the smallest normalized number (biased_e > 1). - bool is_predecessor_closer = f == 0 && biased_e > 1; - if (biased_e != 0) - f += static_cast(implicit_bit); - else - biased_e = 1; // Subnormals use biased exponent 1 (min exponent). - const int exponent_bias = std::numeric_limits::max_exponent - 1; - e = biased_e - exponent_bias - std::numeric_limits::digits + 1; - return is_predecessor_closer; - } - - template ::value)> - bool assign(Float) = delete; -}; - -using fp = basic_fp; - -// Normalizes the value converted from double and multiplied by (1 << SHIFT). -template -FMT_CONSTEXPR basic_fp normalize(basic_fp value) { - // Handle subnormals. - const uint64_t implicit_bit = 1ULL << num_significand_bits(); - const auto shifted_implicit_bit = implicit_bit << SHIFT; - while ((value.f & shifted_implicit_bit) == 0) { - value.f <<= 1; - --value.e; - } - // Subtract 1 to account for hidden bit. - const auto offset = - fp::num_significand_bits - num_significand_bits() - SHIFT - 1; - value.f <<= offset; - value.e -= offset; - return value; -} - template inline bool operator==(basic_fp x, basic_fp y) { return x.f == y.f && x.e == y.e; } -// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking. -FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs) { -#if FMT_USE_INT128 - auto product = static_cast<__uint128_t>(lhs) * rhs; - auto f = static_cast(product >> 64); - return (static_cast(product) & (1ULL << 63)) != 0 ? f + 1 : f; -#else - // Multiply 32-bit parts of significands. - uint64_t mask = (1ULL << 32) - 1; - uint64_t a = lhs >> 32, b = lhs & mask; - uint64_t c = rhs >> 32, d = rhs & mask; - uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d; - // Compute mid 64-bit of result and round. - uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31); - return ac + (ad >> 32) + (bc >> 32) + (mid >> 32); -#endif -} - -FMT_CONSTEXPR inline fp operator*(fp x, fp y) { - return {multiply(x.f, y.f), x.e + y.e + 64}; -} - -// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its -// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`. -FMT_CONSTEXPR inline fp get_cached_power(int min_exponent, - int& pow10_exponent) { - const int shift = 32; - // log10(2) = 0x0.4d104d427de7fbcc... - const int64_t significand = 0x4d104d427de7fbcc; - int index = static_cast( - ((min_exponent + fp::num_significand_bits - 1) * (significand >> shift) + - ((int64_t(1) << shift) - 1)) // ceil - >> 32 // arithmetic shift - ); - // Decimal exponent of the first (smallest) cached power of 10. - const int first_dec_exp = -348; - // Difference between 2 consecutive decimal exponents in cached powers of 10. - const int dec_exp_step = 8; - index = (index - first_dec_exp - 1) / dec_exp_step + 1; - pow10_exponent = first_dec_exp + index * dec_exp_step; - return {impl_data::pow10_significands[index], - impl_data::pow10_exponents[index]}; -} - -class bigint { - private: - // A bigint is stored as an array of bigits (big digits), with bigit at index - // 0 being the least significant one. - using bigit = uint32_t; - using double_bigit = uint64_t; - enum { bigits_capacity = 32 }; - basic_memory_buffer bigits_; - int exp_; - - FMT_CONSTEXPR20 bigit operator[](int index) const { - return bigits_[to_unsigned(index)]; - } - FMT_CONSTEXPR20 bigit& operator[](int index) { - return bigits_[to_unsigned(index)]; - } - - static FMT_CONSTEXPR_DECL const int bigit_bits = bits::value; - - friend struct formatter; - - FMT_CONSTEXPR20 void subtract_bigits(int index, bigit other, bigit& borrow) { - auto result = static_cast((*this)[index]) - other - borrow; - (*this)[index] = static_cast(result); - borrow = static_cast(result >> (bigit_bits * 2 - 1)); - } - - FMT_CONSTEXPR20 void remove_leading_zeros() { - int num_bigits = static_cast(bigits_.size()) - 1; - while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits; - bigits_.resize(to_unsigned(num_bigits + 1)); - } - - // Computes *this -= other assuming aligned bigints and *this >= other. - FMT_CONSTEXPR20 void subtract_aligned(const bigint& other) { - FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints"); - FMT_ASSERT(compare(*this, other) >= 0, ""); - bigit borrow = 0; - int i = other.exp_ - exp_; - for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j) - subtract_bigits(i, other.bigits_[j], borrow); - while (borrow > 0) subtract_bigits(i, 0, borrow); - remove_leading_zeros(); - } - - FMT_CONSTEXPR20 void multiply(uint32_t value) { - const double_bigit wide_value = value; - bigit carry = 0; - for (size_t i = 0, n = bigits_.size(); i < n; ++i) { - double_bigit result = bigits_[i] * wide_value + carry; - bigits_[i] = static_cast(result); - carry = static_cast(result >> bigit_bits); - } - if (carry != 0) bigits_.push_back(carry); - } - - FMT_CONSTEXPR20 void multiply(uint64_t value) { - const bigit mask = ~bigit(0); - const double_bigit lower = value & mask; - const double_bigit upper = value >> bigit_bits; - double_bigit carry = 0; - for (size_t i = 0, n = bigits_.size(); i < n; ++i) { - double_bigit result = bigits_[i] * lower + (carry & mask); - carry = - bigits_[i] * upper + (result >> bigit_bits) + (carry >> bigit_bits); - bigits_[i] = static_cast(result); - } - while (carry != 0) { - bigits_.push_back(carry & mask); - carry >>= bigit_bits; - } - } - - public: - FMT_CONSTEXPR20 bigint() : exp_(0) {} - explicit bigint(uint64_t n) { assign(n); } - - bigint(const bigint&) = delete; - void operator=(const bigint&) = delete; - - FMT_CONSTEXPR20 void assign(const bigint& other) { - auto size = other.bigits_.size(); - bigits_.resize(size); - auto data = other.bigits_.data(); - std::copy(data, data + size, make_checked(bigits_.data(), size)); - exp_ = other.exp_; - } - - FMT_CONSTEXPR20 void assign(uint64_t n) { - size_t num_bigits = 0; - do { - bigits_[num_bigits++] = n & ~bigit(0); - n >>= bigit_bits; - } while (n != 0); - bigits_.resize(num_bigits); - exp_ = 0; - } - - FMT_CONSTEXPR20 int num_bigits() const { - return static_cast(bigits_.size()) + exp_; - } - - FMT_NOINLINE FMT_CONSTEXPR20 bigint& operator<<=(int shift) { - FMT_ASSERT(shift >= 0, ""); - exp_ += shift / bigit_bits; - shift %= bigit_bits; - if (shift == 0) return *this; - bigit carry = 0; - for (size_t i = 0, n = bigits_.size(); i < n; ++i) { - bigit c = bigits_[i] >> (bigit_bits - shift); - bigits_[i] = (bigits_[i] << shift) + carry; - carry = c; - } - if (carry != 0) bigits_.push_back(carry); - return *this; - } - - template FMT_CONSTEXPR20 bigint& operator*=(Int value) { - FMT_ASSERT(value > 0, ""); - multiply(uint32_or_64_or_128_t(value)); - return *this; - } - - friend FMT_CONSTEXPR20 int compare(const bigint& lhs, const bigint& rhs) { - int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits(); - if (num_lhs_bigits != num_rhs_bigits) - return num_lhs_bigits > num_rhs_bigits ? 1 : -1; - int i = static_cast(lhs.bigits_.size()) - 1; - int j = static_cast(rhs.bigits_.size()) - 1; - int end = i - j; - if (end < 0) end = 0; - for (; i >= end; --i, --j) { - bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j]; - if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1; - } - if (i != j) return i > j ? 1 : -1; - return 0; - } - - // Returns compare(lhs1 + lhs2, rhs). - friend FMT_CONSTEXPR20 int add_compare(const bigint& lhs1, const bigint& lhs2, - const bigint& rhs) { - int max_lhs_bigits = (std::max)(lhs1.num_bigits(), lhs2.num_bigits()); - int num_rhs_bigits = rhs.num_bigits(); - if (max_lhs_bigits + 1 < num_rhs_bigits) return -1; - if (max_lhs_bigits > num_rhs_bigits) return 1; - auto get_bigit = [](const bigint& n, int i) -> bigit { - return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0; - }; - double_bigit borrow = 0; - int min_exp = (std::min)((std::min)(lhs1.exp_, lhs2.exp_), rhs.exp_); - for (int i = num_rhs_bigits - 1; i >= min_exp; --i) { - double_bigit sum = - static_cast(get_bigit(lhs1, i)) + get_bigit(lhs2, i); - bigit rhs_bigit = get_bigit(rhs, i); - if (sum > rhs_bigit + borrow) return 1; - borrow = rhs_bigit + borrow - sum; - if (borrow > 1) return -1; - borrow <<= bigit_bits; - } - return borrow != 0 ? -1 : 0; - } - - // Assigns pow(10, exp) to this bigint. - FMT_CONSTEXPR20 void assign_pow10(int exp) { - FMT_ASSERT(exp >= 0, ""); - if (exp == 0) return assign(1); - // Find the top bit. - int bitmask = 1; - while (exp >= bitmask) bitmask <<= 1; - bitmask >>= 1; - // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by - // repeated squaring and multiplication. - assign(5); - bitmask >>= 1; - while (bitmask != 0) { - square(); - if ((exp & bitmask) != 0) *this *= 5; - bitmask >>= 1; - } - *this <<= exp; // Multiply by pow(2, exp) by shifting. - } - - FMT_CONSTEXPR20 void square() { - int num_bigits = static_cast(bigits_.size()); - int num_result_bigits = 2 * num_bigits; - basic_memory_buffer n(std::move(bigits_)); - bigits_.resize(to_unsigned(num_result_bigits)); - auto sum = uint128_t(); - for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) { - // Compute bigit at position bigit_index of the result by adding - // cross-product terms n[i] * n[j] such that i + j == bigit_index. - for (int i = 0, j = bigit_index; j >= 0; ++i, --j) { - // Most terms are multiplied twice which can be optimized in the future. - sum += static_cast(n[i]) * n[j]; - } - (*this)[bigit_index] = static_cast(sum); - sum >>= bits::value; // Compute the carry. - } - // Do the same for the top half. - for (int bigit_index = num_bigits; bigit_index < num_result_bigits; - ++bigit_index) { - for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;) - sum += static_cast(n[i++]) * n[j--]; - (*this)[bigit_index] = static_cast(sum); - sum >>= bits::value; - } - remove_leading_zeros(); - exp_ *= 2; - } - - // If this bigint has a bigger exponent than other, adds trailing zero to make - // exponents equal. This simplifies some operations such as subtraction. - FMT_CONSTEXPR20 void align(const bigint& other) { - int exp_difference = exp_ - other.exp_; - if (exp_difference <= 0) return; - int num_bigits = static_cast(bigits_.size()); - bigits_.resize(to_unsigned(num_bigits + exp_difference)); - for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j) - bigits_[j] = bigits_[i]; - std::uninitialized_fill_n(bigits_.data(), exp_difference, 0); - exp_ -= exp_difference; - } - - // Divides this bignum by divisor, assigning the remainder to this and - // returning the quotient. - FMT_CONSTEXPR20 int divmod_assign(const bigint& divisor) { - FMT_ASSERT(this != &divisor, ""); - if (compare(*this, divisor) < 0) return 0; - FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, ""); - align(divisor); - int quotient = 0; - do { - subtract_aligned(divisor); - ++quotient; - } while (compare(*this, divisor) >= 0); - return quotient; - } -}; - -enum class round_direction { unknown, up, down }; - -// Given the divisor (normally a power of 10), the remainder = v % divisor for -// some number v and the error, returns whether v should be rounded up, down, or -// whether the rounding direction can't be determined due to error. -// error should be less than divisor / 2. -FMT_CONSTEXPR inline round_direction get_round_direction(uint64_t divisor, - uint64_t remainder, - uint64_t error) { - FMT_ASSERT(remainder < divisor, ""); // divisor - remainder won't overflow. - FMT_ASSERT(error < divisor, ""); // divisor - error won't overflow. - FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow. - // Round down if (remainder + error) * 2 <= divisor. - if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2) - return round_direction::down; - // Round up if (remainder - error) * 2 >= divisor. - if (remainder >= error && - remainder - error >= divisor - (remainder - error)) { - return round_direction::up; - } - return round_direction::unknown; -} - -namespace digits { -enum result { - more, // Generate more digits. - done, // Done generating digits. - error // Digit generation cancelled due to an error. -}; -} - -struct gen_digits_handler { - char* buf; - int size; - int precision; - int exp10; - bool fixed; - - FMT_CONSTEXPR digits::result on_digit(char digit, uint64_t divisor, - uint64_t remainder, uint64_t error, - bool integral) { - FMT_ASSERT(remainder < divisor, ""); - buf[size++] = digit; - if (!integral && error >= remainder) return digits::error; - if (size < precision) return digits::more; - if (!integral) { - // Check if error * 2 < divisor with overflow prevention. - // The check is not needed for the integral part because error = 1 - // and divisor > (1 << 32) there. - if (error >= divisor || error >= divisor - error) return digits::error; - } else { - FMT_ASSERT(error == 1 && divisor > 2, ""); - } - auto dir = get_round_direction(divisor, remainder, error); - if (dir != round_direction::up) - return dir == round_direction::down ? digits::done : digits::error; - ++buf[size - 1]; - for (int i = size - 1; i > 0 && buf[i] > '9'; --i) { - buf[i] = '0'; - ++buf[i - 1]; - } - if (buf[0] > '9') { - buf[0] = '1'; - if (fixed) - buf[size++] = '0'; - else - ++exp10; - } - return digits::done; - } -}; - -inline FMT_CONSTEXPR20 void adjust_precision(int& precision, int exp10) { - // Adjust fixed precision by exponent because it is relative to decimal - // point. - if (exp10 > 0 && precision > max_value() - exp10) - FMT_THROW(format_error("number is too big")); - precision += exp10; -} - -// Generates output using the Grisu digit-gen algorithm. -// error: the size of the region (lower, upper) outside of which numbers -// definitely do not round to value (Delta in Grisu3). -FMT_INLINE FMT_CONSTEXPR20 digits::result grisu_gen_digits( - fp value, uint64_t error, int& exp, gen_digits_handler& handler) { - const fp one(1ULL << -value.e, value.e); - // The integral part of scaled value (p1 in Grisu) = value / one. It cannot be - // zero because it contains a product of two 64-bit numbers with MSB set (due - // to normalization) - 1, shifted right by at most 60 bits. - auto integral = static_cast(value.f >> -one.e); - FMT_ASSERT(integral != 0, ""); - FMT_ASSERT(integral == value.f >> -one.e, ""); - // The fractional part of scaled value (p2 in Grisu) c = value % one. - uint64_t fractional = value.f & (one.f - 1); - exp = count_digits(integral); // kappa in Grisu. - // Non-fixed formats require at least one digit and no precision adjustment. - if (handler.fixed) { - adjust_precision(handler.precision, exp + handler.exp10); - // Check if precision is satisfied just by leading zeros, e.g. - // format("{:.2f}", 0.001) gives "0.00" without generating any digits. - if (handler.precision <= 0) { - if (handler.precision < 0) return digits::done; - // Divide by 10 to prevent overflow. - uint64_t divisor = impl_data::power_of_10_64[exp - 1] << -one.e; - auto dir = get_round_direction(divisor, value.f / 10, error * 10); - if (dir == round_direction::unknown) return digits::error; - handler.buf[handler.size++] = dir == round_direction::up ? '1' : '0'; - return digits::done; - } - } - // Generate digits for the integral part. This can produce up to 10 digits. - do { - uint32_t digit = 0; - auto divmod_integral = [&](uint32_t divisor) { - digit = integral / divisor; - integral %= divisor; - }; - // This optimization by Milo Yip reduces the number of integer divisions by - // one per iteration. - switch (exp) { - case 10: - divmod_integral(1000000000); - break; - case 9: - divmod_integral(100000000); - break; - case 8: - divmod_integral(10000000); - break; - case 7: - divmod_integral(1000000); - break; - case 6: - divmod_integral(100000); - break; - case 5: - divmod_integral(10000); - break; - case 4: - divmod_integral(1000); - break; - case 3: - divmod_integral(100); - break; - case 2: - divmod_integral(10); - break; - case 1: - digit = integral; - integral = 0; - break; - default: - FMT_ASSERT(false, "invalid number of digits"); - } - --exp; - auto remainder = (static_cast(integral) << -one.e) + fractional; - auto result = handler.on_digit(static_cast('0' + digit), - impl_data::power_of_10_64[exp] << -one.e, - remainder, error, true); - if (result != digits::more) return result; - } while (exp > 0); - // Generate digits for the fractional part. - for (;;) { - fractional *= 10; - error *= 10; - char digit = static_cast('0' + (fractional >> -one.e)); - fractional &= one.f - 1; - --exp; - auto result = handler.on_digit(digit, one.f, fractional, error, false); - if (result != digits::more) return result; - } -} - -// A 128-bit integer type used internally. -struct uint128_wrapper { - uint128_wrapper() = default; - - uint64_t high_; - uint64_t low_; - - constexpr uint128_wrapper(uint64_t high, uint64_t low) noexcept - : high_{high}, low_{low} {} - - constexpr uint64_t high() const noexcept { return high_; } - constexpr uint64_t low() const noexcept { return low_; } - - uint128_wrapper& operator+=(uint64_t n) noexcept { -#if FMT_HAS_BUILTIN(__builtin_addcll) - unsigned long long carry; - low_ = __builtin_addcll(low_, n, 0, &carry); - high_ += carry; -#elif FMT_HAS_BUILTIN(__builtin_ia32_addcarryx_u64) - unsigned long long result; - auto carry = __builtin_ia32_addcarryx_u64(0, low_, n, &result); - low_ = result; - high_ += carry; -#elif defined(_MSC_VER) && defined(_M_X64) - auto carry = _addcarry_u64(0, low_, n, &low_); - _addcarry_u64(carry, high_, 0, &high_); -#else - low_ += n; - high_ += (low_ < n ? 1 : 0); -#endif - return *this; - } -}; - // Compilers should be able to optimize this into the ror instruction. FMT_CONSTEXPR inline uint32_t rotr(uint32_t n, uint32_t r) noexcept { r &= 31; @@ -790,56 +168,6 @@ FMT_CONSTEXPR inline uint64_t rotr(uint64_t n, uint32_t r) noexcept { // Implementation of Dragonbox algorithm: https://github.com/jk-jeon/dragonbox. namespace dragonbox { -// Computes 128-bit result of multiplication of two 64-bit unsigned integers. -inline uint128_wrapper umul128(uint64_t x, uint64_t y) noexcept { -#if FMT_USE_INT128 - auto p = static_cast(x) * static_cast(y); - return {static_cast(p >> 64), static_cast(p)}; -#elif defined(_MSC_VER) && defined(_M_X64) - uint128_wrapper result; - result.low_ = _umul128(x, y, &result.high_); - return result; -#else - const uint64_t mask = static_cast(max_value()); - - uint64_t a = x >> 32; - uint64_t b = x & mask; - uint64_t c = y >> 32; - uint64_t d = y & mask; - - uint64_t ac = a * c; - uint64_t bc = b * c; - uint64_t ad = a * d; - uint64_t bd = b * d; - - uint64_t intermediate = (bd >> 32) + (ad & mask) + (bc & mask); - - return {ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32), - (intermediate << 32) + (bd & mask)}; -#endif -} - -// Computes upper 64 bits of multiplication of two 64-bit unsigned integers. -inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept { -#if FMT_USE_INT128 - auto p = static_cast(x) * static_cast(y); - return static_cast(p >> 64); -#elif defined(_MSC_VER) && defined(_M_X64) - return __umulh(x, y); -#else - return umul128(x, y).high(); -#endif -} - -// Computes upper 128 bits of multiplication of a 64-bit unsigned integer and a -// 128-bit unsigned integer. -inline uint128_wrapper umul192_upper128(uint64_t x, - uint128_wrapper y) noexcept { - uint128_wrapper r = umul128(x, y.high()); - r += umul128_upper64(x, y.low()); - return r; -} - // Computes upper 64 bits of multiplication of a 32-bit unsigned integer and a // 64-bit unsigned integer. inline uint64_t umul96_upper64(uint32_t x, uint64_t y) noexcept { @@ -848,10 +176,10 @@ inline uint64_t umul96_upper64(uint32_t x, uint64_t y) noexcept { // Computes lower 128 bits of multiplication of a 64-bit unsigned integer and a // 128-bit unsigned integer. -inline uint128_wrapper umul192_lower128(uint64_t x, - uint128_wrapper y) noexcept { +inline uint128_fallback umul192_lower128(uint64_t x, + uint128_fallback y) noexcept { uint64_t high = x * y.high(); - uint128_wrapper high_low = umul128(x, y.low()); + uint128_fallback high_low = umul128(x, y.low()); return {high + high_low.high(), high_low.low()}; } @@ -861,25 +189,13 @@ inline uint64_t umul96_lower64(uint32_t x, uint64_t y) noexcept { return x * y; } -// Computes floor(log10(pow(2, e))) for e in [-2620, 2620] using the method from -// https://fmt.dev/papers/Dragonbox.pdf#page=28, section 6.1. -inline int floor_log10_pow2(int e) noexcept { - FMT_ASSERT(e <= 2620 && e >= -2620, "too large exponent"); - static_assert((-1 >> 1) == -1, "right shift is not arithmetic"); - return (e * 315653) >> 20; -} - // Various fast log computations. -inline int floor_log2_pow10(int e) noexcept { - FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent"); - return (e * 1741647) >> 19; -} inline int floor_log10_pow2_minus_log10_4_over_3(int e) noexcept { FMT_ASSERT(e <= 2936 && e >= -2985, "too large exponent"); return (e * 631305 - 261663) >> 21; } -static constexpr struct { +FMT_INLINE_VARIABLE constexpr struct { uint32_t divisor; int shift_amount; } div_small_pow10_infos[] = {{10, 16}, {100, 16}}; @@ -933,7 +249,7 @@ inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) noexcept { } // Various subroutines using pow10 cache -template struct cache_accessor; +template struct cache_accessor; template <> struct cache_accessor { using carrier_uint = float_info::carrier_uint; @@ -1028,13 +344,13 @@ template <> struct cache_accessor { template <> struct cache_accessor { using carrier_uint = float_info::carrier_uint; - using cache_entry_type = uint128_wrapper; + using cache_entry_type = uint128_fallback; - static uint128_wrapper get_cached_power(int k) noexcept { + static uint128_fallback get_cached_power(int k) noexcept { FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, "k is out of range"); - static constexpr const uint128_wrapper pow10_significands[] = { + static constexpr const uint128_fallback pow10_significands[] = { #if FMT_USE_FULL_CACHE_DRAGONBOX {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, {0x9faacf3df73609b1, 0x77b191618c54e9ad}, @@ -1654,8 +970,23 @@ template <> struct cache_accessor { {0xfcf62c1dee382c42, 0x46729e03dd9ed7b6}, {0x9e19db92b4e31ba9, 0x6c07a2c26a8346d2}, {0xc5a05277621be293, 0xc7098b7305241886}, - { 0xf70867153aa2db38, - 0xb8cbee4fc66d1ea8 } + {0xf70867153aa2db38, 0xb8cbee4fc66d1ea8}, + {0x9a65406d44a5c903, 0x737f74f1dc043329}, + {0xc0fe908895cf3b44, 0x505f522e53053ff3}, + {0xf13e34aabb430a15, 0x647726b9e7c68ff0}, + {0x96c6e0eab509e64d, 0x5eca783430dc19f6}, + {0xbc789925624c5fe0, 0xb67d16413d132073}, + {0xeb96bf6ebadf77d8, 0xe41c5bd18c57e890}, + {0x933e37a534cbaae7, 0x8e91b962f7b6f15a}, + {0xb80dc58e81fe95a1, 0x723627bbb5a4adb1}, + {0xe61136f2227e3b09, 0xcec3b1aaa30dd91d}, + {0x8fcac257558ee4e6, 0x213a4f0aa5e8a7b2}, + {0xb3bd72ed2af29e1f, 0xa988e2cd4f62d19e}, + {0xe0accfa875af45a7, 0x93eb1b80a33b8606}, + {0x8c6c01c9498d8b88, 0xbc72f130660533c4}, + {0xaf87023b9bf0ee6a, 0xeb8fad7c7f8680b5}, + { 0xdb68c2ca82ed2a05, + 0xa67398db9f6820e2 } #else {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, {0xce5d73ff402d98e3, 0xfb0a3d212dc81290}, @@ -1679,8 +1010,8 @@ template <> struct cache_accessor { {0x8da471a9de737e24, 0x5ceaecfed289e5d3}, {0xe4d5e82392a40515, 0x0fabaf3feaa5334b}, {0xb8da1662e7b00a17, 0x3d6a751f3b936244}, - { 0x95527a5202df0ccb, - 0x0f37801e0c43ebc9 } + {0x95527a5202df0ccb, 0x0f37801e0c43ebc9}, + {0xf13e34aabb430a15, 0x647726b9e7c68ff0} #endif }; @@ -1706,7 +1037,7 @@ template <> struct cache_accessor { int offset = k - kb; // Get base cache. - uint128_wrapper base_cache = pow10_significands[cache_index]; + uint128_fallback base_cache = pow10_significands[cache_index]; if (offset == 0) return base_cache; // Compute the required amount of bit-shift. @@ -1715,8 +1046,8 @@ template <> struct cache_accessor { // Try to recover the real cache. uint64_t pow5 = powers_of_5_64[offset]; - uint128_wrapper recovered_cache = umul128(base_cache.high(), pow5); - uint128_wrapper middle_low = umul128(base_cache.low(), pow5); + uint128_fallback recovered_cache = umul128(base_cache.high(), pow5); + uint128_fallback middle_low = umul128(base_cache.low(), pow5); recovered_cache += middle_low.high(); @@ -1724,8 +1055,8 @@ template <> struct cache_accessor { uint64_t middle_to_low = recovered_cache.low() << (64 - alpha); recovered_cache = - uint128_wrapper{(recovered_cache.low() >> alpha) | high_to_middle, - ((middle_low.low() >> alpha) | middle_to_low)}; + uint128_fallback{(recovered_cache.low() >> alpha) | high_to_middle, + ((middle_low.low() >> alpha) | middle_to_low)}; FMT_ASSERT(recovered_cache.low() + 1 != 0, ""); return {recovered_cache.high(), recovered_cache.low() + 1}; #endif @@ -1783,8 +1114,12 @@ template <> struct cache_accessor { } }; +FMT_FUNC uint128_fallback get_cached_power(int k) noexcept { + return cache_accessor::get_cached_power(k); +} + // Various integer checks -template +template bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept { const int case_shorter_interval_left_endpoint_lower_threshold = 2; const int case_shorter_interval_left_endpoint_upper_threshold = 3; @@ -1795,8 +1130,12 @@ bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept { // Remove trailing zeros from n and return the number of zeros removed (float) FMT_INLINE int remove_trailing_zeros(uint32_t& n) noexcept { FMT_ASSERT(n != 0, ""); + // Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1. + // See https://github.com/fmtlib/fmt/issues/3163 for more details. const uint32_t mod_inv_5 = 0xcccccccd; - const uint32_t mod_inv_25 = mod_inv_5 * mod_inv_5; + // Casts are needed to workaround a bug in MSVC 19.22 and older. + const uint32_t mod_inv_25 = + static_cast(uint64_t(mod_inv_5) * mod_inv_5); int s = 0; while (true) { @@ -1810,7 +1149,6 @@ FMT_INLINE int remove_trailing_zeros(uint32_t& n) noexcept { n = q; s |= 1; } - return s; } @@ -1868,7 +1206,7 @@ FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept { } // The main algorithm for shorter interval case -template +template FMT_INLINE decimal_fp shorter_interval_case(int exponent) noexcept { decimal_fp ret_value; // Compute k and beta @@ -1930,8 +1268,7 @@ template decimal_fp to_decimal(T x) noexcept { static_cast((br & exponent_mask()) >> num_significand_bits()); if (exponent != 0) { // Check if normal. - const int exponent_bias = std::numeric_limits::max_exponent - 1; - exponent -= exponent_bias + num_significand_bits(); + exponent -= exponent_bias() + num_significand_bits(); // Shorter interval case; proceed like Schubfach. // In fact, when exponent == 1 and significand == 0, the interval is @@ -1983,7 +1320,7 @@ template decimal_fp to_decimal(T x) noexcept { if (r < deltai) { // Exclude the right endpoint if necessary. - if (r == 0 && z_mul.is_integer && !include_right_endpoint) { + if (r == 0 && (z_mul.is_integer & !include_right_endpoint)) { --ret_value.significand; r = float_info::big_divisor; goto small_divisor_case_label; @@ -1992,26 +1329,11 @@ template decimal_fp to_decimal(T x) noexcept { goto small_divisor_case_label; } else { // r == deltai; compare fractional parts. - const carrier_uint two_fl = two_fc - 1; + const typename cache_accessor::compute_mul_parity_result x_mul = + cache_accessor::compute_mul_parity(two_fc - 1, cache, beta); - if (!include_left_endpoint || - exponent < float_info::case_fc_pm_half_lower_threshold || - exponent > float_info::divisibility_check_by_5_threshold) { - // If the left endpoint is not included, the condition for - // success is z^(f) < delta^(f) (odd parity). - // Otherwise, the inequalities on exponent ensure that - // x is not an integer, so if z^(f) >= delta^(f) (even parity), we in fact - // have strict inequality. - if (!cache_accessor::compute_mul_parity(two_fl, cache, beta).parity) { - goto small_divisor_case_label; - } - } else { - const typename cache_accessor::compute_mul_parity_result x_mul = - cache_accessor::compute_mul_parity(two_fl, cache, beta); - if (!x_mul.parity && !x_mul.is_integer) { - goto small_divisor_case_label; - } - } + if (!(x_mul.parity | (x_mul.is_integer & include_left_endpoint))) + goto small_divisor_case_label; } ret_value.exponent = minus_k + float_info::kappa + 1; @@ -2050,321 +1372,21 @@ small_divisor_case_label: // or equivalently, when y is an integer. if (y_mul.parity != approx_y_parity) --ret_value.significand; - else if (y_mul.is_integer && ret_value.significand % 2 != 0) + else if (y_mul.is_integer & (ret_value.significand % 2 != 0)) --ret_value.significand; return ret_value; } } // namespace dragonbox - -// Formats a floating-point number using a variation of the Fixed-Precision -// Positive Floating-Point Printout ((FPP)^2) algorithm by Steele & White: -// https://fmt.dev/papers/p372-steele.pdf. -FMT_CONSTEXPR20 inline void format_dragon(fp value, bool is_predecessor_closer, - int num_digits, buffer& buf, - int& exp10) { - bigint numerator; // 2 * R in (FPP)^2. - bigint denominator; // 2 * S in (FPP)^2. - // lower and upper are differences between value and corresponding boundaries. - bigint lower; // (M^- in (FPP)^2). - bigint upper_store; // upper's value if different from lower. - bigint* upper = nullptr; // (M^+ in (FPP)^2). - // Shift numerator and denominator by an extra bit or two (if lower boundary - // is closer) to make lower and upper integers. This eliminates multiplication - // by 2 during later computations. - int shift = is_predecessor_closer ? 2 : 1; - if (value.e >= 0) { - numerator.assign(value.f); - numerator <<= value.e + shift; - lower.assign(1); - lower <<= value.e; - if (shift != 1) { - upper_store.assign(1); - upper_store <<= value.e + 1; - upper = &upper_store; - } - denominator.assign_pow10(exp10); - denominator <<= shift; - } else if (exp10 < 0) { - numerator.assign_pow10(-exp10); - lower.assign(numerator); - if (shift != 1) { - upper_store.assign(numerator); - upper_store <<= 1; - upper = &upper_store; - } - numerator *= value.f; - numerator <<= shift; - denominator.assign(1); - denominator <<= shift - value.e; - } else { - numerator.assign(value.f); - numerator <<= shift; - denominator.assign_pow10(exp10); - denominator <<= shift - value.e; - lower.assign(1); - if (shift != 1) { - upper_store.assign(1ULL << 1); - upper = &upper_store; - } - } - // Invariant: value == (numerator / denominator) * pow(10, exp10). - if (num_digits < 0) { - // Generate the shortest representation. - if (!upper) upper = &lower; - bool even = (value.f & 1) == 0; - num_digits = 0; - char* data = buf.data(); - for (;;) { - int digit = numerator.divmod_assign(denominator); - bool low = compare(numerator, lower) - even < 0; // numerator <[=] lower. - // numerator + upper >[=] pow10: - bool high = add_compare(numerator, *upper, denominator) + even > 0; - data[num_digits++] = static_cast('0' + digit); - if (low || high) { - if (!low) { - ++data[num_digits - 1]; - } else if (high) { - int result = add_compare(numerator, numerator, denominator); - // Round half to even. - if (result > 0 || (result == 0 && (digit % 2) != 0)) - ++data[num_digits - 1]; - } - buf.try_resize(to_unsigned(num_digits)); - exp10 -= num_digits - 1; - return; - } - numerator *= 10; - lower *= 10; - if (upper != &lower) *upper *= 10; - } - } - // Generate the given number of digits. - exp10 -= num_digits - 1; - if (num_digits == 0) { - denominator *= 10; - auto digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0'; - buf.push_back(digit); - return; - } - buf.try_resize(to_unsigned(num_digits)); - for (int i = 0; i < num_digits - 1; ++i) { - int digit = numerator.divmod_assign(denominator); - buf[i] = static_cast('0' + digit); - numerator *= 10; - } - int digit = numerator.divmod_assign(denominator); - auto result = add_compare(numerator, numerator, denominator); - if (result > 0 || (result == 0 && (digit % 2) != 0)) { - if (digit == 9) { - const auto overflow = '0' + 10; - buf[num_digits - 1] = overflow; - // Propagate the carry. - for (int i = num_digits - 1; i > 0 && buf[i] == overflow; --i) { - buf[i] = '0'; - ++buf[i - 1]; - } - if (buf[0] == overflow) { - buf[0] = '1'; - ++exp10; - } - return; - } - ++digit; - } - buf[num_digits - 1] = static_cast('0' + digit); -} - -template -FMT_HEADER_ONLY_CONSTEXPR20 int format_float(Float value, int precision, - float_specs specs, - buffer& buf) { - // float is passed as double to reduce the number of instantiations. - static_assert(!std::is_same::value, ""); - FMT_ASSERT(value >= 0, "value is negative"); - - const bool fixed = specs.format == float_format::fixed; - if (value <= 0) { // <= instead of == to silence a warning. - if (precision <= 0 || !fixed) { - buf.push_back('0'); - return 0; - } - buf.try_resize(to_unsigned(precision)); - fill_n(buf.data(), precision, '0'); - return -precision; - } - - int exp = 0; - bool use_dragon = true; - if (!is_fast_float()) { - // Use floor because 0.9 = 9e-1. - exp = static_cast(std::floor(std::log10(value))); - if (fixed) adjust_precision(precision, exp + 1); - } else if (!is_constant_evaluated() && precision < 0) { - // Use Dragonbox for the shortest format. - if (specs.binary32) { - auto dec = dragonbox::to_decimal(static_cast(value)); - write(buffer_appender(buf), dec.significand); - return dec.exponent; - } - auto dec = dragonbox::to_decimal(static_cast(value)); - write(buffer_appender(buf), dec.significand); - return dec.exponent; - } else { - // Use Grisu + Dragon4 for the given precision: - // https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf. - const int min_exp = -60; // alpha in Grisu. - int cached_exp10 = 0; // K in Grisu. - fp normalized = normalize(fp(convert_float(value))); - const auto cached_pow = get_cached_power( - min_exp - (normalized.e + fp::num_significand_bits), cached_exp10); - normalized = normalized * cached_pow; - gen_digits_handler handler{buf.data(), 0, precision, -cached_exp10, fixed}; - if (grisu_gen_digits(normalized, 1, exp, handler) != digits::error && - !is_constant_evaluated()) { - exp += handler.exp10; - buf.try_resize(to_unsigned(handler.size)); - use_dragon = false; - } else { - exp += handler.size - cached_exp10 - 1; - precision = handler.precision; - } - } - if (use_dragon) { - auto f = fp(); - bool is_predecessor_closer = specs.binary32 - ? f.assign(static_cast(value)) - : f.assign(convert_float(value)); - // Limit precision to the maximum possible number of significant digits in - // an IEEE754 double because we don't need to generate zeros. - const int max_double_digits = 767; - if (precision > max_double_digits) precision = max_double_digits; - format_dragon(f, is_predecessor_closer, precision, buf, exp); - } - if (!fixed && !specs.showpoint) { - // Remove trailing zeros. - auto num_digits = buf.size(); - while (num_digits > 0 && buf[num_digits - 1] == '0') { - --num_digits; - ++exp; - } - buf.try_resize(num_digits); - } - return exp; -} - -template -int snprintf_float(T value, int precision, float_specs specs, - buffer& buf) { - // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. - FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer"); - static_assert(!std::is_same::value, ""); - - // Subtract 1 to account for the difference in precision since we use %e for - // both general and exponent format. - if (specs.format == float_format::general || - specs.format == float_format::exp) - precision = (precision >= 0 ? precision : 6) - 1; - - // Build the format string. - enum { max_format_size = 7 }; // The longest format is "%#.*Le". - char format[max_format_size]; - char* format_ptr = format; - *format_ptr++ = '%'; - if (specs.showpoint && specs.format == float_format::hex) *format_ptr++ = '#'; - if (precision >= 0) { - *format_ptr++ = '.'; - *format_ptr++ = '*'; - } - if (std::is_same()) *format_ptr++ = 'L'; - *format_ptr++ = specs.format != float_format::hex - ? (specs.format == float_format::fixed ? 'f' : 'e') - : (specs.upper ? 'A' : 'a'); - *format_ptr = '\0'; - - // Format using snprintf. - auto offset = buf.size(); - for (;;) { - auto begin = buf.data() + offset; - auto capacity = buf.capacity() - offset; -#ifdef FMT_FUZZ - if (precision > 100000) - throw std::runtime_error( - "fuzz mode - avoid large allocation inside snprintf"); -#endif - // Suppress the warning about a nonliteral format string. - // Cannot use auto because of a bug in MinGW (#1532). - int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF; - int result = precision >= 0 - ? snprintf_ptr(begin, capacity, format, precision, value) - : snprintf_ptr(begin, capacity, format, value); - if (result < 0) { - // The buffer will grow exponentially. - buf.try_reserve(buf.capacity() + 1); - continue; - } - auto size = to_unsigned(result); - // Size equal to capacity means that the last character was truncated. - if (size >= capacity) { - buf.try_reserve(size + offset + 1); // Add 1 for the terminating '\0'. - continue; - } - auto is_digit = [](char c) { return c >= '0' && c <= '9'; }; - if (specs.format == float_format::fixed) { - if (precision == 0) { - buf.try_resize(size); - return 0; - } - // Find and remove the decimal point. - auto end = begin + size, p = end; - do { - --p; - } while (is_digit(*p)); - int fraction_size = static_cast(end - p - 1); - std::memmove(p, p + 1, to_unsigned(fraction_size)); - buf.try_resize(size - 1); - return -fraction_size; - } - if (specs.format == float_format::hex) { - buf.try_resize(size + offset); - return 0; - } - // Find and parse the exponent. - auto end = begin + size, exp_pos = end; - do { - --exp_pos; - } while (*exp_pos != 'e'); - char sign = exp_pos[1]; - FMT_ASSERT(sign == '+' || sign == '-', ""); - int exp = 0; - auto p = exp_pos + 2; // Skip 'e' and sign. - do { - FMT_ASSERT(is_digit(*p), ""); - exp = exp * 10 + (*p++ - '0'); - } while (p != end); - if (sign == '-') exp = -exp; - int fraction_size = 0; - if (exp_pos != begin + 1) { - // Remove trailing zeros. - auto fraction_end = exp_pos - 1; - while (*fraction_end == '0') --fraction_end; - // Move the fractional part left to get rid of the decimal point. - fraction_size = static_cast(fraction_end - begin - 1); - std::memmove(begin + 1, begin + 2, to_unsigned(fraction_size)); - } - buf.try_resize(to_unsigned(fraction_size) + offset + 1); - return exp - fraction_size; - } -} } // namespace detail template <> struct formatter { - FMT_CONSTEXPR format_parse_context::iterator parse( - format_parse_context& ctx) { + FMT_CONSTEXPR auto parse(format_parse_context& ctx) + -> format_parse_context::iterator { return ctx.begin(); } - format_context::iterator format(const detail::bigint& n, - format_context& ctx) { + auto format(const detail::bigint& n, format_context& ctx) const + -> format_context::iterator { auto out = ctx.out(); bool first = true; for (auto i = n.bigits_.size(); i > 0; --i) { @@ -2414,12 +1436,6 @@ FMT_FUNC void report_system_error(int error_code, report_error(format_system_error, error_code, message); } -// DEPRECATED! -// This function is defined here and not inline for ABI compatiblity. -FMT_FUNC void detail::error_handler::on_error(const char* message) { - throw_format_error(message); -} - FMT_FUNC std::string vformat(string_view fmt, format_args args) { // Don't optimize the "{}" case to keep the binary size small and because it // can be better optimized in fmt::format anyway. @@ -2428,53 +1444,45 @@ FMT_FUNC std::string vformat(string_view fmt, format_args args) { return to_string(buffer); } -#ifdef _WIN32 namespace detail { +#ifndef _WIN32 +FMT_FUNC bool write_console(std::FILE*, string_view) { return false; } +#else using dword = conditional_t; extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( // void*, const void*, dword, dword*, void*); -} // namespace detail -#endif -namespace detail { -FMT_FUNC void print(std::FILE* f, string_view text) { -#ifdef _WIN32 +FMT_FUNC bool write_console(std::FILE* f, string_view text) { auto fd = _fileno(f); - if (_isatty(fd)) { - detail::utf8_to_utf16 u16(string_view(text.data(), text.size())); - auto written = detail::dword(); - if (detail::WriteConsoleW(reinterpret_cast(_get_osfhandle(fd)), - u16.c_str(), static_cast(u16.size()), - &written, nullptr)) { - return; - } - // Fallback to fwrite on failure. It can happen if the output has been - // redirected to NUL. - } -#endif - detail::fwrite_fully(text.data(), 1, text.size(), f); -} -} // namespace detail - -FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) { - memory_buffer buffer; - detail::vformat_to(buffer, format_str, args); - detail::print(f, {buffer.data(), buffer.size()}); + if (!_isatty(fd)) return false; + auto u16 = utf8_to_utf16(text); + auto written = dword(); + return WriteConsoleW(reinterpret_cast(_get_osfhandle(fd)), u16.c_str(), + static_cast(u16.size()), &written, nullptr); } -#ifdef _WIN32 // Print assuming legacy (non-Unicode) encoding. -FMT_FUNC void detail::vprint_mojibake(std::FILE* f, string_view format_str, - format_args args) { - memory_buffer buffer; - detail::vformat_to(buffer, format_str, +FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args) { + auto buffer = memory_buffer(); + detail::vformat_to(buffer, fmt, basic_format_args>(args)); fwrite_fully(buffer.data(), 1, buffer.size(), f); } #endif -FMT_FUNC void vprint(string_view format_str, format_args args) { - vprint(stdout, format_str, args); +FMT_FUNC void print(std::FILE* f, string_view text) { + if (!write_console(f, text)) fwrite_fully(text.data(), 1, text.size(), f); +} +} // namespace detail + +FMT_FUNC void vprint(std::FILE* f, string_view fmt, format_args args) { + auto buffer = memory_buffer(); + detail::vformat_to(buffer, fmt, args); + detail::print(f, {buffer.data(), buffer.size()}); +} + +FMT_FUNC void vprint(string_view fmt, format_args args) { + vprint(stdout, fmt, args); } namespace detail { diff --git a/vendor/Fmt/include/fmt/format.h b/vendor/Fmt/include/fmt/format.h index 6ed53aa9..ed8b29eb 100644 --- a/vendor/Fmt/include/fmt/format.h +++ b/vendor/Fmt/include/fmt/format.h @@ -33,14 +33,14 @@ #ifndef FMT_FORMAT_H_ #define FMT_FORMAT_H_ -#include // std::signbit -#include // std::byte -#include // uint32_t -#include // std::memcpy -#include // std::numeric_limits -#include // std::uninitialized_copy -#include // std::runtime_error -#include // std::system_error +#include // std::signbit +#include // uint32_t +#include // std::memcpy +#include // std::initializer_list +#include // std::numeric_limits +#include // std::uninitialized_copy +#include // std::runtime_error +#include // std::system_error #ifdef __cpp_lib_bit_cast # include // std::bitcast @@ -48,6 +48,36 @@ #include "core.h" +#ifndef FMT_BEGIN_DETAIL_NAMESPACE +# define FMT_BEGIN_DETAIL_NAMESPACE namespace detail { +# define FMT_END_DETAIL_NAMESPACE } +#endif + +#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough) +# define FMT_FALLTHROUGH [[fallthrough]] +#elif defined(__clang__) +# define FMT_FALLTHROUGH [[clang::fallthrough]] +#elif FMT_GCC_VERSION >= 700 && \ + (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520) +# define FMT_FALLTHROUGH [[gnu::fallthrough]] +#else +# define FMT_FALLTHROUGH +#endif + +#ifndef FMT_DEPRECATED +# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VERSION >= 1900 +# define FMT_DEPRECATED [[deprecated]] +# else +# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__) +# define FMT_DEPRECATED __attribute__((deprecated)) +# elif FMT_MSC_VERSION +# define FMT_DEPRECATED __declspec(deprecated) +# else +# define FMT_DEPRECATED /* deprecated */ +# endif +# endif +#endif + #if FMT_GCC_VERSION # define FMT_GCC_VISIBILITY_HIDDEN __attribute__((visibility("hidden"))) #else @@ -72,15 +102,9 @@ # define FMT_NOINLINE #endif -#if FMT_MSC_VER -# define FMT_MSC_DEFAULT = default -#else -# define FMT_MSC_DEFAULT -#endif - #ifndef FMT_THROW # if FMT_EXCEPTIONS -# if FMT_MSC_VER || FMT_NVCC +# if FMT_MSC_VERSION || defined(__NVCC__) FMT_BEGIN_NAMESPACE namespace detail { template inline void do_throw(const Exception& x) { @@ -119,17 +143,10 @@ FMT_END_NAMESPACE # endif #endif -// Workaround broken [[deprecated]] in the Intel, PGI and NVCC compilers. -#if FMT_ICC_VERSION || defined(__PGI) || FMT_NVCC -# define FMT_DEPRECATED_ALIAS -#else -# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED -#endif - #ifndef FMT_USE_USER_DEFINED_LITERALS // EDG based compilers (Intel, NVIDIA, Elbrus, etc), GCC and MSVC support UDLs. # if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \ - FMT_MSC_VER >= 1900) && \ + FMT_MSC_VERSION >= 1900) && \ (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= /* UDL feature */ 480) # define FMT_USE_USER_DEFINED_LITERALS 1 # else @@ -147,7 +164,7 @@ FMT_END_NAMESPACE // __builtin_clz is broken in clang with Microsoft CodeGen: // https://github.com/fmtlib/fmt/issues/519. -#if !FMT_MSC_VER +#if !FMT_MSC_VERSION # if FMT_HAS_BUILTIN(__builtin_clz) || FMT_GCC_VERSION || FMT_ICC_VERSION # define FMT_BUILTIN_CLZ(n) __builtin_clz(n) # endif @@ -160,23 +177,24 @@ FMT_END_NAMESPACE // https://github.com/fmtlib/fmt/issues/2510. #ifndef __ICL # if FMT_HAS_BUILTIN(__builtin_ctz) || FMT_GCC_VERSION || FMT_ICC_VERSION || \ - FMT_NVCOMPILER_VERSION + defined(__NVCOMPILER) # define FMT_BUILTIN_CTZ(n) __builtin_ctz(n) # endif # if FMT_HAS_BUILTIN(__builtin_ctzll) || FMT_GCC_VERSION || \ - FMT_ICC_VERSION || FMT_NVCOMPILER_VERSION + FMT_ICC_VERSION || defined(__NVCOMPILER) # define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n) # endif #endif -#if FMT_MSC_VER +#if FMT_MSC_VERSION # include // _BitScanReverse[64], _BitScanForward[64], _umul128 #endif // Some compilers masquerade as both MSVC and GCC-likes or otherwise support // __builtin_clz and __builtin_clzll, so only define FMT_BUILTIN_CLZ using the // MSVC intrinsics if the clz and clzll builtins are not available. -#if FMT_MSC_VER && !defined(FMT_BUILTIN_CLZLL) && !defined(FMT_BUILTIN_CTZLL) +#if FMT_MSC_VERSION && !defined(FMT_BUILTIN_CLZLL) && \ + !defined(FMT_BUILTIN_CTZLL) FMT_BEGIN_NAMESPACE namespace detail { // Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning. @@ -207,7 +225,8 @@ inline auto clzll(uint64_t x) -> int { _BitScanReverse64(&r, x); # else // Scan the high 32 bits. - if (_BitScanReverse(&r, static_cast(x >> 32))) return 63 ^ (r + 32); + if (_BitScanReverse(&r, static_cast(x >> 32))) + return 63 ^ static_cast(r + 32); // Scan the low 32 bits. _BitScanReverse(&r, static_cast(x)); # endif @@ -246,15 +265,41 @@ inline auto ctzll(uint64_t x) -> int { FMT_END_NAMESPACE #endif -#ifdef FMT_HEADER_ONLY -# define FMT_HEADER_ONLY_CONSTEXPR20 FMT_CONSTEXPR20 -#else -# define FMT_HEADER_ONLY_CONSTEXPR20 -#endif - FMT_BEGIN_NAMESPACE + +template struct disjunction : std::false_type {}; +template struct disjunction

: P {}; +template +struct disjunction + : conditional_t> {}; + +template struct conjunction : std::true_type {}; +template struct conjunction

: P {}; +template +struct conjunction + : conditional_t, P1> {}; + namespace detail { +FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) { + ignore_unused(condition); +#ifdef FMT_FUZZ + if (condition) throw std::runtime_error("fuzzing limit reached"); +#endif +} + +template struct string_literal { + static constexpr CharT value[sizeof...(C)] = {C...}; + constexpr operator basic_string_view() const { + return {value, sizeof...(C)}; + } +}; + +#if FMT_CPLUSPLUS < 201703L +template +constexpr CharT string_literal::value[sizeof...(C)]; +#endif + template class formatbuf : public Streambuf { private: using char_type = typename Streambuf::char_type; @@ -293,7 +338,8 @@ FMT_CONSTEXPR20 auto bit_cast(const From& from) -> To { if (is_constant_evaluated()) return std::bit_cast(from); #endif auto to = To(); - std::memcpy(&to, &from, sizeof(to)); + // The cast suppresses a bogus -Wclass-memaccess on GCC. + std::memcpy(static_cast(&to), &from, sizeof(to)); return to; } @@ -315,51 +361,113 @@ inline auto is_big_endian() -> bool { class uint128_fallback { private: uint64_t lo_, hi_; - constexpr uint128_fallback(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {} + + friend uint128_fallback umul128(uint64_t x, uint64_t y) noexcept; public: + constexpr uint128_fallback(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {} constexpr uint128_fallback(uint64_t value = 0) : lo_(value), hi_(0) {} + constexpr uint64_t high() const noexcept { return hi_; } + constexpr uint64_t low() const noexcept { return lo_; } + template ::value)> constexpr explicit operator T() const { return static_cast(lo_); } - friend auto operator==(const uint128_fallback& lhs, - const uint128_fallback& rhs) -> bool { + friend constexpr auto operator==(const uint128_fallback& lhs, + const uint128_fallback& rhs) -> bool { return lhs.hi_ == rhs.hi_ && lhs.lo_ == rhs.lo_; } - friend auto operator!=(const uint128_fallback& lhs, - const uint128_fallback& rhs) -> bool { + friend constexpr auto operator!=(const uint128_fallback& lhs, + const uint128_fallback& rhs) -> bool { return !(lhs == rhs); } - friend auto operator|(const uint128_fallback& lhs, - const uint128_fallback& rhs) -> uint128_fallback { + friend constexpr auto operator>(const uint128_fallback& lhs, + const uint128_fallback& rhs) -> bool { + return lhs.hi_ != rhs.hi_ ? lhs.hi_ > rhs.hi_ : lhs.lo_ > rhs.lo_; + } + friend constexpr auto operator|(const uint128_fallback& lhs, + const uint128_fallback& rhs) + -> uint128_fallback { return {lhs.hi_ | rhs.hi_, lhs.lo_ | rhs.lo_}; } - friend auto operator&(const uint128_fallback& lhs, - const uint128_fallback& rhs) -> uint128_fallback { + friend constexpr auto operator&(const uint128_fallback& lhs, + const uint128_fallback& rhs) + -> uint128_fallback { return {lhs.hi_ & rhs.hi_, lhs.lo_ & rhs.lo_}; } + friend constexpr auto operator~(const uint128_fallback& n) + -> uint128_fallback { + return {~n.hi_, ~n.lo_}; + } + friend auto operator+(const uint128_fallback& lhs, + const uint128_fallback& rhs) -> uint128_fallback { + auto result = uint128_fallback(lhs); + result += rhs; + return result; + } + friend auto operator*(const uint128_fallback& lhs, uint32_t rhs) + -> uint128_fallback { + FMT_ASSERT(lhs.hi_ == 0, ""); + uint64_t hi = (lhs.lo_ >> 32) * rhs; + uint64_t lo = (lhs.lo_ & ~uint32_t()) * rhs; + uint64_t new_lo = (hi << 32) + lo; + return {(hi >> 32) + (new_lo < lo ? 1 : 0), new_lo}; + } friend auto operator-(const uint128_fallback& lhs, uint64_t rhs) -> uint128_fallback { - FMT_ASSERT(lhs.lo_ >= rhs, ""); - return {lhs.hi_, lhs.lo_ - rhs}; + return {lhs.hi_ - (lhs.lo_ < rhs ? 1 : 0), lhs.lo_ - rhs}; } FMT_CONSTEXPR auto operator>>(int shift) const -> uint128_fallback { if (shift == 64) return {0, hi_}; + if (shift > 64) return uint128_fallback(0, hi_) >> (shift - 64); return {hi_ >> shift, (hi_ << (64 - shift)) | (lo_ >> shift)}; } FMT_CONSTEXPR auto operator<<(int shift) const -> uint128_fallback { if (shift == 64) return {lo_, 0}; + if (shift > 64) return uint128_fallback(lo_, 0) << (shift - 64); return {hi_ << shift | (lo_ >> (64 - shift)), (lo_ << shift)}; } FMT_CONSTEXPR auto operator>>=(int shift) -> uint128_fallback& { return *this = *this >> shift; } - FMT_CONSTEXPR void operator+=(uint64_t n) { + FMT_CONSTEXPR void operator+=(uint128_fallback n) { + uint64_t new_lo = lo_ + n.lo_; + uint64_t new_hi = hi_ + n.hi_ + (new_lo < lo_ ? 1 : 0); + FMT_ASSERT(new_hi >= hi_, ""); + lo_ = new_lo; + hi_ = new_hi; + } + FMT_CONSTEXPR void operator&=(uint128_fallback n) { + lo_ &= n.lo_; + hi_ &= n.hi_; + } + + FMT_CONSTEXPR20 uint128_fallback& operator+=(uint64_t n) noexcept { + if (is_constant_evaluated()) { + lo_ += n; + hi_ += (lo_ < n ? 1 : 0); + return *this; + } +#if FMT_HAS_BUILTIN(__builtin_addcll) && !defined(__ibmxl__) + unsigned long long carry; + lo_ = __builtin_addcll(lo_, n, 0, &carry); + hi_ += carry; +#elif FMT_HAS_BUILTIN(__builtin_ia32_addcarryx_u64) && !defined(__ibmxl__) + unsigned long long result; + auto carry = __builtin_ia32_addcarryx_u64(0, lo_, n, &result); + lo_ = result; + hi_ += carry; +#elif defined(_MSC_VER) && defined(_M_X64) + auto carry = _addcarry_u64(0, lo_, n, &lo_); + _addcarry_u64(carry, hi_, 0, &hi_); +#else lo_ += n; - if (lo_ < n) ++hi_; + hi_ += (lo_ < n ? 1 : 0); +#endif + return *this; } }; @@ -402,6 +510,28 @@ inline auto bit_cast(const From& from) -> To { return result; } +template +FMT_CONSTEXPR20 inline auto countl_zero_fallback(UInt n) -> int { + int lz = 0; + constexpr UInt msb_mask = static_cast(1) << (num_bits() - 1); + for (; (n & msb_mask) == 0; n <<= 1) lz++; + return lz; +} + +FMT_CONSTEXPR20 inline auto countl_zero(uint32_t n) -> int { +#ifdef FMT_BUILTIN_CLZ + if (!is_constant_evaluated()) return FMT_BUILTIN_CLZ(n); +#endif + return countl_zero_fallback(n); +} + +FMT_CONSTEXPR20 inline auto countl_zero(uint64_t n) -> int { +#ifdef FMT_BUILTIN_CLZLL + if (!is_constant_evaluated()) return FMT_BUILTIN_CLZLL(n); +#endif + return countl_zero_fallback(n); +} + FMT_INLINE void assume(bool condition) { (void)condition; #if FMT_HAS_BUILTIN(__builtin_assume) && !FMT_ICC_VERSION @@ -546,19 +676,24 @@ FMT_CONSTEXPR inline auto utf8_decode(const char* s, uint32_t* c, int* e) constexpr const int shiftc[] = {0, 18, 12, 6, 0}; constexpr const int shifte[] = {0, 6, 4, 2, 0}; - int len = code_point_length(s); - const char* next = s + len; + int len = "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4" + [static_cast(*s) >> 3]; + // Compute the pointer to the next character early so that the next + // iteration can start working on the next character. Neither Clang + // nor GCC figure out this reordering on their own. + const char* next = s + len + !len; + + using uchar = unsigned char; // Assume a four-byte character and load four bytes. Unused bits are // shifted out. - *c = uint32_t(s[0] & masks[len]) << 18; - *c |= uint32_t(s[1] & 0x3f) << 12; - *c |= uint32_t(s[2] & 0x3f) << 6; - *c |= uint32_t(s[3] & 0x3f) << 0; + *c = uint32_t(uchar(s[0]) & masks[len]) << 18; + *c |= uint32_t(uchar(s[1]) & 0x3f) << 12; + *c |= uint32_t(uchar(s[2]) & 0x3f) << 6; + *c |= uint32_t(uchar(s[3]) & 0x3f) << 0; *c >>= shiftc[len]; // Accumulate the various error conditions. - using uchar = unsigned char; *e = (*c < mins[len]) << 6; // non-canonical encoding *e |= ((*c >> 11) == 0x1b) << 7; // surrogate half? *e |= (*c > 0x10FFFF) << 8; // out of range? @@ -571,7 +706,7 @@ FMT_CONSTEXPR inline auto utf8_decode(const char* s, uint32_t* c, int* e) return next; } -constexpr uint32_t invalid_code_point = ~uint32_t(); +constexpr FMT_INLINE_VARIABLE uint32_t invalid_code_point = ~uint32_t(); // Invokes f(cp, sv) for every code point cp in s with sv being the string view // corresponding to the code point. cp is invalid_code_point on error. @@ -582,8 +717,8 @@ FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) { auto error = 0; auto end = utf8_decode(buf_ptr, &cp, &error); bool result = f(error ? invalid_code_point : cp, - string_view(ptr, to_unsigned(end - buf_ptr))); - return result ? end : nullptr; + string_view(ptr, error ? 1 : to_unsigned(end - buf_ptr))); + return result ? (error ? buf_ptr + 1 : end) : nullptr; }; auto p = s.data(); const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars. @@ -641,13 +776,14 @@ FMT_CONSTEXPR inline size_t compute_width(string_view s) { return true; } }; + // We could avoid branches by using utf8_decode directly. for_each_codepoint(s, count_code_points{&num_code_points}); return num_code_points; } inline auto compute_width(basic_string_view s) -> size_t { - return compute_width(basic_string_view( - reinterpret_cast(s.data()), s.size())); + return compute_width( + string_view(reinterpret_cast(s.data()), s.size())); } template @@ -657,9 +793,8 @@ inline auto code_point_index(basic_string_view s, size_t n) -> size_t { } // Calculates the index of the nth code point in a UTF-8 string. -inline auto code_point_index(basic_string_view s, size_t n) - -> size_t { - const char8_type* data = s.data(); +inline auto code_point_index(string_view s, size_t n) -> size_t { + const char* data = s.data(); size_t num_code_points = 0; for (size_t i = 0, size = s.size(); i != size; ++i) { if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) return i; @@ -667,7 +802,55 @@ inline auto code_point_index(basic_string_view s, size_t n) return s.size(); } -#ifdef __SIZEOF_FLOAT128__ +inline auto code_point_index(basic_string_view s, size_t n) + -> size_t { + return code_point_index( + string_view(reinterpret_cast(s.data()), s.size()), n); +} + +template struct is_integral : std::is_integral {}; +template <> struct is_integral : std::true_type {}; +template <> struct is_integral : std::true_type {}; + +template +using is_signed = + std::integral_constant::is_signed || + std::is_same::value>; + +template +using is_integer = + bool_constant::value && !std::is_same::value && + !std::is_same::value && + !std::is_same::value>; + +#ifndef FMT_USE_FLOAT +# define FMT_USE_FLOAT 1 +#endif +#ifndef FMT_USE_DOUBLE +# define FMT_USE_DOUBLE 1 +#endif +#ifndef FMT_USE_LONG_DOUBLE +# define FMT_USE_LONG_DOUBLE 1 +#endif + +#ifndef FMT_USE_FLOAT128 +# ifdef __clang__ +// Clang emulates GCC, so it has to appear early. +# if FMT_HAS_INCLUDE() +# define FMT_USE_FLOAT128 1 +# endif +# elif defined(__GNUC__) +// GNU C++: +# if defined(_GLIBCXX_USE_FLOAT128) && !defined(__STRICT_ANSI__) +# define FMT_USE_FLOAT128 1 +# endif +# endif +# ifndef FMT_USE_FLOAT128 +# define FMT_USE_FLOAT128 0 +# endif +#endif + +#if FMT_USE_FLOAT128 using float128 = __float128; #else using float128 = void; @@ -683,6 +866,9 @@ struct is_fast_float : bool_constant::is_iec559 && sizeof(T) <= sizeof(double)> {}; template struct is_fast_float : std::false_type {}; +template +using is_double_double = bool_constant::digits == 106>; + #ifndef FMT_USE_FULL_CACHE_DRAGONBOX # define FMT_USE_FULL_CACHE_DRAGONBOX 0 #endif @@ -707,7 +893,7 @@ template struct is_locale> : std::true_type {}; } // namespace detail -FMT_MODULE_EXPORT_BEGIN +FMT_BEGIN_EXPORT // The number of characters to store in the basic_memory_buffer object itself // to avoid dynamic memory allocation. @@ -750,7 +936,27 @@ class basic_memory_buffer final : public detail::buffer { } protected: - FMT_CONSTEXPR20 void grow(size_t size) override; + FMT_CONSTEXPR20 void grow(size_t size) override { + detail::abort_fuzzing_if(size > 5000); + const size_t max_size = std::allocator_traits::max_size(alloc_); + size_t old_capacity = this->capacity(); + size_t new_capacity = old_capacity + old_capacity / 2; + if (size > new_capacity) + new_capacity = size; + else if (new_capacity > max_size) + new_capacity = size > max_size ? size : max_size; + T* old_data = this->data(); + T* new_data = + std::allocator_traits::allocate(alloc_, new_capacity); + // The following code doesn't throw, so the raw pointer above doesn't leak. + std::uninitialized_copy(old_data, old_data + this->size(), + detail::make_checked(new_data, new_capacity)); + this->set(new_data, new_capacity); + // deallocate must not throw according to the standard, but even if it does, + // the buffer already uses the new storage and will deallocate it in + // destructor. + if (old_data != store_) alloc_.deallocate(old_data, old_capacity); + } public: using value_type = T; @@ -827,79 +1033,32 @@ class basic_memory_buffer final : public detail::buffer { } }; -template -FMT_CONSTEXPR20 void basic_memory_buffer::grow( - size_t size) { -#ifdef FMT_FUZZ - if (size > 5000) throw std::runtime_error("fuzz mode - won't grow that much"); -#endif - const size_t max_size = std::allocator_traits::max_size(alloc_); - size_t old_capacity = this->capacity(); - size_t new_capacity = old_capacity + old_capacity / 2; - if (size > new_capacity) - new_capacity = size; - else if (new_capacity > max_size) - new_capacity = size > max_size ? size : max_size; - T* old_data = this->data(); - T* new_data = - std::allocator_traits::allocate(alloc_, new_capacity); - // The following code doesn't throw, so the raw pointer above doesn't leak. - std::uninitialized_copy(old_data, old_data + this->size(), - detail::make_checked(new_data, new_capacity)); - this->set(new_data, new_capacity); - // deallocate must not throw according to the standard, but even if it does, - // the buffer already uses the new storage and will deallocate it in - // destructor. - if (old_data != store_) alloc_.deallocate(old_data, old_capacity); -} - using memory_buffer = basic_memory_buffer; template struct is_contiguous> : std::true_type { }; +FMT_END_EXPORT namespace detail { +FMT_API bool write_console(std::FILE* f, string_view text); FMT_API void print(std::FILE*, string_view); -} +} // namespace detail +FMT_BEGIN_EXPORT -/** A formatting error such as invalid format string. */ -FMT_CLASS_API +// Suppress a misleading warning in older versions of clang. +#if FMT_CLANG_VERSION +# pragma clang diagnostic ignored "-Wweak-vtables" +#endif + +/** An error reported from a formatting function. */ class FMT_API format_error : public std::runtime_error { public: - explicit format_error(const char* message) : std::runtime_error(message) {} - explicit format_error(const std::string& message) - : std::runtime_error(message) {} - format_error(const format_error&) = default; - format_error& operator=(const format_error&) = default; - format_error(format_error&&) = default; - format_error& operator=(format_error&&) = default; - ~format_error() noexcept override FMT_MSC_DEFAULT; + using std::runtime_error::runtime_error; }; -/** - \rst - Constructs a `~fmt::format_arg_store` object that contains references - to arguments and can be implicitly converted to `~fmt::format_args`. - If ``fmt`` is a compile-time string then `make_args_checked` checks - its validity at compile time. - \endrst - */ -template > -FMT_DEPRECATED FMT_INLINE auto make_args_checked( - const S& fmt, const remove_reference_t&... args) - -> format_arg_store, remove_reference_t...> { - static_assert( - detail::count<( - std::is_base_of>::value && - std::is_reference::value)...>() == 0, - "passing views as lvalues is disallowed"); - detail::check_format_string(fmt); - return {args...}; -} - namespace detail_exported { -#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +#if FMT_USE_NONTYPE_TEMPLATE_ARGS template struct fixed_string { constexpr fixed_string(const Char (&str)[N]) { detail::copy_str(static_cast(str), @@ -924,33 +1083,70 @@ constexpr auto compile_string_to_view(detail::std_string_view s) } } // namespace detail_exported +class loc_value { + private: + basic_format_arg value_; + + public: + template ::value)> + loc_value(T value) : value_(detail::make_arg(value)) {} + + template ::value)> + loc_value(T) {} + + template auto visit(Visitor&& vis) -> decltype(vis(0)) { + return visit_format_arg(vis, value_); + } +}; + +// A locale facet that formats values in UTF-8. +// It is parameterized on the locale to avoid the heavy include. +template class format_facet : public Locale::facet { + private: + std::string separator_; + std::string grouping_; + std::string decimal_point_; + + protected: + virtual auto do_put(appender out, loc_value val, + const format_specs<>& specs) const -> bool; + + public: + static FMT_API typename Locale::id id; + + explicit format_facet(Locale& loc); + explicit format_facet(string_view sep = "", + std::initializer_list g = {3}, + std::string decimal_point = ".") + : separator_(sep.data(), sep.size()), + grouping_(g.begin(), g.end()), + decimal_point_(decimal_point) {} + + auto put(appender out, loc_value val, const format_specs<>& specs) const + -> bool { + return do_put(out, val, specs); + } +}; + FMT_BEGIN_DETAIL_NAMESPACE -template struct is_integral : std::is_integral {}; -template <> struct is_integral : std::true_type {}; -template <> struct is_integral : std::true_type {}; - -template -using is_signed = - std::integral_constant::is_signed || - std::is_same::value>; - // Returns true if value is negative, false otherwise. // Same as `value < 0` but doesn't produce warnings if T is an unsigned type. template ::value)> -FMT_CONSTEXPR auto is_negative(T value) -> bool { +constexpr auto is_negative(T value) -> bool { return value < 0; } template ::value)> -FMT_CONSTEXPR auto is_negative(T) -> bool { +constexpr auto is_negative(T) -> bool { return false; } template -FMT_CONSTEXPR auto is_supported_floating_point(T) -> uint16_t { - return (std::is_same::value && FMT_USE_FLOAT) || - (std::is_same::value && FMT_USE_DOUBLE) || - (std::is_same::value && FMT_USE_LONG_DOUBLE); +FMT_CONSTEXPR auto is_supported_floating_point(T) -> bool { + if (std::is_same()) return FMT_USE_FLOAT; + if (std::is_same()) return FMT_USE_DOUBLE; + if (std::is_same()) return FMT_USE_LONG_DOUBLE; + return true; } // Smallest of uint32_t, uint64_t, uint128_t that is large enough to @@ -959,10 +1155,9 @@ template using uint32_or_64_or_128_t = conditional_t() <= 32 && !FMT_REDUCE_INT_INSTANTIATIONS, uint32_t, - conditional_t() <= 64, uint64_t, uint128_opt>>; + conditional_t() <= 64, uint64_t, uint128_t>>; template -using uint64_or_128_t = - conditional_t() <= 64, uint64_t, uint128_opt>; +using uint64_or_128_t = conditional_t() <= 64, uint64_t, uint128_t>; #define FMT_POWERS_OF_10(factor) \ factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \ @@ -1175,10 +1370,10 @@ FMT_CONSTEXPR20 auto format_decimal(Char* out, UInt value, int size) template >::value)> -inline auto format_decimal(Iterator out, UInt value, int size) +FMT_CONSTEXPR inline auto format_decimal(Iterator out, UInt value, int size) -> format_decimal_result { // Buffer is large enough to hold all digits (digits10 + 1). - Char buffer[digits10() + 1]; + Char buffer[digits10() + 1] = {}; auto end = format_decimal(buffer, value, size).end; return {out, detail::copy_str_noinline(buffer, end, out)}; } @@ -1223,7 +1418,133 @@ class utf8_to_utf16 { auto str() const -> std::wstring { return {&buffer_[0], size()}; } }; +// A converter from UTF-16/UTF-32 (host endian) to UTF-8. +template +class unicode_to_utf8 { + private: + Buffer buffer_; + + public: + unicode_to_utf8() {} + explicit unicode_to_utf8(basic_string_view s) { + static_assert(sizeof(WChar) == 2 || sizeof(WChar) == 4, + "Expect utf16 or utf32"); + + if (!convert(s)) + FMT_THROW(std::runtime_error(sizeof(WChar) == 2 ? "invalid utf16" + : "invalid utf32")); + } + operator string_view() const { return string_view(&buffer_[0], size()); } + size_t size() const { return buffer_.size() - 1; } + const char* c_str() const { return &buffer_[0]; } + std::string str() const { return std::string(&buffer_[0], size()); } + + // Performs conversion returning a bool instead of throwing exception on + // conversion error. This method may still throw in case of memory allocation + // error. + bool convert(basic_string_view s) { + if (!convert(buffer_, s)) return false; + buffer_.push_back(0); + return true; + } + static bool convert(Buffer& buf, basic_string_view s) { + for (auto p = s.begin(); p != s.end(); ++p) { + uint32_t c = static_cast(*p); + if (sizeof(WChar) == 2 && c >= 0xd800 && c <= 0xdfff) { + // surrogate pair + ++p; + if (p == s.end() || (c & 0xfc00) != 0xd800 || (*p & 0xfc00) != 0xdc00) { + return false; + } + c = (c << 10) + static_cast(*p) - 0x35fdc00; + } + if (c < 0x80) { + buf.push_back(static_cast(c)); + } else if (c < 0x800) { + buf.push_back(static_cast(0xc0 | (c >> 6))); + buf.push_back(static_cast(0x80 | (c & 0x3f))); + } else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) { + buf.push_back(static_cast(0xe0 | (c >> 12))); + buf.push_back(static_cast(0x80 | ((c & 0xfff) >> 6))); + buf.push_back(static_cast(0x80 | (c & 0x3f))); + } else if (c >= 0x10000 && c <= 0x10ffff) { + buf.push_back(static_cast(0xf0 | (c >> 18))); + buf.push_back(static_cast(0x80 | ((c & 0x3ffff) >> 12))); + buf.push_back(static_cast(0x80 | ((c & 0xfff) >> 6))); + buf.push_back(static_cast(0x80 | (c & 0x3f))); + } else { + return false; + } + } + return true; + } +}; + +// Computes 128-bit result of multiplication of two 64-bit unsigned integers. +inline uint128_fallback umul128(uint64_t x, uint64_t y) noexcept { +#if FMT_USE_INT128 + auto p = static_cast(x) * static_cast(y); + return {static_cast(p >> 64), static_cast(p)}; +#elif defined(_MSC_VER) && defined(_M_X64) + auto result = uint128_fallback(); + result.lo_ = _umul128(x, y, &result.hi_); + return result; +#else + const uint64_t mask = static_cast(max_value()); + + uint64_t a = x >> 32; + uint64_t b = x & mask; + uint64_t c = y >> 32; + uint64_t d = y & mask; + + uint64_t ac = a * c; + uint64_t bc = b * c; + uint64_t ad = a * d; + uint64_t bd = b * d; + + uint64_t intermediate = (bd >> 32) + (ad & mask) + (bc & mask); + + return {ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32), + (intermediate << 32) + (bd & mask)}; +#endif +} + namespace dragonbox { +// Computes floor(log10(pow(2, e))) for e in [-2620, 2620] using the method from +// https://fmt.dev/papers/Dragonbox.pdf#page=28, section 6.1. +inline int floor_log10_pow2(int e) noexcept { + FMT_ASSERT(e <= 2620 && e >= -2620, "too large exponent"); + static_assert((-1 >> 1) == -1, "right shift is not arithmetic"); + return (e * 315653) >> 20; +} + +inline int floor_log2_pow10(int e) noexcept { + FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent"); + return (e * 1741647) >> 19; +} + +// Computes upper 64 bits of multiplication of two 64-bit unsigned integers. +inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept { +#if FMT_USE_INT128 + auto p = static_cast(x) * static_cast(y); + return static_cast(p >> 64); +#elif defined(_MSC_VER) && defined(_M_X64) + return __umulh(x, y); +#else + return umul128(x, y).high(); +#endif +} + +// Computes upper 128 bits of multiplication of a 64-bit unsigned integer and a +// 128-bit unsigned integer. +inline uint128_fallback umul192_upper128(uint64_t x, + uint128_fallback y) noexcept { + uint128_fallback r = umul128(x, y.high()); + r += umul128_upper64(x, y.low()); + return r; +} + +FMT_API uint128_fallback get_cached_power(int k) noexcept; // Type-specific information that Dragonbox uses. template struct float_info; @@ -1236,8 +1557,6 @@ template <> struct float_info { static const int small_divisor = 10; static const int min_k = -31; static const int max_k = 46; - static const int divisibility_check_by_5_threshold = 39; - static const int case_fc_pm_half_lower_threshold = -1; static const int shorter_interval_tie_lower_threshold = -35; static const int shorter_interval_tie_upper_threshold = -35; }; @@ -1249,9 +1568,7 @@ template <> struct float_info { static const int big_divisor = 1000; static const int small_divisor = 100; static const int min_k = -292; - static const int max_k = 326; - static const int divisibility_check_by_5_threshold = 86; - static const int case_fc_pm_half_lower_threshold = -2; + static const int max_k = 341; static const int shorter_interval_tie_lower_threshold = -77; static const int shorter_interval_tie_upper_threshold = -77; }; @@ -1259,11 +1576,18 @@ template <> struct float_info { // An 80- or 128-bit floating point number. template struct float_info::digits == 64 || - std::numeric_limits::digits == 113>> { + std::numeric_limits::digits == 113 || + is_float128::value>> { using carrier_uint = detail::uint128_t; static const int exponent_bits = 15; }; +// A double-double floating point number. +template +struct float_info::value>> { + using carrier_uint = detail::uint128_t; +}; + template struct decimal_fp { using significand_type = typename float_info::carrier_uint; significand_type significand; @@ -1273,23 +1597,33 @@ template struct decimal_fp { template FMT_API auto to_decimal(T x) noexcept -> decimal_fp; } // namespace dragonbox +// Returns true iff Float has the implicit bit which is not stored. template constexpr bool has_implicit_bit() { + // An 80-bit FP number has a 64-bit significand an no implicit bit. return std::numeric_limits::digits != 64; } -// Returns the number of significand bits in Float excluding the implicit bit. +// Returns the number of significand bits stored in Float. The implicit bit is +// not counted since it is not stored. template constexpr int num_significand_bits() { - return std::numeric_limits::digits - - (has_implicit_bit() ? 1 : 0); + // std::numeric_limits may not support __float128. + return is_float128() ? 112 + : (std::numeric_limits::digits - + (has_implicit_bit() ? 1 : 0)); } template constexpr auto exponent_mask() -> typename dragonbox::float_info::carrier_uint { - using uint = typename dragonbox::float_info::carrier_uint; - return ((uint(1) << dragonbox::float_info::exponent_bits) - 1) + using float_uint = typename dragonbox::float_info::carrier_uint; + return ((float_uint(1) << dragonbox::float_info::exponent_bits) - 1) << num_significand_bits(); } +template constexpr auto exponent_bias() -> int { + // std::numeric_limits may not support __float128. + return is_float128() ? 16383 + : std::numeric_limits::max_exponent - 1; +} // Writes the exponent exp in the form "[+-]d{2,3}" to buffer. template @@ -1313,19 +1647,208 @@ FMT_CONSTEXPR auto write_exponent(int exp, It it) -> It { return it; } -template -FMT_HEADER_ONLY_CONSTEXPR20 auto format_float(T value, int precision, - float_specs specs, - buffer& buf) -> int; +// A floating-point number f * pow(2, e) where F is an unsigned type. +template struct basic_fp { + F f; + int e; -// Formats a floating-point number with snprintf. + static constexpr const int num_significand_bits = + static_cast(sizeof(F) * num_bits()); + + constexpr basic_fp() : f(0), e(0) {} + constexpr basic_fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {} + + // Constructs fp from an IEEE754 floating-point number. + template FMT_CONSTEXPR basic_fp(Float n) { assign(n); } + + // Assigns n to this and return true iff predecessor is closer than successor. + template ::value)> + FMT_CONSTEXPR auto assign(Float n) -> bool { + static_assert(std::numeric_limits::digits <= 113, "unsupported FP"); + // Assume Float is in the format [sign][exponent][significand]. + using carrier_uint = typename dragonbox::float_info::carrier_uint; + const auto num_float_significand_bits = + detail::num_significand_bits(); + const auto implicit_bit = carrier_uint(1) << num_float_significand_bits; + const auto significand_mask = implicit_bit - 1; + auto u = bit_cast(n); + f = static_cast(u & significand_mask); + auto biased_e = static_cast((u & exponent_mask()) >> + num_float_significand_bits); + // The predecessor is closer if n is a normalized power of 2 (f == 0) + // other than the smallest normalized number (biased_e > 1). + auto is_predecessor_closer = f == 0 && biased_e > 1; + if (biased_e == 0) + biased_e = 1; // Subnormals use biased exponent 1 (min exponent). + else if (has_implicit_bit()) + f += static_cast(implicit_bit); + e = biased_e - exponent_bias() - num_float_significand_bits; + if (!has_implicit_bit()) ++e; + return is_predecessor_closer; + } + + template ::value)> + FMT_CONSTEXPR auto assign(Float n) -> bool { + static_assert(std::numeric_limits::is_iec559, "unsupported FP"); + return assign(static_cast(n)); + } +}; + +using fp = basic_fp; + +// Normalizes the value converted from double and multiplied by (1 << SHIFT). +template +FMT_CONSTEXPR basic_fp normalize(basic_fp value) { + // Handle subnormals. + const auto implicit_bit = F(1) << num_significand_bits(); + const auto shifted_implicit_bit = implicit_bit << SHIFT; + while ((value.f & shifted_implicit_bit) == 0) { + value.f <<= 1; + --value.e; + } + // Subtract 1 to account for hidden bit. + const auto offset = basic_fp::num_significand_bits - + num_significand_bits() - SHIFT - 1; + value.f <<= offset; + value.e -= offset; + return value; +} + +// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking. +FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs) { +#if FMT_USE_INT128 + auto product = static_cast<__uint128_t>(lhs) * rhs; + auto f = static_cast(product >> 64); + return (static_cast(product) & (1ULL << 63)) != 0 ? f + 1 : f; +#else + // Multiply 32-bit parts of significands. + uint64_t mask = (1ULL << 32) - 1; + uint64_t a = lhs >> 32, b = lhs & mask; + uint64_t c = rhs >> 32, d = rhs & mask; + uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d; + // Compute mid 64-bit of result and round. + uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31); + return ac + (ad >> 32) + (bc >> 32) + (mid >> 32); +#endif +} + +FMT_CONSTEXPR inline fp operator*(fp x, fp y) { + return {multiply(x.f, y.f), x.e + y.e + 64}; +} + +template struct basic_data { + // Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340. + // These are generated by support/compute-powers.py. + static constexpr uint64_t pow10_significands[87] = { + 0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76, + 0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df, + 0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c, + 0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5, + 0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57, + 0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7, + 0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e, + 0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996, + 0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126, + 0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053, + 0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f, + 0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b, + 0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06, + 0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb, + 0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000, + 0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984, + 0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068, + 0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8, + 0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758, + 0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85, + 0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d, + 0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25, + 0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2, + 0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a, + 0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410, + 0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129, + 0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85, + 0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841, + 0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b, + }; + +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wnarrowing" +#endif + // Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding + // to significands above. + static constexpr int16_t pow10_exponents[87] = { + -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954, + -927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661, + -635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369, + -343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77, + -50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216, + 242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508, + 534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800, + 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066}; +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 +# pragma GCC diagnostic pop +#endif + + static constexpr uint64_t power_of_10_64[20] = { + 1, FMT_POWERS_OF_10(1ULL), FMT_POWERS_OF_10(1000000000ULL), + 10000000000000000000ULL}; + + // For checking rounding thresholds. + // The kth entry is chosen to be the smallest integer such that the + // upper 32-bits of 10^(k+1) times it is strictly bigger than 5 * 10^k. + static constexpr uint32_t fractional_part_rounding_thresholds[8] = { + 2576980378, // ceil(2^31 + 2^32/10^1) + 2190433321, // ceil(2^31 + 2^32/10^2) + 2151778616, // ceil(2^31 + 2^32/10^3) + 2147913145, // ceil(2^31 + 2^32/10^4) + 2147526598, // ceil(2^31 + 2^32/10^5) + 2147487943, // ceil(2^31 + 2^32/10^6) + 2147484078, // ceil(2^31 + 2^32/10^7) + 2147483691 // ceil(2^31 + 2^32/10^8) + }; +}; + +#if FMT_CPLUSPLUS < 201703L +template constexpr uint64_t basic_data::pow10_significands[]; +template constexpr int16_t basic_data::pow10_exponents[]; +template constexpr uint64_t basic_data::power_of_10_64[]; template -auto snprintf_float(T value, int precision, float_specs specs, - buffer& buf) -> int; +constexpr uint32_t basic_data::fractional_part_rounding_thresholds[]; +#endif + +// This is a struct rather than an alias to avoid shadowing warnings in gcc. +struct data : basic_data<> {}; + +// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its +// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`. +FMT_CONSTEXPR inline fp get_cached_power(int min_exponent, + int& pow10_exponent) { + const int shift = 32; + // log10(2) = 0x0.4d104d427de7fbcc... + const int64_t significand = 0x4d104d427de7fbcc; + int index = static_cast( + ((min_exponent + fp::num_significand_bits - 1) * (significand >> shift) + + ((int64_t(1) << shift) - 1)) // ceil + >> 32 // arithmetic shift + ); + // Decimal exponent of the first (smallest) cached power of 10. + const int first_dec_exp = -348; + // Difference between 2 consecutive decimal exponents in cached powers of 10. + const int dec_exp_step = 8; + index = (index - first_dec_exp - 1) / dec_exp_step + 1; + pow10_exponent = first_dec_exp + index * dec_exp_step; + // Using *(x + index) instead of x[index] avoids an issue with some compilers + // using the EDG frontend (e.g. nvhpc/22.3 in C++17 mode). + return {*(data::pow10_significands + index), + *(data::pow10_exponents + index)}; +} template using convert_float_result = - conditional_t::value || sizeof(T) == sizeof(double), + conditional_t::value || + std::numeric_limits::digits == + std::numeric_limits::digits, double, T>; template @@ -1349,8 +1872,7 @@ FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n, // width: output display width in (terminal) column positions. template -FMT_CONSTEXPR auto write_padded(OutputIt out, - const basic_format_specs& specs, +FMT_CONSTEXPR auto write_padded(OutputIt out, const format_specs& specs, size_t size, size_t width, F&& f) -> OutputIt { static_assert(align == align::left || align == align::right, ""); unsigned spec_width = to_unsigned(specs.width); @@ -1369,15 +1891,14 @@ FMT_CONSTEXPR auto write_padded(OutputIt out, template -constexpr auto write_padded(OutputIt out, const basic_format_specs& specs, +constexpr auto write_padded(OutputIt out, const format_specs& specs, size_t size, F&& f) -> OutputIt { return write_padded(out, specs, size, size, f); } template FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes, - const basic_format_specs& specs) - -> OutputIt { + const format_specs& specs) -> OutputIt { return write_padded( out, specs, bytes.size(), [bytes](reserve_iterator it) { const char* data = bytes.data(); @@ -1386,8 +1907,8 @@ FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes, } template -auto write_ptr(OutputIt out, UIntPtr value, - const basic_format_specs* specs) -> OutputIt { +auto write_ptr(OutputIt out, UIntPtr value, const format_specs* specs) + -> OutputIt { int num_digits = count_digits<4>(value); auto size = to_unsigned(num_digits) + size_t(2); auto write = [=](reserve_iterator it) { @@ -1424,7 +1945,7 @@ auto find_escape(const Char* begin, const Char* end) -> find_escape_result { for (; begin != end; ++begin) { uint32_t cp = static_cast>(*begin); - if (sizeof(Char) == 1 && cp >= 0x80) continue; + if (const_check(sizeof(Char) == 1) && cp >= 0x80) continue; if (needs_escape(cp)) return {begin, begin + 1, cp}; } return {begin, nullptr, 0}; @@ -1445,18 +1966,18 @@ inline auto find_escape(const char* begin, const char* end) return result; } -#define FMT_STRING_IMPL(s, base, explicit) \ - [] { \ - /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \ - /* Use a macro-like name to avoid shadowing warnings. */ \ - struct FMT_GCC_VISIBILITY_HIDDEN FMT_COMPILE_STRING : base { \ - using char_type = fmt::remove_cvref_t; \ - FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit \ - operator fmt::basic_string_view() const { \ - return fmt::detail_exported::compile_string_to_view(s); \ - } \ - }; \ - return FMT_COMPILE_STRING(); \ +#define FMT_STRING_IMPL(s, base, explicit) \ + [] { \ + /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \ + /* Use a macro-like name to avoid shadowing warnings. */ \ + struct FMT_GCC_VISIBILITY_HIDDEN FMT_COMPILE_STRING : base { \ + using char_type FMT_MAYBE_UNUSED = fmt::remove_cvref_t; \ + FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit \ + operator fmt::basic_string_view() const { \ + return fmt::detail_exported::compile_string_to_view(s); \ + } \ + }; \ + return FMT_COMPILE_STRING(); \ }() /** @@ -1469,12 +1990,16 @@ inline auto find_escape(const char* begin, const char* end) std::string s = fmt::format(FMT_STRING("{:d}"), "foo"); \endrst */ -#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::compile_string, ) +#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string, ) -template -auto write_escaped_string(OutputIt out, basic_string_view str) - -> OutputIt { - return copy_str(str.data(), str.data() + str.size(), out); +template +auto write_codepoint(OutputIt out, char prefix, uint32_t cp) -> OutputIt { + *out++ = static_cast('\\'); + *out++ = static_cast(prefix); + Char buf[width]; + fill_n(buf, width, static_cast('0')); + format_uint<4>(buf, cp, width); + return copy_str(buf, buf + width, out); } template @@ -1483,40 +2008,38 @@ auto write_escaped_cp(OutputIt out, const find_escape_result& escape) auto c = static_cast(escape.cp); switch (escape.cp) { case '\n': - *out++ = '\\'; - c = 'n'; + *out++ = static_cast('\\'); + c = static_cast('n'); break; case '\r': - *out++ = '\\'; - c = 'r'; + *out++ = static_cast('\\'); + c = static_cast('r'); break; case '\t': - *out++ = '\\'; - c = 't'; + *out++ = static_cast('\\'); + c = static_cast('t'); break; case '"': FMT_FALLTHROUGH; case '\'': FMT_FALLTHROUGH; case '\\': - *out++ = '\\'; + *out++ = static_cast('\\'); break; default: - if (is_utf8()) { - if (escape.cp < 0x100) { - return format_to(out, FMT_STRING("\\x{:02x}"), escape.cp); - } - if (escape.cp < 0x10000) { - return format_to(out, FMT_STRING("\\u{:04x}"), escape.cp); - } - if (escape.cp < 0x110000) { - return format_to(out, FMT_STRING("\\U{:08x}"), escape.cp); - } + if (escape.cp < 0x100) { + return write_codepoint<2, Char>(out, 'x', escape.cp); } - for (char escape_char : basic_string_view( + if (escape.cp < 0x10000) { + return write_codepoint<4, Char>(out, 'u', escape.cp); + } + if (escape.cp < 0x110000) { + return write_codepoint<8, Char>(out, 'U', escape.cp); + } + for (Char escape_char : basic_string_view( escape.begin, to_unsigned(escape.end - escape.begin))) { - out = format_to(out, FMT_STRING("\\x{:02x}"), - static_cast>(escape_char)); + out = write_codepoint<2, Char>(out, 'x', + static_cast(escape_char) & 0xFF); } return out; } @@ -1524,45 +2047,39 @@ auto write_escaped_cp(OutputIt out, const find_escape_result& escape) return out; } -template -auto write_escaped_string(OutputIt out, basic_string_view str) +template +auto write_escaped_string(OutputIt out, basic_string_view str) -> OutputIt { - *out++ = '"'; + *out++ = static_cast('"'); auto begin = str.begin(), end = str.end(); do { auto escape = find_escape(begin, end); - out = copy_str(begin, escape.begin, out); + out = copy_str(begin, escape.begin, out); begin = escape.end; if (!begin) break; - out = write_escaped_cp(out, escape); + out = write_escaped_cp(out, escape); } while (begin != end); - *out++ = '"'; + *out++ = static_cast('"'); return out; } template auto write_escaped_char(OutputIt out, Char v) -> OutputIt { - *out++ = v; - return out; -} - -template -auto write_escaped_char(OutputIt out, char v) -> OutputIt { - *out++ = '\''; - if ((needs_escape(static_cast(v)) && v != '"') || v == '\'') { + *out++ = static_cast('\''); + if ((needs_escape(static_cast(v)) && v != static_cast('"')) || + v == static_cast('\'')) { out = write_escaped_cp( - out, find_escape_result{&v, &v + 1, static_cast(v)}); + out, find_escape_result{&v, &v + 1, static_cast(v)}); } else { *out++ = v; } - *out++ = '\''; + *out++ = static_cast('\''); return out; } template FMT_CONSTEXPR auto write_char(OutputIt out, Char value, - const basic_format_specs& specs) - -> OutputIt { + const format_specs& specs) -> OutputIt { bool is_debug = specs.type == presentation_type::debug; return write_padded(out, specs, 1, [=](reserve_iterator it) { if (is_debug) return write_escaped_char(it, value); @@ -1572,11 +2089,14 @@ FMT_CONSTEXPR auto write_char(OutputIt out, Char value, } template FMT_CONSTEXPR auto write(OutputIt out, Char value, - const basic_format_specs& specs, - locale_ref loc = {}) -> OutputIt { + const format_specs& specs, locale_ref loc = {}) + -> OutputIt { + // char is formatted as unsigned char for consistency across platforms. + using unsigned_type = + conditional_t::value, unsigned char, unsigned>; return check_char_specs(specs) ? write_char(out, value, specs) - : write(out, static_cast(value), specs, loc); + : write(out, static_cast(value), specs, loc); } // Data for write_int that doesn't depend on output iterator type. It is used to @@ -1586,7 +2106,7 @@ template struct write_int_data { size_t padding; FMT_CONSTEXPR write_int_data(int num_digits, unsigned prefix, - const basic_format_specs& specs) + const format_specs& specs) : size((prefix >> 24) + to_unsigned(num_digits)), padding(0) { if (specs.align == align::numeric) { auto width = to_unsigned(specs.width); @@ -1608,7 +2128,7 @@ template struct write_int_data { template FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits, unsigned prefix, - const basic_format_specs& specs, + const format_specs& specs, W write_digits) -> OutputIt { // Slightly faster check for specs.width == 0 && specs.precision == -1. if ((specs.width | (specs.precision + 1)) == 0) { @@ -1631,19 +2151,19 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits, template class digit_grouping { private: - thousands_sep_result sep_; + std::string grouping_; + std::basic_string thousands_sep_; struct next_state { std::string::const_iterator group; int pos; }; - next_state initial_state() const { return {sep_.grouping.begin(), 0}; } + next_state initial_state() const { return {grouping_.begin(), 0}; } // Returns the next digit group separator position. int next(next_state& state) const { - if (!sep_.thousands_sep) return max_value(); - if (state.group == sep_.grouping.end()) - return state.pos += sep_.grouping.back(); + if (thousands_sep_.empty()) return max_value(); + if (state.group == grouping_.end()) return state.pos += grouping_.back(); if (*state.group <= 0 || *state.group == max_value()) return max_value(); state.pos += *state.group++; @@ -1652,14 +2172,15 @@ template class digit_grouping { public: explicit digit_grouping(locale_ref loc, bool localized = true) { - if (localized) - sep_ = thousands_sep(loc); - else - sep_.thousands_sep = Char(); + if (!localized) return; + auto sep = thousands_sep(loc); + grouping_ = sep.grouping; + if (sep.thousands_sep) thousands_sep_.assign(1, sep.thousands_sep); } - explicit digit_grouping(thousands_sep_result sep) : sep_(sep) {} + digit_grouping(std::string grouping, std::basic_string sep) + : grouping_(std::move(grouping)), thousands_sep_(std::move(sep)) {} - Char separator() const { return sep_.thousands_sep; } + bool has_separator() const { return !thousands_sep_.empty(); } int count_separators(int num_digits) const { int count = 0; @@ -1682,7 +2203,9 @@ template class digit_grouping { for (int i = 0, sep_index = static_cast(separators.size() - 1); i < num_digits; ++i) { if (num_digits - i == separators[sep_index]) { - *out++ = separator(); + out = + copy_str(thousands_sep_.data(), + thousands_sep_.data() + thousands_sep_.size(), out); --sep_index; } *out++ = static_cast(digits[to_unsigned(i)]); @@ -1691,10 +2214,11 @@ template class digit_grouping { } }; +// Writes a decimal integer with digit grouping. template -auto write_int_localized(OutputIt out, UInt value, unsigned prefix, - const basic_format_specs& specs, - const digit_grouping& grouping) -> OutputIt { +auto write_int(OutputIt out, UInt value, unsigned prefix, + const format_specs& specs, + const digit_grouping& grouping) -> OutputIt { static_assert(std::is_same, UInt>::value, ""); int num_digits = count_digits(value); char digits[40]; @@ -1703,18 +2227,21 @@ auto write_int_localized(OutputIt out, UInt value, unsigned prefix, grouping.count_separators(num_digits)); return write_padded( out, specs, size, size, [&](reserve_iterator it) { - if (prefix != 0) *it++ = static_cast(prefix); + if (prefix != 0) { + char sign = static_cast(prefix); + *it++ = static_cast(sign); + } return grouping.apply(it, string_view(digits, to_unsigned(num_digits))); }); } -template -auto write_int_localized(OutputIt& out, UInt value, unsigned prefix, - const basic_format_specs& specs, locale_ref loc) - -> bool { - auto grouping = digit_grouping(loc); - out = write_int_localized(out, value, prefix, specs, grouping); - return true; +// Writes a localized value. +FMT_API auto write_loc(appender out, loc_value value, + const format_specs<>& specs, locale_ref loc) -> bool; +template +inline auto write_loc(OutputIt, loc_value, const format_specs&, + locale_ref) -> bool { + return false; } FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) { @@ -1743,21 +2270,37 @@ FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign) return {abs_value, prefix}; } +template struct loc_writer { + buffer_appender out; + const format_specs& specs; + std::basic_string sep; + std::string grouping; + std::basic_string decimal_point; + + template ::value)> + auto operator()(T value) -> bool { + auto arg = make_write_int_arg(value, specs.sign); + write_int(out, static_cast>(arg.abs_value), arg.prefix, + specs, digit_grouping(grouping, sep)); + return true; + } + + template ::value)> + auto operator()(T) -> bool { + return false; + } +}; + template FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg arg, - const basic_format_specs& specs, - locale_ref loc) -> OutputIt { + const format_specs& specs, + locale_ref) -> OutputIt { static_assert(std::is_same>::value, ""); auto abs_value = arg.abs_value; auto prefix = arg.prefix; switch (specs.type) { case presentation_type::none: case presentation_type::dec: { - if (specs.localized && - write_int_localized(out, static_cast>(abs_value), - prefix, specs, loc)) { - return out; - } auto num_digits = count_digits(abs_value); return write_int( out, num_digits, prefix, specs, [=](reserve_iterator it) { @@ -1800,13 +2343,13 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg arg, case presentation_type::chr: return write_char(out, static_cast(abs_value), specs); default: - throw_format_error("invalid type specifier"); + throw_format_error("invalid format specifier"); } return out; } template FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline( - OutputIt out, write_int_arg arg, const basic_format_specs& specs, + OutputIt out, write_int_arg arg, const format_specs& specs, locale_ref loc) -> OutputIt { return write_int(out, arg, specs, loc); } @@ -1815,8 +2358,9 @@ template ::value && std::is_same>::value)> FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, - const basic_format_specs& specs, + const format_specs& specs, locale_ref loc) -> OutputIt { + if (specs.localized && write_loc(out, value, specs, loc)) return out; return write_int_noinline(out, make_write_int_arg(value, specs.sign), specs, loc); } @@ -1826,8 +2370,9 @@ template ::value && !std::is_same>::value)> FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, - const basic_format_specs& specs, + const format_specs& specs, locale_ref loc) -> OutputIt { + if (specs.localized && write_loc(out, value, specs, loc)) return out; return write_int(out, make_write_int_arg(value, specs.sign), specs, loc); } @@ -1842,37 +2387,38 @@ class counting_iterator { using difference_type = std::ptrdiff_t; using pointer = void; using reference = void; - using _Unchecked_type = counting_iterator; // Mark iterator as checked. + FMT_UNCHECKED_ITERATOR(counting_iterator); struct value_type { - template void operator=(const T&) {} + template FMT_CONSTEXPR void operator=(const T&) {} }; - counting_iterator() : count_(0) {} + FMT_CONSTEXPR counting_iterator() : count_(0) {} - size_t count() const { return count_; } + FMT_CONSTEXPR size_t count() const { return count_; } - counting_iterator& operator++() { + FMT_CONSTEXPR counting_iterator& operator++() { ++count_; return *this; } - counting_iterator operator++(int) { + FMT_CONSTEXPR counting_iterator operator++(int) { auto it = *this; ++*this; return it; } - friend counting_iterator operator+(counting_iterator it, difference_type n) { + FMT_CONSTEXPR friend counting_iterator operator+(counting_iterator it, + difference_type n) { it.count_ += static_cast(n); return it; } - value_type operator*() const { return {}; } + FMT_CONSTEXPR value_type operator*() const { return {}; } }; template FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, - const basic_format_specs& specs) -> OutputIt { + const format_specs& specs) -> OutputIt { auto data = s.data(); auto size = s.size(); if (specs.precision >= 0 && to_unsigned(specs.precision) < size) @@ -1894,23 +2440,106 @@ FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, template FMT_CONSTEXPR auto write(OutputIt out, basic_string_view> s, - const basic_format_specs& specs, locale_ref) + const format_specs& specs, locale_ref) -> OutputIt { - check_string_type_spec(specs.type); return write(out, s, specs); } template FMT_CONSTEXPR auto write(OutputIt out, const Char* s, - const basic_format_specs& specs, locale_ref) + const format_specs& specs, locale_ref) -> OutputIt { - return check_cstring_type_spec(specs.type) + return specs.type != presentation_type::pointer ? write(out, basic_string_view(s), specs, {}) : write_ptr(out, bit_cast(s), &specs); } +template ::value && + !std::is_same::value && + !std::is_same::value)> +FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { + auto abs_value = static_cast>(value); + bool negative = is_negative(value); + // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer. + if (negative) abs_value = ~abs_value + 1; + int num_digits = count_digits(abs_value); + auto size = (negative ? 1 : 0) + static_cast(num_digits); + auto it = reserve(out, size); + if (auto ptr = to_pointer(it, size)) { + if (negative) *ptr++ = static_cast('-'); + format_decimal(ptr, abs_value, num_digits); + return out; + } + if (negative) *it++ = static_cast('-'); + it = format_decimal(it, abs_value, num_digits).end; + return base_iterator(out, it); +} + +// A floating-point presentation format. +enum class float_format : unsigned char { + general, // General: exponent notation or fixed point based on magnitude. + exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3. + fixed, // Fixed point with the default precision of 6, e.g. 0.0012. + hex +}; + +struct float_specs { + int precision; + float_format format : 8; + sign_t sign : 8; + bool upper : 1; + bool locale : 1; + bool binary32 : 1; + bool showpoint : 1; +}; + +template +FMT_CONSTEXPR auto parse_float_type_spec(const format_specs& specs, + ErrorHandler&& eh = {}) + -> float_specs { + auto result = float_specs(); + result.showpoint = specs.alt; + result.locale = specs.localized; + switch (specs.type) { + case presentation_type::none: + result.format = float_format::general; + break; + case presentation_type::general_upper: + result.upper = true; + FMT_FALLTHROUGH; + case presentation_type::general_lower: + result.format = float_format::general; + break; + case presentation_type::exp_upper: + result.upper = true; + FMT_FALLTHROUGH; + case presentation_type::exp_lower: + result.format = float_format::exp; + result.showpoint |= specs.precision != 0; + break; + case presentation_type::fixed_upper: + result.upper = true; + FMT_FALLTHROUGH; + case presentation_type::fixed_lower: + result.format = float_format::fixed; + result.showpoint |= specs.precision != 0; + break; + case presentation_type::hexfloat_upper: + result.upper = true; + FMT_FALLTHROUGH; + case presentation_type::hexfloat_lower: + result.format = float_format::hex; + break; + default: + eh.on_error("invalid format specifier"); + break; + } + return result; +} + template FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isnan, - basic_format_specs specs, + format_specs specs, const float_specs& fspecs) -> OutputIt { auto str = isnan ? (fspecs.upper ? "NAN" : "nan") : (fspecs.upper ? "INF" : "inf"); @@ -1934,12 +2563,12 @@ struct big_decimal_fp { int exponent; }; -constexpr auto get_significand_size(const big_decimal_fp& fp) -> int { - return fp.significand_size; +constexpr auto get_significand_size(const big_decimal_fp& f) -> int { + return f.significand_size; } template -inline auto get_significand_size(const dragonbox::decimal_fp& fp) -> int { - return count_digits(fp.significand); +inline auto get_significand_size(const dragonbox::decimal_fp& f) -> int { + return count_digits(f.significand); } template @@ -1956,7 +2585,7 @@ template FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, int significand_size, int exponent, const Grouping& grouping) -> OutputIt { - if (!grouping.separator()) { + if (!grouping.has_separator()) { out = write_significand(out, significand, significand_size); return detail::fill_n(out, exponent, static_cast('0')); } @@ -2018,7 +2647,7 @@ FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, int significand_size, int integral_size, Char decimal_point, const Grouping& grouping) -> OutputIt { - if (!grouping.separator()) { + if (!grouping.has_separator()) { return write_significand(out, significand, significand_size, integral_size, decimal_point); } @@ -2033,13 +2662,13 @@ FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, template > -FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& fp, - const basic_format_specs& specs, +FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, + const format_specs& specs, float_specs fspecs, locale_ref loc) -> OutputIt { - auto significand = fp.significand; - int significand_size = get_significand_size(fp); - constexpr Char zero = static_cast('0'); + auto significand = f.significand; + int significand_size = get_significand_size(f); + const Char zero = static_cast('0'); auto sign = fspecs.sign; size_t size = to_unsigned(significand_size) + (sign ? 1 : 0); using iterator = reserve_iterator; @@ -2047,7 +2676,7 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& fp, Char decimal_point = fspecs.locale ? detail::decimal_point(loc) : static_cast('.'); - int output_exp = fp.exponent + significand_size - 1; + int output_exp = f.exponent + significand_size - 1; auto use_exp_format = [=]() { if (fspecs.format == float_format::exp) return true; if (fspecs.format != float_format::general) return false; @@ -2085,26 +2714,23 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& fp, : base_iterator(out, write(reserve(out, size))); } - int exp = fp.exponent + significand_size; - if (fp.exponent >= 0) { + int exp = f.exponent + significand_size; + if (f.exponent >= 0) { // 1234e5 -> 123400000[.0+] - size += to_unsigned(fp.exponent); + size += to_unsigned(f.exponent); int num_zeros = fspecs.precision - exp; -#ifdef FMT_FUZZ - if (num_zeros > 5000) - throw std::runtime_error("fuzz mode - avoiding excessive cpu use"); -#endif + abort_fuzzing_if(num_zeros > 5000); if (fspecs.showpoint) { ++size; - if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 1; + if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 0; if (num_zeros > 0) size += to_unsigned(num_zeros); } auto grouping = Grouping(loc, fspecs.locale); - size += to_unsigned(grouping.count_separators(significand_size)); + size += to_unsigned(grouping.count_separators(exp)); return write_padded(out, specs, size, [&](iterator it) { if (sign) *it++ = detail::sign(sign); it = write_significand(it, significand, significand_size, - fp.exponent, grouping); + f.exponent, grouping); if (!fspecs.showpoint) return it; *it++ = decimal_point; return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; @@ -2114,7 +2740,7 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& fp, int num_zeros = fspecs.showpoint ? fspecs.precision - significand_size : 0; size += 1 + to_unsigned(num_zeros > 0 ? num_zeros : 0); auto grouping = Grouping(loc, fspecs.locale); - size += to_unsigned(grouping.count_separators(significand_size)); + size += to_unsigned(grouping.count_separators(exp)); return write_padded(out, specs, size, [&](iterator it) { if (sign) *it++ = detail::sign(sign); it = write_significand(it, significand, significand_size, exp, @@ -2144,7 +2770,7 @@ template class fallback_digit_grouping { public: constexpr fallback_digit_grouping(locale_ref, bool) {} - constexpr Char separator() const { return Char(); } + constexpr bool has_separator() const { return false; } constexpr int count_separators(int) const { return 0; } @@ -2155,32 +2781,43 @@ template class fallback_digit_grouping { }; template -FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& fp, - const basic_format_specs& specs, +FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f, + const format_specs& specs, float_specs fspecs, locale_ref loc) -> OutputIt { if (is_constant_evaluated()) { return do_write_float>(out, fp, specs, fspecs, + fallback_digit_grouping>(out, f, specs, fspecs, loc); } else { - return do_write_float(out, fp, specs, fspecs, loc); + return do_write_float(out, f, specs, fspecs, loc); } } -template ::value && - !is_float128::value)> -FMT_CONSTEXPR20 bool isfinite(T value) { - if (is_constant_evaluated()) return value - value == 0; - return std::isfinite(value); -} -template ::value)> -constexpr bool isfinite(T value) { - return value - value == 0; // std::isfinite doesn't support __float128. +template constexpr bool isnan(T value) { + return !(value >= value); // std::isnan doesn't support __float128. } -template constexpr bool isnan(T value) { - return value != value; // std::isnan doesn't support __float128. +template +struct has_isfinite : std::false_type {}; + +template +struct has_isfinite> + : std::true_type {}; + +template ::value&& + has_isfinite::value)> +FMT_CONSTEXPR20 bool isfinite(T value) { + constexpr T inf = T(std::numeric_limits::infinity()); + if (is_constant_evaluated()) + return !detail::isnan(value) && value < inf && value > -inf; + return std::isfinite(value); +} +template ::value)> +FMT_CONSTEXPR bool isfinite(T value) { + T inf = T(std::numeric_limits::infinity()); + // std::isfinite doesn't support __float128. + return !detail::isnan(value) && value < inf && value > -inf; } template ::value)> @@ -2196,12 +2833,992 @@ FMT_INLINE FMT_CONSTEXPR bool signbit(T value) { return std::signbit(static_cast(value)); } -template ::value)> -FMT_CONSTEXPR20 auto write(OutputIt out, T value, - basic_format_specs specs, locale_ref loc = {}) +enum class round_direction { unknown, up, down }; + +// Given the divisor (normally a power of 10), the remainder = v % divisor for +// some number v and the error, returns whether v should be rounded up, down, or +// whether the rounding direction can't be determined due to error. +// error should be less than divisor / 2. +FMT_CONSTEXPR inline round_direction get_round_direction(uint64_t divisor, + uint64_t remainder, + uint64_t error) { + FMT_ASSERT(remainder < divisor, ""); // divisor - remainder won't overflow. + FMT_ASSERT(error < divisor, ""); // divisor - error won't overflow. + FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow. + // Round down if (remainder + error) * 2 <= divisor. + if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2) + return round_direction::down; + // Round up if (remainder - error) * 2 >= divisor. + if (remainder >= error && + remainder - error >= divisor - (remainder - error)) { + return round_direction::up; + } + return round_direction::unknown; +} + +namespace digits { +enum result { + more, // Generate more digits. + done, // Done generating digits. + error // Digit generation cancelled due to an error. +}; +} + +struct gen_digits_handler { + char* buf; + int size; + int precision; + int exp10; + bool fixed; + + FMT_CONSTEXPR digits::result on_digit(char digit, uint64_t divisor, + uint64_t remainder, uint64_t error, + bool integral) { + FMT_ASSERT(remainder < divisor, ""); + buf[size++] = digit; + if (!integral && error >= remainder) return digits::error; + if (size < precision) return digits::more; + if (!integral) { + // Check if error * 2 < divisor with overflow prevention. + // The check is not needed for the integral part because error = 1 + // and divisor > (1 << 32) there. + if (error >= divisor || error >= divisor - error) return digits::error; + } else { + FMT_ASSERT(error == 1 && divisor > 2, ""); + } + auto dir = get_round_direction(divisor, remainder, error); + if (dir != round_direction::up) + return dir == round_direction::down ? digits::done : digits::error; + ++buf[size - 1]; + for (int i = size - 1; i > 0 && buf[i] > '9'; --i) { + buf[i] = '0'; + ++buf[i - 1]; + } + if (buf[0] > '9') { + buf[0] = '1'; + if (fixed) + buf[size++] = '0'; + else + ++exp10; + } + return digits::done; + } +}; + +inline FMT_CONSTEXPR20 void adjust_precision(int& precision, int exp10) { + // Adjust fixed precision by exponent because it is relative to decimal + // point. + if (exp10 > 0 && precision > max_value() - exp10) + FMT_THROW(format_error("number is too big")); + precision += exp10; +} + +// Generates output using the Grisu digit-gen algorithm. +// error: the size of the region (lower, upper) outside of which numbers +// definitely do not round to value (Delta in Grisu3). +FMT_INLINE FMT_CONSTEXPR20 auto grisu_gen_digits(fp value, uint64_t error, + int& exp, + gen_digits_handler& handler) + -> digits::result { + const fp one(1ULL << -value.e, value.e); + // The integral part of scaled value (p1 in Grisu) = value / one. It cannot be + // zero because it contains a product of two 64-bit numbers with MSB set (due + // to normalization) - 1, shifted right by at most 60 bits. + auto integral = static_cast(value.f >> -one.e); + FMT_ASSERT(integral != 0, ""); + FMT_ASSERT(integral == value.f >> -one.e, ""); + // The fractional part of scaled value (p2 in Grisu) c = value % one. + uint64_t fractional = value.f & (one.f - 1); + exp = count_digits(integral); // kappa in Grisu. + // Non-fixed formats require at least one digit and no precision adjustment. + if (handler.fixed) { + adjust_precision(handler.precision, exp + handler.exp10); + // Check if precision is satisfied just by leading zeros, e.g. + // format("{:.2f}", 0.001) gives "0.00" without generating any digits. + if (handler.precision <= 0) { + if (handler.precision < 0) return digits::done; + // Divide by 10 to prevent overflow. + uint64_t divisor = data::power_of_10_64[exp - 1] << -one.e; + auto dir = get_round_direction(divisor, value.f / 10, error * 10); + if (dir == round_direction::unknown) return digits::error; + handler.buf[handler.size++] = dir == round_direction::up ? '1' : '0'; + return digits::done; + } + } + // Generate digits for the integral part. This can produce up to 10 digits. + do { + uint32_t digit = 0; + auto divmod_integral = [&](uint32_t divisor) { + digit = integral / divisor; + integral %= divisor; + }; + // This optimization by Milo Yip reduces the number of integer divisions by + // one per iteration. + switch (exp) { + case 10: + divmod_integral(1000000000); + break; + case 9: + divmod_integral(100000000); + break; + case 8: + divmod_integral(10000000); + break; + case 7: + divmod_integral(1000000); + break; + case 6: + divmod_integral(100000); + break; + case 5: + divmod_integral(10000); + break; + case 4: + divmod_integral(1000); + break; + case 3: + divmod_integral(100); + break; + case 2: + divmod_integral(10); + break; + case 1: + digit = integral; + integral = 0; + break; + default: + FMT_ASSERT(false, "invalid number of digits"); + } + --exp; + auto remainder = (static_cast(integral) << -one.e) + fractional; + auto result = handler.on_digit(static_cast('0' + digit), + data::power_of_10_64[exp] << -one.e, + remainder, error, true); + if (result != digits::more) return result; + } while (exp > 0); + // Generate digits for the fractional part. + for (;;) { + fractional *= 10; + error *= 10; + char digit = static_cast('0' + (fractional >> -one.e)); + fractional &= one.f - 1; + --exp; + auto result = handler.on_digit(digit, one.f, fractional, error, false); + if (result != digits::more) return result; + } +} + +class bigint { + private: + // A bigint is stored as an array of bigits (big digits), with bigit at index + // 0 being the least significant one. + using bigit = uint32_t; + using double_bigit = uint64_t; + enum { bigits_capacity = 32 }; + basic_memory_buffer bigits_; + int exp_; + + FMT_CONSTEXPR20 bigit operator[](int index) const { + return bigits_[to_unsigned(index)]; + } + FMT_CONSTEXPR20 bigit& operator[](int index) { + return bigits_[to_unsigned(index)]; + } + + static constexpr const int bigit_bits = num_bits(); + + friend struct formatter; + + FMT_CONSTEXPR20 void subtract_bigits(int index, bigit other, bigit& borrow) { + auto result = static_cast((*this)[index]) - other - borrow; + (*this)[index] = static_cast(result); + borrow = static_cast(result >> (bigit_bits * 2 - 1)); + } + + FMT_CONSTEXPR20 void remove_leading_zeros() { + int num_bigits = static_cast(bigits_.size()) - 1; + while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits; + bigits_.resize(to_unsigned(num_bigits + 1)); + } + + // Computes *this -= other assuming aligned bigints and *this >= other. + FMT_CONSTEXPR20 void subtract_aligned(const bigint& other) { + FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints"); + FMT_ASSERT(compare(*this, other) >= 0, ""); + bigit borrow = 0; + int i = other.exp_ - exp_; + for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j) + subtract_bigits(i, other.bigits_[j], borrow); + while (borrow > 0) subtract_bigits(i, 0, borrow); + remove_leading_zeros(); + } + + FMT_CONSTEXPR20 void multiply(uint32_t value) { + const double_bigit wide_value = value; + bigit carry = 0; + for (size_t i = 0, n = bigits_.size(); i < n; ++i) { + double_bigit result = bigits_[i] * wide_value + carry; + bigits_[i] = static_cast(result); + carry = static_cast(result >> bigit_bits); + } + if (carry != 0) bigits_.push_back(carry); + } + + template ::value || + std::is_same::value)> + FMT_CONSTEXPR20 void multiply(UInt value) { + using half_uint = + conditional_t::value, uint64_t, uint32_t>; + const int shift = num_bits() - bigit_bits; + const UInt lower = static_cast(value); + const UInt upper = value >> num_bits(); + UInt carry = 0; + for (size_t i = 0, n = bigits_.size(); i < n; ++i) { + UInt result = lower * bigits_[i] + static_cast(carry); + carry = (upper * bigits_[i] << shift) + (result >> bigit_bits) + + (carry >> bigit_bits); + bigits_[i] = static_cast(result); + } + while (carry != 0) { + bigits_.push_back(static_cast(carry)); + carry >>= bigit_bits; + } + } + + template ::value || + std::is_same::value)> + FMT_CONSTEXPR20 void assign(UInt n) { + size_t num_bigits = 0; + do { + bigits_[num_bigits++] = static_cast(n); + n >>= bigit_bits; + } while (n != 0); + bigits_.resize(num_bigits); + exp_ = 0; + } + + public: + FMT_CONSTEXPR20 bigint() : exp_(0) {} + explicit bigint(uint64_t n) { assign(n); } + + bigint(const bigint&) = delete; + void operator=(const bigint&) = delete; + + FMT_CONSTEXPR20 void assign(const bigint& other) { + auto size = other.bigits_.size(); + bigits_.resize(size); + auto data = other.bigits_.data(); + std::copy(data, data + size, make_checked(bigits_.data(), size)); + exp_ = other.exp_; + } + + template FMT_CONSTEXPR20 void operator=(Int n) { + FMT_ASSERT(n > 0, ""); + assign(uint64_or_128_t(n)); + } + + FMT_CONSTEXPR20 int num_bigits() const { + return static_cast(bigits_.size()) + exp_; + } + + FMT_NOINLINE FMT_CONSTEXPR20 bigint& operator<<=(int shift) { + FMT_ASSERT(shift >= 0, ""); + exp_ += shift / bigit_bits; + shift %= bigit_bits; + if (shift == 0) return *this; + bigit carry = 0; + for (size_t i = 0, n = bigits_.size(); i < n; ++i) { + bigit c = bigits_[i] >> (bigit_bits - shift); + bigits_[i] = (bigits_[i] << shift) + carry; + carry = c; + } + if (carry != 0) bigits_.push_back(carry); + return *this; + } + + template FMT_CONSTEXPR20 bigint& operator*=(Int value) { + FMT_ASSERT(value > 0, ""); + multiply(uint32_or_64_or_128_t(value)); + return *this; + } + + friend FMT_CONSTEXPR20 int compare(const bigint& lhs, const bigint& rhs) { + int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits(); + if (num_lhs_bigits != num_rhs_bigits) + return num_lhs_bigits > num_rhs_bigits ? 1 : -1; + int i = static_cast(lhs.bigits_.size()) - 1; + int j = static_cast(rhs.bigits_.size()) - 1; + int end = i - j; + if (end < 0) end = 0; + for (; i >= end; --i, --j) { + bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j]; + if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1; + } + if (i != j) return i > j ? 1 : -1; + return 0; + } + + // Returns compare(lhs1 + lhs2, rhs). + friend FMT_CONSTEXPR20 int add_compare(const bigint& lhs1, const bigint& lhs2, + const bigint& rhs) { + auto minimum = [](int a, int b) { return a < b ? a : b; }; + auto maximum = [](int a, int b) { return a > b ? a : b; }; + int max_lhs_bigits = maximum(lhs1.num_bigits(), lhs2.num_bigits()); + int num_rhs_bigits = rhs.num_bigits(); + if (max_lhs_bigits + 1 < num_rhs_bigits) return -1; + if (max_lhs_bigits > num_rhs_bigits) return 1; + auto get_bigit = [](const bigint& n, int i) -> bigit { + return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0; + }; + double_bigit borrow = 0; + int min_exp = minimum(minimum(lhs1.exp_, lhs2.exp_), rhs.exp_); + for (int i = num_rhs_bigits - 1; i >= min_exp; --i) { + double_bigit sum = + static_cast(get_bigit(lhs1, i)) + get_bigit(lhs2, i); + bigit rhs_bigit = get_bigit(rhs, i); + if (sum > rhs_bigit + borrow) return 1; + borrow = rhs_bigit + borrow - sum; + if (borrow > 1) return -1; + borrow <<= bigit_bits; + } + return borrow != 0 ? -1 : 0; + } + + // Assigns pow(10, exp) to this bigint. + FMT_CONSTEXPR20 void assign_pow10(int exp) { + FMT_ASSERT(exp >= 0, ""); + if (exp == 0) return *this = 1; + // Find the top bit. + int bitmask = 1; + while (exp >= bitmask) bitmask <<= 1; + bitmask >>= 1; + // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by + // repeated squaring and multiplication. + *this = 5; + bitmask >>= 1; + while (bitmask != 0) { + square(); + if ((exp & bitmask) != 0) *this *= 5; + bitmask >>= 1; + } + *this <<= exp; // Multiply by pow(2, exp) by shifting. + } + + FMT_CONSTEXPR20 void square() { + int num_bigits = static_cast(bigits_.size()); + int num_result_bigits = 2 * num_bigits; + basic_memory_buffer n(std::move(bigits_)); + bigits_.resize(to_unsigned(num_result_bigits)); + auto sum = uint128_t(); + for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) { + // Compute bigit at position bigit_index of the result by adding + // cross-product terms n[i] * n[j] such that i + j == bigit_index. + for (int i = 0, j = bigit_index; j >= 0; ++i, --j) { + // Most terms are multiplied twice which can be optimized in the future. + sum += static_cast(n[i]) * n[j]; + } + (*this)[bigit_index] = static_cast(sum); + sum >>= num_bits(); // Compute the carry. + } + // Do the same for the top half. + for (int bigit_index = num_bigits; bigit_index < num_result_bigits; + ++bigit_index) { + for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;) + sum += static_cast(n[i++]) * n[j--]; + (*this)[bigit_index] = static_cast(sum); + sum >>= num_bits(); + } + remove_leading_zeros(); + exp_ *= 2; + } + + // If this bigint has a bigger exponent than other, adds trailing zero to make + // exponents equal. This simplifies some operations such as subtraction. + FMT_CONSTEXPR20 void align(const bigint& other) { + int exp_difference = exp_ - other.exp_; + if (exp_difference <= 0) return; + int num_bigits = static_cast(bigits_.size()); + bigits_.resize(to_unsigned(num_bigits + exp_difference)); + for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j) + bigits_[j] = bigits_[i]; + std::uninitialized_fill_n(bigits_.data(), exp_difference, 0); + exp_ -= exp_difference; + } + + // Divides this bignum by divisor, assigning the remainder to this and + // returning the quotient. + FMT_CONSTEXPR20 int divmod_assign(const bigint& divisor) { + FMT_ASSERT(this != &divisor, ""); + if (compare(*this, divisor) < 0) return 0; + FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, ""); + align(divisor); + int quotient = 0; + do { + subtract_aligned(divisor); + ++quotient; + } while (compare(*this, divisor) >= 0); + return quotient; + } +}; + +// format_dragon flags. +enum dragon { + predecessor_closer = 1, + fixup = 2, // Run fixup to correct exp10 which can be off by one. + fixed = 4, +}; + +// Formats a floating-point number using a variation of the Fixed-Precision +// Positive Floating-Point Printout ((FPP)^2) algorithm by Steele & White: +// https://fmt.dev/papers/p372-steele.pdf. +FMT_CONSTEXPR20 inline void format_dragon(basic_fp value, + unsigned flags, int num_digits, + buffer& buf, int& exp10) { + bigint numerator; // 2 * R in (FPP)^2. + bigint denominator; // 2 * S in (FPP)^2. + // lower and upper are differences between value and corresponding boundaries. + bigint lower; // (M^- in (FPP)^2). + bigint upper_store; // upper's value if different from lower. + bigint* upper = nullptr; // (M^+ in (FPP)^2). + // Shift numerator and denominator by an extra bit or two (if lower boundary + // is closer) to make lower and upper integers. This eliminates multiplication + // by 2 during later computations. + bool is_predecessor_closer = (flags & dragon::predecessor_closer) != 0; + int shift = is_predecessor_closer ? 2 : 1; + if (value.e >= 0) { + numerator = value.f; + numerator <<= value.e + shift; + lower = 1; + lower <<= value.e; + if (is_predecessor_closer) { + upper_store = 1; + upper_store <<= value.e + 1; + upper = &upper_store; + } + denominator.assign_pow10(exp10); + denominator <<= shift; + } else if (exp10 < 0) { + numerator.assign_pow10(-exp10); + lower.assign(numerator); + if (is_predecessor_closer) { + upper_store.assign(numerator); + upper_store <<= 1; + upper = &upper_store; + } + numerator *= value.f; + numerator <<= shift; + denominator = 1; + denominator <<= shift - value.e; + } else { + numerator = value.f; + numerator <<= shift; + denominator.assign_pow10(exp10); + denominator <<= shift - value.e; + lower = 1; + if (is_predecessor_closer) { + upper_store = 1ULL << 1; + upper = &upper_store; + } + } + int even = static_cast((value.f & 1) == 0); + if (!upper) upper = &lower; + if ((flags & dragon::fixup) != 0) { + if (add_compare(numerator, *upper, denominator) + even <= 0) { + --exp10; + numerator *= 10; + if (num_digits < 0) { + lower *= 10; + if (upper != &lower) *upper *= 10; + } + } + if ((flags & dragon::fixed) != 0) adjust_precision(num_digits, exp10 + 1); + } + // Invariant: value == (numerator / denominator) * pow(10, exp10). + if (num_digits < 0) { + // Generate the shortest representation. + num_digits = 0; + char* data = buf.data(); + for (;;) { + int digit = numerator.divmod_assign(denominator); + bool low = compare(numerator, lower) - even < 0; // numerator <[=] lower. + // numerator + upper >[=] pow10: + bool high = add_compare(numerator, *upper, denominator) + even > 0; + data[num_digits++] = static_cast('0' + digit); + if (low || high) { + if (!low) { + ++data[num_digits - 1]; + } else if (high) { + int result = add_compare(numerator, numerator, denominator); + // Round half to even. + if (result > 0 || (result == 0 && (digit % 2) != 0)) + ++data[num_digits - 1]; + } + buf.try_resize(to_unsigned(num_digits)); + exp10 -= num_digits - 1; + return; + } + numerator *= 10; + lower *= 10; + if (upper != &lower) *upper *= 10; + } + } + // Generate the given number of digits. + exp10 -= num_digits - 1; + if (num_digits == 0) { + denominator *= 10; + auto digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0'; + buf.push_back(digit); + return; + } + buf.try_resize(to_unsigned(num_digits)); + for (int i = 0; i < num_digits - 1; ++i) { + int digit = numerator.divmod_assign(denominator); + buf[i] = static_cast('0' + digit); + numerator *= 10; + } + int digit = numerator.divmod_assign(denominator); + auto result = add_compare(numerator, numerator, denominator); + if (result > 0 || (result == 0 && (digit % 2) != 0)) { + if (digit == 9) { + const auto overflow = '0' + 10; + buf[num_digits - 1] = overflow; + // Propagate the carry. + for (int i = num_digits - 1; i > 0 && buf[i] == overflow; --i) { + buf[i] = '0'; + ++buf[i - 1]; + } + if (buf[0] == overflow) { + buf[0] = '1'; + ++exp10; + } + return; + } + ++digit; + } + buf[num_digits - 1] = static_cast('0' + digit); +} + +// Formats a floating-point number using the hexfloat format. +template ::value)> +FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision, + float_specs specs, buffer& buf) { + // float is passed as double to reduce the number of instantiations and to + // simplify implementation. + static_assert(!std::is_same::value, ""); + + using info = dragonbox::float_info; + + // Assume Float is in the format [sign][exponent][significand]. + using carrier_uint = typename info::carrier_uint; + + constexpr auto num_float_significand_bits = + detail::num_significand_bits(); + + basic_fp f(value); + f.e += num_float_significand_bits; + if (!has_implicit_bit()) --f.e; + + constexpr auto num_fraction_bits = + num_float_significand_bits + (has_implicit_bit() ? 1 : 0); + constexpr auto num_xdigits = (num_fraction_bits + 3) / 4; + + constexpr auto leading_shift = ((num_xdigits - 1) * 4); + const auto leading_mask = carrier_uint(0xF) << leading_shift; + const auto leading_xdigit = + static_cast((f.f & leading_mask) >> leading_shift); + if (leading_xdigit > 1) f.e -= (32 - countl_zero(leading_xdigit) - 1); + + int print_xdigits = num_xdigits - 1; + if (precision >= 0 && print_xdigits > precision) { + const int shift = ((print_xdigits - precision - 1) * 4); + const auto mask = carrier_uint(0xF) << shift; + const auto v = static_cast((f.f & mask) >> shift); + + if (v >= 8) { + const auto inc = carrier_uint(1) << (shift + 4); + f.f += inc; + f.f &= ~(inc - 1); + } + + // Check long double overflow + if (!has_implicit_bit()) { + const auto implicit_bit = carrier_uint(1) << num_float_significand_bits; + if ((f.f & implicit_bit) == implicit_bit) { + f.f >>= 4; + f.e += 4; + } + } + + print_xdigits = precision; + } + + char xdigits[num_bits() / 4]; + detail::fill_n(xdigits, sizeof(xdigits), '0'); + format_uint<4>(xdigits, f.f, num_xdigits, specs.upper); + + // Remove zero tail + while (print_xdigits > 0 && xdigits[print_xdigits] == '0') --print_xdigits; + + buf.push_back('0'); + buf.push_back(specs.upper ? 'X' : 'x'); + buf.push_back(xdigits[0]); + if (specs.showpoint || print_xdigits > 0 || print_xdigits < precision) + buf.push_back('.'); + buf.append(xdigits + 1, xdigits + 1 + print_xdigits); + for (; print_xdigits < precision; ++print_xdigits) buf.push_back('0'); + + buf.push_back(specs.upper ? 'P' : 'p'); + + uint32_t abs_e; + if (f.e < 0) { + buf.push_back('-'); + abs_e = static_cast(-f.e); + } else { + buf.push_back('+'); + abs_e = static_cast(f.e); + } + format_decimal(appender(buf), abs_e, detail::count_digits(abs_e)); +} + +template ::value)> +FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision, + float_specs specs, buffer& buf) { + format_hexfloat(static_cast(value), precision, specs, buf); +} + +template +FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, + buffer& buf) -> int { + // float is passed as double to reduce the number of instantiations. + static_assert(!std::is_same::value, ""); + FMT_ASSERT(value >= 0, "value is negative"); + auto converted_value = convert_float(value); + + const bool fixed = specs.format == float_format::fixed; + if (value <= 0) { // <= instead of == to silence a warning. + if (precision <= 0 || !fixed) { + buf.push_back('0'); + return 0; + } + buf.try_resize(to_unsigned(precision)); + fill_n(buf.data(), precision, '0'); + return -precision; + } + + int exp = 0; + bool use_dragon = true; + unsigned dragon_flags = 0; + if (!is_fast_float()) { + const auto inv_log2_10 = 0.3010299956639812; // 1 / log2(10) + using info = dragonbox::float_info; + const auto f = basic_fp(converted_value); + // Compute exp, an approximate power of 10, such that + // 10^(exp - 1) <= value < 10^exp or 10^exp <= value < 10^(exp + 1). + // This is based on log10(value) == log2(value) / log2(10) and approximation + // of log2(value) by e + num_fraction_bits idea from double-conversion. + exp = static_cast( + std::ceil((f.e + count_digits<1>(f.f) - 1) * inv_log2_10 - 1e-10)); + dragon_flags = dragon::fixup; + } else if (!is_constant_evaluated() && precision < 0) { + // Use Dragonbox for the shortest format. + if (specs.binary32) { + auto dec = dragonbox::to_decimal(static_cast(value)); + write(buffer_appender(buf), dec.significand); + return dec.exponent; + } + auto dec = dragonbox::to_decimal(static_cast(value)); + write(buffer_appender(buf), dec.significand); + return dec.exponent; + } else if (is_constant_evaluated()) { + // Use Grisu + Dragon4 for the given precision: + // https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf. + const int min_exp = -60; // alpha in Grisu. + int cached_exp10 = 0; // K in Grisu. + fp normalized = normalize(fp(converted_value)); + const auto cached_pow = get_cached_power( + min_exp - (normalized.e + fp::num_significand_bits), cached_exp10); + normalized = normalized * cached_pow; + gen_digits_handler handler{buf.data(), 0, precision, -cached_exp10, fixed}; + if (grisu_gen_digits(normalized, 1, exp, handler) != digits::error && + !is_constant_evaluated()) { + exp += handler.exp10; + buf.try_resize(to_unsigned(handler.size)); + use_dragon = false; + } else { + exp += handler.size - cached_exp10 - 1; + precision = handler.precision; + } + } else { + // Extract significand bits and exponent bits. + using info = dragonbox::float_info; + auto br = bit_cast(static_cast(value)); + + const uint64_t significand_mask = + (static_cast(1) << num_significand_bits()) - 1; + uint64_t significand = (br & significand_mask); + int exponent = static_cast((br & exponent_mask()) >> + num_significand_bits()); + + if (exponent != 0) { // Check if normal. + exponent -= exponent_bias() + num_significand_bits(); + significand |= + (static_cast(1) << num_significand_bits()); + significand <<= 1; + } else { + // Normalize subnormal inputs. + FMT_ASSERT(significand != 0, "zeros should not appear hear"); + int shift = countl_zero(significand); + FMT_ASSERT(shift >= num_bits() - num_significand_bits(), + ""); + shift -= (num_bits() - num_significand_bits() - 2); + exponent = (std::numeric_limits::min_exponent - + num_significand_bits()) - + shift; + significand <<= shift; + } + + // Compute the first several nonzero decimal significand digits. + // We call the number we get the first segment. + const int k = info::kappa - dragonbox::floor_log10_pow2(exponent); + exp = -k; + const int beta = exponent + dragonbox::floor_log2_pow10(k); + uint64_t first_segment; + bool has_more_segments; + int digits_in_the_first_segment; + { + const auto r = dragonbox::umul192_upper128( + significand << beta, dragonbox::get_cached_power(k)); + first_segment = r.high(); + has_more_segments = r.low() != 0; + + // The first segment can have 18 ~ 19 digits. + if (first_segment >= 1000000000000000000ULL) { + digits_in_the_first_segment = 19; + } else { + // When it is of 18-digits, we align it to 19-digits by adding a bogus + // zero at the end. + digits_in_the_first_segment = 18; + first_segment *= 10; + } + } + + // Compute the actual number of decimal digits to print. + if (fixed) { + adjust_precision(precision, exp + digits_in_the_first_segment); + } + + // Use Dragon4 only when there might be not enough digits in the first + // segment. + if (digits_in_the_first_segment > precision) { + use_dragon = false; + + if (precision <= 0) { + exp += digits_in_the_first_segment; + + if (precision < 0) { + // Nothing to do, since all we have are just leading zeros. + buf.try_resize(0); + } else { + // We may need to round-up. + buf.try_resize(1); + if ((first_segment | static_cast(has_more_segments)) > + 5000000000000000000ULL) { + buf[0] = '1'; + } else { + buf[0] = '0'; + } + } + } // precision <= 0 + else { + exp += digits_in_the_first_segment - precision; + + // When precision > 0, we divide the first segment into three + // subsegments, each with 9, 9, and 0 ~ 1 digits so that each fits + // in 32-bits which usually allows faster calculation than in + // 64-bits. Since some compiler (e.g. MSVC) doesn't know how to optimize + // division-by-constant for large 64-bit divisors, we do it here + // manually. The magic number 7922816251426433760 below is equal to + // ceil(2^(64+32) / 10^10). + const uint32_t first_subsegment = static_cast( + dragonbox::umul128_upper64(first_segment, 7922816251426433760ULL) >> + 32); + const uint64_t second_third_subsegments = + first_segment - first_subsegment * 10000000000ULL; + + uint64_t prod; + uint32_t digits; + bool should_round_up; + int number_of_digits_to_print = precision > 9 ? 9 : precision; + + // Print a 9-digits subsegment, either the first or the second. + auto print_subsegment = [&](uint32_t subsegment, char* buffer) { + int number_of_digits_printed = 0; + + // If we want to print an odd number of digits from the subsegment, + if ((number_of_digits_to_print & 1) != 0) { + // Convert to 64-bit fixed-point fractional form with 1-digit + // integer part. The magic number 720575941 is a good enough + // approximation of 2^(32 + 24) / 10^8; see + // https://jk-jeon.github.io/posts/2022/12/fixed-precision-formatting/#fixed-length-case + // for details. + prod = ((subsegment * static_cast(720575941)) >> 24) + 1; + digits = static_cast(prod >> 32); + *buffer = static_cast('0' + digits); + number_of_digits_printed++; + } + // If we want to print an even number of digits from the + // first_subsegment, + else { + // Convert to 64-bit fixed-point fractional form with 2-digits + // integer part. The magic number 450359963 is a good enough + // approximation of 2^(32 + 20) / 10^7; see + // https://jk-jeon.github.io/posts/2022/12/fixed-precision-formatting/#fixed-length-case + // for details. + prod = ((subsegment * static_cast(450359963)) >> 20) + 1; + digits = static_cast(prod >> 32); + copy2(buffer, digits2(digits)); + number_of_digits_printed += 2; + } + + // Print all digit pairs. + while (number_of_digits_printed < number_of_digits_to_print) { + prod = static_cast(prod) * static_cast(100); + digits = static_cast(prod >> 32); + copy2(buffer + number_of_digits_printed, digits2(digits)); + number_of_digits_printed += 2; + } + }; + + // Print first subsegment. + print_subsegment(first_subsegment, buf.data()); + + // Perform rounding if the first subsegment is the last subsegment to + // print. + if (precision <= 9) { + // Rounding inside the subsegment. + // We round-up if: + // - either the fractional part is strictly larger than 1/2, or + // - the fractional part is exactly 1/2 and the last digit is odd. + // We rely on the following observations: + // - If fractional_part >= threshold, then the fractional part is + // strictly larger than 1/2. + // - If the MSB of fractional_part is set, then the fractional part + // must be at least 1/2. + // - When the MSB of fractional_part is set, either + // second_third_subsegments being nonzero or has_more_segments + // being true means there are further digits not printed, so the + // fractional part is strictly larger than 1/2. + if (precision < 9) { + uint32_t fractional_part = static_cast(prod); + should_round_up = fractional_part >= + data::fractional_part_rounding_thresholds + [8 - number_of_digits_to_print] || + ((fractional_part >> 31) & + ((digits & 1) | (second_third_subsegments != 0) | + has_more_segments)) != 0; + } + // Rounding at the subsegment boundary. + // In this case, the fractional part is at least 1/2 if and only if + // second_third_subsegments >= 5000000000ULL, and is strictly larger + // than 1/2 if we further have either second_third_subsegments > + // 5000000000ULL or has_more_segments == true. + else { + should_round_up = second_third_subsegments > 5000000000ULL || + (second_third_subsegments == 5000000000ULL && + ((digits & 1) != 0 || has_more_segments)); + } + } + // Otherwise, print the second subsegment. + else { + // Compilers are not aware of how to leverage the maximum value of + // second_third_subsegments to find out a better magic number which + // allows us to eliminate an additional shift. 1844674407370955162 = + // ceil(2^64/10) < ceil(2^64*(10^9/(10^10 - 1))). + const uint32_t second_subsegment = + static_cast(dragonbox::umul128_upper64( + second_third_subsegments, 1844674407370955162ULL)); + const uint32_t third_subsegment = + static_cast(second_third_subsegments) - + second_subsegment * 10; + + number_of_digits_to_print = precision - 9; + print_subsegment(second_subsegment, buf.data() + 9); + + // Rounding inside the subsegment. + if (precision < 18) { + // The condition third_subsegment != 0 implies that the segment was + // of 19 digits, so in this case the third segment should be + // consisting of a genuine digit from the input. + uint32_t fractional_part = static_cast(prod); + should_round_up = fractional_part >= + data::fractional_part_rounding_thresholds + [8 - number_of_digits_to_print] || + ((fractional_part >> 31) & + ((digits & 1) | (third_subsegment != 0) | + has_more_segments)) != 0; + } + // Rounding at the subsegment boundary. + else { + // In this case, the segment must be of 19 digits, thus + // the third subsegment should be consisting of a genuine digit from + // the input. + should_round_up = third_subsegment > 5 || + (third_subsegment == 5 && + ((digits & 1) != 0 || has_more_segments)); + } + } + + // Round-up if necessary. + if (should_round_up) { + ++buf[precision - 1]; + for (int i = precision - 1; i > 0 && buf[i] > '9'; --i) { + buf[i] = '0'; + ++buf[i - 1]; + } + if (buf[0] > '9') { + buf[0] = '1'; + if (fixed) + buf[precision++] = '0'; + else + ++exp; + } + } + buf.try_resize(to_unsigned(precision)); + } + } // if (digits_in_the_first_segment > precision) + else { + // Adjust the exponent for its use in Dragon4. + exp += digits_in_the_first_segment - 1; + } + } + if (use_dragon) { + auto f = basic_fp(); + bool is_predecessor_closer = specs.binary32 + ? f.assign(static_cast(value)) + : f.assign(converted_value); + if (is_predecessor_closer) dragon_flags |= dragon::predecessor_closer; + if (fixed) dragon_flags |= dragon::fixed; + // Limit precision to the maximum possible number of significant digits in + // an IEEE754 double because we don't need to generate zeros. + const int max_double_digits = 767; + if (precision > max_double_digits) precision = max_double_digits; + format_dragon(f, dragon_flags, precision, buf, exp); + } + if (!fixed && !specs.showpoint) { + // Remove trailing zeros. + auto num_digits = buf.size(); + while (num_digits > 0 && buf[num_digits - 1] == '0') { + --num_digits; + ++exp; + } + buf.try_resize(num_digits); + } + return exp; +} +template +FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, + format_specs specs, locale_ref loc) -> OutputIt { - if (const_check(!is_supported_floating_point(value))) return out; float_specs fspecs = parse_float_type_spec(specs); fspecs.sign = specs.sign; if (detail::signbit(value)) { // value < 0 is false for NaN so use signbit. @@ -2225,7 +3842,7 @@ FMT_CONSTEXPR20 auto write(OutputIt out, T value, memory_buffer buffer; if (fspecs.format == float_format::hex) { if (fspecs.sign) buffer.push_back(detail::sign(fspecs.sign)); - snprintf_float(convert_float(value), specs.precision, fspecs, buffer); + format_hexfloat(convert_float(value), specs.precision, fspecs, buffer); return write_bytes(out, {buffer.data(), buffer.size()}, specs); } @@ -2243,31 +3860,37 @@ FMT_CONSTEXPR20 auto write(OutputIt out, T value, if (const_check(std::is_same())) fspecs.binary32 = true; int exp = format_float(convert_float(value), precision, fspecs, buffer); fspecs.precision = precision; - auto fp = big_decimal_fp{buffer.data(), static_cast(buffer.size()), exp}; - return write_float(out, fp, specs, fspecs, loc); + auto f = big_decimal_fp{buffer.data(), static_cast(buffer.size()), exp}; + return write_float(out, f, specs, fspecs, loc); +} + +template ::value)> +FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs specs, + locale_ref loc = {}) -> OutputIt { + if (const_check(!is_supported_floating_point(value))) return out; + return specs.localized && write_loc(out, value, specs, loc) + ? out + : write_float(out, value, specs, loc); } template ::value)> FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt { - if (is_constant_evaluated()) - return write(out, value, basic_format_specs()); - + if (is_constant_evaluated()) return write(out, value, format_specs()); if (const_check(!is_supported_floating_point(value))) return out; - using floaty = conditional_t::value, double, T>; - using uint = typename dragonbox::float_info::carrier_uint; - auto bits = bit_cast(value); - auto fspecs = float_specs(); if (detail::signbit(value)) { fspecs.sign = sign::minus; value = -value; } - constexpr auto specs = basic_format_specs(); - uint mask = exponent_mask(); - if ((bits & mask) == mask) + constexpr auto specs = format_specs(); + using floaty = conditional_t::value, double, T>; + using floaty_uint = typename dragonbox::float_info::carrier_uint; + floaty_uint mask = exponent_mask(); + if ((bit_cast(value) & mask) == mask) return write_nonfinite(out, std::isnan(value), specs, fspecs); auto dec = dragonbox::to_decimal(static_cast(value)); @@ -2278,12 +3901,12 @@ template ::value && !is_fast_float::value)> inline auto write(OutputIt out, T value) -> OutputIt { - return write(out, value, basic_format_specs()); + return write(out, value, format_specs()); } template -auto write(OutputIt out, monostate, basic_format_specs = {}, - locale_ref = {}) -> OutputIt { +auto write(OutputIt out, monostate, format_specs = {}, locale_ref = {}) + -> OutputIt { FMT_ASSERT(false, ""); return out; } @@ -2302,28 +3925,6 @@ constexpr auto write(OutputIt out, const T& value) -> OutputIt { return write(out, to_string_view(value)); } -template ::value && - !std::is_same::value && - !std::is_same::value)> -FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { - auto abs_value = static_cast>(value); - bool negative = is_negative(value); - // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer. - if (negative) abs_value = ~abs_value + 1; - int num_digits = count_digits(abs_value); - auto size = (negative ? 1 : 0) + static_cast(num_digits); - auto it = reserve(out, size); - if (auto ptr = to_pointer(it, size)) { - if (negative) *ptr++ = static_cast('-'); - format_decimal(ptr, abs_value, num_digits); - return out; - } - if (negative) *it++ = static_cast('-'); - it = format_decimal(it, abs_value, num_digits).end; - return base_iterator(out, it); -} - // FMT_ENABLE_IF() condition separated to workaround an MSVC bug. template < typename Char, typename OutputIt, typename T, @@ -2339,8 +3940,8 @@ FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { template ::value)> FMT_CONSTEXPR auto write(OutputIt out, T value, - const basic_format_specs& specs = {}, - locale_ref = {}) -> OutputIt { + const format_specs& specs = {}, locale_ref = {}) + -> OutputIt { return specs.type != presentation_type::none && specs.type != presentation_type::string ? write(out, value ? 1 : 0, specs, {}) @@ -2357,20 +3958,15 @@ FMT_CONSTEXPR auto write(OutputIt out, Char value) -> OutputIt { template FMT_CONSTEXPR_CHAR_TRAITS auto write(OutputIt out, const Char* value) -> OutputIt { - if (!value) { - throw_format_error("string pointer is null"); - } else { - out = write(out, basic_string_view(value)); - } + if (value) return write(out, basic_string_view(value)); + throw_format_error("string pointer is null"); return out; } template ::value)> -auto write(OutputIt out, const T* value, - const basic_format_specs& specs = {}, locale_ref = {}) - -> OutputIt { - check_pointer_type_spec(specs.type, error_handler()); +auto write(OutputIt out, const T* value, const format_specs& specs = {}, + locale_ref = {}) -> OutputIt { return write_ptr(out, bit_cast(value), &specs); } @@ -2379,9 +3975,9 @@ template > FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> enable_if_t< std::is_class::value && !is_string::value && - !std::is_same::value && - !std::is_same().map(value))>::value, + !is_floating_point::value && !std::is_same::value && + !std::is_same().map( + value))>>::value, OutputIt> { return write(out, arg_mapper().map(value)); } @@ -2391,12 +3987,8 @@ template enable_if_t::value == type::custom_type, OutputIt> { - using formatter_type = - conditional_t::value, - typename Context::template formatter_type, - fallback_formatter>; auto ctx = Context(out, {}, {}); - return formatter_type().format(value, ctx); + return typename Context::template formatter_type().format(value, ctx); } // An argument visitor that formats the argument and writes it via the output @@ -2425,7 +4017,7 @@ template struct arg_formatter { using context = buffer_context; iterator out; - const basic_format_specs& specs; + const format_specs& specs; locale_ref locale; template @@ -2450,12 +4042,6 @@ template struct custom_formatter { template void operator()(T) const {} }; -template -using is_integer = - bool_constant::value && !std::is_same::value && - !std::is_same::value && - !std::is_same::value>; - template class width_checker { public: explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {} @@ -2512,48 +4098,6 @@ FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) -> return arg; } -// The standard format specifier handler with checking. -template class specs_handler : public specs_setter { - private: - basic_format_parse_context& parse_context_; - buffer_context& context_; - - // This is only needed for compatibility with gcc 4.4. - using format_arg = basic_format_arg>; - - FMT_CONSTEXPR auto get_arg(auto_id) -> format_arg { - return detail::get_arg(context_, parse_context_.next_arg_id()); - } - - FMT_CONSTEXPR auto get_arg(int arg_id) -> format_arg { - parse_context_.check_arg_id(arg_id); - return detail::get_arg(context_, arg_id); - } - - FMT_CONSTEXPR auto get_arg(basic_string_view arg_id) -> format_arg { - parse_context_.check_arg_id(arg_id); - return detail::get_arg(context_, arg_id); - } - - public: - FMT_CONSTEXPR specs_handler(basic_format_specs& specs, - basic_format_parse_context& parse_ctx, - buffer_context& ctx) - : specs_setter(specs), parse_context_(parse_ctx), context_(ctx) {} - - template FMT_CONSTEXPR void on_dynamic_width(Id arg_id) { - this->specs_.width = get_dynamic_spec( - get_arg(arg_id), context_.error_handler()); - } - - template FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) { - this->specs_.precision = get_dynamic_spec( - get_arg(arg_id), context_.error_handler()); - } - - void on_error(const char* message) { context_.on_error(message); } -}; - template