diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f94c3ca --- /dev/null +++ b/.editorconfig @@ -0,0 +1,7 @@ +root = true + +[*] +indent_style=space +indent_size=4 +end_of_line=crlf +trim_trailing_whitespace=true \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..6c5a3e3 --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +ENCODED_CLIENT_TOKEN=ADD_HERE \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..149c5d2 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +# Auto detect text files and perform LF normalization +*.h linguist-language=C++ +*.cpp linguist-language=C++ \ No newline at end of file diff --git a/.github/workflows/cmake.yaml b/.github/workflows/cmake.yaml index 9c3e62e..e373c36 100644 --- a/.github/workflows/cmake.yaml +++ b/.github/workflows/cmake.yaml @@ -1,9 +1,9 @@ -name: CMake +name: Harshie CMake Build on: [ push, pull_request ] jobs: - build: + linux-build: runs-on: ubuntu-latest steps: @@ -24,9 +24,32 @@ jobs: run: | echo "ENCODED_CLIENT_TOKEN=$ENCODED_CLIENT_TOKEN" > .env - - name: Build Test + - name: Harshie Build Test run: | mkdir build cd build cmake .. - make -j8 \ No newline at end of file + make -j8 + + windows-build: + runs-on: windows-latest + + steps: + - name: Checkout Harshie + uses: actions/checkout@v3 + + - name: Setup MSVC + uses: microsoft/setup-msbuild@v1.1 + + - name: Create .env + env: + ENCODED_CLIENT_TOKEN: ${{ secrets.ENCODED_CLIENT_TOKEN }} + run: | + echo "ENCODED_CLIENT_TOKEN=$ENCODED_CLIENT_TOKEN" > .env + + - name: Harshie Build Test + run: | + mkdir build + cd build + cmake -G "Visual Studio 17 2022" -A x64 .. + cmake --build . --config Release \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 54c45cb..fe40698 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,15 +35,36 @@ set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD_REQUIRED ON ) -set(INCLUDE_INTERNAL "include") -set(INCLUDE_EXTERNAL "/usr/include") +set(INCLUDE_FOLDER "include") +set(DPP_FOLDER "include/dpp") + +set(LIBRARY_FOLDER "lib") target_include_directories(${PROJECT_NAME} PRIVATE - ${INCLUDE_INTERNAL} - ${INCLUDE_EXTERNAL} + ${INCLUDE_FOLDER} + ${DPP_FOLDER} ) -target_link_libraries(${PROJECT_NAME} PRIVATE "/lib/libdpp.so") +if(WIN32) + target_include_directories(${PROJECT_NAME} PRIVATE ${LIBRARY_FOLDER}) + target_link_libraries(${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/lib/dpp.lib) + target_sources(${PROJECT_NAME} PRIVATE "${CMAKE_BINARY_DIR}/harshie.rc") + + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_SOURCE_DIR}/bin $ + ) + + configure_file( + ${CMAKE_SOURCE_DIR}/resources/harshie.rc.in + ${CMAKE_BINARY_DIR}/harshie.rc + @ONLY + ) + +else() + target_link_libraries(${PROJECT_NAME} PRIVATE "/lib/libdpp.so") + +endif() add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..b3b7e02 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,133 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement in the +[Discord][discord] server. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[discord]: https://discord.gg/pAZZfqC5ST +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..776e5ee --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,12 @@ +# Security Policy + +## Supported Versions + +| Name | Version | Supported | +| ---------------- | -------------- | ------------------- | +| Harshie (Stable) | Not released | No | +| Harshie (BETA) | 1.0.x (Github) | Yes | + +## Reporting a Vulnerability + +To report a vulnerability please contact the development team by using Github Issue or Discord server. We will discuss and fix it ASAP. Once the fix has been put into a release version, the vulnerability will be made public. \ No newline at end of file diff --git a/bin/dpp.dll b/bin/dpp.dll new file mode 100644 index 0000000..a15a81a Binary files /dev/null and b/bin/dpp.dll differ diff --git a/bin/libcrypto-1_1-x64.dll b/bin/libcrypto-1_1-x64.dll new file mode 100644 index 0000000..19a42c9 Binary files /dev/null and b/bin/libcrypto-1_1-x64.dll differ diff --git a/bin/libsodium.dll b/bin/libsodium.dll new file mode 100644 index 0000000..0fc90b1 Binary files /dev/null and b/bin/libsodium.dll differ diff --git a/bin/libssl-1_1-x64.dll b/bin/libssl-1_1-x64.dll new file mode 100644 index 0000000..b484de9 Binary files /dev/null and b/bin/libssl-1_1-x64.dll differ diff --git a/bin/mysqlcppconn8-2-vs14.dll b/bin/mysqlcppconn8-2-vs14.dll new file mode 100644 index 0000000..7ab84e5 Binary files /dev/null and b/bin/mysqlcppconn8-2-vs14.dll differ diff --git a/bin/opus.dll b/bin/opus.dll new file mode 100644 index 0000000..e23428e Binary files /dev/null and b/bin/opus.dll differ diff --git a/bin/zlib1.dll b/bin/zlib1.dll new file mode 100644 index 0000000..897a1fe Binary files /dev/null and b/bin/zlib1.dll differ diff --git a/include/dpp/appcommand.h b/include/dpp/appcommand.h new file mode 100644 index 0000000..7cae5d2 --- /dev/null +++ b/include/dpp/appcommand.h @@ -0,0 +1,1248 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace dpp { + +/** + * @brief Discord limits the maximum number of replies to an autocomplete interaction to 25. + * This value represents that maximum. interaction_response::add_autocomplete_choice does not allow + * adding more than this number of elements to the vector. + */ +#ifndef AUTOCOMPLETE_MAX_CHOICES + #define AUTOCOMPLETE_MAX_CHOICES 25 +#endif + +/** + * @brief Represents command option types. + * These are the possible parameter value types. + */ +enum command_option_type : uint8_t { + /** A sub-command */ + co_sub_command = 1, + /** A sub-command group */ + co_sub_command_group = 2, + /** A string value */ + co_string = 3, + /** An integer value */ + co_integer = 4, + /** A boolean value */ + co_boolean = 5, + /** A user snowflake id */ + co_user = 6, + /** A channel snowflake id. Includes all channel types and categories */ + co_channel = 7, + /** A role snowflake id */ + co_role = 8, + /** A mentionable. Includes users and roles */ + co_mentionable = 9, + /** Any double between -2^53 and 2^53 */ + co_number = 10, + /** File attachment type */ + co_attachment = 11, +}; + +/** + * @brief This type is a variant that can hold any of the potential + * native data types represented by the enum dpp::command_option_type. + * It is used in interactions. + * + * std::monostate indicates an invalid parameter value, e.g. an unfilled optional parameter. + * std::int64_t will be for all integer options, double for decimal numbers and dpp::snowflake for anything ID related. + * + * You can retrieve them with std::get(). + */ +typedef std::variant command_value; + +/** + * @brief This struct represents choices in a multiple choice option + * for a command parameter. + * It has both a string name, and a value parameter which is a variant, + * meaning it can hold different potential types (see dpp::command_value) + * that you can retrieve with std::get(). + */ +struct DPP_EXPORT command_option_choice : public json_interface { + std::string name; //!< Option name (1-32 chars) + command_value value; //!< Option value + std::map name_localizations; //!< Localisations of command option name + + /** + * @brief Construct a new command option choice object + */ + command_option_choice() = default; + + virtual ~command_option_choice() = default; + + /** + * @brief Add a localisation for this command option choice + * @see https://discord.com/developers/docs/reference#locales + * @param language Name of language, see the list of locales linked to above + * @param _name name of command option choice in the specified language + * @return command_option_choice& reference to self for fluent chaining + */ + command_option_choice& add_localization(const std::string& language, const std::string& _name); + + /** + * @brief Construct a new command option choice object + * + * @param n name to initialise with + * @param v value to initialise with + */ + command_option_choice(const std::string &n, command_value v); + + /** + * @brief Fill object properties from JSON + * + * @param j JSON to fill from + * @return command_option_choice& Reference to self + */ + command_option_choice& fill_from_json(nlohmann::json* j); +}; + +/** + * @brief helper function to serialize a command_option_choice to json + * + * @see https://github.com/nlohmann/json#arbitrary-types-conversions + * + * @param j output json object + * @param choice command_option_choice to be serialized + */ +void to_json(nlohmann::json& j, const command_option_choice& choice); + +/** + * @brief A minimum or maximum value/length for dpp::co_number, dpp::co_integer and dpp::co_string types of a dpp::command_option. + * The `int64_t` is for the integer range or string length that can be entered. The `double` is for the decimal range that can be entered + */ +typedef std::variant command_option_range; + +/** + * @brief Each command option is a command line parameter. + * It can have a type (see dpp::command_option_type), a name, + * a description, can be required or optional, and can have + * zero or more choices (for multiple choice), plus options. + * Adding options acts like sub-commands and can contain more + * options. + */ +struct DPP_EXPORT command_option : public json_interface { + command_option_type type; //!< Option type (what type of value is accepted) + std::string name; //!< Option name (1-32 chars) + std::string description; //!< Option description (1-100 chars) + bool required; //!< True if this is a mandatory parameter + bool focused; //!< True if the user is typing in this field, when sent via autocomplete + command_value value; //!< Set only by autocomplete went sent as part of an interaction + std::vector choices; //!< List of choices for multiple choice command + bool autocomplete; //!< True if this option supports auto completion + std::vector options; //!< Sub-commands + std::vector channel_types; //!< Allowed channel types for channel snowflake id options + // Combines the `min_length` and `max_length` field by storing its value in the int64_t of the command_option_range + command_option_range min_value; //!< Minimum value/length that can be entered, for dpp::co_number, dpp::co_integer and dpp::co_string types only + command_option_range max_value; //!< Maximum value/length that can be entered, for dpp::co_number, dpp::co_integer and dpp::co_string types only + std::map name_localizations; //!< Localisations of command name + std::map description_localizations; //!< Localisations of command description + + + /** + * @brief Construct a new command option object + */ + command_option() = default; + + /** + * @brief Destroy the command option object + */ + virtual ~command_option() = default; + + /** + * @brief Add a localisation for this slash command option + * @see https://discord.com/developers/docs/reference#locales + * @param language Name of language, see the list of locales linked to above + * @param _name name of slash command option in the specified language + * @param _description description of slash command option in the specified language (optional) + * @return command_option& reference to self for fluent chaining + */ + command_option& add_localization(const std::string& language, const std::string& _name, const std::string& _description = ""); + + /** + * @brief Construct a new command option object + * + * @param t Option type + * @param name Option name + * @param description Option description + * @param required True if this is a mandatory parameter + */ + command_option(command_option_type t, const std::string &name, const std::string &description, bool required = false); + + /** + * @brief Add a multiple choice option + * + * @param o choice to add + * @return command_option& returns a reference to self for chaining of calls + * @throw dpp::logic_exception command_option is an autocomplete, so choices cannot be added + */ + command_option& add_choice(const command_option_choice &o); + + /** + * @brief Set the minimum numeric value of the option. + * Only valid if the type is dpp::co_number or dpp::co_integer. + * @param min_v Minimum value + * @return command_option& returns a reference to self for chaining of calls + */ + command_option& set_min_value(command_option_range min_v); + + /** + * @brief Set the maximum numeric value of the option. + * Only valid if the type is dpp::co_number or dpp::co_integer. + * @param max_v Maximum value + * @return command_option& returns a reference to self for chaining of calls + */ + command_option& set_max_value(command_option_range max_v); + + /** + * @brief Set the minimum string length of the option. Must be between 0 and 6000 (inclusive). + * Only valid if the type is dpp::co_string + * @param min_v Minimum value + * @return command_option& returns a reference to self for chaining of calls + */ + command_option& set_min_length(command_option_range min_v); + + /** + * @brief Set the maximum string length of the option. Must be between 1 and 6000 (inclusive). + * Only valid if the type is dpp::co_string + * @param max_v Maximum value + * @return command_option& returns a reference to self for chaining of calls + */ + command_option& set_max_length(command_option_range max_v); + + /** + * @brief Add a sub-command option + * + * @param o Sub-command option to add + * @return command_option& return a reference to self for chaining of calls + */ + command_option& add_option(const command_option &o); + + /** + * @brief Add channel type for option (only for co_channel type options) + * + * @param ch type to set + * @return command_option& return a reference to self for chaining of calls + */ + command_option& add_channel_type(const channel_type ch); + + /** + * @brief Set the auto complete state + * + * @param autocomp True to enable auto completion for this option + * @return command_option& return a reference to self for chaining of calls + * @throw dpp::logic_exception You attempted to enable auto complete on a command_option that has choices added to it + */ + command_option& set_auto_complete(bool autocomp); + + /** + * @brief Fill object properties from JSON. Fills options recursively. + * + * @param j JSON to fill from + * @return command_option& Reference to self + */ + command_option& fill_from_json(nlohmann::json* j); +}; + +/** + * @brief helper function to serialize a command_option to json + * + * @see https://github.com/nlohmann/json#arbitrary-types-conversions + * + * @param j output json object + * @param opt command_option to be serialized + */ +void to_json(nlohmann::json& j, const command_option& opt); + +/** + * @brief Response types when responding to an interaction within on_interaction_create. + */ +enum interaction_response_type { + ir_pong = 1, //!< Acknowledge a Ping + ir_channel_message_with_source = 4, //!< respond to an interaction with a message + ir_deferred_channel_message_with_source = 5, //!< Acknowledge an interaction and edit a response later, the user sees a loading state + ir_deferred_update_message = 6, //!< for components, acknowledge an interaction and edit the original message later; the user does not see a loading state + ir_update_message = 7, //!< for components, edit the message the component was attached to + ir_autocomplete_reply = 8, //!< Reply to autocomplete interaction. Be sure to do this within 500ms of the interaction! + ir_modal_dialog = 9, //!< A modal dialog box +}; + +/** + * @brief A response to an interaction, used to reply to a command and initiate + * a message, which can be hidden from others (ephemeral) or visible to all. + * + * The dpp::interaction_response object wraps a dpp::message object. To set the + * message as 'ephemeral' (e.g. only the command issuer can see it) you should + * add the dpp::m_ephemeral flag to the dpp::message::flags field. e.g.: + * + * `mymessage.flags |= dpp::m_ephemeral;` + */ +struct DPP_EXPORT interaction_response : public json_interface { + + /** + * @brief Response type from dpp::interaction_response_type. + * Should be one of ir_pong, ir_channel_message_with_source, + * or ir_deferred_channel_message_with_source. + */ + interaction_response_type type{}; + + /** + * @brief Message tied to this response. + */ + message msg{}; + + /** + * @brief Array of up to 25 autocomplete choices + */ + std::vector autocomplete_choices{}; + + /** + * @brief Construct a new interaction response object + */ + interaction_response() = default; + + /** + * @brief Construct a new interaction response object + * + * @param t Type of reply + */ + interaction_response(interaction_response_type t); + + /** + * @brief Construct a new interaction response object + * + * @param t Type of reply + * @param m Message to reply with + */ + interaction_response(interaction_response_type t, const message& m); + + /** + * @brief Construct a new interaction response object + * + * @param t Type of reply + * @param m Message to reply with + */ + interaction_response(interaction_response_type t, message&& m); + + /** + * @brief Fill object properties from JSON + * + * @param j JSON to fill from + * @return interaction_response& Reference to self + */ + interaction_response& fill_from_json(nlohmann::json* j); + + /** + * @brief Build a json string for this object + * + * @return std::string JSON string + */ + virtual std::string build_json(bool with_id = false) const; + + /** + * @brief Add a command option choice + * + * @param achoice command option choice to add + * @return interaction_response& Reference to self + */ + interaction_response& add_autocomplete_choice(const command_option_choice& achoice); + + /** + * @brief Destroy the interaction response object + */ + virtual ~interaction_response() = default; + +}; + +/** + * @brief Represents a modal dialog box response to an interaction. + * + * A dialog box is a modal popup which appears to the user instead of a message. One or more + * components are displayed on a form (the same component structure as within a dpp::message). + * When the user submits the form an on_form_submit event is dispatched to any listeners. + */ +struct DPP_EXPORT interaction_modal_response : public interaction_response, public json_interface { +private: + size_t current_row; +public: + /** + * @brief Custom ID for the modal form + */ + std::string custom_id; + + /** + * @brief Title of the modal form box (max 25 characters) + */ + std::string title; + + /** + * @brief List of components. All components must be placed within + * an action row, each outer vector is the action row. + */ + std::vector> components; + + /** + * @brief Construct a new interaction modal response object + */ + interaction_modal_response(); + + /** + * @brief Construct a new interaction modal response object + * + * @param _custom_id Custom ID of the modal form + * @param _title Title of the modal form. It will be truncated to the maximum length of 45 UTF-8 characters. + * @param _components Components to add to the modal form + */ + interaction_modal_response(const std::string& _custom_id, const std::string& _title, const std::vector _components = {}); + + /** + * @brief Set the custom id + * + * @param _custom_id custom id to set + * @return interaction_modal_response& Reference to self + */ + interaction_modal_response& set_custom_id(const std::string& _custom_id); + + /** + * @brief Set the title + * + * @param _title title to set + * @return interaction_modal_response& Reference to self + */ + interaction_modal_response& set_title(const std::string& _title); + + /** + * @brief Add a component to an interaction modal response + * + * @param c component to add + * @return interaction_modal_response& Reference to self + */ + interaction_modal_response& add_component(const component& c); + + /** + * @brief Add a new row to the interaction modal response. + * @note A modal response can have a maximum of five rows. + * @throw dpp::logic_exception if more than five rows are attempted to be added + * @return interaction_modal_response& Reference to self + */ + interaction_modal_response& add_row(); + + /** + * @brief Fill object properties from JSON + * + * @param j JSON to fill from + * @return interaction_response& Reference to self + */ + interaction_modal_response& fill_from_json(nlohmann::json* j); + + /** + * @brief Build a json string for this object + * @param with_id include id in json output + * + * @return std::string JSON string + */ + std::string build_json(bool with_id = false) const; + + /** + * @brief Destroy the interaction modal response object + */ + virtual ~interaction_modal_response() = default; +}; + +/** + * @brief Resolved snowflake ids to users, guild members, roles and channels. You can use the `interaction::get_resolved_*` methods to easily get a resolved set + */ +struct DPP_EXPORT command_resolved { + /** + * @brief Resolved users + * @see interaction::get_resolved_user + */ + std::map users; + /** + * @brief Resolved guild members + * @see interaction::get_resolved_member + */ + std::map members; + /** + * @brief Resolved total guild member permissions including channel overwrites, permissions from roles and administrator privileges + * @see interaction::get_resolved_permission + */ + std::map member_permissions; + /** + * @brief Resolved roles + * @see interaction::get_resolved_role + */ + std::map roles; + /** + * @brief Resolved channels + * @see interaction::get_resolved_channel + */ + std::map channels; + /** + * @brief Resolved messages + * @see interaction::get_resolved_message + */ + std::map messages; + /** + * @brief Resolved attachments + * @see interaction::get_resolved_attachment + */ + std::map attachments; +}; + +/** + * @brief Values in the command interaction. + * These are the values specified by the user when actually issuing + * the command on a channel or in DM. + */ +struct DPP_EXPORT command_data_option { + std::string name; //!< the name of the parameter + command_option_type type; //!< value of ApplicationCommandOptionType + command_value value; //!< Optional: the value of the pair + std::vector options; //!< Optional: present if this option is a group or subcommand + bool focused; //!< Optional: true if this option is the currently focused option for autocomplete + + /** + * @brief Check if the value variant holds std::monostate and options vector is empty (i.e. the option wasn't supplied) + * @return bool true, if value variant holds std::monostate and options vector is empty + */ + bool empty() { + return std::holds_alternative(value) && options.empty(); + } + + /** + * @brief Get an option value by index + * + * @tparam T Type to get from the parameter + * @param index index of the option + * @return T returned type + */ + template T& get_value(size_t index) { + return std::get(options.at(index).value); + } +}; + +/** + * @brief helper function to deserialize a command_data_option from json + * + * @see https://github.com/nlohmann/json#arbitrary-types-conversions + * + * @param j output json object + * @param cdo command_data_option to be deserialized + */ +void from_json(const nlohmann::json& j, command_data_option& cdo); + +/** Types of interaction in the dpp::interaction class + */ +enum interaction_type { + it_ping = 1, //!< ping + it_application_command = 2, //!< application command (slash command) + it_component_button = 3, //!< button click or select menu chosen (component interaction) + it_autocomplete = 4, //!< Autocomplete interaction + it_modal_submit = 5, //!< Modal form submission +}; + +/** + * @brief Right-click context menu types + */ +enum slashcommand_contextmenu_type { + ctxm_none = 0, //!< Undefined context menu type + ctxm_chat_input = 1, //!< DEFAULT, these are the slash commands you're used to + ctxm_user = 2, //!< Add command to user context menu + ctxm_message = 3 //!< Add command to message context menu +}; + +/** + * @brief Details of a command within an interaction. + * This subobject represents the application command associated + * with the interaction. + */ +struct DPP_EXPORT command_interaction { + snowflake id; //!< the ID of the invoked command + std::string name; //!< the name of the invoked command + std::vector options; //!< Optional: the params + values from the user + slashcommand_contextmenu_type type; //!< type of the command interaction + dpp::snowflake target_id; //!< Non-zero target ID for context menu actions. e.g. user id or message id whom clicked or tapped with the context menu https://discord.com/developers/docs/interactions/application-commands#user-commands + + /** + * @brief Get an option value by index + * + * @tparam T Type to get from the parameter + * @param index index of the option + * @return T returned type + */ + template T& get_value(size_t index) { + return std::get(options.at(index).value); + } + + /** + * @brief Return a ping/mention for the slash command + * + * @return std::string mention. e.g. `` + * @note If you want a mention for a subcommand or subcommand group, you can use dpp::utility::slashcommand_mention + */ + std::string get_mention() const; +}; + +/** + * @brief helper function to deserialize a command_interaction from json + * + * @see https://github.com/nlohmann/json#arbitrary-types-conversions + * + * @param j output json object + * @param ci command_interaction to be deserialized + */ +void from_json(const nlohmann::json& j, command_interaction& ci); + +/** + * @brief A button click for a button component + */ +struct DPP_EXPORT component_interaction { + /** + * @brief Component type (dpp::component_type) + */ + uint8_t component_type; + /** + * @brief Custom ID set when created + */ + std::string custom_id; + /** + * @brief Possible values for a drop down list + */ + std::vector values; +}; + +/** + * @brief An auto complete interaction + */ +struct DPP_EXPORT autocomplete_interaction : public command_interaction { +}; + +/** + * @brief helper function to deserialize a component_interaction from json + * + * @see https://github.com/nlohmann/json#arbitrary-types-conversions + * + * @param j output json object + * @param bi button_interaction to be deserialized + */ +void from_json(const nlohmann::json& j, component_interaction& bi); + +/** + * @brief helper function to deserialize an autocomplete_interaction from json + * + * @see https://github.com/nlohmann/json#arbitrary-types-conversions + * + * @param j output json object + * @param ai autocomplete_interaction to be deserialized + */ +void from_json(const nlohmann::json& j, autocomplete_interaction& ai); + +/** + * @brief An interaction represents a user running a command and arrives + * via the dpp::cluster::on_interaction_create event. This is further split + * into the events on_form_submit, on_slashcommand, on_user_context_menu, + * on_button_click, on_select_menu, etc. + */ +class DPP_EXPORT interaction : public managed, public json_interface { + + /** + * @brief Get a resolved object from the resolved set + * + * @tparam T type of object to retrieve + * @tparam C container defintion for resolved container + * @param id Snowflake ID + * @param resolved_set container for the type + * @return const T& retrieved type + * @throws dpp::logic_exception on object not found in resolved set + */ + template const T& get_resolved(snowflake id, const C& resolved_set) const { + auto i = resolved_set.find(id); + if (i == resolved_set.end()) { + throw dpp::logic_exception("ID not found in resolved properties of application command"); + } + return i->second; + } + +public: + snowflake application_id; //!< id of the application this interaction is for + uint8_t type; //!< the type of interaction (dpp::interaction_type) + std::variant data; //!< Optional: the command data payload + snowflake guild_id; //!< Optional: the guild it was sent from + snowflake channel_id; //!< Optional: the channel it was sent from + dpp::channel channel; //!< Optional: The partial channel object where it was sent from + snowflake message_id; //!< Originating message id for context menu actions + permission app_permissions; //!< Permissions of the bot in the channel/guild where this command was issued + message msg; //!< Originating message for context menu actions + guild_member member; //!< Optional: guild member data for the invoking user, including permissions. Filled when the interaction is invoked in a guild + user usr; //!< User object for the invoking user + std::string token; //!< a continuation token for responding to the interaction + uint8_t version; //!< read-only property, always 1 + command_resolved resolved; //!< Resolved data e.g. users, members, roles, channels, permissions, etc. + std::string locale; //!< User's [locale](https://discord.com/developers/docs/reference#locales) (language) + std::string guild_locale; //!< Guild's locale (language) - for guild interactions only + cache_policy_t cache_policy; //!< Cache policy from cluster + + /** + * @brief Construct a new interaction object + */ + interaction(); + + /** + * @brief Destroy the interaction object + */ + virtual ~interaction() = default; + + /** + * @brief Get a user associated with the slash command from the resolved list. + * The resolved list contains associated structures for this command and does not + * use the cache or require any extra API calls. + * + * @param id User snowflake ID to find + * @return const dpp::user& user + * @throws dpp::logic_exception on object not found in resolved set + */ + const dpp::user& get_resolved_user(snowflake id) const; + + /** + * @brief Get the channel this command originated on + * + * @return const dpp::channel& channel + * @throws dpp::logic_exception Command originated from a DM or channel not in cache + */ + const dpp::channel& get_channel() const; + + /** + * @brief Get the guild this command originated on + * + * @return const dpp::guild& guild + * @throws dpp::logic_exception Command originated from a DM or guild not in cache + */ + const dpp::guild& get_guild() const; + + /** + * @brief Get the user who issued this command + * + * @return const dpp::user& user + */ + const dpp::user& get_issuing_user() const; + + /** + * @brief Get the message this action refers to if it is a context menu command + * + * @return const dpp::message& context menu message + */ + const dpp::message& get_context_message() const; + + /** + * @brief Get a role associated with the slash command from the resolved list. + * The resolved list contains associated structures for this command and does not + * use the cache or require any extra API calls. + * + * @param id Role snowflake ID to find + * @return const dpp::role& role + * @throws dpp::logic_exception on object not found in resolved set + */ + const dpp::role& get_resolved_role(snowflake id) const; + + /** + * @brief Get a channel associated with the slash command from the resolved list. + * The resolved list contains associated structures for this command and does not + * use the cache or require any extra API calls. + * + * @param id Channel snowflake ID to find + * @return const dpp::channel& channel + * @throws dpp::logic_exception on object not found in resolved set + */ + const dpp::channel& get_resolved_channel(snowflake id) const; + + /** + * @brief Get a guild member associated with the slash command from the resolved list. + * The resolved list contains associated structures for this command and does not + * use the cache or require any extra API calls. + * + * @param id User snowflake ID to find + * @return const dpp::guild_member& guild member + * @throws dpp::logic_exception on object not found in resolved set + */ + const dpp::guild_member& get_resolved_member(snowflake id) const; + + /** + * @brief Get a permission associated with the slash command from the resolved list. + * The resolved list contains associated structures for this command and does not + * use the cache or require any extra API calls. + * + * @param id User snowflake ID to find + * @return const dpp::permission& total permissions for the user including overrides on + * the channel where the command was issued. + * @throws dpp::logic_exception on object not found in resolved set + */ + const dpp::permission& get_resolved_permission(snowflake id) const; + + /** + * @brief Get a message associated with the slash command from the resolved list. + * The resolved list contains associated structures for this command and does not + * use the cache or require any extra API calls. + * + * @param id Message snowflake ID to find + * @return const dpp::message& message + * @throws dpp::logic_exception on object not found in resolved set + */ + const dpp::message& get_resolved_message(snowflake id) const; + + /** + * @brief Get an uploaded attachment associated with the slash command from the resolved list. + * The resolved list contains associated structures for this command and does not + * use the cache or require any extra API calls. + * + * @param id Attachment snowflake ID to find + * @return const dpp::attachment& file attachment + * @throws dpp::logic_exception on object not found in resolved set + */ + const dpp::attachment& get_resolved_attachment(snowflake id) const; + + /** + * @brief Get the command interaction object + * + * @throw dpp::logic_exception if the interaction is not for a command + * + * @return command_interaction object + */ + command_interaction get_command_interaction() const; + + /** + * @brief Get the component interaction object + * + * @throw dpp::logic_exception if the interaction is not for a component + * + * @return component_interaction object + */ + component_interaction get_component_interaction() const; + + /** + * @brief Get the autocomplete interaction object + * + * @throw dpp::logic_exception if the interaction is not for an autocomplete + * + * @return autocomplete_interaction object + */ + autocomplete_interaction get_autocomplete_interaction() const; + + /** + * @brief Get the command name for a command interaction + * + * @return std::string command interaction, or empty string if the interaction + * is not for a command. + */ + std::string get_command_name() const; + + /** + * @brief Fill object properties from JSON + * + * @param j JSON to fill from + * @return interaction& Reference to self + */ + interaction& fill_from_json(nlohmann::json* j); + + /** + * @brief Build a json string for this object + * + * @param with_id True if to include the ID in the JSON + * @return std::string JSON string + */ + std::string build_json(bool with_id = false) const; +}; + +/** + * @brief helper function to deserialize an interaction from json + * + * @see https://github.com/nlohmann/json#arbitrary-types-conversions + * + * @param j output json object + * @param i interaction to be deserialized + */ +void from_json(const nlohmann::json& j, interaction& i); + +/** + * @brief type of permission in the dpp::command_permission class + */ +enum command_permission_type { + /** + * @brief Role permission + * + */ + cpt_role = 1, + /** + * @brief User permission + * + */ + cpt_user = 2, +}; + +/** + * @brief Application command permissions allow you to enable or + * disable commands for specific users or roles within a guild + */ +class DPP_EXPORT command_permission : public json_interface { +public: + snowflake id; //!< the ID of the role or user + command_permission_type type; //!< the type of permission + bool permission; //!< true to allow, false, to disallow + + /** + * @brief Construct a new command permission object + */ + command_permission() = default; + + virtual ~command_permission() = default; + + /** + * @brief Construct a new command permission object + * + * @param id The ID of the role or user + * @param t The permission type + * @param permission True to allow, false, to disallow + */ + command_permission(snowflake id, const command_permission_type t, bool permission); + + /** + * @brief Fill object properties from JSON + * + * @param j JSON to fill from + * @return command_permission& Reference to self + */ + command_permission &fill_from_json(nlohmann::json *j); +}; + +/** + * @brief helper function to serialize a command_permission to json + * + * @see https://github.com/nlohmann/json#arbitrary-types-conversions + * + * @param j output json object + * @param cp command_permission to be serialized + */ +void to_json(nlohmann::json& j, const command_permission& cp); + +/** + * @brief Returned when fetching the permissions for a command in a guild. + */ +class DPP_EXPORT guild_command_permissions : public json_interface { +public: + snowflake id; //!< the id of the command + snowflake application_id; //!< the id of the application the command belongs to + snowflake guild_id; //!< the id of the guild + std::vector permissions; //!< the permissions for the command in the guild + + /** + * @brief Construct a new guild command permissions object + */ + guild_command_permissions(); + + virtual ~guild_command_permissions() = default; + + /** + * @brief Fill object properties from JSON + * + * @param j JSON to fill from + * @return guild_command_permissions& Reference to self + */ + guild_command_permissions &fill_from_json(nlohmann::json *j); + +}; + +/** + * @brief helper function to serialize a guild_command_permissions to json + * + * @see https://github.com/nlohmann/json#arbitrary-types-conversions + * + * @param j output json object + * @param gcp guild_command_permissions to be serialized + */ +void to_json(nlohmann::json& j, const guild_command_permissions& gcp); + +/** + * @brief Represents an application command, created by your bot + * either globally, or on a guild. + */ +class DPP_EXPORT slashcommand : public managed, public json_interface { +public: + /** + * @brief Application id (usually matches your bots id) + */ + snowflake application_id; + + /** + * @brief Context menu type, defaults to dpp::ctxm_chat_input + */ + slashcommand_contextmenu_type type; + + /** + * @brief Command name (1-32 chars) + */ + std::string name; + + /** + * @brief Command description (1-100 chars) + */ + std::string description; + + /** + * @brief Command options (parameters) + */ + std::vector options; + + /** + * @brief Whether the command is enabled by default when the app is added to a guild. + * This has no effect as the default_member_permissions value is used instead. + * @deprecated Discord discourage use of this value and instead you should use slashcommand::default_member_permissions. + */ + bool default_permission; + + /** + * @brief command permissions + * @deprecated Discord discourage use of this value and instead you should use default_member_permissions. + */ + std::vector permissions; + + /** + * @brief autoincrementing version identifier updated during substantial record changes + */ + snowflake version; + + /** + * @brief Localisations of command name + */ + std::map name_localizations; + + /** + * @brief Localisations of command description + */ + std::map description_localizations; + + /** + * @brief The default permissions of this command on a guild. + * D++ defaults this to dpp::p_use_application_commands. + * @note You can set it to 0 to disable the command for everyone except admins by default + */ + permission default_member_permissions; + + /** + * @brief True if this command should be allowed in a DM + * D++ defaults this to false. Cannot be set to true in a guild + * command, only a global command. + */ + bool dm_permission; + + /** + * @brief Indicates whether the command is [age-restricted](https://discord.com/developers/docs/interactions/application-commands#agerestricted-commands). + * Defaults to false + */ + bool nsfw; + + /** + * @brief Construct a new slashcommand object + */ + slashcommand(); + + /** + * @brief Construct a new slashcommand object + * + * @param _name Command name + * @param _description Command description + * @param _application_id Application id (usually the bot's user id) + */ + slashcommand(const std::string &_name, const std::string &_description, const dpp::snowflake _application_id); + + /** + * @brief Construct a new slashcommand object + * + * @param _name Command name + * @param _type Context menu type + * @param _application_id Application id (usually the bot's user id) + */ + slashcommand(const std::string &_name, const slashcommand_contextmenu_type _type, const dpp::snowflake _application_id); + + /** + * @brief Destroy the slashcommand object + */ + virtual ~slashcommand() = default; + + /** + * @brief Add a localisation for this slash command + * @see https://discord.com/developers/docs/reference#locales + * @param language Name of language, see the list of locales linked to above + * @param _name name of slash command in the specified language + * @param _description description of slash command in the specified language (optional) + * @return slashcommand& reference to self for chaining of calls + */ + slashcommand& add_localization(const std::string& language, const std::string& _name, const std::string& _description = ""); + + /** + * @brief Set the dm permission for the command + * + * @param dm true to allow this command in dms + * @return slashcommand& reference to self for chaining of calls + */ + slashcommand& set_dm_permission(bool dm); + + /** + * @brief Set whether the command should be age-restricted or not + * + * @param is_nsfw true if the command should be age-restricted + * @return slashcommand& reference to self for chaining of calls + */ + slashcommand& set_nsfw(bool is_nsfw); + + /** + * @brief Set the default permissions of the slash command + * + * @param defaults default permissions to set. This is a permission bitmask of bits from dpp::permissions + * @note You can set it to 0 to disable the command for everyone except admins by default + * + * @return slashcommand& reference to self for chaining of calls + */ + slashcommand& set_default_permissions(uint64_t defaults); + + /** + * @brief Add an option (parameter) + * + * @param o option (parameter) to add + * @return slashcommand& reference to self for chaining of calls + */ + slashcommand& add_option(const command_option &o); + + /** + * @brief Set the type of the slash command (only for context menu entries) + * + * @param _type Type of context menu entry this command represents + * @note If the type is dpp::ctxm_chat_input, the command name will be set to lowercase. + * @return slashcommand& reference to self for chaining of calls + */ + slashcommand& set_type(slashcommand_contextmenu_type _type); + + /** + * @brief Set the name of the command + * + * @param n name of command + * @note The maximum length of a command name is 32 UTF-8 codepoints. + * If your command name is longer than this, it will be truncated. + * The command name will be set to lowercase when the type is the default dpp::ctxm_chat_input. + * @return slashcommand& reference to self for chaining of calls + */ + slashcommand& set_name(const std::string &n); + + /** + * @brief Set the description of the command + * + * @param d description + * @note The maximum length of a command description is 100 UTF-8 codepoints. + * If your command description is longer than this, it will be truncated. + * @return slashcommand& reference to self for chaining of calls + */ + slashcommand& set_description(const std::string &d); + + /** + * @brief Set the application id of the command + * + * @param i application id + * @return slashcommand& reference to self for chaining of calls + */ + slashcommand& set_application_id(snowflake i); + + /** + * @brief Adds a permission to the command + * + * @param p permission to add + * @return slashcommand& reference to self for chaining of calls + * @deprecated Discord discourage use of this value and instead you should use default_member_permissions. + */ + slashcommand& add_permission(const command_permission& p); + + /** + * @brief Disable default permissions, command will be unusable unless + * permissions are overridden with add_permission and + * dpp::guild_command_edit_permissions + * + * @return slashcommand& reference to self for chaining of calls + * @deprecated Discord discourage use of this value and instead you should use default_member_permissions. + */ + slashcommand& disable_default_permissions(); + + /** + * @brief Return a ping/mention for the slash command + * + * @return std::string mention. e.g. `` + * @note If you want a mention for a subcommand or subcommand group, you can use dpp::utility::slashcommand_mention + */ + std::string get_mention() const; + + /** + * @brief Fill object properties from JSON + * + * @param j JSON to fill from + * @return slashcommand& Reference to self + */ + slashcommand& fill_from_json(nlohmann::json* j); + + /** + * @brief Build a json string for this object + * + * @param with_id True if to include the ID in the JSON + * @return std::string JSON string + */ + std::string build_json(bool with_id = false) const; +}; + +/** + * @brief helper function to serialize a slashcommand to json + * + * @see https://github.com/nlohmann/json#arbitrary-types-conversions + * + * @param j output json object + * @param cmd slashcommand to be serialized + */ +void to_json(nlohmann::json& j, const slashcommand& cmd); + +/** + * @brief A group of application slash commands + */ +typedef std::unordered_map slashcommand_map; + +/** + * @brief A group of guild command permissions + */ +typedef std::unordered_map guild_command_permissions_map; + +} // namespace dpp diff --git a/include/dpp/application.h b/include/dpp/application.h new file mode 100644 index 0000000..24a0b49 --- /dev/null +++ b/include/dpp/application.h @@ -0,0 +1,183 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +namespace dpp { + +/** + * @brief status of a member of a team who maintain a bot/application + */ +enum team_member_status : uint8_t { + /// User was invited to the team + tms_invited = 1, + /// User has accepted membership onto the team + tms_accepted = 2 +}; + +/** + * @brief Flags for a bot or application + */ +enum application_flags : uint32_t { + /// Indicates if an app uses the Auto Moderation API + apf_application_automod_rule_create_badge = (1 << 6), + /// Has gateway presence intent + apf_gateway_presence = (1 << 12), + /// Has gateway presence intent for <100 guilds + apf_gateway_presence_limited = (1 << 13), + /// Has guild members intent + apf_gateway_guild_members = (1 << 14), + /// Has guild members intent for <100 guilds + apf_gateway_guild_members_limited = (1 << 15), + /// Verification is pending + apf_verification_pending_guild_limit = (1 << 16), + /// Embedded + apf_embedded = (1 << 17), + /// Has approval for message content + apf_gateway_message_content = (1 << 18), + /// Has message content, but <100 guilds + apf_gateway_message_content_limited = (1 << 19), + /// Indicates if the app has registered global application commands + apf_application_command_badge = (1 << 23) +}; + +/** + * @brief Represents the settings for the bot/application's in-app authorization link + */ +struct DPP_EXPORT application_install_params { + permission permissions; //!< A bitmask of dpp::permissions to request for the bot role + std::vector scopes; //!< The [scopes](https://discord.com/developers/docs/topics/oauth2#shared-resources-oauth2-scopes) as strings to add the application to the server with +}; + +/** + * @brief Team member role types for application team members. + * + * These are hard coded to string forms by discord. If further types are added, + * this enum will be extended to support them. + */ +enum team_member_role_t : uint8_t { + tmr_owner, //!< Team owner + tmr_admin, //!< Team admin + tmr_developer, //!< Developer + tmr_readonly, //!< Read-Only +}; + +/** + * @brief Represents a team member on a team who maintain a bot/application + */ +class DPP_EXPORT team_member { +public: + team_member_status membership_state; //!< the user's membership state on the team + std::string permissions; //!< will always be [""] + snowflake team_id; //!< the id of the parent team of which they are a member + user member_user; //!< the avatar, discriminator, id, and username of the user + team_member_role_t member_role; //!< the role of the user +}; + +/** + * @brief Represents a team of users who maintain a bot/application + */ +class DPP_EXPORT app_team { +public: + utility::iconhash icon; //!< a hash of the image of the team's icon (may be empty) + snowflake id; //!< the unique id of the team + std::vector members; //!< the members of the team + std::string name; //!< the name of the team + snowflake owner_user_id; //!< the user id of the current team owner +}; + +/** + * @brief The application class represents details of a bot application + */ +class DPP_EXPORT application : public managed, public json_interface { +public: + std::string name; //!< the name of the app + utility::iconhash icon; //!< the icon hash of the app (may be empty) + std::string description; //!< the description of the app + std::string rpc_origins; //!< Optional: an array of rpc origin urls, if rpc is enabled + bool bot_public; //!< when false only app owner can join the app's bot to guilds + bool bot_require_code_grant; //!< when true the app's bot will only join upon completion of the full oauth2 code grant flow + std::string terms_of_service_url; //!< Optional: the url of the app's terms of service + std::string privacy_policy_url; //!< Optional: the url of the app's privacy policy + user owner; //!< Optional: partial user object containing info on the owner of the application + std::string summary; //!< if this application is a game sold on Discord, this field will be the summary field for the store page of its primary sku @deprecated Will be removed in v11 + std::string verify_key; //!< the hex encoded key for verification in interactions and the GameSDK's GetTicket + app_team team; //!< if the application belongs to a team, this will be a list of the members of that team (may be empty) + snowflake guild_id; //!< Optional: if this application is a game sold on Discord, this field will be the guild to which it has been linked + snowflake primary_sku_id; //!< Optional: if this application is a game sold on Discord, this field will be the id of the "Game SKU" that is created, if exists + std::string slug; //!< Optional: if this application is a game sold on Discord, this field will be the URL slug that links to the store page + utility::iconhash cover_image; //!< Optional: the application's default rich presence invite cover image hash + uint32_t flags; //!< Optional: the application's public flags + std::vector tags; //!< Up to 5 tags describing the content and functionality of the application + application_install_params install_params; //!< Settings for the application's default in-app authorization link, if enabled + std::string custom_install_url; //!< The application's default custom authorization link, if enabled + std::string role_connections_verification_url; //!< The application's role connection verification entry point, which when configured will render the app as a verification method in the guild role verification configuration + + /** Constructor */ + application(); + + /** Destructor */ + ~application(); + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + application& fill_from_json(nlohmann::json* j); + + /** + * @brief Get the application's cover image url if they have one, otherwise returns an empty string + * + * @param size The size of the cover image in pixels. It can be any power of two between 16 and 4096, + * otherwise the default sized cover image is returned. + * @param format The format to use for the avatar. It can be one of `i_webp`, `i_jpg` or `i_png`. + * @return std::string cover image url or an empty string, if required attributes are missing or an invalid format was passed + */ + std::string get_cover_image_url(uint16_t size = 0, const image_type format = i_png) const; + + /** + * @brief Get the application's icon url if they have one, otherwise returns an empty string + * + * @param size The size of the icon in pixels. It can be any power of two between 16 and 4096, + * otherwise the default sized icon is returned. + * @param format The format to use for the avatar. It can be one of `i_webp`, `i_jpg` or `i_png`. + * @return std::string icon url or an empty string, if required attributes are missing or an invalid format was passed + */ + std::string get_icon_url(uint16_t size = 0, const image_type format = i_png) const; +}; + +/** A group of applications. + * This is not currently ever sent by Discord API but the DPP standard setup for + * objects that can be received by REST has the possibility for this, so this exists. + * Don't ever expect to see one at present. + */ +typedef std::unordered_map application_map; + +} // namespace dpp diff --git a/include/dpp/auditlog.h b/include/dpp/auditlog.h new file mode 100644 index 0000000..e3bf153 --- /dev/null +++ b/include/dpp/auditlog.h @@ -0,0 +1,231 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ + +#pragma once +#include +#include +#include +#include +#include + +namespace dpp { + +/** + * @brief Defines types of audit log entry + */ +enum audit_type { + /// Guild update + aut_guild_update = 1, + /// Channel create + aut_channel_create = 10, + /// Channel update + aut_channel_update = 11, + /// Channel delete + aut_channel_delete = 12, + /// Channel overwrite create + aut_channel_overwrite_create = 13, + /// Channel overwrite update + aut_channel_overwrite_update = 14, + /// Channel overwrite delete + aut_channel_overwrite_delete = 15, + /// Channel member kick + aut_member_kick = 20, + /// Channel member prune + aut_member_prune = 21, + /// Channel member ban add + aut_member_ban_add = 22, + /// Channel member ban remove + aut_member_ban_remove = 23, + /// Guild member update + aut_member_update = 24, + /// Guild member role update + aut_member_role_update = 25, + /// Guild member move + aut_member_move = 26, + /// Guild member voice disconnect + aut_member_disconnect = 27, + /// Guild bot add + aut_bot_add = 28, + /// Guild role create + aut_role_create = 30, + /// Guild role update + aut_role_update = 31, + /// Guild role delete + aut_role_delete = 32, + /// Guild invite create + aut_invite_create = 40, + /// Guild invite update + aut_invite_update = 41, + /// Guild invite delete + aut_invite_delete = 42, + /// Guild webhook create + aut_webhook_create = 50, + /// Guild webhook update + aut_webhook_update = 51, + /// Guild webhook delete + aut_webhook_delete = 52, + /// Guild emoji create + aut_emoji_create = 60, + /// Guild emoji update + aut_emoji_update = 61, + /// Guild emoji delete + aut_emoji_delete = 62, + /// Guild message delete + aut_message_delete = 72, + /// Guild message bulk delete + aut_message_bulk_delete = 73, + /// Guild message pin + aut_message_pin = 74, + /// Guild message unpin + aut_message_unpin = 75, + /// Guild integration create + aut_integration_create = 80, + /// Guild integration update + aut_integration_update = 81, + /// Guild integration delete + aut_integration_delete = 82, + /// Stage instance create + aut_stage_instance_create = 83, + /// Stage instance update + aut_stage_instance_update = 84, + /// stage instance delete + aut_stage_instance_delete = 85, + /// Sticker create + aut_sticker_create = 90, + /// Sticker update + aut_sticker_update = 91, + /// Sticker delete + aut_sticker_delete = 92, + /// Scheduled event creation + aut_guild_scheduled_event_create = 100, + /// Scheduled event update + aut_guild_scheduled_event_update = 101, + /// Scheduled event deletion + aut_guild_scheduled_event_delete = 102, + /// Thread create + aut_thread_create = 110, + /// Thread update + aut_thread_update = 111, + /// Thread delete + aut_thread_delete = 112, + /// Application command permissions update + aut_appcommand_permission_update = 121, + /// Auto moderation rule creation + aut_automod_rule_create = 140, + /// Auto moderation rule update + aut_automod_rule_update = 141, + /// Auto moderation rule deletion + aut_automod_rule_delete = 142, + /// Message was blocked by Auto Moderation + aut_automod_block_message = 143, + /// Message was flagged by Auto Moderation + aut_automod_flag_to_channel = 144, + /// Member was timed out by Auto Moderation + aut_automod_user_communication_disabled = 145, + /// Creator monetization request was created + aut_creator_monetization_request_created = 150, + /// Creator monetization terms were accepted + aut_creator_monetization_terms_accepted = 151, +}; + +/** + * @brief Defines audit log changes + */ +struct DPP_EXPORT audit_change { + /// Optional: Serialised new value of the change, e.g. for nicknames, the new nickname + std::string new_value; + /// Optional: Serialised old value of the change, e.g. for nicknames, the old nickname + std::string old_value; + /** + * The property name that was changed, e.g. `nick` for nickname changes + * @note For dpp::aut_appcommand_permission_update updates the key is the id of the user, channel, role, or a permission constant that was updated instead of an actual property name + */ + std::string key; +}; + +/** + * @brief Extra information for an audit log entry + */ +struct DPP_EXPORT audit_extra { + std::string automod_rule_name; //!< Name of the Auto Moderation rule that was triggered + std::string automod_rule_trigger_type; //!< Trigger type of the Auto Moderation rule that was triggered + std::string delete_member_days; //!< number of days after which inactive members were kicked + std::string members_removed; //!< number of members removed by the prune + snowflake channel_id; //!< channel in which the entities were targeted + snowflake message_id; //!< id of the message that was targeted + std::string count; //!< number of entities that were targeted + snowflake id; //!< id of the overwritten entity + std::string type; //!< type of overwritten entity - "0" for "role" or "1" for "member" + std::string role_name; //!< name of the role if type is "0" (not present if type is "1") + snowflake application_id; //!< ID of the app whose permissions were targeted +}; + +/** + * @brief An individual audit log entry + */ +struct DPP_EXPORT audit_entry : public json_interface { + snowflake id; //!< id of the entry + /** + * ID of the affected entity (webhook, user, role, etc.) (may be empty) + * @note For dpp::audit_type::aut_appcommand_permission_update updates, it's the command ID or the app ID + */ + snowflake target_id; + std::vector changes; //!< Optional: changes made to the target_id + snowflake user_id; //!< the user or app that made the changes (may be empty) + audit_type type; //!< type of action that occurred + std::optional extra; //!< Optional: additional info for certain action types + std::string reason; //!< Optional: the reason for the change (1-512 characters) + + /** Constructor */ + audit_entry(); + + /** Destructor */ + virtual ~audit_entry() = default; + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + audit_entry& fill_from_json(nlohmann::json* j); +}; + +/** + * @brief The auditlog class represents the audit log entries of a guild. + */ +class DPP_EXPORT auditlog : public json_interface { +public: + std::vector entries; //!< Audit log entries + + /** Constructor */ + auditlog() = default; + + /** Destructor */ + virtual ~auditlog() = default; + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + auditlog& fill_from_json(nlohmann::json* j); +}; + +} // namespace dpp diff --git a/include/dpp/automod.h b/include/dpp/automod.h new file mode 100644 index 0000000..bd0d067 --- /dev/null +++ b/include/dpp/automod.h @@ -0,0 +1,375 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ + +#pragma once +#include +#include +#include +#include +#include +#include + +namespace dpp { + +/** + * @brief Possible types of preset filter lists + */ +enum automod_preset_type : uint8_t { + /** + * @brief Strong swearing + */ + amod_preset_profanity = 1, + /** + * @brief Sexual phrases and words + */ + amod_preset_sexual_content = 2, + /** + * @brief Racial and other slurs, hate speech + */ + amod_preset_slurs = 3, +}; + +/** + * @brief Action types to perform on filtering + */ +enum automod_action_type : uint8_t { + /** + * @brief Blocks the message and prevents it from being posted. + * A custom explanation can be specified and shown to members whenever their message is blocked + */ + amod_action_block_message = 1, + /** + * @brief Send an alert to a given channel + */ + amod_action_send_alert = 2, + /** + * @brief timeout the user + * @note Can only be set up for rules with trigger types of dpp::amod_type_keyword and dpp::amod_type_mention_spam + */ + amod_action_timeout = 3, +}; + +/** + * @brief Event types, only message send is currently supported + */ +enum automod_event_type : uint8_t { + /** + * @brief Trigger on message send or edit + */ + amod_message_send = 1, +}; + +/** + * @brief Types of moderation to trigger + */ +enum automod_trigger_type : uint8_t { + /** + * @brief Check if content contains words from a user defined list of keywords (max 6 of this type per guild) + */ + amod_type_keyword = 1, + /** + * @brief Harmful/malware links + * @deprecated Removed by Discord + */ + amod_type_harmful_link = 2, + /** + * @brief Check if content represents generic spam (max 1 of this type per guild) + */ + amod_type_spam = 3, + /** + * @brief Check if content contains words from discord pre-defined wordsets (max 1 of this type per guild) + */ + amod_type_keyword_preset = 4, + /** + * @brief Check if content contains more mentions than allowed (max 1 of this type per guild) + */ + amod_type_mention_spam = 5, +}; + +/** + * @brief Metadata associated with an automod action. Different fields are relevant based on the value of dpp::automod_rule::trigger_type. + */ +struct DPP_EXPORT automod_metadata : public json_interface { + /** + * @brief @brief Substrings which will be searched for in content (Maximum of 1000). + * + * Each keyword can be a phrase which contains multiple words. + * All keywords are case insensitive and can be up to 60 characters. + * + * Wildcard symbols (`*`) can be used to customize how each keyword will be matched. + * + * **Examples for the `*` wildcard symbol:** + * + * Prefix - word must start with the keyword + * + * | keyword | matches | + * |----------|-------------------------------------| + * | cat* | catch, Catapult, CAttLE | + * | the mat* | the matrix | + * + * Suffix - word must end with the keyword + * + * | keyword | matches | + * |----------|--------------------------| + * | *cat | wildcat, copyCat | + * | *the mat | breathe mat | + * + * Anywhere - keyword can appear anywhere in the content + * + * | keyword | matches | + * |-----------|-----------------------------| + * | \*cat* | location, eduCation | + * | \*the mat* | breathe matter | + * + * Whole Word - keyword is a full word or phrase and must be surrounded by whitespace at the beginning and end + * + * | keyword | matches | + * |---------|-------------| + * | cat | Cat | + * | the mat | the mat | + * + */ + std::vector keywords; + + /** + * @brief Regular expression patterns which will be matched against content (Maximum of 10). + * + * Only Rust flavored regex is currently supported, which can be tested in online editors such as [Rustexp](https://rustexp.lpil.uk/). + * Each regex pattern can be up to 260 characters. + */ + std::vector regex_patterns; + + /** + * @brief Preset keyword list types to moderate + * @see automod_preset_type + */ + std::vector presets; + + /** + * @brief Substrings which should not trigger the rule (Maximum of 100 for the trigger type dpp::amod_type_keyword, Maximum of 1000 for the trigger type dpp::amod_type_keyword_preset). + * + * Each keyword can be a phrase which contains multiple words. + * All keywords are case insensitive and can be up to 60 characters. + * + * Wildcard symbols (`*`) can be used to customize how each keyword will be matched. + * + * **Examples for the `*` wildcard symbol:** + * + * Prefix - word must start with the keyword + * + * | keyword | matches | + * |----------|-------------------------------------| + * | cat* | catch, Catapult, CAttLE | + * | the mat* | the matrix | + * + * Suffix - word must end with the keyword + * + * | keyword | matches | + * |----------|--------------------------| + * | *cat | wildcat, copyCat | + * | *the mat | breathe mat | + * + * Anywhere - keyword can appear anywhere in the content + * + * | keyword | matches | + * |-----------|-----------------------------| + * | \*cat* | location, eduCation | + * | \*the mat* | breathe matter | + * + * Whole Word - keyword is a full word or phrase and must be surrounded by whitespace at the beginning and end + * + * | keyword | matches | + * |---------|-------------| + * | cat | Cat | + * | the mat | the mat | + * + */ + std::vector allow_list; + + /** + * @brief Total number of unique role and user mentions allowed per message (Maximum of 50) + */ + uint8_t mention_total_limit; + + /** + * @brief Whether to automatically detect mention raids + */ + bool mention_raid_protection_enabled; + + /** + * @brief Construct a new automod metadata object + */ + automod_metadata(); + + /** + * @brief Destroy the automod metadata object + */ + virtual ~automod_metadata(); + + /** + * @brief Fill object properties from JSON + * + * @param j JSON to fill from + * @return automod_metadata& Reference to self + */ + automod_metadata& fill_from_json(nlohmann::json* j); + + /** + * @brief Build a json string for this object + * + * @return std::string JSON string + */ + virtual std::string build_json(bool with_id = false) const; + +}; + +/** + * @brief Represents an automod action + */ +struct DPP_EXPORT automod_action : public json_interface { + /** + * @brief Type of action to take + */ + automod_action_type type; + + /** + * @brief Channel ID to which user content should be logged, for type dpp::amod_action_send_alert + */ + snowflake channel_id; + + /** + * @brief Additional explanation that will be shown to members whenever their message is blocked. For type dpp::amod_action_block_message + */ + std::string custom_message; + + /** + * @brief Timeout duration in seconds (Maximum of 2419200), for dpp::amod_action_timeout + */ + uint32_t duration_seconds; + + /** + * @brief Construct a new automod action object + */ + automod_action(); + + /** + * @brief Destroy the automod action object + */ + virtual ~automod_action(); + + /** + * @brief Fill object properties from JSON + * + * @param j JSON to fill from + * @return automod_action& Reference to self + */ + automod_action& fill_from_json(nlohmann::json* j); + + /** + * @brief Build a json string for this object + * + * @return std::string JSON string + */ + virtual std::string build_json(bool with_id = false) const; +}; + +/** + * @brief Represents an automod rule + */ +class DPP_EXPORT automod_rule : public managed, public json_interface { +public: + /** + * @brief the id of this rule + */ + snowflake id; + /** + * @brief the guild which this rule belongs to + */ + snowflake guild_id; + /** + * @brief the rule name + */ + std::string name; + /** + * @brief The user which first created this rule + */ + snowflake creator_id; + /** + * @brief The rule event type + */ + automod_event_type event_type; + /** + * @brief The rule trigger type + */ + automod_trigger_type trigger_type; + /** + * @brief The rule trigger metadata + */ + automod_metadata trigger_metadata; + /** + * @brief the actions which will execute when the rule is triggered + */ + std::vector actions; + /** + * @brief Whether the rule is enabled + */ + bool enabled; + /** + * @brief the role ids that should not be affected by the rule (Maximum of 20) + */ + std::vector exempt_roles; + /** + * @brief the channel ids that should not be affected by the rule (Maximum of 50) + */ + std::vector exempt_channels; + + /** + * @brief Construct a new automod rule object + */ + automod_rule(); + + /** + * @brief Destroy the automod rule object + */ + virtual ~automod_rule(); + + /** + * @brief Fill object properties from JSON + * + * @param j JSON to fill from + * @return automod_rule& Reference to self + */ + automod_rule& fill_from_json(nlohmann::json* j); + + /** + * @brief Build a json string for this object + * + * @return std::string JSON string + */ + virtual std::string build_json(bool with_id = false) const; +}; + +/** A group of automod rules. + */ +typedef std::unordered_map automod_rule_map; + +} // namespace dpp diff --git a/include/dpp/ban.h b/include/dpp/ban.h new file mode 100644 index 0000000..ddf4fa1 --- /dev/null +++ b/include/dpp/ban.h @@ -0,0 +1,69 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ + +#pragma once +#include +#include +#include +#include +#include + +namespace dpp { + +/** + * @brief The ban class represents a ban on a guild. + * + */ +class DPP_EXPORT ban : public json_interface { +public: + /** The ban reason */ + std::string reason; + /** User ID the ban applies to */ + snowflake user_id; + + /** Constructor */ + ban(); + + /** Destructor */ + virtual ~ban() = default; + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + ban& fill_from_json(nlohmann::json* j); + + /** + * @brief Build json representation of a ban + * @param with_id Include ID in json + * + * @return std::string stringified json + */ + std::string build_json(bool with_id = false) const; +}; + +/** + * A group of bans. The key is the user ID + */ +typedef std::unordered_map ban_map; + +} // namespace dpp diff --git a/include/dpp/cache.h b/include/dpp/cache.h new file mode 100644 index 0000000..5895af5 --- /dev/null +++ b/include/dpp/cache.h @@ -0,0 +1,273 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ + +#pragma once +#include +#include +#include +#include +#include +#include + +namespace dpp { + +extern DPP_EXPORT std::unordered_map deletion_queue; +extern DPP_EXPORT std::mutex deletion_mutex; + +/** forward declaration */ +class guild_member; + +/** + * @brief A cache object maintains a cache of dpp::managed objects. + * + * This is for example users, channels or guilds. You may instantiate + * your own caches, to contain any type derived from dpp::managed including + * your own types. + * + * @note This class is critical to the operation of the library and therefore + * designed with thread safety in mind. + * @tparam T class type to store, which should be derived from dpp::managed. + */ +template class cache { +private: + /** + * @brief Mutex to protect the cache + * + * This is a shared mutex so reading is cheap. + */ + std::shared_mutex cache_mutex; + + /** + * @brief Container of pointers to cached items + */ + std::unordered_map* cache_map; +public: + + /** + * @brief Construct a new cache object. + * + * Caches must contain classes derived from dpp::managed. + */ + cache() { + cache_map = new std::unordered_map; + } + + /** + * @brief Destroy the cache object + * + * @note This does not delete objects stored in the cache. + */ + ~cache() { + std::unique_lock l(cache_mutex); + delete cache_map; + } + + /** + * @brief Store an object in the cache. Passing a nullptr will have no effect. + * + * The object must be derived from dpp::managed and should be allocated on the heap. + * Generally this is done via `new`. Once stored in the cache the lifetime of the stored + * object is managed by the cache class unless the cache is deleted (at which point responsibility + * for deleting the object returns to its allocator). Objects stored are removed when the + * cache::remove() method is called by placing them into a garbage collection queue for deletion + * within the next 60 seconds, which are then deleted in bulk for efficiency and to aid thread + * safety. + * + * @note Adding an object to the cache with an ID which already exists replaces that entry. + * The previously entered cache item is inserted into the garbage collection queue for deletion + * similarly to if cache::remove() was called first. + * + * @param object object to store. Storing a pointer to the cache relinquishes ownership to the cache object. + */ + void store(T* object) { + if (!object) { + return; + } + std::unique_lock l(cache_mutex); + auto existing = cache_map->find(object->id); + if (existing == cache_map->end()) { + (*cache_map)[object->id] = object; + } else if (object != existing->second) { + /* Flag old pointer for deletion and replace */ + std::lock_guard delete_lock(deletion_mutex); + deletion_queue[existing->second] = time(nullptr); + (*cache_map)[object->id] = object; + } + } + + /** + * @brief Remove an object from the cache. + * + * @note The cache class takes ownership of the pointer, and calling this method will + * cause deletion of the object within the next 60 seconds by means of a garbage + * collection queue. This queue aids in efficiency by freeing memory in bulk, and + * assists in thread safety by ensuring that all deletions can be locked and freed + * at the same time. + * + * @param object object to remove. Passing a nullptr will have no effect. + */ + void remove(T* object) { + if (!object) { + return; + } + std::unique_lock l(cache_mutex); + std::lock_guard delete_lock(deletion_mutex); + auto existing = cache_map->find(object->id); + if (existing != cache_map->end()) { + cache_map->erase(existing); + deletion_queue[object] = time(nullptr); + } + } + + /** + * @brief Find an object in the cache by id. + * + * The cache is searched for the object. All dpp::managed objects have a snowflake id + * (this is the only field dpp::managed actually has). + * + * @warning Do not hang onto objects returned by cache::find() indefinitely. They may be + * deleted at a later date if cache::remove() is called. If persistence is required, + * take a copy of the object after checking its pointer is non-null. + * + * @param id Object snowflake id to find + * @return Found object or nullptr if the object with this id does not exist. + */ + T* find(snowflake id) { + std::shared_lock l(cache_mutex); + auto r = cache_map->find(id); + if (r != cache_map->end()) { + return r->second; + } + return nullptr; + } + + /** + * @brief Return a count of the number of items in the cache. + * + * This is used by the library e.g. to count guilds, users, and roles + * stored within caches. + * get + * @return uint64_t count of items in the cache + */ + uint64_t count() { + std::shared_lock l(cache_mutex); + return cache_map->size(); + } + + /** + * @brief Return the cache's locking mutex. + * + * Use this whenever you manipulate or iterate raw elements in the cache! + * + * @note If you are only reading from the cache's container, wrap this + * mutex in `std::shared_lock`, else wrap it in a `std::unique_lock`. + * Shared locks will allow for multiple readers whilst blocking writers, + * and unique locks will allow only one writer whilst blocking readers + * and writers. + * + * **Example:** + * + * ```cpp + * dpp::cache* c = dpp::get_guild_cache(); + * std::unordered_map& gc = c->get_container(); + * std::shared_lock l(c->get_mutex()); // MUST LOCK HERE + * for (auto g = gc.begin(); g != gc.end(); ++g) { + * dpp::guild* gp = (dpp::guild*)g->second; + * // Do something here with the guild* in 'gp' + * } + * ``` + * + * @return The mutex used to protect the container + */ + std::shared_mutex& get_mutex() { + return this->cache_mutex; + } + + /** + * @brief Get the container unordered map + * + * @warning Be sure to use cache::get_mutex() correctly if you + * manipulate or iterate the map returned by this method! If you do + * not, this is not thread safe and will cause crashes! + * + * @see cache::get_mutex + * + * @return A reference to the cache's container map + */ + auto & get_container() { + return *(this->cache_map); + } + + /** + * @brief "Rehash" a cache by reallocating the map and copying + * all elements into the new one. + * + * Over a long running timeframe, unordered maps can grow in size + * due to bucket allocation, this function frees that unused memory + * to keep the maps in control over time. If this is an issue which + * is apparent with your use of dpp::cache objects, you should periodically + * call this method. + * + * @warning May be time consuming! This function is O(n) in relation to the + * number of cached entries. + */ + void rehash() { + std::unique_lock l(cache_mutex); + std::unordered_map* n = new std::unordered_map; + n->reserve(cache_map->size()); + for (auto t = cache_map->begin(); t != cache_map->end(); ++t) { + n->insert(*t); + } + delete cache_map; + cache_map = n; + } + + /** + * @brief Get "real" size in RAM of the cached objects + * + * This does not include metadata used to maintain the unordered map itself. + * + * @return size_t size of cache in bytes + */ + size_t bytes() { + std::shared_lock l(cache_mutex); + return sizeof(this) + (cache_map->bucket_count() * sizeof(size_t)); + } + +}; + +/** Run garbage collection across all caches removing deleted items + * that have been deleted over 60 seconds ago. + */ +void DPP_EXPORT garbage_collection(); + +#define cache_decl(type, setter, getter, counter) /** Find an object in the cache by id. @return type* Pointer to the object or nullptr when it's not found */ DPP_EXPORT class type * setter (snowflake id); DPP_EXPORT cache * getter (); /** Get the amount of cached type objects. */ DPP_EXPORT uint64_t counter (); + +/* Declare major caches */ +cache_decl(user, find_user, get_user_cache, get_user_count); +cache_decl(guild, find_guild, get_guild_cache, get_guild_count); +cache_decl(role, find_role, get_role_cache, get_role_count); +cache_decl(channel, find_channel, get_channel_cache, get_channel_count); +cache_decl(emoji, find_emoji, get_emoji_cache, get_emoji_count); + +} // namespace dpp + diff --git a/include/dpp/channel.h b/include/dpp/channel.h new file mode 100644 index 0000000..d14037b --- /dev/null +++ b/include/dpp/channel.h @@ -0,0 +1,867 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace dpp { + +/** @brief Flag integers as received from and sent to discord */ +enum channel_type : uint8_t { + CHANNEL_TEXT = 0, //!< a text channel within a server + DM = 1, //!< a direct message between users + CHANNEL_VOICE = 2, //!< a voice channel within a server + /** + * @brief a direct message between multiple users + * @deprecated this channel type was intended to be used with the now deprecated GameBridge SDK. Existing group dms with bots will continue to function, but newly created channels will be unusable + */ + GROUP_DM = 3, + CHANNEL_CATEGORY = 4, //!< an organizational category that contains up to 50 channels + CHANNEL_ANNOUNCEMENT = 5, //!< a channel that users can follow and crosspost into their own server + /** + * @brief a channel in which game developers can sell their game on Discord + * @deprecated store channels are deprecated by Discord + */ + CHANNEL_STORE = 6, + CHANNEL_ANNOUNCEMENT_THREAD = 10, //!< a temporary sub-channel within a GUILD_ANNOUNCEMENT channel + CHANNEL_PUBLIC_THREAD = 11, //!< a temporary sub-channel within a GUILD_TEXT or GUILD_FORUM channel + CHANNEL_PRIVATE_THREAD = 12, //!< a temporary sub-channel within a GUILD_TEXT channel that is only viewable by those invited and those with the MANAGE_THREADS permission + CHANNEL_STAGE = 13, //!< a "stage" channel, like a voice channel with one authorised speaker + CHANNEL_DIRECTORY = 14, //!< the channel in a [hub](https://support.discord.com/hc/en-us/articles/4406046651927-Discord-Student-Hubs-FAQ) containing the listed servers + CHANNEL_FORUM = 15, //!< forum channel that can only contain threads + CHANNEL_MEDIA = 16, //!< Media channel that can only contain threads, similar to forum channels +}; + +/** @brief Our flags as stored in the object + * @note The bottom four bits of this flag are reserved to contain the channel_type values + * listed above as provided by Discord. If discord add another value > 15, we will have to + * shuffle these values upwards by one bit. + */ +enum channel_flags : uint16_t { + /* Note that bits 1 to 4 are used for the channel type mask */ + /// NSFW Gated Channel + c_nsfw = 0b0000000000010000, + /// Video quality forced to 720p + c_video_quality_720p = 0b0000000000100000, + /// Lock permissions (only used when updating channel positions) + c_lock_permissions = 0b0000000001000000, + /// Thread is pinned to the top of its parent forum or media channel + c_pinned_thread = 0b0000000010000000, + /// Whether a tag is required to be specified when creating a thread in a forum or a media channel. Tags are specified in the thread::applied_tags field. + c_require_tag = 0b0000000100000000, + /* Note that the 9th and 10th bit are used for the forum layout type */ + /// When set hides the embedded media download options. Available only for media channels + c_hide_media_download_options = 0b0001000000000000, +}; + +/** + * @brief Types for sort posts in a forum channel + */ +enum default_forum_sort_order_t : uint8_t { + /// Sort forum posts by activity (default) + so_latest_activity = 0, + /// Sort forum posts by creation time (from most recent to oldest) + so_creation_date = 1, +}; + +/** + * @brief Types of forum layout views that indicates how the threads in a forum channel will be displayed for users by default + */ +enum forum_layout_type : uint8_t { + fl_not_set = 0, //!< No default has been set for the forum channel + fl_list_view = 1, //!< Display posts as a list + fl_gallery_view = 2, //!< Display posts as a collection of tiles +}; + +/** + * @brief channel permission overwrite types + */ +enum overwrite_type : uint8_t { + /// Role + ot_role = 0, + /// Member + ot_member = 1 +}; + +/** + * @brief Channel permission overwrites + */ +struct DPP_EXPORT permission_overwrite { + /// ID of the role or the member + snowflake id; + /// Bitmask of allowed permissions + permission allow; + /// Bitmask of denied permissions + permission deny; + /// Type of overwrite. See dpp::overwrite_type + uint8_t type; + + /** + * @brief Construct a new permission_overwrite object + */ + permission_overwrite(); + + /** + * @brief Construct a new permission_overwrite object + * @param id ID of the role or the member to create the overwrite for + * @param allow Bitmask of allowed permissions (refer to enum dpp::permissions) for this user/role in this channel + * @param deny Bitmask of denied permissions (refer to enum dpp::permissions) for this user/role in this channel + * @param type Type of overwrite + */ + permission_overwrite(snowflake id, uint64_t allow, uint64_t deny, overwrite_type type); +}; + + +/** + * @brief metadata for threads + */ +struct DPP_EXPORT thread_metadata { + /// Timestamp when the thread's archive status was last changed, used for calculating recent activity + time_t archive_timestamp; + /// The duration in minutes to automatically archive the thread after recent activity, can be set to: 60, 1440, 4320, 10080 + uint16_t auto_archive_duration; + /// Whether a thread is archived + bool archived; + /// Whether a thread is locked. When a thread is locked, only users with `MANAGE_THREADS` can unarchive it + bool locked; + /// Whether non-moderators can add other non-moderators. Only for private threads + bool invitable; +}; + +/** + * @brief Auto archive duration of threads which will stop showing in the channel list after the specified period of inactivity. + * Defined as an enum to fit into 1 byte. Internally it'll be translated to minutes to match the API + */ +enum auto_archive_duration_t : uint8_t { + /// Auto archive duration of 1 hour. (60 minutes) + arc_1_hour = 1, + /// Auto archive duration of 1 day. (1440 minutes) + arc_1_day = 2, + /// Auto archive duration of 3 days. (4320 minutes) + arc_3_days = 3, + /// Auto archive duration of 1 week. (10080 minutes) + arc_1_week = 4, +}; + +/** + * @brief represents membership of a user with a thread + */ +struct DPP_EXPORT thread_member +{ + /// ID of the thread member is part of + snowflake thread_id; + /// ID of the member + snowflake user_id; + /// The time when user last joined the thread + time_t joined; + /// Any user-thread settings, currently only used for notifications + uint32_t flags; + + /** + * @brief Read struct values from a json object + * @param j json to read values from + * @return A reference to self + */ + thread_member& fill_from_json(nlohmann::json* j); +}; + +/** + * @brief Represents a tag that is able to be applied to a thread in a forum or media channel + */ +struct DPP_EXPORT forum_tag : public managed, public json_interface { + /** The name of the tag (0-20 characters) */ + std::string name; + /** The emoji of the tag. Contains either nothing, the id of a guild's custom emoji or the unicode character of the emoji */ + std::variant emoji; + /** Whether this tag can only be added to or removed from threads by a member with the `MANAGE_THREADS` permission */ + bool moderated; + + /** Constructor */ + forum_tag(); + + /** + * @brief Constructor + * + * @param name The name of the tag. It will be truncated to the maximum length of 20 UTF-8 characters. + */ + forum_tag(const std::string& name); + + /** Destructor */ + virtual ~forum_tag() = default; + + /** + * @brief Read struct values from a json object + * @param j json to read values from + * @return A reference to self + */ + forum_tag& fill_from_json(nlohmann::json* j); + + /** + * @brief Build json for this forum_tag object + * + * @param with_id include the ID in the json + * @return std::string JSON string + */ + std::string build_json(bool with_id = false) const; + + /** + * @brief Set name of this forum_tag object + * + * @param name Name to set + * @return Reference to self, so these method calls may be chained + * + * @note name will be truncated to 20 chars, if longer + */ + forum_tag& set_name(const std::string& name); +}; + +/** + * @brief A group of thread member objects. the key is the user_id of the dpp::thread_member + */ +typedef std::unordered_map thread_member_map; + +/** + * @brief A definition of a discord channel. + * There are one of these for every channel type except threads. Threads are + * special snowflakes. Get it? A Discord pun. Hahaha. .... I'll get my coat. + */ +class DPP_EXPORT channel : public managed, public json_interface { +public: + /** Channel name (1-100 characters) */ + std::string name; + + /** Channel topic (0-4096 characters for forum and media channels, 0-1024 characters for all others) */ + std::string topic; + + /** + * @brief Voice region if set for voice channel, otherwise empty string + */ + std::string rtc_region; + + /** DM recipients */ + std::vector recipients; + + /** Permission overwrites to apply to base permissions */ + std::vector permission_overwrites; + + /** A set of tags that can be used in a forum or media channel */ + std::vector available_tags; + + /** + * @brief The emoji to show as the default reaction button on a thread in a forum or media channel. + * Contains either nothing, the id of a guild's custom emoji or the unicode character of the emoji + */ + std::variant default_reaction; + + /** + * @brief Channel icon (for group DMs) + */ + utility::iconhash icon; + + /** User ID of the creator for group DMs or threads */ + snowflake owner_id; + + /** Parent ID (for guild channels: id of the parent category, for threads: id of the text channel this thread was created) */ + snowflake parent_id; + + /** Guild id of the guild that owns the channel */ + snowflake guild_id; + + /** ID of last message to be sent to the channel (may not point to an existing or valid message or thread) */ + snowflake last_message_id; + + /** Timestamp of last pinned message */ + time_t last_pin_timestamp; + + /** + * @brief This is only filled when the channel is part of the `resolved` set + * sent within an interaction. Any other time it contains zero. When filled, + * it contains the calculated permission bitmask of the user issuing the command + * within this channel. + */ + permission permissions; + + /** Sorting position, lower number means higher up the list */ + uint16_t position; + + /** the bitrate (in kilobits) of the voice channel */ + uint16_t bitrate; + + /** amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission manage_messages or manage_channel, are unaffected*/ + uint16_t rate_limit_per_user; + + /** The initial `rate_limit_per_user` to set on newly created threads in a channel. This field is copied to the thread at creation time and does not live update */ + uint16_t default_thread_rate_limit_per_user; + + /** + * @brief Default duration, copied onto newly created threads. Used by the clients, not the API. + * Threads will stop showing in the channel list after the specified period of inactivity. Defaults to dpp::arc_1_day + */ + auto_archive_duration_t default_auto_archive_duration; + + /** the default sort order type used to order posts in forum and media channels */ + default_forum_sort_order_t default_sort_order; + + /** Flags bitmap (dpp::channel_flags) */ + uint16_t flags; + + /** Maximum user limit for voice channels (0-99) */ + uint8_t user_limit; + + /** Constructor */ + channel(); + + /** Destructor */ + virtual ~channel(); + + /** + * @brief Create a mentionable channel. + * @param id The ID of the channel. + * @return std::string The formatted mention of the channel. + */ + static std::string get_mention(const snowflake& id); + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + channel& fill_from_json(nlohmann::json* j); + + /** + * @brief Build json for this channel object + * + * @param with_id include the ID in the json + * @return std::string JSON string + */ + virtual std::string build_json(bool with_id = false) const; + + /** + * @brief Set name of this channel object + * + * @param name Name to set + * @return Reference to self, so these method calls may be chained + * + * @note name will be truncated to 100 chars, if longer + * @throw dpp::length_exception if length < 1 + */ + channel& set_name(const std::string& name); + + /** + * @brief Set topic of this channel object + * + * @param topic Topic to set + * @return Reference to self, so these method calls may be chained + * + * @note topic will be truncated to 1024 chars, if longer + */ + channel& set_topic(const std::string& topic); + + /** + * @brief Set type of this channel object + * + * @param type Channel type to set + * @return Reference to self, so these method calls may be chained + */ + channel& set_type(channel_type type); + + /** + * @brief Set the default forum layout type for the forum channel + * + * @param layout_type The layout type + * @return Reference to self, so these method calls may be chained + */ + channel& set_default_forum_layout(forum_layout_type layout_type); + + /** + * @brief Set the default forum sort order for the forum channel + * + * @param sort_order The sort order + * @return Reference to self, so these method calls may be chained + */ + channel& set_default_sort_order(default_forum_sort_order_t sort_order); + + /** + * @brief Set flags for this channel object + * + * @param flags Flag bitmask to set from dpp::channel_flags + * @return Reference to self, so these method calls may be chained + */ + channel& set_flags(const uint16_t flags); + + /** + * @brief Add (bitwise OR) a flag to this channel object + * + * @param flag Flag bit to add from dpp::channel_flags + * @return Reference to self, so these method calls may be chained + */ + channel& add_flag(const channel_flags flag); + + /** + * @brief Remove (bitwise NOT AND) a flag from this channel object + * + * @param flag Flag bit to remove from dpp::channel_flags + * @return Reference to self, so these method calls may be chained + */ + channel& remove_flag(const channel_flags flag); + + /** + * @brief Set position of this channel object + * + * @param position Position to set + * @return Reference to self, so these method calls may be chained + */ + channel& set_position(const uint16_t position); + + /** + * @brief Set guild_id of this channel object + * + * @param guild_id Guild ID to set + * @return Reference to self, so these method calls may be chained + */ + channel& set_guild_id(const snowflake guild_id); + + /** + * @brief Set parent_id of this channel object + * + * @param parent_id Parent ID to set + * @return Reference to self, so these method calls may be chained + */ + channel& set_parent_id(const snowflake parent_id); + + /** + * @brief Set user_limit of this channel object + * + * @param user_limit Limit to set + * @return Reference to self, so these method calls may be chained + */ + channel& set_user_limit(const uint8_t user_limit); + + /** + * @brief Set bitrate of this channel object + * + * @param bitrate Bitrate to set (in kilobits) + * @return Reference to self, so these method calls may be chained + */ + channel& set_bitrate(const uint16_t bitrate); + + /** + * @brief Set nsfw property of this channel object + * + * @param is_nsfw true, if channel is nsfw + * @return Reference to self, so these method calls may be chained + */ + channel& set_nsfw(const bool is_nsfw); + + /** + * @brief Set lock permissions property of this channel object + * Used only with the reorder channels method + * + * @param is_lock_permissions true, if we are to inherit permissions from the category + * @return Reference to self, so these method calls may be chained + */ + channel& set_lock_permissions(const bool is_lock_permissions); + + /** + * @brief Set rate_limit_per_user of this channel object + * + * @param rate_limit_per_user rate_limit_per_user (slowmode in sec) to set + * @return Reference to self, so these method calls may be chained + */ + channel& set_rate_limit_per_user(const uint16_t rate_limit_per_user); + + /** + * @brief Add permission overwrites for a user or role. + * If the channel already has permission overwrites for the passed target, the existing ones will be adjusted by the passed permissions + * + * @param target ID of the role or the member you want to adjust overwrites for + * @param type type of overwrite + * @param allowed_permissions bitmask of dpp::permissions you want to allow for this user/role in this channel. Note: You can use the dpp::permission class + * @param denied_permissions bitmask of dpp::permissions you want to deny for this user/role in this channel. Note: You can use the dpp::permission class + * + * @return Reference to self, so these method calls may be chained + */ + channel& add_permission_overwrite(const snowflake target, const overwrite_type type, const uint64_t allowed_permissions, const uint64_t denied_permissions); + /** + * @brief Set permission overwrites for a user or role on this channel object. Old permission overwrites for the target will be overwritten + * + * @param target ID of the role or the member you want to set overwrites for + * @param type type of overwrite + * @param allowed_permissions bitmask of allowed dpp::permissions for this user/role in this channel. Note: You can use the dpp::permission class + * @param denied_permissions bitmask of denied dpp::permissions for this user/role in this channel. Note: You can use the dpp::permission class + * + * @return Reference to self, so these method calls may be chained + * + * @note If both `allowed_permissions` and `denied_permissions` parameters are 0, the permission overwrite for the target will be removed + */ + channel& set_permission_overwrite(const snowflake target, const overwrite_type type, const uint64_t allowed_permissions, const uint64_t denied_permissions); + /** + * @brief Remove channel specific permission overwrites of a user or role + * + * @param target ID of the role or the member you want to remove permission overwrites of + * @param type type of overwrite + * + * @return Reference to self, so these method calls may be chained + */ + channel& remove_permission_overwrite(const snowflake target, const overwrite_type type); + + /** + * @brief Get the channel type + * + * @return channel_type Channel type + */ + channel_type get_type() const; + + /** + * @brief Get the default forum layout type used to display posts in forum channels + * + * @return forum_layout_types Forum layout type + */ + forum_layout_type get_default_forum_layout() const; + + /** + * @brief Get the mention ping for the channel + * + * @return std::string mention + */ + std::string get_mention() const; + + /** + * @brief Get the overall permissions for a member in this channel, including channel overwrites, role permissions and admin privileges. + * + * @param user The user to resolve the permissions for + * @return permission Permission overwrites for the member. Made of bits in dpp::permissions. + * @note Requires role cache to be enabled (it's enabled by default). + * + * @note This is an alias for guild::permission_overwrites and searches for the guild in the cache, + * so consider using guild::permission_overwrites if you already have the guild object. + * + * @warning The method will search for the guild member in the cache by the users id. + * If the guild member is not in cache, the method will always return 0. + */ + permission get_user_permissions(const class user* user) const; + + /** + * @brief Get the overall permissions for a member in this channel, including channel overwrites, role permissions and admin privileges. + * + * @param member The member to resolve the permissions for + * @return permission Permission overwrites for the member. Made of bits in dpp::permissions. + * @note Requires role cache to be enabled (it's enabled by default). + * + * @note This is an alias for guild::permission_overwrites and searches for the guild in the cache, + * so consider using guild::permission_overwrites if you already have the guild object. + */ + permission get_user_permissions(const class guild_member &member) const; + + /** + * @brief Return a map of members on the channel, built from the guild's + * member list based on which members have the VIEW_CHANNEL permission. + * Does not return reliable information for voice channels, use + * dpp::channel::get_voice_members() instead for this. + * @return A map of guild members keyed by user id. + * @note If the guild this channel belongs to is not in the cache, the function will always return 0. + */ + std::map get_members(); + + /** + * @brief Get a map of members in this channel, if it is a voice channel. + * The map is keyed by snowflake id of the user. + * + * @return std::map The voice members of the channel + */ + std::map get_voice_members(); + + /** + * @brief Get the channel's icon url (if its a group DM), otherwise returns an empty string + * + * @param size The size of the icon in pixels. It can be any power of two between 16 and 4096, + * otherwise the default sized icon is returned. + * @param format The format to use for the avatar. It can be one of `i_webp`, `i_jpg` or `i_png`. + * @return std::string icon url or an empty string, if required attributes are missing or an invalid format was passed + */ + std::string get_icon_url(uint16_t size = 0, const image_type format = i_png) const; + + /** + * @brief Returns true if the channel is NSFW gated + * + * @return true if NSFW + */ + bool is_nsfw() const; + + /** + * @brief Returns true if the permissions are to be synced with the category it is in. + * Used only and set manually when using the reorder channels method. + * + * @return true if keeping permissions + */ + bool is_locked_permissions() const; + + /** + * @brief Returns true if the channel is a text channel + * + * @return true if text channel + */ + bool is_text_channel() const; + + /** + * @brief Returns true if the channel is a DM + * + * @return true if is a DM + */ + bool is_dm() const; + + /** + * @brief Returns true if the channel is a voice channel + * + * @return true if voice channel + */ + bool is_voice_channel() const; + + /** + * @brief Returns true if the channel is a group DM channel + * + * @return true if group DM + */ + bool is_group_dm() const; + + /** + * @brief Returns true if the channel is a category + * + * @return true if a category + */ + bool is_category() const; + + /** + * @brief Returns true if the channel is a forum + * + * @return true if a forum + */ + bool is_forum() const; + + /** + * @brief Returns true if the channel is a media channel + * + * @return true if media channel + */ + bool is_media_channel() const; + + /** + * @brief Returns true if the channel is an announcement channel + * + * @return true if announcement channel + */ + bool is_news_channel() const; + + /** + * @brief Returns true if the channel is a store channel + * @deprecated store channels are deprecated by Discord + * + * @return true if store channel + */ + bool is_store_channel() const; + + /** + * @brief Returns true if the channel is a stage channel + * + * @return true if stage channel + */ + bool is_stage_channel() const; + + /** + * @brief Returns true if video quality is auto + * + * @return true if video quality is auto + */ + bool is_video_auto() const; + + /** + * @brief Returns true if video quality is 720p + * + * @return true if video quality is 720p + */ + bool is_video_720p() const; + + /** + * @brief Returns true if channel is a pinned thread in forum + * + * @return true, if channel is a pinned thread in forum + */ + bool is_pinned_thread() const; + + /** + * @brief Returns true if a tag is required to be specified when creating a thread in a forum channel + * + * @return true, if a tag is required to be specified when creating a thread in a forum channel + */ + bool is_tag_required() const; + + /** + * @brief Returns true if embedded media download options are hidden in a media channel + * + * @return true, if embedded media download options are hidden in a media channel + */ + bool is_download_options_hidden() const; + +}; + +/** @brief A definition of a discord thread. + * A thread is a superset of a channel. Not to be confused with `std::thread`! + */ +class DPP_EXPORT thread : public channel { +public: + /** + * @brief Thread member of current user if joined to the thread. + * Note this is only set by certain api calls otherwise contains default data + */ + thread_member member; + + /** Thread metadata (threads) */ + thread_metadata metadata; + + /** Created message. Only filled within the cluster::thread_create_in_forum() method */ + message msg; + + /** + * A list of dpp::forum_tag IDs that have been applied to a thread in a forum or media channel + */ + std::vector applied_tags; + + /** + * @brief Number of messages ever sent in the thread. + * It's similar to thread::message_count on message creation, but will not decrement the number when a message is deleted + */ + uint32_t total_messages_sent; + + /** + * @brief Number of messages (not including the initial message or deleted messages) of the thread. + * For threads created before July 1, 2022, the message count is inaccurate when it's greater than 50. + */ + uint8_t message_count; + + /** Approximate count of members in a thread (stops counting at 50) */ + uint8_t member_count; + + /** + * @brief Construct a new thread object + */ + thread(); + + /** + * @brief Returns true if the thread is within an announcement channel + * + * @return true if announcement thread + */ + bool is_news_thread() const; + + /** + * @brief Returns true if the channel is a public thread + * + * @return true if public thread + */ + bool is_public_thread() const; + + /** + * @brief Returns true if the channel is a private thread + * + * @return true if private thread + */ + bool is_private_thread() const; + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + thread& fill_from_json(nlohmann::json* j); + + /** + * @brief Destroy the thread object + */ + virtual ~thread() = default; + + /** + * @brief Build json for this thread object + * + * @param with_id include the ID in the json + * @return std::string JSON string + */ + std::string build_json(bool with_id = false) const; + +}; + + +/** + * @brief Serialize a thread_metadata object to json + * + * @param j JSON object to serialize to + * @param tmdata object to serialize + */ +void to_json(nlohmann::json& j, const thread_metadata& tmdata); + +/** + * @brief Serialize a permission_overwrite object to json + * + * @param j JSON object to serialize to + * @param po object to serialize + */ +void to_json(nlohmann::json& j, const permission_overwrite& po); + +/** + * @brief A group of channels + */ +typedef std::unordered_map channel_map; + +/** + * @brief A group of threads + */ +typedef std::unordered_map thread_map; + +/** + * @brief A thread alongside the bot's optional thread_member object tied to it + */ +struct active_thread_info { + /** + * @brief The thread object + */ + thread active_thread; + + /** + * @brief The bot as a thread member, only present if the bot is in the thread + */ + std::optional bot_member; +}; + +/** + * @brief A map of threads alongside optionally the thread_member tied to the bot if it is in the thread. The map's key is the thread id. Returned from the cluster::threads_get_active method + */ +using active_threads = std::map; + +} // namespace dpp + diff --git a/include/dpp/cluster.h b/include/dpp/cluster.h new file mode 100644 index 0000000..926f91d --- /dev/null +++ b/include/dpp/cluster.h @@ -0,0 +1,3535 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace dpp { + +using json = nlohmann::json; + +/** + * @brief Types of startup for cluster::start() + */ +enum start_type : bool { + /** + * @brief Wait forever on a condition variable. + * The cluster will spawn threads for each shard + * and start() will not return in normal operation. + */ + st_wait = false, + + /** + * @brief Return immediately after starting shard threads. + * If you set the parameter of cluster::start() to + * this value, you will have to manage the lifetime + * and scope of your cluster object yourself. Taking it + * out of scope or deleting its pointer will terminate + * the bot. + */ + st_return = true, +}; + +/** @brief The cluster class represents a group of shards and a command queue for sending and + * receiving commands from discord via HTTP. You should usually instantiate a cluster object + * at the very least to make use of the library. + */ +class DPP_EXPORT cluster { + + friend class discord_client; + friend class discord_voice_client; + + /** + * @brief default gateway for connecting the websocket. + */ + std::string default_gateway; + + /** + * @brief queue system for commands sent to Discord, and any replies + */ + request_queue* rest; + + /** + * @brief queue system for arbitrary HTTP requests sent by the user to sites other than Discord + */ + request_queue* raw_rest; + + /** + * @brief True if to use compression on shards + */ + bool compressed; + + /** + * @brief Lock to prevent concurrent access to dm_channels + */ + std::mutex dm_list_lock; + + /** + * @brief Start time of cluster + */ + time_t start_time; + + /** + * @brief Active DM channels for the bot + */ + std::unordered_map dm_channels; + + /** + * @brief Active shards on this cluster. Shard IDs may have gaps between if there + * are multiple clusters. + */ + shard_list shards; + + /** + * @brief List of all active registered timers + */ + timer_reg_t timer_list; + + /** + * @brief List of timers by time + */ + timer_next_t next_timer; + + /** + * @brief Tick active timers + */ + void tick_timers(); + + /** + * @brief Reschedule a timer for its next tick + * + * @param t Timer to reschedule + */ + void timer_reschedule(timer_t* t); +public: + /** + * @brief Current bot token for all shards on this cluster and all commands sent via HTTP + */ + std::string token; + + /** + * @brief Last time the bot sent an IDENTIFY + */ + time_t last_identify; + + /** + * @brief Current bitmask of gateway intents + */ + uint32_t intents; + + /** + * @brief Total number of shards across all clusters + */ + uint32_t numshards; + + /** + * @brief ID of this cluster, between 0 and MAXCLUSTERS-1 inclusive + */ + uint32_t cluster_id; + + /** + * @brief Total number of clusters that are active + */ + uint32_t maxclusters; + + /** + * @brief REST latency (HTTPS ping) in seconds + */ + double rest_ping; + + /** + * @brief The details of the bot user. This is assumed to be identical across all shards + * in the cluster. Each connecting shard updates this information. + */ + dpp::user me; + + /** + * @brief Current cache policy for the cluster + */ + cache_policy_t cache_policy; + + /** + * @brief Websocket mode for all shards in the cluster, either ws_json or ws_etf. + * Production bots should use ETF, while development bots should use JSON. + */ + websocket_protocol_t ws_mode; + + /** + * @brief Condition variable notified when the cluster is terminating. + */ + std::condition_variable terminating; + + /** + * @brief Constructor for creating a cluster. All but the token are optional. + * @param token The bot token to use for all HTTP commands and websocket connections + * @param intents 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. + * @param shards 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. + * @param cluster_id The ID of this cluster, should be between 0 and MAXCLUSTERS-1 + * @param maxclusters The total number of clusters that are active, which may be on separate processes or even separate machines. + * @param compressed Whether or not to use compression for shards on this cluster. Saves a ton of bandwidth at the cost of some CPU + * @param policy 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) + * @param request_threads 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(). + * @param request_threads_raw 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(). + * @throw dpp::exception Thrown on windows, if WinSock fails to initialise, or on any other system if a dpp::request_queue fails to construct + */ + cluster(const std::string& token, uint32_t intents = i_default_intents, uint32_t shards = 0, uint32_t cluster_id = 0, uint32_t maxclusters = 1, bool compressed = true, cache_policy_t policy = { cp_aggressive, cp_aggressive, cp_aggressive }, uint32_t request_threads = 12, uint32_t request_threads_raw = 1); + + /** + * @brief dpp::cluster is non-copyable + */ + cluster(const cluster&) = delete; + + /** + * @brief dpp::cluster is non-moveable + */ + cluster(const cluster&&) = delete; + + /** + * @brief dpp::cluster is non-copyable + */ + cluster& operator=(const cluster&) = delete; + + /** + * @brief dpp::cluster is non-moveable + */ + cluster& operator=(const cluster&&) = delete; + + /** + * @brief Destroy the cluster object + */ + virtual ~cluster(); + + /** + * @brief End cluster execution without destructing it. + * To restart the cluster, call cluster::start() again. + */ + void shutdown(); + + /** + * @brief Get the rest_queue object which handles HTTPS requests to Discord + * @return request_queue* pointer to request_queue object + */ + request_queue* get_rest(); + + /** + * @brief Get the raw rest_queue object which handles all HTTP(S) requests that are not directed at Discord + * @return request_queue* pointer to request_queue object + */ + request_queue* get_raw_rest(); + + /** + * @brief Set the websocket protocol for all shards on this cluster. + * You should call this method before cluster::start. + * Generally ws_etf is faster, but provides less facilities for debugging should something + * go wrong. It is recommended to use ETF in production and JSON in development. + * + * @param mode websocket protocol to use, either ws_json or ws_etf. + * @return cluster& Reference to self for chaining. + * @throw dpp::logic_exception If called after the cluster is started (this is not supported) + */ + cluster& set_websocket_protocol(websocket_protocol_t mode); + + /** + * @brief Set the audit log reason for the next REST call to be made. + * This is set per-thread, so you must ensure that if you call this method, your request that + * is associated with the reason happens on the same thread where you set the reason. + * Once the next call is made, the audit log reason is cleared for this thread automatically. + * + * Example: + * ``` + * bot.set_audit_reason("Too much abusive content") + * .channel_delete(my_channel_id); + * ``` + * + * @param reason The reason to set for the next REST call on this thread + * @return cluster& Reference to self for chaining. + */ + cluster& set_audit_reason(const std::string &reason); + + /** + * @brief Clear the audit log reason for the next REST call to be made. + * This is set per-thread, so you must ensure that if you call this method, your request that + * is associated with the reason happens on the same thread where you set the reason. + * Once the next call is made, the audit log reason is cleared for this thread automatically. + * + * Example: + * ``` + * bot.set_audit_reason("Won't be sent") + * .clear_audit_reason() + * .channel_delete(my_channel_id); + * ``` + * + * @return cluster& Reference to self for chaining. + */ + cluster& clear_audit_reason(); + + /** + * @brief Get the audit reason set for the next REST call to be made on this thread. + * This is set per-thread, so you must ensure that if you call this method, your request that + * is associated with the reason happens on the same thread where you set the reason. + * Once the next call is made, the audit log reason is cleared for this thread automatically. + * + * @note This method call clears the audit reason when it returns it. + * + * @return std::string The audit reason to be used. + * + */ + std::string get_audit_reason(); + + /** + * @brief Sets the address of the default gateway, for connecting the websockets. + * + * @return cluster& Reference to self for chaining. + */ + cluster& set_default_gateway(std::string& default_gateway); + + /** + * @brief Log a message to whatever log the user is using. + * The logged message is passed up the chain to the on_log event in user code which can then do whatever + * it wants to do with it. + * @param severity The log level from dpp::loglevel + * @param msg The log message to output + */ + void log(dpp::loglevel severity, const std::string &msg) const; + + /** + * @brief Start a timer. Every `frequency` seconds, the callback is called. + * + * @param on_tick The callback lambda to call for this timer when ticked + * @param on_stop The callback lambda to call for this timer when it is stopped + * @param frequency How often to tick the timer in seconds + * @return timer A handle to the timer, used to remove that timer later + */ + timer start_timer(timer_callback_t on_tick, uint64_t frequency, timer_callback_t on_stop = {}); + + /** + * @brief Stop a ticking timer + * + * @param t Timer handle received from cluster::start_timer + * @return bool True if the timer was stopped, false if it did not exist + * @note If the timer has an on_stop lambda, the on_stop lambda will be called. + */ + bool stop_timer(timer t); + +#ifdef DPP_CORO + /** + * @brief Get an awaitable to wait a certain amount of seconds. Use the co_await keyword on its return value to suspend the coroutine until the timer ends + * + * @param seconds How long to wait for + * @return async Object that can be co_await-ed to suspend the function for a certain time + */ + [[nodiscard]] async co_sleep(uint64_t seconds); +#endif + + /** + * @brief Get the dm channel for a user id + * + * @param user_id the user id to get the dm channel for + * @return Returns 0 on failure + */ + snowflake get_dm_channel(snowflake user_id); + + /** + * @brief Set the dm channel id for a user id + * + * @param user_id user id to set the dm channel for + * @param channel_id dm channel to set + */ + void set_dm_channel(snowflake user_id, snowflake channel_id); + + /** + * @brief Returns the uptime of the cluster + * + * @return dpp::utility::uptime The uptime of the cluster + */ + dpp::utility::uptime uptime(); + + /** + * @brief Start the cluster, connecting all its shards. + * + * Returns once all shards are connected if return_after is true, + * otherwise enters an infinite loop while the shards run. + * + * @param return_after If true the bot will return to your program after starting shards, if false this function will never return. + */ + void start(bool return_after = true); + + /** + * @brief Set the presence for all shards on the cluster + * + * @param p The presence to set. Only the online status and the first activity are sent. + */ + void set_presence(const class dpp::presence &p); + + /** + * @brief Get a shard by id, returning the discord_client + * + * @param id Shard ID + * @return discord_client* shard, or null + */ + discord_client* get_shard(uint32_t id); + + /** + * @brief Get the list of shards + * + * @return shard_list& Reference to map of shards for this cluster + */ + const shard_list& get_shards(); + + /* Functions for attaching to event handlers */ + + /** + * @brief on voice state update event + * + * @see https://discord.com/developers/docs/topics/gateway-events#voice-state-update + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type voice_state_update_t&, and returns void. + */ + event_router_t on_voice_state_update; + + + /** + * @brief on voice client disconnect event + * + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type voice_client_disconnect_t&, and returns void. + */ + event_router_t on_voice_client_disconnect; + + + /** + * @brief on voice client speaking event + * + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type voice_client_speaking_t&, and returns void. + */ + event_router_t on_voice_client_speaking; + + + /** + * @brief Called when a log message is to be written to the log. + * You can attach any logging system here you wish, e.g. spdlog, or even just a simple + * use of std::cout or printf. If nothing attaches this log event, then the + * library will be silent. + * + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type log_t&, and returns void. + */ + event_router_t on_log; + + /** + * @brief on guild join request delete. + * Triggered when a user declines the membership screening questionnaire for a guild. + * + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type guild_join_request_delete_t&, and returns void. + */ + event_router_t on_guild_join_request_delete; + + + /** + * @brief Called when a new interaction is created. + * Interactions are created by discord when commands you have registered are issued + * by a user. For an example of this in action please see \ref slashcommands + * + * @see https://discord.com/developers/docs/topics/gateway-events#interaction-create + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type interaction_create_t&, and returns void. + * + * @note There are dedicated events to handle slashcommands (See dpp::cluster::on_slashcommand), + * user context menus (See dpp::cluster::on_user_context_menu) and message context menus (See dpp::cluster::on_message_context_menu) + */ + event_router_t on_interaction_create; + + /** + * @brief Called when a slash command is issued. + * Only dpp::ctxm_chat_input types of interaction are routed to this event. + * For an example of this in action please see \ref slashcommands + * + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type slashcommand_t&, and returns void. + */ + event_router_t on_slashcommand; + + /** + * @brief Called when a button is clicked attached to a message. + * Button clicks are triggered by discord when buttons are clicked which you have + * associated with a message using dpp::component. + * + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type button_click_t&, and returns void. + */ + event_router_t on_button_click; + + /** + * @brief Called when an auto completed field needs suggestions to present to the user + * This is triggered by discord when option choices have auto completion enabled which you have + * associated with a dpp::slashcommand. + * + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type autocomplete_t&, and returns void. + */ + event_router_t on_autocomplete; + + + /** + * @brief Called when a select menu is clicked attached to a message. + * Select menu clicks are triggered by discord when select menus are clicked which you have + * associated with a message using dpp::component. + * + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type select_click_t&, and returns void. + */ + event_router_t on_select_click; + + /** + * @brief Called when a user right-clicks or long-presses on a message, + * where a slash command is bound to the dpp::ctxm_message command type. + * + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type message_context_menu_t&, and returns void. + */ + event_router_t on_message_context_menu; + + /** + * @brief Called when a user right-clicks or long-presses on a user, + * where a slash command is bound to the dpp::ctxm_user command type. + * + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type user_context_menu_t&, and returns void. + */ + event_router_t on_user_context_menu; + + /** + * @brief Called when a modal dialog is submitted. + * Form submits are triggered by discord when modal dialogs are submitted which you have + * associated with a slash command using dpp::interaction_modal_response. + * + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type form_submit_t&, and returns void. + */ + event_router_t on_form_submit; + + + /** + * @brief Called when a guild is deleted. + * A guild can be deleted via the bot being kicked, the bot leaving the guild + * explicitly with dpp::cluster::guild_delete, or via the guild being unavailable due to + * an outage. + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-delete + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type guild_delete_t&, and returns void. + */ + event_router_t on_guild_delete; + + + /** + * @brief Called when a channel is deleted from a guild. + * The channel will still be temporarily available in the cache. Pointers to the + * channel should not be retained long-term as they will be deleted by the garbage + * collector. + * + * @see https://discord.com/developers/docs/topics/gateway-events#channel-delete + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type channel_delete_t&, and returns void. + */ + event_router_t on_channel_delete; + + + /** + * @brief Called when a channel is edited on a guild. + * The new channel details have already been applied to the guild when you + * receive this event. + * + * @see https://discord.com/developers/docs/topics/gateway-events#channel-update + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type channel_update_t&, and returns void. + */ + event_router_t on_channel_update; + + + /** + * @brief Called when a shard is connected and ready. + * A set of cluster::on_guild_create events will follow this event. + * + * @see https://discord.com/developers/docs/topics/gateway-events#ready + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type ready_t&, and returns void. + */ + event_router_t on_ready; + + + /** + * @brief Called when a message is deleted. + * The message has already been deleted from Discord when you + * receive this event. + * + * @see https://discord.com/developers/docs/topics/gateway-events#message-delete + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type message_delete_t&, and returns void. + */ + event_router_t on_message_delete; + + + /** + * @brief Called when a user leaves a guild (either through being kicked, or choosing to leave) + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-member-remove + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type guild_member_remove_t&, and returns void. + */ + event_router_t on_guild_member_remove; + + + /** + * @brief Called when a connection to a shard successfully resumes. + * A resumed session does not need to re-synchronise guilds, members, etc. + * This is generally non-fatal and informational only. + * + * @see https://discord.com/developers/docs/topics/gateway-events#resumed + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type resumed_t&, and returns void. + */ + event_router_t on_resumed; + + + /** + * @brief Called when a new role is created on a guild. + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-role-create + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type guild_role_create_t&, and returns void. + */ + event_router_t on_guild_role_create; + + + /** + * @brief Called when a user is typing on a channel. + * + * @see https://discord.com/developers/docs/topics/gateway-events#typing-start + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type typing_start_t&, and returns void. + */ + event_router_t on_typing_start; + + + /** + * @brief Called when a new reaction is added to a message. + * + * @see https://discord.com/developers/docs/topics/gateway-events#message-reaction-add + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type message_reaction_add_t&, and returns void. + */ + event_router_t on_message_reaction_add; + + + /** + * @brief Called when a set of members is received for a guild. + * D++ will request these for all new guilds if needed, after the cluster::on_guild_create + * events. + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-members-chunk + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type guild_members_chunk_t&, and returns void. + */ + event_router_t on_guild_members_chunk; + + + /** + * @brief Called when a single reaction is removed from a message. + * + * @see https://discord.com/developers/docs/topics/gateway-events#message-reaction-remove + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type message_reaction_remove_t&, and returns void. + */ + event_router_t on_message_reaction_remove; + + + /** + * @brief Called when a new guild is created. + * D++ will request members for the guild for its cache using guild_members_chunk. + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-create + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type guild_create_t&, and returns void. + */ + event_router_t on_guild_create; + + + /** + * @brief Called when a new channel is created on a guild. + * + * @see https://discord.com/developers/docs/topics/gateway-events#channel-create + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type channel_create_t&, and returns void. + */ + event_router_t on_channel_create; + + + /** + * @brief Called when all reactions for a particular emoji are removed from a message. + * + * @see https://discord.com/developers/docs/topics/gateway-events#message-reaction-remove-emoji + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type message_reaction_remove_emoji_t&, and returns void. + */ + event_router_t on_message_reaction_remove_emoji; + + + /** + * @brief Called when multiple messages are deleted from a channel or DM. + * + * @see https://discord.com/developers/docs/topics/gateway-events#message-delete-bulk + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type message_delete_bulk_t&, and returns void. + */ + event_router_t on_message_delete_bulk; + + + /** + * @brief Called when an existing role is updated on a guild. + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-role-update + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type guild_role_update_t&, and returns void. + */ + event_router_t on_guild_role_update; + + + /** + * @brief Called when a role is deleted in a guild. + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-role-delete + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type guild_role_delete_t&, and returns void. + */ + event_router_t on_guild_role_delete; + + + /** + * @brief Called when a message is pinned. + * Note that the pinned message is not returned to this event, just the timestamp + * of the last pinned message. + * + * @see https://discord.com/developers/docs/topics/gateway-events#channel-pins-update + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type channel_pins_update_t&, and returns void. + */ + event_router_t on_channel_pins_update; + + + /** + * @brief Called when all reactions are removed from a message. + * + * @see https://discord.com/developers/docs/topics/gateway-events#message-reaction-remove-all + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type message_reaction_remove_all_t&, and returns void. + */ + event_router_t on_message_reaction_remove_all; + + + /** + * @brief Called when we are told which voice server we can use. + * This will be sent either when we establish a new voice channel connection, + * or as discord rearrange their infrastructure. + * + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type voice_server_update_t&, and returns void. + */ + event_router_t on_voice_server_update; + + + /** + * @brief Called when new emojis are added to a guild. + * The complete set of emojis is sent every time. + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-emojis-update + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type guild_emojis_update_t&, and returns void. + */ + event_router_t on_guild_emojis_update; + + + /** + * @brief Called when new stickers are added to a guild. + * The complete set of stickers is sent every time. + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-stickers-update + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type guild_stickers_update_t&, and returns void. + */ + event_router_t on_guild_stickers_update; + + + /** + * @brief Called when a user's presence is updated. + * To receive these you will need the GUILD_PRESENCES privileged intent. + * You will receive many of these, very often, and receiving them will significantly + * increase your bot's CPU usage. If you don't need them it is recommended to not ask + * for them. + * + * @see https://discord.com/developers/docs/topics/gateway-events#presence-update + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type presence_update_t&, and returns void. + */ + event_router_t on_presence_update; + + + /** + * @brief Called when the webhooks for a guild are updated. + * + * @see https://discord.com/developers/docs/topics/gateway-events#webhooks-update + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type webhooks_update_t&, and returns void. + */ + event_router_t on_webhooks_update; + + /** + * @brief Called when a new automod rule is created. + * + * @see https://discord.com/developers/docs/topics/gateway-events#auto-moderation-rule-create + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type automod_rule_create_t&, and returns void. + */ + event_router_t on_automod_rule_create; + + + /** + * @brief Called when an automod rule is updated. + * + * @see https://discord.com/developers/docs/topics/gateway-events#auto-moderation-rule-update + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type automod_rule_update_t&, and returns void. + */ + event_router_t on_automod_rule_update; + + /** + * @brief Called when an automod rule is deleted. + * + * @see https://discord.com/developers/docs/topics/gateway-events#auto-moderation-rule-delete + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type automod_rule_delete_t&, and returns void. + */ + event_router_t on_automod_rule_delete; + + /** + * @brief Called when an automod rule is triggered/executed. + * + * @see https://discord.com/developers/docs/topics/gateway-events#auto-moderation-action-execution + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type automod_rule_execute_t&, and returns void. + */ + event_router_t on_automod_rule_execute; + + /** + * @brief Called when a new member joins a guild. + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-member-add + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type guild_member_add_t&, and returns void. + */ + event_router_t on_guild_member_add; + + + /** + * @brief Called when an invite is deleted from a guild. + * + * @see https://discord.com/developers/docs/topics/gateway-events#invite-delete + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type invite_delete_t&, and returns void. + */ + event_router_t on_invite_delete; + + + /** + * @brief Called when details of a guild are updated. + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-update + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type guild_update_t&, and returns void. + */ + event_router_t on_guild_update; + + + /** + * @brief Called when an integration is updated for a guild. + * This returns the complete list. + * An integration is a connection to a guild of a user's associated accounts, + * e.g. youtube or twitch, for automatic assignment of roles etc. + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-integrations-update + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type guild_integrations_update_t&, and returns void. + */ + event_router_t on_guild_integrations_update; + + + /** + * @brief Called when details of a guild member (e.g. their roles or nickname) are updated. + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-member-update + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type guild_member_update_t&, and returns void. + */ + event_router_t on_guild_member_update; + + + /** + * @brief Called when a new invite is created for a guild. + * + * @see https://discord.com/developers/docs/topics/gateway-events#invite-create + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type invite_create_t&, and returns void. + */ + event_router_t on_invite_create; + + + /** + * @brief Called when a message is updated (edited). + * + * @see https://discord.com/developers/docs/topics/gateway-events#message-update + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type message_update_t&, and returns void. + */ + event_router_t on_message_update; + + + /** + * @brief Called when a user is updated. + * This is separate to cluster::on_guild_member_update and includes things such as an avatar change, + * username change, discriminator change or change in subscription status for nitro. + * + * @see https://discord.com/developers/docs/topics/gateway-events#user-update + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type user_update_t&, and returns void. + */ + event_router_t on_user_update; + + + /** + * @brief Called when a new message arrives from discord. + * Note that D++ does not cache messages. If you want to cache these objects you + * should create something yourself within your bot. Caching of messages is not on + * the roadmap to be supported as it consumes excessive amounts of RAM. + * For an example for caching of messages, please see \ref caching-messages + * + * @see https://discord.com/developers/docs/topics/gateway-events#message-create + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type message_create_t&, and returns void. + */ + event_router_t on_message_create; + + /** + * @brief Called when a guild audit log entry is created. + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-audit-log-entry-create + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type guild_audit_log_entry_create_t&, and returns void. + */ + event_router_t on_guild_audit_log_entry_create; + + /** + * @brief Called when a ban is added to a guild. + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-ban-add + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type guild_ban_add_t&, and returns void. + */ + event_router_t on_guild_ban_add; + + + /** + * @brief Called when a ban is removed from a guild. + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-ban-remove + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type guild_ban_remove_t&, and returns void. + */ + event_router_t on_guild_ban_remove; + + + /** + * @brief Called when a new integration is attached to a guild by a user. + * An integration is a connection to a guild of a user's associated accounts, + * e.g. youtube or twitch, for automatic assignment of roles etc. + * + * @see https://discord.com/developers/docs/topics/gateway-events#integration-create + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type integration_create_t&, and returns void. + */ + event_router_t on_integration_create; + + + /** + * @brief Called when an integration is updated by a user. + * This returns details of just the single integration that has changed. + * An integration is a connection to a guild of a user's associated accounts, + * e.g. youtube or twitch, for automatic assignment of roles etc. + * + * @see https://discord.com/developers/docs/topics/gateway-events#integration-update + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type integration_update_t&, and returns void. + */ + event_router_t on_integration_update; + + + /** + * @brief Called when an integration is removed by a user. + * An integration is a connection to a guild of a user's associated accounts, + * e.g. youtube or twitch, for automatic assignment of roles etc. + * + * @see https://discord.com/developers/docs/topics/gateway-events#integration-delete + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type integration_delete_t&, and returns void. + */ + event_router_t on_integration_delete; + + + /** + * @brief Called when a thread is created. + * Note that threads are not cached by D++, but a list of thread IDs is accessible in a guild object + * + * @see https://discord.com/developers/docs/topics/gateway-events#thread-create + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type thread_create_t&, and returns void. + */ + event_router_t on_thread_create; + + + /** + * @brief Called when a thread is updated + * + * @see https://discord.com/developers/docs/topics/gateway-events#thread-update + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type thread_update_t&, and returns void. + */ + event_router_t on_thread_update; + + + /** + * @brief Called when a thread is deleted + * + * @see https://discord.com/developers/docs/topics/gateway-events#thread-delete + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type thread_delete_t&, and returns void. + */ + event_router_t on_thread_delete; + + + /** + * @brief Called when thread list is synced (upon gaining access to a channel). + * Note that threads are not cached by D++, but a list of thread IDs is accessible in a guild object + * + * @see https://discord.com/developers/docs/topics/gateway-events#thread-list-sync + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type thread_list_sync_t&, and returns void. + */ + event_router_t on_thread_list_sync; + + + /** + * @brief Called when current user's thread member object is updated + * + * @see https://discord.com/developers/docs/topics/gateway-events#thread-member-update + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type thread_member_update_t&, and returns void. + */ + event_router_t on_thread_member_update; + + + /** + * @brief Called when a thread's member list is updated (without GUILD_MEMBERS intent, is only called for current user) + * + * @see https://discord.com/developers/docs/topics/gateway-events#thread-members-update + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type thread_members_update_t&, and returns void. + */ + event_router_t on_thread_members_update; + + + /** + * @brief Called when a new scheduled event is created + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-create + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type guild_scheduled_event_create_t&, and returns void. + */ + event_router_t on_guild_scheduled_event_create; + + + /** + * @brief Called when a new scheduled event is updated + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-update + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type guild_scheduled_event_update_t&, and returns void. + */ + event_router_t on_guild_scheduled_event_update; + + + /** + * @brief Called when a new scheduled event is deleted + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-delete + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type guild_scheduled_event_delete_t&, and returns void. + */ + event_router_t on_guild_scheduled_event_delete; + + + /** + * @brief Called when a user is added to a scheduled event + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-user-add + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type guild_scheduled_event_user_add_t&, and returns void. + */ + event_router_t on_guild_scheduled_event_user_add; + + + /** + * @brief Called when a user is removed to a scheduled event + * + * @see https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-user-remove + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type guild_scheduled_event_user_remove_t&, and returns void. + */ + event_router_t on_guild_scheduled_event_user_remove; + + + /** + * @brief Called when packets are sent from the voice buffer. + * The voice buffer contains packets that are already encoded with Opus and encrypted + * with Sodium, and merged into packets by the repacketizer, which is done in the + * dpp::discord_voice_client::send_audio method. You should use the buffer size properties + * of dpp::voice_buffer_send_t to determine if you should fill the buffer with more + * content. + * + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type voice_buffer_send_t&, and returns void. + */ + event_router_t on_voice_buffer_send; + + + /** + * @brief Called when a user is talking on a voice channel. + * + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type voice_user_talking_t&, and returns void. + */ + event_router_t on_voice_user_talking; + + + /** + * @brief Called when a voice channel is connected and ready to send audio. + * Note that this is not directly attached to the READY event of the websocket, + * as there is further connection that needs to be done before audio is ready to send. + * + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type voice_ready_t&, and returns void. + */ + event_router_t on_voice_ready; + + + /** + * @brief Called when new audio data is received. + * Each separate user's audio from the voice channel will arrive tagged with + * their user id in the event, if a user can be attributed to the received audio. + * + * @note Receiving audio for bots is not officially supported by discord. + * + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type voice_receive_t&, and returns void. + */ + event_router_t on_voice_receive; + + /** + * @brief Called when new audio data is received, combined and mixed for all speaking users. + * + * @note Receiving audio for bots is not officially supported by discord. + * + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type voice_receive_t&, and returns void. + */ + event_router_t on_voice_receive_combined; + + /** + * @brief Called when sending of audio passes over a track marker. + * Track markers are arbitrarily placed "bookmarks" in the audio buffer, placed + * by the bot developer. Each track marker can have a string value associated with it + * which is specified in dpp::discord_voice_client::insert_marker and returned to this + * event. + * + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type voice_track_marker_t&, and returns void. + */ + event_router_t on_voice_track_marker; + + + /** + * @brief Called when a new stage instance is created on a stage channel. + * + * @see https://discord.com/developers/docs/topics/gateway-events#stage-instance-create + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type stage_instance_create_t&, and returns void. + */ + event_router_t on_stage_instance_create; + + + /** + * @brief Called when a stage instance is updated. + * + * @see https://discord.com/developers/docs/topics/gateway-events#stage-instance-update + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type stage_instance_update_t&, and returns void. + */ + event_router_t on_stage_instance_update; + + + /** + * @brief Called when an existing stage instance is deleted from a stage channel. + * + * @see https://discord.com/developers/docs/topics/gateway-events#stage-instance-delete + * @note Use operator() to attach a lambda to this event, and the detach method to detach the listener using the returned ID. + * The function signature for this event takes a single `const` reference of type stage_instance_delete_t&, and returns void. + */ + event_router_t on_stage_instance_delete; + + + /** + * @brief Post a REST request. Where possible use a helper method instead like message_create + * + * @param endpoint Endpoint to post to, e.g. /api/guilds + * @param major_parameters Major parameters for the endpoint e.g. a guild id + * @param parameters Minor parameters for the API request + * @param method Method, e.g. GET, POST + * @param postdata Post data (usually JSON encoded) + * @param callback Function to call when the HTTP call completes. The callback parameter will contain amongst other things, the decoded json. + * @param filename Filename to post for POST requests (for uploading files) + * @param filecontent File content to post for POST requests (for uploading files) + * @param filemimetype File content to post for POST requests (for uploading files) + */ + void post_rest(const std::string &endpoint, const std::string &major_parameters, const std::string ¶meters, http_method method, const std::string &postdata, json_encode_t callback, const std::string &filename = "", const std::string &filecontent = "", const std::string &filemimetype = ""); + + /** + * @brief Post a multipart REST request. Where possible use a helper method instead like message_create + * + * @param endpoint Endpoint to post to, e.g. /api/guilds + * @param major_parameters Major parameters for the endpoint e.g. a guild id + * @param parameters Minor parameters for the API request + * @param method Method, e.g. GET, POST + * @param postdata Post data (usually JSON encoded) + * @param callback Function to call when the HTTP call completes. The callback parameter will contain amongst other things, the decoded json. + * @param filename List of filenames to post for POST requests (for uploading files) + * @param filecontent List of file content to post for POST requests (for uploading files) + * @param filemimetypes List of mime types for each file to post for POST requests (for uploading files) + */ + void post_rest_multipart(const std::string &endpoint, const std::string &major_parameters, const std::string ¶meters, http_method method, const std::string &postdata, json_encode_t callback, const std::vector &filename = {}, const std::vector& filecontent = {}, const std::vector& filemimetypes = {}); + + /** + * @brief Make a HTTP(S) request. For use when wanting asynchronous access to HTTP APIs outside of Discord. + * + * @param url Full URL to post to, e.g. https://api.somewhere.com/v1/foo/ + * @param method Method, e.g. GET, POST + * @param callback Function to call when the HTTP call completes. No processing is done on the returned data. + * @param postdata POST data + * @param mimetype MIME type of POST data + * @param headers Headers to send with the request + */ + void request(const std::string &url, http_method method, http_completion_event callback, const std::string &postdata = "", const std::string &mimetype = "text/plain", const std::multimap &headers = {}); + + /** + * @brief Respond to a slash command + * + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#create-interaction-response + * @param interaction_id Interaction id to respond to + * @param token Token for the interaction webhook + * @param r Response to send + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void interaction_response_create(snowflake interaction_id, const std::string &token, const interaction_response &r, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Edit response to a slash command + * + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#edit-original-interaction-response + * @param token Token for the interaction webhook + * @param m Message to send + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void interaction_response_edit(const std::string &token, const message &m, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get the original response to a slash command + * + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#get-original-interaction-response + * @param token Token for the interaction webhook + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::message object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void interaction_response_get_original(const std::string &token, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Create a followup message to a slash command + * + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#create-interaction-response + * @param token Token for the interaction webhook + * @param m followup message to create + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void interaction_followup_create(const std::string &token, const message &m, command_completion_event_t callback); + + /** + * @brief Edit original followup message to a slash command + * This is an alias for cluster::interaction_response_edit + * @see cluster::interaction_response_edit + * + * @param token Token for the interaction webhook + * @param m message to edit, the ID should be set + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void interaction_followup_edit_original(const std::string &token, const message &m, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Delete the initial interaction response + * + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#delete-original-interaction-response + * @param token Token for the interaction webhook + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void interaction_followup_delete(const std::string &token, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Edit followup message to a slash command + * The message ID in the message you pass should be correctly set to that of a followup message you previously sent + * + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#edit-followup-message + * @param token Token for the interaction webhook + * @param m message to edit, the ID should be set + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void interaction_followup_edit(const std::string &token, const message &m, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get the followup message to a slash command + * + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#get-followup-message + * @param token Token for the interaction webhook + * @param message_id message to retrieve + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::message object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void interaction_followup_get(const std::string &token, snowflake message_id, command_completion_event_t callback); + + /** + * @brief Get the original followup message to a slash command + * This is an alias for cluster::interaction_response_get_original + * @see cluster::interaction_response_get_original + * + * @param token Token for the interaction webhook + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::message object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void interaction_followup_get_original(const std::string &token, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Create a global slash command (a bot can have a maximum of 100 of these). + * + * @see https://discord.com/developers/docs/interactions/application-commands#create-global-application-command + * @param s Slash command to create + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::slashcommand object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void global_command_create(const slashcommand &s, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get a global slash command + * + * @see https://discord.com/developers/docs/interactions/application-commands#get-global-application-command + * @param id The ID of the slash command + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::slashcommand object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void global_command_get(snowflake id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get the audit log for a guild + * + * @see https://discord.com/developers/docs/resources/audit-log#get-guild-audit-log + * @param guild_id Guild to get the audit log of + * @param user_id Entries from a specific user ID. Set this to `0` will fetch any user + * @param action_type Entries for a specific dpp::audit_type. Set this to `0` will fetch any type + * @param before Entries with ID less than a specific audit log entry ID. Used for paginating + * @param after Entries with ID greater than a specific audit log entry ID. Used for paginating + * @param limit Maximum number of entries (between 1-100) to return + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::auditlog object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_auditlog_get(snowflake guild_id, snowflake user_id, uint32_t action_type, snowflake before, snowflake after, uint32_t limit, command_completion_event_t callback); + + /** + * @brief Create a slash command local to a guild + * + * @see https://discord.com/developers/docs/interactions/application-commands#create-guild-application-command + * @note Creating a command with the same name as an existing command for your application will overwrite the old command. + * @param s Slash command to create + * @param guild_id Guild ID to create the slash command in + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::slashcommand object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_command_create(const slashcommand &s, snowflake guild_id, command_completion_event_t callback = utility::log_error()); + + + /** + * @brief Create/overwrite guild slash commands. + * Any existing guild slash commands on this guild will be deleted and replaced with these. + * + * @see https://discord.com/developers/docs/interactions/application-commands#bulk-overwrite-guild-application-commands + * @param commands Vector of slash commands to create/update. + * New guild commands will be available in the guild immediately. If the command did not already exist, it will count toward daily application command create limits. + * @param guild_id Guild ID to create/update the slash commands in + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::slashcommand_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_bulk_command_create(const std::vector &commands, snowflake guild_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Create/overwrite global slash commands. + * Any existing global slash commands will be deleted and replaced with these. + * + * @see https://discord.com/developers/docs/interactions/application-commands#bulk-overwrite-global-application-commands + * @param commands Vector of slash commands to create/update. + * overwriting existing commands that are registered globally for this application. Updates will be available in all guilds after 1 hour. + * Commands that do not already exist will count toward daily application command create limits. + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::slashcommand_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void global_bulk_command_create(const std::vector &commands, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Edit a global slash command (a bot can have a maximum of 100 of these) + * + * @see https://discord.com/developers/docs/interactions/application-commands#edit-global-application-command + * @param s Slash command to change + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void global_command_edit(const slashcommand &s, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get a slash command of a guild + * + * @see https://discord.com/developers/docs/interactions/application-commands#get-guild-application-command + * @note The returned slash commands will not have permissions set, you need to use a permissions getter e.g. dpp::guild_commands_get_permissions to get the guild command permissions + * @param id The ID of the slash command + * @param guild_id Guild ID to get the slash command from + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::slashcommand object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_command_get(snowflake id, snowflake guild_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Edit a slash command local to a guild + * + * @see https://discord.com/developers/docs/interactions/application-commands#edit-guild-application-command + * @param s Slash command to edit + * @param guild_id Guild ID to edit the slash command in + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_command_edit(const slashcommand &s, snowflake guild_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Edit slash command permissions of a guild + * + * @see https://discord.com/developers/docs/interactions/application-commands#edit-application-command-permissions + * @note You can only add up to 10 permission overwrites for a command + * @param s Slash command to edit the permissions for + * @param guild_id Guild ID to edit the slash command in + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_command_edit_permissions(const slashcommand &s, snowflake guild_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get the permissions for a slash command of a guild + * + * @see https://discord.com/developers/docs/interactions/application-commands#get-application-command-permissions + * @param id The ID of the slash command to get the permissions for + * @param guild_id Guild ID to get the permissions of + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::guild_command_permissions object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_command_get_permissions(snowflake id, snowflake guild_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Edit/Overwrite the permissions of all existing slash commands in a guild + * + * @note You can only add up to 10 permission overwrites for a command + * + * @see https://discord.com/developers/docs/interactions/application-commands#batch-edit-application-command-permissions + * @warning The endpoint will overwrite all existing permissions for all commands of the application in a guild, including slash commands, user commands, and message commands. Meaning that if you forgot to pass a slash command, the permissions of it might be removed. + * @param commands A vector of slash commands to edit/overwrite the permissions for + * @param guild_id Guild ID to edit permissions of the slash commands in + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::guild_command_permissions_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + * @deprecated This has been disabled with updates to Permissions v2. You can use guild_command_edit_permissions instead + */ + void guild_bulk_command_edit_permissions(const std::vector &commands, snowflake guild_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Delete a global slash command (a bot can have a maximum of 100 of these) + * + * @see https://discord.com/developers/docs/interactions/application-commands#delete-global-application-command + * @param id Slash command to delete + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void global_command_delete(snowflake id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Delete a slash command local to a guild + * + * @see https://discord.com/developers/docs/interactions/application-commands#delete-guild-application-command + * @param id Slash command to delete + * @param guild_id Guild ID to delete the slash command in + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_command_delete(snowflake id, snowflake guild_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get the application's slash commands for a guild + * + * @see https://discord.com/developers/docs/interactions/application-commands#get-guild-application-commands + * @note The returned slash commands will not have permissions set, you need to use a permissions getter e.g. dpp::guild_commands_get_permissions to get the guild command permissions + * @param guild_id Guild ID to get the slash commands for + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::slashcommand_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_commands_get(snowflake guild_id, command_completion_event_t callback); + + /** + * @brief Get all slash command permissions of a guild + * + * @see https://discord.com/developers/docs/interactions/application-commands#get-application-command-permissions + * @param guild_id Guild ID to get the slash commands permissions for + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::guild_command_permissions_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_commands_get_permissions(snowflake guild_id, command_completion_event_t callback); + + /** + * @brief Get the application's global slash commands + * + * @see https://discord.com/developers/docs/interactions/application-commands#get-global-application-commands + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::slashcommand_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void global_commands_get(command_completion_event_t callback); + + /** + * @brief Create a direct message, also create the channel for the direct message if needed + * + * @see https://discord.com/developers/docs/resources/user#create-dm + * @see https://discord.com/developers/docs/resources/channel#create-message + * @param user_id User ID of user to send message to + * @param m Message object + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::message object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void direct_message_create(snowflake user_id, const message &m, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get a message + * + * @see https://discord.com/developers/docs/resources/channel#get-channel-message + * @param message_id Message ID + * @param channel_id Channel ID + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::message object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void message_get(snowflake message_id, snowflake channel_id, command_completion_event_t callback); + + /** + * @brief Get multiple messages. + * + * This function will attempt to fetch as many messages as possible using multiple API calls if needed. + * + * @see https://discord.com/developers/docs/resources/channel#get-channel-messages + * @param channel_id Channel ID to retrieve messages for + * @param around Messages should be retrieved around this ID if this is set to non-zero + * @param before Messages before this ID should be retrieved if this is set to non-zero + * @param after Messages after this ID should be retrieved if this is set to non-zero + * @param limit This number of messages maximum should be returned, up to a maximum of 100. + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::message_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void messages_get(snowflake channel_id, snowflake around, snowflake before, snowflake after, uint64_t limit, command_completion_event_t callback); + + /** + * @brief Send a message to a channel. The callback function is called when the message has been sent + * + * @see https://discord.com/developers/docs/resources/channel#create-message + * @param m Message to send + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::message object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void message_create(const struct message &m, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Crosspost a message. The callback function is called when the message has been sent + * + * @see https://discord.com/developers/docs/resources/channel#crosspost-message + * @param message_id Message to crosspost + * @param channel_id Channel ID to crosspost from + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::message object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void message_crosspost(snowflake message_id, snowflake channel_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Edit a message on a channel. The callback function is called when the message has been edited + * + * @see https://discord.com/developers/docs/resources/channel#edit-message + * @param m Message to edit + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::message object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void message_edit(const struct message &m, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Add a reaction to a message. The reaction string must be either an `emojiname:id` or a unicode character. + * + * @see https://discord.com/developers/docs/resources/channel#create-reaction + * @param m Message to add a reaction to + * @param reaction Reaction to add. Emojis should be in the form emojiname:id + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void message_add_reaction(const struct message &m, const std::string &reaction, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Delete own reaction from a message. The reaction string must be either an `emojiname:id` or a unicode character. + * + * @see https://discord.com/developers/docs/resources/channel#delete-own-reaction + * @param m Message to delete own reaction from + * @param reaction Reaction to delete. The reaction should be in the form emojiname:id + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void message_delete_own_reaction(const struct message &m, const std::string &reaction, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Delete a user's reaction from a message. The reaction string must be either an `emojiname:id` or a unicode character + * + * @see https://discord.com/developers/docs/resources/channel#delete-user-reaction + * @param m Message to delete a user's reaction from + * @param user_id User ID who's reaction you want to remove + * @param reaction Reaction to remove. Reactions should be in the form emojiname:id + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void message_delete_reaction(const struct message &m, snowflake user_id, const std::string &reaction, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get reactions on a message for a particular emoji. The reaction string must be either an `emojiname:id` or a unicode character + * + * @see https://discord.com/developers/docs/resources/channel#get-reactions + * @param m Message to get reactions for + * @param reaction Reaction should be in the form emojiname:id or a unicode character + * @param before Reactions before this ID should be retrieved if this is set to non-zero + * @param after Reactions before this ID should be retrieved if this is set to non-zero + * @param limit This number of reactions maximum should be returned + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::user_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void message_get_reactions(const struct message &m, const std::string &reaction, snowflake before, snowflake after, snowflake limit, command_completion_event_t callback); + + /** + * @brief Delete all reactions on a message + * + * @see https://discord.com/developers/docs/resources/channel#delete-all-reactions + * @param m Message to delete reactions from + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void message_delete_all_reactions(const struct message &m, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Delete all reactions on a message using a particular emoji. The reaction string must be either an `emojiname:id` or a unicode character + * + * @see https://discord.com/developers/docs/resources/channel#delete-all-reactions-for-emoji + * @param m Message to delete reactions from + * @param reaction Reaction to delete, in the form emojiname:id or a unicode character + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void message_delete_reaction_emoji(const struct message &m, const std::string &reaction, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Add a reaction to a message by id. The reaction string must be either an `emojiname:id` or a unicode character. + * + * @see https://discord.com/developers/docs/topics/gateway#message-reaction-add + * @param message_id Message to add reactions to + * @param channel_id Channel to add reactions to + * @param reaction Reaction to add. Emojis should be in the form emojiname:id + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void message_add_reaction(snowflake message_id, snowflake channel_id, const std::string &reaction, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Delete own reaction from a message by id. The reaction string must be either an `emojiname:id` or a unicode character. + * + * @see https://discord.com/developers/docs/resources/channel#delete-own-reaction + * @param message_id Message to delete reactions from + * @param channel_id Channel to delete reactions from + * @param reaction Reaction to delete. The reaction should be in the form emojiname:id + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void message_delete_own_reaction(snowflake message_id, snowflake channel_id, const std::string &reaction, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Delete a user's reaction from a message by id. The reaction string must be either an `emojiname:id` or a unicode character + * + * @see https://discord.com/developers/docs/resources/channel#delete-user-reaction + * @param message_id Message to delete reactions from + * @param channel_id Channel to delete reactions from + * @param user_id User ID who's reaction you want to remove + * @param reaction Reaction to remove. Reactions should be in the form emojiname:id + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void message_delete_reaction(snowflake message_id, snowflake channel_id, snowflake user_id, const std::string &reaction, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get reactions on a message for a particular emoji by id. The reaction string must be either an `emojiname:id` or a unicode character + * + * @see https://discord.com/developers/docs/resources/channel#get-reactions + * @param message_id Message to get reactions for + * @param channel_id Channel to get reactions for + * @param reaction Reaction should be in the form emojiname:id or a unicode character + * @param before Reactions before this ID should be retrieved if this is set to non-zero + * @param after Reactions before this ID should be retrieved if this is set to non-zero + * @param limit This number of reactions maximum should be returned + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::user_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void message_get_reactions(snowflake message_id, snowflake channel_id, const std::string &reaction, snowflake before, snowflake after, snowflake limit, command_completion_event_t callback); + + /** + * @brief Delete all reactions on a message by id + * + * @see https://discord.com/developers/docs/resources/channel#delete-all-reactions + * @param message_id Message to delete reactions from + * @param channel_id Channel to delete reactions from + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void message_delete_all_reactions(snowflake message_id, snowflake channel_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Delete all reactions on a message using a particular emoji by id. The reaction string must be either an `emojiname:id` or a unicode character + * + * @see https://discord.com/developers/docs/resources/channel#delete-all-reactions-for-emoji + * @param message_id Message to delete reactions from + * @param channel_id Channel to delete reactions from + * @param reaction Reaction to delete, in the form emojiname:id or a unicode character + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void message_delete_reaction_emoji(snowflake message_id, snowflake channel_id, const std::string &reaction, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Delete a message from a channel. The callback function is called when the message has been edited + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * + * @see https://discord.com/developers/docs/resources/channel#delete-message + * @param message_id Message ID to delete + * @param channel_id Channel to delete from + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void message_delete(snowflake message_id, snowflake channel_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Bulk delete messages from a channel. The callback function is called when the message has been edited + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * + * @note If any message provided older than 2 weeks or any duplicate message ID, it will fail. + * + * @see https://discord.com/developers/docs/resources/channel#bulk-delete-messages + * @param message_ids List of message IDs to delete (at least 2 and at most 100 message IDs) + * @param channel_id Channel to delete from + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void message_delete_bulk(const std::vector &message_ids, snowflake channel_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get a channel + * + * @see https://discord.com/developers/docs/resources/channel#get-channel + * @param c Channel ID to retrieve + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::channel object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void channel_get(snowflake c, command_completion_event_t callback); + + /** + * @brief Get all channels for a guild + * + * @see https://discord.com/developers/docs/resources/channel#get-channels + * @param guild_id Guild ID to retrieve channels for + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::channel_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void channels_get(snowflake guild_id, command_completion_event_t callback); + + /** + * @brief Create a channel + * + * Create a new channel object for the guild. Requires the `MANAGE_CHANNELS` permission. If setting permission overwrites, + * only permissions your bot has in the guild can be allowed/denied. Setting `MANAGE_ROLES` permission in channels is only possible + * for guild administrators. Returns the new channel object on success. Fires a `Channel Create Gateway` event. + * + * All parameters to this endpoint are optional excluding `name` + * + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see https://discord.com/developers/docs/resources/channel#create-channel + * @param c Channel to create + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::channel object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void channel_create(const class channel &c, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Edit a channel + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see https://discord.com/developers/docs/resources/channel#modify-channel + * @param c Channel to edit/update + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::channel object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void channel_edit(const class channel &c, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Edit multiple channels positions + * + * Modify the positions of a set of channel objects for the guild. + * Requires `MANAGE_CHANNELS` permission. Fires multiple `Channel Update Gateway` events. + * Only channels to be modified are required. + * + * @see https://discord.com/developers/docs/resources/guild#modify-guild-channel-positions + * @param c Channel to change the position for + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void channel_edit_positions(const std::vector &c, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Edit a channel's permissions + * + * @see https://discord.com/developers/docs/resources/channel#edit-channel-permissions + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param c Channel to set permissions for + * @param overwrite_id Overwrite to change (a user or role ID) + * @param allow allow permissions bitmask + * @param deny deny permissions bitmask + * @param member true if the overwrite_id is a user id, false if it is a channel id + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void channel_edit_permissions(const class channel &c, const snowflake overwrite_id, const uint64_t allow, const uint64_t deny, const bool member, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Edit a channel's permissions + * + * @see https://discord.com/developers/docs/resources/channel#edit-channel-permissions + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param channel_id ID of the channel to set permissions for + * @param overwrite_id Overwrite to change (a user or role ID) + * @param allow allow permissions bitmask + * @param deny deny permissions bitmask + * @param member true if the overwrite_id is a user id, false if it is a channel id + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void channel_edit_permissions(const snowflake channel_id, const snowflake overwrite_id, const uint64_t allow, const uint64_t deny, const bool member, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Delete a channel + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see https://discord.com/developers/docs/resources/channel#deleteclose-channel + * @param channel_id Channel id to delete + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void channel_delete(snowflake channel_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get details about an invite + * + * @see https://discord.com/developers/docs/resources/invite#get-invite + * @param invite_code Invite code to get information on + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::invite object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void invite_get(const std::string &invite_code, command_completion_event_t callback); + + /** + * @brief Delete an invite + * + * @see https://discord.com/developers/docs/resources/invite#delete-invite + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param invite Invite code to delete + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::invite object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void invite_delete(const std::string &invite, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get invites for a channel + * + * @see https://discord.com/developers/docs/resources/invite#get-invites + * @param c Channel to get invites for + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::invite_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void channel_invites_get(const class channel &c, command_completion_event_t callback); + + /** + * @brief Create invite for a channel + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see https://discord.com/developers/docs/resources/channel#create-channel-invite + * @param c Channel to create an invite on + * @param i Invite to create + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::invite object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void channel_invite_create(const class channel &c, const class invite &i, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get a channel's pins + * @see https://discord.com/developers/docs/resources/channel#get-pinned-messages + * @param channel_id Channel ID to get pins for + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::message_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void channel_pins_get(snowflake channel_id, command_completion_event_t callback); + + /** + * @brief Adds a recipient to a Group DM using their access token + * @see https://discord.com/developers/docs/resources/channel#group-dm-add-recipient + * @param channel_id Channel id to add group DM recipients to + * @param user_id User ID to add + * @param access_token Access token from OAuth2 + * @param nick Nickname of user to apply to the chat + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void gdm_add(snowflake channel_id, snowflake user_id, const std::string &access_token, const std::string &nick, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Removes a recipient from a Group DM + * @see https://discord.com/developers/docs/resources/channel#group-dm-remove-recipient + * @param channel_id Channel ID of group DM + * @param user_id User ID to remove from group DM + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void gdm_remove(snowflake channel_id, snowflake user_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Remove a permission from a channel + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see https://discord.com/developers/docs/resources/channel#delete-channel-permission + * @param c Channel to remove permission from + * @param overwrite_id Overwrite to remove, user or channel ID + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void channel_delete_permission(const class channel &c, snowflake overwrite_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Follow an announcement (news) channel + * @see https://discord.com/developers/docs/resources/channel#follow-news-channel + * @param c Channel id to follow + * @param target_channel_id Channel to subscribe the channel to + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void channel_follow_news(const class channel &c, snowflake target_channel_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Trigger channel typing indicator + * @see https://discord.com/developers/docs/resources/channel#trigger-typing-indicator + * @param c Channel to set as typing on + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void channel_typing(const class channel &c, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Trigger channel typing indicator + * @see https://discord.com/developers/docs/resources/channel#trigger-typing-indicator + * @param cid Channel ID to set as typing on + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void channel_typing(snowflake cid, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Pin a message + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see https://discord.com/developers/docs/resources/channel#pin-message + * @param channel_id Channel id to pin message on + * @param message_id Message id to pin message on + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void message_pin(snowflake channel_id, snowflake message_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Unpin a message + * @see https://discord.com/developers/docs/resources/channel#unpin-message + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param channel_id Channel id to unpin message on + * @param message_id Message id to unpin message on + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void message_unpin(snowflake channel_id, snowflake message_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get a guild + * + * Returns the guild object for the given id. This endpoint will also return approximate_member_count and approximate_presence_count + * for the guild. + * @see https://discord.com/developers/docs/resources/guild#get-guild + * @param g Guild ID to retrieve + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::guild object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_get(snowflake g, command_completion_event_t callback); + + /** + * @brief Get a guild preview. Returns a guild object but only a subset of the fields will be populated. + * + * Returns the guild preview object for the given id `g`. If the user is not in the guild, then the guild + * must be lurkable (it must be Discoverable or have a live public stage). + * @see https://discord.com/developers/docs/resources/guild#get-guild-preview + * @param g Guild ID to retrieve + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::guild object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_get_preview(snowflake g, command_completion_event_t callback); + + /** + * @brief Get a guild member + * @see https://discord.com/developers/docs/resources/guild#get-guild-member + * @param guild_id Guild ID to get member for + * @param user_id User ID of member to get + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::guild_member object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_get_member(snowflake guild_id, snowflake user_id, command_completion_event_t callback); + + /** + * @brief Search for guild members based on whether their username or nickname starts with the given string. + * + * @note This endpoint is restricted according to whether the `GUILD_MEMBERS` Privileged Intent is enabled for your application. + * @see https://discord.com/developers/docs/resources/guild#search-guild-members + * @param guild_id Guild ID to search in + * @param query Query string to match username(s) and nickname(s) against + * @param limit max number of members to return (1-1000) + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::guild_member_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_search_members(snowflake guild_id, const std::string& query, uint16_t limit, command_completion_event_t callback); + + /** + * @brief Get all guild members + * + * @note This endpoint is restricted according to whether the `GUILD_MEMBERS` Privileged Intent is enabled for your application. + * @see https://discord.com/developers/docs/resources/guild#get-guild-members + * @param guild_id Guild ID to get all members for + * @param limit max number of members to return (1-1000) + * @param after the highest user id in the previous page + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::guild_member_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_get_members(snowflake guild_id, uint16_t limit, snowflake after, command_completion_event_t callback); + + /** + * @brief Add guild member. Needs a specific oauth2 scope, from which you get the access_token. + * + * Adds a user to the guild, provided you have a valid oauth2 access token for the user with the guilds.join scope. + * Returns the guild_member, which is defaulted if the user is already a member of the guild. Fires a `Guild Member Add` Gateway event. + * + * For guilds with Membership Screening enabled, this endpoint will default to adding new members as pending in the guild member object. + * Members that are pending will have to complete membership screening before they become full members that can talk. + * + * @note All parameters to this endpoint except for access_token are optional. + * The bot must be a member of the guild with `CREATE_INSTANT_INVITE` permission. + * @see https://discord.com/developers/docs/resources/guild#add-guild-member + * @param gm Guild member to add + * @param access_token Access token from Oauth2 scope + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::guild_member object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_add_member(const guild_member& gm, const std::string &access_token, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Edit the properties of an existing guild member + * + * Modify attributes of a guild member. Returns the guild_member. Fires a `Guild Member Update` Gateway event. + * To remove a timeout, set the `communication_disabled_until` to a non-zero time in the past, e.g. 1. + * When moving members to channels, the API user must have permissions to both connect to the channel and have the `MOVE_MEMBERS` permission. + * For moving and disconnecting users from voice, use dpp::cluster::guild_member_move. + * @see https://discord.com/developers/docs/resources/guild#modify-guild-member + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param gm Guild member to edit + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::guild_member object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_edit_member(const guild_member& gm, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Moves the guild member to a other voice channel, if member is connected to one. + * Set the `channel_id` to `0` to disconnect the user. + * + * Fires a `Guild Member Update` Gateway event. + * @note When moving members to channels, the API user __must__ have permissions to both connect to the channel and have the `MOVE_MEMBERS` permission. + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see https://discord.com/developers/docs/resources/guild#modify-guild-member + * @param channel_id Id of the channel to which the user is used. Set to `0` to disconnect the user + * @param guild_id Guild id to which the user is connected + * @param user_id User id, who should be moved + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::guild_member object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_member_move(const snowflake channel_id, const snowflake guild_id, const snowflake user_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Change current user nickname + * + * Modifies the nickname of the current user in a guild. + * Fires a `Guild Member Update` Gateway event. + * + * @deprecated Deprecated in favor of Modify Current Member. Will be replaced by dpp::cluster::guild_current_member_edit + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see https://discord.com/developers/docs/resources/guild#modify-current-user-nick + * @param guild_id Guild ID to change nickname on + * @param nickname New nickname, or empty string to clear nickname + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_set_nickname(snowflake guild_id, const std::string &nickname, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Add role to guild member + * + * Adds a role to a guild member. Requires the `MANAGE_ROLES` permission. + * Fires a `Guild Member Update` Gateway event. + * @see https://discord.com/developers/docs/resources/guild#add-guild-member-role + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to add a role to + * @param user_id User ID to add role to + * @param role_id Role ID to add to the user + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_member_add_role(snowflake guild_id, snowflake user_id, snowflake role_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Remove role from guild member + * + * Removes a role from a guild member. Requires the `MANAGE_ROLES` permission. + * Fires a `Guild Member Update` Gateway event. + * @see https://discord.com/developers/docs/resources/guild#remove-guild-member-role + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to remove role from user on + * @param user_id User ID to remove role from + * @param role_id Role to remove + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + * @deprecated Use dpp::cluster::guild_member_remove_role instead + */ + void guild_member_delete_role(snowflake guild_id, snowflake user_id, snowflake role_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Remove role from guild member + * + * Removes a role from a guild member. Requires the `MANAGE_ROLES` permission. + * Fires a `Guild Member Update` Gateway event. + * @see https://discord.com/developers/docs/resources/guild#remove-guild-member-role + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to remove role from user on + * @param user_id User ID to remove role from + * @param role_id Role to remove + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_member_remove_role(snowflake guild_id, snowflake user_id, snowflake role_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Remove (kick) a guild member + * + * Remove a member from a guild. Requires `KICK_MEMBERS` permission. + * Fires a `Guild Member Remove` Gateway event. + * @see https://discord.com/developers/docs/resources/guild#remove-guild-member + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @deprecated Replaced by dpp::cluster::guild_member_kick + * @param guild_id Guild ID to kick member from + * @param user_id User ID to kick + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_member_delete(snowflake guild_id, snowflake user_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Remove (kick) a guild member + * + * Remove a member from a guild. Requires `KICK_MEMBERS` permission. + * Fires a `Guild Member Remove` Gateway event. + * @see https://discord.com/developers/docs/resources/guild#remove-guild-member + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to kick member from + * @param user_id User ID to kick + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_member_kick(snowflake guild_id, snowflake user_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Set the timeout of a guild member + * + * Fires a `Guild Member Update` Gateway event. + * @see https://discord.com/developers/docs/resources/guild#modify-guild-member + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to timeout the member in + * @param user_id User ID to set the timeout for + * @param communication_disabled_until The timestamp when the user's timeout will expire (up to 28 days in the future). Set to 0 to remove the timeout + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_member_timeout(snowflake guild_id, snowflake user_id, time_t communication_disabled_until, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Add guild ban + * + * Create a guild ban, and optionally delete previous messages sent by the banned user. + * Requires the `BAN_MEMBERS` permission. Fires a `Guild Ban Add` Gateway event. + * @see https://discord.com/developers/docs/resources/guild#create-guild-ban + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to add ban to + * @param user_id User ID to ban + * @param delete_message_seconds How many seconds to delete messages for, between 0 and 604800 (7 days). Defaults to 0 + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_ban_add(snowflake guild_id, snowflake user_id, uint32_t delete_message_seconds = 0, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Delete guild ban + * + * Remove the ban for a user. Requires the `BAN_MEMBERS` permissions. + * Fires a Guild Ban Remove Gateway event. + * @see https://discord.com/developers/docs/resources/guild#remove-guild-ban + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild to delete ban from + * @param user_id User ID to delete ban for + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_ban_delete(snowflake guild_id, snowflake user_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get guild ban list + * + * Requires the `BAN_MEMBERS` permission. + * @see https://discord.com/developers/docs/resources/guild#get-guild-bans + * @note Provide a user ID to `before` and `after` for pagination. Users will always be returned in ascending order by the user ID. If both before and after are provided, only before is respected. + * @param guild_id Guild ID to get bans for + * @param before If non-zero, all bans for user ids before this user id will be returned up to the limit + * @param after if non-zero, all bans for user ids after this user id will be returned up to the limit + * @param limit the maximum number of bans to retrieve in this call up to a maximum of 1000 + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::ban_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_get_bans(snowflake guild_id, snowflake before, snowflake after, snowflake limit, command_completion_event_t callback); + + /** + * @brief Get single guild ban + * + * Requires the `BAN_MEMBERS` permission. + * @see https://discord.com/developers/docs/resources/guild#get-guild-ban + * @param guild_id Guild ID to get ban for + * @param user_id User ID of ban to retrieve + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::ban object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_get_ban(snowflake guild_id, snowflake user_id, command_completion_event_t callback); + + /** + * @brief Get a template + * @see https://discord.com/developers/docs/resources/guild-template#get-guild-template + * @param code Template code + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::dtemplate object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void template_get(const std::string &code, command_completion_event_t callback); + + /** + * @brief Create a new guild based on a template. + * @note This endpoint can be used only by bots in less than 10 guilds. + * @see https://discord.com/developers/docs/resources/guild-template#create-guild-from-guild-template + * @param code Template code to create guild from + * @param name Guild name to create + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::guild object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_create_from_template(const std::string &code, const std::string &name, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get guild templates + * + * @see https://discord.com/developers/docs/resources/guild-template#get-guild-templates + * @param guild_id Guild ID to get templates for + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::dtemplate_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_templates_get(snowflake guild_id, command_completion_event_t callback); + + /** + * @brief Creates a template for the guild + * + * @see https://discord.com/developers/docs/resources/guild-template#create-guild-template + * @param guild_id Guild to create template from + * @param name Template name to create + * @param description Description of template to create + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::dtemplate object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_template_create(snowflake guild_id, const std::string &name, const std::string &description, command_completion_event_t callback); + + /** + * @brief Syncs the template to the guild's current state. + * + * @see https://discord.com/developers/docs/resources/guild-template#sync-guild-template + * @param guild_id Guild to synchronise template for + * @param code Code of template to synchronise + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::dtemplate object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_template_sync(snowflake guild_id, const std::string &code, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Modifies the template's metadata. + * + * @see https://discord.com/developers/docs/resources/guild-template#modify-guild-template + * @param guild_id Guild ID of template to modify + * @param code Template code to modify + * @param name New name of template + * @param description New description of template + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::dtemplate object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_template_modify(snowflake guild_id, const std::string &code, const std::string &name, const std::string &description, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Deletes the template + * + * @see https://discord.com/developers/docs/resources/guild-template#delete-guild-template + * @param guild_id Guild ID of template to delete + * @param code Template code to delete + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_template_delete(snowflake guild_id, const std::string &code, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Create a guild + * + * Create a new guild. Returns a guild object on success. `Fires a Guild Create Gateway` event. + * + * When using the roles parameter, the first member of the array is used to change properties of the guild's everyone role. + * If you are trying to bootstrap a guild with additional roles, keep this in mind. The required id field within each role object is an + * integer placeholder, and will be replaced by the API upon consumption. Its purpose is to allow you to overwrite a role's permissions + * in a channel when also passing in channels with the channels array. + * When using the channels parameter, the position field is ignored, and none of the default channels are created. The id field within + * each channel object may be set to an integer placeholder, and will be replaced by the API upon consumption. Its purpose is to + * allow you to create `GUILD_CATEGORY` channels by setting the `parent_id` field on any children to the category's id field. + * Category channels must be listed before any children. + * + * @see https://discord.com/developers/docs/resources/guild#create-guild + * @note The region field is deprecated and is replaced by channel.rtc_region. This endpoint can be used only by bots in less than 10 guilds. + * @param g Guild to create + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::guild object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_create(const class guild &g, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Edit a guild + * + * Modify a guild's settings. Requires the `MANAGE_GUILD` permission. Returns the updated guild object on success. + * Fires a `Guild Update Gateway` event. + * + * @see https://discord.com/developers/docs/resources/guild#modify-guild + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param g Guild to edit + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::guild object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_edit(const class guild &g, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Delete a guild + * + * Delete a guild permanently. User must be owner. Fires a `Guild Delete Gateway` event. + * + * @see https://discord.com/developers/docs/resources/guild#delete-guild + * @param guild_id Guild ID to delete + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_delete(snowflake guild_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get all emojis for a guild + * + * @see https://discord.com/developers/docs/resources/emoji#list-guild-emojis + * @param guild_id Guild ID to get emojis for + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::emoji_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_emojis_get(snowflake guild_id, command_completion_event_t callback); + + /** + * @brief Get a single emoji + * + * @see https://discord.com/developers/docs/resources/emoji#get-guild-emoji + * @param guild_id Guild ID to get emoji for + * @param emoji_id Emoji ID to get + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::emoji object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_emoji_get(snowflake guild_id, snowflake emoji_id, command_completion_event_t callback); + + /** + * @brief Create single emoji. + * You must ensure that the emoji passed contained image data using the emoji::load_image() method. + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * + * @see https://discord.com/developers/docs/resources/emoji#create-guild-emoji + * @param guild_id Guild ID to create emoji om + * @param newemoji Emoji to create + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::emoji object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_emoji_create(snowflake guild_id, const class emoji& newemoji, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Edit a single emoji. + * + * You must ensure that the emoji passed contained image data using the emoji::load_image() method. + * @see https://discord.com/developers/docs/resources/emoji#modify-guild-emoji + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to edit emoji on + * @param newemoji Emoji to edit + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::emoji object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_emoji_edit(snowflake guild_id, const class emoji& newemoji, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Delete a guild emoji + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * + * @see https://discord.com/developers/docs/resources/emoji#delete-guild-emoji + * @param guild_id Guild ID to delete emoji on + * @param emoji_id Emoji ID to delete + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_emoji_delete(snowflake guild_id, snowflake emoji_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get prune counts + * + * Returns a prune object indicating the number of members that would be removed in a prune operation. Requires the `KICK_MEMBERS` + * permission. By default, prune will not remove users with roles. You can optionally include specific roles in your prune by providing the + * include_roles parameter. Any inactive user that has a subset of the provided role(s) will be counted in the prune and users with additional + * roles will not. + * + * @see https://discord.com/developers/docs/resources/guild#get-guild-prune-count + * @param guild_id Guild ID to count for pruning + * @param pruneinfo Pruning info + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::prune object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_get_prune_counts(snowflake guild_id, const struct prune& pruneinfo, command_completion_event_t callback); + + /** + * @brief Begin guild prune + * + * Begin a prune operation. Requires the `KICK_MEMBERS` permission. Returns a prune object indicating the number of members + * that were removed in the prune operation. For large guilds it's recommended to set the `compute_prune_count` option to false, forcing + * 'pruned' to 0. Fires multiple `Guild Member Remove` Gateway events. + * By default, prune will not remove users with roles. You can optionally include specific roles in your prune by providing the `include_roles` + * parameter. Any inactive user that has a subset of the provided role(s) will be included in the prune and users with additional roles will not. + * + * @see https://discord.com/developers/docs/resources/guild#begin-guild-prune + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to prune + * @param pruneinfo Pruning info + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::prune object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_begin_prune(snowflake guild_id, const struct prune& pruneinfo, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get guild voice regions. + * + * Voice regions per guild are somewhat deprecated in preference of per-channel voice regions. + * Returns a list of voice region objects for the guild. Unlike the similar /voice route, this returns VIP servers when + * the guild is VIP-enabled. + * + * @see https://discord.com/developers/docs/resources/guild#get-guild-voice-regions + * @param guild_id Guild ID to get voice regions for + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::voiceregion_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_get_voice_regions(snowflake guild_id, command_completion_event_t callback); + + /** + * @brief Get guild invites + * + * Returns a list of invite objects (with invite metadata) for the guild. Requires the `MANAGE_GUILD` permission. + * + * @see https://discord.com/developers/docs/resources/guild#get-guild-invites + * @param guild_id Guild ID to get invites for + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::invite_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_get_invites(snowflake guild_id, command_completion_event_t callback); + + /** + * @brief Get guild integrations + * + * Requires the `MANAGE_GUILD` permission. + * + * @see https://discord.com/developers/docs/resources/guild#get-guild-integrations + * @param guild_id Guild ID to get integrations for + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::integration_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + * + * @note This endpoint returns a maximum of 50 integrations. If a guild has more integrations, they cannot be accessed. + */ + void guild_get_integrations(snowflake guild_id, command_completion_event_t callback); + + /** + * @brief Modify guild integration + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * + * @see https://discord.com/developers/docs/resources/guild#modify-guild-integration + * @param guild_id Guild ID to modify integration for + * @param i Integration to modify + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::integration object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_modify_integration(snowflake guild_id, const class integration &i, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Delete guild integration + * + * Delete the attached integration object for the guild. Deletes any associated webhooks and kicks the associated bot if there is one. + * Requires the `MANAGE_GUILD` permission. Fires a Guild Integrations Update Gateway event. + * + * @see https://discord.com/developers/docs/resources/guild#delete-guild-integration + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to delete integration for + * @param integration_id Integration ID to delete + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_delete_integration(snowflake guild_id, snowflake integration_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Sync guild integration + * + * @see https://discord.com/developers/docs/resources/guild#sync-guild-integration + * @param guild_id Guild ID to sync integration on + * @param integration_id Integration ID to synchronise + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_sync_integration(snowflake guild_id, snowflake integration_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get guild widget + * + * Requires the `MANAGE_GUILD` permission. + * + * @see https://discord.com/developers/docs/resources/guild#get-guild-widget + * @param guild_id Guild ID to get widget for + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::guild_widget object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_get_widget(snowflake guild_id, command_completion_event_t callback); + + /** + * @brief Edit guild widget + * + * Requires the `MANAGE_GUILD` permission. + * + * @see https://discord.com/developers/docs/resources/guild#modify-guild-widget + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to edit widget for + * @param gw New guild widget information + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::guild_widget object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_edit_widget(snowflake guild_id, const class guild_widget &gw, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get guild vanity url, if enabled + * + * Returns a partial dpp::invite object for guilds with that feature enabled. Requires the `MANAGE_GUILD` permission. code will be null if a vanity url for the guild is not set. + * @see https://discord.com/developers/docs/resources/guild#get-guild-vanity-url + * @param guild_id Guild to get vanity URL for + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::invite object in confirmation_callback_t::value filled to match the vanity url. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_get_vanity(snowflake guild_id, command_completion_event_t callback); + + /** + * @brief Get the guild's onboarding configuration + * + * @see https://discord.com/developers/docs/resources/guild#get-guild-onboarding + * @param o The onboarding object + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::onboarding object in confirmation_callback_t::value filled to match the vanity url. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_get_onboarding(snowflake guild_id, command_completion_event_t callback); + + /** + * @brief Edit the guild's onboarding configuration + * + * Requires the `MANAGE_GUILD` and `MANAGE_ROLES` permissions. + * + * @note Onboarding enforces constraints when enabled. These constraints are that there must be at least 7 Default Channels and at least 5 of them must allow sending messages to the \@everyone role. The `onboarding::mode` field modifies what is considered when enforcing these constraints. + * + * @see https://discord.com/developers/docs/resources/guild#modify-guild-onboarding + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param o The onboarding object + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::onboarding object in confirmation_callback_t::value filled to match the vanity url. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_edit_onboarding(const struct onboarding& o, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get the guild's welcome screen + * + * If the welcome screen is not enabled, the `MANAGE_GUILD` permission is required. + * + * @see https://discord.com/developers/docs/resources/guild#get-guild-welcome-screen + * @param guild_id The guild ID to get the welcome screen from + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::welcome_screen object in confirmation_callback_t::value filled to match the vanity url. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_get_welcome_screen(snowflake guild_id, command_completion_event_t callback); + + /** + * @brief Edit the guild's welcome screen + * + * Requires the `MANAGE_GUILD` permission. May fire a `Guild Update` Gateway event. + * + * @see https://discord.com/developers/docs/resources/guild#modify-guild-welcome-screen + * @param guild_id The guild ID to edit the welcome screen for + * @param welcome_screen The welcome screen + * @param enabled Whether the welcome screen should be enabled or disabled + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::welcome_screen object in confirmation_callback_t::value filled to match the vanity url. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_edit_welcome_screen(snowflake guild_id, const struct welcome_screen& welcome_screen, bool enabled, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Create a webhook + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see https://discord.com/developers/docs/resources/webhook#create-webhook + * @param w Webhook to create + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::webhook object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void create_webhook(const class webhook &w, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get guild webhooks + * @see https://discord.com/developers/docs/resources/webhook#get-guild-webhooks + * @param guild_id Guild ID to get webhooks for + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::webhook_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void get_guild_webhooks(snowflake guild_id, command_completion_event_t callback); + + /** + * @brief Get channel webhooks + * @see https://discord.com/developers/docs/resources/webhook#get-guild-webhooks + * @param channel_id Channel ID to get webhooks for + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::webhook_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void get_channel_webhooks(snowflake channel_id, command_completion_event_t callback); + + /** + * @brief Get webhook + * @see https://discord.com/developers/docs/resources/webhook#get-webhook + * @param webhook_id Webhook ID to get + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::webhook object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void get_webhook(snowflake webhook_id, command_completion_event_t callback); + + /** + * @brief Get webhook using token + * @see https://discord.com/developers/docs/resources/webhook#get-webhook-with-token + * @param webhook_id Webhook ID to retrieve + * @param token Token of webhook + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::webhook object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void get_webhook_with_token(snowflake webhook_id, const std::string &token, command_completion_event_t callback); + + /** + * @brief Edit webhook + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see https://discord.com/developers/docs/resources/webhook#modify-webhook + * @param wh Webhook to edit + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::webhook object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void edit_webhook(const class webhook& wh, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Edit webhook with token (token is encapsulated in the webhook object) + * @see https://discord.com/developers/docs/resources/webhook#modify-webhook-with-token + * @param wh Webhook to edit (should include token) + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::webhook object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void edit_webhook_with_token(const class webhook& wh, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Delete a webhook + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see https://discord.com/developers/docs/resources/webhook#delete-webhook + * @param webhook_id Webhook ID to delete + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void delete_webhook(snowflake webhook_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Delete webhook with token + * @see https://discord.com/developers/docs/resources/webhook#delete-webhook-with-token + * @param webhook_id Webhook ID to delete + * @param token Token of webhook to delete + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void delete_webhook_with_token(snowflake webhook_id, const std::string &token, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Execute webhook + * + * @see https://discord.com/developers/docs/resources/webhook#execute-webhook + * @param wh Webhook to execute + * @param m Message to send + * @param wait waits for server confirmation of message send before response, and returns the created message body + * @param thread_id Send a message to the specified thread within a webhook's channel. The thread will automatically be unarchived + * @param thread_name Name of thread to create (requires the webhook channel to be a forum channel) + * @param callback Function to call when the API call completes. + * @note If the webhook channel is a forum channel, you must provide either `thread_id` or `thread_name`. If `thread_id` is provided, the message will send in that thread. If `thread_name` is provided, a thread with that name will be created in the forum channel. + * On success the callback will contain a dpp::message object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void execute_webhook(const class webhook &wh, const struct message &m, bool wait = false, snowflake thread_id = 0, const std::string& thread_name = "", command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get webhook message + * + * @see https://discord.com/developers/docs/resources/webhook#get-webhook-message + * @param wh Webhook to get the original message for + * @param message_id The message ID + * @param thread_id ID of the thread the message is in + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::message object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void get_webhook_message(const class webhook &wh, snowflake message_id, snowflake thread_id = 0, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Edit webhook message + * + * When the content field is edited, the mentions array in the message object will be reconstructed from scratch based on + * the new content. The allowed_mentions field of the edit request controls how this happens. If there is no explicit + * allowed_mentions in the edit request, the content will be parsed with default allowances, that is, without regard to + * whether or not an allowed_mentions was present in the request that originally created the message. + * + * @see https://discord.com/developers/docs/resources/webhook#edit-webhook-message + * @note the attachments array must contain all attachments that should be present after edit, including retained and new attachments provided in the request body. + * @param wh Webhook to edit message for + * @param m New message + * @param thread_id ID of the thread the message is in + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::message object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void edit_webhook_message(const class webhook &wh, const struct message &m, snowflake thread_id = 0, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Delete webhook message + * + * @see https://discord.com/developers/docs/resources/webhook#delete-webhook-message + * @param wh Webhook to delete message for + * @param message_id Message ID to delete + * @param thread_id ID of the thread the message is in + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void delete_webhook_message(const class webhook &wh, snowflake message_id, snowflake thread_id = 0, command_completion_event_t callback = utility::log_error()); + + + /** + * @brief Get a role for a guild + * + * @see https://discord.com/developers/docs/resources/guild#get-guild-roles + * @param guild_id Guild ID to get role for + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::role_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void roles_get(snowflake guild_id, command_completion_event_t callback); + + /** + * @brief Create a role on a guild + * + * Create a new role for the guild. Requires the `MANAGE_ROLES` permission. Returns the new role object on success. + * Fires a `Guild Role Create` Gateway event. + * + * @see https://discord.com/developers/docs/resources/guild#create-guild-role + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param r Role to create (guild ID is encapsulated in the role object) + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::role object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void role_create(const class role &r, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Edit a role on a guild + * + * Requires the `MANAGE_ROLES` permission. Returns the updated role on success. Fires a `Guild Role Update` Gateway event. + * + * @see https://discord.com/developers/docs/resources/guild#modify-guild-role + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param r Role to edit + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::role object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void role_edit(const class role &r, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Edit multiple role's position in a guild. Returns a list of all roles of the guild on success. + * + * Modify the positions of a set of role objects for the guild. Requires the `MANAGE_ROLES` permission. + * Fires multiple `Guild Role Update` Gateway events. + * + * @see https://discord.com/developers/docs/resources/guild#modify-guild-role-positions + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to change the roles position on + * @param roles Vector of roles to change the positions of + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::role_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void roles_edit_position(snowflake guild_id, const std::vector &roles, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Delete a role + * + * Requires the `MANAGE_ROLES` permission. Fires a `Guild Role Delete` Gateway event. + * + * @see https://discord.com/developers/docs/resources/guild#delete-guild-role + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to delete the role on + * @param role_id Role ID to delete + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void role_delete(snowflake guild_id, snowflake role_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get the application's role connection metadata records + * + * @see https://discord.com/developers/docs/resources/application-role-connection-metadata#get-application-role-connection-metadata-records + * @param application_id The application ID + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::application_role_connection_metadata_list object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void application_role_connection_get(snowflake application_id, command_completion_event_t callback); + + /** + * @brief Update the application's role connection metadata records + * + * @see https://discord.com/developers/docs/resources/application-role-connection-metadata#update-application-role-connection-metadata-records + * @param application_id The application ID + * @param connection_metadata The application role connection metadata to update + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::application_role_connection_metadata_list object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + * @note An application can have a maximum of 5 metadata records. + */ + void application_role_connection_update(snowflake application_id, const std::vector &connection_metadata, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get user application role connection + * + * @see https://discord.com/developers/docs/resources/user#get-user-application-role-connection + * @param application_id The application ID + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::application_role_connection object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void user_application_role_connection_get(snowflake application_id, command_completion_event_t callback); + + /** + * @brief Update user application role connection + * + * @see https://discord.com/developers/docs/resources/user#update-user-application-role-connection + * @param application_id The application ID + * @param connection The application role connection to update + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::application_role_connection object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void user_application_role_connection_update(snowflake application_id, const application_role_connection &connection, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get a user by id, without using the cache + * + * @see https://discord.com/developers/docs/resources/user#get-user + * @param user_id User ID to retrieve + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::user_identified object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + * @note The user_identified object is a subclass of dpp::user which contains further details if you have the oauth2 identify or email scopes. + * If you do not have these scopes, these fields are empty. You can safely convert a user_identified to user with `dynamic_cast`. + * @note unless you want something special from `dpp::user_identified` or you've turned off caching, you have no need to call this. + * Call `dpp::find_user` instead that looks up the user in the cache rather than a REST call. + */ + void user_get(snowflake user_id, command_completion_event_t callback); + + /** + * @brief Get a user by id, checking in the cache first + * + * @see https://discord.com/developers/docs/resources/user#get-user + * @param user_id User ID to retrieve + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::user_identified object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + * @note The user_identified object is a subclass of dpp::user which contains further details if you have the oauth2 identify or email scopes. + * If you do not have these scopes, these fields are empty. You can safely convert a user_identified to user with `dynamic_cast`. + * @note If the user is found in the cache, special values set in `dpp::user_identified` will be undefined. This call should be used + * where you want to for example resolve a user who may no longer be in the bot's guilds, for something like a ban log message. + */ + void user_get_cached(snowflake user_id, command_completion_event_t callback); + + /** + * @brief Get current (bot) user + * + * @see https://discord.com/developers/docs/resources/user#get-current-user + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::user_identified object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + * @note The user_identified object is a subclass of dpp::user which contains further details if you have the oauth2 identify or email scopes. + * If you do not have these scopes, these fields are empty. You can safely convert a user_identified to user with `dynamic_cast`. + */ + void current_user_get(command_completion_event_t callback); + + /** + * @brief Get current (bot) application + * + * @see https://discord.com/developers/docs/topics/oauth2#get-current-bot-application-information + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::application object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void current_application_get(command_completion_event_t callback); + + /** + * @brief Modify current member + * + * Modifies the current member in a guild. + * Fires a `Guild Member Update` Gateway event. + * + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see https://discord.com/developers/docs/resources/guild#modify-current-member + * @param guild_id Guild ID to change on + * @param nickname New nickname, or empty string to clear nickname + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_current_member_edit(snowflake guild_id, const std::string &nickname, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get current user's connections (linked accounts, e.g. steam, xbox). + * This call requires the oauth2 `connections` scope and cannot be executed + * against a bot token. + * @see https://discord.com/developers/docs/resources/user#get-user-connections + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::connection_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void current_user_connections_get(command_completion_event_t callback); + + /** + * @brief Get current (bot) user guilds + * @see https://discord.com/developers/docs/resources/user#get-current-user-guilds + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::guild_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void current_user_get_guilds(command_completion_event_t callback); + + /** + * @brief Edit current (bot) user + * + * Modifies the current member in a guild. Returns the updated guild_member object on success. + * Fires a `Guild Member Update` Gateway event. + * @see https://discord.com/developers/docs/resources/user#modify-current-user + * @param nickname Nickname to set + * @param image_blob Avatar data to upload (NOTE: Very heavily rate limited!) + * @param type Type of image for avatar. It can be one of `i_gif`, `i_jpg` or `i_png`. + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::user object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + * @throw dpp::length_exception Image data is larger than the maximum size of 256 kilobytes + */ + void current_user_edit(const std::string &nickname, const std::string& image_blob = "", const image_type type = i_png, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get current user DM channels + * + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::channel_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void current_user_get_dms(command_completion_event_t callback); + + /** + * @brief Create a dm channel + * @see https://discord.com/developers/docs/resources/user#create-dm + * @param user_id User ID to create DM channel with + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::channel object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void create_dm_channel(snowflake user_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Leave a guild + * @see https://discord.com/developers/docs/resources/user#leave-guild + * @param guild_id Guild ID to leave + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void current_user_leave_guild(snowflake guild_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Create a thread in a forum or media channel + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * + * @see https://discord.com/developers/docs/resources/channel#start-thread-in-forum-channel + * @param thread_name Name of the forum thread + * @param channel_id Forum channel in which thread to create + * @param msg The message to start the thread with + * @param auto_archive_duration Duration to automatically archive the thread after recent activity + * @param rate_limit_per_user amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission manage_messages, manage_thread, or manage_channel, are unaffected + * @param applied_tags List of IDs of forum tags (dpp::forum_tag) to apply to this thread + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::thread object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void thread_create_in_forum(const std::string& thread_name, snowflake channel_id, const message& msg, auto_archive_duration_t auto_archive_duration, uint16_t rate_limit_per_user, std::vector applied_tags = {}, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Create a thread + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * + * @see https://discord.com/developers/docs/resources/channel#start-thread-without-message + * @param thread_name Name of the thread + * @param channel_id Channel in which thread to create + * @param auto_archive_duration Duration after which thread auto-archives. Can be set to - 60, 1440 (for boosted guilds can also be: 4320, 10080) + * @param thread_type Type of thread - CHANNEL_PUBLIC_THREAD, CHANNEL_ANNOUNCEMENT_THREAD, CHANNEL_PRIVATE_THREAD + * @param invitable whether non-moderators can add other non-moderators to a thread; only available when creating a private thread + * @param rate_limit_per_user amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission manage_messages, manage_thread, or manage_channel, are unaffected + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::thread object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void thread_create(const std::string& thread_name, snowflake channel_id, uint16_t auto_archive_duration, channel_type thread_type, bool invitable, uint16_t rate_limit_per_user, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Edit a thread + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * + * @see https://discord.com/developers/docs/topics/threads#editing-deleting-threads + * @param t Thread to edit + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::thread object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void thread_edit(const thread &t, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Create a thread with a message (Discord: ID of a thread is same as message ID) + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see https://discord.com/developers/docs/resources/channel#start-thread-from-message + * @param thread_name Name of the thread + * @param channel_id Channel in which thread to create + * @param message_id message to start thread with + * @param auto_archive_duration Duration after which thread auto-archives. Can be set to - 60, 1440 (for boosted guilds can also be: 4320, 10080) + * @param rate_limit_per_user amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission manage_messages, manage_thread, or manage_channel, are unaffected + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::thread object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void thread_create_with_message(const std::string& thread_name, snowflake channel_id, snowflake message_id, uint16_t auto_archive_duration, uint16_t rate_limit_per_user, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Join a thread + * @see https://discord.com/developers/docs/resources/channel#join-thread + * @param thread_id Thread ID to join + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void current_user_join_thread(snowflake thread_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Leave a thread + * @see https://discord.com/developers/docs/resources/channel#leave-thread + * @param thread_id Thread ID to leave + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void current_user_leave_thread(snowflake thread_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Add a member to a thread + * @see https://discord.com/developers/docs/resources/channel#add-thread-member + * @param thread_id Thread ID to add to + * @param user_id Member ID to add + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void thread_member_add(snowflake thread_id, snowflake user_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Remove a member from a thread + * @see https://discord.com/developers/docs/resources/channel#remove-thread-member + * @param thread_id Thread ID to remove from + * @param user_id Member ID to remove + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void thread_member_remove(snowflake thread_id, snowflake user_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get a thread member + * @see https://discord.com/developers/docs/resources/channel#get-thread-member + * @param thread_id Thread to get member for + * @param user_id ID of the user to get + * @param callback Function to call when the API call completes + * On success the callback will contain a dpp::thread_member object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void thread_member_get(const snowflake thread_id, const snowflake user_id, command_completion_event_t callback); + + /** + * @brief Get members of a thread + * @see https://discord.com/developers/docs/resources/channel#list-thread-members + * @param thread_id Thread to get members for + * @param callback Function to call when the API call completes + * On success the callback will contain a dpp::thread_member_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void thread_members_get(snowflake thread_id, command_completion_event_t callback); + + /** + * @brief Get all active threads in the guild, including public and private threads. Threads are ordered by their id, in descending order. + * @see https://discord.com/developers/docs/resources/guild#list-active-guild-threads + * @param guild_id Guild to get active threads for + * @param callback Function to call when the API call completes + * On success the callback will contain a dpp::active_threads object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void threads_get_active(snowflake guild_id, command_completion_event_t callback); + + /** + * @brief Get public archived threads in a channel (Sorted by archive_timestamp in descending order) + * @see https://discord.com/developers/docs/resources/channel#list-public-archived-threads + * @param channel_id Channel to get public archived threads for + * @param before_timestamp Get threads archived before this timestamp + * @param limit Number of threads to get + * @param callback Function to call when the API call completes + * On success the callback will contain a dpp::thread_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void threads_get_public_archived(snowflake channel_id, time_t before_timestamp, uint16_t limit, command_completion_event_t callback); + + /** + * @brief Get private archived threads in a channel (Sorted by archive_timestamp in descending order) + * @see https://discord.com/developers/docs/resources/channel#list-private-archived-threads + * @param channel_id Channel to get public archived threads for + * @param before_timestamp Get threads archived before this timestamp + * @param limit Number of threads to get + * @param callback Function to call when the API call completes + * On success the callback will contain a dpp::thread_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void threads_get_private_archived(snowflake channel_id, time_t before_timestamp, uint16_t limit, command_completion_event_t callback); + + /** + * @brief Get private archived threads in a channel which current user has joined (Sorted by ID in descending order) + * @see https://discord.com/developers/docs/resources/channel#list-joined-private-archived-threads + * @param channel_id Channel to get public archived threads for + * @param before_id Get threads before this id + * @param limit Number of threads to get + * @param callback Function to call when the API call completes + * On success the callback will contain a dpp::thread_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void threads_get_joined_private_archived(snowflake channel_id, snowflake before_id, uint16_t limit, command_completion_event_t callback); + + /** + * @brief Create a sticker in a guild + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see https://discord.com/developers/docs/resources/sticker#create-guild-sticker + * @param s Sticker to create. Must have its guild ID set. + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::sticker object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_sticker_create(const sticker &s, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Modify a sticker in a guild + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see https://discord.com/developers/docs/resources/sticker#modify-guild-sticker + * @param s Sticker to modify. Must have its guild ID and sticker ID set. + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::sticker object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_sticker_modify(const sticker &s, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Delete a sticker from a guild + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see https://discord.com/developers/docs/resources/sticker#delete-guild-sticker + * @param sticker_id sticker ID to delete + * @param guild_id guild ID to delete from + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::sticker object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_sticker_delete(snowflake sticker_id, snowflake guild_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get a nitro sticker + * @see https://discord.com/developers/docs/resources/sticker#get-sticker + * @param id Id of sticker to get. + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::sticker object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void nitro_sticker_get(snowflake id, command_completion_event_t callback); + + /** + * @brief Get a guild sticker + * @see https://discord.com/developers/docs/resources/sticker#get-guild-sticker + * @param id Id of sticker to get. + * @param guild_id Guild ID of the guild where the sticker is + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::sticker object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_sticker_get(snowflake id, snowflake guild_id, command_completion_event_t callback); + + /** + * @brief Get all guild stickers + * @see https://discord.com/developers/docs/resources/sticker#get-guild-stickers + * @param guild_id Guild ID of the guild where the sticker is + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::sticker_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_stickers_get(snowflake guild_id, command_completion_event_t callback); + + /** + * @brief Get a list of available sticker packs + * @see https://discord.com/developers/docs/resources/sticker#list-nitro-sticker-packs + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::sticker_pack_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void sticker_packs_get(command_completion_event_t callback); + + /** + * @brief Create a stage instance on a stage channel. + * @see https://discord.com/developers/docs/resources/stage-instance#create-stage-instance + * @param instance Stage instance to create + * @param callback User function to execute when the api call completes + * On success the callback will contain a dpp::stage_instance object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + */ + void stage_instance_create(const stage_instance& instance, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get the stage instance associated with the channel id, if it exists. + * @see https://discord.com/developers/docs/resources/stage-instance#get-stage-instance + * @param channel_id ID of the associated channel + * @param callback User function to execute when the api call completes + * On success the callback will contain a dpp::stage_instance object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void stage_instance_get(const snowflake channel_id, command_completion_event_t callback); + + /** + * @brief Edit a stage instance. + * @see https://discord.com/developers/docs/resources/stage-instance#modify-stage-instance + * @param instance Stage instance to edit + * @param callback User function to execute when the api call completes + * On success the callback will contain a dpp::stage_instance object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + */ + void stage_instance_edit(const stage_instance& instance, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Delete a stage instance. + * @see https://discord.com/developers/docs/resources/stage-instance#delete-stage-instance + * @param channel_id ID of the associated channel + * @param callback User function to execute when the api call completes + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + */ + void stage_instance_delete(const snowflake channel_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get all voice regions + * @see https://discord.com/developers/docs/resources/voice#list-voice-regions + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::voiceregion_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void get_voice_regions(command_completion_event_t callback); + + /** + * @brief Get the gateway information for the bot using the token + * @see https://discord.com/developers/docs/topics/gateway#get-gateway-bot + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::gateway object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void get_gateway_bot(command_completion_event_t callback); + + /** + * @brief Get all scheduled events for a guild + * @see https://discord.com/developers/docs/resources/guild-scheduled-event#list-scheduled-events-for-guild + * @param guild_id Guild to get events for + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::scheduled_event_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_events_get(snowflake guild_id, command_completion_event_t callback); + + /** + * @brief Get users RSVP'd to an event + * + * @see https://discord.com/developers/docs/resources/guild-scheduled-event#get-guild-scheduled-event-users + * @param guild_id Guild to get user list for + * @param event_id Guild to get user list for + * @param limit Maximum number of results to return + * @param before Return user IDs that fall before this ID, if provided + * @param after Return user IDs that fall after this ID, if provided + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::event_member_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_event_users_get(snowflake guild_id, snowflake event_id, command_completion_event_t callback, uint8_t limit = 100, snowflake before = 0, snowflake after = 0); + + /** + * @brief Create a scheduled event on a guild + * + * @see https://discord.com/developers/docs/resources/guild-scheduled-event#create-guild-scheduled-event + * @param event Event to create (guild ID must be populated) + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::scheduled_event_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_event_create(const scheduled_event& event, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Delete a scheduled event from a guild + * + * @see https://discord.com/developers/docs/resources/guild-scheduled-event#delete-guild-scheduled-event + * @param event_id Event ID to delete + * @param guild_id Guild ID of event to delete + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_event_delete(snowflake event_id, snowflake guild_id, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Edit/modify a scheduled event on a guild + * + * @see https://discord.com/developers/docs/resources/guild-scheduled-event#modify-guild-scheduled-event + * @param event Event to create (event ID and guild ID must be populated) + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::scheduled_event_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_event_edit(const scheduled_event& event, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get a scheduled event for a guild + * + * @see https://discord.com/developers/docs/resources/guild-scheduled-event#get-guild-scheduled-event + * @param guild_id Guild to get event for + * @param event_id Event ID to get + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::scheduled_event object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void guild_event_get(snowflake guild_id, snowflake event_id, command_completion_event_t callback); + + /** + * @brief Set the bot's voice state on a stage channel + * + * **Caveats** + * + * There are currently several caveats for this endpoint: + * + * - `channel_id` must currently point to a stage channel. + * - current user must already have joined `channel_id`. + * - You must have the `MUTE_MEMBERS` permission to unsuppress yourself. You can always suppress yourself. + * - You must have the `REQUEST_TO_SPEAK` permission to request to speak. You can always clear your own request to speak. + * - You are able to set `request_to_speak_timestamp` to any present or future time. + * + * @see https://discord.com/developers/docs/resources/guild#modify-current-user-voice-state + * @param guild_id Guild to set voice state on + * @param channel_id Stage channel to set voice state on + * @param callback Function to call when the API call completes. + * @param suppress True if the user's audio should be suppressed, false if it should not + * @param request_to_speak_timestamp The time at which we requested to speak, or 0 to clear the request. The time set here must be the current time or in the future. + * On success the callback will contain a dpp::scheduled_event object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + * @throw std::logic_exception You attempted to set a request_to_speak_timestamp in the past which is not the value of 0. + */ + void current_user_set_voice_state(snowflake guild_id, snowflake channel_id, bool suppress = false, time_t request_to_speak_timestamp = 0, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Set a user's voice state on a stage channel + * + * **Caveats** + * + * There are currently several caveats for this endpoint: + * + * - `channel_id` must currently point to a stage channel. + * - User must already have joined `channel_id`. + * - You must have the `MUTE_MEMBERS` permission. (Since suppression is the only thing that is available currently) + * - When unsuppressed, non-bot users will have their `request_to_speak_timestamp` set to the current time. Bot users will not. + * - When suppressed, the user will have their `request_to_speak_timestamp` removed. + * + * @see https://discord.com/developers/docs/resources/guild#modify-user-voice-state + * @param user_id The user to set the voice state of + * @param guild_id Guild to set voice state on + * @param channel_id Stage channel to set voice state on + * @param callback Function to call when the API call completes. + * @param suppress True if the user's audio should be suppressed, false if it should not + * On success the callback will contain a dpp::scheduled_event object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void user_set_voice_state(snowflake user_id, snowflake guild_id, snowflake channel_id, bool suppress = false, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Get all auto moderation rules for a guild + * + * @param guild_id Guild id of the auto moderation rule + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::automod_rule_map object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void automod_rules_get(snowflake guild_id, command_completion_event_t callback); + + /** + * @brief Get a single auto moderation rule + * + * @param guild_id Guild id of the auto moderation rule + * @param rule_id Rule id to retrieve + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::automod_rule object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void automod_rule_get(snowflake guild_id, snowflake rule_id, command_completion_event_t callback); + + /** + * @brief Create an auto moderation rule + * + * @param guild_id Guild id of the auto moderation rule + * @param r Auto moderation rule to create + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::automod_rule object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void automod_rule_create(snowflake guild_id, const automod_rule& r, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Edit an auto moderation rule + * + * @param guild_id Guild id of the auto moderation rule + * @param r Auto moderation rule to edit. The rule's id must be set. + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::automod_rule object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void automod_rule_edit(snowflake guild_id, const automod_rule& r, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Delete an auto moderation rule + * + * @param guild_id Guild id of the auto moderation rule + * @param rule_id Auto moderation rule id to delete + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void automod_rule_delete(snowflake guild_id, snowflake rule_id, command_completion_event_t callback = utility::log_error()); + +#include +#ifdef DPP_CORO +#include +#endif + +}; + +} // namespace dpp diff --git a/include/dpp/cluster_coro_calls.h b/include/dpp/cluster_coro_calls.h new file mode 100644 index 0000000..a6d0604 --- /dev/null +++ b/include/dpp/cluster_coro_calls.h @@ -0,0 +1,2394 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2022 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ + + +/* Auto @generated by buildtools/make_coro_struct.php. + * + * DO NOT EDIT BY HAND! + * + * To re-generate this header file re-run the script! + */ +/** + * @brief Create/overwrite global slash commands. + * Any existing global slash commands will be deleted and replaced with these. + * + * @see dpp::cluster::global_bulk_command_create + * @see https://discord.com/developers/docs/interactions/application-commands#bulk-overwrite-global-application-commands + * @param commands Vector of slash commands to create/update. + * overwriting existing commands that are registered globally for this application. Updates will be available in all guilds after 1 hour. + * Commands that do not already exist will count toward daily application command create limits. + * @return slashcommand_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_global_bulk_command_create(const std::vector &commands); + +/** + * @brief Create a global slash command (a bot can have a maximum of 100 of these). + * + * @see dpp::cluster::global_command_create + * @see https://discord.com/developers/docs/interactions/application-commands#create-global-application-command + * @param s Slash command to create + * @return slashcommand returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_global_command_create(const slashcommand &s); + +/** + * @brief Get a global slash command + * + * @see dpp::cluster::global_command_get + * @see https://discord.com/developers/docs/interactions/application-commands#get-global-application-command + * @param id The ID of the slash command + * @return slashcommand returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_global_command_get(snowflake id); + +/** + * @brief Delete a global slash command (a bot can have a maximum of 100 of these) + * + * @see dpp::cluster::global_command_delete + * @see https://discord.com/developers/docs/interactions/application-commands#delete-global-application-command + * @param id Slash command to delete + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_global_command_delete(snowflake id); + +/** + * @brief Edit a global slash command (a bot can have a maximum of 100 of these) + * + * @see dpp::cluster::global_command_edit + * @see https://discord.com/developers/docs/interactions/application-commands#edit-global-application-command + * @param s Slash command to change + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_global_command_edit(const slashcommand &s); + +/** + * @brief Get the application's global slash commands + * + * @see dpp::cluster::global_commands_get + * @see https://discord.com/developers/docs/interactions/application-commands#get-global-application-commands + * @return slashcommand_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_global_commands_get(); + +/** + * @brief Create/overwrite guild slash commands. + * Any existing guild slash commands on this guild will be deleted and replaced with these. + * + * @see dpp::cluster::guild_bulk_command_create + * @see https://discord.com/developers/docs/interactions/application-commands#bulk-overwrite-guild-application-commands + * @param commands Vector of slash commands to create/update. + * New guild commands will be available in the guild immediately. If the command did not already exist, it will count toward daily application command create limits. + * @param guild_id Guild ID to create/update the slash commands in + * @return slashcommand_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_bulk_command_create(const std::vector &commands, snowflake guild_id); + +/** + * @brief Get all slash command permissions of a guild + * + * @see dpp::cluster::guild_commands_get_permissions + * @see https://discord.com/developers/docs/interactions/application-commands#get-application-command-permissions + * @param guild_id Guild ID to get the slash commands permissions for + * @return guild_command_permissions_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_commands_get_permissions(snowflake guild_id); + +/** + * @brief Edit/Overwrite the permissions of all existing slash commands in a guild + * + * @note You can only add up to 10 permission overwrites for a command + * + * @see dpp::cluster::guild_bulk_command_edit_permissions + * @see https://discord.com/developers/docs/interactions/application-commands#batch-edit-application-command-permissions + * @warning The endpoint will overwrite all existing permissions for all commands of the application in a guild, including slash commands, user commands, and message commands. Meaning that if you forgot to pass a slash command, the permissions of it might be removed. + * @param commands A vector of slash commands to edit/overwrite the permissions for + * @param guild_id Guild ID to edit permissions of the slash commands in + * @return guild_command_permissions_map returned object on completion + * @deprecated This has been disabled with updates to Permissions v2. You can use guild_command_edit_permissions instead + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_bulk_command_edit_permissions(const std::vector &commands, snowflake guild_id); + +/** + * @brief Create a slash command local to a guild + * + * @see dpp::cluster::guild_command_create + * @see https://discord.com/developers/docs/interactions/application-commands#create-guild-application-command + * @note Creating a command with the same name as an existing command for your application will overwrite the old command. + * @param s Slash command to create + * @param guild_id Guild ID to create the slash command in + * @return slashcommand returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_command_create(const slashcommand &s, snowflake guild_id); + +/** + * @brief Delete a slash command local to a guild + * + * @see dpp::cluster::guild_command_delete + * @see https://discord.com/developers/docs/interactions/application-commands#delete-guild-application-command + * @param id Slash command to delete + * @param guild_id Guild ID to delete the slash command in + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_command_delete(snowflake id, snowflake guild_id); + +/** + * @brief Edit slash command permissions of a guild + * + * @see dpp::cluster::guild_command_edit_permissions + * @see https://discord.com/developers/docs/interactions/application-commands#edit-application-command-permissions + * @note You can only add up to 10 permission overwrites for a command + * @param s Slash command to edit the permissions for + * @param guild_id Guild ID to edit the slash command in + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_command_edit_permissions(const slashcommand &s, snowflake guild_id); + +/** + * @brief Get a slash command of a guild + * + * @see dpp::cluster::guild_command_get + * @see https://discord.com/developers/docs/interactions/application-commands#get-guild-application-command + * @note The returned slash commands will not have permissions set, you need to use a permissions getter e.g. dpp::guild_commands_get_permissions to get the guild command permissions + * @param id The ID of the slash command + * @param guild_id Guild ID to get the slash command from + * @return slashcommand returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_command_get(snowflake id, snowflake guild_id); + +/** + * @brief Get the permissions for a slash command of a guild + * + * @see dpp::cluster::guild_command_get_permissions + * @see https://discord.com/developers/docs/interactions/application-commands#get-application-command-permissions + * @param id The ID of the slash command to get the permissions for + * @param guild_id Guild ID to get the permissions of + * @return guild_command_permissions returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_command_get_permissions(snowflake id, snowflake guild_id); + +/** + * @brief Edit a slash command local to a guild + * + * @see dpp::cluster::guild_command_edit + * @see https://discord.com/developers/docs/interactions/application-commands#edit-guild-application-command + * @param s Slash command to edit + * @param guild_id Guild ID to edit the slash command in + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_command_edit(const slashcommand &s, snowflake guild_id); + +/** + * @brief Get the application's slash commands for a guild + * + * @see dpp::cluster::guild_commands_get + * @see https://discord.com/developers/docs/interactions/application-commands#get-guild-application-commands + * @note The returned slash commands will not have permissions set, you need to use a permissions getter e.g. dpp::guild_commands_get_permissions to get the guild command permissions + * @param guild_id Guild ID to get the slash commands for + * @return slashcommand_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_commands_get(snowflake guild_id); + +/** + * @brief Respond to a slash command + * + * @see dpp::cluster::interaction_response_create + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#create-interaction-response + * @param interaction_id Interaction id to respond to + * @param token Token for the interaction webhook + * @param r Response to send + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_interaction_response_create(snowflake interaction_id, const std::string &token, const interaction_response &r); + +/** + * @brief Edit response to a slash command + * + * @see dpp::cluster::interaction_response_edit + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#edit-original-interaction-response + * @param token Token for the interaction webhook + * @param m Message to send + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_interaction_response_edit(const std::string &token, const message &m); + +/** + * @brief Get the original response to a slash command + * + * @see dpp::cluster::interaction_response_get_original + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#get-original-interaction-response + * @param token Token for the interaction webhook + * @return message returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_interaction_response_get_original(const std::string &token); + +/** + * @brief Create a followup message to a slash command + * + * @see dpp::cluster::interaction_followup_create + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#create-interaction-response + * @param token Token for the interaction webhook + * @param m followup message to create + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_interaction_followup_create(const std::string &token, const message &m); + +/** + * @brief Edit original followup message to a slash command + * This is an alias for cluster::interaction_response_edit + * @see dpp::cluster::interaction_followup_edit_original + * @see cluster::interaction_response_edit + * + * @param token Token for the interaction webhook + * @param m message to edit, the ID should be set + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_interaction_followup_edit_original(const std::string &token, const message &m); + +/** + * @brief Delete the initial interaction response + * + * @see dpp::cluster::interaction_followup_delete + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#delete-original-interaction-response + * @param token Token for the interaction webhook + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_interaction_followup_delete(const std::string &token); + +/** + * @brief Edit followup message to a slash command + * The message ID in the message you pass should be correctly set to that of a followup message you previously sent + * + * @see dpp::cluster::interaction_followup_edit + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#edit-followup-message + * @param token Token for the interaction webhook + * @param m message to edit, the ID should be set + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_interaction_followup_edit(const std::string &token, const message &m); + +/** + * @brief Get the followup message to a slash command + * + * @see dpp::cluster::interaction_followup_get + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#get-followup-message + * @param token Token for the interaction webhook + * @param message_id message to retrieve + * @return message returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_interaction_followup_get(const std::string &token, snowflake message_id); + +/** + * @brief Get the original followup message to a slash command + * This is an alias for cluster::interaction_response_get_original + * @see dpp::cluster::interaction_followup_get_original + * @see cluster::interaction_response_get_original + * + * @param token Token for the interaction webhook + * @return message returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_interaction_followup_get_original(const std::string &token); + +/** + * @brief Get all auto moderation rules for a guild + * + * @param guild_id Guild id of the auto moderation rule + * @return automod_rule_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_automod_rules_get(snowflake guild_id); + +/** + * @brief Get a single auto moderation rule + * + * @param guild_id Guild id of the auto moderation rule + * @param rule_id Rule id to retrieve + * @return automod_rule returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_automod_rule_get(snowflake guild_id, snowflake rule_id); + +/** + * @brief Create an auto moderation rule + * + * @param guild_id Guild id of the auto moderation rule + * @param r Auto moderation rule to create + * @return automod_rule returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_automod_rule_create(snowflake guild_id, const automod_rule& r); + +/** + * @brief Edit an auto moderation rule + * + * @param guild_id Guild id of the auto moderation rule + * @param r Auto moderation rule to edit. The rule's id must be set. + * @return automod_rule returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_automod_rule_edit(snowflake guild_id, const automod_rule& r); + +/** + * @brief Delete an auto moderation rule + * + * @param guild_id Guild id of the auto moderation rule + * @param rule_id Auto moderation rule id to delete + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_automod_rule_delete(snowflake guild_id, snowflake rule_id); + +/** + * @brief Create a channel + * + * Create a new channel object for the guild. Requires the `MANAGE_CHANNELS` permission. If setting permission overwrites, + * only permissions your bot has in the guild can be allowed/denied. Setting `MANAGE_ROLES` permission in channels is only possible + * for guild administrators. Returns the new channel object on success. Fires a `Channel Create Gateway` event. + * + * All parameters to this endpoint are optional excluding `name` + * + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::channel_create + * @see https://discord.com/developers/docs/resources/channel#create-channel + * @param c Channel to create + * @return channel returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_channel_create(const class channel &c); + +/** + * @brief Remove a permission from a channel + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::channel_delete_permission + * @see https://discord.com/developers/docs/resources/channel#delete-channel-permission + * @param c Channel to remove permission from + * @param overwrite_id Overwrite to remove, user or channel ID + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_channel_delete_permission(const class channel &c, snowflake overwrite_id); + +/** + * @brief Delete a channel + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::channel_delete + * @see https://discord.com/developers/docs/resources/channel#deleteclose-channel + * @param channel_id Channel id to delete + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_channel_delete(snowflake channel_id); + +/** + * @brief Edit a channel's permissions + * + * @see dpp::cluster::channel_edit_permissions + * @see https://discord.com/developers/docs/resources/channel#edit-channel-permissions + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param c Channel to set permissions for + * @param overwrite_id Overwrite to change (a user or role ID) + * @param allow allow permissions bitmask + * @param deny deny permissions bitmask + * @param member true if the overwrite_id is a user id, false if it is a channel id + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_channel_edit_permissions(const class channel &c, const snowflake overwrite_id, const uint64_t allow, const uint64_t deny, const bool member); + +/** + * @brief Edit a channel's permissions + * + * @see dpp::cluster::channel_edit_permissions + * @see https://discord.com/developers/docs/resources/channel#edit-channel-permissions + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param channel_id ID of the channel to set permissions for + * @param overwrite_id Overwrite to change (a user or role ID) + * @param allow allow permissions bitmask + * @param deny deny permissions bitmask + * @param member true if the overwrite_id is a user id, false if it is a channel id + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_channel_edit_permissions(const snowflake channel_id, const snowflake overwrite_id, const uint64_t allow, const uint64_t deny, const bool member); + +/** + * @brief Edit multiple channels positions + * + * Modify the positions of a set of channel objects for the guild. + * Requires `MANAGE_CHANNELS` permission. Fires multiple `Channel Update Gateway` events. + * Only channels to be modified are required. + * + * @see dpp::cluster::channel_edit_positions + * @see https://discord.com/developers/docs/resources/guild#modify-guild-channel-positions + * @param c Channel to change the position for + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_channel_edit_positions(const std::vector &c); + +/** + * @brief Edit a channel + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::channel_edit + * @see https://discord.com/developers/docs/resources/channel#modify-channel + * @param c Channel to edit/update + * @return channel returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_channel_edit(const class channel &c); + +/** + * @brief Follow an announcement (news) channel + * @see dpp::cluster::channel_follow_news + * @see https://discord.com/developers/docs/resources/channel#follow-news-channel + * @param c Channel id to follow + * @param target_channel_id Channel to subscribe the channel to + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_channel_follow_news(const class channel &c, snowflake target_channel_id); + +/** + * @brief Get a channel + * + * @see dpp::cluster::channel_get + * @see https://discord.com/developers/docs/resources/channel#get-channel + * @param c Channel ID to retrieve + * @return channel returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_channel_get(snowflake c); + +/** + * @brief Create invite for a channel + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::channel_invite_create + * @see https://discord.com/developers/docs/resources/channel#create-channel-invite + * @param c Channel to create an invite on + * @param i Invite to create + * @return invite returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_channel_invite_create(const class channel &c, const class invite &i); + +/** + * @brief Get invites for a channel + * + * @see dpp::cluster::channel_invites_get + * @see https://discord.com/developers/docs/resources/invite#get-invites + * @param c Channel to get invites for + * @return invite_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_channel_invites_get(const class channel &c); + +/** + * @brief Trigger channel typing indicator + * @see dpp::cluster::channel_typing + * @see https://discord.com/developers/docs/resources/channel#trigger-typing-indicator + * @param c Channel to set as typing on + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_channel_typing(const class channel &c); + +/** + * @brief Trigger channel typing indicator + * @see dpp::cluster::channel_typing + * @see https://discord.com/developers/docs/resources/channel#trigger-typing-indicator + * @param cid Channel ID to set as typing on + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_channel_typing(snowflake cid); + +/** + * @brief Get all channels for a guild + * + * @see dpp::cluster::channels_get + * @see https://discord.com/developers/docs/resources/channel#get-channels + * @param guild_id Guild ID to retrieve channels for + * @return channel_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_channels_get(snowflake guild_id); + +/** + * @brief Create a dm channel + * @see dpp::cluster::create_dm_channel + * @see https://discord.com/developers/docs/resources/user#create-dm + * @param user_id User ID to create DM channel with + * @return channel returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_create_dm_channel(snowflake user_id); + +/** + * @brief Get current user DM channels + * + * @return channel_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_current_user_get_dms(); + +/** + * @brief Create a direct message, also create the channel for the direct message if needed + * + * @see dpp::cluster::direct_message_create + * @see https://discord.com/developers/docs/resources/user#create-dm + * @see dpp::cluster::direct_message_create + * @see https://discord.com/developers/docs/resources/channel#create-message + * @param user_id User ID of user to send message to + * @param m Message object + * @return message returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_direct_message_create(snowflake user_id, const message &m); + +/** + * @brief Adds a recipient to a Group DM using their access token + * @see dpp::cluster::gdm_add + * @see https://discord.com/developers/docs/resources/channel#group-dm-add-recipient + * @param channel_id Channel id to add group DM recipients to + * @param user_id User ID to add + * @param access_token Access token from OAuth2 + * @param nick Nickname of user to apply to the chat + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_gdm_add(snowflake channel_id, snowflake user_id, const std::string &access_token, const std::string &nick); + +/** + * @brief Removes a recipient from a Group DM + * @see dpp::cluster::gdm_remove + * @see https://discord.com/developers/docs/resources/channel#group-dm-remove-recipient + * @param channel_id Channel ID of group DM + * @param user_id User ID to remove from group DM + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_gdm_remove(snowflake channel_id, snowflake user_id); + +/** + * @brief Create single emoji. + * You must ensure that the emoji passed contained image data using the emoji::load_image() method. + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * + * @see dpp::cluster::guild_emoji_create + * @see https://discord.com/developers/docs/resources/emoji#create-guild-emoji + * @param guild_id Guild ID to create emoji om + * @param newemoji Emoji to create + * @return emoji returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_emoji_create(snowflake guild_id, const class emoji& newemoji); + +/** + * @brief Delete a guild emoji + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * + * @see dpp::cluster::guild_emoji_delete + * @see https://discord.com/developers/docs/resources/emoji#delete-guild-emoji + * @param guild_id Guild ID to delete emoji on + * @param emoji_id Emoji ID to delete + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_emoji_delete(snowflake guild_id, snowflake emoji_id); + +/** + * @brief Edit a single emoji. + * + * You must ensure that the emoji passed contained image data using the emoji::load_image() method. + * @see dpp::cluster::guild_emoji_edit + * @see https://discord.com/developers/docs/resources/emoji#modify-guild-emoji + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to edit emoji on + * @param newemoji Emoji to edit + * @return emoji returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_emoji_edit(snowflake guild_id, const class emoji& newemoji); + +/** + * @brief Get a single emoji + * + * @see dpp::cluster::guild_emoji_get + * @see https://discord.com/developers/docs/resources/emoji#get-guild-emoji + * @param guild_id Guild ID to get emoji for + * @param emoji_id Emoji ID to get + * @return emoji returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_emoji_get(snowflake guild_id, snowflake emoji_id); + +/** + * @brief Get all emojis for a guild + * + * @see dpp::cluster::guild_emojis_get + * @see https://discord.com/developers/docs/resources/emoji#list-guild-emojis + * @param guild_id Guild ID to get emojis for + * @return emoji_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_emojis_get(snowflake guild_id); + +/** + * @brief Get the gateway information for the bot using the token + * @see dpp::cluster::get_gateway_bot + * @see https://discord.com/developers/docs/topics/gateway#get-gateway-bot + * @return gateway returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_get_gateway_bot(); + +/** + * @brief Modify current member + * + * Modifies the current member in a guild. + * Fires a `Guild Member Update` Gateway event. + * + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::guild_current_member_edit + * @see https://discord.com/developers/docs/resources/guild#modify-current-member + * @param guild_id Guild ID to change on + * @param nickname New nickname, or empty string to clear nickname + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_current_member_edit(snowflake guild_id, const std::string &nickname); + +/** + * @brief Get the audit log for a guild + * + * @see dpp::cluster::guild_auditlog_get + * @see https://discord.com/developers/docs/resources/audit-log#get-guild-audit-log + * @param guild_id Guild to get the audit log of + * @param user_id Entries from a specific user ID. Set this to `0` will fetch any user + * @param action_type Entries for a specific dpp::audit_type. Set this to `0` will fetch any type + * @param before Entries with ID less than a specific audit log entry ID. Used for paginating + * @param after Entries with ID greater than a specific audit log entry ID. Used for paginating + * @param limit Maximum number of entries (between 1-100) to return + * @return auditlog returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_auditlog_get(snowflake guild_id, snowflake user_id, uint32_t action_type, snowflake before, snowflake after, uint32_t limit); + +/** + * @brief Add guild ban + * + * Create a guild ban, and optionally delete previous messages sent by the banned user. + * Requires the `BAN_MEMBERS` permission. Fires a `Guild Ban Add` Gateway event. + * @see dpp::cluster::guild_ban_add + * @see https://discord.com/developers/docs/resources/guild#create-guild-ban + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to add ban to + * @param user_id User ID to ban + * @param delete_message_seconds How many seconds to delete messages for, between 0 and 604800 (7 days). Defaults to 0 + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_ban_add(snowflake guild_id, snowflake user_id, uint32_t delete_message_seconds = 0); + +/** + * @brief Delete guild ban + * + * Remove the ban for a user. Requires the `BAN_MEMBERS` permissions. + * Fires a Guild Ban Remove Gateway event. + * @see dpp::cluster::guild_ban_delete + * @see https://discord.com/developers/docs/resources/guild#remove-guild-ban + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild to delete ban from + * @param user_id User ID to delete ban for + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_ban_delete(snowflake guild_id, snowflake user_id); + +/** + * @brief Create a guild + * + * Create a new guild. Returns a guild object on success. `Fires a Guild Create Gateway` event. + * + * When using the roles parameter, the first member of the array is used to change properties of the guild's everyone role. + * If you are trying to bootstrap a guild with additional roles, keep this in mind. The required id field within each role object is an + * integer placeholder, and will be replaced by the API upon consumption. Its purpose is to allow you to overwrite a role's permissions + * in a channel when also passing in channels with the channels array. + * When using the channels parameter, the position field is ignored, and none of the default channels are created. The id field within + * each channel object may be set to an integer placeholder, and will be replaced by the API upon consumption. Its purpose is to + * allow you to create `GUILD_CATEGORY` channels by setting the `parent_id` field on any children to the category's id field. + * Category channels must be listed before any children. + * + * @see dpp::cluster::guild_create + * @see https://discord.com/developers/docs/resources/guild#create-guild + * @note The region field is deprecated and is replaced by channel.rtc_region. This endpoint can be used only by bots in less than 10 guilds. + * @param g Guild to create + * @return guild returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_create(const class guild &g); + +/** + * @brief Delete a guild + * + * Delete a guild permanently. User must be owner. Fires a `Guild Delete Gateway` event. + * + * @see dpp::cluster::guild_delete + * @see https://discord.com/developers/docs/resources/guild#delete-guild + * @param guild_id Guild ID to delete + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_delete(snowflake guild_id); + +/** + * @brief Delete guild integration + * + * Delete the attached integration object for the guild. Deletes any associated webhooks and kicks the associated bot if there is one. + * Requires the `MANAGE_GUILD` permission. Fires a Guild Integrations Update Gateway event. + * + * @see dpp::cluster::guild_delete_integration + * @see https://discord.com/developers/docs/resources/guild#delete-guild-integration + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to delete integration for + * @param integration_id Integration ID to delete + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_delete_integration(snowflake guild_id, snowflake integration_id); + +/** + * @brief Edit a guild + * + * Modify a guild's settings. Requires the `MANAGE_GUILD` permission. Returns the updated guild object on success. + * Fires a `Guild Update Gateway` event. + * + * @see dpp::cluster::guild_edit + * @see https://discord.com/developers/docs/resources/guild#modify-guild + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param g Guild to edit + * @return guild returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_edit(const class guild &g); + +/** + * @brief Edit guild widget + * + * Requires the `MANAGE_GUILD` permission. + * + * @see dpp::cluster::guild_edit_widget + * @see https://discord.com/developers/docs/resources/guild#modify-guild-widget + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to edit widget for + * @param gw New guild widget information + * @return guild_widget returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_edit_widget(snowflake guild_id, const class guild_widget &gw); + +/** + * @brief Get single guild ban + * + * Requires the `BAN_MEMBERS` permission. + * @see dpp::cluster::guild_get_ban + * @see https://discord.com/developers/docs/resources/guild#get-guild-ban + * @param guild_id Guild ID to get ban for + * @param user_id User ID of ban to retrieve + * @return ban returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_get_ban(snowflake guild_id, snowflake user_id); + +/** + * @brief Get guild ban list + * + * Requires the `BAN_MEMBERS` permission. + * @see dpp::cluster::guild_get_bans + * @see https://discord.com/developers/docs/resources/guild#get-guild-bans + * @note Provide a user ID to `before` and `after` for pagination. Users will always be returned in ascending order by the user ID. If both before and after are provided, only before is respected. + * @param guild_id Guild ID to get bans for + * @param before If non-zero, all bans for user ids before this user id will be returned up to the limit + * @param after if non-zero, all bans for user ids after this user id will be returned up to the limit + * @param limit the maximum number of bans to retrieve in this call up to a maximum of 1000 + * @return ban_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_get_bans(snowflake guild_id, snowflake before, snowflake after, snowflake limit); + + +[[nodiscard]] async co_guild_get(snowflake guild_id); + +/** + * @brief Get guild integrations + * + * Requires the `MANAGE_GUILD` permission. + * + * @see dpp::cluster::guild_get_integrations + * @see https://discord.com/developers/docs/resources/guild#get-guild-integrations + * @param guild_id Guild ID to get integrations for + * @return integration_map returned object on completion + * + * @note This endpoint returns a maximum of 50 integrations. If a guild has more integrations, they cannot be accessed. + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_get_integrations(snowflake guild_id); + + +[[nodiscard]] async co_guild_get_preview(snowflake guild_id); + +/** + * @brief Get guild vanity url, if enabled + * + * Returns a partial dpp::invite object for guilds with that feature enabled. Requires the `MANAGE_GUILD` permission. code will be null if a vanity url for the guild is not set. + * @see dpp::cluster::guild_get_vanity + * @see https://discord.com/developers/docs/resources/guild#get-guild-vanity-url + * @param guild_id Guild to get vanity URL for + * @return invite returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_get_vanity(snowflake guild_id); + +/** + * @brief Get guild widget + * + * Requires the `MANAGE_GUILD` permission. + * + * @see dpp::cluster::guild_get_widget + * @see https://discord.com/developers/docs/resources/guild#get-guild-widget + * @param guild_id Guild ID to get widget for + * @return guild_widget returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_get_widget(snowflake guild_id); + +/** + * @brief Modify guild integration + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * + * @see dpp::cluster::guild_modify_integration + * @see https://discord.com/developers/docs/resources/guild#modify-guild-integration + * @param guild_id Guild ID to modify integration for + * @param i Integration to modify + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_modify_integration(snowflake guild_id, const class integration &i); + +/** + * @brief Get prune counts + * + * Returns a prune object indicating the number of members that would be removed in a prune operation. Requires the `KICK_MEMBERS` + * permission. By default, prune will not remove users with roles. You can optionally include specific roles in your prune by providing the + * include_roles parameter. Any inactive user that has a subset of the provided role(s) will be counted in the prune and users with additional + * roles will not. + * + * @see dpp::cluster::guild_get_prune_counts + * @see https://discord.com/developers/docs/resources/guild#get-guild-prune-count + * @param guild_id Guild ID to count for pruning + * @param pruneinfo Pruning info + * @return prune returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_get_prune_counts(snowflake guild_id, const struct prune& pruneinfo); + +/** + * @brief Begin guild prune + * + * Begin a prune operation. Requires the `KICK_MEMBERS` permission. Returns a prune object indicating the number of members + * that were removed in the prune operation. For large guilds it's recommended to set the `compute_prune_count` option to false, forcing + * 'pruned' to 0. Fires multiple `Guild Member Remove` Gateway events. + * By default, prune will not remove users with roles. You can optionally include specific roles in your prune by providing the `include_roles` + * parameter. Any inactive user that has a subset of the provided role(s) will be included in the prune and users with additional roles will not. + * + * @see dpp::cluster::guild_begin_prune + * @see https://discord.com/developers/docs/resources/guild#begin-guild-prune + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to prune + * @param pruneinfo Pruning info + * @return prune returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_begin_prune(snowflake guild_id, const struct prune& pruneinfo); + +/** + * @brief Change current user nickname + * + * Modifies the nickname of the current user in a guild. + * Fires a `Guild Member Update` Gateway event. + * + * @deprecated Deprecated in favor of Modify Current Member. Will be replaced by dpp::cluster::guild_current_member_edit + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::guild_set_nickname + * @see https://discord.com/developers/docs/resources/guild#modify-current-user-nick + * @param guild_id Guild ID to change nickname on + * @param nickname New nickname, or empty string to clear nickname + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_set_nickname(snowflake guild_id, const std::string &nickname); + +/** + * @brief Sync guild integration + * + * @see dpp::cluster::guild_sync_integration + * @see https://discord.com/developers/docs/resources/guild#sync-guild-integration + * @param guild_id Guild ID to sync integration on + * @param integration_id Integration ID to synchronise + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_sync_integration(snowflake guild_id, snowflake integration_id); + +/** + * @brief Get the guild's onboarding configuration + * + * @see dpp::cluster::guild_get_onboarding + * @see https://discord.com/developers/docs/resources/guild#get-guild-onboarding + * @param o The onboarding object + * @return onboarding returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_get_onboarding(snowflake guild_id); + +/** + * @brief Edit the guild's onboarding configuration + * + * Requires the `MANAGE_GUILD` and `MANAGE_ROLES` permissions. + * + * @note Onboarding enforces constraints when enabled. These constraints are that there must be at least 7 Default Channels and at least 5 of them must allow sending messages to the \@everyone role. The `onboarding::mode` field modifies what is considered when enforcing these constraints. + * + * @see dpp::cluster::guild_edit_onboarding + * @see https://discord.com/developers/docs/resources/guild#modify-guild-onboarding + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param o The onboarding object + * @return onboarding returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_edit_onboarding(const struct onboarding& o); + +/** + * @brief Get the guild's welcome screen + * + * If the welcome screen is not enabled, the `MANAGE_GUILD` permission is required. + * + * @see dpp::cluster::guild_get_welcome_screen + * @see https://discord.com/developers/docs/resources/guild#get-guild-welcome-screen + * @param guild_id The guild ID to get the welcome screen from + * @return dpp::welcome_screen returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_get_welcome_screen(snowflake guild_id); + +/** + * @brief Edit the guild's welcome screen + * + * Requires the `MANAGE_GUILD` permission. May fire a `Guild Update` Gateway event. + * + * @see dpp::cluster::guild_edit_welcome_screen + * @see https://discord.com/developers/docs/resources/guild#modify-guild-welcome-screen + * @param guild_id The guild ID to edit the welcome screen for + * @param welcome_screen The welcome screen + * @param enabled Whether the welcome screen should be enabled or disabled + * @return dpp::welcome_screen returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_edit_welcome_screen(snowflake guild_id, const struct welcome_screen& welcome_screen, bool enabled); + +/** + * @brief Add guild member. Needs a specific oauth2 scope, from which you get the access_token. + * + * Adds a user to the guild, provided you have a valid oauth2 access token for the user with the guilds.join scope. + * Returns the guild_member, which is defaulted if the user is already a member of the guild. Fires a `Guild Member Add` Gateway event. + * + * For guilds with Membership Screening enabled, this endpoint will default to adding new members as pending in the guild member object. + * Members that are pending will have to complete membership screening before they become full members that can talk. + * + * @note All parameters to this endpoint except for access_token are optional. + * The bot must be a member of the guild with `CREATE_INSTANT_INVITE` permission. + * @see dpp::cluster::guild_add_member + * @see https://discord.com/developers/docs/resources/guild#add-guild-member + * @param gm Guild member to add + * @param access_token Access token from Oauth2 scope + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_add_member(const guild_member& gm, const std::string &access_token); + +/** + * @brief Edit the properties of an existing guild member + * + * Modify attributes of a guild member. Returns the guild_member. Fires a `Guild Member Update` Gateway event. + * To remove a timeout, set the `communication_disabled_until` to a non-zero time in the past, e.g. 1. + * When moving members to channels, the API user must have permissions to both connect to the channel and have the `MOVE_MEMBERS` permission. + * For moving and disconnecting users from voice, use dpp::cluster::guild_member_move. + * @see dpp::cluster::guild_edit_member + * @see https://discord.com/developers/docs/resources/guild#modify-guild-member + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param gm Guild member to edit + * @return guild_member returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_edit_member(const guild_member& gm); + +/** + * @brief Get a guild member + * @see dpp::cluster::guild_get_member + * @see https://discord.com/developers/docs/resources/guild#get-guild-member + * @param guild_id Guild ID to get member for + * @param user_id User ID of member to get + * @return guild_member returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_get_member(snowflake guild_id, snowflake user_id); + +/** + * @brief Get all guild members + * + * @note This endpoint is restricted according to whether the `GUILD_MEMBERS` Privileged Intent is enabled for your application. + * @see dpp::cluster::guild_get_members + * @see https://discord.com/developers/docs/resources/guild#get-guild-members + * @param guild_id Guild ID to get all members for + * @param limit max number of members to return (1-1000) + * @param after the highest user id in the previous page + * @return guild_member_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_get_members(snowflake guild_id, uint16_t limit, snowflake after); + +/** + * @brief Add role to guild member + * + * Adds a role to a guild member. Requires the `MANAGE_ROLES` permission. + * Fires a `Guild Member Update` Gateway event. + * @see dpp::cluster::guild_member_add_role + * @see https://discord.com/developers/docs/resources/guild#add-guild-member-role + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to add a role to + * @param user_id User ID to add role to + * @param role_id Role ID to add to the user + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_member_add_role(snowflake guild_id, snowflake user_id, snowflake role_id); + +/** + * @brief Remove (kick) a guild member + * + * Remove a member from a guild. Requires `KICK_MEMBERS` permission. + * Fires a `Guild Member Remove` Gateway event. + * @see dpp::cluster::guild_member_delete + * @see https://discord.com/developers/docs/resources/guild#remove-guild-member + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @deprecated Replaced by dpp::cluster::guild_member_kick + * @param guild_id Guild ID to kick member from + * @param user_id User ID to kick + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_member_delete(snowflake guild_id, snowflake user_id); + +/** + * @brief Remove (kick) a guild member + * + * Remove a member from a guild. Requires `KICK_MEMBERS` permission. + * Fires a `Guild Member Remove` Gateway event. + * @see dpp::cluster::guild_member_kick + * @see https://discord.com/developers/docs/resources/guild#remove-guild-member + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to kick member from + * @param user_id User ID to kick + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_member_kick(snowflake guild_id, snowflake user_id); + +/** + * @brief Set the timeout of a guild member + * + * Fires a `Guild Member Update` Gateway event. + * @see dpp::cluster::guild_member_timeout + * @see https://discord.com/developers/docs/resources/guild#modify-guild-member + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to timeout the member in + * @param user_id User ID to set the timeout for + * @param communication_disabled_until The timestamp when the user's timeout will expire (up to 28 days in the future). Set to 0 to remove the timeout + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_member_timeout(snowflake guild_id, snowflake user_id, time_t communication_disabled_until); + +/** + * @brief Remove role from guild member + * + * Removes a role from a guild member. Requires the `MANAGE_ROLES` permission. + * Fires a `Guild Member Update` Gateway event. + * @see dpp::cluster::guild_member_delete_role + * @see https://discord.com/developers/docs/resources/guild#remove-guild-member-role + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to remove role from user on + * @param user_id User ID to remove role from + * @param role_id Role to remove + * @return confirmation returned object on completion + * @deprecated Use dpp::cluster::guild_member_remove_role instead + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_member_delete_role(snowflake guild_id, snowflake user_id, snowflake role_id); + +/** + * @brief Remove role from guild member + * + * Removes a role from a guild member. Requires the `MANAGE_ROLES` permission. + * Fires a `Guild Member Update` Gateway event. + * @see dpp::cluster::guild_member_remove_role + * @see https://discord.com/developers/docs/resources/guild#remove-guild-member-role + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to remove role from user on + * @param user_id User ID to remove role from + * @param role_id Role to remove + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_member_remove_role(snowflake guild_id, snowflake user_id, snowflake role_id); + +/** + * @brief Moves the guild member to a other voice channel, if member is connected to one. + * Set the `channel_id` to `0` to disconnect the user. + * + * Fires a `Guild Member Update` Gateway event. + * @note When moving members to channels, the API user __must__ have permissions to both connect to the channel and have the `MOVE_MEMBERS` permission. + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::guild_member_move + * @see https://discord.com/developers/docs/resources/guild#modify-guild-member + * @param channel_id Id of the channel to which the user is used. Set to `0` to disconnect the user + * @param guild_id Guild id to which the user is connected + * @param user_id User id, who should be moved + * @return guild_member returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_member_move(const snowflake channel_id, const snowflake guild_id, const snowflake user_id); + +/** + * @brief Search for guild members based on whether their username or nickname starts with the given string. + * + * @note This endpoint is restricted according to whether the `GUILD_MEMBERS` Privileged Intent is enabled for your application. + * @see dpp::cluster::guild_search_members + * @see https://discord.com/developers/docs/resources/guild#search-guild-members + * @param guild_id Guild ID to search in + * @param query Query string to match username(s) and nickname(s) against + * @param limit max number of members to return (1-1000) + * @return guild_member_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_search_members(snowflake guild_id, const std::string& query, uint16_t limit); + +/** + * @brief Get guild invites + * + * Returns a list of invite objects (with invite metadata) for the guild. Requires the `MANAGE_GUILD` permission. + * + * @see dpp::cluster::guild_get_invites + * @see https://discord.com/developers/docs/resources/guild#get-guild-invites + * @param guild_id Guild ID to get invites for + * @return invite_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_get_invites(snowflake guild_id); + + +[[nodiscard]] async co_invite_delete(const std::string &invitecode); + +/** + * @brief Get details about an invite + * + * @see dpp::cluster::invite_get + * @see https://discord.com/developers/docs/resources/invite#get-invite + * @param invite_code Invite code to get information on + * @return invite returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_invite_get(const std::string &invite_code); + +/** + * @brief Add a reaction to a message. The reaction string must be either an `emojiname:id` or a unicode character. + * + * @see dpp::cluster::message_add_reaction + * @see https://discord.com/developers/docs/resources/channel#create-reaction + * @param m Message to add a reaction to + * @param reaction Reaction to add. Emojis should be in the form emojiname:id + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_add_reaction(const struct message &m, const std::string &reaction); + +/** + * @brief Add a reaction to a message by id. The reaction string must be either an `emojiname:id` or a unicode character. + * + * @see dpp::cluster::message_add_reaction + * @see https://discord.com/developers/docs/topics/gateway#message-reaction-add + * @param message_id Message to add reactions to + * @param channel_id Channel to add reactions to + * @param reaction Reaction to add. Emojis should be in the form emojiname:id + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_add_reaction(snowflake message_id, snowflake channel_id, const std::string &reaction); + +/** + * @brief Send a message to a channel. The callback function is called when the message has been sent + * + * @see dpp::cluster::message_create + * @see https://discord.com/developers/docs/resources/channel#create-message + * @param m Message to send + * @return message returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_create(const struct message &m); + +/** + * @brief Crosspost a message. The callback function is called when the message has been sent + * + * @see dpp::cluster::message_crosspost + * @see https://discord.com/developers/docs/resources/channel#crosspost-message + * @param message_id Message to crosspost + * @param channel_id Channel ID to crosspost from + * @return message returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_crosspost(snowflake message_id, snowflake channel_id); + +/** + * @brief Delete all reactions on a message + * + * @see dpp::cluster::message_delete_all_reactions + * @see https://discord.com/developers/docs/resources/channel#delete-all-reactions + * @param m Message to delete reactions from + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_delete_all_reactions(const struct message &m); + +/** + * @brief Delete all reactions on a message by id + * + * @see dpp::cluster::message_delete_all_reactions + * @see https://discord.com/developers/docs/resources/channel#delete-all-reactions + * @param message_id Message to delete reactions from + * @param channel_id Channel to delete reactions from + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_delete_all_reactions(snowflake message_id, snowflake channel_id); + +/** + * @brief Bulk delete messages from a channel. The callback function is called when the message has been edited + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * + * @note If any message provided older than 2 weeks or any duplicate message ID, it will fail. + * + * @see dpp::cluster::message_delete_bulk + * @see https://discord.com/developers/docs/resources/channel#bulk-delete-messages + * @param message_ids List of message IDs to delete (at least 2 and at most 100 message IDs) + * @param channel_id Channel to delete from + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_delete_bulk(const std::vector &message_ids, snowflake channel_id); + +/** + * @brief Delete a message from a channel. The callback function is called when the message has been edited + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * + * @see dpp::cluster::message_delete + * @see https://discord.com/developers/docs/resources/channel#delete-message + * @param message_id Message ID to delete + * @param channel_id Channel to delete from + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_delete(snowflake message_id, snowflake channel_id); + +/** + * @brief Delete own reaction from a message. The reaction string must be either an `emojiname:id` or a unicode character. + * + * @see dpp::cluster::message_delete_own_reaction + * @see https://discord.com/developers/docs/resources/channel#delete-own-reaction + * @param m Message to delete own reaction from + * @param reaction Reaction to delete. The reaction should be in the form emojiname:id + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_delete_own_reaction(const struct message &m, const std::string &reaction); + +/** + * @brief Delete own reaction from a message by id. The reaction string must be either an `emojiname:id` or a unicode character. + * + * @see dpp::cluster::message_delete_own_reaction + * @see https://discord.com/developers/docs/resources/channel#delete-own-reaction + * @param message_id Message to delete reactions from + * @param channel_id Channel to delete reactions from + * @param reaction Reaction to delete. The reaction should be in the form emojiname:id + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_delete_own_reaction(snowflake message_id, snowflake channel_id, const std::string &reaction); + +/** + * @brief Delete a user's reaction from a message. The reaction string must be either an `emojiname:id` or a unicode character + * + * @see dpp::cluster::message_delete_reaction + * @see https://discord.com/developers/docs/resources/channel#delete-user-reaction + * @param m Message to delete a user's reaction from + * @param user_id User ID who's reaction you want to remove + * @param reaction Reaction to remove. Reactions should be in the form emojiname:id + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_delete_reaction(const struct message &m, snowflake user_id, const std::string &reaction); + +/** + * @brief Delete a user's reaction from a message by id. The reaction string must be either an `emojiname:id` or a unicode character + * + * @see dpp::cluster::message_delete_reaction + * @see https://discord.com/developers/docs/resources/channel#delete-user-reaction + * @param message_id Message to delete reactions from + * @param channel_id Channel to delete reactions from + * @param user_id User ID who's reaction you want to remove + * @param reaction Reaction to remove. Reactions should be in the form emojiname:id + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_delete_reaction(snowflake message_id, snowflake channel_id, snowflake user_id, const std::string &reaction); + +/** + * @brief Delete all reactions on a message using a particular emoji. The reaction string must be either an `emojiname:id` or a unicode character + * + * @see dpp::cluster::message_delete_reaction_emoji + * @see https://discord.com/developers/docs/resources/channel#delete-all-reactions-for-emoji + * @param m Message to delete reactions from + * @param reaction Reaction to delete, in the form emojiname:id or a unicode character + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_delete_reaction_emoji(const struct message &m, const std::string &reaction); + +/** + * @brief Delete all reactions on a message using a particular emoji by id. The reaction string must be either an `emojiname:id` or a unicode character + * + * @see dpp::cluster::message_delete_reaction_emoji + * @see https://discord.com/developers/docs/resources/channel#delete-all-reactions-for-emoji + * @param message_id Message to delete reactions from + * @param channel_id Channel to delete reactions from + * @param reaction Reaction to delete, in the form emojiname:id or a unicode character + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_delete_reaction_emoji(snowflake message_id, snowflake channel_id, const std::string &reaction); + +/** + * @brief Edit a message on a channel. The callback function is called when the message has been edited + * + * @see dpp::cluster::message_edit + * @see https://discord.com/developers/docs/resources/channel#edit-message + * @param m Message to edit + * @return message returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_edit(const struct message &m); + +/** + * @brief Get a message + * + * @see dpp::cluster::message_get + * @see https://discord.com/developers/docs/resources/channel#get-channel-message + * @param message_id Message ID + * @param channel_id Channel ID + * @return message returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_get(snowflake message_id, snowflake channel_id); + +/** + * @brief Get reactions on a message for a particular emoji. The reaction string must be either an `emojiname:id` or a unicode character + * + * @see dpp::cluster::message_get_reactions + * @see https://discord.com/developers/docs/resources/channel#get-reactions + * @param m Message to get reactions for + * @param reaction Reaction should be in the form emojiname:id or a unicode character + * @param before Reactions before this ID should be retrieved if this is set to non-zero + * @param after Reactions before this ID should be retrieved if this is set to non-zero + * @param limit This number of reactions maximum should be returned + * @return user_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_get_reactions(const struct message &m, const std::string &reaction, snowflake before, snowflake after, snowflake limit); + +/** + * @brief Get reactions on a message for a particular emoji by id. The reaction string must be either an `emojiname:id` or a unicode character + * + * @see dpp::cluster::message_get_reactions + * @see https://discord.com/developers/docs/resources/channel#get-reactions + * @param message_id Message to get reactions for + * @param channel_id Channel to get reactions for + * @param reaction Reaction should be in the form emojiname:id or a unicode character + * @param before Reactions before this ID should be retrieved if this is set to non-zero + * @param after Reactions before this ID should be retrieved if this is set to non-zero + * @param limit This number of reactions maximum should be returned + * @return emoji_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_get_reactions(snowflake message_id, snowflake channel_id, const std::string &reaction, snowflake before, snowflake after, snowflake limit); + +/** + * @brief Pin a message + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::message_pin + * @see https://discord.com/developers/docs/resources/channel#pin-message + * @param channel_id Channel id to pin message on + * @param message_id Message id to pin message on + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_pin(snowflake channel_id, snowflake message_id); + +/** + * @brief Get multiple messages. + * + * This function will attempt to fetch as many messages as possible using multiple API calls if needed. + * + * @see dpp::cluster::messages_get + * @see https://discord.com/developers/docs/resources/channel#get-channel-messages + * @param channel_id Channel ID to retrieve messages for + * @param around Messages should be retrieved around this ID if this is set to non-zero + * @param before Messages before this ID should be retrieved if this is set to non-zero + * @param after Messages after this ID should be retrieved if this is set to non-zero + * @param limit This number of messages maximum should be returned, up to a maximum of 100. + * @return message_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_messages_get(snowflake channel_id, snowflake around, snowflake before, snowflake after, uint64_t limit); + +/** + * @brief Unpin a message + * @see dpp::cluster::message_unpin + * @see https://discord.com/developers/docs/resources/channel#unpin-message + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param channel_id Channel id to unpin message on + * @param message_id Message id to unpin message on + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_message_unpin(snowflake channel_id, snowflake message_id); + +/** + * @brief Get a channel's pins + * @see dpp::cluster::channel_pins_get + * @see https://discord.com/developers/docs/resources/channel#get-pinned-messages + * @param channel_id Channel ID to get pins for + * @return message_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_channel_pins_get(snowflake channel_id); + +/** + * @brief Create a role on a guild + * + * Create a new role for the guild. Requires the `MANAGE_ROLES` permission. Returns the new role object on success. + * Fires a `Guild Role Create` Gateway event. + * + * @see dpp::cluster::role_create + * @see https://discord.com/developers/docs/resources/guild#create-guild-role + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param r Role to create (guild ID is encapsulated in the role object) + * @return role returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_role_create(const class role &r); + +/** + * @brief Delete a role + * + * Requires the `MANAGE_ROLES` permission. Fires a `Guild Role Delete` Gateway event. + * + * @see dpp::cluster::role_delete + * @see https://discord.com/developers/docs/resources/guild#delete-guild-role + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to delete the role on + * @param role_id Role ID to delete + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_role_delete(snowflake guild_id, snowflake role_id); + +/** + * @brief Edit a role on a guild + * + * Requires the `MANAGE_ROLES` permission. Returns the updated role on success. Fires a `Guild Role Update` Gateway event. + * + * @see dpp::cluster::role_edit + * @see https://discord.com/developers/docs/resources/guild#modify-guild-role + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param r Role to edit + * @return role returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_role_edit(const class role &r); + +/** + * @brief Edit multiple role's position in a guild. Returns a list of all roles of the guild on success. + * + * Modify the positions of a set of role objects for the guild. Requires the `MANAGE_ROLES` permission. + * Fires multiple `Guild Role Update` Gateway events. + * + * @see dpp::cluster::roles_edit_position + * @see https://discord.com/developers/docs/resources/guild#modify-guild-role-positions + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to change the roles position on + * @param roles Vector of roles to change the positions of + * @return role_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_roles_edit_position(snowflake guild_id, const std::vector &roles); + +/** + * @brief Get a role for a guild + * + * @see dpp::cluster::roles_get + * @see https://discord.com/developers/docs/resources/guild#get-guild-roles + * @param guild_id Guild ID to get role for + * @return role_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_roles_get(snowflake guild_id); + +/** + * @brief Get the application's role connection metadata records + * + * @see dpp::cluster::application_role_connection_get + * @see https://discord.com/developers/docs/resources/application-role-connection-metadata#get-application-role-connection-metadata-records + * @param application_id The application ID + * @return application_role_connection returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_application_role_connection_get(snowflake application_id); + +/** + * @brief Update the application's role connection metadata records + * + * @see dpp::cluster::application_role_connection_update + * @see https://discord.com/developers/docs/resources/application-role-connection-metadata#update-application-role-connection-metadata-records + * @param application_id The application ID + * @param connection_metadata The application role connection metadata to update + * @return application_role_connection returned object on completion + * @note An application can have a maximum of 5 metadata records. + * \memberof dpp::cluster + */ +[[nodiscard]] async co_application_role_connection_update(snowflake application_id, const std::vector &connection_metadata); + +/** + * @brief Get user application role connection + * + * @see dpp::cluster::user_application_role_connection_get + * @see https://discord.com/developers/docs/resources/user#get-user-application-role-connection + * @param application_id The application ID + * @return application_role_connection returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_user_application_role_connection_get(snowflake application_id); + +/** + * @brief Update user application role connection + * + * @see dpp::cluster::user_application_role_connection_update + * @see https://discord.com/developers/docs/resources/user#update-user-application-role-connection + * @param application_id The application ID + * @param connection The application role connection to update + * @return application_role_connection returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_user_application_role_connection_update(snowflake application_id, const application_role_connection &connection); + +/** + * @brief Get all scheduled events for a guild + * @see dpp::cluster::guild_events_get + * @see https://discord.com/developers/docs/resources/guild-scheduled-event#list-scheduled-events-for-guild + * @param guild_id Guild to get events for + * @return scheduled_event_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_events_get(snowflake guild_id); + +/** + * @brief Create a scheduled event on a guild + * + * @see dpp::cluster::guild_event_create + * @see https://discord.com/developers/docs/resources/guild-scheduled-event#create-guild-scheduled-event + * @param event Event to create (guild ID must be populated) + * @return scheduled_event returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_event_create(const scheduled_event& event); + +/** + * @brief Delete a scheduled event from a guild + * + * @see dpp::cluster::guild_event_delete + * @see https://discord.com/developers/docs/resources/guild-scheduled-event#delete-guild-scheduled-event + * @param event_id Event ID to delete + * @param guild_id Guild ID of event to delete + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_event_delete(snowflake event_id, snowflake guild_id); + +/** + * @brief Edit/modify a scheduled event on a guild + * + * @see dpp::cluster::guild_event_edit + * @see https://discord.com/developers/docs/resources/guild-scheduled-event#modify-guild-scheduled-event + * @param event Event to create (event ID and guild ID must be populated) + * @return scheduled_event returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_event_edit(const scheduled_event& event); + +/** + * @brief Get a scheduled event for a guild + * + * @see dpp::cluster::guild_event_get + * @see https://discord.com/developers/docs/resources/guild-scheduled-event#get-guild-scheduled-event + * @param guild_id Guild to get event for + * @param event_id Event ID to get + * @return scheduled_event returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_event_get(snowflake guild_id, snowflake event_id); + + +[[nodiscard]] async co_stage_instance_create(const stage_instance& si); + +/** + * @brief Get the stage instance associated with the channel id, if it exists. + * @see dpp::cluster::stage_instance_get + * @see https://discord.com/developers/docs/resources/stage-instance#get-stage-instance + * @param channel_id ID of the associated channel + * @return stage_instance returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_stage_instance_get(const snowflake channel_id); + + +[[nodiscard]] async co_stage_instance_edit(const stage_instance& si); + +/** + * @brief Delete a stage instance. + * @see dpp::cluster::stage_instance_delete + * @see https://discord.com/developers/docs/resources/stage-instance#delete-stage-instance + * @param channel_id ID of the associated channel + * @return confirmation returned object on completion + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * \memberof dpp::cluster + */ +[[nodiscard]] async co_stage_instance_delete(const snowflake channel_id); + +/** + * @brief Create a sticker in a guild + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::guild_sticker_create + * @see https://discord.com/developers/docs/resources/sticker#create-guild-sticker + * @param s Sticker to create. Must have its guild ID set. + * @return sticker returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_sticker_create(const sticker &s); + +/** + * @brief Delete a sticker from a guild + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::guild_sticker_delete + * @see https://discord.com/developers/docs/resources/sticker#delete-guild-sticker + * @param sticker_id sticker ID to delete + * @param guild_id guild ID to delete from + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_sticker_delete(snowflake sticker_id, snowflake guild_id); + +/** + * @brief Get a guild sticker + * @see dpp::cluster::guild_sticker_get + * @see https://discord.com/developers/docs/resources/sticker#get-guild-sticker + * @param id Id of sticker to get. + * @param guild_id Guild ID of the guild where the sticker is + * @return sticker returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_sticker_get(snowflake id, snowflake guild_id); + +/** + * @brief Modify a sticker in a guild + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::guild_sticker_modify + * @see https://discord.com/developers/docs/resources/sticker#modify-guild-sticker + * @param s Sticker to modify. Must have its guild ID and sticker ID set. + * @return sticker returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_sticker_modify(const sticker &s); + +/** + * @brief Get all guild stickers + * @see dpp::cluster::guild_stickers_get + * @see https://discord.com/developers/docs/resources/sticker#get-guild-stickers + * @param guild_id Guild ID of the guild where the sticker is + * @return sticker_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_stickers_get(snowflake guild_id); + +/** + * @brief Get a nitro sticker + * @see dpp::cluster::nitro_sticker_get + * @see https://discord.com/developers/docs/resources/sticker#get-sticker + * @param id Id of sticker to get. + * @return sticker returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_nitro_sticker_get(snowflake id); + +/** + * @brief Get a list of available sticker packs + * @see dpp::cluster::sticker_packs_get + * @see https://discord.com/developers/docs/resources/sticker#list-nitro-sticker-packs + * @return sticker_pack_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_sticker_packs_get(); + +/** + * @brief Create a new guild based on a template. + * @note This endpoint can be used only by bots in less than 10 guilds. + * @see dpp::cluster::guild_create_from_template + * @see https://discord.com/developers/docs/resources/guild-template#create-guild-from-guild-template + * @param code Template code to create guild from + * @param name Guild name to create + * @return guild returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_create_from_template(const std::string &code, const std::string &name); + +/** + * @brief Creates a template for the guild + * + * @see dpp::cluster::guild_template_create + * @see https://discord.com/developers/docs/resources/guild-template#create-guild-template + * @param guild_id Guild to create template from + * @param name Template name to create + * @param description Description of template to create + * @return dtemplate returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_template_create(snowflake guild_id, const std::string &name, const std::string &description); + +/** + * @brief Deletes the template + * + * @see dpp::cluster::guild_template_delete + * @see https://discord.com/developers/docs/resources/guild-template#delete-guild-template + * @param guild_id Guild ID of template to delete + * @param code Template code to delete + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_template_delete(snowflake guild_id, const std::string &code); + +/** + * @brief Modifies the template's metadata. + * + * @see dpp::cluster::guild_template_modify + * @see https://discord.com/developers/docs/resources/guild-template#modify-guild-template + * @param guild_id Guild ID of template to modify + * @param code Template code to modify + * @param name New name of template + * @param description New description of template + * @return dtemplate returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_template_modify(snowflake guild_id, const std::string &code, const std::string &name, const std::string &description); + +/** + * @brief Get guild templates + * + * @see dpp::cluster::guild_templates_get + * @see https://discord.com/developers/docs/resources/guild-template#get-guild-templates + * @param guild_id Guild ID to get templates for + * @return dtemplate_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_templates_get(snowflake guild_id); + +/** + * @brief Syncs the template to the guild's current state. + * + * @see dpp::cluster::guild_template_sync + * @see https://discord.com/developers/docs/resources/guild-template#sync-guild-template + * @param guild_id Guild to synchronise template for + * @param code Code of template to synchronise + * @return dtemplate returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_template_sync(snowflake guild_id, const std::string &code); + +/** + * @brief Get a template + * @see dpp::cluster::template_get + * @see https://discord.com/developers/docs/resources/guild-template#get-guild-template + * @param code Template code + * @return dtemplate returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_template_get(const std::string &code); + +/** + * @brief Join a thread + * @see dpp::cluster::current_user_join_thread + * @see https://discord.com/developers/docs/resources/channel#join-thread + * @param thread_id Thread ID to join + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_current_user_join_thread(snowflake thread_id); + +/** + * @brief Leave a thread + * @see dpp::cluster::current_user_leave_thread + * @see https://discord.com/developers/docs/resources/channel#leave-thread + * @param thread_id Thread ID to leave + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_current_user_leave_thread(snowflake thread_id); + +/** + * @brief Get all active threads in the guild, including public and private threads. Threads are ordered by their id, in descending order. + * @see dpp::cluster::threads_get_active + * @see https://discord.com/developers/docs/resources/guild#list-active-guild-threads + * @param guild_id Guild to get active threads for + * @return active_threads returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_threads_get_active(snowflake guild_id); + +/** + * @brief Get private archived threads in a channel which current user has joined (Sorted by ID in descending order) + * @see dpp::cluster::threads_get_joined_private_archived + * @see https://discord.com/developers/docs/resources/channel#list-joined-private-archived-threads + * @param channel_id Channel to get public archived threads for + * @param before_id Get threads before this id + * @param limit Number of threads to get + * @return thread_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_threads_get_joined_private_archived(snowflake channel_id, snowflake before_id, uint16_t limit); + +/** + * @brief Get private archived threads in a channel (Sorted by archive_timestamp in descending order) + * @see dpp::cluster::threads_get_private_archived + * @see https://discord.com/developers/docs/resources/channel#list-private-archived-threads + * @param channel_id Channel to get public archived threads for + * @param before_timestamp Get threads archived before this timestamp + * @param limit Number of threads to get + * @return thread_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_threads_get_private_archived(snowflake channel_id, time_t before_timestamp, uint16_t limit); + +/** + * @brief Get public archived threads in a channel (Sorted by archive_timestamp in descending order) + * @see dpp::cluster::threads_get_public_archived + * @see https://discord.com/developers/docs/resources/channel#list-public-archived-threads + * @param channel_id Channel to get public archived threads for + * @param before_timestamp Get threads archived before this timestamp + * @param limit Number of threads to get + * @return thread_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_threads_get_public_archived(snowflake channel_id, time_t before_timestamp, uint16_t limit); + +/** + * @brief Get a thread member + * @see dpp::cluster::thread_member_get + * @see https://discord.com/developers/docs/resources/channel#get-thread-member + * @param thread_id Thread to get member for + * @param user_id ID of the user to get + * @return thread_member returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_thread_member_get(const snowflake thread_id, const snowflake user_id); + +/** + * @brief Get members of a thread + * @see dpp::cluster::thread_members_get + * @see https://discord.com/developers/docs/resources/channel#list-thread-members + * @param thread_id Thread to get members for + * @return thread_member_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_thread_members_get(snowflake thread_id); + +/** + * @brief Create a thread in a forum or media channel + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * + * @see dpp::cluster::thread_create_in_forum + * @see https://discord.com/developers/docs/resources/channel#start-thread-in-forum-channel + * @param thread_name Name of the forum thread + * @param channel_id Forum channel in which thread to create + * @param msg The message to start the thread with + * @param auto_archive_duration Duration to automatically archive the thread after recent activity + * @param rate_limit_per_user amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission manage_messages, manage_thread, or manage_channel, are unaffected + * @param applied_tags List of IDs of forum tags (dpp::forum_tag) to apply to this thread + * @return thread returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_thread_create_in_forum(const std::string& thread_name, snowflake channel_id, const message& msg, auto_archive_duration_t auto_archive_duration, uint16_t rate_limit_per_user, std::vector applied_tags = {}); + +/** + * @brief Create a thread + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * + * @see dpp::cluster::thread_create + * @see https://discord.com/developers/docs/resources/channel#start-thread-without-message + * @param thread_name Name of the thread + * @param channel_id Channel in which thread to create + * @param auto_archive_duration Duration after which thread auto-archives. Can be set to - 60, 1440 (for boosted guilds can also be: 4320, 10080) + * @param thread_type Type of thread - CHANNEL_PUBLIC_THREAD, CHANNEL_ANNOUNCEMENT_THREAD, CHANNEL_PRIVATE_THREAD + * @param invitable whether non-moderators can add other non-moderators to a thread; only available when creating a private thread + * @param rate_limit_per_user amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission manage_messages, manage_thread, or manage_channel, are unaffected + * @return thread returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_thread_create(const std::string& thread_name, snowflake channel_id, uint16_t auto_archive_duration, channel_type thread_type, bool invitable, uint16_t rate_limit_per_user); + +/** + * @brief Edit a thread + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * + * @see dpp::cluster::thread_edit + * @see https://discord.com/developers/docs/topics/threads#editing-deleting-threads + * @param t Thread to edit + * @return thread returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_thread_edit(const thread &t); + +/** + * @brief Create a thread with a message (Discord: ID of a thread is same as message ID) + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::thread_create_with_message + * @see https://discord.com/developers/docs/resources/channel#start-thread-from-message + * @param thread_name Name of the thread + * @param channel_id Channel in which thread to create + * @param message_id message to start thread with + * @param auto_archive_duration Duration after which thread auto-archives. Can be set to - 60, 1440 (for boosted guilds can also be: 4320, 10080) + * @param rate_limit_per_user amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission manage_messages, manage_thread, or manage_channel, are unaffected + * @return thread returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_thread_create_with_message(const std::string& thread_name, snowflake channel_id, snowflake message_id, uint16_t auto_archive_duration, uint16_t rate_limit_per_user); + +/** + * @brief Add a member to a thread + * @see dpp::cluster::thread_member_add + * @see https://discord.com/developers/docs/resources/channel#add-thread-member + * @param thread_id Thread ID to add to + * @param user_id Member ID to add + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_thread_member_add(snowflake thread_id, snowflake user_id); + +/** + * @brief Remove a member from a thread + * @see dpp::cluster::thread_member_remove + * @see https://discord.com/developers/docs/resources/channel#remove-thread-member + * @param thread_id Thread ID to remove from + * @param user_id Member ID to remove + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_thread_member_remove(snowflake thread_id, snowflake user_id); + +/** + * @brief Edit current (bot) user + * + * Modifies the current member in a guild. Returns the updated guild_member object on success. + * Fires a `Guild Member Update` Gateway event. + * @see dpp::cluster::current_user_edit + * @see https://discord.com/developers/docs/resources/user#modify-current-user + * @param nickname Nickname to set + * @param image_blob Avatar data to upload (NOTE: Very heavily rate limited!) + * @param type Type of image for avatar. It can be one of `i_gif`, `i_jpg` or `i_png`. + * @return user returned object on completion + * @throw dpp::length_exception Image data is larger than the maximum size of 256 kilobytes + * \memberof dpp::cluster + */ +[[nodiscard]] async co_current_user_edit(const std::string &nickname, const std::string& image_blob = "", const image_type type = i_png); + +/** + * @brief Get current (bot) application + * + * @see dpp::cluster::current_application_get + * @see https://discord.com/developers/docs/topics/oauth2#get-current-bot-application-information + * @return application returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_current_application_get(); + +/** + * @brief Get current (bot) user + * + * @see dpp::cluster::current_user_get + * @see https://discord.com/developers/docs/resources/user#get-current-user + * @return user_identified returned object on completion + * @note The user_identified object is a subclass of dpp::user which contains further details if you have the oauth2 identify or email scopes. + * If you do not have these scopes, these fields are empty. You can safely convert a user_identified to user with `dynamic_cast`. + * \memberof dpp::cluster + */ +[[nodiscard]] async co_current_user_get(); + +/** + * @brief Set the bot's voice state on a stage channel + * + * **Caveats** + * + * There are currently several caveats for this endpoint: + * + * - `channel_id` must currently point to a stage channel. + * - current user must already have joined `channel_id`. + * - You must have the `MUTE_MEMBERS` permission to unsuppress yourself. You can always suppress yourself. + * - You must have the `REQUEST_TO_SPEAK` permission to request to speak. You can always clear your own request to speak. + * - You are able to set `request_to_speak_timestamp` to any present or future time. + * + * @see dpp::cluster::current_user_set_voice_state + * @see https://discord.com/developers/docs/resources/guild#modify-current-user-voice-state + * @param guild_id Guild to set voice state on + * @param channel_id Stage channel to set voice state on + * @return confirmation returned object on completion + * @param suppress True if the user's audio should be suppressed, false if it should not + * @param request_to_speak_timestamp The time at which we requested to speak, or 0 to clear the request. The time set here must be the current time or in the future. + * @throw std::logic_exception You attempted to set a request_to_speak_timestamp in the past which is not the value of 0. + * \memberof dpp::cluster + */ +[[nodiscard]] async co_current_user_set_voice_state(snowflake guild_id, snowflake channel_id, bool suppress = false, time_t request_to_speak_timestamp = 0); + +/** + * @brief Set a user's voice state on a stage channel + * + * **Caveats** + * + * There are currently several caveats for this endpoint: + * + * - `channel_id` must currently point to a stage channel. + * - User must already have joined `channel_id`. + * - You must have the `MUTE_MEMBERS` permission. (Since suppression is the only thing that is available currently) + * - When unsuppressed, non-bot users will have their `request_to_speak_timestamp` set to the current time. Bot users will not. + * - When suppressed, the user will have their `request_to_speak_timestamp` removed. + * + * @see dpp::cluster::user_set_voice_state + * @see https://discord.com/developers/docs/resources/guild#modify-user-voice-state + * @param user_id The user to set the voice state of + * @param guild_id Guild to set voice state on + * @param channel_id Stage channel to set voice state on + * @return confirmation returned object on completion + * @param suppress True if the user's audio should be suppressed, false if it should not + * \memberof dpp::cluster + */ +[[nodiscard]] async co_user_set_voice_state(snowflake user_id, snowflake guild_id, snowflake channel_id, bool suppress = false); + +/** + * @brief Get current user's connections (linked accounts, e.g. steam, xbox). + * This call requires the oauth2 `connections` scope and cannot be executed + * against a bot token. + * @see dpp::cluster::current_user_connections_get + * @see https://discord.com/developers/docs/resources/user#get-user-connections + * @return connection_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_current_user_connections_get(); + +/** + * @brief Get current (bot) user guilds + * @see dpp::cluster::current_user_get_guilds + * @see https://discord.com/developers/docs/resources/user#get-current-user-guilds + * @return guild_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_current_user_get_guilds(); + +/** + * @brief Leave a guild + * @see dpp::cluster::current_user_leave_guild + * @see https://discord.com/developers/docs/resources/user#leave-guild + * @param guild_id Guild ID to leave + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_current_user_leave_guild(snowflake guild_id); + +/** + * @brief Get a user by id, without using the cache + * + * @see dpp::cluster::user_get + * @see https://discord.com/developers/docs/resources/user#get-user + * @param user_id User ID to retrieve + * @return user_identified returned object on completion + * @note The user_identified object is a subclass of dpp::user which contains further details if you have the oauth2 identify or email scopes. + * If you do not have these scopes, these fields are empty. You can safely convert a user_identified to user with `dynamic_cast`. + * @note unless you want something special from `dpp::user_identified` or you've turned off caching, you have no need to call this. + * Call `dpp::find_user` instead that looks up the user in the cache rather than a REST call. + * \memberof dpp::cluster + */ +[[nodiscard]] async co_user_get(snowflake user_id); + +/** + * @brief Get a user by id, checking in the cache first + * + * @see dpp::cluster::user_get_cached + * @see https://discord.com/developers/docs/resources/user#get-user + * @param user_id User ID to retrieve + * @return user_identified returned object on completion + * @note The user_identified object is a subclass of dpp::user which contains further details if you have the oauth2 identify or email scopes. + * If you do not have these scopes, these fields are empty. You can safely convert a user_identified to user with `dynamic_cast`. + * @note If the user is found in the cache, special values set in `dpp::user_identified` will be undefined. This call should be used + * where you want to for example resolve a user who may no longer be in the bot's guilds, for something like a ban log message. + * \memberof dpp::cluster + */ +[[nodiscard]] async co_user_get_cached(snowflake user_id); + +/** + * @brief Get all voice regions + * @see dpp::cluster::get_voice_regions + * @see https://discord.com/developers/docs/resources/voice#list-voice-regions + * @return voiceregion_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_get_voice_regions(); + +/** + * @brief Get guild voice regions. + * + * Voice regions per guild are somewhat deprecated in preference of per-channel voice regions. + * Returns a list of voice region objects for the guild. Unlike the similar /voice route, this returns VIP servers when + * the guild is VIP-enabled. + * + * @see dpp::cluster::guild_get_voice_regions + * @see https://discord.com/developers/docs/resources/guild#get-guild-voice-regions + * @param guild_id Guild ID to get voice regions for + * @return voiceregion_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_guild_get_voice_regions(snowflake guild_id); + +/** + * @brief Create a webhook + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::create_webhook + * @see https://discord.com/developers/docs/resources/webhook#create-webhook + * @param w Webhook to create + * @return webhook returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_create_webhook(const class webhook &w); + +/** + * @brief Delete a webhook + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::delete_webhook + * @see https://discord.com/developers/docs/resources/webhook#delete-webhook + * @param webhook_id Webhook ID to delete + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_delete_webhook(snowflake webhook_id); + +/** + * @brief Delete webhook message + * + * @see dpp::cluster::delete_webhook_message + * @see https://discord.com/developers/docs/resources/webhook#delete-webhook-message + * @param wh Webhook to delete message for + * @param message_id Message ID to delete + * @param thread_id ID of the thread the message is in + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_delete_webhook_message(const class webhook &wh, snowflake message_id, snowflake thread_id = 0); + +/** + * @brief Delete webhook with token + * @see dpp::cluster::delete_webhook_with_token + * @see https://discord.com/developers/docs/resources/webhook#delete-webhook-with-token + * @param webhook_id Webhook ID to delete + * @param token Token of webhook to delete + * @return confirmation returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_delete_webhook_with_token(snowflake webhook_id, const std::string &token); + +/** + * @brief Edit webhook + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::edit_webhook + * @see https://discord.com/developers/docs/resources/webhook#modify-webhook + * @param wh Webhook to edit + * @return webhook returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_edit_webhook(const class webhook& wh); + +/** + * @brief Edit webhook message + * + * When the content field is edited, the mentions array in the message object will be reconstructed from scratch based on + * the new content. The allowed_mentions field of the edit request controls how this happens. If there is no explicit + * allowed_mentions in the edit request, the content will be parsed with default allowances, that is, without regard to + * whether or not an allowed_mentions was present in the request that originally created the message. + * + * @see dpp::cluster::edit_webhook_message + * @see https://discord.com/developers/docs/resources/webhook#edit-webhook-message + * @note the attachments array must contain all attachments that should be present after edit, including retained and new attachments provided in the request body. + * @param wh Webhook to edit message for + * @param m New message + * @param thread_id ID of the thread the message is in + * @return message returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_edit_webhook_message(const class webhook &wh, const struct message &m, snowflake thread_id = 0); + +/** + * @brief Edit webhook with token (token is encapsulated in the webhook object) + * @see dpp::cluster::edit_webhook_with_token + * @see https://discord.com/developers/docs/resources/webhook#modify-webhook-with-token + * @param wh Webhook to edit (should include token) + * @return webhook returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_edit_webhook_with_token(const class webhook& wh); + +/** + * @brief Execute webhook + * + * @see dpp::cluster::execute_webhook + * @see https://discord.com/developers/docs/resources/webhook#execute-webhook + * @param wh Webhook to execute + * @param m Message to send + * @param wait waits for server confirmation of message send before response, and returns the created message body + * @param thread_id Send a message to the specified thread within a webhook's channel. The thread will automatically be unarchived + * @param thread_name Name of thread to create (requires the webhook channel to be a forum channel) + * @return message returned object on completion + * @note If the webhook channel is a forum channel, you must provide either `thread_id` or `thread_name`. If `thread_id` is provided, the message will send in that thread. If `thread_name` is provided, a thread with that name will be created in the forum channel. + * \memberof dpp::cluster + */ +[[nodiscard]] async co_execute_webhook(const class webhook &wh, const struct message &m, bool wait = false, snowflake thread_id = 0, const std::string& thread_name = ""); + +/** + * @brief Get channel webhooks + * @see dpp::cluster::get_channel_webhooks + * @see https://discord.com/developers/docs/resources/webhook#get-guild-webhooks + * @param channel_id Channel ID to get webhooks for + * @return webhook_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_get_channel_webhooks(snowflake channel_id); + +/** + * @brief Get guild webhooks + * @see dpp::cluster::get_guild_webhooks + * @see https://discord.com/developers/docs/resources/webhook#get-guild-webhooks + * @param guild_id Guild ID to get webhooks for + * @return webhook_map returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_get_guild_webhooks(snowflake guild_id); + +/** + * @brief Get webhook + * @see dpp::cluster::get_webhook + * @see https://discord.com/developers/docs/resources/webhook#get-webhook + * @param webhook_id Webhook ID to get + * @return webhook returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_get_webhook(snowflake webhook_id); + +/** + * @brief Get webhook message + * + * @see dpp::cluster::get_webhook_message + * @see https://discord.com/developers/docs/resources/webhook#get-webhook-message + * @param wh Webhook to get the original message for + * @param message_id The message ID + * @param thread_id ID of the thread the message is in + * @return message returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_get_webhook_message(const class webhook &wh, snowflake message_id, snowflake thread_id = 0); + +/** + * @brief Get webhook using token + * @see dpp::cluster::get_webhook_with_token + * @see https://discord.com/developers/docs/resources/webhook#get-webhook-with-token + * @param webhook_id Webhook ID to retrieve + * @param token Token of webhook + * @return webhook returned object on completion + * \memberof dpp::cluster + */ +[[nodiscard]] async co_get_webhook_with_token(snowflake webhook_id, const std::string &token); + + +/* End of auto-generated definitions */ +[[nodiscard]] async co_request(const std::string &url, http_method method, const std::string &postdata = "", const std::string &mimetype = "text/plain", const std::multimap &headers = {}); + diff --git a/include/dpp/cluster_sync_calls.h b/include/dpp/cluster_sync_calls.h new file mode 100644 index 0000000..7ae7d3f --- /dev/null +++ b/include/dpp/cluster_sync_calls.h @@ -0,0 +1,2941 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2022 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ + + +/* Auto @generated by buildtools/make_sync_struct.php. + * + * DO NOT EDIT BY HAND! + * + * To re-generate this header file re-run the script! + */ +/** + * @brief Create/overwrite global slash commands. + * Any existing global slash commands will be deleted and replaced with these. + * + * @see dpp::cluster::global_bulk_command_create + * @see https://discord.com/developers/docs/interactions/application-commands#bulk-overwrite-global-application-commands + * @param commands Vector of slash commands to create/update. + * overwriting existing commands that are registered globally for this application. Updates will be available in all guilds after 1 hour. + * Commands that do not already exist will count toward daily application command create limits. + * @return slashcommand_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +slashcommand_map global_bulk_command_create_sync(const std::vector &commands); + +/** + * @brief Create a global slash command (a bot can have a maximum of 100 of these). + * + * @see dpp::cluster::global_command_create + * @see https://discord.com/developers/docs/interactions/application-commands#create-global-application-command + * @param s Slash command to create + * @return slashcommand returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +slashcommand global_command_create_sync(const slashcommand &s); + +/** + * @brief Get a global slash command + * + * @see dpp::cluster::global_command_get + * @see https://discord.com/developers/docs/interactions/application-commands#get-global-application-command + * @param id The ID of the slash command + * @return slashcommand returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +slashcommand global_command_get_sync(snowflake id); + +/** + * @brief Delete a global slash command (a bot can have a maximum of 100 of these) + * + * @see dpp::cluster::global_command_delete + * @see https://discord.com/developers/docs/interactions/application-commands#delete-global-application-command + * @param id Slash command to delete + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation global_command_delete_sync(snowflake id); + +/** + * @brief Edit a global slash command (a bot can have a maximum of 100 of these) + * + * @see dpp::cluster::global_command_edit + * @see https://discord.com/developers/docs/interactions/application-commands#edit-global-application-command + * @param s Slash command to change + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation global_command_edit_sync(const slashcommand &s); + +/** + * @brief Get the application's global slash commands + * + * @see dpp::cluster::global_commands_get + * @see https://discord.com/developers/docs/interactions/application-commands#get-global-application-commands + * @return slashcommand_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +slashcommand_map global_commands_get_sync(); + +/** + * @brief Create/overwrite guild slash commands. + * Any existing guild slash commands on this guild will be deleted and replaced with these. + * + * @see dpp::cluster::guild_bulk_command_create + * @see https://discord.com/developers/docs/interactions/application-commands#bulk-overwrite-guild-application-commands + * @param commands Vector of slash commands to create/update. + * New guild commands will be available in the guild immediately. If the command did not already exist, it will count toward daily application command create limits. + * @param guild_id Guild ID to create/update the slash commands in + * @return slashcommand_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +slashcommand_map guild_bulk_command_create_sync(const std::vector &commands, snowflake guild_id); + +/** + * @brief Get all slash command permissions of a guild + * + * @see dpp::cluster::guild_commands_get_permissions + * @see https://discord.com/developers/docs/interactions/application-commands#get-application-command-permissions + * @param guild_id Guild ID to get the slash commands permissions for + * @return guild_command_permissions_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +guild_command_permissions_map guild_commands_get_permissions_sync(snowflake guild_id); + +/** + * @brief Edit/Overwrite the permissions of all existing slash commands in a guild + * + * @note You can only add up to 10 permission overwrites for a command + * + * @see dpp::cluster::guild_bulk_command_edit_permissions + * @see https://discord.com/developers/docs/interactions/application-commands#batch-edit-application-command-permissions + * @warning The endpoint will overwrite all existing permissions for all commands of the application in a guild, including slash commands, user commands, and message commands. Meaning that if you forgot to pass a slash command, the permissions of it might be removed. + * @param commands A vector of slash commands to edit/overwrite the permissions for + * @param guild_id Guild ID to edit permissions of the slash commands in + * @return guild_command_permissions_map returned object on completion + * @deprecated This has been disabled with updates to Permissions v2. You can use guild_command_edit_permissions instead + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +guild_command_permissions_map guild_bulk_command_edit_permissions_sync(const std::vector &commands, snowflake guild_id); + +/** + * @brief Create a slash command local to a guild + * + * @see dpp::cluster::guild_command_create + * @see https://discord.com/developers/docs/interactions/application-commands#create-guild-application-command + * @note Creating a command with the same name as an existing command for your application will overwrite the old command. + * @param s Slash command to create + * @param guild_id Guild ID to create the slash command in + * @return slashcommand returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +slashcommand guild_command_create_sync(const slashcommand &s, snowflake guild_id); + +/** + * @brief Delete a slash command local to a guild + * + * @see dpp::cluster::guild_command_delete + * @see https://discord.com/developers/docs/interactions/application-commands#delete-guild-application-command + * @param id Slash command to delete + * @param guild_id Guild ID to delete the slash command in + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation guild_command_delete_sync(snowflake id, snowflake guild_id); + +/** + * @brief Edit slash command permissions of a guild + * + * @see dpp::cluster::guild_command_edit_permissions + * @see https://discord.com/developers/docs/interactions/application-commands#edit-application-command-permissions + * @note You can only add up to 10 permission overwrites for a command + * @param s Slash command to edit the permissions for + * @param guild_id Guild ID to edit the slash command in + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation guild_command_edit_permissions_sync(const slashcommand &s, snowflake guild_id); + +/** + * @brief Get a slash command of a guild + * + * @see dpp::cluster::guild_command_get + * @see https://discord.com/developers/docs/interactions/application-commands#get-guild-application-command + * @note The returned slash commands will not have permissions set, you need to use a permissions getter e.g. dpp::guild_commands_get_permissions to get the guild command permissions + * @param id The ID of the slash command + * @param guild_id Guild ID to get the slash command from + * @return slashcommand returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +slashcommand guild_command_get_sync(snowflake id, snowflake guild_id); + +/** + * @brief Get the permissions for a slash command of a guild + * + * @see dpp::cluster::guild_command_get_permissions + * @see https://discord.com/developers/docs/interactions/application-commands#get-application-command-permissions + * @param id The ID of the slash command to get the permissions for + * @param guild_id Guild ID to get the permissions of + * @return guild_command_permissions returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +guild_command_permissions guild_command_get_permissions_sync(snowflake id, snowflake guild_id); + +/** + * @brief Edit a slash command local to a guild + * + * @see dpp::cluster::guild_command_edit + * @see https://discord.com/developers/docs/interactions/application-commands#edit-guild-application-command + * @param s Slash command to edit + * @param guild_id Guild ID to edit the slash command in + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation guild_command_edit_sync(const slashcommand &s, snowflake guild_id); + +/** + * @brief Get the application's slash commands for a guild + * + * @see dpp::cluster::guild_commands_get + * @see https://discord.com/developers/docs/interactions/application-commands#get-guild-application-commands + * @note The returned slash commands will not have permissions set, you need to use a permissions getter e.g. dpp::guild_commands_get_permissions to get the guild command permissions + * @param guild_id Guild ID to get the slash commands for + * @return slashcommand_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +slashcommand_map guild_commands_get_sync(snowflake guild_id); + +/** + * @brief Respond to a slash command + * + * @see dpp::cluster::interaction_response_create + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#create-interaction-response + * @param interaction_id Interaction id to respond to + * @param token Token for the interaction webhook + * @param r Response to send + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation interaction_response_create_sync(snowflake interaction_id, const std::string &token, const interaction_response &r); + +/** + * @brief Edit response to a slash command + * + * @see dpp::cluster::interaction_response_edit + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#edit-original-interaction-response + * @param token Token for the interaction webhook + * @param m Message to send + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation interaction_response_edit_sync(const std::string &token, const message &m); + +/** + * @brief Get the original response to a slash command + * + * @see dpp::cluster::interaction_response_get_original + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#get-original-interaction-response + * @param token Token for the interaction webhook + * @return message returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +message interaction_response_get_original_sync(const std::string &token); + +/** + * @brief Create a followup message to a slash command + * + * @see dpp::cluster::interaction_followup_create + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#create-interaction-response + * @param token Token for the interaction webhook + * @param m followup message to create + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation interaction_followup_create_sync(const std::string &token, const message &m); + +/** + * @brief Edit original followup message to a slash command + * This is an alias for cluster::interaction_response_edit + * @see dpp::cluster::interaction_followup_edit_original + * @see cluster::interaction_response_edit + * + * @param token Token for the interaction webhook + * @param m message to edit, the ID should be set + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation interaction_followup_edit_original_sync(const std::string &token, const message &m); + +/** + * @brief Delete the initial interaction response + * + * @see dpp::cluster::interaction_followup_delete + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#delete-original-interaction-response + * @param token Token for the interaction webhook + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation interaction_followup_delete_sync(const std::string &token); + +/** + * @brief Edit followup message to a slash command + * The message ID in the message you pass should be correctly set to that of a followup message you previously sent + * + * @see dpp::cluster::interaction_followup_edit + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#edit-followup-message + * @param token Token for the interaction webhook + * @param m message to edit, the ID should be set + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation interaction_followup_edit_sync(const std::string &token, const message &m); + +/** + * @brief Get the followup message to a slash command + * + * @see dpp::cluster::interaction_followup_get + * @see https://discord.com/developers/docs/interactions/receiving-and-responding#get-followup-message + * @param token Token for the interaction webhook + * @param message_id message to retrieve + * @return message returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +message interaction_followup_get_sync(const std::string &token, snowflake message_id); + +/** + * @brief Get the original followup message to a slash command + * This is an alias for cluster::interaction_response_get_original + * @see dpp::cluster::interaction_followup_get_original + * @see cluster::interaction_response_get_original + * + * @param token Token for the interaction webhook + * @return message returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +message interaction_followup_get_original_sync(const std::string &token); + +/** + * @brief Get all auto moderation rules for a guild + * + * @param guild_id Guild id of the auto moderation rule + * @return automod_rule_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +automod_rule_map automod_rules_get_sync(snowflake guild_id); + +/** + * @brief Get a single auto moderation rule + * + * @param guild_id Guild id of the auto moderation rule + * @param rule_id Rule id to retrieve + * @return automod_rule returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +automod_rule automod_rule_get_sync(snowflake guild_id, snowflake rule_id); + +/** + * @brief Create an auto moderation rule + * + * @param guild_id Guild id of the auto moderation rule + * @param r Auto moderation rule to create + * @return automod_rule returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +automod_rule automod_rule_create_sync(snowflake guild_id, const automod_rule& r); + +/** + * @brief Edit an auto moderation rule + * + * @param guild_id Guild id of the auto moderation rule + * @param r Auto moderation rule to edit. The rule's id must be set. + * @return automod_rule returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +automod_rule automod_rule_edit_sync(snowflake guild_id, const automod_rule& r); + +/** + * @brief Delete an auto moderation rule + * + * @param guild_id Guild id of the auto moderation rule + * @param rule_id Auto moderation rule id to delete + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation automod_rule_delete_sync(snowflake guild_id, snowflake rule_id); + +/** + * @brief Create a channel + * + * Create a new channel object for the guild. Requires the `MANAGE_CHANNELS` permission. If setting permission overwrites, + * only permissions your bot has in the guild can be allowed/denied. Setting `MANAGE_ROLES` permission in channels is only possible + * for guild administrators. Returns the new channel object on success. Fires a `Channel Create Gateway` event. + * + * All parameters to this endpoint are optional excluding `name` + * + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::channel_create + * @see https://discord.com/developers/docs/resources/channel#create-channel + * @param c Channel to create + * @return channel returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +channel channel_create_sync(const class channel &c); + +/** + * @brief Remove a permission from a channel + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::channel_delete_permission + * @see https://discord.com/developers/docs/resources/channel#delete-channel-permission + * @param c Channel to remove permission from + * @param overwrite_id Overwrite to remove, user or channel ID + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation channel_delete_permission_sync(const class channel &c, snowflake overwrite_id); + +/** + * @brief Delete a channel + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::channel_delete + * @see https://discord.com/developers/docs/resources/channel#deleteclose-channel + * @param channel_id Channel id to delete + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation channel_delete_sync(snowflake channel_id); + +/** + * @brief Edit a channel's permissions + * + * @see dpp::cluster::channel_edit_permissions + * @see https://discord.com/developers/docs/resources/channel#edit-channel-permissions + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param c Channel to set permissions for + * @param overwrite_id Overwrite to change (a user or role ID) + * @param allow allow permissions bitmask + * @param deny deny permissions bitmask + * @param member true if the overwrite_id is a user id, false if it is a channel id + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation channel_edit_permissions_sync(const class channel &c, const snowflake overwrite_id, const uint64_t allow, const uint64_t deny, const bool member); + +/** + * @brief Edit a channel's permissions + * + * @see dpp::cluster::channel_edit_permissions + * @see https://discord.com/developers/docs/resources/channel#edit-channel-permissions + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param channel_id ID of the channel to set permissions for + * @param overwrite_id Overwrite to change (a user or role ID) + * @param allow allow permissions bitmask + * @param deny deny permissions bitmask + * @param member true if the overwrite_id is a user id, false if it is a channel id + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation channel_edit_permissions_sync(const snowflake channel_id, const snowflake overwrite_id, const uint64_t allow, const uint64_t deny, const bool member); + +/** + * @brief Edit multiple channels positions + * + * Modify the positions of a set of channel objects for the guild. + * Requires `MANAGE_CHANNELS` permission. Fires multiple `Channel Update Gateway` events. + * Only channels to be modified are required. + * + * @see dpp::cluster::channel_edit_positions + * @see https://discord.com/developers/docs/resources/guild#modify-guild-channel-positions + * @param c Channel to change the position for + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation channel_edit_positions_sync(const std::vector &c); + +/** + * @brief Edit a channel + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::channel_edit + * @see https://discord.com/developers/docs/resources/channel#modify-channel + * @param c Channel to edit/update + * @return channel returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +channel channel_edit_sync(const class channel &c); + +/** + * @brief Follow an announcement (news) channel + * @see dpp::cluster::channel_follow_news + * @see https://discord.com/developers/docs/resources/channel#follow-news-channel + * @param c Channel id to follow + * @param target_channel_id Channel to subscribe the channel to + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation channel_follow_news_sync(const class channel &c, snowflake target_channel_id); + +/** + * @brief Get a channel + * + * @see dpp::cluster::channel_get + * @see https://discord.com/developers/docs/resources/channel#get-channel + * @param c Channel ID to retrieve + * @return channel returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +channel channel_get_sync(snowflake c); + +/** + * @brief Create invite for a channel + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::channel_invite_create + * @see https://discord.com/developers/docs/resources/channel#create-channel-invite + * @param c Channel to create an invite on + * @param i Invite to create + * @return invite returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +invite channel_invite_create_sync(const class channel &c, const class invite &i); + +/** + * @brief Get invites for a channel + * + * @see dpp::cluster::channel_invites_get + * @see https://discord.com/developers/docs/resources/invite#get-invites + * @param c Channel to get invites for + * @return invite_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +invite_map channel_invites_get_sync(const class channel &c); + +/** + * @brief Trigger channel typing indicator + * @see dpp::cluster::channel_typing + * @see https://discord.com/developers/docs/resources/channel#trigger-typing-indicator + * @param c Channel to set as typing on + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation channel_typing_sync(const class channel &c); + +/** + * @brief Trigger channel typing indicator + * @see dpp::cluster::channel_typing + * @see https://discord.com/developers/docs/resources/channel#trigger-typing-indicator + * @param cid Channel ID to set as typing on + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation channel_typing_sync(snowflake cid); + +/** + * @brief Get all channels for a guild + * + * @see dpp::cluster::channels_get + * @see https://discord.com/developers/docs/resources/channel#get-channels + * @param guild_id Guild ID to retrieve channels for + * @return channel_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +channel_map channels_get_sync(snowflake guild_id); + +/** + * @brief Create a dm channel + * @see dpp::cluster::create_dm_channel + * @see https://discord.com/developers/docs/resources/user#create-dm + * @param user_id User ID to create DM channel with + * @return channel returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +channel create_dm_channel_sync(snowflake user_id); + +/** + * @brief Get current user DM channels + * + * @return channel_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +channel_map current_user_get_dms_sync(); + +/** + * @brief Create a direct message, also create the channel for the direct message if needed + * + * @see dpp::cluster::direct_message_create + * @see https://discord.com/developers/docs/resources/user#create-dm + * @see dpp::cluster::direct_message_create + * @see https://discord.com/developers/docs/resources/channel#create-message + * @param user_id User ID of user to send message to + * @param m Message object + * @return message returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +message direct_message_create_sync(snowflake user_id, const message &m); + +/** + * @brief Adds a recipient to a Group DM using their access token + * @see dpp::cluster::gdm_add + * @see https://discord.com/developers/docs/resources/channel#group-dm-add-recipient + * @param channel_id Channel id to add group DM recipients to + * @param user_id User ID to add + * @param access_token Access token from OAuth2 + * @param nick Nickname of user to apply to the chat + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation gdm_add_sync(snowflake channel_id, snowflake user_id, const std::string &access_token, const std::string &nick); + +/** + * @brief Removes a recipient from a Group DM + * @see dpp::cluster::gdm_remove + * @see https://discord.com/developers/docs/resources/channel#group-dm-remove-recipient + * @param channel_id Channel ID of group DM + * @param user_id User ID to remove from group DM + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation gdm_remove_sync(snowflake channel_id, snowflake user_id); + +/** + * @brief Create single emoji. + * You must ensure that the emoji passed contained image data using the emoji::load_image() method. + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * + * @see dpp::cluster::guild_emoji_create + * @see https://discord.com/developers/docs/resources/emoji#create-guild-emoji + * @param guild_id Guild ID to create emoji om + * @param newemoji Emoji to create + * @return emoji returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +emoji guild_emoji_create_sync(snowflake guild_id, const class emoji& newemoji); + +/** + * @brief Delete a guild emoji + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * + * @see dpp::cluster::guild_emoji_delete + * @see https://discord.com/developers/docs/resources/emoji#delete-guild-emoji + * @param guild_id Guild ID to delete emoji on + * @param emoji_id Emoji ID to delete + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation guild_emoji_delete_sync(snowflake guild_id, snowflake emoji_id); + +/** + * @brief Edit a single emoji. + * + * You must ensure that the emoji passed contained image data using the emoji::load_image() method. + * @see dpp::cluster::guild_emoji_edit + * @see https://discord.com/developers/docs/resources/emoji#modify-guild-emoji + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to edit emoji on + * @param newemoji Emoji to edit + * @return emoji returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +emoji guild_emoji_edit_sync(snowflake guild_id, const class emoji& newemoji); + +/** + * @brief Get a single emoji + * + * @see dpp::cluster::guild_emoji_get + * @see https://discord.com/developers/docs/resources/emoji#get-guild-emoji + * @param guild_id Guild ID to get emoji for + * @param emoji_id Emoji ID to get + * @return emoji returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +emoji guild_emoji_get_sync(snowflake guild_id, snowflake emoji_id); + +/** + * @brief Get all emojis for a guild + * + * @see dpp::cluster::guild_emojis_get + * @see https://discord.com/developers/docs/resources/emoji#list-guild-emojis + * @param guild_id Guild ID to get emojis for + * @return emoji_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +emoji_map guild_emojis_get_sync(snowflake guild_id); + +/** + * @brief Get the gateway information for the bot using the token + * @see dpp::cluster::get_gateway_bot + * @see https://discord.com/developers/docs/topics/gateway#get-gateway-bot + * @return gateway returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +gateway get_gateway_bot_sync(); + +/** + * @brief Modify current member + * + * Modifies the current member in a guild. + * Fires a `Guild Member Update` Gateway event. + * + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::guild_current_member_edit + * @see https://discord.com/developers/docs/resources/guild#modify-current-member + * @param guild_id Guild ID to change on + * @param nickname New nickname, or empty string to clear nickname + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation guild_current_member_edit_sync(snowflake guild_id, const std::string &nickname); + +/** + * @brief Get the audit log for a guild + * + * @see dpp::cluster::guild_auditlog_get + * @see https://discord.com/developers/docs/resources/audit-log#get-guild-audit-log + * @param guild_id Guild to get the audit log of + * @param user_id Entries from a specific user ID. Set this to `0` will fetch any user + * @param action_type Entries for a specific dpp::audit_type. Set this to `0` will fetch any type + * @param before Entries with ID less than a specific audit log entry ID. Used for paginating + * @param after Entries with ID greater than a specific audit log entry ID. Used for paginating + * @param limit Maximum number of entries (between 1-100) to return + * @return auditlog returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +auditlog guild_auditlog_get_sync(snowflake guild_id, snowflake user_id, uint32_t action_type, snowflake before, snowflake after, uint32_t limit); + +/** + * @brief Add guild ban + * + * Create a guild ban, and optionally delete previous messages sent by the banned user. + * Requires the `BAN_MEMBERS` permission. Fires a `Guild Ban Add` Gateway event. + * @see dpp::cluster::guild_ban_add + * @see https://discord.com/developers/docs/resources/guild#create-guild-ban + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to add ban to + * @param user_id User ID to ban + * @param delete_message_seconds How many seconds to delete messages for, between 0 and 604800 (7 days). Defaults to 0 + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation guild_ban_add_sync(snowflake guild_id, snowflake user_id, uint32_t delete_message_seconds = 0); + +/** + * @brief Delete guild ban + * + * Remove the ban for a user. Requires the `BAN_MEMBERS` permissions. + * Fires a Guild Ban Remove Gateway event. + * @see dpp::cluster::guild_ban_delete + * @see https://discord.com/developers/docs/resources/guild#remove-guild-ban + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild to delete ban from + * @param user_id User ID to delete ban for + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation guild_ban_delete_sync(snowflake guild_id, snowflake user_id); + +/** + * @brief Create a guild + * + * Create a new guild. Returns a guild object on success. `Fires a Guild Create Gateway` event. + * + * When using the roles parameter, the first member of the array is used to change properties of the guild's everyone role. + * If you are trying to bootstrap a guild with additional roles, keep this in mind. The required id field within each role object is an + * integer placeholder, and will be replaced by the API upon consumption. Its purpose is to allow you to overwrite a role's permissions + * in a channel when also passing in channels with the channels array. + * When using the channels parameter, the position field is ignored, and none of the default channels are created. The id field within + * each channel object may be set to an integer placeholder, and will be replaced by the API upon consumption. Its purpose is to + * allow you to create `GUILD_CATEGORY` channels by setting the `parent_id` field on any children to the category's id field. + * Category channels must be listed before any children. + * + * @see dpp::cluster::guild_create + * @see https://discord.com/developers/docs/resources/guild#create-guild + * @note The region field is deprecated and is replaced by channel.rtc_region. This endpoint can be used only by bots in less than 10 guilds. + * @param g Guild to create + * @return guild returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +guild guild_create_sync(const class guild &g); + +/** + * @brief Delete a guild + * + * Delete a guild permanently. User must be owner. Fires a `Guild Delete Gateway` event. + * + * @see dpp::cluster::guild_delete + * @see https://discord.com/developers/docs/resources/guild#delete-guild + * @param guild_id Guild ID to delete + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation guild_delete_sync(snowflake guild_id); + +/** + * @brief Delete guild integration + * + * Delete the attached integration object for the guild. Deletes any associated webhooks and kicks the associated bot if there is one. + * Requires the `MANAGE_GUILD` permission. Fires a Guild Integrations Update Gateway event. + * + * @see dpp::cluster::guild_delete_integration + * @see https://discord.com/developers/docs/resources/guild#delete-guild-integration + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to delete integration for + * @param integration_id Integration ID to delete + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation guild_delete_integration_sync(snowflake guild_id, snowflake integration_id); + +/** + * @brief Edit a guild + * + * Modify a guild's settings. Requires the `MANAGE_GUILD` permission. Returns the updated guild object on success. + * Fires a `Guild Update Gateway` event. + * + * @see dpp::cluster::guild_edit + * @see https://discord.com/developers/docs/resources/guild#modify-guild + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param g Guild to edit + * @return guild returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +guild guild_edit_sync(const class guild &g); + +/** + * @brief Edit guild widget + * + * Requires the `MANAGE_GUILD` permission. + * + * @see dpp::cluster::guild_edit_widget + * @see https://discord.com/developers/docs/resources/guild#modify-guild-widget + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to edit widget for + * @param gw New guild widget information + * @return guild_widget returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +guild_widget guild_edit_widget_sync(snowflake guild_id, const class guild_widget &gw); + +/** + * @brief Get single guild ban + * + * Requires the `BAN_MEMBERS` permission. + * @see dpp::cluster::guild_get_ban + * @see https://discord.com/developers/docs/resources/guild#get-guild-ban + * @param guild_id Guild ID to get ban for + * @param user_id User ID of ban to retrieve + * @return ban returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +ban guild_get_ban_sync(snowflake guild_id, snowflake user_id); + +/** + * @brief Get guild ban list + * + * Requires the `BAN_MEMBERS` permission. + * @see dpp::cluster::guild_get_bans + * @see https://discord.com/developers/docs/resources/guild#get-guild-bans + * @note Provide a user ID to `before` and `after` for pagination. Users will always be returned in ascending order by the user ID. If both before and after are provided, only before is respected. + * @param guild_id Guild ID to get bans for + * @param before If non-zero, all bans for user ids before this user id will be returned up to the limit + * @param after if non-zero, all bans for user ids after this user id will be returned up to the limit + * @param limit the maximum number of bans to retrieve in this call up to a maximum of 1000 + * @return ban_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +ban_map guild_get_bans_sync(snowflake guild_id, snowflake before, snowflake after, snowflake limit); + + +guild guild_get_sync(snowflake guild_id); + +/** + * @brief Get guild integrations + * + * Requires the `MANAGE_GUILD` permission. + * + * @see dpp::cluster::guild_get_integrations + * @see https://discord.com/developers/docs/resources/guild#get-guild-integrations + * @param guild_id Guild ID to get integrations for + * @return integration_map returned object on completion + * + * @note This endpoint returns a maximum of 50 integrations. If a guild has more integrations, they cannot be accessed. + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +integration_map guild_get_integrations_sync(snowflake guild_id); + + +guild guild_get_preview_sync(snowflake guild_id); + +/** + * @brief Get guild vanity url, if enabled + * + * Returns a partial dpp::invite object for guilds with that feature enabled. Requires the `MANAGE_GUILD` permission. code will be null if a vanity url for the guild is not set. + * @see dpp::cluster::guild_get_vanity + * @see https://discord.com/developers/docs/resources/guild#get-guild-vanity-url + * @param guild_id Guild to get vanity URL for + * @return invite returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +invite guild_get_vanity_sync(snowflake guild_id); + +/** + * @brief Get guild widget + * + * Requires the `MANAGE_GUILD` permission. + * + * @see dpp::cluster::guild_get_widget + * @see https://discord.com/developers/docs/resources/guild#get-guild-widget + * @param guild_id Guild ID to get widget for + * @return guild_widget returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +guild_widget guild_get_widget_sync(snowflake guild_id); + +/** + * @brief Modify guild integration + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * + * @see dpp::cluster::guild_modify_integration + * @see https://discord.com/developers/docs/resources/guild#modify-guild-integration + * @param guild_id Guild ID to modify integration for + * @param i Integration to modify + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation guild_modify_integration_sync(snowflake guild_id, const class integration &i); + +/** + * @brief Get prune counts + * + * Returns a prune object indicating the number of members that would be removed in a prune operation. Requires the `KICK_MEMBERS` + * permission. By default, prune will not remove users with roles. You can optionally include specific roles in your prune by providing the + * include_roles parameter. Any inactive user that has a subset of the provided role(s) will be counted in the prune and users with additional + * roles will not. + * + * @see dpp::cluster::guild_get_prune_counts + * @see https://discord.com/developers/docs/resources/guild#get-guild-prune-count + * @param guild_id Guild ID to count for pruning + * @param pruneinfo Pruning info + * @return prune returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +prune guild_get_prune_counts_sync(snowflake guild_id, const struct prune& pruneinfo); + +/** + * @brief Begin guild prune + * + * Begin a prune operation. Requires the `KICK_MEMBERS` permission. Returns a prune object indicating the number of members + * that were removed in the prune operation. For large guilds it's recommended to set the `compute_prune_count` option to false, forcing + * 'pruned' to 0. Fires multiple `Guild Member Remove` Gateway events. + * By default, prune will not remove users with roles. You can optionally include specific roles in your prune by providing the `include_roles` + * parameter. Any inactive user that has a subset of the provided role(s) will be included in the prune and users with additional roles will not. + * + * @see dpp::cluster::guild_begin_prune + * @see https://discord.com/developers/docs/resources/guild#begin-guild-prune + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to prune + * @param pruneinfo Pruning info + * @return prune returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +prune guild_begin_prune_sync(snowflake guild_id, const struct prune& pruneinfo); + +/** + * @brief Change current user nickname + * + * Modifies the nickname of the current user in a guild. + * Fires a `Guild Member Update` Gateway event. + * + * @deprecated Deprecated in favor of Modify Current Member. Will be replaced by dpp::cluster::guild_current_member_edit + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::guild_set_nickname + * @see https://discord.com/developers/docs/resources/guild#modify-current-user-nick + * @param guild_id Guild ID to change nickname on + * @param nickname New nickname, or empty string to clear nickname + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation guild_set_nickname_sync(snowflake guild_id, const std::string &nickname); + +/** + * @brief Sync guild integration + * + * @see dpp::cluster::guild_sync_integration + * @see https://discord.com/developers/docs/resources/guild#sync-guild-integration + * @param guild_id Guild ID to sync integration on + * @param integration_id Integration ID to synchronise + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation guild_sync_integration_sync(snowflake guild_id, snowflake integration_id); + +/** + * @brief Get the guild's onboarding configuration + * + * @see dpp::cluster::guild_get_onboarding + * @see https://discord.com/developers/docs/resources/guild#get-guild-onboarding + * @param o The onboarding object + * @return onboarding returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +onboarding guild_get_onboarding_sync(snowflake guild_id); + +/** + * @brief Edit the guild's onboarding configuration + * + * Requires the `MANAGE_GUILD` and `MANAGE_ROLES` permissions. + * + * @note Onboarding enforces constraints when enabled. These constraints are that there must be at least 7 Default Channels and at least 5 of them must allow sending messages to the \@everyone role. The `onboarding::mode` field modifies what is considered when enforcing these constraints. + * + * @see dpp::cluster::guild_edit_onboarding + * @see https://discord.com/developers/docs/resources/guild#modify-guild-onboarding + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param o The onboarding object + * @return onboarding returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +onboarding guild_edit_onboarding_sync(const struct onboarding& o); + +/** + * @brief Get the guild's welcome screen + * + * If the welcome screen is not enabled, the `MANAGE_GUILD` permission is required. + * + * @see dpp::cluster::guild_get_welcome_screen + * @see https://discord.com/developers/docs/resources/guild#get-guild-welcome-screen + * @param guild_id The guild ID to get the welcome screen from + * @return dpp::welcome_screen returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +dpp::welcome_screen guild_get_welcome_screen_sync(snowflake guild_id); + +/** + * @brief Edit the guild's welcome screen + * + * Requires the `MANAGE_GUILD` permission. May fire a `Guild Update` Gateway event. + * + * @see dpp::cluster::guild_edit_welcome_screen + * @see https://discord.com/developers/docs/resources/guild#modify-guild-welcome-screen + * @param guild_id The guild ID to edit the welcome screen for + * @param welcome_screen The welcome screen + * @param enabled Whether the welcome screen should be enabled or disabled + * @return dpp::welcome_screen returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +dpp::welcome_screen guild_edit_welcome_screen_sync(snowflake guild_id, const struct welcome_screen& welcome_screen, bool enabled); + +/** + * @brief Add guild member. Needs a specific oauth2 scope, from which you get the access_token. + * + * Adds a user to the guild, provided you have a valid oauth2 access token for the user with the guilds.join scope. + * Returns the guild_member, which is defaulted if the user is already a member of the guild. Fires a `Guild Member Add` Gateway event. + * + * For guilds with Membership Screening enabled, this endpoint will default to adding new members as pending in the guild member object. + * Members that are pending will have to complete membership screening before they become full members that can talk. + * + * @note All parameters to this endpoint except for access_token are optional. + * The bot must be a member of the guild with `CREATE_INSTANT_INVITE` permission. + * @see dpp::cluster::guild_add_member + * @see https://discord.com/developers/docs/resources/guild#add-guild-member + * @param gm Guild member to add + * @param access_token Access token from Oauth2 scope + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation guild_add_member_sync(const guild_member& gm, const std::string &access_token); + +/** + * @brief Edit the properties of an existing guild member + * + * Modify attributes of a guild member. Returns the guild_member. Fires a `Guild Member Update` Gateway event. + * To remove a timeout, set the `communication_disabled_until` to a non-zero time in the past, e.g. 1. + * When moving members to channels, the API user must have permissions to both connect to the channel and have the `MOVE_MEMBERS` permission. + * For moving and disconnecting users from voice, use dpp::cluster::guild_member_move. + * @see dpp::cluster::guild_edit_member + * @see https://discord.com/developers/docs/resources/guild#modify-guild-member + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param gm Guild member to edit + * @return guild_member returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +guild_member guild_edit_member_sync(const guild_member& gm); + +/** + * @brief Get a guild member + * @see dpp::cluster::guild_get_member + * @see https://discord.com/developers/docs/resources/guild#get-guild-member + * @param guild_id Guild ID to get member for + * @param user_id User ID of member to get + * @return guild_member returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +guild_member guild_get_member_sync(snowflake guild_id, snowflake user_id); + +/** + * @brief Get all guild members + * + * @note This endpoint is restricted according to whether the `GUILD_MEMBERS` Privileged Intent is enabled for your application. + * @see dpp::cluster::guild_get_members + * @see https://discord.com/developers/docs/resources/guild#get-guild-members + * @param guild_id Guild ID to get all members for + * @param limit max number of members to return (1-1000) + * @param after the highest user id in the previous page + * @return guild_member_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +guild_member_map guild_get_members_sync(snowflake guild_id, uint16_t limit, snowflake after); + +/** + * @brief Add role to guild member + * + * Adds a role to a guild member. Requires the `MANAGE_ROLES` permission. + * Fires a `Guild Member Update` Gateway event. + * @see dpp::cluster::guild_member_add_role + * @see https://discord.com/developers/docs/resources/guild#add-guild-member-role + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to add a role to + * @param user_id User ID to add role to + * @param role_id Role ID to add to the user + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation guild_member_add_role_sync(snowflake guild_id, snowflake user_id, snowflake role_id); + +/** + * @brief Remove (kick) a guild member + * + * Remove a member from a guild. Requires `KICK_MEMBERS` permission. + * Fires a `Guild Member Remove` Gateway event. + * @see dpp::cluster::guild_member_delete + * @see https://discord.com/developers/docs/resources/guild#remove-guild-member + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @deprecated Replaced by dpp::cluster::guild_member_kick + * @param guild_id Guild ID to kick member from + * @param user_id User ID to kick + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation guild_member_delete_sync(snowflake guild_id, snowflake user_id); + +/** + * @brief Remove (kick) a guild member + * + * Remove a member from a guild. Requires `KICK_MEMBERS` permission. + * Fires a `Guild Member Remove` Gateway event. + * @see dpp::cluster::guild_member_kick + * @see https://discord.com/developers/docs/resources/guild#remove-guild-member + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to kick member from + * @param user_id User ID to kick + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation guild_member_kick_sync(snowflake guild_id, snowflake user_id); + +/** + * @brief Set the timeout of a guild member + * + * Fires a `Guild Member Update` Gateway event. + * @see dpp::cluster::guild_member_timeout + * @see https://discord.com/developers/docs/resources/guild#modify-guild-member + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to timeout the member in + * @param user_id User ID to set the timeout for + * @param communication_disabled_until The timestamp when the user's timeout will expire (up to 28 days in the future). Set to 0 to remove the timeout + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation guild_member_timeout_sync(snowflake guild_id, snowflake user_id, time_t communication_disabled_until); + +/** + * @brief Remove role from guild member + * + * Removes a role from a guild member. Requires the `MANAGE_ROLES` permission. + * Fires a `Guild Member Update` Gateway event. + * @see dpp::cluster::guild_member_delete_role + * @see https://discord.com/developers/docs/resources/guild#remove-guild-member-role + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to remove role from user on + * @param user_id User ID to remove role from + * @param role_id Role to remove + * @return confirmation returned object on completion + * @deprecated Use dpp::cluster::guild_member_remove_role instead + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation guild_member_delete_role_sync(snowflake guild_id, snowflake user_id, snowflake role_id); + +/** + * @brief Remove role from guild member + * + * Removes a role from a guild member. Requires the `MANAGE_ROLES` permission. + * Fires a `Guild Member Update` Gateway event. + * @see dpp::cluster::guild_member_remove_role + * @see https://discord.com/developers/docs/resources/guild#remove-guild-member-role + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to remove role from user on + * @param user_id User ID to remove role from + * @param role_id Role to remove + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation guild_member_remove_role_sync(snowflake guild_id, snowflake user_id, snowflake role_id); + +/** + * @brief Moves the guild member to a other voice channel, if member is connected to one. + * Set the `channel_id` to `0` to disconnect the user. + * + * Fires a `Guild Member Update` Gateway event. + * @note When moving members to channels, the API user __must__ have permissions to both connect to the channel and have the `MOVE_MEMBERS` permission. + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::guild_member_move + * @see https://discord.com/developers/docs/resources/guild#modify-guild-member + * @param channel_id Id of the channel to which the user is used. Set to `0` to disconnect the user + * @param guild_id Guild id to which the user is connected + * @param user_id User id, who should be moved + * @return guild_member returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +guild_member guild_member_move_sync(const snowflake channel_id, const snowflake guild_id, const snowflake user_id); + +/** + * @brief Search for guild members based on whether their username or nickname starts with the given string. + * + * @note This endpoint is restricted according to whether the `GUILD_MEMBERS` Privileged Intent is enabled for your application. + * @see dpp::cluster::guild_search_members + * @see https://discord.com/developers/docs/resources/guild#search-guild-members + * @param guild_id Guild ID to search in + * @param query Query string to match username(s) and nickname(s) against + * @param limit max number of members to return (1-1000) + * @return guild_member_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +guild_member_map guild_search_members_sync(snowflake guild_id, const std::string& query, uint16_t limit); + +/** + * @brief Get guild invites + * + * Returns a list of invite objects (with invite metadata) for the guild. Requires the `MANAGE_GUILD` permission. + * + * @see dpp::cluster::guild_get_invites + * @see https://discord.com/developers/docs/resources/guild#get-guild-invites + * @param guild_id Guild ID to get invites for + * @return invite_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +invite_map guild_get_invites_sync(snowflake guild_id); + + +invite invite_delete_sync(const std::string &invitecode); + +/** + * @brief Get details about an invite + * + * @see dpp::cluster::invite_get + * @see https://discord.com/developers/docs/resources/invite#get-invite + * @param invite_code Invite code to get information on + * @return invite returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +invite invite_get_sync(const std::string &invite_code); + +/** + * @brief Add a reaction to a message. The reaction string must be either an `emojiname:id` or a unicode character. + * + * @see dpp::cluster::message_add_reaction + * @see https://discord.com/developers/docs/resources/channel#create-reaction + * @param m Message to add a reaction to + * @param reaction Reaction to add. Emojis should be in the form emojiname:id + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation message_add_reaction_sync(const struct message &m, const std::string &reaction); + +/** + * @brief Add a reaction to a message by id. The reaction string must be either an `emojiname:id` or a unicode character. + * + * @see dpp::cluster::message_add_reaction + * @see https://discord.com/developers/docs/topics/gateway#message-reaction-add + * @param message_id Message to add reactions to + * @param channel_id Channel to add reactions to + * @param reaction Reaction to add. Emojis should be in the form emojiname:id + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation message_add_reaction_sync(snowflake message_id, snowflake channel_id, const std::string &reaction); + +/** + * @brief Send a message to a channel. The callback function is called when the message has been sent + * + * @see dpp::cluster::message_create + * @see https://discord.com/developers/docs/resources/channel#create-message + * @param m Message to send + * @return message returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +message message_create_sync(const struct message &m); + +/** + * @brief Crosspost a message. The callback function is called when the message has been sent + * + * @see dpp::cluster::message_crosspost + * @see https://discord.com/developers/docs/resources/channel#crosspost-message + * @param message_id Message to crosspost + * @param channel_id Channel ID to crosspost from + * @return message returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +message message_crosspost_sync(snowflake message_id, snowflake channel_id); + +/** + * @brief Delete all reactions on a message + * + * @see dpp::cluster::message_delete_all_reactions + * @see https://discord.com/developers/docs/resources/channel#delete-all-reactions + * @param m Message to delete reactions from + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation message_delete_all_reactions_sync(const struct message &m); + +/** + * @brief Delete all reactions on a message by id + * + * @see dpp::cluster::message_delete_all_reactions + * @see https://discord.com/developers/docs/resources/channel#delete-all-reactions + * @param message_id Message to delete reactions from + * @param channel_id Channel to delete reactions from + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation message_delete_all_reactions_sync(snowflake message_id, snowflake channel_id); + +/** + * @brief Bulk delete messages from a channel. The callback function is called when the message has been edited + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * + * @note If any message provided older than 2 weeks or any duplicate message ID, it will fail. + * + * @see dpp::cluster::message_delete_bulk + * @see https://discord.com/developers/docs/resources/channel#bulk-delete-messages + * @param message_ids List of message IDs to delete (at least 2 and at most 100 message IDs) + * @param channel_id Channel to delete from + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation message_delete_bulk_sync(const std::vector &message_ids, snowflake channel_id); + +/** + * @brief Delete a message from a channel. The callback function is called when the message has been edited + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * + * @see dpp::cluster::message_delete + * @see https://discord.com/developers/docs/resources/channel#delete-message + * @param message_id Message ID to delete + * @param channel_id Channel to delete from + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation message_delete_sync(snowflake message_id, snowflake channel_id); + +/** + * @brief Delete own reaction from a message. The reaction string must be either an `emojiname:id` or a unicode character. + * + * @see dpp::cluster::message_delete_own_reaction + * @see https://discord.com/developers/docs/resources/channel#delete-own-reaction + * @param m Message to delete own reaction from + * @param reaction Reaction to delete. The reaction should be in the form emojiname:id + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation message_delete_own_reaction_sync(const struct message &m, const std::string &reaction); + +/** + * @brief Delete own reaction from a message by id. The reaction string must be either an `emojiname:id` or a unicode character. + * + * @see dpp::cluster::message_delete_own_reaction + * @see https://discord.com/developers/docs/resources/channel#delete-own-reaction + * @param message_id Message to delete reactions from + * @param channel_id Channel to delete reactions from + * @param reaction Reaction to delete. The reaction should be in the form emojiname:id + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation message_delete_own_reaction_sync(snowflake message_id, snowflake channel_id, const std::string &reaction); + +/** + * @brief Delete a user's reaction from a message. The reaction string must be either an `emojiname:id` or a unicode character + * + * @see dpp::cluster::message_delete_reaction + * @see https://discord.com/developers/docs/resources/channel#delete-user-reaction + * @param m Message to delete a user's reaction from + * @param user_id User ID who's reaction you want to remove + * @param reaction Reaction to remove. Reactions should be in the form emojiname:id + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation message_delete_reaction_sync(const struct message &m, snowflake user_id, const std::string &reaction); + +/** + * @brief Delete a user's reaction from a message by id. The reaction string must be either an `emojiname:id` or a unicode character + * + * @see dpp::cluster::message_delete_reaction + * @see https://discord.com/developers/docs/resources/channel#delete-user-reaction + * @param message_id Message to delete reactions from + * @param channel_id Channel to delete reactions from + * @param user_id User ID who's reaction you want to remove + * @param reaction Reaction to remove. Reactions should be in the form emojiname:id + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation message_delete_reaction_sync(snowflake message_id, snowflake channel_id, snowflake user_id, const std::string &reaction); + +/** + * @brief Delete all reactions on a message using a particular emoji. The reaction string must be either an `emojiname:id` or a unicode character + * + * @see dpp::cluster::message_delete_reaction_emoji + * @see https://discord.com/developers/docs/resources/channel#delete-all-reactions-for-emoji + * @param m Message to delete reactions from + * @param reaction Reaction to delete, in the form emojiname:id or a unicode character + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation message_delete_reaction_emoji_sync(const struct message &m, const std::string &reaction); + +/** + * @brief Delete all reactions on a message using a particular emoji by id. The reaction string must be either an `emojiname:id` or a unicode character + * + * @see dpp::cluster::message_delete_reaction_emoji + * @see https://discord.com/developers/docs/resources/channel#delete-all-reactions-for-emoji + * @param message_id Message to delete reactions from + * @param channel_id Channel to delete reactions from + * @param reaction Reaction to delete, in the form emojiname:id or a unicode character + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation message_delete_reaction_emoji_sync(snowflake message_id, snowflake channel_id, const std::string &reaction); + +/** + * @brief Edit a message on a channel. The callback function is called when the message has been edited + * + * @see dpp::cluster::message_edit + * @see https://discord.com/developers/docs/resources/channel#edit-message + * @param m Message to edit + * @return message returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +message message_edit_sync(const struct message &m); + +/** + * @brief Get a message + * + * @see dpp::cluster::message_get + * @see https://discord.com/developers/docs/resources/channel#get-channel-message + * @param message_id Message ID + * @param channel_id Channel ID + * @return message returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +message message_get_sync(snowflake message_id, snowflake channel_id); + +/** + * @brief Get reactions on a message for a particular emoji. The reaction string must be either an `emojiname:id` or a unicode character + * + * @see dpp::cluster::message_get_reactions + * @see https://discord.com/developers/docs/resources/channel#get-reactions + * @param m Message to get reactions for + * @param reaction Reaction should be in the form emojiname:id or a unicode character + * @param before Reactions before this ID should be retrieved if this is set to non-zero + * @param after Reactions before this ID should be retrieved if this is set to non-zero + * @param limit This number of reactions maximum should be returned + * @return user_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +user_map message_get_reactions_sync(const struct message &m, const std::string &reaction, snowflake before, snowflake after, snowflake limit); + +/** + * @brief Get reactions on a message for a particular emoji by id. The reaction string must be either an `emojiname:id` or a unicode character + * + * @see dpp::cluster::message_get_reactions + * @see https://discord.com/developers/docs/resources/channel#get-reactions + * @param message_id Message to get reactions for + * @param channel_id Channel to get reactions for + * @param reaction Reaction should be in the form emojiname:id or a unicode character + * @param before Reactions before this ID should be retrieved if this is set to non-zero + * @param after Reactions before this ID should be retrieved if this is set to non-zero + * @param limit This number of reactions maximum should be returned + * @return emoji_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +emoji_map message_get_reactions_sync(snowflake message_id, snowflake channel_id, const std::string &reaction, snowflake before, snowflake after, snowflake limit); + +/** + * @brief Pin a message + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::message_pin + * @see https://discord.com/developers/docs/resources/channel#pin-message + * @param channel_id Channel id to pin message on + * @param message_id Message id to pin message on + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation message_pin_sync(snowflake channel_id, snowflake message_id); + +/** + * @brief Get multiple messages. + * + * This function will attempt to fetch as many messages as possible using multiple API calls if needed. + * + * @see dpp::cluster::messages_get + * @see https://discord.com/developers/docs/resources/channel#get-channel-messages + * @param channel_id Channel ID to retrieve messages for + * @param around Messages should be retrieved around this ID if this is set to non-zero + * @param before Messages before this ID should be retrieved if this is set to non-zero + * @param after Messages after this ID should be retrieved if this is set to non-zero + * @param limit This number of messages maximum should be returned, up to a maximum of 100. + * @return message_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +message_map messages_get_sync(snowflake channel_id, snowflake around, snowflake before, snowflake after, uint64_t limit); + +/** + * @brief Unpin a message + * @see dpp::cluster::message_unpin + * @see https://discord.com/developers/docs/resources/channel#unpin-message + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param channel_id Channel id to unpin message on + * @param message_id Message id to unpin message on + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation message_unpin_sync(snowflake channel_id, snowflake message_id); + +/** + * @brief Get a channel's pins + * @see dpp::cluster::channel_pins_get + * @see https://discord.com/developers/docs/resources/channel#get-pinned-messages + * @param channel_id Channel ID to get pins for + * @return message_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +message_map channel_pins_get_sync(snowflake channel_id); + +/** + * @brief Create a role on a guild + * + * Create a new role for the guild. Requires the `MANAGE_ROLES` permission. Returns the new role object on success. + * Fires a `Guild Role Create` Gateway event. + * + * @see dpp::cluster::role_create + * @see https://discord.com/developers/docs/resources/guild#create-guild-role + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param r Role to create (guild ID is encapsulated in the role object) + * @return role returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +role role_create_sync(const class role &r); + +/** + * @brief Delete a role + * + * Requires the `MANAGE_ROLES` permission. Fires a `Guild Role Delete` Gateway event. + * + * @see dpp::cluster::role_delete + * @see https://discord.com/developers/docs/resources/guild#delete-guild-role + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to delete the role on + * @param role_id Role ID to delete + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation role_delete_sync(snowflake guild_id, snowflake role_id); + +/** + * @brief Edit a role on a guild + * + * Requires the `MANAGE_ROLES` permission. Returns the updated role on success. Fires a `Guild Role Update` Gateway event. + * + * @see dpp::cluster::role_edit + * @see https://discord.com/developers/docs/resources/guild#modify-guild-role + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param r Role to edit + * @return role returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +role role_edit_sync(const class role &r); + +/** + * @brief Edit multiple role's position in a guild. Returns a list of all roles of the guild on success. + * + * Modify the positions of a set of role objects for the guild. Requires the `MANAGE_ROLES` permission. + * Fires multiple `Guild Role Update` Gateway events. + * + * @see dpp::cluster::roles_edit_position + * @see https://discord.com/developers/docs/resources/guild#modify-guild-role-positions + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @param guild_id Guild ID to change the roles position on + * @param roles Vector of roles to change the positions of + * @return role_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +role_map roles_edit_position_sync(snowflake guild_id, const std::vector &roles); + +/** + * @brief Get a role for a guild + * + * @see dpp::cluster::roles_get + * @see https://discord.com/developers/docs/resources/guild#get-guild-roles + * @param guild_id Guild ID to get role for + * @return role_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +role_map roles_get_sync(snowflake guild_id); + +/** + * @brief Get the application's role connection metadata records + * + * @see dpp::cluster::application_role_connection_get + * @see https://discord.com/developers/docs/resources/application-role-connection-metadata#get-application-role-connection-metadata-records + * @param application_id The application ID + * @return application_role_connection returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +application_role_connection application_role_connection_get_sync(snowflake application_id); + +/** + * @brief Update the application's role connection metadata records + * + * @see dpp::cluster::application_role_connection_update + * @see https://discord.com/developers/docs/resources/application-role-connection-metadata#update-application-role-connection-metadata-records + * @param application_id The application ID + * @param connection_metadata The application role connection metadata to update + * @return application_role_connection returned object on completion + * @note An application can have a maximum of 5 metadata records. + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +application_role_connection application_role_connection_update_sync(snowflake application_id, const std::vector &connection_metadata); + +/** + * @brief Get user application role connection + * + * @see dpp::cluster::user_application_role_connection_get + * @see https://discord.com/developers/docs/resources/user#get-user-application-role-connection + * @param application_id The application ID + * @return application_role_connection returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +application_role_connection user_application_role_connection_get_sync(snowflake application_id); + +/** + * @brief Update user application role connection + * + * @see dpp::cluster::user_application_role_connection_update + * @see https://discord.com/developers/docs/resources/user#update-user-application-role-connection + * @param application_id The application ID + * @param connection The application role connection to update + * @return application_role_connection returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +application_role_connection user_application_role_connection_update_sync(snowflake application_id, const application_role_connection &connection); + +/** + * @brief Get all scheduled events for a guild + * @see dpp::cluster::guild_events_get + * @see https://discord.com/developers/docs/resources/guild-scheduled-event#list-scheduled-events-for-guild + * @param guild_id Guild to get events for + * @return scheduled_event_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +scheduled_event_map guild_events_get_sync(snowflake guild_id); + +/** + * @brief Create a scheduled event on a guild + * + * @see dpp::cluster::guild_event_create + * @see https://discord.com/developers/docs/resources/guild-scheduled-event#create-guild-scheduled-event + * @param event Event to create (guild ID must be populated) + * @return scheduled_event returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +scheduled_event guild_event_create_sync(const scheduled_event& event); + +/** + * @brief Delete a scheduled event from a guild + * + * @see dpp::cluster::guild_event_delete + * @see https://discord.com/developers/docs/resources/guild-scheduled-event#delete-guild-scheduled-event + * @param event_id Event ID to delete + * @param guild_id Guild ID of event to delete + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation guild_event_delete_sync(snowflake event_id, snowflake guild_id); + +/** + * @brief Edit/modify a scheduled event on a guild + * + * @see dpp::cluster::guild_event_edit + * @see https://discord.com/developers/docs/resources/guild-scheduled-event#modify-guild-scheduled-event + * @param event Event to create (event ID and guild ID must be populated) + * @return scheduled_event returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +scheduled_event guild_event_edit_sync(const scheduled_event& event); + +/** + * @brief Get a scheduled event for a guild + * + * @see dpp::cluster::guild_event_get + * @see https://discord.com/developers/docs/resources/guild-scheduled-event#get-guild-scheduled-event + * @param guild_id Guild to get event for + * @param event_id Event ID to get + * @return scheduled_event returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +scheduled_event guild_event_get_sync(snowflake guild_id, snowflake event_id); + + +stage_instance stage_instance_create_sync(const stage_instance& si); + +/** + * @brief Get the stage instance associated with the channel id, if it exists. + * @see dpp::cluster::stage_instance_get + * @see https://discord.com/developers/docs/resources/stage-instance#get-stage-instance + * @param channel_id ID of the associated channel + * @return stage_instance returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +stage_instance stage_instance_get_sync(const snowflake channel_id); + + +stage_instance stage_instance_edit_sync(const stage_instance& si); + +/** + * @brief Delete a stage instance. + * @see dpp::cluster::stage_instance_delete + * @see https://discord.com/developers/docs/resources/stage-instance#delete-stage-instance + * @param channel_id ID of the associated channel + * @return confirmation returned object on completion + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation stage_instance_delete_sync(const snowflake channel_id); + +/** + * @brief Create a sticker in a guild + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::guild_sticker_create + * @see https://discord.com/developers/docs/resources/sticker#create-guild-sticker + * @param s Sticker to create. Must have its guild ID set. + * @return sticker returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +sticker guild_sticker_create_sync(const sticker &s); + +/** + * @brief Delete a sticker from a guild + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::guild_sticker_delete + * @see https://discord.com/developers/docs/resources/sticker#delete-guild-sticker + * @param sticker_id sticker ID to delete + * @param guild_id guild ID to delete from + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation guild_sticker_delete_sync(snowflake sticker_id, snowflake guild_id); + +/** + * @brief Get a guild sticker + * @see dpp::cluster::guild_sticker_get + * @see https://discord.com/developers/docs/resources/sticker#get-guild-sticker + * @param id Id of sticker to get. + * @param guild_id Guild ID of the guild where the sticker is + * @return sticker returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +sticker guild_sticker_get_sync(snowflake id, snowflake guild_id); + +/** + * @brief Modify a sticker in a guild + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::guild_sticker_modify + * @see https://discord.com/developers/docs/resources/sticker#modify-guild-sticker + * @param s Sticker to modify. Must have its guild ID and sticker ID set. + * @return sticker returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +sticker guild_sticker_modify_sync(const sticker &s); + +/** + * @brief Get all guild stickers + * @see dpp::cluster::guild_stickers_get + * @see https://discord.com/developers/docs/resources/sticker#get-guild-stickers + * @param guild_id Guild ID of the guild where the sticker is + * @return sticker_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +sticker_map guild_stickers_get_sync(snowflake guild_id); + +/** + * @brief Get a nitro sticker + * @see dpp::cluster::nitro_sticker_get + * @see https://discord.com/developers/docs/resources/sticker#get-sticker + * @param id Id of sticker to get. + * @return sticker returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +sticker nitro_sticker_get_sync(snowflake id); + +/** + * @brief Get a list of available sticker packs + * @see dpp::cluster::sticker_packs_get + * @see https://discord.com/developers/docs/resources/sticker#list-nitro-sticker-packs + * @return sticker_pack_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +sticker_pack_map sticker_packs_get_sync(); + +/** + * @brief Create a new guild based on a template. + * @note This endpoint can be used only by bots in less than 10 guilds. + * @see dpp::cluster::guild_create_from_template + * @see https://discord.com/developers/docs/resources/guild-template#create-guild-from-guild-template + * @param code Template code to create guild from + * @param name Guild name to create + * @return guild returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +guild guild_create_from_template_sync(const std::string &code, const std::string &name); + +/** + * @brief Creates a template for the guild + * + * @see dpp::cluster::guild_template_create + * @see https://discord.com/developers/docs/resources/guild-template#create-guild-template + * @param guild_id Guild to create template from + * @param name Template name to create + * @param description Description of template to create + * @return dtemplate returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +dtemplate guild_template_create_sync(snowflake guild_id, const std::string &name, const std::string &description); + +/** + * @brief Deletes the template + * + * @see dpp::cluster::guild_template_delete + * @see https://discord.com/developers/docs/resources/guild-template#delete-guild-template + * @param guild_id Guild ID of template to delete + * @param code Template code to delete + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation guild_template_delete_sync(snowflake guild_id, const std::string &code); + +/** + * @brief Modifies the template's metadata. + * + * @see dpp::cluster::guild_template_modify + * @see https://discord.com/developers/docs/resources/guild-template#modify-guild-template + * @param guild_id Guild ID of template to modify + * @param code Template code to modify + * @param name New name of template + * @param description New description of template + * @return dtemplate returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +dtemplate guild_template_modify_sync(snowflake guild_id, const std::string &code, const std::string &name, const std::string &description); + +/** + * @brief Get guild templates + * + * @see dpp::cluster::guild_templates_get + * @see https://discord.com/developers/docs/resources/guild-template#get-guild-templates + * @param guild_id Guild ID to get templates for + * @return dtemplate_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +dtemplate_map guild_templates_get_sync(snowflake guild_id); + +/** + * @brief Syncs the template to the guild's current state. + * + * @see dpp::cluster::guild_template_sync + * @see https://discord.com/developers/docs/resources/guild-template#sync-guild-template + * @param guild_id Guild to synchronise template for + * @param code Code of template to synchronise + * @return dtemplate returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +dtemplate guild_template_sync_sync(snowflake guild_id, const std::string &code); + +/** + * @brief Get a template + * @see dpp::cluster::template_get + * @see https://discord.com/developers/docs/resources/guild-template#get-guild-template + * @param code Template code + * @return dtemplate returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +dtemplate template_get_sync(const std::string &code); + +/** + * @brief Join a thread + * @see dpp::cluster::current_user_join_thread + * @see https://discord.com/developers/docs/resources/channel#join-thread + * @param thread_id Thread ID to join + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation current_user_join_thread_sync(snowflake thread_id); + +/** + * @brief Leave a thread + * @see dpp::cluster::current_user_leave_thread + * @see https://discord.com/developers/docs/resources/channel#leave-thread + * @param thread_id Thread ID to leave + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation current_user_leave_thread_sync(snowflake thread_id); + +/** + * @brief Get all active threads in the guild, including public and private threads. Threads are ordered by their id, in descending order. + * @see dpp::cluster::threads_get_active + * @see https://discord.com/developers/docs/resources/guild#list-active-guild-threads + * @param guild_id Guild to get active threads for + * @return active_threads returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +active_threads threads_get_active_sync(snowflake guild_id); + +/** + * @brief Get private archived threads in a channel which current user has joined (Sorted by ID in descending order) + * @see dpp::cluster::threads_get_joined_private_archived + * @see https://discord.com/developers/docs/resources/channel#list-joined-private-archived-threads + * @param channel_id Channel to get public archived threads for + * @param before_id Get threads before this id + * @param limit Number of threads to get + * @return thread_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +thread_map threads_get_joined_private_archived_sync(snowflake channel_id, snowflake before_id, uint16_t limit); + +/** + * @brief Get private archived threads in a channel (Sorted by archive_timestamp in descending order) + * @see dpp::cluster::threads_get_private_archived + * @see https://discord.com/developers/docs/resources/channel#list-private-archived-threads + * @param channel_id Channel to get public archived threads for + * @param before_timestamp Get threads archived before this timestamp + * @param limit Number of threads to get + * @return thread_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +thread_map threads_get_private_archived_sync(snowflake channel_id, time_t before_timestamp, uint16_t limit); + +/** + * @brief Get public archived threads in a channel (Sorted by archive_timestamp in descending order) + * @see dpp::cluster::threads_get_public_archived + * @see https://discord.com/developers/docs/resources/channel#list-public-archived-threads + * @param channel_id Channel to get public archived threads for + * @param before_timestamp Get threads archived before this timestamp + * @param limit Number of threads to get + * @return thread_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +thread_map threads_get_public_archived_sync(snowflake channel_id, time_t before_timestamp, uint16_t limit); + +/** + * @brief Get a thread member + * @see dpp::cluster::thread_member_get + * @see https://discord.com/developers/docs/resources/channel#get-thread-member + * @param thread_id Thread to get member for + * @param user_id ID of the user to get + * @return thread_member returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +thread_member thread_member_get_sync(const snowflake thread_id, const snowflake user_id); + +/** + * @brief Get members of a thread + * @see dpp::cluster::thread_members_get + * @see https://discord.com/developers/docs/resources/channel#list-thread-members + * @param thread_id Thread to get members for + * @return thread_member_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +thread_member_map thread_members_get_sync(snowflake thread_id); + +/** + * @brief Create a thread in a forum or media channel + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * + * @see dpp::cluster::thread_create_in_forum + * @see https://discord.com/developers/docs/resources/channel#start-thread-in-forum-channel + * @param thread_name Name of the forum thread + * @param channel_id Forum channel in which thread to create + * @param msg The message to start the thread with + * @param auto_archive_duration Duration to automatically archive the thread after recent activity + * @param rate_limit_per_user amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission manage_messages, manage_thread, or manage_channel, are unaffected + * @param applied_tags List of IDs of forum tags (dpp::forum_tag) to apply to this thread + * @return thread returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +thread thread_create_in_forum_sync(const std::string& thread_name, snowflake channel_id, const message& msg, auto_archive_duration_t auto_archive_duration, uint16_t rate_limit_per_user, std::vector applied_tags = {}); + +/** + * @brief Create a thread + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * + * @see dpp::cluster::thread_create + * @see https://discord.com/developers/docs/resources/channel#start-thread-without-message + * @param thread_name Name of the thread + * @param channel_id Channel in which thread to create + * @param auto_archive_duration Duration after which thread auto-archives. Can be set to - 60, 1440 (for boosted guilds can also be: 4320, 10080) + * @param thread_type Type of thread - CHANNEL_PUBLIC_THREAD, CHANNEL_ANNOUNCEMENT_THREAD, CHANNEL_PRIVATE_THREAD + * @param invitable whether non-moderators can add other non-moderators to a thread; only available when creating a private thread + * @param rate_limit_per_user amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission manage_messages, manage_thread, or manage_channel, are unaffected + * @return thread returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +thread thread_create_sync(const std::string& thread_name, snowflake channel_id, uint16_t auto_archive_duration, channel_type thread_type, bool invitable, uint16_t rate_limit_per_user); + +/** + * @brief Edit a thread + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * + * @see dpp::cluster::thread_edit + * @see https://discord.com/developers/docs/topics/threads#editing-deleting-threads + * @param t Thread to edit + * @return thread returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +thread thread_edit_sync(const thread &t); + +/** + * @brief Create a thread with a message (Discord: ID of a thread is same as message ID) + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::thread_create_with_message + * @see https://discord.com/developers/docs/resources/channel#start-thread-from-message + * @param thread_name Name of the thread + * @param channel_id Channel in which thread to create + * @param message_id message to start thread with + * @param auto_archive_duration Duration after which thread auto-archives. Can be set to - 60, 1440 (for boosted guilds can also be: 4320, 10080) + * @param rate_limit_per_user amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission manage_messages, manage_thread, or manage_channel, are unaffected + * @return thread returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +thread thread_create_with_message_sync(const std::string& thread_name, snowflake channel_id, snowflake message_id, uint16_t auto_archive_duration, uint16_t rate_limit_per_user); + +/** + * @brief Add a member to a thread + * @see dpp::cluster::thread_member_add + * @see https://discord.com/developers/docs/resources/channel#add-thread-member + * @param thread_id Thread ID to add to + * @param user_id Member ID to add + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation thread_member_add_sync(snowflake thread_id, snowflake user_id); + +/** + * @brief Remove a member from a thread + * @see dpp::cluster::thread_member_remove + * @see https://discord.com/developers/docs/resources/channel#remove-thread-member + * @param thread_id Thread ID to remove from + * @param user_id Member ID to remove + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation thread_member_remove_sync(snowflake thread_id, snowflake user_id); + +/** + * @brief Edit current (bot) user + * + * Modifies the current member in a guild. Returns the updated guild_member object on success. + * Fires a `Guild Member Update` Gateway event. + * @see dpp::cluster::current_user_edit + * @see https://discord.com/developers/docs/resources/user#modify-current-user + * @param nickname Nickname to set + * @param image_blob Avatar data to upload (NOTE: Very heavily rate limited!) + * @param type Type of image for avatar. It can be one of `i_gif`, `i_jpg` or `i_png`. + * @return user returned object on completion + * @throw dpp::length_exception Image data is larger than the maximum size of 256 kilobytes + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +user current_user_edit_sync(const std::string &nickname, const std::string& image_blob = "", const image_type type = i_png); + +/** + * @brief Get current (bot) application + * + * @see dpp::cluster::current_application_get + * @see https://discord.com/developers/docs/topics/oauth2#get-current-bot-application-information + * @return application returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +application current_application_get_sync(); + +/** + * @brief Get current (bot) user + * + * @see dpp::cluster::current_user_get + * @see https://discord.com/developers/docs/resources/user#get-current-user + * @return user_identified returned object on completion + * @note The user_identified object is a subclass of dpp::user which contains further details if you have the oauth2 identify or email scopes. + * If you do not have these scopes, these fields are empty. You can safely convert a user_identified to user with `dynamic_cast`. + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +user_identified current_user_get_sync(); + +/** + * @brief Set the bot's voice state on a stage channel + * + * **Caveats** + * + * There are currently several caveats for this endpoint: + * + * - `channel_id` must currently point to a stage channel. + * - current user must already have joined `channel_id`. + * - You must have the `MUTE_MEMBERS` permission to unsuppress yourself. You can always suppress yourself. + * - You must have the `REQUEST_TO_SPEAK` permission to request to speak. You can always clear your own request to speak. + * - You are able to set `request_to_speak_timestamp` to any present or future time. + * + * @see dpp::cluster::current_user_set_voice_state + * @see https://discord.com/developers/docs/resources/guild#modify-current-user-voice-state + * @param guild_id Guild to set voice state on + * @param channel_id Stage channel to set voice state on + * @return confirmation returned object on completion + * @param suppress True if the user's audio should be suppressed, false if it should not + * @param request_to_speak_timestamp The time at which we requested to speak, or 0 to clear the request. The time set here must be the current time or in the future. + * @throw std::logic_exception You attempted to set a request_to_speak_timestamp in the past which is not the value of 0. + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation current_user_set_voice_state_sync(snowflake guild_id, snowflake channel_id, bool suppress = false, time_t request_to_speak_timestamp = 0); + +/** + * @brief Set a user's voice state on a stage channel + * + * **Caveats** + * + * There are currently several caveats for this endpoint: + * + * - `channel_id` must currently point to a stage channel. + * - User must already have joined `channel_id`. + * - You must have the `MUTE_MEMBERS` permission. (Since suppression is the only thing that is available currently) + * - When unsuppressed, non-bot users will have their `request_to_speak_timestamp` set to the current time. Bot users will not. + * - When suppressed, the user will have their `request_to_speak_timestamp` removed. + * + * @see dpp::cluster::user_set_voice_state + * @see https://discord.com/developers/docs/resources/guild#modify-user-voice-state + * @param user_id The user to set the voice state of + * @param guild_id Guild to set voice state on + * @param channel_id Stage channel to set voice state on + * @return confirmation returned object on completion + * @param suppress True if the user's audio should be suppressed, false if it should not + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation user_set_voice_state_sync(snowflake user_id, snowflake guild_id, snowflake channel_id, bool suppress = false); + +/** + * @brief Get current user's connections (linked accounts, e.g. steam, xbox). + * This call requires the oauth2 `connections` scope and cannot be executed + * against a bot token. + * @see dpp::cluster::current_user_connections_get + * @see https://discord.com/developers/docs/resources/user#get-user-connections + * @return connection_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +connection_map current_user_connections_get_sync(); + +/** + * @brief Get current (bot) user guilds + * @see dpp::cluster::current_user_get_guilds + * @see https://discord.com/developers/docs/resources/user#get-current-user-guilds + * @return guild_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +guild_map current_user_get_guilds_sync(); + +/** + * @brief Leave a guild + * @see dpp::cluster::current_user_leave_guild + * @see https://discord.com/developers/docs/resources/user#leave-guild + * @param guild_id Guild ID to leave + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation current_user_leave_guild_sync(snowflake guild_id); + +/** + * @brief Get a user by id, without using the cache + * + * @see dpp::cluster::user_get + * @see https://discord.com/developers/docs/resources/user#get-user + * @param user_id User ID to retrieve + * @return user_identified returned object on completion + * @note The user_identified object is a subclass of dpp::user which contains further details if you have the oauth2 identify or email scopes. + * If you do not have these scopes, these fields are empty. You can safely convert a user_identified to user with `dynamic_cast`. + * @note unless you want something special from `dpp::user_identified` or you've turned off caching, you have no need to call this. + * Call `dpp::find_user` instead that looks up the user in the cache rather than a REST call. + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +user_identified user_get_sync(snowflake user_id); + +/** + * @brief Get a user by id, checking in the cache first + * + * @see dpp::cluster::user_get_cached + * @see https://discord.com/developers/docs/resources/user#get-user + * @param user_id User ID to retrieve + * @return user_identified returned object on completion + * @note The user_identified object is a subclass of dpp::user which contains further details if you have the oauth2 identify or email scopes. + * If you do not have these scopes, these fields are empty. You can safely convert a user_identified to user with `dynamic_cast`. + * @note If the user is found in the cache, special values set in `dpp::user_identified` will be undefined. This call should be used + * where you want to for example resolve a user who may no longer be in the bot's guilds, for something like a ban log message. + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +user_identified user_get_cached_sync(snowflake user_id); + +/** + * @brief Get all voice regions + * @see dpp::cluster::get_voice_regions + * @see https://discord.com/developers/docs/resources/voice#list-voice-regions + * @return voiceregion_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +voiceregion_map get_voice_regions_sync(); + +/** + * @brief Get guild voice regions. + * + * Voice regions per guild are somewhat deprecated in preference of per-channel voice regions. + * Returns a list of voice region objects for the guild. Unlike the similar /voice route, this returns VIP servers when + * the guild is VIP-enabled. + * + * @see dpp::cluster::guild_get_voice_regions + * @see https://discord.com/developers/docs/resources/guild#get-guild-voice-regions + * @param guild_id Guild ID to get voice regions for + * @return voiceregion_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +voiceregion_map guild_get_voice_regions_sync(snowflake guild_id); + +/** + * @brief Create a webhook + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::create_webhook + * @see https://discord.com/developers/docs/resources/webhook#create-webhook + * @param w Webhook to create + * @return webhook returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +webhook create_webhook_sync(const class webhook &w); + +/** + * @brief Delete a webhook + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::delete_webhook + * @see https://discord.com/developers/docs/resources/webhook#delete-webhook + * @param webhook_id Webhook ID to delete + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation delete_webhook_sync(snowflake webhook_id); + +/** + * @brief Delete webhook message + * + * @see dpp::cluster::delete_webhook_message + * @see https://discord.com/developers/docs/resources/webhook#delete-webhook-message + * @param wh Webhook to delete message for + * @param message_id Message ID to delete + * @param thread_id ID of the thread the message is in + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation delete_webhook_message_sync(const class webhook &wh, snowflake message_id, snowflake thread_id = 0); + +/** + * @brief Delete webhook with token + * @see dpp::cluster::delete_webhook_with_token + * @see https://discord.com/developers/docs/resources/webhook#delete-webhook-with-token + * @param webhook_id Webhook ID to delete + * @param token Token of webhook to delete + * @return confirmation returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +confirmation delete_webhook_with_token_sync(snowflake webhook_id, const std::string &token); + +/** + * @brief Edit webhook + * @note This method supports audit log reasons set by the cluster::set_audit_reason() method. + * @see dpp::cluster::edit_webhook + * @see https://discord.com/developers/docs/resources/webhook#modify-webhook + * @param wh Webhook to edit + * @return webhook returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +webhook edit_webhook_sync(const class webhook& wh); + +/** + * @brief Edit webhook message + * + * When the content field is edited, the mentions array in the message object will be reconstructed from scratch based on + * the new content. The allowed_mentions field of the edit request controls how this happens. If there is no explicit + * allowed_mentions in the edit request, the content will be parsed with default allowances, that is, without regard to + * whether or not an allowed_mentions was present in the request that originally created the message. + * + * @see dpp::cluster::edit_webhook_message + * @see https://discord.com/developers/docs/resources/webhook#edit-webhook-message + * @note the attachments array must contain all attachments that should be present after edit, including retained and new attachments provided in the request body. + * @param wh Webhook to edit message for + * @param m New message + * @param thread_id ID of the thread the message is in + * @return message returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +message edit_webhook_message_sync(const class webhook &wh, const struct message &m, snowflake thread_id = 0); + +/** + * @brief Edit webhook with token (token is encapsulated in the webhook object) + * @see dpp::cluster::edit_webhook_with_token + * @see https://discord.com/developers/docs/resources/webhook#modify-webhook-with-token + * @param wh Webhook to edit (should include token) + * @return webhook returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +webhook edit_webhook_with_token_sync(const class webhook& wh); + +/** + * @brief Execute webhook + * + * @see dpp::cluster::execute_webhook + * @see https://discord.com/developers/docs/resources/webhook#execute-webhook + * @param wh Webhook to execute + * @param m Message to send + * @param wait waits for server confirmation of message send before response, and returns the created message body + * @param thread_id Send a message to the specified thread within a webhook's channel. The thread will automatically be unarchived + * @param thread_name Name of thread to create (requires the webhook channel to be a forum channel) + * @return message returned object on completion + * @note If the webhook channel is a forum channel, you must provide either `thread_id` or `thread_name`. If `thread_id` is provided, the message will send in that thread. If `thread_name` is provided, a thread with that name will be created in the forum channel. + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +message execute_webhook_sync(const class webhook &wh, const struct message &m, bool wait = false, snowflake thread_id = 0, const std::string& thread_name = ""); + +/** + * @brief Get channel webhooks + * @see dpp::cluster::get_channel_webhooks + * @see https://discord.com/developers/docs/resources/webhook#get-guild-webhooks + * @param channel_id Channel ID to get webhooks for + * @return webhook_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +webhook_map get_channel_webhooks_sync(snowflake channel_id); + +/** + * @brief Get guild webhooks + * @see dpp::cluster::get_guild_webhooks + * @see https://discord.com/developers/docs/resources/webhook#get-guild-webhooks + * @param guild_id Guild ID to get webhooks for + * @return webhook_map returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +webhook_map get_guild_webhooks_sync(snowflake guild_id); + +/** + * @brief Get webhook + * @see dpp::cluster::get_webhook + * @see https://discord.com/developers/docs/resources/webhook#get-webhook + * @param webhook_id Webhook ID to get + * @return webhook returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +webhook get_webhook_sync(snowflake webhook_id); + +/** + * @brief Get webhook message + * + * @see dpp::cluster::get_webhook_message + * @see https://discord.com/developers/docs/resources/webhook#get-webhook-message + * @param wh Webhook to get the original message for + * @param message_id The message ID + * @param thread_id ID of the thread the message is in + * @return message returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +message get_webhook_message_sync(const class webhook &wh, snowflake message_id, snowflake thread_id = 0); + +/** + * @brief Get webhook using token + * @see dpp::cluster::get_webhook_with_token + * @see https://discord.com/developers/docs/resources/webhook#get-webhook-with-token + * @param webhook_id Webhook ID to retrieve + * @param token Token of webhook + * @return webhook returned object on completion + * \memberof dpp::cluster + * @throw dpp::rest_exception upon failure to execute REST function + * @warning This function is a blocking (synchronous) call and should only be used from within a separate thread. + * Avoid direct use of this function inside an event handler. + */ +webhook get_webhook_with_token_sync(snowflake webhook_id, const std::string &token); + + +/* End of auto-generated definitions */ diff --git a/include/dpp/collector.h b/include/dpp/collector.h new file mode 100644 index 0000000..dbc8caf --- /dev/null +++ b/include/dpp/collector.h @@ -0,0 +1,439 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace dpp { + +/** + * @brief Collects objects from events during a specified time period. + * + * This template must be specialised. There are premade specialisations which you can use + * such as dpp::reaction_collector and dpp::message_collector. For these specialised instances + * all you need to do is derive a simple class from them which implements collector::completed(). + * + * A collector will run for the specified number of seconds, attaching itself to the + * given event. During this time any events pass through the collector and collector::filter(). + * This function can return a pointer to an object to allow a copy of that object to be stored + * to a vector, or it can return nullptr to do nothing with that object. For example a collector + * attached to on_message_create would receive an event with the type message_create_t, and from + * this may decide to extract the message_create_t::msg structure, returning a pointer to it, or + * instead may choose to return a nullptr. + * + * When either the predetermined timeout is reached, or the collector::cancel() method is called, + * or the collector is destroyed, the collector::completed() method is called, which will be + * passed a list of collected objects in the order they were collected. + * + * @tparam T parameter type of the event this collector will monitor + * @tparam C object type this collector will store + */ +template class collector +{ +protected: + /// Owning cluster + class cluster* owner; +private: + /// Timed listener + timed_listener, std::function>* tl; + /// stored list + std::vector stored; + /// Trigger flag + bool triggered; +public: + /** + * @brief Construct a new collector object. + * + * The timer for the collector begins immediately on construction of the object. + * + * @param cl Pointer to cluster which manages this collector + * @param duration Duration in seconds to run the collector for + * @param event Event to attach to, e.g. cluster::on_message_create + */ + collector(class cluster* cl, uint64_t duration, event_router_t & event) : owner(cl), triggered(false) { + std::function f = [this](const T& event) { + const C* v = filter(event); + if (v) { + stored.push_back(*v); + } + }; + tl = new dpp::timed_listener, std::function>(cl, duration, event, f, [this](dpp::timer timer_handle) { + if (!triggered) { + triggered = true; + completed(stored); + } + }); + } + + /** + * @brief You must implement this function to receive the completed list of + * captured objects. + * @param list The list of captured objects in captured order + */ + virtual void completed(const std::vector& list) = 0; + + /** + * @brief Filter the list of elements. + * + * Every time an event is fired on the collector, this method wil be called + * to determine if we should add an object to the list or not. This function + * can then process the `element` value, extract the parts which are to be + * saved to a list (e.g. a dpp::message out of a dpp::message_create_t) and + * return it as the return value. Returning a value of nullptr causes no + * object to be stored. + * + * Here is an example of how to filter messages which have specific text in them. + * This should be used with the specialised type dpp::message_collector + * + * ```cpp + * virtual const dpp::message* filter(const dpp::message_create_t& m) { + * if (m.msg.content.find("something i want") != std::string::npos) { + * return &m.msg; + * } else { + * return nullptr; + * } + * } + * ``` + * + * @param element The event data to filter + * @return const C* Returned object or nullptr + */ + virtual const C* filter(const T& element) = 0; + + /** + * @brief Immediately cancels the collector. + * + * Use this if you have met the conditions for which you are collecting objects + * early, e.g. you were watching for a message containing 'yes' or 'no' and have + * received it before the time is up. + * + * @note Causes calling of the completed() method if it has not yet been called. + */ + virtual void cancel() { + delete tl; + tl = nullptr; + } + + /** + * @brief Destroy the collector object. + * @note Causes calling of the completed() method if it has not yet been called. + */ + virtual ~collector() { + delete tl; + } +}; + +/** + * @brief Represents a reaction. + * Can be filled for use in a collector + */ +class collected_reaction : public managed { +public: + /// Reacting user + user react_user; + /// Reacting guild + guild* react_guild{}; + /// Reacting guild member + guild_member react_member; + /// Reacting channel + channel* react_channel{}; + /// Reacted emoji + emoji react_emoji; + /// ID of the user who authored the message which was reacted to (Optional) + snowflake message_author_id{}; +}; + +/** + * @brief Template type for base class of channel collector + */ +typedef dpp::collector channel_collector_t; + +/** + * @brief Template type for base class of thread collector + */ +typedef dpp::collector thread_collector_t; + +/** + * @brief Template type for base class of role collector + */ +typedef dpp::collector role_collector_t; + +/** + * @brief Template type for base class of scheduled event collector + */ +typedef dpp::collector scheduled_event_collector_t; + +/** + * @brief Template type for base class of message collector + */ +typedef dpp::collector message_collector_t; + +/** + * @brief Template type for base class of message reaction collector + */ +typedef dpp::collector reaction_collector_t; + +/** + * @brief Message collector. + * Collects messages during a set timeframe and returns them in a list via the completed() method. + */ +class message_collector : public message_collector_t { +public: + /** + * @brief Construct a new message collector object + * + * @param cl cluster to associate the collector with + * @param duration Duration of time to run the collector for in seconds + */ + message_collector(cluster* cl, uint64_t duration) : message_collector_t::collector(cl, duration, cl->on_message_create) { } + + /** + * @brief Return the completed collection + * + * @param list items collected during the timeframe specified + */ + virtual void completed(const std::vector& list) = 0; + + /** + * @brief Select and filter the items which are to appear in the list + * This is called every time a new event is fired, to filter the event and determine which + * of the items is sent to the list. Returning nullptr excludes the item from the list. + * + * @param element element to filter + * @return Returned item to add to the list, or nullptr to skip adding this element + */ + virtual const dpp::message* filter(const dpp::message_create_t& element) { return &element.msg; } + + /** + * @brief Destroy the message collector object + */ + virtual ~message_collector() = default; +}; + +/** + * @brief Reaction collector. + * Collects message reactions during a set timeframe and returns them in a list via the completed() method. + */ +class reaction_collector : public reaction_collector_t { + snowflake message_id; + collected_reaction react; +public: + /** + * @brief Construct a new reaction collector object + * + * @param cl cluster to associate the collector with + * @param duration Duration of time to run the collector for in seconds + * @param msg_id Optional message ID. If specified, only collects reactions for the given message + */ + reaction_collector(cluster* cl, uint64_t duration, snowflake msg_id = 0) : reaction_collector_t::collector(cl, duration, cl->on_message_reaction_add), message_id(msg_id) { } + + /** + * @brief Return the completed collection + * + * @param list items collected during the timeframe specified + */ + virtual void completed(const std::vector& list) = 0; + + /** + * @brief Select and filter the items which are to appear in the list + * This is called every time a new event is fired, to filter the event and determine which + * of the items is sent to the list. Returning nullptr excludes the item from the list. + * + * @param element element to filter + * @return Returned item to add to the list, or nullptr to skip adding this element + */ + virtual const dpp::collected_reaction* filter(const dpp::message_reaction_add_t& element) { + /* Capture reactions for given message ID only */ + if (message_id.empty() || element.message_id == message_id) { + react.id = element.message_id; + react.react_user = element.reacting_user; + react.react_guild = element.reacting_guild; + react.react_member = element.reacting_member; + react.react_channel = element.reacting_channel; + react.react_emoji = element.reacting_emoji; + react.message_author_id = element.message_author_id; + return &react; + } else { + return nullptr; + } + } + + /** + * @brief Destroy the reaction collector object + */ + virtual ~reaction_collector() = default; +}; + +/** + * @brief Channel collector. + * Collects channels during a set timeframe and returns them in a list via the completed() method. + */ +class channel_collector : public channel_collector_t { +public: + /** + * @brief Construct a new channel collector object + * + * @param cl cluster to associate the collector with + * @param duration Duration of time to run the collector for in seconds + */ + channel_collector(cluster* cl, uint64_t duration) : channel_collector_t::collector(cl, duration, cl->on_channel_create) { } + + /** + * @brief Return the completed collection + * + * @param list items collected during the timeframe specified + */ + virtual void completed(const std::vector& list) = 0; + + /** + * @brief Select and filter the items which are to appear in the list + * This is called every time a new event is fired, to filter the event and determine which + * of the items is sent to the list. Returning nullptr excludes the item from the list. + * + * @param element element to filter + * @return Returned item to add to the list, or nullptr to skip adding this element + */ + virtual const dpp::channel* filter(const dpp::channel_create_t& element) { return element.created; } + + /** + * @brief Destroy the channel collector object + */ + virtual ~channel_collector() = default; +}; + +/** + * @brief Thread collector. + * Collects threads during a set timeframe and returns them in a list via the completed() method. + */ +class thread_collector : public thread_collector_t { +public: + /** + * @brief Construct a new thread collector object + * + * @param cl cluster to associate the collector with + * @param duration Duration of time to run the collector for in seconds + */ + thread_collector(cluster* cl, uint64_t duration) : thread_collector_t::collector(cl, duration, cl->on_thread_create) { } + + /** + * @brief Return the completed collection + * + * @param list items collected during the timeframe specified + */ + virtual void completed(const std::vector& list) = 0; + + /** + * @brief Select and filter the items which are to appear in the list + * This is called every time a new event is fired, to filter the event and determine which + * of the items is sent to the list. Returning nullptr excludes the item from the list. + * + * @param element element to filter + * @return Returned item to add to the list, or nullptr to skip adding this element + */ + virtual const dpp::thread* filter(const dpp::thread_create_t& element) { return &element.created; } + + /** + * @brief Destroy the thread collector object + */ + virtual ~thread_collector() = default; +}; + +/** + * @brief Role collector. + * Collects guild roles during a set timeframe and returns them in a list via the completed() method. + */ +class role_collector : public role_collector_t { +public: + /** + * @brief Construct a new role collector object + * + * @param cl cluster to associate the collector with + * @param duration Duration of time to run the collector for in seconds + */ + role_collector(cluster* cl, uint64_t duration) : role_collector_t::collector(cl, duration, cl->on_guild_role_create) { } + + /** + * @brief Return the completed collection + * + * @param list items collected during the timeframe specified + */ + virtual void completed(const std::vector& list) = 0; + + /** + * @brief Select and filter the items which are to appear in the list + * This is called every time a new event is fired, to filter the event and determine which + * of the items is sent to the list. Returning nullptr excludes the item from the list. + * + * @param element element to filter + * @return Returned item to add to the list, or nullptr to skip adding this element + */ + virtual const dpp::role* filter(const dpp::guild_role_create_t& element) { return element.created; } + + /** + * @brief Destroy the role collector object + */ + virtual ~role_collector() = default; +}; + +/** + * @brief Scheduled event collector. + * Collects messages during a set timeframe and returns them in a list via the completed() method. + */ +class scheduled_event_collector : public scheduled_event_collector_t { +public: + /** + * @brief Construct a new scheduled event collector object + * + * @param cl cluster to associate the collector with + * @param duration Duration of time to run the collector for in seconds + */ + scheduled_event_collector(cluster* cl, uint64_t duration) : scheduled_event_collector_t::collector(cl, duration, cl->on_guild_scheduled_event_create) { } + + /** + * @brief Return the completed collection + * + * @param list items collected during the timeframe specified + */ + virtual void completed(const std::vector& list) = 0; + + /** + * @brief Select and filter the items which are to appear in the list + * This is called every time a new event is fired, to filter the event and determine which + * of the items is sent to the list. Returning nullptr excludes the item from the list. + * + * @param element element to filter + * @return Returned item to add to the list, or nullptr to skip adding this element + */ + virtual const dpp::scheduled_event* filter(const dpp::guild_scheduled_event_create_t& element) { return &element.created; } + + /** + * @brief Destroy the scheduled event collector object + */ + virtual ~scheduled_event_collector() = default; +}; + +} // namespace dpp diff --git a/include/dpp/colors.h b/include/dpp/colors.h new file mode 100644 index 0000000..d5f6424 --- /dev/null +++ b/include/dpp/colors.h @@ -0,0 +1,744 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once + +#include + + /** + * @brief The main namespace for D++ functions. classes and types + */ +namespace dpp { + /** + * @brief predefined color constants + */ + namespace colors { + const uint32_t + white = 0xFFFFFF, + discord_white = 0xFFFFFE, + light_gray = 0xC0C0C0, + gray = 0x808080, + dark_gray = 0x404040, + black = 0x000000, + discord_black = 0x000001, + red = 0xFF0000, + pink = 0xFFAFAF, + orange = 0xFFC800, + yellow = 0xFFFF00, + green = 0x00FF00, + magenta = 0xFF00FF, + cyan = 0x00FFFF, + blue = 0x0000FF, + light_sea_green = 0x1ABC9C, + medium_sea_green = 0x2ECC71, + summer_sky = 0x3498DB, + deep_lilac = 0x9B59B6, + ruby = 0xE91E63, + moon_yellow = 0xF1C40F, + tahiti_gold = 0xE67E22, + cinnabar = 0xE74C3C, + submarine = 0x95A5A6, + blue_aquamarine = 0x607D8B, + deep_sea = 0x11806A, + sea_green = 0x1F8B4C, + endeavour = 0x206694, + vivid_violet = 0x71368A, + jazzberry_jam = 0xAD1457, + dark_goldenrod = 0xC27C0E, + rust = 0xA84300, + brown = 0x992D22, + gray_chateau = 0x979C9F, + bismark = 0x546E7A, + sti_blue = 0x0E4BEF, + wrx_blue = 0x00247D, + rallyart_crimson = 0xE60012, + lime = 0x00FF00, + forest_green = 0x228B22, + cadmium_green = 0x097969, + aquamarine = 0x7FFFD4, + blue_green = 0x088F8F, + raspberry = 0xE30B5C, + scarlet_red = 0xFF2400, + night = 0x0C090A, + charcoal = 0x34282C, + oil = 0x3B3131, + light_black = 0x454545, + black_cat = 0x413839, + iridium = 0x3D3C3A, + black_eel = 0x463E3F, + black_cow = 0x4C4646, + gray_wolf = 0x504A4B, + grey_wolf = 0x504A4B, + vampire_gray = 0x565051, + vampire_grey = 0x565051, + iron_gray = 0x52595D, + iron_grey = 0x52595D, + gray_dolphin = 0x5C5858, + grey_dolphin = 0x5C5858, + carbon_gray = 0x625D5D, + carbon_grey = 0x625D5D, + ash_gray = 0x666362, + ash_grey = 0x666362, + dim_gray = 0x696969, + dim_grey = 0x696969, + nardo_gray = 0x686A6C, + nardo_grey = 0x686A6C, + cloudy_gray = 0x6D6968, + cloudy_grey = 0x6D6968, + smokey_gray = 0x726E6D, + smokey_grey = 0x726E6D, + alien_gray = 0x736F6E, + alien_grey = 0x736F6E, + sonic_silver = 0x757575, + platinum_gray = 0x797979, + platinum_grey = 0x797979, + granite = 0x837E7C, + battleship_gray = 0x848482, + battleship_grey = 0x848482, + gunmetal_gray = 0x8D918D, + gunmetal_grey = 0x8D918D, + gray_cloud = 0xB6B6B4, + grey_cloud = 0xB6B6B4, + silver = 0xC0C0C0, + pale_silver = 0xC9C0BB, + gray_goose = 0xD1D0CE, + grey_goose = 0xD1D0CE, + platinum_silver = 0xCECECE, + silver_white = 0xDADBDD, + gainsboro = 0xDCDCDC, + platinum = 0xE5E4E2, + metallic_silver = 0xBCC6CC, + blue_gray = 0x98AFC7, + blue_grey = 0x98AFC7, + roman_silver = 0x838996, + light_slate_gray = 0x778899, + light_slate_grey = 0x778899, + slate_gray = 0x708090, + slate_grey = 0x708090, + rat_gray = 0x6D7B8D, + slate_granite_gray = 0x657383, + slate_granite_grey = 0x657383, + jet_gray = 0x616D7E, + jet_grey = 0x616D7E, + mist_blue = 0x646D7E, + marble_blue = 0x566D7E, + slate_blue_grey = 0x737CA1, + slate_blue_gray = 0x737CA1, + light_purple_blue = 0x728FCE, + azure_blue = 0x4863A0, + blue_jay = 0x2B547E, + charcoal_blue = 0x36454F, + dark_blue_grey = 0x29465B, + dark_slate = 0x2B3856, + deep_sea_blue = 0x123456, + night_blue = 0x151B54, + midnight_blue = 0x191970, + navy = 0x000080, + denim_dark_blue = 0x151B8D, + dark_blue = 0x00008B, + lapis_blue = 0x15317E, + new_midnight_blue = 0x0000A0, + earth_blue = 0x0000A5, + cobalt_blue = 0x0020C2, + medium_blue = 0x0000CD, + blueberry_blue = 0x0041C2, + canary_blue = 0x2916F5, + samco_blue = 0x0002FF, + bright_blue = 0x0909FF, + blue_orchid = 0x1F45FC, + sapphire_blue = 0x2554C7, + blue_eyes = 0x1569C7, + bright_navy_blue = 0x1974D2, + balloon_blue = 0x2B60DE, + royal_blue = 0x4169E1, + ocean_blue = 0x2B65EC, + blue_ribbon = 0x306EFF, + blue_dress = 0x157DEC, + neon_blue = 0x1589FF, + dodger_blue = 0x1E90FF, + glacial_blue_ice = 0x368BC1, + steel_blue = 0x4682B4, + silk_blue = 0x488AC7, + windows_blue = 0x357EC7, + blue_ivy = 0x3090C7, + blue_koi = 0x659EC7, + columbia_blue = 0x87AFC7, + baby_blue = 0x95B9C7, + cornflower_blue = 0x6495ED, + sky_blue_dress = 0x6698FF, + iceberg = 0x56A5EC, + butterfly_blue = 0x38ACEC, + deep_sky_blue = 0x00BFFF, + midday_blue = 0x3BB9FF, + crystal_blue = 0x5CB3FF, + denim_blue = 0x79BAEC, + day_sky_blue = 0x82CAFF, + light_sky_blue = 0x87CEFA, + sky_blue = 0x87CEEB, + jeans_blue = 0xA0CFEC, + blue_angel = 0xB7CEEC, + pastel_blue = 0xB4CFEC, + light_day_blue = 0xADDFFF, + sea_blue = 0xC2DFFF, + heavenly_blue = 0xC6DEFF, + robin_egg_blue = 0xBDEDFF, + powder_blue = 0xB0E0E6, + coral_blue = 0xAFDCEC, + light_blue = 0xADD8E6, + light_steel_blue = 0xB0CFDE, + gulf_blue = 0xC9DFEC, + pastel_light_blue = 0xD5D6EA, + lavender_blue = 0xE3E4FA, + white_blue = 0xDBE9FA, + lavender = 0xE6E6FA, + water = 0xEBF4FA, + alice_blue = 0xF0F8FF, + ghost_white = 0xF8F8FF, + azure = 0xF0FFFF, + light_cyan = 0xE0FFFF, + light_slate = 0xCCFFFF, + electric_blue = 0x9AFEFF, + tron_blue = 0x7DFDFE, + blue_zircon = 0x57FEFF, + aqua = 0x00FFFF, + bright_cyan = 0x0AFFFF, + celeste = 0x50EBEC, + blue_diamond = 0x4EE2EC, + bright_turquoise = 0x16E2F5, + blue_lagoon = 0x8EEBEC, + pale_turquoise = 0xAFEEEE, + pale_blue_lily = 0xCFECEC, + light_teal = 0xB3D9D9, + tiffany_blue = 0x81D8D0, + blue_hosta = 0x77BFC7, + cyan_opaque = 0x92C7C7, + northern_lights_blue = 0x78C7C7, + medium_aquamarine = 0x66CDAA, + magic_mint = 0xAAF0D1, + light_aquamarine = 0x93FFE8, + bright_teal = 0x01F9C6, + turquoise = 0x40E0D0, + medium_turquoise = 0x48D1CC, + deep_turquoise = 0x48CCCD, + jellyfish = 0x46C7C7, + blue_turquoise = 0x43C6DB, + dark_turquoise = 0x00CED1, + macaw_blue_green = 0x43BFC7, + seafoam_green = 0x3EA99F, + cadet_blue = 0x5F9EA0, + blue_chill = 0x3B9C9C, + dark_cyan = 0x008B8B, + teal_green = 0x00827F, + teal = 0x008080, + teal_blue = 0x007C80, + medium_teal = 0x045F5F, + dark_teal = 0x045D5D, + deep_teal = 0x033E3E, + dark_slate_gray = 0x25383C, + dark_slate_grey = 0x25383C, + gunmetal = 0x2C3539, + blue_moss_green = 0x3C565B, + beetle_green = 0x4C787E, + grayish_turquoise = 0x5E7D7E, + greenish_blue = 0x307D7E, + aquamarine_stone = 0x348781, + sea_turtle_green = 0x438D80, + dull_sea_green = 0x4E8975, + dark_green_blue = 0x1F6357, + deep_sea_green = 0x306754, + bottle_green = 0x006A4E, + elf_green = 0x1B8A6B, + dark_mint = 0x31906E, + jade = 0x00A36C, + earth_green = 0x34A56F, + chrome_green = 0x1AA260, + emerald = 0x50C878, + mint = 0x3EB489, + metallic_green = 0x7C9D8E, + camouflage_green = 0x78866B, + sage_green = 0x848B79, + hazel_green = 0x617C58, + venom_green = 0x728C00, + olive_drab = 0x6B8E23, + olive = 0x808000, + dark_olive_green = 0x556B2F, + military_green = 0x4E5B31, + green_leaves = 0x3A5F0B, + army_green = 0x4B5320, + fern_green = 0x667C26, + fall_forest_green = 0x4E9258, + irish_green = 0x08A04B, + pine_green = 0x387C44, + medium_forest_green = 0x347235, + jungle_green = 0x347C2C, + cactus_green = 0x227442, + dark_green = 0x006400, + deep_green = 0x056608, + deep_emerald_green = 0x046307, + hunter_green = 0x355E3B, + dark_forest_green = 0x254117, + lotus_green = 0x004225, + seaweed_green = 0x437C17, + shamrock_green = 0x347C17, + green_onion = 0x6AA121, + moss_green = 0x8A9A5B, + grass_green = 0x3F9B0B, + green_pepper = 0x4AA02C, + dark_lime_green = 0x41A317, + parrot_green = 0x12AD2B, + clover_green = 0x3EA055, + dinosaur_green = 0x73A16C, + green_snake = 0x6CBB3C, + alien_green = 0x6CC417, + green_apple = 0x4CC417, + lime_green = 0x32CD32, + pea_green = 0x52D017, + kelly_green = 0x4CC552, + zombie_green = 0x54C571, + green_peas = 0x89C35C, + dollar_bill_green = 0x85BB65, + frog_green = 0x99C68E, + turquoise_green = 0xA0D6B4, + dark_sea_green = 0x8FBC8F, + basil_green = 0x829F82, + gray_green = 0xA2AD9C, + iguana_green = 0x9CB071, + citron_green = 0x8FB31D, + acid_green = 0xB0BF1A, + avocado_green = 0xB2C248, + pistachio_green = 0x9DC209, + salad_green = 0xA1C935, + yellow_green = 0x9ACD32, + pastel_green = 0x77DD77, + hummingbird_green = 0x7FE817, + nebula_green = 0x59E817, + stoplight_go_green = 0x57E964, + neon_green = 0x16F529, + jade_green = 0x5EFB6E, + lime_mint_green = 0x36F57F, + spring_green = 0x00FF7F, + medium_spring_green = 0x00FA9A, + emerald_green = 0x5FFB17, + lawn_green = 0x7CFC00, + bright_green = 0x66FF00, + chartreuse = 0x7FFF00, + yellow_lawn_green = 0x87F717, + aloe_vera_green = 0x98F516, + dull_green_yellow = 0xB1FB17, + lemon_green = 0xADF802, + green_yellow = 0xADFF2F, + chameleon_green = 0xBDF516, + neon_yellow_green = 0xDAEE01, + yellow_green_grosbeak = 0xE2F516, + tea_green = 0xCCFB5D, + slime_green = 0xBCE954, + algae_green = 0x64E986, + light_green = 0x90EE90, + dragon_green = 0x6AFB92, + pale_green = 0x98FB98, + mint_green = 0x98FF98, + green_thumb = 0xB5EAAA, + organic_brown = 0xE3F9A6, + light_jade = 0xC3FDB8, + light_mint_green = 0xC2E5D3, + light_rose_green = 0xDBF9DB, + chrome_white = 0xE8F1D4, + honeydew = 0xF0FFF0, + mint_cream = 0xF5FFFA, + lemon_chiffon = 0xFFFACD, + parchment = 0xFFFFC2, + cream = 0xFFFFCC, + cream_white = 0xFFFDD0, + light_goldenrod_yellow = 0xFAFAD2, + light_yellow = 0xFFFFE0, + beige = 0xF5F5DC, + cornsilk = 0xFFF8DC, + blonde = 0xFBF6D9, + champagne = 0xF7E7CE, + antique_white = 0xFAEBD7, + papaya_whip = 0xFFEFD5, + blanched_almond = 0xFFEBCD, + bisque = 0xFFE4C4, + wheat = 0xF5DEB3, + moccasin = 0xFFE4B5, + peach = 0xFFE5B4, + light_orange = 0xFED8B1, + peach_puff = 0xFFDAB9, + coral_peach = 0xFBD5AB, + navajo_white = 0xFFDEAD, + golden_blonde = 0xFBE7A1, + golden_silk = 0xF3E3C3, + dark_blonde = 0xF0E2B6, + light_gold = 0xF1E5AC, + vanilla = 0xF3E5AB, + tan_brown = 0xECE5B6, + dirty_white = 0xE8E4C9, + pale_goldenrod = 0xEEE8AA, + khaki = 0xF0E68C, + cardboard_brown = 0xEDDA74, + harvest_gold = 0xEDE275, + sun_yellow = 0xFFE87C, + corn_yellow = 0xFFF380, + pastel_yellow = 0xFAF884, + neon_yellow = 0xFFFF33, + canary_yellow = 0xFFEF00, + banana_yellow = 0xF5E216, + mustard_yellow = 0xFFDB58, + golden_yellow = 0xFFDF00, + bold_yellow = 0xF9DB24, + rubber_ducky_yellow = 0xFFD801, + gold = 0xFFD700, + bright_gold = 0xFDD017, + chrome_gold = 0xFFCE44, + golden_brown = 0xEAC117, + deep_yellow = 0xF6BE00, + macaroni_and_cheese = 0xF2BB66, + saffron = 0xFBB917, + neon_gold = 0xFDBD01, + beer = 0xFBB117, + yellow_orange = 0xFFAE42, + orange_yellow = 0xFFAE42, + cantaloupe = 0xFFA62F, + cheese_orange = 0xFFA600, + brown_sand = 0xEE9A4D, + sandy_brown = 0xF4A460, + brown_sugar = 0xE2A76F, + camel_brown = 0xC19A6B, + deer_brown = 0xE6BF83, + burly_wood = 0xDEB887, + tan = 0xD2B48C, + light_french_beige = 0xC8AD7F, + sand = 0xC2B280, + sage = 0xBCB88A, + fall_leaf_brown = 0xC8B560, + ginger_brown = 0xC9BE62, + bronze_gold = 0xC9AE5D, + dark_khaki = 0xBDB76B, + olive_green = 0xBAB86C, + brass = 0xB5A642, + cookie_brown = 0xC7A317, + metallic_gold = 0xD4AF37, + bee_yellow = 0xE9AB17, + school_bus_yellow = 0xE8A317, + goldenrod = 0xDAA520, + orange_gold = 0xD4A017, + caramel = 0xC68E17, + cinnamon = 0xC58917, + peru = 0xCD853F, + bronze = 0xCD7F32, + tiger_orange = 0xC88141, + copper = 0xB87333, + dark_gold = 0xAA6C39, + metallic_bronze = 0xA97142, + dark_almond = 0xAB784E, + wood = 0x966F33, + oak_brown = 0x806517, + antique_bronze = 0x665D1E, + hazel = 0x8E7618, + dark_yellow = 0x8B8000, + dark_moccasin = 0x827839, + khaki_green = 0x8A865D, + millennium_jade = 0x93917C, + dark_beige = 0x9F8C76, + bullet_shell = 0xAF9B60, + army_brown = 0x827B60, + sandstone = 0x786D5F, + taupe = 0x483C32, + mocha = 0x493D26, + milk_chocolate = 0x513B1C, + gray_brown = 0x3D3635, + dark_coffee = 0x3B2F2F, + old_burgundy = 0x43302E, + western_charcoal = 0x49413F, + bakers_brown = 0x5C3317, + dark_brown = 0x654321, + sepia_brown = 0x704214, + dark_bronze = 0x804A00, + coffee = 0x6F4E37, + brown_bear = 0x835C3B, + red_dirt = 0x7F5217, + sepia = 0x7F462C, + sienna = 0xA0522D, + saddle_brown = 0x8B4513, + dark_sienna = 0x8A4117, + sangria = 0x7E3817, + blood_red = 0x7E3517, + chestnut = 0x954535, + coral_brown = 0x9E4638, + chestnut_red = 0xC34A2C, + mahogany = 0xC04000, + red_gold = 0xEB5406, + red_fox = 0xC35817, + dark_bisque = 0xB86500, + light_brown = 0xB5651D, + petra_gold = 0xB76734, + copper_red = 0xCB6D51, + orange_salmon = 0xC47451, + chocolate = 0xD2691E, + sedona = 0xCC6600, + papaya_orange = 0xE56717, + halloween_orange = 0xE66C2C, + neon_orange = 0xFF6700, + bright_orange = 0xFF5F1F, + pumpkin_orange = 0xF87217, + carrot_orange = 0xF88017, + dark_orange = 0xFF8C00, + construction_cone_orange = 0xF87431, + indian_saffron = 0xFF7722, + sunrise_orange = 0xE67451, + mango_orange = 0xFF8040, + coral = 0xFF7F50, + basket_ball_orange = 0xF88158, + light_salmon_rose = 0xF9966B, + light_salmon = 0xFFA07A, + dark_salmon = 0xE9967A, + tangerine = 0xE78A61, + light_copper = 0xDA8A67, + salmon_pink = 0xFF8674, + salmon = 0xFA8072, + peach_pink = 0xF98B88, + light_coral = 0xF08080, + pastel_red = 0xF67280, + pink_coral = 0xE77471, + bean_red = 0xF75D59, + valentine_red = 0xE55451, + indian_red = 0xCD5C5C, + tomato = 0xFF6347, + shocking_orange = 0xE55B3C, + orange_red = 0xFF4500, + neon_red = 0xFD1C03, + ruby_red = 0xF62217, + ferrari_red = 0xF70D1A, + fire_engine_red = 0xF62817, + lava_red = 0xE42217, + love_red = 0xE41B17, + grapefruit = 0xDC381F, + cherry_red = 0xC24641, + chilli_pepper = 0xC11B17, + fire_brick = 0xB22222, + tomato_sauce_red = 0xB21807, + carbon_red = 0xA70D2A, + cranberry = 0x9F000F, + saffron_red = 0x931314, + crimson_red = 0x990000, + red_wine = 0x990012, + wine_red = 0x990012, + dark_red = 0x8B0000, + maroon = 0x800000, + burgundy = 0x8C001A, + vermilion = 0x7E191B, + deep_red = 0x800517, + red_blood = 0x660000, + blood_night = 0x551606, + dark_scarlet = 0x560319, + black_bean = 0x3D0C02, + chocolate_brown = 0x3F000F, + midnight = 0x2B1B17, + purple_lily = 0x550A35, + purple_maroon = 0x810541, + plum_pie = 0x7D0541, + plum_velvet = 0x7D0552, + dark_raspberry = 0x872657, + velvet_maroon = 0x7E354D, + rosy_finch = 0x7F4E52, + dull_purple = 0x7F525D, + puce = 0x7F5A58, + rose_dust = 0x997070, + pastel_brown = 0xB1907F, + rosy_pink = 0xB38481, + rosy_brown = 0xBC8F8F, + khaki_rose = 0xC5908E, + lipstick_pink = 0xC48793, + pink_brown = 0xC48189, + old_rose = 0xC08081, + dusty_pink = 0xD58A94, + pink_daisy = 0xE799A3, + rose = 0xE8ADAA, + dusty_rose = 0xC9A9A6, + silver_pink = 0xC4AEAD, + gold_pink = 0xE6C7C2, + rose_gold = 0xECC5C0, + deep_peach = 0xFFCBA4, + pastel_orange = 0xF8B88B, + desert_sand = 0xEDC9AF, + unbleached_silk = 0xFFDDCA, + pig_pink = 0xFDD7E4, + pale_pink = 0xF2D4D7, + blush = 0xFFE6E8, + misty_rose = 0xFFE4E1, + pink_bubble_gum = 0xFFDFDD, + light_rose = 0xFBCFCD, + light_red = 0xFFCCCB, + warm_pink = 0xF6C6BD, + deep_rose = 0xFBBBB9, + light_pink = 0xFFB6C1, + soft_pink = 0xFFB8BF, + donut_pink = 0xFAAFBE, + baby_pink = 0xFAAFBA, + flamingo_pink = 0xF9A7B0, + pastel_pink = 0xFEA3AA, + rose_pink = 0xE7A1B0, + pink_rose = 0xE7A1B0, + cadillac_pink = 0xE38AAE, + carnation_pink = 0xF778A1, + pastel_rose = 0xE5788F, + blush_red = 0xE56E94, + pale_violet_red = 0xDB7093, + purple_pink = 0xD16587, + tulip_pink = 0xC25A7C, + bashful_pink = 0xC25283, + dark_pink = 0xE75480, + dark_hot_pink = 0xF660AB, + hot_pink = 0xFF69B4, + watermelon_pink = 0xFC6C85, + violet_red = 0xF6358A, + hot_deep_pink = 0xF52887, + bright_pink = 0xFF007F, + deep_pink = 0xFF1493, + neon_pink = 0xF535AA, + chrome_pink = 0xFF33AA, + neon_hot_pink = 0xFD349C, + pink_cupcake = 0xE45E9D, + royal_pink = 0xE759AC, + dimorphotheca_magenta = 0xE3319D, + pink_lemonade = 0xE4287C, + red_pink = 0xFA2A55, + crimson = 0xDC143C, + bright_maroon = 0xC32148, + rose_red = 0xC21E56, + rogue_pink = 0xC12869, + burnt_pink = 0xC12267, + pink_violet = 0xCA226B, + magenta_pink = 0xCC338B, + medium_violet_red = 0xC71585, + dark_carnation_pink = 0xC12283, + raspberry_purple = 0xB3446C, + pink_plum = 0xB93B8F, + orchid = 0xDA70D6, + deep_mauve = 0xDF73D4, + violet = 0xEE82EE, + fuchsia_pink = 0xFF77FF, + bright_neon_pink = 0xF433FF, + fuchsia = 0xFF00FF, + crimson_purple = 0xE238EC, + heliotrope_purple = 0xD462FF, + tyrian_purple = 0xC45AEC, + medium_orchid = 0xBA55D3, + purple_flower = 0xA74AC7, + orchid_purple = 0xB048B5, + rich_lilac = 0xB666D2, + pastel_violet = 0xD291BC, + mauve_taupe = 0x915F6D, + viola_purple = 0x7E587E, + eggplant = 0x614051, + plum_purple = 0x583759, + grape = 0x5E5A80, + purple_navy = 0x4E5180, + slate_blue = 0x6A5ACD, + blue_lotus = 0x6960EC, + blurple = 0x5865F2, + light_slate_blue = 0x736AFF, + medium_slate_blue = 0x7B68EE, + periwinkle_purple = 0x7575CF, + very_peri = 0x6667AB, + bright_grape = 0x6F2DA8, + purple_amethyst = 0x6C2DC7, + bright_purple = 0x6A0DAD, + deep_periwinkle = 0x5453A6, + dark_slate_blue = 0x483D8B, + purple_haze = 0x4E387E, + purple_iris = 0x571B7E, + dark_purple = 0x4B0150, + deep_purple = 0x36013F, + midnight_purple = 0x2E1A47, + purple_monster = 0x461B7E, + indigo = 0x4B0082, + blue_whale = 0x342D7E, + rebecca_purple = 0x663399, + purple_jam = 0x6A287E, + dark_magenta = 0x8B008B, + purple = 0x800080, + french_lilac = 0x86608E, + dark_orchid = 0x9932CC, + dark_violet = 0x9400D3, + purple_violet = 0x8D38C9, + jasmine_purple = 0xA23BEC, + purple_daffodil = 0xB041FF, + clematis_violet = 0x842DCE, + blue_violet = 0x8A2BE2, + purple_sage_bush = 0x7A5DC7, + lovely_purple = 0x7F38EC, + neon_purple = 0x9D00FF, + purple_plum = 0x8E35EF, + aztech_purple = 0x893BFF, + medium_purple = 0x9370DB, + light_purple = 0x8467D7, + crocus_purple = 0x9172EC, + purple_mimosa = 0x9E7BFF, + periwinkle = 0xCCCCFF, + pale_lilac = 0xDCD0FF, + lavender_purple = 0x967BB6, + rose_purple = 0xB09FCA, + lilac = 0xC8A2C8, + mauve = 0xE0B0FF, + bright_lilac = 0xD891EF, + purple_dragon = 0xC38EC7, + plum = 0xDDA0DD, + blush_pink = 0xE6A9EC, + pastel_purple = 0xF2A2E8, + blossom_pink = 0xF9B7FF, + wisteria_purple = 0xC6AEC7, + purple_thistle = 0xD2B9D3, + thistle = 0xD8BFD8, + purple_white = 0xDFD3E3, + periwinkle_pink = 0xE9CFEC, + cotton_candy = 0xFCDFFF, + lavender_pinocchio = 0xEBDDE2, + dark_white = 0xE1D9D1, + ash_white = 0xE9E4D4, + white_chocolate = 0xEDE6D6, + soft_ivory = 0xFAF0DD, + off_white = 0xF8F0E3, + pearl_white = 0xF8F6F0, + red_white = 0xF3E8EA, + lavender_blush = 0xFFF0F5, + pearl = 0xFDEEF4, + egg_shell = 0xFFF9E3, + old_lace = 0xFEF0E3, + linen = 0xFAF0E6, + sea_shell = 0xFFF5EE, + bone_white = 0xF9F6EE, + rice = 0xFAF5EF, + floral_white = 0xFFFAF0, + ivory = 0xFFFFF0, + white_gold = 0xFFFFF4, + light_white = 0xFFFFF7, + white_smoke = 0xF5F5F5, + cotton = 0xFBFBF9, + snow = 0xFFFAFA, + milk_white = 0xFEFCFF, + half_white = 0xFFFEFA; + } // namespace colors + + /** + * @brief predefined color constants, same as colors + */ + namespace colours = colors; +} // namespace dpp diff --git a/include/dpp/commandhandler.h b/include/dpp/commandhandler.h new file mode 100644 index 0000000..304ba93 --- /dev/null +++ b/include/dpp/commandhandler.h @@ -0,0 +1,392 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace dpp { + +/** + * @brief dpp::resolved_user contains both a dpp::guild_member and a dpp::user. + * The user can be used to obtain in-depth user details such as if they are nitro, + * and the guild member information to check their roles on a guild etc. + * The Discord API provides both if a parameter is a user ping, + * so we offer both in a combined structure. + */ +struct DPP_EXPORT resolved_user { + /** + * @brief Holds user information + */ + dpp::user user; + /** + * @brief Holds member information + */ + dpp::guild_member member; +}; + +/** + * @brief Represents a received parameter. + * We use variant so that multiple non-related types can be contained within. + */ +typedef std::variant command_parameter; + +/** + * @brief Parameter types when registering a command. + * We don't pass these in when triggering the command in the handler, because it is + * expected the developer added the command so they know what types to expect for each named + * parameter. + */ +enum parameter_type { + pt_string, //!< String value + pt_role, //!< Role object + pt_channel, //!< Channel object + pt_user, //!< User object + pt_integer, //!< 64 bit signed integer + pt_double, //!< double floating point + pt_boolean //!< boolean +}; + +/** + * @brief Details of a command parameter used in registration. + * Note that for non-slash commands optional parameters can only be at the end of + * the list of parameters. + */ +struct DPP_EXPORT param_info { + + /** + * @brief Type of parameter + */ + parameter_type type; + + /** + * @brief True if the parameter is optional. + * For non-slash commands optional parameters may only be on the end of the list. + */ + bool optional; + + /** + * @brief Description of command. Displayed only for slash commands + */ + std::string description; + + /** + * @brief Allowed multiple choice options. + * The key name is the string passed to the command handler + * and the key value is its description displayed to the user. + */ + std::map choices; + + /** + * @brief Construct a new param_info object + * + * @param t Type of parameter + * @param o True if parameter is optional + * @param description The parameter description + * @param opts The options for a multiple choice parameter + */ + param_info(parameter_type t, bool o, const std::string &description, const std::map &opts = {}); +}; + +/** + * @brief Parameter list used during registration. + * Note that use of vector/pair is important here to preserve parameter order, + * as opposed to unordered_map (which doesn't guarantee any order at all) and + * std::map, which reorders keys alphabetically. + */ +typedef std::vector> parameter_registration_t; + +/** + * @brief Parameter list for a called command. + * See dpp::parameter_registration_t for an explanation as to why vector is used. + */ +typedef std::vector> parameter_list_t; + +/** + * @brief Represents the sending source of a command. + * This is passed to any command handler and should be passed back to + * commandhandler::reply(), allowing the reply method to route any replies back + * to the origin, which may be a slash command or a message. Both require different + * response facilities but we want this to be transparent if you use the command + * handler class. + * @deprecated commandhandler and message commands are deprecated and dpp::slashcommand is encouraged as a replacement. + */ +struct DPP_EXPORT command_source { + /** + * @brief Sending guild id + */ + snowflake guild_id; + /** + * @brief Source channel id + */ + snowflake channel_id; + /** + * @brief Command ID of a slash command + */ + snowflake command_id; + /** + * @brief Token for sending a slash command reply + */ + std::string command_token; + /** + * @brief The user who issued the command + */ + user issuer; + + /** + * @brief Copy of the underlying message_create_t event, if it was a message create event + */ + std::optional message_event; + + /** + * @brief Copy of the underlying interaction_create_t event, if it was an interaction create event + */ + std::optional interaction_event; + + /** + * @brief Construct a command_source object from a message_create_t event + */ + command_source(const struct message_create_t& event); + + /** + * @brief Construct a command_source object from an interaction_create_t event + */ + command_source(const struct interaction_create_t& event); +}; + +/** + * @brief The function definition for a command handler. Expects a command name string, + * and a list of command parameters. + * @deprecated commandhandler and message commands are deprecated and dpp::slashcommand is encouraged as a replacement. + */ +typedef std::function command_handler; + +/** + * @brief Represents the details of a command added to the command handler class. + * @deprecated commandhandler and message commands are deprecated and dpp::slashcommand is encouraged as a replacement. + */ +struct DPP_EXPORT command_info_t { + /** + * @brief Function reference for the handler. This is std::function so it can represent + * a class member, a lambda or a raw C function pointer. + */ + command_handler func; + /** + * @brief Parameters requested for the command, with their types + */ + parameter_registration_t parameters; + /** + * @brief Guild ID the command exists on, or 0 to be present on all guilds + */ + snowflake guild_id; +}; + + +/** + * @brief The commandhandler class represents a group of commands, prefixed or slash commands with handling functions. + * + * It can automatically register slash commands, and handle routing of messages and interactions to separated command handler + * functions. + * @deprecated commandhandler and message commands are deprecated and dpp::slashcommand is encouraged as a replacement. + */ +class DPP_EXPORT commandhandler { +private: + /** + * @brief List of guild commands to bulk register + */ + std::map> bulk_registration_list_guild; + /** + * @brief List of global commands to bulk register + */ + std::vector bulk_registration_list_global; +public: + /** + * @brief Commands in the handler + */ + std::unordered_map commands; + + /** + * @brief Valid prefixes + */ + std::vector prefixes; + + /** + * @brief Set to true automatically if one of the prefixes added is "/" + */ + bool slash_commands_enabled; + + /** + * @brief Cluster we are attached to for issuing REST calls + */ + class cluster* owner; + + /** + * @brief Application ID + */ + snowflake app_id; + + /** + * @brief Interaction event handle + */ + event_handle interactions; + + /** + * @brief Message event handle + */ + event_handle messages; + + /** + * @brief Returns true if the string has a known prefix on the start. + * Modifies string to remove prefix if it returns true. + * + * @param str String to check and modify + * @return true string contained a prefix, prefix removed from string + * @return false string did not contain a prefix + */ + bool string_has_prefix(std::string &str); + +public: + + /** + * @brief Construct a new commandhandler object + * + * @param o Owning cluster to attach to + * @param auto_hook_events Set to true to automatically hook the on_slashcommand + * and on_message events. You should not need to set this to false unless you have a specific + * use case, as D++ supports multiple listeners to an event, so will allow the commandhandler + * to hook to your command events without disrupting other uses for the events you may have. + * @param application_id The application id of the bot. If not specified, the class will + * look within the cluster object and use cluster::me::id instead. + */ + commandhandler(class cluster* o, bool auto_hook_events = true, snowflake application_id = 0); + + /** + * @brief Destroy the commandhandler object + */ + ~commandhandler(); + + /** + * @brief Set the application id after construction + * + * @param o Owning cluster to attach to + */ + commandhandler& set_owner(class cluster* o); + + /** + * @brief Add a prefix to the command handler + * + * @param prefix Prefix to be handled by the command handler + * @return commandhandler& reference to self + */ + commandhandler& add_prefix(const std::string &prefix); + + /** + * @brief Add a command to the command handler + * + * @param command Command to be handled. + * Note that if any one of your prefixes is "/" this will attempt to register + * a global command using the API and you will receive notification of this command + * via an interaction event. + * @param handler Handler function + * @param parameters Parameters to use for the command + * @param description The description of the command, shown for slash commands + * @param guild_id The guild ID to restrict the command to. For slash commands causes registration of a guild command as opposed to a global command. + * @return commandhandler& reference to self + * @throw dpp::logic_exception if application ID cannot be determined + */ + commandhandler& add_command(const std::string &command, const parameter_registration_t ¶meters, command_handler handler, const std::string &description = "", snowflake guild_id = 0); + + /** + * @brief Register all slash commands with Discord + * This method must be called at least once if you are using the "/" prefix to mark the + * end of commands being added to the handler. Note that this uses bulk registration and will replace any + * existing slash commands. + * + * Note that if you have previously registered your commands and they have not changed, you do + * not need to call this again. Discord retains a cache of previously added commands. + * + * @return commandhandler& Reference to self for chaining method calls + */ + commandhandler& register_commands(); + + /** + * @brief Route a command from the on_message_create function. + * Call this method from within your on_message_create with the received + * dpp::message object if you have disabled automatic registration of events. + * + * @param event message create event to parse + */ + void route(const struct dpp::message_create_t& event); + + /** + * @brief Route a command from the on_slashcommand function. + * Call this method from your on_slashcommand with the received + * dpp::interaction_create_t object if you have disabled automatic registration of events. + * + * @param event command interaction event to parse + */ + void route(const struct slashcommand_t & event); + + /** + * @brief Reply to a command. + * You should use this method rather than cluster::message_create as + * the way you reply varies between slash commands and message commands. + * Note you should ALWAYS reply. Slash commands will emit an ugly error + * to the user if you do not emit some form of reply within 3 seconds. + * + * @param m message to reply with. + * @param source source of the command + * @param callback User function to execute when the api call completes. + */ + void reply(const dpp::message &m, command_source source, command_completion_event_t callback = utility::log_error()); + + /** + * @brief Reply to a command without a message, causing the discord client + * to display "Bot name is thinking...". + * The "thinking" message will persist for a maximum of 15 minutes. + * This counts as a reply for a slash command. Slash commands will emit an + * ugly error to the user if you do not emit some form of reply within 3 + * seconds. + * + * @param source source of the command + * @param callback User function to execute when the api call completes. + */ + void thinking(command_source source, command_completion_event_t callback = utility::log_error()); + + /* Easter egg */ + void thonk(command_source source, command_completion_event_t callback = utility::log_error()); + +}; + +} // namespace dpp diff --git a/include/dpp/coro.h b/include/dpp/coro.h new file mode 100644 index 0000000..a515d0e --- /dev/null +++ b/include/dpp/coro.h @@ -0,0 +1,31 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2022 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ + +#ifdef DPP_CORO +#pragma once + +#include "coro/async.h" +#include "coro/coroutine.h" +#include "coro/job.h" +#include "coro/task.h" +#include "coro/when_any.h" + +#endif /* DPP_CORO */ diff --git a/include/dpp/coro/async.h b/include/dpp/coro/async.h new file mode 100644 index 0000000..2790aa5 --- /dev/null +++ b/include/dpp/coro/async.h @@ -0,0 +1,495 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2022 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ + +#ifdef DPP_CORO +#pragma once + +#include "coro.h" + +#include +#include +#include +#include +#include +#include + +namespace dpp { + +namespace detail { + +/** + * @brief Empty struct used for overload resolution. + */ +struct empty_tag_t{}; + +namespace async { + +/** + * @brief Represents the step an std::async is at. + */ +enum class state_t { + sent, /* Request was sent but not co_await-ed. handle is nullptr, result_storage is not constructed */ + waiting, /* Request was co_await-ed. handle is valid, result_storage is not constructed */ + done, /* Request was completed. handle is unknown, result_storage is valid */ + dangling /* Request was never co_await-ed. */ +}; + +/** + * @brief State of the async and its callback. + * + * Defined outside of dpp::async because this seems to work better with Intellisense. + */ +template +struct async_callback_data { + /** + * @brief Number of references to this callback state. + */ + std::atomic ref_count{1}; + + /** + * @brief State of the awaitable and the API callback + */ + std::atomic state = state_t::sent; + + /** + * @brief The stored result of the API call, stored as an array of bytes to directly construct in place + */ + alignas(R) std::array result_storage; + + /** + * @brief Handle to the coroutine co_await-ing on this API call + * + * @see std::coroutine_handle + */ + std_coroutine::coroutine_handle<> coro_handle = nullptr; + + /** + * @brief Convenience function to construct the result in the storage and initialize its lifetime + * + * @warning This is only a convenience function, ONLY CALL THIS IN THE CALLBACK, before setting state to done. + */ + template + void construct_result(Ts&&... ts) { + // Standard-compliant type punning yay + std::construct_at(reinterpret_cast(result_storage.data()), std::forward(ts)...); + } + + /** + * @brief Destructor. + * + * Also destroys the result if present. + */ + ~async_callback_data() { + if (state.load() == state_t::done) { + std::destroy_at(reinterpret_cast(result_storage.data())); + } + } +}; + +/** + * @brief Base class of dpp::async. + * + * @warning This class should not be used directly by a user, use dpp::async instead. + * @note This class contains all the functions used internally by co_await. It is intentionally opaque and a private base of dpp::async so a user cannot call await_suspend and await_resume directly. + */ +template +class async_base { + /** + * @brief Ref-counted callback, contains the callback logic and manages the lifetime of the callback data over multiple threads. + */ + struct shared_callback { + /** + * @brief Self-managed ref-counted pointer to the state data + */ + async_callback_data *state = new async_callback_data; + + /** + * @brief Callback function. + * + * Constructs the callback data, and if the coroutine was awaiting, resume it + * @param cback The result of the API call. + * @tparam V Forwarding reference convertible to R + */ + template V> + void operator()(V &&cback) const { + state->construct_result(std::forward(cback)); + if (auto previous_state = state->state.exchange(state_t::done); previous_state == state_t::waiting) { + state->coro_handle.resume(); + } + } + + /** + * @brief Main constructor, allocates a new callback_state object. + */ + shared_callback() = default; + + /** + * @brief Empty constructor, holds no state. + */ + explicit shared_callback(detail::empty_tag_t) noexcept : state{nullptr} {} + + /** + * @brief Copy constructor. Takes shared ownership of the callback state, increasing the reference count. + */ + shared_callback(const shared_callback &other) noexcept { + this->operator=(other); + } + + /** + * @brief Move constructor. Transfers ownership from another object, leaving intact the reference count. The other object releases the callback state. + */ + shared_callback(shared_callback &&other) noexcept { + this->operator=(std::move(other)); + } + + /** + * @brief Destructor. Releases the held reference and destroys if no other references exist. + */ + ~shared_callback() { + if (!state) { // Moved-from object + return; + } + + auto count = state->ref_count.fetch_sub(1); + if (count == 0) { + delete state; + } + } + + /** + * @brief Copy assignment. Takes shared ownership of the callback state, increasing the reference count. + */ + shared_callback &operator=(const shared_callback &other) noexcept { + state = other.state; + ++state->ref_count; + return *this; + } + + /** + * @brief Move assignment. Transfers ownership from another object, leaving intact the reference count. The other object releases the callback state. + */ + shared_callback &operator=(shared_callback &&other) noexcept { + state = std::exchange(other.state, nullptr); + return *this; + } + + /** + * @brief Function called by the async when it is destroyed when it was never co_awaited, signals to the callback to abort. + */ + void set_dangling() noexcept { + if (!state) { // moved-from object + return; + } + state->state.store(state_t::dangling); + } + + bool done(std::memory_order order = std::memory_order_seq_cst) const noexcept { + return (state->state.load(order) == state_t::done); + } + + /** + * @brief Convenience function to get the shared callback state's result. + * + * @warning It is UB to call this on a callback whose state is anything else but state_t::done. + */ + R &get_result() noexcept { + assert(state && done()); + return (*reinterpret_cast(state->result_storage.data())); + } + + /** + * @brief Convenience function to get the shared callback state's result. + * + * @warning It is UB to call this on a callback whose state is anything else but state_t::done. + */ + const R &get_result() const noexcept { + assert(state && done()); + return (*reinterpret_cast(state->result_storage.data())); + } + }; + + /** + * @brief Shared state of the async and its callback, to be used across threads. + */ + shared_callback api_callback{nullptr}; + +public: + /** + * @brief Construct an async object wrapping an object method, the call is made immediately by forwarding to std::invoke and can be awaited later to retrieve the result. + * + * @param obj The object to call the method on + * @param fun The method of the object to call. Its last parameter must be a callback taking a parameter of type R + * @param args Parameters to pass to the method, excluding the callback + */ + template +#ifndef _DOXYGEN_ + requires std::invocable> +#endif + explicit async_base(Obj &&obj, Fun &&fun, Args&&... args) : api_callback{} { + std::invoke(std::forward(fun), std::forward(obj), std::forward(args)..., api_callback); + } + + /** + * @brief Construct an async object wrapping an invokeable object, the call is made immediately by forwarding to std::invoke and can be awaited later to retrieve the result. + * + * @param fun The object to call using std::invoke. Its last parameter must be a callable taking a parameter of type R + * @param args Parameters to pass to the object, excluding the callback + */ + template +#ifndef _DOXYGEN_ + requires std::invocable> +#endif + explicit async_base(Fun &&fun, Args&&... args) : api_callback{} { + std::invoke(std::forward(fun), std::forward(args)..., api_callback); + } + + /** + * @brief Construct an empty async. Using `co_await` on an empty async is undefined behavior. + */ + async_base() noexcept : api_callback{detail::empty_tag_t{}} {} + + /** + * @brief Destructor. If any callback is pending it will be aborted. + */ + ~async_base() { + api_callback.set_dangling(); + } + + /** + * @brief Copy constructor is disabled + */ + async_base(const async_base &) = delete; + + /** + * @brief Move constructor + * + * NOTE: Despite being marked noexcept, this function uses std::lock_guard which may throw. The implementation assumes this can never happen, hence noexcept. Report it if it does, as that would be a bug. + * + * @remark Using the moved-from async after this function is undefined behavior. + * @param other The async object to move the data from. + */ + async_base(async_base &&other) noexcept = default; + + /** + * @brief Copy assignment is disabled + */ + async_base &operator=(const async_base &) = delete; + + /** + * @brief Move assignment operator. + * + * NOTE: Despite being marked noexcept, this function uses std::lock_guard which may throw. The implementation assumes this can never happen, hence noexcept. Report it if it does, as that would be a bug. + * + * @remark Using the moved-from async after this function is undefined behavior. + * @param other The async object to move the data from + */ + async_base &operator=(async_base &&other) noexcept = default; + + /** + * @brief Check whether or not co_await-ing this would suspend the caller, i.e. if we have the result or not + * + * @return bool Whether we already have the result of the API call or not + */ + [[nodiscard]] bool await_ready() const noexcept { + return api_callback.done(); + } + + /** + * @brief Second function called by the standard library when the object is co-awaited, if await_ready returned false. + * + * Checks again for the presence of the result, if absent, signals to suspend and keep track of the calling coroutine for the callback to resume. + * + * @remark Do not call this manually, use the co_await keyword instead. + * @param caller The handle to the coroutine co_await-ing and being suspended + */ + [[nodiscard]] bool await_suspend(detail::std_coroutine::coroutine_handle<> caller) noexcept { + auto sent = state_t::sent; + api_callback.state->coro_handle = caller; + return api_callback.state->state.compare_exchange_strong(sent, state_t::waiting); // true (suspend) if `sent` was replaced with `waiting` -- false (resume) if the value was not `sent` (`done` is the only other option) + } + + /** + * @brief Function called by the standard library when the async is resumed. Its return value is what the whole co_await expression evaluates to + * + * @remark Do not call this manually, use the co_await keyword instead. + * @return The result of the API call as an lvalue reference. + */ + R& await_resume() & noexcept { + return api_callback.get_result(); + } + + + /** + * @brief Function called by the standard library when the async is resumed. Its return value is what the whole co_await expression evaluates to + * + * @remark Do not call this manually, use the co_await keyword instead. + * @return The result of the API call as a const lvalue reference. + */ + const R& await_resume() const& noexcept { + return api_callback.get_result(); + } + + /** + * @brief Function called by the standard library when the async is resumed. Its return value is what the whole co_await expression evaluates to + * + * @remark Do not call this manually, use the co_await keyword instead. + * @return The result of the API call as an rvalue reference. + */ + R&& await_resume() && noexcept { + return std::move(api_callback.get_result()); + } +}; + +} // namespace async + +} // namespace detail + +struct confirmation_callback_t; + +/** @class async async.h coro/async.h + * @brief A co_await-able object handling an API call in parallel with the caller. + * + * This class is the return type of the dpp::cluster::co_* methods, but it can also be created manually to wrap any async call. + * + * @remark - The coroutine may be resumed in another thread, do not rely on thread_local variables. + * @warning - This feature is EXPERIMENTAL. The API may change at any time and there may be bugs. Please report any to GitHub issues or to the D++ Discord server. + * @tparam R The return type of the API call. Defaults to confirmation_callback_t + */ +template +class async : private detail::async::async_base { + /** + * @brief Internal use only base class. It serves to prevent await_suspend and await_resume from being used directly. + * + * @warning For internal use only, do not use. + * @see operator co_await() + */ + friend class detail::async::async_base; + +public: + using detail::async::async_base::async_base; // use async_base's constructors. unfortunately on clang this doesn't include the templated ones so we have to delegate below + using detail::async::async_base::operator=; // use async_base's assignment operator + using detail::async::async_base::await_ready; // expose await_ready as public + + /** + * @brief Construct an async object wrapping an object method, the call is made immediately by forwarding to std::invoke and can be awaited later to retrieve the result. + * + * @param obj The object to call the method on + * @param fun The method of the object to call. Its last parameter must be a callback taking a parameter of type R + * @param args Parameters to pass to the method, excluding the callback + */ + template +#ifndef _DOXYGEN_ + requires std::invocable> +#endif + explicit async(Obj &&obj, Fun &&fun, Args&&... args) : detail::async::async_base{std::forward(obj), std::forward(fun), std::forward(args)...} {} + + /** + * @brief Construct an async object wrapping an invokeable object, the call is made immediately by forwarding to std::invoke and can be awaited later to retrieve the result. + * + * @param fun The object to call using std::invoke. Its last parameter must be a callable taking a parameter of type R + * @param args Parameters to pass to the object, excluding the callback + */ + template +#ifndef _DOXYGEN_ + requires std::invocable> +#endif + explicit async(Fun &&fun, Args&&... args) : detail::async::async_base{std::forward(fun), std::forward(args)...} {} + +#ifdef _DOXYGEN_ // :) + /** + * @brief Construct an empty async. Using `co_await` on an empty async is undefined behavior. + */ + async() noexcept; + + /** + * @brief Destructor. If any callback is pending it will be aborted. + */ + ~async(); + + /** + * @brief Copy constructor is disabled + */ + async(const async &); + + /** + * @brief Move constructor + * + * NOTE: Despite being marked noexcept, this function uses std::lock_guard which may throw. The implementation assumes this can never happen, hence noexcept. Report it if it does, as that would be a bug. + * + * @remark Using the moved-from async after this function is undefined behavior. + * @param other The async object to move the data from. + */ + async(async &&other) noexcept = default; + + /** + * @brief Copy assignment is disabled + */ + async &operator=(const async &) = delete; + + /** + * @brief Move assignment operator. + * + * NOTE: Despite being marked noexcept, this function uses std::lock_guard which may throw. The implementation assumes this can never happen, hence noexcept. Report it if it does, as that would be a bug. + * + * @remark Using the moved-from async after this function is undefined behavior. + * @param other The async object to move the data from + */ + async &operator=(async &&other) noexcept = default; + + /** + * @brief Check whether or not co_await-ing this would suspend the caller, i.e. if we have the result or not + * + * @return bool Whether we already have the result of the API call or not + */ + [[nodiscard]] bool await_ready() const noexcept; +#endif + + /** + * @brief Suspend the caller until the request completes. + * + * @return On resumption, this expression evaluates to the result object of type R, as a reference. + */ + [[nodiscard]] auto& operator co_await() & noexcept { + return static_cast&>(*this); + } + + /** + * @brief Suspend the caller until the request completes. + * + * @return On resumption, this expression evaluates to the result object of type R, as a const reference. + */ + [[nodiscard]] const auto& operator co_await() const & noexcept { + return static_cast const&>(*this); + } + + /** + * @brief Suspend the caller until the request completes. + * + * @return On resumption, this expression evaluates to the result object of type R, as an rvalue reference. + */ + [[nodiscard]] auto&& operator co_await() && noexcept { + return static_cast&&>(*this); + } +}; + +} // namespace dpp + +#endif /* DPP_CORO */ diff --git a/include/dpp/coro/coro.h b/include/dpp/coro/coro.h new file mode 100644 index 0000000..2dd0836 --- /dev/null +++ b/include/dpp/coro/coro.h @@ -0,0 +1,186 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2022 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ + +#ifdef DPP_CORO +#pragma once + +#if (defined(_LIBCPP_VERSION) and !defined(__cpp_impl_coroutine)) // if libc++ experimental implementation (LLVM < 14) +# define STDCORO_EXPERIMENTAL_HEADER +# define STDCORO_EXPERIMENTAL_NAMESPACE +#endif + +#ifdef STDCORO_GLIBCXX_COMPAT +# define __cpp_impl_coroutine 1 +namespace std { + namespace experimental { + using namespace std; + } +} +#endif + +#ifdef STDCORO_EXPERIMENTAL_HEADER +# include +#else +# include +#endif + +namespace dpp { + +/** + * @brief Implementation details for internal use only. + * + * @attention This is only meant to be used by D++ internally. Support will not be given regarding the facilities in this namespace. + */ +namespace detail { +#ifdef _DOXYGEN_ +/** + * @brief Alias for either std or std::experimental depending on compiler and library. Used by coroutine implementation. + * + * @todo Remove and use std when all supported libraries have coroutines in it + */ +namespace std_coroutine {} +#else +# ifdef STDCORO_EXPERIMENTAL_NAMESPACE +namespace std_coroutine = std::experimental; +# else +namespace std_coroutine = std; +# endif +#endif + +#ifndef _DOXYGEN_ +/** + * @brief Concept to check if a type has a useable `operator co_await()` member + */ +template +concept has_co_await_member = requires (T expr) { expr.operator co_await(); }; + +/** + * @brief Concept to check if a type has a useable overload of the free function `operator co_await(expr)` + */ +template +concept has_free_co_await = requires (T expr) { operator co_await(expr); }; + +/** + * @brief Concept to check if a type has useable `await_ready()`, `await_suspend()` and `await_resume()` member functions. + */ +template +concept has_await_members = requires (T expr) { expr.await_ready(); expr.await_suspend(); expr.await_resume(); }; + +/** + * @brief Mimics the compiler's behavior of using co_await. That is, it returns whichever works first, in order : `expr.operator co_await();` > `operator co_await(expr)` > `expr` + */ +template +requires (has_co_await_member) +decltype(auto) co_await_resolve(T&& expr) noexcept(noexcept(expr.operator co_await())) { + decltype(auto) awaiter = expr.operator co_await(); + return awaiter; +} + +/** + * @brief Mimics the compiler's behavior of using co_await. That is, it returns whichever works first, in order : `expr.operator co_await();` > `operator co_await(expr)` > `expr` + */ +template +requires (!has_co_await_member && has_free_co_await) +decltype(auto) co_await_resolve(T&& expr) noexcept(noexcept(operator co_await(expr))) { + decltype(auto) awaiter = operator co_await(static_cast(expr)); + return awaiter; +} + +/** + * @brief Mimics the compiler's behavior of using co_await. That is, it returns whichever works first, in order : `expr.operator co_await();` > `operator co_await(expr)` > `expr` + */ +template +requires (!has_co_await_member && !has_free_co_await) +decltype(auto) co_await_resolve(T&& expr) noexcept { + return static_cast(expr); +} + +#else +/** + * @brief Concept to check if a type has a useable `operator co_await()` member + * + * @note This is actually a C++20 concept but Doxygen doesn't do well with them + */ +template +bool has_co_await_member; + +/** + * @brief Concept to check if a type has a useable overload of the free function `operator co_await(expr)` + * + * @note This is actually a C++20 concept but Doxygen doesn't do well with them + */ +template +bool has_free_co_await; + +/** + * @brief Concept to check if a type has useable `await_ready()`, `await_suspend()` and `await_resume()` member functions. + * + * @note This is actually a C++20 concept but Doxygen doesn't do well with them + */ +template +bool has_await_members; + +/** + * @brief Mimics the compiler's behavior of using co_await. That is, it returns whichever works first, in order : `expr.operator co_await();` > `operator co_await(expr)` > `expr` + * + * This function is conditionally noexcept, if the returned expression also is. + */ +decltype(auto) co_await_resolve(auto&& expr) {} +#endif + +/** + * @brief Convenience alias for the result of a certain awaitable's await_resume. + */ +template +using awaitable_result = decltype(co_await_resolve(std::declval()).await_resume()); + +} // namespace detail + +struct confirmation_callback_t; + +template +class async; + +template +#ifndef _DOXYGEN_ +requires (!std::is_reference_v) +#endif +class task; + +template +class coroutine; + +struct job; + +#ifdef DPP_CORO_TEST +/** + * @brief Allocation count of a certain type, for testing purposes. + * + * @todo Remove when coro is stable + */ +template +inline int coro_alloc_count = 0; +#endif + +} // namespace dpp + +#endif /* DPP_CORO */ + diff --git a/include/dpp/coro/coroutine.h b/include/dpp/coro/coroutine.h new file mode 100644 index 0000000..38b80b9 --- /dev/null +++ b/include/dpp/coro/coroutine.h @@ -0,0 +1,575 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2022 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ + +#ifdef DPP_CORO +#pragma once + +#include "coro.h" + +#include +#include +#include +#include +#include + +namespace dpp { + +namespace detail { + +namespace coroutine { + + template +struct promise_t; + +template +/** + * @brief Alias for the handle_t of a coroutine. + */ +using handle_t = std_coroutine::coroutine_handle>; + +/** + * @brief Base class of dpp::coroutine. + * + * @warn This class should not be used directly by a user, use dpp::coroutine instead. + * @note This class contains all the functions used internally by co_await. It is intentionally opaque and a private base of dpp::coroutine so a user cannot call await_suspend and await_resume directly. + */ +template +class coroutine_base { +protected: + /** + * @brief Promise has friend access for the constructor + */ + friend struct promise_t; + + /** + * @brief Coroutine handle. + */ + detail::coroutine::handle_t handle{nullptr}; + +private: + /** + * @brief Construct from a handle. Internal use only. + */ + coroutine_base(detail::coroutine::handle_t h) : handle{h} {} + +public: + /** + * @brief Default constructor, creates an empty coroutine. + */ + coroutine_base() = default; + + /** + * @brief Copy constructor is disabled + */ + coroutine_base(const coroutine_base &) = delete; + + /** + * @brief Move constructor, grabs another coroutine's handle + * + * @param other Coroutine to move the handle from + */ + coroutine_base(coroutine_base &&other) noexcept : handle(std::exchange(other.handle, nullptr)) {} + + /** + * @brief Destructor, destroys the handle. + */ + ~coroutine_base() { + if (handle) { + handle.destroy(); + } + } + + /** + * @brief Copy assignment is disabled + */ + coroutine_base &operator=(const coroutine_base &) = delete; + + /** + * @brief Move assignment, grabs another coroutine's handle + * + * @param other Coroutine to move the handle from + */ + coroutine_base &operator=(coroutine_base &&other) noexcept { + handle = std::exchange(other.handle, nullptr); + return *this; + } + + /** + * @brief First function called by the standard library when the coroutine is co_await-ed. + * + * @remark Do not call this manually, use the co_await keyword instead. + * @throws invalid_operation_exception if the coroutine is empty or finished. + * @return bool Whether the coroutine is done + */ + [[nodiscard]] bool await_ready() const { + if (!handle) { + throw dpp::logic_exception("cannot co_await an empty coroutine"); + } + return handle.done(); + } + + /** + * @brief Second function called by the standard library when the coroutine is co_await-ed. + * + * Stores the calling coroutine in the promise to resume when this coroutine suspends. + * + * @remark Do not call this manually, use the co_await keyword instead. + * @param caller The calling coroutine, now suspended + */ + template + [[nodiscard]] handle_t await_suspend(detail::std_coroutine::coroutine_handle caller) noexcept { + handle.promise().parent = caller; + return handle; + } + + /** + * @brief Function called by the standard library when the coroutine is resumed. + * + * @remark Do not call this manually, use the co_await keyword instead. + * @throw Throws any exception thrown or uncaught by the coroutine + * @return R The result of the coroutine. It is given to the caller as a result to `co_await` + */ + decltype(auto) await_resume() & { + return static_cast &>(*this).await_resume_impl(); + } + + /** + * @brief Function called by the standard library when the coroutine is resumed. + * + * @remark Do not call this manually, use the co_await keyword instead. + * @throw Throws any exception thrown or uncaught by the coroutine + * @return R The result of the coroutine. It is given to the caller as a result to `co_await` + */ + [[nodiscard]] decltype(auto) await_resume() const & { + return static_cast const&>(*this).await_resume_impl(); + } + + /** + * @brief Function called by the standard library when the coroutine is resumed. + * + * @remark Do not call this manually, use the co_await keyword instead. + * @throw Throws any exception thrown or uncaught by the coroutine + * @return R The result of the coroutine. It is given to the caller as a result to `co_await` + */ + [[nodiscard]] decltype(auto) await_resume() && { + return static_cast &&>(*this).await_resume_impl(); + } +}; + +} // namespace coroutine + +} // namespace detail + +/** @class coroutine coroutine.h coro/coroutine.h + * @brief Base type for a coroutine, starts on co_await. + * + * @warning - This feature is EXPERIMENTAL. The API may change at any time and there may be bugs. + * Please report any to GitHub Issues or to our Discord Server. + * @warning - Using co_await on this object more than once is undefined behavior. + * @tparam R Return type of the coroutine. Can be void, or a complete object that supports move construction and move assignment. + */ +template +class coroutine : private detail::coroutine::coroutine_base { + /** + * @brief Internal use only base class containing common logic between coroutine and coroutine. It also serves to prevent await_suspend and await_resume from being used directly. + * + * @warning For internal use only, do not use. + * @see operator co_await() + */ + friend class detail::coroutine::coroutine_base; + + [[nodiscard]] R& await_resume_impl() & { + detail::coroutine::promise_t &promise = this->handle.promise(); + if (promise.exception) { + std::rethrow_exception(promise.exception); + } + return *promise.result; + } + + [[nodiscard]] const R& await_resume_impl() const & { + detail::coroutine::promise_t &promise = this->handle.promise(); + if (promise.exception) { + std::rethrow_exception(promise.exception); + } + return *promise.result; + } + + [[nodiscard]] R&& await_resume_impl() && { + detail::coroutine::promise_t &promise = this->handle.promise(); + if (promise.exception) { + std::rethrow_exception(promise.exception); + } + return *std::move(promise.result); + } + +public: +#ifdef _DOXYGEN_ // :)))) + /** + * @brief Default constructor, creates an empty coroutine. + */ + coroutine() = default; + + /** + * @brief Copy constructor is disabled + */ + coroutine(const coroutine &) = delete; + + /** + * @brief Move constructor, grabs another coroutine's handle + * + * @param other Coroutine to move the handle from + */ + coroutine(coroutine &&other) noexcept; + + /** + * @brief Destructor, destroys the handle. + */ + ~coroutine(); + + /** + * @brief Copy assignment is disabled + */ + coroutine &operator=(const coroutine &) = delete; + + /** + * @brief Move assignment, grabs another coroutine's handle + * + * @param other Coroutine to move the handle from + */ + coroutine &operator=(coroutine &&other) noexcept; + + /** + * @brief First function called by the standard library when the coroutine is co_await-ed. + * + * @remark Do not call this manually, use the co_await keyword instead. + * @throws invalid_operation_exception if the coroutine is empty or finished. + * @return bool Whether the coroutine is done + */ + [[nodiscard]] bool await_ready() const; +#else + using detail::coroutine::coroutine_base::coroutine_base; // use coroutine_base's constructors + using detail::coroutine::coroutine_base::operator=; // use coroutine_base's assignment operators + using detail::coroutine::coroutine_base::await_ready; // expose await_ready as public +#endif + + /** + * @brief Suspend the caller until the coroutine completes. + * + * @throw On resumption, any exception thrown by the coroutine is propagated to the caller. + * @return On resumption, this expression evaluates to the result object of type R, as a reference. + */ + [[nodiscard]] auto& operator co_await() & noexcept { + return static_cast&>(*this); + } + + /** + * @brief Suspend the caller until the coroutine completes. + * + * @throw On resumption, any exception thrown by the coroutine is propagated to the caller. + * @return On resumption, this expression evaluates to the result object of type R, as a const reference. + */ + [[nodiscard]] const auto& operator co_await() const & noexcept { + return static_cast const&>(*this); + } + + /** + * @brief Suspend the caller until the coroutine completes. + * + * @throw On resumption, any exception thrown by the coroutine is propagated to the caller. + * @return On resumption, this expression evaluates to the result object of type R, as an rvalue reference. + */ + [[nodiscard]] auto&& operator co_await() && noexcept { + return static_cast&&>(*this); + } +}; + +#ifndef _DOXYGEN_ // don't generate this on doxygen because `using` doesn't work and 2 copies of coroutine_base's docs is enough +/** + * @brief Base type for a coroutine, starts on co_await. + * + * @warning - This feature is EXPERIMENTAL. The API may change at any time and there may be bugs. Please report any to GitHub issues or to the D++ Discord server. + * @warning - Using co_await on this object more than once is undefined behavior. + * @tparam R Return type of the coroutine. Can be void, or a complete object that supports move construction and move assignment. + */ +template <> +class coroutine : private detail::coroutine::coroutine_base { + /** + * @brief Base class has friend access for CRTP downcast + */ + friend class detail::coroutine::coroutine_base; + + void await_resume_impl() const; + +public: + using detail::coroutine::coroutine_base::coroutine_base; // use coroutine_base's constructors + using detail::coroutine::coroutine_base::operator=; // use coroutine_base's assignment operators + using detail::coroutine::coroutine_base::await_ready; // expose await_ready as public + + /** + * @brief Suspend the current coroutine until the coroutine completes. + * + * @throw On resumption, any exception thrown by the coroutine is propagated to the caller. + * @return On resumption, this expression evaluates to the result object of type R, as a reference. + */ + [[nodiscard]] auto& operator co_await() & noexcept { + return static_cast&>(*this); + } + + /** + * @brief Suspend the current coroutine until the coroutine completes. + * + * @throw On resumption, any exception thrown by the coroutine is propagated to the caller. + * @return On resumption, this expression evaluates to the result object of type R, as a const reference. + */ + [[nodiscard]] const auto& operator co_await() const & noexcept { + return static_cast const &>(*this); + } + + /** + * @brief Suspend the current coroutine until the coroutine completes. + * + * @throw On resumption, any exception thrown by the coroutine is propagated to the caller. + * @return On resumption, this expression evaluates to the result object of type R, as an rvalue reference. + */ + [[nodiscard]] auto&& operator co_await() && noexcept { + return static_cast&&>(*this); + } +}; +#endif /* _DOXYGEN_ */ + +namespace detail::coroutine { + template + struct final_awaiter; + +#ifdef DPP_CORO_TEST + struct promise_t_base{}; +#endif + + /** + * @brief Promise type for coroutine. + */ + template + struct promise_t { + /** + * @brief Handle of the coroutine co_await-ing this coroutine. + */ + std_coroutine::coroutine_handle<> parent{nullptr}; + + /** + * @brief Return value of the coroutine + */ + std::optional result{}; + + /** + * @brief Pointer to an uncaught exception thrown by the coroutine + */ + std::exception_ptr exception{nullptr}; + +#ifdef DPP_CORO_TEST + promise_t() { + ++coro_alloc_count; + } + + ~promise_t() { + --coro_alloc_count; + } +#endif + + /** + * @brief Function called by the standard library when reaching the end of a coroutine + * + * @return final_awaiter Resumes any coroutine co_await-ing on this + */ + [[nodiscard]] final_awaiter final_suspend() const noexcept; + + /** + * @brief Function called by the standard library when the coroutine start + * + * @return @return std::suspend_always Always suspend at the start, for a lazy start + */ + [[nodiscard]] std_coroutine::suspend_always initial_suspend() const noexcept { + return {}; + } + + /** + * @brief Function called when an exception escapes the coroutine + * + * Stores the exception to throw to the co_await-er + */ + void unhandled_exception() noexcept { + exception = std::current_exception(); + } + + /** + * @brief Function called by the standard library when the coroutine co_returns a value. + * + * Stores the value internally to hand to the caller when it resumes. + * + * @param expr The value given to co_return + */ + void return_value(R&& expr) noexcept(std::is_nothrow_move_constructible_v) requires std::move_constructible { + result = static_cast(expr); + } + + /** + * @brief Function called by the standard library when the coroutine co_returns a value. + * + * Stores the value internally to hand to the caller when it resumes. + * + * @param expr The value given to co_return + */ + void return_value(const R &expr) noexcept(std::is_nothrow_copy_constructible_v) requires std::copy_constructible { + result = expr; + } + + /** + * @brief Function called by the standard library when the coroutine co_returns a value. + * + * Stores the value internally to hand to the caller when it resumes. + * + * @param expr The value given to co_return + */ + template + requires (!std::is_same_v> && std::convertible_to) + void return_value(T&& expr) noexcept (std::is_nothrow_convertible_v) { + result = std::forward(expr); + } + + /** + * @brief Function called to get the coroutine object + */ + dpp::coroutine get_return_object() { + return dpp::coroutine{handle_t::from_promise(*this)}; + } + }; + + /** + * @brief Struct returned by a coroutine's final_suspend, resumes the continuation + */ + template + struct final_awaiter { + /** + * @brief First function called by the standard library when reaching the end of a coroutine + * + * @return false Always return false, we need to suspend to resume the parent + */ + [[nodiscard]] bool await_ready() const noexcept { + return false; + } + + /** + * @brief Second function called by the standard library when reaching the end of a coroutine. + * + * @return std::handle_t<> Coroutine handle to resume, this is either the parent if present or std::noop_coroutine() + */ + [[nodiscard]] std_coroutine::coroutine_handle<> await_suspend(std_coroutine::coroutine_handle> handle) const noexcept { + auto parent = handle.promise().parent; + + return parent ? parent : std_coroutine::noop_coroutine(); + } + + /** + * @brief Function called by the standard library when this object is resumed + */ + void await_resume() const noexcept {} + }; + + template + final_awaiter promise_t::final_suspend() const noexcept { + return {}; + } + + /** + * @brief Struct returned by a coroutine's final_suspend, resumes the continuation + */ + template <> + struct promise_t { + /** + * @brief Handle of the coroutine co_await-ing this coroutine. + */ + std_coroutine::coroutine_handle<> parent{nullptr}; + + /** + * @brief Pointer to an uncaught exception thrown by the coroutine + */ + std::exception_ptr exception{nullptr}; + + /** + * @brief Function called by the standard library when reaching the end of a coroutine + * + * @return final_awaiter Resumes any coroutine co_await-ing on this + */ + [[nodiscard]] final_awaiter final_suspend() const noexcept { + return {}; + } + + /** + * @brief Function called by the standard library when the coroutine start + * + * @return @return std::suspend_always Always suspend at the start, for a lazy start + */ + [[nodiscard]] std_coroutine::suspend_always initial_suspend() const noexcept { + return {}; + } + + /** + * @brief Function called when an exception escapes the coroutine + * + * Stores the exception to throw to the co_await-er + */ + void unhandled_exception() noexcept { + exception = std::current_exception(); + } + + /** + * @brief Function called when co_return is used + */ + void return_void() const noexcept {} + + /** + * @brief Function called to get the coroutine object + */ + [[nodiscard]] dpp::coroutine get_return_object() { + return dpp::coroutine{handle_t::from_promise(*this)}; + } + }; + +} // namespace detail + +#ifndef _DOXYGEN_ +inline void coroutine::await_resume_impl() const { + if (handle.promise().exception) { + std::rethrow_exception(handle.promise().exception); + } +} +#endif /* _DOXYGEN_ */ + +} // namespace dpp + +/** + * @brief Specialization of std::coroutine_traits, helps the standard library figure out a promise type from a coroutine function. + */ +template +struct dpp::detail::std_coroutine::coroutine_traits, Args...> { + using promise_type = dpp::detail::coroutine::promise_t; +}; + +#endif /* DPP_CORO */ diff --git a/include/dpp/coro/job.h b/include/dpp/coro/job.h new file mode 100644 index 0000000..e2473f3 --- /dev/null +++ b/include/dpp/coro/job.h @@ -0,0 +1,162 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2022 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ + +#ifdef DPP_CORO +#pragma once + +#include "coro.h" + +#include +#include + +namespace dpp { + +/** @class job job.h coro/job.h + * @brief Extremely light coroutine object designed to send off a coroutine to execute on its own. + * Can be used in conjunction with coroutine events via @ref dpp::event_router_t::operator()(F&&) "event routers", or on its own. + * + * This object stores no state and is the recommended way to use coroutines if you do not need to co_await the result. + * + * @warning - This feature is EXPERIMENTAL. The API may change at any time and there may be bugs. + * Please report any to GitHub Issues or to our Discord Server. + * @warning - It cannot be co_awaited, which means the second it co_awaits something, the program jumps back to the calling function, which continues executing. + * At this point, if the function returns, every object declared in the function including its parameters are destroyed, which causes @ref lambdas-and-locals "dangling references". + * For this reason, `co_await` will error if any parameters are passed by reference. + * If you must pass a reference, pass it as a pointer or with std::ref, but you must fully understand the reason behind this warning, and what to avoid. + * If you prefer a safer type, use `coroutine` for synchronous execution, or `task` for parallel tasks, and co_await them. + */ +struct job {}; + +namespace detail { + +namespace job { + +template +inline constexpr bool coroutine_has_no_ref_params_v = false; + +template <> +inline constexpr bool coroutine_has_no_ref_params_v<> = true; + +template +inline constexpr bool coroutine_has_no_ref_params_v = (std::is_invocable_v || !std::is_reference_v) && (!std::is_reference_v && ... && true); + +#ifdef DPP_CORO_TEST + struct promise{}; +#endif + +/** + * @brief Coroutine promise type for a job + */ +template +struct promise { + +#ifdef DPP_CORO_TEST + promise() { + ++coro_alloc_count; + } + + ~promise() { + --coro_alloc_count; + } +#endif + + /* + * @brief Function called when the job is done. + * + * @return std::suspend_never Do not suspend at the end, destroying the handle immediately + */ + std_coroutine::suspend_never final_suspend() const noexcept { + return {}; + } + + /* + * @brief Function called when the job is started. + * + * @return std::suspend_never Do not suspend at the start, starting the job immediately + */ + std_coroutine::suspend_never initial_suspend() const noexcept { + return {}; + } + + /** + * @brief Function called to get the job object + * + * @return job + */ + dpp::job get_return_object() const noexcept { + return {}; + } + + /** + * @brief Function called when an exception is thrown and not caught. + * + * @throw Immediately rethrows the exception to the caller / resumer + */ + void unhandled_exception() const { + throw; + } + + /** + * @brief Function called when the job returns. Does nothing. + */ + void return_void() const noexcept {} + + /** + * @brief Function that will wrap every co_await inside of the job. + */ + template + T await_transform(T &&expr) const noexcept { + /** + * `job` is extremely efficient as a coroutine but this comes with drawbacks : + * It cannot be co_awaited, which means the second it co_awaits something, the program jumps back to the calling function, which continues executing. + * At this point, if the function returns, every object declared in the function including its parameters are destroyed, which causes dangling references. + * This is exactly the same problem as references in lambdas : https://dpp.dev/lambdas-and-locals.html. + * + * If you must pass a reference, pass it as a pointer or with std::ref, but you must fully understand the reason behind this warning, and what to avoid. + * If you prefer a safer type, use `coroutine` for synchronous execution, or `task` for parallel tasks, and co_await them. + */ + static_assert(coroutine_has_no_ref_params_v, "co_await is disabled in dpp::job when taking parameters by reference. read comment above this line for more info"); + + return std::forward(expr); + } +}; + +} // namespace job + +} // namespace detail + +} // namespace dpp + +/** + * @brief Specialization of std::coroutine_traits, helps the standard library figure out a promise type from a coroutine function. + */ +template +struct dpp::detail::std_coroutine::coroutine_traits { + /** + * @brief Promise type for this coroutine signature. + * + * When the coroutine is created from a lambda, that lambda is passed as a first parameter. + * Not ideal but we'll allow any callable that takes the rest of the arguments passed + */ + using promise_type = dpp::detail::job::promise; +}; + +#endif /* DPP_CORO */ diff --git a/include/dpp/coro/task.h b/include/dpp/coro/task.h new file mode 100644 index 0000000..5960e0b --- /dev/null +++ b/include/dpp/coro/task.h @@ -0,0 +1,760 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2022 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ + +#ifdef DPP_CORO +#pragma once + +#include "coro.h" + +#include +#include +#include +#include +#include +#include +#include + +#include // std::cerr in final_suspend + +namespace dpp { + +namespace detail { + +/* Internal cogwheels for dpp::task */ +namespace task { + +/** @brief State of a task */ +enum class state_t { + /** @brief Task was started but never co_await-ed */ + started, + /** @brief Task was co_await-ed and is pending completion */ + awaited, + /** @brief Task is completed */ + done, + /** @brief Task is still running but the actual dpp::task object is destroyed */ + dangling +}; + +/** + * @brief A @ref dpp::task "task"'s promise_t type, with special logic for handling nested tasks. + * + * @tparam R Return type of the task + */ +template +struct promise_t; + +/** + * @brief The object automatically co_await-ed at the end of a @ref dpp::task "task". Ensures nested coroutine chains are resolved, and the promise_t cleans up if it needs to. + * + * @tparam R Return type of the task + */ +template +struct final_awaiter; + +/** + * @brief Alias for for a @ref dpp::task "task"'s @ref promise_t. + * + * @tparam R Return type of the task + */ +template +using handle_t = std_coroutine::coroutine_handle>; + +/** + * @brief Base class of @ref dpp::task. + * + * @warning This class should not be used directly by a user, use @ref dpp::task instead. + * @note This class contains all the functions used internally by co_await. It is intentionally opaque and a private base of @ref dpp::task so a user cannot call await_suspend() and await_resume() directly. + */ +template +class task_base { +protected: + /** + * @brief The coroutine handle of this task. + */ + handle_t handle; + + /** + * @brief Promise type of this coroutine. For internal use only, do not use. + */ + friend struct promise_t; + +private: + /** + * @brief Construct from a coroutine handle. Internal use only + */ + explicit task_base(handle_t handle_) : handle(handle_) {} + +public: + /** + * @brief Default constructor, creates a task not bound to a coroutine. + */ + task_base() = default; + + /** + * @brief Copy constructor is disabled + */ + task_base(const task_base &) = delete; + + /** + * @brief Move constructor, grabs another task's coroutine handle + * + * @param other Task to move the handle from + */ + task_base(task_base &&other) noexcept : handle(std::exchange(other.handle, nullptr)) {} + + /** + * @brief Destructor. + * + * Destroys the handle. + * @warning The coroutine must be finished before this is called, otherwise it runs the risk of being resumed after it is destroyed, resuming in use-after-free undefined behavior. + */ + ~task_base() { + if (handle) { + promise_t &promise = handle.promise(); + state_t previous_state = promise.state.exchange(state_t::dangling); + + if (previous_state == state_t::done) { + handle.destroy(); + } + else { + cancel(); + } + } + } + + /** + * @brief Copy assignment is disabled + */ + task_base &operator=(const task_base &) = delete; + + /** + * @brief Move assignment, grabs another task's coroutine handle + * + * @param other Task to move the handle from + */ + task_base &operator=(task_base &&other) noexcept { + handle = std::exchange(other.handle, nullptr); + return (*this); + } + + /** + * @brief Check whether or not a call to co_await will suspend the caller. + * + * This function is called by the standard library as a first step when using co_await. If it returns true then the caller is not suspended. + * @throws logic_exception if the task is empty. + * @return bool Whether not to suspend the caller or not + */ + [[nodiscard]] bool await_ready() const { + if (!handle) { + throw dpp::logic_exception{"cannot co_await an empty task"}; + } + return handle.promise().state.load() == state_t::done; + } + + /** + * @brief Second function called by the standard library when the task is co_await-ed, if await_ready returned false. + * + * Stores the calling coroutine in the promise to resume when this task suspends. + * + * @remark Do not call this manually, use the co_await keyword instead. + * @param caller The calling coroutine, now suspended + * @return bool Whether to suspend the caller or not + */ + [[nodiscard]] bool await_suspend(std_coroutine::coroutine_handle<> caller) noexcept { + promise_t &my_promise = handle.promise(); + auto previous_state = state_t::started; + + my_promise.parent = caller; + // Replace `sent` state with `awaited` ; if that fails, the only logical option is the state was `done`, in which case return false to resume + if (!handle.promise().state.compare_exchange_strong(previous_state, state_t::awaited) && previous_state == state_t::done) { + return false; + } + return true; + } + + /** + * @brief Function to check if the task has finished its execution entirely + * + * @return bool Whether the task is finished. + */ + [[nodiscard]] bool done() const noexcept { + return handle && handle.promise().state.load(std::memory_order_relaxed) == state_t::done; + } + + /** + * @brief Cancel the task, it will stop the next time it uses co_await. On co_await-ing this task, throws dpp::task_cancelled_exception. + * + * @return *this + */ + dpp::task& cancel() & noexcept { + handle.promise().cancelled.exchange(true, std::memory_order_relaxed); + return static_cast &>(*this); + } + + /** + * @brief Cancel the task, it will stop the next time it uses co_await. On co_await-ing this task, throws dpp::task_cancelled_exception. + * + * @return *this + */ + dpp::task&& cancel() && noexcept { + handle.promise().cancelled.exchange(true, std::memory_order_relaxed); + return static_cast &&>(*this); + } + + /** + * @brief Function called by the standard library when resuming. + * + * @return Return value of the coroutine, handed to the caller of co_await. + */ + decltype(auto) await_resume() & { + return static_cast &>(*this).await_resume_impl(); + } + + /** + * @brief Function called by the standard library when resuming. + * + * @return Return value of the coroutine, handed to the caller of co_await. + */ + decltype(auto) await_resume() const & { + return static_cast &>(*this).await_resume_impl(); + } + + /** + * @brief Function called by the standard library when resuming. + * + * @return Return value of the coroutine, handed to the caller of co_await. + */ + decltype(auto) await_resume() && { + return static_cast &&>(*this).await_resume_impl(); + } +}; + +} // namespace task + +} // namespace detail + +/** @class task task.h coro/task.h + * @brief A coroutine task. It starts immediately on construction and can be co_await-ed, making it perfect for parallel coroutines returning a value. + * + * @warning - This feature is EXPERIMENTAL. The API may change at any time and there may be bugs. + * Please report any to GitHub Issues or to our Discord Server. + * @tparam R Return type of the task. Cannot be a reference but can be void. + */ +template +#ifndef _DOXYGEN_ +requires (!std::is_reference_v) +#endif +class task : private detail::task::task_base { + /** + * @brief Internal use only base class containing common logic between task and task. It also serves to prevent await_suspend and await_resume from being used directly. + * + * @warning For internal use only, do not use. + * @see operator co_await() + */ + friend class detail::task::task_base; + + /** + * @brief Function called by the standard library when the coroutine is resumed. + * + * @throw Throws any exception thrown or uncaught by the coroutine + * @return The result of the coroutine. This is returned to the awaiter as the result of co_await + */ + R& await_resume_impl() & { + detail::task::promise_t &promise = this->handle.promise(); + if (promise.exception) { + std::rethrow_exception(promise.exception); + } + return *reinterpret_cast(promise.result_storage.data()); + } + + /** + * @brief Function called by the standard library when the coroutine is resumed. + * + * @throw Throws any exception thrown or uncaught by the coroutine + * @return The result of the coroutine. This is returned to the awaiter as the result of co_await + */ + const R& await_resume_impl() const & { + detail::task::promise_t &promise = this->handle.promise(); + if (promise.exception) { + std::rethrow_exception(promise.exception); + } + return *reinterpret_cast(promise.result_storage.data()); + } + + /** + * @brief Function called by the standard library when the coroutine is resumed. + * + * @throw Throws any exception thrown or uncaught by the coroutine + * @return The result of the coroutine. This is returned to the awaiter as the result of co_await + */ + R&& await_resume_impl() && { + detail::task::promise_t &promise = this->handle.promise(); + if (promise.exception) { + std::rethrow_exception(promise.exception); + } + return *reinterpret_cast(promise.result_storage.data()); + } + +public: +#ifdef _DOXYGEN_ // :) + /** + * @brief Default constructor, creates a task not bound to a coroutine. + */ + task() = default; + + /** + * @brief Copy constructor is disabled + */ + task(const task &) = delete; + + /** + * @brief Move constructor, grabs another task's coroutine handle + * + * @param other Task to move the handle from + */ + task(task &&other) noexcept; + + /** + * @brief Destructor. + * + * Destroys the handle. + * @warning The coroutine must be finished before this is called, otherwise it runs the risk of being resumed after it is destroyed, resuming in use-after-free undefined behavior. + */ + ~task(); + + /** + * @brief Copy assignment is disabled + */ + task &operator=(const task &) = delete; + + /** + * @brief Move assignment, grabs another task's coroutine handle + * + * @param other Task to move the handle from + */ + task &operator=(task &&other) noexcept; + + /** + * @brief Function to check if the task has finished its execution entirely + * + * @return bool Whether the task is finished. + */ + [[nodiscard]] bool done() const noexcept; + + /** + * @brief Cancel the task, it will stop the next time it uses co_await. On co_await-ing this task, throws dpp::task_cancelled_exception. + */ + dpp::task& cancel() & noexcept; + + /** + * @brief Check whether or not a call to co_await will suspend the caller. + * + * This function is called by the standard library as a first step when using co_await. If it returns true then the caller is not suspended. + * @throws logic_exception if the task is empty. + * @return bool Whether not to suspend the caller or not + */ + [[nodiscard]] bool await_ready() const; +#else + using detail::task::task_base::task_base; // use task_base's constructors + using detail::task::task_base::operator=; // use task_base's assignment operators + using detail::task::task_base::done; // expose done() as public + using detail::task::task_base::cancel; // expose cancel() as public + using detail::task::task_base::await_ready; // expose await_ready as public +#endif + + /** + * @brief Suspend the current coroutine until the task completes. + * + * @throw On resumption, any exception thrown by the coroutine is propagated to the caller. + * @return On resumption, this expression evaluates to the result object of type R, as a reference. + */ + [[nodiscard]] auto& operator co_await() & noexcept { + return static_cast&>(*this); + } + + /** + * @brief Suspend the current coroutine until the task completes. + * + * @throw On resumption, any exception thrown by the coroutine is propagated to the caller. + * @return On resumption, this expression evaluates to the result object of type R, as a const reference. + */ + [[nodiscard]] const auto& operator co_await() const & noexcept { + return static_cast&>(*this); + } + + /** + * @brief Suspend the current coroutine until the task completes. + * + * @throw On resumption, any exception thrown by the coroutine is propagated to the caller. + * @return On resumption, this expression evaluates to the result object of type R, as an rvalue reference. + */ + [[nodiscard]] auto&& operator co_await() && noexcept { + return static_cast&&>(*this); + } +}; + +#ifndef _DOXYGEN_ // don't generate this on doxygen because `using` doesn't work and 2 copies of coroutine_base's docs is enough +/** + * @brief A coroutine task. It starts immediately on construction and can be co_await-ed, making it perfect for parallel coroutines returning a value. + * + * Can be used in conjunction with coroutine events via dpp::event_router_t::co_attach, or on its own. + * + * @warning - This feature is EXPERIMENTAL. The API may change at any time and there may be bugs. Please report any to GitHub issues or to the D++ Discord server. + * @tparam R Return type of the coroutine. Cannot be a reference, can be void. + */ +template <> +class task : private detail::task::task_base { + /** + * @brief Private base class containing common logic between task and task. It also serves to prevent await_suspend and await_resume from being used directly. + * + * @see operator co_await() + */ + friend class detail::task::task_base; + + /** + * @brief Function called by the standard library when the coroutine is resumed. + * + * @remark Do not call this manually, use the co_await keyword instead. + * @throw Throws any exception thrown or uncaught by the coroutine + */ + void await_resume_impl() const; + +public: + using detail::task::task_base::task_base; // use task_base's constructors + using detail::task::task_base::operator=; // use task_base's assignment operators + using detail::task::task_base::done; // expose done() as public + using detail::task::task_base::cancel; // expose cancel() as public + using detail::task::task_base::await_ready; // expose await_ready as public + + /** + * @brief Suspend the current coroutine until the task completes. + * + * @throw On resumption, any exception thrown by the coroutine is propagated to the caller. + * @return On resumption, returns a reference to the contained result. + */ + auto& operator co_await() & { + return static_cast&>(*this); + } + + /** + * @brief Suspend the current coroutine until the task completes. + * + * @throw On resumption, any exception thrown by the coroutine is propagated to the caller. + * @return On resumption, returns a const reference to the contained result. + */ + const auto& operator co_await() const & { + return static_cast&>(*this); + } + + /** + * @brief Suspend the current coroutine until the task completes. + * + * @throw On resumption, any exception thrown by the coroutine is propagated to the caller. + * @return On resumption, returns a reference to the contained result. + */ + auto&& operator co_await() && { + return static_cast&&>(*this); + } +}; +#endif /* _DOXYGEN_ */ + +namespace detail::task { + /** + * @brief Awaitable returned from task::promise_t's final_suspend. Resumes the parent and cleans up its handle if needed + */ +template +struct final_awaiter { + /** + * @brief Always suspend at the end of the task. This allows us to clean up and resume the parent + */ + [[nodiscard]] bool await_ready() const noexcept { + return (false); + } + + /* + * @brief The suspension logic of the coroutine when it finishes. Always suspend the caller, meaning cleaning up the handle is on us + * + * @param handle The handle of this coroutine + * @return std::coroutine_handle<> Handle to resume, which is either the parent if present or std::noop_coroutine() otherwise + */ + [[nodiscard]] std_coroutine::coroutine_handle<> await_suspend(handle_t handle) const noexcept; + + /* + * @brief Function called when this object is co_awaited by the standard library at the end of final_suspend. Do nothing, return nothing + */ + void await_resume() const noexcept {} +}; + +/** + * @brief Base implementation of task::promise_t, without the logic that would depend on the return type. Meant to be inherited from + */ +struct promise_base { + /** + * @brief State of the task, used to keep track of lifetime and status + */ + std::atomic state = state_t::started; + + /** + * @brief Whether the task is cancelled or not. + */ + std::atomic cancelled = false; + + /** + * @brief Parent coroutine to return to for nested coroutines. + */ + detail::std_coroutine::coroutine_handle<> parent = nullptr; + + /** + * @brief Exception ptr if any was thrown during the coroutine + * + * @see std::suspend_never Don't suspend, the coroutine starts immediately. + */ + [[nodiscard]] std_coroutine::suspend_never initial_suspend() const noexcept { + return {}; + } + + /** + * @brief Function called by the standard library when an exception is thrown and not caught in the coroutine. + * + * Stores the exception pointer to rethrow on co_await. If the task object is destroyed and was not cancelled, throw instead + */ + void unhandled_exception() { + exception = std::current_exception(); + if ((state.load() == task::state_t::dangling) && !cancelled) { + throw; + } + } + + /** + * @brief Proxy awaitable that wraps any co_await inside the task and checks for cancellation on resumption + * + * @see await_transform + */ + template + struct proxy_awaiter { + /** @brief The promise_t object bound to this proxy */ + const task::promise_base &promise; + + /** @brief The inner awaitable being awaited */ + A awaitable; + + /** @brief Wrapper for the awaitable's await_ready */ + [[nodiscard]] bool await_ready() noexcept(noexcept(awaitable.await_ready())) { + return awaitable.await_ready(); + } + + /** @brief Wrapper for the awaitable's await_suspend */ + template + [[nodiscard]] decltype(auto) await_suspend(T&& handle) noexcept(noexcept(awaitable.await_suspend(std::forward(handle)))) { + return awaitable.await_suspend(std::forward(handle)); + } + + /** + * @brief Wrapper for the awaitable's await_resume, throws if the task is cancelled + * + * @throw dpp::task_cancelled_exception If the task was cancelled + */ + decltype(auto) await_resume() { + if (promise.cancelled.load()) { + throw dpp::task_cancelled_exception{"task was cancelled"}; + } + return awaitable.await_resume(); + } + }; + + /** + * @brief Function called whenever co_await is used inside of the task + * + * @throw dpp::task_cancelled_exception On resumption if the task was cancelled + * + * @return @ref proxy_awaiter Returns a proxy awaiter that will check for cancellation on resumption + */ + template + [[nodiscard]] auto await_transform(T&& expr) const noexcept(noexcept(co_await_resolve(std::forward(expr)))) { + using awaitable_t = decltype(co_await_resolve(std::forward(expr))); + return proxy_awaiter{*this, co_await_resolve(std::forward(expr))}; + } +}; + +/** + * @brief Implementation of task::promise_t for non-void return type + */ +template +struct promise_t : promise_base { + /** + * @brief Destructor. Destroys the value if it was constructed. + */ + ~promise_t() { + if (state.load() == state_t::done && !exception) { + std::destroy_at(reinterpret_cast(result_storage.data())); + } + } + + /** + * @brief Stored return value of the coroutine. + * + */ + alignas(R) std::array result_storage; + + /** + * @brief Function called by the standard library when the coroutine co_returns a value. + * + * Stores the value internally to hand to the caller when it resumes. + * + * @param expr The value given to co_return + */ + void return_value(R&& expr) noexcept(std::is_nothrow_move_constructible_v) requires std::move_constructible { + std::construct_at(reinterpret_cast(result_storage.data()), static_cast(expr)); + } + + /** + * @brief Function called by the standard library when the coroutine co_returns a value. + * + * Stores the value internally to hand to the caller when it resumes. + * + * @param expr The value given to co_return + */ + void return_value(const R &expr) noexcept(std::is_nothrow_copy_constructible_v) requires std::copy_constructible { + std::construct_at(reinterpret_cast(result_storage.data()), expr); + } + + /** + * @brief Function called by the standard library when the coroutine co_returns a value. + * + * Stores the value internally to hand to the caller when it resumes. + * + * @param expr The value given to co_return + */ + template + requires (!std::is_same_v> && std::convertible_to) + void return_value(T&& expr) noexcept (std::is_nothrow_convertible_v) { + std::construct_at(reinterpret_cast(result_storage.data()), std::forward(expr)); + } + + /** + * @brief Function called by the standard library when the coroutine is created. + * + * @return dpp::task The coroutine object + */ + [[nodiscard]] dpp::task get_return_object() noexcept { + return dpp::task{handle_t::from_promise(*this)}; + } + + /** + * @brief Function called by the standard library when the coroutine reaches its last suspension point + * + * @return final_awaiter Special object containing the chain resolution and clean-up logic. + */ + [[nodiscard]] final_awaiter final_suspend() const noexcept { + return {}; + } +}; + +/** + * @brief Implementation of task::promise_t for void return type + */ +template <> +struct promise_t : promise_base { + /** + * @brief Function called by the standard library when the coroutine co_returns + * + * Does nothing but is required by the standard library. + */ + void return_void() const noexcept {} + + /** + * @brief Function called by the standard library when the coroutine is created. + * + * @return task The coroutine object + */ + [[nodiscard]] dpp::task get_return_object() noexcept { + return dpp::task{handle_t::from_promise(*this)}; + } + + /** + * @brief Function called by the standard library when the coroutine reaches its last suspension point + * + * @return final_awaiter Special object containing the chain resolution and clean-up logic. + */ + [[nodiscard]] final_awaiter final_suspend() const noexcept { + return {}; + } +}; + +template +std_coroutine::coroutine_handle<> final_awaiter::await_suspend(handle_t handle) const noexcept { + promise_t &promise = handle.promise(); + state_t previous_state = promise.state.exchange(state_t::done); + + switch (previous_state) { + case state_t::started: // started but never awaited, suspend + return std_coroutine::noop_coroutine(); + case state_t::awaited: // co_await-ed, resume parent + return promise.parent; + case state_t::dangling: // task object is gone, free the handle + handle.destroy(); + return std_coroutine::noop_coroutine(); + case state_t::done: // what + // this should never happen. log it. we don't have a cluster so just write it on cerr + std::cerr << "dpp::task: final_suspend called twice. something went very wrong here, please report to GitHub issues or the D++ Discord server" << std::endl; + } + // TODO: replace with __builtin_unreachable when we confirm this never happens with normal usage + return std_coroutine::noop_coroutine(); +} + +} // namespace detail::task + +#ifndef _DOXYGEN_ +inline void task::await_resume_impl() const { + if (handle.promise().exception) { + std::rethrow_exception(handle.promise().exception); + } +} +#endif /* _DOXYGEN_ */ + +} // namespace dpp + +/** + * @brief Specialization of std::coroutine_traits, helps the standard library figure out a promise_t type from a coroutine function. + */ +template +struct dpp::detail::std_coroutine::coroutine_traits, Args...> { + using promise_type = dpp::detail::task::promise_t; +}; + +#endif /* DPP_CORO */ diff --git a/include/dpp/coro/when_any.h b/include/dpp/coro/when_any.h new file mode 100644 index 0000000..96952c1 --- /dev/null +++ b/include/dpp/coro/when_any.h @@ -0,0 +1,483 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2022 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ + +#ifdef DPP_CORO +#pragma once + +#include "coro.h" +#include "job.h" + +#include +#include +#include +#include +#include + +namespace dpp { + +template +class event_router_t; + +namespace detail { + +namespace event_router { + +template +class awaitable; + +} + +/** @brief Internal cogwheels for dpp::when_any */ +namespace when_any { + +/** @brief Current state of a when_any object */ +enum class await_state { + /** @brief Object was started but not awaited */ + started, + /** @brief Object is being awaited */ + waiting, + /** @brief Object was resumed*/ + done, + /** @brief Object was destroyed */ + dangling +}; + +/** + * @brief Type trait helper to obtain the actual type that will be used by a when_any when a type is passed as a parameter. + * May specialize for certain types for specific behavior, e.g. for an event_router, store the awaitable directly + */ +template +struct arg_helper_s { + /** Raw type of the awaitable */ + using type = T; + + /** Helper static method to get the awaitable from a variable */ + static decltype(auto) get(auto&& v) { + return static_cast(v); + } +}; + +template +struct arg_helper_s> { + using type = event_router::awaitable; + + template +#ifndef _DOXYGEN + requires (std::same_as, dpp::event_router_t>) +#endif + static event_router::awaitable get(U&& v) { + return static_cast(v).operator co_await(); + } +}; + +/** + * @brief Alias for the actual type that an awaitable will be stored as in a when_any. + * For example if given an event_router, store the awaitable, not the event_router. + */ +template +using awaitable_type = typename arg_helper_s::type; + +/** + * @brief Helper struct with a method to convert an awaitable parameter to the actual value it will be stored as. + * For example if given an event_router, store the awaitable, not the event_router. + */ +template +using arg_helper = arg_helper_s>; + +/** @brief Empty result from void-returning awaitable */ +struct empty{}; + +/** @brief Actual type a result will be stores as in when_any */ +template +using storage_type = std::conditional_t, empty, T>; + +/** @brief Concept satisfied if a stored result is void */ +template +concept void_result = std::same_as; + +} + +} // namespace detail + +/** @class when_any when_any.h coro/when_any.h + * @brief Experimental class to co_await on a bunch of awaitable objects, resuming when the first one completes. + * On completion, returns a @ref result object that contains the index of the awaitable that finished first. + * A user can call @ref result::index() and @ref result::get() on the result object to get the result, similar to std::variant. + * + * @see when_any::result + * @tparam Args... Type of each awaitable to await on + */ +template +#ifndef _DOXYGEN_ +requires (sizeof...(Args) >= 1) +#endif +class when_any { + /** @brief Alias for the type of the result variant */ + using variant_type = std::variant>>...>; + + /** + * @brief Alias for the result type of the Nth arg. + * + * @tparam N index of the argument to fetch + */ + template + using result_t = std::variant_alternative_t; + + /** + * @brief State shared between all the jobs to spawn + */ + struct state_t { + /** + * @brief Constructor for the internal state. Its arguments are used to construct each awaitable + */ + template + state_t(Args_&&... args) : awaitables{std::forward(args)...} {} + + /** + * @brief Awaitable objects to handle. + */ + std::tuple awaitables; + + /** + * @brief Result or exception, as a variant. This will contain the result of the first awaitable to finish + */ + variant_type result{}; + + /** + * @brief Coroutine handle to resume after finishing an awaitable + */ + detail::std_coroutine::coroutine_handle<> handle{}; + + /** + * @brief Index of the awaitable that finished. Initialized to the maximum value of std::size_t. + */ + size_t index_finished = std::numeric_limits::max(); + + /** + * @brief State of the when_any object. + * + * @see detail::when_any::await_state + */ + std::atomic owner_state{detail::when_any::await_state::started}; + }; + + /** + * @brief Shared pointer to the state shared between the jobs spawned. Contains the awaitable objects and the result. + */ + std::shared_ptr my_state{nullptr}; + + /** + * @brief Spawn a dpp::job handling the Nth argument. + * + * @tparam N Index of the argument to handle + * @return dpp::job Job handling the Nth argument + */ + template + static dpp::job make_job(std::shared_ptr shared_state) { + /** + * Any exceptions from the awaitable's await_suspend should be thrown to the caller (the coroutine creating the when_any object) + * If the co_await passes, and it is the first one to complete, try construct the result, catch any exceptions to rethrow at resumption, and resume. + */ + if constexpr (!std::same_as, detail::when_any::empty>) { + decltype(auto) result = co_await std::get(shared_state->awaitables); + + if (auto s = shared_state->owner_state.load(std::memory_order_relaxed); s == detail::when_any::await_state::dangling || s == detail::when_any::await_state::done) { + co_return; + } + + using result_t = decltype(result); + + /* Try construct, prefer move if possible, store any exception to rethrow */ + try { + if constexpr (std::is_lvalue_reference_v && !std::is_const_v && std::is_move_constructible_v>) { + shared_state->result.template emplace(std::move(result)); + } else { + shared_state->result.template emplace(result); + } + } catch (...) { + shared_state->result.template emplace<0>(std::current_exception()); + } + } else { + co_await std::get(shared_state->awaitables); + + if (auto s = shared_state->owner_state.load(std::memory_order_relaxed); s == detail::when_any::await_state::dangling || s == detail::when_any::await_state::done) { + co_return; + } + + shared_state->result.template emplace(); + } + + if (shared_state->owner_state.exchange(detail::when_any::await_state::done) != detail::when_any::await_state::waiting) { + co_return; + } + + if (auto handle = shared_state->handle; handle) { + shared_state->index_finished = N; + shared_state->handle.resume(); + } + } + + /** + * @brief Spawn a dpp::job to handle each awaitable. Each of them will co_await the awaitable and set the result if they are the first to finish + */ + void make_jobs() { + [](when_any *self, std::index_sequence) { + (make_job(self->my_state), ...); + }(this, std::index_sequence_for{}); + } + +public: + /** + * @brief Object returned by \ref operator co_await() on resumption. Can be moved but not copied. + */ + class result { + friend class when_any; + + /** @brief Reference to the shared state to pull the data from */ + std::shared_ptr shared_state; + + /** @brief Default construction is deleted */ + result() = delete; + + /** @brief Internal constructor taking the shared state */ + result(std::shared_ptr state) : shared_state{state} {} + + public: + /** @brief Move constructor */ + result(result&&) = default; + + /** @brief This object is not copyable. */ + result(const result &) = delete; + + /** @brief Move assignment operator */ + result &operator=(result&&) = default; + + /** @brief This object is not copyable. */ + result &operator=(const result&) = delete; + + /** + * @brief Retrieve the index of the awaitable that finished first. + * + * @return size_t Index of the awaitable that finished first, relative to the template arguments of when_any + */ + size_t index() const noexcept { + return shared_state->index_finished; + } + + /** + * @brief Retrieve the non-void result of an awaitable. + * + * @tparam N Index of the result to retrieve. Must correspond to index(). + * @throw ??? Throws any exception triggered at construction, or std::bad_variant_access if N does not correspond to index() + * @return Result of the awaitable as a reference. + */ + template +#ifndef _DOXYGEN_ + requires (!detail::when_any::void_result>) +#endif + result_t& get() & { + if (is_exception()) { + std::rethrow_exception(std::get<0>(shared_state->result)); + } + return std::get(shared_state->result); + } + + /** + * @brief Retrieve the non-void result of an awaitable. + * + * @tparam N Index of the result to retrieve. Must correspond to index(). + * @throw ??? Throws any exception triggered at construction, or std::bad_variant_access if N does not correspond to index() + * @return Result of the awaitable as a cpnst reference. + */ + template +#ifndef _DOXYGEN_ + requires (!detail::when_any::void_result>) +#endif + const result_t& get() const& { + if (is_exception()) { + std::rethrow_exception(std::get<0>(shared_state->result)); + } + return std::get(shared_state->result); + } + + /** + * @brief Retrieve the non-void result of an awaitable. + * + * @tparam N Index of the result to retrieve. Must correspond to index(). + * @throw ??? Throws any exception triggered at construction, or std::bad_variant_access if N does not correspond to index() + * @return Result of the awaitable as an rvalue reference. + */ + template +#ifndef _DOXYGEN_ + requires (!detail::when_any::void_result>) +#endif + result_t&& get() && { + if (is_exception()) { + std::rethrow_exception(std::get<0>(shared_state->result)); + } + return std::get(shared_state->result); + } + + /** + * @brief Cannot retrieve a void result. + */ + template +#ifndef _DOXYGEN + requires (detail::when_any::void_result>) +#endif + [[deprecated("cannot retrieve a void result")]] void get() = delete; + + /** + * @brief Checks whether the return of the first awaitable triggered an exception, that is, a call to get() will rethrow. + * + * @return Whether or not the result is an exception + */ + [[nodiscard]] bool is_exception() const noexcept { + return shared_state->result.index() == 0; + } + }; + + /** + * @brief Object returned by \ref operator co_await(). Meant to be used by the standard library, not by a user. + * + * @see result + */ + struct awaiter { + /** @brief Pointer to the when_any object */ + when_any *self; + + /** + * @brief First function called by the standard library when using co_await. + * + * @return bool Whether the result is ready + */ + [[nodiscard]] bool await_ready() const noexcept { + return self->await_ready(); + } + + /** + * @brief Second function called by the standard library when using co_await. + * + * @return bool Returns false if we want to resume immediately. + */ + bool await_suspend(detail::std_coroutine::coroutine_handle<> caller) noexcept { + auto sent = detail::when_any::await_state::started; + self->my_state->handle = caller; + return self->my_state->owner_state.compare_exchange_strong(sent, detail::when_any::await_state::waiting); // true (suspend) if `started` was replaced with `waiting` -- false (resume) if the value was not `started` (`done` is the only other option) + } + + /** + * @brief Third and final function called by the standard library when using co_await. Returns the result object. + * + * @see result + */ + result await_resume() const noexcept { + return {self->my_state}; + } + }; + + /** @brief Default constructor. A when_any object created this way holds no state */ + when_any() = default; + + /** + * @brief Constructor from awaitable objects. Each awaitable is executed immediately and the when_any object can then be co_await-ed later. + * + * @throw ??? Any exception thrown by the start of each awaitable will propagate to the caller. + * @param args Arguments to construct each awaitable from. The when_any object will construct an awaitable for each, it is recommended to pass rvalues or std::move. + */ + template +#ifndef _DOXYGEN_ + requires (sizeof...(Args_) == sizeof...(Args)) +#endif /* _DOXYGEN_ */ + when_any(Args_&&... args) : my_state{std::make_shared(detail::when_any::arg_helper::get(std::forward(args))...)} { + make_jobs(); + } + + /** @brief This object is not copyable. */ + when_any(const when_any &) = delete; + + /** @brief Move constructor. */ + when_any(when_any &&) noexcept = default; + + /** + * @brief On destruction the when_any will try to call @ref dpp::task::cancel() cancel() on each of its awaitable if they have such a method. + * + * @note If you are looking to use a custom type with when_any and want it to cancel on its destruction, + * make sure it has a cancel() method, which will trigger an await_resume() throwing a dpp::task_cancelled_exception. + * This object will swallow the exception and return cleanly. Any other exception will be thrown back to the resumer. + */ + ~when_any() { + if (!my_state) + return; + + my_state->owner_state = detail::when_any::await_state::dangling; + + [](when_any *self, std::index_sequence) constexpr { + constexpr auto cancel = [](when_any *self) constexpr { + if constexpr (requires { std::get(self->my_state->awaitables).cancel(); }) { + try { + std::get(self->my_state->awaitables).cancel(); + } catch (...) { + // swallow any exception. no choice here, we're in a destructor + } + } + }; + (cancel.template operator()(self), ...); + }(this, std::index_sequence_for()); + } + + /** @brief This object is not copyable. */ + when_any &operator=(const when_any &) = delete; + + /** @brief Move assignment operator. */ + when_any &operator=(when_any &&) noexcept = default; + + /** + * @brief Check whether a call to co_await would suspend. + * + * @note This can change from false to true at any point, but not the other way around. + * @return bool Whether co_await would suspend + */ + [[nodiscard]] bool await_ready() const noexcept { + return my_state->owner_state == detail::when_any::await_state::done; + } + + /** + * @brief Suspend the caller until any of the awaitables completes. + * + * @see result + * @throw ??? On resumption, throws any exception caused by the construction of the result. + * @return result On resumption, this object returns an object that allows to retrieve the index and result of the awaitable. + */ + [[nodiscard]] awaiter operator co_await() noexcept { + return {this}; + } +}; + +template +#ifndef _DOXYGEN_ +requires (sizeof...(Args) >= 1) +#endif /* _DOXYGEN_ */ +when_any(Args...) -> when_any...>; + +} /* namespace dpp */ + +#endif \ No newline at end of file diff --git a/include/dpp/discordclient.h b/include/dpp/discordclient.h new file mode 100644 index 0000000..08f6ed0 --- /dev/null +++ b/include/dpp/discordclient.h @@ -0,0 +1,528 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +#define DISCORD_API_VERSION "10" +#define API_PATH "/api/v" DISCORD_API_VERSION +namespace dpp { + +using json = nlohmann::json; + +// Forward declarations +class cluster; + +/** + * @brief This is an opaque class containing zlib library specific structures. + * We define it this way so that the public facing D++ library doesn't require + * the zlib headers be available to build against it. + */ +class zlibcontext; + +/** + * @brief Represents a connection to a voice channel. + * A client can only connect to one voice channel per guild at a time, so these are stored in a map + * in the dpp::discord_client keyed by guild_id. + */ +class DPP_EXPORT voiceconn { + /** + * @brief Owning dpp::discord_client instance + */ + class discord_client* creator; +public: + /** + * @brief Voice Channel ID + */ + snowflake channel_id; + + /** + * @brief Websocket hostname for status + */ + std::string websocket_hostname; + + /** + * @brief Voice Voice session ID + */ + std::string session_id; + + /** + * @brief Voice websocket token + */ + std::string token; + + /** + * @brief voice websocket client + */ + class discord_voice_client* voiceclient; + + /** + * @brief Construct a new voiceconn object + */ + voiceconn() = default; + + /** + * @brief Construct a new voiceconn object + * + * @param o owner + * @param _channel_id voice channel id + */ + voiceconn(class discord_client* o, snowflake _channel_id); + + /** + * @brief Destroy the voiceconn object + */ + ~voiceconn(); + + /** + * @brief return true if the connection is ready to connect + * (has hostname, token and session id) + * + * @return true if ready to connect + */ + bool is_ready(); + + /** + * @brief return true if the connection is active (websocket exists) + * + * @return true if has an active websocket + */ + bool is_active(); + + /** + * @brief Create websocket object and connect it. + * Needs hostname, token and session_id to be set or does nothing. + * + * @param guild_id Guild to connect to the voice channel on + * @return reference to self + * @note It can spawn a thread to establish the connection, so this is NOT a synchronous blocking call! + * You shouldn't call this directly. Use a wrapper function instead. e.g. dpp::guild::connect_member_voice + */ + voiceconn& connect(snowflake guild_id); + + /** + * @brief Disconnect from the currently connected voice channel + * @return reference to self + */ + voiceconn& disconnect(); +}; + +/** @brief Implements a discord client. Each discord_client connects to one shard and derives from a websocket client. */ +class DPP_EXPORT discord_client : public websocket_client +{ +protected: + /** + * @brief Needed so that voice_state_update can call dpp::discord_client::disconnect_voice_internal + */ + friend class dpp::events::voice_state_update; + + /** + * @brief Needed so that guild_create can request member chunks if you have the correct intents + */ + friend class dpp::events::guild_create; + + /** + * @brief Needed to allow cluster::set_presence to use the ETF functions + */ + friend class dpp::cluster; + + /** + * @brief True if the shard is terminating + */ + bool terminating; + + /** + * @brief Disconnect from the connected voice channel on a guild + * + * @param guild_id The guild who's voice channel you wish to disconnect from + * @param send_json True if we should send a json message confirming we are leaving the VC + * Should be set to false if we already receive this message in an event. + */ + void disconnect_voice_internal(snowflake guild_id, bool send_json = true); + +private: + + /** + * @brief Mutex for message queue + */ + std::shared_mutex queue_mutex; + + /** + * @brief Queue of outbound messages + */ + std::deque message_queue; + + /** + * @brief Thread this shard is executing on + */ + std::thread* runner; + + /** + * @brief Run shard loop under a thread. + * Calls discord_client::run() from within a std::thread. + */ + void thread_run(); + + /** + * @brief If true, stream compression is enabled + */ + bool compressed; + + /** + * @brief ZLib decompression buffer + */ + unsigned char* decomp_buffer; + + /** + * @brief Decompressed string + */ + std::string decompressed; + + /** + * @brief This object contains the various zlib structs which + * are not usable by the user of the library directly. They + * are wrapped within this opaque object so that this header + * file does not bring in a dependency on zlib.h. + */ + zlibcontext* zlib; + + /** + * @brief Total decompressed received bytes + */ + uint64_t decompressed_total; + + /** + * @brief Last connect time of cluster + */ + time_t connect_time; + + /** + * @brief Time last ping sent to websocket, in fractional seconds + */ + double ping_start; + + /** + * @brief ETF parser for when in ws_etf mode + */ + class etf_parser* etf; + + /** + * @brief Convert a JSON object to string. + * In JSON protocol mode, call json.dump(), and in ETF mode, + * call etf::build(). + * + * @param json nlohmann::json object to convert + * @return std::string string output in the correct format + */ + std::string jsonobj_to_string(const nlohmann::json& json); + + /** + * @brief Initialise ZLib (websocket compression) + * @throw dpp::exception if ZLib cannot be initialised + */ + void setup_zlib(); + + /** + * @brief Shut down ZLib (websocket compression) + */ + void end_zlib(); + + /** + * @brief Update the websocket hostname with the resume url + * from the last READY event + */ + void set_resume_hostname(); + + /** + * @brief Clean up resources + */ + void cleanup(); +public: + /** + * @brief Owning cluster + */ + class dpp::cluster* creator; + + /** + * @brief Heartbeat interval for sending heartbeat keepalive + * @note value in milliseconds + */ + uint32_t heartbeat_interval; + + /** + * @brief Last heartbeat + */ + time_t last_heartbeat; + + /** + * @brief Shard ID of this client + */ + uint32_t shard_id; + + /** + * @brief Total number of shards + */ + uint32_t max_shards; + + /** + * @brief Thread ID + */ + std::thread::native_handle_type thread_id; + + /** + * @brief Last sequence number received, for resumes and pings + */ + uint64_t last_seq; + + /** + * @brief Discord bot token + */ + std::string token; + + /** + * @brief Privileged gateway intents + * @see dpp::intents + */ + uint32_t intents; + + /** + * @brief Discord session id + */ + std::string sessionid; + + /** + * @brief Mutex for voice connections map + */ + std::shared_mutex voice_mutex; + + /** + * @brief Resume count + */ + uint32_t resumes; + + /** + * @brief Reconnection count + */ + uint32_t reconnects; + + /** + * @brief Websocket latency in fractional seconds + */ + double websocket_ping; + + /** + * @brief True if READY or RESUMED has been received + */ + bool ready; + + /** + * @brief Last heartbeat ACK (opcode 11) + */ + time_t last_heartbeat_ack; + + /** + * @brief Current websocket protocol, currently either ETF or JSON + */ + websocket_protocol_t protocol; + + /** + * @brief List of voice channels we are connecting to keyed by guild id + */ + std::unordered_map> connecting_voice_channels; + + /** + * @brief The gateway address we reconnect to when we resume a session + */ + std::string resume_gateway_url; + + /** + * @brief Log a message to whatever log the user is using. + * The logged message is passed up the chain to the on_log event in user code which can then do whatever + * it wants to do with it. + * @param severity The log level from dpp::loglevel + * @param msg The log message to output + */ + virtual void log(dpp::loglevel severity, const std::string &msg) const; + + /** + * @brief Handle an event (opcode 0) + * @param event Event name, e.g. MESSAGE_CREATE + * @param j JSON object for the event content + * @param raw Raw JSON event string + */ + virtual void handle_event(const std::string &event, json &j, const std::string &raw); + + /** + * @brief Get the Guild Count for this shard + * + * @return uint64_t guild count + */ + uint64_t get_guild_count(); + + /** + * @brief Get the Member Count for this shard + * + * @return uint64_t member count + */ + uint64_t get_member_count(); + + /** + * @brief Get the Channel Count for this shard + * + * @return uint64_t channel count + */ + uint64_t get_channel_count(); + + /** Fires every second from the underlying socket I/O loop, used for sending heartbeats */ + virtual void one_second_timer(); + + /** + * @brief Queue a message to be sent via the websocket + * + * @param j The JSON data of the message to be sent + * @param to_front If set to true, will place the message at the front of the queue not the back + * (this is for urgent messages such as heartbeat, presence, so they can take precedence over + * chunk requests etc) + */ + void queue_message(const std::string &j, bool to_front = false); + + /** + * @brief Clear the outbound message queue + * @return reference to self + */ + discord_client& clear_queue(); + + /** + * @brief Get the size of the outbound message queue + * + * @return The size of the queue + */ + size_t get_queue_size(); + + /** + * @brief Returns true if the shard is connected + * + * @return True if connected + */ + bool is_connected(); + + /** + * @brief Returns the connection time of the shard + * + * @return dpp::utility::uptime Detail of how long the shard has been connected for + */ + dpp::utility::uptime get_uptime(); + + /** + * @brief Construct a new discord_client object + * + * @param _cluster The owning cluster for this shard + * @param _shard_id The ID of the shard to start + * @param _max_shards The total number of shards across all clusters + * @param _token The bot token to use for identifying to the websocket + * @param intents Privileged intents to use, a bitmask of values from dpp::intents + * @param compressed True if the received data will be gzip compressed + * @param ws_protocol Websocket protocol to use for the connection, JSON or ETF + * + * @throws std::bad_alloc Passed up to the caller if any internal objects fail to allocate, after cleanup has completed + */ + discord_client(dpp::cluster* _cluster, uint32_t _shard_id, uint32_t _max_shards, const std::string &_token, uint32_t intents = 0, bool compressed = true, websocket_protocol_t ws_protocol = ws_json); + + /** + * @brief Destroy the discord client object + */ + virtual ~discord_client(); + + /** + * @brief Get the decompressed bytes in objectGet decompressed total bytes received + * @return uint64_t bytes received + */ + uint64_t get_decompressed_bytes_in(); + + /** + * @brief Handle JSON from the websocket. + * @param buffer The entire buffer content from the websocket client + * @returns True if a frame has been handled + */ + virtual bool handle_frame(const std::string &buffer); + + /** + * @brief Handle a websocket error. + * @param errorcode The error returned from the websocket + */ + virtual void error(uint32_t errorcode); + + /** + * @brief Start and monitor I/O loop. + * @note this is a blocking call and is usually executed within a + * thread by whatever creates the object. + */ + void run(); + + /** + * @brief Connect to a voice channel + * + * @param guild_id Guild where the voice channel is + * @param channel_id Channel ID of the voice channel + * @param self_mute True if the bot should mute itself + * @param self_deaf True if the bot should deafen itself + * @return reference to self + * @note This is NOT a synchronous blocking call! The bot isn't instantly ready to send or listen for audio, + * as we have to wait for the connection to the voice server to be established! + * e.g. wait for dpp::cluster::on_voice_ready event, and then send the audio within that event. + */ + discord_client& connect_voice(snowflake guild_id, snowflake channel_id, bool self_mute = false, bool self_deaf = false); + + /** + * @brief Disconnect from the connected voice channel on a guild + * + * @param guild_id The guild who's voice channel you wish to disconnect from + * @return reference to self + * @note This is NOT a synchronous blocking call! The bot isn't instantly disconnected. + */ + discord_client& disconnect_voice(snowflake guild_id); + + /** + * @brief Get the dpp::voiceconn object for a specific guild on this shard. + * + * @param guild_id The guild ID to retrieve the voice connection for + * @return voiceconn* The voice connection for the guild, or nullptr if there is no + * voice connection to this guild. + */ + voiceconn* get_voice(snowflake guild_id); +}; + +} // namespace dpp diff --git a/include/dpp/discordevents.h b/include/dpp/discordevents.h new file mode 100644 index 0000000..6b4285f --- /dev/null +++ b/include/dpp/discordevents.h @@ -0,0 +1,197 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once + +#include +#include +#include +#include + +namespace dpp { + +/** @brief Returns a snowflake id from a json field value, if defined, else returns 0 + * @param j nlohmann::json instance to retrieve value from + * @param keyname key name to check for a value + * @return found value + */ +uint64_t DPP_EXPORT snowflake_not_null(const nlohmann::json* j, const char *keyname); + +/** @brief Sets a snowflake id from a json field value, if defined, else does nothing + * @param j nlohmann::json instance to retrieve value from + * @param keyname key name to check for a value + * @param v Value to change + */ +void DPP_EXPORT set_snowflake_not_null(const nlohmann::json* j, const char *keyname, uint64_t &v); + +/** @brief Sets an array of snowflakes from a json field value, if defined, else does nothing + * @param j nlohmann::json instance to retrieve value from + * @param keyname key name to check for the values + * @param v Value to change + */ +void DPP_EXPORT set_snowflake_array_not_null(const nlohmann::json* j, const char *keyname, std::vector &v); + +/** @brief Sets an array of objects from a json field value, if defined, else does nothing + * @tparam T The class of which the array consists of. Must be derived from dpp::json_interface + * @param j nlohmann::json instance to retrieve value from + * @param keyname key name to check for the values + * @param v Value to change + */ +template std::enable_if_t, T>, void> set_object_array_not_null(nlohmann::json* j, const char *keyname, std::vector &v) { + v.clear(); + auto k = j->find(keyname); + if (k != j->end() && !k->is_null()) { + v.reserve(j->at(keyname).size()); + for (auto &obj : j->at(keyname)) { + v.emplace_back(T().fill_from_json(&obj)); + } + } +} + +/** @brief Returns a string from a json field value, if defined, else returns an empty string. + * @param j nlohmann::json instance to retrieve value from + * @param keyname key name to check for a value + * @return found value + */ +std::string DPP_EXPORT string_not_null(const nlohmann::json* j, const char *keyname); + +/** @brief Sets a string from a json field value, if defined, else does nothing + * @param j nlohmann::json instance to retrieve value from + * @param keyname key name to check for a value + * @param v Value to change + */ +void DPP_EXPORT set_string_not_null(const nlohmann::json* j, const char *keyname, std::string &v); + +/** @brief Returns a double from a json field value, if defined, else returns 0. + * @param j nlohmann::json instance to retrieve value from + * @param keyname key name to check for a value + * @return found value + */ +double DPP_EXPORT double_not_null(const nlohmann::json* j, const char *keyname); + +/** @brief Sets a double from a json field value, if defined, else does nothing + * @param j nlohmann::json instance to retrieve value from + * @param keyname key name to check for a value + * @param v Value to change + */ +void DPP_EXPORT set_double_not_null(const nlohmann::json* j, const char *keyname, double &v); + +/** @brief Returns a 64 bit unsigned integer from a json field value, if defined, else returns 0. + * DO NOT use this for snowflakes, as usually snowflakes are wrapped in a string! + * @param j nlohmann::json instance to retrieve value from + * @param keyname key name to check for a value + * @return found value + */ +uint64_t DPP_EXPORT int64_not_null(const nlohmann::json* j, const char *keyname); + +/** @brief Sets an unsigned 64 bit integer from a json field value, if defined, else does nothing + * @param j nlohmann::json instance to retrieve value from + * @param keyname key name to check for a value + * @param v Value to change + */ +void DPP_EXPORT set_int64_not_null(const nlohmann::json* j, const char *keyname, uint64_t &v); + +/** @brief Returns a 32 bit unsigned integer from a json field value, if defined, else returns 0 + * @param j nlohmann::json instance to retrieve value from + * @param keyname key name to check for a value + * @return found value + */ +uint32_t DPP_EXPORT int32_not_null(const nlohmann::json* j, const char *keyname); + +/** @brief Sets an unsigned 32 bit integer from a json field value, if defined, else does nothing + * @param j nlohmann::json instance to retrieve value from + * @param keyname key name to check for a value + * @param v Value to change + */ +void DPP_EXPORT set_int32_not_null(const nlohmann::json* j, const char *keyname, uint32_t &v); + +/** @brief Returns a 16 bit unsigned integer from a json field value, if defined, else returns 0 + * @param j nlohmann::json instance to retrieve value from + * @param keyname key name to check for a value + * @return found value + */ +uint16_t DPP_EXPORT int16_not_null(const nlohmann::json* j, const char *keyname); + +/** @brief Sets an unsigned 16 bit integer from a json field value, if defined, else does nothing + * @param j nlohmann::json instance to retrieve value from + * @param keyname key name to check for a value + * @param v Value to change + */ +void DPP_EXPORT set_int16_not_null(const nlohmann::json* j, const char *keyname, uint16_t &v); + +/** @brief Returns an 8 bit unsigned integer from a json field value, if defined, else returns 0 + * @param j nlohmann::json instance to retrieve value from + * @param keyname key name to check for a value + * @return found value + */ +uint8_t DPP_EXPORT int8_not_null(const nlohmann::json* j, const char *keyname); + +/** @brief Sets an unsigned 8 bit integer from a json field value, if defined, else does nothing + * @param j nlohmann::json instance to retrieve value from + * @param keyname key name to check for a value + * @param v Value to change + */ +void DPP_EXPORT set_int8_not_null(const nlohmann::json* j, const char *keyname, uint8_t &v); + +/** @brief Returns a boolean value from a json field value, if defined, else returns false + * @param j nlohmann::json instance to retrieve value from + * @param keyname key name to check for a value + * @return found value + */ +bool DPP_EXPORT bool_not_null(const nlohmann::json* j, const char *keyname); + +/** @brief Sets a boolean from a json field value, if defined, else does nothing + * @param j nlohmann::json instance to retrieve value from + * @param keyname key name to check for a value + * @param v Value to change + */ +void DPP_EXPORT set_bool_not_null(const nlohmann::json* j, const char *keyname, bool &v); + +/** @brief Returns a time_t from an ISO8601 timestamp field in a json value, if defined, else returns + * epoch value of 0. + * @param j nlohmann::json instance to retrieve value from + * @param keyname key name to check for a value + * @return found value + */ +time_t DPP_EXPORT ts_not_null(const nlohmann::json* j, const char *keyname); + +/** @brief Sets an timestamp from a json field value containing an ISO8601 string, if defined, else does nothing + * @param j nlohmann::json instance to retrieve value from + * @param keyname key name to check for a value + * @param v Value to change + */ +void DPP_EXPORT set_ts_not_null(const nlohmann::json* j, const char *keyname, time_t &v); + +/** @brief Base64 encode data into a string. + * @param buf Raw binary buffer + * @param buffer_length Buffer length to encode + * @return The base64 encoded string + */ +std::string DPP_EXPORT base64_encode(unsigned char const* buf, unsigned int buffer_length); + +/** + * @brief Convert time_t unix epoch to std::string ISO date/time + * + * @param ts Timestamp to convert + * @return std::string Converted time/date string + */ +std::string DPP_EXPORT ts_to_string(time_t ts); + +} // namespace dpp diff --git a/include/dpp/discordvoiceclient.h b/include/dpp/discordvoiceclient.h new file mode 100644 index 0000000..eba704e --- /dev/null +++ b/include/dpp/discordvoiceclient.h @@ -0,0 +1,915 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +struct OpusDecoder; +struct OpusEncoder; +struct OpusRepacketizer; + +namespace dpp { + +using json = nlohmann::json; + +/* +* @brief For holding a moving average of the number of current voice users, for applying a smooth gain ramp. +*/ +struct DPP_EXPORT moving_averager { + moving_averager() = default; + + moving_averager(uint64_t collection_count_new); + + moving_averager operator+=(int64_t value); + + operator float(); + +protected: + std::deque values{}; + uint64_t collectionCount{}; +}; + +// Forward declaration +class cluster; + +/** + * @brief An opus-encoded RTP packet to be sent out to a voice channel + */ +struct DPP_EXPORT voice_out_packet { + /** + * @brief Each string is a UDP packet. + * Generally these will be RTP. + */ + std::string packet; + /** + * @brief Duration of packet + */ + uint64_t duration; +}; + +#define AUDIO_TRACK_MARKER (uint16_t)0xFFFF + +#define AUDIO_OVERLAP_SLEEP_SAMPLES 30 + +/** @brief Implements a discord voice connection. + * Each discord_voice_client connects to one voice channel and derives from a websocket client. + */ +class DPP_EXPORT discord_voice_client : public websocket_client +{ + /** + * @brief Clean up resources + */ + void cleanup(); + + /** + * @brief Mutex for outbound packet stream + */ + std::mutex stream_mutex; + + /** + * @brief Mutex for message queue + */ + std::shared_mutex queue_mutex; + + /** + * @brief Queue of outbound messages + */ + std::deque message_queue; + + /** + * @brief Thread this connection is executing on + */ + std::thread* runner; + + /** + * @brief Run shard loop under a thread + */ + void thread_run(); + + /** + * @brief Last connect time of voice session + */ + time_t connect_time; + + /** + * @brief IP of UDP/RTP endpoint + */ + std::string ip; + + /** + * @brief Port number of UDP/RTP endpoint + */ + uint16_t port; + + /** + * @brief SSRC value + */ + uint64_t ssrc; + + /** + * @brief List of supported audio encoding modes + */ + std::vector modes; + + /** + * @brief Timescale in nanoseconds + */ + uint64_t timescale; + + /** + * @brief Output buffer + */ + std::vector outbuf; + + /** + * @brief Data type of RTP packet sequence number field. + */ + using rtp_seq_t = uint16_t; + using rtp_timestamp_t = uint32_t; + + /** + * @brief Keeps track of the voice payload to deliver to voice handlers. + */ + struct voice_payload { + /** + * @brief The sequence number of the RTP packet that generated this + * voice payload. + */ + rtp_seq_t seq; + /** + * @brief The timestamp of the RTP packet that generated this voice + * payload. + * + * The timestamp is used to detect the order around where sequence + * number wraps around. + */ + rtp_timestamp_t timestamp; + /** + * @brief The event payload that voice handlers receive. + */ + std::unique_ptr vr; + + /** + * @brief For priority_queue sorting. + * @return true if "this" has lower priority that "other", + * i.e. appears later in the queue; false otherwise. + */ + bool operator<(const voice_payload& other) const; + }; + + struct voice_payload_parking_lot { + /** + * @brief The range of RTP packet sequence number and timestamp in the lot. + * + * The minimum is used to drop packets that arrive too late. Packets + * less than the minimum have been delivered to voice handlers and + * there is no going back. Unfortunately we just have to drop them. + * + * The maximum is used, at flush time, to calculate the minimum for + * the next batch. The maximum is also updated every time we receive an + * RTP packet with a larger value. + */ + struct seq_range_t { + rtp_seq_t min_seq, max_seq; + rtp_timestamp_t min_timestamp, max_timestamp; + } range; + /** + * @brief The queue of parked voice payloads. + * + * We group payloads and deliver them to handlers periodically as the + * handling of out-of-order RTP packets. Payloads in between flushes + * are parked and sorted in this queue. + */ + std::priority_queue parked_payloads; + /** + * @brief The decoder ctls to be set on the decoder. + */ + std::vector> pending_decoder_ctls; + /** + * @brief libopus decoder + * + * Shared with the voice courier thread that does the decoding. + * This is not protected by a mutex because only the courier thread + * uses the decoder. + */ + std::shared_ptr decoder; + }; + /** + * @brief Thread used to deliver incoming voice data to handlers. + */ + std::thread voice_courier; + /** + * @brief Shared state between this voice client and the courier thread. + */ + struct courier_shared_state_t { + /** + * @brief Protects all following members. + */ + std::mutex mtx; + /** + * @brief Signaled when there is a new payload to deliver or terminating state has changed. + */ + std::condition_variable signal_iteration; + /** + * @brief Voice buffers to be reported to handler, grouped by speaker. + * + * Buffers are parked here and flushed every 500ms. + */ + std::map parked_voice_payloads; + /** + * @brief Used to signal termination. + * + * @note Pending payloads are delivered first before termination. + */ + bool terminating = false; + } voice_courier_shared_state; + /** + * @brief The run loop of the voice courier thread. + */ + static void voice_courier_loop(discord_voice_client&, courier_shared_state_t&); + + /** + * @brief If true, audio packet sending is paused + */ + bool paused; + +#ifdef HAVE_VOICE + /** + * @brief libopus encoder + */ + OpusEncoder* encoder; + + /** + * @brief libopus repacketizer + * (merges frames into one packet) + */ + OpusRepacketizer* repacketizer; +#else + /** + * @brief libopus encoder + */ + void* encoder; + + /** + * @brief libopus repacketizer + * (merges frames into one packet) + */ + void* repacketizer; +#endif + + /** + * @brief File descriptor for UDP connection + */ + dpp::socket fd; + + /** + * @brief Secret key for encrypting voice. + * If it has been sent, this is non-null and points to a + * sequence of exactly 32 bytes. + */ + uint8_t* secret_key; + + /** + * @brief Sequence number of outbound audio. This is incremented + * once per frame sent. + */ + uint16_t sequence; + + /** + * @brief Timestamp value used in outbound audio. Each packet + * has the timestamp value which is incremented to match + * how many frames are sent. + */ + uint32_t timestamp; + + /** + * @brief Last sent packet high-resolution timestamp + */ + std::chrono::high_resolution_clock::time_point last_timestamp; + + /** + * @brief Fraction of the sleep that was not executed after the last audio packet was sent + */ + std::chrono::nanoseconds last_sleep_remainder; + + /** + * @brief Maps receiving ssrc to user id + */ + std::unordered_map ssrc_map; + + /** + * @brief This is set to true if we have started sending audio. + * When this moves from false to true, this causes the + * client to send the 'talking' notification to the websocket. + */ + bool sending; + + /** + * @brief Number of track markers in the buffer. For example if there + * are two track markers in the buffer there are 3 tracks. + * + * **Special case:** + * + * If the buffer is empty, there are zero tracks in the + * buffer. + */ + uint32_t tracks; + + /** + * @brief Meta data associated with each track. + * Arbitrary string that the user can set via + * dpp::discord_voice_client::add_marker + */ + std::vector track_meta; + + /** + * @brief Encoding buffer for opus repacketizer and encode + */ + uint8_t encode_buffer[65536]; + + /** + * @brief Send data to UDP socket immediately. + * + * @param data data to send + * @param length length of data to send + * @return int bytes sent. Will return -1 if we cannot send + */ + int udp_send(const char* data, size_t length); + + /** + * @brief Receive data from UDP socket immediately. + * + * @param data data to receive + * @param max_length size of data receiving buffer + * @return int bytes received. -1 if there is an error + * (e.g. EAGAIN) + */ + int udp_recv(char* data, size_t max_length); + + /** + * @brief This hooks the ssl_client, returning the file + * descriptor if we want to send buffered data, or + * -1 if there is nothing to send + * + * @return int file descriptor or -1 + */ + dpp::socket want_write(); + + /** + * @brief This hooks the ssl_client, returning the file + * descriptor if we want to receive buffered data, or + * -1 if we are not wanting to receive + * + * @return int file descriptor or -1 + */ + dpp::socket want_read(); + + /** + * @brief Called by ssl_client when the socket is ready + * for writing, at this point we pick the head item off + * the buffer and send it. So long as it doesn't error + * completely, we pop it off the head of the queue. + */ + void write_ready(); + + /** + * @brief Called by ssl_client when there is data to be + * read. At this point we insert that data into the + * input queue. + * @throw dpp::voice_exception if voice support is not compiled into D++ + */ + void read_ready(); + + /** + * @brief Send data to the UDP socket, using the buffer. + * + * @param packet packet data + * @param len length of packet + * @param duration duration of opus packet + */ + void send(const char* packet, size_t len, uint64_t duration); + + /** + * @brief Queue a message to be sent via the websocket + * + * @param j The JSON data of the message to be sent + * @param to_front If set to true, will place the message at the front of the queue not the back + * (this is for urgent messages such as heartbeat, presence, so they can take precedence over + * chunk requests etc) + */ + void queue_message(const std::string &j, bool to_front = false); + + /** + * @brief Clear the outbound message queue + * + */ + void clear_queue(); + + /** + * @brief Get the size of the outbound message queue + * + * @return The size of the queue + */ + size_t get_queue_size(); + + /** + * @brief Encode a byte buffer using opus codec. + * Multiple opus frames (2880 bytes each) will be encoded into one packet for sending. + * + * @param input Input data as raw bytes of PCM data + * @param inDataSize Input data length + * @param output Output data as an opus encoded packet + * @param outDataSize Output data length, should be at least equal to the input size. + * Will be adjusted on return to the actual compressed data size. + * @return size_t The compressed data size that was encoded. + * @throw dpp::voice_exception If data length to encode is invalid or voice support not compiled into D++ + */ + size_t encode(uint8_t *input, size_t inDataSize, uint8_t *output, size_t &outDataSize); + +public: + + /** + * @brief Owning cluster + */ + class dpp::cluster* creator; + + /** + * @brief This needs to be static, we only initialise libsodium once per program start, + * so initialising it on first use in a voice connection is best. + */ + static bool sodium_initialised; + + /** + * @brief True when the thread is shutting down + */ + bool terminating; + + /** + * @brief The gain value for the end of the current voice iteration. + */ + float end_gain; + + /** + * @brief The gain value for the current voice iteration. + */ + float current_gain; + + /** + * @brief The amount to increment each successive sample for, for the current voice iteration. + */ + float increment; + + /** + * @brief Heartbeat interval for sending heartbeat keepalive + */ + uint32_t heartbeat_interval; + + /** + * @brief Last voice channel websocket heartbeat + */ + time_t last_heartbeat; + + /** + * @brief Thread ID + */ + std::thread::native_handle_type thread_id; + + /** + * @brief Discord voice session token + */ + std::string token; + + /** + * @brief Discord voice session id + */ + std::string sessionid; + + /** + * @brief Server ID + */ + snowflake server_id; + + /** + * @brief Moving averager. + */ + moving_averager moving_average; + + /** + * @brief Channel ID + */ + snowflake channel_id; + + /** + * @brief The audio type to be sent. The default type is recorded audio. + * + * If the audio is recorded, the sending of audio packets is throttled. + * Otherwise, if the audio is live, the sending is not throttled. + * + * Discord voice engine is expecting audio data as if they were from + * some audio device, e.g. microphone, where the data become available + * as they get captured from the audio device. + * + * In case of recorded audio, unlike from a device, the audio data are + * usually instantly available in large chunks. Throttling is needed to + * simulate audio data coming from an audio device. In case of live audio, + * the throttling is by nature, so no extra throttling is needed. + * + * Using live audio mode for recorded audio can cause Discord to skip + * audio data because Discord does not expect to receive, say, 3 minutes' + * worth of audio data in 1 second. + * + * There are some inaccuracies in the throttling method used by the recorded + * audio mode on some systems (mainly Windows) which causes gaps and stutters + * in the resulting audio stream. The overlap audio mode provides a different + * implementation that fixes the issue. This method is slightly more CPU + * intensive, and should only be used if you encounter issues with recorded audio + * on your system. + * + * Use discord_voice_client::set_send_audio_type to change this value as + * it ensures thread safety. + */ + enum send_audio_type_t + { + satype_recorded_audio, + satype_live_audio, + satype_overlap_audio + } send_audio_type = satype_recorded_audio; + + /** + * @brief Sets the gain for the specified user. + * + * Similar to the User Volume slider, controls the listening volume per user. + * Uses native Opus gain control, so clients don't have to perform extra + * audio processing. + * + * The gain setting will affect the both individual and combined voice audio. + * + * The gain value can also be set even before the user connects to the voice + * channel. + * + * @param user_id The ID of the user where the gain is to be controlled. + * @param factor Nonnegative factor to scale the amplitude by, where 1.f reverts + * to the default volume. + */ + void set_user_gain(snowflake user_id, float factor); + + /** + * @brief Log a message to whatever log the user is using. + * The logged message is passed up the chain to the on_log event in user code which can then do whatever + * it wants to do with it. + * @param severity The log level from dpp::loglevel + * @param msg The log message to output + */ + virtual void log(dpp::loglevel severity, const std::string &msg) const; + + /** + * @brief Fires every second from the underlying socket I/O loop, used for sending heartbeats + * @throw dpp::exception if the socket needs to disconnect + */ + virtual void one_second_timer(); + + /** + * @brief voice client is ready to stream audio. + * The voice client is considered ready if it has a secret key. + * + * @return true if ready to stream audio + */ + bool is_ready(); + + /** + * @brief Returns true if the voice client is connected to the websocket + * + * @return True if connected + */ + bool is_connected(); + + /** + * @brief Returns the connection time of the voice client + * + * @return dpp::utility::uptime Detail of how long the voice client has been connected for + */ + dpp::utility::uptime get_uptime(); + + /** Constructor takes shard id, max shards and token. + * @param _cluster The cluster which owns this voice connection, for related logging, REST requests etc + * @param _channel_id The channel id to identify the voice connection as + * @param _server_id The server id (guild id) to identify the voice connection as + * @param _token The voice session token to use for identifying to the websocket + * @param _session_id The voice session id to identify with + * @param _host The voice server hostname to connect to (hostname:port format) + * @throw dpp::voice_exception Sodium or Opus failed to initialise, or D++ is not compiled with voice support + */ + discord_voice_client(dpp::cluster* _cluster, snowflake _channel_id, snowflake _server_id, const std::string &_token, const std::string &_session_id, const std::string &_host); + + /** + * @brief Destroy the discord voice client object + */ + virtual ~discord_voice_client(); + + /** + * @brief Handle JSON from the websocket. + * @param buffer The entire buffer content from the websocket client + * @return bool True if a frame has been handled + * @throw dpp::exception If there was an error processing the frame, or connection to UDP socket failed + */ + virtual bool handle_frame(const std::string &buffer); + + /** + * @brief Handle a websocket error. + * @param errorcode The error returned from the websocket + */ + virtual void error(uint32_t errorcode); + + /** + * @brief Start and monitor I/O loop + */ + void run(); + + /** + * @brief Send raw audio to the voice channel. + * + * You should send an audio packet of 11520 bytes. + * Note that this function can be costly as it has to opus encode + * the PCM audio on the fly, and also encrypt it with libsodium. + * + * @note Because this function encrypts and encodes packets before + * pushing them onto the output queue, if you have a complete stream + * ready to send and know its length it is advisable to call this + * method multiple times to enqueue the entire stream audio so that + * it is all encoded at once (unless you have set use_opus to false). + * Constantly calling this from the dpp::on_voice_buffer_send callback + * can and will eat a TON of cpu! + * + * @param audio_data Raw PCM audio data. Channels are interleaved, + * with each channel's amplitude being a 16 bit value. + * + * The audio data should be 48000Hz signed 16 bit audio. + * + * @param length The length of the audio data. The length should + * be a multiple of 4 (2x 16 bit stereo channels) with a maximum + * length of 11520, which is a complete opus frame at highest + * quality. + * + * @return discord_voice_client& Reference to self + * + * @throw dpp::voice_exception If data length is invalid or voice support not compiled into D++ + */ + discord_voice_client& send_audio_raw(uint16_t* audio_data, const size_t length); + + /** + * @brief Send opus packets to the voice channel + * + * Some containers such as .ogg may contain OPUS + * encoded data already. In this case, we don't need to encode the + * frames using opus here. We can bypass the codec, only applying + * libsodium to the stream. + * + * @param opus_packet Opus packets. Discord expects opus frames + * to be encoded at 48000Hz + * + * @param length The length of the audio data. + * + * @param duration Generally duration is 2.5, 5, 10, 20, 40 or 60 + * if the timescale is 1000000 (1ms) + * + * @return discord_voice_client& Reference to self + * + * @note It is your responsibility to ensure that packets of data + * sent to send_audio are correctly repacketized for streaming, + * e.g. that audio frames are not too large or contain + * an incorrect format. Discord will still expect the same frequency + * and bit width of audio and the same signedness. + * + * @throw dpp::voice_exception If data length is invalid or voice support not compiled into D++ + */ + discord_voice_client& send_audio_opus(uint8_t* opus_packet, const size_t length, uint64_t duration); + + /** + * @brief Send opus packets to the voice channel + * + * Some containers such as .ogg may contain OPUS + * encoded data already. In this case, we don't need to encode the + * frames using opus here. We can bypass the codec, only applying + * libsodium to the stream. + * + * Duration is calculated internally + * + * @param opus_packet Opus packets. Discord expects opus frames + * to be encoded at 48000Hz + * + * @param length The length of the audio data. + * + * @return discord_voice_client& Reference to self + * + * @note It is your responsibility to ensure that packets of data + * sent to send_audio are correctly repacketized for streaming, + * e.g. that audio frames are not too large or contain + * an incorrect format. Discord will still expect the same frequency + * and bit width of audio and the same signedness. + * + * @throw dpp::voice_exception If data length is invalid or voice support not compiled into D++ + */ + discord_voice_client& send_audio_opus(uint8_t* opus_packet, const size_t length); + + /** + * @brief Send silence to the voice channel + * + * @param duration How long to send silence for. With the standard + * timescale this is in milliseconds. Allowed values are 2.5, + * 5, 10, 20, 40 or 60 milliseconds. + * @return discord_voice_client& Reference to self + * @throw dpp::voice_exception if voice support is not compiled into D++ + */ + discord_voice_client& send_silence(const uint64_t duration); + + /** + * @brief Sets the audio type that will be sent with send_audio_* methods. + * + * @see send_audio_type_t + */ + discord_voice_client& set_send_audio_type(send_audio_type_t type); + + /** + * @brief Set the timescale in nanoseconds. + * + * @param new_timescale Timescale to set. This defaults to 1000000, + * which means 1 millisecond. + * @return discord_voice_client& Reference to self + * @throw dpp::voice_exception If data length is invalid or voice support not compiled into D++ + */ + discord_voice_client& set_timescale(uint64_t new_timescale); + + /** + * @brief Get the current timescale, this will default to 1000000 + * which means 1 millisecond. + * + * @return uint64_t timescale in nanoseconds + */ + uint64_t get_timescale(); + + /** + * @brief Mark the voice connection as 'speaking'. + * This sends a JSON message to the voice websocket which tells discord + * that the user is speaking. The library automatically calls this for you + * whenever you send audio. + * + * @return discord_voice_client& Reference to self + */ + discord_voice_client& speak(); + + /** + * @brief Pause sending of audio + * + * @param pause True to pause, false to resume + * @return reference to self + */ + discord_voice_client& pause_audio(bool pause); + + /** + * @brief Immediately stop all audio. + * Clears the packet queue. + * @return reference to self + */ + discord_voice_client& stop_audio(); + + /** + * @brief Returns true if we are playing audio + * + * @return true if audio is playing + */ + bool is_playing(); + + /** + * @brief Get the number of seconds remaining + * of the audio output buffer + * + * @return float number of seconds remaining + */ + float get_secs_remaining(); + + /** + * @brief Get the number of tracks remaining + * in the output buffer. + * This is calculated by the number of track + * markers plus one. + * @return uint32_t Number of tracks in the + * buffer + */ + uint32_t get_tracks_remaining(); + + /** + * @brief Get the time remaining to send the + * audio output buffer in hours:minutes:seconds + * + * @return dpp::utility::uptime length of buffer + */ + dpp::utility::uptime get_remaining(); + + /** + * @brief Insert a track marker into the audio + * output buffer. + * A track marker is an arbitrary flag in the + * buffer contents that indicates the end of some + * block of audio of significance to the sender. + * This may be a song from a streaming site, or + * some voice audio/speech, a sound effect, or + * whatever you choose. You can later skip + * to the next marker using the + * dpp::discord_voice_client::skip_to_next_marker + * function. + * @param metadata Arbitrary information related to this + * track + * @return reference to self + */ + discord_voice_client& insert_marker(const std::string& metadata = ""); + + /** + * @brief Skip tp the next track marker, + * previously inserted by using the + * dpp::discord_voice_client::insert_marker + * function. If there are no markers in the + * output buffer, then this skips to the end + * of the buffer and is equivalent to the + * dpp::discord_voice_client::stop_audio + * function. + * @note It is possible to use this function + * while the output stream is paused. + * @return reference to self + */ + discord_voice_client& skip_to_next_marker(); + + /** + * @brief Get the metadata string associated with each inserted marker. + * + * @return const std::vector& list of metadata strings + */ + const std::vector get_marker_metadata(); + + /** + * @brief Returns true if the audio is paused. + * You can unpause with + * dpp::discord_voice_client::pause_audio. + * + * @return true if paused + */ + bool is_paused(); + + /** + * @brief Discord external IP detection. + * @return std::string Your external IP address + * @note This is a blocking operation that waits + * for a single packet from Discord's voice servers. + */ + std::string discover_ip(); +}; + +} // namespace dpp + diff --git a/include/dpp/dispatcher.h b/include/dpp/dispatcher.h new file mode 100644 index 0000000..4a6c183 --- /dev/null +++ b/include/dpp/dispatcher.h @@ -0,0 +1,1910 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DPP_CORO +#include +#endif /* DPP_CORO */ + +namespace dpp { + +/* Forward declaration */ +struct confirmation_callback_t; + +/** + * @brief A function used as a callback for any REST based command + */ +typedef std::function command_completion_event_t; + +/** @brief Base event parameter struct. + * Each event you receive from the library will have its parameter derived from this class. + * The class contains the raw event data, and a pointer to the current shard's dpp::discord_client object. + * You can also use this object to cancel the current event, meaning that any listeners after yours do + * not get notified of the current event if you call it. + */ +struct DPP_EXPORT event_dispatch_t { + /** + * @brief Raw event data. + * If you are using json on your websocket, this will contain json, and if you are using + * ETF as your websocket protocol, it will contain raw ETF data. + */ + const std::string raw_event; + + /** + * @brief Shard the event came from. + * Note that for some events, notably voice events, this may be nullptr. + */ + class discord_client* from; + + /** + * @brief Construct a new event_dispatch_t object + * + * @param client The shard the event originated on. May be a nullptr, e.g. for voice events + * @param raw Raw event data as JSON or ETF + */ + event_dispatch_t(class discord_client* client, const std::string& raw); + + /** + * @brief Cancels the event in progress. Any other attached lambdas for this event after this one are not called. + * Note that event cancellation is a thread local state, and not stored in the object (because object which can + * be cancelled is `const` during the event, and cannot itself contain the changeable state). + * @return const event_dispatch_t& reference to self for chaining + */ + const event_dispatch_t& cancel_event() const; + + /** + * @brief Returns true if the event is cancelled. + * Note that event cancellation is a thread local state, and not stored in the object (because object which can + * be cancelled is `const` during the event, and cannot itself contain the changeable state). + * @return true if the event is cancelled + */ + bool is_cancelled() const; +}; + +/** @brief Log messages */ +struct DPP_EXPORT log_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on. CAN BE NULL + * for log events originating from the cluster object + * @param raw Raw event text as JSON + */ + log_t(class discord_client* client, const std::string& raw); + /** Severity */ + loglevel severity; + /** Log Message */ + std::string message; + + log_t(const log_t&) = default; +}; + +namespace utility { + /** + * @brief Get a default logger that outputs to std::cout. + * e.g. + * ``` + * bot.on_log(dpp::utility::cout_logger()); + * ``` + * + * @return A logger for attaching to on_log + */ + std::function DPP_EXPORT cout_logger(); + + /** + * @brief The default callback handler for API calls. + * on error, sends the error to the logger. + * + * @return A lambda for attaching to an API callback + */ + std::function DPP_EXPORT log_error(); +} // namespace utility + +/** @brief Add user to scheduled event */ +struct DPP_EXPORT guild_scheduled_event_user_add_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on. CAN BE NULL + * for log events originating from the cluster object + * @param raw Raw event text as JSON + */ + guild_scheduled_event_user_add_t(class discord_client* client, const std::string& raw); + /** + * @brief event user added to + */ + snowflake event_id; + + /** + * @brief User being added + * + */ + snowflake user_id; + + /** + * @brief Guild being added to + * + */ + snowflake guild_id; +}; + +/** @brief Delete user from scheduled event */ +struct DPP_EXPORT guild_scheduled_event_user_remove_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on. CAN BE NULL + * for log events originating from the cluster object + * @param raw Raw event text as JSON + */ + guild_scheduled_event_user_remove_t(class discord_client* client, const std::string& raw); + /** + * @brief event user removed from + */ + snowflake event_id; + + /** + * @brief User being removed + * + */ + snowflake user_id; + + /** + * @brief Guild being removed from + * + */ + snowflake guild_id; +}; + +/** @brief Create scheduled event */ +struct DPP_EXPORT guild_scheduled_event_create_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on. CAN BE NULL + * for log events originating from the cluster object + * @param raw Raw event text as JSON + */ + guild_scheduled_event_create_t(class discord_client* client, const std::string& raw); + /** + * @brief created event + */ + scheduled_event created; +}; + +/** @brief Create scheduled event */ +struct DPP_EXPORT guild_scheduled_event_update_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on. CAN BE NULL + * for log events originating from the cluster object + * @param raw Raw event text as JSON + */ + guild_scheduled_event_update_t(class discord_client* client, const std::string& raw); + /** + * @brief updated event + */ + scheduled_event updated; +}; + +/** @brief Delete scheduled event */ +struct DPP_EXPORT guild_scheduled_event_delete_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on. CAN BE NULL + * for log events originating from the cluster object + * @param raw Raw event text as JSON + */ + guild_scheduled_event_delete_t(class discord_client* client, const std::string& raw); + /** + * @brief deleted event + */ + scheduled_event deleted; +}; + +/** @brief Create automod rule */ +struct DPP_EXPORT automod_rule_create_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on. CAN BE NULL + * for log events originating from the cluster object + * @param raw Raw event text as JSON + */ + automod_rule_create_t(class discord_client* client, const std::string& raw); + /** + * @brief updated event + */ + automod_rule created; +}; + +/** @brief Update automod rule */ +struct DPP_EXPORT automod_rule_update_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on. CAN BE NULL + * for log events originating from the cluster object + * @param raw Raw event text as JSON + */ + automod_rule_update_t(class discord_client* client, const std::string& raw); + /** + * @brief updated event + */ + automod_rule updated; +}; + +/** @brief Delete automod rule */ +struct DPP_EXPORT automod_rule_delete_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on. CAN BE NULL + * for log events originating from the cluster object + * @param raw Raw event text as JSON + */ + automod_rule_delete_t(class discord_client* client, const std::string& raw); + /** + * @brief updated event + */ + automod_rule deleted; +}; + +/** @brief Execute/trigger automod rule */ +struct DPP_EXPORT automod_rule_execute_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on. CAN BE NULL + * for log events originating from the cluster object + * @param raw Raw event text as JSON + */ + automod_rule_execute_t(class discord_client* client, const std::string& raw); + + snowflake guild_id; //!< the id of the guild in which action was executed + automod_action action; //!< the action which was executed + snowflake rule_id; //!< the id of the rule which action belongs to + automod_trigger_type rule_trigger_type; //!< the trigger type of rule which was triggered + snowflake user_id; //!< the id of the user which generated the content which triggered the rule + snowflake channel_id; //!< Optional: the id of the channel in which user content was posted + snowflake message_id; //!< Optional: the id of any user message which content belongs to + snowflake alert_system_message_id; //!< Optional: the id of any system auto moderation messages posted as a result of this action + std::string content; //!< the user generated text content + std::string matched_keyword; //!< the word or phrase configured in the rule that triggered the rule (may be empty) + std::string matched_content; //!< the substring in content that triggered the rule (may be empty) +}; + + + +/** @brief Create stage instance */ +struct DPP_EXPORT stage_instance_create_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on. CAN BE NULL + * for log events originating from the cluster object + * @param raw Raw event text as JSON + */ + stage_instance_create_t(class discord_client* client, const std::string& raw); + /** + * @brief created stage instance + */ + stage_instance created; +}; + +/** @brief Update stage instance */ +struct DPP_EXPORT stage_instance_update_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on. CAN BE NULL + * for log events originating from the cluster object + * @param raw Raw event text as JSON + */ + stage_instance_update_t(class discord_client* client, const std::string& raw); + /** + * @brief updated stage instance + */ + stage_instance updated; +}; + +/** @brief Delete stage instance */ +struct DPP_EXPORT stage_instance_delete_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on. CAN BE NULL + * for log events originating from the cluster object + * @param raw Raw event text as JSON + */ + stage_instance_delete_t(class discord_client* client, const std::string& raw); + /** + * @brief deleted stage instance + */ + stage_instance deleted; +}; + +/** @brief Voice state update */ +struct DPP_EXPORT voice_state_update_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + voice_state_update_t(class discord_client* client, const std::string& raw); + /** Voice state */ + voicestate state; +}; + +/** + * @brief Create interaction + */ +struct DPP_EXPORT interaction_create_t : public event_dispatch_t { + + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + interaction_create_t(class discord_client* client, const std::string& raw); + + + /** + * @brief Acknowledge interaction without displaying a message to the user, + * for use with button and select menu components. + * + * @param callback User function to execute when the api call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void reply(command_completion_event_t callback = utility::log_error()) const; + + /** + * @brief Send a reply for this interaction + * + * @param t Type of reply to send + * @param m Message object to send. Not all fields are supported by Discord. + * @param callback User function to execute when the api call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void reply(interaction_response_type t, const message & m, command_completion_event_t callback = utility::log_error()) const; + + /** + * @brief Send a reply for this interaction + * + * @param t Type of reply to send + * @param mt The string value to send, for simple text only messages + * @param callback User function to execute when the api call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void reply(interaction_response_type t, const std::string & mt, command_completion_event_t callback = utility::log_error()) const; + + /** + * @brief Send a reply for this interaction. + * Uses the default type of dpp::ir_channel_message_with_source, a simple message reply. + * + * @param m Message object to send. Not all fields are supported by Discord. + * @param callback User function to execute when the api call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void reply(const message & m, command_completion_event_t callback = utility::log_error()) const; + + /** + * @brief Send a reply for this interaction. + * Uses the default type of dpp::ir_channel_message_with_source, a simple message reply. + * + * @param mt The string value to send, for simple text only messages + * @param callback User function to execute when the api call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void reply(const std::string & mt, command_completion_event_t callback = utility::log_error()) const; + + /** + * @brief Reply to interaction with a dialog box + * + * @param mr Dialog box response to send + * @param callback User function to execute when the api call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void dialog(const interaction_modal_response& mr, command_completion_event_t callback = utility::log_error()) const; + + /** + * @brief Edit the response for this interaction + * + * @param m Message object to send. Not all fields are supported by Discord. + * @param callback User function to execute when the api call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void edit_response(const message & m, command_completion_event_t callback = utility::log_error()) const; + + /** + * @brief Edit the response for this interaction + * + * @param mt The string value to send, for simple text only messages + * @param callback User function to execute when the api call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void edit_response(const std::string & mt, command_completion_event_t callback = utility::log_error()) const; + + /** + * @brief Set the bot to 'thinking' state where you have up to 15 minutes to respond + * + * @param ephemeral True if the thinking state should be ephemeral + * @param callback User function to execute when the api call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void thinking(bool ephemeral = false, command_completion_event_t callback = utility::log_error()) const; + + /** + * @brief Get original response message for this interaction + * + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::message object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void get_original_response(command_completion_event_t callback) const; + + /** + * @brief Edit original response message for this interaction + * + * @param m Message object to send. Not all fields are supported by Discord. + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::message object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void edit_original_response(const message & m, command_completion_event_t callback = utility::log_error()) const; + + /** + * @brief Delete original response message for this interaction. This cannot be used on an ephemeral interaction response. + * + * @param callback Function to call when the API call completes. + * On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + void delete_original_response(command_completion_event_t callback = utility::log_error()) const; + +#ifdef DPP_CORO + /** + * @brief Acknowledge interaction without displaying a message to the user, + * for use with button and select menu components. + * + * On success the result will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + dpp::async co_reply() const; + + /** + * @brief Send a reply for this interaction + * + * @param t Type of reply to send + * @param m Message object to send. Not all fields are supported by Discord. + * On success the result will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + dpp::async co_reply(interaction_response_type t, const message & m) const; + + /** + * @brief Send a reply for this interaction + * + * @param t Type of reply to send + * @param mt The string value to send, for simple text only messages + * On success the result will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + dpp::async co_reply(interaction_response_type t, const std::string & mt) const; + + /** + * @brief Send a reply for this interaction. + * Uses the default type of dpp::ir_channel_message_with_source, a simple message reply. + * + * @param m Message object to send. Not all fields are supported by Discord. + * On success the result will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + dpp::async co_reply(const message & m) const; + + /** + * @brief Send a reply for this interaction. + * Uses the default type of dpp::ir_channel_message_with_source, a simple message reply. + * + * @param mt The string value to send, for simple text only messages + * On success the result will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + dpp::async co_reply(const std::string & mt) const; + + /** + * @brief Reply to interaction with a dialog box + * + * @param mr Dialog box response to send + * On success the result will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + dpp::async co_dialog(const interaction_modal_response& mr) const; + + /** + * @brief Edit the response for this interaction + * + * @param m Message object to send. Not all fields are supported by Discord. + * On success the result will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + dpp::async co_edit_response(const message & m) const; + + /** + * @brief Edit the response for this interaction + * + * @param mt The string value to send, for simple text only messages + * On success the result will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + dpp::async co_edit_response(const std::string & mt) const; + + /** + * @brief Set the bot to 'thinking' state where you have up to 15 minutes to respond + * + * @param ephemeral True if the thinking state should be ephemeral + * On success the result will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + dpp::async co_thinking(bool ephemeral = false) const; + + /** + * @brief Get original response message for this interaction + * + * On success the result will contain a dpp::message object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + dpp::async co_get_original_response() const; + + /** + * @brief Edit original response message for this interaction + * + * @param m Message object to send. Not all fields are supported by Discord. + * On success the result will contain a dpp::message object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + dpp::async co_edit_original_response(const message & m) const; + + /** + * @brief Delete original response message for this interaction. This cannot be used on an ephemeral interaction response. + * + * On success the result will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error(). + */ + dpp::async co_delete_original_response() const; +#endif /* DPP_CORO */ + + /** + * @brief command interaction + */ + interaction command; + + /** + * @brief Destroy this object + */ + virtual ~interaction_create_t() = default; + + /** + * @brief Get a slashcommand parameter + * + * @param name The name of the command line parameter to retrieve the value for + * @return command_value Returns the value of the first option that matches the given name. + * If no matches are found, an empty variant is returned. + * + * @throw dpp::logic_exception if the interaction is not for a command + */ + virtual command_value get_parameter(const std::string& name) const; +}; + +/** + * @brief User has issued a slash command + */ +struct DPP_EXPORT slashcommand_t : public interaction_create_t { + + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + slashcommand_t(class discord_client* client, const std::string& raw); +}; + +/** + * @brief Click on button + */ +struct DPP_EXPORT button_click_t : public interaction_create_t { +private: + using interaction_create_t::get_parameter; +public: + + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + button_click_t(class discord_client* client, const std::string& raw); + + /** + * @brief button custom id + */ + std::string custom_id; + /** + * @brief component type + */ + uint8_t component_type; +}; + +struct DPP_EXPORT form_submit_t : public interaction_create_t { +private: + using interaction_create_t::get_parameter; +public: + + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + form_submit_t(class discord_client* client, const std::string& raw); + + /** + * @brief button custom id + */ + std::string custom_id; + /** + * @brief Message components for form reply + */ + std::vector components; +}; + +/** + * @brief Discord requests that we fill a list of auto completion choices for a command option + */ +struct DPP_EXPORT autocomplete_t : public interaction_create_t { +private: + using interaction_create_t::get_parameter; +public: + + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + autocomplete_t(class discord_client* client, const std::string& raw); + + /** + * @brief Command ID + */ + dpp::snowflake id; + + /** + * @brief Command name + */ + std::string name; + + /** + * @brief auto completion options + */ + std::vector options; +}; + +/** + * @brief Base class for context menu interactions, e.g. right click on + * user or message. + */ +struct DPP_EXPORT context_menu_t : public interaction_create_t { +private: + using interaction_create_t::get_parameter; +public: + + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + context_menu_t(class discord_client* client, const std::string& raw); +}; + +/** + * @brief Event parameter for context menu interactions for messages + */ +struct DPP_EXPORT message_context_menu_t : public context_menu_t { + + /** + * @brief Related message + */ + message ctx_message; +public: + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + message_context_menu_t(class discord_client* client, const std::string& raw); + + /** + * @brief Get the message which was right-clicked on + * + * @return message right-clicked on + */ + message get_message() const; + + /** + * @brief Set the message object for this event + * + * @param m message to set + * @return message_context_menu_t& reference to self for fluent chaining + */ + message_context_menu_t& set_message(const message& m); +}; + +/** + * @brief Event parameter for context menu interactions for users + */ +struct DPP_EXPORT user_context_menu_t : public context_menu_t { + + /** + * @brief Related user + */ + user ctx_user; +public: + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + user_context_menu_t(class discord_client* client, const std::string& raw); + + /** + * @brief Get the user which was right-clicked on + * + * @return user right clicked on + */ + user get_user() const; + + /** + * @brief Set the user object for this event + * + * @param u user to set + * @return user_context_menu_t& reference to self for fluent chaining + */ + user_context_menu_t& set_user(const user& u); + +}; + +/** + * @brief Click on select + */ +struct DPP_EXPORT select_click_t : public interaction_create_t { +private: + using interaction_create_t::get_parameter; +public: + + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + select_click_t(class discord_client* client, const std::string& raw); + + /** + * @brief select menu custom id + */ + std::string custom_id; + /** + * @brief select menu values + */ + std::vector values; + /** + * @brief select menu component type (dpp::component_type) + */ + uint8_t component_type; +}; + + +/** @brief Delete guild */ +struct DPP_EXPORT guild_delete_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + guild_delete_t(class discord_client* client, const std::string& raw); + /** Deleted guild */ + guild* deleted; +}; + +/** @brief Update guild stickers */ +struct DPP_EXPORT guild_stickers_update_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + guild_stickers_update_t(class discord_client* client, const std::string& raw); + /** Updating guild */ + guild* updating_guild; + /** + * @brief stickers being updated + */ + std::vector stickers; +}; + +/** @brief Guild join request delete (user declined membership screening) */ +struct DPP_EXPORT guild_join_request_delete_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + guild_join_request_delete_t(class discord_client* client, const std::string& raw); + /** Deleted guild */ + snowflake guild_id; + /** + * @brief user id + */ + snowflake user_id; +}; + +/** @brief Delete channel */ +struct DPP_EXPORT channel_delete_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + channel_delete_t(class discord_client* client, const std::string& raw); + /** + * @brief guild channel is being deleted from + */ + guild* deleting_guild; + /** + * @brief channel being deleted + */ + channel* deleted; +}; + +/** @brief Update channel */ +struct DPP_EXPORT channel_update_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + channel_update_t(class discord_client* client, const std::string& raw); + /** + * @brief guild channel is being updated on + */ + guild* updating_guild; + /** + * @brief channel being updated + */ + channel* updated; +}; + +/** @brief Session ready */ +struct DPP_EXPORT ready_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + ready_t(class discord_client* client, const std::string& raw); + /** + * @brief websocket session id + */ + std::string session_id; + /** + * @brief shard id + */ + uint32_t shard_id; +}; + +/** @brief Message Deleted */ +struct DPP_EXPORT message_delete_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + message_delete_t(class discord_client* client, const std::string& raw); + /** + * @brief message being deleted + */ + message* deleted; +}; + +/** @brief Guild member remove */ +struct DPP_EXPORT guild_member_remove_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + guild_member_remove_t(class discord_client* client, const std::string& raw); + /** + * @brief guild user is being removed from + */ + guild* removing_guild; + /** + * @brief user being removed + */ + user* removed; +}; + +/** @brief Session resumed */ +struct DPP_EXPORT resumed_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + resumed_t(class discord_client* client, const std::string& raw); + /** + * @brief websocket session id + */ + std::string session_id; + /** + * @brief shard id + */ + uint32_t shard_id; +}; + +/** @brief Guild role create */ +struct DPP_EXPORT guild_role_create_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + guild_role_create_t(class discord_client* client, const std::string& raw); + /** + * @brief guild role is being created on + */ + guild* creating_guild; + /** + * @brief role being created + */ + role* created; +}; + +/** @brief Typing start */ +struct DPP_EXPORT typing_start_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + typing_start_t(class discord_client* client, const std::string& raw); + /** + * @brief guild user is typing on + */ + guild* typing_guild; + /** + * @brief channel user is typing on + */ + channel* typing_channel; + /** + * @brief user who is typing. + * Can be nullptr if user is not cached + */ + user* typing_user; + /** + * @brief User id of user typing. + * Always set regardless of caching + */ + snowflake user_id; + /** + * @brief Time of typing event + */ + time_t timestamp; +}; + +/** @brief Voice state update */ +struct DPP_EXPORT voice_track_marker_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on. + * Will always be null. + * @param raw Raw event text as JSON. + * Will always be empty. + */ + voice_track_marker_t(class discord_client* client, const std::string& raw); + /** Voice client */ + class discord_voice_client* voice_client; + /** Track metadata */ + std::string track_meta; +}; + + +/** @brief Message reaction add */ +struct DPP_EXPORT message_reaction_add_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + message_reaction_add_t(class discord_client* client, const std::string& raw); + /** + * @brief Guild reaction occurred on + */ + guild* reacting_guild; + /** + * @brief User who reacted + */ + user reacting_user; + /** + * @brief member data of user who reacted + */ + guild_member reacting_member; + /** + * @brief Channel ID the reaction happened on + */ + snowflake channel_id; + /** + * @brief channel the reaction happened on (Optional) + * @note only filled when the channel is cached + */ + channel* reacting_channel; + /** + * @brief emoji of reaction + */ + emoji reacting_emoji; + /** + * @brief message id of the message reacted upon + */ + snowflake message_id; + /** + * @brief ID of the user who authored the message which was reacted to (Optional) + */ + snowflake message_author_id; +}; + +/** @brief Guild members chunk */ +struct DPP_EXPORT guild_members_chunk_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + guild_members_chunk_t(class discord_client* client, const std::string& raw); + /** + * @brief guild the members chunk is for + */ + guild* adding; + /** + * @brief list of members in the chunk + */ + guild_member_map* members; +}; + +/** @brief Message reaction remove */ +struct DPP_EXPORT message_reaction_remove_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + message_reaction_remove_t(class discord_client* client, const std::string& raw); + /** + * @brief Guild reaction occurred on + */ + guild* reacting_guild; + /** + * @brief User who reacted + */ + dpp::snowflake reacting_user_id; + /** + * @brief Channel ID the reaction was removed in + */ + snowflake channel_id; + /** + * @brief channel the reaction happened on (optional) + * @note only filled when the channel is cached + */ + channel* reacting_channel; + /** + * @brief emoji of reaction + */ + emoji reacting_emoji; + /** + * @brief message id of the message reacted upon + */ + snowflake message_id; +}; + +/** @brief Create guild */ +struct DPP_EXPORT guild_create_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + guild_create_t(class discord_client* client, const std::string& raw); + /** + * @brief guild that was created + */ + guild* created; + /** + * @brief List of presences of all users on the guild. + * + * This is only filled if you have the GUILD_PRESENCES + * privileged intent. + */ + presence_map presences; + /** + * @brief List of scheduled events in the guild + */ + scheduled_event_map scheduled_events; + /** + * @brief List of stage instances in the guild + */ + stage_instance_map stage_instances; + /** + * @brief List of threads in the guild + */ + thread_map threads; + /** + * @brief List of stickers in the guild + */ + sticker_map stickers; +}; + +/** @brief Create channel */ +struct DPP_EXPORT channel_create_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + channel_create_t(class discord_client* client, const std::string& raw); + /** + * @brief guild channel was created on + */ + guild* creating_guild; + /** + * @brief channel that was created + */ + channel* created; +}; + +/** @brief Message remove emoji */ +struct DPP_EXPORT message_reaction_remove_emoji_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + message_reaction_remove_emoji_t(class discord_client* client, const std::string& raw); + /** + * @brief Guild reaction occurred on + */ + guild* reacting_guild; + /** + * @brief Channel ID the reactions was removed in + */ + snowflake channel_id; + /** + * @brief channel the reaction happened on (optional) + * @note only filled when the channel is cached + */ + channel* reacting_channel; + /** + * @brief emoji of reaction + */ + emoji reacting_emoji; + /** + * @brief message id of the message reacted upon + */ + snowflake message_id; +}; + +/** @brief Message delete bulk */ +struct DPP_EXPORT message_delete_bulk_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + message_delete_bulk_t(class discord_client* client, const std::string& raw); + /** + * @brief guild messages are being deleted upon + */ + guild* deleting_guild; + /** + * @brief user who is deleting the messages + */ + user* deleting_user; + /** + * @brief channel messages are being deleted from + */ + channel* deleting_channel; + /** + * @brief list of message ids of deleted messages + */ + std::vector deleted; +}; + +/** @brief Guild role update */ +struct DPP_EXPORT guild_role_update_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + guild_role_update_t(class discord_client* client, const std::string& raw); + /** + * @brief guild where roles are being updated + */ + guild* updating_guild; + /** + * @brief the role being updated + */ + role* updated; +}; + +/** @brief Guild role delete */ +struct DPP_EXPORT guild_role_delete_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + guild_role_delete_t(class discord_client* client, const std::string& raw); + /** + * @brief guild where role is being deleted + */ + guild* deleting_guild; + /** + * @brief role being deleted + */ + role* deleted; + /** + * @brief ID of the deleted role + */ + snowflake role_id; +}; + +/** @brief Channel pins update */ +struct DPP_EXPORT channel_pins_update_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + channel_pins_update_t(class discord_client* client, const std::string& raw); + /** + * @brief guild where message is being pinned + */ + guild* pin_guild; + /** + * @brief channel where message is being pinned + */ + channel* pin_channel; + /** + * @brief timestamp of pin + */ + time_t timestamp; +}; + +/** @brief Message remove all reactions */ +struct DPP_EXPORT message_reaction_remove_all_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + message_reaction_remove_all_t(class discord_client* client, const std::string& raw); + /** + * @brief Guild reaction occurred on + */ + guild* reacting_guild; + /** + * @brief Channel ID the reactions was removed in + */ + snowflake channel_id; + /** + * @brief channel the reaction happened on (optional) + * @note only filled when the channel is cached + */ + channel* reacting_channel; + /** + * @brief message id of the message reacted upon + */ + snowflake message_id; + +}; + +/** @brief Voice server update */ +struct DPP_EXPORT voice_server_update_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + voice_server_update_t(class discord_client* client, const std::string& raw); + /** + * @brief guild id where voice server updated + */ + snowflake guild_id; + /** + * @brief voice server token, used to connect to vc + */ + std::string token; + /** + * @brief voice server endpoint wss:// address + * + */ + std::string endpoint; +}; + +/** @brief Guild emojis update */ +struct DPP_EXPORT guild_emojis_update_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + guild_emojis_update_t(class discord_client* client, const std::string& raw); + /** + * @brief snowflake ids of list of emojis + */ + std::vector emojis; + /** + * @brief guild where emojis are being updated + */ + guild* updating_guild; +}; + +/** + * @brief Presence update + * + */ +struct DPP_EXPORT presence_update_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + presence_update_t(class discord_client* client, const std::string& raw); + /** + * @brief rich presence being updated + */ + presence rich_presence; +}; + +/** @brief Webhooks update */ +struct DPP_EXPORT webhooks_update_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + webhooks_update_t(class discord_client* client, const std::string& raw); + /** + * @brief guild where webhooks are being updated + */ + guild* webhook_guild; + /** + * @brief channel where webhooks are being updated + */ + channel* webhook_channel; +}; + +/** @brief Guild member add */ +struct DPP_EXPORT guild_member_add_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + guild_member_add_t(class discord_client* client, const std::string& raw); + /** + * @brief guild which gained new member + */ + guild* adding_guild; + /** + * @brief member which was added + */ + guild_member added; +}; + +/** @brief Invite delete */ +struct DPP_EXPORT invite_delete_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + invite_delete_t(class discord_client* client, const std::string& raw); + /** + * @brief the deleted invite + */ + invite deleted_invite; +}; + +/** @brief Guild update */ +struct DPP_EXPORT guild_update_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + guild_update_t(class discord_client* client, const std::string& raw); + /** + * @brief guild being updated + */ + guild* updated; +}; + +/** @brief Guild integrations update */ +struct DPP_EXPORT guild_integrations_update_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + guild_integrations_update_t(class discord_client* client, const std::string& raw); + /** + * @brief guild where integrations are being updated + */ + guild* updating_guild; +}; + +/** @brief Guild member update */ +struct DPP_EXPORT guild_member_update_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + guild_member_update_t(class discord_client* client, const std::string& raw); + /** + * @brief guild where member is being updated + */ + guild* updating_guild; + /** + * @brief member being updated + */ + guild_member updated; +}; + +/** @brief Invite create */ +struct DPP_EXPORT invite_create_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + invite_create_t(class discord_client* client, const std::string& raw); + /** + * @brief created invite + */ + invite created_invite; +}; + +/** @brief Message update */ +struct DPP_EXPORT message_update_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + message_update_t(class discord_client* client, const std::string& raw); + /** + * @brief message being updated + */ + message msg; +}; + +/** @brief User update */ +struct DPP_EXPORT user_update_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + user_update_t(class discord_client* client, const std::string& raw); + /** + * @brief user being updated + */ + user updated; +}; + +/** @brief Create message */ +struct DPP_EXPORT message_create_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + message_create_t(class discord_client* client, const std::string& raw); + /** + * @brief message that was created (sent). + */ + message msg; + /** + * @brief Send a text to the same channel as the channel_id in received event. + * @param m Text to send + * @param callback User function to execute once the API call completes. + * @note confirmation_callback_t::value contains a message object on success. On failure, value is undefined and confirmation_callback_t::is_error() is true. + */ + void send(const std::string& m, command_completion_event_t callback = utility::log_error()) const; + /** + * @brief Send a message to the same channel as the channel_id in received event. + * @param msg Message to send + * @param callback User function to execute once the API call completes. + * @note confirmation_callback_t::value contains a message object on success. On failure, value is undefined and confirmation_callback_t::is_error() is true. + */ + void send(message& msg, command_completion_event_t callback = utility::log_error()) const; + /** + * @brief Send a message to the same channel as the channel_id in received event. + * @param msg Message to send + * @param callback User function to execute once the API call completes. + * @note confirmation_callback_t::value contains a message object on success. On failure, value is undefined and confirmation_callback_t::is_error() is true. + */ + void send(message&& msg, command_completion_event_t callback = utility::log_error()) const; + /** + * @brief Reply to the message received in the event. + * @param m Text to send + * @param mention_replied_user mentions (pings) the author of message replied to, if true + * @param callback User function to execute once the API call completes. + * @note confirmation_callback_t::value contains a message object on success. On failure, value is undefined and confirmation_callback_t::is_error() is true. + */ + void reply(const std::string& m, bool mention_replied_user = false, command_completion_event_t callback = utility::log_error()) const; + /** + * @brief Reply to the message received in the event. + * @param msg Message to send as a reply. + * @param mention_replied_user mentions (pings) the author of message replied to, if true + * @param callback User function to execute once the API call completes. + * @note confirmation_callback_t::value contains a message object on success. On failure, value is undefined and confirmation_callback_t::is_error() is true. + */ + void reply(message& msg, bool mention_replied_user = false, command_completion_event_t callback = utility::log_error()) const; + /** + * @brief Reply to the message received in the event. + * @param msg Message to send as a reply. + * @param mention_replied_user mentions (pings) the author of message replied to, if true + * @param callback User function to execute once the API call completes. + * @note confirmation_callback_t::value contains a message object on success. On failure, value is undefined and confirmation_callback_t::is_error() is true. + */ + void reply(message&& msg, bool mention_replied_user = false, command_completion_event_t callback = utility::log_error()) const; +}; + +/** @brief Guild audit log entry create */ +struct DPP_EXPORT guild_audit_log_entry_create_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + guild_audit_log_entry_create_t(class discord_client* client, const std::string& raw); + /** + * @brief created audit log entry + */ + audit_entry entry; +}; + +/** @brief Guild ban add */ +struct DPP_EXPORT guild_ban_add_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + guild_ban_add_t(class discord_client* client, const std::string& raw); + /** + * @brief guild where ban was added + */ + guild* banning_guild; + /** + * @brief user being banned + */ + user banned; +}; + +/** @brief Guild ban remove */ +struct DPP_EXPORT guild_ban_remove_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + guild_ban_remove_t(class discord_client* client, const std::string& raw); + /** + * @brief guild where ban is being removed + */ + guild* unbanning_guild; + /** + * @brief user being unbanned + */ + user unbanned; +}; + +/** @brief Integration create */ +struct DPP_EXPORT integration_create_t : public event_dispatch_t { + /** Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + integration_create_t(class discord_client* client, const std::string& raw); + /** + * @brief created integration + */ + integration created_integration; +}; + +/** @brief Integration update */ +struct DPP_EXPORT integration_update_t : public event_dispatch_t { + /** + * @brief Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + integration_update_t(class discord_client* client, const std::string& raw); + /** + * @brief updated integration + */ + integration updated_integration; +}; + +/** @brief Integration delete */ +struct DPP_EXPORT integration_delete_t : public event_dispatch_t { + /** + * @brief Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + integration_delete_t(class discord_client* client, const std::string& raw); + /** + * @brief deleted integration + */ + integration deleted_integration; +}; + +/** @brief Thread Create*/ +struct DPP_EXPORT thread_create_t : public event_dispatch_t { + /** + * @brief Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + thread_create_t(class discord_client* client, const std::string& raw); + /** + * @brief guild where thread was created + */ + guild* creating_guild; + /** + * @brief thread created + */ + thread created; +}; + +/** @brief Thread Update +*/ +struct DPP_EXPORT thread_update_t : public event_dispatch_t { + /** + * @brief Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + thread_update_t(class discord_client* client, const std::string& raw); + /** + * @brief guild where thread was updated + */ + guild* updating_guild; + /** + * @brief thread updated + */ + thread updated; +}; + +/** @brief Thread Delete + */ +struct DPP_EXPORT thread_delete_t : public event_dispatch_t { + /** + * @brief Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + thread_delete_t(class discord_client* client, const std::string& raw); + /** + * @brief guild where thread was deleted + */ + guild* deleting_guild; + /** + * @brief thread deleted + */ + thread deleted; +}; + +/** @brief Thread List Sync + */ +struct DPP_EXPORT thread_list_sync_t : public event_dispatch_t { + /** + * @brief Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + thread_list_sync_t(class discord_client* client, const std::string& raw); + /** + * @brief guild where thread list was synchronised + */ + guild* updating_guild; + /** + * @brief list of threads (channels) synchronised + */ + std::vector threads; + /** + * @brief list of thread members for the channels (threads) + */ + std::vector members; +}; + +/** @brief Thread Member Update + */ +struct DPP_EXPORT thread_member_update_t : public event_dispatch_t { + /** + * @brief Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + thread_member_update_t(class discord_client* client, const std::string& raw); + /** + * @brief updated thread member + */ + thread_member updated; +}; + +/** @brief Thread Members Update + */ +struct DPP_EXPORT thread_members_update_t : public event_dispatch_t { + /** + * @brief Constructor + * @param client The shard the event originated on + * @param raw Raw event text as JSON + */ + thread_members_update_t(class discord_client* client, const std::string& raw); + /** + * @brief thread (channel) id + */ + snowflake thread_id; + /** + * @brief guild thread members updated on + */ + guild* updating_guild; + /** + * @brief new approximate member count + */ + uint8_t member_count; + /** + * @brief added members + */ + std::vector added; + /** + * @brief ids only of removed members + */ + std::vector removed_ids; +}; + +/** @brief voice buffer send + */ +struct DPP_EXPORT voice_buffer_send_t : public event_dispatch_t { + /** + * @brief Constructor + * @param client The shard the event originated on + * WILL ALWAYS be NULL. + * @param raw Raw event text as JSON + */ + voice_buffer_send_t(class discord_client* client, const std::string &raw); + /** + * @brief voice client where buffer was sent + */ + class discord_voice_client* voice_client; + /** + * @brief encoded size of sent buffer + */ + int buffer_size; +}; + +/** @brief voice user talking */ +struct DPP_EXPORT voice_user_talking_t : public event_dispatch_t { + /** + * @brief Constructor + * @param client The shard the event originated on + * WILL ALWAYS be NULL. + * @param raw Raw event text as JSON + */ + voice_user_talking_t(class discord_client* client, const std::string &raw); + /** + * @brief voice client where user is talking + */ + class discord_voice_client* voice_client; + /** + * @brief talking user id + */ + snowflake user_id; + /** + * @brief flags for talking user + */ + uint8_t talking_flags; +}; + +/** @brief voice user talking */ +struct DPP_EXPORT voice_ready_t : public event_dispatch_t { + /** + * @brief Constructor + * @param client The shard the event originated on + * WILL ALWAYS be NULL. + * @param raw Raw event text as JSON + */ + voice_ready_t(class discord_client* client, const std::string &raw); + /** + * @brief voice client which is ready + */ + class discord_voice_client* voice_client; + /** + * @brief id of voice channel + */ + snowflake voice_channel_id; +}; + +/** @brief voice receive packet */ +struct DPP_EXPORT voice_receive_t : public event_dispatch_t { + +friend class discord_voice_client; + + /** + * @brief Constructor + * @param client The shard the event originated on. + * WILL ALWAYS be NULL. + * @param raw Raw event text as UDP packet. + */ + voice_receive_t(class discord_client* client, const std::string &raw); + /** + * @brief Construct a new voice receive t object + * + * @param client The shard the event originated on. + * WILL ALWAYS be NULL. + * @param raw Raw event text as UDP packet. + * @param vc owning voice client pointer + * @param _user_id user id who is speaking, 0 for a mix of all user audio + * @param pcm user audio to set + * @param length length of user audio in bytes + */ + voice_receive_t(class discord_client* client, const std::string &raw, class discord_voice_client* vc, snowflake _user_id, uint8_t* pcm, size_t length); + /** + * @brief Voice client + */ + class discord_voice_client* voice_client; + /** + * @brief Audio data, encoded as 48kHz stereo PCM or Opus, + * @deprecated Please switch to using audio_data. + */ + uint8_t* audio = nullptr; + /** + * @brief Size of audio buffer + * @deprecated Please switch to using audio_data. + */ + size_t audio_size = 0; + /** + * @brief Audio data, encoded as 48kHz stereo PCM or Opus, + */ + std::basic_string audio_data; + /** + * @brief User ID of speaker (zero if unknown) + */ + snowflake user_id; +protected: + /** + * @brief Reassign values outside of the constructor for use within discord_voice_client + * + * @param vc owning voice client pointer + * @param _user_id user id who is speaking, 0 for a mix of all user audio + * @param pcm user audio to set + * @param length length of user audio in bytes + */ + void reassign(class discord_voice_client* vc, snowflake _user_id, uint8_t* pcm, size_t length); +}; + +/** @brief voice client speaking event */ +struct DPP_EXPORT voice_client_speaking_t : public event_dispatch_t { + /** + * @brief Constructor + * @param client The shard the event originated on. + * WILL ALWAYS be NULL. + * @param raw Raw event text as JSON + */ + voice_client_speaking_t(class discord_client* client, const std::string &raw); + /** + * @brief voice client where user is speaking + */ + class discord_voice_client* voice_client; + /** + * @brief speaking user id + * + */ + snowflake user_id; + /** + * @brief ssrc value of speaking user + */ + uint32_t ssrc; +}; + +/** @brief voice client disconnect event */ +struct DPP_EXPORT voice_client_disconnect_t : public event_dispatch_t { + /** + * @brief Constructor + * @param client The shard the event originated on. + * WILL ALWAYS be NULL. + * @param raw Raw event text as JSON + */ + voice_client_disconnect_t(class discord_client* client, const std::string &raw); + /** + * @brief voice client where user disconnected + */ + class discord_voice_client* voice_client; + /** + * @brief user id of user who left vc + */ + snowflake user_id; +}; + +} // namespace dpp + diff --git a/include/dpp/dns.h b/include/dpp/dns.h new file mode 100644 index 0000000..5a3a566 --- /dev/null +++ b/include/dpp/dns.h @@ -0,0 +1,76 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#ifdef _WIN32 +#include +#include +#else +#include +#include +#include +#endif +#include +#include +#include + +namespace dpp { + + /** + * @brief Represents a cached DNS result. + * Used by the ssl_client class to store cached copies of dns lookups. + */ + struct dns_cache_entry { + /** + * @brief Resolved address information + */ + addrinfo addr; + + /** + * @brief Socket address. + * Discord only supports ipv4, but sockaddr_in6 is larger + * than sockaddr_in, sockaddr_storage will hold either. This + * means that if discord ever do support ipv6 we just flip + * one value in dns.cpp and that should be all that is needed. + */ + sockaddr_storage ai_addr; + + /** + * @brief Time at which this cache entry is invalidated + */ + time_t expire_timestamp; + }; + + /** + * @brief Cache container type + */ + using dns_cache_t = std::unordered_map; + + /** + * @brief Resolve a hostname to an addrinfo + * + * @param hostname Hostname to resolve + * @param port A port number or named service, e.g. "80" + * @return dns_cache_entry* First IP address associated with the hostname DNS record + * @throw dpp::connection_exception On failure to resolve hostname + */ + const dns_cache_entry* resolve_hostname(const std::string& hostname, const std::string& port); +} // namespace dpp diff --git a/include/dpp/dpp.h b/include/dpp/dpp.h new file mode 100644 index 0000000..cade039 --- /dev/null +++ b/include/dpp/dpp.h @@ -0,0 +1,75 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/include/dpp/dtemplate.h b/include/dpp/dtemplate.h new file mode 100644 index 0000000..f0618f4 --- /dev/null +++ b/include/dpp/dtemplate.h @@ -0,0 +1,104 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include +#include +#include + +namespace dpp { + +/** + * @brief Represents a guild template + */ +class DPP_EXPORT dtemplate : public json_interface { +public: + /** + * @brief Template code + */ + std::string code; + /** + * @brief Template name + */ + std::string name; + /** + * @brief Template description + */ + std::string description; + /** + * @brief Usage counter + */ + uint32_t usage_count; + /** + * @brief User ID of creator + */ + snowflake creator_id; + /** + * @brief Creation date/time + * + */ + time_t created_at; + /** + * @brief Last update date/time + */ + time_t updated_at; + /** + * @brief Guild id the template is created from + */ + snowflake source_guild_id; + /** + * @brief True if needs synchronising + */ + bool is_dirty; + + /** + * @brief Construct a new dtemplate object + */ + dtemplate(); + + /** + * @brief Destroy the dtemplate object + */ + virtual ~dtemplate() = default; + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + dtemplate& fill_from_json(nlohmann::json* j); + + /** + * @brief Build the JSON for this object + * + * @param with_id Add ID to output + * @return std::string JSON content + */ + std::string build_json(bool with_id = false) const; + +}; + +/** A container of invites */ +typedef std::unordered_map dtemplate_map; + + +} // namespace dpp diff --git a/include/dpp/emoji.h b/include/dpp/emoji.h new file mode 100644 index 0000000..ab95736 --- /dev/null +++ b/include/dpp/emoji.h @@ -0,0 +1,219 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +namespace dpp { + +#define MAX_EMOJI_SIZE 256 * 1024 + +/** + * @brief Flags for dpp::emoji + */ +enum emoji_flags : uint8_t { + /// Emoji requires colons + e_require_colons = 0b00000001, + /// Managed (introduced by application) + e_managed = 0b00000010, + /// Animated + e_animated = 0b00000100, + /// Available (false if the guild doesn't meet boosting criteria, etc) + e_available = 0b00001000, +}; + +/** + * @brief Represents an emoji for a dpp::guild + */ +class DPP_EXPORT emoji : public managed, public json_interface { +public: + /** + * @brief Emoji name + */ + std::string name{}; + /** + * @brief User id who uploaded the emoji + */ + snowflake user_id{0}; + /** + * @brief Flags for the emoji from dpp::emoji_flags + */ + uint8_t flags{0}; + /** + * @brief Image data for the emoji if uploading + */ + std::string image_data{}; + + /** + * @brief Construct a new emoji object + */ + emoji() = default; + + /** + * @brief Construct a new emoji object with name, ID and flags + * + * @param name The emoji's name + * @param id ID, if it has one (unicode does not) + * @param flags Emoji flags (emoji_flags) + */ + emoji(const std::string_view name, const snowflake id = 0, const uint8_t flags = 0); + + /** + * @brief Copy constructor, copies another emoji's data + * + * @param rhs Emoji to copy + */ + emoji(const emoji &rhs) = default; + + /** + * @brief Move constructor, moves another emoji's data to this + * + * @param rhs Emoji to move from + */ + emoji(emoji &&rhs) noexcept = default; + + /** + * @brief Destroy the emoji object + */ + ~emoji() override = default; + + /** + * @brief Copy assignment operator, copies another emoji's data + * + * @param rhs Emoji to copy + */ + emoji &operator=(const emoji &rhs) = default; + + /** + * @brief Move constructor, moves another emoji's data to this + * + * @param rhs Emoji to move from + */ + emoji &operator=(emoji &&rhs) noexcept = default; + + /** + * @brief Create a mentionable emoji + * @param name The name of the emoji. + * @param id The ID of the emoji. + * @param is_animated is emoji animated. + * @return std::string The formatted mention of the emoji. + */ + static std::string get_mention(std::string_view name, snowflake id, bool is_animated = false); + + /** + * @brief Read class values from json object + * + * @param j A json object to read from + * @return A reference to self + */ + emoji& fill_from_json(nlohmann::json* j); + + /** + * @brief Build the json for this object + * + * @param with_id include the id in the JSON + * @return std::string json data + */ + std::string build_json(bool with_id = false) const override; + + /** + * @brief Emoji requires colons + * + * @return true Requires colons + * @return false Does not require colons + */ + bool requires_colons() const; + + /** + * @brief Emoji is managed + * + * @return true Is managed + * @return false Is not managed + */ + bool is_managed() const; + + /** + * @brief Emoji is animated + * + * @return true Is animated + * @return false Is noy animated + */ + bool is_animated() const; + + /** + * @brief Is available + * + * @return true Is available + * @return false Is unavailable + */ + bool is_available() const; + + /** + * @brief Load an image into the object as base64 + * + * @param image_blob Image binary data + * @param type Type of image. It can be one of `i_gif`, `i_jpg` or `i_png`. + * @return emoji& Reference to self + * @throw dpp::length_exception Image content exceeds discord maximum of 256 kilobytes + */ + emoji& load_image(std::string_view image_blob, const image_type type); + + /** + * @brief Format to name if unicode, name:id if has id or a:name:id if animated + * + * @return Formatted name for reactions + */ + std::string format() const; + + /** + * @brief Get the mention/ping for the emoji + * + * @return std::string mention + */ + std::string get_mention() const; + + /** + * @brief Get the custom emoji url + * + * @param size The size of the emoji in pixels. It can be any power of two between 16 and 4096, + * otherwise the default sized emoji is returned. + * @param format The format to use for the emoji. It can be one of `i_webp`, `i_jpg`, `i_png` or `i_gif`. + * When passing `i_gif`, it returns an empty string for non-animated emojis. Consider using the `prefer_animated` parameter instead. + * @param prefer_animated Whether you prefer gif format. + * If true, it'll return gif format whenever the emoji is available as animated. + * @return std::string emoji url or an empty string, if the id is not set + */ + std::string get_url(uint16_t size = 0, const image_type format = i_png, bool prefer_animated = true) const; +}; + +/** + * @brief Group of emojis + */ +typedef std::unordered_map emoji_map; + +} // namespace dpp diff --git a/include/dpp/etf.h b/include/dpp/etf.h new file mode 100644 index 0000000..6bcad06 --- /dev/null +++ b/include/dpp/etf.h @@ -0,0 +1,643 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Parts of this file inspired by, or outright copied from erlpack: + * https://github.com/discord/erlpack/ + * + * Acknowledgements: + * + * sysdep.h: + * Based on work by FURUHASHI Sadayuki in msgpack-python + * (https://github.com/msgpack/msgpack-python) + * + * Copyright (C) 2008-2010 FURUHASHI Sadayuki + * Licensed under the Apache License, Version 2.0 (the "License"). + * + ************************************************************************************/ + +#pragma once +#include +#include +#include + +namespace dpp { + +/** Current ETF format version in use */ +const uint8_t FORMAT_VERSION = 131; + +/** + * @brief Represents a token which identifies the type of value which follows it + * in the ETF binary structure. + */ +enum etf_token_type : uint8_t { + /// 68 [Distribution header] + ett_distribution = 'D', + /// 70 [Float64:IEEE float] + ett_new_float = 'F', + /// 77 [UInt32:Len, UInt8:Bits, Len:Data] + ett_bit_binary = 'M', + /// 80 [UInt4:UncompressedSize, N:ZlibCompressedData] + ett_compressed = 'P', + /// 97 [UInt8:Int] + ett_smallint = 'a', + /// 98 [Int32:Int] + ett_integer = 'b', + /// 99 [31:Float String] Float in string format (formatted "%.20e", sscanf "%lf"). Superseded by ett_new_float + ett_float = 'c', + /// 100 [UInt16:Len, Len:AtomName] max Len is 255 + ett_atom = 'd', + /// 101 [atom:Node, UInt32:ID, UInt8:Creation] + ett_reference = 'e', + /// 102 [atom:Node, UInt32:ID, UInt8:Creation] + ett_port = 'f', + /// 103 [atom:Node, UInt32:ID, UInt32:Serial, UInt8:Creation] + ett_pid = 'g', + /// 104 [UInt8:Arity, N:Elements] + ett_small_tuple = 'h', + /// 105 [UInt32:Arity, N:Elements] + ett_large_tuple = 'i', + /// 106 empty list + ett_nil = 'j', + /// 107 [UInt16:Len, Len:Characters] + ett_string = 'k', + /// 108 [UInt32:Len, Elements, Tail] + ett_list = 'l', + /// 109 [UInt32:Len, Len:Data] + ett_binary = 'm', + /// 110 [UInt8:n, UInt8:Sign, n:nums] + ett_bigint_small = 'n', + /// 111 [UInt32:n, UInt8:Sign, n:nums] + ett_bigint_large = 'o', + /// 112 [UInt32:Size, UInt8:Arity, 16*Uint6-MD5:Uniq, UInt32:Index, UInt32:NumFree, atom:Module, int:OldIndex, int:OldUniq, pid:Pid, NunFree*ext:FreeVars] + ett_new_function = 'p', + /// 113 [atom:Module, atom:Function, smallint:Arity] + ett_export = 'q', + /// 114 [UInt16:Len, atom:Node, UInt8:Creation, Len*UInt32:ID] + ett_new_reference = 'r', + /// 115 [UInt8:Len, Len:AtomName] + ett_atom_small = 's', + /// 116 [UInt32:Airty, N:Pairs] + ett_map = 't', + /// 117 [UInt4:NumFree, pid:Pid, atom:Module, int:Index, int:Uniq, NumFree*ext:FreeVars] + ett_function = 'u', + /// 118 [UInt16:Len, Len:AtomName] max Len is 255 characters (up to 4 bytes per) + ett_atom_utf8 = 'v', + /// 119 [UInt8:Len, Len:AtomName] + ett_atom_utf8_small = 'w' +}; + +/** + * @brief A horrible structure used within the ETF parser to convert uint64_t to double and back. + * This is horrible, but it is the official way erlang term format does this, so we can't really + * mess with it much. + */ +union type_punner { + /** + * @brief binary integer value + */ + uint64_t ui64; + /** + * @brief double floating point value + */ + double df; +}; + +/** + * @brief Represents a buffer of bytes being encoded into ETF + */ +struct DPP_EXPORT etf_buffer { + /** + * @brief Raw buffer + */ + std::vector buf; + /** + * @brief Current used length of buffer + * (this is different from buf.size() as it is pre-allocated + * using resize and may not all be in use) + */ + size_t length; + + /** + * @brief Construct a new etf buffer object + * + * @param initial initial buffer size to allocate + */ + etf_buffer(size_t initial); + + /** + * @brief Destroy the etf buffer object + */ + ~etf_buffer(); +}; + +/** + * @brief The etf_parser class can serialise and deserialise ETF (Erlang Term Format) + * into and out of an nlohmann::json object, so that layers above the websocket don't + * have to be any different for handling ETF. + */ +class DPP_EXPORT etf_parser { + + /** + * @brief Current size of binary data + */ + size_t size; + + /** + * @brief Current offset into binary data + */ + size_t offset; + + /** + * @brief Pointer to binary ETF data to be decoded + */ + uint8_t* data; + + /** + * @brief Parse a single value, and if that value contains other + * values (e.g. an array or map) then call itself recursively. + * + * @return nlohmann::json JSON value from the ETF + */ + nlohmann::json inner_parse(); + + /** + * @brief Read 8 bits of data from the buffer + * + * @return uint8_t data retrieved + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + uint8_t read_8_bits(); + + /** + * @brief Read 16 bits of data from the buffer + * + * @return uint16_t data retrieved + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + uint16_t read_16_bits(); + + /** + * @brief Read 32 bits of data from the buffer + * + * @return uint32_t data retrieved + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + uint32_t read_32_bits(); + + /** + * @brief Read 64 bits of data from the buffer + * + * @return uint64_t data retrieved + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + uint64_t read_64_bits(); + + /** + * @brief Read string data from the buffer + * + * @param length Length of string to retrieve + * @return const char* data retrieved + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + const char* read_string(uint32_t length); + + /** + * @brief Process an 'atom' value. + * An atom is a "label" or constant value within the data, + * such as a key name, nullptr, or false. + * + * @return nlohmann::json value converted to JSON + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + nlohmann::json process_atom(const char* atom, uint16_t length); + + /** + * @brief Decode an 'atom' value. + * + * @return nlohmann::json value converted to JSON + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + nlohmann::json decode_atom(); + + /** + * @brief Decode a small 'atom' value. + * + * @return nlohmann::json value converted to JSON + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + nlohmann::json decode_small_atom(); + + /** + * @brief Decode a small integer value (0-255). + * + * @return nlohmann::json value converted to JSON + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + nlohmann::json decode_small_integer(); + + /** + * @brief Decode an integer value (-MAXINT -> MAXINT-1). + * + * @return nlohmann::json value converted to JSON + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + nlohmann::json decode_integer(); + + /** + * @brief Decode an array of values. + * + * @return nlohmann::json values converted to JSON + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + nlohmann::json decode_array(uint32_t length); + + /** + * @brief Decode a list of values. + * + * @return nlohmann::json values converted to JSON + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + nlohmann::json decode_list(); + + /** + * @brief Decode a 'tuple' value. + * + * @return nlohmann::json value converted to JSON + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + nlohmann::json decode_tuple(uint32_t length); + + /** + * @brief Decode a nil 'atom' value. + * + * @return nlohmann::json value converted to JSON + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + nlohmann::json decode_nil(); + + /** + * @brief Decode a map (object) value. + * Will recurse to evaluate each member variable. + * + * @return nlohmann::json values converted to JSON + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + nlohmann::json decode_map(); + + /** + * @brief Decode a floating point numeric value. + * (depreciated in erlang but still expected to be supported) + * + * @return nlohmann::json value converted to JSON + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + nlohmann::json decode_float(); + + /** + * @brief Decode a floating type numeric value. + * + * @return nlohmann::json value converted to JSON + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + nlohmann::json decode_new_float(); + + /** + * @brief Decode a 'bigint' value. + * + * @return nlohmann::json value converted to JSON + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + nlohmann::json decode_bigint(uint32_t digits); + + /** + * @brief Decode a small 'bigint' value. + * + * @return nlohmann::json value converted to JSON + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + nlohmann::json decode_bigint_small(); + + /** + * @brief Decode a large 'bigint' value. + * + * @return nlohmann::json value converted to JSON + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + nlohmann::json decode_bigint_large(); + + /** + * @brief Decode a binary value. + * + * @return nlohmann::json value converted to JSON + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + nlohmann::json decode_binary(); + + /** + * @brief Decode a string value. + * + * @return nlohmann::json value converted to JSON + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + nlohmann::json decode_string(); + + /** + * @brief Decode a string list value. + * + * @return nlohmann::json value converted to JSON + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + nlohmann::json decode_string_as_list(); + + /** + * @brief Decode a 'small tuple' value. + * + * @return nlohmann::json value converted to JSON + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + nlohmann::json decode_tuple_small(); + + /** + * @brief Decode a 'large tuple' value. + * + * @return nlohmann::json value converted to JSON + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + nlohmann::json decode_tuple_large(); + + /** + * @brief Decode a compressed value. + * This is a zlib-compressed binary blob which contains another + * ETF object. + * + * @return nlohmann::json value converted to JSON + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + nlohmann::json decode_compressed(); + + /** + * @brief Decode a 'reference' value. + * Erlang expects this to be supported, in practice Discord doesn't send these right now. + * + * @return nlohmann::json value converted to JSON + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + nlohmann::json decode_reference(); + + /** + * @brief Decode a 'new reference' value. + * Erlang expects this to be supported, in practice Discord doesn't send these right now. + * + * @return nlohmann::json value converted to JSON + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + nlohmann::json decode_new_reference(); + + /** + * @brief Decode a 'port' value. + * Erlang expects this to be supported, in practice Discord doesn't send these right now. + * + * @return nlohmann::json value converted to JSON + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + nlohmann::json decode_port(); + + /** + * @brief Decode a 'PID' value. + * Erlang expects this to be supported, in practice Discord doesn't send these right now. + * + * @return nlohmann::json value converted to JSON + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + nlohmann::json decode_pid(); + + /** + * @brief Decode an 'export' value. + * Erlang expects this to be supported, in practice Discord doesn't send these right now. + * + * @return nlohmann::json value converted to JSON + * @throw dpp::exception Data stream isn't long enough to fetch requested bits + */ + nlohmann::json decode_export(); + + /** + * @brief Write to output buffer for creation of ETF from JSON + * + * @param pk buffer struct + * @param bytes byte buffer to write + * @param l number of bytes to write + * @throw std::exception Buffer cannot be extended + */ + void buffer_write(etf_buffer *pk, const char *bytes, size_t l); + + /** + * @brief Append version number to ETF buffer + * + * @param b buffer to append to + * @throw std::exception Buffer cannot be extended + */ + void append_version(etf_buffer *b); + + /** + * @brief Append nil value to ETF buffer + * + * @param b buffer to append to + * @throw std::exception Buffer cannot be extended + */ + void append_nil(etf_buffer *b); + + /** + * @brief Append false value to ETF buffer + * + * @param b buffer to append to + * @throw std::exception Buffer cannot be extended + */ + void append_false(etf_buffer *b); + + /** + * @brief Append true value to ETF buffer + * + * @param b buffer to append to + * @throw std::exception Buffer cannot be extended + */ + void append_true(etf_buffer *b); + + /** + * @brief Append small integer value to ETF buffer + * + * @param b buffer to append to + * @param d double to append + * @throw std::exception Buffer cannot be extended + */ + void append_small_integer(etf_buffer *b, unsigned char d); + + /** + * @brief Append integer value to ETF buffer + * + * @param b buffer to append to + * @param d integer to append + * @throw std::exception Buffer cannot be extended + */ + void append_integer(etf_buffer *b, int32_t d); + + /** + * @brief Append 64 bit integer value to ETF buffer + * + * @param b buffer to append to + * @param d integer to append + * @throw std::exception Buffer cannot be extended + */ + void append_unsigned_long_long(etf_buffer *b, unsigned long long d); + + /** + * @brief Append 64 bit integer value to ETF buffer + * + * @param b buffer to append to + * @param d integer to append + * @throw std::exception Buffer cannot be extended + */ + void append_long_long(etf_buffer *b, long long d); + + /** + * @brief Append double value to ETF buffer + * + * @param b buffer to append to + * @param f double to append + * @throw std::exception Buffer cannot be extended + */ + void append_double(etf_buffer *b, double f); + + /** + * @brief Append atom value to ETF buffer + * + * @param b buffer to append to + * @param bytes pointer to string to append + * @param size size of string to append + * @throw std::exception Buffer cannot be extended + */ + void append_atom(etf_buffer *b, const char *bytes, size_t size); + + /** + * @brief Append utf8 atom value to ETF buffer + * + * @param b buffer to append to + * @param bytes pointer to string to append + * @param size size of string to append + * @throw std::exception Buffer cannot be extended + */ + void append_atom_utf8(etf_buffer *b, const char *bytes, size_t size); + + /** + * @brief Append binary value to ETF buffer + * + * @param b buffer to append to + * @param bytes pointer to string to append + * @param size size of string to append + * @throw std::exception Buffer cannot be extended + */ + void append_binary(etf_buffer *b, const char *bytes, size_t size); + + /** + * @brief Append string value to ETF buffer + * + * @param b buffer to append to + * @param bytes pointer to string to append + * @param size size of string to append + * @throw std::exception Buffer cannot be extended + */ + void append_string(etf_buffer *b, const char *bytes, size_t size); + + /** + * @brief Append tuple value to ETF buffer + * + * @param b buffer to append to + * @param size size of value to append + * @throw std::exception Buffer cannot be extended + */ + void append_tuple_header(etf_buffer *b, size_t size); + + /** + * @brief Append list terminator to ETF buffer + * + * @param b buffer to append to + * @throw std::exception Buffer cannot be extended + */ + void append_nil_ext(etf_buffer *b); + + /** + * @brief Append a list header value to ETF buffer + * + * @param b buffer to append to + * @param size size of values to append + * @throw std::exception Buffer cannot be extended + */ + void append_list_header(etf_buffer *b, size_t size); + + /** + * @brief Append a map header value to ETF buffer + * + * @param b buffer to append to + * @param size size of values to append + * @throw std::exception Buffer cannot be extended + */ + void append_map_header(etf_buffer *b, size_t size); + + /** + * @brief Build ETF buffer + * + * @param j JSON object to build from + * @param b Buffer to append to + * @throw std::exception Buffer cannot be extended + */ + void inner_build(const nlohmann::json* j, etf_buffer* b); + +public: + /** + * @brief Construct a new etf parser object + */ + etf_parser(); + + /** + * @brief Destroy the etf parser object + */ + ~etf_parser(); + + /** + * @brief Convert ETF binary content to nlohmann::json + * + * @param in Raw binary ETF data (generally from a websocket) + * @return nlohmann::json JSON data for use in the library + * @throw dpp::exception Malformed or otherwise invalid ETF content + */ + nlohmann::json parse(const std::string& in); + + /** + * @brief Create ETF binary data from nlohmann::json + * + * @param j JSON value to encode to ETF + * @return std::string raw ETF data. Note that this can + * and probably will contain null values, use std::string::data() + * and std::string::size() to manipulate or send it. + * @throw std::exception Not enough memory, or invalid data types/values + */ + std::string build(const nlohmann::json& j); +}; + +} // namespace dpp diff --git a/include/dpp/event.h b/include/dpp/event.h new file mode 100644 index 0000000..e229d51 --- /dev/null +++ b/include/dpp/event.h @@ -0,0 +1,156 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include + +#define event_decl(x,wstype) /** @brief Internal event handler for wstype websocket events. Called for each websocket message of this type. @internal */ \ + class x : public event { public: virtual void handle(dpp::discord_client* client, nlohmann::json &j, const std::string &raw); }; + +namespace dpp { + +class discord_client; + +/** + * @brief The events namespace holds the internal event handlers for each websocket event. + * These are handled internally and also dispatched to the user code if the event is hooked. + */ +namespace events { + +/** + * @brief An event object represents an event handled internally, passed from the websocket e.g. MESSAGE_CREATE. + */ +class DPP_EXPORT event { +public: + /** Pure virtual method for event handler code + * @param client The creating shard + * @param j The json data of the event + * @param raw The raw event json + */ + virtual void handle(class discord_client* client, nlohmann::json &j, const std::string &raw) = 0; +}; + +/* Internal logger */ +event_decl(logger,LOG); + +/* Guilds */ +event_decl(guild_create,GUILD_CREATE); +event_decl(guild_update,GUILD_UPDATE); +event_decl(guild_delete,GUILD_DELETE); +event_decl(guild_ban_add,GUILD_BAN_ADD); +event_decl(guild_ban_remove,GUILD_BAN_REMOVE); +event_decl(guild_emojis_update,GUILD_EMOJIS_UPDATE); +event_decl(guild_integrations_update,GUILD_INTEGRATIONS_UPDATE); +event_decl(guild_join_request_delete,GUILD_JOIN_REQUEST_DELETE); +event_decl(guild_stickers_update,GUILD_STICKERS_UPDATE); + +/* Stage channels */ +event_decl(stage_instance_create,STAGE_INSTANCE_CREATE); +event_decl(stage_instance_update,STAGE_INSTANCE_UPDATE); +event_decl(stage_instance_delete,STAGE_INSTANCE_DELETE); + +/* Guild members */ +event_decl(guild_member_add,GUILD_MEMBER_ADD); +event_decl(guild_member_remove,GUILD_MEMBER_REMOVE); +event_decl(guild_members_chunk,GUILD_MEMBERS_CHUNK); +event_decl(guild_member_update,GUILD_MEMBERS_UPDATE); + +/* Guild roles */ +event_decl(guild_role_create,GUILD_ROLE_CREATE); +event_decl(guild_role_update,GUILD_ROLE_UPDATE); +event_decl(guild_role_delete,GUILD_ROLE_DELETE); + +/* Session state */ +event_decl(resumed,RESUMED); +event_decl(ready,READY); + +/* Channels */ +event_decl(channel_create,CHANNEL_CREATE); +event_decl(channel_update,CHANNEL_UPDATE); +event_decl(channel_delete,CHANNEL_DELETE); +event_decl(channel_pins_update,CHANNEL_PINS_UPDATE); + +/* Threads */ +event_decl(thread_create,THREAD_CREATE); +event_decl(thread_update,THREAD_UPDATE); +event_decl(thread_delete,THREAD_DELETE); +event_decl(thread_list_sync,THREAD_LIST_SYNC); +event_decl(thread_member_update,THREAD_MEMBER_UPDATE); +event_decl(thread_members_update,THREAD_MEMBERS_UPDATE); + +/* Messages */ +event_decl(message_create,MESSAGE_CREATE); +event_decl(message_update,MESSAGE_UPDATE); +event_decl(message_delete,MESSAGE_DELETE); +event_decl(message_delete_bulk,MESSAGE_DELETE_BULK); + +/* Presence/typing */ +event_decl(presence_update,PRESENCE_UPDATE); +event_decl(typing_start,TYPING_START); + +/* Users (outside of guild) */ +event_decl(user_update,USER_UPDATE); + +/* Message reactions */ +event_decl(message_reaction_add,MESSAGE_REACTION_ADD); +event_decl(message_reaction_remove,MESSAGE_REACTION_REMOVE); +event_decl(message_reaction_remove_all,MESSAGE_REACTION_REMOVE_ALL); +event_decl(message_reaction_remove_emoji,MESSAGE_REACTION_REMOVE_EMOJI); + +/* Invites */ +event_decl(invite_create,INVITE_CREATE); +event_decl(invite_delete,INVITE_DELETE); + +/* Voice */ +event_decl(voice_state_update,VOICE_STATE_UPDATE); +event_decl(voice_server_update,VOICE_SERVER_UPDATE); + +/* Webhooks */ +event_decl(webhooks_update,WEBHOOKS_UPDATE); + +/* Application commands */ +event_decl(interaction_create,INTERACTION_CREATE); + +/* Integrations */ +event_decl(integration_create,INTEGRATION_CREATE); +event_decl(integration_update,INTEGRATION_UPDATE); +event_decl(integration_delete,INTEGRATION_DELETE); + +/* Scheduled events */ +event_decl(guild_scheduled_event_create,GUILD_SCHEDULED_EVENT_CREATE); +event_decl(guild_scheduled_event_update,GUILD_SCHEDULED_EVENT_UPDATE); +event_decl(guild_scheduled_event_delete,GUILD_SCHEDULED_EVENT_DELETE); +event_decl(guild_scheduled_event_user_add,GUILD_SCHEDULED_EVENT_USER_ADD); +event_decl(guild_scheduled_event_user_remove,GUILD_SCHEDULED_EVENT_USER_REMOVE); + +/* Auto moderation */ +event_decl(automod_rule_create, AUTO_MODERATION_RULE_CREATE); +event_decl(automod_rule_update, AUTO_MODERATION_RULE_UPDATE); +event_decl(automod_rule_delete, AUTO_MODERATION_RULE_DELETE); +event_decl(automod_rule_execute, AUTO_MODERATION_ACTION_EXECUTION); + +/* Audit log */ +event_decl(guild_audit_log_entry_create, GUILD_AUDIT_LOG_ENTRY_CREATE); + +} // namespace events +} // namespace dpp diff --git a/include/dpp/event_router.h b/include/dpp/event_router.h new file mode 100644 index 0000000..f6dd3b4 --- /dev/null +++ b/include/dpp/event_router.h @@ -0,0 +1,644 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using json = nlohmann::json; + +namespace dpp { + +#ifdef DPP_CORO + +template +class event_router_t; + +namespace detail { + +/** @brief Internal cogwheels for dpp::event_router_t */ +namespace event_router { + +/** @brief State of an owner of an event_router::awaitable */ +enum class awaiter_state { + /** @brief Awaitable is not being awaited */ + none, + /** @brief Awaitable is being awaited */ + waiting, + /** @brief Awaitable will be resumed imminently */ + resuming, + /** @brief Awaitable will be cancelled imminently */ + cancelling +}; + +/** + * @brief Awaitable object representing an event. + * A user can co_await on this object to resume the next time the event is fired, + * optionally with a condition. + */ +template +class awaitable { + friend class event_router_t; + + /** @brief Resume the coroutine waiting on this object */ + void resume() { + std_coroutine::coroutine_handle<>::from_address(handle).resume(); + } + + /** @brief Event router that will manage this object */ + event_router_t *self; + + /** @brief Predicate on the event, or nullptr for always match */ + std::function predicate = nullptr; + + /** @brief Event that triggered a resumption, to give to the resumer */ + const T *event = nullptr; + + /** @brief Coroutine handle, type-erased */ + void* handle = nullptr; + + /** @brief The state of the awaiting coroutine */ + std::atomic state = awaiter_state::none; + + /** Default constructor is accessible only to event_router_t */ + awaitable() = default; + + /** Normal constructor is accessible only to event_router_t */ + template + awaitable(event_router_t *router, F&& fun) : self{router}, predicate{std::forward(fun)} {} + +public: + /** This object is not copyable. */ + awaitable(const awaitable &) = delete; + + /** Move constructor. */ + awaitable(awaitable &&rhs) noexcept : self{rhs.self}, predicate{std::move(rhs.predicate)}, event{rhs.event}, handle{std::exchange(rhs.handle, nullptr)}, state{rhs.state.load(std::memory_order_relaxed)} {} + + /** This object is not copyable. */ + awaitable& operator=(const awaitable &) = delete; + + /** Move assignment operator. */ + awaitable& operator=(awaitable&& rhs) noexcept { + self = rhs.self; + predicate = std::move(rhs.predicate); + event = rhs.event; + handle = std::exchange(rhs.handle, nullptr); + state = rhs.state.load(std::memory_order_relaxed); + return *this; + } + + /** + * @brief Request cancellation. This will detach this object from the event router and resume the awaiter, which will be thrown dpp::task_cancelled::exception. + * + * @throw ??? As this resumes the coroutine, it may throw any exceptions at the caller. + */ + void cancel(); + + /** + * @brief First function called by the standard library when awaiting this object. Returns true if we need to suspend. + * + * @retval false always. + */ + [[nodiscard]] constexpr bool await_ready() const noexcept; + + /** + * @brief Second function called by the standard library when awaiting this object, after suspension. + * This will attach the object to its event router, to be resumed on the next event that satisfies the predicate. + * + * @return void never resume on call. + */ + void await_suspend(detail::std_coroutine::coroutine_handle<> caller); + + /** + * @brief Third and final function called by the standard library, called when resuming the coroutine. + * + * @throw @ref task_cancelled_exception if cancel() has been called + * @return const T& __Reference__ to the event that matched + */ + [[maybe_unused]] const T& await_resume(); +}; + +} + +} +#endif + +/** + * @brief A returned event handle for an event which was attached + */ +typedef size_t event_handle; + +/** + * @brief Handles routing of an event to multiple listeners. + * Multiple listeners may attach to the event_router_t by means of @ref operator()(F&&) "operator()". Passing a + * lambda into @ref operator()(F&&) "operator()" attaches to the event. + * + * @details Dispatchers of the event may call the @ref call() method to cause all listeners + * to receive the event. + * + * The @ref empty() method will return true if there are no listeners attached + * to the event_router_t (this can be used to save time by not constructing objects that + * nobody will ever see). + * + * The @ref detach() method removes an existing listener from the event, + * using the event_handle ID returned by @ref operator()(F&&) "operator()". + * + * This class is used by the library to route all websocket events to listening code. + * + * Example: + * + * @code{cpp} + * // Declare an event that takes log_t as its parameter + * event_router_t my_event; + * + * // Attach a listener to the event + * event_handle id = my_event([&](const log_t& cc) { + * std::cout << cc.message << "\n"; + * }); + * + * // Construct a log_t and call the event (listeners will receive the log_t object) + * log_t lt; + * lt.message = "foo"; + * my_event.call(lt); + * + * // Detach from an event using the handle returned by operator() + * my_event.detach(id); + * @endcode + * + * @tparam T type of single parameter passed to event lambda derived from event_dispatch_t + */ +template class event_router_t { +private: + friend class cluster; + +#ifdef DPP_CORO + friend class detail::event_router::awaitable; +#endif + + event_handle next_handle = 1; + + /** + * @brief Thread safety mutex + */ + mutable std::shared_mutex mutex; + + /** + * @brief Container of event listeners keyed by handle, + * as handles are handed out sequentially they will always + * be called in they order they are bound to the event + * as std::map is an ordered container. + */ + std::map> dispatch_container; + +#ifdef DPP_CORO + /** + * @brief Mutex for messing with coro_awaiters. + */ + mutable std::shared_mutex coro_mutex; + + /** + * @brief Vector containing the awaitables currently being awaited on for this event router. + */ + mutable std::vector *> coro_awaiters; +#else + /** + * @brief Dummy for ABI compatibility between DPP_CORO and not + */ + utility::dummy definitely_not_a_mutex; + + /** + * @brief Dummy for ABI compatibility between DPP_CORO and not + */ + utility::dummy> definitely_not_a_vector; +#endif + + /** + * @brief A function to be called whenever the method is called, to check + * some condition that is required for this event to trigger correctly. + */ + std::function warning; + + /** + * @brief Next handle to be given out by the event router + */ + +protected: + + /** + * @brief Set the warning callback object used to check that this + * event is capable of running properly + * + * @param warning_function A checking function to call + */ + void set_warning_callback(std::function warning_function) { + warning = warning_function; + } + +#ifdef DPP_CORO + /** + * @brief Attach a suspended coroutine to this event router via detail::event_router::awaitable. + * It will be resumed and detached when an event satisfying its condition completes, or it is cancelled. + * + * This is for internal usage only, the user way to do this is to co_await it (which will call this when suspending) + * This guarantees that the coroutine is indeed suspended and thus can be resumed at any time + * + * @param awaiter Awaiter to attach + */ + void attach_awaiter(detail::event_router::awaitable *awaiter) { + std::unique_lock lock{coro_mutex}; + + coro_awaiters.emplace_back(awaiter); + } + + /** + * @brief Detach an awaiting coroutine handle from this event router. + * This is mostly called when a detail::event_router::awaitable is cancelled. + * + * @param handle Coroutine handle to find in the attached coroutines + */ + void detach_coro(void *handle) { + std::unique_lock lock{coro_mutex}; + + coro_awaiters.erase(std::remove_if(coro_awaiters.begin(), coro_awaiters.end(), [handle](detail::event_router::awaitable const *awaiter) { return awaiter->handle == handle; }), coro_awaiters.end()); + } + + /** + * @brief Resume any awaiter whose predicate matches this event, or is null. + * + * @param event Event to compare and pass to accepting awaiters + */ + void resume_awaiters(const T& event) const { + std::vector*> to_resume; + std::unique_lock lock{coro_mutex}; + + for (auto it = coro_awaiters.begin(); it != coro_awaiters.end();) { + detail::event_router::awaitable* awaiter = *it; + + if (awaiter->predicate && !awaiter->predicate(event)) { + ++it; + } else { + using state_t = detail::event_router::awaiter_state; + + /** + * If state == none (was never awaited), do nothing + * If state == waiting, prevent resumption, resume on our end + * If state == resuming || cancelling, ignore + * + * Technically only cancelling || waiting should be possible here + * We do this by trying to exchange "waiting" with "resuming". If that returns false, this is presumed to be "cancelling" + */ + + state_t s = state_t::waiting; + if (awaiter->state.compare_exchange_strong(s, state_t::resuming)) { + to_resume.emplace_back(awaiter); + awaiter->event = &event; + + it = coro_awaiters.erase(it); + } else { + ++it; + } + } + } + lock.unlock(); + for (detail::event_router::awaitable* awaiter : to_resume) + awaiter->resume(); + } +#endif + +public: + /** + * @brief Construct a new event_router_t object. + */ + event_router_t() = default; + + /** + * @brief Destructor. Will cancel any coroutine awaiting on events. + * + * @throw ! Cancelling a coroutine will throw a dpp::task_cancelled_exception to it. + * This will be caught in this destructor, however, make sure no other exceptions are thrown in the coroutine after that or it will terminate. + */ + ~event_router_t() { +#ifdef DPP_CORO + while (!coro_awaiters.empty()) { + // cancel all awaiters. here we cannot do the usual loop as we'd need to lock coro_mutex, and cancel() locks and modifies coro_awaiters + try { + coro_awaiters.back()->cancel(); + /* + * will resume coroutines and may throw ANY exception, including dpp::task_cancelled_exception cancel() throws at them. + * we catch that one. for the rest, good luck :) + * realistically the only way any other exception would pop up here is if someone catches dpp::task_cancelled_exception THEN throws another exception. + */ + } catch (const dpp::task_cancelled_exception &) { + // ok. likely we threw this one + } + } +#endif + } + + /** + * @brief Call all attached listeners. + * Listeners may cancel, by calling the event.cancel method. + * + * @param event Class to pass as parameter to all listeners. + */ + void call(const T& event) const { + if (warning) { + warning(event); + } + +#ifdef DPP_CORO + resume_awaiters(event); +#endif + + std::shared_lock l(mutex); + for (const auto& [_, listener] : dispatch_container) { + if (!event.is_cancelled()) { + listener(event); + } + }; + }; + +#ifdef DPP_CORO + /** + * @brief Obtain an awaitable object that refers to an event with a certain condition. + * It can be co_await-ed to wait for the next event that satisfies this condition. + * On resumption the awaiter will be given __a reference__ to the event, + * saving it in a variable is recommended to avoid variable lifetime issues. + * + * @details Example: @code{cpp} + * dpp::job my_handler(dpp::slashcommand_t event) { + * co_await event.co_reply(dpp::message().add_component(dpp::component().add_component().set_label("click me!").set_id("test"))); + * + * dpp::button_click_t b = co_await c->on_button_click.with([](const dpp::button_click_t &event){ return event.custom_id == "test"; }); + * + * // do something on button click + * } + * @endcode + * + * This can be combined with dpp::when_any and other awaitables, for example dpp::cluster::co_sleep to create @ref expiring-buttons "expiring buttons". + * + * @warning On resumption the awaiter will be given a reference to the event. + * This means that variable may become dangling at the next co_await, be careful and save it in a variable + * if you need to. + * @param pred Predicate to check the event against. This should be a callable of the form `bool(const T&)` + * where T is the event type, returning true if the event is to match. + * @return awaitable An awaitable object that can be co_await-ed to await an event matching the condition. + */ + template +#ifndef _DOXYGEN_ + requires utility::callable_returns +#endif + auto when(Predicate&& pred) +#ifndef _DOXYGEN_ + noexcept(noexcept(std::function{std::declval()})) +#endif + { + return detail::event_router::awaitable{this, std::forward(pred)}; + } + + /** + * @brief Obtain an awaitable object that refers to any event. + * It can be co_await-ed to wait for the next event. + * + * Example: + * @details Example: @code{cpp} + * dpp::job my_handler(dpp::slashcommand_t event) { + * co_await event.co_reply(dpp::message().add_component(dpp::component().add_component().set_label("click me!").set_id("test"))); + * + * dpp::button_click_t b = co_await c->on_message_create; + * + * // do something on button click + * } + * @endcode + * + * This can be combined with dpp::when_any and other awaitables, for example dpp::cluster::co_sleep to create expiring buttons. + * + * @warning On resumption the awaiter will be given a reference to the event. + * This means that variable may become dangling at the next co_await, be careful and save it in a variable + * if you need to. + * @return awaitable An awaitable object that can be co_await-ed to await an event matching the condition. + */ + [[nodiscard]] auto operator co_await() noexcept { + return detail::event_router::awaitable{this, nullptr}; + } +#endif + + /** + * @brief Returns true if the container of listeners is empty, + * i.e. there is nothing listening for this event right now. + * + * @retval true if there are no listeners + * @retval false if there are some listeners + */ + [[nodiscard]] bool empty() const { +#ifdef DPP_CORO + std::shared_lock lock{mutex}; + std::shared_lock coro_lock{coro_mutex}; + + return dispatch_container.empty() && coro_awaiters.empty(); +#else + std::shared_lock lock{mutex}; + + return dispatch_container.empty(); +#endif + } + + /** + * @brief Returns true if any listeners are attached. + * + * This is the boolean opposite of event_router_t::empty(). + * @retval true if listeners are attached + * @retval false if no listeners are attached + */ + operator bool() const { + return !empty(); + } + +#ifdef _DOXYGEN_ + /** + * @brief Attach a callable to the event, adding a listener. + * The callable should either be of the form `void(const T &)` or + * `dpp::job(T)` (the latter requires DPP_CORO to be defined), + * where T is the event type for this event router. + * + * This has the exact same behavior as using \ref attach(F&&) "attach". + * + * @see attach + * @param fun Callable to attach to event + * @return event_handle An event handle unique to this event, used to + * detach the listener from the event later if necessary. + */ + template + [[maybe_unused]] event_handle operator()(F&& fun); + + /** + * @brief Attach a callable to the event, adding a listener. + * The callable should either be of the form `void(const T &)` or + * `dpp::job(T)` (the latter requires DPP_CORO to be defined), + * where T is the event type for this event router. + * + * @param fun Callable to attach to event + * @return event_handle An event handle unique to this event, used to + * detach the listener from the event later if necessary. + */ + template + [[maybe_unused]] event_handle attach(F&& fun); +#else /* not _DOXYGEN_ */ +# ifdef DPP_CORO + /** + * @brief Attach a callable to the event, adding a listener. + * The callable should either be of the form `void(const T &)` or + * `dpp::job(T)`, where T is the event type for this event router. + * + * @param fun Callable to attach to event + * @return event_handle An event handle unique to this event, used to + * detach the listener from the event later if necessary. + */ + template + requires (utility::callable_returns || utility::callable_returns) + [[maybe_unused]] event_handle operator()(F&& fun) { + return this->attach(std::forward(fun)); + } + + /** + * @brief Attach a callable to the event, adding a listener. + * The callable should either be of the form `void(const T &)` or + * `dpp::job(T)`, where T is the event type for this event router. + * + * @param fun Callable to attach to event + * @return event_handle An event handle unique to this event, used to + * detach the listener from the event later if necessary. + */ + template + requires (utility::callable_returns || utility::callable_returns) + [[maybe_unused]] event_handle attach(F&& fun) { + std::unique_lock l(mutex); + event_handle h = next_handle++; + dispatch_container.emplace(h, std::forward(fun)); + return h; + } +# else + /** + * @brief Attach a callable to the event, adding a listener. + * The callable should be of the form `void(const T &)` + * where T is the event type for this event router. + * + * @param fun Callable to attach to event + * @return event_handle An event handle unique to this event, used to + * detach the listener from the event later if necessary. + */ + template + [[maybe_unused]] std::enable_if_t, event_handle> operator()(F&& fun) { + return this->attach(std::forward(fun)); + } + + /** + * @brief Attach a callable to the event, adding a listener. + * The callable should be of the form `void(const T &)` + * where T is the event type for this event router. + * + * @warning You cannot call this within an event handler. + * + * @param fun Callable to attach to event + * @return event_handle An event handle unique to this event, used to + * detach the listener from the event later if necessary. + */ + template + [[maybe_unused]] std::enable_if_t, event_handle> attach(F&& fun) { + std::unique_lock l(mutex); + event_handle h = next_handle++; + dispatch_container.emplace(h, std::forward(fun)); + return h; + } +# endif /* DPP_CORO */ +#endif /* _DOXYGEN_ */ + /** + * @brief Detach a listener from the event using a previously obtained ID. + * + * @warning You cannot call this within an event handler. + * + * @param handle An ID obtained from @ref operator(F&&) "operator()" + * @retval true The event was successfully detached + * @retval false The ID is invalid (possibly already detached, or does not exist) + */ + [[maybe_unused]] bool detach(const event_handle& handle) { + std::unique_lock l(mutex); + return this->dispatch_container.erase(handle); + } +}; + +#ifdef DPP_CORO + +namespace detail::event_router { + +template +void awaitable::cancel() { + awaiter_state s = awaiter_state::waiting; + /** + * If state == none (was never awaited), do nothing + * If state == waiting, prevent resumption, resume on our end + * If state == resuming || cancelling, ignore + */ + if (state.compare_exchange_strong(s, awaiter_state::cancelling)) { + self->detach_coro(handle); + resume(); + } +} + +template +constexpr bool awaitable::await_ready() const noexcept { + return false; +} + +template +void awaitable::await_suspend(detail::std_coroutine::coroutine_handle<> caller) { + state.store(awaiter_state::waiting); + handle = caller.address(); + self->attach_awaiter(this); +} + +template +const T &awaitable::await_resume() { + handle = nullptr; + predicate = nullptr; + if (state.exchange(awaiter_state::none, std::memory_order_relaxed) == awaiter_state::cancelling) { + throw dpp::task_cancelled_exception{"event_router::awaitable was cancelled"}; + } + return *std::exchange(event, nullptr); +} + +} +#endif + +} // namespace dpp diff --git a/include/dpp/exception.h b/include/dpp/exception.h new file mode 100644 index 0000000..b389ecc --- /dev/null +++ b/include/dpp/exception.h @@ -0,0 +1,212 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include +#include + +namespace dpp { + +/** + * @brief The dpp::exception class derives from std::exception and supports some other + * ways of passing in error details such as via std::string. + */ +class exception : public std::exception +{ +protected: + /** + * @brief Exception message + */ + std::string msg; + +public: + + using std::exception::exception; + + /** + * @brief Construct a new exception object + */ + exception() = default; + + /** + * @brief Construct a new exception object + * + * @param what reason message + */ + explicit exception(const char* what) : msg(what) { } + + /** + * @brief Construct a new exception object + * + * @param what reason message + * @param len length of reason message + */ + exception(const char* what, size_t len) : msg(what, len) { } + + /** + * @brief Construct a new exception object + * + * @param what reason message + */ + explicit exception(const std::string& what) : msg(what) { } + + /** + * @brief Construct a new exception object + * + * @param what reason message + */ + explicit exception(std::string&& what) : msg(std::move(what)) { } + + /** + * @brief Construct a new exception object (copy constructor) + */ + exception(const exception&) = default; + + /** + * @brief Construct a new exception object (move constructor) + */ + exception(exception&&) = default; + + /** + * @brief Destroy the exception object + */ + ~exception() override = default; + + /** + * @brief Copy assignment operator + * + * @return exception& reference to self + */ + exception & operator = (const exception &) = default; + + /** + * @brief Move assignment operator + * + * @return exception& reference to self + */ + exception & operator = (exception&&) = default; + + /** + * @brief Get exception message + * + * @return const char* error message + */ + [[nodiscard]] const char* what() const noexcept override { return msg.c_str(); }; + +}; + +#ifndef _DOXYGEN_ + #define derived_exception(name, ancestor) class name : public dpp::ancestor { \ + public: \ + using dpp::ancestor::ancestor; \ + name() = default; \ + explicit name(const char* what) : ancestor(what) { } \ + name(const char* what, size_t len) : ancestor(what, len) { } \ + explicit name(const std::string& what) : ancestor(what) { } \ + explicit name(std::string&& what) : ancestor(what) { } \ + name(const name&) = default; \ + name(name&&) = default; \ + ~name() override = default; \ + name & operator = (const name &) = default; \ + name & operator = (name&&) = default; \ + [[nodiscard]] const char* what() const noexcept override { return msg.c_str(); }; \ + }; +#endif + +#ifdef _DOXYGEN_ + /* + * THESE DEFINITIONS ARE NOT THE REAL DEFINITIONS USED BY PROGRAM CODE. + * + * They exist only to cause Doxygen to emit proper documentation for the derived exception types. + * Proper definitions are emitted by the `derived_exception` macro in the "else" section. + */ + + /** + * @brief Represents an error in logic, e.g. you asked the library to do something the Discord API does not support + * @note This is a stub for documentation purposes. For full information on supported methods please see dpp::exception. + */ + class logic_exception : public dpp::exception { }; + /** + * @brief Represents an error reading or writing to a file + * @note This is a stub for documentation purposes. For full information on supported methods please see dpp::exception. + */ + class file_exception : public dpp::exception { }; + /** + * @brief Represents an error establishing or maintaining a connection + * @note This is a stub for documentation purposes. For full information on supported methods please see dpp::exception. + */ + class connection_exception : public dpp::exception { }; + /** + * @brief Represents an error with voice processing + * @note This is a stub for documentation purposes. For full information on supported methods please see dpp::exception. + */ + class voice_exception : public dpp::exception { }; + /** + * @brief Represents an error on a REST API call, e.g. a HTTPS request + * @note This is a stub for documentation purposes. For full information on supported methods please see dpp::exception. + */ + class rest_exception : public dpp::exception { }; + /** + * @brief Represents invalid length of argument being passed to a function + * @note This is a stub for documentation purposes. For full information on supported methods please see dpp::exception. + */ + class length_exception : public dpp::exception { }; + /** + * @brief Represents inability to parse data, usually caused by malformed JSON or ETF + * @note This is a stub for documentation purposes. For full information on supported methods please see dpp::exception. + */ + class parse_exception : public dpp::exception { }; + /** + * @brief Represents invalid access to dpp's cache or its members, which may or may not exist. + * @note This is a stub for documentation purposes. For full information on supported methods please see dpp::exception. + */ + class cache_exception : public dpp::exception { }; + /** + * @brief Represents an attempt to construct a cluster with an invalid bot token. + * @note This is a stub for documentation purposes. For full information on supported methods please see dpp::exception. + */ + class invalid_token_exception : public dpp::rest_exception { }; +#ifdef DPP_CORO + /** + * @brief Represents the cancellation of a task. Will be thrown to the awaiter of a cancelled task. + * @note This is a stub for documentation purposes. For full information on supported methods please see dpp::exception. + */ + class task_cancelled_exception : public dpp::exception { }; +#endif /* DPP_CORO */ +#else + derived_exception(logic_exception, exception); + derived_exception(file_exception, exception); + derived_exception(connection_exception, exception); + derived_exception(voice_exception, exception); + derived_exception(rest_exception, exception); + derived_exception(invalid_token_exception, rest_exception); + derived_exception(length_exception, exception); + derived_exception(parse_exception, exception); + derived_exception(cache_exception, exception); +# ifdef DPP_CORO + derived_exception(task_cancelled_exception, exception); +# endif /* DPP_CORO */ +#endif + +} // namespace dpp + diff --git a/include/dpp/export.h b/include/dpp/export.h new file mode 100644 index 0000000..4672f2f --- /dev/null +++ b/include/dpp/export.h @@ -0,0 +1,68 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once + +/* Compile-time check for C++17. + * Either one of the following causes a compile time error: + * __cplusplus not defined at all (this means we are being compiled on a C compiler) + * MSVC defined and _MSVC_LANG < 201703L (Visual Studio, but not C++17 or newer) + * MSVC not defined and __cplusplus < 201703L (Non-visual studio, but not C++17 or newer) + * The additional checks are required because MSVC doesn't correctly set __cplusplus to 201703L, + * which is hugely non-standard, but apparently "it broke stuff" so they dont ever change it + * from C++98. Ugh. + */ +#if (!defined(__cplusplus) || (defined(_MSC_VER) && (!defined(_MSVC_LANG) || _MSVC_LANG < 201703L)) || (!defined(_MSC_VER) && __cplusplus < 201703L)) + #error "D++ Requires a C++17 compatible C++ compiler. Please ensure that you have enabled C++17 in your compiler flags." +#endif + +#ifndef DPP_STATIC + /* Dynamic linked build as shared object or dll */ + #ifdef DPP_BUILD + /* Building the library */ + #ifdef _WIN32 + #include + #define DPP_EXPORT __declspec(dllexport) + #else + #define DPP_EXPORT + #endif + #else + /* Including the library */ + #ifdef _WIN32 + #define DPP_EXPORT __declspec(dllimport) + #else + #define DPP_EXPORT + #endif + #endif +#else + /* Static linked build */ + #if defined(_WIN32) && defined(DPP_BUILD) + #include + #endif + #define DPP_EXPORT +#endif + +#ifndef _WIN32 + #define SOCKET int +#else + #define NOMINMAX + + #include +#endif \ No newline at end of file diff --git a/include/dpp/guild.h b/include/dpp/guild.h new file mode 100644 index 0000000..dd00231 --- /dev/null +++ b/include/dpp/guild.h @@ -0,0 +1,1374 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace dpp { + +class channel; + +/** + * @brief Represents voice regions for guilds and channels. + * @deprecated Deprecated in favour of per-channel regions. + * Please use channel::rtc_region instead. + */ +enum region : uint8_t { + r_brazil, //!< Brazil + r_central_europe, //!< Central Europe + r_hong_kong, //!< Hong Kong + r_india, //!< India + r_japan, //!< Japan + r_russia, //!< Russia + r_singapore, //!< Singapore + r_south_africa, //!< South Africa + r_sydney, //!< Sydney + r_us_central, //!< US Central + r_us_east, //!< US East Coast + r_us_south, //!< US South + r_us_west, //!< US West Coast + r_western_europe //!< Western Europe +}; + +/** + * @brief The various flags that represent the status of a dpp::guild object + */ +enum guild_flags : uint32_t { + /** Large guild */ + g_large = 0b00000000000000000000000000000001, + /** Unavailable guild (inaccessible due to an outage) */ + g_unavailable = 0b00000000000000000000000000000010, + /** Guild has widget enabled */ + g_widget_enabled = 0b00000000000000000000000000000100, + /** Guild can have an invite splash image */ + g_invite_splash = 0b00000000000000000000000000001000, + /** Guild can have VIP regions */ + g_vip_regions = 0b00000000000000000000000000010000, + /** Guild can have a vanity url */ + g_vanity_url = 0b00000000000000000000000000100000, + /** Guild is verified */ + g_verified = 0b00000000000000000000000001000000, + /** Guild is partnered */ + g_partnered = 0b00000000000000000000000010000000, + /** Community features enabled */ + g_community = 0b00000000000000000000000100000000, + /** Guild has enabled role subscriptions */ + g_role_subscription_enabled = 0b00000000000000000000001000000000, + /** Guild has access to create announcement channels */ + g_news = 0b00000000000000000000010000000000, + /** Guild is discoverable in discovery */ + g_discoverable = 0b00000000000000000000100000000000, + /** Guild is featureable */ + g_featureable = 0b00000000000000000001000000000000, + /** Guild can have an animated icon (doesn't mean it actually has one though) */ + g_animated_icon = 0b00000000000000000010000000000000, + /** Guild can have a banner image */ + g_banner = 0b00000000000000000100000000000000, + /** Guild has a welcome screen */ + g_welcome_screen_enabled = 0b00000000000000001000000000000000, + /** Guild has a member verification gate */ + g_member_verification_gate = 0b00000000000000010000000000000000, + /** Guild has a preview */ + g_preview_enabled = 0b00000000000000100000000000000000, + /** Guild join notifications are off */ + g_no_join_notifications = 0b00000000000001000000000000000000, + /** Guild boost notifications are off */ + g_no_boost_notifications = 0b00000000000010000000000000000000, + /** Guild has an actual animated icon (set by the icon hash starting with 'a_') */ + g_has_animated_icon = 0b00000000000100000000000000000000, + /** Guild has an actual animated banner (set by the icon hash starting with 'a_') */ + g_has_animated_banner = 0b00000000001000000000000000000000, + /** Guild setup tips are off */ + g_no_setup_tips = 0b00000000010000000000000000000000, + /** "Wave to say hi" sticker prompt buttons are off */ + g_no_sticker_greeting = 0b00000000100000000000000000000000, + /** guild has enabled monetization */ + g_monetization_enabled = 0b00000001000000000000000000000000, + /** guild has increased custom sticker slots */ + g_more_stickers = 0b00000010000000000000000000000000, + /** Guild has enabled the role subscription promo page */ + g_creator_store_page_enabled = 0b00000100000000000000000000000000, + /** guild is able to set role icons */ + g_role_icons = 0b00001000000000000000000000000000, + /** guild has access to the seven day archive time for threads + * @deprecated Removed by Discord + */ + g_seven_day_thread_archive = 0b00010000000000000000000000000000, + /** guild has access to the three day archive time for threads + * @deprecated Removed by Discord + */ + g_three_day_thread_archive = 0b00100000000000000000000000000000, + /** guild has enabled ticketed events */ + g_ticketed_events = 0b01000000000000000000000000000000, + /** guild can have channel banners + * @deprecated Removed by Discord + */ + g_channel_banners = 0b10000000000000000000000000000000, +}; + +/** + * @brief Additional boolean flag values for guild, as guild_flags is full + */ +enum guild_flags_extra : uint16_t { + /** Guild has premium progress bar enabled */ + g_premium_progress_bar_enabled = 0b0000000000000001, + /** Guild can have an animated banner (doesn't mean it actually has one though) */ + g_animated_banner = 0b0000000000000010, + /** Guild has auto moderation */ + g_auto_moderation = 0b0000000000000100, + /** Guild has paused invites, preventing new users from joining */ + g_invites_disabled = 0b0000000000001000, + /** Guild has been set as support server of an app in the App Directory */ + g_developer_support_server = 0b0000000000010000, + /** Guild role subscription purchase and renewal notifications are off */ + g_no_role_subscription_notifications = 0b0000000000100000, + /** Guild role subscription sticker reply buttons are off */ + g_no_role_subscription_notification_replies = 0b0000000001000000, + /** Guild has role subscriptions that can be purchased */ + g_role_subscriptions_available_for_purchase = 0b0000000010000000, + /** Guild has disabled alerts for join raids in the configured safety alerts channel */ + g_raid_alerts_disabled = 0b0000000100000000, +}; + +/** + * @brief Various flags that can be used to indicate the status of a guild member. + * @note Use the setter functions in dpp::guild_member and do not toggle the bits yourself. + */ +enum guild_member_flags : uint16_t { + /** Member deafened in voice channels */ + gm_deaf = 0b0000000000000001, + /** Member muted in voice channels */ + gm_mute = 0b0000000000000010, + /** Member pending verification by membership screening */ + gm_pending = 0b0000000000000100, + /** Member has animated guild-specific avatar */ + gm_animated_avatar = 0b0000000000001000, + /** gm_deaf or gm_mute has been toggled */ + gm_voice_action = 0b0000000000010000, + /** Member has left and rejoined the guild */ + gm_did_rejoin = 0b0000000000100000, + /** Member has completed onboarding */ + gm_completed_onboarding = 0b0000000001000000, + /** Member is exempt from guild verification requirements */ + gm_bypasses_verification = 0b0000000010000000, + /** Member has started onboarding */ + gm_started_onboarding = 0b0000000100000000, +}; + +/** + * @brief Represents dpp::user membership upon a dpp::guild. + * This contains the user's nickname, guild roles, and any other guild-specific flags. + */ +class DPP_EXPORT guild_member { +public: + /** Nickname, or empty string if they don't have a nickname on this guild */ + std::string nickname; + /** List of roles this user has on this guild */ + std::vector roles; + /** Guild id */ + snowflake guild_id; + /** User id */ + snowflake user_id; + /** User avatar (per-server avatar is a nitro only feature) */ + utility::iconhash avatar; + /** timestamp of when the time out will be removed; until then, they cannot interact with the guild */ + time_t communication_disabled_until; + /** Date and time the user joined the guild */ + time_t joined_at; + /** Boosting since */ + time_t premium_since; + /** A set of flags built from the bitmask defined by dpp::guild_member_flags */ + uint16_t flags; + + /** Default constructor */ + guild_member(); + + /** Fill this object from a json object. + * @param j The json object to get data from + * @param g_id The guild id to associate the member with + * @param u_id The user id to associate the member with + * @return Reference to self for call chaining + */ + guild_member& fill_from_json(nlohmann::json* j, snowflake g_id, snowflake u_id); + + /** + * @brief Build json string for the member object + * + * @param with_id Add ID to output + * @return std::string json string + */ + std::string build_json(bool with_id = false) const; + + /** + * @brief Returns true if the user is in time-out (communication disabled) + * + * @return true user is in time-out + * @return false user is not in time-out + */ + bool is_communication_disabled() const; + + /** + * @brief Returns true if the user is deafened + * + * @return true user is deafened + * @return false user is not deafened + */ + bool is_deaf() const; + + /** + * @brief Returns true if the user is muted + * + * @return true user muted + * @return false user not muted + */ + bool is_muted() const; + + /** + * @brief Returns true if pending verification by membership screening + * + * @return true user has completed membership screening + * @return false user has not completed membership screening + */ + bool is_pending() const; + + /** + * @brief Returns true if the user has left and rejoined the guild + * + * @return true user has left and rejoined the guild + * @return false user has not rejoined + */ + bool has_rejoined() const; + + /** + * @brief Returns true if the user has completed onboarding + * + * @return true user has completed onboarding + * @return false user has not completed onboarding + */ + bool has_completed_onboarding() const; + + /** + * @brief Returns true if the user has started onboarding + * + * @return true user has started onboarding + * @return false user has not started onboarding yet + */ + bool has_started_onboarding() const; + + /** + * @brief Returns true if the user is exempt from guild verification requirements + * + * @return true user bypasses verification + * @return false user doesn't bypass verification + */ + bool has_bypasses_verification() const; + + /** + * @brief Returns true if the user's per-guild custom avatar is animated + * + * @return true user's custom avatar is animated + * @return false user's custom avatar is not animated + */ + bool has_animated_guild_avatar() const; + + /** + * @brief Returns the member's per guild avatar url if they have one, otherwise returns an empty string. + * + * @note per-server avatar is a nitro only feature so it might be not set. If you need the user avatar, use user::get_avatar_url. + * + * @param size The size of the avatar in pixels. It can be any power of two between 16 and 4096, + * otherwise the default sized avatar is returned. + * @param format The format to use for the avatar. It can be one of `i_webp`, `i_jpg`, `i_png` or `i_gif`. + * When passing `i_gif`, it returns an empty string for non-animated images. Consider using the `prefer_animated` parameter instead. + * @param prefer_animated Whether you prefer gif format. + * If true, it'll return gif format whenever the image is available as animated. + * @return std::string avatar url or an empty string, if required attributes are missing or an invalid format was passed + */ + std::string get_avatar_url(uint16_t size = 0, const image_type format = i_png, bool prefer_animated = true) const; + + /** + * @brief Set the nickname + * + * @param nick Nickname to set + * + * @return guild_member& reference to self + */ + guild_member& set_nickname(const std::string& nick); + + /** + * @brief Find the dpp::user object for this member. This is an alias for dpp::find_user + * @return dpp::user* Pointer to the user object. If not in cache, it returns nullptr + */ + user* get_user() const; + + /** + * @brief Check if this member is equal to another member object. + * @param other_member other member object to compare + * @return true if their user ids are equal, false otherwise. + */ + + bool operator == (guild_member const& other_member) const; + + /** + * @brief Set whether the user is exempt from guild verification requirements + * + * @param is_bypassing_verification value to set + * + * @return guild_member& reference to self + */ + guild_member& set_bypasses_verification(const bool is_bypassing_verification); + + /** + * @brief Set whether the user is muted in voice channels + * + * @param is_muted value to set, true if mute in voice channels + * + * @return guild_member& reference to self + */ + guild_member& set_mute(const bool is_muted); + + /** + * @brief Set whether the user is deafened in voice channels + * + * @param is_deafened value to set, true if deaf in voice channels + * + * @return guild_member& reference to self + */ + guild_member& set_deaf(const bool is_deafened); + + /** + * @brief Set communication_disabled_until + * + * @param timestamp timestamp until communication is disabled + * + * @return guild_member& reference to self + */ + guild_member& set_communication_disabled_until(const time_t timestamp); + + /** + * @brief Return a ping/mention for the user by nickname + * + * @return std::string mention + */ + std::string get_mention() const; +}; + +/** + * @brief Defines a channel on a server's welcome screen + */ +struct DPP_EXPORT welcome_channel: public json_interface { + /// The description shown for the channel + std::string description; + /// The emoji name if custom, the unicode character if standard, or null if no emoji is set + std::string emoji_name; + /// The channel's id + snowflake channel_id; + /// The emoji id, if the emoji is custom + snowflake emoji_id; + + /** + * @brief Construct a new welcome channel object + */ + welcome_channel(); + + /** + * @brief Destroy the welcome channel object + */ + virtual ~welcome_channel() = default; + + /** + * @brief Read class values from json object + * + * @param j A json object to read from + * @return A reference to self + */ + welcome_channel& fill_from_json(nlohmann::json* j); + + /** + * @brief Build the json for this object + * + * @param with_id include the id in the JSON + * @return std::string json data + */ + std::string build_json(bool with_id = false) const; + + /** + * @brief Set the channel ID of this welcome channel object + * + * @param _channel_id The channel ID to set + * @return Reference to self, so these method calls may be chained + */ + welcome_channel& set_channel_id(const snowflake _channel_id); + + /** + * @brief Set the description of this welcome channel object + * + * @param _description The description to set + * @return Reference to self, so these method calls may be chained + */ + welcome_channel& set_description(const std::string& _description); +}; + + +/** + * @brief Defines a server's welcome screen + */ +struct DPP_EXPORT welcome_screen: public json_interface { + /// The server description shown in the welcome screen + std::string description; + /// The channels shown in the welcome screen (max 5) + std::vector welcome_channels; + + /** + * @brief Construct a new welcome screen object + */ + welcome_screen() = default; + + /** + * @brief Destroy the welcome screen object + */ + virtual ~welcome_screen() = default; + + /** + * @brief Read class values from json object + * + * @param j A json object to read from + * @return A reference to self + */ + welcome_screen& fill_from_json(nlohmann::json* j); + + /** + * @brief Build the json for this object + * + * @param with_id include the id in the JSON + * @return std::string json data + */ + std::string build_json(bool with_id = false) const; + + /** + * @brief Set the server description for this welcome screen object shown in the welcome screen + * + * @param s string The server description + * @return Reference to self, so these method calls may be chained + */ + welcome_screen& set_description(const std::string& s); +}; + +/** + * @brief Guild NSFW level. + * Used to represent just how naughty this guild is. Naughty guild, go sit in the corner. + * @note This is set by Discord, and cannot be set by any bot or user on the guild. + */ +enum guild_nsfw_level_t : uint8_t { + /// Default setting, not configured + nsfw_default = 0, + /// Explicit content may be in this guild + nsfw_explicit = 1, + /// Safe for work content only + nsfw_safe = 2, + /// Age restricted, 18+ + nsfw_age_restricted = 3 +}; + +/** + * @brief explicit content filter level. + * This is set by a guild admin, but can be forced to a setting if the server is verified, + * partnered, official etc. + */ +enum guild_explicit_content_t : uint8_t { + /// media content will not be scanned + expl_disabled = 0, + /// media content sent by members without roles will be scanned + expl_members_without_roles = 1, + /// media content sent by all members will be scanned + expl_all_members = 2 +}; + +/** + * @brief MFA level for server. If set to elevated all moderators need MFA to perform specific + * actions such as kick or ban. + */ +enum mfa_level_t : uint8_t { + /// MFA not elevated + mfa_none = 0, + /// MFA elevated + mfa_elevated = 1 +}; + +/** + * @brief Guild verification level + */ +enum verification_level_t : uint8_t { + /// unrestricted + ver_none = 0, + /// must have verified email on account + ver_low = 1, + /// must be registered on Discord for longer than 5 minutes + ver_medium = 2, + /// must be a member of the server for longer than 10 minutes + ver_high = 3, + /// must have a verified phone number + ver_very_high = 4, +}; + +/** + * @brief Default message notification level + */ +enum default_message_notification_t: uint8_t { + /// members will receive notifications for all messages by default + dmn_all = 0, + /// members will receive notifications only for messages that \@mention them by default + dmn_only_mentions = 1, +}; + +/** + * @brief Premium tier + */ +enum guild_premium_tier_t: uint8_t { + /// guild has not unlocked any Server Boost perks + tier_none = 0, + /// guild has unlocked Server Boost level 1 perks + tier_1 = 1, + /// guild has unlocked Server Boost level 2 perks + tier_2 = 2, + /// guild has unlocked Server Boost level 3 perks + tier_3 = 3, +}; + +/** + * @brief Voice AFK timeout values for guild::afk_timeout + */ +enum guild_afk_timeout_t: uint8_t { + /// AFK timeout disabled + afk_off, + /// AFK timeout of 1 Minute + afk_60, + /// AFK timeout of 5 Minutes + afk_300, + /// AFK timeout of 15 Minutes + afk_900, + /// AFK timeout of 30 Minutes + afk_1800, + /// AFK timeout of 1 Hour + afk_3600, +}; + +/** @brief Guild members container + */ +typedef std::unordered_map members_container; + +/** + * @brief Represents a guild on Discord (AKA a server) + */ +class DPP_EXPORT guild : public managed, public json_interface { +public: + /** Guild name */ + std::string name; + + /** Server description */ + std::string description; + + /** + * @brief Vanity url code for verified or partnered servers and boost level 3 + * @note This field cannot be set from the API. Attempts to change this value will be + * silently ignored even if the correct number of boosts or verified/partnered status exist. + * See: https://github.com/discord/discord-api-docs/issues/519 + */ + std::string vanity_url_code; + + /** Roles defined on this server */ + std::vector roles; + + /** List of channels on this server */ + std::vector channels; + + /** List of threads on this server */ + std::vector threads; + + /** List of emojis + */ + std::vector emojis; + + /** List of members in voice channels in the guild. + */ + std::map voice_members; + + /** List of guild members. Note that when you first receive the + * guild create event, this may be empty or near empty. + * This depends upon your dpp::intents and the size of your bot. + * It will be filled by guild member chunk requests. + */ + members_container members; + + /** Welcome screen + */ + dpp::welcome_screen welcome_screen; + + /** Guild icon hash */ + utility::iconhash icon; + + /** Guild splash hash */ + utility::iconhash splash; + + /** Guild discovery splash hash */ + utility::iconhash discovery_splash; + + /** Server banner hash */ + utility::iconhash banner; + + /** Snowflake id of guild owner */ + snowflake owner_id; + + /** Snowflake ID of AFK voice channel or 0 */ + snowflake afk_channel_id; + + /** ID of creating application, if any, or 0 */ + snowflake application_id; + + /** ID of system channel where discord update messages are sent */ + snowflake system_channel_id; + + /** ID of rules channel for communities */ + snowflake rules_channel_id; + + /** Public updates channel id or 0 */ + snowflake public_updates_channel_id; + + /** Snowflake ID of widget channel, or 0 */ + snowflake widget_channel_id; + + /** The id of the channel where admins and moderators of Community guilds receive safety alerts from Discord */ + snowflake safety_alerts_channel_id; + + /** Approximate member count. May be sent as zero */ + uint32_t member_count; + + /** Flags bitmask as defined by values within dpp::guild_flags */ + uint32_t flags; + + /** + * @brief the maximum number of presences for the guild. + * @note Generally Discord always fills this with 0, apart from for the largest of guilds + */ + uint32_t max_presences; + + /** + * @brief the maximum number of members for the guild + */ + uint32_t max_members; + + /** + * @brief Additional flags (values from dpp::guild_flags_extra) + */ + uint16_t flags_extra; + + /** Shard ID of the guild */ + uint16_t shard_id; + + /** Number of boosters */ + uint16_t premium_subscription_count; + + /** Voice AFK timeout before moving users to AFK channel */ + guild_afk_timeout_t afk_timeout; + + /** Maximum users in a video channel, or 0 */ + uint8_t max_video_channel_users; + + /** Setting for how notifications are to be delivered to users */ + default_message_notification_t default_message_notifications; + + /** Boost level */ + guild_premium_tier_t premium_tier; + + /** Verification level of server */ + verification_level_t verification_level; + + /** Whether or not explicit content filtering is enable and what setting it is */ + guild_explicit_content_t explicit_content_filter; + + /** If multi factor authentication is required for moderators or not */ + mfa_level_t mfa_level; + + /** + * @brief Guild NSFW level + */ + guild_nsfw_level_t nsfw_level; + + /** Default constructor, zeroes all values */ + guild(); + + /** + * @brief Destroy the guild object + */ + virtual ~guild() = default; + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + guild& fill_from_json(nlohmann::json* j); + + /** Read class values from json object + * @param shard originating shard + * @param j A json object to read from + * @return A reference to self + */ + guild& fill_from_json(class discord_client* shard, nlohmann::json* j); + + /** Build a JSON string from this object. + * @param with_id True if an ID is to be included in the JSON + * @return JSON string + */ + std::string build_json(bool with_id = false) const; + + /** + * @brief Compute the base permissions for a member on this guild, + * before channel overwrites are applied. + * This method takes into consideration the following cases: + * - Guild owner + * - Guild roles including \@everyone + * + * @param user User to get permissions for + * @return permission permissions bitmask. If the member has administrator privileges, the bitmask returns with all flags set + * @note Requires role cache to be enabled (it's enabled by default). + * + * @warning The method will search for the guild member in the cache by the users id. + * If the guild member is not in cache, the method will always return 0. + */ + permission base_permissions(const class user* user) const; + + /** + * @brief Compute the base permissions for a member on this guild, + * before channel overwrites are applied. + * This method takes into consideration the following cases: + * - Guild owner + * - Guild roles including \@everyone + * + * @param member member to get permissions for + * @return permission permissions bitmask. If the member has administrator privileges, the bitmask returns with all flags set + * @note Requires role cache to be enabled (it's enabled by default). + */ + permission base_permissions(const guild_member &member) const; + + /** + * @brief Get the overall permissions for a member in this channel, including channel overwrites, role permissions and admin privileges. + * + * @param base_permissions base permissions before overwrites, + * from guild::base_permissions + * @param user The user to resolve the permissions for + * @param channel Channel to compute permission overwrites for + * @return permission Permission overwrites for the member. Made of bits in dpp::permissions. + * @note Requires role cache to be enabled (it's enabled by default). + * + * @warning The method will search for the guild member in the cache by the users id. + * If the guild member is not in cache, the method will always return 0. + */ + permission permission_overwrites(const uint64_t base_permissions, const user* user, const channel* channel) const; + + /** + * @brief Get the overall permissions for a member in this channel, including channel overwrites, role permissions and admin privileges. + * + * @param member The member to resolve the permissions for + * @param channel Channel to compute permission overwrites for + * @return permission Permission overwrites for the member. Made of bits in dpp::permissions. + * @note Requires role cache to be enabled (it's enabled by default). + */ + permission permission_overwrites(const guild_member &member, const channel &channel) const; + + /** + * @brief Rehash members map + */ + void rehash_members(); + + /** + * @brief Connect to a voice channel another guild member is in + * + * @param user_id User id to join + * @param self_mute True if the bot should mute itself + * @param self_deaf True if the bot should deafen itself + * @return True if the user specified is in a vc, false if they aren't + * @note This is NOT a synchronous blocking call! The bot isn't instantly ready to send or listen for audio, + * as we have to wait for the connection to the voice server to be established! + * e.g. wait for dpp::cluster::on_voice_ready event, and then send the audio within that event. + */ + bool connect_member_voice(snowflake user_id, bool self_mute = false, bool self_deaf = false); + + /** + * @brief Get the banner url of the guild if it have one, otherwise returns an empty string + * + * @param size The size of the banner in pixels. It can be any power of two between 16 and 4096, + * otherwise the default sized banner is returned. + * @param format The format to use for the avatar. It can be one of `i_webp`, `i_jpg`, `i_png` or `i_gif`. + * Passing `i_gif` might result in an invalid url for non-animated images. Consider using the `prefer_animated` parameter instead. + * @param prefer_animated Whether you prefer gif format. + * If true, it'll return gif format whenever the image is available as animated. + * @return std::string banner url or an empty string, if required attributes are missing or an invalid format was passed + */ + std::string get_banner_url(uint16_t size = 0, const image_type format = i_png, bool prefer_animated = true) const; + + /** + * @brief Get the discovery splash url of the guild if it have one, otherwise returns an empty string + * + * @param size The size of the discovery splash in pixels. It can be any power of two between 16 and 4096, + * otherwise the default sized discovery splash is returned. + * @param format The format to use for the avatar. It can be one of `i_webp`, `i_jpg` or `i_png`. + * @return std::string discovery splash url or an empty string, if required attributes are missing or an invalid format was passed + */ + std::string get_discovery_splash_url(uint16_t size = 0, const image_type format = i_png) const; + + /** + * @brief Get the icon url of the guild if it have one, otherwise returns an empty string + * + * @param size The size of the icon in pixels. It can be any power of two between 16 and 4096, + * otherwise the default sized icon is returned. + * @param format The format to use for the avatar. It can be one of `i_webp`, `i_jpg`, `i_png` or `i_gif`. + * When passing `i_gif`, it returns an empty string for non-animated images. Consider using the `prefer_animated` parameter instead. + * @param prefer_animated Whether you prefer gif format. + * If true, it'll return gif format whenever the image is available as animated. + * @return std::string icon url or an empty string, if required attributes are missing or an invalid format was passed + */ + std::string get_icon_url(uint16_t size = 0, const image_type format = i_png, bool prefer_animated = true) const; + + /** + * @brief Get the splash url of the guild if it have one, otherwise returns an empty string + * + * @param size The size of the splash in pixels. It can be any power of two between 16 and 4096, + * otherwise the default sized splash is returned. + * @param format The format to use for the avatar. It can be one of `i_webp`, `i_jpg` or `i_png`. + * @return std::string splash url or an empty string, if required attributes are missing or an invalid format was passed + */ + std::string get_splash_url(uint16_t size = 0, const image_type format = i_png) const; + + /** + * @brief Set the name of the guild in the object + * Min length: 2, Max length: 100 (not including leading/trailing spaces) + * @param n Guild name + * @return guild& reference to self + * @throw dpp::length_exception if guild name is too short + */ + guild& set_name(const std::string& n); + + /** + * @brief Is a large server (>250 users) + * @return bool is a large guild + */ + bool is_large() const; + + /** + * @brief Is unavailable due to outage (most other fields will be blank or outdated + * @return bool is unavailable + */ + bool is_unavailable() const; + + /** + * @brief Widget is enabled for this server + * @return bool widget enabled + */ + bool widget_enabled() const; + + /** + * @brief Guild has access to set an invite splash background + * @return bool can have an invite splash + */ + bool has_invite_splash() const; + + /** + * @brief Guild has access to set 384kbps bitrate in voice + * @return bool can have VIP voice regions + */ + bool has_vip_regions() const; + + /** + * @brief Guild has access to set a vanity URL + * @return bool can have vanity url + */ + bool has_vanity_url() const; + + /** + * @brief Guild is a verified server + * @return bool is verified + */ + bool is_verified() const; + + /** + * @brief Guild is a discord partnered server + * @return bool is discord partnered + */ + bool is_partnered() const; + + /** + * @brief Has enabled community + * @return bool has enabled community + */ + bool is_community() const; + + /** + * @brief Has enabled role subscriptions + * @return bool has enabled role subscriptions + */ + bool has_role_subscriptions() const; + + /** + * @brief Guild has access to create announcement channels + * @return bool has announcement channels features enabled + */ + bool has_news() const; + + /** + * @brief Guild is discoverable + * @return bool is discoverable + */ + bool is_discoverable() const; + + /** + * @brief Guild is featurable + * @return bool is featurable + */ + bool is_featureable() const; + + /** + * @brief Guild has access to set an animated guild banner image + * @return bool can have animated banner image + */ + bool has_animated_banner() const; + + /** + * @brief Guild has auto moderation features + * @return bool has auto moderation features + */ + bool has_auto_moderation() const; + + /** + * @brief Guild has been set as a support server on the App Directory + * @return bool has been set as a support server of an app in the app directory + */ + bool has_support_server() const; + + /** + * @brief Guild has role subscriptions that can be purchased + * @return bool has role subscriptions that can be purchased + */ + bool has_role_subscriptions_available_for_purchase() const; + + /** + * @brief Guild has disabled alerts for join raids in the configured safety alerts channel + * @return bool dpp::g_raid_alerts_disabled flag is set + */ + bool has_raid_alerts_disabled() const; + + /** + * @brief Guild has access to set an animated guild icon + * @return bool can have animated icon + */ + bool has_animated_icon() const; + + /** + * @brief Guild has access to set a guild banner image + * @return bool can have banner image + */ + bool has_banner() const; + + /** + * @brief Guild has enabled the welcome screen + * @return bool enabled welcome screen + */ + bool is_welcome_screen_enabled() const; + + /** + * @brief Guild has enabled membership screening + * @return bool has membership screening + */ + bool has_member_verification_gate() const; + + /** + * @brief Guild has preview enabled + * @return bool has preview + */ + bool is_preview_enabled() const; + + /** + * @brief Guild icon is actually an animated gif + * @return bool is animated gif + */ + bool has_animated_icon_hash() const; + + /** + * @brief Guild banner is actually an animated gif + * @return bool is animated gif + */ + bool has_animated_banner_hash() const; + + + /** + * @brief guild has access to monetization features + * @return bool + */ + bool has_monetization_enabled() const; + + /** + * @brief guild has increased custom sticker slots + * @return bool has more stickers + */ + bool has_more_stickers() const; + + /** + * @brief guild has enabled the role subscription promo page + * @return bool has role subscription promo page enabled + */ + bool has_creator_store_page() const; + + /** + * @brief guild is able to set role icons + * @return bool has role icons + */ + bool has_role_icons() const; + + /** + * @brief guild has access to the seven day archive time for threads + * @return bool has seven day thread archive + * @deprecated Removed by Discord + */ + bool has_seven_day_thread_archive() const; + + /** + * @brief guild has access to the three day archive time for threads + * @return bool has three day thread archive + * @deprecated Removed by Discord + */ + bool has_three_day_thread_archive() const; + + /** + * @brief guild has enabled ticketed events + * @return bool has ticketed events + */ + bool has_ticketed_events() const; + + /** + * @brief guild has access to channel banners feature + * @return bool has channel banners + * @deprecated Removed by Discord + */ + bool has_channel_banners() const; + + /** + * @brief True if the premium progress bar is enabled + * @return bool has progress bar enabled + */ + bool has_premium_progress_bar_enabled() const; + + /** + * @brief True if has paused invites, preventing new users from joining + * @return bool has paused invites + */ + bool has_invites_disabled() const; +}; + +/** A container of guilds */ +typedef std::unordered_map guild_map; + +/** + * @brief Represents a guild widget, simple web widget of member list + */ +class DPP_EXPORT guild_widget { +public: + /** + * @brief Channel widget points to + */ + snowflake channel_id; + + /** + * @brief True if enabled + */ + bool enabled; + + /** + * @brief Construct a new guild widget object + */ + guild_widget(); + + /** + * @brief Build a guild widget from json + * + * @param j json to build from + * @return guild_widget& reference to self + */ + guild_widget& fill_from_json(nlohmann::json* j); + + /** + * @brief Build json for a guild widget + * + * @param with_id Add ID to output + * @return std::string guild widget stringified json + */ + std::string build_json(bool with_id = false) const; +}; + +/** + * @brief The onboarding mode for the dpp::onboarding object. Defines the criteria used to satisfy Onboarding constraints that are required for enabling. + */ +enum onboarding_mode: uint8_t { + gom_default = 0, //!< Counts only Default Channels towards constraints + gom_advanced = 1, //!< Counts Default Channels and Questions towards constraints +}; + +/** + * @brief The various types of dpp::onboarding_prompt + */ +enum onboarding_prompt_type: uint8_t { + opt_multiple_choice = 0, //!< Multiple choice + opt_dropdown = 1, //!< Dropdown +}; + +/** + * @brief Various flags for dpp::onboarding_prompt + */ +enum onboarding_prompt_flags: uint8_t { + opf_single_select = 1 << 0, //!< Indicates whether users are limited to selecting one option for the prompt + opf_required = 1 << 1, //!< Indicates whether the prompt is required before a user completes the onboarding flow + opf_in_onboarding = 1 << 2, //!< Indicates whether the prompt is present in the onboarding flow. If set, the prompt will only appear in the Channels & Roles tab +}; + +/** + * @brief Represents an onboarding prompt option + */ +struct DPP_EXPORT onboarding_prompt_option: public managed, public json_interface { + std::vector channel_ids; //!< IDs for channels a member is added to when the option is selected + std::vector role_ids; //!< IDs for roles assigned to a member when the option is selected + dpp::emoji emoji; //!< Emoji of the option + std::string title; //!< Title of the option + std::string description; //!< Description of the option + + /** + * @brief Construct a new onboarding prompt option object + */ + onboarding_prompt_option(); + + /** + * @brief Destroy the onboarding prompt option object + */ + virtual ~onboarding_prompt_option() = default; + + /** + * @brief Read class values from json object + * + * @param j A json object to read from + * @return A reference to self + */ + onboarding_prompt_option& fill_from_json(nlohmann::json* j); + + /** + * @brief Build the json for this object + * + * @param with_id include the id in the JSON + * @return std::string json data + */ + std::string build_json(bool with_id = false) const; + + /** + * @brief Set the emoji of this onboarding prompt option object + * + * @param _emoji The emoji to set + * @return Reference to self, so these method calls may be chained + */ + onboarding_prompt_option& set_emoji(const dpp::emoji& _emoji); + + /** + * @brief Set the title of this onboarding prompt option object + * + * @param _title The title to set + * @return Reference to self, so these method calls may be chained + */ + onboarding_prompt_option& set_title(const std::string& _title); + + /** + * @brief Set the description of this onboarding prompt option object + * + * @param _description The description to set + * @return Reference to self, so these method calls may be chained + */ + onboarding_prompt_option& set_description(const std::string& _description); +}; + +/** + * @brief Represents an onboarding prompt + */ +struct DPP_EXPORT onboarding_prompt: public managed, public json_interface { + onboarding_prompt_type type; //!< Type of prompt (defaults to dpp::opt_multiple_choice) + std::vector options; //!< Options available within the prompt + std::string title; //!< Title of the prompt + uint8_t flags; //!< A set of flags built from the bitmask defined by dpp::onboarding_prompt_flags + + /** + * @brief Construct a new onboarding prompt object + */ + onboarding_prompt(); + + /** + * @brief Destroy the onboarding prompt object + */ + virtual ~onboarding_prompt() = default; + + /** + * @brief Read class values from json object + * + * @param j A json object to read from + * @return A reference to self + */ + onboarding_prompt& fill_from_json(nlohmann::json* j); + + /** + * @brief Build the json for this object + * + * @param with_id include the id in the JSON + * @return std::string json data + */ + std::string build_json(bool with_id = false) const; + + /** + * @brief Set the type of this onboarding prompt object + * + * @param _type The prompt type to set + * @return Reference to self, so these method calls may be chained + */ + onboarding_prompt& set_type(const onboarding_prompt_type _type); + + /** + * @brief Set the title of this onboarding prompt object + * + * @param _title The title to set + * @return Reference to self, so these method calls may be chained + */ + onboarding_prompt& set_title(const std::string& _title); + + /** + * @brief Indicates whether users are limited to selecting one option for the prompt + * @return bool True if the users are limited to selecting one option for the prompt + */ + bool is_single_select() const; + + /** + * @brief Indicates whether the prompt is required before a user completes the onboarding flow + * @return bool True if the prompt is required before a user completes the onboarding flow + */ + bool is_required() const; + + /** + * @brief Indicates whether the prompt is present in the onboarding flow + * @return bool True if the prompt is present in the onboarding flow. False if the prompt will only appear in the Channels & Roles tab + */ + bool is_in_onboarding() const; +}; + +/** + * @brief Represents a guild's onboarding flow + */ +struct DPP_EXPORT onboarding: public json_interface { + snowflake guild_id; //!< ID of the guild this onboarding is part of + std::vector prompts; //!< Prompts shown during onboarding and in customize community + std::vector default_channel_ids; //!< Channel IDs that members get opted into automatically + onboarding_mode mode; //!< Current mode of onboarding (defaults to dpp::gom_default) + bool enabled; //!< Whether onboarding is enabled in the guild + + /** + * @brief Construct a new onboarding object + */ + onboarding(); + + /** + * @brief Destroy the onboarding object + */ + virtual ~onboarding() = default; + + /** + * @brief Read class values from json object + * + * @param j A json object to read from + * @return A reference to self + */ + onboarding& fill_from_json(nlohmann::json* j); + + /** + * @brief Build the json for this object + * + * @param with_id include the id in the JSON + * @return std::string json data + */ + std::string build_json(bool with_id = false) const; + + /** + * @brief Set guild_id of this onboarding object + * + * @param guild_id Guild ID to set + * @return Reference to self, so these method calls may be chained + */ + onboarding& set_guild_id(const snowflake id); + + /** + * @brief Set the mode of this onboarding object + * + * @param m onboarding_mode Mode to set + * @return Reference to self, so these method calls may be chained + */ + onboarding& set_mode(const onboarding_mode m); + + /** + * @brief Set the enabling of this onboarding object + * + * @param is_enabled bool Whether onboarding is enabled in the guild + * @return Reference to self, so these method calls may be chained + */ + onboarding& set_enabled(const bool is_enabled); +}; + +/** + * @brief helper function to deserialize a guild_member from json + * + * @see https://github.com/nlohmann/json#arbitrary-types-conversions + * + * @param j output json object + * @param gm guild_member to be deserialized + */ +void from_json(const nlohmann::json& j, guild_member& gm); + +/** A container of guild members */ +typedef std::unordered_map guild_member_map; + +/** + * @brief Get the guild_member from cache of given IDs + * + * @param guild_id ID of the guild to find guild_member for + * @param user_id ID of the user to find guild_member for + * + * @throw dpp::cache_exception if the guild or guild_member is not found in the cache + * @return guild_member the cached object, if found + */ +guild_member DPP_EXPORT find_guild_member(const snowflake guild_id, const snowflake user_id); + +} // namespace dpp diff --git a/include/dpp/httpsclient.h b/include/dpp/httpsclient.h new file mode 100644 index 0000000..09bbd84 --- /dev/null +++ b/include/dpp/httpsclient.h @@ -0,0 +1,339 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace dpp { + + +/** + * @brief HTTP connection status + */ +enum http_state : uint8_t { + /** + * @brief Sending/receiving HTTP headers and request body + */ + HTTPS_HEADERS, + + /** + * @brief Receiving body content. + */ + HTTPS_CONTENT, + + /** + * @brief Completed connection, as it was closed or the body is >= Content-Length + * + */ + HTTPS_DONE, + + /** + * @brief Received chunk length + * + */ + HTTPS_CHUNK_LEN, + + /** + * @brief Received chunk trailing CRLF + */ + HTTPS_CHUNK_TRAILER, + + /** + * @brief The last received chunk is the final chunk + */ + HTTPS_CHUNK_LAST, + + /** + * @brief Receiving contents of a chunk + */ + HTTPS_CHUNK_CONTENT +}; + +/** + * @brief Request headers + */ +typedef std::multimap http_headers; + +/** + * @brief Represents a multipart mime body and the correct top-level mime type + * If a non-multipart request is passed in, this is represented as a plain body + * and the application/json mime type. + */ +struct multipart_content { + /** + * @brief Multipart body + */ + std::string body; + /** + * @brief MIME type + */ + std::string mimetype; +}; + +/** + * @brief Represents a HTTP scheme, hostname and port + * split into parts for easy use in https_client. + */ +struct http_connect_info { + /** + * @brief True if the connection should be SSL + */ + bool is_ssl; + /** + * @brief The request scheme, e.g. 'https' or 'http' + */ + std::string scheme; + /** + * @brief The request hostname part, e.g. 'discord.com' + */ + std::string hostname; + /** + * @brief The port number, either determined from the scheme, + * or from the part of the hostname after a colon ":" character + */ + uint16_t port; +}; + +/** + * @brief Implements a HTTPS socket client based on the SSL client. + * @note plaintext HTTP without SSL is also supported via a "downgrade" setting + */ +class DPP_EXPORT https_client : public ssl_client +{ + /** + * @brief Current connection state + */ + http_state state; + + /** + * @brief The type of the request, e.g. GET, POST + */ + std::string request_type; + + /** + * @brief Path part of URL for HTTPS connection + */ + std::string path; + + /** + * @brief The request body, e.g. form data + */ + std::string request_body; + + /** + * @brief The response body, e.g. file content or JSON + */ + std::string body; + + /** + * @brief The reported length of the content. If this is + * UULONG_MAX, then no length was reported by the server. + */ + uint64_t content_length; + + /** + * @brief Headers for the request, e.g. Authorization, etc. + */ + http_headers request_headers; + + /** + * @brief The status of the HTTP request from the server, + * e.g. 200 for OK, 404 for not found. A value of 0 means + * no request has been completed. + */ + uint16_t status; + + /** + * @brief Time at which the request should be abandoned + */ + time_t timeout; + + /** + * @brief If true the content is chunked encoding + */ + bool chunked; + + /** + * @brief Size of current chunk + */ + size_t chunk_size; + + /** + * @brief Number of bytes received in current chunk + */ + size_t chunk_receive; + + /** + * @brief Headers from the server's response, e.g. RateLimit + * headers, cookies, etc. + */ + std::multimap response_headers; + + /** + * @brief Handle input buffer + * + * @param buffer Buffer to read + * @return returns true if the connection should remain open + */ + bool do_buffer(std::string& buffer); + +protected: + + /** + * @brief Start the connection + */ + virtual void connect(); + + /** + * @brief Get request state + * @return request state + */ + http_state get_state(); + +public: + + /** + * @brief Connect to a specific HTTP(S) server and complete a request. + * + * The constructor will attempt the connection, and return the content. + * By the time the constructor completes, the HTTP request will be stored + * in the object. + * + * @note This is a blocking call. It starts a loop which runs non-blocking + * functions within it, but does not return until the request completes. + * See queues.cpp for how to make this asynchronous. + * + * @param hostname Hostname to connect to + * @param port Port number to connect to, usually 443 for SSL and 80 for plaintext + * @param urlpath path part of URL, e.g. "/api" + * @param verb Request verb, e.g. GET or POST + * @param req_body Request body, use dpp::https_client::build_multipart() to build a multipart MIME body (e.g. for multiple file upload) + * @param extra_headers Additional request headers, e.g. user-agent, authorization, etc + * @param plaintext_connection Set to true to make the connection plaintext (turns off SSL) + * @param request_timeout How many seconds before the connection is considered failed if not finished + */ + https_client(const std::string &hostname, uint16_t port = 443, const std::string &urlpath = "/", const std::string &verb = "GET", const std::string &req_body = "", const http_headers& extra_headers = {}, bool plaintext_connection = false, uint16_t request_timeout = 5); + + /** + * @brief Destroy the https client object + */ + virtual ~https_client() = default; + + /** + * @brief Build a multipart content from a set of files and some json + * + * @param json The json content + * @param filenames File names of files to send + * @param contents Contents of each of the files to send + * @param mimetypes MIME types of each of the files to send + * @return multipart mime content and headers + */ + static multipart_content build_multipart(const std::string &json, const std::vector& filenames = {}, const std::vector& contents = {}, const std::vector& mimetypes = {}); + + /** + * @brief Processes incoming data from the SSL socket input buffer. + * + * @param buffer The buffer contents. Can modify this value removing the head elements when processed. + */ + virtual bool handle_buffer(std::string &buffer); + + /** + * @brief Close HTTPS socket + */ + virtual void close(); + + /** + * @brief Fires every second from the underlying socket I/O loop, used for timeouts + */ + virtual void one_second_timer(); + + /** + * @brief Get a HTTP response header + * + * @param header_name Header name to find, case insensitive + * @return Header content or empty string if not found. + * If multiple values have the same header_name, this will return one of them. + * @see get_header_count to determine if multiple are present + * @see get_header_list to retrieve all entries of the same header_name + */ + const std::string get_header(std::string header_name) const; + + /** + * @brief Get the number of headers with the same header name + * + * @param header_name + * @return the number of headers with this count + */ + size_t get_header_count(std::string header_name) const; + + + /** + * @brief Get a set of HTTP response headers with a common name + * + * @param header_name + * @return A list of headers with the same name, or an empty list if not found + */ + const std::list get_header_list(std::string header_name) const; + + /** + * @brief Get all HTTP response headers + * + * @return headers as a map + */ + const std::multimap get_headers() const; + + /** + * @brief Get the response content + * + * @return response content + */ + const std::string get_content() const; + + /** + * @brief Get the response HTTP status, e.g. + * 200 for OK, 404 for not found, 429 for rate limited. + * A value of 0 indicates the request was not completed. + * + * @return uint16_t HTTP status + */ + uint16_t get_status() const; + + /** + * @brief Break down a scheme, hostname and port into + * a http_connect_info. + * + * All but the hostname portion are optional. The path component + * should not be passed to this function. + * + * @param url URL to break down + * @return Split URL + */ + static http_connect_info get_host_info(std::string url); + +}; + +} // namespace dpp diff --git a/include/dpp/integration.h b/include/dpp/integration.h new file mode 100644 index 0000000..b26d40f --- /dev/null +++ b/include/dpp/integration.h @@ -0,0 +1,174 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace dpp { + +/** + * @brief Integration types + */ +enum integration_type { + /// Twitch integration + i_twitch, + /// YouTube integration + i_youtube, + /// Discord integration + i_discord, + /// Subscription + i_guild_subscription, +}; + +/** + * @brief Integration flags + */ +enum integration_flags { + /// Integration enabled + if_enabled = 0b00000001, + /// Integration syncing + if_syncing = 0b00000010, + /// Emoji integration + if_emoticons = 0b00000100, + /// Integration revoked + if_revoked = 0b00001000, + /// Kick users when their subscription expires + if_expire_kick = 0b00010000, +}; + +/** + * @brief An application that has been integrated + */ +struct DPP_EXPORT integration_app { + /// Integration id + snowflake id; + /// Name + std::string name; + /// Icon + std::string icon; + /// Description + std::string description; + /// Integration summary @deprecated Removed by Discord + std::string summary; + /// Pointer to bot user + class user* bot; +}; + +/** + * @brief Represents an integration on a guild, e.g. a connection to twitch. + */ +class DPP_EXPORT integration : public managed, public json_interface { +public: + /** Integration name */ + std::string name; + /** Integration type */ + integration_type type; + /** Integration flags from dpp::integration_flags */ + uint8_t flags; + /** Role id */ + snowflake role_id; + /** User id */ + snowflake user_id; + /** The grace period (in days) before expiring subscribers */ + uint32_t expire_grace_period; + /** Sync time */ + time_t synced_at; + /** Subscriber count */ + uint32_t subscriber_count; + /** Account id */ + std::string account_id; + /** Account name */ + std::string account_name; + /** The bot/OAuth2 application for discord integrations */ + integration_app app; + + /** Default constructor */ + integration(); + + /** Default destructor */ + ~integration() = default; + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + integration& fill_from_json(nlohmann::json* j); + + /** Build a json string from this object. + * @param with_id Add ID to output + * @return JSON string of the object + */ + virtual std::string build_json(bool with_id = false) const; + + /** True if emoticons are enabled */ + bool emoticons_enabled() const; + /** True if integration is enabled */ + bool is_enabled() const; + /** True if is syncing */ + bool is_syncing() const; + /** True if has been revoked */ + bool is_revoked() const; + /** True if expiring kicks the user */ + bool expiry_kicks_user() const; +}; + +/** + * @brief The connection object that the user has attached. + */ +class DPP_EXPORT connection { +public: + std::string id; //!< id of the connection account + std::string name; //!< the username of the connection account + std::string type; //!< the service of the connection (twitch, youtube, discord, or guild_subscription) + bool revoked; //!< Optional: whether the connection is revoked + std::vector integrations; //!< Optional: an array of partial server integrations + bool verified; //!< whether the connection is verified + bool friend_sync; //!< whether friend sync is enabled for this connection + bool show_activity; //!< whether activities related to this connection will be shown in presence updates + bool two_way_link; //!< Whether this connection has a corresponding third party OAuth2 token + bool visible; //!< visibility of this connection + + /** + * @brief Construct a new connection object + */ + connection(); + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + connection& fill_from_json(nlohmann::json* j); + +}; + +/** A group of integrations */ +typedef std::unordered_map integration_map; + +/** A group of connections */ +typedef std::unordered_map connection_map; + +} // namespace dpp + diff --git a/include/dpp/intents.h b/include/dpp/intents.h new file mode 100644 index 0000000..80bc94b --- /dev/null +++ b/include/dpp/intents.h @@ -0,0 +1,87 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once + +namespace dpp { + +/** + * @brief intents are a bitmask of allowed events on your websocket. + * + * Some of these are known as Privileged intents (GUILD_MEMBERS and GUILD_PRESENCES) + * and require verification of a bot over 100 servers by discord via submission of + * your real life ID. + */ +enum intents { + /// Intent for receipt of guild information + i_guilds = (1 << 0), + /// Intent for receipt of guild members + i_guild_members = (1 << 1), + /// Intent for receipt of guild bans + i_guild_bans = (1 << 2), + /// Intent for receipt of guild emojis + i_guild_emojis = (1 << 3), + /// Intent for receipt of guild integrations + i_guild_integrations = (1 << 4), + /// Intent for receipt of guild webhooks + i_guild_webhooks = (1 << 5), + /// Intent for receipt of guild invites + i_guild_invites = (1 << 6), + /// Intent for receipt of guild voice states + i_guild_voice_states = (1 << 7), + /// Intent for receipt of guild presences + i_guild_presences = (1 << 8), + /// Intent for receipt of guild messages + i_guild_messages = (1 << 9), + /// Intent for receipt of guild message reactions + i_guild_message_reactions = (1 << 10), + /// Intent for receipt of guild message typing notifications + i_guild_message_typing = (1 << 11), + /// Intent for receipt of direct messages (DMs) + i_direct_messages = (1 << 12), + /// Intent for receipt of direct message reactions + i_direct_message_reactions = (1 << 13), + /// Intent for receipt of direct message typing notifications + i_direct_message_typing = (1 << 14), + /// Intent for receipt of message content + i_message_content = (1 << 15), + /// Scheduled events + i_guild_scheduled_events = (1 << 16), + /// Auto moderation configuration + i_auto_moderation_configuration = (1 << 20), + /// Auto moderation configuration + i_auto_moderation_execution = (1 << 21), + /// Default D++ intents (all non-privileged intents) + i_default_intents = dpp::i_guilds | dpp::i_guild_bans | dpp::i_guild_emojis | dpp::i_guild_integrations | + dpp::i_guild_webhooks | dpp::i_guild_invites | dpp::i_guild_voice_states | + dpp::i_guild_messages | dpp::i_guild_message_reactions | dpp::i_guild_message_typing | + dpp::i_direct_messages | dpp::i_direct_message_typing | dpp::i_direct_message_reactions | + dpp::i_guild_scheduled_events | dpp::i_auto_moderation_configuration | + dpp::i_auto_moderation_execution, + /// Privileged intents requiring ID + i_privileged_intents = dpp::i_guild_members | dpp::i_guild_presences | dpp::i_message_content, + /// Every single intent + i_all_intents = dpp::i_default_intents | dpp::i_privileged_intents, + /// Unverified bots default intents + i_unverified_default_intents = dpp::i_default_intents | dpp::i_message_content +}; + +} // namespace dpp diff --git a/include/dpp/invite.h b/include/dpp/invite.h new file mode 100644 index 0000000..de780bc --- /dev/null +++ b/include/dpp/invite.h @@ -0,0 +1,185 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace dpp { + +/** + * @brief Invite target types for dpp::invite + */ +enum invite_target_t : uint8_t { + itt_none = 0, //!< Undefined invite target type + itt_stream = 1, //!< Stream target type + itt_embedded_application = 2, //!< Embedded Application target type +}; + +/** + * @brief Represents an invite to a discord guild or channel + */ +class DPP_EXPORT invite : public json_interface { +public: + /** Invite code + */ + std::string code; + /** Readonly expiration timestamp of this invite or 0 if the invite doesn't expire + * @note Only returned from cluster::invite_get + */ + time_t expires_at; + /** Guild ID this invite is for + */ + snowflake guild_id; + /** The partial guild this invite is for. Only filled in retrieved invites + */ + guild destination_guild; + /** Channel ID this invite is for + */ + snowflake channel_id; + /** The partial channel this invite is for. Only filled in retrieved invites + */ + channel destination_channel; + /** User ID who created this invite + * @deprecated Use the `inviter` field instead + */ + snowflake inviter_id; + /** User who created this invite + */ + user inviter; + /** The user ID whose stream to display for this voice channel stream invite + */ + snowflake target_user_id; + /** Target type for this voice channel invite + */ + invite_target_t target_type; + /** Approximate number of online users + * @note Only returned from cluster::invite_get + */ + uint32_t approximate_presence_count; + /** Approximate number of total users online and offline + * @note Only returned from cluster::invite_get + */ + uint32_t approximate_member_count; + /** Duration (in seconds) after which the invite expires, or 0 for no expiration. Must be between 0 and 604800 (7 days). Defaults to 86400 (1 day) + */ + uint32_t max_age; + /** Maximum number of uses, or 0 for unlimited. Must be between 0 and 100. Defaults to 0 + */ + uint8_t max_uses; + /** Whether this invite only grants temporary membership + */ + bool temporary; + /** True if this invite should not replace or "attach to" similar invites + */ + bool unique; + /** How many times this invite has been used + */ + uint32_t uses; + /** The stage instance data if there is a public stage instance in the stage channel this invite is for + * @deprecated Deprecated + */ + stage_instance stage; + /** Timestamp at which the invite was created + */ + time_t created_at; + + /** Constructor + */ + invite(); + + /** Destructor + */ + virtual ~invite() = default; + + /** + * @brief Set the max age after which the invite expires + * + * @param max_age_ The duration in seconds, or 0 for no expiration. Must be between 0 and 604800 (7 days) + * @return invite& reference to self for chaining of calls + */ + invite& set_max_age(const uint32_t max_age_); + + /** + * @brief Set the maximum number of uses for this invite + * + * @param max_uses_ Maximum number of uses, or 0 for unlimited. Must be between 0 and 100 + * @return invite& reference to self for chaining of calls + */ + invite& set_max_uses(const uint8_t max_uses_); + + /** + * @brief Set the target user id + * + * @param user_id The user ID whose stream to display for this voice channel stream invite + * @return invite& reference to self for chaining of calls + */ + invite& set_target_user_id(const snowflake user_id); + + /** + * @brief Set the target type for this voice channel invite + * + * @param type invite_target_t Target type + * @return invite& reference to self for chaining of calls + */ + invite& set_target_type(const invite_target_t type); + + /** + * @brief Set temporary property of this invite object + * + * @param is_temporary Whether this invite only grants temporary membership + * @return invite& reference to self for chaining of calls + */ + invite& set_temporary(const bool is_temporary); + + /** + * @brief Set unique property of this invite object + * + * @param is_unique True if this invite should not replace or "attach to" similar invites + * @return invite& reference to self for chaining of calls + */ + invite& set_unique(const bool is_unique); + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + invite& fill_from_json(nlohmann::json* j); + + /** Build JSON from this object. + * @param with_id Include ID in JSON + * @return The JSON text of the invite + */ + virtual std::string build_json(bool with_id = false) const; + +}; + +/** A container of invites */ +typedef std::unordered_map invite_map; + +} // namespace dpp diff --git a/include/dpp/isa_detection.h b/include/dpp/isa_detection.h new file mode 100644 index 0000000..4a0f853 --- /dev/null +++ b/include/dpp/isa_detection.h @@ -0,0 +1,403 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once + +#if defined _MSC_VER || defined __GNUC__ || defined __clang__ + + #ifndef T_fallback + #include + + using avx_512_float = __m512; + using avx_512_int = __m512i; + using avx_2_float = __m256; + using avx_2_int = __m256i; + using avx_float = __m128; + using avx_int = __m128i; + + /* + * @brief Extracts a 32-bit integer from a 128it AVX2 register. + * @param value The AVX2 register containing packed 16-bit integers. + * @param index The index of the 32-bit integer to extract (0-3). + * @return The extracted 32-bit integer. + */ + inline int32_t extract_int32_from_avx(const avx_int& value, int64_t index) { + switch (index) { + case 0: { + return _mm_extract_epi32(value, 0); + } + case 1: { + return _mm_extract_epi32(value, 1); + } + case 2: { + return _mm_extract_epi32(value, 2); + } + case 3: { + return _mm_extract_epi32(value, 3); + } + default: { + return _mm_extract_epi32(value, 0); + } + } + } + + /* + * @brief Extracts a 32-bit integer from a 256-bit AVX2 register. + * @param value The AVX2 register containing packed 32-bit integers. + * @param index The index of the 32bit integer to extract (0-7). + * @return The extracted 32-bit integer. + */ + inline int32_t extract_int32_from_avx2(const avx_2_int& value, int64_t index) { + switch (index) { + case 0: { + return _mm256_extract_epi32(value, 0); + } + case 1: { + return _mm256_extract_epi32(value, 1); + } + case 2: { + return _mm256_extract_epi32(value, 2); + } + case 3: { + return _mm256_extract_epi32(value, 3); + } + case 4: { + return _mm256_extract_epi32(value, 4); + } + case 5: { + return _mm256_extract_epi32(value, 5); + } + case 6: { + return _mm256_extract_epi32(value, 6); + } + case 7: { + return _mm256_extract_epi32(value, 7); + } + default: { + return _mm256_extract_epi32(value, 0); + } + } + } + + /* + * @brief Extracts a 32-bit integer from a 512-bit AVX-512 register. + * @param value The AVX-512 register containing packed 16-bit integers. + * @param index The index of the 32-bit integer to extract (0-15). + * @return The extracted 32-bit integer. + */ + inline int32_t extract_int32_from_avx512(const avx_512_int& value, int64_t index) { + alignas(64) int32_t result[32]; + _mm512_store_si512(result, value); + return result[index]; + } + #endif +#endif + +#ifdef max + #undef max +#endif +#ifdef min + #undef min +#endif + +namespace dpp { + +#ifdef T_AVX512 + + /** + * @brief A class for audio mixing operations using AVX2 instructions. + */ + class audio_mixer { + public: + /* + * @brief The number of 32-bit values per CPU register. + */ + inline static constexpr int32_t byte_blocks_per_register{ 16 }; + + /* + * @brief Stores values from a 512-bit AVX vector to a storage location. + * @tparam avx_type The 512-bit AVX vector type. + * @tparam value_type The target value type for storage. + * @param values_to_store The 512-bit AVX vector containing values to store. + * @param storage_location Pointer to the storage location. + */ + template inline static void store_values(const avx_512_int& values_to_store, value_type* storage_location) { + for (int64_t x = 0; x < byte_blocks_per_register; ++x) { + storage_location[x] = static_cast(extract_int32_from_avx512(values_to_store, x)); + } + } + + /** + * @brief Specialization for gathering non-float values into an AVX register. + * @tparam avx_type The AVX type to be used (AVX, AVX2, etc.). + * @tparam value_type The type of values being gathered. + * @tparam Indices Parameter pack of indices for gathering values. + * @return An AVX register containing gathered values. + */ + template inline static avx_512_float gather_values(value_type* values) { + float newArray[byte_blocks_per_register]{}; + for (size_t x = 0; x < byte_blocks_per_register; ++x) { + newArray[x] = static_cast(values[x]); + } + return _mm512_loadu_ps(newArray); + } + + /** + * @brief Collect a single register worth of data from data_in, apply gain and increment, and store the result in data_out. + * This version uses AVX2 instructions. + * + * @param data_in Pointer to the input array of int32_t values. + * @param data_out Pointer to the output array of int16_t values. + * @param current_gain The gain to be applied to the elements. + * @param increment The increment value to be added to each element. + */ + inline static void collect_single_register(int32_t* data_in, int16_t* data_out, float current_gain, float increment) { + avx_512_float current_samples_new{ _mm512_mul_ps(gather_values(data_in), + _mm512_add_ps(_mm512_set1_ps(current_gain), + _mm512_mul_ps(_mm512_set1_ps(increment), + _mm512_set_ps(0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f)))) }; + + current_samples_new = _mm512_mask_blend_ps(_mm512_cmp_ps_mask(current_samples_new, _mm512_set1_ps(0.0f), _CMP_GE_OQ), + _mm512_max_ps(current_samples_new, _mm512_set1_ps(static_cast(std::numeric_limits::min()))), + _mm512_min_ps(current_samples_new, _mm512_set1_ps(static_cast(std::numeric_limits::max())))); + + store_values(_mm512_cvtps_epi32(current_samples_new), data_out); + } + + /** + * @brief Combine a register worth of elements from decoded_data and store the result in up_sampled_vector. + * This version uses AVX instructions. + * + * @param up_sampled_vector Pointer to the array of int32_t values. + * @param decoded_data Pointer to the array of int16_t values. + * @param x Index to select a specific set of elements to combine. + */ + inline static void combine_samples(int32_t* up_sampled_vector, const int16_t* decoded_data) { + auto newValues{ _mm512_cvtps_epi32(_mm512_add_ps(gather_values(up_sampled_vector), gather_values(decoded_data))) }; + store_values(newValues, up_sampled_vector); + } + }; + +#elif T_AVX2 + + /** + * @brief A class for audio mixing operations using AVX2 instructions. + */ + class audio_mixer { + public: + /* + * @brief The number of 32-bit values per CPU register. + */ + inline static constexpr int32_t byte_blocks_per_register{ 8 }; + + /* + * @brief Stores values from a 256-bit AVX vector to a storage location. + * @tparam avx_type The 256-bit AVX vector type. + * @tparam value_type The target value type for storage. + * @param values_to_store The 256-bit AVX vector containing values to store. + * @param storage_location Pointer to the storage location. + */ + template inline static void store_values(const avx_2_int& values_to_store, value_type* storage_location) { + for (int64_t x = 0; x < byte_blocks_per_register; ++x) { + storage_location[x] = static_cast(extract_int32_from_avx2(values_to_store, x)); + } + } + + /** + * @brief Specialization for gathering non-float values into an AVX register. + * @tparam avx_type The AVX type to be used (AVX, AVX2, etc.). + * @tparam value_type The type of values being gathered. + * @tparam Indices Parameter pack of indices for gathering values. + * @return An AVX register containing gathered values. + */ + template inline static avx_2_float gather_values(value_type* values) { + float newArray[byte_blocks_per_register]{}; + for (size_t x = 0; x < byte_blocks_per_register; ++x) { + newArray[x] = static_cast(values[x]); + } + return _mm256_loadu_ps(newArray); + } + + /** + * @brief Collect a single register worth of data from data_in, apply gain and increment, and store the result in data_out. + * This version uses AVX2 instructions. + * + * @param data_in Pointer to the input array of int32_t values. + * @param data_out Pointer to the output array of int16_t values. + * @param current_gain The gain to be applied to the elements. + * @param increment The increment value to be added to each element. + */ + inline static void collect_single_register(int32_t* data_in, int16_t* data_out, float current_gain, float increment) { + avx_2_float current_samples_new{ _mm256_mul_ps(gather_values(data_in), + _mm256_add_ps(_mm256_set1_ps(current_gain), + _mm256_mul_ps(_mm256_set1_ps(increment), _mm256_set_ps(0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f)))) }; + + current_samples_new = + _mm256_blendv_ps(_mm256_max_ps(current_samples_new, _mm256_set1_ps(static_cast(std::numeric_limits::min()))), + _mm256_min_ps(current_samples_new, _mm256_set1_ps(static_cast(std::numeric_limits::max()))), + _mm256_cmp_ps(current_samples_new, _mm256_set1_ps(0.0f), _CMP_GE_OQ)); + + store_values(_mm256_cvtps_epi32(current_samples_new), data_out); + } + + /** + * @brief Combine a register worth of elements from decoded_data and store the result in up_sampled_vector. + * This version uses AVX instructions. + * + * @param up_sampled_vector Pointer to the array of int32_t values. + * @param decoded_data Pointer to the array of int16_t values. + * @param x Index to select a specific set of elements to combine. + */ + inline static void combine_samples(int32_t* up_sampled_vector, const int16_t* decoded_data) { + auto newValues{ _mm256_cvtps_epi32(_mm256_add_ps(gather_values(up_sampled_vector), gather_values(decoded_data))) }; + store_values(newValues, up_sampled_vector); + } + }; + +#elif T_AVX + + /** + * @brief A class for audio mixing operations using AVX2 instructions. + */ + class audio_mixer { + public: + /* + * @brief The number of 32-bit values per CPU register. + */ + inline static constexpr int32_t byte_blocks_per_register{ 4 }; + + /* + * @brief Stores values from a 128-bit AVX vector to a storage location. + * @tparam avx_type The 128-bit AVX vector type. + * @tparam value_type The target value type for storage. + * @param values_to_store The 128-bit AVX vector containing values to store. + * @param storage_location Pointer to the storage location. + */ + template inline static void store_values(const avx_int& values_to_store, value_type* storage_location) { + for (int64_t x = 0; x < byte_blocks_per_register; ++x) { + storage_location[x] = static_cast(extract_int32_from_avx(values_to_store, x)); + } + } + + /** + * @brief Specialization for gathering non-float values into an AVX register. + * @tparam avx_type The AVX type to be used (AVX, AVX2, etc.). + * @tparam value_type The type of values being gathered. + * @tparam Indices Parameter pack of indices for gathering values. + * @return An AVX register containing gathered values. + */ + template inline static avx_float gather_values(value_type* values) { + float newArray[byte_blocks_per_register]{}; + for (size_t x = 0; x < byte_blocks_per_register; ++x) { + newArray[x] = static_cast(values[x]); + } + return _mm_loadu_ps(newArray); + } + + /** + * @brief Collect a single register worth of data from data_in, apply gain and increment, and store the result in data_out. + * This version uses AVX2 instructions. + * + * @param data_in Pointer to the input array of int32_t values. + * @param data_out Pointer to the output array of int16_t values. + * @param current_gain The gain to be applied to the elements. + * @param increment The increment value to be added to each element. + */ + inline static void collect_single_register(int32_t* data_in, int16_t* data_out, float current_gain, float increment) { + avx_float current_samples_new{ _mm_mul_ps(gather_values(data_in), + _mm_add_ps(_mm_set1_ps(current_gain), _mm_mul_ps(_mm_set1_ps(increment), _mm_set_ps(0.0f, 1.0f, 2.0f, 3.0f)))) }; + + current_samples_new = _mm_blendv_ps(_mm_max_ps(current_samples_new, _mm_set1_ps(static_cast(std::numeric_limits::min()))), + _mm_min_ps(current_samples_new, _mm_set1_ps(static_cast(std::numeric_limits::max()))), + _mm_cmp_ps(current_samples_new, _mm_set1_ps(0.0f), _CMP_GE_OQ)); + + store_values(_mm_cvtps_epi32(current_samples_new), data_out); + } + + /** + * @brief Combine a register worth of elements from decoded_data and store the result in up_sampled_vector. + * This version uses AVX instructions. + * + * @param up_sampled_vector Pointer to the array of int32_t values. + * @param decoded_data Pointer to the array of int16_t values. + * @param x Index to select a specific set of elements to combine. + */ + inline static void combine_samples(int32_t* up_sampled_vector, const int16_t* decoded_data) { + auto newValues{ _mm_cvtps_epi32(_mm_add_ps(gather_values(up_sampled_vector), gather_values(decoded_data))) }; + store_values(newValues, up_sampled_vector); + } + }; + +#else + + /** + * @brief A class for audio mixing operations using AVX instructions. + */ + class audio_mixer { + public: + /* + * @brief The number of 32-bit values per CPU register. + */ + inline static constexpr int32_t byte_blocks_per_register{ 2 }; + + /** + * @brief Collect a single register worth of data from data_in, apply gain and increment, and store the result in data_out. + * This version uses x64 instructions. + * + * @param data_in Pointer to the input array of int32_t values. + * @param data_out Pointer to the output array of int16_t values. + * @param current_gain The gain to be applied to the elements. + * @param increment The increment value to be added to each element. + */ + inline static void collect_single_register(int32_t* data_in, int16_t* data_out, float current_gain, float increment) { + for (uint64_t x = 0; x < byte_blocks_per_register; ++x) { + auto increment_neww = increment * x; + auto current_gain_new = current_gain + increment_neww; + auto current_sample_new = data_in[x] * current_gain_new; + if (current_sample_new >= std::numeric_limits::max()) { + current_sample_new = std::numeric_limits::max(); + } + else if (current_sample_new <= std::numeric_limits::min()) { + current_sample_new = std::numeric_limits::min(); + } + data_out[x] = static_cast(current_sample_new); + } + } + + /** + * @brief Combine a register worth of elements from decoded_data and store the result in up_sampled_vector. + * This version uses instructions. + * + * @param up_sampled_vector Pointer to the array of int32_t values. + * @param decoded_data Pointer to the array of int16_t values. + */ + inline static void combine_samples(int32_t* up_sampled_vector, const int16_t* decoded_data) { + for (uint64_t x = 0; x < byte_blocks_per_register; ++x) { + up_sampled_vector[x] += static_cast(decoded_data[x]); + } + + } + }; + +/**@}*/ + +#endif + +} // namespace dpp diff --git a/include/dpp/json.h b/include/dpp/json.h new file mode 100644 index 0000000..fc47bb4 --- /dev/null +++ b/include/dpp/json.h @@ -0,0 +1,26 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#ifdef DPP_USE_EXTERNAL_JSON + #include +#else + #include +#endif \ No newline at end of file diff --git a/include/dpp/json_fwd.h b/include/dpp/json_fwd.h new file mode 100644 index 0000000..7532619 --- /dev/null +++ b/include/dpp/json_fwd.h @@ -0,0 +1,26 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#ifdef DPP_USE_EXTERNAL_JSON + #include +#else + #include +#endif \ No newline at end of file diff --git a/include/dpp/json_interface.h b/include/dpp/json_interface.h new file mode 100644 index 0000000..56d1c3e --- /dev/null +++ b/include/dpp/json_interface.h @@ -0,0 +1,61 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2022 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ + +#pragma once +#include +#include +#include + +namespace dpp { + /** + * @brief Represents an interface for an object that can optionally implement functions + * for converting to and from nlohmann::json. In the event either parse_from_json() or + * build_json() are not implemented and are called, they will throw at runtime. + * + * @tparam T Type of class that implements the interface + */ + template struct DPP_EXPORT json_interface { + protected: + /* Must not destruct through pointer to json_interface. */ + ~json_interface() = default; + + public: + /** + * @brief Convert object from nlohmann::json + * + * @param j nlohmann::json object + * @return T& Reference to self for fluent calling + */ + T& fill_from_json(nlohmann::json* j) { + throw dpp::logic_exception("JSON interface doesn't implement parse_from_json"); + } + + /** + * @brief Build JSON string from the object + * + * @param with_id Include the ID in the JSON + * @return std::string JSON string version of object + */ + virtual std::string build_json(bool with_id = false) const { + throw dpp::logic_exception("JSON interface doesn't implement build_json"); + } + }; +} // namespace dpp diff --git a/include/dpp/managed.h b/include/dpp/managed.h new file mode 100644 index 0000000..5e79953 --- /dev/null +++ b/include/dpp/managed.h @@ -0,0 +1,77 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include + +namespace dpp { + + /** @brief The managed class is the base class for various types that can + * be stored in a cache that are identified by a dpp::snowflake id. + */ + class DPP_EXPORT managed { + public: + /** + * @brief Unique ID of object set by Discord. + * This value contains a timestamp, worker ID, internal server ID, and an incrementing value. + * Only the timestamp is relevant to us as useful metadata. + */ + snowflake id; + /** + * @brief Constructor, initialises ID + * @param nid ID to set + */ + managed(const snowflake nid = 0); + /** + * @brief Destroy the managed object + */ + virtual ~managed() = default; + + /** + * @brief Get the creation time of this object according to Discord. + * + * @return double creation time inferred from the snowflake ID. + * The minimum possible value is the first second of 2015. + */ + double get_creation_time() const; + + /** + * @brief Comparison operator for comparing two managed objects by id + * + * @param other Other object to compare against + * @return true objects are the same id + * @return false objects are not the same id + */ + bool operator==(const managed& other) const noexcept; + + /** + * @brief Comparison operator for comparing two managed objects by id + * + * @param other Other object to compare against + * @return true objects are not the same id + * @return false objects are the same id + */ + bool operator!=(const managed& other) const noexcept; + }; + +} // namespace dpp diff --git a/include/dpp/message.h b/include/dpp/message.h new file mode 100644 index 0000000..ed7137e --- /dev/null +++ b/include/dpp/message.h @@ -0,0 +1,1569 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace dpp { + +/** + * @brief Represents the type of a component + */ +enum component_type : uint8_t { + /// Action row, a container for other components + cot_action_row = 1, + /// Clickable button + cot_button = 2, + /// Select menu for picking from defined text options + cot_selectmenu = 3, + /// Text input + cot_text = 4, + /// Select menu for users + cot_user_selectmenu = 5, + /// Select menu for roles + cot_role_selectmenu = 6, + /// Select menu for mentionables (users and roles) + cot_mentionable_selectmenu = 7, + /// Select menu for channels + cot_channel_selectmenu = 8, +}; + +/** + * @brief Types of text input + */ +enum text_style_type : uint8_t { + /// Intended for short single-line text. + text_short = 1, + /// Intended for much longer inputs. + text_paragraph = 2, +}; + +/** + * @brief Represents the style of a button + */ +enum component_style : uint8_t { + /// Blurple + cos_primary = 1, + /// Grey + cos_secondary, + /// Green + cos_success, + /// Red + cos_danger, + /// An external hyperlink to a website + cos_link +}; + +/** + * @brief An option for a select component + */ +struct DPP_EXPORT select_option : public json_interface { + /** + * @brief User-facing name of the option + */ + std::string label; + /** + * @brief Dev-defined value of the option + */ + std::string value; + /** + * @brief Additional description of the option + */ + std::string description; + /** + * @brief True if option is the default option + */ + bool is_default; + /** + * @brief Emoji definition. To set an emoji on your button + * you must set one of either the name or id fields. + * The easiest way is to use the component::set_emoji + * method. + */ + struct inner_select_emoji { + /** + * @brief Set the name field to the name of the emoji. + * For built in unicode emojis, set this to the + * actual unicode value of the emoji e.g. "😄" + * and not for example ":smile:" + */ + std::string name; + /** + * @brief The emoji ID value for emojis that are custom + * ones belonging to a guild. The same rules apply + * as with other emojis, that the bot must be on + * the guild where the emoji resides and it must + * be available for use (e.g. not disabled due to + * lack of boosts etc) + */ + dpp::snowflake id = 0; + /** + * @brief True if the emoji is animated. Only applies to + * custom emojis. + */ + bool animated = false; + } emoji; + + /** + * @brief Construct a new select option object + */ + select_option(); + + /** + * @brief Destructs the select option object. + */ + virtual ~select_option() = default; + + /** + * @brief Construct a new select option object + * + * @param label Label of option + * @param value Value of option + * @param description Description of option + */ + select_option(const std::string &label, const std::string &value, const std::string &description = ""); + + /** + * @brief Set the label + * + * @param l the user-facing name of the option. It will be truncated to the maximum length of 100 UTF-8 characters. + * @return select_option& reference to self for chaining + */ + select_option& set_label(const std::string &l); + + /** + * @brief Set the value + * + * @param v value to set. It will be truncated to the maximum length of 100 UTF-8 characters. + * @return select_option& reference to self for chaining + */ + select_option& set_value(const std::string &v); + + /** + * @brief Set the description + * + * @param d description to set. It will be truncated to the maximum length of 100 UTF-8 characters. + * @return select_option& reference to self for chaining + */ + select_option& set_description(const std::string &d); + + /** + * @brief Set the emoji + * + * @param n emoji name + * @param id emoji id for custom emojis + * @param animated true if animated emoji + * @return select_option& reference to self for chaining + */ + select_option& set_emoji(const std::string &n, dpp::snowflake id = 0, bool animated = false); + + /** + * @brief Set the option as default + * + * @param def true to set the option as default + * @return select_option& reference to self for chaining + */ + select_option& set_default(bool def); + + /** + * @brief Set the emoji as animated + * + * @param anim true if animated + * @return select_option& reference to self for chaining + */ + select_option& set_animated(bool anim); + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + select_option& fill_from_json(nlohmann::json* j); +}; + +/** + * @brief Represents the component object. + * A component is a clickable button or drop down list + * within a discord message, where the buttons emit + * on_button_click events when the user interacts with + * them. + * + * You should generally define one component object and + * then insert one or more additional components into it + * using component::add_component(), so that the parent + * object is an action row and the child objects are buttons. + */ +class DPP_EXPORT component : public json_interface { +public: + /** Component type, either a button or action row + */ + component_type type; + + /** Sub components, buttons on an action row + */ + std::vector components; + + /** Component label (for buttons, text inputs). + * Maximum of 80 characters. + */ + std::string label; + + /** Component style (for buttons) + */ + component_style style; + + /** + * @brief Text style (for text inputs) + */ + text_style_type text_style; + + /** Component id (for buttons, menus, text inputs). + * Maximum of 100 characters. + */ + std::string custom_id; + + /** URL for link types (dpp::cos_link). + * Maximum of 512 characters. + */ + std::string url; + + /** Placeholder text for select menus and text inputs (max 150 characters) + */ + std::string placeholder; + + /** Minimum number of items that must be chosen for a select menu (0-25). + * Default is -1 to not set this + */ + int32_t min_values; + + /** Maximum number of items that can be chosen for a select menu (0-25). + * Default is -1 to not set this + */ + int32_t max_values; + + /** Minimum length for text input (0-4000) + */ + int32_t min_length; + + /** Maximum length for text input (1-4000) + */ + int32_t max_length; + + /** Select options for select menus. Only required and available for select menus of type dpp::cot_selectmenu + */ + std::vector options; + + /** List of channel types (dpp::channel_type) to include in the channel select component (dpp::cot_channel_selectmenu) + */ + std::vector channel_types; + + /** Disabled flag (for buttons) + */ + bool disabled; + + /** Whether the text input is required to be filled + */ + bool required; + + /** Value of the modal (filled or valid when populated from an + * on_form_submit event, or from the set_value function) + */ + std::variant value; + + /** Emoji definition. To set an emoji on your button + * you must set one of either the name or id fields. + * The easiest way is to use the component::set_emoji + * method. + */ + struct inner_emoji { + /** Set the name field to the name of the emoji. + * For built in unicode emojis, set this to the + * actual unicode value of the emoji e.g. "😄" + * and not for example ":smile:" + */ + std::string name; + /** The emoji ID value for emojis that are custom + * ones belonging to a guild. The same rules apply + * as with other emojis, that the bot must be on + * the guild where the emoji resides and it must + * be available for use (e.g. not disabled due to + * lack of boosts etc) + */ + dpp::snowflake id; + /** True if the emoji is animated. Only applies to + * custom emojis. + */ + bool animated; + } emoji; + + /** Constructor + */ + component(); + + /** Destructor + */ + virtual ~component() = default; + + /** + * @brief Add a channel type to include in the channel select component (dpp::cot_channel_selectmenu) + * + * @param ct The dpp::channel_type + * @return component& reference to self + */ + component& add_channel_type(uint8_t ct); + + /** + * @brief Set the type of the component. Button components + * (type dpp::cot_button) should always be contained within + * an action row (type dpp::cot_action_row). As described + * below, many of the other methods automatically set this + * to the correct type so usually you should not need to + * manually call component::set_type(). + * + * @param ct The component type + * @return component& reference to self + */ + component& set_type(component_type ct); + + /** + * @brief Set the text style of a text component + * @note Sets type to `cot_text` + * + * @param ts Text style type to set + * @return component& reference to self + */ + component& set_text_style(text_style_type ts); + + /** + * @brief Set the label of the component, e.g. button text. + * For action rows, this field is ignored. Setting the + * label will auto-set the type to dpp::cot_button. + * + * @param label Label text to set. It will be truncated to the maximum length of 80 UTF-8 characters. + * @return component& Reference to self + */ + component& set_label(const std::string &label); + + /** + * @brief Set the default value of the text input component. + * For action rows, this field is ignored. Setting the + * value will auto-set the type to dpp::cot_text. + * + * @param val Value text to set. It will be truncated to the maximum length of 4000 UTF-8 characters. + * @return component& Reference to self + */ + component& set_default_value(const std::string &val); + + /** + * @brief Set the url for dpp::cos_link types. + * Calling this function sets the style to dpp::cos_link + * and the type to dpp::cot_button. + * + * @param url URL to set. It will be truncated to the maximum length of 512 UTF-8 characters. + * @return component& reference to self. + */ + component& set_url(const std::string &url); + + /** + * @brief Set the style of the component, e.g. button colour. + * For action rows, this field is ignored. Setting the + * style will auto-set the type to dpp::cot_button. + * + * @param cs Component style to set + * @return component& reference to self + */ + component& set_style(component_style cs); + + /** + * @brief Set the id of the component. + * For action rows, this field is ignored. Setting the + * id will auto-set the type to dpp::cot_button. + * + * @param id Custom ID string to set. This ID will be sent + * for any on_button_click events related to the button. + * @note The maximum length of the Custom ID is 100 UTF-8 codepoints. + * If your Custom ID is longer than this, it will be truncated. + * @return component& Reference to self + */ + component& set_id(const std::string &id); + + /** + * @brief Set the component to disabled. + * Defaults to false on all created components. + * + * @param disable True to disable, false to disable. + * @return component& Reference to self + */ + component& set_disabled(bool disable); + + /** + * @brief Set if this component is required. + * Defaults to false on all created components. + * + * @param require True to require this, false to make it optional. + * @return component& Reference to self + */ + component& set_required(bool require); + + /** + * @brief Set the placeholder + * + * @param placeholder placeholder string. It will be truncated to the + * maximum length of 150 UTF-8 characters for select menus, and 100 UTF-8 + * characters for modals. + * @return component& Reference to self + */ + component& set_placeholder(const std::string &placeholder); + + /** + * @brief Set the minimum number of items that must be chosen for a select menu + * + * @param min_values min value to set (0-25) + * @return component& Reference to self + */ + component& set_min_values(uint32_t min_values); + + /** + * @brief Set the maximum number of items that can be chosen for a select menu + * + * @param max_values max value to set (0-25) + * @return component& Reference to self + */ + component& set_max_values(uint32_t max_values); + + /** + * @brief Set the minimum input length for a text input + * + * @param min_l min length to set (0-4000) + * @return component& Reference to self + */ + component& set_min_length(uint32_t min_l); + + /** + * @brief Set the maximum input length for a text input + * + * @param max_l max length to set (1-4000) + * @return component& Reference to self + */ + component& set_max_length(uint32_t max_l); + + /** + * @brief Add a select option + * + * @param option option to add + * @return component& Reference to self + */ + component& add_select_option(const select_option &option); + + /** + * @brief Add a sub-component, only valid for action rows. + * Adding subcomponents to a component will automatically + * set this component's type to dpp::cot_action_row. + * + * @param c The sub-component to add + * @return component& reference to self + */ + component& add_component(const component& c); + + /** + * @brief Set the emoji of the current sub-component. + * Only valid for buttons. Adding an emoji to a component + * will automatically set this components type to + * dpp::cot_button. One or both of name and id must be set. + * For a built in unicode emoji, you only need set name, + * and should set it to a unicode character e.g. "😄". + * For custom emojis, set the name to the name of the emoji + * on the guild, and the id to the emoji's ID. + * Setting the animated boolean is only valid for custom + * emojis. + * + * @param name Emoji name, or unicode character to use + * @param id Emoji id, for custom emojis only. + * @param animated True if the custom emoji is animated. + * @return component& Reference to self + */ + component& set_emoji(const std::string& name, dpp::snowflake id = 0, bool animated = false); + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + component& fill_from_json(nlohmann::json* j); + +}; + +/** + * @brief A footer in a dpp::embed + */ +struct DPP_EXPORT embed_footer { + /** Footer text */ + std::string text; + /** Footer icon url (only supports http(s) and attachments) */ + std::string icon_url; + /** Proxied icon url */ + std::string proxy_url; + + /** Set footer's text. Returns footer itself so these methods may be "chained" + * @param t string to set as footer text. It will be truncated to the maximum length of 2048 UTF-8 characters. + * @return A reference to self + */ + embed_footer& set_text(const std::string& t); + + /** Set footer's icon url. Returns footer itself so these methods may be "chained" + * @param i url to set as footer icon url + * @return A reference to self + */ + embed_footer& set_icon(const std::string& i); + + /** Set footer's proxied icon url. Returns footer itself so these methods may be "chained" + * @param p url to set as footer proxied icon url + * @return A reference to self + */ + embed_footer& set_proxy(const std::string& p); +}; + +/** + * @brief An video, image or thumbnail in a dpp::embed + */ +struct DPP_EXPORT embed_image { + /** URL to image or video */ + std::string url; + /** Proxied image url */ + std::string proxy_url; + /** Height (calculated by discord) */ + std::string height; + /** Width (calculated by discord) */ + std::string width; +}; + +/** + * @brief Embed provider in a dpp::embed. Received from discord but cannot be sent + */ +struct DPP_EXPORT embed_provider { + /** Provider name */ + std::string name; + /** Provider URL */ + std::string url; +}; + +/** + * @brief Author within a dpp::embed object + */ +struct DPP_EXPORT embed_author { + /** Author name */ + std::string name; + /** Author url (only supports http(s)) */ + std::string url; + /** Author icon url (only supports http(s) and attachments) */ + std::string icon_url; + /** Proxied icon url */ + std::string proxy_icon_url; +}; + +/** + * @brief A dpp::embed may contain zero or more fields + */ +struct DPP_EXPORT embed_field { + /** Name of field (max length 256) */ + std::string name; + /** Value of field (max length 1024) */ + std::string value; + /** True if the field is to be displayed inline */ + bool is_inline; +}; + +/** + * @brief A rich embed for display within a dpp::message + */ +struct DPP_EXPORT embed { + /** Optional: title of embed */ + std::string title; + /** Optional: type of embed (always "rich" for webhook embeds) */ + std::string type; + /** Optional: description of embed */ + std::string description; + /** Optional: url of embed */ + std::string url; + /** Optional: timestamp of embed content */ + time_t timestamp; + /** Optional: color code of the embed */ + uint32_t color; + /** Optional: footer information */ + std::optional footer; + /** Optional: image information */ + std::optional image; + /** Optional: thumbnail information */ + std::optional thumbnail; + /** Optional: video information (can't send these) */ + std::optional video; + /** Optional: provider information (can't send these) */ + std::optional provider; + /** Optional: author information */ + std::optional author; + /** Optional: fields information */ + std::vector fields; + + /** Constructor */ + embed(); + + /** Constructor to build embed from json object + * @param j JSON to read content from + */ + embed(nlohmann::json* j); + + /** Destructor */ + ~embed(); + + /** Set embed title. Returns the embed itself so these method calls may be "chained" + * @param text The text of the title. It will be truncated to the maximum length of 256 UTF-8 characters. + * @return A reference to self + */ + embed& set_title(const std::string &text); + + /** Set embed description. Returns the embed itself so these method calls may be "chained" + * @param text The text of the title. It will be truncated to the maximum length of 4096 UTF-8 characters. + * @return A reference to self + */ + embed& set_description(const std::string &text); + + /** Set the footer of the embed. Returns the embed itself so these method calls may be "chained" + * @param f the footer to set + * @return A reference to self + */ + embed& set_footer(const embed_footer& f); + + /** Set the footer of the embed. Returns the embed itself so these method calls may be "chained" + * @param text string to set as footer text. It will be truncated to the maximum length of 2048 UTF-8 characters. + * @param icon_url an url to set as footer icon url (only supports http(s) and attachments) + * @return A reference to self + */ + embed& set_footer(const std::string& text, const std::string& icon_url); + + /** Set embed colour. Returns the embed itself so these method calls may be "chained" + * @param col The colour of the embed + * @return A reference to self + */ + embed& set_color(uint32_t col); + + /** Set embed colour. Returns the embed itself so these method calls may be "chained" + * @param col The colour of the embed + * @return A reference to self + */ + embed& set_colour(uint32_t col); + + /** Set embed timestamp. Returns the embed itself so these method calls may be "chained" + * @param tstamp The timestamp to show in the footer, should be in UTC + * @return A reference to self + */ + embed& set_timestamp(time_t tstamp); + + /** Set embed url. Returns the embed itself so these method calls may be "chained" + * @param url the url of the embed + * @return A reference to self + */ + embed& set_url(const std::string &url); + + /** Add an embed field. Returns the embed itself so these method calls may be "chained" + * @param name The name of the field. It will be truncated to the maximum length of 256 UTF-8 characters. + * @param value The value of the field. It will be truncated to the maximum length of 1024 UTF-8 characters. + * @param is_inline Whether or not to display the field 'inline' or on its own line + * @return A reference to self + */ + embed& add_field(const std::string& name, const std::string &value, bool is_inline = false); + + /** Set embed author. Returns the embed itself so these method calls may be "chained" + * @param a The author to set + * @return A reference to self + */ + embed& set_author(const dpp::embed_author& a); + + /** Set embed author. Returns the embed itself so these method calls may be "chained" + * @param name The name of the author. It will be truncated to the maximum length of 256 UTF-8 characters. + * @param url The url of the author (only supports http(s)) + * @param icon_url The icon URL of the author (only supports http(s) and attachments) + * @return A reference to self + */ + embed& set_author(const std::string& name, const std::string& url, const std::string& icon_url); + + /** Set embed provider. Returns the embed itself so these method calls may be "chained" + * @param name The provider name. It will be truncated to the maximum length of 256 UTF-8 characters. + * @param url The provider url + * @return A reference to self + */ + embed& set_provider(const std::string& name, const std::string& url); + + /** Set embed image. Returns the embed itself so these method calls may be "chained" + * @param url The embed image URL (only supports http(s) and attachments) + * @return A reference to self + */ + embed& set_image(const std::string& url); + + /** Set embed video. Returns the embed itself so these method calls may be "chained" + * @param url The embed video url + * @return A reference to self + */ + embed& set_video(const std::string& url); + + /** Set embed thumbnail. Returns the embed itself so these method calls may be "chained" + * @param url The embed thumbnail url (only supports http(s) and attachments) + * @return A reference to self + */ + embed& set_thumbnail(const std::string& url); +}; + +/** + * @brief Represents a reaction to a dpp::message + */ +struct DPP_EXPORT reaction { + /** Total number of times this emoji has been used to react (including super reacts) */ + uint32_t count; + /** Count of super reactions */ + uint32_t count_burst; + /** Count of normal reactions */ + uint32_t count_normal; + /** ID of emoji for reaction */ + snowflake emoji_id; + /** Name of emoji, if applicable */ + std::string emoji_name; + /** Whether your bot reacted using this emoji */ + bool me; + /** Whether your bot super-reacted using this emoji */ + bool me_burst; + /** HEX colors used for super reaction. Stored as integers */ + std::vector burst_colors; + + /** + * @brief Constructs a new reaction object. + */ + reaction(); + + /** + * @brief Constructs a new reaction object from a JSON object. + * @param j The JSON to read data from + */ + reaction(nlohmann::json* j); + + /** + * @brief Destructs the reaction object. + */ + ~reaction() = default; +}; + +/** + * @brief Bitmask flags for a dpp::attachment + */ +enum attachment_flags : uint8_t { + /// this attachment has been edited using the remix feature on mobile + a_is_remix = 1 << 2, +}; + +/** + * @brief Represents an attachment in a dpp::message + */ +struct DPP_EXPORT attachment { + /** ID of attachment */ + snowflake id; + /** Size of the attachment in bytes */ + uint32_t size; + /** File name of the attachment */ + std::string filename; + /** Optional: Description of the attachment (max 1024 characters) */ + std::string description; + /** URL which points to the attachment */ + std::string url; + /** Proxied URL which points to the attachment */ + std::string proxy_url; + /** Width of the attachment, if applicable */ + uint32_t width; + /** Height of the attachment, if applicable */ + uint32_t height; + /** MIME type of the attachment, if applicable */ + std::string content_type; + /** Whether this attachment is ephemeral, if applicable */ + bool ephemeral; + /** The duration of the audio file (currently for voice messages) */ + double duration_secs; + /** base64 encoded bytearray representing a sampled waveform (currently for voice messages) */ + std::string waveform; + /** Flags. Made of bits in dpp::attachment_flags */ + uint8_t flags; + /** Owning message */ + struct message* owner; + + /** + * @brief Constructs a new attachment object. + * @param o Owning dpp::message object + */ + attachment(struct message* o); + + /** + * @brief Constructs a new attachment object from a JSON object. + * @param o Owning dpp::message object + * @param j JSON to read information from + */ + attachment(struct message* o, nlohmann::json* j); + + /** + * @brief Destructs the attachment object. + */ + ~attachment() = default; + + /** + * @brief Download this attachment + * @param callback A callback which is called when the download completes. + * @note The content of the file will be in the http_info.body parameter of the + * callback parameter. + * @throw dpp::logic_exception If there is no owner associated with this attachment that + * itself has an owning cluster, this method will throw a dpp::logic_exception when called. + */ + void download(http_completion_event callback) const; + + /** + * @brief Returns true if remixed + * + * @return true if remixed + */ + bool is_remix() const; +}; + +/** + * @brief Represents the type of a sticker + */ +enum sticker_type : uint8_t { + /// An official sticker in a pack + st_standard = 1, + /// Guild sticker + st_guild = 2 +}; + +/** + * @brief The file format (png, apng, lottie) of a sticker + */ +enum sticker_format : uint8_t { + sf_png = 1, + sf_apng = 2, + sf_lottie = 3, + sf_gif = 4, +}; + +/** + * @brief Represents stickers received in messages + */ +struct DPP_EXPORT sticker : public managed, public json_interface { + /** Optional: for standard stickers, id of the pack the sticker is from + */ + snowflake pack_id; + /** The name of the sticker */ + std::string name; + /// description of the sticker (may be empty) + std::string description; + /** for guild stickers, the Discord name of a unicode emoji representing the sticker's expression. + * for standard stickers, a comma-separated list of related expressions. + */ + std::string tags; + /** + * @brief Asset ID + * @deprecated now an empty string but still sent by discord. + * While discord still send this empty string value we will still have a field + * here in the library. + */ + std::string asset; + /** The type of sticker */ + sticker_type type; + /// type of sticker format + sticker_format format_type; + /// Optional: whether this guild sticker can be used, may be false due to loss of Server Boosts + bool available; + /// Optional: id of the guild that owns this sticker + snowflake guild_id; + /// Optional: the user that uploaded the guild sticker + user sticker_user; + /// Optional: the standard sticker's sort order within its pack + uint8_t sort_value; + /** Name of file to upload (when adding or editing a sticker) */ + std::string filename; + /** File content to upload (raw binary) */ + std::string filecontent; + + /** + * @brief Construct a new sticker object + */ + sticker(); + + virtual ~sticker() = default; + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + sticker& fill_from_json(nlohmann::json* j); + + /** Build JSON from this object. + * @param with_id True if the ID is to be set in the JSON structure + * @return The JSON text of the invite + */ + virtual std::string build_json(bool with_id = true) const; + + /** + * @brief Get the sticker url. + * + * @return std::string The sticker url or an empty string, if the id is empty + */ + std::string get_url() const; + + /** + * @brief Set the filename + * + * @param fn filename + * @return message& reference to self + */ + sticker& set_filename(const std::string &fn); + + /** + * @brief Set the file content + * + * @param fc raw file content contained in std::string + * @return message& reference to self + */ + sticker& set_file_content(const std::string &fc); + +}; + +/** + * @brief Represents a sticker pack (the built in groups of stickers that all nitro users get to use) + */ +struct DPP_EXPORT sticker_pack : public managed, public json_interface { + /// the stickers in the pack + std::map stickers; + /// name of the sticker pack + std::string name; + /// id of the pack's SKU + snowflake sku_id; + /// Optional: id of a sticker in the pack which is shown as the pack's icon + snowflake cover_sticker_id; + /// description of the sticker pack + std::string description; + /// id of the sticker pack's banner image + snowflake banner_asset_id; + + /** + * @brief Construct a new sticker pack object + */ + sticker_pack(); + + virtual ~sticker_pack() = default; + + /** Read class values from json object + * @param j A json object to read from + * @return A reference to self + */ + sticker_pack& fill_from_json(nlohmann::json* j); + + /** Build JSON from this object. + * @param with_id True if the ID is to be set in the JSON structure + * @return The JSON text of the invite + */ + virtual std::string build_json(bool with_id = true) const; + +}; + +/** + * @brief Bitmask flags for a dpp::message + */ +enum message_flags : uint16_t { + /// this message has been published to subscribed channels (via Channel Following) + m_crossposted = 1 << 0, + /// this message originated from a message in another channel (via Channel Following) + m_is_crosspost = 1 << 1, + /// do not include any embeds when serializing this message + m_suppress_embeds = 1 << 2, + /// the source message for this crosspost has been deleted (via Channel Following) + m_source_message_deleted = 1 << 3, + /// this message came from the urgent message system + m_urgent = 1 << 4, + /// this message has an associated thread, with the same id as the message + m_has_thread = 1 << 5, + /// this message is only visible to the user who invoked the Interaction + m_ephemeral = 1 << 6, + /// this message is an Interaction Response and the bot is "thinking" + m_loading = 1 << 7, + /// this message failed to mention some roles and add their members to the thread + m_thread_mention_failed = 1 << 8, + /// this message will not trigger push and desktop notifications + m_suppress_notifications = 1 << 12, + /// this message is a voice message + m_is_voice_message = 1 << 13, +}; + +/** + * @brief Represents possible values for the dpp::embed type field. + * These are loosely defined by the API docs and do not influence how the client renders embeds. + * The only type a bot can send is dpp::embed_type::emt_rich. + */ +namespace embed_type { + /** + * @brief Rich text + */ + const std::string emt_rich = "rich"; + /** + * @brief Image + */ + const std::string emt_image = "image"; + /** + * @brief Video link + */ + const std::string emt_video = "video"; + /** + * @brief Animated gif + */ + const std::string emt_gifv = "gifv"; + /** + * @brief Article + */ + const std::string emt_article = "article"; + /** + * @brief Link URL + */ + const std::string emt_link = "link"; + /** + * @brief Auto moderation filter + */ + const std::string emt_automod = "auto_moderation_message"; +} // namespace embed_type + +/** + * @brief Message types for dpp::message::type + */ +enum message_type { + /// Default + mt_default = 0, + /// Add recipient + mt_recipient_add = 1, + /// Remove recipient + mt_recipient_remove = 2, + /// Call + mt_call = 3, + /// Channel name change + mt_channel_name_change = 4, + /// Channel icon change + mt_channel_icon_change = 5, + /// Message pinned + mt_channel_pinned_message = 6, + /// Member joined + mt_guild_member_join = 7, + /// Boost + mt_user_premium_guild_subscription = 8, + /// Boost level 1 + mt_user_premium_guild_subscription_tier_1 = 9, + /// Boost level 2 + mt_user_premium_guild_subscription_tier_2 = 10, + /// Boost level 3 + mt_user_premium_guild_subscription_tier_3 = 11, + /// Follow channel + mt_channel_follow_add = 12, + /// Disqualified from discovery + mt_guild_discovery_disqualified = 14, + /// Re-qualified for discovery + mt_guild_discovery_requalified = 15, + /// Discovery grace period warning 1 + mt_guild_discovery_grace_period_initial_warning = 16, + /// Discovery grace period warning 2 + mt_guild_discovery_grace_period_final_warning = 17, + /// Thread Created + mt_thread_created = 18, + /// Reply + mt_reply = 19, + /// Application command + mt_application_command = 20, + /// Thread starter message + mt_thread_starter_message = 21, + /// Invite reminder + mt_guild_invite_reminder = 22, + /// Context Menu Command + mt_context_menu_command = 23, + /// Auto moderation action + mt_auto_moderation_action = 24, + /// Role subscription purchase + mt_role_subscription_purchase = 25, + /// Interaction premium upsell + mt_interaction_premium_upsell = 26, + /// Stage start + mt_stage_start = 27, + /// Stage end + mt_stage_end = 28, + /// Stage speaker + mt_stage_speaker = 29, + /// Stage topic + mt_stage_topic = 31, + /// Guild application premium subscription + mt_application_premium_subscription = 32, +}; + +/** + * @brief Represents the caching policy of a cache in the library. + */ +enum cache_policy_setting_t { + /** + * @brief request aggressively on seeing new guilds, and also store missing data from messages. + * This is the default behaviour and the least memory-efficient option. Memory usage will increase + * over time, initially quite rapidly, and then linearly over time. It is the least cpu-intensive + * setting. + */ + cp_aggressive = 0, + /** + * @brief only cache when there is relevant activity, e.g. a message to the bot. + * This is a good middle-ground, memory usage will increase linearly over time. + */ + cp_lazy = 1, + /** + * @brief Don't cache anything. Fill details when we see them. + * This is the most memory-efficient option but consumes more CPU time + */ + cp_none = 2 +}; + +/** + * @brief Represents the caching policy of the cluster. + * + * Channels and guilds are always cached as these caches are used + * internally by the library. The memory usage of these is minimal. + * + * All default to 'aggressive' which means to actively attempt to cache, + * going out of the way to fill the caches completely. On large bots this + * can take a LOT of RAM. + */ +struct DPP_EXPORT cache_policy_t { + /** + * @brief Caching policy for users and guild members + */ + cache_policy_setting_t user_policy = cp_aggressive; + + /** + * @brief Caching policy for emojis + */ + cache_policy_setting_t emoji_policy = cp_aggressive; + + /** + * @brief Caching policy for roles + */ + cache_policy_setting_t role_policy = cp_aggressive; +}; + +/** + * @brief Represents messages sent and received on Discord + */ +struct DPP_EXPORT message : public managed { + /** id of the channel the message was sent in */ + snowflake channel_id; + /** Optional: id of the guild the message was sent in */ + snowflake guild_id; + /** the author of this message (not guaranteed to be a valid user) */ + user author; + /** Optional: member properties for this message's author */ + guild_member member; + /** contents of the message */ + std::string content; + /** message components */ + std::vector components; + /** when this message was sent */ + time_t sent; + /** when this message was edited (may be 0 if never edited) */ + time_t edited; + /** users specifically mentioned in the message */ + std::vector> mentions; + /** roles specifically mentioned in this message (only IDs currently)*/ + std::vector mention_roles; + /** Channels mentioned in the message. (Discord: not all types supported) + * Discord: Only textual channels that are visible to everyone in a lurkable guild will ever be included. Only crossposted messages (via Channel Following) currently include mention_channels at all. (includes ID, Guild ID, Type, Name)*/ + std::vector mention_channels; + /** any attached files */ + std::vector attachments; + /** Up to 10 dpp::embed objects */ + std::vector embeds; + /** Optional: reactions to the message */ + std::vector reactions; + /** Optional: used for validating a message was sent */ + std::string nonce; + /** Optional: if the message is generated by a webhook, its id will be here otherwise the field will be 0 */ + snowflake webhook_id; + /** Stickers */ + std::vector stickers; + + /** Name of file to upload (for use server-side in discord's url) */ + std::vector filename; + + /** File content to upload (raw binary) */ + std::vector filecontent; + + /** Mime type of files to upload */ + std::vector filemimetype; + + /** + * @brief Reference to another message, e.g. a reply + */ + struct message_ref { + /// id of the originating message + snowflake message_id; + /// id of the originating message's channel + snowflake channel_id; + /// id of the originating message's guild + snowflake guild_id; + /// when sending, whether to error if the referenced message doesn't exist instead of sending as a normal (non-reply) message, default true + bool fail_if_not_exists; + } message_reference; + + /** + * @brief Reference to an interaction + */ + struct message_interaction_struct{ + /// id of the interaction + snowflake id; + /// type of interaction + uint8_t type; + /// name of the application command + std::string name; + /// the user who invoked the interaction + user usr; + } interaction; + + /** + * @brief Allowed mentions details + */ + struct allowed_ref { + /** + * @brief Set to true to parse user mentions in the text. Default is false + */ + bool parse_users; + /** + * @brief Set to true to at-everyone and at-here mentions in the text. Default is false + */ + bool parse_everyone; + /** + * @brief Set to true to parse role mentions in the text. Default is false + */ + bool parse_roles; + /** + * @brief Set to true to mention the user who sent the message this one is replying to. Default is false + */ + bool replied_user; + /** + * @brief List of users to allow pings for + */ + std::vector users; + /** + * @brief List of roles to allow pings for + */ + std::vector roles; + } allowed_mentions; + + /** + * @brief The cluster which created this message object + */ + class cluster* owner; + + /** Message type */ + message_type type; + + /** Flags. Made of bits in dpp::message_flags */ + uint16_t flags; + + /** whether this message is pinned */ + bool pinned; + /** whether this was a TTS message */ + bool tts; + /** whether this message mentions everyone */ + bool mention_everyone; + + /** + * @brief Construct a new message object + */ + message(); + + /** + * @brief Construct a new message object + * @param o Owning cluster, passed down to various things such as dpp::attachment. + * Owning cluster is optional (can be nullptr) and if nulled, will prevent some functions + * such as attachment::download from functioning (they will throw, if used) + */ + message(class cluster* o); + + /** + * @brief Destroy the message object + */ + virtual ~message(); + + /** + * @brief Construct a new message object with a channel and content + * + * @param channel_id The channel to send the message to + * @param content The content of the message. It will be truncated to the maximum length of 4000 UTF-8 characters. + * @param type The message type to create + */ + message(snowflake channel_id, const std::string &content, message_type type = mt_default); + + /** + * @brief Construct a new message object with a channel and content + * + * @param channel_id The channel to send the message to + * @param _embed An embed to send + */ + message(snowflake channel_id, const embed & _embed); + + /** + * @brief Construct a new message object with content + * + * @param content The content of the message. It will be truncated to the maximum length of 4000 UTF-8 characters. + * @param type The message type to create + */ + message(const std::string &content, message_type type = mt_default); + + /** + * @brief Set the original message reference for replies/crossposts + * + * @param _message_id message id to reply to + * @param _guild_id guild id to reply to (optional) + * @param _channel_id channel id to reply to (optional) + * @param fail_if_not_exists true if the message send should fail if these values are invalid (optional) + * @return message& reference to self + */ + message& set_reference(snowflake _message_id, snowflake _guild_id = 0, snowflake _channel_id = 0, bool fail_if_not_exists = false); + + /** + * @brief Set the allowed mentions object for pings on the message + * + * @param _parse_users whether or not to parse users in the message content or embeds + * @param _parse_roles whether or not to parse roles in the message content or embeds + * @param _parse_everyone whether or not to parse everyone/here in the message content or embeds + * @param _replied_user if set to true and this is a reply, then ping the user we reply to + * @param users list of user ids to allow pings for + * @param roles list of role ids to allow pings for + * @return message& reference to self + */ + message& set_allowed_mentions(bool _parse_users, bool _parse_roles, bool _parse_everyone, bool _replied_user, const std::vector &users, const std::vector &roles); + + /** Fill this object from json. + * @param j JSON object to fill from + * @param cp Cache policy for user records, whether or not we cache users when a message is received + * @return A reference to self + */ + message& fill_from_json(nlohmann::json* j, cache_policy_t cp = {cp_aggressive, cp_aggressive, cp_aggressive}); + + /** Build JSON from this object. + * @param with_id True if the ID is to be included in the built JSON + * @param is_interaction_response Set to true if this message is intended to be included in an interaction response. + * This will exclude some fields that are not valid in interactions at this time. + * @return The JSON text of the message + */ + virtual std::string build_json(bool with_id = false, bool is_interaction_response = false) const; + + /** + * @brief Returns true if the message was crossposted to other servers + * + * @return true if crossposted + */ + bool is_crossposted() const; + + /** + * @brief Returns true if posted from other servers announcement channel via webhook + * + * @return true if posted from other server + */ + bool is_crosspost() const; + + /** + * @brief True if embeds have been removed + * + * @return true if embeds removed + */ + bool suppress_embeds() const; + + /** + * @brief True if source message was deleted + * + * @return true if source message deleted + */ + bool is_source_message_deleted() const; + + /** + * @brief True if urgent + * + * @return true if urgent + */ + bool is_urgent() const; + + /** + * @brief True if has thread attached + * + * @return true if has thread attached + */ + bool has_thread() const; + + /** + * @brief True if ephemeral (visible only to issuer of a slash command) + * + * @return true if ephemeral + */ + bool is_ephemeral() const; + + /** + * @brief True if loading + * + * @return true if loading + */ + bool is_loading() const; + + /** + * @brief Returns true if this message failed to mention some roles and add their members to the thread + * + * @return true if this message failed to mention some roles and add their members to the thread + */ + bool is_thread_mention_failed() const; + + /** + * @brief True if the message will not trigger push and desktop notifications + * + * @return True if notifications suppressed + */ + bool suppress_notifications() const; + + /** + * @brief True if the message is a voice message + * + * @return True if voice message + */ + bool is_voice_message() const; + + /** + * @brief Add a component (button) to message + * + * @param c component to add + * @return message& reference to self + */ + message& add_component(const component& c); + + /** + * @brief Add an embed to message + * + * @param e embed to add + * @return message& reference to self + */ + message& add_embed(const embed& e); + + /** + * @brief Set the flags + * + * @param f flags to set from dpp::message_flags + * @return message& reference to self + */ + message& set_flags(uint16_t f); + + /** + * @brief Set the message type + * + * @param t type to set + * @return message& reference to self + */ + message& set_type(message_type t); + + /** + * @brief Set the filename of the last file in list + * + * @param fn filename + * @return message& reference to self + * @deprecated Use message::add_file instead + */ + message& set_filename(const std::string &fn); + + /** + * @brief Set the file content of the last file in list + * + * @param fc raw file content contained in std::string + * @return message& reference to self + * @deprecated Use message::add_file instead + */ + message& set_file_content(const std::string &fc); + + /** + * @brief Add a file to the message + * + * @param filename filename + * @param filecontent raw file content contained in std::string + * @param filemimetype optional mime type of the file + * @return message& reference to self + */ + message& add_file(const std::string &filename, const std::string &filecontent, const std::string &filemimetype = ""); + + /** + * @brief Set the message content + * + * @param c message content. It will be truncated to the maximum length of 4000 UTF-8 characters. + * @return message& reference to self + */ + message& set_content(const std::string &c); + + /** + * @brief Set the channel id + * + * @param _channel_id channel id + * @return message& reference to self + */ + message& set_channel_id(snowflake _channel_id); + + /** + * @brief Set the channel id + * + * @param _guild_id channel id + * @return message& reference to self + */ + message& set_guild_id(snowflake _guild_id); + + /** + * @brief Returns true if the message is from a DM + * + * @return true if message is a DM + */ + bool is_dm() const; + + /** + * @brief Returns true if message has remixed attachment + * + * @return true if message has remixed attachment + */ + bool has_remix_attachment() const; +}; + +/** A group of messages */ +typedef std::unordered_map message_map; + +/** A group of stickers */ +typedef std::unordered_map sticker_map; + +/** A group of sticker packs */ +typedef std::unordered_map sticker_pack_map; + +} // namespace dpp diff --git a/include/dpp/misc-enum.h b/include/dpp/misc-enum.h new file mode 100644 index 0000000..fd60895 --- /dev/null +++ b/include/dpp/misc-enum.h @@ -0,0 +1,56 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include + +namespace dpp { + + /** @brief Supported image types for profile pictures and CDN endpoints */ + enum image_type { + /// image/png + i_png, + /// image/jpeg + i_jpg, + /// image/gif + i_gif, + /// WebP + i_webp, + }; + + /** @brief Log levels */ + enum loglevel { + /// Trace + ll_trace = 0, + /// Debug + ll_debug, + /// Information + ll_info, + /// Warning + ll_warning, + /// Error + ll_error, + /// Critical + ll_critical + }; + +} // namespace dpp diff --git a/include/dpp/nlohmann/json.hpp b/include/dpp/nlohmann/json.hpp new file mode 100644 index 0000000..cb27e05 --- /dev/null +++ b/include/dpp/nlohmann/json.hpp @@ -0,0 +1,22091 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ +| | |__ | | | | | | version 3.10.5 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +SPDX-License-Identifier: MIT +Copyright (c) 2013-2022 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/****************************************************************************\ + * Note on documentation: The source files contain links to the online * + * documentation of the public API at https://json.nlohmann.me. This URL * + * contains the most recent documentation and should also be applicable to * + * previous versions; documentation for deprecated functions is not * + * removed, but marked deprecated. See "Generate documentation" section in * + * file doc/README.md. * +\****************************************************************************/ + +#ifndef INCLUDE_NLOHMANN_JSON_HPP_ +#define INCLUDE_NLOHMANN_JSON_HPP_ + +#define NLOHMANN_JSON_VERSION_MAJOR 3 +#define NLOHMANN_JSON_VERSION_MINOR 10 +#define NLOHMANN_JSON_VERSION_PATCH 5 + +#include // all_of, find, for_each +#include // nullptr_t, ptrdiff_t, size_t +#include // hash, less +#include // initializer_list +#ifndef JSON_NO_IO + #include // istream, ostream +#endif // JSON_NO_IO +#include // random_access_iterator_tag +#include // unique_ptr +#include // accumulate +#include // string, stoi, to_string +#include // declval, forward, move, pair, swap +#include // vector + +// #include + + +#include +#include + +// #include + + +#include // transform +#include // array +#include // forward_list +#include // inserter, front_inserter, end +#include // map +#include // string +#include // tuple, make_tuple +#include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible +#include // unordered_map +#include // pair, declval +#include // valarray + +// #include + + +#include // exception +#include // runtime_error +#include // to_string +#include // vector + +// #include + + +#include // array +#include // size_t +#include // uint8_t +#include // string + +namespace nlohmann +{ +namespace detail +{ +/////////////////////////// +// JSON type enumeration // +/////////////////////////// + +/*! +@brief the JSON type enumeration + +This enumeration collects the different JSON types. It is internally used to +distinguish the stored values, and the functions @ref basic_json::is_null(), +@ref basic_json::is_object(), @ref basic_json::is_array(), +@ref basic_json::is_string(), @ref basic_json::is_boolean(), +@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), +@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), +@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and +@ref basic_json::is_structured() rely on it. + +@note There are three enumeration entries (number_integer, number_unsigned, and +number_float), because the library distinguishes these three types for numbers: +@ref basic_json::number_unsigned_t is used for unsigned integers, +@ref basic_json::number_integer_t is used for signed integers, and +@ref basic_json::number_float_t is used for floating-point numbers or to +approximate integers which do not fit in the limits of their respective type. + +@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON +value with the default value for a given type + +@since version 1.0.0 +*/ +enum class value_t : std::uint8_t +{ + null, ///< null value + object, ///< object (unordered set of name/value pairs) + array, ///< array (ordered collection of values) + string, ///< string value + boolean, ///< boolean value + number_integer, ///< number value (signed integer) + number_unsigned, ///< number value (unsigned integer) + number_float, ///< number value (floating-point) + binary, ///< binary array (ordered collection of bytes) + discarded ///< discarded by the parser callback function +}; + +/*! +@brief comparison operator for JSON types + +Returns an ordering that is similar to Python: +- order: null < boolean < number < object < array < string < binary +- furthermore, each type is not smaller than itself +- discarded values are not comparable +- binary is represented as a b"" string in python and directly comparable to a + string; however, making a binary array directly comparable with a string would + be surprising behavior in a JSON file. + +@since version 1.0.0 +*/ +inline bool operator<(const value_t lhs, const value_t rhs) noexcept +{ + static constexpr std::array order = {{ + 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, + 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, + 6 /* binary */ + } + }; + + const auto l_index = static_cast(lhs); + const auto r_index = static_cast(rhs); + return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; +} +} // namespace detail +} // namespace nlohmann + +// #include + + +#include +// #include + + +#include // declval, pair +// #include + + +/* Hedley - https://nemequ.github.io/hedley + * Created by Evan Nemerson + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to + * the public domain worldwide. This software is distributed without + * any warranty. + * + * For details, see . + * SPDX-License-Identifier: CC0-1.0 + */ + +#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15) +#if defined(JSON_HEDLEY_VERSION) + #undef JSON_HEDLEY_VERSION +#endif +#define JSON_HEDLEY_VERSION 15 + +#if defined(JSON_HEDLEY_STRINGIFY_EX) + #undef JSON_HEDLEY_STRINGIFY_EX +#endif +#define JSON_HEDLEY_STRINGIFY_EX(x) #x + +#if defined(JSON_HEDLEY_STRINGIFY) + #undef JSON_HEDLEY_STRINGIFY +#endif +#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x) + +#if defined(JSON_HEDLEY_CONCAT_EX) + #undef JSON_HEDLEY_CONCAT_EX +#endif +#define JSON_HEDLEY_CONCAT_EX(a,b) a##b + +#if defined(JSON_HEDLEY_CONCAT) + #undef JSON_HEDLEY_CONCAT +#endif +#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b) + +#if defined(JSON_HEDLEY_CONCAT3_EX) + #undef JSON_HEDLEY_CONCAT3_EX +#endif +#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c + +#if defined(JSON_HEDLEY_CONCAT3) + #undef JSON_HEDLEY_CONCAT3 +#endif +#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c) + +#if defined(JSON_HEDLEY_VERSION_ENCODE) + #undef JSON_HEDLEY_VERSION_ENCODE +#endif +#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision)) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR) + #undef JSON_HEDLEY_VERSION_DECODE_MAJOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR) + #undef JSON_HEDLEY_VERSION_DECODE_MINOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION) + #undef JSON_HEDLEY_VERSION_DECODE_REVISION +#endif +#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000) + +#if defined(JSON_HEDLEY_GNUC_VERSION) + #undef JSON_HEDLEY_GNUC_VERSION +#endif +#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#elif defined(__GNUC__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) +#endif + +#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK) + #undef JSON_HEDLEY_GNUC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GNUC_VERSION) + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION) + #undef JSON_HEDLEY_MSVC_VERSION +#endif +#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) +#elif defined(_MSC_FULL_VER) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) +#elif defined(_MSC_VER) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK) + #undef JSON_HEDLEY_MSVC_VERSION_CHECK +#endif +#if !defined(JSON_HEDLEY_MSVC_VERSION) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0) +#elif defined(_MSC_VER) && (_MSC_VER >= 1400) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) +#elif defined(_MSC_VER) && (_MSC_VER >= 1200) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) +#else + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor))) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION) + #undef JSON_HEDLEY_INTEL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) +#elif defined(__INTEL_COMPILER) && !defined(__ICL) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_VERSION) + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_INTEL_CL_VERSION) + #undef JSON_HEDLEY_INTEL_CL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL) + #define JSON_HEDLEY_INTEL_CL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_CL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_CL_VERSION) + #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_CL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION) + #undef JSON_HEDLEY_PGI_VERSION +#endif +#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) + #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION_CHECK) + #undef JSON_HEDLEY_PGI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PGI_VERSION) + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #undef JSON_HEDLEY_SUNPRO_VERSION +#endif +#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10) +#elif defined(__SUNPRO_C) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf) +#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10) +#elif defined(__SUNPRO_CC) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK) + #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION +#endif +#if defined(__EMSCRIPTEN__) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION) + #undef JSON_HEDLEY_ARM_VERSION +#endif +#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100) +#elif defined(__CC_ARM) && defined(__ARMCC_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION_CHECK) + #undef JSON_HEDLEY_ARM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_ARM_VERSION) + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION) + #undef JSON_HEDLEY_IBM_VERSION +#endif +#if defined(__ibmxl__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) +#elif defined(__xlC__) && defined(__xlC_ver__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) +#elif defined(__xlC__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION_CHECK) + #undef JSON_HEDLEY_IBM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IBM_VERSION) + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_VERSION) + #undef JSON_HEDLEY_TI_VERSION +#endif +#if \ + defined(__TI_COMPILER_VERSION__) && \ + ( \ + defined(__TMS470__) || defined(__TI_ARM__) || \ + defined(__MSP430__) || \ + defined(__TMS320C2000__) \ + ) +#if (__TI_COMPILER_VERSION__ >= 16000000) + #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif +#endif + +#if defined(JSON_HEDLEY_TI_VERSION_CHECK) + #undef JSON_HEDLEY_TI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_VERSION) + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #undef JSON_HEDLEY_TI_CL2000_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__) + #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #undef JSON_HEDLEY_TI_CL430_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__) + #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #undef JSON_HEDLEY_TI_ARMCL_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__)) + #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK) + #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #undef JSON_HEDLEY_TI_CL6X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__) + #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #undef JSON_HEDLEY_TI_CL7X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__) + #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #undef JSON_HEDLEY_TI_CLPRU_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__) + #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION) + #undef JSON_HEDLEY_CRAY_VERSION +#endif +#if defined(_CRAYC) + #if defined(_RELEASE_PATCHLEVEL) + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) + #else + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK) + #undef JSON_HEDLEY_CRAY_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_CRAY_VERSION) + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION) + #undef JSON_HEDLEY_IAR_VERSION +#endif +#if defined(__IAR_SYSTEMS_ICC__) + #if __VER__ > 1000 + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) + #else + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION_CHECK) + #undef JSON_HEDLEY_IAR_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IAR_VERSION) + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION) + #undef JSON_HEDLEY_TINYC_VERSION +#endif +#if defined(__TINYC__) + #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK) + #undef JSON_HEDLEY_TINYC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION) + #undef JSON_HEDLEY_DMC_VERSION +#endif +#if defined(__DMC__) + #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION_CHECK) + #undef JSON_HEDLEY_DMC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_DMC_VERSION) + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #undef JSON_HEDLEY_COMPCERT_VERSION +#endif +#if defined(__COMPCERT_VERSION__) + #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK) + #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION) + #undef JSON_HEDLEY_PELLES_VERSION +#endif +#if defined(__POCC__) + #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK) + #undef JSON_HEDLEY_PELLES_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PELLES_VERSION) + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_MCST_LCC_VERSION) + #undef JSON_HEDLEY_MCST_LCC_VERSION +#endif +#if defined(__LCC__) && defined(__LCC_MINOR__) + #define JSON_HEDLEY_MCST_LCC_VERSION JSON_HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__) +#endif + +#if defined(JSON_HEDLEY_MCST_LCC_VERSION_CHECK) + #undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_MCST_LCC_VERSION) + #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_MCST_LCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION) + #undef JSON_HEDLEY_GCC_VERSION +#endif +#if \ + defined(JSON_HEDLEY_GNUC_VERSION) && \ + !defined(__clang__) && \ + !defined(JSON_HEDLEY_INTEL_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_ARM_VERSION) && \ + !defined(JSON_HEDLEY_CRAY_VERSION) && \ + !defined(JSON_HEDLEY_TI_VERSION) && \ + !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL430_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \ + !defined(__COMPCERT__) && \ + !defined(JSON_HEDLEY_MCST_LCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_ATTRIBUTE +#endif +#if \ + defined(__has_attribute) && \ + ( \ + (!defined(JSON_HEDLEY_IAR_VERSION) || JSON_HEDLEY_IAR_VERSION_CHECK(8,5,9)) \ + ) +# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) +#else +# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE +#endif +#if \ + defined(__has_cpp_attribute) && \ + defined(__cplusplus) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#endif +#if !defined(__cplusplus) || !defined(__has_cpp_attribute) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#elif \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \ + (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_BUILTIN) + #undef JSON_HEDLEY_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin) +#else + #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN) + #undef JSON_HEDLEY_GNUC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN) + #undef JSON_HEDLEY_GCC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_FEATURE) + #undef JSON_HEDLEY_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature) +#else + #define JSON_HEDLEY_HAS_FEATURE(feature) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE) + #undef JSON_HEDLEY_GNUC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_FEATURE) + #undef JSON_HEDLEY_GCC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_EXTENSION) + #undef JSON_HEDLEY_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension) +#else + #define JSON_HEDLEY_HAS_EXTENSION(extension) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION) + #undef JSON_HEDLEY_GNUC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION) + #undef JSON_HEDLEY_GCC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_WARNING) + #undef JSON_HEDLEY_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning) +#else + #define JSON_HEDLEY_HAS_WARNING(warning) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_WARNING) + #undef JSON_HEDLEY_GNUC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_WARNING) + #undef JSON_HEDLEY_GCC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + defined(__clang__) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \ + (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR)) + #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_PRAGMA(value) __pragma(value) +#else + #define JSON_HEDLEY_PRAGMA(value) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH) + #undef JSON_HEDLEY_DIAGNOSTIC_PUSH +#endif +#if defined(JSON_HEDLEY_DIAGNOSTIC_POP) + #undef JSON_HEDLEY_DIAGNOSTIC_POP +#endif +#if defined(__clang__) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) + #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) +#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_PUSH + #define JSON_HEDLEY_DIAGNOSTIC_POP +#endif + +/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat") +# if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions") +# if JSON_HEDLEY_HAS_WARNING("-Wc++1z-extensions") +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + _Pragma("clang diagnostic ignored \"-Wc++1z-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# endif +#endif +#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x +#endif + +#if defined(JSON_HEDLEY_CONST_CAST) + #undef JSON_HEDLEY_CONST_CAST +#endif +#if defined(__cplusplus) +# define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) +#elif \ + JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_REINTERPRET_CAST) + #undef JSON_HEDLEY_REINTERPRET_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) +#else + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_STATIC_CAST) + #undef JSON_HEDLEY_STATIC_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) +#else + #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_CPP_CAST) + #undef JSON_HEDLEY_CPP_CAST +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wold-style-cast") +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \ + ((T) (expr)) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0) +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("diag_suppress=Pe137") \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr)) +# endif +#else +# define JSON_HEDLEY_CPP_CAST(T, expr) (expr) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1216,1444,1445") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 161") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292)) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097,1098") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097") +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wcast-qual") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunused-function") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("clang diagnostic ignored \"-Wunused-function\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("GCC diagnostic ignored \"-Wunused-function\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(1,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505)) +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("diag_suppress 3142") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#endif + +#if defined(JSON_HEDLEY_DEPRECATED) + #undef JSON_HEDLEY_DEPRECATED +#endif +#if defined(JSON_HEDLEY_DEPRECATED_FOR) + #undef JSON_HEDLEY_DEPRECATED_FOR +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) +#elif \ + (JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) +#elif defined(__cplusplus) && (__cplusplus >= 201402L) + #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]]) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated") + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") +#else + #define JSON_HEDLEY_DEPRECATED(since) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) +#endif + +#if defined(JSON_HEDLEY_UNAVAILABLE) + #undef JSON_HEDLEY_UNAVAILABLE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) +#else + #define JSON_HEDLEY_UNAVAILABLE(available_since) +#endif + +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT +#endif +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__)) +#elif (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) +#elif defined(_Check_return_) /* SAL */ + #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_ + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_ +#else + #define JSON_HEDLEY_WARN_UNUSED_RESULT + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) +#endif + +#if defined(JSON_HEDLEY_SENTINEL) + #undef JSON_HEDLEY_SENTINEL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) +#else + #define JSON_HEDLEY_SENTINEL(position) +#endif + +#if defined(JSON_HEDLEY_NO_RETURN) + #undef JSON_HEDLEY_NO_RETURN +#endif +#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NO_RETURN __noreturn +#elif \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #define JSON_HEDLEY_NO_RETURN _Noreturn +#elif defined(__cplusplus) && (__cplusplus >= 201103L) + #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NO_RETURN __attribute((noreturn)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#else + #define JSON_HEDLEY_NO_RETURN +#endif + +#if defined(JSON_HEDLEY_NO_ESCAPE) + #undef JSON_HEDLEY_NO_ESCAPE +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape) + #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__)) +#else + #define JSON_HEDLEY_NO_ESCAPE +#endif + +#if defined(JSON_HEDLEY_UNREACHABLE) + #undef JSON_HEDLEY_UNREACHABLE +#endif +#if defined(JSON_HEDLEY_UNREACHABLE_RETURN) + #undef JSON_HEDLEY_UNREACHABLE_RETURN +#endif +#if defined(JSON_HEDLEY_ASSUME) + #undef JSON_HEDLEY_ASSUME +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_ASSUME(expr) __assume(expr) +#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume) + #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr) +#elif \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #if defined(__cplusplus) + #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr) + #else + #define JSON_HEDLEY_ASSUME(expr) _nassert(expr) + #endif +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable() +#elif defined(JSON_HEDLEY_ASSUME) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif +#if !defined(JSON_HEDLEY_ASSUME) + #if defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1))) + #else + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr) + #endif +#endif +#if defined(JSON_HEDLEY_UNREACHABLE) + #if \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value)) + #else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE() + #endif +#else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value) +#endif +#if !defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif + +JSON_HEDLEY_DIAGNOSTIC_PUSH +#if JSON_HEDLEY_HAS_WARNING("-Wpedantic") + #pragma clang diagnostic ignored "-Wpedantic" +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus) + #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif +#if JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0) + #if defined(__clang__) + #pragma clang diagnostic ignored "-Wvariadic-macros" + #elif defined(JSON_HEDLEY_GCC_VERSION) + #pragma GCC diagnostic ignored "-Wvariadic-macros" + #endif +#endif +#if defined(JSON_HEDLEY_NON_NULL) + #undef JSON_HEDLEY_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) +#else + #define JSON_HEDLEY_NON_NULL(...) +#endif +JSON_HEDLEY_DIAGNOSTIC_POP + +#if defined(JSON_HEDLEY_PRINTF_FORMAT) + #undef JSON_HEDLEY_PRINTF_FORMAT +#endif +#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check))) +#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check))) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(format) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) +#else + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) +#endif + +#if defined(JSON_HEDLEY_CONSTEXPR) + #undef JSON_HEDLEY_CONSTEXPR +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr) + #endif +#endif +#if !defined(JSON_HEDLEY_CONSTEXPR) + #define JSON_HEDLEY_CONSTEXPR +#endif + +#if defined(JSON_HEDLEY_PREDICT) + #undef JSON_HEDLEY_PREDICT +#endif +#if defined(JSON_HEDLEY_LIKELY) + #undef JSON_HEDLEY_LIKELY +#endif +#if defined(JSON_HEDLEY_UNLIKELY) + #undef JSON_HEDLEY_UNLIKELY +#endif +#if defined(JSON_HEDLEY_UNPREDICTABLE) + #undef JSON_HEDLEY_UNPREDICTABLE +#endif +#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable) + #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr)) +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(JSON_HEDLEY_PGI_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability( (expr), (value), (probability)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1 , (probability)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0 , (probability)) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect (!!(expr), 1 ) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect (!!(expr), 0 ) +#elif \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PREDICT(expr, expected, probability) \ + (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ + })) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ + })) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) +#else +# define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_LIKELY(expr) (!!(expr)) +# define JSON_HEDLEY_UNLIKELY(expr) (!!(expr)) +#endif +#if !defined(JSON_HEDLEY_UNPREDICTABLE) + #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5) +#endif + +#if defined(JSON_HEDLEY_MALLOC) + #undef JSON_HEDLEY_MALLOC +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_MALLOC __attribute__((__malloc__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_MALLOC __declspec(restrict) +#else + #define JSON_HEDLEY_MALLOC +#endif + +#if defined(JSON_HEDLEY_PURE) + #undef JSON_HEDLEY_PURE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PURE __attribute__((__pure__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) +# define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data") +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \ + ) +# define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;") +#else +# define JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_CONST) + #undef JSON_HEDLEY_CONST +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(const) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_CONST __attribute__((__const__)) +#elif \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_CONST _Pragma("no_side_effect") +#else + #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_RESTRICT) + #undef JSON_HEDLEY_RESTRICT +#endif +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT restrict +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + defined(__clang__) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_RESTRICT __restrict +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT _Restrict +#else + #define JSON_HEDLEY_RESTRICT +#endif + +#if defined(JSON_HEDLEY_INLINE) + #undef JSON_HEDLEY_INLINE +#endif +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + (defined(__cplusplus) && (__cplusplus >= 199711L)) + #define JSON_HEDLEY_INLINE inline +#elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0) + #define JSON_HEDLEY_INLINE __inline__ +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_INLINE __inline +#else + #define JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_ALWAYS_INLINE) + #undef JSON_HEDLEY_ALWAYS_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) +# define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_ALWAYS_INLINE __forceinline +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \ + ) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") +#else +# define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_NEVER_INLINE) + #undef JSON_HEDLEY_NEVER_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#else + #define JSON_HEDLEY_NEVER_INLINE +#endif + +#if defined(JSON_HEDLEY_PRIVATE) + #undef JSON_HEDLEY_PRIVATE +#endif +#if defined(JSON_HEDLEY_PUBLIC) + #undef JSON_HEDLEY_PUBLIC +#endif +#if defined(JSON_HEDLEY_IMPORT) + #undef JSON_HEDLEY_IMPORT +#endif +#if defined(_WIN32) || defined(__CYGWIN__) +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC __declspec(dllexport) +# define JSON_HEDLEY_IMPORT __declspec(dllimport) +#else +# if \ + JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + ( \ + defined(__TI_EABI__) && \ + ( \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \ + ) \ + ) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) +# define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default"))) +# else +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC +# endif +# define JSON_HEDLEY_IMPORT extern +#endif + +#if defined(JSON_HEDLEY_NO_THROW) + #undef JSON_HEDLEY_NO_THROW +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NO_THROW __declspec(nothrow) +#else + #define JSON_HEDLEY_NO_THROW +#endif + +#if defined(JSON_HEDLEY_FALL_THROUGH) + #undef JSON_HEDLEY_FALL_THROUGH +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]]) +#elif defined(__fallthrough) /* SAL */ + #define JSON_HEDLEY_FALL_THROUGH __fallthrough +#else + #define JSON_HEDLEY_FALL_THROUGH +#endif + +#if defined(JSON_HEDLEY_RETURNS_NON_NULL) + #undef JSON_HEDLEY_RETURNS_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) +#elif defined(_Ret_notnull_) /* SAL */ + #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_ +#else + #define JSON_HEDLEY_RETURNS_NON_NULL +#endif + +#if defined(JSON_HEDLEY_ARRAY_PARAM) + #undef JSON_HEDLEY_ARRAY_PARAM +#endif +#if \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__STDC_NO_VLA__) && \ + !defined(__cplusplus) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_ARRAY_PARAM(name) (name) +#else + #define JSON_HEDLEY_ARRAY_PARAM(name) +#endif + +#if defined(JSON_HEDLEY_IS_CONSTANT) + #undef JSON_HEDLEY_IS_CONSTANT +#endif +#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR) + #undef JSON_HEDLEY_REQUIRE_CONSTEXPR +#endif +/* JSON_HEDLEY_IS_CONSTEXPR_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #undef JSON_HEDLEY_IS_CONSTEXPR_ +#endif +#if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) +#endif +#if !defined(__cplusplus) +# if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) +#endif +# elif \ + ( \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ + !defined(JSON_HEDLEY_SUNPRO_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION)) || \ + (JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) +#endif +# elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + defined(JSON_HEDLEY_INTEL_VERSION) || \ + defined(JSON_HEDLEY_TINYC_VERSION) || \ + defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \ + defined(JSON_HEDLEY_TI_CL2000_VERSION) || \ + defined(JSON_HEDLEY_TI_CL6X_VERSION) || \ + defined(JSON_HEDLEY_TI_CL7X_VERSION) || \ + defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \ + defined(__clang__) +# define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \ + sizeof(void) != \ + sizeof(*( \ + 1 ? \ + ((void*) ((expr) * 0L) ) : \ +((struct { char v[sizeof(void) * 2]; } *) 1) \ + ) \ + ) \ + ) +# endif +#endif +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1)) +#else + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) (0) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr) +#endif + +#if defined(JSON_HEDLEY_BEGIN_C_DECLS) + #undef JSON_HEDLEY_BEGIN_C_DECLS +#endif +#if defined(JSON_HEDLEY_END_C_DECLS) + #undef JSON_HEDLEY_END_C_DECLS +#endif +#if defined(JSON_HEDLEY_C_DECL) + #undef JSON_HEDLEY_C_DECL +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_BEGIN_C_DECLS extern "C" { + #define JSON_HEDLEY_END_C_DECLS } + #define JSON_HEDLEY_C_DECL extern "C" +#else + #define JSON_HEDLEY_BEGIN_C_DECLS + #define JSON_HEDLEY_END_C_DECLS + #define JSON_HEDLEY_C_DECL +#endif + +#if defined(JSON_HEDLEY_STATIC_ASSERT) + #undef JSON_HEDLEY_STATIC_ASSERT +#endif +#if \ + !defined(__cplusplus) && ( \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \ + (JSON_HEDLEY_HAS_FEATURE(c_static_assert) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + defined(_Static_assert) \ + ) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) +#elif \ + (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message)) +#else +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) +#endif + +#if defined(JSON_HEDLEY_NULL) + #undef JSON_HEDLEY_NULL +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr) + #elif defined(NULL) + #define JSON_HEDLEY_NULL NULL + #else + #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0) + #endif +#elif defined(NULL) + #define JSON_HEDLEY_NULL NULL +#else + #define JSON_HEDLEY_NULL ((void*) 0) +#endif + +#if defined(JSON_HEDLEY_MESSAGE) + #undef JSON_HEDLEY_MESSAGE +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_MESSAGE(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(message msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg) +#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_WARNING) + #undef JSON_HEDLEY_WARNING +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_WARNING(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(clang warning msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_REQUIRE) + #undef JSON_HEDLEY_REQUIRE +#endif +#if defined(JSON_HEDLEY_REQUIRE_MSG) + #undef JSON_HEDLEY_REQUIRE_MSG +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if) +# if JSON_HEDLEY_HAS_WARNING("-Wgcc-compat") +# define JSON_HEDLEY_REQUIRE(expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), #expr, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), msg, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error"))) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error"))) +# endif +#else +# define JSON_HEDLEY_REQUIRE(expr) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) +#endif + +#if defined(JSON_HEDLEY_FLAGS) + #undef JSON_HEDLEY_FLAGS +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || JSON_HEDLEY_HAS_WARNING("-Wbitfield-enum-conversion")) + #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__)) +#else + #define JSON_HEDLEY_FLAGS +#endif + +#if defined(JSON_HEDLEY_FLAGS_CAST) + #undef JSON_HEDLEY_FLAGS_CAST +#endif +#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0) +# define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("warning(disable:188)") \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr) +#endif + +#if defined(JSON_HEDLEY_EMPTY_BASES) + #undef JSON_HEDLEY_EMPTY_BASES +#endif +#if \ + (JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases) +#else + #define JSON_HEDLEY_EMPTY_BASES +#endif + +/* Remaining macros are deprecated. */ + +#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#endif +#if defined(__clang__) + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0) +#else + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN) + #undef JSON_HEDLEY_CLANG_HAS_BUILTIN +#endif +#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin) + +#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE) + #undef JSON_HEDLEY_CLANG_HAS_FEATURE +#endif +#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature) + +#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION) + #undef JSON_HEDLEY_CLANG_HAS_EXTENSION +#endif +#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension) + +#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_WARNING) + #undef JSON_HEDLEY_CLANG_HAS_WARNING +#endif +#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning) + +#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */ + +// #include + + +#include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template struct make_void +{ + using type = void; +}; +template using void_t = typename make_void::type; +} // namespace detail +} // namespace nlohmann + + +// https://en.cppreference.com/w/cpp/experimental/is_detected +namespace nlohmann +{ +namespace detail +{ +struct nonesuch +{ + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + nonesuch(nonesuch const&&) = delete; + void operator=(nonesuch const&) = delete; + void operator=(nonesuch&&) = delete; +}; + +template class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op; +}; + +template class Op, class... Args> +using is_detected = typename detector::value_t; + +template class Op, class... Args> +struct is_detected_lazy : is_detected { }; + +template class Op, class... Args> +using detected_t = typename detector::type; + +template class Op, class... Args> +using detected_or = detector; + +template class Op, class... Args> +using detected_or_t = typename detected_or::type; + +template class Op, class... Args> +using is_detected_exact = std::is_same>; + +template class Op, class... Args> +using is_detected_convertible = + std::is_convertible, To>; +} // namespace detail +} // namespace nlohmann + + +// This file contains all internal macro definitions +// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them + +// exclude unsupported compilers +#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) + #if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #endif +#endif + +// C++ language standard detection +// if the user manually specified the used c++ version this is skipped +#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) + #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 + #endif + // the cpp 11 flag is always specified because it is the minimal required version + #define JSON_HAS_CPP_11 +#endif + +#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM) + #ifdef JSON_HAS_CPP_17 + #if defined(__cpp_lib_filesystem) + #define JSON_HAS_FILESYSTEM 1 + #elif defined(__cpp_lib_experimental_filesystem) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #elif !defined(__has_include) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #elif __has_include() + #define JSON_HAS_FILESYSTEM 1 + #elif __has_include() + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #endif + + // std::filesystem does not work on MinGW GCC 8: https://sourceforge.net/p/mingw-w64/bugs/737/ + #if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ == 8 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before GCC 8: https://en.cppreference.com/w/cpp/compiler_support + #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before Clang 7: https://en.cppreference.com/w/cpp/compiler_support + #if defined(__clang_major__) && __clang_major__ < 7 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support + #if defined(_MSC_VER) && _MSC_VER < 1940 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before iOS 13 + #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before macOS Catalina + #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + #endif +#endif + +#ifndef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 0 +#endif + +#ifndef JSON_HAS_FILESYSTEM + #define JSON_HAS_FILESYSTEM 0 +#endif + +// disable documentation warnings on clang +#if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdocumentation" + #pragma clang diagnostic ignored "-Wdocumentation-unknown-command" +#endif + +// allow disabling exceptions +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) + #define JSON_INTERNAL_CATCH(exception) catch(exception) +#else + #include + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) + #define JSON_INTERNAL_CATCH(exception) if(false) +#endif + +// override exception macros +#if defined(JSON_THROW_USER) + #undef JSON_THROW + #define JSON_THROW JSON_THROW_USER +#endif +#if defined(JSON_TRY_USER) + #undef JSON_TRY + #define JSON_TRY JSON_TRY_USER +#endif +#if defined(JSON_CATCH_USER) + #undef JSON_CATCH + #define JSON_CATCH JSON_CATCH_USER + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_CATCH_USER +#endif +#if defined(JSON_INTERNAL_CATCH_USER) + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER +#endif + +// allow overriding assert +#if !defined(JSON_ASSERT) + #include // assert + #define JSON_ASSERT(x) assert(x) +#endif + +// allow to access some private functions (needed by the test suite) +#if defined(JSON_TESTS_PRIVATE) + #define JSON_PRIVATE_UNLESS_TESTED public +#else + #define JSON_PRIVATE_UNLESS_TESTED private +#endif + +/*! +@brief macro to briefly define a mapping between an enum and JSON +@def NLOHMANN_JSON_SERIALIZE_ENUM +@since version 3.4.0 +*/ +#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ + template \ + inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template \ + inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [&j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ + } + +// Ugly macros to avoid uglier copy-paste when specializing basic_json. They +// may be removed in the future once the class is split. + +#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ + template class ObjectType, \ + template class ArrayType, \ + class StringType, class BooleanType, class NumberIntegerType, \ + class NumberUnsignedType, class NumberFloatType, \ + template class AllocatorType, \ + template class JSONSerializer, \ + class BinaryType> + +#define NLOHMANN_BASIC_JSON_TPL \ + basic_json + +// Macros to simplify conversion from/to types + +#define NLOHMANN_JSON_EXPAND( x ) x +#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME +#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \ + NLOHMANN_JSON_PASTE64, \ + NLOHMANN_JSON_PASTE63, \ + NLOHMANN_JSON_PASTE62, \ + NLOHMANN_JSON_PASTE61, \ + NLOHMANN_JSON_PASTE60, \ + NLOHMANN_JSON_PASTE59, \ + NLOHMANN_JSON_PASTE58, \ + NLOHMANN_JSON_PASTE57, \ + NLOHMANN_JSON_PASTE56, \ + NLOHMANN_JSON_PASTE55, \ + NLOHMANN_JSON_PASTE54, \ + NLOHMANN_JSON_PASTE53, \ + NLOHMANN_JSON_PASTE52, \ + NLOHMANN_JSON_PASTE51, \ + NLOHMANN_JSON_PASTE50, \ + NLOHMANN_JSON_PASTE49, \ + NLOHMANN_JSON_PASTE48, \ + NLOHMANN_JSON_PASTE47, \ + NLOHMANN_JSON_PASTE46, \ + NLOHMANN_JSON_PASTE45, \ + NLOHMANN_JSON_PASTE44, \ + NLOHMANN_JSON_PASTE43, \ + NLOHMANN_JSON_PASTE42, \ + NLOHMANN_JSON_PASTE41, \ + NLOHMANN_JSON_PASTE40, \ + NLOHMANN_JSON_PASTE39, \ + NLOHMANN_JSON_PASTE38, \ + NLOHMANN_JSON_PASTE37, \ + NLOHMANN_JSON_PASTE36, \ + NLOHMANN_JSON_PASTE35, \ + NLOHMANN_JSON_PASTE34, \ + NLOHMANN_JSON_PASTE33, \ + NLOHMANN_JSON_PASTE32, \ + NLOHMANN_JSON_PASTE31, \ + NLOHMANN_JSON_PASTE30, \ + NLOHMANN_JSON_PASTE29, \ + NLOHMANN_JSON_PASTE28, \ + NLOHMANN_JSON_PASTE27, \ + NLOHMANN_JSON_PASTE26, \ + NLOHMANN_JSON_PASTE25, \ + NLOHMANN_JSON_PASTE24, \ + NLOHMANN_JSON_PASTE23, \ + NLOHMANN_JSON_PASTE22, \ + NLOHMANN_JSON_PASTE21, \ + NLOHMANN_JSON_PASTE20, \ + NLOHMANN_JSON_PASTE19, \ + NLOHMANN_JSON_PASTE18, \ + NLOHMANN_JSON_PASTE17, \ + NLOHMANN_JSON_PASTE16, \ + NLOHMANN_JSON_PASTE15, \ + NLOHMANN_JSON_PASTE14, \ + NLOHMANN_JSON_PASTE13, \ + NLOHMANN_JSON_PASTE12, \ + NLOHMANN_JSON_PASTE11, \ + NLOHMANN_JSON_PASTE10, \ + NLOHMANN_JSON_PASTE9, \ + NLOHMANN_JSON_PASTE8, \ + NLOHMANN_JSON_PASTE7, \ + NLOHMANN_JSON_PASTE6, \ + NLOHMANN_JSON_PASTE5, \ + NLOHMANN_JSON_PASTE4, \ + NLOHMANN_JSON_PASTE3, \ + NLOHMANN_JSON_PASTE2, \ + NLOHMANN_JSON_PASTE1)(__VA_ARGS__)) +#define NLOHMANN_JSON_PASTE2(func, v1) func(v1) +#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2) +#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3) +#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4) +#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5) +#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6) +#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7) +#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8) +#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9) +#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10) +#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) +#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) +#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) +#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) +#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) +#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) +#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) +#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) +#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) +#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) +#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) +#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) +#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) +#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) +#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) +#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) +#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) +#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) +#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) +#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) +#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) +#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) +#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) +#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) +#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) +#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) +#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) +#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) +#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) +#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) +#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) +#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) +#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) +#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) +#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) +#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) +#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) +#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) +#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) +#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) +#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) +#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) +#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) +#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) +#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) +#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) +#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) +#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) +#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) +#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) +#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) +#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) +#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) + +#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; +#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + + +// inspired from https://stackoverflow.com/a/26745591 +// allows to call any std function as if (e.g. with begin): +// using std::begin; begin(x); +// +// it allows using the detected idiom to retrieve the return type +// of such an expression +#define NLOHMANN_CAN_CALL_STD_FUNC_IMPL(std_name) \ + namespace detail { \ + using std::std_name; \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + } \ + \ + namespace detail2 { \ + struct std_name##_tag \ + { \ + }; \ + \ + template \ + std_name##_tag std_name(T&&...); \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + \ + template \ + struct would_call_std_##std_name \ + { \ + static constexpr auto const value = ::nlohmann::detail:: \ + is_detected_exact::value; \ + }; \ + } /* namespace detail2 */ \ + \ + template \ + struct would_call_std_##std_name : detail2::would_call_std_##std_name \ + { \ + } + +#ifndef JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_USE_IMPLICIT_CONVERSIONS 1 +#endif + +#if JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_EXPLICIT +#else + #define JSON_EXPLICIT explicit +#endif + +#ifndef JSON_DIAGNOSTICS + #define JSON_DIAGNOSTICS 0 +#endif + + +namespace nlohmann +{ +namespace detail +{ + +/*! +@brief replace all occurrences of a substring by another string + +@param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t +@param[in] f the substring to replace with @a t +@param[in] t the string to replace @a f + +@pre The search string @a f must not be empty. **This precondition is +enforced with an assertion.** + +@since version 2.0.0 +*/ +inline void replace_substring(std::string& s, const std::string& f, + const std::string& t) +{ + JSON_ASSERT(!f.empty()); + for (auto pos = s.find(f); // find first occurrence of f + pos != std::string::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t, and + pos = s.find(f, pos + t.size())) // find next occurrence of f + {} +} + +/*! + * @brief string escaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to escape + * @return escaped string + * + * Note the order of escaping "~" to "~0" and "/" to "~1" is important. + */ +inline std::string escape(std::string s) +{ + replace_substring(s, "~", "~0"); + replace_substring(s, "/", "~1"); + return s; +} + +/*! + * @brief string unescaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to unescape + * @return unescaped string + * + * Note the order of escaping "~1" to "/" and "~0" to "~" is important. + */ +static void unescape(std::string& s) +{ + replace_substring(s, "~1", "/"); + replace_substring(s, "~0", "~"); +} + +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // size_t + +namespace nlohmann +{ +namespace detail +{ +/// struct to capture the start position of the current token +struct position_t +{ + /// the total number of characters read + std::size_t chars_read_total = 0; + /// the number of characters read in the current line + std::size_t chars_read_current_line = 0; + /// the number of lines read + std::size_t lines_read = 0; + + /// conversion to size_t to preserve SAX interface + constexpr operator size_t() const + { + return chars_read_total; + } +}; + +} // namespace detail +} // namespace nlohmann + +// #include + + +namespace nlohmann +{ +namespace detail +{ +//////////////// +// exceptions // +//////////////// + +/// @brief general exception of the @ref basic_json class +/// @sa https://json.nlohmann.me/api/basic_json/exception/ +class exception : public std::exception +{ + public: + /// returns the explanatory string + const char* what() const noexcept override + { + return m.what(); + } + + /// the id of the exception + const int id; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes) + + protected: + JSON_HEDLEY_NON_NULL(3) + exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} // NOLINT(bugprone-throw-keyword-missing) + + static std::string name(const std::string& ename, int id_) + { + return "[json.exception." + ename + "." + std::to_string(id_) + "] "; + } + + template + static std::string diagnostics(const BasicJsonType& leaf_element) + { +#if JSON_DIAGNOSTICS + std::vector tokens; + for (const auto* current = &leaf_element; current->m_parent != nullptr; current = current->m_parent) + { + switch (current->m_parent->type()) + { + case value_t::array: + { + for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i) + { + if (¤t->m_parent->m_value.array->operator[](i) == current) + { + tokens.emplace_back(std::to_string(i)); + break; + } + } + break; + } + + case value_t::object: + { + for (const auto& element : *current->m_parent->m_value.object) + { + if (&element.second == current) + { + tokens.emplace_back(element.first.c_str()); + break; + } + } + break; + } + + case value_t::null: // LCOV_EXCL_LINE + case value_t::string: // LCOV_EXCL_LINE + case value_t::boolean: // LCOV_EXCL_LINE + case value_t::number_integer: // LCOV_EXCL_LINE + case value_t::number_unsigned: // LCOV_EXCL_LINE + case value_t::number_float: // LCOV_EXCL_LINE + case value_t::binary: // LCOV_EXCL_LINE + case value_t::discarded: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + break; // LCOV_EXCL_LINE + } + } + + if (tokens.empty()) + { + return ""; + } + + return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + detail::escape(b); + }) + ") "; +#else + static_cast(leaf_element); + return ""; +#endif + } + + private: + /// an exception object as storage for error messages + std::runtime_error m; +}; + +/// @brief exception indicating a parse error +/// @sa https://json.nlohmann.me/api/basic_json/parse_error/ +class parse_error : public exception +{ + public: + /*! + @brief create a parse error exception + @param[in] id_ the id of the exception + @param[in] pos the position where the error occurred (or with + chars_read_total=0 if the position cannot be + determined) + @param[in] what_arg the explanatory string + @return parse_error object + */ + template + static parse_error create(int id_, const position_t& pos, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("parse_error", id_) + "parse error" + + position_string(pos) + ": " + exception::diagnostics(context) + what_arg; + return {id_, pos.chars_read_total, w.c_str()}; + } + + template + static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("parse_error", id_) + "parse error" + + (byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") + + ": " + exception::diagnostics(context) + what_arg; + return {id_, byte_, w.c_str()}; + } + + /*! + @brief byte index of the parse error + + The byte index of the last read character in the input file. + + @note For an input with n bytes, 1 is the index of the first character and + n+1 is the index of the terminating null byte or the end of file. + This also holds true when reading a byte vector (CBOR or MessagePack). + */ + const std::size_t byte; + + private: + parse_error(int id_, std::size_t byte_, const char* what_arg) + : exception(id_, what_arg), byte(byte_) {} + + static std::string position_string(const position_t& pos) + { + return " at line " + std::to_string(pos.lines_read + 1) + + ", column " + std::to_string(pos.chars_read_current_line); + } +}; + +/// @brief exception indicating errors with iterators +/// @sa https://json.nlohmann.me/api/basic_json/invalid_iterator/ +class invalid_iterator : public exception +{ + public: + template + static invalid_iterator create(int id_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("invalid_iterator", id_) + exception::diagnostics(context) + what_arg; + return {id_, w.c_str()}; + } + + private: + JSON_HEDLEY_NON_NULL(3) + invalid_iterator(int id_, const char* what_arg) + : exception(id_, what_arg) {} +}; + +/// @brief exception indicating executing a member function with a wrong type +/// @sa https://json.nlohmann.me/api/basic_json/type_error/ +class type_error : public exception +{ + public: + template + static type_error create(int id_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("type_error", id_) + exception::diagnostics(context) + what_arg; + return {id_, w.c_str()}; + } + + private: + JSON_HEDLEY_NON_NULL(3) + type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +/// @brief exception indicating access out of the defined range +/// @sa https://json.nlohmann.me/api/basic_json/out_of_range/ +class out_of_range : public exception +{ + public: + template + static out_of_range create(int id_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("out_of_range", id_) + exception::diagnostics(context) + what_arg; + return {id_, w.c_str()}; + } + + private: + JSON_HEDLEY_NON_NULL(3) + out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +/// @brief exception indicating other library errors +/// @sa https://json.nlohmann.me/api/basic_json/other_error/ +class other_error : public exception +{ + public: + template + static other_error create(int id_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("other_error", id_) + exception::diagnostics(context) + what_arg; + return {id_, w.c_str()}; + } + + private: + JSON_HEDLEY_NON_NULL(3) + other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // size_t +#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type +#include // index_sequence, make_index_sequence, index_sequence_for + +// #include + + +namespace nlohmann +{ +namespace detail +{ + +template +using uncvref_t = typename std::remove_cv::type>::type; + +#ifdef JSON_HAS_CPP_14 + +// the following utilities are natively available in C++14 +using std::enable_if_t; +using std::index_sequence; +using std::make_index_sequence; +using std::index_sequence_for; + +#else + +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; + +// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h +// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0. + +//// START OF CODE FROM GOOGLE ABSEIL + +// integer_sequence +// +// Class template representing a compile-time integer sequence. An instantiation +// of `integer_sequence` has a sequence of integers encoded in its +// type through its template arguments (which is a common need when +// working with C++11 variadic templates). `absl::integer_sequence` is designed +// to be a drop-in replacement for C++14's `std::integer_sequence`. +// +// Example: +// +// template< class T, T... Ints > +// void user_function(integer_sequence); +// +// int main() +// { +// // user_function's `T` will be deduced to `int` and `Ints...` +// // will be deduced to `0, 1, 2, 3, 4`. +// user_function(make_integer_sequence()); +// } +template +struct integer_sequence +{ + using value_type = T; + static constexpr std::size_t size() noexcept + { + return sizeof...(Ints); + } +}; + +// index_sequence +// +// A helper template for an `integer_sequence` of `size_t`, +// `absl::index_sequence` is designed to be a drop-in replacement for C++14's +// `std::index_sequence`. +template +using index_sequence = integer_sequence; + +namespace utility_internal +{ + +template +struct Extend; + +// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency. +template +struct Extend, SeqSize, 0> +{ + using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >; +}; + +template +struct Extend, SeqSize, 1> +{ + using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >; +}; + +// Recursion helper for 'make_integer_sequence'. +// 'Gen::type' is an alias for 'integer_sequence'. +template +struct Gen +{ + using type = + typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type; +}; + +template +struct Gen +{ + using type = integer_sequence; +}; + +} // namespace utility_internal + +// Compile-time sequences of integers + +// make_integer_sequence +// +// This template alias is equivalent to +// `integer_sequence`, and is designed to be a drop-in +// replacement for C++14's `std::make_integer_sequence`. +template +using make_integer_sequence = typename utility_internal::Gen::type; + +// make_index_sequence +// +// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`, +// and is designed to be a drop-in replacement for C++14's +// `std::make_index_sequence`. +template +using make_index_sequence = make_integer_sequence; + +// index_sequence_for +// +// Converts a typename pack into an index sequence of the same length, and +// is designed to be a drop-in replacement for C++14's +// `std::index_sequence_for()` +template +using index_sequence_for = make_index_sequence; + +//// END OF CODE FROM GOOGLE ABSEIL + +#endif + +// dispatch utility (taken from ranges-v3) +template struct priority_tag : priority_tag < N - 1 > {}; +template<> struct priority_tag<0> {}; + +// taken from ranges-v3 +template +struct static_const +{ + static constexpr T value{}; +}; + +template +constexpr T static_const::value; // NOLINT(readability-redundant-declaration) + +} // namespace detail +} // namespace nlohmann + +// #include + + +namespace nlohmann +{ +namespace detail +{ +// dispatching helper struct +template struct identity_tag {}; +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // numeric_limits +#include // false_type, is_constructible, is_integral, is_same, true_type +#include // declval +#include // tuple + +// #include + + +// #include + + +#include // random_access_iterator_tag + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +struct iterator_types {}; + +template +struct iterator_types < + It, + void_t> +{ + using difference_type = typename It::difference_type; + using value_type = typename It::value_type; + using pointer = typename It::pointer; + using reference = typename It::reference; + using iterator_category = typename It::iterator_category; +}; + +// This is required as some compilers implement std::iterator_traits in a way that +// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. +template +struct iterator_traits +{ +}; + +template +struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> + : iterator_types +{ +}; + +template +struct iterator_traits::value>> +{ + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; +}; +} // namespace detail +} // namespace nlohmann + +// #include + + +// #include + + +namespace nlohmann +{ +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin); +} // namespace nlohmann + +// #include + + +// #include + + +namespace nlohmann +{ +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end); +} // namespace nlohmann + +// #include + +// #include + +// #include +#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ +#define INCLUDE_NLOHMANN_JSON_FWD_HPP_ + +#include // int64_t, uint64_t +#include // map +#include // allocator +#include // string +#include // vector + +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +namespace nlohmann +{ +/*! +@brief default JSONSerializer template argument + +This serializer ignores the template arguments and uses ADL +([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) +for serialization. +*/ +template +struct adl_serializer; + +/// a class to store JSON values +/// @sa https://json.nlohmann.me/api/basic_json/ +template class ObjectType = + std::map, + template class ArrayType = std::vector, + class StringType = std::string, class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator, + template class JSONSerializer = + adl_serializer, + class BinaryType = std::vector> +class basic_json; + +/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document +/// @sa https://json.nlohmann.me/api/json_pointer/ +template +class json_pointer; + +/*! +@brief default specialization +@sa https://json.nlohmann.me/api/json/ +*/ +using json = basic_json<>; + +/// @brief a minimal map-like container that preserves insertion order +/// @sa https://json.nlohmann.me/api/ordered_map/ +template +struct ordered_map; + +/// @brief specialization that maintains the insertion order of object keys +/// @sa https://json.nlohmann.me/api/ordered_json/ +using ordered_json = basic_json; + +} // namespace nlohmann + +#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ + + +namespace nlohmann +{ +/*! +@brief detail namespace with internal helper functions + +This namespace collects functions that should not be exposed, +implementations of some @ref basic_json methods, and meta-programming helpers. + +@since version 2.1.0 +*/ +namespace detail +{ +///////////// +// helpers // +///////////// + +// Note to maintainers: +// +// Every trait in this file expects a non CV-qualified type. +// The only exceptions are in the 'aliases for detected' section +// (i.e. those of the form: decltype(T::member_function(std::declval()))) +// +// In this case, T has to be properly CV-qualified to constraint the function arguments +// (e.g. to_json(BasicJsonType&, const T&)) + +template struct is_basic_json : std::false_type {}; + +NLOHMANN_BASIC_JSON_TPL_DECLARATION +struct is_basic_json : std::true_type {}; + +////////////////////// +// json_ref helpers // +////////////////////// + +template +class json_ref; + +template +struct is_json_ref : std::false_type {}; + +template +struct is_json_ref> : std::true_type {}; + +////////////////////////// +// aliases for detected // +////////////////////////// + +template +using mapped_type_t = typename T::mapped_type; + +template +using key_type_t = typename T::key_type; + +template +using value_type_t = typename T::value_type; + +template +using difference_type_t = typename T::difference_type; + +template +using pointer_t = typename T::pointer; + +template +using reference_t = typename T::reference; + +template +using iterator_category_t = typename T::iterator_category; + +template +using to_json_function = decltype(T::to_json(std::declval()...)); + +template +using from_json_function = decltype(T::from_json(std::declval()...)); + +template +using get_template_function = decltype(std::declval().template get()); + +// trait checking if JSONSerializer::from_json(json const&, udt&) exists +template +struct has_from_json : std::false_type {}; + +// trait checking if j.get is valid +// use this trait instead of std::is_constructible or std::is_convertible, +// both rely on, or make use of implicit conversions, and thus fail when T +// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958) +template +struct is_getable +{ + static constexpr bool value = is_detected::value; +}; + +template +struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +// This trait checks if JSONSerializer::from_json(json const&) exists +// this overload is used for non-default-constructible user-defined-types +template +struct has_non_default_from_json : std::false_type {}; + +template +struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +// This trait checks if BasicJsonType::json_serializer::to_json exists +// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. +template +struct has_to_json : std::false_type {}; + +template +struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + + +/////////////////// +// is_ functions // +/////////////////// + +// https://en.cppreference.com/w/cpp/types/conjunction +template struct conjunction : std::true_type { }; +template struct conjunction : B1 { }; +template +struct conjunction +: std::conditional, B1>::type {}; + +// https://en.cppreference.com/w/cpp/types/negation +template struct negation : std::integral_constant < bool, !B::value > { }; + +// Reimplementation of is_constructible and is_default_constructible, due to them being broken for +// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367). +// This causes compile errors in e.g. clang 3.5 or gcc 4.9. +template +struct is_default_constructible : std::is_default_constructible {}; + +template +struct is_default_constructible> + : conjunction, is_default_constructible> {}; + +template +struct is_default_constructible> + : conjunction, is_default_constructible> {}; + +template +struct is_default_constructible> + : conjunction...> {}; + +template +struct is_default_constructible> + : conjunction...> {}; + + +template +struct is_constructible : std::is_constructible {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + + +template +struct is_iterator_traits : std::false_type {}; + +template +struct is_iterator_traits> +{ + private: + using traits = iterator_traits; + + public: + static constexpr auto value = + is_detected::value && + is_detected::value && + is_detected::value && + is_detected::value && + is_detected::value; +}; + +template +struct is_range +{ + private: + using t_ref = typename std::add_lvalue_reference::type; + + using iterator = detected_t; + using sentinel = detected_t; + + // to be 100% correct, it should use https://en.cppreference.com/w/cpp/iterator/input_or_output_iterator + // and https://en.cppreference.com/w/cpp/iterator/sentinel_for + // but reimplementing these would be too much work, as a lot of other concepts are used underneath + static constexpr auto is_iterator_begin = + is_iterator_traits>::value; + + public: + static constexpr bool value = !std::is_same::value && !std::is_same::value && is_iterator_begin; +}; + +template +using iterator_t = enable_if_t::value, result_of_begin())>>; + +template +using range_value_t = value_type_t>>; + +// The following implementation of is_complete_type is taken from +// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ +// and is written by Xiang Fan who agreed to using it in this library. + +template +struct is_complete_type : std::false_type {}; + +template +struct is_complete_type : std::true_type {}; + +template +struct is_compatible_object_type_impl : std::false_type {}; + +template +struct is_compatible_object_type_impl < + BasicJsonType, CompatibleObjectType, + enable_if_t < is_detected::value&& + is_detected::value >> +{ + using object_t = typename BasicJsonType::object_t; + + // macOS's is_constructible does not play well with nonesuch... + static constexpr bool value = + is_constructible::value && + is_constructible::value; +}; + +template +struct is_compatible_object_type + : is_compatible_object_type_impl {}; + +template +struct is_constructible_object_type_impl : std::false_type {}; + +template +struct is_constructible_object_type_impl < + BasicJsonType, ConstructibleObjectType, + enable_if_t < is_detected::value&& + is_detected::value >> +{ + using object_t = typename BasicJsonType::object_t; + + static constexpr bool value = + (is_default_constructible::value && + (std::is_move_assignable::value || + std::is_copy_assignable::value) && + (is_constructible::value && + std::is_same < + typename object_t::mapped_type, + typename ConstructibleObjectType::mapped_type >::value)) || + (has_from_json::value || + has_non_default_from_json < + BasicJsonType, + typename ConstructibleObjectType::mapped_type >::value); +}; + +template +struct is_constructible_object_type + : is_constructible_object_type_impl {}; + +template +struct is_compatible_string_type +{ + static constexpr auto value = + is_constructible::value; +}; + +template +struct is_constructible_string_type +{ + static constexpr auto value = + is_constructible::value; +}; + +template +struct is_compatible_array_type_impl : std::false_type {}; + +template +struct is_compatible_array_type_impl < + BasicJsonType, CompatibleArrayType, + enable_if_t < + is_detected::value&& + is_iterator_traits>>::value&& +// special case for types like std::filesystem::path whose iterator's value_type are themselves +// c.f. https://github.com/nlohmann/json/pull/3073 + !std::is_same>::value >> +{ + static constexpr bool value = + is_constructible>::value; +}; + +template +struct is_compatible_array_type + : is_compatible_array_type_impl {}; + +template +struct is_constructible_array_type_impl : std::false_type {}; + +template +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t::value >> + : std::true_type {}; + +template +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t < !std::is_same::value&& + !is_compatible_string_type::value&& + is_default_constructible::value&& +(std::is_move_assignable::value || + std::is_copy_assignable::value)&& +is_detected::value&& +is_iterator_traits>>::value&& +is_detected::value&& +// special case for types like std::filesystem::path whose iterator's value_type are themselves +// c.f. https://github.com/nlohmann/json/pull/3073 +!std::is_same>::value&& + is_complete_type < + detected_t>::value >> +{ + using value_type = range_value_t; + + static constexpr bool value = + std::is_same::value || + has_from_json::value || + has_non_default_from_json < + BasicJsonType, + value_type >::value; +}; + +template +struct is_constructible_array_type + : is_constructible_array_type_impl {}; + +template +struct is_compatible_integer_type_impl : std::false_type {}; + +template +struct is_compatible_integer_type_impl < + RealIntegerType, CompatibleNumberIntegerType, + enable_if_t < std::is_integral::value&& + std::is_integral::value&& + !std::is_same::value >> +{ + // is there an assert somewhere on overflows? + using RealLimits = std::numeric_limits; + using CompatibleLimits = std::numeric_limits; + + static constexpr auto value = + is_constructible::value && + CompatibleLimits::is_integer && + RealLimits::is_signed == CompatibleLimits::is_signed; +}; + +template +struct is_compatible_integer_type + : is_compatible_integer_type_impl {}; + +template +struct is_compatible_type_impl: std::false_type {}; + +template +struct is_compatible_type_impl < + BasicJsonType, CompatibleType, + enable_if_t::value >> +{ + static constexpr bool value = + has_to_json::value; +}; + +template +struct is_compatible_type + : is_compatible_type_impl {}; + +template +struct is_constructible_tuple : std::false_type {}; + +template +struct is_constructible_tuple> : conjunction...> {}; + +// a naive helper to check if a type is an ordered_map (exploits the fact that +// ordered_map inherits capacity() from std::vector) +template +struct is_ordered_map +{ + using one = char; + + struct two + { + char x[2]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + }; + + template static one test( decltype(&C::capacity) ) ; + template static two test(...); + + enum { value = sizeof(test(nullptr)) == sizeof(char) }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) +}; + +// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324) +template < typename T, typename U, enable_if_t < !std::is_same::value, int > = 0 > +T conditional_static_cast(U value) +{ + return static_cast(value); +} + +template::value, int> = 0> +T conditional_static_cast(U value) +{ + return value; +} + +} // namespace detail +} // namespace nlohmann + +// #include + + +#if JSON_HAS_EXPERIMENTAL_FILESYSTEM +#include +namespace nlohmann::detail +{ +namespace std_fs = std::experimental::filesystem; +} // namespace nlohmann::detail +#elif JSON_HAS_FILESYSTEM +#include +namespace nlohmann::detail +{ +namespace std_fs = std::filesystem; +} // namespace nlohmann::detail +#endif + +namespace nlohmann +{ +namespace detail +{ +template +void from_json(const BasicJsonType& j, typename std::nullptr_t& n) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_null())) + { + JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name()), j)); + } + n = nullptr; +} + +// overloads for basic_json template parameters +template < typename BasicJsonType, typename ArithmeticType, + enable_if_t < std::is_arithmetic::value&& + !std::is_same::value, + int > = 0 > +void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) +{ + switch (static_cast(j)) + { + case value_t::number_unsigned: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_integer: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_float: + { + val = static_cast(*j.template get_ptr()); + break; + } + + case value_t::null: + case value_t::object: + case value_t::array: + case value_t::string: + case value_t::boolean: + case value_t::binary: + case value_t::discarded: + default: + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j)); + } +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_boolean())) + { + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()), j)); + } + b = *j.template get_ptr(); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_string())) + { + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); + } + s = *j.template get_ptr(); +} + +template < + typename BasicJsonType, typename ConstructibleStringType, + enable_if_t < + is_constructible_string_type::value&& + !std::is_same::value, + int > = 0 > +void from_json(const BasicJsonType& j, ConstructibleStringType& s) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_string())) + { + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); + } + + s = *j.template get_ptr(); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val) +{ + get_arithmetic_value(j, val); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val) +{ + get_arithmetic_value(j, val); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val) +{ + get_arithmetic_value(j, val); +} + +template::value, int> = 0> +void from_json(const BasicJsonType& j, EnumType& e) +{ + typename std::underlying_type::type val; + get_arithmetic_value(j, val); + e = static_cast(val); +} + +// forward_list doesn't have an insert method +template::value, int> = 0> +void from_json(const BasicJsonType& j, std::forward_list& l) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + l.clear(); + std::transform(j.rbegin(), j.rend(), + std::front_inserter(l), [](const BasicJsonType & i) + { + return i.template get(); + }); +} + +// valarray doesn't have an insert method +template::value, int> = 0> +void from_json(const BasicJsonType& j, std::valarray& l) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + l.resize(j.size()); + std::transform(j.begin(), j.end(), std::begin(l), + [](const BasicJsonType & elem) + { + return elem.template get(); + }); +} + +template +auto from_json(const BasicJsonType& j, T (&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) +-> decltype(j.template get(), void()) +{ + for (std::size_t i = 0; i < N; ++i) + { + arr[i] = j.at(i).template get(); + } +} + +template +void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/) +{ + arr = *j.template get_ptr(); +} + +template +auto from_json_array_impl(const BasicJsonType& j, std::array& arr, + priority_tag<2> /*unused*/) +-> decltype(j.template get(), void()) +{ + for (std::size_t i = 0; i < N; ++i) + { + arr[i] = j.at(i).template get(); + } +} + +template::value, + int> = 0> +auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/) +-> decltype( + arr.reserve(std::declval()), + j.template get(), + void()) +{ + using std::end; + + ConstructibleArrayType ret; + ret.reserve(j.size()); + std::transform(j.begin(), j.end(), + std::inserter(ret, end(ret)), [](const BasicJsonType & i) + { + // get() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get(); + }); + arr = std::move(ret); +} + +template::value, + int> = 0> +void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, + priority_tag<0> /*unused*/) +{ + using std::end; + + ConstructibleArrayType ret; + std::transform( + j.begin(), j.end(), std::inserter(ret, end(ret)), + [](const BasicJsonType & i) + { + // get() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get(); + }); + arr = std::move(ret); +} + +template < typename BasicJsonType, typename ConstructibleArrayType, + enable_if_t < + is_constructible_array_type::value&& + !is_constructible_object_type::value&& + !is_constructible_string_type::value&& + !std::is_same::value&& + !is_basic_json::value, + int > = 0 > +auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr) +-> decltype(from_json_array_impl(j, arr, priority_tag<3> {}), +j.template get(), +void()) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + + from_json_array_impl(j, arr, priority_tag<3> {}); +} + +template < typename BasicJsonType, typename T, std::size_t... Idx > +std::array from_json_inplace_array_impl(BasicJsonType&& j, + identity_tag> /*unused*/, index_sequence /*unused*/) +{ + return { { std::forward(j).at(Idx).template get()... } }; +} + +template < typename BasicJsonType, typename T, std::size_t N > +auto from_json(BasicJsonType&& j, identity_tag> tag) +-> decltype(from_json_inplace_array_impl(std::forward(j), tag, make_index_sequence {})) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + + return from_json_inplace_array_impl(std::forward(j), tag, make_index_sequence {}); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_binary())) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()), j)); + } + + bin = *j.template get_ptr(); +} + +template::value, int> = 0> +void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_object())) + { + JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()), j)); + } + + ConstructibleObjectType ret; + const auto* inner_object = j.template get_ptr(); + using value_type = typename ConstructibleObjectType::value_type; + std::transform( + inner_object->begin(), inner_object->end(), + std::inserter(ret, ret.begin()), + [](typename BasicJsonType::object_t::value_type const & p) + { + return value_type(p.first, p.second.template get()); + }); + obj = std::move(ret); +} + +// overload for arithmetic types, not chosen for basic_json template arguments +// (BooleanType, etc..); note: Is it really necessary to provide explicit +// overloads for boolean_t etc. in case of a custom BooleanType which is not +// an arithmetic type? +template < typename BasicJsonType, typename ArithmeticType, + enable_if_t < + std::is_arithmetic::value&& + !std::is_same::value&& + !std::is_same::value&& + !std::is_same::value&& + !std::is_same::value, + int > = 0 > +void from_json(const BasicJsonType& j, ArithmeticType& val) +{ + switch (static_cast(j)) + { + case value_t::number_unsigned: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_integer: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_float: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::boolean: + { + val = static_cast(*j.template get_ptr()); + break; + } + + case value_t::null: + case value_t::object: + case value_t::array: + case value_t::string: + case value_t::binary: + case value_t::discarded: + default: + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j)); + } +} + +template +std::tuple from_json_tuple_impl_base(BasicJsonType&& j, index_sequence /*unused*/) +{ + return std::make_tuple(std::forward(j).at(Idx).template get()...); +} + +template < typename BasicJsonType, class A1, class A2 > +std::pair from_json_tuple_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<0> /*unused*/) +{ + return {std::forward(j).at(0).template get(), + std::forward(j).at(1).template get()}; +} + +template +void from_json_tuple_impl(BasicJsonType&& j, std::pair& p, priority_tag<1> /*unused*/) +{ + p = from_json_tuple_impl(std::forward(j), identity_tag> {}, priority_tag<0> {}); +} + +template +std::tuple from_json_tuple_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<2> /*unused*/) +{ + return from_json_tuple_impl_base(std::forward(j), index_sequence_for {}); +} + +template +void from_json_tuple_impl(BasicJsonType&& j, std::tuple& t, priority_tag<3> /*unused*/) +{ + t = from_json_tuple_impl_base(std::forward(j), index_sequence_for {}); +} + +template +auto from_json(BasicJsonType&& j, TupleRelated&& t) +-> decltype(from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<3> {})) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + + return from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<3> {}); +} + +template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, + typename = enable_if_t < !std::is_constructible < + typename BasicJsonType::string_t, Key >::value >> +void from_json(const BasicJsonType& j, std::map& m) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + m.clear(); + for (const auto& p : j) + { + if (JSON_HEDLEY_UNLIKELY(!p.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()), j)); + } + m.emplace(p.at(0).template get(), p.at(1).template get()); + } +} + +template < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator, + typename = enable_if_t < !std::is_constructible < + typename BasicJsonType::string_t, Key >::value >> +void from_json(const BasicJsonType& j, std::unordered_map& m) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + m.clear(); + for (const auto& p : j) + { + if (JSON_HEDLEY_UNLIKELY(!p.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()), j)); + } + m.emplace(p.at(0).template get(), p.at(1).template get()); + } +} + +#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM +template +void from_json(const BasicJsonType& j, std_fs::path& p) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_string())) + { + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); + } + p = *j.template get_ptr(); +} +#endif + +struct from_json_fn +{ + template + auto operator()(const BasicJsonType& j, T&& val) const + noexcept(noexcept(from_json(j, std::forward(val)))) + -> decltype(from_json(j, std::forward(val))) + { + return from_json(j, std::forward(val)); + } +}; +} // namespace detail + +/// namespace to hold default `from_json` function +/// to see why this is required: +/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html +namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces) +{ +constexpr const auto& from_json = detail::static_const::value; // NOLINT(misc-definitions-in-headers) +} // namespace +} // namespace nlohmann + +// #include + + +#include // copy +#include // begin, end +#include // string +#include // tuple, get +#include // is_same, is_constructible, is_floating_point, is_enum, underlying_type +#include // move, forward, declval, pair +#include // valarray +#include // vector + +// #include + +// #include + + +#include // size_t +#include // input_iterator_tag +#include // string, to_string +#include // tuple_size, get, tuple_element +#include // move + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +void int_to_string( string_type& target, std::size_t value ) +{ + // For ADL + using std::to_string; + target = to_string(value); +} +template class iteration_proxy_value +{ + public: + using difference_type = std::ptrdiff_t; + using value_type = iteration_proxy_value; + using pointer = value_type * ; + using reference = value_type & ; + using iterator_category = std::input_iterator_tag; + using string_type = typename std::remove_cv< typename std::remove_reference().key() ) >::type >::type; + + private: + /// the iterator + IteratorType anchor; + /// an index for arrays (used to create key names) + std::size_t array_index = 0; + /// last stringified array index + mutable std::size_t array_index_last = 0; + /// a string representation of the array index + mutable string_type array_index_str = "0"; + /// an empty string (to return a reference for primitive values) + const string_type empty_str{}; + + public: + explicit iteration_proxy_value(IteratorType it) noexcept + : anchor(std::move(it)) + {} + + /// dereference operator (needed for range-based for) + iteration_proxy_value& operator*() + { + return *this; + } + + /// increment operator (needed for range-based for) + iteration_proxy_value& operator++() + { + ++anchor; + ++array_index; + + return *this; + } + + /// equality operator (needed for InputIterator) + bool operator==(const iteration_proxy_value& o) const + { + return anchor == o.anchor; + } + + /// inequality operator (needed for range-based for) + bool operator!=(const iteration_proxy_value& o) const + { + return anchor != o.anchor; + } + + /// return key of the iterator + const string_type& key() const + { + JSON_ASSERT(anchor.m_object != nullptr); + + switch (anchor.m_object->type()) + { + // use integer array index as key + case value_t::array: + { + if (array_index != array_index_last) + { + int_to_string( array_index_str, array_index ); + array_index_last = array_index; + } + return array_index_str; + } + + // use key from the object + case value_t::object: + return anchor.key(); + + // use an empty key for all primitive types + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + return empty_str; + } + } + + /// return value of the iterator + typename IteratorType::reference value() const + { + return anchor.value(); + } +}; + +/// proxy class for the items() function +template class iteration_proxy +{ + private: + /// the container to iterate + typename IteratorType::reference container; + + public: + /// construct iteration proxy from a container + explicit iteration_proxy(typename IteratorType::reference cont) noexcept + : container(cont) {} + + /// return iterator begin (needed for range-based for) + iteration_proxy_value begin() noexcept + { + return iteration_proxy_value(container.begin()); + } + + /// return iterator end (needed for range-based for) + iteration_proxy_value end() noexcept + { + return iteration_proxy_value(container.end()); + } +}; +// Structured Bindings Support +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +template = 0> +auto get(const nlohmann::detail::iteration_proxy_value& i) -> decltype(i.key()) +{ + return i.key(); +} +// Structured Bindings Support +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +template = 0> +auto get(const nlohmann::detail::iteration_proxy_value& i) -> decltype(i.value()) +{ + return i.value(); +} +} // namespace detail +} // namespace nlohmann + +// The Addition to the STD Namespace is required to add +// Structured Bindings Support to the iteration_proxy_value class +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +namespace std +{ +#if defined(__clang__) + // Fix: https://github.com/nlohmann/json/issues/1401 + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wmismatched-tags" +#endif +template +class tuple_size<::nlohmann::detail::iteration_proxy_value> + : public std::integral_constant {}; + +template +class tuple_element> +{ + public: + using type = decltype( + get(std::declval < + ::nlohmann::detail::iteration_proxy_value> ())); +}; +#if defined(__clang__) + #pragma clang diagnostic pop +#endif +} // namespace std + +// #include + +// #include + +// #include + + +#if JSON_HAS_EXPERIMENTAL_FILESYSTEM +#include +namespace nlohmann::detail +{ +namespace std_fs = std::experimental::filesystem; +} // namespace nlohmann::detail +#elif JSON_HAS_FILESYSTEM +#include +namespace nlohmann::detail +{ +namespace std_fs = std::filesystem; +} // namespace nlohmann::detail +#endif + +namespace nlohmann +{ +namespace detail +{ +////////////////// +// constructors // +////////////////// + +/* + * Note all external_constructor<>::construct functions need to call + * j.m_value.destroy(j.m_type) to avoid a memory leak in case j contains an + * allocated value (e.g., a string). See bug issue + * https://github.com/nlohmann/json/issues/2865 for more information. + */ + +template struct external_constructor; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::boolean; + j.m_value = b; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::string; + j.m_value = s; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::string; + j.m_value = std::move(s); + j.assert_invariant(); + } + + template < typename BasicJsonType, typename CompatibleStringType, + enable_if_t < !std::is_same::value, + int > = 0 > + static void construct(BasicJsonType& j, const CompatibleStringType& str) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::string; + j.m_value.string = j.template create(str); + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::binary; + j.m_value = typename BasicJsonType::binary_t(b); + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::binary; + j.m_value = typename BasicJsonType::binary_t(std::move(b)); + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::number_float; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::number_unsigned; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::number_integer; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::array; + j.m_value = arr; + j.set_parents(); + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::array; + j.m_value = std::move(arr); + j.set_parents(); + j.assert_invariant(); + } + + template < typename BasicJsonType, typename CompatibleArrayType, + enable_if_t < !std::is_same::value, + int > = 0 > + static void construct(BasicJsonType& j, const CompatibleArrayType& arr) + { + using std::begin; + using std::end; + + j.m_value.destroy(j.m_type); + j.m_type = value_t::array; + j.m_value.array = j.template create(begin(arr), end(arr)); + j.set_parents(); + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, const std::vector& arr) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array->reserve(arr.size()); + for (const bool x : arr) + { + j.m_value.array->push_back(x); + j.set_parent(j.m_value.array->back()); + } + j.assert_invariant(); + } + + template::value, int> = 0> + static void construct(BasicJsonType& j, const std::valarray& arr) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array->resize(arr.size()); + if (arr.size() > 0) + { + std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin()); + } + j.set_parents(); + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::object; + j.m_value = obj; + j.set_parents(); + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::object; + j.m_value = std::move(obj); + j.set_parents(); + j.assert_invariant(); + } + + template < typename BasicJsonType, typename CompatibleObjectType, + enable_if_t < !std::is_same::value, int > = 0 > + static void construct(BasicJsonType& j, const CompatibleObjectType& obj) + { + using std::begin; + using std::end; + + j.m_value.destroy(j.m_type); + j.m_type = value_t::object; + j.m_value.object = j.template create(begin(obj), end(obj)); + j.set_parents(); + j.assert_invariant(); + } +}; + +///////////// +// to_json // +///////////// + +template::value, int> = 0> +void to_json(BasicJsonType& j, T b) noexcept +{ + external_constructor::construct(j, b); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, const CompatibleString& s) +{ + external_constructor::construct(j, s); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s) +{ + external_constructor::construct(j, std::move(s)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, FloatType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, EnumType e) noexcept +{ + using underlying_type = typename std::underlying_type::type; + external_constructor::construct(j, static_cast(e)); +} + +template +void to_json(BasicJsonType& j, const std::vector& e) +{ + external_constructor::construct(j, e); +} + +template < typename BasicJsonType, typename CompatibleArrayType, + enable_if_t < is_compatible_array_type::value&& + !is_compatible_object_type::value&& + !is_compatible_string_type::value&& + !std::is_same::value&& + !is_basic_json::value, + int > = 0 > +void to_json(BasicJsonType& j, const CompatibleArrayType& arr) +{ + external_constructor::construct(j, arr); +} + +template +void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin) +{ + external_constructor::construct(j, bin); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, const std::valarray& arr) +{ + external_constructor::construct(j, std::move(arr)); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr) +{ + external_constructor::construct(j, std::move(arr)); +} + +template < typename BasicJsonType, typename CompatibleObjectType, + enable_if_t < is_compatible_object_type::value&& !is_basic_json::value, int > = 0 > +void to_json(BasicJsonType& j, const CompatibleObjectType& obj) +{ + external_constructor::construct(j, obj); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj) +{ + external_constructor::construct(j, std::move(obj)); +} + +template < + typename BasicJsonType, typename T, std::size_t N, + enable_if_t < !std::is_constructible::value, // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + int > = 0 > +void to_json(BasicJsonType& j, const T(&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) +{ + external_constructor::construct(j, arr); +} + +template < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible::value&& std::is_constructible::value, int > = 0 > +void to_json(BasicJsonType& j, const std::pair& p) +{ + j = { p.first, p.second }; +} + +// for https://github.com/nlohmann/json/pull/1134 +template>::value, int> = 0> +void to_json(BasicJsonType& j, const T& b) +{ + j = { {b.key(), b.value()} }; +} + +template +void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence /*unused*/) +{ + j = { std::get(t)... }; +} + +template::value, int > = 0> +void to_json(BasicJsonType& j, const T& t) +{ + to_json_tuple_impl(j, t, make_index_sequence::value> {}); +} + +#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM +template +void to_json(BasicJsonType& j, const std_fs::path& p) +{ + j = p.string(); +} +#endif + +struct to_json_fn +{ + template + auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward(val)))) + -> decltype(to_json(j, std::forward(val)), void()) + { + return to_json(j, std::forward(val)); + } +}; +} // namespace detail + +/// namespace to hold default `to_json` function +/// to see why this is required: +/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html +namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces) +{ +constexpr const auto& to_json = detail::static_const::value; // NOLINT(misc-definitions-in-headers) +} // namespace +} // namespace nlohmann + +// #include + +// #include + + +namespace nlohmann +{ + +/// @sa https://json.nlohmann.me/api/adl_serializer/ +template +struct adl_serializer +{ + /// @brief convert a JSON value to any value type + /// @sa https://json.nlohmann.me/api/adl_serializer/from_json/ + template + static auto from_json(BasicJsonType && j, TargetType& val) noexcept( + noexcept(::nlohmann::from_json(std::forward(j), val))) + -> decltype(::nlohmann::from_json(std::forward(j), val), void()) + { + ::nlohmann::from_json(std::forward(j), val); + } + + /// @brief convert a JSON value to any value type + /// @sa https://json.nlohmann.me/api/adl_serializer/from_json/ + template + static auto from_json(BasicJsonType && j) noexcept( + noexcept(::nlohmann::from_json(std::forward(j), detail::identity_tag {}))) + -> decltype(::nlohmann::from_json(std::forward(j), detail::identity_tag {})) + { + return ::nlohmann::from_json(std::forward(j), detail::identity_tag {}); + } + + /// @brief convert any value type to a JSON value + /// @sa https://json.nlohmann.me/api/adl_serializer/to_json/ + template + static auto to_json(BasicJsonType& j, TargetType && val) noexcept( + noexcept(::nlohmann::to_json(j, std::forward(val)))) + -> decltype(::nlohmann::to_json(j, std::forward(val)), void()) + { + ::nlohmann::to_json(j, std::forward(val)); + } +}; +} // namespace nlohmann + +// #include + + +#include // uint8_t, uint64_t +#include // tie +#include // move + +namespace nlohmann +{ + +/// @brief an internal type for a backed binary type +/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/ +template +class byte_container_with_subtype : public BinaryType +{ + public: + using container_type = BinaryType; + using subtype_type = std::uint64_t; + + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/ + byte_container_with_subtype() noexcept(noexcept(container_type())) + : container_type() + {} + + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/ + byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b))) + : container_type(b) + {} + + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/ + byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b)))) + : container_type(std::move(b)) + {} + + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/ + byte_container_with_subtype(const container_type& b, subtype_type subtype_) noexcept(noexcept(container_type(b))) + : container_type(b) + , m_subtype(subtype_) + , m_has_subtype(true) + {} + + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/ + byte_container_with_subtype(container_type&& b, subtype_type subtype_) noexcept(noexcept(container_type(std::move(b)))) + : container_type(std::move(b)) + , m_subtype(subtype_) + , m_has_subtype(true) + {} + + bool operator==(const byte_container_with_subtype& rhs) const + { + return std::tie(static_cast(*this), m_subtype, m_has_subtype) == + std::tie(static_cast(rhs), rhs.m_subtype, rhs.m_has_subtype); + } + + bool operator!=(const byte_container_with_subtype& rhs) const + { + return !(rhs == *this); + } + + /// @brief sets the binary subtype + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/set_subtype/ + void set_subtype(subtype_type subtype_) noexcept + { + m_subtype = subtype_; + m_has_subtype = true; + } + + /// @brief return the binary subtype + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/subtype/ + constexpr subtype_type subtype() const noexcept + { + return m_has_subtype ? m_subtype : static_cast(-1); + } + + /// @brief return whether the value has a subtype + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/has_subtype/ + constexpr bool has_subtype() const noexcept + { + return m_has_subtype; + } + + /// @brief clears the binary subtype + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/clear_subtype/ + void clear_subtype() noexcept + { + m_subtype = 0; + m_has_subtype = false; + } + + private: + subtype_type m_subtype = 0; + bool m_has_subtype = false; +}; + +} // namespace nlohmann + +// #include + +// #include + +// #include + +// #include + + +#include // uint8_t +#include // size_t +#include // hash + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ + +// boost::hash_combine +inline std::size_t combine(std::size_t seed, std::size_t h) noexcept +{ + seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U); + return seed; +} + +/*! +@brief hash a JSON value + +The hash function tries to rely on std::hash where possible. Furthermore, the +type of the JSON value is taken into account to have different hash values for +null, 0, 0U, and false, etc. + +@tparam BasicJsonType basic_json specialization +@param j JSON value to hash +@return hash value of j +*/ +template +std::size_t hash(const BasicJsonType& j) +{ + using string_t = typename BasicJsonType::string_t; + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + + const auto type = static_cast(j.type()); + switch (j.type()) + { + case BasicJsonType::value_t::null: + case BasicJsonType::value_t::discarded: + { + return combine(type, 0); + } + + case BasicJsonType::value_t::object: + { + auto seed = combine(type, j.size()); + for (const auto& element : j.items()) + { + const auto h = std::hash {}(element.key()); + seed = combine(seed, h); + seed = combine(seed, hash(element.value())); + } + return seed; + } + + case BasicJsonType::value_t::array: + { + auto seed = combine(type, j.size()); + for (const auto& element : j) + { + seed = combine(seed, hash(element)); + } + return seed; + } + + case BasicJsonType::value_t::string: + { + const auto h = std::hash {}(j.template get_ref()); + return combine(type, h); + } + + case BasicJsonType::value_t::boolean: + { + const auto h = std::hash {}(j.template get()); + return combine(type, h); + } + + case BasicJsonType::value_t::number_integer: + { + const auto h = std::hash {}(j.template get()); + return combine(type, h); + } + + case BasicJsonType::value_t::number_unsigned: + { + const auto h = std::hash {}(j.template get()); + return combine(type, h); + } + + case BasicJsonType::value_t::number_float: + { + const auto h = std::hash {}(j.template get()); + return combine(type, h); + } + + case BasicJsonType::value_t::binary: + { + auto seed = combine(type, j.get_binary().size()); + const auto h = std::hash {}(j.get_binary().has_subtype()); + seed = combine(seed, h); + seed = combine(seed, static_cast(j.get_binary().subtype())); + for (const auto byte : j.get_binary()) + { + seed = combine(seed, std::hash {}(byte)); + } + return seed; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + return 0; // LCOV_EXCL_LINE + } +} + +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // generate_n +#include // array +#include // ldexp +#include // size_t +#include // uint8_t, uint16_t, uint32_t, uint64_t +#include // snprintf +#include // memcpy +#include // back_inserter +#include // numeric_limits +#include // char_traits, string +#include // make_pair, move +#include // vector + +// #include + +// #include + + +#include // array +#include // size_t +#include // strlen +#include // begin, end, iterator_traits, random_access_iterator_tag, distance, next +#include // shared_ptr, make_shared, addressof +#include // accumulate +#include // string, char_traits +#include // enable_if, is_base_of, is_pointer, is_integral, remove_pointer +#include // pair, declval + +#ifndef JSON_NO_IO + #include // FILE * + #include // istream +#endif // JSON_NO_IO + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/// the supported input formats +enum class input_format_t { json, cbor, msgpack, ubjson, bson }; + +//////////////////// +// input adapters // +//////////////////// + +#ifndef JSON_NO_IO +/*! +Input adapter for stdio file access. This adapter read only 1 byte and do not use any + buffer. This adapter is a very low level adapter. +*/ +class file_input_adapter +{ + public: + using char_type = char; + + JSON_HEDLEY_NON_NULL(2) + explicit file_input_adapter(std::FILE* f) noexcept + : m_file(f) + {} + + // make class move-only + file_input_adapter(const file_input_adapter&) = delete; + file_input_adapter(file_input_adapter&&) noexcept = default; + file_input_adapter& operator=(const file_input_adapter&) = delete; + file_input_adapter& operator=(file_input_adapter&&) = delete; + ~file_input_adapter() = default; + + std::char_traits::int_type get_character() noexcept + { + return std::fgetc(m_file); + } + + private: + /// the file pointer to read from + std::FILE* m_file; +}; + + +/*! +Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at +beginning of input. Does not support changing the underlying std::streambuf +in mid-input. Maintains underlying std::istream and std::streambuf to support +subsequent use of standard std::istream operations to process any input +characters following those used in parsing the JSON input. Clears the +std::istream flags; any input errors (e.g., EOF) will be detected by the first +subsequent call for input from the std::istream. +*/ +class input_stream_adapter +{ + public: + using char_type = char; + + ~input_stream_adapter() + { + // clear stream flags; we use underlying streambuf I/O, do not + // maintain ifstream flags, except eof + if (is != nullptr) + { + is->clear(is->rdstate() & std::ios::eofbit); + } + } + + explicit input_stream_adapter(std::istream& i) + : is(&i), sb(i.rdbuf()) + {} + + // delete because of pointer members + input_stream_adapter(const input_stream_adapter&) = delete; + input_stream_adapter& operator=(input_stream_adapter&) = delete; + input_stream_adapter& operator=(input_stream_adapter&&) = delete; + + input_stream_adapter(input_stream_adapter&& rhs) noexcept + : is(rhs.is), sb(rhs.sb) + { + rhs.is = nullptr; + rhs.sb = nullptr; + } + + // std::istream/std::streambuf use std::char_traits::to_int_type, to + // ensure that std::char_traits::eof() and the character 0xFF do not + // end up as the same value, e.g. 0xFFFFFFFF. + std::char_traits::int_type get_character() + { + auto res = sb->sbumpc(); + // set eof manually, as we don't use the istream interface. + if (JSON_HEDLEY_UNLIKELY(res == std::char_traits::eof())) + { + is->clear(is->rdstate() | std::ios::eofbit); + } + return res; + } + + private: + /// the associated input stream + std::istream* is = nullptr; + std::streambuf* sb = nullptr; +}; +#endif // JSON_NO_IO + +// General-purpose iterator-based adapter. It might not be as fast as +// theoretically possible for some containers, but it is extremely versatile. +template +class iterator_input_adapter +{ + public: + using char_type = typename std::iterator_traits::value_type; + + iterator_input_adapter(IteratorType first, IteratorType last) + : current(std::move(first)), end(std::move(last)) + {} + + typename std::char_traits::int_type get_character() + { + if (JSON_HEDLEY_LIKELY(current != end)) + { + auto result = std::char_traits::to_int_type(*current); + std::advance(current, 1); + return result; + } + + return std::char_traits::eof(); + } + + private: + IteratorType current; + IteratorType end; + + template + friend struct wide_string_input_helper; + + bool empty() const + { + return current == end; + } +}; + + +template +struct wide_string_input_helper; + +template +struct wide_string_input_helper +{ + // UTF-32 + static void fill_buffer(BaseInputAdapter& input, + std::array::int_type, 4>& utf8_bytes, + size_t& utf8_bytes_index, + size_t& utf8_bytes_filled) + { + utf8_bytes_index = 0; + + if (JSON_HEDLEY_UNLIKELY(input.empty())) + { + utf8_bytes[0] = std::char_traits::eof(); + utf8_bytes_filled = 1; + } + else + { + // get the current character + const auto wc = input.get_character(); + + // UTF-32 to UTF-8 encoding + if (wc < 0x80) + { + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + else if (wc <= 0x7FF) + { + utf8_bytes[0] = static_cast::int_type>(0xC0u | ((static_cast(wc) >> 6u) & 0x1Fu)); + utf8_bytes[1] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 2; + } + else if (wc <= 0xFFFF) + { + utf8_bytes[0] = static_cast::int_type>(0xE0u | ((static_cast(wc) >> 12u) & 0x0Fu)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 3; + } + else if (wc <= 0x10FFFF) + { + utf8_bytes[0] = static_cast::int_type>(0xF0u | ((static_cast(wc) >> 18u) & 0x07u)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 12u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); + utf8_bytes[3] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 4; + } + else + { + // unknown character + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + } + } +}; + +template +struct wide_string_input_helper +{ + // UTF-16 + static void fill_buffer(BaseInputAdapter& input, + std::array::int_type, 4>& utf8_bytes, + size_t& utf8_bytes_index, + size_t& utf8_bytes_filled) + { + utf8_bytes_index = 0; + + if (JSON_HEDLEY_UNLIKELY(input.empty())) + { + utf8_bytes[0] = std::char_traits::eof(); + utf8_bytes_filled = 1; + } + else + { + // get the current character + const auto wc = input.get_character(); + + // UTF-16 to UTF-8 encoding + if (wc < 0x80) + { + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + else if (wc <= 0x7FF) + { + utf8_bytes[0] = static_cast::int_type>(0xC0u | ((static_cast(wc) >> 6u))); + utf8_bytes[1] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 2; + } + else if (0xD800 > wc || wc >= 0xE000) + { + utf8_bytes[0] = static_cast::int_type>(0xE0u | ((static_cast(wc) >> 12u))); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 3; + } + else + { + if (JSON_HEDLEY_UNLIKELY(!input.empty())) + { + const auto wc2 = static_cast(input.get_character()); + const auto charcode = 0x10000u + (((static_cast(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu)); + utf8_bytes[0] = static_cast::int_type>(0xF0u | (charcode >> 18u)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu)); + utf8_bytes[3] = static_cast::int_type>(0x80u | (charcode & 0x3Fu)); + utf8_bytes_filled = 4; + } + else + { + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + } + } + } +}; + +// Wraps another input apdater to convert wide character types into individual bytes. +template +class wide_string_input_adapter +{ + public: + using char_type = char; + + wide_string_input_adapter(BaseInputAdapter base) + : base_adapter(base) {} + + typename std::char_traits::int_type get_character() noexcept + { + // check if buffer needs to be filled + if (utf8_bytes_index == utf8_bytes_filled) + { + fill_buffer(); + + JSON_ASSERT(utf8_bytes_filled > 0); + JSON_ASSERT(utf8_bytes_index == 0); + } + + // use buffer + JSON_ASSERT(utf8_bytes_filled > 0); + JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled); + return utf8_bytes[utf8_bytes_index++]; + } + + private: + BaseInputAdapter base_adapter; + + template + void fill_buffer() + { + wide_string_input_helper::fill_buffer(base_adapter, utf8_bytes, utf8_bytes_index, utf8_bytes_filled); + } + + /// a buffer for UTF-8 bytes + std::array::int_type, 4> utf8_bytes = {{0, 0, 0, 0}}; + + /// index to the utf8_codes array for the next valid byte + std::size_t utf8_bytes_index = 0; + /// number of valid bytes in the utf8_codes array + std::size_t utf8_bytes_filled = 0; +}; + + +template +struct iterator_input_adapter_factory +{ + using iterator_type = IteratorType; + using char_type = typename std::iterator_traits::value_type; + using adapter_type = iterator_input_adapter; + + static adapter_type create(IteratorType first, IteratorType last) + { + return adapter_type(std::move(first), std::move(last)); + } +}; + +template +struct is_iterator_of_multibyte +{ + using value_type = typename std::iterator_traits::value_type; + enum + { + value = sizeof(value_type) > 1 + }; +}; + +template +struct iterator_input_adapter_factory::value>> +{ + using iterator_type = IteratorType; + using char_type = typename std::iterator_traits::value_type; + using base_adapter_type = iterator_input_adapter; + using adapter_type = wide_string_input_adapter; + + static adapter_type create(IteratorType first, IteratorType last) + { + return adapter_type(base_adapter_type(std::move(first), std::move(last))); + } +}; + +// General purpose iterator-based input +template +typename iterator_input_adapter_factory::adapter_type input_adapter(IteratorType first, IteratorType last) +{ + using factory_type = iterator_input_adapter_factory; + return factory_type::create(first, last); +} + +// Convenience shorthand from container to iterator +// Enables ADL on begin(container) and end(container) +// Encloses the using declarations in namespace for not to leak them to outside scope + +namespace container_input_adapter_factory_impl +{ + +using std::begin; +using std::end; + +template +struct container_input_adapter_factory {}; + +template +struct container_input_adapter_factory< ContainerType, + void_t()), end(std::declval()))>> + { + using adapter_type = decltype(input_adapter(begin(std::declval()), end(std::declval()))); + + static adapter_type create(const ContainerType& container) +{ + return input_adapter(begin(container), end(container)); +} + }; + +} // namespace container_input_adapter_factory_impl + +template +typename container_input_adapter_factory_impl::container_input_adapter_factory::adapter_type input_adapter(const ContainerType& container) +{ + return container_input_adapter_factory_impl::container_input_adapter_factory::create(container); +} + +#ifndef JSON_NO_IO +// Special cases with fast paths +inline file_input_adapter input_adapter(std::FILE* file) +{ + return file_input_adapter(file); +} + +inline input_stream_adapter input_adapter(std::istream& stream) +{ + return input_stream_adapter(stream); +} + +inline input_stream_adapter input_adapter(std::istream&& stream) +{ + return input_stream_adapter(stream); +} +#endif // JSON_NO_IO + +using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval(), std::declval())); + +// Null-delimited strings, and the like. +template < typename CharT, + typename std::enable_if < + std::is_pointer::value&& + !std::is_array::value&& + std::is_integral::type>::value&& + sizeof(typename std::remove_pointer::type) == 1, + int >::type = 0 > +contiguous_bytes_input_adapter input_adapter(CharT b) +{ + auto length = std::strlen(reinterpret_cast(b)); + const auto* ptr = reinterpret_cast(b); + return input_adapter(ptr, ptr + length); +} + +template +auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) +{ + return input_adapter(array, array + N); +} + +// This class only handles inputs of input_buffer_adapter type. +// It's required so that expressions like {ptr, len} can be implicitly cast +// to the correct adapter. +class span_input_adapter +{ + public: + template < typename CharT, + typename std::enable_if < + std::is_pointer::value&& + std::is_integral::type>::value&& + sizeof(typename std::remove_pointer::type) == 1, + int >::type = 0 > + span_input_adapter(CharT b, std::size_t l) + : ia(reinterpret_cast(b), reinterpret_cast(b) + l) {} + + template::iterator_category, std::random_access_iterator_tag>::value, + int>::type = 0> + span_input_adapter(IteratorType first, IteratorType last) + : ia(input_adapter(first, last)) {} + + contiguous_bytes_input_adapter&& get() + { + return std::move(ia); // NOLINT(hicpp-move-const-arg,performance-move-const-arg) + } + + private: + contiguous_bytes_input_adapter ia; +}; +} // namespace detail +} // namespace nlohmann + +// #include + + +#include +#include // string +#include // move +#include // vector + +// #include + +// #include + + +namespace nlohmann +{ + +/*! +@brief SAX interface + +This class describes the SAX interface used by @ref nlohmann::json::sax_parse. +Each function is called in different situations while the input is parsed. The +boolean return value informs the parser whether to continue processing the +input. +*/ +template +struct json_sax +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + + /*! + @brief a null value was read + @return whether parsing should proceed + */ + virtual bool null() = 0; + + /*! + @brief a boolean value was read + @param[in] val boolean value + @return whether parsing should proceed + */ + virtual bool boolean(bool val) = 0; + + /*! + @brief an integer number was read + @param[in] val integer value + @return whether parsing should proceed + */ + virtual bool number_integer(number_integer_t val) = 0; + + /*! + @brief an unsigned integer number was read + @param[in] val unsigned integer value + @return whether parsing should proceed + */ + virtual bool number_unsigned(number_unsigned_t val) = 0; + + /*! + @brief a floating-point number was read + @param[in] val floating-point value + @param[in] s raw token value + @return whether parsing should proceed + */ + virtual bool number_float(number_float_t val, const string_t& s) = 0; + + /*! + @brief a string value was read + @param[in] val string value + @return whether parsing should proceed + @note It is safe to move the passed string value. + */ + virtual bool string(string_t& val) = 0; + + /*! + @brief a binary value was read + @param[in] val binary value + @return whether parsing should proceed + @note It is safe to move the passed binary value. + */ + virtual bool binary(binary_t& val) = 0; + + /*! + @brief the beginning of an object was read + @param[in] elements number of object elements or -1 if unknown + @return whether parsing should proceed + @note binary formats may report the number of elements + */ + virtual bool start_object(std::size_t elements) = 0; + + /*! + @brief an object key was read + @param[in] val object key + @return whether parsing should proceed + @note It is safe to move the passed string. + */ + virtual bool key(string_t& val) = 0; + + /*! + @brief the end of an object was read + @return whether parsing should proceed + */ + virtual bool end_object() = 0; + + /*! + @brief the beginning of an array was read + @param[in] elements number of array elements or -1 if unknown + @return whether parsing should proceed + @note binary formats may report the number of elements + */ + virtual bool start_array(std::size_t elements) = 0; + + /*! + @brief the end of an array was read + @return whether parsing should proceed + */ + virtual bool end_array() = 0; + + /*! + @brief a parse error occurred + @param[in] position the position in the input where the error occurs + @param[in] last_token the last read token + @param[in] ex an exception object describing the error + @return whether parsing should proceed (must return false) + */ + virtual bool parse_error(std::size_t position, + const std::string& last_token, + const detail::exception& ex) = 0; + + json_sax() = default; + json_sax(const json_sax&) = default; + json_sax(json_sax&&) noexcept = default; + json_sax& operator=(const json_sax&) = default; + json_sax& operator=(json_sax&&) noexcept = default; + virtual ~json_sax() = default; +}; + + +namespace detail +{ +/*! +@brief SAX implementation to create a JSON value from SAX events + +This class implements the @ref json_sax interface and processes the SAX events +to create a JSON value which makes it basically a DOM parser. The structure or +hierarchy of the JSON value is managed by the stack `ref_stack` which contains +a pointer to the respective array or object for each recursion depth. + +After successful parsing, the value that is passed by reference to the +constructor contains the parsed value. + +@tparam BasicJsonType the JSON type +*/ +template +class json_sax_dom_parser +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + + /*! + @param[in,out] r reference to a JSON value that is manipulated while + parsing + @param[in] allow_exceptions_ whether parse errors yield exceptions + */ + explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true) + : root(r), allow_exceptions(allow_exceptions_) + {} + + // make class move-only + json_sax_dom_parser(const json_sax_dom_parser&) = delete; + json_sax_dom_parser(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete; + json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + ~json_sax_dom_parser() = default; + + bool null() + { + handle_value(nullptr); + return true; + } + + bool boolean(bool val) + { + handle_value(val); + return true; + } + + bool number_integer(number_integer_t val) + { + handle_value(val); + return true; + } + + bool number_unsigned(number_unsigned_t val) + { + handle_value(val); + return true; + } + + bool number_float(number_float_t val, const string_t& /*unused*/) + { + handle_value(val); + return true; + } + + bool string(string_t& val) + { + handle_value(val); + return true; + } + + bool binary(binary_t& val) + { + handle_value(std::move(val)); + return true; + } + + bool start_object(std::size_t len) + { + ref_stack.push_back(handle_value(BasicJsonType::value_t::object)); + + if (JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), *ref_stack.back())); + } + + return true; + } + + bool key(string_t& val) + { + // add null at given key and store the reference for later + object_element = &(ref_stack.back()->m_value.object->operator[](val)); + return true; + } + + bool end_object() + { + ref_stack.back()->set_parents(); + ref_stack.pop_back(); + return true; + } + + bool start_array(std::size_t len) + { + ref_stack.push_back(handle_value(BasicJsonType::value_t::array)); + + if (JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), *ref_stack.back())); + } + + return true; + } + + bool end_array() + { + ref_stack.back()->set_parents(); + ref_stack.pop_back(); + return true; + } + + template + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, + const Exception& ex) + { + errored = true; + static_cast(ex); + if (allow_exceptions) + { + JSON_THROW(ex); + } + return false; + } + + constexpr bool is_errored() const + { + return errored; + } + + private: + /*! + @invariant If the ref stack is empty, then the passed value will be the new + root. + @invariant If the ref stack contains a value, then it is an array or an + object to which we can add elements + */ + template + JSON_HEDLEY_RETURNS_NON_NULL + BasicJsonType* handle_value(Value&& v) + { + if (ref_stack.empty()) + { + root = BasicJsonType(std::forward(v)); + return &root; + } + + JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object()); + + if (ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->emplace_back(std::forward(v)); + return &(ref_stack.back()->m_value.array->back()); + } + + JSON_ASSERT(ref_stack.back()->is_object()); + JSON_ASSERT(object_element); + *object_element = BasicJsonType(std::forward(v)); + return object_element; + } + + /// the parsed JSON value + BasicJsonType& root; + /// stack to model hierarchy of values + std::vector ref_stack {}; + /// helper to hold the reference for the next object element + BasicJsonType* object_element = nullptr; + /// whether a syntax error occurred + bool errored = false; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; +}; + +template +class json_sax_dom_callback_parser +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using parser_callback_t = typename BasicJsonType::parser_callback_t; + using parse_event_t = typename BasicJsonType::parse_event_t; + + json_sax_dom_callback_parser(BasicJsonType& r, + const parser_callback_t cb, + const bool allow_exceptions_ = true) + : root(r), callback(cb), allow_exceptions(allow_exceptions_) + { + keep_stack.push_back(true); + } + + // make class move-only + json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete; + json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete; + json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + ~json_sax_dom_callback_parser() = default; + + bool null() + { + handle_value(nullptr); + return true; + } + + bool boolean(bool val) + { + handle_value(val); + return true; + } + + bool number_integer(number_integer_t val) + { + handle_value(val); + return true; + } + + bool number_unsigned(number_unsigned_t val) + { + handle_value(val); + return true; + } + + bool number_float(number_float_t val, const string_t& /*unused*/) + { + handle_value(val); + return true; + } + + bool string(string_t& val) + { + handle_value(val); + return true; + } + + bool binary(binary_t& val) + { + handle_value(std::move(val)); + return true; + } + + bool start_object(std::size_t len) + { + // check callback for object start + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::object_start, discarded); + keep_stack.push_back(keep); + + auto val = handle_value(BasicJsonType::value_t::object, true); + ref_stack.push_back(val.second); + + // check object limit + if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), *ref_stack.back())); + } + + return true; + } + + bool key(string_t& val) + { + BasicJsonType k = BasicJsonType(val); + + // check callback for key + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::key, k); + key_keep_stack.push_back(keep); + + // add discarded value at given key and store the reference for later + if (keep && ref_stack.back()) + { + object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded); + } + + return true; + } + + bool end_object() + { + if (ref_stack.back()) + { + if (!callback(static_cast(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back())) + { + // discard object + *ref_stack.back() = discarded; + } + else + { + ref_stack.back()->set_parents(); + } + } + + JSON_ASSERT(!ref_stack.empty()); + JSON_ASSERT(!keep_stack.empty()); + ref_stack.pop_back(); + keep_stack.pop_back(); + + if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured()) + { + // remove discarded value + for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it) + { + if (it->is_discarded()) + { + ref_stack.back()->erase(it); + break; + } + } + } + + return true; + } + + bool start_array(std::size_t len) + { + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::array_start, discarded); + keep_stack.push_back(keep); + + auto val = handle_value(BasicJsonType::value_t::array, true); + ref_stack.push_back(val.second); + + // check array limit + if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), *ref_stack.back())); + } + + return true; + } + + bool end_array() + { + bool keep = true; + + if (ref_stack.back()) + { + keep = callback(static_cast(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back()); + if (keep) + { + ref_stack.back()->set_parents(); + } + else + { + // discard array + *ref_stack.back() = discarded; + } + } + + JSON_ASSERT(!ref_stack.empty()); + JSON_ASSERT(!keep_stack.empty()); + ref_stack.pop_back(); + keep_stack.pop_back(); + + // remove discarded value + if (!keep && !ref_stack.empty() && ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->pop_back(); + } + + return true; + } + + template + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, + const Exception& ex) + { + errored = true; + static_cast(ex); + if (allow_exceptions) + { + JSON_THROW(ex); + } + return false; + } + + constexpr bool is_errored() const + { + return errored; + } + + private: + /*! + @param[in] v value to add to the JSON value we build during parsing + @param[in] skip_callback whether we should skip calling the callback + function; this is required after start_array() and + start_object() SAX events, because otherwise we would call the + callback function with an empty array or object, respectively. + + @invariant If the ref stack is empty, then the passed value will be the new + root. + @invariant If the ref stack contains a value, then it is an array or an + object to which we can add elements + + @return pair of boolean (whether value should be kept) and pointer (to the + passed value in the ref_stack hierarchy; nullptr if not kept) + */ + template + std::pair handle_value(Value&& v, const bool skip_callback = false) + { + JSON_ASSERT(!keep_stack.empty()); + + // do not handle this value if we know it would be added to a discarded + // container + if (!keep_stack.back()) + { + return {false, nullptr}; + } + + // create value + auto value = BasicJsonType(std::forward(v)); + + // check callback + const bool keep = skip_callback || callback(static_cast(ref_stack.size()), parse_event_t::value, value); + + // do not handle this value if we just learnt it shall be discarded + if (!keep) + { + return {false, nullptr}; + } + + if (ref_stack.empty()) + { + root = std::move(value); + return {true, &root}; + } + + // skip this value if we already decided to skip the parent + // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360) + if (!ref_stack.back()) + { + return {false, nullptr}; + } + + // we now only expect arrays and objects + JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object()); + + // array + if (ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->emplace_back(std::move(value)); + return {true, &(ref_stack.back()->m_value.array->back())}; + } + + // object + JSON_ASSERT(ref_stack.back()->is_object()); + // check if we should store an element for the current key + JSON_ASSERT(!key_keep_stack.empty()); + const bool store_element = key_keep_stack.back(); + key_keep_stack.pop_back(); + + if (!store_element) + { + return {false, nullptr}; + } + + JSON_ASSERT(object_element); + *object_element = std::move(value); + return {true, object_element}; + } + + /// the parsed JSON value + BasicJsonType& root; + /// stack to model hierarchy of values + std::vector ref_stack {}; + /// stack to manage which values to keep + std::vector keep_stack {}; + /// stack to manage which object keys to keep + std::vector key_keep_stack {}; + /// helper to hold the reference for the next object element + BasicJsonType* object_element = nullptr; + /// whether a syntax error occurred + bool errored = false; + /// callback function + const parser_callback_t callback = nullptr; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; + /// a discarded value for the callback + BasicJsonType discarded = BasicJsonType::value_t::discarded; +}; + +template +class json_sax_acceptor +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + + bool null() + { + return true; + } + + bool boolean(bool /*unused*/) + { + return true; + } + + bool number_integer(number_integer_t /*unused*/) + { + return true; + } + + bool number_unsigned(number_unsigned_t /*unused*/) + { + return true; + } + + bool number_float(number_float_t /*unused*/, const string_t& /*unused*/) + { + return true; + } + + bool string(string_t& /*unused*/) + { + return true; + } + + bool binary(binary_t& /*unused*/) + { + return true; + } + + bool start_object(std::size_t /*unused*/ = static_cast(-1)) + { + return true; + } + + bool key(string_t& /*unused*/) + { + return true; + } + + bool end_object() + { + return true; + } + + bool start_array(std::size_t /*unused*/ = static_cast(-1)) + { + return true; + } + + bool end_array() + { + return true; + } + + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/) + { + return false; + } +}; +} // namespace detail + +} // namespace nlohmann + +// #include + + +#include // array +#include // localeconv +#include // size_t +#include // snprintf +#include // strtof, strtod, strtold, strtoll, strtoull +#include // initializer_list +#include // char_traits, string +#include // move +#include // vector + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/////////// +// lexer // +/////////// + +template +class lexer_base +{ + public: + /// token types for the parser + enum class token_type + { + uninitialized, ///< indicating the scanner is uninitialized + literal_true, ///< the `true` literal + literal_false, ///< the `false` literal + literal_null, ///< the `null` literal + value_string, ///< a string -- use get_string() for actual value + value_unsigned, ///< an unsigned integer -- use get_number_unsigned() for actual value + value_integer, ///< a signed integer -- use get_number_integer() for actual value + value_float, ///< an floating point number -- use get_number_float() for actual value + begin_array, ///< the character for array begin `[` + begin_object, ///< the character for object begin `{` + end_array, ///< the character for array end `]` + end_object, ///< the character for object end `}` + name_separator, ///< the name separator `:` + value_separator, ///< the value separator `,` + parse_error, ///< indicating a parse error + end_of_input, ///< indicating the end of the input buffer + literal_or_value ///< a literal or the begin of a value (only for diagnostics) + }; + + /// return name of values of type token_type (only used for errors) + JSON_HEDLEY_RETURNS_NON_NULL + JSON_HEDLEY_CONST + static const char* token_type_name(const token_type t) noexcept + { + switch (t) + { + case token_type::uninitialized: + return ""; + case token_type::literal_true: + return "true literal"; + case token_type::literal_false: + return "false literal"; + case token_type::literal_null: + return "null literal"; + case token_type::value_string: + return "string literal"; + case token_type::value_unsigned: + case token_type::value_integer: + case token_type::value_float: + return "number literal"; + case token_type::begin_array: + return "'['"; + case token_type::begin_object: + return "'{'"; + case token_type::end_array: + return "']'"; + case token_type::end_object: + return "'}'"; + case token_type::name_separator: + return "':'"; + case token_type::value_separator: + return "','"; + case token_type::parse_error: + return ""; + case token_type::end_of_input: + return "end of input"; + case token_type::literal_or_value: + return "'[', '{', or a literal"; + // LCOV_EXCL_START + default: // catch non-enum values + return "unknown token"; + // LCOV_EXCL_STOP + } + } +}; +/*! +@brief lexical analysis + +This class organizes the lexical analysis during JSON deserialization. +*/ +template +class lexer : public lexer_base +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using char_type = typename InputAdapterType::char_type; + using char_int_type = typename std::char_traits::int_type; + + public: + using token_type = typename lexer_base::token_type; + + explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) noexcept + : ia(std::move(adapter)) + , ignore_comments(ignore_comments_) + , decimal_point_char(static_cast(get_decimal_point())) + {} + + // delete because of pointer members + lexer(const lexer&) = delete; + lexer(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + lexer& operator=(lexer&) = delete; + lexer& operator=(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + ~lexer() = default; + + private: + ///////////////////// + // locales + ///////////////////// + + /// return the locale-dependent decimal point + JSON_HEDLEY_PURE + static char get_decimal_point() noexcept + { + const auto* loc = localeconv(); + JSON_ASSERT(loc != nullptr); + return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); + } + + ///////////////////// + // scan functions + ///////////////////// + + /*! + @brief get codepoint from 4 hex characters following `\u` + + For input "\u c1 c2 c3 c4" the codepoint is: + (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4 + = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0) + + Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f' + must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The + conversion is done by subtracting the offset (0x30, 0x37, and 0x57) + between the ASCII value of the character and the desired integer value. + + @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or + non-hex character) + */ + int get_codepoint() + { + // this function only makes sense after reading `\u` + JSON_ASSERT(current == 'u'); + int codepoint = 0; + + const auto factors = { 12u, 8u, 4u, 0u }; + for (const auto factor : factors) + { + get(); + + if (current >= '0' && current <= '9') + { + codepoint += static_cast((static_cast(current) - 0x30u) << factor); + } + else if (current >= 'A' && current <= 'F') + { + codepoint += static_cast((static_cast(current) - 0x37u) << factor); + } + else if (current >= 'a' && current <= 'f') + { + codepoint += static_cast((static_cast(current) - 0x57u) << factor); + } + else + { + return -1; + } + } + + JSON_ASSERT(0x0000 <= codepoint && codepoint <= 0xFFFF); + return codepoint; + } + + /*! + @brief check if the next byte(s) are inside a given range + + Adds the current byte and, for each passed range, reads a new byte and + checks if it is inside the range. If a violation was detected, set up an + error message and return false. Otherwise, return true. + + @param[in] ranges list of integers; interpreted as list of pairs of + inclusive lower and upper bound, respectively + + @pre The passed list @a ranges must have 2, 4, or 6 elements; that is, + 1, 2, or 3 pairs. This precondition is enforced by an assertion. + + @return true if and only if no range violation was detected + */ + bool next_byte_in_range(std::initializer_list ranges) + { + JSON_ASSERT(ranges.size() == 2 || ranges.size() == 4 || ranges.size() == 6); + add(current); + + for (auto range = ranges.begin(); range != ranges.end(); ++range) + { + get(); + if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range))) + { + add(current); + } + else + { + error_message = "invalid string: ill-formed UTF-8 byte"; + return false; + } + } + + return true; + } + + /*! + @brief scan a string literal + + This function scans a string according to Sect. 7 of RFC 8259. While + scanning, bytes are escaped and copied into buffer token_buffer. Then the + function returns successfully, token_buffer is *not* null-terminated (as it + may contain \0 bytes), and token_buffer.size() is the number of bytes in the + string. + + @return token_type::value_string if string could be successfully scanned, + token_type::parse_error otherwise + + @note In case of errors, variable error_message contains a textual + description. + */ + token_type scan_string() + { + // reset token_buffer (ignore opening quote) + reset(); + + // we entered the function by reading an open quote + JSON_ASSERT(current == '\"'); + + while (true) + { + // get next character + switch (get()) + { + // end of file while parsing string + case std::char_traits::eof(): + { + error_message = "invalid string: missing closing quote"; + return token_type::parse_error; + } + + // closing quote + case '\"': + { + return token_type::value_string; + } + + // escapes + case '\\': + { + switch (get()) + { + // quotation mark + case '\"': + add('\"'); + break; + // reverse solidus + case '\\': + add('\\'); + break; + // solidus + case '/': + add('/'); + break; + // backspace + case 'b': + add('\b'); + break; + // form feed + case 'f': + add('\f'); + break; + // line feed + case 'n': + add('\n'); + break; + // carriage return + case 'r': + add('\r'); + break; + // tab + case 't': + add('\t'); + break; + + // unicode escapes + case 'u': + { + const int codepoint1 = get_codepoint(); + int codepoint = codepoint1; // start with codepoint1 + + if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1)) + { + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } + + // check if code point is a high surrogate + if (0xD800 <= codepoint1 && codepoint1 <= 0xDBFF) + { + // expect next \uxxxx entry + if (JSON_HEDLEY_LIKELY(get() == '\\' && get() == 'u')) + { + const int codepoint2 = get_codepoint(); + + if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1)) + { + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } + + // check if codepoint2 is a low surrogate + if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 && codepoint2 <= 0xDFFF)) + { + // overwrite codepoint + codepoint = static_cast( + // high surrogate occupies the most significant 22 bits + (static_cast(codepoint1) << 10u) + // low surrogate occupies the least significant 15 bits + + static_cast(codepoint2) + // there is still the 0xD800, 0xDC00 and 0x10000 noise + // in the result, so we have to subtract with: + // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 + - 0x35FDC00u); + } + else + { + error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } + else + { + error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 && codepoint1 <= 0xDFFF)) + { + error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF"; + return token_type::parse_error; + } + } + + // result of the above calculation yields a proper codepoint + JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF); + + // translate codepoint into bytes + if (codepoint < 0x80) + { + // 1-byte characters: 0xxxxxxx (ASCII) + add(static_cast(codepoint)); + } + else if (codepoint <= 0x7FF) + { + // 2-byte characters: 110xxxxx 10xxxxxx + add(static_cast(0xC0u | (static_cast(codepoint) >> 6u))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + else if (codepoint <= 0xFFFF) + { + // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx + add(static_cast(0xE0u | (static_cast(codepoint) >> 12u))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + else + { + // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + add(static_cast(0xF0u | (static_cast(codepoint) >> 18u))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 12u) & 0x3Fu))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + + break; + } + + // other characters after escape + default: + error_message = "invalid string: forbidden character after backslash"; + return token_type::parse_error; + } + + break; + } + + // invalid control characters + case 0x00: + { + error_message = "invalid string: control character U+0000 (NUL) must be escaped to \\u0000"; + return token_type::parse_error; + } + + case 0x01: + { + error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001"; + return token_type::parse_error; + } + + case 0x02: + { + error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002"; + return token_type::parse_error; + } + + case 0x03: + { + error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003"; + return token_type::parse_error; + } + + case 0x04: + { + error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004"; + return token_type::parse_error; + } + + case 0x05: + { + error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005"; + return token_type::parse_error; + } + + case 0x06: + { + error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006"; + return token_type::parse_error; + } + + case 0x07: + { + error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007"; + return token_type::parse_error; + } + + case 0x08: + { + error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b"; + return token_type::parse_error; + } + + case 0x09: + { + error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t"; + return token_type::parse_error; + } + + case 0x0A: + { + error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n"; + return token_type::parse_error; + } + + case 0x0B: + { + error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B"; + return token_type::parse_error; + } + + case 0x0C: + { + error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f"; + return token_type::parse_error; + } + + case 0x0D: + { + error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r"; + return token_type::parse_error; + } + + case 0x0E: + { + error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E"; + return token_type::parse_error; + } + + case 0x0F: + { + error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F"; + return token_type::parse_error; + } + + case 0x10: + { + error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010"; + return token_type::parse_error; + } + + case 0x11: + { + error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011"; + return token_type::parse_error; + } + + case 0x12: + { + error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012"; + return token_type::parse_error; + } + + case 0x13: + { + error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013"; + return token_type::parse_error; + } + + case 0x14: + { + error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014"; + return token_type::parse_error; + } + + case 0x15: + { + error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015"; + return token_type::parse_error; + } + + case 0x16: + { + error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016"; + return token_type::parse_error; + } + + case 0x17: + { + error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017"; + return token_type::parse_error; + } + + case 0x18: + { + error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018"; + return token_type::parse_error; + } + + case 0x19: + { + error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019"; + return token_type::parse_error; + } + + case 0x1A: + { + error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A"; + return token_type::parse_error; + } + + case 0x1B: + { + error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B"; + return token_type::parse_error; + } + + case 0x1C: + { + error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C"; + return token_type::parse_error; + } + + case 0x1D: + { + error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D"; + return token_type::parse_error; + } + + case 0x1E: + { + error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E"; + return token_type::parse_error; + } + + case 0x1F: + { + error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F"; + return token_type::parse_error; + } + + // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace)) + case 0x20: + case 0x21: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + { + add(current); + break; + } + + // U+0080..U+07FF: bytes C2..DF 80..BF + case 0xC2: + case 0xC3: + case 0xC4: + case 0xC5: + case 0xC6: + case 0xC7: + case 0xC8: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCC: + case 0xCD: + case 0xCE: + case 0xCF: + case 0xD0: + case 0xD1: + case 0xD2: + case 0xD3: + case 0xD4: + case 0xD5: + case 0xD6: + case 0xD7: + case 0xD8: + case 0xD9: + case 0xDA: + case 0xDB: + case 0xDC: + case 0xDD: + case 0xDE: + case 0xDF: + { + if (JSON_HEDLEY_UNLIKELY(!next_byte_in_range({0x80, 0xBF}))) + { + return token_type::parse_error; + } + break; + } + + // U+0800..U+0FFF: bytes E0 A0..BF 80..BF + case 0xE0: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF + // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF + case 0xE1: + case 0xE2: + case 0xE3: + case 0xE4: + case 0xE5: + case 0xE6: + case 0xE7: + case 0xE8: + case 0xE9: + case 0xEA: + case 0xEB: + case 0xEC: + case 0xEE: + case 0xEF: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+D000..U+D7FF: bytes ED 80..9F 80..BF + case 0xED: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x9F, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF + case 0xF0: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF + case 0xF1: + case 0xF2: + case 0xF3: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF + case 0xF4: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // remaining bytes (80..C1 and F5..FF) are ill-formed + default: + { + error_message = "invalid string: ill-formed UTF-8 byte"; + return token_type::parse_error; + } + } + } + } + + /*! + * @brief scan a comment + * @return whether comment could be scanned successfully + */ + bool scan_comment() + { + switch (get()) + { + // single-line comments skip input until a newline or EOF is read + case '/': + { + while (true) + { + switch (get()) + { + case '\n': + case '\r': + case std::char_traits::eof(): + case '\0': + return true; + + default: + break; + } + } + } + + // multi-line comments skip input until */ is read + case '*': + { + while (true) + { + switch (get()) + { + case std::char_traits::eof(): + case '\0': + { + error_message = "invalid comment; missing closing '*/'"; + return false; + } + + case '*': + { + switch (get()) + { + case '/': + return true; + + default: + { + unget(); + continue; + } + } + } + + default: + continue; + } + } + } + + // unexpected character after reading '/' + default: + { + error_message = "invalid comment; expecting '/' or '*' after '/'"; + return false; + } + } + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(float& f, const char* str, char** endptr) noexcept + { + f = std::strtof(str, endptr); + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(double& f, const char* str, char** endptr) noexcept + { + f = std::strtod(str, endptr); + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(long double& f, const char* str, char** endptr) noexcept + { + f = std::strtold(str, endptr); + } + + /*! + @brief scan a number literal + + This function scans a string according to Sect. 6 of RFC 8259. + + The function is realized with a deterministic finite state machine derived + from the grammar described in RFC 8259. Starting in state "init", the + input is read and used to determined the next state. Only state "done" + accepts the number. State "error" is a trap state to model errors. In the + table below, "anything" means any character but the ones listed before. + + state | 0 | 1-9 | e E | + | - | . | anything + ---------|----------|----------|----------|---------|---------|----------|----------- + init | zero | any1 | [error] | [error] | minus | [error] | [error] + minus | zero | any1 | [error] | [error] | [error] | [error] | [error] + zero | done | done | exponent | done | done | decimal1 | done + any1 | any1 | any1 | exponent | done | done | decimal1 | done + decimal1 | decimal2 | decimal2 | [error] | [error] | [error] | [error] | [error] + decimal2 | decimal2 | decimal2 | exponent | done | done | done | done + exponent | any2 | any2 | [error] | sign | sign | [error] | [error] + sign | any2 | any2 | [error] | [error] | [error] | [error] | [error] + any2 | any2 | any2 | done | done | done | done | done + + The state machine is realized with one label per state (prefixed with + "scan_number_") and `goto` statements between them. The state machine + contains cycles, but any cycle can be left when EOF is read. Therefore, + the function is guaranteed to terminate. + + During scanning, the read bytes are stored in token_buffer. This string is + then converted to a signed integer, an unsigned integer, or a + floating-point number. + + @return token_type::value_unsigned, token_type::value_integer, or + token_type::value_float if number could be successfully scanned, + token_type::parse_error otherwise + + @note The scanner is independent of the current locale. Internally, the + locale's decimal point is used instead of `.` to work with the + locale-dependent converters. + */ + token_type scan_number() // lgtm [cpp/use-of-goto] + { + // reset token_buffer to store the number's bytes + reset(); + + // the type of the parsed number; initially set to unsigned; will be + // changed if minus sign, decimal point or exponent is read + token_type number_type = token_type::value_unsigned; + + // state (init): we just found out we need to scan a number + switch (current) + { + case '-': + { + add(current); + goto scan_number_minus; + } + + case '0': + { + add(current); + goto scan_number_zero; + } + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + // all other characters are rejected outside scan_number() + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + +scan_number_minus: + // state: we just parsed a leading minus sign + number_type = token_type::value_integer; + switch (get()) + { + case '0': + { + add(current); + goto scan_number_zero; + } + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + default: + { + error_message = "invalid number; expected digit after '-'"; + return token_type::parse_error; + } + } + +scan_number_zero: + // state: we just parse a zero (maybe with a leading minus sign) + switch (get()) + { + case '.': + { + add(decimal_point_char); + goto scan_number_decimal1; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_any1: + // state: we just parsed a number 0-9 (maybe with a leading minus sign) + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + case '.': + { + add(decimal_point_char); + goto scan_number_decimal1; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_decimal1: + // state: we just parsed a decimal point + number_type = token_type::value_float; + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_decimal2; + } + + default: + { + error_message = "invalid number; expected digit after '.'"; + return token_type::parse_error; + } + } + +scan_number_decimal2: + // we just parsed at least one number after a decimal point + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_decimal2; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_exponent: + // we just parsed an exponent + number_type = token_type::value_float; + switch (get()) + { + case '+': + case '-': + { + add(current); + goto scan_number_sign; + } + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + { + error_message = + "invalid number; expected '+', '-', or digit after exponent"; + return token_type::parse_error; + } + } + +scan_number_sign: + // we just parsed an exponent sign + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + { + error_message = "invalid number; expected digit after exponent sign"; + return token_type::parse_error; + } + } + +scan_number_any2: + // we just parsed a number after the exponent or exponent sign + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + goto scan_number_done; + } + +scan_number_done: + // unget the character after the number (we only read it to know that + // we are done scanning a number) + unget(); + + char* endptr = nullptr; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + errno = 0; + + // try to parse integers first and fall back to floats + if (number_type == token_type::value_unsigned) + { + const auto x = std::strtoull(token_buffer.data(), &endptr, 10); + + // we checked the number format before + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); + + if (errno == 0) + { + value_unsigned = static_cast(x); + if (value_unsigned == x) + { + return token_type::value_unsigned; + } + } + } + else if (number_type == token_type::value_integer) + { + const auto x = std::strtoll(token_buffer.data(), &endptr, 10); + + // we checked the number format before + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); + + if (errno == 0) + { + value_integer = static_cast(x); + if (value_integer == x) + { + return token_type::value_integer; + } + } + } + + // this code is reached if we parse a floating-point number or if an + // integer conversion above failed + strtof(value_float, token_buffer.data(), &endptr); + + // we checked the number format before + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); + + return token_type::value_float; + } + + /*! + @param[in] literal_text the literal text to expect + @param[in] length the length of the passed literal text + @param[in] return_type the token type to return on success + */ + JSON_HEDLEY_NON_NULL(2) + token_type scan_literal(const char_type* literal_text, const std::size_t length, + token_type return_type) + { + JSON_ASSERT(std::char_traits::to_char_type(current) == literal_text[0]); + for (std::size_t i = 1; i < length; ++i) + { + if (JSON_HEDLEY_UNLIKELY(std::char_traits::to_char_type(get()) != literal_text[i])) + { + error_message = "invalid literal"; + return token_type::parse_error; + } + } + return return_type; + } + + ///////////////////// + // input management + ///////////////////// + + /// reset token_buffer; current character is beginning of token + void reset() noexcept + { + token_buffer.clear(); + token_string.clear(); + token_string.push_back(std::char_traits::to_char_type(current)); + } + + /* + @brief get next character from the input + + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns a + `std::char_traits::eof()` in that case. Stores the scanned characters + for use in error messages. + + @return character read from the input + */ + char_int_type get() + { + ++position.chars_read_total; + ++position.chars_read_current_line; + + if (next_unget) + { + // just reset the next_unget variable and work with current + next_unget = false; + } + else + { + current = ia.get_character(); + } + + if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) + { + token_string.push_back(std::char_traits::to_char_type(current)); + } + + if (current == '\n') + { + ++position.lines_read; + position.chars_read_current_line = 0; + } + + return current; + } + + /*! + @brief unget current character (read it again on next get) + + We implement unget by setting variable next_unget to true. The input is not + changed - we just simulate ungetting by modifying chars_read_total, + chars_read_current_line, and token_string. The next call to get() will + behave as if the unget character is read again. + */ + void unget() + { + next_unget = true; + + --position.chars_read_total; + + // in case we "unget" a newline, we have to also decrement the lines_read + if (position.chars_read_current_line == 0) + { + if (position.lines_read > 0) + { + --position.lines_read; + } + } + else + { + --position.chars_read_current_line; + } + + if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) + { + JSON_ASSERT(!token_string.empty()); + token_string.pop_back(); + } + } + + /// add a character to token_buffer + void add(char_int_type c) + { + token_buffer.push_back(static_cast(c)); + } + + public: + ///////////////////// + // value getters + ///////////////////// + + /// return integer value + constexpr number_integer_t get_number_integer() const noexcept + { + return value_integer; + } + + /// return unsigned integer value + constexpr number_unsigned_t get_number_unsigned() const noexcept + { + return value_unsigned; + } + + /// return floating-point value + constexpr number_float_t get_number_float() const noexcept + { + return value_float; + } + + /// return current string value (implicitly resets the token; useful only once) + string_t& get_string() + { + return token_buffer; + } + + ///////////////////// + // diagnostics + ///////////////////// + + /// return position of last read token + constexpr position_t get_position() const noexcept + { + return position; + } + + /// return the last read token (for errors only). Will never contain EOF + /// (an arbitrary value that is not a valid char value, often -1), because + /// 255 may legitimately occur. May contain NUL, which should be escaped. + std::string get_token_string() const + { + // escape control characters + std::string result; + for (const auto c : token_string) + { + if (static_cast(c) <= '\x1F') + { + // escape control characters + std::array cs{{}}; + static_cast((std::snprintf)(cs.data(), cs.size(), "", static_cast(c))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + result += cs.data(); + } + else + { + // add character as is + result.push_back(static_cast(c)); + } + } + + return result; + } + + /// return syntax error message + JSON_HEDLEY_RETURNS_NON_NULL + constexpr const char* get_error_message() const noexcept + { + return error_message; + } + + ///////////////////// + // actual scanner + ///////////////////// + + /*! + @brief skip the UTF-8 byte order mark + @return true iff there is no BOM or the correct BOM has been skipped + */ + bool skip_bom() + { + if (get() == 0xEF) + { + // check if we completely parse the BOM + return get() == 0xBB && get() == 0xBF; + } + + // the first character is not the beginning of the BOM; unget it to + // process is later + unget(); + return true; + } + + void skip_whitespace() + { + do + { + get(); + } + while (current == ' ' || current == '\t' || current == '\n' || current == '\r'); + } + + token_type scan() + { + // initially, skip the BOM + if (position.chars_read_total == 0 && !skip_bom()) + { + error_message = "invalid BOM; must be 0xEF 0xBB 0xBF if given"; + return token_type::parse_error; + } + + // read next character and ignore whitespace + skip_whitespace(); + + // ignore comments + while (ignore_comments && current == '/') + { + if (!scan_comment()) + { + return token_type::parse_error; + } + + // skip following whitespace + skip_whitespace(); + } + + switch (current) + { + // structural characters + case '[': + return token_type::begin_array; + case ']': + return token_type::end_array; + case '{': + return token_type::begin_object; + case '}': + return token_type::end_object; + case ':': + return token_type::name_separator; + case ',': + return token_type::value_separator; + + // literals + case 't': + { + std::array true_literal = {{static_cast('t'), static_cast('r'), static_cast('u'), static_cast('e')}}; + return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true); + } + case 'f': + { + std::array false_literal = {{static_cast('f'), static_cast('a'), static_cast('l'), static_cast('s'), static_cast('e')}}; + return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false); + } + case 'n': + { + std::array null_literal = {{static_cast('n'), static_cast('u'), static_cast('l'), static_cast('l')}}; + return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null); + } + + // string + case '\"': + return scan_string(); + + // number + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return scan_number(); + + // end of input (the null byte is needed when parsing from + // string literals) + case '\0': + case std::char_traits::eof(): + return token_type::end_of_input; + + // error + default: + error_message = "invalid literal"; + return token_type::parse_error; + } + } + + private: + /// input adapter + InputAdapterType ia; + + /// whether comments should be ignored (true) or signaled as errors (false) + const bool ignore_comments = false; + + /// the current character + char_int_type current = std::char_traits::eof(); + + /// whether the next get() call should just return current + bool next_unget = false; + + /// the start position of the current token + position_t position {}; + + /// raw input token string (for error messages) + std::vector token_string {}; + + /// buffer for variable-length tokens (numbers, strings) + string_t token_buffer {}; + + /// a description of occurred lexer errors + const char* error_message = ""; + + // number values + number_integer_t value_integer = 0; + number_unsigned_t value_unsigned = 0; + number_float_t value_float = 0; + + /// the decimal point + const char_int_type decimal_point_char = '.'; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // size_t +#include // declval +#include // string + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +using null_function_t = decltype(std::declval().null()); + +template +using boolean_function_t = + decltype(std::declval().boolean(std::declval())); + +template +using number_integer_function_t = + decltype(std::declval().number_integer(std::declval())); + +template +using number_unsigned_function_t = + decltype(std::declval().number_unsigned(std::declval())); + +template +using number_float_function_t = decltype(std::declval().number_float( + std::declval(), std::declval())); + +template +using string_function_t = + decltype(std::declval().string(std::declval())); + +template +using binary_function_t = + decltype(std::declval().binary(std::declval())); + +template +using start_object_function_t = + decltype(std::declval().start_object(std::declval())); + +template +using key_function_t = + decltype(std::declval().key(std::declval())); + +template +using end_object_function_t = decltype(std::declval().end_object()); + +template +using start_array_function_t = + decltype(std::declval().start_array(std::declval())); + +template +using end_array_function_t = decltype(std::declval().end_array()); + +template +using parse_error_function_t = decltype(std::declval().parse_error( + std::declval(), std::declval(), + std::declval())); + +template +struct is_sax +{ + private: + static_assert(is_basic_json::value, + "BasicJsonType must be of type basic_json<...>"); + + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using exception_t = typename BasicJsonType::exception; + + public: + static constexpr bool value = + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value; +}; + +template +struct is_sax_static_asserts +{ + private: + static_assert(is_basic_json::value, + "BasicJsonType must be of type basic_json<...>"); + + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using exception_t = typename BasicJsonType::exception; + + public: + static_assert(is_detected_exact::value, + "Missing/invalid function: bool null()"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool boolean(bool)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool boolean(bool)"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool number_integer(number_integer_t)"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool number_unsigned(number_unsigned_t)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool number_float(number_float_t, const string_t&)"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool string(string_t&)"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool binary(binary_t&)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool start_object(std::size_t)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool key(string_t&)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool end_object()"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool start_array(std::size_t)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool end_array()"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool parse_error(std::size_t, const " + "std::string&, const exception&)"); +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ + +/// how to treat CBOR tags +enum class cbor_tag_handler_t +{ + error, ///< throw a parse_error exception in case of a tag + ignore, ///< ignore tags + store ///< store tags as binary type +}; + +/*! +@brief determine system byte order + +@return true if and only if system's byte order is little endian + +@note from https://stackoverflow.com/a/1001328/266378 +*/ +static inline bool little_endianness(int num = 1) noexcept +{ + return *reinterpret_cast(&num) == 1; +} + + +/////////////////// +// binary reader // +/////////////////// + +/*! +@brief deserialization of CBOR, MessagePack, and UBJSON values +*/ +template> +class binary_reader +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using json_sax_t = SAX; + using char_type = typename InputAdapterType::char_type; + using char_int_type = typename std::char_traits::int_type; + + public: + /*! + @brief create a binary reader + + @param[in] adapter input adapter to read from + */ + explicit binary_reader(InputAdapterType&& adapter) noexcept : ia(std::move(adapter)) + { + (void)detail::is_sax_static_asserts {}; + } + + // make class move-only + binary_reader(const binary_reader&) = delete; + binary_reader(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + binary_reader& operator=(const binary_reader&) = delete; + binary_reader& operator=(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + ~binary_reader() = default; + + /*! + @param[in] format the binary format to parse + @param[in] sax_ a SAX event processor + @param[in] strict whether to expect the input to be consumed completed + @param[in] tag_handler how to treat CBOR tags + + @return whether parsing was successful + */ + JSON_HEDLEY_NON_NULL(3) + bool sax_parse(const input_format_t format, + json_sax_t* sax_, + const bool strict = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + sax = sax_; + bool result = false; + + switch (format) + { + case input_format_t::bson: + result = parse_bson_internal(); + break; + + case input_format_t::cbor: + result = parse_cbor_internal(true, tag_handler); + break; + + case input_format_t::msgpack: + result = parse_msgpack_internal(); + break; + + case input_format_t::ubjson: + result = parse_ubjson_internal(); + break; + + case input_format_t::json: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + + // strict mode: next byte must be EOF + if (result && strict) + { + if (format == input_format_t::ubjson) + { + get_ignore_noop(); + } + else + { + get(); + } + + if (JSON_HEDLEY_UNLIKELY(current != std::char_traits::eof())) + { + return sax->parse_error(chars_read, get_token_string(), + parse_error::create(110, chars_read, exception_message(format, "expected end of input; last byte: 0x" + get_token_string(), "value"), BasicJsonType())); + } + } + + return result; + } + + private: + ////////// + // BSON // + ////////// + + /*! + @brief Reads in a BSON-object and passes it to the SAX-parser. + @return whether a valid BSON-value was passed to the SAX parser + */ + bool parse_bson_internal() + { + std::int32_t document_size{}; + get_number(input_format_t::bson, document_size); + + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast(-1)))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/false))) + { + return false; + } + + return sax->end_object(); + } + + /*! + @brief Parses a C-style string from the BSON input. + @param[in,out] result A reference to the string variable where the read + string is to be stored. + @return `true` if the \x00-byte indicating the end of the string was + encountered before the EOF; false` indicates an unexpected EOF. + */ + bool get_bson_cstr(string_t& result) + { + auto out = std::back_inserter(result); + while (true) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "cstring"))) + { + return false; + } + if (current == 0x00) + { + return true; + } + *out++ = static_cast(current); + } + } + + /*! + @brief Parses a zero-terminated string of length @a len from the BSON + input. + @param[in] len The length (including the zero-byte at the end) of the + string to be read. + @param[in,out] result A reference to the string variable where the read + string is to be stored. + @tparam NumberType The type of the length @a len + @pre len >= 1 + @return `true` if the string was successfully parsed + */ + template + bool get_bson_string(const NumberType len, string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(len < 1)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "string length must be at least 1, is " + std::to_string(len), "string"), BasicJsonType())); + } + + return get_string(input_format_t::bson, len - static_cast(1), result) && get() != std::char_traits::eof(); + } + + /*! + @brief Parses a byte array input of length @a len from the BSON input. + @param[in] len The length of the byte array to be read. + @param[in,out] result A reference to the binary variable where the read + array is to be stored. + @tparam NumberType The type of the length @a len + @pre len >= 0 + @return `true` if the byte array was successfully parsed + */ + template + bool get_bson_binary(const NumberType len, binary_t& result) + { + if (JSON_HEDLEY_UNLIKELY(len < 0)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary"), BasicJsonType())); + } + + // All BSON binary values have a subtype + std::uint8_t subtype{}; + get_number(input_format_t::bson, subtype); + result.set_subtype(subtype); + + return get_binary(input_format_t::bson, len, result); + } + + /*! + @brief Read a BSON document element of the given @a element_type. + @param[in] element_type The BSON element type, c.f. http://bsonspec.org/spec.html + @param[in] element_type_parse_position The position in the input stream, + where the `element_type` was read. + @warning Not all BSON element types are supported yet. An unsupported + @a element_type will give rise to a parse_error.114: + Unsupported BSON record type 0x... + @return whether a valid BSON-object/array was passed to the SAX parser + */ + bool parse_bson_element_internal(const char_int_type element_type, + const std::size_t element_type_parse_position) + { + switch (element_type) + { + case 0x01: // double + { + double number{}; + return get_number(input_format_t::bson, number) && sax->number_float(static_cast(number), ""); + } + + case 0x02: // string + { + std::int32_t len{}; + string_t value; + return get_number(input_format_t::bson, len) && get_bson_string(len, value) && sax->string(value); + } + + case 0x03: // object + { + return parse_bson_internal(); + } + + case 0x04: // array + { + return parse_bson_array(); + } + + case 0x05: // binary + { + std::int32_t len{}; + binary_t value; + return get_number(input_format_t::bson, len) && get_bson_binary(len, value) && sax->binary(value); + } + + case 0x08: // boolean + { + return sax->boolean(get() != 0); + } + + case 0x0A: // null + { + return sax->null(); + } + + case 0x10: // int32 + { + std::int32_t value{}; + return get_number(input_format_t::bson, value) && sax->number_integer(value); + } + + case 0x12: // int64 + { + std::int64_t value{}; + return get_number(input_format_t::bson, value) && sax->number_integer(value); + } + + default: // anything else not supported (yet) + { + std::array cr{{}}; + static_cast((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(element_type))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + return sax->parse_error(element_type_parse_position, std::string(cr.data()), parse_error::create(114, element_type_parse_position, "Unsupported BSON record type 0x" + std::string(cr.data()), BasicJsonType())); + } + } + } + + /*! + @brief Read a BSON element list (as specified in the BSON-spec) + + The same binary layout is used for objects and arrays, hence it must be + indicated with the argument @a is_array which one is expected + (true --> array, false --> object). + + @param[in] is_array Determines if the element list being read is to be + treated as an object (@a is_array == false), or as an + array (@a is_array == true). + @return whether a valid BSON-object/array was passed to the SAX parser + */ + bool parse_bson_element_list(const bool is_array) + { + string_t key; + + while (auto element_type = get()) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "element list"))) + { + return false; + } + + const std::size_t element_type_parse_position = chars_read; + if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key))) + { + return false; + } + + if (!is_array && !sax->key(key)) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_internal(element_type, element_type_parse_position))) + { + return false; + } + + // get_bson_cstr only appends + key.clear(); + } + + return true; + } + + /*! + @brief Reads an array from the BSON input and passes it to the SAX-parser. + @return whether a valid BSON-array was passed to the SAX parser + */ + bool parse_bson_array() + { + std::int32_t document_size{}; + get_number(input_format_t::bson, document_size); + + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast(-1)))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/true))) + { + return false; + } + + return sax->end_array(); + } + + ////////// + // CBOR // + ////////// + + /*! + @param[in] get_char whether a new character should be retrieved from the + input (true) or whether the last read character should + be considered instead (false) + @param[in] tag_handler how CBOR tags should be treated + + @return whether a valid CBOR value was passed to the SAX parser + */ + bool parse_cbor_internal(const bool get_char, + const cbor_tag_handler_t tag_handler) + { + switch (get_char ? get() : current) + { + // EOF + case std::char_traits::eof(): + return unexpect_eof(input_format_t::cbor, "value"); + + // Integer 0x00..0x17 (0..23) + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + return sax->number_unsigned(static_cast(current)); + + case 0x18: // Unsigned integer (one-byte uint8_t follows) + { + std::uint8_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + case 0x19: // Unsigned integer (two-byte uint16_t follows) + { + std::uint16_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + case 0x1A: // Unsigned integer (four-byte uint32_t follows) + { + std::uint32_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + case 0x1B: // Unsigned integer (eight-byte uint64_t follows) + { + std::uint64_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + // Negative integer -1-0x00..-1-0x17 (-1..-24) + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + return sax->number_integer(static_cast(0x20 - 1 - current)); + + case 0x38: // Negative integer (one-byte uint8_t follows) + { + std::uint8_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) - number); + } + + case 0x39: // Negative integer -1-n (two-byte uint16_t follows) + { + std::uint16_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) - number); + } + + case 0x3A: // Negative integer -1-n (four-byte uint32_t follows) + { + std::uint32_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) - number); + } + + case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows) + { + std::uint64_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) + - static_cast(number)); + } + + // Binary data (0x00..0x17 bytes follow) + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: // Binary data (one-byte uint8_t for n follows) + case 0x59: // Binary data (two-byte uint16_t for n follow) + case 0x5A: // Binary data (four-byte uint32_t for n follow) + case 0x5B: // Binary data (eight-byte uint64_t for n follow) + case 0x5F: // Binary data (indefinite length) + { + binary_t b; + return get_cbor_binary(b) && sax->binary(b); + } + + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) + case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) + case 0x7F: // UTF-8 string (indefinite length) + { + string_t s; + return get_cbor_string(s) && sax->string(s); + } + + // array (0x00..0x17 data items follow) + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x8E: + case 0x8F: + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + return get_cbor_array(static_cast(static_cast(current) & 0x1Fu), tag_handler); + + case 0x98: // array (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast(len), tag_handler); + } + + case 0x99: // array (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast(len), tag_handler); + } + + case 0x9A: // array (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast(len), tag_handler); + } + + case 0x9B: // array (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(detail::conditional_static_cast(len), tag_handler); + } + + case 0x9F: // array (indefinite length) + return get_cbor_array(static_cast(-1), tag_handler); + + // map (0x00..0x17 pairs of data items follow) + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + return get_cbor_object(static_cast(static_cast(current) & 0x1Fu), tag_handler); + + case 0xB8: // map (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast(len), tag_handler); + } + + case 0xB9: // map (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast(len), tag_handler); + } + + case 0xBA: // map (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast(len), tag_handler); + } + + case 0xBB: // map (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(detail::conditional_static_cast(len), tag_handler); + } + + case 0xBF: // map (indefinite length) + return get_cbor_object(static_cast(-1), tag_handler); + + case 0xC6: // tagged item + case 0xC7: + case 0xC8: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCC: + case 0xCD: + case 0xCE: + case 0xCF: + case 0xD0: + case 0xD1: + case 0xD2: + case 0xD3: + case 0xD4: + case 0xD8: // tagged item (1 bytes follow) + case 0xD9: // tagged item (2 bytes follow) + case 0xDA: // tagged item (4 bytes follow) + case 0xDB: // tagged item (8 bytes follow) + { + switch (tag_handler) + { + case cbor_tag_handler_t::error: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); + } + + case cbor_tag_handler_t::ignore: + { + // ignore binary subtype + switch (current) + { + case 0xD8: + { + std::uint8_t subtype_to_ignore{}; + get_number(input_format_t::cbor, subtype_to_ignore); + break; + } + case 0xD9: + { + std::uint16_t subtype_to_ignore{}; + get_number(input_format_t::cbor, subtype_to_ignore); + break; + } + case 0xDA: + { + std::uint32_t subtype_to_ignore{}; + get_number(input_format_t::cbor, subtype_to_ignore); + break; + } + case 0xDB: + { + std::uint64_t subtype_to_ignore{}; + get_number(input_format_t::cbor, subtype_to_ignore); + break; + } + default: + break; + } + return parse_cbor_internal(true, tag_handler); + } + + case cbor_tag_handler_t::store: + { + binary_t b; + // use binary subtype and store in binary container + switch (current) + { + case 0xD8: + { + std::uint8_t subtype{}; + get_number(input_format_t::cbor, subtype); + b.set_subtype(detail::conditional_static_cast(subtype)); + break; + } + case 0xD9: + { + std::uint16_t subtype{}; + get_number(input_format_t::cbor, subtype); + b.set_subtype(detail::conditional_static_cast(subtype)); + break; + } + case 0xDA: + { + std::uint32_t subtype{}; + get_number(input_format_t::cbor, subtype); + b.set_subtype(detail::conditional_static_cast(subtype)); + break; + } + case 0xDB: + { + std::uint64_t subtype{}; + get_number(input_format_t::cbor, subtype); + b.set_subtype(detail::conditional_static_cast(subtype)); + break; + } + default: + return parse_cbor_internal(true, tag_handler); + } + get(); + return get_cbor_binary(b) && sax->binary(b); + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + return false; // LCOV_EXCL_LINE + } + } + + case 0xF4: // false + return sax->boolean(false); + + case 0xF5: // true + return sax->boolean(true); + + case 0xF6: // null + return sax->null(); + + case 0xF9: // Half-Precision Float (two-byte IEEE 754) + { + const auto byte1_raw = get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number"))) + { + return false; + } + const auto byte2_raw = get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number"))) + { + return false; + } + + const auto byte1 = static_cast(byte1_raw); + const auto byte2 = static_cast(byte2_raw); + + // code from RFC 7049, Appendix D, Figure 3: + // As half-precision floating-point numbers were only added + // to IEEE 754 in 2008, today's programming platforms often + // still only have limited support for them. It is very + // easy to include at least decoding support for them even + // without such support. An example of a small decoder for + // half-precision floating-point numbers in the C language + // is shown in Fig. 3. + const auto half = static_cast((byte1 << 8u) + byte2); + const double val = [&half] + { + const int exp = (half >> 10u) & 0x1Fu; + const unsigned int mant = half & 0x3FFu; + JSON_ASSERT(0 <= exp&& exp <= 32); + JSON_ASSERT(mant <= 1024); + switch (exp) + { + case 0: + return std::ldexp(mant, -24); + case 31: + return (mant == 0) + ? std::numeric_limits::infinity() + : std::numeric_limits::quiet_NaN(); + default: + return std::ldexp(mant + 1024, exp - 25); + } + }(); + return sax->number_float((half & 0x8000u) != 0 + ? static_cast(-val) + : static_cast(val), ""); + } + + case 0xFA: // Single-Precision Float (four-byte IEEE 754) + { + float number{}; + return get_number(input_format_t::cbor, number) && sax->number_float(static_cast(number), ""); + } + + case 0xFB: // Double-Precision Float (eight-byte IEEE 754) + { + double number{}; + return get_number(input_format_t::cbor, number) && sax->number_float(static_cast(number), ""); + } + + default: // anything else (0xFF is handled inside the other types) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); + } + } + } + + /*! + @brief reads a CBOR string + + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + Additionally, CBOR's strings with indefinite lengths are supported. + + @param[out] result created string + + @return whether string creation completed + */ + bool get_cbor_string(string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "string"))) + { + return false; + } + + switch (current) + { + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + { + return get_string(input_format_t::cbor, static_cast(current) & 0x1Fu, result); + } + + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x7F: // UTF-8 string (indefinite length) + { + while (get() != 0xFF) + { + string_t chunk; + if (!get_cbor_string(chunk)) + { + return false; + } + result.append(chunk); + } + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x" + last_token, "string"), BasicJsonType())); + } + } + } + + /*! + @brief reads a CBOR byte array + + This function first reads starting bytes to determine the expected + byte array length and then copies this number of bytes into the byte array. + Additionally, CBOR's byte arrays with indefinite lengths are supported. + + @param[out] result created byte array + + @return whether byte array creation completed + */ + bool get_cbor_binary(binary_t& result) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "binary"))) + { + return false; + } + + switch (current) + { + // Binary data (0x00..0x17 bytes follow) + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + { + return get_binary(input_format_t::cbor, static_cast(current) & 0x1Fu, result); + } + + case 0x58: // Binary data (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x59: // Binary data (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x5A: // Binary data (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x5B: // Binary data (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x5F: // Binary data (indefinite length) + { + while (get() != 0xFF) + { + binary_t chunk; + if (!get_cbor_binary(chunk)) + { + return false; + } + result.insert(result.end(), chunk.begin(), chunk.end()); + } + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x" + last_token, "binary"), BasicJsonType())); + } + } + } + + /*! + @param[in] len the length of the array or static_cast(-1) for an + array of indefinite size + @param[in] tag_handler how CBOR tags should be treated + @return whether array creation completed + */ + bool get_cbor_array(const std::size_t len, + const cbor_tag_handler_t tag_handler) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len))) + { + return false; + } + + if (len != static_cast(-1)) + { + for (std::size_t i = 0; i < len; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) + { + return false; + } + } + } + else + { + while (get() != 0xFF) + { + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false, tag_handler))) + { + return false; + } + } + } + + return sax->end_array(); + } + + /*! + @param[in] len the length of the object or static_cast(-1) for an + object of indefinite size + @param[in] tag_handler how CBOR tags should be treated + @return whether object creation completed + */ + bool get_cbor_object(const std::size_t len, + const cbor_tag_handler_t tag_handler) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len))) + { + return false; + } + + if (len != 0) + { + string_t key; + if (len != static_cast(-1)) + { + for (std::size_t i = 0; i < len; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) + { + return false; + } + key.clear(); + } + } + else + { + while (get() != 0xFF) + { + if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) + { + return false; + } + key.clear(); + } + } + } + + return sax->end_object(); + } + + ///////////// + // MsgPack // + ///////////// + + /*! + @return whether a valid MessagePack value was passed to the SAX parser + */ + bool parse_msgpack_internal() + { + switch (get()) + { + // EOF + case std::char_traits::eof(): + return unexpect_eof(input_format_t::msgpack, "value"); + + // positive fixint + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + case 0x1E: + case 0x1F: + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5C: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + return sax->number_unsigned(static_cast(current)); + + // fixmap + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x8E: + case 0x8F: + return get_msgpack_object(static_cast(static_cast(current) & 0x0Fu)); + + // fixarray + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9A: + case 0x9B: + case 0x9C: + case 0x9D: + case 0x9E: + case 0x9F: + return get_msgpack_array(static_cast(static_cast(current) & 0x0Fu)); + + // fixstr + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + case 0xB8: + case 0xB9: + case 0xBA: + case 0xBB: + case 0xBC: + case 0xBD: + case 0xBE: + case 0xBF: + case 0xD9: // str 8 + case 0xDA: // str 16 + case 0xDB: // str 32 + { + string_t s; + return get_msgpack_string(s) && sax->string(s); + } + + case 0xC0: // nil + return sax->null(); + + case 0xC2: // false + return sax->boolean(false); + + case 0xC3: // true + return sax->boolean(true); + + case 0xC4: // bin 8 + case 0xC5: // bin 16 + case 0xC6: // bin 32 + case 0xC7: // ext 8 + case 0xC8: // ext 16 + case 0xC9: // ext 32 + case 0xD4: // fixext 1 + case 0xD5: // fixext 2 + case 0xD6: // fixext 4 + case 0xD7: // fixext 8 + case 0xD8: // fixext 16 + { + binary_t b; + return get_msgpack_binary(b) && sax->binary(b); + } + + case 0xCA: // float 32 + { + float number{}; + return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast(number), ""); + } + + case 0xCB: // float 64 + { + double number{}; + return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast(number), ""); + } + + case 0xCC: // uint 8 + { + std::uint8_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } + + case 0xCD: // uint 16 + { + std::uint16_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } + + case 0xCE: // uint 32 + { + std::uint32_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } + + case 0xCF: // uint 64 + { + std::uint64_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } + + case 0xD0: // int 8 + { + std::int8_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } + + case 0xD1: // int 16 + { + std::int16_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } + + case 0xD2: // int 32 + { + std::int32_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } + + case 0xD3: // int 64 + { + std::int64_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } + + case 0xDC: // array 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast(len)); + } + + case 0xDD: // array 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast(len)); + } + + case 0xDE: // map 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast(len)); + } + + case 0xDF: // map 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast(len)); + } + + // negative fixint + case 0xE0: + case 0xE1: + case 0xE2: + case 0xE3: + case 0xE4: + case 0xE5: + case 0xE6: + case 0xE7: + case 0xE8: + case 0xE9: + case 0xEA: + case 0xEB: + case 0xEC: + case 0xED: + case 0xEE: + case 0xEF: + case 0xF0: + case 0xF1: + case 0xF2: + case 0xF3: + case 0xF4: + case 0xF5: + case 0xF6: + case 0xF7: + case 0xF8: + case 0xF9: + case 0xFA: + case 0xFB: + case 0xFC: + case 0xFD: + case 0xFE: + case 0xFF: + return sax->number_integer(static_cast(current)); + + default: // anything else + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); + } + } + } + + /*! + @brief reads a MessagePack string + + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + + @param[out] result created string + + @return whether string creation completed + */ + bool get_msgpack_string(string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::msgpack, "string"))) + { + return false; + } + + switch (current) + { + // fixstr + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + case 0xB8: + case 0xB9: + case 0xBA: + case 0xBB: + case 0xBC: + case 0xBD: + case 0xBE: + case 0xBF: + { + return get_string(input_format_t::msgpack, static_cast(current) & 0x1Fu, result); + } + + case 0xD9: // str 8 + { + std::uint8_t len{}; + return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); + } + + case 0xDA: // str 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); + } + + case 0xDB: // str 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, "expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x" + last_token, "string"), BasicJsonType())); + } + } + } + + /*! + @brief reads a MessagePack byte array + + This function first reads starting bytes to determine the expected + byte array length and then copies this number of bytes into a byte array. + + @param[out] result created byte array + + @return whether byte array creation completed + */ + bool get_msgpack_binary(binary_t& result) + { + // helper function to set the subtype + auto assign_and_return_true = [&result](std::int8_t subtype) + { + result.set_subtype(static_cast(subtype)); + return true; + }; + + switch (current) + { + case 0xC4: // bin 8 + { + std::uint8_t len{}; + return get_number(input_format_t::msgpack, len) && + get_binary(input_format_t::msgpack, len, result); + } + + case 0xC5: // bin 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && + get_binary(input_format_t::msgpack, len, result); + } + + case 0xC6: // bin 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && + get_binary(input_format_t::msgpack, len, result); + } + + case 0xC7: // ext 8 + { + std::uint8_t len{}; + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, len) && + get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, len, result) && + assign_and_return_true(subtype); + } + + case 0xC8: // ext 16 + { + std::uint16_t len{}; + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, len) && + get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, len, result) && + assign_and_return_true(subtype); + } + + case 0xC9: // ext 32 + { + std::uint32_t len{}; + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, len) && + get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, len, result) && + assign_and_return_true(subtype); + } + + case 0xD4: // fixext 1 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 1, result) && + assign_and_return_true(subtype); + } + + case 0xD5: // fixext 2 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 2, result) && + assign_and_return_true(subtype); + } + + case 0xD6: // fixext 4 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 4, result) && + assign_and_return_true(subtype); + } + + case 0xD7: // fixext 8 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 8, result) && + assign_and_return_true(subtype); + } + + case 0xD8: // fixext 16 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 16, result) && + assign_and_return_true(subtype); + } + + default: // LCOV_EXCL_LINE + return false; // LCOV_EXCL_LINE + } + } + + /*! + @param[in] len the length of the array + @return whether array creation completed + */ + bool get_msgpack_array(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len))) + { + return false; + } + + for (std::size_t i = 0; i < len; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal())) + { + return false; + } + } + + return sax->end_array(); + } + + /*! + @param[in] len the length of the object + @return whether object creation completed + */ + bool get_msgpack_object(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len))) + { + return false; + } + + string_t key; + for (std::size_t i = 0; i < len; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!get_msgpack_string(key) || !sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal())) + { + return false; + } + key.clear(); + } + + return sax->end_object(); + } + + //////////// + // UBJSON // + //////////// + + /*! + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + + @return whether a valid UBJSON value was passed to the SAX parser + */ + bool parse_ubjson_internal(const bool get_char = true) + { + return get_ubjson_value(get_char ? get_ignore_noop() : current); + } + + /*! + @brief reads a UBJSON string + + This function is either called after reading the 'S' byte explicitly + indicating a string, or in case of an object key where the 'S' byte can be + left out. + + @param[out] result created string + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + + @return whether string creation completed + */ + bool get_ubjson_string(string_t& result, const bool get_char = true) + { + if (get_char) + { + get(); // TODO(niels): may we ignore N here? + } + + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "value"))) + { + return false; + } + + switch (current) + { + case 'U': + { + std::uint8_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } + + case 'i': + { + std::int8_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } + + case 'I': + { + std::int16_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } + + case 'l': + { + std::int32_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } + + case 'L': + { + std::int64_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } + + default: + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token, "string"), BasicJsonType())); + } + } + + /*! + @param[out] result determined size + @return whether size determination completed + */ + bool get_ubjson_size_value(std::size_t& result) + { + switch (get_ignore_noop()) + { + case 'U': + { + std::uint8_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'i': + { + std::int8_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char + return true; + } + + case 'I': + { + std::int16_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'l': + { + std::int32_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'L': + { + std::int64_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token, "size"), BasicJsonType())); + } + } + } + + /*! + @brief determine the type and size for a container + + In the optimized UBJSON format, a type and a size can be provided to allow + for a more compact representation. + + @param[out] result pair of the size and the type + + @return whether pair creation completed + */ + bool get_ubjson_size_type(std::pair& result) + { + result.first = string_t::npos; // size + result.second = 0; // type + + get_ignore_noop(); + + if (current == '$') + { + result.second = get(); // must not ignore 'N', because 'N' maybe the type + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "type"))) + { + return false; + } + + get_ignore_noop(); + if (JSON_HEDLEY_UNLIKELY(current != '#')) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "value"))) + { + return false; + } + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "expected '#' after type information; last byte: 0x" + last_token, "size"), BasicJsonType())); + } + + return get_ubjson_size_value(result.first); + } + + if (current == '#') + { + return get_ubjson_size_value(result.first); + } + + return true; + } + + /*! + @param prefix the previously read or set type prefix + @return whether value creation completed + */ + bool get_ubjson_value(const char_int_type prefix) + { + switch (prefix) + { + case std::char_traits::eof(): // EOF + return unexpect_eof(input_format_t::ubjson, "value"); + + case 'T': // true + return sax->boolean(true); + case 'F': // false + return sax->boolean(false); + + case 'Z': // null + return sax->null(); + + case 'U': + { + std::uint8_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_unsigned(number); + } + + case 'i': + { + std::int8_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + } + + case 'I': + { + std::int16_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + } + + case 'l': + { + std::int32_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + } + + case 'L': + { + std::int64_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + } + + case 'd': + { + float number{}; + return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast(number), ""); + } + + case 'D': + { + double number{}; + return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast(number), ""); + } + + case 'H': + { + return get_ubjson_high_precision_number(); + } + + case 'C': // char + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "char"))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(current > 127)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + last_token, "char"), BasicJsonType())); + } + string_t s(1, static_cast(current)); + return sax->string(s); + } + + case 'S': // string + { + string_t s; + return get_ubjson_string(s) && sax->string(s); + } + + case '[': // array + return get_ubjson_array(); + + case '{': // object + return get_ubjson_object(); + + default: // anything else + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); + } + } + } + + /*! + @return whether array creation completed + */ + bool get_ubjson_array() + { + std::pair size_and_type; + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type))) + { + return false; + } + + if (size_and_type.first != string_t::npos) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(size_and_type.first))) + { + return false; + } + + if (size_and_type.second != 0) + { + if (size_and_type.second != 'N') + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second))) + { + return false; + } + } + } + } + else + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) + { + return false; + } + } + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast(-1)))) + { + return false; + } + + while (current != ']') + { + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal(false))) + { + return false; + } + get_ignore_noop(); + } + } + + return sax->end_array(); + } + + /*! + @return whether object creation completed + */ + bool get_ubjson_object() + { + std::pair size_and_type; + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type))) + { + return false; + } + + string_t key; + if (size_and_type.first != string_t::npos) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(size_and_type.first))) + { + return false; + } + + if (size_and_type.second != 0) + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second))) + { + return false; + } + key.clear(); + } + } + else + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) + { + return false; + } + key.clear(); + } + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast(-1)))) + { + return false; + } + + while (current != '}') + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key, false) || !sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) + { + return false; + } + get_ignore_noop(); + key.clear(); + } + } + + return sax->end_object(); + } + + // Note, no reader for UBJSON binary types is implemented because they do + // not exist + + bool get_ubjson_high_precision_number() + { + // get size of following number string + std::size_t size{}; + auto res = get_ubjson_size_value(size); + if (JSON_HEDLEY_UNLIKELY(!res)) + { + return res; + } + + // get number string + std::vector number_vector; + for (std::size_t i = 0; i < size; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "number"))) + { + return false; + } + number_vector.push_back(static_cast(current)); + } + + // parse number string + using ia_type = decltype(detail::input_adapter(number_vector)); + auto number_lexer = detail::lexer(detail::input_adapter(number_vector), false); + const auto result_number = number_lexer.scan(); + const auto number_string = number_lexer.get_token_string(); + const auto result_remainder = number_lexer.scan(); + + using token_type = typename detail::lexer_base::token_type; + + if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input)) + { + return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"), BasicJsonType())); + } + + switch (result_number) + { + case token_type::value_integer: + return sax->number_integer(number_lexer.get_number_integer()); + case token_type::value_unsigned: + return sax->number_unsigned(number_lexer.get_number_unsigned()); + case token_type::value_float: + return sax->number_float(number_lexer.get_number_float(), std::move(number_string)); + case token_type::uninitialized: + case token_type::literal_true: + case token_type::literal_false: + case token_type::literal_null: + case token_type::value_string: + case token_type::begin_array: + case token_type::begin_object: + case token_type::end_array: + case token_type::end_object: + case token_type::name_separator: + case token_type::value_separator: + case token_type::parse_error: + case token_type::end_of_input: + case token_type::literal_or_value: + default: + return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"), BasicJsonType())); + } + } + + /////////////////////// + // Utility functions // + /////////////////////// + + /*! + @brief get next character from the input + + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns a -'ve valued + `std::char_traits::eof()` in that case. + + @return character read from the input + */ + char_int_type get() + { + ++chars_read; + return current = ia.get_character(); + } + + /*! + @return character read from the input after ignoring all 'N' entries + */ + char_int_type get_ignore_noop() + { + do + { + get(); + } + while (current == 'N'); + + return current; + } + + /* + @brief read a number from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[out] result number of type @a NumberType + + @return whether conversion completed + + @note This function needs to respect the system's endianness, because + bytes in CBOR, MessagePack, and UBJSON are stored in network order + (big endian) and therefore need reordering on little endian systems. + */ + template + bool get_number(const input_format_t format, NumberType& result) + { + // step 1: read input into array with system's byte order + std::array vec{}; + for (std::size_t i = 0; i < sizeof(NumberType); ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "number"))) + { + return false; + } + + // reverse byte order prior to conversion if necessary + if (is_little_endian != InputIsLittleEndian) + { + vec[sizeof(NumberType) - i - 1] = static_cast(current); + } + else + { + vec[i] = static_cast(current); // LCOV_EXCL_LINE + } + } + + // step 2: convert array into number of type T and return + std::memcpy(&result, vec.data(), sizeof(NumberType)); + return true; + } + + /*! + @brief create a string by reading characters from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[in] len number of characters to read + @param[out] result string created by reading @a len bytes + + @return whether string creation completed + + @note We can not reserve @a len bytes for the result, because @a len + may be too large. Usually, @ref unexpect_eof() detects the end of + the input before we run out of string memory. + */ + template + bool get_string(const input_format_t format, + const NumberType len, + string_t& result) + { + bool success = true; + for (NumberType i = 0; i < len; i++) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "string"))) + { + success = false; + break; + } + result.push_back(static_cast(current)); + } + return success; + } + + /*! + @brief create a byte array by reading bytes from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[in] len number of bytes to read + @param[out] result byte array created by reading @a len bytes + + @return whether byte array creation completed + + @note We can not reserve @a len bytes for the result, because @a len + may be too large. Usually, @ref unexpect_eof() detects the end of + the input before we run out of memory. + */ + template + bool get_binary(const input_format_t format, + const NumberType len, + binary_t& result) + { + bool success = true; + for (NumberType i = 0; i < len; i++) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "binary"))) + { + success = false; + break; + } + result.push_back(static_cast(current)); + } + return success; + } + + /*! + @param[in] format the current format (for diagnostics) + @param[in] context further context information (for diagnostics) + @return whether the last read character is not EOF + */ + JSON_HEDLEY_NON_NULL(3) + bool unexpect_eof(const input_format_t format, const char* context) const + { + if (JSON_HEDLEY_UNLIKELY(current == std::char_traits::eof())) + { + return sax->parse_error(chars_read, "", + parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context), BasicJsonType())); + } + return true; + } + + /*! + @return a string representation of the last read byte + */ + std::string get_token_string() const + { + std::array cr{{}}; + static_cast((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(current))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + return std::string{cr.data()}; + } + + /*! + @param[in] format the current format + @param[in] detail a detailed error message + @param[in] context further context information + @return a message string to use in the parse_error exceptions + */ + std::string exception_message(const input_format_t format, + const std::string& detail, + const std::string& context) const + { + std::string error_msg = "syntax error while parsing "; + + switch (format) + { + case input_format_t::cbor: + error_msg += "CBOR"; + break; + + case input_format_t::msgpack: + error_msg += "MessagePack"; + break; + + case input_format_t::ubjson: + error_msg += "UBJSON"; + break; + + case input_format_t::bson: + error_msg += "BSON"; + break; + + case input_format_t::json: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + + return error_msg + " " + context + ": " + detail; + } + + private: + /// input adapter + InputAdapterType ia; + + /// the current character + char_int_type current = std::char_traits::eof(); + + /// the number of characters read + std::size_t chars_read = 0; + + /// whether we can assume little endianness + const bool is_little_endian = little_endianness(); + + /// the SAX parser + json_sax_t* sax = nullptr; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + + +#include // isfinite +#include // uint8_t +#include // function +#include // string +#include // move +#include // vector + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +//////////// +// parser // +//////////// + +enum class parse_event_t : std::uint8_t +{ + /// the parser read `{` and started to process a JSON object + object_start, + /// the parser read `}` and finished processing a JSON object + object_end, + /// the parser read `[` and started to process a JSON array + array_start, + /// the parser read `]` and finished processing a JSON array + array_end, + /// the parser read a key of a value in an object + key, + /// the parser finished reading a JSON value + value +}; + +template +using parser_callback_t = + std::function; + +/*! +@brief syntax analysis + +This class implements a recursive descent parser. +*/ +template +class parser +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using lexer_t = lexer; + using token_type = typename lexer_t::token_type; + + public: + /// a parser reading from an input adapter + explicit parser(InputAdapterType&& adapter, + const parser_callback_t cb = nullptr, + const bool allow_exceptions_ = true, + const bool skip_comments = false) + : callback(cb) + , m_lexer(std::move(adapter), skip_comments) + , allow_exceptions(allow_exceptions_) + { + // read first token + get_token(); + } + + /*! + @brief public parser interface + + @param[in] strict whether to expect the last token to be EOF + @param[in,out] result parsed JSON value + + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + */ + void parse(const bool strict, BasicJsonType& result) + { + if (callback) + { + json_sax_dom_callback_parser sdp(result, callback, allow_exceptions); + sax_parse_internal(&sdp); + + // in strict mode, input must be completely read + if (strict && (get_token() != token_type::end_of_input)) + { + sdp.parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_of_input, "value"), BasicJsonType())); + } + + // in case of an error, return discarded value + if (sdp.is_errored()) + { + result = value_t::discarded; + return; + } + + // set top-level value to null if it was discarded by the callback + // function + if (result.is_discarded()) + { + result = nullptr; + } + } + else + { + json_sax_dom_parser sdp(result, allow_exceptions); + sax_parse_internal(&sdp); + + // in strict mode, input must be completely read + if (strict && (get_token() != token_type::end_of_input)) + { + sdp.parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType())); + } + + // in case of an error, return discarded value + if (sdp.is_errored()) + { + result = value_t::discarded; + return; + } + } + + result.assert_invariant(); + } + + /*! + @brief public accept interface + + @param[in] strict whether to expect the last token to be EOF + @return whether the input is a proper JSON text + */ + bool accept(const bool strict = true) + { + json_sax_acceptor sax_acceptor; + return sax_parse(&sax_acceptor, strict); + } + + template + JSON_HEDLEY_NON_NULL(2) + bool sax_parse(SAX* sax, const bool strict = true) + { + (void)detail::is_sax_static_asserts {}; + const bool result = sax_parse_internal(sax); + + // strict mode: next byte must be EOF + if (result && strict && (get_token() != token_type::end_of_input)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType())); + } + + return result; + } + + private: + template + JSON_HEDLEY_NON_NULL(2) + bool sax_parse_internal(SAX* sax) + { + // stack to remember the hierarchy of structured values we are parsing + // true = array; false = object + std::vector states; + // value to avoid a goto (see comment where set to true) + bool skip_to_state_evaluation = false; + + while (true) + { + if (!skip_to_state_evaluation) + { + // invariant: get_token() was called before each iteration + switch (last_token) + { + case token_type::begin_object: + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast(-1)))) + { + return false; + } + + // closing } -> we are done + if (get_token() == token_type::end_object) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_object())) + { + return false; + } + break; + } + + // parse key + if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType())); + } + if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) + { + return false; + } + + // parse separator (:) + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType())); + } + + // remember we are now inside an object + states.push_back(false); + + // parse values + get_token(); + continue; + } + + case token_type::begin_array: + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast(-1)))) + { + return false; + } + + // closing ] -> we are done + if (get_token() == token_type::end_array) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_array())) + { + return false; + } + break; + } + + // remember we are now inside an array + states.push_back(true); + + // parse values (no need to call get_token) + continue; + } + + case token_type::value_float: + { + const auto res = m_lexer.get_number_float(); + + if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res))) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'", BasicJsonType())); + } + + if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string()))) + { + return false; + } + + break; + } + + case token_type::literal_false: + { + if (JSON_HEDLEY_UNLIKELY(!sax->boolean(false))) + { + return false; + } + break; + } + + case token_type::literal_null: + { + if (JSON_HEDLEY_UNLIKELY(!sax->null())) + { + return false; + } + break; + } + + case token_type::literal_true: + { + if (JSON_HEDLEY_UNLIKELY(!sax->boolean(true))) + { + return false; + } + break; + } + + case token_type::value_integer: + { + if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(m_lexer.get_number_integer()))) + { + return false; + } + break; + } + + case token_type::value_string: + { + if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string()))) + { + return false; + } + break; + } + + case token_type::value_unsigned: + { + if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(m_lexer.get_number_unsigned()))) + { + return false; + } + break; + } + + case token_type::parse_error: + { + // using "uninitialized" to avoid "expected" message + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), BasicJsonType())); + } + + case token_type::uninitialized: + case token_type::end_array: + case token_type::end_object: + case token_type::name_separator: + case token_type::value_separator: + case token_type::end_of_input: + case token_type::literal_or_value: + default: // the last token was unexpected + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), BasicJsonType())); + } + } + } + else + { + skip_to_state_evaluation = false; + } + + // we reached this line after we successfully parsed a value + if (states.empty()) + { + // empty stack: we reached the end of the hierarchy: done + return true; + } + + if (states.back()) // array + { + // comma -> next value + if (get_token() == token_type::value_separator) + { + // parse a new value + get_token(); + continue; + } + + // closing ] + if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array)) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_array())) + { + return false; + } + + // We are done with this array. Before we can parse a + // new value, we need to evaluate the new state first. + // By setting skip_to_state_evaluation to false, we + // are effectively jumping to the beginning of this if. + JSON_ASSERT(!states.empty()); + states.pop_back(); + skip_to_state_evaluation = true; + continue; + } + + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), BasicJsonType())); + } + + // states.back() is false -> object + + // comma -> next value + if (get_token() == token_type::value_separator) + { + // parse key + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType())); + } + + if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) + { + return false; + } + + // parse separator (:) + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType())); + } + + // parse values + get_token(); + continue; + } + + // closing } + if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object)) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_object())) + { + return false; + } + + // We are done with this object. Before we can parse a + // new value, we need to evaluate the new state first. + // By setting skip_to_state_evaluation to false, we + // are effectively jumping to the beginning of this if. + JSON_ASSERT(!states.empty()); + states.pop_back(); + skip_to_state_evaluation = true; + continue; + } + + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), BasicJsonType())); + } + } + + /// get next token from lexer + token_type get_token() + { + return last_token = m_lexer.scan(); + } + + std::string exception_message(const token_type expected, const std::string& context) + { + std::string error_msg = "syntax error "; + + if (!context.empty()) + { + error_msg += "while parsing " + context + " "; + } + + error_msg += "- "; + + if (last_token == token_type::parse_error) + { + error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" + + m_lexer.get_token_string() + "'"; + } + else + { + error_msg += "unexpected " + std::string(lexer_t::token_type_name(last_token)); + } + + if (expected != token_type::uninitialized) + { + error_msg += "; expected " + std::string(lexer_t::token_type_name(expected)); + } + + return error_msg; + } + + private: + /// callback function + const parser_callback_t callback = nullptr; + /// the type of the last read token + token_type last_token = token_type::uninitialized; + /// the lexer + lexer_t m_lexer; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; +}; + +} // namespace detail +} // namespace nlohmann + +// #include + + +// #include + + +#include // ptrdiff_t +#include // numeric_limits + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/* +@brief an iterator for primitive JSON types + +This class models an iterator for primitive JSON types (boolean, number, +string). It's only purpose is to allow the iterator/const_iterator classes +to "iterate" over primitive values. Internally, the iterator is modeled by +a `difference_type` variable. Value begin_value (`0`) models the begin, +end_value (`1`) models past the end. +*/ +class primitive_iterator_t +{ + private: + using difference_type = std::ptrdiff_t; + static constexpr difference_type begin_value = 0; + static constexpr difference_type end_value = begin_value + 1; + + JSON_PRIVATE_UNLESS_TESTED: + /// iterator as signed integer type + difference_type m_it = (std::numeric_limits::min)(); + + public: + constexpr difference_type get_value() const noexcept + { + return m_it; + } + + /// set iterator to a defined beginning + void set_begin() noexcept + { + m_it = begin_value; + } + + /// set iterator to a defined past the end + void set_end() noexcept + { + m_it = end_value; + } + + /// return whether the iterator can be dereferenced + constexpr bool is_begin() const noexcept + { + return m_it == begin_value; + } + + /// return whether the iterator is at end + constexpr bool is_end() const noexcept + { + return m_it == end_value; + } + + friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it == rhs.m_it; + } + + friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it < rhs.m_it; + } + + primitive_iterator_t operator+(difference_type n) noexcept + { + auto result = *this; + result += n; + return result; + } + + friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it - rhs.m_it; + } + + primitive_iterator_t& operator++() noexcept + { + ++m_it; + return *this; + } + + primitive_iterator_t const operator++(int) noexcept // NOLINT(readability-const-return-type) + { + auto result = *this; + ++m_it; + return result; + } + + primitive_iterator_t& operator--() noexcept + { + --m_it; + return *this; + } + + primitive_iterator_t const operator--(int) noexcept // NOLINT(readability-const-return-type) + { + auto result = *this; + --m_it; + return result; + } + + primitive_iterator_t& operator+=(difference_type n) noexcept + { + m_it += n; + return *this; + } + + primitive_iterator_t& operator-=(difference_type n) noexcept + { + m_it -= n; + return *this; + } +}; +} // namespace detail +} // namespace nlohmann + + +namespace nlohmann +{ +namespace detail +{ +/*! +@brief an iterator value + +@note This structure could easily be a union, but MSVC currently does not allow +unions members with complex constructors, see https://github.com/nlohmann/json/pull/105. +*/ +template struct internal_iterator +{ + /// iterator for JSON objects + typename BasicJsonType::object_t::iterator object_iterator {}; + /// iterator for JSON arrays + typename BasicJsonType::array_t::iterator array_iterator {}; + /// generic iterator for all other types + primitive_iterator_t primitive_iterator {}; +}; +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next +#include // conditional, is_const, remove_const + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +// forward declare, to be able to friend it later on +template class iteration_proxy; +template class iteration_proxy_value; + +/*! +@brief a template for a bidirectional iterator for the @ref basic_json class +This class implements a both iterators (iterator and const_iterator) for the +@ref basic_json class. +@note An iterator is called *initialized* when a pointer to a JSON value has + been set (e.g., by a constructor or a copy assignment). If the iterator is + default-constructed, it is *uninitialized* and most methods are undefined. + **The library uses assertions to detect calls on uninitialized iterators.** +@requirement The class satisfies the following concept requirements: +- +[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): + The iterator that can be moved can be moved in both directions (i.e. + incremented and decremented). +@since version 1.0.0, simplified in version 2.0.9, change to bidirectional + iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593) +*/ +template +class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions) +{ + /// the iterator with BasicJsonType of different const-ness + using other_iter_impl = iter_impl::value, typename std::remove_const::type, const BasicJsonType>::type>; + /// allow basic_json to access private members + friend other_iter_impl; + friend BasicJsonType; + friend iteration_proxy; + friend iteration_proxy_value; + + using object_t = typename BasicJsonType::object_t; + using array_t = typename BasicJsonType::array_t; + // make sure BasicJsonType is basic_json or const basic_json + static_assert(is_basic_json::type>::value, + "iter_impl only accepts (const) basic_json"); + + public: + + /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17. + /// The C++ Standard has never required user-defined iterators to derive from std::iterator. + /// A user-defined iterator should provide publicly accessible typedefs named + /// iterator_category, value_type, difference_type, pointer, and reference. + /// Note that value_type is required to be non-const, even for constant iterators. + using iterator_category = std::bidirectional_iterator_tag; + + /// the type of the values when the iterator is dereferenced + using value_type = typename BasicJsonType::value_type; + /// a type to represent differences between iterators + using difference_type = typename BasicJsonType::difference_type; + /// defines a pointer to the type iterated over (value_type) + using pointer = typename std::conditional::value, + typename BasicJsonType::const_pointer, + typename BasicJsonType::pointer>::type; + /// defines a reference to the type iterated over (value_type) + using reference = + typename std::conditional::value, + typename BasicJsonType::const_reference, + typename BasicJsonType::reference>::type; + + iter_impl() = default; + ~iter_impl() = default; + iter_impl(iter_impl&&) noexcept = default; + iter_impl& operator=(iter_impl&&) noexcept = default; + + /*! + @brief constructor for a given JSON instance + @param[in] object pointer to a JSON object for this iterator + @pre object != nullptr + @post The iterator is initialized; i.e. `m_object != nullptr`. + */ + explicit iter_impl(pointer object) noexcept : m_object(object) + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = typename object_t::iterator(); + break; + } + + case value_t::array: + { + m_it.array_iterator = typename array_t::iterator(); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + m_it.primitive_iterator = primitive_iterator_t(); + break; + } + } + } + + /*! + @note The conventional copy constructor and copy assignment are implicitly + defined. Combined with the following converting constructor and + assignment, they support: (1) copy from iterator to iterator, (2) + copy from const iterator to const iterator, and (3) conversion from + iterator to const iterator. However conversion from const iterator + to iterator is not defined. + */ + + /*! + @brief const copy constructor + @param[in] other const iterator to copy from + @note This copy constructor had to be defined explicitly to circumvent a bug + occurring on msvc v19.0 compiler (VS 2015) debug build. For more + information refer to: https://github.com/nlohmann/json/issues/1608 + */ + iter_impl(const iter_impl& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} + + /*! + @brief converting assignment + @param[in] other const iterator to copy from + @return const/non-const iterator + @note It is not checked whether @a other is initialized. + */ + iter_impl& operator=(const iter_impl& other) noexcept + { + if (&other != this) + { + m_object = other.m_object; + m_it = other.m_it; + } + return *this; + } + + /*! + @brief converting constructor + @param[in] other non-const iterator to copy from + @note It is not checked whether @a other is initialized. + */ + iter_impl(const iter_impl::type>& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} + + /*! + @brief converting assignment + @param[in] other non-const iterator to copy from + @return const/non-const iterator + @note It is not checked whether @a other is initialized. + */ + iter_impl& operator=(const iter_impl::type>& other) noexcept // NOLINT(cert-oop54-cpp) + { + m_object = other.m_object; + m_it = other.m_it; + return *this; + } + + JSON_PRIVATE_UNLESS_TESTED: + /*! + @brief set the iterator to the first value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_begin() noexcept + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = m_object->m_value.object->begin(); + break; + } + + case value_t::array: + { + m_it.array_iterator = m_object->m_value.array->begin(); + break; + } + + case value_t::null: + { + // set to end so begin()==end() is true: null is empty + m_it.primitive_iterator.set_end(); + break; + } + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + m_it.primitive_iterator.set_begin(); + break; + } + } + } + + /*! + @brief set the iterator past the last value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_end() noexcept + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = m_object->m_value.object->end(); + break; + } + + case value_t::array: + { + m_it.array_iterator = m_object->m_value.array->end(); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + m_it.primitive_iterator.set_end(); + break; + } + } + } + + public: + /*! + @brief return a reference to the value pointed to by the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator*() const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end()); + return m_it.object_iterator->second; + } + + case value_t::array: + { + JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end()); + return *m_it.array_iterator; + } + + case value_t::null: + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) + { + return *m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + } + } + } + + /*! + @brief dereference the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + pointer operator->() const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end()); + return &(m_it.object_iterator->second); + } + + case value_t::array: + { + JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end()); + return &*m_it.array_iterator; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) + { + return m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + } + } + } + + /*! + @brief post-increment (it++) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl const operator++(int) // NOLINT(readability-const-return-type) + { + auto result = *this; + ++(*this); + return result; + } + + /*! + @brief pre-increment (++it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator++() + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + std::advance(m_it.object_iterator, 1); + break; + } + + case value_t::array: + { + std::advance(m_it.array_iterator, 1); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + ++m_it.primitive_iterator; + break; + } + } + + return *this; + } + + /*! + @brief post-decrement (it--) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl const operator--(int) // NOLINT(readability-const-return-type) + { + auto result = *this; + --(*this); + return result; + } + + /*! + @brief pre-decrement (--it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator--() + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + std::advance(m_it.object_iterator, -1); + break; + } + + case value_t::array: + { + std::advance(m_it.array_iterator, -1); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + --m_it.primitive_iterator; + break; + } + } + + return *this; + } + + /*! + @brief comparison: equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + template < typename IterImpl, detail::enable_if_t < (std::is_same::value || std::is_same::value), std::nullptr_t > = nullptr > + bool operator==(const IterImpl& other) const + { + // if objects are not the same, the comparison is undefined + if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) + { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", *m_object)); + } + + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + return (m_it.object_iterator == other.m_it.object_iterator); + + case value_t::array: + return (m_it.array_iterator == other.m_it.array_iterator); + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + return (m_it.primitive_iterator == other.m_it.primitive_iterator); + } + } + + /*! + @brief comparison: not equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + template < typename IterImpl, detail::enable_if_t < (std::is_same::value || std::is_same::value), std::nullptr_t > = nullptr > + bool operator!=(const IterImpl& other) const + { + return !operator==(other); + } + + /*! + @brief comparison: smaller + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<(const iter_impl& other) const + { + // if objects are not the same, the comparison is undefined + if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) + { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", *m_object)); + } + + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", *m_object)); + + case value_t::array: + return (m_it.array_iterator < other.m_it.array_iterator); + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + return (m_it.primitive_iterator < other.m_it.primitive_iterator); + } + } + + /*! + @brief comparison: less than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<=(const iter_impl& other) const + { + return !other.operator < (*this); + } + + /*! + @brief comparison: greater than + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>(const iter_impl& other) const + { + return !operator<=(other); + } + + /*! + @brief comparison: greater than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>=(const iter_impl& other) const + { + return !operator<(other); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator+=(difference_type i) + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", *m_object)); + + case value_t::array: + { + std::advance(m_it.array_iterator, i); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + m_it.primitive_iterator += i; + break; + } + } + + return *this; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator-=(difference_type i) + { + return operator+=(-i); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator+(difference_type i) const + { + auto result = *this; + result += i; + return result; + } + + /*! + @brief addition of distance and iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + friend iter_impl operator+(difference_type i, const iter_impl& it) + { + auto result = it; + result += i; + return result; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator-(difference_type i) const + { + auto result = *this; + result -= i; + return result; + } + + /*! + @brief return difference + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + difference_type operator-(const iter_impl& other) const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", *m_object)); + + case value_t::array: + return m_it.array_iterator - other.m_it.array_iterator; + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + return m_it.primitive_iterator - other.m_it.primitive_iterator; + } + } + + /*! + @brief access to successor + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator[](difference_type n) const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", *m_object)); + + case value_t::array: + return *std::next(m_it.array_iterator, n); + + case value_t::null: + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n)) + { + return *m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + } + } + } + + /*! + @brief return the key of an object iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + const typename object_t::key_type& key() const + { + JSON_ASSERT(m_object != nullptr); + + if (JSON_HEDLEY_LIKELY(m_object->is_object())) + { + return m_it.object_iterator->first; + } + + JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", *m_object)); + } + + /*! + @brief return the value of an iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference value() const + { + return operator*(); + } + + JSON_PRIVATE_UNLESS_TESTED: + /// associated JSON instance + pointer m_object = nullptr; + /// the actual iterator of the associated instance + internal_iterator::type> m_it {}; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // ptrdiff_t +#include // reverse_iterator +#include // declval + +namespace nlohmann +{ +namespace detail +{ +////////////////////// +// reverse_iterator // +////////////////////// + +/*! +@brief a template for a reverse iterator class + +@tparam Base the base iterator type to reverse. Valid types are @ref +iterator (to create @ref reverse_iterator) and @ref const_iterator (to +create @ref const_reverse_iterator). + +@requirement The class satisfies the following concept requirements: +- +[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): + The iterator that can be moved can be moved in both directions (i.e. + incremented and decremented). +- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator): + It is possible to write to the pointed-to element (only if @a Base is + @ref iterator). + +@since version 1.0.0 +*/ +template +class json_reverse_iterator : public std::reverse_iterator +{ + public: + using difference_type = std::ptrdiff_t; + /// shortcut to the reverse iterator adapter + using base_iterator = std::reverse_iterator; + /// the reference type for the pointed-to element + using reference = typename Base::reference; + + /// create reverse iterator from iterator + explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept + : base_iterator(it) {} + + /// create reverse iterator from base class + explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {} + + /// post-increment (it++) + json_reverse_iterator const operator++(int) // NOLINT(readability-const-return-type) + { + return static_cast(base_iterator::operator++(1)); + } + + /// pre-increment (++it) + json_reverse_iterator& operator++() + { + return static_cast(base_iterator::operator++()); + } + + /// post-decrement (it--) + json_reverse_iterator const operator--(int) // NOLINT(readability-const-return-type) + { + return static_cast(base_iterator::operator--(1)); + } + + /// pre-decrement (--it) + json_reverse_iterator& operator--() + { + return static_cast(base_iterator::operator--()); + } + + /// add to iterator + json_reverse_iterator& operator+=(difference_type i) + { + return static_cast(base_iterator::operator+=(i)); + } + + /// add to iterator + json_reverse_iterator operator+(difference_type i) const + { + return static_cast(base_iterator::operator+(i)); + } + + /// subtract from iterator + json_reverse_iterator operator-(difference_type i) const + { + return static_cast(base_iterator::operator-(i)); + } + + /// return difference + difference_type operator-(const json_reverse_iterator& other) const + { + return base_iterator(*this) - base_iterator(other); + } + + /// access to successor + reference operator[](difference_type n) const + { + return *(this->operator+(n)); + } + + /// return the key of an object iterator + auto key() const -> decltype(std::declval().key()) + { + auto it = --this->base(); + return it.key(); + } + + /// return the value of an iterator + reference value() const + { + auto it = --this->base(); + return it.operator * (); + } +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // all_of +#include // isdigit +#include // max +#include // accumulate +#include // string +#include // move +#include // vector + +// #include + +// #include + +// #include + +// #include + + +namespace nlohmann +{ + +/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document +/// @sa https://json.nlohmann.me/api/json_pointer/ +template +class json_pointer +{ + // allow basic_json to access private members + NLOHMANN_BASIC_JSON_TPL_DECLARATION + friend class basic_json; + + public: + /// @brief create JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/json_pointer/ + explicit json_pointer(const std::string& s = "") + : reference_tokens(split(s)) + {} + + /// @brief return a string representation of the JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/to_string/ + std::string to_string() const + { + return std::accumulate(reference_tokens.begin(), reference_tokens.end(), + std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + detail::escape(b); + }); + } + + /// @brief return a string representation of the JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_string/ + operator std::string() const + { + return to_string(); + } + + /// @brief append another JSON pointer at the end of this JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/ + json_pointer& operator/=(const json_pointer& ptr) + { + reference_tokens.insert(reference_tokens.end(), + ptr.reference_tokens.begin(), + ptr.reference_tokens.end()); + return *this; + } + + /// @brief append an unescaped reference token at the end of this JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/ + json_pointer& operator/=(std::string token) + { + push_back(std::move(token)); + return *this; + } + + /// @brief append an array index at the end of this JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/ + json_pointer& operator/=(std::size_t array_idx) + { + return *this /= std::to_string(array_idx); + } + + /// @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/ + friend json_pointer operator/(const json_pointer& lhs, + const json_pointer& rhs) + { + return json_pointer(lhs) /= rhs; + } + + /// @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/ + friend json_pointer operator/(const json_pointer& lhs, std::string token) // NOLINT(performance-unnecessary-value-param) + { + return json_pointer(lhs) /= std::move(token); + } + + /// @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/ + friend json_pointer operator/(const json_pointer& lhs, std::size_t array_idx) + { + return json_pointer(lhs) /= array_idx; + } + + /// @brief returns the parent of this JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/parent_pointer/ + json_pointer parent_pointer() const + { + if (empty()) + { + return *this; + } + + json_pointer res = *this; + res.pop_back(); + return res; + } + + /// @brief remove last reference token + /// @sa https://json.nlohmann.me/api/json_pointer/pop_back/ + void pop_back() + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType())); + } + + reference_tokens.pop_back(); + } + + /// @brief return last reference token + /// @sa https://json.nlohmann.me/api/json_pointer/back/ + const std::string& back() const + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType())); + } + + return reference_tokens.back(); + } + + /// @brief append an unescaped token at the end of the reference pointer + /// @sa https://json.nlohmann.me/api/json_pointer/push_back/ + void push_back(const std::string& token) + { + reference_tokens.push_back(token); + } + + /// @brief append an unescaped token at the end of the reference pointer + /// @sa https://json.nlohmann.me/api/json_pointer/push_back/ + void push_back(std::string&& token) + { + reference_tokens.push_back(std::move(token)); + } + + /// @brief return whether pointer points to the root document + /// @sa https://json.nlohmann.me/api/json_pointer/empty/ + bool empty() const noexcept + { + return reference_tokens.empty(); + } + + private: + /*! + @param[in] s reference token to be converted into an array index + + @return integer representation of @a s + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index begins not with a digit + @throw out_of_range.404 if string @a s could not be converted to an integer + @throw out_of_range.410 if an array index exceeds size_type + */ + static typename BasicJsonType::size_type array_index(const std::string& s) + { + using size_type = typename BasicJsonType::size_type; + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, "array index '" + s + "' must not begin with '0'", BasicJsonType())); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9'))) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number", BasicJsonType())); + } + + std::size_t processed_chars = 0; + unsigned long long res = 0; // NOLINT(runtime/int) + JSON_TRY + { + res = std::stoull(s, &processed_chars); + } + JSON_CATCH(std::out_of_range&) + { + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", BasicJsonType())); + } + + // check if the string was completely read + if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size())) + { + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", BasicJsonType())); + } + + // only triggered on special platforms (like 32bit), see also + // https://github.com/nlohmann/json/pull/2203 + if (res >= static_cast((std::numeric_limits::max)())) // NOLINT(runtime/int) + { + JSON_THROW(detail::out_of_range::create(410, "array index " + s + " exceeds size_type", BasicJsonType())); // LCOV_EXCL_LINE + } + + return static_cast(res); + } + + JSON_PRIVATE_UNLESS_TESTED: + json_pointer top() const + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType())); + } + + json_pointer result = *this; + result.reference_tokens = {reference_tokens[0]}; + return result; + } + + private: + /*! + @brief create and return a reference to the pointed to value + + @complexity Linear in the number of reference tokens. + + @throw parse_error.109 if array index is not a number + @throw type_error.313 if value cannot be unflattened + */ + BasicJsonType& get_and_create(BasicJsonType& j) const + { + auto* result = &j; + + // in case no reference tokens exist, return a reference to the JSON value + // j which will be overwritten by a primitive value + for (const auto& reference_token : reference_tokens) + { + switch (result->type()) + { + case detail::value_t::null: + { + if (reference_token == "0") + { + // start a new array if reference token is 0 + result = &result->operator[](0); + } + else + { + // start a new object otherwise + result = &result->operator[](reference_token); + } + break; + } + + case detail::value_t::object: + { + // create an entry in the object + result = &result->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + // create an entry in the array + result = &result->operator[](array_index(reference_token)); + break; + } + + /* + The following code is only reached if there exists a reference + token _and_ the current value is primitive. In this case, we have + an error situation, because primitive values may only occur as + single value; that is, with an empty list of reference tokens. + */ + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", j)); + } + } + + return *result; + } + + /*! + @brief return a reference to the pointed to value + + @note This version does not throw if a value is not present, but tries to + create nested values instead. For instance, calling this function + with pointer `"/this/that"` on a null value is equivalent to calling + `operator[]("this").operator[]("that")` on that value, effectively + changing the null value to an object. + + @param[in] ptr a JSON value + + @return reference to the JSON value pointed to by the JSON pointer + + @complexity Linear in the length of the JSON pointer. + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + BasicJsonType& get_unchecked(BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + // convert null values to arrays or objects before continuing + if (ptr->is_null()) + { + // check if reference token is a number + const bool nums = + std::all_of(reference_token.begin(), reference_token.end(), + [](const unsigned char x) + { + return std::isdigit(x); + }); + + // change value to array for numbers or "-" or to object otherwise + *ptr = (nums || reference_token == "-") + ? detail::value_t::array + : detail::value_t::object; + } + + switch (ptr->type()) + { + case detail::value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (reference_token == "-") + { + // explicitly treat "-" as index beyond the end + ptr = &ptr->operator[](ptr->m_value.array->size()); + } + else + { + // convert array index to number; unchecked access + ptr = &ptr->operator[](array_index(reference_token)); + } + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + BasicJsonType& get_checked(BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range", *ptr)); + } + + // note: at performs range check + ptr = &ptr->at(array_index(reference_token)); + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + } + } + + return *ptr; + } + + /*! + @brief return a const reference to the pointed to value + + @param[in] ptr a JSON value + + @return const reference to the JSON value pointed to by the JSON + pointer + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" cannot be used for const access + JSON_THROW(detail::out_of_range::create(402, "array index '-' (" + std::to_string(ptr->m_value.array->size()) + ") is out of range", *ptr)); + } + + // use unchecked array access + ptr = &ptr->operator[](array_index(reference_token)); + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + const BasicJsonType& get_checked(const BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range", *ptr)); + } + + // note: at performs range check + ptr = &ptr->at(array_index(reference_token)); + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + */ + bool contains(const BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + if (!ptr->contains(reference_token)) + { + // we did not find the key in the object + return false; + } + + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + return false; + } + if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !("0" <= reference_token && reference_token <= "9"))) + { + // invalid char + return false; + } + if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1)) + { + if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9'))) + { + // first char should be between '1' and '9' + return false; + } + for (std::size_t i = 1; i < reference_token.size(); i++) + { + if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9'))) + { + // other char should be between '0' and '9' + return false; + } + } + } + + const auto idx = array_index(reference_token); + if (idx >= ptr->size()) + { + // index out of range + return false; + } + + ptr = &ptr->operator[](idx); + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + { + // we do not expect primitive values if there is still a + // reference token to process + return false; + } + } + } + + // no reference token left means we found a primitive value + return true; + } + + /*! + @brief split the string input to reference tokens + + @note This function is only called by the json_pointer constructor. + All exceptions below are documented there. + + @throw parse_error.107 if the pointer is not empty or begins with '/' + @throw parse_error.108 if character '~' is not followed by '0' or '1' + */ + static std::vector split(const std::string& reference_string) + { + std::vector result; + + // special case: empty reference string -> no reference tokens + if (reference_string.empty()) + { + return result; + } + + // check if nonempty reference string begins with slash + if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) + { + JSON_THROW(detail::parse_error::create(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'", BasicJsonType())); + } + + // extract the reference tokens: + // - slash: position of the last read slash (or end of string) + // - start: position after the previous slash + for ( + // search for the first slash after the first character + std::size_t slash = reference_string.find_first_of('/', 1), + // set the beginning of the first reference token + start = 1; + // we can stop if start == 0 (if slash == std::string::npos) + start != 0; + // set the beginning of the next reference token + // (will eventually be 0 if slash == std::string::npos) + start = (slash == std::string::npos) ? 0 : slash + 1, + // find next slash + slash = reference_string.find_first_of('/', start)) + { + // use the text between the beginning of the reference token + // (start) and the last slash (slash). + auto reference_token = reference_string.substr(start, slash - start); + + // check reference tokens are properly escaped + for (std::size_t pos = reference_token.find_first_of('~'); + pos != std::string::npos; + pos = reference_token.find_first_of('~', pos + 1)) + { + JSON_ASSERT(reference_token[pos] == '~'); + + // ~ must be followed by 0 or 1 + if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 || + (reference_token[pos + 1] != '0' && + reference_token[pos + 1] != '1'))) + { + JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", BasicJsonType())); + } + } + + // finally, store the reference token + detail::unescape(reference_token); + result.push_back(reference_token); + } + + return result; + } + + private: + /*! + @param[in] reference_string the reference string to the current value + @param[in] value the value to consider + @param[in,out] result the result object to insert values to + + @note Empty objects or arrays are flattened to `null`. + */ + static void flatten(const std::string& reference_string, + const BasicJsonType& value, + BasicJsonType& result) + { + switch (value.type()) + { + case detail::value_t::array: + { + if (value.m_value.array->empty()) + { + // flatten empty array as null + result[reference_string] = nullptr; + } + else + { + // iterate array and use index as reference string + for (std::size_t i = 0; i < value.m_value.array->size(); ++i) + { + flatten(reference_string + "/" + std::to_string(i), + value.m_value.array->operator[](i), result); + } + } + break; + } + + case detail::value_t::object: + { + if (value.m_value.object->empty()) + { + // flatten empty object as null + result[reference_string] = nullptr; + } + else + { + // iterate object and use keys as reference string + for (const auto& element : *value.m_value.object) + { + flatten(reference_string + "/" + detail::escape(element.first), element.second, result); + } + } + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + { + // add primitive value with its reference string + result[reference_string] = value; + break; + } + } + } + + /*! + @param[in] value flattened JSON + + @return unflattened JSON + + @throw parse_error.109 if array index is not a number + @throw type_error.314 if value is not an object + @throw type_error.315 if object values are not primitive + @throw type_error.313 if value cannot be unflattened + */ + static BasicJsonType + unflatten(const BasicJsonType& value) + { + if (JSON_HEDLEY_UNLIKELY(!value.is_object())) + { + JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", value)); + } + + BasicJsonType result; + + // iterate the JSON object values + for (const auto& element : *value.m_value.object) + { + if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive())) + { + JSON_THROW(detail::type_error::create(315, "values in object must be primitive", element.second)); + } + + // assign value to reference pointed to by JSON pointer; Note that if + // the JSON pointer is "" (i.e., points to the whole value), function + // get_and_create returns a reference to result itself. An assignment + // will then create a primitive value. + json_pointer(element.first).get_and_create(result) = element.second; + } + + return result; + } + + /*! + @brief compares two JSON pointers for equality + + @param[in] lhs JSON pointer to compare + @param[in] rhs JSON pointer to compare + @return whether @a lhs is equal to @a rhs + + @complexity Linear in the length of the JSON pointer + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + */ + friend bool operator==(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return lhs.reference_tokens == rhs.reference_tokens; + } + + /*! + @brief compares two JSON pointers for inequality + + @param[in] lhs JSON pointer to compare + @param[in] rhs JSON pointer to compare + @return whether @a lhs is not equal @a rhs + + @complexity Linear in the length of the JSON pointer + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + */ + friend bool operator!=(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return !(lhs == rhs); + } + + /// the reference tokens + std::vector reference_tokens; +}; +} // namespace nlohmann + +// #include + + +#include +#include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +class json_ref +{ + public: + using value_type = BasicJsonType; + + json_ref(value_type&& value) + : owned_value(std::move(value)) + {} + + json_ref(const value_type& value) + : value_ref(&value) + {} + + json_ref(std::initializer_list init) + : owned_value(init) + {} + + template < + class... Args, + enable_if_t::value, int> = 0 > + json_ref(Args && ... args) + : owned_value(std::forward(args)...) + {} + + // class should be movable only + json_ref(json_ref&&) noexcept = default; + json_ref(const json_ref&) = delete; + json_ref& operator=(const json_ref&) = delete; + json_ref& operator=(json_ref&&) = delete; + ~json_ref() = default; + + value_type moved_or_copied() const + { + if (value_ref == nullptr) + { + return std::move(owned_value); + } + return *value_ref; + } + + value_type const& operator*() const + { + return value_ref ? *value_ref : owned_value; + } + + value_type const* operator->() const + { + return &** this; + } + + private: + mutable value_type owned_value = nullptr; + value_type const* value_ref = nullptr; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + +// #include + +// #include + + +#include // reverse +#include // array +#include // isnan, isinf +#include // uint8_t, uint16_t, uint32_t, uint64_t +#include // memcpy +#include // numeric_limits +#include // string +#include // move + +// #include + +// #include + +// #include + + +#include // copy +#include // size_t +#include // back_inserter +#include // shared_ptr, make_shared +#include // basic_string +#include // vector + +#ifndef JSON_NO_IO + #include // streamsize + #include // basic_ostream +#endif // JSON_NO_IO + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/// abstract output adapter interface +template struct output_adapter_protocol +{ + virtual void write_character(CharType c) = 0; + virtual void write_characters(const CharType* s, std::size_t length) = 0; + virtual ~output_adapter_protocol() = default; + + output_adapter_protocol() = default; + output_adapter_protocol(const output_adapter_protocol&) = default; + output_adapter_protocol(output_adapter_protocol&&) noexcept = default; + output_adapter_protocol& operator=(const output_adapter_protocol&) = default; + output_adapter_protocol& operator=(output_adapter_protocol&&) noexcept = default; +}; + +/// a type to simplify interfaces +template +using output_adapter_t = std::shared_ptr>; + +/// output adapter for byte vectors +template> +class output_vector_adapter : public output_adapter_protocol +{ + public: + explicit output_vector_adapter(std::vector& vec) noexcept + : v(vec) + {} + + void write_character(CharType c) override + { + v.push_back(c); + } + + JSON_HEDLEY_NON_NULL(2) + void write_characters(const CharType* s, std::size_t length) override + { + std::copy(s, s + length, std::back_inserter(v)); + } + + private: + std::vector& v; +}; + +#ifndef JSON_NO_IO +/// output adapter for output streams +template +class output_stream_adapter : public output_adapter_protocol +{ + public: + explicit output_stream_adapter(std::basic_ostream& s) noexcept + : stream(s) + {} + + void write_character(CharType c) override + { + stream.put(c); + } + + JSON_HEDLEY_NON_NULL(2) + void write_characters(const CharType* s, std::size_t length) override + { + stream.write(s, static_cast(length)); + } + + private: + std::basic_ostream& stream; +}; +#endif // JSON_NO_IO + +/// output adapter for basic_string +template> +class output_string_adapter : public output_adapter_protocol +{ + public: + explicit output_string_adapter(StringType& s) noexcept + : str(s) + {} + + void write_character(CharType c) override + { + str.push_back(c); + } + + JSON_HEDLEY_NON_NULL(2) + void write_characters(const CharType* s, std::size_t length) override + { + str.append(s, length); + } + + private: + StringType& str; +}; + +template> +class output_adapter +{ + public: + template> + output_adapter(std::vector& vec) + : oa(std::make_shared>(vec)) {} + +#ifndef JSON_NO_IO + output_adapter(std::basic_ostream& s) + : oa(std::make_shared>(s)) {} +#endif // JSON_NO_IO + + output_adapter(StringType& s) + : oa(std::make_shared>(s)) {} + + operator output_adapter_t() + { + return oa; + } + + private: + output_adapter_t oa = nullptr; +}; +} // namespace detail +} // namespace nlohmann + + +namespace nlohmann +{ +namespace detail +{ +/////////////////// +// binary writer // +/////////////////// + +/*! +@brief serialization to CBOR and MessagePack values +*/ +template +class binary_writer +{ + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using number_float_t = typename BasicJsonType::number_float_t; + + public: + /*! + @brief create a binary writer + + @param[in] adapter output adapter to write to + */ + explicit binary_writer(output_adapter_t adapter) : oa(std::move(adapter)) + { + JSON_ASSERT(oa); + } + + /*! + @param[in] j JSON value to serialize + @pre j.type() == value_t::object + */ + void write_bson(const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::object: + { + write_bson_object(*j.m_value.object); + break; + } + + case value_t::null: + case value_t::array: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name()), j)); + } + } + } + + /*! + @param[in] j JSON value to serialize + */ + void write_cbor(const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::null: + { + oa->write_character(to_char_type(0xF6)); + break; + } + + case value_t::boolean: + { + oa->write_character(j.m_value.boolean + ? to_char_type(0xF5) + : to_char_type(0xF4)); + break; + } + + case value_t::number_integer: + { + if (j.m_value.number_integer >= 0) + { + // CBOR does not differentiate between positive signed + // integers and unsigned integers. Therefore, we used the + // code from the value_t::number_unsigned case here. + if (j.m_value.number_integer <= 0x17) + { + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x18)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x19)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x1A)); + write_number(static_cast(j.m_value.number_integer)); + } + else + { + oa->write_character(to_char_type(0x1B)); + write_number(static_cast(j.m_value.number_integer)); + } + } + else + { + // The conversions below encode the sign in the first + // byte, and the value is converted to a positive number. + const auto positive_number = -1 - j.m_value.number_integer; + if (j.m_value.number_integer >= -24) + { + write_number(static_cast(0x20 + positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x38)); + write_number(static_cast(positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x39)); + write_number(static_cast(positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x3A)); + write_number(static_cast(positive_number)); + } + else + { + oa->write_character(to_char_type(0x3B)); + write_number(static_cast(positive_number)); + } + } + break; + } + + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned <= 0x17) + { + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x18)); + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x19)); + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x1A)); + write_number(static_cast(j.m_value.number_unsigned)); + } + else + { + oa->write_character(to_char_type(0x1B)); + write_number(static_cast(j.m_value.number_unsigned)); + } + break; + } + + case value_t::number_float: + { + if (std::isnan(j.m_value.number_float)) + { + // NaN is 0xf97e00 in CBOR + oa->write_character(to_char_type(0xF9)); + oa->write_character(to_char_type(0x7E)); + oa->write_character(to_char_type(0x00)); + } + else if (std::isinf(j.m_value.number_float)) + { + // Infinity is 0xf97c00, -Infinity is 0xf9fc00 + oa->write_character(to_char_type(0xf9)); + oa->write_character(j.m_value.number_float > 0 ? to_char_type(0x7C) : to_char_type(0xFC)); + oa->write_character(to_char_type(0x00)); + } + else + { + write_compact_float(j.m_value.number_float, detail::input_format_t::cbor); + } + break; + } + + case value_t::string: + { + // step 1: write control byte and the string length + const auto N = j.m_value.string->size(); + if (N <= 0x17) + { + write_number(static_cast(0x60 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x78)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x79)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x7A)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x7B)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write the string + oa->write_characters( + reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; + } + + case value_t::array: + { + // step 1: write control byte and the array size + const auto N = j.m_value.array->size(); + if (N <= 0x17) + { + write_number(static_cast(0x80 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x98)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x99)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x9A)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x9B)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write each element + for (const auto& el : *j.m_value.array) + { + write_cbor(el); + } + break; + } + + case value_t::binary: + { + if (j.m_value.binary->has_subtype()) + { + if (j.m_value.binary->subtype() <= (std::numeric_limits::max)()) + { + write_number(static_cast(0xd8)); + write_number(static_cast(j.m_value.binary->subtype())); + } + else if (j.m_value.binary->subtype() <= (std::numeric_limits::max)()) + { + write_number(static_cast(0xd9)); + write_number(static_cast(j.m_value.binary->subtype())); + } + else if (j.m_value.binary->subtype() <= (std::numeric_limits::max)()) + { + write_number(static_cast(0xda)); + write_number(static_cast(j.m_value.binary->subtype())); + } + else if (j.m_value.binary->subtype() <= (std::numeric_limits::max)()) + { + write_number(static_cast(0xdb)); + write_number(static_cast(j.m_value.binary->subtype())); + } + } + + // step 1: write control byte and the binary array size + const auto N = j.m_value.binary->size(); + if (N <= 0x17) + { + write_number(static_cast(0x40 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x58)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x59)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x5A)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x5B)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write each element + oa->write_characters( + reinterpret_cast(j.m_value.binary->data()), + N); + + break; + } + + case value_t::object: + { + // step 1: write control byte and the object size + const auto N = j.m_value.object->size(); + if (N <= 0x17) + { + write_number(static_cast(0xA0 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xB8)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xB9)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xBA)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xBB)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write each element + for (const auto& el : *j.m_value.object) + { + write_cbor(el.first); + write_cbor(el.second); + } + break; + } + + case value_t::discarded: + default: + break; + } + } + + /*! + @param[in] j JSON value to serialize + */ + void write_msgpack(const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::null: // nil + { + oa->write_character(to_char_type(0xC0)); + break; + } + + case value_t::boolean: // true and false + { + oa->write_character(j.m_value.boolean + ? to_char_type(0xC3) + : to_char_type(0xC2)); + break; + } + + case value_t::number_integer: + { + if (j.m_value.number_integer >= 0) + { + // MessagePack does not differentiate between positive + // signed integers and unsigned integers. Therefore, we used + // the code from the value_t::number_unsigned case here. + if (j.m_value.number_unsigned < 128) + { + // positive fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 8 + oa->write_character(to_char_type(0xCC)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 16 + oa->write_character(to_char_type(0xCD)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 32 + oa->write_character(to_char_type(0xCE)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 64 + oa->write_character(to_char_type(0xCF)); + write_number(static_cast(j.m_value.number_integer)); + } + } + else + { + if (j.m_value.number_integer >= -32) + { + // negative fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 8 + oa->write_character(to_char_type(0xD0)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 16 + oa->write_character(to_char_type(0xD1)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 32 + oa->write_character(to_char_type(0xD2)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 64 + oa->write_character(to_char_type(0xD3)); + write_number(static_cast(j.m_value.number_integer)); + } + } + break; + } + + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned < 128) + { + // positive fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 8 + oa->write_character(to_char_type(0xCC)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 16 + oa->write_character(to_char_type(0xCD)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 32 + oa->write_character(to_char_type(0xCE)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 64 + oa->write_character(to_char_type(0xCF)); + write_number(static_cast(j.m_value.number_integer)); + } + break; + } + + case value_t::number_float: + { + write_compact_float(j.m_value.number_float, detail::input_format_t::msgpack); + break; + } + + case value_t::string: + { + // step 1: write control byte and the string length + const auto N = j.m_value.string->size(); + if (N <= 31) + { + // fixstr + write_number(static_cast(0xA0 | N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // str 8 + oa->write_character(to_char_type(0xD9)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // str 16 + oa->write_character(to_char_type(0xDA)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // str 32 + oa->write_character(to_char_type(0xDB)); + write_number(static_cast(N)); + } + + // step 2: write the string + oa->write_characters( + reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; + } + + case value_t::array: + { + // step 1: write control byte and the array size + const auto N = j.m_value.array->size(); + if (N <= 15) + { + // fixarray + write_number(static_cast(0x90 | N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // array 16 + oa->write_character(to_char_type(0xDC)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // array 32 + oa->write_character(to_char_type(0xDD)); + write_number(static_cast(N)); + } + + // step 2: write each element + for (const auto& el : *j.m_value.array) + { + write_msgpack(el); + } + break; + } + + case value_t::binary: + { + // step 0: determine if the binary type has a set subtype to + // determine whether or not to use the ext or fixext types + const bool use_ext = j.m_value.binary->has_subtype(); + + // step 1: write control byte and the byte string length + const auto N = j.m_value.binary->size(); + if (N <= (std::numeric_limits::max)()) + { + std::uint8_t output_type{}; + bool fixed = true; + if (use_ext) + { + switch (N) + { + case 1: + output_type = 0xD4; // fixext 1 + break; + case 2: + output_type = 0xD5; // fixext 2 + break; + case 4: + output_type = 0xD6; // fixext 4 + break; + case 8: + output_type = 0xD7; // fixext 8 + break; + case 16: + output_type = 0xD8; // fixext 16 + break; + default: + output_type = 0xC7; // ext 8 + fixed = false; + break; + } + + } + else + { + output_type = 0xC4; // bin 8 + fixed = false; + } + + oa->write_character(to_char_type(output_type)); + if (!fixed) + { + write_number(static_cast(N)); + } + } + else if (N <= (std::numeric_limits::max)()) + { + std::uint8_t output_type = use_ext + ? 0xC8 // ext 16 + : 0xC5; // bin 16 + + oa->write_character(to_char_type(output_type)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + std::uint8_t output_type = use_ext + ? 0xC9 // ext 32 + : 0xC6; // bin 32 + + oa->write_character(to_char_type(output_type)); + write_number(static_cast(N)); + } + + // step 1.5: if this is an ext type, write the subtype + if (use_ext) + { + write_number(static_cast(j.m_value.binary->subtype())); + } + + // step 2: write the byte string + oa->write_characters( + reinterpret_cast(j.m_value.binary->data()), + N); + + break; + } + + case value_t::object: + { + // step 1: write control byte and the object size + const auto N = j.m_value.object->size(); + if (N <= 15) + { + // fixmap + write_number(static_cast(0x80 | (N & 0xF))); + } + else if (N <= (std::numeric_limits::max)()) + { + // map 16 + oa->write_character(to_char_type(0xDE)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // map 32 + oa->write_character(to_char_type(0xDF)); + write_number(static_cast(N)); + } + + // step 2: write each element + for (const auto& el : *j.m_value.object) + { + write_msgpack(el.first); + write_msgpack(el.second); + } + break; + } + + case value_t::discarded: + default: + break; + } + } + + /*! + @param[in] j JSON value to serialize + @param[in] use_count whether to use '#' prefixes (optimized format) + @param[in] use_type whether to use '$' prefixes (optimized format) + @param[in] add_prefix whether prefixes need to be used for this value + */ + void write_ubjson(const BasicJsonType& j, const bool use_count, + const bool use_type, const bool add_prefix = true) + { + switch (j.type()) + { + case value_t::null: + { + if (add_prefix) + { + oa->write_character(to_char_type('Z')); + } + break; + } + + case value_t::boolean: + { + if (add_prefix) + { + oa->write_character(j.m_value.boolean + ? to_char_type('T') + : to_char_type('F')); + } + break; + } + + case value_t::number_integer: + { + write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix); + break; + } + + case value_t::number_unsigned: + { + write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix); + break; + } + + case value_t::number_float: + { + write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix); + break; + } + + case value_t::string: + { + if (add_prefix) + { + oa->write_character(to_char_type('S')); + } + write_number_with_ubjson_prefix(j.m_value.string->size(), true); + oa->write_characters( + reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; + } + + case value_t::array: + { + if (add_prefix) + { + oa->write_character(to_char_type('[')); + } + + bool prefix_required = true; + if (use_type && !j.m_value.array->empty()) + { + JSON_ASSERT(use_count); + const CharType first_prefix = ubjson_prefix(j.front()); + const bool same_prefix = std::all_of(j.begin() + 1, j.end(), + [this, first_prefix](const BasicJsonType & v) + { + return ubjson_prefix(v) == first_prefix; + }); + + if (same_prefix) + { + prefix_required = false; + oa->write_character(to_char_type('$')); + oa->write_character(first_prefix); + } + } + + if (use_count) + { + oa->write_character(to_char_type('#')); + write_number_with_ubjson_prefix(j.m_value.array->size(), true); + } + + for (const auto& el : *j.m_value.array) + { + write_ubjson(el, use_count, use_type, prefix_required); + } + + if (!use_count) + { + oa->write_character(to_char_type(']')); + } + + break; + } + + case value_t::binary: + { + if (add_prefix) + { + oa->write_character(to_char_type('[')); + } + + if (use_type && !j.m_value.binary->empty()) + { + JSON_ASSERT(use_count); + oa->write_character(to_char_type('$')); + oa->write_character('U'); + } + + if (use_count) + { + oa->write_character(to_char_type('#')); + write_number_with_ubjson_prefix(j.m_value.binary->size(), true); + } + + if (use_type) + { + oa->write_characters( + reinterpret_cast(j.m_value.binary->data()), + j.m_value.binary->size()); + } + else + { + for (size_t i = 0; i < j.m_value.binary->size(); ++i) + { + oa->write_character(to_char_type('U')); + oa->write_character(j.m_value.binary->data()[i]); + } + } + + if (!use_count) + { + oa->write_character(to_char_type(']')); + } + + break; + } + + case value_t::object: + { + if (add_prefix) + { + oa->write_character(to_char_type('{')); + } + + bool prefix_required = true; + if (use_type && !j.m_value.object->empty()) + { + JSON_ASSERT(use_count); + const CharType first_prefix = ubjson_prefix(j.front()); + const bool same_prefix = std::all_of(j.begin(), j.end(), + [this, first_prefix](const BasicJsonType & v) + { + return ubjson_prefix(v) == first_prefix; + }); + + if (same_prefix) + { + prefix_required = false; + oa->write_character(to_char_type('$')); + oa->write_character(first_prefix); + } + } + + if (use_count) + { + oa->write_character(to_char_type('#')); + write_number_with_ubjson_prefix(j.m_value.object->size(), true); + } + + for (const auto& el : *j.m_value.object) + { + write_number_with_ubjson_prefix(el.first.size(), true); + oa->write_characters( + reinterpret_cast(el.first.c_str()), + el.first.size()); + write_ubjson(el.second, use_count, use_type, prefix_required); + } + + if (!use_count) + { + oa->write_character(to_char_type('}')); + } + + break; + } + + case value_t::discarded: + default: + break; + } + } + + private: + ////////// + // BSON // + ////////// + + /*! + @return The size of a BSON document entry header, including the id marker + and the entry name size (and its null-terminator). + */ + static std::size_t calc_bson_entry_header_size(const string_t& name, const BasicJsonType& j) + { + const auto it = name.find(static_cast(0)); + if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos)) + { + JSON_THROW(out_of_range::create(409, "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")", j)); + static_cast(j); + } + + return /*id*/ 1ul + name.size() + /*zero-terminator*/1u; + } + + /*! + @brief Writes the given @a element_type and @a name to the output adapter + */ + void write_bson_entry_header(const string_t& name, + const std::uint8_t element_type) + { + oa->write_character(to_char_type(element_type)); // boolean + oa->write_characters( + reinterpret_cast(name.c_str()), + name.size() + 1u); + } + + /*! + @brief Writes a BSON element with key @a name and boolean value @a value + */ + void write_bson_boolean(const string_t& name, + const bool value) + { + write_bson_entry_header(name, 0x08); + oa->write_character(value ? to_char_type(0x01) : to_char_type(0x00)); + } + + /*! + @brief Writes a BSON element with key @a name and double value @a value + */ + void write_bson_double(const string_t& name, + const double value) + { + write_bson_entry_header(name, 0x01); + write_number(value); + } + + /*! + @return The size of the BSON-encoded string in @a value + */ + static std::size_t calc_bson_string_size(const string_t& value) + { + return sizeof(std::int32_t) + value.size() + 1ul; + } + + /*! + @brief Writes a BSON element with key @a name and string value @a value + */ + void write_bson_string(const string_t& name, + const string_t& value) + { + write_bson_entry_header(name, 0x02); + + write_number(static_cast(value.size() + 1ul)); + oa->write_characters( + reinterpret_cast(value.c_str()), + value.size() + 1); + } + + /*! + @brief Writes a BSON element with key @a name and null value + */ + void write_bson_null(const string_t& name) + { + write_bson_entry_header(name, 0x0A); + } + + /*! + @return The size of the BSON-encoded integer @a value + */ + static std::size_t calc_bson_integer_size(const std::int64_t value) + { + return (std::numeric_limits::min)() <= value && value <= (std::numeric_limits::max)() + ? sizeof(std::int32_t) + : sizeof(std::int64_t); + } + + /*! + @brief Writes a BSON element with key @a name and integer @a value + */ + void write_bson_integer(const string_t& name, + const std::int64_t value) + { + if ((std::numeric_limits::min)() <= value && value <= (std::numeric_limits::max)()) + { + write_bson_entry_header(name, 0x10); // int32 + write_number(static_cast(value)); + } + else + { + write_bson_entry_header(name, 0x12); // int64 + write_number(static_cast(value)); + } + } + + /*! + @return The size of the BSON-encoded unsigned integer in @a j + */ + static constexpr std::size_t calc_bson_unsigned_size(const std::uint64_t value) noexcept + { + return (value <= static_cast((std::numeric_limits::max)())) + ? sizeof(std::int32_t) + : sizeof(std::int64_t); + } + + /*! + @brief Writes a BSON element with key @a name and unsigned @a value + */ + void write_bson_unsigned(const string_t& name, + const BasicJsonType& j) + { + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + write_bson_entry_header(name, 0x10 /* int32 */); + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + write_bson_entry_header(name, 0x12 /* int64 */); + write_number(static_cast(j.m_value.number_unsigned)); + } + else + { + JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(j.m_value.number_unsigned) + " cannot be represented by BSON as it does not fit int64", j)); + } + } + + /*! + @brief Writes a BSON element with key @a name and object @a value + */ + void write_bson_object_entry(const string_t& name, + const typename BasicJsonType::object_t& value) + { + write_bson_entry_header(name, 0x03); // object + write_bson_object(value); + } + + /*! + @return The size of the BSON-encoded array @a value + */ + static std::size_t calc_bson_array_size(const typename BasicJsonType::array_t& value) + { + std::size_t array_index = 0ul; + + const std::size_t embedded_document_size = std::accumulate(std::begin(value), std::end(value), static_cast(0), [&array_index](std::size_t result, const typename BasicJsonType::array_t::value_type & el) + { + return result + calc_bson_element_size(std::to_string(array_index++), el); + }); + + return sizeof(std::int32_t) + embedded_document_size + 1ul; + } + + /*! + @return The size of the BSON-encoded binary array @a value + */ + static std::size_t calc_bson_binary_size(const typename BasicJsonType::binary_t& value) + { + return sizeof(std::int32_t) + value.size() + 1ul; + } + + /*! + @brief Writes a BSON element with key @a name and array @a value + */ + void write_bson_array(const string_t& name, + const typename BasicJsonType::array_t& value) + { + write_bson_entry_header(name, 0x04); // array + write_number(static_cast(calc_bson_array_size(value))); + + std::size_t array_index = 0ul; + + for (const auto& el : value) + { + write_bson_element(std::to_string(array_index++), el); + } + + oa->write_character(to_char_type(0x00)); + } + + /*! + @brief Writes a BSON element with key @a name and binary value @a value + */ + void write_bson_binary(const string_t& name, + const binary_t& value) + { + write_bson_entry_header(name, 0x05); + + write_number(static_cast(value.size())); + write_number(value.has_subtype() ? static_cast(value.subtype()) : static_cast(0x00)); + + oa->write_characters(reinterpret_cast(value.data()), value.size()); + } + + /*! + @brief Calculates the size necessary to serialize the JSON value @a j with its @a name + @return The calculated size for the BSON document entry for @a j with the given @a name. + */ + static std::size_t calc_bson_element_size(const string_t& name, + const BasicJsonType& j) + { + const auto header_size = calc_bson_entry_header_size(name, j); + switch (j.type()) + { + case value_t::object: + return header_size + calc_bson_object_size(*j.m_value.object); + + case value_t::array: + return header_size + calc_bson_array_size(*j.m_value.array); + + case value_t::binary: + return header_size + calc_bson_binary_size(*j.m_value.binary); + + case value_t::boolean: + return header_size + 1ul; + + case value_t::number_float: + return header_size + 8ul; + + case value_t::number_integer: + return header_size + calc_bson_integer_size(j.m_value.number_integer); + + case value_t::number_unsigned: + return header_size + calc_bson_unsigned_size(j.m_value.number_unsigned); + + case value_t::string: + return header_size + calc_bson_string_size(*j.m_value.string); + + case value_t::null: + return header_size + 0ul; + + // LCOV_EXCL_START + case value_t::discarded: + default: + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) + return 0ul; + // LCOV_EXCL_STOP + } + } + + /*! + @brief Serializes the JSON value @a j to BSON and associates it with the + key @a name. + @param name The name to associate with the JSON entity @a j within the + current BSON document + */ + void write_bson_element(const string_t& name, + const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::object: + return write_bson_object_entry(name, *j.m_value.object); + + case value_t::array: + return write_bson_array(name, *j.m_value.array); + + case value_t::binary: + return write_bson_binary(name, *j.m_value.binary); + + case value_t::boolean: + return write_bson_boolean(name, j.m_value.boolean); + + case value_t::number_float: + return write_bson_double(name, j.m_value.number_float); + + case value_t::number_integer: + return write_bson_integer(name, j.m_value.number_integer); + + case value_t::number_unsigned: + return write_bson_unsigned(name, j); + + case value_t::string: + return write_bson_string(name, *j.m_value.string); + + case value_t::null: + return write_bson_null(name); + + // LCOV_EXCL_START + case value_t::discarded: + default: + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) + return; + // LCOV_EXCL_STOP + } + } + + /*! + @brief Calculates the size of the BSON serialization of the given + JSON-object @a j. + @param[in] value JSON value to serialize + @pre value.type() == value_t::object + */ + static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value) + { + std::size_t document_size = std::accumulate(value.begin(), value.end(), static_cast(0), + [](size_t result, const typename BasicJsonType::object_t::value_type & el) + { + return result += calc_bson_element_size(el.first, el.second); + }); + + return sizeof(std::int32_t) + document_size + 1ul; + } + + /*! + @param[in] value JSON value to serialize + @pre value.type() == value_t::object + */ + void write_bson_object(const typename BasicJsonType::object_t& value) + { + write_number(static_cast(calc_bson_object_size(value))); + + for (const auto& el : value) + { + write_bson_element(el.first, el.second); + } + + oa->write_character(to_char_type(0x00)); + } + + ////////// + // CBOR // + ////////// + + static constexpr CharType get_cbor_float_prefix(float /*unused*/) + { + return to_char_type(0xFA); // Single-Precision Float + } + + static constexpr CharType get_cbor_float_prefix(double /*unused*/) + { + return to_char_type(0xFB); // Double-Precision Float + } + + ///////////// + // MsgPack // + ///////////// + + static constexpr CharType get_msgpack_float_prefix(float /*unused*/) + { + return to_char_type(0xCA); // float 32 + } + + static constexpr CharType get_msgpack_float_prefix(double /*unused*/) + { + return to_char_type(0xCB); // float 64 + } + + //////////// + // UBJSON // + //////////// + + // UBJSON: write number (floating point) + template::value, int>::type = 0> + void write_number_with_ubjson_prefix(const NumberType n, + const bool add_prefix) + { + if (add_prefix) + { + oa->write_character(get_ubjson_float_prefix(n)); + } + write_number(n); + } + + // UBJSON: write number (unsigned integer) + template::value, int>::type = 0> + void write_number_with_ubjson_prefix(const NumberType n, + const bool add_prefix) + { + if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('i')); // int8 + } + write_number(static_cast(n)); + } + else if (n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('U')); // uint8 + } + write_number(static_cast(n)); + } + else if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('I')); // int16 + } + write_number(static_cast(n)); + } + else if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('l')); // int32 + } + write_number(static_cast(n)); + } + else if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('L')); // int64 + } + write_number(static_cast(n)); + } + else + { + if (add_prefix) + { + oa->write_character(to_char_type('H')); // high-precision number + } + + const auto number = BasicJsonType(n).dump(); + write_number_with_ubjson_prefix(number.size(), true); + for (std::size_t i = 0; i < number.size(); ++i) + { + oa->write_character(to_char_type(static_cast(number[i]))); + } + } + } + + // UBJSON: write number (signed integer) + template < typename NumberType, typename std::enable_if < + std::is_signed::value&& + !std::is_floating_point::value, int >::type = 0 > + void write_number_with_ubjson_prefix(const NumberType n, + const bool add_prefix) + { + if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('i')); // int8 + } + write_number(static_cast(n)); + } + else if (static_cast((std::numeric_limits::min)()) <= n && n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('U')); // uint8 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('I')); // int16 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('l')); // int32 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('L')); // int64 + } + write_number(static_cast(n)); + } + // LCOV_EXCL_START + else + { + if (add_prefix) + { + oa->write_character(to_char_type('H')); // high-precision number + } + + const auto number = BasicJsonType(n).dump(); + write_number_with_ubjson_prefix(number.size(), true); + for (std::size_t i = 0; i < number.size(); ++i) + { + oa->write_character(to_char_type(static_cast(number[i]))); + } + } + // LCOV_EXCL_STOP + } + + /*! + @brief determine the type prefix of container values + */ + CharType ubjson_prefix(const BasicJsonType& j) const noexcept + { + switch (j.type()) + { + case value_t::null: + return 'Z'; + + case value_t::boolean: + return j.m_value.boolean ? 'T' : 'F'; + + case value_t::number_integer: + { + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'i'; + } + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'U'; + } + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'I'; + } + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'l'; + } + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'L'; + } + // anything else is treated as high-precision number + return 'H'; // LCOV_EXCL_LINE + } + + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'i'; + } + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'U'; + } + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'I'; + } + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'l'; + } + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'L'; + } + // anything else is treated as high-precision number + return 'H'; // LCOV_EXCL_LINE + } + + case value_t::number_float: + return get_ubjson_float_prefix(j.m_value.number_float); + + case value_t::string: + return 'S'; + + case value_t::array: // fallthrough + case value_t::binary: + return '['; + + case value_t::object: + return '{'; + + case value_t::discarded: + default: // discarded values + return 'N'; + } + } + + static constexpr CharType get_ubjson_float_prefix(float /*unused*/) + { + return 'd'; // float 32 + } + + static constexpr CharType get_ubjson_float_prefix(double /*unused*/) + { + return 'D'; // float 64 + } + + /////////////////////// + // Utility functions // + /////////////////////// + + /* + @brief write a number to output input + @param[in] n number of type @a NumberType + @tparam NumberType the type of the number + @tparam OutputIsLittleEndian Set to true if output data is + required to be little endian + + @note This function needs to respect the system's endianness, because bytes + in CBOR, MessagePack, and UBJSON are stored in network order (big + endian) and therefore need reordering on little endian systems. + */ + template + void write_number(const NumberType n) + { + // step 1: write number to array of length NumberType + std::array vec{}; + std::memcpy(vec.data(), &n, sizeof(NumberType)); + + // step 2: write array to output (with possible reordering) + if (is_little_endian != OutputIsLittleEndian) + { + // reverse byte order prior to conversion if necessary + std::reverse(vec.begin(), vec.end()); + } + + oa->write_characters(vec.data(), sizeof(NumberType)); + } + + void write_compact_float(const number_float_t n, detail::input_format_t format) + { +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + if (static_cast(n) >= static_cast(std::numeric_limits::lowest()) && + static_cast(n) <= static_cast((std::numeric_limits::max)()) && + static_cast(static_cast(n)) == static_cast(n)) + { + oa->write_character(format == detail::input_format_t::cbor + ? get_cbor_float_prefix(static_cast(n)) + : get_msgpack_float_prefix(static_cast(n))); + write_number(static_cast(n)); + } + else + { + oa->write_character(format == detail::input_format_t::cbor + ? get_cbor_float_prefix(n) + : get_msgpack_float_prefix(n)); + write_number(n); + } +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + } + + public: + // The following to_char_type functions are implement the conversion + // between uint8_t and CharType. In case CharType is not unsigned, + // such a conversion is required to allow values greater than 128. + // See for a discussion. + template < typename C = CharType, + enable_if_t < std::is_signed::value && std::is_signed::value > * = nullptr > + static constexpr CharType to_char_type(std::uint8_t x) noexcept + { + return *reinterpret_cast(&x); + } + + template < typename C = CharType, + enable_if_t < std::is_signed::value && std::is_unsigned::value > * = nullptr > + static CharType to_char_type(std::uint8_t x) noexcept + { + static_assert(sizeof(std::uint8_t) == sizeof(CharType), "size of CharType must be equal to std::uint8_t"); + static_assert(std::is_trivial::value, "CharType must be trivial"); + CharType result; + std::memcpy(&result, &x, sizeof(x)); + return result; + } + + template::value>* = nullptr> + static constexpr CharType to_char_type(std::uint8_t x) noexcept + { + return x; + } + + template < typename InputCharType, typename C = CharType, + enable_if_t < + std::is_signed::value && + std::is_signed::value && + std::is_same::type>::value + > * = nullptr > + static constexpr CharType to_char_type(InputCharType x) noexcept + { + return x; + } + + private: + /// whether we can assume little endianness + const bool is_little_endian = little_endianness(); + + /// the output + output_adapter_t oa = nullptr; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // reverse, remove, fill, find, none_of +#include // array +#include // localeconv, lconv +#include // labs, isfinite, isnan, signbit +#include // size_t, ptrdiff_t +#include // uint8_t +#include // snprintf +#include // numeric_limits +#include // string, char_traits +#include // setfill, setw +#include // stringstream +#include // is_same +#include // move + +// #include + + +#include // array +#include // signbit, isfinite +#include // intN_t, uintN_t +#include // memcpy, memmove +#include // numeric_limits +#include // conditional + +// #include + + +namespace nlohmann +{ +namespace detail +{ + +/*! +@brief implements the Grisu2 algorithm for binary to decimal floating-point +conversion. + +This implementation is a slightly modified version of the reference +implementation which may be obtained from +http://florian.loitsch.com/publications (bench.tar.gz). + +The code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch. + +For a detailed description of the algorithm see: + +[1] Loitsch, "Printing Floating-Point Numbers Quickly and Accurately with + Integers", Proceedings of the ACM SIGPLAN 2010 Conference on Programming + Language Design and Implementation, PLDI 2010 +[2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and Accurately", + Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language + Design and Implementation, PLDI 1996 +*/ +namespace dtoa_impl +{ + +template +Target reinterpret_bits(const Source source) +{ + static_assert(sizeof(Target) == sizeof(Source), "size mismatch"); + + Target target; + std::memcpy(&target, &source, sizeof(Source)); + return target; +} + +struct diyfp // f * 2^e +{ + static constexpr int kPrecision = 64; // = q + + std::uint64_t f = 0; + int e = 0; + + constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {} + + /*! + @brief returns x - y + @pre x.e == y.e and x.f >= y.f + */ + static diyfp sub(const diyfp& x, const diyfp& y) noexcept + { + JSON_ASSERT(x.e == y.e); + JSON_ASSERT(x.f >= y.f); + + return {x.f - y.f, x.e}; + } + + /*! + @brief returns x * y + @note The result is rounded. (Only the upper q bits are returned.) + */ + static diyfp mul(const diyfp& x, const diyfp& y) noexcept + { + static_assert(kPrecision == 64, "internal error"); + + // Computes: + // f = round((x.f * y.f) / 2^q) + // e = x.e + y.e + q + + // Emulate the 64-bit * 64-bit multiplication: + // + // p = u * v + // = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi) + // = (u_lo v_lo ) + 2^32 ((u_lo v_hi ) + (u_hi v_lo )) + 2^64 (u_hi v_hi ) + // = (p0 ) + 2^32 ((p1 ) + (p2 )) + 2^64 (p3 ) + // = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3 ) + // = (p0_lo ) + 2^32 (p0_hi + p1_lo + p2_lo ) + 2^64 (p1_hi + p2_hi + p3) + // = (p0_lo ) + 2^32 (Q ) + 2^64 (H ) + // = (p0_lo ) + 2^32 (Q_lo + 2^32 Q_hi ) + 2^64 (H ) + // + // (Since Q might be larger than 2^32 - 1) + // + // = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H) + // + // (Q_hi + H does not overflow a 64-bit int) + // + // = p_lo + 2^64 p_hi + + const std::uint64_t u_lo = x.f & 0xFFFFFFFFu; + const std::uint64_t u_hi = x.f >> 32u; + const std::uint64_t v_lo = y.f & 0xFFFFFFFFu; + const std::uint64_t v_hi = y.f >> 32u; + + const std::uint64_t p0 = u_lo * v_lo; + const std::uint64_t p1 = u_lo * v_hi; + const std::uint64_t p2 = u_hi * v_lo; + const std::uint64_t p3 = u_hi * v_hi; + + const std::uint64_t p0_hi = p0 >> 32u; + const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu; + const std::uint64_t p1_hi = p1 >> 32u; + const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu; + const std::uint64_t p2_hi = p2 >> 32u; + + std::uint64_t Q = p0_hi + p1_lo + p2_lo; + + // The full product might now be computed as + // + // p_hi = p3 + p2_hi + p1_hi + (Q >> 32) + // p_lo = p0_lo + (Q << 32) + // + // But in this particular case here, the full p_lo is not required. + // Effectively we only need to add the highest bit in p_lo to p_hi (and + // Q_hi + 1 does not overflow). + + Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up + + const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u); + + return {h, x.e + y.e + 64}; + } + + /*! + @brief normalize x such that the significand is >= 2^(q-1) + @pre x.f != 0 + */ + static diyfp normalize(diyfp x) noexcept + { + JSON_ASSERT(x.f != 0); + + while ((x.f >> 63u) == 0) + { + x.f <<= 1u; + x.e--; + } + + return x; + } + + /*! + @brief normalize x such that the result has the exponent E + @pre e >= x.e and the upper e - x.e bits of x.f must be zero. + */ + static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept + { + const int delta = x.e - target_exponent; + + JSON_ASSERT(delta >= 0); + JSON_ASSERT(((x.f << delta) >> delta) == x.f); + + return {x.f << delta, target_exponent}; + } +}; + +struct boundaries +{ + diyfp w; + diyfp minus; + diyfp plus; +}; + +/*! +Compute the (normalized) diyfp representing the input number 'value' and its +boundaries. + +@pre value must be finite and positive +*/ +template +boundaries compute_boundaries(FloatType value) +{ + JSON_ASSERT(std::isfinite(value)); + JSON_ASSERT(value > 0); + + // Convert the IEEE representation into a diyfp. + // + // If v is denormal: + // value = 0.F * 2^(1 - bias) = ( F) * 2^(1 - bias - (p-1)) + // If v is normalized: + // value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1)) + + static_assert(std::numeric_limits::is_iec559, + "internal error: dtoa_short requires an IEEE-754 floating-point implementation"); + + constexpr int kPrecision = std::numeric_limits::digits; // = p (includes the hidden bit) + constexpr int kBias = std::numeric_limits::max_exponent - 1 + (kPrecision - 1); + constexpr int kMinExp = 1 - kBias; + constexpr std::uint64_t kHiddenBit = std::uint64_t{1} << (kPrecision - 1); // = 2^(p-1) + + using bits_type = typename std::conditional::type; + + const auto bits = static_cast(reinterpret_bits(value)); + const std::uint64_t E = bits >> (kPrecision - 1); + const std::uint64_t F = bits & (kHiddenBit - 1); + + const bool is_denormal = E == 0; + const diyfp v = is_denormal + ? diyfp(F, kMinExp) + : diyfp(F + kHiddenBit, static_cast(E) - kBias); + + // Compute the boundaries m- and m+ of the floating-point value + // v = f * 2^e. + // + // Determine v- and v+, the floating-point predecessor and successor if v, + // respectively. + // + // v- = v - 2^e if f != 2^(p-1) or e == e_min (A) + // = v - 2^(e-1) if f == 2^(p-1) and e > e_min (B) + // + // v+ = v + 2^e + // + // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_ + // between m- and m+ round to v, regardless of how the input rounding + // algorithm breaks ties. + // + // ---+-------------+-------------+-------------+-------------+--- (A) + // v- m- v m+ v+ + // + // -----------------+------+------+-------------+-------------+--- (B) + // v- m- v m+ v+ + + const bool lower_boundary_is_closer = F == 0 && E > 1; + const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1); + const diyfp m_minus = lower_boundary_is_closer + ? diyfp(4 * v.f - 1, v.e - 2) // (B) + : diyfp(2 * v.f - 1, v.e - 1); // (A) + + // Determine the normalized w+ = m+. + const diyfp w_plus = diyfp::normalize(m_plus); + + // Determine w- = m- such that e_(w-) = e_(w+). + const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e); + + return {diyfp::normalize(v), w_minus, w_plus}; +} + +// Given normalized diyfp w, Grisu needs to find a (normalized) cached +// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies +// within a certain range [alpha, gamma] (Definition 3.2 from [1]) +// +// alpha <= e = e_c + e_w + q <= gamma +// +// or +// +// f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q +// <= f_c * f_w * 2^gamma +// +// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies +// +// 2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma +// +// or +// +// 2^(q - 2 + alpha) <= c * w < 2^(q + gamma) +// +// The choice of (alpha,gamma) determines the size of the table and the form of +// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well +// in practice: +// +// The idea is to cut the number c * w = f * 2^e into two parts, which can be +// processed independently: An integral part p1, and a fractional part p2: +// +// f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e +// = (f div 2^-e) + (f mod 2^-e) * 2^e +// = p1 + p2 * 2^e +// +// The conversion of p1 into decimal form requires a series of divisions and +// modulos by (a power of) 10. These operations are faster for 32-bit than for +// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be +// achieved by choosing +// +// -e >= 32 or e <= -32 := gamma +// +// In order to convert the fractional part +// +// p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ... +// +// into decimal form, the fraction is repeatedly multiplied by 10 and the digits +// d[-i] are extracted in order: +// +// (10 * p2) div 2^-e = d[-1] +// (10 * p2) mod 2^-e = d[-2] / 10^1 + ... +// +// The multiplication by 10 must not overflow. It is sufficient to choose +// +// 10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64. +// +// Since p2 = f mod 2^-e < 2^-e, +// +// -e <= 60 or e >= -60 := alpha + +constexpr int kAlpha = -60; +constexpr int kGamma = -32; + +struct cached_power // c = f * 2^e ~= 10^k +{ + std::uint64_t f; + int e; + int k; +}; + +/*! +For a normalized diyfp w = f * 2^e, this function returns a (normalized) cached +power-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c +satisfies (Definition 3.2 from [1]) + + alpha <= e_c + e + q <= gamma. +*/ +inline cached_power get_cached_power_for_binary_exponent(int e) +{ + // Now + // + // alpha <= e_c + e + q <= gamma (1) + // ==> f_c * 2^alpha <= c * 2^e * 2^q + // + // and since the c's are normalized, 2^(q-1) <= f_c, + // + // ==> 2^(q - 1 + alpha) <= c * 2^(e + q) + // ==> 2^(alpha - e - 1) <= c + // + // If c were an exact power of ten, i.e. c = 10^k, one may determine k as + // + // k = ceil( log_10( 2^(alpha - e - 1) ) ) + // = ceil( (alpha - e - 1) * log_10(2) ) + // + // From the paper: + // "In theory the result of the procedure could be wrong since c is rounded, + // and the computation itself is approximated [...]. In practice, however, + // this simple function is sufficient." + // + // For IEEE double precision floating-point numbers converted into + // normalized diyfp's w = f * 2^e, with q = 64, + // + // e >= -1022 (min IEEE exponent) + // -52 (p - 1) + // -52 (p - 1, possibly normalize denormal IEEE numbers) + // -11 (normalize the diyfp) + // = -1137 + // + // and + // + // e <= +1023 (max IEEE exponent) + // -52 (p - 1) + // -11 (normalize the diyfp) + // = 960 + // + // This binary exponent range [-1137,960] results in a decimal exponent + // range [-307,324]. One does not need to store a cached power for each + // k in this range. For each such k it suffices to find a cached power + // such that the exponent of the product lies in [alpha,gamma]. + // This implies that the difference of the decimal exponents of adjacent + // table entries must be less than or equal to + // + // floor( (gamma - alpha) * log_10(2) ) = 8. + // + // (A smaller distance gamma-alpha would require a larger table.) + + // NB: + // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34. + + constexpr int kCachedPowersMinDecExp = -300; + constexpr int kCachedPowersDecStep = 8; + + static constexpr std::array kCachedPowers = + { + { + { 0xAB70FE17C79AC6CA, -1060, -300 }, + { 0xFF77B1FCBEBCDC4F, -1034, -292 }, + { 0xBE5691EF416BD60C, -1007, -284 }, + { 0x8DD01FAD907FFC3C, -980, -276 }, + { 0xD3515C2831559A83, -954, -268 }, + { 0x9D71AC8FADA6C9B5, -927, -260 }, + { 0xEA9C227723EE8BCB, -901, -252 }, + { 0xAECC49914078536D, -874, -244 }, + { 0x823C12795DB6CE57, -847, -236 }, + { 0xC21094364DFB5637, -821, -228 }, + { 0x9096EA6F3848984F, -794, -220 }, + { 0xD77485CB25823AC7, -768, -212 }, + { 0xA086CFCD97BF97F4, -741, -204 }, + { 0xEF340A98172AACE5, -715, -196 }, + { 0xB23867FB2A35B28E, -688, -188 }, + { 0x84C8D4DFD2C63F3B, -661, -180 }, + { 0xC5DD44271AD3CDBA, -635, -172 }, + { 0x936B9FCEBB25C996, -608, -164 }, + { 0xDBAC6C247D62A584, -582, -156 }, + { 0xA3AB66580D5FDAF6, -555, -148 }, + { 0xF3E2F893DEC3F126, -529, -140 }, + { 0xB5B5ADA8AAFF80B8, -502, -132 }, + { 0x87625F056C7C4A8B, -475, -124 }, + { 0xC9BCFF6034C13053, -449, -116 }, + { 0x964E858C91BA2655, -422, -108 }, + { 0xDFF9772470297EBD, -396, -100 }, + { 0xA6DFBD9FB8E5B88F, -369, -92 }, + { 0xF8A95FCF88747D94, -343, -84 }, + { 0xB94470938FA89BCF, -316, -76 }, + { 0x8A08F0F8BF0F156B, -289, -68 }, + { 0xCDB02555653131B6, -263, -60 }, + { 0x993FE2C6D07B7FAC, -236, -52 }, + { 0xE45C10C42A2B3B06, -210, -44 }, + { 0xAA242499697392D3, -183, -36 }, + { 0xFD87B5F28300CA0E, -157, -28 }, + { 0xBCE5086492111AEB, -130, -20 }, + { 0x8CBCCC096F5088CC, -103, -12 }, + { 0xD1B71758E219652C, -77, -4 }, + { 0x9C40000000000000, -50, 4 }, + { 0xE8D4A51000000000, -24, 12 }, + { 0xAD78EBC5AC620000, 3, 20 }, + { 0x813F3978F8940984, 30, 28 }, + { 0xC097CE7BC90715B3, 56, 36 }, + { 0x8F7E32CE7BEA5C70, 83, 44 }, + { 0xD5D238A4ABE98068, 109, 52 }, + { 0x9F4F2726179A2245, 136, 60 }, + { 0xED63A231D4C4FB27, 162, 68 }, + { 0xB0DE65388CC8ADA8, 189, 76 }, + { 0x83C7088E1AAB65DB, 216, 84 }, + { 0xC45D1DF942711D9A, 242, 92 }, + { 0x924D692CA61BE758, 269, 100 }, + { 0xDA01EE641A708DEA, 295, 108 }, + { 0xA26DA3999AEF774A, 322, 116 }, + { 0xF209787BB47D6B85, 348, 124 }, + { 0xB454E4A179DD1877, 375, 132 }, + { 0x865B86925B9BC5C2, 402, 140 }, + { 0xC83553C5C8965D3D, 428, 148 }, + { 0x952AB45CFA97A0B3, 455, 156 }, + { 0xDE469FBD99A05FE3, 481, 164 }, + { 0xA59BC234DB398C25, 508, 172 }, + { 0xF6C69A72A3989F5C, 534, 180 }, + { 0xB7DCBF5354E9BECE, 561, 188 }, + { 0x88FCF317F22241E2, 588, 196 }, + { 0xCC20CE9BD35C78A5, 614, 204 }, + { 0x98165AF37B2153DF, 641, 212 }, + { 0xE2A0B5DC971F303A, 667, 220 }, + { 0xA8D9D1535CE3B396, 694, 228 }, + { 0xFB9B7CD9A4A7443C, 720, 236 }, + { 0xBB764C4CA7A44410, 747, 244 }, + { 0x8BAB8EEFB6409C1A, 774, 252 }, + { 0xD01FEF10A657842C, 800, 260 }, + { 0x9B10A4E5E9913129, 827, 268 }, + { 0xE7109BFBA19C0C9D, 853, 276 }, + { 0xAC2820D9623BF429, 880, 284 }, + { 0x80444B5E7AA7CF85, 907, 292 }, + { 0xBF21E44003ACDD2D, 933, 300 }, + { 0x8E679C2F5E44FF8F, 960, 308 }, + { 0xD433179D9C8CB841, 986, 316 }, + { 0x9E19DB92B4E31BA9, 1013, 324 }, + } + }; + + // This computation gives exactly the same results for k as + // k = ceil((kAlpha - e - 1) * 0.30102999566398114) + // for |e| <= 1500, but doesn't require floating-point operations. + // NB: log_10(2) ~= 78913 / 2^18 + JSON_ASSERT(e >= -1500); + JSON_ASSERT(e <= 1500); + const int f = kAlpha - e - 1; + const int k = (f * 78913) / (1 << 18) + static_cast(f > 0); + + const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep; + JSON_ASSERT(index >= 0); + JSON_ASSERT(static_cast(index) < kCachedPowers.size()); + + const cached_power cached = kCachedPowers[static_cast(index)]; + JSON_ASSERT(kAlpha <= cached.e + e + 64); + JSON_ASSERT(kGamma >= cached.e + e + 64); + + return cached; +} + +/*! +For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k. +For n == 0, returns 1 and sets pow10 := 1. +*/ +inline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10) +{ + // LCOV_EXCL_START + if (n >= 1000000000) + { + pow10 = 1000000000; + return 10; + } + // LCOV_EXCL_STOP + if (n >= 100000000) + { + pow10 = 100000000; + return 9; + } + if (n >= 10000000) + { + pow10 = 10000000; + return 8; + } + if (n >= 1000000) + { + pow10 = 1000000; + return 7; + } + if (n >= 100000) + { + pow10 = 100000; + return 6; + } + if (n >= 10000) + { + pow10 = 10000; + return 5; + } + if (n >= 1000) + { + pow10 = 1000; + return 4; + } + if (n >= 100) + { + pow10 = 100; + return 3; + } + if (n >= 10) + { + pow10 = 10; + return 2; + } + + pow10 = 1; + return 1; +} + +inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta, + std::uint64_t rest, std::uint64_t ten_k) +{ + JSON_ASSERT(len >= 1); + JSON_ASSERT(dist <= delta); + JSON_ASSERT(rest <= delta); + JSON_ASSERT(ten_k > 0); + + // <--------------------------- delta ----> + // <---- dist ---------> + // --------------[------------------+-------------------]-------------- + // M- w M+ + // + // ten_k + // <------> + // <---- rest ----> + // --------------[------------------+----+--------------]-------------- + // w V + // = buf * 10^k + // + // ten_k represents a unit-in-the-last-place in the decimal representation + // stored in buf. + // Decrement buf by ten_k while this takes buf closer to w. + + // The tests are written in this order to avoid overflow in unsigned + // integer arithmetic. + + while (rest < dist + && delta - rest >= ten_k + && (rest + ten_k < dist || dist - rest > rest + ten_k - dist)) + { + JSON_ASSERT(buf[len - 1] != '0'); + buf[len - 1]--; + rest += ten_k; + } +} + +/*! +Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+. +M- and M+ must be normalized and share the same exponent -60 <= e <= -32. +*/ +inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, + diyfp M_minus, diyfp w, diyfp M_plus) +{ + static_assert(kAlpha >= -60, "internal error"); + static_assert(kGamma <= -32, "internal error"); + + // Generates the digits (and the exponent) of a decimal floating-point + // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's + // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma. + // + // <--------------------------- delta ----> + // <---- dist ---------> + // --------------[------------------+-------------------]-------------- + // M- w M+ + // + // Grisu2 generates the digits of M+ from left to right and stops as soon as + // V is in [M-,M+]. + + JSON_ASSERT(M_plus.e >= kAlpha); + JSON_ASSERT(M_plus.e <= kGamma); + + std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e) + std::uint64_t dist = diyfp::sub(M_plus, w ).f; // (significand of (M+ - w ), implicit exponent is e) + + // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0): + // + // M+ = f * 2^e + // = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e + // = ((p1 ) * 2^-e + (p2 )) * 2^e + // = p1 + p2 * 2^e + + const diyfp one(std::uint64_t{1} << -M_plus.e, M_plus.e); + + auto p1 = static_cast(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.) + std::uint64_t p2 = M_plus.f & (one.f - 1); // p2 = f mod 2^-e + + // 1) + // + // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0] + + JSON_ASSERT(p1 > 0); + + std::uint32_t pow10{}; + const int k = find_largest_pow10(p1, pow10); + + // 10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1) + // + // p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1)) + // = (d[k-1] ) * 10^(k-1) + (p1 mod 10^(k-1)) + // + // M+ = p1 + p2 * 2^e + // = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1)) + p2 * 2^e + // = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e + // = d[k-1] * 10^(k-1) + ( rest) * 2^e + // + // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0) + // + // p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0] + // + // but stop as soon as + // + // rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e + + int n = k; + while (n > 0) + { + // Invariants: + // M+ = buffer * 10^n + (p1 + p2 * 2^e) (buffer = 0 for n = k) + // pow10 = 10^(n-1) <= p1 < 10^n + // + const std::uint32_t d = p1 / pow10; // d = p1 div 10^(n-1) + const std::uint32_t r = p1 % pow10; // r = p1 mod 10^(n-1) + // + // M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e + // = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e) + // + JSON_ASSERT(d <= 9); + buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d + // + // M+ = buffer * 10^(n-1) + (r + p2 * 2^e) + // + p1 = r; + n--; + // + // M+ = buffer * 10^n + (p1 + p2 * 2^e) + // pow10 = 10^n + // + + // Now check if enough digits have been generated. + // Compute + // + // p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e + // + // Note: + // Since rest and delta share the same exponent e, it suffices to + // compare the significands. + const std::uint64_t rest = (std::uint64_t{p1} << -one.e) + p2; + if (rest <= delta) + { + // V = buffer * 10^n, with M- <= V <= M+. + + decimal_exponent += n; + + // We may now just stop. But instead look if the buffer could be + // decremented to bring V closer to w. + // + // pow10 = 10^n is now 1 ulp in the decimal representation V. + // The rounding procedure works with diyfp's with an implicit + // exponent of e. + // + // 10^n = (10^n * 2^-e) * 2^e = ulp * 2^e + // + const std::uint64_t ten_n = std::uint64_t{pow10} << -one.e; + grisu2_round(buffer, length, dist, delta, rest, ten_n); + + return; + } + + pow10 /= 10; + // + // pow10 = 10^(n-1) <= p1 < 10^n + // Invariants restored. + } + + // 2) + // + // The digits of the integral part have been generated: + // + // M+ = d[k-1]...d[1]d[0] + p2 * 2^e + // = buffer + p2 * 2^e + // + // Now generate the digits of the fractional part p2 * 2^e. + // + // Note: + // No decimal point is generated: the exponent is adjusted instead. + // + // p2 actually represents the fraction + // + // p2 * 2^e + // = p2 / 2^-e + // = d[-1] / 10^1 + d[-2] / 10^2 + ... + // + // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...) + // + // p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m + // + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...) + // + // using + // + // 10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e) + // = ( d) * 2^-e + ( r) + // + // or + // 10^m * p2 * 2^e = d + r * 2^e + // + // i.e. + // + // M+ = buffer + p2 * 2^e + // = buffer + 10^-m * (d + r * 2^e) + // = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e + // + // and stop as soon as 10^-m * r * 2^e <= delta * 2^e + + JSON_ASSERT(p2 > delta); + + int m = 0; + for (;;) + { + // Invariant: + // M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e + // = buffer * 10^-m + 10^-m * (p2 ) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * (10 * p2) ) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e + // + JSON_ASSERT(p2 <= (std::numeric_limits::max)() / 10); + p2 *= 10; + const std::uint64_t d = p2 >> -one.e; // d = (10 * p2) div 2^-e + const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e + // + // M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e)) + // = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e + // + JSON_ASSERT(d <= 9); + buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d + // + // M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e + // + p2 = r; + m++; + // + // M+ = buffer * 10^-m + 10^-m * p2 * 2^e + // Invariant restored. + + // Check if enough digits have been generated. + // + // 10^-m * p2 * 2^e <= delta * 2^e + // p2 * 2^e <= 10^m * delta * 2^e + // p2 <= 10^m * delta + delta *= 10; + dist *= 10; + if (p2 <= delta) + { + break; + } + } + + // V = buffer * 10^-m, with M- <= V <= M+. + + decimal_exponent -= m; + + // 1 ulp in the decimal representation is now 10^-m. + // Since delta and dist are now scaled by 10^m, we need to do the + // same with ulp in order to keep the units in sync. + // + // 10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e + // + const std::uint64_t ten_m = one.f; + grisu2_round(buffer, length, dist, delta, p2, ten_m); + + // By construction this algorithm generates the shortest possible decimal + // number (Loitsch, Theorem 6.2) which rounds back to w. + // For an input number of precision p, at least + // + // N = 1 + ceil(p * log_10(2)) + // + // decimal digits are sufficient to identify all binary floating-point + // numbers (Matula, "In-and-Out conversions"). + // This implies that the algorithm does not produce more than N decimal + // digits. + // + // N = 17 for p = 53 (IEEE double precision) + // N = 9 for p = 24 (IEEE single precision) +} + +/*! +v = buf * 10^decimal_exponent +len is the length of the buffer (number of decimal digits) +The buffer must be large enough, i.e. >= max_digits10. +*/ +JSON_HEDLEY_NON_NULL(1) +inline void grisu2(char* buf, int& len, int& decimal_exponent, + diyfp m_minus, diyfp v, diyfp m_plus) +{ + JSON_ASSERT(m_plus.e == m_minus.e); + JSON_ASSERT(m_plus.e == v.e); + + // --------(-----------------------+-----------------------)-------- (A) + // m- v m+ + // + // --------------------(-----------+-----------------------)-------- (B) + // m- v m+ + // + // First scale v (and m- and m+) such that the exponent is in the range + // [alpha, gamma]. + + const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e); + + const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k + + // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma] + const diyfp w = diyfp::mul(v, c_minus_k); + const diyfp w_minus = diyfp::mul(m_minus, c_minus_k); + const diyfp w_plus = diyfp::mul(m_plus, c_minus_k); + + // ----(---+---)---------------(---+---)---------------(---+---)---- + // w- w w+ + // = c*m- = c*v = c*m+ + // + // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and + // w+ are now off by a small amount. + // In fact: + // + // w - v * 10^k < 1 ulp + // + // To account for this inaccuracy, add resp. subtract 1 ulp. + // + // --------+---[---------------(---+---)---------------]---+-------- + // w- M- w M+ w+ + // + // Now any number in [M-, M+] (bounds included) will round to w when input, + // regardless of how the input rounding algorithm breaks ties. + // + // And digit_gen generates the shortest possible such number in [M-, M+]. + // Note that this does not mean that Grisu2 always generates the shortest + // possible number in the interval (m-, m+). + const diyfp M_minus(w_minus.f + 1, w_minus.e); + const diyfp M_plus (w_plus.f - 1, w_plus.e ); + + decimal_exponent = -cached.k; // = -(-k) = k + + grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus); +} + +/*! +v = buf * 10^decimal_exponent +len is the length of the buffer (number of decimal digits) +The buffer must be large enough, i.e. >= max_digits10. +*/ +template +JSON_HEDLEY_NON_NULL(1) +void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value) +{ + static_assert(diyfp::kPrecision >= std::numeric_limits::digits + 3, + "internal error: not enough precision"); + + JSON_ASSERT(std::isfinite(value)); + JSON_ASSERT(value > 0); + + // If the neighbors (and boundaries) of 'value' are always computed for double-precision + // numbers, all float's can be recovered using strtod (and strtof). However, the resulting + // decimal representations are not exactly "short". + // + // The documentation for 'std::to_chars' (https://en.cppreference.com/w/cpp/utility/to_chars) + // says "value is converted to a string as if by std::sprintf in the default ("C") locale" + // and since sprintf promotes floats to doubles, I think this is exactly what 'std::to_chars' + // does. + // On the other hand, the documentation for 'std::to_chars' requires that "parsing the + // representation using the corresponding std::from_chars function recovers value exactly". That + // indicates that single precision floating-point numbers should be recovered using + // 'std::strtof'. + // + // NB: If the neighbors are computed for single-precision numbers, there is a single float + // (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision + // value is off by 1 ulp. +#if 0 + const boundaries w = compute_boundaries(static_cast(value)); +#else + const boundaries w = compute_boundaries(value); +#endif + + grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus); +} + +/*! +@brief appends a decimal representation of e to buf +@return a pointer to the element following the exponent. +@pre -1000 < e < 1000 +*/ +JSON_HEDLEY_NON_NULL(1) +JSON_HEDLEY_RETURNS_NON_NULL +inline char* append_exponent(char* buf, int e) +{ + JSON_ASSERT(e > -1000); + JSON_ASSERT(e < 1000); + + if (e < 0) + { + e = -e; + *buf++ = '-'; + } + else + { + *buf++ = '+'; + } + + auto k = static_cast(e); + if (k < 10) + { + // Always print at least two digits in the exponent. + // This is for compatibility with printf("%g"). + *buf++ = '0'; + *buf++ = static_cast('0' + k); + } + else if (k < 100) + { + *buf++ = static_cast('0' + k / 10); + k %= 10; + *buf++ = static_cast('0' + k); + } + else + { + *buf++ = static_cast('0' + k / 100); + k %= 100; + *buf++ = static_cast('0' + k / 10); + k %= 10; + *buf++ = static_cast('0' + k); + } + + return buf; +} + +/*! +@brief prettify v = buf * 10^decimal_exponent + +If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point +notation. Otherwise it will be printed in exponential notation. + +@pre min_exp < 0 +@pre max_exp > 0 +*/ +JSON_HEDLEY_NON_NULL(1) +JSON_HEDLEY_RETURNS_NON_NULL +inline char* format_buffer(char* buf, int len, int decimal_exponent, + int min_exp, int max_exp) +{ + JSON_ASSERT(min_exp < 0); + JSON_ASSERT(max_exp > 0); + + const int k = len; + const int n = len + decimal_exponent; + + // v = buf * 10^(n-k) + // k is the length of the buffer (number of decimal digits) + // n is the position of the decimal point relative to the start of the buffer. + + if (k <= n && n <= max_exp) + { + // digits[000] + // len <= max_exp + 2 + + std::memset(buf + k, '0', static_cast(n) - static_cast(k)); + // Make it look like a floating-point number (#362, #378) + buf[n + 0] = '.'; + buf[n + 1] = '0'; + return buf + (static_cast(n) + 2); + } + + if (0 < n && n <= max_exp) + { + // dig.its + // len <= max_digits10 + 1 + + JSON_ASSERT(k > n); + + std::memmove(buf + (static_cast(n) + 1), buf + n, static_cast(k) - static_cast(n)); + buf[n] = '.'; + return buf + (static_cast(k) + 1U); + } + + if (min_exp < n && n <= 0) + { + // 0.[000]digits + // len <= 2 + (-min_exp - 1) + max_digits10 + + std::memmove(buf + (2 + static_cast(-n)), buf, static_cast(k)); + buf[0] = '0'; + buf[1] = '.'; + std::memset(buf + 2, '0', static_cast(-n)); + return buf + (2U + static_cast(-n) + static_cast(k)); + } + + if (k == 1) + { + // dE+123 + // len <= 1 + 5 + + buf += 1; + } + else + { + // d.igitsE+123 + // len <= max_digits10 + 1 + 5 + + std::memmove(buf + 2, buf + 1, static_cast(k) - 1); + buf[1] = '.'; + buf += 1 + static_cast(k); + } + + *buf++ = 'e'; + return append_exponent(buf, n - 1); +} + +} // namespace dtoa_impl + +/*! +@brief generates a decimal representation of the floating-point number value in [first, last). + +The format of the resulting decimal representation is similar to printf's %g +format. Returns an iterator pointing past-the-end of the decimal representation. + +@note The input number must be finite, i.e. NaN's and Inf's are not supported. +@note The buffer must be large enough. +@note The result is NOT null-terminated. +*/ +template +JSON_HEDLEY_NON_NULL(1, 2) +JSON_HEDLEY_RETURNS_NON_NULL +char* to_chars(char* first, const char* last, FloatType value) +{ + static_cast(last); // maybe unused - fix warning + JSON_ASSERT(std::isfinite(value)); + + // Use signbit(value) instead of (value < 0) since signbit works for -0. + if (std::signbit(value)) + { + value = -value; + *first++ = '-'; + } + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + if (value == 0) // +-0 + { + *first++ = '0'; + // Make it look like a floating-point number (#362, #378) + *first++ = '.'; + *first++ = '0'; + return first; + } +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + + JSON_ASSERT(last - first >= std::numeric_limits::max_digits10); + + // Compute v = buffer * 10^decimal_exponent. + // The decimal digits are stored in the buffer, which needs to be interpreted + // as an unsigned decimal integer. + // len is the length of the buffer, i.e. the number of decimal digits. + int len = 0; + int decimal_exponent = 0; + dtoa_impl::grisu2(first, len, decimal_exponent, value); + + JSON_ASSERT(len <= std::numeric_limits::max_digits10); + + // Format the buffer like printf("%.*g", prec, value) + constexpr int kMinExp = -4; + // Use digits10 here to increase compatibility with version 2. + constexpr int kMaxExp = std::numeric_limits::digits10; + + JSON_ASSERT(last - first >= kMaxExp + 2); + JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits::max_digits10); + JSON_ASSERT(last - first >= std::numeric_limits::max_digits10 + 6); + + return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp); +} + +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/////////////////// +// serialization // +/////////////////// + +/// how to treat decoding errors +enum class error_handler_t +{ + strict, ///< throw a type_error exception in case of invalid UTF-8 + replace, ///< replace invalid UTF-8 sequences with U+FFFD + ignore ///< ignore invalid UTF-8 sequences +}; + +template +class serializer +{ + using string_t = typename BasicJsonType::string_t; + using number_float_t = typename BasicJsonType::number_float_t; + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using binary_char_t = typename BasicJsonType::binary_t::value_type; + static constexpr std::uint8_t UTF8_ACCEPT = 0; + static constexpr std::uint8_t UTF8_REJECT = 1; + + public: + /*! + @param[in] s output stream to serialize to + @param[in] ichar indentation character to use + @param[in] error_handler_ how to react on decoding errors + */ + serializer(output_adapter_t s, const char ichar, + error_handler_t error_handler_ = error_handler_t::strict) + : o(std::move(s)) + , loc(std::localeconv()) + , thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->thousands_sep))) + , decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->decimal_point))) + , indent_char(ichar) + , indent_string(512, indent_char) + , error_handler(error_handler_) + {} + + // delete because of pointer members + serializer(const serializer&) = delete; + serializer& operator=(const serializer&) = delete; + serializer(serializer&&) = delete; + serializer& operator=(serializer&&) = delete; + ~serializer() = default; + + /*! + @brief internal implementation of the serialization function + + This function is called by the public member function dump and organizes + the serialization internally. The indentation level is propagated as + additional parameter. In case of arrays and objects, the function is + called recursively. + + - strings and object keys are escaped using `escape_string()` + - integer numbers are converted implicitly via `operator<<` + - floating-point numbers are converted to a string using `"%g"` format + - binary values are serialized as objects containing the subtype and the + byte array + + @param[in] val value to serialize + @param[in] pretty_print whether the output shall be pretty-printed + @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters + in the output are escaped with `\uXXXX` sequences, and the result consists + of ASCII characters only. + @param[in] indent_step the indent level + @param[in] current_indent the current indent level (only used internally) + */ + void dump(const BasicJsonType& val, + const bool pretty_print, + const bool ensure_ascii, + const unsigned int indent_step, + const unsigned int current_indent = 0) + { + switch (val.m_type) + { + case value_t::object: + { + if (val.m_value.object->empty()) + { + o->write_characters("{}", 2); + return; + } + + if (pretty_print) + { + o->write_characters("{\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + // first n-1 elements + auto i = val.m_value.object->cbegin(); + for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) + { + o->write_characters(indent_string.c_str(), new_indent); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\": ", 3); + dump(i->second, true, ensure_ascii, indent_step, new_indent); + o->write_characters(",\n", 2); + } + + // last element + JSON_ASSERT(i != val.m_value.object->cend()); + JSON_ASSERT(std::next(i) == val.m_value.object->cend()); + o->write_characters(indent_string.c_str(), new_indent); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\": ", 3); + dump(i->second, true, ensure_ascii, indent_step, new_indent); + + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character('}'); + } + else + { + o->write_character('{'); + + // first n-1 elements + auto i = val.m_value.object->cbegin(); + for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) + { + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\":", 2); + dump(i->second, false, ensure_ascii, indent_step, current_indent); + o->write_character(','); + } + + // last element + JSON_ASSERT(i != val.m_value.object->cend()); + JSON_ASSERT(std::next(i) == val.m_value.object->cend()); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\":", 2); + dump(i->second, false, ensure_ascii, indent_step, current_indent); + + o->write_character('}'); + } + + return; + } + + case value_t::array: + { + if (val.m_value.array->empty()) + { + o->write_characters("[]", 2); + return; + } + + if (pretty_print) + { + o->write_characters("[\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + // first n-1 elements + for (auto i = val.m_value.array->cbegin(); + i != val.m_value.array->cend() - 1; ++i) + { + o->write_characters(indent_string.c_str(), new_indent); + dump(*i, true, ensure_ascii, indent_step, new_indent); + o->write_characters(",\n", 2); + } + + // last element + JSON_ASSERT(!val.m_value.array->empty()); + o->write_characters(indent_string.c_str(), new_indent); + dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent); + + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character(']'); + } + else + { + o->write_character('['); + + // first n-1 elements + for (auto i = val.m_value.array->cbegin(); + i != val.m_value.array->cend() - 1; ++i) + { + dump(*i, false, ensure_ascii, indent_step, current_indent); + o->write_character(','); + } + + // last element + JSON_ASSERT(!val.m_value.array->empty()); + dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent); + + o->write_character(']'); + } + + return; + } + + case value_t::string: + { + o->write_character('\"'); + dump_escaped(*val.m_value.string, ensure_ascii); + o->write_character('\"'); + return; + } + + case value_t::binary: + { + if (pretty_print) + { + o->write_characters("{\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"bytes\": [", 10); + + if (!val.m_value.binary->empty()) + { + for (auto i = val.m_value.binary->cbegin(); + i != val.m_value.binary->cend() - 1; ++i) + { + dump_integer(*i); + o->write_characters(", ", 2); + } + dump_integer(val.m_value.binary->back()); + } + + o->write_characters("],\n", 3); + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"subtype\": ", 11); + if (val.m_value.binary->has_subtype()) + { + dump_integer(val.m_value.binary->subtype()); + } + else + { + o->write_characters("null", 4); + } + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character('}'); + } + else + { + o->write_characters("{\"bytes\":[", 10); + + if (!val.m_value.binary->empty()) + { + for (auto i = val.m_value.binary->cbegin(); + i != val.m_value.binary->cend() - 1; ++i) + { + dump_integer(*i); + o->write_character(','); + } + dump_integer(val.m_value.binary->back()); + } + + o->write_characters("],\"subtype\":", 12); + if (val.m_value.binary->has_subtype()) + { + dump_integer(val.m_value.binary->subtype()); + o->write_character('}'); + } + else + { + o->write_characters("null}", 5); + } + } + return; + } + + case value_t::boolean: + { + if (val.m_value.boolean) + { + o->write_characters("true", 4); + } + else + { + o->write_characters("false", 5); + } + return; + } + + case value_t::number_integer: + { + dump_integer(val.m_value.number_integer); + return; + } + + case value_t::number_unsigned: + { + dump_integer(val.m_value.number_unsigned); + return; + } + + case value_t::number_float: + { + dump_float(val.m_value.number_float); + return; + } + + case value_t::discarded: + { + o->write_characters("", 11); + return; + } + + case value_t::null: + { + o->write_characters("null", 4); + return; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + } + + JSON_PRIVATE_UNLESS_TESTED: + /*! + @brief dump escaped string + + Escape a string by replacing certain special characters by a sequence of an + escape character (backslash) and another character and other control + characters by a sequence of "\u" followed by a four-digit hex + representation. The escaped string is written to output stream @a o. + + @param[in] s the string to escape + @param[in] ensure_ascii whether to escape non-ASCII characters with + \uXXXX sequences + + @complexity Linear in the length of string @a s. + */ + void dump_escaped(const string_t& s, const bool ensure_ascii) + { + std::uint32_t codepoint{}; + std::uint8_t state = UTF8_ACCEPT; + std::size_t bytes = 0; // number of bytes written to string_buffer + + // number of bytes written at the point of the last valid byte + std::size_t bytes_after_last_accept = 0; + std::size_t undumped_chars = 0; + + for (std::size_t i = 0; i < s.size(); ++i) + { + const auto byte = static_cast(s[i]); + + switch (decode(state, codepoint, byte)) + { + case UTF8_ACCEPT: // decode found a new code point + { + switch (codepoint) + { + case 0x08: // backspace + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'b'; + break; + } + + case 0x09: // horizontal tab + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 't'; + break; + } + + case 0x0A: // newline + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'n'; + break; + } + + case 0x0C: // formfeed + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'f'; + break; + } + + case 0x0D: // carriage return + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'r'; + break; + } + + case 0x22: // quotation mark + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = '\"'; + break; + } + + case 0x5C: // reverse solidus + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = '\\'; + break; + } + + default: + { + // escape control characters (0x00..0x1F) or, if + // ensure_ascii parameter is used, non-ASCII characters + if ((codepoint <= 0x1F) || (ensure_ascii && (codepoint >= 0x7F))) + { + if (codepoint <= 0xFFFF) + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + static_cast((std::snprintf)(string_buffer.data() + bytes, 7, "\\u%04x", + static_cast(codepoint))); + bytes += 6; + } + else + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + static_cast((std::snprintf)(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x", + static_cast(0xD7C0u + (codepoint >> 10u)), + static_cast(0xDC00u + (codepoint & 0x3FFu)))); + bytes += 12; + } + } + else + { + // copy byte to buffer (all previous bytes + // been copied have in default case above) + string_buffer[bytes++] = s[i]; + } + break; + } + } + + // write buffer and reset index; there must be 13 bytes + // left, as this is the maximal number of bytes to be + // written ("\uxxxx\uxxxx\0") for one code point + if (string_buffer.size() - bytes < 13) + { + o->write_characters(string_buffer.data(), bytes); + bytes = 0; + } + + // remember the byte position of this accept + bytes_after_last_accept = bytes; + undumped_chars = 0; + break; + } + + case UTF8_REJECT: // decode found invalid UTF-8 byte + { + switch (error_handler) + { + case error_handler_t::strict: + { + std::stringstream ss; + ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex << (byte | 0); + JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + ss.str(), BasicJsonType())); + } + + case error_handler_t::ignore: + case error_handler_t::replace: + { + // in case we saw this character the first time, we + // would like to read it again, because the byte + // may be OK for itself, but just not OK for the + // previous sequence + if (undumped_chars > 0) + { + --i; + } + + // reset length buffer to the last accepted index; + // thus removing/ignoring the invalid characters + bytes = bytes_after_last_accept; + + if (error_handler == error_handler_t::replace) + { + // add a replacement character + if (ensure_ascii) + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'u'; + string_buffer[bytes++] = 'f'; + string_buffer[bytes++] = 'f'; + string_buffer[bytes++] = 'f'; + string_buffer[bytes++] = 'd'; + } + else + { + string_buffer[bytes++] = detail::binary_writer::to_char_type('\xEF'); + string_buffer[bytes++] = detail::binary_writer::to_char_type('\xBF'); + string_buffer[bytes++] = detail::binary_writer::to_char_type('\xBD'); + } + + // write buffer and reset index; there must be 13 bytes + // left, as this is the maximal number of bytes to be + // written ("\uxxxx\uxxxx\0") for one code point + if (string_buffer.size() - bytes < 13) + { + o->write_characters(string_buffer.data(), bytes); + bytes = 0; + } + + bytes_after_last_accept = bytes; + } + + undumped_chars = 0; + + // continue processing the string + state = UTF8_ACCEPT; + break; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + break; + } + + default: // decode found yet incomplete multi-byte code point + { + if (!ensure_ascii) + { + // code point will not be escaped - copy byte to buffer + string_buffer[bytes++] = s[i]; + } + ++undumped_chars; + break; + } + } + } + + // we finished processing the string + if (JSON_HEDLEY_LIKELY(state == UTF8_ACCEPT)) + { + // write buffer + if (bytes > 0) + { + o->write_characters(string_buffer.data(), bytes); + } + } + else + { + // we finish reading, but do not accept: string was incomplete + switch (error_handler) + { + case error_handler_t::strict: + { + std::stringstream ss; + ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex << (static_cast(s.back()) | 0); + JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + ss.str(), BasicJsonType())); + } + + case error_handler_t::ignore: + { + // write all accepted bytes + o->write_characters(string_buffer.data(), bytes_after_last_accept); + break; + } + + case error_handler_t::replace: + { + // write all accepted bytes + o->write_characters(string_buffer.data(), bytes_after_last_accept); + // add a replacement character + if (ensure_ascii) + { + o->write_characters("\\ufffd", 6); + } + else + { + o->write_characters("\xEF\xBF\xBD", 3); + } + break; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + } + } + + private: + /*! + @brief count digits + + Count the number of decimal (base 10) digits for an input unsigned integer. + + @param[in] x unsigned integer number to count its digits + @return number of decimal digits + */ + inline unsigned int count_digits(number_unsigned_t x) noexcept + { + unsigned int n_digits = 1; + for (;;) + { + if (x < 10) + { + return n_digits; + } + if (x < 100) + { + return n_digits + 1; + } + if (x < 1000) + { + return n_digits + 2; + } + if (x < 10000) + { + return n_digits + 3; + } + x = x / 10000u; + n_digits += 4; + } + } + + // templates to avoid warnings about useless casts + template ::value, int> = 0> + bool is_negative_number(NumberType x) + { + return x < 0; + } + + template < typename NumberType, enable_if_t ::value, int > = 0 > + bool is_negative_number(NumberType /*unused*/) + { + return false; + } + + /*! + @brief dump an integer + + Dump a given integer to output stream @a o. Works internally with + @a number_buffer. + + @param[in] x integer number (signed or unsigned) to dump + @tparam NumberType either @a number_integer_t or @a number_unsigned_t + */ + template < typename NumberType, detail::enable_if_t < + std::is_integral::value || + std::is_same::value || + std::is_same::value || + std::is_same::value, + int > = 0 > + void dump_integer(NumberType x) + { + static constexpr std::array, 100> digits_to_99 + { + { + {{'0', '0'}}, {{'0', '1'}}, {{'0', '2'}}, {{'0', '3'}}, {{'0', '4'}}, {{'0', '5'}}, {{'0', '6'}}, {{'0', '7'}}, {{'0', '8'}}, {{'0', '9'}}, + {{'1', '0'}}, {{'1', '1'}}, {{'1', '2'}}, {{'1', '3'}}, {{'1', '4'}}, {{'1', '5'}}, {{'1', '6'}}, {{'1', '7'}}, {{'1', '8'}}, {{'1', '9'}}, + {{'2', '0'}}, {{'2', '1'}}, {{'2', '2'}}, {{'2', '3'}}, {{'2', '4'}}, {{'2', '5'}}, {{'2', '6'}}, {{'2', '7'}}, {{'2', '8'}}, {{'2', '9'}}, + {{'3', '0'}}, {{'3', '1'}}, {{'3', '2'}}, {{'3', '3'}}, {{'3', '4'}}, {{'3', '5'}}, {{'3', '6'}}, {{'3', '7'}}, {{'3', '8'}}, {{'3', '9'}}, + {{'4', '0'}}, {{'4', '1'}}, {{'4', '2'}}, {{'4', '3'}}, {{'4', '4'}}, {{'4', '5'}}, {{'4', '6'}}, {{'4', '7'}}, {{'4', '8'}}, {{'4', '9'}}, + {{'5', '0'}}, {{'5', '1'}}, {{'5', '2'}}, {{'5', '3'}}, {{'5', '4'}}, {{'5', '5'}}, {{'5', '6'}}, {{'5', '7'}}, {{'5', '8'}}, {{'5', '9'}}, + {{'6', '0'}}, {{'6', '1'}}, {{'6', '2'}}, {{'6', '3'}}, {{'6', '4'}}, {{'6', '5'}}, {{'6', '6'}}, {{'6', '7'}}, {{'6', '8'}}, {{'6', '9'}}, + {{'7', '0'}}, {{'7', '1'}}, {{'7', '2'}}, {{'7', '3'}}, {{'7', '4'}}, {{'7', '5'}}, {{'7', '6'}}, {{'7', '7'}}, {{'7', '8'}}, {{'7', '9'}}, + {{'8', '0'}}, {{'8', '1'}}, {{'8', '2'}}, {{'8', '3'}}, {{'8', '4'}}, {{'8', '5'}}, {{'8', '6'}}, {{'8', '7'}}, {{'8', '8'}}, {{'8', '9'}}, + {{'9', '0'}}, {{'9', '1'}}, {{'9', '2'}}, {{'9', '3'}}, {{'9', '4'}}, {{'9', '5'}}, {{'9', '6'}}, {{'9', '7'}}, {{'9', '8'}}, {{'9', '9'}}, + } + }; + + // special case for "0" + if (x == 0) + { + o->write_character('0'); + return; + } + + // use a pointer to fill the buffer + auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto,cppcoreguidelines-pro-type-vararg,hicpp-vararg) + + number_unsigned_t abs_value; + + unsigned int n_chars{}; + + if (is_negative_number(x)) + { + *buffer_ptr = '-'; + abs_value = remove_sign(static_cast(x)); + + // account one more byte for the minus sign + n_chars = 1 + count_digits(abs_value); + } + else + { + abs_value = static_cast(x); + n_chars = count_digits(abs_value); + } + + // spare 1 byte for '\0' + JSON_ASSERT(n_chars < number_buffer.size() - 1); + + // jump to the end to generate the string from backward, + // so we later avoid reversing the result + buffer_ptr += n_chars; + + // Fast int2ascii implementation inspired by "Fastware" talk by Andrei Alexandrescu + // See: https://www.youtube.com/watch?v=o4-CwDo2zpg + while (abs_value >= 100) + { + const auto digits_index = static_cast((abs_value % 100)); + abs_value /= 100; + *(--buffer_ptr) = digits_to_99[digits_index][1]; + *(--buffer_ptr) = digits_to_99[digits_index][0]; + } + + if (abs_value >= 10) + { + const auto digits_index = static_cast(abs_value); + *(--buffer_ptr) = digits_to_99[digits_index][1]; + *(--buffer_ptr) = digits_to_99[digits_index][0]; + } + else + { + *(--buffer_ptr) = static_cast('0' + abs_value); + } + + o->write_characters(number_buffer.data(), n_chars); + } + + /*! + @brief dump a floating-point number + + Dump a given floating-point number to output stream @a o. Works internally + with @a number_buffer. + + @param[in] x floating-point number to dump + */ + void dump_float(number_float_t x) + { + // NaN / inf + if (!std::isfinite(x)) + { + o->write_characters("null", 4); + return; + } + + // If number_float_t is an IEEE-754 single or double precision number, + // use the Grisu2 algorithm to produce short numbers which are + // guaranteed to round-trip, using strtof and strtod, resp. + // + // NB: The test below works if == . + static constexpr bool is_ieee_single_or_double + = (std::numeric_limits::is_iec559 && std::numeric_limits::digits == 24 && std::numeric_limits::max_exponent == 128) || + (std::numeric_limits::is_iec559 && std::numeric_limits::digits == 53 && std::numeric_limits::max_exponent == 1024); + + dump_float(x, std::integral_constant()); + } + + void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/) + { + auto* begin = number_buffer.data(); + auto* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x); + + o->write_characters(begin, static_cast(end - begin)); + } + + void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/) + { + // get number of digits for a float -> text -> float round-trip + static constexpr auto d = std::numeric_limits::max_digits10; + + // the actual conversion + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), "%.*g", d, x); + + // negative value indicates an error + JSON_ASSERT(len > 0); + // check if buffer was large enough + JSON_ASSERT(static_cast(len) < number_buffer.size()); + + // erase thousands separator + if (thousands_sep != '\0') + { + // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::remove returns an iterator, see https://github.com/nlohmann/json/issues/3081 + const auto end = std::remove(number_buffer.begin(), number_buffer.begin() + len, thousands_sep); + std::fill(end, number_buffer.end(), '\0'); + JSON_ASSERT((end - number_buffer.begin()) <= len); + len = (end - number_buffer.begin()); + } + + // convert decimal point to '.' + if (decimal_point != '\0' && decimal_point != '.') + { + // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::find returns an iterator, see https://github.com/nlohmann/json/issues/3081 + const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point); + if (dec_pos != number_buffer.end()) + { + *dec_pos = '.'; + } + } + + o->write_characters(number_buffer.data(), static_cast(len)); + + // determine if we need to append ".0" + const bool value_is_int_like = + std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1, + [](char c) + { + return c == '.' || c == 'e'; + }); + + if (value_is_int_like) + { + o->write_characters(".0", 2); + } + } + + /*! + @brief check whether a string is UTF-8 encoded + + The function checks each byte of a string whether it is UTF-8 encoded. The + result of the check is stored in the @a state parameter. The function must + be called initially with state 0 (accept). State 1 means the string must + be rejected, because the current byte is not allowed. If the string is + completely processed, but the state is non-zero, the string ended + prematurely; that is, the last byte indicated more bytes should have + followed. + + @param[in,out] state the state of the decoding + @param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT) + @param[in] byte next byte to decode + @return new state + + @note The function has been edited: a std::array is used. + + @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann + @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ + */ + static std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte) noexcept + { + static const std::array utf8d = + { + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF + 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF + 0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF + 0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF + 0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2 + 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4 + 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6 + 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8 + } + }; + + JSON_ASSERT(byte < utf8d.size()); + const std::uint8_t type = utf8d[byte]; + + codep = (state != UTF8_ACCEPT) + ? (byte & 0x3fu) | (codep << 6u) + : (0xFFu >> type) & (byte); + + std::size_t index = 256u + static_cast(state) * 16u + static_cast(type); + JSON_ASSERT(index < 400); + state = utf8d[index]; + return state; + } + + /* + * Overload to make the compiler happy while it is instantiating + * dump_integer for number_unsigned_t. + * Must never be called. + */ + number_unsigned_t remove_sign(number_unsigned_t x) + { + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + return x; // LCOV_EXCL_LINE + } + + /* + * Helper function for dump_integer + * + * This function takes a negative signed integer and returns its absolute + * value as unsigned integer. The plus/minus shuffling is necessary as we can + * not directly remove the sign of an arbitrary signed integer as the + * absolute values of INT_MIN and INT_MAX are usually not the same. See + * #1708 for details. + */ + inline number_unsigned_t remove_sign(number_integer_t x) noexcept + { + JSON_ASSERT(x < 0 && x < (std::numeric_limits::max)()); // NOLINT(misc-redundant-expression) + return static_cast(-(x + 1)) + 1; + } + + private: + /// the output of the serializer + output_adapter_t o = nullptr; + + /// a (hopefully) large enough character buffer + std::array number_buffer{{}}; + + /// the locale + const std::lconv* loc = nullptr; + /// the locale's thousand separator character + const char thousands_sep = '\0'; + /// the locale's decimal point character + const char decimal_point = '\0'; + + /// string buffer + std::array string_buffer{{}}; + + /// the indentation character + const char indent_char; + /// the indentation string + string_t indent_string; + + /// error_handler how to react on decoding errors + const error_handler_t error_handler; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + + +#include // less +#include // initializer_list +#include // input_iterator_tag, iterator_traits +#include // allocator +#include // for out_of_range +#include // enable_if, is_convertible +#include // pair +#include // vector + +// #include + + +namespace nlohmann +{ + +/// ordered_map: a minimal map-like container that preserves insertion order +/// for use within nlohmann::basic_json +template , + class Allocator = std::allocator>> + struct ordered_map : std::vector, Allocator> +{ + using key_type = Key; + using mapped_type = T; + using Container = std::vector, Allocator>; + using iterator = typename Container::iterator; + using const_iterator = typename Container::const_iterator; + using size_type = typename Container::size_type; + using value_type = typename Container::value_type; + + // Explicit constructors instead of `using Container::Container` + // otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4) + ordered_map(const Allocator& alloc = Allocator()) : Container{alloc} {} + template + ordered_map(It first, It last, const Allocator& alloc = Allocator()) + : Container{first, last, alloc} {} + ordered_map(std::initializer_list init, const Allocator& alloc = Allocator() ) + : Container{init, alloc} {} + + std::pair emplace(const key_type& key, T&& t) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return {it, false}; + } + } + Container::emplace_back(key, t); + return {--this->end(), true}; + } + + T& operator[](const Key& key) + { + return emplace(key, T{}).first->second; + } + + const T& operator[](const Key& key) const + { + return at(key); + } + + T& at(const Key& key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it->second; + } + } + + JSON_THROW(std::out_of_range("key not found")); + } + + const T& at(const Key& key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it->second; + } + } + + JSON_THROW(std::out_of_range("key not found")); + } + + size_type erase(const Key& key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + // Since we cannot move const Keys, re-construct them in place + for (auto next = it; ++next != this->end(); ++it) + { + it->~value_type(); // Destroy but keep allocation + new (&*it) value_type{std::move(*next)}; + } + Container::pop_back(); + return 1; + } + } + return 0; + } + + iterator erase(iterator pos) + { + return erase(pos, std::next(pos)); + } + + iterator erase(iterator first, iterator last) + { + const auto elements_affected = std::distance(first, last); + const auto offset = std::distance(Container::begin(), first); + + // This is the start situation. We need to delete elements_affected + // elements (3 in this example: e, f, g), and need to return an + // iterator past the last deleted element (h in this example). + // Note that offset is the distance from the start of the vector + // to first. We will need this later. + + // [ a, b, c, d, e, f, g, h, i, j ] + // ^ ^ + // first last + + // Since we cannot move const Keys, we re-construct them in place. + // We start at first and re-construct (viz. copy) the elements from + // the back of the vector. Example for first iteration: + + // ,--------. + // v | destroy e and re-construct with h + // [ a, b, c, d, e, f, g, h, i, j ] + // ^ ^ + // it it + elements_affected + + for (auto it = first; std::next(it, elements_affected) != Container::end(); ++it) + { + it->~value_type(); // destroy but keep allocation + new (&*it) value_type{std::move(*std::next(it, elements_affected))}; // "move" next element to it + } + + // [ a, b, c, d, h, i, j, h, i, j ] + // ^ ^ + // first last + + // remove the unneeded elements at the end of the vector + Container::resize(this->size() - static_cast(elements_affected)); + + // [ a, b, c, d, h, i, j ] + // ^ ^ + // first last + + // first is now pointing past the last deleted element, but we cannot + // use this iterator, because it may have been invalidated by the + // resize call. Instead, we can return begin() + offset. + return Container::begin() + offset; + } + + size_type count(const Key& key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return 1; + } + } + return 0; + } + + iterator find(const Key& key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it; + } + } + return Container::end(); + } + + const_iterator find(const Key& key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it; + } + } + return Container::end(); + } + + std::pair insert( value_type&& value ) + { + return emplace(value.first, std::move(value.second)); + } + + std::pair insert( const value_type& value ) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == value.first) + { + return {it, false}; + } + } + Container::push_back(value); + return {--this->end(), true}; + } + + template + using require_input_iter = typename std::enable_if::iterator_category, + std::input_iterator_tag>::value>::type; + + template> + void insert(InputIt first, InputIt last) + { + for (auto it = first; it != last; ++it) + { + insert(*it); + } + } +}; + +} // namespace nlohmann + + +#if defined(JSON_HAS_CPP_17) + #include +#endif + +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +namespace nlohmann +{ + +/*! +@brief a class to store JSON values + +@internal +@invariant The member variables @a m_value and @a m_type have the following +relationship: +- If `m_type == value_t::object`, then `m_value.object != nullptr`. +- If `m_type == value_t::array`, then `m_value.array != nullptr`. +- If `m_type == value_t::string`, then `m_value.string != nullptr`. +The invariants are checked by member function assert_invariant(). + +@note ObjectType trick from https://stackoverflow.com/a/9860911 +@endinternal + +@since version 1.0.0 + +@nosubgrouping +*/ +NLOHMANN_BASIC_JSON_TPL_DECLARATION +class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions) +{ + private: + template friend struct detail::external_constructor; + friend ::nlohmann::json_pointer; + + template + friend class ::nlohmann::detail::parser; + friend ::nlohmann::detail::serializer; + template + friend class ::nlohmann::detail::iter_impl; + template + friend class ::nlohmann::detail::binary_writer; + template + friend class ::nlohmann::detail::binary_reader; + template + friend class ::nlohmann::detail::json_sax_dom_parser; + template + friend class ::nlohmann::detail::json_sax_dom_callback_parser; + friend class ::nlohmann::detail::exception; + + /// workaround type for MSVC + using basic_json_t = NLOHMANN_BASIC_JSON_TPL; + + JSON_PRIVATE_UNLESS_TESTED: + // convenience aliases for types residing in namespace detail; + using lexer = ::nlohmann::detail::lexer_base; + + template + static ::nlohmann::detail::parser parser( + InputAdapterType adapter, + detail::parser_callback_tcb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false + ) + { + return ::nlohmann::detail::parser(std::move(adapter), + std::move(cb), allow_exceptions, ignore_comments); + } + + private: + using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t; + template + using internal_iterator = ::nlohmann::detail::internal_iterator; + template + using iter_impl = ::nlohmann::detail::iter_impl; + template + using iteration_proxy = ::nlohmann::detail::iteration_proxy; + template using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator; + + template + using output_adapter_t = ::nlohmann::detail::output_adapter_t; + + template + using binary_reader = ::nlohmann::detail::binary_reader; + template using binary_writer = ::nlohmann::detail::binary_writer; + + JSON_PRIVATE_UNLESS_TESTED: + using serializer = ::nlohmann::detail::serializer; + + public: + using value_t = detail::value_t; + /// JSON Pointer, see @ref nlohmann::json_pointer + using json_pointer = ::nlohmann::json_pointer; + template + using json_serializer = JSONSerializer; + /// how to treat decoding errors + using error_handler_t = detail::error_handler_t; + /// how to treat CBOR tags + using cbor_tag_handler_t = detail::cbor_tag_handler_t; + /// helper type for initializer lists of basic_json values + using initializer_list_t = std::initializer_list>; + + using input_format_t = detail::input_format_t; + /// SAX interface type, see @ref nlohmann::json_sax + using json_sax_t = json_sax; + + //////////////// + // exceptions // + //////////////// + + /// @name exceptions + /// Classes to implement user-defined exceptions. + /// @{ + + using exception = detail::exception; + using parse_error = detail::parse_error; + using invalid_iterator = detail::invalid_iterator; + using type_error = detail::type_error; + using out_of_range = detail::out_of_range; + using other_error = detail::other_error; + + /// @} + + + ///////////////////// + // container types // + ///////////////////// + + /// @name container types + /// The canonic container types to use @ref basic_json like any other STL + /// container. + /// @{ + + /// the type of elements in a basic_json container + using value_type = basic_json; + + /// the type of an element reference + using reference = value_type&; + /// the type of an element const reference + using const_reference = const value_type&; + + /// a type to represent differences between iterators + using difference_type = std::ptrdiff_t; + /// a type to represent container sizes + using size_type = std::size_t; + + /// the allocator type + using allocator_type = AllocatorType; + + /// the type of an element pointer + using pointer = typename std::allocator_traits::pointer; + /// the type of an element const pointer + using const_pointer = typename std::allocator_traits::const_pointer; + + /// an iterator for a basic_json container + using iterator = iter_impl; + /// a const iterator for a basic_json container + using const_iterator = iter_impl; + /// a reverse iterator for a basic_json container + using reverse_iterator = json_reverse_iterator; + /// a const reverse iterator for a basic_json container + using const_reverse_iterator = json_reverse_iterator; + + /// @} + + + /// @brief returns the allocator associated with the container + /// @sa https://json.nlohmann.me/api/basic_json/get_allocator/ + static allocator_type get_allocator() + { + return allocator_type(); + } + + /// @brief returns version information on the library + /// @sa https://json.nlohmann.me/api/basic_json/meta/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json meta() + { + basic_json result; + + result["copyright"] = "(C) 2013-2022 Niels Lohmann"; + result["name"] = "JSON for Modern C++"; + result["url"] = "https://github.com/nlohmann/json"; + result["version"]["string"] = + std::to_string(NLOHMANN_JSON_VERSION_MAJOR) + "." + + std::to_string(NLOHMANN_JSON_VERSION_MINOR) + "." + + std::to_string(NLOHMANN_JSON_VERSION_PATCH); + result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR; + result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR; + result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH; + +#ifdef _WIN32 + result["platform"] = "win32"; +#elif defined __linux__ + result["platform"] = "linux"; +#elif defined __APPLE__ + result["platform"] = "apple"; +#elif defined __unix__ + result["platform"] = "unix"; +#else + result["platform"] = "unknown"; +#endif + +#if defined(__ICC) || defined(__INTEL_COMPILER) + result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}}; +#elif defined(__clang__) + result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}}; +#elif defined(__GNUC__) || defined(__GNUG__) + result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}}; +#elif defined(__HP_cc) || defined(__HP_aCC) + result["compiler"] = "hp" +#elif defined(__IBMCPP__) + result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}}; +#elif defined(_MSC_VER) + result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}}; +#elif defined(__PGI) + result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}}; +#elif defined(__SUNPRO_CC) + result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}}; +#else + result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}}; +#endif + +#ifdef __cplusplus + result["compiler"]["c++"] = std::to_string(__cplusplus); +#else + result["compiler"]["c++"] = "unknown"; +#endif + return result; + } + + + /////////////////////////// + // JSON value data types // + /////////////////////////// + + /// @name JSON value data types + /// The data types to store a JSON value. These types are derived from + /// the template arguments passed to class @ref basic_json. + /// @{ + + /// @brief object key comparator type + /// @sa https://json.nlohmann.me/api/basic_json/object_comparator_t/ +#if defined(JSON_HAS_CPP_14) + // Use transparent comparator if possible, combined with perfect forwarding + // on find() and count() calls prevents unnecessary string construction. + using object_comparator_t = std::less<>; +#else + using object_comparator_t = std::less; +#endif + + /// @brief a type for an object + /// @sa https://json.nlohmann.me/api/basic_json/object_t/ + using object_t = ObjectType>>; + + /// @brief a type for an array + /// @sa https://json.nlohmann.me/api/basic_json/array_t/ + using array_t = ArrayType>; + + /// @brief a type for a string + /// @sa https://json.nlohmann.me/api/basic_json/string_t/ + using string_t = StringType; + + /// @brief a type for a boolean + /// @sa https://json.nlohmann.me/api/basic_json/boolean_t/ + using boolean_t = BooleanType; + + /// @brief a type for a number (integer) + /// @sa https://json.nlohmann.me/api/basic_json/number_integer_t/ + using number_integer_t = NumberIntegerType; + + /// @brief a type for a number (unsigned) + /// @sa https://json.nlohmann.me/api/basic_json/number_unsigned_t/ + using number_unsigned_t = NumberUnsignedType; + + /// @brief a type for a number (floating-point) + /// @sa https://json.nlohmann.me/api/basic_json/number_float_t/ + using number_float_t = NumberFloatType; + + /// @brief a type for a packed binary type + /// @sa https://json.nlohmann.me/api/basic_json/binary_t/ + using binary_t = nlohmann::byte_container_with_subtype; + + /// @} + + private: + + /// helper for exception-safe object creation + template + JSON_HEDLEY_RETURNS_NON_NULL + static T* create(Args&& ... args) + { + AllocatorType alloc; + using AllocatorTraits = std::allocator_traits>; + + auto deleter = [&](T * obj) + { + AllocatorTraits::deallocate(alloc, obj, 1); + }; + std::unique_ptr obj(AllocatorTraits::allocate(alloc, 1), deleter); + AllocatorTraits::construct(alloc, obj.get(), std::forward(args)...); + JSON_ASSERT(obj != nullptr); + return obj.release(); + } + + //////////////////////// + // JSON value storage // + //////////////////////// + + JSON_PRIVATE_UNLESS_TESTED: + /*! + @brief a JSON value + + The actual storage for a JSON value of the @ref basic_json class. This + union combines the different storage types for the JSON value types + defined in @ref value_t. + + JSON type | value_t type | used type + --------- | --------------- | ------------------------ + object | object | pointer to @ref object_t + array | array | pointer to @ref array_t + string | string | pointer to @ref string_t + boolean | boolean | @ref boolean_t + number | number_integer | @ref number_integer_t + number | number_unsigned | @ref number_unsigned_t + number | number_float | @ref number_float_t + binary | binary | pointer to @ref binary_t + null | null | *no value is stored* + + @note Variable-length types (objects, arrays, and strings) are stored as + pointers. The size of the union should not exceed 64 bits if the default + value types are used. + + @since version 1.0.0 + */ + union json_value + { + /// object (stored with pointer to save storage) + object_t* object; + /// array (stored with pointer to save storage) + array_t* array; + /// string (stored with pointer to save storage) + string_t* string; + /// binary (stored with pointer to save storage) + binary_t* binary; + /// boolean + boolean_t boolean; + /// number (integer) + number_integer_t number_integer; + /// number (unsigned integer) + number_unsigned_t number_unsigned; + /// number (floating-point) + number_float_t number_float; + + /// default constructor (for null values) + json_value() = default; + /// constructor for booleans + json_value(boolean_t v) noexcept : boolean(v) {} + /// constructor for numbers (integer) + json_value(number_integer_t v) noexcept : number_integer(v) {} + /// constructor for numbers (unsigned) + json_value(number_unsigned_t v) noexcept : number_unsigned(v) {} + /// constructor for numbers (floating-point) + json_value(number_float_t v) noexcept : number_float(v) {} + /// constructor for empty values of a given type + json_value(value_t t) + { + switch (t) + { + case value_t::object: + { + object = create(); + break; + } + + case value_t::array: + { + array = create(); + break; + } + + case value_t::string: + { + string = create(""); + break; + } + + case value_t::binary: + { + binary = create(); + break; + } + + case value_t::boolean: + { + boolean = static_cast(false); + break; + } + + case value_t::number_integer: + { + number_integer = static_cast(0); + break; + } + + case value_t::number_unsigned: + { + number_unsigned = static_cast(0); + break; + } + + case value_t::number_float: + { + number_float = static_cast(0.0); + break; + } + + case value_t::null: + { + object = nullptr; // silence warning, see #821 + break; + } + + case value_t::discarded: + default: + { + object = nullptr; // silence warning, see #821 + if (JSON_HEDLEY_UNLIKELY(t == value_t::null)) + { + JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.10.5", basic_json())); // LCOV_EXCL_LINE + } + break; + } + } + } + + /// constructor for strings + json_value(const string_t& value) : string(create(value)) {} + + /// constructor for rvalue strings + json_value(string_t&& value) : string(create(std::move(value))) {} + + /// constructor for objects + json_value(const object_t& value) : object(create(value)) {} + + /// constructor for rvalue objects + json_value(object_t&& value) : object(create(std::move(value))) {} + + /// constructor for arrays + json_value(const array_t& value) : array(create(value)) {} + + /// constructor for rvalue arrays + json_value(array_t&& value) : array(create(std::move(value))) {} + + /// constructor for binary arrays + json_value(const typename binary_t::container_type& value) : binary(create(value)) {} + + /// constructor for rvalue binary arrays + json_value(typename binary_t::container_type&& value) : binary(create(std::move(value))) {} + + /// constructor for binary arrays (internal type) + json_value(const binary_t& value) : binary(create(value)) {} + + /// constructor for rvalue binary arrays (internal type) + json_value(binary_t&& value) : binary(create(std::move(value))) {} + + void destroy(value_t t) + { + if (t == value_t::array || t == value_t::object) + { + // flatten the current json_value to a heap-allocated stack + std::vector stack; + + // move the top-level items to stack + if (t == value_t::array) + { + stack.reserve(array->size()); + std::move(array->begin(), array->end(), std::back_inserter(stack)); + } + else + { + stack.reserve(object->size()); + for (auto&& it : *object) + { + stack.push_back(std::move(it.second)); + } + } + + while (!stack.empty()) + { + // move the last item to local variable to be processed + basic_json current_item(std::move(stack.back())); + stack.pop_back(); + + // if current_item is array/object, move + // its children to the stack to be processed later + if (current_item.is_array()) + { + std::move(current_item.m_value.array->begin(), current_item.m_value.array->end(), std::back_inserter(stack)); + + current_item.m_value.array->clear(); + } + else if (current_item.is_object()) + { + for (auto&& it : *current_item.m_value.object) + { + stack.push_back(std::move(it.second)); + } + + current_item.m_value.object->clear(); + } + + // it's now safe that current_item get destructed + // since it doesn't have any children + } + } + + switch (t) + { + case value_t::object: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, object); + std::allocator_traits::deallocate(alloc, object, 1); + break; + } + + case value_t::array: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, array); + std::allocator_traits::deallocate(alloc, array, 1); + break; + } + + case value_t::string: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, string); + std::allocator_traits::deallocate(alloc, string, 1); + break; + } + + case value_t::binary: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, binary); + std::allocator_traits::deallocate(alloc, binary, 1); + break; + } + + case value_t::null: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::discarded: + default: + { + break; + } + } + } + }; + + private: + /*! + @brief checks the class invariants + + This function asserts the class invariants. It needs to be called at the + end of every constructor to make sure that created objects respect the + invariant. Furthermore, it has to be called each time the type of a JSON + value is changed, because the invariant expresses a relationship between + @a m_type and @a m_value. + + Furthermore, the parent relation is checked for arrays and objects: If + @a check_parents true and the value is an array or object, then the + container's elements must have the current value as parent. + + @param[in] check_parents whether the parent relation should be checked. + The value is true by default and should only be set to false + during destruction of objects when the invariant does not + need to hold. + */ + void assert_invariant(bool check_parents = true) const noexcept + { + JSON_ASSERT(m_type != value_t::object || m_value.object != nullptr); + JSON_ASSERT(m_type != value_t::array || m_value.array != nullptr); + JSON_ASSERT(m_type != value_t::string || m_value.string != nullptr); + JSON_ASSERT(m_type != value_t::binary || m_value.binary != nullptr); + +#if JSON_DIAGNOSTICS + JSON_TRY + { + // cppcheck-suppress assertWithSideEffect + JSON_ASSERT(!check_parents || !is_structured() || std::all_of(begin(), end(), [this](const basic_json & j) + { + return j.m_parent == this; + })); + } + JSON_CATCH(...) {} // LCOV_EXCL_LINE +#endif + static_cast(check_parents); + } + + void set_parents() + { +#if JSON_DIAGNOSTICS + switch (m_type) + { + case value_t::array: + { + for (auto& element : *m_value.array) + { + element.m_parent = this; + } + break; + } + + case value_t::object: + { + for (auto& element : *m_value.object) + { + element.second.m_parent = this; + } + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + break; + } +#endif + } + + iterator set_parents(iterator it, typename iterator::difference_type count_set_parents) + { +#if JSON_DIAGNOSTICS + for (typename iterator::difference_type i = 0; i < count_set_parents; ++i) + { + (it + i)->m_parent = this; + } +#else + static_cast(count_set_parents); +#endif + return it; + } + + reference set_parent(reference j, std::size_t old_capacity = static_cast(-1)) + { +#if JSON_DIAGNOSTICS + if (old_capacity != static_cast(-1)) + { + // see https://github.com/nlohmann/json/issues/2838 + JSON_ASSERT(type() == value_t::array); + if (JSON_HEDLEY_UNLIKELY(m_value.array->capacity() != old_capacity)) + { + // capacity has changed: update all parents + set_parents(); + return j; + } + } + + // ordered_json uses a vector internally, so pointers could have + // been invalidated; see https://github.com/nlohmann/json/issues/2962 +#ifdef JSON_HEDLEY_MSVC_VERSION +#pragma warning(push ) +#pragma warning(disable : 4127) // ignore warning to replace if with if constexpr +#endif + if (detail::is_ordered_map::value) + { + set_parents(); + return j; + } +#ifdef JSON_HEDLEY_MSVC_VERSION +#pragma warning( pop ) +#endif + + j.m_parent = this; +#else + static_cast(j); + static_cast(old_capacity); +#endif + return j; + } + + public: + ////////////////////////// + // JSON parser callback // + ////////////////////////// + + /// @brief parser event types + /// @sa https://json.nlohmann.me/api/basic_json/parse_event_t/ + using parse_event_t = detail::parse_event_t; + + /// @brief per-element parser callback type + /// @sa https://json.nlohmann.me/api/basic_json/parser_callback_t/ + using parser_callback_t = detail::parser_callback_t; + + ////////////////// + // constructors // + ////////////////// + + /// @name constructors and destructors + /// Constructors of class @ref basic_json, copy/move constructor, copy + /// assignment, static functions creating objects, and the destructor. + /// @{ + + /// @brief create an empty value with a given type + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + basic_json(const value_t v) + : m_type(v), m_value(v) + { + assert_invariant(); + } + + /// @brief create a null object + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + basic_json(std::nullptr_t = nullptr) noexcept + : basic_json(value_t::null) + { + assert_invariant(); + } + + /// @brief create a JSON value from compatible types + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + template < typename CompatibleType, + typename U = detail::uncvref_t, + detail::enable_if_t < + !detail::is_basic_json::value && detail::is_compatible_type::value, int > = 0 > + basic_json(CompatibleType && val) noexcept(noexcept( // NOLINT(bugprone-forwarding-reference-overload,bugprone-exception-escape) + JSONSerializer::to_json(std::declval(), + std::forward(val)))) + { + JSONSerializer::to_json(*this, std::forward(val)); + set_parents(); + assert_invariant(); + } + + /// @brief create a JSON value from an existing one + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + template < typename BasicJsonType, + detail::enable_if_t < + detail::is_basic_json::value&& !std::is_same::value, int > = 0 > + basic_json(const BasicJsonType& val) + { + using other_boolean_t = typename BasicJsonType::boolean_t; + using other_number_float_t = typename BasicJsonType::number_float_t; + using other_number_integer_t = typename BasicJsonType::number_integer_t; + using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using other_string_t = typename BasicJsonType::string_t; + using other_object_t = typename BasicJsonType::object_t; + using other_array_t = typename BasicJsonType::array_t; + using other_binary_t = typename BasicJsonType::binary_t; + + switch (val.type()) + { + case value_t::boolean: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_float: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_integer: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_unsigned: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::string: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::object: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::array: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::binary: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::null: + *this = nullptr; + break; + case value_t::discarded: + m_type = value_t::discarded; + break; + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + set_parents(); + assert_invariant(); + } + + /// @brief create a container (array or object) from an initializer list + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + basic_json(initializer_list_t init, + bool type_deduction = true, + value_t manual_type = value_t::array) + { + // check if each element is an array with two elements whose first + // element is a string + bool is_an_object = std::all_of(init.begin(), init.end(), + [](const detail::json_ref& element_ref) + { + return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[0].is_string(); + }); + + // adjust type if type deduction is not wanted + if (!type_deduction) + { + // if array is wanted, do not create an object though possible + if (manual_type == value_t::array) + { + is_an_object = false; + } + + // if object is wanted but impossible, throw an exception + if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object)) + { + JSON_THROW(type_error::create(301, "cannot create object from initializer list", basic_json())); + } + } + + if (is_an_object) + { + // the initializer list is a list of pairs -> create object + m_type = value_t::object; + m_value = value_t::object; + + for (auto& element_ref : init) + { + auto element = element_ref.moved_or_copied(); + m_value.object->emplace( + std::move(*((*element.m_value.array)[0].m_value.string)), + std::move((*element.m_value.array)[1])); + } + } + else + { + // the initializer list describes an array -> create array + m_type = value_t::array; + m_value.array = create(init.begin(), init.end()); + } + + set_parents(); + assert_invariant(); + } + + /// @brief explicitly create a binary array (without subtype) + /// @sa https://json.nlohmann.me/api/basic_json/binary/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(const typename binary_t::container_type& init) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = init; + return res; + } + + /// @brief explicitly create a binary array (with subtype) + /// @sa https://json.nlohmann.me/api/basic_json/binary/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = binary_t(init, subtype); + return res; + } + + /// @brief explicitly create a binary array + /// @sa https://json.nlohmann.me/api/basic_json/binary/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(typename binary_t::container_type&& init) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = std::move(init); + return res; + } + + /// @brief explicitly create a binary array (with subtype) + /// @sa https://json.nlohmann.me/api/basic_json/binary/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = binary_t(std::move(init), subtype); + return res; + } + + /// @brief explicitly create an array from an initializer list + /// @sa https://json.nlohmann.me/api/basic_json/array/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json array(initializer_list_t init = {}) + { + return basic_json(init, false, value_t::array); + } + + /// @brief explicitly create an object from an initializer list + /// @sa https://json.nlohmann.me/api/basic_json/object/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json object(initializer_list_t init = {}) + { + return basic_json(init, false, value_t::object); + } + + /// @brief construct an array with count copies of given value + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + basic_json(size_type cnt, const basic_json& val) + : m_type(value_t::array) + { + m_value.array = create(cnt, val); + set_parents(); + assert_invariant(); + } + + /// @brief construct a JSON container given an iterator range + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + template < class InputIT, typename std::enable_if < + std::is_same::value || + std::is_same::value, int >::type = 0 > + basic_json(InputIT first, InputIT last) + { + JSON_ASSERT(first.m_object != nullptr); + JSON_ASSERT(last.m_object != nullptr); + + // make sure iterator fits the current value + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(201, "iterators are not compatible", basic_json())); + } + + // copy type from first iterator + m_type = first.m_object->m_type; + + // check if iterator range is complete for primitive values + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + { + if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin() + || !last.m_it.primitive_iterator.is_end())) + { + JSON_THROW(invalid_iterator::create(204, "iterators out of range", *first.m_object)); + } + break; + } + + case value_t::null: + case value_t::object: + case value_t::array: + case value_t::binary: + case value_t::discarded: + default: + break; + } + + switch (m_type) + { + case value_t::number_integer: + { + m_value.number_integer = first.m_object->m_value.number_integer; + break; + } + + case value_t::number_unsigned: + { + m_value.number_unsigned = first.m_object->m_value.number_unsigned; + break; + } + + case value_t::number_float: + { + m_value.number_float = first.m_object->m_value.number_float; + break; + } + + case value_t::boolean: + { + m_value.boolean = first.m_object->m_value.boolean; + break; + } + + case value_t::string: + { + m_value = *first.m_object->m_value.string; + break; + } + + case value_t::object: + { + m_value.object = create(first.m_it.object_iterator, + last.m_it.object_iterator); + break; + } + + case value_t::array: + { + m_value.array = create(first.m_it.array_iterator, + last.m_it.array_iterator); + break; + } + + case value_t::binary: + { + m_value = *first.m_object->m_value.binary; + break; + } + + case value_t::null: + case value_t::discarded: + default: + JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + std::string(first.m_object->type_name()), *first.m_object)); + } + + set_parents(); + assert_invariant(); + } + + + /////////////////////////////////////// + // other constructors and destructor // + /////////////////////////////////////// + + template, + std::is_same>::value, int> = 0 > + basic_json(const JsonRef& ref) : basic_json(ref.moved_or_copied()) {} + + /// @brief copy constructor + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + basic_json(const basic_json& other) + : m_type(other.m_type) + { + // check of passed value is valid + other.assert_invariant(); + + switch (m_type) + { + case value_t::object: + { + m_value = *other.m_value.object; + break; + } + + case value_t::array: + { + m_value = *other.m_value.array; + break; + } + + case value_t::string: + { + m_value = *other.m_value.string; + break; + } + + case value_t::boolean: + { + m_value = other.m_value.boolean; + break; + } + + case value_t::number_integer: + { + m_value = other.m_value.number_integer; + break; + } + + case value_t::number_unsigned: + { + m_value = other.m_value.number_unsigned; + break; + } + + case value_t::number_float: + { + m_value = other.m_value.number_float; + break; + } + + case value_t::binary: + { + m_value = *other.m_value.binary; + break; + } + + case value_t::null: + case value_t::discarded: + default: + break; + } + + set_parents(); + assert_invariant(); + } + + /// @brief move constructor + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + basic_json(basic_json&& other) noexcept + : m_type(std::move(other.m_type)), + m_value(std::move(other.m_value)) + { + // check that passed value is valid + other.assert_invariant(false); + + // invalidate payload + other.m_type = value_t::null; + other.m_value = {}; + + set_parents(); + assert_invariant(); + } + + /// @brief copy assignment + /// @sa https://json.nlohmann.me/api/basic_json/operator=/ + basic_json& operator=(basic_json other) noexcept ( + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value + ) + { + // check that passed value is valid + other.assert_invariant(); + + using std::swap; + swap(m_type, other.m_type); + swap(m_value, other.m_value); + + set_parents(); + assert_invariant(); + return *this; + } + + /// @brief destructor + /// @sa https://json.nlohmann.me/api/basic_json/~basic_json/ + ~basic_json() noexcept + { + assert_invariant(false); + m_value.destroy(m_type); + } + + /// @} + + public: + /////////////////////// + // object inspection // + /////////////////////// + + /// @name object inspection + /// Functions to inspect the type of a JSON value. + /// @{ + + /// @brief serialization + /// @sa https://json.nlohmann.me/api/basic_json/dump/ + string_t dump(const int indent = -1, + const char indent_char = ' ', + const bool ensure_ascii = false, + const error_handler_t error_handler = error_handler_t::strict) const + { + string_t result; + serializer s(detail::output_adapter(result), indent_char, error_handler); + + if (indent >= 0) + { + s.dump(*this, true, ensure_ascii, static_cast(indent)); + } + else + { + s.dump(*this, false, ensure_ascii, 0); + } + + return result; + } + + /// @brief return the type of the JSON value (explicit) + /// @sa https://json.nlohmann.me/api/basic_json/type/ + constexpr value_t type() const noexcept + { + return m_type; + } + + /// @brief return whether type is primitive + /// @sa https://json.nlohmann.me/api/basic_json/is_primitive/ + constexpr bool is_primitive() const noexcept + { + return is_null() || is_string() || is_boolean() || is_number() || is_binary(); + } + + /// @brief return whether type is structured + /// @sa https://json.nlohmann.me/api/basic_json/is_structured/ + constexpr bool is_structured() const noexcept + { + return is_array() || is_object(); + } + + /// @brief return whether value is null + /// @sa https://json.nlohmann.me/api/basic_json/is_null/ + constexpr bool is_null() const noexcept + { + return m_type == value_t::null; + } + + /// @brief return whether value is a boolean + /// @sa https://json.nlohmann.me/api/basic_json/is_boolean/ + constexpr bool is_boolean() const noexcept + { + return m_type == value_t::boolean; + } + + /// @brief return whether value is a number + /// @sa https://json.nlohmann.me/api/basic_json/is_number/ + constexpr bool is_number() const noexcept + { + return is_number_integer() || is_number_float(); + } + + /// @brief return whether value is an integer number + /// @sa https://json.nlohmann.me/api/basic_json/is_number_integer/ + constexpr bool is_number_integer() const noexcept + { + return m_type == value_t::number_integer || m_type == value_t::number_unsigned; + } + + /// @brief return whether value is an unsigned integer number + /// @sa https://json.nlohmann.me/api/basic_json/is_number_unsigned/ + constexpr bool is_number_unsigned() const noexcept + { + return m_type == value_t::number_unsigned; + } + + /// @brief return whether value is a floating-point number + /// @sa https://json.nlohmann.me/api/basic_json/is_number_float/ + constexpr bool is_number_float() const noexcept + { + return m_type == value_t::number_float; + } + + /// @brief return whether value is an object + /// @sa https://json.nlohmann.me/api/basic_json/is_object/ + constexpr bool is_object() const noexcept + { + return m_type == value_t::object; + } + + /// @brief return whether value is an array + /// @sa https://json.nlohmann.me/api/basic_json/is_array/ + constexpr bool is_array() const noexcept + { + return m_type == value_t::array; + } + + /// @brief return whether value is a string + /// @sa https://json.nlohmann.me/api/basic_json/is_string/ + constexpr bool is_string() const noexcept + { + return m_type == value_t::string; + } + + /// @brief return whether value is a binary array + /// @sa https://json.nlohmann.me/api/basic_json/is_binary/ + constexpr bool is_binary() const noexcept + { + return m_type == value_t::binary; + } + + /// @brief return whether value is discarded + /// @sa https://json.nlohmann.me/api/basic_json/is_discarded/ + constexpr bool is_discarded() const noexcept + { + return m_type == value_t::discarded; + } + + /// @brief return the type of the JSON value (implicit) + /// @sa https://json.nlohmann.me/api/basic_json/operator_value_t/ + constexpr operator value_t() const noexcept + { + return m_type; + } + + /// @} + + private: + ////////////////// + // value access // + ////////////////// + + /// get a boolean (explicit) + boolean_t get_impl(boolean_t* /*unused*/) const + { + if (JSON_HEDLEY_LIKELY(is_boolean())) + { + return m_value.boolean; + } + + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name()), *this)); + } + + /// get a pointer to the value (object) + object_t* get_impl_ptr(object_t* /*unused*/) noexcept + { + return is_object() ? m_value.object : nullptr; + } + + /// get a pointer to the value (object) + constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept + { + return is_object() ? m_value.object : nullptr; + } + + /// get a pointer to the value (array) + array_t* get_impl_ptr(array_t* /*unused*/) noexcept + { + return is_array() ? m_value.array : nullptr; + } + + /// get a pointer to the value (array) + constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept + { + return is_array() ? m_value.array : nullptr; + } + + /// get a pointer to the value (string) + string_t* get_impl_ptr(string_t* /*unused*/) noexcept + { + return is_string() ? m_value.string : nullptr; + } + + /// get a pointer to the value (string) + constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept + { + return is_string() ? m_value.string : nullptr; + } + + /// get a pointer to the value (boolean) + boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept + { + return is_boolean() ? &m_value.boolean : nullptr; + } + + /// get a pointer to the value (boolean) + constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept + { + return is_boolean() ? &m_value.boolean : nullptr; + } + + /// get a pointer to the value (integer number) + number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept + { + return is_number_integer() ? &m_value.number_integer : nullptr; + } + + /// get a pointer to the value (integer number) + constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept + { + return is_number_integer() ? &m_value.number_integer : nullptr; + } + + /// get a pointer to the value (unsigned number) + number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept + { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } + + /// get a pointer to the value (unsigned number) + constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept + { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } + + /// get a pointer to the value (floating-point number) + number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept + { + return is_number_float() ? &m_value.number_float : nullptr; + } + + /// get a pointer to the value (floating-point number) + constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept + { + return is_number_float() ? &m_value.number_float : nullptr; + } + + /// get a pointer to the value (binary) + binary_t* get_impl_ptr(binary_t* /*unused*/) noexcept + { + return is_binary() ? m_value.binary : nullptr; + } + + /// get a pointer to the value (binary) + constexpr const binary_t* get_impl_ptr(const binary_t* /*unused*/) const noexcept + { + return is_binary() ? m_value.binary : nullptr; + } + + /*! + @brief helper function to implement get_ref() + + This function helps to implement get_ref() without code duplication for + const and non-const overloads + + @tparam ThisType will be deduced as `basic_json` or `const basic_json` + + @throw type_error.303 if ReferenceType does not match underlying value + type of the current JSON + */ + template + static ReferenceType get_ref_impl(ThisType& obj) + { + // delegate the call to get_ptr<>() + auto* ptr = obj.template get_ptr::type>(); + + if (JSON_HEDLEY_LIKELY(ptr != nullptr)) + { + return *ptr; + } + + JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name()), obj)); + } + + public: + /// @name value access + /// Direct access to the stored value of a JSON value. + /// @{ + + /// @brief get a pointer value (implicit) + /// @sa https://json.nlohmann.me/api/basic_json/get_ptr/ + template::value, int>::type = 0> + auto get_ptr() noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) + { + // delegate the call to get_impl_ptr<>() + return get_impl_ptr(static_cast(nullptr)); + } + + /// @brief get a pointer value (implicit) + /// @sa https://json.nlohmann.me/api/basic_json/get_ptr/ + template < typename PointerType, typename std::enable_if < + std::is_pointer::value&& + std::is_const::type>::value, int >::type = 0 > + constexpr auto get_ptr() const noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) + { + // delegate the call to get_impl_ptr<>() const + return get_impl_ptr(static_cast(nullptr)); + } + + private: + /*! + @brief get a value (explicit) + + Explicit type conversion between the JSON value and a compatible value + which is [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) + and [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). + The value is converted by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + ValueType ret; + JSONSerializer::from_json(*this, ret); + return ret; + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json, + - @ref json_serializer has a `from_json()` method of the form + `void from_json(const basic_json&, ValueType&)`, and + - @ref json_serializer does not have a `from_json()` method of + the form `ValueType from_json(const basic_json&)` + + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @a ValueType + + @throw what @ref json_serializer `from_json()` method throws + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,get__ValueType_const} + + @since version 2.1.0 + */ + template < typename ValueType, + detail::enable_if_t < + detail::is_default_constructible::value&& + detail::has_from_json::value, + int > = 0 > + ValueType get_impl(detail::priority_tag<0> /*unused*/) const noexcept(noexcept( + JSONSerializer::from_json(std::declval(), std::declval()))) + { + auto ret = ValueType(); + JSONSerializer::from_json(*this, ret); + return ret; + } + + /*! + @brief get a value (explicit); special case + + Explicit type conversion between the JSON value and a compatible value + which is **not** [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) + and **not** [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). + The value is converted by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + return JSONSerializer::from_json(*this); + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json and + - @ref json_serializer has a `from_json()` method of the form + `ValueType from_json(const basic_json&)` + + @note If @ref json_serializer has both overloads of + `from_json()`, this one is chosen. + + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @a ValueType + + @throw what @ref json_serializer `from_json()` method throws + + @since version 2.1.0 + */ + template < typename ValueType, + detail::enable_if_t < + detail::has_non_default_from_json::value, + int > = 0 > + ValueType get_impl(detail::priority_tag<1> /*unused*/) const noexcept(noexcept( + JSONSerializer::from_json(std::declval()))) + { + return JSONSerializer::from_json(*this); + } + + /*! + @brief get special-case overload + + This overloads converts the current @ref basic_json in a different + @ref basic_json type + + @tparam BasicJsonType == @ref basic_json + + @return a copy of *this, converted into @a BasicJsonType + + @complexity Depending on the implementation of the called `from_json()` + method. + + @since version 3.2.0 + */ + template < typename BasicJsonType, + detail::enable_if_t < + detail::is_basic_json::value, + int > = 0 > + BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const + { + return *this; + } + + /*! + @brief get special-case overload + + This overloads avoids a lot of template boilerplate, it can be seen as the + identity method + + @tparam BasicJsonType == @ref basic_json + + @return a copy of *this + + @complexity Constant. + + @since version 2.1.0 + */ + template::value, + int> = 0> + basic_json get_impl(detail::priority_tag<3> /*unused*/) const + { + return *this; + } + + /*! + @brief get a pointer value (explicit) + @copydoc get() + */ + template::value, + int> = 0> + constexpr auto get_impl(detail::priority_tag<4> /*unused*/) const noexcept + -> decltype(std::declval().template get_ptr()) + { + // delegate the call to get_ptr + return get_ptr(); + } + + public: + /*! + @brief get a (pointer) value (explicit) + + Performs explicit type conversion between the JSON value and a compatible value if required. + + - If the requested type is a pointer to the internally stored JSON value that pointer is returned. + No copies are made. + + - If the requested type is the current @ref basic_json, or a different @ref basic_json convertible + from the current @ref basic_json. + + - Otherwise the value is converted by calling the @ref json_serializer `from_json()` + method. + + @tparam ValueTypeCV the provided value type + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @tparam ValueType if necessary + + @throw what @ref json_serializer `from_json()` method throws if conversion is required + + @since version 2.1.0 + */ + template < typename ValueTypeCV, typename ValueType = detail::uncvref_t> +#if defined(JSON_HAS_CPP_14) + constexpr +#endif + auto get() const noexcept( + noexcept(std::declval().template get_impl(detail::priority_tag<4> {}))) + -> decltype(std::declval().template get_impl(detail::priority_tag<4> {})) + { + // we cannot static_assert on ValueTypeCV being non-const, because + // there is support for get(), which is why we + // still need the uncvref + static_assert(!std::is_reference::value, + "get() cannot be used with reference types, you might want to use get_ref()"); + return get_impl(detail::priority_tag<4> {}); + } + + /*! + @brief get a pointer value (explicit) + + Explicit pointer access to the internally stored JSON value. No copies are + made. + + @warning The pointer becomes invalid if the underlying JSON object + changes. + + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. + + @return pointer to the internally stored JSON value if the requested + pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + + @complexity Constant. + + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get__PointerType} + + @sa see @ref get_ptr() for explicit pointer-member access + + @since version 1.0.0 + */ + template::value, int>::type = 0> + auto get() noexcept -> decltype(std::declval().template get_ptr()) + { + // delegate the call to get_ptr + return get_ptr(); + } + + /// @brief get a value (explicit) + /// @sa https://json.nlohmann.me/api/basic_json/get_to/ + template < typename ValueType, + detail::enable_if_t < + !detail::is_basic_json::value&& + detail::has_from_json::value, + int > = 0 > + ValueType & get_to(ValueType& v) const noexcept(noexcept( + JSONSerializer::from_json(std::declval(), v))) + { + JSONSerializer::from_json(*this, v); + return v; + } + + // specialization to allow calling get_to with a basic_json value + // see https://github.com/nlohmann/json/issues/2175 + template::value, + int> = 0> + ValueType & get_to(ValueType& v) const + { + v = *this; + return v; + } + + template < + typename T, std::size_t N, + typename Array = T (&)[N], // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + detail::enable_if_t < + detail::has_from_json::value, int > = 0 > + Array get_to(T (&v)[N]) const // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + noexcept(noexcept(JSONSerializer::from_json( + std::declval(), v))) + { + JSONSerializer::from_json(*this, v); + return v; + } + + /// @brief get a reference value (implicit) + /// @sa https://json.nlohmann.me/api/basic_json/get_ref/ + template::value, int>::type = 0> + ReferenceType get_ref() + { + // delegate call to get_ref_impl + return get_ref_impl(*this); + } + + /// @brief get a reference value (implicit) + /// @sa https://json.nlohmann.me/api/basic_json/get_ref/ + template < typename ReferenceType, typename std::enable_if < + std::is_reference::value&& + std::is_const::type>::value, int >::type = 0 > + ReferenceType get_ref() const + { + // delegate call to get_ref_impl + return get_ref_impl(*this); + } + + /*! + @brief get a value (implicit) + + Implicit type conversion between the JSON value and a compatible value. + The call is realized by calling @ref get() const. + + @tparam ValueType non-pointer type compatible to the JSON value, for + instance `int` for JSON integer numbers, `bool` for JSON booleans, or + `std::vector` types for JSON arrays. The character type of @ref string_t + as well as an initializer list of this type is excluded to avoid + ambiguities as these types implicitly convert to `std::string`. + + @return copy of the JSON value, converted to type @a ValueType + + @throw type_error.302 in case passed type @a ValueType is incompatible + to the JSON value type (e.g., the JSON value is of type boolean, but a + string is requested); see example below + + @complexity Linear in the size of the JSON value. + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,operator__ValueType} + + @since version 1.0.0 + */ + template < typename ValueType, typename std::enable_if < + detail::conjunction < + detail::negation>, + detail::negation>>, + detail::negation>, + detail::negation>, + detail::negation>>, + +#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914)) + detail::negation>, +#endif + detail::is_detected_lazy + >::value, int >::type = 0 > + JSON_EXPLICIT operator ValueType() const + { + // delegate the call to get<>() const + return get(); + } + + /// @brief get a binary value + /// @sa https://json.nlohmann.me/api/basic_json/get_binary/ + binary_t& get_binary() + { + if (!is_binary()) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()), *this)); + } + + return *get_ptr(); + } + + /// @brief get a binary value + /// @sa https://json.nlohmann.me/api/basic_json/get_binary/ + const binary_t& get_binary() const + { + if (!is_binary()) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()), *this)); + } + + return *get_ptr(); + } + + /// @} + + + //////////////////// + // element access // + //////////////////// + + /// @name element access + /// Access to the JSON value. + /// @{ + + /// @brief access specified array element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ + reference at(size_type idx) + { + // at only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + JSON_TRY + { + return set_parent(m_value.array->at(idx)); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this)); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + } + } + + /// @brief access specified array element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ + const_reference at(size_type idx) const + { + // at only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + JSON_TRY + { + return m_value.array->at(idx); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this)); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + } + } + + /// @brief access specified object element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ + reference at(const typename object_t::key_type& key) + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + JSON_TRY + { + return set_parent(m_value.object->at(key)); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", *this)); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + } + } + + /// @brief access specified object element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ + const_reference at(const typename object_t::key_type& key) const + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + JSON_TRY + { + return m_value.object->at(key); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", *this)); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + } + } + + /// @brief access specified array element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + reference operator[](size_type idx) + { + // implicitly convert null value to an empty array + if (is_null()) + { + m_type = value_t::array; + m_value.array = create(); + assert_invariant(); + } + + // operator[] only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + // fill up array with null values if given idx is outside range + if (idx >= m_value.array->size()) + { +#if JSON_DIAGNOSTICS + // remember array size & capacity before resizing + const auto old_size = m_value.array->size(); + const auto old_capacity = m_value.array->capacity(); +#endif + m_value.array->resize(idx + 1); + +#if JSON_DIAGNOSTICS + if (JSON_HEDLEY_UNLIKELY(m_value.array->capacity() != old_capacity)) + { + // capacity has changed: update all parents + set_parents(); + } + else + { + // set parent for values added above + set_parents(begin() + static_cast(old_size), static_cast(idx + 1 - old_size)); + } +#endif + assert_invariant(); + } + + return m_value.array->operator[](idx); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()), *this)); + } + + /// @brief access specified array element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + const_reference operator[](size_type idx) const + { + // const operator[] only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + return m_value.array->operator[](idx); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()), *this)); + } + + /// @brief access specified object element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + reference operator[](const typename object_t::key_type& key) + { + // implicitly convert null value to an empty object + if (is_null()) + { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } + + // operator[] only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + return set_parent(m_value.object->operator[](key)); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + } + + /// @brief access specified object element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + const_reference operator[](const typename object_t::key_type& key) const + { + // const operator[] only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); + return m_value.object->find(key)->second; + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + } + + /// @brief access specified object element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + template + JSON_HEDLEY_NON_NULL(2) + reference operator[](T* key) + { + // implicitly convert null to object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + return set_parent(m_value.object->operator[](key)); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + } + + /// @brief access specified object element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + template + JSON_HEDLEY_NON_NULL(2) + const_reference operator[](T* key) const + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); + return m_value.object->find(key)->second; + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + } + + /// @brief access specified object element with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + /// using std::is_convertible in a std::enable_if will fail when using explicit conversions + template < class ValueType, typename std::enable_if < + detail::is_getable::value + && !std::is_same::value, int >::type = 0 > + ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // if key is found, return value and given default value otherwise + const auto it = find(key); + if (it != end()) + { + return it->template get(); + } + + return default_value; + } + + JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), *this)); + } + + /// @brief access specified object element with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + /// overload for a default value of type const char* + string_t value(const typename object_t::key_type& key, const char* default_value) const + { + return value(key, string_t(default_value)); + } + + /// @brief access specified object element via JSON Pointer with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + template::value, int>::type = 0> + ValueType value(const json_pointer& ptr, const ValueType& default_value) const + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // if pointer resolves a value, return it or use default value + JSON_TRY + { + return ptr.get_checked(this).template get(); + } + JSON_INTERNAL_CATCH (out_of_range&) + { + return default_value; + } + } + + JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), *this)); + } + + /// @brief access specified object element via JSON Pointer with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + /// overload for a default value of type const char* + JSON_HEDLEY_NON_NULL(3) + string_t value(const json_pointer& ptr, const char* default_value) const + { + return value(ptr, string_t(default_value)); + } + + /// @brief access the first element + /// @sa https://json.nlohmann.me/api/basic_json/front/ + reference front() + { + return *begin(); + } + + /// @brief access the first element + /// @sa https://json.nlohmann.me/api/basic_json/front/ + const_reference front() const + { + return *cbegin(); + } + + /// @brief access the last element + /// @sa https://json.nlohmann.me/api/basic_json/back/ + reference back() + { + auto tmp = end(); + --tmp; + return *tmp; + } + + /// @brief access the last element + /// @sa https://json.nlohmann.me/api/basic_json/back/ + const_reference back() const + { + auto tmp = cend(); + --tmp; + return *tmp; + } + + /// @brief remove element given an iterator + /// @sa https://json.nlohmann.me/api/basic_json/erase/ + template < class IteratorType, typename std::enable_if < + std::is_same::value || + std::is_same::value, int >::type + = 0 > + IteratorType erase(IteratorType pos) + { + // make sure iterator fits the current value + if (JSON_HEDLEY_UNLIKELY(this != pos.m_object)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + } + + IteratorType result = end(); + + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + case value_t::binary: + { + if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin())) + { + JSON_THROW(invalid_iterator::create(205, "iterator out of range", *this)); + } + + if (is_string()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.string); + std::allocator_traits::deallocate(alloc, m_value.string, 1); + m_value.string = nullptr; + } + else if (is_binary()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.binary); + std::allocator_traits::deallocate(alloc, m_value.binary, 1); + m_value.binary = nullptr; + } + + m_type = value_t::null; + assert_invariant(); + break; + } + + case value_t::object: + { + result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator); + break; + } + + case value_t::array: + { + result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator); + break; + } + + case value_t::null: + case value_t::discarded: + default: + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + } + + return result; + } + + /// @brief remove elements given an iterator range + /// @sa https://json.nlohmann.me/api/basic_json/erase/ + template < class IteratorType, typename std::enable_if < + std::is_same::value || + std::is_same::value, int >::type + = 0 > + IteratorType erase(IteratorType first, IteratorType last) + { + // make sure iterator fits the current value + if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object)) + { + JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value", *this)); + } + + IteratorType result = end(); + + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + case value_t::binary: + { + if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin() + || !last.m_it.primitive_iterator.is_end())) + { + JSON_THROW(invalid_iterator::create(204, "iterators out of range", *this)); + } + + if (is_string()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.string); + std::allocator_traits::deallocate(alloc, m_value.string, 1); + m_value.string = nullptr; + } + else if (is_binary()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.binary); + std::allocator_traits::deallocate(alloc, m_value.binary, 1); + m_value.binary = nullptr; + } + + m_type = value_t::null; + assert_invariant(); + break; + } + + case value_t::object: + { + result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator, + last.m_it.object_iterator); + break; + } + + case value_t::array: + { + result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator, + last.m_it.array_iterator); + break; + } + + case value_t::null: + case value_t::discarded: + default: + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + } + + return result; + } + + /// @brief remove element from a JSON object given a key + /// @sa https://json.nlohmann.me/api/basic_json/erase/ + size_type erase(const typename object_t::key_type& key) + { + // this erase only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + return m_value.object->erase(key); + } + + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + } + + /// @brief remove element from a JSON array given an index + /// @sa https://json.nlohmann.me/api/basic_json/erase/ + void erase(const size_type idx) + { + // this erase only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + if (JSON_HEDLEY_UNLIKELY(idx >= size())) + { + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this)); + } + + m_value.array->erase(m_value.array->begin() + static_cast(idx)); + } + else + { + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + } + } + + /// @} + + + //////////// + // lookup // + //////////// + + /// @name lookup + /// @{ + + /// @brief find an element in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/find/ + template + iterator find(KeyT&& key) + { + auto result = end(); + + if (is_object()) + { + result.m_it.object_iterator = m_value.object->find(std::forward(key)); + } + + return result; + } + + /// @brief find an element in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/find/ + template + const_iterator find(KeyT&& key) const + { + auto result = cend(); + + if (is_object()) + { + result.m_it.object_iterator = m_value.object->find(std::forward(key)); + } + + return result; + } + + /// @brief returns the number of occurrences of a key in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/count/ + template + size_type count(KeyT&& key) const + { + // return 0 for all nonobject types + return is_object() ? m_value.object->count(std::forward(key)) : 0; + } + + /// @brief check the existence of an element in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/contains/ + template < typename KeyT, typename std::enable_if < + !std::is_same::type, json_pointer>::value, int >::type = 0 > + bool contains(KeyT && key) const + { + return is_object() && m_value.object->find(std::forward(key)) != m_value.object->end(); + } + + /// @brief check the existence of an element in a JSON object given a JSON pointer + /// @sa https://json.nlohmann.me/api/basic_json/contains/ + bool contains(const json_pointer& ptr) const + { + return ptr.contains(this); + } + + /// @} + + + /////////////// + // iterators // + /////////////// + + /// @name iterators + /// @{ + + /// @brief returns an iterator to the first element + /// @sa https://json.nlohmann.me/api/basic_json/begin/ + iterator begin() noexcept + { + iterator result(this); + result.set_begin(); + return result; + } + + /// @brief returns an iterator to the first element + /// @sa https://json.nlohmann.me/api/basic_json/begin/ + const_iterator begin() const noexcept + { + return cbegin(); + } + + /// @brief returns a const iterator to the first element + /// @sa https://json.nlohmann.me/api/basic_json/cbegin/ + const_iterator cbegin() const noexcept + { + const_iterator result(this); + result.set_begin(); + return result; + } + + /// @brief returns an iterator to one past the last element + /// @sa https://json.nlohmann.me/api/basic_json/end/ + iterator end() noexcept + { + iterator result(this); + result.set_end(); + return result; + } + + /// @brief returns an iterator to one past the last element + /// @sa https://json.nlohmann.me/api/basic_json/end/ + const_iterator end() const noexcept + { + return cend(); + } + + /// @brief returns an iterator to one past the last element + /// @sa https://json.nlohmann.me/api/basic_json/cend/ + const_iterator cend() const noexcept + { + const_iterator result(this); + result.set_end(); + return result; + } + + /// @brief returns an iterator to the reverse-beginning + /// @sa https://json.nlohmann.me/api/basic_json/rbegin/ + reverse_iterator rbegin() noexcept + { + return reverse_iterator(end()); + } + + /// @brief returns an iterator to the reverse-beginning + /// @sa https://json.nlohmann.me/api/basic_json/rbegin/ + const_reverse_iterator rbegin() const noexcept + { + return crbegin(); + } + + /// @brief returns an iterator to the reverse-end + /// @sa https://json.nlohmann.me/api/basic_json/rend/ + reverse_iterator rend() noexcept + { + return reverse_iterator(begin()); + } + + /// @brief returns an iterator to the reverse-end + /// @sa https://json.nlohmann.me/api/basic_json/rend/ + const_reverse_iterator rend() const noexcept + { + return crend(); + } + + /// @brief returns a const reverse iterator to the last element + /// @sa https://json.nlohmann.me/api/basic_json/crbegin/ + const_reverse_iterator crbegin() const noexcept + { + return const_reverse_iterator(cend()); + } + + /// @brief returns a const reverse iterator to one before the first + /// @sa https://json.nlohmann.me/api/basic_json/crend/ + const_reverse_iterator crend() const noexcept + { + return const_reverse_iterator(cbegin()); + } + + public: + /// @brief wrapper to access iterator member functions in range-based for + /// @sa https://json.nlohmann.me/api/basic_json/items/ + /// @deprecated This function is deprecated since 3.1.0 and will be removed in + /// version 4.0.0 of the library. Please use @ref items() instead; + /// that is, replace `json::iterator_wrapper(j)` with `j.items()`. + JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items()) + static iteration_proxy iterator_wrapper(reference ref) noexcept + { + return ref.items(); + } + + /// @brief wrapper to access iterator member functions in range-based for + /// @sa https://json.nlohmann.me/api/basic_json/items/ + /// @deprecated This function is deprecated since 3.1.0 and will be removed in + /// version 4.0.0 of the library. Please use @ref items() instead; + /// that is, replace `json::iterator_wrapper(j)` with `j.items()`. + JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items()) + static iteration_proxy iterator_wrapper(const_reference ref) noexcept + { + return ref.items(); + } + + /// @brief helper to access iterator member functions in range-based for + /// @sa https://json.nlohmann.me/api/basic_json/items/ + iteration_proxy items() noexcept + { + return iteration_proxy(*this); + } + + /// @brief helper to access iterator member functions in range-based for + /// @sa https://json.nlohmann.me/api/basic_json/items/ + iteration_proxy items() const noexcept + { + return iteration_proxy(*this); + } + + /// @} + + + ////////////// + // capacity // + ////////////// + + /// @name capacity + /// @{ + + /// @brief checks whether the container is empty. + /// @sa https://json.nlohmann.me/api/basic_json/empty/ + bool empty() const noexcept + { + switch (m_type) + { + case value_t::null: + { + // null values are empty + return true; + } + + case value_t::array: + { + // delegate call to array_t::empty() + return m_value.array->empty(); + } + + case value_t::object: + { + // delegate call to object_t::empty() + return m_value.object->empty(); + } + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + // all other types are nonempty + return false; + } + } + } + + /// @brief returns the number of elements + /// @sa https://json.nlohmann.me/api/basic_json/size/ + size_type size() const noexcept + { + switch (m_type) + { + case value_t::null: + { + // null values are empty + return 0; + } + + case value_t::array: + { + // delegate call to array_t::size() + return m_value.array->size(); + } + + case value_t::object: + { + // delegate call to object_t::size() + return m_value.object->size(); + } + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + // all other types have size 1 + return 1; + } + } + } + + /// @brief returns the maximum possible number of elements + /// @sa https://json.nlohmann.me/api/basic_json/max_size/ + size_type max_size() const noexcept + { + switch (m_type) + { + case value_t::array: + { + // delegate call to array_t::max_size() + return m_value.array->max_size(); + } + + case value_t::object: + { + // delegate call to object_t::max_size() + return m_value.object->max_size(); + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + // all other types have max_size() == size() + return size(); + } + } + } + + /// @} + + + /////////////// + // modifiers // + /////////////// + + /// @name modifiers + /// @{ + + /// @brief clears the contents + /// @sa https://json.nlohmann.me/api/basic_json/clear/ + void clear() noexcept + { + switch (m_type) + { + case value_t::number_integer: + { + m_value.number_integer = 0; + break; + } + + case value_t::number_unsigned: + { + m_value.number_unsigned = 0; + break; + } + + case value_t::number_float: + { + m_value.number_float = 0.0; + break; + } + + case value_t::boolean: + { + m_value.boolean = false; + break; + } + + case value_t::string: + { + m_value.string->clear(); + break; + } + + case value_t::binary: + { + m_value.binary->clear(); + break; + } + + case value_t::array: + { + m_value.array->clear(); + break; + } + + case value_t::object: + { + m_value.object->clear(); + break; + } + + case value_t::null: + case value_t::discarded: + default: + break; + } + } + + /// @brief add an object to an array + /// @sa https://json.nlohmann.me/api/basic_json/push_back/ + void push_back(basic_json&& val) + { + // push_back only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) + { + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this)); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array (move semantics) + const auto old_capacity = m_value.array->capacity(); + m_value.array->push_back(std::move(val)); + set_parent(m_value.array->back(), old_capacity); + // if val is moved from, basic_json move constructor marks it null, so we do not call the destructor + } + + /// @brief add an object to an array + /// @sa https://json.nlohmann.me/api/basic_json/operator+=/ + reference operator+=(basic_json&& val) + { + push_back(std::move(val)); + return *this; + } + + /// @brief add an object to an array + /// @sa https://json.nlohmann.me/api/basic_json/push_back/ + void push_back(const basic_json& val) + { + // push_back only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) + { + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this)); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array + const auto old_capacity = m_value.array->capacity(); + m_value.array->push_back(val); + set_parent(m_value.array->back(), old_capacity); + } + + /// @brief add an object to an array + /// @sa https://json.nlohmann.me/api/basic_json/operator+=/ + reference operator+=(const basic_json& val) + { + push_back(val); + return *this; + } + + /// @brief add an object to an object + /// @sa https://json.nlohmann.me/api/basic_json/push_back/ + void push_back(const typename object_t::value_type& val) + { + // push_back only works for null objects or objects + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) + { + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this)); + } + + // transform null object into an object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // add element to object + auto res = m_value.object->insert(val); + set_parent(res.first->second); + } + + /// @brief add an object to an object + /// @sa https://json.nlohmann.me/api/basic_json/operator+=/ + reference operator+=(const typename object_t::value_type& val) + { + push_back(val); + return *this; + } + + /// @brief add an object to an object + /// @sa https://json.nlohmann.me/api/basic_json/push_back/ + void push_back(initializer_list_t init) + { + if (is_object() && init.size() == 2 && (*init.begin())->is_string()) + { + basic_json&& key = init.begin()->moved_or_copied(); + push_back(typename object_t::value_type( + std::move(key.get_ref()), (init.begin() + 1)->moved_or_copied())); + } + else + { + push_back(basic_json(init)); + } + } + + /// @brief add an object to an object + /// @sa https://json.nlohmann.me/api/basic_json/operator+=/ + reference operator+=(initializer_list_t init) + { + push_back(init); + return *this; + } + + /// @brief add an object to an array + /// @sa https://json.nlohmann.me/api/basic_json/emplace_back/ + template + reference emplace_back(Args&& ... args) + { + // emplace_back only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) + { + JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + std::string(type_name()), *this)); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array (perfect forwarding) + const auto old_capacity = m_value.array->capacity(); + m_value.array->emplace_back(std::forward(args)...); + return set_parent(m_value.array->back(), old_capacity); + } + + /// @brief add an object to an object if key does not exist + /// @sa https://json.nlohmann.me/api/basic_json/emplace/ + template + std::pair emplace(Args&& ... args) + { + // emplace only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) + { + JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name()), *this)); + } + + // transform null object into an object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // add element to array (perfect forwarding) + auto res = m_value.object->emplace(std::forward(args)...); + set_parent(res.first->second); + + // create result iterator and set iterator to the result of emplace + auto it = begin(); + it.m_it.object_iterator = res.first; + + // return pair of iterator and boolean + return {it, res.second}; + } + + /// Helper for insertion of an iterator + /// @note: This uses std::distance to support GCC 4.8, + /// see https://github.com/nlohmann/json/pull/1257 + template + iterator insert_iterator(const_iterator pos, Args&& ... args) + { + iterator result(this); + JSON_ASSERT(m_value.array != nullptr); + + auto insert_pos = std::distance(m_value.array->begin(), pos.m_it.array_iterator); + m_value.array->insert(pos.m_it.array_iterator, std::forward(args)...); + result.m_it.array_iterator = m_value.array->begin() + insert_pos; + + // This could have been written as: + // result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); + // but the return value of insert is missing in GCC 4.8, so it is written this way instead. + + set_parents(); + return result; + } + + /// @brief inserts element into array + /// @sa https://json.nlohmann.me/api/basic_json/insert/ + iterator insert(const_iterator pos, const basic_json& val) + { + // insert only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + } + + // insert to array and return iterator + return insert_iterator(pos, val); + } + + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + } + + /// @brief inserts element into array + /// @sa https://json.nlohmann.me/api/basic_json/insert/ + iterator insert(const_iterator pos, basic_json&& val) + { + return insert(pos, val); + } + + /// @brief inserts copies of element into array + /// @sa https://json.nlohmann.me/api/basic_json/insert/ + iterator insert(const_iterator pos, size_type cnt, const basic_json& val) + { + // insert only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + } + + // insert to array and return iterator + return insert_iterator(pos, cnt, val); + } + + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + } + + /// @brief inserts range of elements into array + /// @sa https://json.nlohmann.me/api/basic_json/insert/ + iterator insert(const_iterator pos, const_iterator first, const_iterator last) + { + // insert only works for arrays + if (JSON_HEDLEY_UNLIKELY(!is_array())) + { + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + } + + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + } + + // check if range iterators belong to the same JSON object + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this)); + } + + if (JSON_HEDLEY_UNLIKELY(first.m_object == this)) + { + JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container", *this)); + } + + // insert to array and return iterator + return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator); + } + + /// @brief inserts elements from initializer list into array + /// @sa https://json.nlohmann.me/api/basic_json/insert/ + iterator insert(const_iterator pos, initializer_list_t ilist) + { + // insert only works for arrays + if (JSON_HEDLEY_UNLIKELY(!is_array())) + { + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + } + + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + } + + // insert to array and return iterator + return insert_iterator(pos, ilist.begin(), ilist.end()); + } + + /// @brief inserts range of elements into object + /// @sa https://json.nlohmann.me/api/basic_json/insert/ + void insert(const_iterator first, const_iterator last) + { + // insert only works for objects + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + } + + // check if range iterators belong to the same JSON object + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this)); + } + + // passed iterators must belong to objects + if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) + { + JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", *this)); + } + + m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); + } + + /// @brief updates a JSON object from another object, overwriting existing keys + /// @sa https://json.nlohmann.me/api/basic_json/update/ + void update(const_reference j, bool merge_objects = false) + { + update(j.begin(), j.end(), merge_objects); + } + + /// @brief updates a JSON object from another object, overwriting existing keys + /// @sa https://json.nlohmann.me/api/basic_json/update/ + void update(const_iterator first, const_iterator last, bool merge_objects = false) + { + // implicitly convert null value to an empty object + if (is_null()) + { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } + + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()), *this)); + } + + // check if range iterators belong to the same JSON object + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this)); + } + + // passed iterators must belong to objects + if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) + { + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(first.m_object->type_name()), *first.m_object)); + } + + for (auto it = first; it != last; ++it) + { + if (merge_objects && it.value().is_object()) + { + auto it2 = m_value.object->find(it.key()); + if (it2 != m_value.object->end()) + { + it2->second.update(it.value(), true); + continue; + } + } + m_value.object->operator[](it.key()) = it.value(); +#if JSON_DIAGNOSTICS + m_value.object->operator[](it.key()).m_parent = this; +#endif + } + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(reference other) noexcept ( + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value + ) + { + std::swap(m_type, other.m_type); + std::swap(m_value, other.m_value); + + set_parents(); + other.set_parents(); + assert_invariant(); + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + friend void swap(reference left, reference right) noexcept ( + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value + ) + { + left.swap(right); + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(array_t& other) // NOLINT(bugprone-exception-escape) + { + // swap only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + std::swap(*(m_value.array), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + } + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(object_t& other) // NOLINT(bugprone-exception-escape) + { + // swap only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + std::swap(*(m_value.object), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + } + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(string_t& other) // NOLINT(bugprone-exception-escape) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_string())) + { + std::swap(*(m_value.string), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + } + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(binary_t& other) // NOLINT(bugprone-exception-escape) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_binary())) + { + std::swap(*(m_value.binary), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + } + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(typename binary_t::container_type& other) // NOLINT(bugprone-exception-escape) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_binary())) + { + std::swap(*(m_value.binary), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + } + } + + /// @} + + public: + ////////////////////////////////////////// + // lexicographical comparison operators // + ////////////////////////////////////////// + + /// @name lexicographical comparison operators + /// @{ + + /// @brief comparison: equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/ + friend bool operator==(const_reference lhs, const_reference rhs) noexcept + { +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + const auto lhs_type = lhs.type(); + const auto rhs_type = rhs.type(); + + if (lhs_type == rhs_type) + { + switch (lhs_type) + { + case value_t::array: + return *lhs.m_value.array == *rhs.m_value.array; + + case value_t::object: + return *lhs.m_value.object == *rhs.m_value.object; + + case value_t::null: + return true; + + case value_t::string: + return *lhs.m_value.string == *rhs.m_value.string; + + case value_t::boolean: + return lhs.m_value.boolean == rhs.m_value.boolean; + + case value_t::number_integer: + return lhs.m_value.number_integer == rhs.m_value.number_integer; + + case value_t::number_unsigned: + return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned; + + case value_t::number_float: + return lhs.m_value.number_float == rhs.m_value.number_float; + + case value_t::binary: + return *lhs.m_value.binary == *rhs.m_value.binary; + + case value_t::discarded: + default: + return false; + } + } + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_integer) == rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer) + { + return lhs.m_value.number_float == static_cast(rhs.m_value.number_integer); + } + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_float == static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer) + { + return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_integer; + } + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_integer == static_cast(rhs.m_value.number_unsigned); + } + + return false; +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + } + + /// @brief comparison: equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/ + template::value, int>::type = 0> + friend bool operator==(const_reference lhs, ScalarType rhs) noexcept + { + return lhs == basic_json(rhs); + } + + /// @brief comparison: equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/ + template::value, int>::type = 0> + friend bool operator==(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) == rhs; + } + + /// @brief comparison: not equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/ + friend bool operator!=(const_reference lhs, const_reference rhs) noexcept + { + return !(lhs == rhs); + } + + /// @brief comparison: not equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/ + template::value, int>::type = 0> + friend bool operator!=(const_reference lhs, ScalarType rhs) noexcept + { + return lhs != basic_json(rhs); + } + + /// @brief comparison: not equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/ + template::value, int>::type = 0> + friend bool operator!=(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) != rhs; + } + + /// @brief comparison: less than + /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/ + friend bool operator<(const_reference lhs, const_reference rhs) noexcept + { + const auto lhs_type = lhs.type(); + const auto rhs_type = rhs.type(); + + if (lhs_type == rhs_type) + { + switch (lhs_type) + { + case value_t::array: + // note parentheses are necessary, see + // https://github.com/nlohmann/json/issues/1530 + return (*lhs.m_value.array) < (*rhs.m_value.array); + + case value_t::object: + return (*lhs.m_value.object) < (*rhs.m_value.object); + + case value_t::null: + return false; + + case value_t::string: + return (*lhs.m_value.string) < (*rhs.m_value.string); + + case value_t::boolean: + return (lhs.m_value.boolean) < (rhs.m_value.boolean); + + case value_t::number_integer: + return (lhs.m_value.number_integer) < (rhs.m_value.number_integer); + + case value_t::number_unsigned: + return (lhs.m_value.number_unsigned) < (rhs.m_value.number_unsigned); + + case value_t::number_float: + return (lhs.m_value.number_float) < (rhs.m_value.number_float); + + case value_t::binary: + return (*lhs.m_value.binary) < (*rhs.m_value.binary); + + case value_t::discarded: + default: + return false; + } + } + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_integer) < rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer) + { + return lhs.m_value.number_float < static_cast(rhs.m_value.number_integer); + } + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_float < static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_integer < static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer) + { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_integer; + } + + // We only reach this line if we cannot compare values. In that case, + // we compare types. Note we have to call the operator explicitly, + // because MSVC has problems otherwise. + return operator<(lhs_type, rhs_type); + } + + /// @brief comparison: less than + /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/ + template::value, int>::type = 0> + friend bool operator<(const_reference lhs, ScalarType rhs) noexcept + { + return lhs < basic_json(rhs); + } + + /// @brief comparison: less than + /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/ + template::value, int>::type = 0> + friend bool operator<(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) < rhs; + } + + /// @brief comparison: less than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_le/ + friend bool operator<=(const_reference lhs, const_reference rhs) noexcept + { + return !(rhs < lhs); + } + + /// @brief comparison: less than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_le/ + template::value, int>::type = 0> + friend bool operator<=(const_reference lhs, ScalarType rhs) noexcept + { + return lhs <= basic_json(rhs); + } + + /// @brief comparison: less than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_le/ + template::value, int>::type = 0> + friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) <= rhs; + } + + /// @brief comparison: greater than + /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/ + friend bool operator>(const_reference lhs, const_reference rhs) noexcept + { + return !(lhs <= rhs); + } + + /// @brief comparison: greater than + /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/ + template::value, int>::type = 0> + friend bool operator>(const_reference lhs, ScalarType rhs) noexcept + { + return lhs > basic_json(rhs); + } + + /// @brief comparison: greater than + /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/ + template::value, int>::type = 0> + friend bool operator>(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) > rhs; + } + + /// @brief comparison: greater than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/ + friend bool operator>=(const_reference lhs, const_reference rhs) noexcept + { + return !(lhs < rhs); + } + + /// @brief comparison: greater than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/ + template::value, int>::type = 0> + friend bool operator>=(const_reference lhs, ScalarType rhs) noexcept + { + return lhs >= basic_json(rhs); + } + + /// @brief comparison: greater than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/ + template::value, int>::type = 0> + friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) >= rhs; + } + + /// @} + + /////////////////// + // serialization // + /////////////////// + + /// @name serialization + /// @{ +#ifndef JSON_NO_IO + /// @brief serialize to stream + /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/ + friend std::ostream& operator<<(std::ostream& o, const basic_json& j) + { + // read width member and use it as indentation parameter if nonzero + const bool pretty_print = o.width() > 0; + const auto indentation = pretty_print ? o.width() : 0; + + // reset width to 0 for subsequent calls to this stream + o.width(0); + + // do the actual serialization + serializer s(detail::output_adapter(o), o.fill()); + s.dump(j, pretty_print, false, static_cast(indentation)); + return o; + } + + /// @brief serialize to stream + /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/ + /// @deprecated This function is deprecated since 3.0.0 and will be removed in + /// version 4.0.0 of the library. Please use + /// operator<<(std::ostream&, const basic_json&) instead; that is, + /// replace calls like `j >> o;` with `o << j;`. + JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator<<(std::ostream&, const basic_json&)) + friend std::ostream& operator>>(const basic_json& j, std::ostream& o) + { + return o << j; + } +#endif // JSON_NO_IO + /// @} + + + ///////////////////// + // deserialization // + ///////////////////// + + /// @name deserialization + /// @{ + + /// @brief deserialize from a compatible input + /// @sa https://json.nlohmann.me/api/basic_json/parse/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json parse(InputType&& i, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false) + { + basic_json result; + parser(detail::input_adapter(std::forward(i)), cb, allow_exceptions, ignore_comments).parse(true, result); + return result; + } + + /// @brief deserialize from a pair of character iterators + /// @sa https://json.nlohmann.me/api/basic_json/parse/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json parse(IteratorType first, + IteratorType last, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false) + { + basic_json result; + parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions, ignore_comments).parse(true, result); + return result; + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len)) + static basic_json parse(detail::span_input_adapter&& i, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false) + { + basic_json result; + parser(i.get(), cb, allow_exceptions, ignore_comments).parse(true, result); + return result; + } + + /// @brief check if the input is valid JSON + /// @sa https://json.nlohmann.me/api/basic_json/accept/ + template + static bool accept(InputType&& i, + const bool ignore_comments = false) + { + return parser(detail::input_adapter(std::forward(i)), nullptr, false, ignore_comments).accept(true); + } + + /// @brief check if the input is valid JSON + /// @sa https://json.nlohmann.me/api/basic_json/accept/ + template + static bool accept(IteratorType first, IteratorType last, + const bool ignore_comments = false) + { + return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments).accept(true); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len)) + static bool accept(detail::span_input_adapter&& i, + const bool ignore_comments = false) + { + return parser(i.get(), nullptr, false, ignore_comments).accept(true); + } + + /// @brief generate SAX events + /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/ + template + JSON_HEDLEY_NON_NULL(2) + static bool sax_parse(InputType&& i, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true, + const bool ignore_comments = false) + { + auto ia = detail::input_adapter(std::forward(i)); + return format == input_format_t::json + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) + : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); + } + + /// @brief generate SAX events + /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/ + template + JSON_HEDLEY_NON_NULL(3) + static bool sax_parse(IteratorType first, IteratorType last, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true, + const bool ignore_comments = false) + { + auto ia = detail::input_adapter(std::move(first), std::move(last)); + return format == input_format_t::json + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) + : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); + } + + /// @brief generate SAX events + /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/ + /// @deprecated This function is deprecated since 3.8.0 and will be removed in + /// version 4.0.0 of the library. Please use + /// sax_parse(ptr, ptr + len) instead. + template + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, sax_parse(ptr, ptr + len, ...)) + JSON_HEDLEY_NON_NULL(2) + static bool sax_parse(detail::span_input_adapter&& i, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true, + const bool ignore_comments = false) + { + auto ia = i.get(); + return format == input_format_t::json + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); + } +#ifndef JSON_NO_IO + /// @brief deserialize from stream + /// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/ + /// @deprecated This stream operator is deprecated since 3.0.0 and will be removed in + /// version 4.0.0 of the library. Please use + /// operator>>(std::istream&, basic_json&) instead; that is, + /// replace calls like `j << i;` with `i >> j;`. + JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator>>(std::istream&, basic_json&)) + friend std::istream& operator<<(basic_json& j, std::istream& i) + { + return operator>>(i, j); + } + + /// @brief deserialize from stream + /// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/ + friend std::istream& operator>>(std::istream& i, basic_json& j) + { + parser(detail::input_adapter(i)).parse(false, j); + return i; + } +#endif // JSON_NO_IO + /// @} + + /////////////////////////// + // convenience functions // + /////////////////////////// + + /// @brief return the type as string + /// @sa https://json.nlohmann.me/api/basic_json/type_name/ + JSON_HEDLEY_RETURNS_NON_NULL + const char* type_name() const noexcept + { + switch (m_type) + { + case value_t::null: + return "null"; + case value_t::object: + return "object"; + case value_t::array: + return "array"; + case value_t::string: + return "string"; + case value_t::boolean: + return "boolean"; + case value_t::binary: + return "binary"; + case value_t::discarded: + return "discarded"; + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + default: + return "number"; + } + } + + + JSON_PRIVATE_UNLESS_TESTED: + ////////////////////// + // member variables // + ////////////////////// + + /// the type of the current element + value_t m_type = value_t::null; + + /// the value of the current element + json_value m_value = {}; + +#if JSON_DIAGNOSTICS + /// a pointer to a parent value (for debugging purposes) + basic_json* m_parent = nullptr; +#endif + + ////////////////////////////////////////// + // binary serialization/deserialization // + ////////////////////////////////////////// + + /// @name binary serialization/deserialization support + /// @{ + + public: + /// @brief create a CBOR serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/ + static std::vector to_cbor(const basic_json& j) + { + std::vector result; + to_cbor(j, result); + return result; + } + + /// @brief create a CBOR serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/ + static void to_cbor(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_cbor(j); + } + + /// @brief create a CBOR serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/ + static void to_cbor(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_cbor(j); + } + + /// @brief create a MessagePack serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/ + static std::vector to_msgpack(const basic_json& j) + { + std::vector result; + to_msgpack(j, result); + return result; + } + + /// @brief create a MessagePack serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/ + static void to_msgpack(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_msgpack(j); + } + + /// @brief create a MessagePack serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/ + static void to_msgpack(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_msgpack(j); + } + + /// @brief create a UBJSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/ + static std::vector to_ubjson(const basic_json& j, + const bool use_size = false, + const bool use_type = false) + { + std::vector result; + to_ubjson(j, result, use_size, use_type); + return result; + } + + /// @brief create a UBJSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/ + static void to_ubjson(const basic_json& j, detail::output_adapter o, + const bool use_size = false, const bool use_type = false) + { + binary_writer(o).write_ubjson(j, use_size, use_type); + } + + /// @brief create a UBJSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/ + static void to_ubjson(const basic_json& j, detail::output_adapter o, + const bool use_size = false, const bool use_type = false) + { + binary_writer(o).write_ubjson(j, use_size, use_type); + } + + /// @brief create a BSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_bson/ + static std::vector to_bson(const basic_json& j) + { + std::vector result; + to_bson(j, result); + return result; + } + + /// @brief create a BSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_bson/ + static void to_bson(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_bson(j); + } + + /// @brief create a BSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_bson/ + static void to_bson(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_bson(j); + } + + /// @brief create a JSON value from an input in CBOR format + /// @sa https://json.nlohmann.me/api/basic_json/from_cbor/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_cbor(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in CBOR format + /// @sa https://json.nlohmann.me/api/basic_json/from_cbor/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_cbor(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) + static basic_json from_cbor(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler); + } + + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) + static basic_json from_cbor(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in MessagePack format + /// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_msgpack(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in MessagePack format + /// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_msgpack(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) + static basic_json from_msgpack(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_msgpack(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) + static basic_json from_msgpack(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in UBJSON format + /// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_ubjson(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in UBJSON format + /// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_ubjson(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) + static basic_json from_ubjson(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_ubjson(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) + static basic_json from_ubjson(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in BSON format + /// @sa https://json.nlohmann.me/api/basic_json/from_bson/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bson(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in BSON format + /// @sa https://json.nlohmann.me/api/basic_json/from_bson/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bson(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) + static basic_json from_bson(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_bson(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) + static basic_json from_bson(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + /// @} + + ////////////////////////// + // JSON Pointer support // + ////////////////////////// + + /// @name JSON Pointer functions + /// @{ + + /// @brief access specified element via JSON Pointer + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + reference operator[](const json_pointer& ptr) + { + return ptr.get_unchecked(this); + } + + /// @brief access specified element via JSON Pointer + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + const_reference operator[](const json_pointer& ptr) const + { + return ptr.get_unchecked(this); + } + + /// @brief access specified element via JSON Pointer + /// @sa https://json.nlohmann.me/api/basic_json/at/ + reference at(const json_pointer& ptr) + { + return ptr.get_checked(this); + } + + /// @brief access specified element via JSON Pointer + /// @sa https://json.nlohmann.me/api/basic_json/at/ + const_reference at(const json_pointer& ptr) const + { + return ptr.get_checked(this); + } + + /// @brief return flattened JSON value + /// @sa https://json.nlohmann.me/api/basic_json/flatten/ + basic_json flatten() const + { + basic_json result(value_t::object); + json_pointer::flatten("", *this, result); + return result; + } + + /// @brief unflatten a previously flattened JSON value + /// @sa https://json.nlohmann.me/api/basic_json/unflatten/ + basic_json unflatten() const + { + return json_pointer::unflatten(*this); + } + + /// @} + + ////////////////////////// + // JSON Patch functions // + ////////////////////////// + + /// @name JSON Patch functions + /// @{ + + /// @brief applies a JSON patch + /// @sa https://json.nlohmann.me/api/basic_json/patch/ + basic_json patch(const basic_json& json_patch) const + { + // make a working copy to apply the patch to + basic_json result = *this; + + // the valid JSON Patch operations + enum class patch_operations {add, remove, replace, move, copy, test, invalid}; + + const auto get_op = [](const std::string & op) + { + if (op == "add") + { + return patch_operations::add; + } + if (op == "remove") + { + return patch_operations::remove; + } + if (op == "replace") + { + return patch_operations::replace; + } + if (op == "move") + { + return patch_operations::move; + } + if (op == "copy") + { + return patch_operations::copy; + } + if (op == "test") + { + return patch_operations::test; + } + + return patch_operations::invalid; + }; + + // wrapper for "add" operation; add value at ptr + const auto operation_add = [&result](json_pointer & ptr, basic_json val) + { + // adding to the root of the target document means replacing it + if (ptr.empty()) + { + result = val; + return; + } + + // make sure the top element of the pointer exists + json_pointer top_pointer = ptr.top(); + if (top_pointer != ptr) + { + result.at(top_pointer); + } + + // get reference to parent of JSON pointer ptr + const auto last_path = ptr.back(); + ptr.pop_back(); + basic_json& parent = result[ptr]; + + switch (parent.m_type) + { + case value_t::null: + case value_t::object: + { + // use operator[] to add value + parent[last_path] = val; + break; + } + + case value_t::array: + { + if (last_path == "-") + { + // special case: append to back + parent.push_back(val); + } + else + { + const auto idx = json_pointer::array_index(last_path); + if (JSON_HEDLEY_UNLIKELY(idx > parent.size())) + { + // avoid undefined behavior + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", parent)); + } + + // default case: insert add offset + parent.insert(parent.begin() + static_cast(idx), val); + } + break; + } + + // if there exists a parent it cannot be primitive + case value_t::string: // LCOV_EXCL_LINE + case value_t::boolean: // LCOV_EXCL_LINE + case value_t::number_integer: // LCOV_EXCL_LINE + case value_t::number_unsigned: // LCOV_EXCL_LINE + case value_t::number_float: // LCOV_EXCL_LINE + case value_t::binary: // LCOV_EXCL_LINE + case value_t::discarded: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + }; + + // wrapper for "remove" operation; remove value at ptr + const auto operation_remove = [this, &result](json_pointer & ptr) + { + // get reference to parent of JSON pointer ptr + const auto last_path = ptr.back(); + ptr.pop_back(); + basic_json& parent = result.at(ptr); + + // remove child + if (parent.is_object()) + { + // perform range check + auto it = parent.find(last_path); + if (JSON_HEDLEY_LIKELY(it != parent.end())) + { + parent.erase(it); + } + else + { + JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found", *this)); + } + } + else if (parent.is_array()) + { + // note erase performs range check + parent.erase(json_pointer::array_index(last_path)); + } + }; + + // type check: top level value must be an array + if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array())) + { + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", json_patch)); + } + + // iterate and apply the operations + for (const auto& val : json_patch) + { + // wrapper to get a value for an operation + const auto get_value = [&val](const std::string & op, + const std::string & member, + bool string_type) -> basic_json & + { + // find value + auto it = val.m_value.object->find(member); + + // context-sensitive error message + const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'"; + + // check if desired value is present + if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end())) + { + // NOLINTNEXTLINE(performance-inefficient-string-concatenation) + JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'", val)); + } + + // check if result is of type string + if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string())) + { + // NOLINTNEXTLINE(performance-inefficient-string-concatenation) + JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'", val)); + } + + // no error: return value + return it->second; + }; + + // type check: every element of the array must be an object + if (JSON_HEDLEY_UNLIKELY(!val.is_object())) + { + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", val)); + } + + // collect mandatory members + const auto op = get_value("op", "op", true).template get(); + const auto path = get_value(op, "path", true).template get(); + json_pointer ptr(path); + + switch (get_op(op)) + { + case patch_operations::add: + { + operation_add(ptr, get_value("add", "value", false)); + break; + } + + case patch_operations::remove: + { + operation_remove(ptr); + break; + } + + case patch_operations::replace: + { + // the "path" location must exist - use at() + result.at(ptr) = get_value("replace", "value", false); + break; + } + + case patch_operations::move: + { + const auto from_path = get_value("move", "from", true).template get(); + json_pointer from_ptr(from_path); + + // the "from" location must exist - use at() + basic_json v = result.at(from_ptr); + + // The move operation is functionally identical to a + // "remove" operation on the "from" location, followed + // immediately by an "add" operation at the target + // location with the value that was just removed. + operation_remove(from_ptr); + operation_add(ptr, v); + break; + } + + case patch_operations::copy: + { + const auto from_path = get_value("copy", "from", true).template get(); + const json_pointer from_ptr(from_path); + + // the "from" location must exist - use at() + basic_json v = result.at(from_ptr); + + // The copy is functionally identical to an "add" + // operation at the target location using the value + // specified in the "from" member. + operation_add(ptr, v); + break; + } + + case patch_operations::test: + { + bool success = false; + JSON_TRY + { + // check if "value" matches the one at "path" + // the "path" location must exist - use at() + success = (result.at(ptr) == get_value("test", "value", false)); + } + JSON_INTERNAL_CATCH (out_of_range&) + { + // ignore out of range errors: success remains false + } + + // throw an exception if test fails + if (JSON_HEDLEY_UNLIKELY(!success)) + { + JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump(), val)); + } + + break; + } + + case patch_operations::invalid: + default: + { + // op must be "add", "remove", "replace", "move", "copy", or + // "test" + JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid", val)); + } + } + } + + return result; + } + + /// @brief creates a diff as a JSON patch + /// @sa https://json.nlohmann.me/api/basic_json/diff/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json diff(const basic_json& source, const basic_json& target, + const std::string& path = "") + { + // the patch + basic_json result(value_t::array); + + // if the values are the same, return empty patch + if (source == target) + { + return result; + } + + if (source.type() != target.type()) + { + // different types: replace value + result.push_back( + { + {"op", "replace"}, {"path", path}, {"value", target} + }); + return result; + } + + switch (source.type()) + { + case value_t::array: + { + // first pass: traverse common elements + std::size_t i = 0; + while (i < source.size() && i < target.size()) + { + // recursive call to compare array values at index i + auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i)); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + ++i; + } + + // We now reached the end of at least one array + // in a second pass, traverse the remaining elements + + // remove my remaining elements + const auto end_index = static_cast(result.size()); + while (i < source.size()) + { + // add operations in reverse order to avoid invalid + // indices + result.insert(result.begin() + end_index, object( + { + {"op", "remove"}, + {"path", path + "/" + std::to_string(i)} + })); + ++i; + } + + // add other remaining elements + while (i < target.size()) + { + result.push_back( + { + {"op", "add"}, + {"path", path + "/-"}, + {"value", target[i]} + }); + ++i; + } + + break; + } + + case value_t::object: + { + // first pass: traverse this object's elements + for (auto it = source.cbegin(); it != source.cend(); ++it) + { + // escape the key name to be used in a JSON patch + const auto path_key = path + "/" + detail::escape(it.key()); + + if (target.find(it.key()) != target.end()) + { + // recursive call to compare object values at key it + auto temp_diff = diff(it.value(), target[it.key()], path_key); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + } + else + { + // found a key that is not in o -> remove it + result.push_back(object( + { + {"op", "remove"}, {"path", path_key} + })); + } + } + + // second pass: traverse other object's elements + for (auto it = target.cbegin(); it != target.cend(); ++it) + { + if (source.find(it.key()) == source.end()) + { + // found a key that is not in this -> add it + const auto path_key = path + "/" + detail::escape(it.key()); + result.push_back( + { + {"op", "add"}, {"path", path_key}, + {"value", it.value()} + }); + } + } + + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + // both primitive type: replace value + result.push_back( + { + {"op", "replace"}, {"path", path}, {"value", target} + }); + break; + } + } + + return result; + } + + /// @} + + //////////////////////////////// + // JSON Merge Patch functions // + //////////////////////////////// + + /// @name JSON Merge Patch functions + /// @{ + + /// @brief applies a JSON Merge Patch + /// @sa https://json.nlohmann.me/api/basic_json/merge_patch/ + void merge_patch(const basic_json& apply_patch) + { + if (apply_patch.is_object()) + { + if (!is_object()) + { + *this = object(); + } + for (auto it = apply_patch.begin(); it != apply_patch.end(); ++it) + { + if (it.value().is_null()) + { + erase(it.key()); + } + else + { + operator[](it.key()).merge_patch(it.value()); + } + } + } + else + { + *this = apply_patch; + } + } + + /// @} +}; + +/// @brief user-defined to_string function for JSON values +/// @sa https://json.nlohmann.me/api/basic_json/to_string/ +NLOHMANN_BASIC_JSON_TPL_DECLARATION +std::string to_string(const NLOHMANN_BASIC_JSON_TPL& j) +{ + return j.dump(); +} + +} // namespace nlohmann + +/////////////////////// +// nonmember support // +/////////////////////// + +namespace std // NOLINT(cert-dcl58-cpp) +{ + +/// @brief hash value for JSON objects +/// @sa https://json.nlohmann.me/api/basic_json/std_hash/ +NLOHMANN_BASIC_JSON_TPL_DECLARATION +struct hash +{ + std::size_t operator()(const nlohmann::NLOHMANN_BASIC_JSON_TPL& j) const + { + return nlohmann::detail::hash(j); + } +}; + +// specialization for std::less +template<> +struct less< ::nlohmann::detail::value_t> // do not remove the space after '<', see https://github.com/nlohmann/json/pull/679 +{ + /*! + @brief compare two value_t enum values + @since version 3.0.0 + */ + bool operator()(nlohmann::detail::value_t lhs, + nlohmann::detail::value_t rhs) const noexcept + { + return nlohmann::detail::operator<(lhs, rhs); + } +}; + +// C++20 prohibit function specialization in the std namespace. +#ifndef JSON_HAS_CPP_20 + +/// @brief exchanges the values of two JSON objects +/// @sa https://json.nlohmann.me/api/basic_json/std_swap/ +NLOHMANN_BASIC_JSON_TPL_DECLARATION +inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC_JSON_TPL& j2) noexcept( // NOLINT(readability-inconsistent-declaration-parameter-name) + is_nothrow_move_constructible::value&& // NOLINT(misc-redundant-expression) + is_nothrow_move_assignable::value) +{ + j1.swap(j2); +} + +#endif + +} // namespace std + +/// @brief user-defined string literal for JSON values +/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json/ +JSON_HEDLEY_NON_NULL(1) +inline nlohmann::json operator "" _json(const char* s, std::size_t n) +{ + return nlohmann::json::parse(s, s + n); +} + +/// @brief user-defined string literal for JSON pointer +/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json_pointer/ +JSON_HEDLEY_NON_NULL(1) +inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n) +{ + return nlohmann::json::json_pointer(std::string(s, n)); +} + +// #include + + +// restore clang diagnostic settings +#if defined(__clang__) + #pragma clang diagnostic pop +#endif + +// clean up +#undef JSON_ASSERT +#undef JSON_INTERNAL_CATCH +#undef JSON_CATCH +#undef JSON_THROW +#undef JSON_TRY +#undef JSON_PRIVATE_UNLESS_TESTED +#undef JSON_HAS_CPP_11 +#undef JSON_HAS_CPP_14 +#undef JSON_HAS_CPP_17 +#undef JSON_HAS_CPP_20 +#undef JSON_HAS_FILESYSTEM +#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM +#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION +#undef NLOHMANN_BASIC_JSON_TPL +#undef JSON_EXPLICIT +#undef NLOHMANN_CAN_CALL_STD_FUNC_IMPL + +// #include + + +#undef JSON_HEDLEY_ALWAYS_INLINE +#undef JSON_HEDLEY_ARM_VERSION +#undef JSON_HEDLEY_ARM_VERSION_CHECK +#undef JSON_HEDLEY_ARRAY_PARAM +#undef JSON_HEDLEY_ASSUME +#undef JSON_HEDLEY_BEGIN_C_DECLS +#undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE +#undef JSON_HEDLEY_CLANG_HAS_BUILTIN +#undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_CLANG_HAS_EXTENSION +#undef JSON_HEDLEY_CLANG_HAS_FEATURE +#undef JSON_HEDLEY_CLANG_HAS_WARNING +#undef JSON_HEDLEY_COMPCERT_VERSION +#undef JSON_HEDLEY_COMPCERT_VERSION_CHECK +#undef JSON_HEDLEY_CONCAT +#undef JSON_HEDLEY_CONCAT3 +#undef JSON_HEDLEY_CONCAT3_EX +#undef JSON_HEDLEY_CONCAT_EX +#undef JSON_HEDLEY_CONST +#undef JSON_HEDLEY_CONSTEXPR +#undef JSON_HEDLEY_CONST_CAST +#undef JSON_HEDLEY_CPP_CAST +#undef JSON_HEDLEY_CRAY_VERSION +#undef JSON_HEDLEY_CRAY_VERSION_CHECK +#undef JSON_HEDLEY_C_DECL +#undef JSON_HEDLEY_DEPRECATED +#undef JSON_HEDLEY_DEPRECATED_FOR +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#undef JSON_HEDLEY_DIAGNOSTIC_POP +#undef JSON_HEDLEY_DIAGNOSTIC_PUSH +#undef JSON_HEDLEY_DMC_VERSION +#undef JSON_HEDLEY_DMC_VERSION_CHECK +#undef JSON_HEDLEY_EMPTY_BASES +#undef JSON_HEDLEY_EMSCRIPTEN_VERSION +#undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK +#undef JSON_HEDLEY_END_C_DECLS +#undef JSON_HEDLEY_FLAGS +#undef JSON_HEDLEY_FLAGS_CAST +#undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE +#undef JSON_HEDLEY_GCC_HAS_BUILTIN +#undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_GCC_HAS_EXTENSION +#undef JSON_HEDLEY_GCC_HAS_FEATURE +#undef JSON_HEDLEY_GCC_HAS_WARNING +#undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#undef JSON_HEDLEY_GCC_VERSION +#undef JSON_HEDLEY_GCC_VERSION_CHECK +#undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE +#undef JSON_HEDLEY_GNUC_HAS_BUILTIN +#undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_GNUC_HAS_EXTENSION +#undef JSON_HEDLEY_GNUC_HAS_FEATURE +#undef JSON_HEDLEY_GNUC_HAS_WARNING +#undef JSON_HEDLEY_GNUC_VERSION +#undef JSON_HEDLEY_GNUC_VERSION_CHECK +#undef JSON_HEDLEY_HAS_ATTRIBUTE +#undef JSON_HEDLEY_HAS_BUILTIN +#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_HAS_EXTENSION +#undef JSON_HEDLEY_HAS_FEATURE +#undef JSON_HEDLEY_HAS_WARNING +#undef JSON_HEDLEY_IAR_VERSION +#undef JSON_HEDLEY_IAR_VERSION_CHECK +#undef JSON_HEDLEY_IBM_VERSION +#undef JSON_HEDLEY_IBM_VERSION_CHECK +#undef JSON_HEDLEY_IMPORT +#undef JSON_HEDLEY_INLINE +#undef JSON_HEDLEY_INTEL_CL_VERSION +#undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK +#undef JSON_HEDLEY_INTEL_VERSION +#undef JSON_HEDLEY_INTEL_VERSION_CHECK +#undef JSON_HEDLEY_IS_CONSTANT +#undef JSON_HEDLEY_IS_CONSTEXPR_ +#undef JSON_HEDLEY_LIKELY +#undef JSON_HEDLEY_MALLOC +#undef JSON_HEDLEY_MCST_LCC_VERSION +#undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK +#undef JSON_HEDLEY_MESSAGE +#undef JSON_HEDLEY_MSVC_VERSION +#undef JSON_HEDLEY_MSVC_VERSION_CHECK +#undef JSON_HEDLEY_NEVER_INLINE +#undef JSON_HEDLEY_NON_NULL +#undef JSON_HEDLEY_NO_ESCAPE +#undef JSON_HEDLEY_NO_RETURN +#undef JSON_HEDLEY_NO_THROW +#undef JSON_HEDLEY_NULL +#undef JSON_HEDLEY_PELLES_VERSION +#undef JSON_HEDLEY_PELLES_VERSION_CHECK +#undef JSON_HEDLEY_PGI_VERSION +#undef JSON_HEDLEY_PGI_VERSION_CHECK +#undef JSON_HEDLEY_PREDICT +#undef JSON_HEDLEY_PRINTF_FORMAT +#undef JSON_HEDLEY_PRIVATE +#undef JSON_HEDLEY_PUBLIC +#undef JSON_HEDLEY_PURE +#undef JSON_HEDLEY_REINTERPRET_CAST +#undef JSON_HEDLEY_REQUIRE +#undef JSON_HEDLEY_REQUIRE_CONSTEXPR +#undef JSON_HEDLEY_REQUIRE_MSG +#undef JSON_HEDLEY_RESTRICT +#undef JSON_HEDLEY_RETURNS_NON_NULL +#undef JSON_HEDLEY_SENTINEL +#undef JSON_HEDLEY_STATIC_ASSERT +#undef JSON_HEDLEY_STATIC_CAST +#undef JSON_HEDLEY_STRINGIFY +#undef JSON_HEDLEY_STRINGIFY_EX +#undef JSON_HEDLEY_SUNPRO_VERSION +#undef JSON_HEDLEY_SUNPRO_VERSION_CHECK +#undef JSON_HEDLEY_TINYC_VERSION +#undef JSON_HEDLEY_TINYC_VERSION_CHECK +#undef JSON_HEDLEY_TI_ARMCL_VERSION +#undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL2000_VERSION +#undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL430_VERSION +#undef JSON_HEDLEY_TI_CL430_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL6X_VERSION +#undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL7X_VERSION +#undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#undef JSON_HEDLEY_TI_CLPRU_VERSION +#undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#undef JSON_HEDLEY_TI_VERSION +#undef JSON_HEDLEY_TI_VERSION_CHECK +#undef JSON_HEDLEY_UNAVAILABLE +#undef JSON_HEDLEY_UNLIKELY +#undef JSON_HEDLEY_UNPREDICTABLE +#undef JSON_HEDLEY_UNREACHABLE +#undef JSON_HEDLEY_UNREACHABLE_RETURN +#undef JSON_HEDLEY_VERSION +#undef JSON_HEDLEY_VERSION_DECODE_MAJOR +#undef JSON_HEDLEY_VERSION_DECODE_MINOR +#undef JSON_HEDLEY_VERSION_DECODE_REVISION +#undef JSON_HEDLEY_VERSION_ENCODE +#undef JSON_HEDLEY_WARNING +#undef JSON_HEDLEY_WARN_UNUSED_RESULT +#undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#undef JSON_HEDLEY_FALL_THROUGH + + + +#endif // INCLUDE_NLOHMANN_JSON_HPP_ diff --git a/include/dpp/nlohmann/json_fwd.hpp b/include/dpp/nlohmann/json_fwd.hpp new file mode 100644 index 0000000..2d5ba38 --- /dev/null +++ b/include/dpp/nlohmann/json_fwd.hpp @@ -0,0 +1,64 @@ +#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ +#define INCLUDE_NLOHMANN_JSON_FWD_HPP_ + +#include // int64_t, uint64_t +#include // map +#include // allocator +#include // string +#include // vector + +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +namespace nlohmann +{ +/*! +@brief default JSONSerializer template argument + +This serializer ignores the template arguments and uses ADL +([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) +for serialization. +*/ +template +struct adl_serializer; + +/// a class to store JSON values +/// @sa https://json.nlohmann.me/api/basic_json/ +template class ObjectType = + std::map, + template class ArrayType = std::vector, + class StringType = std::string, class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator, + template class JSONSerializer = + adl_serializer, + class BinaryType = std::vector> +class basic_json; + +/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document +/// @sa https://json.nlohmann.me/api/json_pointer/ +template +class json_pointer; + +/*! +@brief default specialization +@sa https://json.nlohmann.me/api/json/ +*/ +using json = basic_json<>; + +/// @brief a minimal map-like container that preserves insertion order +/// @sa https://json.nlohmann.me/api/ordered_map/ +template +struct ordered_map; + +/// @brief specialization that maintains the insertion order of object keys +/// @sa https://json.nlohmann.me/api/ordered_json/ +using ordered_json = basic_json; + +} // namespace nlohmann + +#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ diff --git a/include/dpp/once.h b/include/dpp/once.h new file mode 100644 index 0000000..4ca494e --- /dev/null +++ b/include/dpp/once.h @@ -0,0 +1,46 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2022 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include + +namespace dpp { + + /** + * @brief Run some code within an if() statement only once. + * + * Use this template like this: + * + * ``` + * if (dpp::run_once()) { + * // Your code here + * } + * ``` + * + * @tparam T any unique 'tag' identifier name + * @return auto a true/false return to say if we should execute or not + */ + template auto run_once() { + static auto called = false; + return !std::exchange(called, true); + }; + +} // namespace dpp diff --git a/include/dpp/permissions.h b/include/dpp/permissions.h new file mode 100644 index 0000000..fccb333 --- /dev/null +++ b/include/dpp/permissions.h @@ -0,0 +1,269 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include +#include + +namespace dpp { + +/** + * @brief Represents the various discord permissions + */ +enum permissions : uint64_t { + p_create_instant_invite = 0x00000000001, //!< allows creation of instant invites + p_kick_members = 0x00000000002, //!< allows kicking members + p_ban_members = 0x00000000004, //!< allows banning members + p_administrator = 0x00000000008, //!< allows all permissions and bypasses channel permission overwrites + p_manage_channels = 0x00000000010, //!< allows management and editing of channels + p_manage_guild = 0x00000000020, //!< allows management and editing of the guild + p_add_reactions = 0x00000000040, //!< allows for the addition of reactions to messages + p_view_audit_log = 0x00000000080, //!< allows for viewing of audit logs + p_priority_speaker = 0x00000000100, //!< allows for using priority speaker in a voice channel + p_stream = 0x00000000200, //!< allows the user to go live + p_view_channel = 0x00000000400, //!< allows guild members to view a channel, which includes reading messages in text channels and joining voice channels + p_send_messages = 0x00000000800, //!< allows for sending messages in a channel + p_send_tts_messages = 0x00000001000, //!< allows for sending of /tts messages + p_manage_messages = 0x00000002000, //!< allows for deletion of other users messages + p_embed_links = 0x00000004000, //!< links sent by users with this permission will be auto-embedded + p_attach_files = 0x00000008000, //!< allows for uploading images and files + p_read_message_history = 0x00000010000, //!< allows for reading of message history + p_mention_everyone = 0x00000020000, //!< allows for using the everyone and the here tag to notify users in a channel + p_use_external_emojis = 0x00000040000, //!< allows the usage of custom emojis from other servers + p_view_guild_insights = 0x00000080000, //!< allows for viewing guild insights + p_connect = 0x00000100000, //!< allows for joining of a voice channel + p_speak = 0x00000200000, //!< allows for speaking in a voice channel + p_mute_members = 0x00000400000, //!< allows for muting members in a voice channel + p_deafen_members = 0x00000800000, //!< allows for deafening of members in a voice channel + p_move_members = 0x00001000000, //!< allows for moving of members between voice channels + p_use_vad = 0x00002000000, //!< allows for using voice-activity-detection in a voice channel + p_change_nickname = 0x00004000000, //!< allows for modification of own nickname + p_manage_nicknames = 0x00008000000, //!< allows for modification of other users nicknames + p_manage_roles = 0x00010000000, //!< allows management and editing of roles + p_manage_webhooks = 0x00020000000, //!< allows management and editing of webhooks + p_manage_emojis_and_stickers = 0x00040000000, //!< allows management and editing of emojis and stickers + p_use_application_commands = 0x00080000000, //!< allows members to use application commands, including slash commands and context menus + p_request_to_speak = 0x00100000000, //!< allows for requesting to speak in stage channels. (Discord: This permission is under active development and may be changed or removed.) + p_manage_events = 0x00200000000, //!< allows for management (creation, updating, deleting, starting) of scheduled events + p_manage_threads = 0x00400000000, //!< allows for deleting and archiving threads, and viewing all private threads + p_create_public_threads = 0x00800000000, //!< allows for creating public and announcement threads + p_create_private_threads = 0x01000000000, //!< allows for creating private threads + p_use_external_stickers = 0x02000000000, //!< allows the usage of custom stickers from other servers + p_send_messages_in_threads = 0x04000000000, //!< allows for sending messages in threads + p_use_embedded_activities = 0x08000000000, //!< allows for using activities (applications with the EMBEDDED flag) in a voice channel + p_moderate_members = 0x10000000000, //!< allows for timing out users to prevent them from sending or reacting to messages in chat and threads, and from speaking in voice and stage channels + p_view_creator_monetization_analytics = 0x20000000000, //!< allows for viewing role subscription insights + p_use_soundboard = 0x40000000000, //!< allows for using soundboard in a voice channel + p_use_external_sounds = 0x0000200000000000, //!< allows the usage of custom soundboard sounds from other servers + p_send_voice_messages = 0x0000400000000000, //!< allows sending voice messages +}; + +/** + * @brief Represents the various discord permissions + * @deprecated Use dpp::permissions instead. + */ +using role_permissions = permissions; + +/** + * @brief Represents a permission bitmask (refer to enum dpp::permissions) which are held in an uint64_t + */ +class DPP_EXPORT permission { +protected: + /** + * @brief The permission bitmask value + */ + uint64_t value{0}; + +public: + /** + * @brief Default constructor, initializes permission to 0 + */ + constexpr permission() = default; + + /** + * @brief Bitmask constructor, initializes permission to the argument + * @param value The bitmask to initialize the permission to + */ + constexpr permission(uint64_t value) noexcept : value{value} {} + + /** + * @brief For acting like an integer + * @return The permission bitmask value + */ + constexpr operator uint64_t() const noexcept { + return value; + } + + /** + * @brief For acting like an integer + * @return A reference to the permission bitmask value + */ + constexpr operator uint64_t &() noexcept { + return value; + } + + /** + * @brief For building json + * @return The permission bitmask value as a string + */ + operator nlohmann::json() const; + + /** + * @brief Check for certain permissions, taking into account administrator privileges. It uses the Bitwise AND operator + * @tparam T one or more uint64_t permission bits + * @param values The permissions (from dpp::permissions) to check for + * + * **Example:** + * + * ```cpp + * bool is_mod = permission.can(dpp::p_kick_members, dpp::p_ban_members); + * // Returns true if it has permission to p_kick_members and p_ban_members + * ``` + * + * @return bool True if it has **all** the given permissions or dpp::p_administrator + */ + template + constexpr bool can(T... values) const noexcept { + return has(values...) || (value & p_administrator); + } + + /** + * @brief Check for certain permissions, taking into account administrator privileges. It uses the Bitwise AND operator + * @tparam T one or more uint64_t permission bits + * @param values The permissions (from dpp::permissions) to check for + * + * **Example:** + * + * ```cpp + * bool is_mod = permission.can_any(dpp::p_kick_members, dpp::p_ban_members); + * // Returns true if it has permission to p_kick_members or p_ban_members + * ``` + * + * @return bool True if it has **any** of the given permissions or dpp::p_administrator + */ + template + constexpr bool can_any(T... values) const noexcept { + return has_any(values...) || (value & p_administrator); + } + + /** + * @brief Check for permission flags set. It uses the Bitwise AND operator + * @tparam T one or more uint64_t permission bits + * @param values The permissions (from dpp::permissions) to check for + * + * **Example:** + * + * ```cpp + * bool is_mod = permission.has(dpp::p_kick_members, dpp::p_ban_members); + * // Returns true if the permission bitmask contains p_kick_members and p_ban_members + * ``` + * + * @return bool True if it has **all** the given permissions + */ + template + constexpr bool has(T... values) const noexcept { + return (value & (0 | ... | values)) == (0 | ... | values); + } + + /** + * @brief Check for permission flags set. It uses the Bitwise AND operator + * @tparam T one or more uint64_t permission bits + * @param values The permissions (from dpp::permissions) to check for + * + * **Example:** + * + * ```cpp + * bool is_mod = permission.has_any(dpp::p_administrator, dpp::p_ban_members); + * // Returns true if the permission bitmask contains p_administrator or p_ban_members + * ``` + * + * @return bool True if it has **any** of the given permissions + */ + template + constexpr bool has_any(T... values) const noexcept { + return (value & (0 | ... | values)) != 0; + } + + /** + * @brief Add a permission with the Bitwise OR operation + * @tparam T one or more uint64_t permission bits + * @param values The permissions (from dpp::permissions) to add + * + * **Example:** + * + * ```cpp + * permission.add(dpp::p_view_channel, dpp::p_send_messages); + * // Adds p_view_channel and p_send_messages to the permission bitmask + * ``` + * + * @return permission& reference to self for chaining + */ + template + std::enable_if_t<(std::is_convertible_v && ...), permission&> + constexpr add(T... values) noexcept { + value |= (0 | ... | values); + return *this; + } + + /** + * @brief Assign permissions. This will reset the bitmask to the new value. + * @tparam T one or more uint64_t permission bits + * @param values The permissions (from dpp::permissions) to set + * + * **Example:** + * + * ```cpp + * permission.set(dpp::p_view_channel, dpp::p_send_messages); + * ``` + * + * @return permission& reference to self for chaining + */ + template + std::enable_if_t<(std::is_convertible_v && ...), permission&> + constexpr set(T... values) noexcept { + value = (0 | ... | values); + return *this; + } + + /** + * @brief Remove a permission with the Bitwise NOT operation + * @tparam T one or more uint64_t permission bits + * @param values The permissions (from dpp::permissions) to remove + * + * **Example:** + * + * ```cpp + * permission.remove(dpp::p_view_channel, dpp::p_send_messages); + * // Removes p_view_channel and p_send_messages permission + * ``` + * + * @return permission& reference to self for chaining + */ + template + std::enable_if_t<(std::is_convertible_v && ...), permission&> + constexpr remove(T... values) noexcept { + value &= ~(0 | ... | values); + return *this; + } +}; + +} // namespace dpp diff --git a/include/dpp/presence.h b/include/dpp/presence.h new file mode 100644 index 0000000..eafbfdc --- /dev/null +++ b/include/dpp/presence.h @@ -0,0 +1,395 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace dpp { + +/** + * @brief Presence flags bitmask + */ +enum presence_flags { + /// Desktop: Online + p_desktop_online = 0b00000001, + /// Desktop: DND + p_desktop_dnd = 0b00000010, + /// Desktop: Idle + p_desktop_idle = 0b00000011, + /// Web: Online + p_web_online = 0b00000100, + /// Web: DND + p_web_dnd = 0b00001000, + /// Web: Idle + p_web_idle = 0b00001100, + /// Mobile: Online + p_mobile_online = 0b00010000, + /// Mobile: DND + p_mobile_dnd = 0b00100000, + /// Mobile: Idle + p_mobile_idle = 0b00110000, + /// General: Online + p_status_online = 0b01000000, + /// General: DND + p_status_dnd = 0b10000000, + /// General: Idle + p_status_idle = 0b11000000 +}; + +/** + * @brief Online presence status values + */ +enum presence_status : uint8_t { + /// Offline + ps_offline = 0, + /// Online + ps_online = 1, + /// DND + ps_dnd = 2, + /// Idle + ps_idle = 3, + /// Invisible (show as offline) + ps_invisible = 4, +}; + +/** + * @brief Bit shift for desktop status + */ +#define PF_SHIFT_DESKTOP 0 +/** Bit shift for web status */ +#define PF_SHIFT_WEB 2 +/** Bit shift for mobile status */ +#define PF_SHIFT_MOBILE 4 +/** Bit shift for main status */ +#define PF_SHIFT_MAIN 6 +/** Bit mask for status */ +#define PF_STATUS_MASK 0b00000011 +/** Bit mask for clearing desktop status */ +#define PF_CLEAR_DESKTOP 0b11111100 +/** Bit mask for clearing web status */ +#define PF_CLEAR_WEB 0b11110011 +/** Bit mask for clearing mobile status */ +#define PF_CLEAR_MOBILE 0b11001111 +/** Bit mask for clearing main status */ +#define PF_CLEAR_STATUS 0b00111111 + +/** + * @brief Game types + */ +enum activity_type : uint8_t { + /// "Playing ..." + at_game = 0, + /// "Streaming ..." + at_streaming = 1, + /// "Listening to..." + at_listening = 2, + /// "Watching..." + at_watching = 3, + /// "Emoji..." + at_custom = 4, + /// "Competing in..." + at_competing = 5 +}; + +/** + * @brief Activity types for rich presence + */ +enum activity_flags { + /// In an instance + af_instance = 0b000000001, + /// Joining + af_join = 0b000000010, + /// Spectating + af_spectate = 0b000000100, + /// Sending join request + af_join_request = 0b000001000, + /// Synchronising + af_sync = 0b000010000, + /// Playing + af_play = 0b000100000, + /// Party privacy friends + af_party_privacy_friends = 0b001000000, + /// Party privacy voice channel + af_party_privacy_voice_channel = 0b010000000, + /// Embedded + af_embedded = 0b100000000 +}; + +/** + * @brief An activity button is a custom button shown in the rich presence. Can be to join a game or whatever + */ +struct DPP_EXPORT activity_button { +public: + /** The text shown on the button (1-32 characters) + */ + std::string label; + /** The url opened when clicking the button (1-512 characters). It's may be empty + * + * @note Bots cannot access the activity button URLs. + */ + std::string url; + + /** Constructor */ + activity_button() = default; +}; + +/** + * @brief An activity asset are the images and the hover text displayed in the rich presence + */ +struct DPP_EXPORT activity_assets { +public: + /** The large asset image which usually contain snowflake ID or prefixed image ID + */ + std::string large_image; + /** Text displayed when hovering over the large image of the activity + */ + std::string large_text; + /** The small asset image which usually contain snowflake ID or prefixed image ID + */ + std::string small_image; + /** Text displayed when hovering over the small image of the activity + */ + std::string small_text; + + /** Constructor */ + activity_assets() = default; +}; + +/** + * @brief Secrets for Rich Presence joining and spectating + */ +struct DPP_EXPORT activity_secrets { +public: + /** The secret for joining a party + */ + std::string join; + /** The secret for spectating a game + */ + std::string spectate; + /** The secret for a specific instanced match + */ + std::string match; + + /** Constructor */ + activity_secrets() = default; +}; + +/** + * @brief Information for the current party of the player + */ +struct DPP_EXPORT activity_party { +public: + /** The ID of the party + */ + snowflake id; + /** The party's current size. Used to show the party's current size + */ + int32_t current_size; + /** The party's maximum size. Used to show the party's maximum size + */ + int32_t maximum_size; + + /** Constructor */ + activity_party(); +}; + +/** + * @brief An activity is a representation of what a user is doing. It might be a game, or a website, or a movie. Whatever. + */ +class DPP_EXPORT activity { +public: + /** Name of activity + * e.g. "Fortnite" + */ + std::string name; + /** State of activity or the custom user status. + * e.g. "Waiting in lobby" + */ + std::string state; + /** What the player is currently doing + */ + std::string details; + /** Images for the presence and their hover texts + */ + activity_assets assets; + /** URL. + * Only applicable for certain sites such a YouTube + * Alias: details + */ + std::string url; + /** The custom buttons shown in the Rich Presence (max 2) + */ + std::vector buttons; + /** The emoji used for the custom status + */ + dpp::emoji emoji; + /** Information of the current party if there is one + */ + activity_party party; + /** Secrets for rich presence joining and spectating + */ + activity_secrets secrets; + /** Activity type + */ + activity_type type; + /** Time activity was created + */ + time_t created_at; + /** Start time. e.g. when game was started + */ + time_t start; + /** End time, e.g. for songs on spotify + */ + time_t end; + /** Creating application (e.g. a linked account on the user's client) + */ + snowflake application_id; + /** Flags bitmask from dpp::activity_flags + */ + uint8_t flags; + /** Whether or not the activity is an instanced game session + */ + bool is_instance; + + /** + * @brief 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 + * + * @param size The size of the image in pixels. It can be any power of two between 16 and 4096, + * otherwise the default sized image is returned. + * @param format The format to use for the avatar. It can be one of `i_webp`, `i_jpg` or `i_png`. + * @return std::string image url or an empty string, if required attributes are missing or an invalid format was passed + */ + std::string get_large_asset_url(uint16_t size = 0, const image_type format = i_png) const; + + /** + * @brief 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 + * + * @param size The size of the image in pixels. It can be any power of two between 16 and 4096, + * otherwise the default sized image is returned. + * @param format The format to use for the avatar. It can be one of `i_webp`, `i_jpg` or `i_png`. + * @return std::string image url or an empty string, if required attributes are missing or an invalid format was passed + */ + std::string get_small_asset_url(uint16_t size = 0, const image_type format = i_png) const; + + activity(); + + /** + * @brief Construct a new activity + * + * @param typ activity type + * @param nam Name of the activity + * @param stat State of the activity + * @param url_ url of the activity, only works for certain sites, such as YouTube + */ + activity(const activity_type typ, const std::string& nam, const std::string& stat, const std::string& url_); +}; + +/** + * @brief Represents user presence, e.g. what game they are playing and if they are online + */ +class DPP_EXPORT presence : public json_interface { +public: + /** The user the presence applies to */ + snowflake user_id; + + /** Guild ID. Apparently, Discord supports this internally but the client doesn't... */ + snowflake guild_id; + + /** Flags bitmask containing dpp::presence_flags */ + uint8_t flags; + + /** List of activities */ + std::vector activities; + + /** Constructor */ + presence(); + + /** + * @brief Construct a new presence object with some parameters for sending to a websocket + * + * @param status Status of the activity + * @param type Type of activity + * @param activity_description Description of the activity + */ + presence(presence_status status, activity_type type, const std::string& activity_description); + + /** + * @brief Construct a new presence object with some parameters for sending to a websocket. + * + * @param status Status of the activity + * @param a Activity itself + */ + presence(presence_status status, const activity& a); + + /** Destructor */ + ~presence(); + + /** Fill this object from json. + * @param j JSON object to fill from + * @return A reference to self + */ + presence& fill_from_json(nlohmann::json* j); + + /** Build JSON from this object. + * + * Note: This excludes any part of the presence object that are not valid for websockets and bots, + * and includes websocket opcode 3. You will not get what you expect if you call this on a user's + * presence received from on_presence_update or on_guild_create! + * + * @param with_id Add ID to output + * @return The JSON text of the presence + */ + virtual std::string build_json(bool with_id = false) const; + + /** The users status on desktop + * @return The user's status on desktop + */ + presence_status desktop_status() const; + + /** The user's status on web + * @return The user's status on web + */ + presence_status web_status() const; + + /** The user's status on mobile + * @return The user's status on mobile + */ + presence_status mobile_status() const; + + /** The user's status as shown to other users + * @return The user's status as shown to other users + */ + presence_status status() const; +}; + +/** A container of presences */ +typedef std::unordered_map presence_map; + +} // namespace dpp diff --git a/include/dpp/prune.h b/include/dpp/prune.h new file mode 100644 index 0000000..be7b228 --- /dev/null +++ b/include/dpp/prune.h @@ -0,0 +1,64 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include +#include + +namespace dpp { + +/** + * @brief Defines a request to count prunable users, or start a prune operation + */ +struct DPP_EXPORT prune : public json_interface { + /** + * Destroy this prune object + */ + virtual ~prune() = default; + + /** Number of days to include in the prune + */ + uint32_t days = 0; + /** Roles to include in the prune (empty to include everyone) + */ + std::vector include_roles; + /** True if the count of pruneable users should be returned + * (discord recommend not using this on big guilds) + */ + bool compute_prune_count; + + /** Fill this object from json. + * @param j JSON object to fill from + * @return A reference to self + */ + prune& fill_from_json(nlohmann::json* j); + + /** Build JSON from this object. + * @param with_prune_count True if the prune count boolean is to be set in the built JSON + * @return The JSON text of the prune object + */ + virtual std::string build_json(bool with_prune_count = false) const; + +}; + +} // namespace dpp diff --git a/include/dpp/queues.h b/include/dpp/queues.h new file mode 100644 index 0000000..61fde7c --- /dev/null +++ b/include/dpp/queues.h @@ -0,0 +1,462 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace dpp { + +/** + * @brief Error values. Most of these are currently unused in https_client. + */ +enum http_error { + /// Request successful + h_success = 0, + /// Status unknown + h_unknown, + /// Connect failed + h_connection, + /// Invalid local ip address + h_bind_ip_address, + /// Read error + h_read, + /// Write error + h_write, + /// Too many 30x redirects + h_exceed_redirect_count, + /// Request cancelled + h_canceled, + /// SSL connection error + h_ssl_connection, + /// SSL cert loading error + h_ssl_loading_certs, + /// SSL server verification error + h_ssl_server_verification, + /// Unsupported multipart boundary characters + h_unsupported_multipart_boundary_chars, + /// Compression error + h_compression, +}; + +/** + * @brief The result of any HTTP request. Contains the headers, vital + * rate limit figures, and returned request body. + */ +struct DPP_EXPORT http_request_completion_t { + /** @brief HTTP headers of response */ + std::multimap headers; + /** @brief HTTP status, e.g. 200 = OK, 404 = Not found, 429 = Rate limited */ + uint16_t status = 0; + /** @brief Error status (e.g. if the request could not connect at all) */ + http_error error = h_success; + /** @brief Ratelimit bucket */ + std::string ratelimit_bucket; + /** @brief Ratelimit limit of requests */ + uint64_t ratelimit_limit = 0; + /** @brief Ratelimit remaining requests */ + uint64_t ratelimit_remaining = 0; + /** @brief Ratelimit reset after (seconds) */ + uint64_t ratelimit_reset_after = 0; + /** @brief Ratelimit retry after (seconds) */ + uint64_t ratelimit_retry_after = 0; + /** @brief True if this request has caused us to be globally rate limited */ + bool ratelimit_global = false; + /** @brief Reply body */ + std::string body; + /** @brief Ping latency */ + double latency; +}; + +/** + * @brief Results of HTTP requests are called back to these std::function types. + * @note Returned http_completion_events are called ASYNCHRONOUSLY in your + * code which means they execute in a separate thread. The completion events + * arrive in order. + */ +typedef std::function http_completion_event; + +/** + * @brief Various types of http method supported by the Discord API + */ +enum http_method { + /// GET + m_get, + /// POST + m_post, + /// PUT + m_put, + /// PATCH + m_patch, + /// DELETE + m_delete +}; + +/** + * @brief A HTTP request. + * + * You should instantiate one of these objects via its constructor, + * and pass a pointer to it into an instance of request_queue. Although you can + * directly call the run() method of the object and it will make a HTTP call, be + * aware that if you do this, it will be a **BLOCKING call** (not asynchronous) and + * will not respect rate limits, as both of these functions are managed by the + * request_queue class. + */ +class DPP_EXPORT http_request { + /** @brief Completion callback */ + http_completion_event complete_handler; + /** @brief True if request has been made */ + bool completed; + /** @brief True for requests that are not going to discord (rate limits code skipped) */ + bool non_discord; +public: + /** @brief Endpoint name e.g. /api/users */ + std::string endpoint; + /** @brief Major and minor parameters */ + std::string parameters; + /** @brief Postdata for POST and PUT */ + std::string postdata; + /** @brief HTTP method for request */ + http_method method; + /** @brief Audit log reason for Discord requests, if non-empty */ + std::string reason; + /** @brief Upload file name (server side) */ + std::vector file_name; + /** @brief Upload file contents (binary) */ + std::vector file_content; + /** @brief Upload file mime types (application/octet-stream if unspecified) */ + std::vector file_mimetypes; + /** @brief Request mime type */ + std::string mimetype; + /** @brief Request headers (non-discord requests only) */ + std::multimap req_headers; + /** @brief Waiting for rate limit to expire */ + bool waiting; + + /** + * @brief Constructor. When constructing one of these objects it should be passed to request_queue::post_request(). + * @param _endpoint The API endpoint, e.g. /api/guilds + * @param _parameters Major and minor parameters for the endpoint e.g. a user id or guild id + * @param completion completion event to call when done + * @param _postdata Data to send in POST and PUT requests + * @param method The HTTP method to use from dpp::http_method + * @param audit_reason Audit log reason to send, empty to send none + * @param filename The filename (server side) of any uploaded file + * @param filecontent The binary content of any uploaded file for the request + * @param filemimetype The MIME type of any uploaded file for the request + */ + http_request(const std::string &_endpoint, const std::string &_parameters, http_completion_event completion, const std::string &_postdata = "", http_method method = m_get, const std::string &audit_reason = "", const std::string &filename = "", const std::string &filecontent = "", const std::string &filemimetype = ""); + + /** + * @brief Constructor. When constructing one of these objects it should be passed to request_queue::post_request(). + * @param _endpoint The API endpoint, e.g. /api/guilds + * @param _parameters Major and minor parameters for the endpoint e.g. a user id or guild id + * @param completion completion event to call when done + * @param _postdata Data to send in POST and PUT requests + * @param method The HTTP method to use from dpp::http_method + * @param audit_reason Audit log reason to send, empty to send none + * @param filename The filename (server side) of any uploaded file + * @param filecontent The binary content of any uploaded file for the request + * @param filemimetypes The MIME type of any uploaded file for the request + */ + http_request(const std::string &_endpoint, const std::string &_parameters, http_completion_event completion, const std::string &_postdata = "", http_method method = m_get, const std::string &audit_reason = "", const std::vector &filename = {}, const std::vector &filecontent = {}, const std::vector &filemimetypes = {}); + + /** + * @brief Constructor. When constructing one of these objects it should be passed to request_queue::post_request(). + * @param _url Raw HTTP url + * @param completion completion event to call when done + * @param method The HTTP method to use from dpp::http_method + * @param _postdata Data to send in POST and PUT requests + * @param _mimetype POST data mime type + * @param _headers HTTP headers to send + */ + http_request(const std::string &_url, http_completion_event completion, http_method method = m_get, const std::string &_postdata = "", const std::string &_mimetype = "text/plain", const std::multimap &_headers = {}); + + /** + * @brief Destroy the http request object + */ + ~http_request(); + + /** + * @brief Call the completion callback, if the request is complete. + * @param c callback to call + */ + void complete(const http_request_completion_t &c); + + /** + * @brief Execute the HTTP request and mark the request complete. + * @param owner creating cluster + */ + http_request_completion_t run(class cluster* owner); + + /** @brief Returns true if the request is complete */ + bool is_completed(); +}; + +/** + * @brief A rate limit bucket. The library builds one of these for + * each endpoint. + */ +struct DPP_EXPORT bucket_t { + /** @brief Request limit */ + uint64_t limit; + /** @brief Requests remaining */ + uint64_t remaining; + /** @brief Ratelimit of this bucket resets after this many seconds */ + uint64_t reset_after; + /** @brief Ratelimit of this bucket can be retried after this many seconds */ + uint64_t retry_after; + /** @brief Timestamp this buckets counters were updated */ + time_t timestamp; +}; + + +/** + * @brief Represents a thread in the thread pool handling requests to HTTP(S) servers. + * There are several of these, the total defined by a constant in queues.cpp, and each + * one will always receive requests for the same rate limit bucket based on its endpoint + * portion of the url. This makes rate limit handling reliable and easy to manage. + * Each of these also has its own mutex, so that requests are less likely to block while + * waiting for internal containers to be usable. + */ +class DPP_EXPORT in_thread { +private: + /** + * @brief True if ending + */ + bool terminating; + + /** + * @brief Request queue that owns this in_thread + */ + class request_queue* requests; + + /** + * @brief The cluster that owns this in_thread + */ + class cluster* creator; + + /** + * @brief Inbound queue mutex thread safety + */ + std::shared_mutex in_mutex; + + /** + * @brief Inbound queue thread + */ + std::thread* in_thr; + + /** + * @brief Inbound queue condition, signalled when there are requests to fulfill + */ + std::condition_variable in_ready; + + /** + * @brief Ratelimit bucket counters + */ + std::map buckets; + + /** + * @brief Queue of requests to be made + */ + std::map> requests_in; + + /** + * @brief Inbound queue thread loop + * @param index Thread index + */ + void in_loop(uint32_t index); +public: + /** + * @brief Construct a new in thread object + * + * @param owner Owning cluster + * @param req_q Owning request queue + * @param index Thread index number + */ + in_thread(class cluster* owner, class request_queue* req_q, uint32_t index); + + /** + * @brief Destroy the in thread object + * This will end the thread that is owned by this object by joining it. + */ + ~in_thread(); + + /** + * @brief Post a http_request to this thread. + * + * @param req http_request to post. The pointer will be freed when it has + * been executed. + */ + void post_request(http_request* req); +}; + +/** + * @brief The request_queue class manages rate limits and marshalls HTTP requests that have + * been built as http_request objects. + * + * It ensures asynchronous delivery of events and queueing of requests. + * + * It will spawn two threads, one to make outbound HTTP requests and push the returned + * results into a queue, and the second to call the callback methods with these results. + * They are separated so that if the user decides to take a long time processing a reply + * in their callback it won't affect when other requests are sent, and if a HTTP request + * takes a long time due to latency, it won't hold up user processing. + * + * There are usually two request_queue objects in each dpp::cluster, one of which is used + * internally for the various REST methods to Discord such as sending messages, and the other + * used to support user REST calls via dpp::cluster::request(). + */ +class DPP_EXPORT request_queue { +protected: + /** + * @brief Required so in_thread can access these member variables + */ + friend class in_thread; + + /** + * @brief The cluster that owns this request_queue + */ + class cluster* creator; + + /** + * @brief Outbound queue mutex thread safety + */ + std::shared_mutex out_mutex; + + /** + * @brief Outbound queue thread + * Note that although there are many 'in queues', which handle the HTTP requests, + * there is only ever one 'out queue' which dispatches the results to the caller. + * This is to simplify thread management in bots that use the library, as less mutexing + * and thread safety boilerplate is required. + */ + std::thread* out_thread; + + /** + * @brief Outbound queue condition, signalled when there are requests completed to call callbacks for + */ + std::condition_variable out_ready; + + /** + * @brief Completed requests queue + */ + std::queue> responses_out; + + /** + * @brief A vector of inbound request threads forming a pool. + * There are a set number of these defined by a constant in queues.cpp. A request is always placed + * on the same element in this vector, based upon its url, so that two conditions are satisfied: + * 1) Any requests for the same ratelimit bucket are handled by the same thread in the pool so that + * they do not create unnecessary 429 errors, + * 2) Requests for different endpoints go into different buckets, so that they may be requested in parallel + * A global ratelimit event pauses all threads in the pool. These are few and far between. + */ + std::vector requests_in; + + /** + * @brief Completed requests to delete + */ + std::multimap> responses_to_delete; + + /** + * @brief Set to true if the threads should terminate + */ + bool terminating; + + /** + * @brief True if globally rate limited - makes the entire request thread wait + */ + bool globally_ratelimited; + + /** + * @brief How many seconds we are globally rate limited for, if globally_ratelimited is true + */ + uint64_t globally_limited_for; + + /** + * @brief Number of request threads in the thread pool + */ + uint32_t in_thread_pool_size; + + /** + * @brief Outbound queue thread loop + */ + void out_loop(); +public: + + /** + * @brief constructor + * @param owner The creating cluster. + * @param request_threads The number of http request threads to allocate to the threadpool. + * By default eight threads are allocated. + * Side effects: Creates threads for the queue + */ + request_queue(class cluster* owner, uint32_t request_threads = 8); + + /** + * @brief Add more request threads to the library at runtime. + * @note You should do this at a quiet time when there are few requests happening. + * This will reorganise the hashing used to place requests into the thread pool so if you do + * this while the bot is busy there is a small chance of receiving "429 rate limited" errors. + * @param request_threads Number of threads to add. It is not possible to scale down at runtime. + * @return reference to self + */ + request_queue& add_request_threads(uint32_t request_threads); + + /** + * @brief Get the request thread count + * @return uint32_t number of request threads that are active + */ + uint32_t get_request_thread_count() const; + + /** + * @brief Destroy the request queue object. + * Side effects: Joins and deletes queue threads + */ + ~request_queue(); + + /** + * @brief Put a http_request into the request queue. You should ALWAYS "new" an object + * to pass to here -- don't submit an object that's on the stack! + * @note Will use a simple hash function to determine which of the 'in queues' to place + * this request onto. + * @param req request to add + * @return reference to self + */ + request_queue& post_request(http_request *req); + + /** + * @brief Returns true if the bot is currently globally rate limited + * @return true if globally rate limited + */ + bool is_globally_ratelimited() const; +}; + +} // namespace dpp diff --git a/include/dpp/restrequest.h b/include/dpp/restrequest.h new file mode 100644 index 0000000..e948713 --- /dev/null +++ b/include/dpp/restrequest.h @@ -0,0 +1,264 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2022 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include +#include +#include + +namespace dpp { + +/** + * @brief Templated REST request helper to save on typing + * + * @tparam T type to return in lambda callback + * @param c calling cluster + * @param basepath base path for API call + * @param major major API function + * @param minor minor API function + * @param method HTTP method + * @param postdata Post data or empty string + * @param callback Callback lambda + */ +template inline void rest_request(dpp::cluster* c, const char* basepath, const std::string &major, const std::string &minor, http_method method, const std::string& postdata, command_completion_event_t callback) { + c->post_rest(basepath, major, minor, method, postdata, [c, callback](json &j, const http_request_completion_t& http) { + if (callback) { + callback(confirmation_callback_t(c, T().fill_from_json(&j), http)); + } + }); +}; + +/** + * @brief Templated REST request helper to save on typing (specialised for message) + * + * @tparam T type to return in lambda callback + * @param c calling cluster + * @param basepath base path for API call + * @param major major API function + * @param minor minor API function + * @param method HTTP method + * @param postdata Post data or empty string + * @param callback Callback lambda + */ +template<> inline void rest_request(dpp::cluster* c, const char* basepath, const std::string &major, const std::string &minor, http_method method, const std::string& postdata, command_completion_event_t callback) { + c->post_rest(basepath, major, minor, method, postdata, [c, callback](json &j, const http_request_completion_t& http) { + if (callback) { + callback(confirmation_callback_t(c, message(c).fill_from_json(&j), http)); + } + }); +}; + +/** + * @brief Templated REST request helper to save on typing (specialised for confirmation) + * + * @tparam T type to return in lambda callback + * @param c calling cluster + * @param basepath base path for API call + * @param major major API function + * @param minor minor API function + * @param method HTTP method + * @param postdata Post data or empty string + * @param callback Callback lambda + */ +template<> inline void rest_request(dpp::cluster* c, const char* basepath, const std::string &major, const std::string &minor, http_method method, const std::string& postdata, command_completion_event_t callback) { + c->post_rest(basepath, major, minor, method, postdata, [c, callback](json &j, const http_request_completion_t& http) { + if (callback) { + callback(confirmation_callback_t(c, confirmation(), http)); + } + }); +}; + +/** + * @brief Templated REST request helper to save on typing (for returned lists) + * + * @tparam T singular type to return in lambda callback + * @tparam T map type to return in lambda callback + * @param c calling cluster + * @param basepath base path for API call + * @param major major API function + * @param minor minor API function + * @param method HTTP method + * @param postdata Post data or empty string + * @param key Key name of elements in the json list + * @param callback Callback lambda + */ +template inline void rest_request_list(dpp::cluster* c, const char* basepath, const std::string &major, const std::string &minor, http_method method, const std::string& postdata, command_completion_event_t callback, const std::string& key = "id") { + c->post_rest(basepath, major, minor, method, postdata, [c, key, callback](json &j, const http_request_completion_t& http) { + std::unordered_map list; + confirmation_callback_t e(c, confirmation(), http); + if (!e.is_error()) { + for (auto & curr_item : j) { + list[snowflake_not_null(&curr_item, key.c_str())] = T().fill_from_json(&curr_item); + } + } + if (callback) { + callback(confirmation_callback_t(c, list, http)); + } + }); +} + +/** + * @brief Templated REST request helper to save on typing (for returned lists, specialised for invites) + * + * @tparam T singular type to return in lambda callback + * @tparam T map type to return in lambda callback + * @param c calling cluster + * @param basepath base path for API call + * @param major major API function + * @param minor minor API function + * @param method HTTP method + * @param postdata Post data or empty string + * @param key Key name of elements in the json list + * @param callback Callback lambda + */ +template<> inline void rest_request_list(dpp::cluster* c, const char* basepath, const std::string &major, const std::string &minor, http_method method, const std::string& postdata, command_completion_event_t callback, const std::string& key) { + c->post_rest(basepath, major, minor, method, postdata, [c, callback](json &j, const http_request_completion_t& http) { + invite_map list; + confirmation_callback_t e(c, confirmation(), http); + if (!e.is_error()) { + for (auto & curr_item : j) { + list[string_not_null(&curr_item, "code")] = invite().fill_from_json(&curr_item); + } + } + if (callback) { + callback(confirmation_callback_t(c, list, http)); + } + }); +} +/** + * @brief Templated REST request helper to save on typing (for returned lists, specialised for voiceregions) + * + * @tparam T singular type to return in lambda callback + * @tparam T map type to return in lambda callback + * @param c calling cluster + * @param basepath base path for API call + * @param major major API function + * @param minor minor API function + * @param method HTTP method + * @param postdata Post data or empty string + * @param key Key name of elements in the json list + * @param callback Callback lambda + */ +template<> inline void rest_request_list(dpp::cluster* c, const char* basepath, const std::string &major, const std::string &minor, http_method method, const std::string& postdata, command_completion_event_t callback, const std::string& key) { + c->post_rest(basepath, major, minor, method, postdata, [c, callback](json &j, const http_request_completion_t& http) { + voiceregion_map list; + confirmation_callback_t e(c, confirmation(), http); + if (!e.is_error()) { + for (auto & curr_item : j) { + list[string_not_null(&curr_item, "id")] = voiceregion().fill_from_json(&curr_item); + } + } + if (callback) { + callback(confirmation_callback_t(c, list, http)); + } + }); +} +/** + * @brief Templated REST request helper to save on typing (for returned lists, specialised for bans) + * + * @tparam T singular type to return in lambda callback + * @tparam T map type to return in lambda callback + * @param c calling cluster + * @param basepath base path for API call + * @param major major API function + * @param minor minor API function + * @param method HTTP method + * @param postdata Post data or empty string + * @param key Key name of elements in the json list + * @param callback Callback lambda + */ +template<> inline void rest_request_list(dpp::cluster* c, const char* basepath, const std::string &major, const std::string &minor, http_method method, const std::string& postdata, command_completion_event_t callback, const std::string& key) { + c->post_rest(basepath, major, minor, method, postdata, [c, callback](json &j, const http_request_completion_t& http) { + std::unordered_map list; + confirmation_callback_t e(c, confirmation(), http); + if (!e.is_error()) { + for (auto & curr_item : j) { + ban curr_ban = ban().fill_from_json(&curr_item); + list[curr_ban.user_id] = curr_ban; + } + } + if (callback) { + callback(confirmation_callback_t(c, list, http)); + } + }); +} +/** + * @brief Templated REST request helper to save on typing (for returned lists, specialised for sticker packs) + * + * @tparam T singular type to return in lambda callback + * @tparam T map type to return in lambda callback + * @param c calling cluster + * @param basepath base path for API call + * @param major major API function + * @param minor minor API function + * @param method HTTP method + * @param postdata Post data or empty string + * @param key Key name of elements in the json list + * @param callback Callback lambda + */ +template<> inline void rest_request_list(dpp::cluster* c, const char* basepath, const std::string &major, const std::string &minor, http_method method, const std::string& postdata, command_completion_event_t callback, const std::string& key) { + c->post_rest(basepath, major, minor, method, postdata, [c, key, callback](json &j, const http_request_completion_t& http) { + std::unordered_map list; + confirmation_callback_t e(c, confirmation(), http); + if (!e.is_error()) { + if (j.contains("sticker_packs")) { + for (auto &curr_item: j["sticker_packs"]) { + list[snowflake_not_null(&curr_item, key.c_str())] = sticker_pack().fill_from_json(&curr_item); + } + } + } + if (callback) { + callback(confirmation_callback_t(c, list, http)); + } + }); +} + +/** + * @brief Templated REST request helper to save on typing (for returned lists, specialised for objects which doesn't have ids) + * + * @tparam T singular type to return in lambda callback + * @tparam T vector type to return in lambda callback + * @param c calling cluster + * @param basepath base path for API call + * @param major major API function + * @param minor minor API function + * @param method HTTP method + * @param postdata Post data or empty string + * @param callback Callback lambda + */ +template inline void rest_request_vector(dpp::cluster* c, const char* basepath, const std::string &major, const std::string &minor, http_method method, const std::string& postdata, command_completion_event_t callback) { + c->post_rest(basepath, major, minor, method, postdata, [c, callback](json &j, const http_request_completion_t& http) { + std::vector list; + confirmation_callback_t e(c, confirmation(), http); + if (!e.is_error()) { + for (auto & curr_item : j) { + list.push_back(T().fill_from_json(&curr_item)); + } + } + if (callback) { + callback(confirmation_callback_t(c, list, http)); + } + }); +} + + +} // namespace dpp diff --git a/include/dpp/restresults.h b/include/dpp/restresults.h new file mode 100644 index 0000000..0691b41 --- /dev/null +++ b/include/dpp/restresults.h @@ -0,0 +1,319 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using json = nlohmann::json; + +namespace dpp { + +#ifdef _WIN32 + #ifdef _DEBUG + extern "C" DPP_EXPORT void you_are_using_a_debug_build_of_dpp_on_a_release_project(); + #else + extern "C" DPP_EXPORT void you_are_using_a_release_build_of_dpp_on_a_debug_project(); + #endif +#endif + +struct DPP_EXPORT version_checker { + version_checker() { + #ifdef _WIN32 + #ifdef _DEBUG + you_are_using_a_debug_build_of_dpp_on_a_release_project(); + #else + you_are_using_a_release_build_of_dpp_on_a_debug_project(); + #endif + #endif + } +}; + +static version_checker dpp_vc; + + +/** + * @brief A list of shards + */ +typedef std::map shard_list; + +/** + * @brief Represents the various information from the 'get gateway bot' api call + */ +struct DPP_EXPORT gateway { + /// Gateway websocket url + std::string url; + + /// Number of suggested shards to start + uint32_t shards; + + /// Total number of sessions that can be started + uint32_t session_start_total; + + /// How many sessions are left + uint32_t session_start_remaining; + + /// How many seconds until the session start quota resets + uint32_t session_start_reset_after; + + /// How many sessions can be started at the same time + uint32_t session_start_max_concurrency; + + /** + * @brief Construct a new gateway object + * + * @param j JSON data to construct from + */ + gateway(nlohmann::json* j); + + /** + * @brief Construct a new gateway object + */ + gateway(); + + /** + * @brief Fill this object from json + * + * @param j json to fill from + * @return gateway& reference to self + */ + gateway& fill_from_json(nlohmann::json* j); +}; + +/** + * @brief Confirmation object represents any true or false simple REST request + * + */ +struct DPP_EXPORT confirmation { + bool success; +}; + +/** + * @brief A container for types that can be returned for a REST API call + * + */ +typedef std::variant< + active_threads, + application_role_connection, + application_role_connection_metadata_list, + confirmation, + message, + message_map, + user, + user_identified, + user_map, + guild_member, + guild_member_map, + channel, + channel_map, + thread_member, + thread_member_map, + guild, + guild_map, + guild_command_permissions, + guild_command_permissions_map, + role, + role_map, + invite, + invite_map, + dtemplate, + dtemplate_map, + emoji, + emoji_map, + ban, + ban_map, + voiceregion, + voiceregion_map, + integration, + integration_map, + webhook, + webhook_map, + prune, + guild_widget, + gateway, + interaction, + interaction_response, + auditlog, + slashcommand, + slashcommand_map, + stage_instance, + sticker, + sticker_map, + sticker_pack, + sticker_pack_map, + application, + application_map, + connection, + connection_map, + thread, + thread_map, + scheduled_event, + scheduled_event_map, + event_member, + event_member_map, + automod_rule, + automod_rule_map, + onboarding, + welcome_screen + > confirmable_t; + +/** + * @brief The details of a field in an error response + */ +struct DPP_EXPORT error_detail { + /** + * @brief Object name which is in error + */ + std::string object; + /** + * @brief Field name which is in error + */ + std::string field; + /** + * @brief Error code + */ + std::string code; + /** + * @brief Error reason (full message) + */ + std::string reason; +}; + +/** + * @brief The full details of an error from a REST response + */ +struct DPP_EXPORT error_info { + /** + * @brief Error code + */ + uint32_t code = 0; + /** + * @brief Error message + * + */ + std::string message; + /** + * @brief Field specific error descriptions + */ + std::vector errors; +}; + +/** + * @brief The results of a REST call wrapped in a convenient struct + */ +struct DPP_EXPORT confirmation_callback_t { + /** Information about the HTTP call used to make the request */ + http_request_completion_t http_info; + + /** Value returned, wrapped in variant */ + confirmable_t value; + + /** Owner/creator of the callback object */ + const class cluster* bot; + + /** + * @brief Construct a new confirmation callback t object + */ + confirmation_callback_t() = default; + + /** + * @brief Construct a new confirmation callback t object + * + * @param creator owning cluster object + */ + confirmation_callback_t(cluster* creator); + + /** + * @brief Construct a new confirmation callback object + * + * @param _http The HTTP metadata from the REST call + */ + confirmation_callback_t(const http_request_completion_t& _http); + + /** + * @brief Construct a new confirmation callback object + * + * @param creator owning cluster object + * @param _value The value to encapsulate in the confirmable_t + * @param _http The HTTP metadata from the REST call + */ + confirmation_callback_t(cluster* creator, const confirmable_t& _value, const http_request_completion_t& _http); + + /** + * @brief Returns true if the call resulted in an error rather than a legitimate value in the + * confirmation_callback_t::value member. + * + * @return true There was an error who's details can be obtained by get_error() + * @return false There was no error + */ + bool is_error() const; + + /** + * @brief Get the error_info object. + * The error_info object contains the details of any REST error, if there is an error + * (to find out if there is an error check confirmation_callback_t::is_error()) + * + * @return error_info The details of the error message + */ + error_info get_error() const; + + /** + * @brief Get the stored value via std::get + * @tparam T type to get + * @return stored value as type T + */ + template + T get() const { + return std::get(value); + } +}; + +/** + * @brief A callback upon command completion + */ +typedef std::function command_completion_event_t; + +/** + * @brief Automatically JSON encoded HTTP result + */ +typedef std::function json_encode_t; +} // namespace dpp diff --git a/include/dpp/role.h b/include/dpp/role.h new file mode 100644 index 0000000..d4b122a --- /dev/null +++ b/include/dpp/role.h @@ -0,0 +1,704 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace dpp { + +/** Various flags related to dpp::role */ +enum role_flags : uint8_t { + r_hoist = 0b00000001, //!< Hoisted role (if the role is pinned in the user listing) + r_managed = 0b00000010, //!< Managed role (introduced by a bot or application) + r_mentionable = 0b00000100, //!< Whether this role is mentionable with a ping + r_premium_subscriber = 0b00001000, //!< Whether this is the guild's booster role + r_available_for_purchase = 0b00010000, //!< Whether the role is available for purchase + r_guild_connections = 0b00100000, //!< Whether the role is a guild's linked role + r_in_prompt = 0b01000000, //!< Whether the role can be selected by members in an onboarding prompt +}; + +/** + * @brief Represents a role within a dpp::guild. + * Roles are combined via logical OR of the permission bitmasks, then channel-specific overrides + * can be applied on top, deny types apply a logic NOT to the bit mask, and allows apply a logical OR. + * @note Every guild has at least one role, called the 'everyone' role, which always has the same role + * ID as the guild's ID. This is the base permission set applied to all users where no other role or override + * applies, and is the starting value of the bit mask looped through to calculate channel permissions. + */ +class DPP_EXPORT role : public managed, public json_interface { +public: + /** + * @brief Role name + * Between 1 and 100 characters. + */ + std::string name; + /** + * @brief Guild ID + */ + snowflake guild_id; + /** + * @brief Role colour. + * A colour of 0 means no colour. If you want a black role, + * you must use the value 0x000001. + */ + uint32_t colour; + /** Role position */ + uint8_t position; + /** Role permissions bitmask values from dpp::permissions */ + permission permissions; + /** Role flags from dpp::role_flags */ + uint8_t flags; + /** Integration id if any (e.g. role is a bot's role created when it was invited) */ + snowflake integration_id; + /** Bot id if any (e.g. role is a bot's role created when it was invited) */ + snowflake bot_id; + /** The id of the role's subscription sku and listing */ + snowflake subscription_listing_id; + /** The unicode emoji used for the role's icon, can be an empty string */ + std::string unicode_emoji; + /** The role icon hash, can be an empty string */ + utility::iconhash icon; + /** Image data for the role icon (if any) */ + std::string* image_data; + + /** + * @brief Construct a new role object + */ + role(); + + /** + * @brief Destroy the role object + */ + virtual ~role(); + + /** + * @brief Create a mentionable role. + * @param id The ID of the role. + * @return std::string The formatted mention of the role. + */ + static std::string get_mention(const snowflake& id); + + /** + * @brief Set the name of the role + * Maximum length: 100 + * Minimum length: 1 + * @param n Name to set + * @return role& reference to self + * @throw dpp::exception thrown if role length is less than 1 character + */ + role& set_name(const std::string& n); + + /** + * @brief Set the colour + * + * @param c Colour to set + * @note There is an americanised version of this method, role::set_color(). + * @return role& reference to self + */ + role& set_colour(uint32_t c); + + /** + * @brief Set the color + * + * @param c Colour to set + * @note This is an alias of role::set_colour for American spelling. + * @return role& reference to self + */ + role& set_color(uint32_t c); + + /** + * @brief Set the flags + * + * @param f Flags to set from dpp::role_flags + * @return role& reference to self + */ + role& set_flags(uint8_t f); + + /** + * @brief Set the integration id + * + * @param i Integration ID to set + * @return role& reference to self + */ + role& set_integration_id(snowflake i); + + /** + * @brief Set the bot id + * + * @param b Bot ID to set + * @return role& reference to self + */ + role& set_bot_id(snowflake b); + + /** + * @brief Set the guild id + * + * @param gid Guild ID to set + * @return role& reference to self + */ + role& set_guild_id(snowflake gid); + + /** + * @brief Fill this role from json. + * + * @param j The json data + * @return A reference to self + */ + role& fill_from_json(nlohmann::json* j); + + /** + * @brief Fill this role from json. + * + * @param guild_id the guild id to place in the json + * @param j The json data + * @return A reference to self + */ + role& fill_from_json(snowflake guild_id, nlohmann::json* j); + + /** + * @brief Build a json string from this object. + * + * @param with_id true if the ID is to be included in the json text + * @return The json of the role + */ + virtual std::string build_json(bool with_id = false) const; + + /** + * @brief Get the mention/ping for the role + * + * @return std::string mention + */ + std::string get_mention() const; + + /** + * @brief Returns the role's icon url if they have one, otherwise returns an empty string + * + * @param size The size of the icon in pixels. It can be any power of two between 16 and 4096, + * otherwise the default sized icon is returned. + * @param format The format to use for the avatar. It can be one of `i_webp`, `i_jpg` or `i_png`. + * @return std::string icon url or an empty string, if required attributes are missing or an invalid format was passed + */ + std::string get_icon_url(uint16_t size = 0, const image_type format = i_png) const; + + /** + * @brief Load an image into the object as base64 + * + * @param image_blob Image binary data + * @param type Type of image. It can be one of `i_gif`, `i_jpg` or `i_png`. + * @return emoji& Reference to self + */ + role& load_image(const std::string &image_blob, const image_type type); + + /** + * @brief Operator less than, used for checking if a role is below another. + * + * @param lhs first role to compare + * @param rhs second role to compare + * @return true if lhs is less than rhs + */ + friend inline bool operator< (const role& lhs, const role& rhs) + { + return lhs.position < rhs.position; + } + + /** + * @brief Operator greater than, used for checking if a role is above another. + * + * @param lhs first role to compare + * @param rhs second role to compare + * @return true if lhs is greater than rhs + */ + friend inline bool operator> (const role& lhs, const role& rhs) + { + return lhs.position > rhs.position; + } + + /** + * @brief Operator equals, used for checking if a role is ranked equal to another. + * + * @param other role to compare + * @return true if is equal to other + */ + inline bool operator== (const role& other) const + { + return this->position == other.position; + } + + /** + * @brief Operator not equals, used for checking if a role is ranked equal to another. + * + * @param other role to compare + * @return true if is not equal to other + */ + inline bool operator!= (const role& other) const + { + return this->position != other.position; + } + + /** + * @brief True if the role is hoisted + * @return bool Role appears separated from others in the member list + */ + bool is_hoisted() const; + /** + * @brief True if the role is mentionable + * @return bool Role is mentionable + */ + bool is_mentionable() const; + /** + * @brief True if the role is managed (belongs to a bot or application) + * @return bool True if the role is managed (introduced for a bot or other application by Discord) + */ + bool is_managed() const; + /** + * @brief True if the role is the guild's Booster role + * @return bool whether the role is the premium subscriber, AKA "boost", role for the guild + */ + bool is_premium_subscriber() const; + /** + * @brief True if the role is available for purchase + * @return bool whether this role is available for purchase + */ + bool is_available_for_purchase() const; + /** + * @brief True if the role is a linked role + * @return bool True if the role is a linked role + */ + bool is_linked() const; + /** + * @brief True if the role can be selected by members in an onboarding prompt + * @return bool True if the role can be selected by members in an onboarding prompt + */ + bool is_selectable_in_prompt() const; + /** + * @brief True if has create instant invite permission + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the instant invite permission or is administrator. + */ + bool has_create_instant_invite() const; + /** + * @brief True if has the kick members permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the kick members permission or is administrator. + */ + bool has_kick_members() const; + /** + * @brief True if has the ban members permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the ban members permission or is administrator. + */ + bool has_ban_members() const; + /** + * @brief True if has the administrator permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the administrator permission or is administrator. + */ + bool has_administrator() const; + /** + * @brief True if has the manage channels permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the manage channels permission or is administrator. + */ + bool has_manage_channels() const; + /** + * @brief True if has the manage guild permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the manage guild permission or is administrator. + */ + bool has_manage_guild() const; + /** + * @brief True if has the add reactions permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the add reactions permission or is administrator. + */ + bool has_add_reactions() const; + /** + * @brief True if has the view audit log permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the view audit log permission or is administrator. + */ + bool has_view_audit_log() const; + /** + * @brief True if has the priority speaker permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the priority speaker permission or is administrator. + */ + bool has_priority_speaker() const; + /** + * @brief True if has the stream permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the stream permission or is administrator. + */ + bool has_stream() const; + /** + * @brief True if has the view channel permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the view channel permission or is administrator. + */ + bool has_view_channel() const; + /** + * @brief True if has the send messages permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the send messages permission or is administrator. + */ + bool has_send_messages() const; + /** + * @brief True if has the send TTS messages permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the send TTS messages permission or is administrator. + */ + bool has_send_tts_messages() const; + /** + * @brief True if has the manage messages permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the manage messages permission or is administrator. + */ + bool has_manage_messages() const; + /** + * @brief True if has the embed links permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the embed links permission or is administrator. + */ + bool has_embed_links() const; + /** + * @brief True if has the attach files permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the attach files permission or is administrator. + */ + bool has_attach_files() const; + /** + * @brief True if has the read message history permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the read message history permission or is administrator. + */ + bool has_read_message_history() const; + /** + * @brief True if has the mention \@everyone and \@here permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the mention \@everyone and \@here permission or is administrator. + */ + bool has_mention_everyone() const; + /** + * @brief True if has the use external emojis permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the use external emojis permission or is administrator. + */ + bool has_use_external_emojis() const; + /** + * @brief True if has the view guild insights permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the view guild insights permission or is administrator. + */ + bool has_view_guild_insights() const; + /** + * @brief True if has the connect voice permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the connect voice permission or is administrator. + */ + bool has_connect() const; + /** + * @brief True if has the speak permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the speak permission or is administrator. + */ + bool has_speak() const; + /** + * @brief True if has the mute members permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the mute members permission or is administrator. + */ + bool has_mute_members() const; + /** + * @brief True if has the deafen members permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the deafen members permission or is administrator. + */ + bool has_deafen_members() const; + /** + * @brief True if has the move members permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the move members permission or is administrator. + */ + bool has_move_members() const; + /** True if has use voice activity detection permission */ + bool has_use_vad() const; + /** + * @brief True if has the change nickname permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the change nickname permission or is administrator. + */ + bool has_change_nickname() const; + /** + * @brief True if has the manage nicknames permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the manage nicknames permission or is administrator. + */ + bool has_manage_nicknames() const; + /** + * @brief True if has the manage roles permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the manage roles permission or is administrator. + */ + bool has_manage_roles() const; + /** + * @brief True if has the manage webhooks permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the manage webhooks permission or is administrator. + */ + bool has_manage_webhooks() const; + /** + * @brief True if has the manage emojis and stickers permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the manage emojis and stickers permission or is administrator. + */ + bool has_manage_emojis_and_stickers() const; + /** + * @brief True if has the use application commands permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the use application commands permission or is administrator. + */ + bool has_use_application_commands() const; + /** + * @brief True if has the request to speak permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the request to speak permission or is administrator. + */ + bool has_request_to_speak() const; + /** + * @brief True if has the manage threads permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the manage threads permission or is administrator. + */ + bool has_manage_threads() const; + /** + * @brief True if has the create public threads permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the create public threads permission or is administrator. + */ + bool has_create_public_threads() const; + /** + * @brief True if has the create private threads permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the create private threads permission or is administrator. + */ + bool has_create_private_threads() const; + /** + * @brief True if has the use external stickers permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the use external stickers permission or is administrator. + */ + bool has_use_external_stickers() const; + /** + * @brief True if has the send messages in threads permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the send messages in threads permission or is administrator. + */ + bool has_send_messages_in_threads() const; + /** + * @brief True if has the start embedded activities permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the start embedded activities permission or is administrator. + */ + bool has_use_embedded_activities() const; + /** + * @brief True if has the manage events permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the manage events permission or is administrator. + */ + bool has_manage_events() const; + /** + * @brief True if has the moderate users permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the moderate users permission or is administrator. + */ + bool has_moderate_members() const; + /** + * @brief True if has the view creator monetization analytics permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the view creator monetization analytics permission or is administrator. + */ + bool has_view_creator_monetization_analytics() const; + /** + * @brief True if has the use soundboard permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the use soundboard permission or is administrator. + */ + bool has_use_soundboard() const; + /** + * @brief True if has the use external sounds permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the use external sounds permission or is administrator. + */ + bool has_use_external_sounds() const; + /** + * @brief True if has the send voice messages permission. + * @note Having the administrator permission causes this method to always return true + * Channel specific overrides may apply to permissions. + * @return bool True if user has the send voice messages permission or is administrator. + */ + bool has_send_voice_messages() const; + + /** + * @brief Get guild members who have this role + * @note This method requires user/members cache to be active + * @return members_container List of members who have this role + */ + members_container get_members() const; +}; + +/** + * @brief Application Role Connection Metadata Type + * + * @note Each metadata type offers a comparison operation that allows guilds to configure role requirements based on metadata values stored by the bot. Bots specify a `metadata value` for each user and guilds specify the required `guild's configured value` within the guild role settings. + */ +enum application_role_connection_metadata_type : uint8_t { + rc_integer_less_than_or_equal = 1, //!< The metadata value (integer) is less than or equal to the guild's configured value (integer) + rc_integer_greater_than_or_equal = 2, //!< The metadata value (integer) is greater than or equal to the guild's configured value (integer) + rc_integer_equal = 3, //!< The metadata value (integer) is equal to the guild's configured value (integer) + rc_integer_not_equal = 4, //!< The metadata value (integer) is not equal to the guild's configured value (integer) + rc_datetime_less_than_or_equal = 5, //!< The metadata value (ISO8601 string) is less than or equal to the guild's configured value (integer; days before current date) + rc_datetime_greater_than_or_equal = 6, //!< The metadata value (ISO8601 string) is greater than or equal to the guild's configured value (integer; days before current date) + rc_boolean_equal = 7, //!< The metadata value (integer) is equal to the guild's configured value (integer; 1) + rc_boolean_not_equal = 8, //!< The metadata value (integer) is not equal to the guild's configured value (integer; 1) +}; + +/** + * @brief Application Role Connection Metadata. Represents a role connection metadata for an dpp::application + */ +class DPP_EXPORT application_role_connection_metadata : public json_interface { +public: + application_role_connection_metadata_type type; //!< Type of metadata value + std::string key; //!< Dictionary key for the metadata field (must be `a-z`, `0-9`, or `_` characters; 1-50 characters) + std::string name; //!< Name of the metadata field (1-100 characters) + std::map name_localizations; //!< Translations of the name + std::string description; //!< Description of the metadata field (1-200 characters) + std::map description_localizations; //!< Translations of the description + + /** + * Constructor + */ + application_role_connection_metadata(); + + virtual ~application_role_connection_metadata() = default; + + /** Fill this record from json. + * @param j The json to fill this record from + * @return Reference to self + */ + application_role_connection_metadata& fill_from_json(nlohmann::json* j); + + /** + * @brief Convert to JSON string + * + * @param with_id include ID in output + * @return std::string JSON output + */ + virtual std::string build_json(bool with_id = false) const; +}; + +/** + * @brief The application role connection that an application has attached to a user. + */ +class DPP_EXPORT application_role_connection : public json_interface { +public: + std::string platform_name; //!< Optional: The vanity name of the platform a bot has connected (max 50 characters) + std::string platform_username; //!< Optional: The username on the platform a bot has connected (max 100 characters) + std::variant metadata; //!< Optional: Object mapping application role connection metadata keys to their stringified value (max 100 characters) for the user on the platform a bot has connected + + /** + * Constructor + */ + application_role_connection(); + + virtual ~application_role_connection() = default; + + /** Fill this record from json. + * @param j The json to fill this record from + * @return Reference to self + */ + application_role_connection& fill_from_json(nlohmann::json* j); + + /** + * @brief Convert to JSON string + * + * @param with_id include ID in output + * @return std::string JSON output + */ + virtual std::string build_json(bool with_id = false) const; +}; + +/** A group of roles */ +typedef std::unordered_map role_map; + +/** A group of application_role_connection_metadata objects */ +typedef std::vector application_role_connection_metadata_list; + +} // namespace dpp + diff --git a/include/dpp/scheduled_event.h b/include/dpp/scheduled_event.h new file mode 100644 index 0000000..f128d03 --- /dev/null +++ b/include/dpp/scheduled_event.h @@ -0,0 +1,225 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace dpp { + +/** + * @brief Represents the privacy of an event + */ +enum event_privacy_level : uint8_t { + /// The event is visible to only guild members. + ep_guild_only = 2 +}; + +/** + * @brief Event entity types + */ +enum event_entity_type : uint8_t { + /// A stage instance + eet_stage_instance = 1, + /// A voice channel + eet_voice = 2, + /// External to discord, or a text channel etc + eet_external = 3 +}; + +/** + * @brief Event status types + */ +enum event_status : uint8_t { + /// Scheduled + es_scheduled = 1, + /// Active now + es_active = 2, + /// Completed + es_completed = 3, + /// Cancelled + es_cancelled = 4 +}; + +/** + * @brief Entities for the event + */ +struct DPP_EXPORT event_entities { + /// location of the event + std::string location; +}; + +/** + * @brief Represents a guild member/user who has registered interest in an event + * + */ +struct DPP_EXPORT event_member { + /** + * @brief Event ID associated with + */ + snowflake guild_scheduled_event_id; + /** + * @brief User details of associated user + * + */ + dpp::user user; + /** + * @brief Member details of user on the associated guild + */ + dpp::guild_member member; +}; + +/** + * @brief A scheduled event + */ +struct DPP_EXPORT scheduled_event : public managed, public json_interface { + snowflake guild_id; //!< the guild id which the scheduled event belongs to + snowflake channel_id; //!< the channel id in which the scheduled event will be hosted, or null if scheduled entity type is EXTERNAL (may be empty) + snowflake creator_id; //!< Optional: the id of the user that created the scheduled event + std::string name; //!< the name of the scheduled event + std::string description; //!< Optional: the description of the scheduled event (1-1000 characters) + std::string image; //!< the image of the scheduled event (may be empty) + time_t scheduled_start_time; //!< the time the scheduled event will start + time_t scheduled_end_time; //!< the time the scheduled event will end, or null if the event does not have a scheduled time to end (may be empty) + event_privacy_level privacy_level; //!< the privacy level of the scheduled event + event_status status; //!< the status of the scheduled event + event_entity_type entity_type; //!< the type of hosting entity associated with a scheduled event, e.g. voice channel or stage channel + snowflake entity_id; //!< any additional id of the hosting entity associated with event, e.g. stage instance id) (may be empty) + event_entities entity_metadata; //!< the entity metadata for the scheduled event (may be empty) + user creator; //!< Optional: the creator of the scheduled event + uint32_t user_count; //!< Optional: the number of users subscribed to the scheduled event + + /** + * @brief Create a scheduled_event object + */ + scheduled_event(); + + /** + * @brief Destroy the scheduled_event object + */ + ~scheduled_event() = default; + + /** + * @brief Set the name of the event + * Minimum length: 1, Maximum length: 100 + * @param n event name + * @return scheduled_event& reference to self + * @throw dpp::length_error if length < 1 + */ + scheduled_event& set_name(const std::string& n); + + /** + * @brief Set the description of the event + * Minimum length: 1 (if set), Maximum length: 100 + * @param d event description + * @return scheduled_event& reference to self + * @throw dpp::length_error if length < 1 + */ + scheduled_event& set_description(const std::string& d); + + /** + * @brief Clear the description of the event + * @return scheduled_event& reference to self + */ + scheduled_event& clear_description(); + + /** + * @brief Set the location of the event. + * Minimum length: 1, Maximum length: 1000 + * @note Clears channel_id + * @param l event location + * @return scheduled_event& reference to self + * @throw dpp::length_error if length < 1 + */ + scheduled_event& set_location(const std::string& l); + + /** + * @brief Set the voice channel id of the event + * @note clears location + * @param c channel ID + * @return scheduled_event& reference to self + */ + scheduled_event& set_channel_id(snowflake c); + + /** + * @brief Set the creator id of the event + * @param c creator user ID + * @return scheduled_event& reference to self + */ + scheduled_event& set_creator_id(snowflake c); + + /** + * @brief Set the status of the event + * @param s status to set + * @return scheduled_event& reference to self + * @throw dpp::logic_exception if status change is not valid + */ + scheduled_event& set_status(event_status s); + + /** + * @brief Set the start time of the event + * @param t starting time + * @return scheduled_event& reference to self + * @throw dpp::length_error if time is before now + */ + scheduled_event& set_start_time(time_t t); + + /** + * @brief Set the end time of the event + * @param t ending time + * @return scheduled_event& reference to self + * @throw dpp::length_error if time is before now + */ + scheduled_event& set_end_time(time_t t); + + /** + * @brief Serialise a scheduled_event object from json + * + * @return scheduled_event& a reference to self + */ + scheduled_event& fill_from_json(const nlohmann::json* j); + + /** + * @brief Build json for this object + * @param with_id Include id field in json + * + * @return std::string Dumped json of this object + */ + virtual std::string build_json(bool with_id = false) const; +}; + +/** + * @brief A group of scheduled events + */ +typedef std::unordered_map scheduled_event_map; + +/** + * @brief A group of scheduled event members + */ +typedef std::unordered_map event_member_map; + + +} // namespace dpp diff --git a/include/dpp/snowflake.h b/include/dpp/snowflake.h new file mode 100644 index 0000000..df223f3 --- /dev/null +++ b/include/dpp/snowflake.h @@ -0,0 +1,207 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include +#include + +/** + * @brief The main namespace for D++ functions. classes and types + */ +namespace dpp { + +/** @brief A container for a 64 bit unsigned value representing many things on discord. + * This value is known in distributed computing as a snowflake value. + * + * Snowflakes are: + * + * - Performant (very fast to generate at source and to compare in code) + * - Uncoordinated (allowing high availability across clusters, data centres etc) + * - Time ordered (newer snowflakes have higher IDs) + * - Directly Sortable (due to time ordering) + * - Compact (64 bit numbers, not 128 bit, or string) + * + * An identical format of snowflake is used by Twitter, Instagram and several other platforms. + * + * @see https://en.wikipedia.org/wiki/Snowflake_ID + * @see https://github.com/twitter-archive/snowflake/tree/b3f6a3c6ca8e1b6847baa6ff42bf72201e2c2231 + */ +class DPP_EXPORT snowflake final { + friend struct std::hash; +protected: + /** + * @brief The snowflake value + */ + uint64_t value; + +public: + /** + * @brief Construct a snowflake object + * @param value A snowflake value + */ + snowflake(const uint64_t& value); + + /** + * @brief Construct a snowflake object + * @param string_value A snowflake value + */ + snowflake(const std::string& string_value); + + /** + * @brief Construct a snowflake object + */ + snowflake(); + + /** + * @brief Destroy the snowflake object + */ + ~snowflake() = default; + + /** + * @brief For acting like an integer + * @return The snowflake value + */ + operator uint64_t() const; + + /** + * @brief Returns true if the snowflake holds an empty value (is 0) + * + * @return true if empty (zero) + */ + inline bool empty() const + { + return value == 0; + } + + /** + * @brief Returns the stringified version of the snowflake value + * + * @return std::string string form of snowflake value + */ + inline std::string str() const + { + return std::to_string(value); + } + + /** + * @brief Operator less than, used for maps/unordered maps + * when the snowflake is a key value. + * + * @param lhs fist snowflake to compare + * @param rhs second snowflake to compare + * @return true if lhs is less than rhs + */ + friend inline bool operator< (const snowflake& lhs, const snowflake& rhs) + { + return lhs.value < rhs.value; + } + + /** + * @brief Assign from std::string + * + * @param snowflake_val string to assign from. + */ + snowflake& operator=(const std::string &snowflake_val); + + /** + * @brief Assign from std::string + * + * @param snowflake_val value to assign from. + */ + snowflake& operator=(const uint64_t &snowflake_val); + + /** + * @brief Check if one snowflake value is equal to another + * + * @param other other snowflake to compare + * @return True if the snowflake objects match + */ + bool operator==(const snowflake& other) const; + + /** + * @brief Check if one snowflake value is equal to a uint64_t + * + * @param other other snowflake to compare + * @return True if the snowflake objects match + */ + bool operator==(const uint64_t& other) const; + + /** + * @brief For acting like an integer + * @return A reference to the snowflake value + */ + operator uint64_t &(); + + /** + * @brief For building json + * @return The snowflake value as a string + */ + operator nlohmann::json() const; + + /** + * @brief Get the creation time of this snowflake according to Discord. + * + * @return double creation time inferred from the snowflake ID. + * The minimum possible value is the first second of 2015. + */ + double get_creation_time() const; + + /** + * @brief Get the worker id that produced this snowflake value + * + * @return uint8_t worker id + */ + uint8_t get_worker_id() const; + + /** + * @brief Get the process id that produced this snowflake value + * + * @return uint8_t process id + */ + uint8_t get_process_id() const; + + /** + * @brief Get the increment, which is incremented for every snowflake + * created over the one millisecond resolution in the timestamp. + * + * @return uint64_t millisecond increment + */ + uint16_t get_increment() const; +}; + +} // namespace dpp + +template<> +struct std::hash +{ + /** + * @brief Hashing function for dpp::slowflake + * Used by std::unordered_map. This just calls std::hash. + * + * @param s Snowflake value to hash + * @return std::size_t hash value + */ + std::size_t operator()(dpp::snowflake const& s) const noexcept { + return std::hash{}(s.value); + } +}; diff --git a/include/dpp/socket.h b/include/dpp/socket.h new file mode 100644 index 0000000..04d1080 --- /dev/null +++ b/include/dpp/socket.h @@ -0,0 +1,30 @@ +#pragma once + +#ifndef _WIN32 +#ifndef SOCKET +#define SOCKET int +#endif +#endif + +namespace dpp +{ + /** + * @brief Represents a socket file descriptor. + * This is used to ensure parity between windows and unix-like systems. + */ + typedef SOCKET socket; +} // namespace dpp + +#ifndef SOCKET_ERROR +/** + * @brief Represents a socket in error state + */ +#define SOCKET_ERROR -1 +#endif + +#ifndef INVALID_SOCKET +/** + * @brief Represents a socket which is not yet assigned + */ +#define INVALID_SOCKET ~0 +#endif diff --git a/include/dpp/sslclient.h b/include/dpp/sslclient.h new file mode 100644 index 0000000..b1e674e --- /dev/null +++ b/include/dpp/sslclient.h @@ -0,0 +1,259 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace dpp { + +/** + * @brief This is an opaque class containing openssl library specific structures. + * We define it this way so that the public facing D++ library doesn't require + * the openssl headers be available to build against it. + */ +class openssl_connection; + +/** + * @brief A callback for socket status + */ +typedef std::function socket_callback_t; + +/** + * @brief A socket notification callback + */ +typedef std::function socket_notification_t; + +/** + * @brief Close a socket + * + * @param sfd Socket to close + * @return false on error, true on success + */ +bool close_socket(dpp::socket sfd); + +/** + * @brief Set a socket to blocking or non-blocking IO + * + * @param sockfd socket to act upon + * @return false on error, true on success + */ +bool set_nonblocking(dpp::socket sockfd, bool non_blocking); + +/** + * @brief Implements a simple non-blocking SSL stream client. + * + * @note although the design is non-blocking the run() method will + * execute in an infinite loop until the socket disconnects. This is intended + * to be run within a std::thread. + */ +class DPP_EXPORT ssl_client +{ +private: + /** + * @brief Clean up resources + */ + void cleanup(); +protected: + /** + * @brief Input buffer received from socket + */ + std::string buffer; + + /** + * @brief Output buffer for sending to socket + */ + std::string obuffer; + + /** + * @brief True if in nonblocking mode. The socket switches to nonblocking mode + * once ReadLoop is called. + */ + bool nonblocking; + + /** + * @brief Raw file descriptor of connection + */ + dpp::socket sfd; + + /** + * @brief Openssl opaque contexts + */ + openssl_connection* ssl; + + /** + * @brief SSL cipher in use + */ + std::string cipher; + + /** + * @brief For timers + */ + time_t last_tick; + + /** + * @brief Hostname connected to + */ + std::string hostname; + + /** + * @brief Port connected to + */ + std::string port; + + /** + * @brief Bytes out + */ + uint64_t bytes_out; + + /** + * @brief Bytes in + */ + uint64_t bytes_in; + + /** + * @brief True for a plain text connection + */ + bool plaintext; + + /** + * @brief True if we are establishing a new connection, false if otherwise. + */ + bool make_new; + + + /** + * @brief Called every second + */ + virtual void one_second_timer(); + + /** + * @brief Start SSL connection and connect to TCP endpoint + * @throw dpp::exception Failed to initialise connection + */ + virtual void connect(); +public: + /** + * @brief Get the bytes out objectGet total bytes sent + * @return uint64_t bytes sent + */ + uint64_t get_bytes_out(); + + /** + * @brief Get total bytes received + * @return uint64_t bytes received + */ + uint64_t get_bytes_in(); + + /** + * @brief Get SSL cipher name + * @return std::string ssl cipher name + */ + std::string get_cipher(); + + /** + * @brief Attaching an additional file descriptor to this function will send notifications when there is data to read. + * + * NOTE: Only hook this if you NEED it as it can increase CPU usage of the thread! + * Returning -1 means that you don't want to be notified. + */ + socket_callback_t custom_readable_fd; + + /** + * @brief Attaching an additional file descriptor to this function will send notifications when you are able to write + * to the socket. + * + * NOTE: Only hook this if you NEED it as it can increase CPU usage of the thread! You should toggle this + * to -1 when you do not have anything to write otherwise it'll keep triggering repeatedly (it is level triggered). + */ + socket_callback_t custom_writeable_fd; + + /** + * @brief This event will be called when you can read from the custom fd + */ + socket_notification_t custom_readable_ready; + + /** + * @brief This event will be called when you can write to a custom fd + */ + socket_notification_t custom_writeable_ready; + + /** + * @brief True if we are keeping the connection alive after it has finished + */ + bool keepalive; + + /** + * @brief Connect to a specified host and port. Throws std::runtime_error on fatal error. + * @param _hostname The hostname to connect to + * @param _port the Port number to connect to + * @param plaintext_downgrade Set to true to connect using plaintext only, without initialising SSL. + * @param reuse Attempt to reuse previous connections for this hostname and port, if available + * Note that no Discord endpoints will function when downgraded. This option is provided only for + * connection to non-Discord addresses such as within dpp::cluster::request(). + * @throw dpp::exception Failed to initialise connection + */ + ssl_client(const std::string &_hostname, const std::string &_port = "443", bool plaintext_downgrade = false, bool reuse = false); + + /** + * @brief Nonblocking I/O loop + * @throw std::exception Any std::exception (or derivative) thrown from read_loop() causes reconnection of the shard + */ + void read_loop(); + + /** + * @brief Destroy the ssl_client object + */ + virtual ~ssl_client(); + + /** + * @brief Handle input from the input buffer. This function will be called until + * all data in the buffer has been processed and the buffer is empty. + * @param buffer the buffer content. Will be modified removing any processed front elements + * @return bool True if the socket should remain connected + */ + virtual bool handle_buffer(std::string &buffer); + + /** + * @brief Write to the output buffer. + * @param data Data to be written to the buffer + * @note The data may not be written immediately and may be written at a later time to the socket. + */ + virtual void write(const std::string &data); + + /** + * @brief Close socket connection + */ + virtual void close(); + + /** + * @brief Log a message + * @param severity severity of log message + * @param msg Log message to send + */ + virtual void log(dpp::loglevel severity, const std::string &msg) const; +}; + +} // namespace dpp diff --git a/include/dpp/stage_instance.h b/include/dpp/stage_instance.h new file mode 100644 index 0000000..c75b103 --- /dev/null +++ b/include/dpp/stage_instance.h @@ -0,0 +1,87 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace dpp { + +/** + * @brief Represents the privacy of a stage instance + */ +enum stage_privacy_level : uint8_t { + /// The Stage instance is visible publicly, such as on Stage Discovery. + sp_public = 1, + /// The Stage instance is visible to only guild members. + sp_guild_only = 2 +}; + +/** + * @brief A stage instance. + * Stage instances are like a conference facility, with moderators/speakers and listeners. + */ +struct DPP_EXPORT stage_instance : public managed, public json_interface { + /// The guild id of the associated Stage channel + snowflake guild_id; + /// The id of the associated Stage channel + snowflake channel_id; + /// The topic of the Stage instance (1-120 characters) + std::string topic; + /// The privacy level of the Stage instance + stage_privacy_level privacy_level; + /// Whether or not Stage Discovery is disabled + bool discoverable_disabled; + + /** + * @brief Create a stage_instance object + */ + stage_instance(); + + /** + * @brief Destroy the stage_instance object + */ + ~stage_instance() = default; + + /** + * @brief Serialise a stage_instance object rom json + * + * @return stage_instance& a reference to self + */ + stage_instance& fill_from_json(const nlohmann::json* j); + + /** + * @brief Build json for this object + * + * @param with_id include ID + * @return std::string Dumped json of this object + */ + virtual std::string build_json(bool with_id = false) const; +}; + +/** A group of stage instances */ +typedef std::unordered_map stage_instance_map; + +} // namespace dpp diff --git a/include/dpp/stringops.h b/include/dpp/stringops.h new file mode 100644 index 0000000..77de2e1 --- /dev/null +++ b/include/dpp/stringops.h @@ -0,0 +1,214 @@ +/************************************************************************************ + * + * D++ - A Lightweight C++ Library for Discord + * + * stringops.h taken from TriviaBot + * + * Copyright 2004 Craig Edwards + * + * Core based on Sporks, the Learning Discord Bot, Craig Edwards (c) 2019. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ + +#pragma once +#include +#include +#include +#include +#include +#include + +namespace dpp { +/** + * @brief Convert a string to lowercase using tolower() + * + * @tparam T type of string + * @param s String to lowercase + * @return std::basic_string lowercased string + */ +template std::basic_string lowercase(const std::basic_string& s) +{ + std::basic_string s2 = s; + std::transform(s2.begin(), s2.end(), s2.begin(), tolower); + return s2; +} + +/** + * @brief Convert a string to uppercase using toupper() + * + * @tparam T type of string + * @param s String to uppercase + * @return std::basic_string uppercased string + */ +template std::basic_string uppercase(const std::basic_string& s) +{ + std::basic_string s2 = s; + std::transform(s2.begin(), s2.end(), s2.begin(), toupper); + return s2; +} + +/** + * @brief trim from end of string (right) + * + * @param s String to trim + * @return std::string trimmed string + */ +inline std::string rtrim(std::string s) +{ + s.erase(s.find_last_not_of(" \t\n\r\f\v") + 1); + return s; +} + +/** + * @brief trim from beginning of string (left) + * + * @param s string to trim + * @return std::string trimmed string + */ +inline std::string ltrim(std::string s) +{ + s.erase(0, s.find_first_not_of(" \t\n\r\f\v")); + return s; +} + +/** + * @brief Trim from both ends of string (right then left) + * + * @param s string to trim + * @return std::string trimmed string + */ +inline std::string trim(std::string s) +{ + return ltrim(rtrim(s)); +} + +/** + * @brief Add commas to a string (or dots) based on current locale server-side + * + * @tparam T type of numeric value + * @param value Value + * @return std::string number with commas added + */ +template std::string comma(T value) +{ + std::stringstream ss; + ss.imbue(std::locale("")); + ss << std::fixed << value; + return ss.str(); +} + +/** + * @brief Convert any value from a string to another type using stringstream. + * The optional second parameter indicates the format of the input string, + * e.g. std::dec for decimal, std::hex for hex, std::oct for octal. + * + * @tparam T Type to convert to + * @param s String to convert from + * @param f Numeric base, e.g. `std::dec` or `std::hex` + * @return T Returned numeric value + */ +template T from_string(const std::string &s, std::ios_base & (*f)(std::ios_base&)) +{ + T t; + std::istringstream iss(s); + iss >> f, iss >> t; + return t; +} + +/** + * @brief Convert any value from a string to another type using stringstream. + * + * @tparam T Type to convert to + * @param s String to convert from + * @return T Returned numeric value + * + * @note Base 10 for numeric conversions. + */ +template T from_string(const std::string &s) +{ + T t; + std::istringstream iss(s); + iss >> t; + return t; +} + +/** + * @brief Specialised conversion of uint64_t from string + * + * @tparam int64_t + * @param s string to convert + * @return uint64_t return value + */ +template uint64_t from_string(const std::string &s) +{ + return std::stoull(s, 0, 10); +} + +/** + * @brief Specialised conversion of uint32_t from string + * + * @tparam uint32_t + * @param s string to convert + * @return uint32_t return value + */ +template uint32_t from_string(const std::string &s) +{ + return (uint32_t) std::stoul(s, 0, 10); +} + +/** + * @brief Specialised conversion of int from string + * + * @tparam int + * @param s string to convert + * @return int return value + */ +template int from_string(const std::string &s) +{ + return std::stoi(s, 0, 10); +} + +/** + * @brief Convert a numeric value to hex + * + * @tparam T numeric type + * @param i numeric value + * @return std::string value in hex, the length will be 2* the raw size of the type + */ +template std::string to_hex(T i) +{ + std::stringstream stream; + stream.imbue(std::locale::classic()); + stream << std::setfill('0') << std::setw(sizeof(T)*2) << std::hex << i; + return stream.str(); +} + +/** + * @brief Format a numeric type as a string with leading zeroes + * + * @tparam T numeric type + * @param i numeric value + * @param width width of type including the leading zeroes + * @return std::string resultant string with leading zeroes + */ +template std::string leading_zeroes(T i, size_t width) +{ + std::stringstream stream; + stream.imbue(std::locale::classic()); + stream << std::setfill('0') << std::setw((int)width) << std::dec << i; + return stream.str(); +} + +} // namespace dpp diff --git a/include/dpp/sync.h b/include/dpp/sync.h new file mode 100644 index 0000000..035a205 --- /dev/null +++ b/include/dpp/sync.h @@ -0,0 +1,80 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2022 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include +#include +#include + +namespace dpp { + + /** + * @brief Call a D++ REST function synchronously. + * + * Synchronously calling a REST function means *IT WILL BLOCK* - This is a Bad Thingâ„ĸ and strongly discouraged. + * There are very few circumstances you actually need this. If you do need to use this, you'll know it. + * + * Example: + * + * ```cpp + * dpp::message m = dpp::sync(&bot, &dpp::cluster::message_create, dpp::message(channel_id, "moo.")); + * ``` + * + * @warning As previously mentioned, this template will block. It is ill-advised to call this outside of + * a separate thread and this should never be directly used in any event such as dpp::cluster::on_interaction_create! + * @tparam T type of expected return value, should match up with the method called + * @tparam F Type of class method in dpp::cluster to call. + * @tparam Ts Function parameters in method call + * @param c A pointer to dpp::cluster object + * @param func pointer to class method in dpp::cluster to call. This can call any + * dpp::cluster member function who's last parameter is a dpp::command_completion_event_t callback type. + * @param args Zero or more arguments for the method call + * @return An instantiated object of type T + * @throw dpp::rest_exception On failure of the method call, an exception is thrown + */ + template T sync(class cluster* c, F func, Ts&&... args) { + std::promise _p; + std::future _f = _p.get_future(); + /* (obj ->* func) is the obscure syntax for calling a method pointer on an object instance */ + (c ->* func)(std::forward(args)..., [&_p](const auto& cc) { + try { + if (cc.is_error()) { + throw dpp::rest_exception(cc.get_error().message); + } else { + try { + _p.set_value(std::get(cc.value)); + } catch (const std::exception& e) { + throw dpp::rest_exception(e.what()); + } + } + } catch (const dpp::rest_exception&) { + _p.set_exception(std::current_exception()); + } + }); + + /* Blocking calling thread until rest request is completed. + * Exceptions encountered on the other thread are re-thrown. + */ + return _f.get(); + } + +} // namespace dpp diff --git a/include/dpp/sysdep.h b/include/dpp/sysdep.h new file mode 100644 index 0000000..8971c30 --- /dev/null +++ b/include/dpp/sysdep.h @@ -0,0 +1,120 @@ +/* + * Discord erlpack - tidied up for D++, Craig Edwards 2021. + * + * MessagePack system dependencies modified for erlpack. + * + * Copyright (C) 2008-2010 FURUHASHI Sadayuki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include +#if defined(__linux__) +#include +#endif + +#ifdef _WIN32 + +#ifdef __cplusplus +/* numeric_limits::min,max */ +#ifdef max +#undef max +#endif +#ifdef min +#undef min +#endif +#endif + +#else +#include /* __BYTE_ORDER */ +#endif + +#if !defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__) +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define __LITTLE_ENDIAN__ +#elif __BYTE_ORDER == __BIG_ENDIAN +#define __BIG_ENDIAN__ +#elif _WIN32 +#define __LITTLE_ENDIAN__ +#endif +#endif + + +#ifdef __LITTLE_ENDIAN__ + +#ifdef _WIN32 +# if defined(ntohs) +# define etf_byte_order_16(x) ntohs(x) +# elif defined(_byteswap_ushort) || (defined(_MSC_VER) && _MSC_VER >= 1400) +# define etf_byte_order_16(x) ((uint16_t)_byteswap_ushort((unsigned short)x)) +# else +# define etf_byte_order_16(x) ( \ + ((((uint16_t)x) << 8) ) | \ + ((((uint16_t)x) >> 8) ) ) +# endif +#else +# define etf_byte_order_16(x) ntohs(x) +#endif + +#ifdef _WIN32 +# if defined(ntohl) +# define etf_byte_order_32(x) ntohl(x) +# elif defined(_byteswap_ulong) || (defined(_MSC_VER) && _MSC_VER >= 1400) +# define etf_byte_order_32(x) ((uint32_t)_byteswap_ulong((unsigned long)x)) +# else +# define etf_byte_order_32(x) \ + ( ((((uint32_t)x) << 24) ) | \ + ((((uint32_t)x) << 8) & 0x00ff0000U ) | \ + ((((uint32_t)x) >> 8) & 0x0000ff00U ) | \ + ((((uint32_t)x) >> 24) ) ) +# endif +#else +# define etf_byte_order_32(x) ntohl(x) +#endif + +#if defined(_byteswap_uint64) || (defined(_MSC_VER) && _MSC_VER >= 1400) +# define etf_byte_order_64(x) (_byteswap_uint64(x)) +#elif defined(bswap_64) +# define etf_byte_order_64(x) bswap_64(x) +#elif defined(__DARWIN_OSSwapInt64) +# define etf_byte_order_64(x) __DARWIN_OSSwapInt64(x) +#elif defined(__linux__) +# define etf_byte_order_64(x) be64toh(x) +#else +# define etf_byte_order_64(x) \ + ( ((((uint64_t)x) << 56) ) | \ + ((((uint64_t)x) << 40) & 0x00ff000000000000ULL ) | \ + ((((uint64_t)x) << 24) & 0x0000ff0000000000ULL ) | \ + ((((uint64_t)x) << 8) & 0x000000ff00000000ULL ) | \ + ((((uint64_t)x) >> 8) & 0x00000000ff000000ULL ) | \ + ((((uint64_t)x) >> 24) & 0x0000000000ff0000ULL ) | \ + ((((uint64_t)x) >> 40) & 0x000000000000ff00ULL ) | \ + ((((uint64_t)x) >> 56) ) ) +#endif + +#else +#define etf_byte_order_16(x) (x) +#define etf_byte_order_32(x) (x) +#define etf_byte_order_64(x) (x) +#endif + +#define store_16_bits(to, num) \ + do { uint16_t val = etf_byte_order_16(num); memcpy(to, &val, 2); } while(0) +#define store_32_bits(to, num) \ + do { uint32_t val = etf_byte_order_32(num); memcpy(to, &val, 4); } while(0) +#define store_64_bits(to, num) \ + do { uint64_t val = etf_byte_order_64(num); memcpy(to, &val, 8); } while(0) diff --git a/include/dpp/timed_listener.h b/include/dpp/timed_listener.h new file mode 100644 index 0000000..0ec9a4d --- /dev/null +++ b/include/dpp/timed_listener.h @@ -0,0 +1,95 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace dpp { + +/** + * @brief A timed_listener is a way to temporarily attach to an event for a specific timeframe, then detach when complete. + * A lambda may also be optionally called when the timeout is reached. Destructing the timed_listener detaches any attached + * event listeners, and cancels any created timers, but does not call any timeout lambda. + * + * @tparam attached_event Event within cluster to attach to within the cluster::dispatch member (dpp::dispatcher object) + * @tparam listening_function Definition of lambda function that matches up with the attached_event. + */ +template class timed_listener +{ +private: + /// Owning cluster + cluster* owner; + + /// Duration of listen + time_t duration; + + /// Reference to attached event in cluster + //event_router_t on_thread_member_update; + attached_event& ev; + + /// Timer handle + timer th; + + /// Event handle + event_handle listener_handle; + +public: + /** + * @brief Construct a new timed listener object + * + * @param cl Owning cluster + * @param _duration Duration of timed event in seconds + * @param event Event to hook, e.g. cluster.on_message_create + * @param on_end An optional void() lambda to trigger when the timed_listener times out. + * Calling the destructor before the timeout is reached does not call this lambda. + * @param listener Lambda to receive events. Type must match up properly with that passed into the 'event' parameter. + */ + timed_listener(cluster* cl, uint64_t _duration, attached_event& event, listening_function listener, timer_callback_t on_end = {}) + : owner(cl), duration(_duration), ev(event) + { + /* Attach event */ + listener_handle = ev(listener); + /* Create timer */ + th = cl->start_timer([this](dpp::timer timer_handle) { + /* Timer has finished, detach it from event. + * Only allowed to tick once. + */ + ev.detach(listener_handle); + owner->stop_timer(th); + }, duration, on_end); + } + + /** + * @brief Destroy the timed listener object + */ + ~timed_listener() { + /* Stop timer and detach event, but do not call on_end */ + ev.detach(listener_handle); + owner->stop_timer(th); + } +}; + +} // namespace dpp diff --git a/include/dpp/timer.h b/include/dpp/timer.h new file mode 100644 index 0000000..bfcbf99 --- /dev/null +++ b/include/dpp/timer.h @@ -0,0 +1,125 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ + +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace dpp { + +/** + * @brief Represents a timer handle. + * Returned from cluster::start_timer and used by cluster::stop_timer. + * This is obtained from a simple incrementing value, internally. + */ +typedef size_t timer; + +/** + * @brief The type for a timer callback + */ +typedef std::function timer_callback_t; + +/** + * @brief Used internally to store state of active timers + */ +struct DPP_EXPORT timer_t { + /** + * @brief Timer handle + */ + timer handle; + /** + * @brief Next timer tick as unix epoch + */ + time_t next_tick; + /** + * @brief Frequency between ticks + */ + uint64_t frequency; + /** + * @brief Lambda to call on tick + */ + timer_callback_t on_tick; + /** + * @brief Lambda to call on stop (optional) + */ + timer_callback_t on_stop; +}; + +/** + * @brief A map of timers, ordered by earliest first so that map::begin() is always the + * soonest to be due. + */ +typedef std::multimap timer_next_t; + +/** + * @brief A map of timers stored by handle + */ +typedef std::unordered_map timer_reg_t; + +/** + * @brief Trigger a timed event once. + * The provided callback is called only once. + */ +class DPP_EXPORT oneshot_timer +{ +private: + /// Owning cluster + class cluster* owner; + /// Timer handle + timer th; +public: + /** + * @brief Construct a new oneshot timer object + * + * @param cl cluster owner + * @param duration duration before firing + * @param callback callback to call on firing + */ + oneshot_timer(class cluster* cl, uint64_t duration, timer_callback_t callback); + + /** + * @brief Get the handle for the created one-shot timer + * + * @return timer handle for use with stop_timer + */ + timer get_handle(); + + /** + * @brief Cancel the one shot timer immediately. + * Callback function is not called. + */ + void cancel(); + + /** + * @brief Destroy the oneshot timer object + */ + ~oneshot_timer(); +}; + + + +} // namespace dpp diff --git a/include/dpp/unicode_emoji.h b/include/dpp/unicode_emoji.h new file mode 100644 index 0000000..1fe77f1 --- /dev/null +++ b/include/dpp/unicode_emoji.h @@ -0,0 +1,5483 @@ +#pragma once + +namespace dpp { + +/** + * The unicode emojis in this namespace are auto-generated from https://raw.githubusercontent.com/ArkinSolomon/discord-emoji-converter/master/emojis.json + * + * If you want to use this, you have to pull the header in separately. e.g. + * ```cpp + * #include + * #include + * ``` + */ +namespace unicode_emoji { + inline constexpr const char _100[] = "đŸ’¯"; + inline constexpr const char _1234[] = "đŸ”ĸ"; + inline constexpr const char soccer[] = "âšŊ"; + inline constexpr const char basketball[] = "🏀"; + inline constexpr const char football[] = "🏈"; + inline constexpr const char baseball[] = "⚾"; + inline constexpr const char softball[] = "đŸĨŽ"; + inline constexpr const char tennis[] = "🎾"; + inline constexpr const char volleyball[] = "🏐"; + inline constexpr const char rugby_football[] = "🏉"; + inline constexpr const char flying_disc[] = "đŸĨ"; + inline constexpr const char _8ball[] = "🎱"; + inline constexpr const char yo_yo[] = "đŸĒ€"; + inline constexpr const char ping_pong[] = "🏓"; + inline constexpr const char table_tennis[] = "🏓"; + inline constexpr const char badminton[] = "🏸"; + inline constexpr const char hockey[] = "🏒"; + inline constexpr const char field_hockey[] = "🏑"; + inline constexpr const char lacrosse[] = "đŸĨ"; + inline constexpr const char cricket_game[] = "🏏"; + inline constexpr const char cricket_bat_ball[] = "🏏"; + inline constexpr const char boomerang[] = "đŸĒƒ"; + inline constexpr const char goal[] = "đŸĨ…"; + inline constexpr const char goal_net[] = "đŸĨ…"; + inline constexpr const char golf[] = "â›ŗ"; + inline constexpr const char kite[] = "đŸĒ"; + inline constexpr const char playground_slide[] = "🛝"; + inline constexpr const char bow_and_arrow[] = "🏹"; + inline constexpr const char archery[] = "🏹"; + inline constexpr const char fishing_pole_and_fish[] = "đŸŽŖ"; + inline constexpr const char diving_mask[] = "đŸ¤ŋ"; + inline constexpr const char boxing_glove[] = "đŸĨŠ"; + inline constexpr const char boxing_gloves[] = "đŸĨŠ"; + inline constexpr const char martial_arts_uniform[] = "đŸĨ‹"; + inline constexpr const char karate_uniform[] = "đŸĨ‹"; + inline constexpr const char running_shirt_with_sash[] = "đŸŽŊ"; + inline constexpr const char skateboard[] = "🛹"; + inline constexpr const char roller_skate[] = "đŸ›ŧ"; + inline constexpr const char sled[] = "🛷"; + inline constexpr const char ice_skate[] = "⛸ī¸"; + inline constexpr const char curling_stone[] = "đŸĨŒ"; + inline constexpr const char ski[] = "đŸŽŋ"; + inline constexpr const char skier[] = "⛷ī¸"; + inline constexpr const char snowboarder[] = "🏂"; + inline constexpr const char snowboarder_tone1[] = "🏂đŸģ"; + inline constexpr const char snowboarder_light_skin_tone[] = "🏂đŸģ"; + inline constexpr const char snowboarder_tone2[] = "🏂đŸŧ"; + inline constexpr const char snowboarder_medium_light_skin_tone[] = "🏂đŸŧ"; + inline constexpr const char snowboarder_tone3[] = "🏂đŸŊ"; + inline constexpr const char snowboarder_medium_skin_tone[] = "🏂đŸŊ"; + inline constexpr const char snowboarder_tone4[] = "🏂🏾"; + inline constexpr const char snowboarder_medium_dark_skin_tone[] = "🏂🏾"; + inline constexpr const char snowboarder_tone5[] = "🏂đŸŋ"; + inline constexpr const char snowboarder_dark_skin_tone[] = "🏂đŸŋ"; + inline constexpr const char parachute[] = "đŸĒ‚"; + inline constexpr const char person_lifting_weights[] = "🏋ī¸"; + inline constexpr const char lifter[] = "🏋ī¸"; + inline constexpr const char weight_lifter[] = "🏋ī¸"; + inline constexpr const char person_lifting_weights_tone1[] = "🏋đŸģ"; + inline constexpr const char lifter_tone1[] = "🏋đŸģ"; + inline constexpr const char weight_lifter_tone1[] = "🏋đŸģ"; + inline constexpr const char person_lifting_weights_tone2[] = "🏋đŸŧ"; + inline constexpr const char lifter_tone2[] = "🏋đŸŧ"; + inline constexpr const char weight_lifter_tone2[] = "🏋đŸŧ"; + inline constexpr const char person_lifting_weights_tone3[] = "🏋đŸŊ"; + inline constexpr const char lifter_tone3[] = "🏋đŸŊ"; + inline constexpr const char weight_lifter_tone3[] = "🏋đŸŊ"; + inline constexpr const char person_lifting_weights_tone4[] = "🏋🏾"; + inline constexpr const char lifter_tone4[] = "🏋🏾"; + inline constexpr const char weight_lifter_tone4[] = "🏋🏾"; + inline constexpr const char person_lifting_weights_tone5[] = "🏋đŸŋ"; + inline constexpr const char lifter_tone5[] = "🏋đŸŋ"; + inline constexpr const char weight_lifter_tone5[] = "🏋đŸŋ"; + inline constexpr const char woman_lifting_weights[] = "🏋ī¸â€â™€ī¸"; + inline constexpr const char woman_lifting_weights_tone1[] = "🏋đŸģ‍♀ī¸"; + inline constexpr const char woman_lifting_weights_light_skin_tone[] = "🏋đŸģ‍♀ī¸"; + inline constexpr const char woman_lifting_weights_tone2[] = "🏋đŸŧ‍♀ī¸"; + inline constexpr const char woman_lifting_weights_medium_light_skin_tone[] = "🏋đŸŧ‍♀ī¸"; + inline constexpr const char woman_lifting_weights_tone3[] = "🏋đŸŊ‍♀ī¸"; + inline constexpr const char woman_lifting_weights_medium_skin_tone[] = "🏋đŸŊ‍♀ī¸"; + inline constexpr const char woman_lifting_weights_tone4[] = "🏋🏾‍♀ī¸"; + inline constexpr const char woman_lifting_weights_medium_dark_skin_tone[] = "🏋🏾‍♀ī¸"; + inline constexpr const char woman_lifting_weights_tone5[] = "🏋đŸŋ‍♀ī¸"; + inline constexpr const char woman_lifting_weights_dark_skin_tone[] = "🏋đŸŋ‍♀ī¸"; + inline constexpr const char man_lifting_weights[] = "🏋ī¸â€â™‚ī¸"; + inline constexpr const char man_lifting_weights_tone1[] = "🏋đŸģ‍♂ī¸"; + inline constexpr const char man_lifting_weights_light_skin_tone[] = "🏋đŸģ‍♂ī¸"; + inline constexpr const char man_lifting_weights_tone2[] = "🏋đŸŧ‍♂ī¸"; + inline constexpr const char man_lifting_weights_medium_light_skin_tone[] = "🏋đŸŧ‍♂ī¸"; + inline constexpr const char man_lifting_weights_tone3[] = "🏋đŸŊ‍♂ī¸"; + inline constexpr const char man_lifting_weights_medium_skin_tone[] = "🏋đŸŊ‍♂ī¸"; + inline constexpr const char man_lifting_weights_tone4[] = "🏋🏾‍♂ī¸"; + inline constexpr const char man_lifting_weights_medium_dark_skin_tone[] = "🏋🏾‍♂ī¸"; + inline constexpr const char man_lifting_weights_tone5[] = "🏋đŸŋ‍♂ī¸"; + inline constexpr const char man_lifting_weights_dark_skin_tone[] = "🏋đŸŋ‍♂ī¸"; + inline constexpr const char people_wrestling[] = "đŸ¤ŧ"; + inline constexpr const char wrestlers[] = "đŸ¤ŧ"; + inline constexpr const char wrestling[] = "đŸ¤ŧ"; + inline constexpr const char women_wrestling[] = "đŸ¤ŧ‍♀ī¸"; + inline constexpr const char men_wrestling[] = "đŸ¤ŧ‍♂ī¸"; + inline constexpr const char person_doing_cartwheel[] = "🤸"; + inline constexpr const char cartwheel[] = "🤸"; + inline constexpr const char person_doing_cartwheel_tone1[] = "🤸đŸģ"; + inline constexpr const char cartwheel_tone1[] = "🤸đŸģ"; + inline constexpr const char person_doing_cartwheel_tone2[] = "🤸đŸŧ"; + inline constexpr const char cartwheel_tone2[] = "🤸đŸŧ"; + inline constexpr const char person_doing_cartwheel_tone3[] = "🤸đŸŊ"; + inline constexpr const char cartwheel_tone3[] = "🤸đŸŊ"; + inline constexpr const char person_doing_cartwheel_tone4[] = "🤸🏾"; + inline constexpr const char cartwheel_tone4[] = "🤸🏾"; + inline constexpr const char person_doing_cartwheel_tone5[] = "🤸đŸŋ"; + inline constexpr const char cartwheel_tone5[] = "🤸đŸŋ"; + inline constexpr const char woman_cartwheeling[] = "🤸‍♀ī¸"; + inline constexpr const char woman_cartwheeling_tone1[] = "🤸đŸģ‍♀ī¸"; + inline constexpr const char woman_cartwheeling_light_skin_tone[] = "🤸đŸģ‍♀ī¸"; + inline constexpr const char woman_cartwheeling_tone2[] = "🤸đŸŧ‍♀ī¸"; + inline constexpr const char woman_cartwheeling_medium_light_skin_tone[] = "🤸đŸŧ‍♀ī¸"; + inline constexpr const char woman_cartwheeling_tone3[] = "🤸đŸŊ‍♀ī¸"; + inline constexpr const char woman_cartwheeling_medium_skin_tone[] = "🤸đŸŊ‍♀ī¸"; + inline constexpr const char woman_cartwheeling_tone4[] = "🤸🏾‍♀ī¸"; + inline constexpr const char woman_cartwheeling_medium_dark_skin_tone[] = "🤸🏾‍♀ī¸"; + inline constexpr const char woman_cartwheeling_tone5[] = "🤸đŸŋ‍♀ī¸"; + inline constexpr const char woman_cartwheeling_dark_skin_tone[] = "🤸đŸŋ‍♀ī¸"; + inline constexpr const char man_cartwheeling[] = "🤸‍♂ī¸"; + inline constexpr const char man_cartwheeling_tone1[] = "🤸đŸģ‍♂ī¸"; + inline constexpr const char man_cartwheeling_light_skin_tone[] = "🤸đŸģ‍♂ī¸"; + inline constexpr const char man_cartwheeling_tone2[] = "🤸đŸŧ‍♂ī¸"; + inline constexpr const char man_cartwheeling_medium_light_skin_tone[] = "🤸đŸŧ‍♂ī¸"; + inline constexpr const char man_cartwheeling_tone3[] = "🤸đŸŊ‍♂ī¸"; + inline constexpr const char man_cartwheeling_medium_skin_tone[] = "🤸đŸŊ‍♂ī¸"; + inline constexpr const char man_cartwheeling_tone4[] = "🤸🏾‍♂ī¸"; + inline constexpr const char man_cartwheeling_medium_dark_skin_tone[] = "🤸🏾‍♂ī¸"; + inline constexpr const char man_cartwheeling_tone5[] = "🤸đŸŋ‍♂ī¸"; + inline constexpr const char man_cartwheeling_dark_skin_tone[] = "🤸đŸŋ‍♂ī¸"; + inline constexpr const char person_bouncing_ball[] = "⛹ī¸"; + inline constexpr const char basketball_player[] = "⛹ī¸"; + inline constexpr const char person_with_ball[] = "⛹ī¸"; + inline constexpr const char person_bouncing_ball_tone1[] = "⛹đŸģ"; + inline constexpr const char basketball_player_tone1[] = "⛹đŸģ"; + inline constexpr const char person_with_ball_tone1[] = "⛹đŸģ"; + inline constexpr const char person_bouncing_ball_tone2[] = "⛹đŸŧ"; + inline constexpr const char basketball_player_tone2[] = "⛹đŸŧ"; + inline constexpr const char person_with_ball_tone2[] = "⛹đŸŧ"; + inline constexpr const char person_bouncing_ball_tone3[] = "⛹đŸŊ"; + inline constexpr const char basketball_player_tone3[] = "⛹đŸŊ"; + inline constexpr const char person_with_ball_tone3[] = "⛹đŸŊ"; + inline constexpr const char person_bouncing_ball_tone4[] = "⛹🏾"; + inline constexpr const char basketball_player_tone4[] = "⛹🏾"; + inline constexpr const char person_with_ball_tone4[] = "⛹🏾"; + inline constexpr const char person_bouncing_ball_tone5[] = "⛹đŸŋ"; + inline constexpr const char basketball_player_tone5[] = "⛹đŸŋ"; + inline constexpr const char person_with_ball_tone5[] = "⛹đŸŋ"; + inline constexpr const char woman_bouncing_ball[] = "⛹ī¸â€â™€ī¸"; + inline constexpr const char woman_bouncing_ball_tone1[] = "⛹đŸģ‍♀ī¸"; + inline constexpr const char woman_bouncing_ball_light_skin_tone[] = "⛹đŸģ‍♀ī¸"; + inline constexpr const char woman_bouncing_ball_tone2[] = "⛹đŸŧ‍♀ī¸"; + inline constexpr const char woman_bouncing_ball_medium_light_skin_tone[] = "⛹đŸŧ‍♀ī¸"; + inline constexpr const char woman_bouncing_ball_tone3[] = "⛹đŸŊ‍♀ī¸"; + inline constexpr const char woman_bouncing_ball_medium_skin_tone[] = "⛹đŸŊ‍♀ī¸"; + inline constexpr const char woman_bouncing_ball_tone4[] = "⛹🏾‍♀ī¸"; + inline constexpr const char woman_bouncing_ball_medium_dark_skin_tone[] = "⛹🏾‍♀ī¸"; + inline constexpr const char woman_bouncing_ball_tone5[] = "⛹đŸŋ‍♀ī¸"; + inline constexpr const char woman_bouncing_ball_dark_skin_tone[] = "⛹đŸŋ‍♀ī¸"; + inline constexpr const char man_bouncing_ball[] = "⛹ī¸â€â™‚ī¸"; + inline constexpr const char man_bouncing_ball_tone1[] = "⛹đŸģ‍♂ī¸"; + inline constexpr const char man_bouncing_ball_light_skin_tone[] = "⛹đŸģ‍♂ī¸"; + inline constexpr const char man_bouncing_ball_tone2[] = "⛹đŸŧ‍♂ī¸"; + inline constexpr const char man_bouncing_ball_medium_light_skin_tone[] = "⛹đŸŧ‍♂ī¸"; + inline constexpr const char man_bouncing_ball_tone3[] = "⛹đŸŊ‍♂ī¸"; + inline constexpr const char man_bouncing_ball_medium_skin_tone[] = "⛹đŸŊ‍♂ī¸"; + inline constexpr const char man_bouncing_ball_tone4[] = "⛹🏾‍♂ī¸"; + inline constexpr const char man_bouncing_ball_medium_dark_skin_tone[] = "⛹🏾‍♂ī¸"; + inline constexpr const char man_bouncing_ball_tone5[] = "⛹đŸŋ‍♂ī¸"; + inline constexpr const char man_bouncing_ball_dark_skin_tone[] = "⛹đŸŋ‍♂ī¸"; + inline constexpr const char person_fencing[] = "đŸ¤ē"; + inline constexpr const char fencer[] = "đŸ¤ē"; + inline constexpr const char fencing[] = "đŸ¤ē"; + inline constexpr const char person_playing_handball[] = "🤾"; + inline constexpr const char handball[] = "🤾"; + inline constexpr const char person_playing_handball_tone1[] = "🤾đŸģ"; + inline constexpr const char handball_tone1[] = "🤾đŸģ"; + inline constexpr const char person_playing_handball_tone2[] = "🤾đŸŧ"; + inline constexpr const char handball_tone2[] = "🤾đŸŧ"; + inline constexpr const char person_playing_handball_tone3[] = "🤾đŸŊ"; + inline constexpr const char handball_tone3[] = "🤾đŸŊ"; + inline constexpr const char person_playing_handball_tone4[] = "🤾🏾"; + inline constexpr const char handball_tone4[] = "🤾🏾"; + inline constexpr const char person_playing_handball_tone5[] = "🤾đŸŋ"; + inline constexpr const char handball_tone5[] = "🤾đŸŋ"; + inline constexpr const char woman_playing_handball[] = "🤾‍♀ī¸"; + inline constexpr const char woman_playing_handball_tone1[] = "🤾đŸģ‍♀ī¸"; + inline constexpr const char woman_playing_handball_light_skin_tone[] = "🤾đŸģ‍♀ī¸"; + inline constexpr const char woman_playing_handball_tone2[] = "🤾đŸŧ‍♀ī¸"; + inline constexpr const char woman_playing_handball_medium_light_skin_tone[] = "🤾đŸŧ‍♀ī¸"; + inline constexpr const char woman_playing_handball_tone3[] = "🤾đŸŊ‍♀ī¸"; + inline constexpr const char woman_playing_handball_medium_skin_tone[] = "🤾đŸŊ‍♀ī¸"; + inline constexpr const char woman_playing_handball_tone4[] = "🤾🏾‍♀ī¸"; + inline constexpr const char woman_playing_handball_medium_dark_skin_tone[] = "🤾🏾‍♀ī¸"; + inline constexpr const char woman_playing_handball_tone5[] = "🤾đŸŋ‍♀ī¸"; + inline constexpr const char woman_playing_handball_dark_skin_tone[] = "🤾đŸŋ‍♀ī¸"; + inline constexpr const char man_playing_handball[] = "🤾‍♂ī¸"; + inline constexpr const char man_playing_handball_tone1[] = "🤾đŸģ‍♂ī¸"; + inline constexpr const char man_playing_handball_light_skin_tone[] = "🤾đŸģ‍♂ī¸"; + inline constexpr const char man_playing_handball_tone2[] = "🤾đŸŧ‍♂ī¸"; + inline constexpr const char man_playing_handball_medium_light_skin_tone[] = "🤾đŸŧ‍♂ī¸"; + inline constexpr const char man_playing_handball_tone3[] = "🤾đŸŊ‍♂ī¸"; + inline constexpr const char man_playing_handball_medium_skin_tone[] = "🤾đŸŊ‍♂ī¸"; + inline constexpr const char man_playing_handball_tone4[] = "🤾🏾‍♂ī¸"; + inline constexpr const char man_playing_handball_medium_dark_skin_tone[] = "🤾🏾‍♂ī¸"; + inline constexpr const char man_playing_handball_tone5[] = "🤾đŸŋ‍♂ī¸"; + inline constexpr const char man_playing_handball_dark_skin_tone[] = "🤾đŸŋ‍♂ī¸"; + inline constexpr const char person_golfing[] = "🏌ī¸"; + inline constexpr const char golfer[] = "🏌ī¸"; + inline constexpr const char person_golfing_tone1[] = "🏌đŸģ"; + inline constexpr const char person_golfing_light_skin_tone[] = "🏌đŸģ"; + inline constexpr const char person_golfing_tone2[] = "🏌đŸŧ"; + inline constexpr const char person_golfing_medium_light_skin_tone[] = "🏌đŸŧ"; + inline constexpr const char person_golfing_tone3[] = "🏌đŸŊ"; + inline constexpr const char person_golfing_medium_skin_tone[] = "🏌đŸŊ"; + inline constexpr const char person_golfing_tone4[] = "🏌🏾"; + inline constexpr const char person_golfing_medium_dark_skin_tone[] = "🏌🏾"; + inline constexpr const char person_golfing_tone5[] = "🏌đŸŋ"; + inline constexpr const char person_golfing_dark_skin_tone[] = "🏌đŸŋ"; + inline constexpr const char woman_golfing[] = "🏌ī¸â€â™€ī¸"; + inline constexpr const char woman_golfing_tone1[] = "🏌đŸģ‍♀ī¸"; + inline constexpr const char woman_golfing_light_skin_tone[] = "🏌đŸģ‍♀ī¸"; + inline constexpr const char woman_golfing_tone2[] = "🏌đŸŧ‍♀ī¸"; + inline constexpr const char woman_golfing_medium_light_skin_tone[] = "🏌đŸŧ‍♀ī¸"; + inline constexpr const char woman_golfing_tone3[] = "🏌đŸŊ‍♀ī¸"; + inline constexpr const char woman_golfing_medium_skin_tone[] = "🏌đŸŊ‍♀ī¸"; + inline constexpr const char woman_golfing_tone4[] = "🏌🏾‍♀ī¸"; + inline constexpr const char woman_golfing_medium_dark_skin_tone[] = "🏌🏾‍♀ī¸"; + inline constexpr const char woman_golfing_tone5[] = "🏌đŸŋ‍♀ī¸"; + inline constexpr const char woman_golfing_dark_skin_tone[] = "🏌đŸŋ‍♀ī¸"; + inline constexpr const char man_golfing[] = "🏌ī¸â€â™‚ī¸"; + inline constexpr const char man_golfing_tone1[] = "🏌đŸģ‍♂ī¸"; + inline constexpr const char man_golfing_light_skin_tone[] = "🏌đŸģ‍♂ī¸"; + inline constexpr const char man_golfing_tone2[] = "🏌đŸŧ‍♂ī¸"; + inline constexpr const char man_golfing_medium_light_skin_tone[] = "🏌đŸŧ‍♂ī¸"; + inline constexpr const char man_golfing_tone3[] = "🏌đŸŊ‍♂ī¸"; + inline constexpr const char man_golfing_medium_skin_tone[] = "🏌đŸŊ‍♂ī¸"; + inline constexpr const char man_golfing_tone4[] = "🏌🏾‍♂ī¸"; + inline constexpr const char man_golfing_medium_dark_skin_tone[] = "🏌🏾‍♂ī¸"; + inline constexpr const char man_golfing_tone5[] = "🏌đŸŋ‍♂ī¸"; + inline constexpr const char man_golfing_dark_skin_tone[] = "🏌đŸŋ‍♂ī¸"; + inline constexpr const char horse_racing[] = "🏇"; + inline constexpr const char horse_racing_tone1[] = "🏇đŸģ"; + inline constexpr const char horse_racing_tone2[] = "🏇đŸŧ"; + inline constexpr const char horse_racing_tone3[] = "🏇đŸŊ"; + inline constexpr const char horse_racing_tone4[] = "🏇🏾"; + inline constexpr const char horse_racing_tone5[] = "🏇đŸŋ"; + inline constexpr const char person_in_lotus_position[] = "🧘"; + inline constexpr const char person_in_lotus_position_tone1[] = "🧘đŸģ"; + inline constexpr const char person_in_lotus_position_light_skin_tone[] = "🧘đŸģ"; + inline constexpr const char person_in_lotus_position_tone2[] = "🧘đŸŧ"; + inline constexpr const char person_in_lotus_position_medium_light_skin_tone[] = "🧘đŸŧ"; + inline constexpr const char person_in_lotus_position_tone3[] = "🧘đŸŊ"; + inline constexpr const char person_in_lotus_position_medium_skin_tone[] = "🧘đŸŊ"; + inline constexpr const char person_in_lotus_position_tone4[] = "🧘🏾"; + inline constexpr const char person_in_lotus_position_medium_dark_skin_tone[] = "🧘🏾"; + inline constexpr const char person_in_lotus_position_tone5[] = "🧘đŸŋ"; + inline constexpr const char person_in_lotus_position_dark_skin_tone[] = "🧘đŸŋ"; + inline constexpr const char woman_in_lotus_position[] = "🧘‍♀ī¸"; + inline constexpr const char woman_in_lotus_position_tone1[] = "🧘đŸģ‍♀ī¸"; + inline constexpr const char woman_in_lotus_position_light_skin_tone[] = "🧘đŸģ‍♀ī¸"; + inline constexpr const char woman_in_lotus_position_tone2[] = "🧘đŸŧ‍♀ī¸"; + inline constexpr const char woman_in_lotus_position_medium_light_skin_tone[] = "🧘đŸŧ‍♀ī¸"; + inline constexpr const char woman_in_lotus_position_tone3[] = "🧘đŸŊ‍♀ī¸"; + inline constexpr const char woman_in_lotus_position_medium_skin_tone[] = "🧘đŸŊ‍♀ī¸"; + inline constexpr const char woman_in_lotus_position_tone4[] = "🧘🏾‍♀ī¸"; + inline constexpr const char woman_in_lotus_position_medium_dark_skin_tone[] = "🧘🏾‍♀ī¸"; + inline constexpr const char woman_in_lotus_position_tone5[] = "🧘đŸŋ‍♀ī¸"; + inline constexpr const char woman_in_lotus_position_dark_skin_tone[] = "🧘đŸŋ‍♀ī¸"; + inline constexpr const char man_in_lotus_position[] = "🧘‍♂ī¸"; + inline constexpr const char man_in_lotus_position_tone1[] = "🧘đŸģ‍♂ī¸"; + inline constexpr const char man_in_lotus_position_light_skin_tone[] = "🧘đŸģ‍♂ī¸"; + inline constexpr const char man_in_lotus_position_tone2[] = "🧘đŸŧ‍♂ī¸"; + inline constexpr const char man_in_lotus_position_medium_light_skin_tone[] = "🧘đŸŧ‍♂ī¸"; + inline constexpr const char man_in_lotus_position_tone3[] = "🧘đŸŊ‍♂ī¸"; + inline constexpr const char man_in_lotus_position_medium_skin_tone[] = "🧘đŸŊ‍♂ī¸"; + inline constexpr const char man_in_lotus_position_tone4[] = "🧘🏾‍♂ī¸"; + inline constexpr const char man_in_lotus_position_medium_dark_skin_tone[] = "🧘🏾‍♂ī¸"; + inline constexpr const char man_in_lotus_position_tone5[] = "🧘đŸŋ‍♂ī¸"; + inline constexpr const char man_in_lotus_position_dark_skin_tone[] = "🧘đŸŋ‍♂ī¸"; + inline constexpr const char person_surfing[] = "🏄"; + inline constexpr const char surfer[] = "🏄"; + inline constexpr const char person_surfing_tone1[] = "🏄đŸģ"; + inline constexpr const char surfer_tone1[] = "🏄đŸģ"; + inline constexpr const char person_surfing_tone2[] = "🏄đŸŧ"; + inline constexpr const char surfer_tone2[] = "🏄đŸŧ"; + inline constexpr const char person_surfing_tone3[] = "🏄đŸŊ"; + inline constexpr const char surfer_tone3[] = "🏄đŸŊ"; + inline constexpr const char person_surfing_tone4[] = "🏄🏾"; + inline constexpr const char surfer_tone4[] = "🏄🏾"; + inline constexpr const char person_surfing_tone5[] = "🏄đŸŋ"; + inline constexpr const char surfer_tone5[] = "🏄đŸŋ"; + inline constexpr const char woman_surfing[] = "🏄‍♀ī¸"; + inline constexpr const char woman_surfing_tone1[] = "🏄đŸģ‍♀ī¸"; + inline constexpr const char woman_surfing_light_skin_tone[] = "🏄đŸģ‍♀ī¸"; + inline constexpr const char woman_surfing_tone2[] = "🏄đŸŧ‍♀ī¸"; + inline constexpr const char woman_surfing_medium_light_skin_tone[] = "🏄đŸŧ‍♀ī¸"; + inline constexpr const char woman_surfing_tone3[] = "🏄đŸŊ‍♀ī¸"; + inline constexpr const char woman_surfing_medium_skin_tone[] = "🏄đŸŊ‍♀ī¸"; + inline constexpr const char woman_surfing_tone4[] = "🏄🏾‍♀ī¸"; + inline constexpr const char woman_surfing_medium_dark_skin_tone[] = "🏄🏾‍♀ī¸"; + inline constexpr const char woman_surfing_tone5[] = "🏄đŸŋ‍♀ī¸"; + inline constexpr const char woman_surfing_dark_skin_tone[] = "🏄đŸŋ‍♀ī¸"; + inline constexpr const char man_surfing[] = "🏄‍♂ī¸"; + inline constexpr const char man_surfing_tone1[] = "🏄đŸģ‍♂ī¸"; + inline constexpr const char man_surfing_light_skin_tone[] = "🏄đŸģ‍♂ī¸"; + inline constexpr const char man_surfing_tone2[] = "🏄đŸŧ‍♂ī¸"; + inline constexpr const char man_surfing_medium_light_skin_tone[] = "🏄đŸŧ‍♂ī¸"; + inline constexpr const char man_surfing_tone3[] = "🏄đŸŊ‍♂ī¸"; + inline constexpr const char man_surfing_medium_skin_tone[] = "🏄đŸŊ‍♂ī¸"; + inline constexpr const char man_surfing_tone4[] = "🏄🏾‍♂ī¸"; + inline constexpr const char man_surfing_medium_dark_skin_tone[] = "🏄🏾‍♂ī¸"; + inline constexpr const char man_surfing_tone5[] = "🏄đŸŋ‍♂ī¸"; + inline constexpr const char man_surfing_dark_skin_tone[] = "🏄đŸŋ‍♂ī¸"; + inline constexpr const char person_swimming[] = "🏊"; + inline constexpr const char swimmer[] = "🏊"; + inline constexpr const char person_swimming_tone1[] = "🏊đŸģ"; + inline constexpr const char swimmer_tone1[] = "🏊đŸģ"; + inline constexpr const char person_swimming_tone2[] = "🏊đŸŧ"; + inline constexpr const char swimmer_tone2[] = "🏊đŸŧ"; + inline constexpr const char person_swimming_tone3[] = "🏊đŸŊ"; + inline constexpr const char swimmer_tone3[] = "🏊đŸŊ"; + inline constexpr const char person_swimming_tone4[] = "🏊🏾"; + inline constexpr const char swimmer_tone4[] = "🏊🏾"; + inline constexpr const char person_swimming_tone5[] = "🏊đŸŋ"; + inline constexpr const char swimmer_tone5[] = "🏊đŸŋ"; + inline constexpr const char woman_swimming[] = "🏊‍♀ī¸"; + inline constexpr const char woman_swimming_tone1[] = "🏊đŸģ‍♀ī¸"; + inline constexpr const char woman_swimming_light_skin_tone[] = "🏊đŸģ‍♀ī¸"; + inline constexpr const char woman_swimming_tone2[] = "🏊đŸŧ‍♀ī¸"; + inline constexpr const char woman_swimming_medium_light_skin_tone[] = "🏊đŸŧ‍♀ī¸"; + inline constexpr const char woman_swimming_tone3[] = "🏊đŸŊ‍♀ī¸"; + inline constexpr const char woman_swimming_medium_skin_tone[] = "🏊đŸŊ‍♀ī¸"; + inline constexpr const char woman_swimming_tone4[] = "🏊🏾‍♀ī¸"; + inline constexpr const char woman_swimming_medium_dark_skin_tone[] = "🏊🏾‍♀ī¸"; + inline constexpr const char woman_swimming_tone5[] = "🏊đŸŋ‍♀ī¸"; + inline constexpr const char woman_swimming_dark_skin_tone[] = "🏊đŸŋ‍♀ī¸"; + inline constexpr const char man_swimming[] = "🏊‍♂ī¸"; + inline constexpr const char man_swimming_tone1[] = "🏊đŸģ‍♂ī¸"; + inline constexpr const char man_swimming_light_skin_tone[] = "🏊đŸģ‍♂ī¸"; + inline constexpr const char man_swimming_tone2[] = "🏊đŸŧ‍♂ī¸"; + inline constexpr const char man_swimming_medium_light_skin_tone[] = "🏊đŸŧ‍♂ī¸"; + inline constexpr const char man_swimming_tone3[] = "🏊đŸŊ‍♂ī¸"; + inline constexpr const char man_swimming_medium_skin_tone[] = "🏊đŸŊ‍♂ī¸"; + inline constexpr const char man_swimming_tone4[] = "🏊🏾‍♂ī¸"; + inline constexpr const char man_swimming_medium_dark_skin_tone[] = "🏊🏾‍♂ī¸"; + inline constexpr const char man_swimming_tone5[] = "🏊đŸŋ‍♂ī¸"; + inline constexpr const char man_swimming_dark_skin_tone[] = "🏊đŸŋ‍♂ī¸"; + inline constexpr const char person_playing_water_polo[] = "đŸ¤Ŋ"; + inline constexpr const char water_polo[] = "đŸ¤Ŋ"; + inline constexpr const char person_playing_water_polo_tone1[] = "đŸ¤ŊđŸģ"; + inline constexpr const char water_polo_tone1[] = "đŸ¤ŊđŸģ"; + inline constexpr const char person_playing_water_polo_tone2[] = "đŸ¤ŊđŸŧ"; + inline constexpr const char water_polo_tone2[] = "đŸ¤ŊđŸŧ"; + inline constexpr const char person_playing_water_polo_tone3[] = "đŸ¤ŊđŸŊ"; + inline constexpr const char water_polo_tone3[] = "đŸ¤ŊđŸŊ"; + inline constexpr const char person_playing_water_polo_tone4[] = "đŸ¤Ŋ🏾"; + inline constexpr const char water_polo_tone4[] = "đŸ¤Ŋ🏾"; + inline constexpr const char person_playing_water_polo_tone5[] = "đŸ¤ŊđŸŋ"; + inline constexpr const char water_polo_tone5[] = "đŸ¤ŊđŸŋ"; + inline constexpr const char woman_playing_water_polo[] = "đŸ¤Ŋ‍♀ī¸"; + inline constexpr const char woman_playing_water_polo_tone1[] = "đŸ¤ŊđŸģ‍♀ī¸"; + inline constexpr const char woman_playing_water_polo_light_skin_tone[] = "đŸ¤ŊđŸģ‍♀ī¸"; + inline constexpr const char woman_playing_water_polo_tone2[] = "đŸ¤ŊđŸŧ‍♀ī¸"; + inline constexpr const char woman_playing_water_polo_medium_light_skin_tone[] = "đŸ¤ŊđŸŧ‍♀ī¸"; + inline constexpr const char woman_playing_water_polo_tone3[] = "đŸ¤ŊđŸŊ‍♀ī¸"; + inline constexpr const char woman_playing_water_polo_medium_skin_tone[] = "đŸ¤ŊđŸŊ‍♀ī¸"; + inline constexpr const char woman_playing_water_polo_tone4[] = "đŸ¤Ŋ🏾‍♀ī¸"; + inline constexpr const char woman_playing_water_polo_medium_dark_skin_tone[] = "đŸ¤Ŋ🏾‍♀ī¸"; + inline constexpr const char woman_playing_water_polo_tone5[] = "đŸ¤ŊđŸŋ‍♀ī¸"; + inline constexpr const char woman_playing_water_polo_dark_skin_tone[] = "đŸ¤ŊđŸŋ‍♀ī¸"; + inline constexpr const char man_playing_water_polo[] = "đŸ¤Ŋ‍♂ī¸"; + inline constexpr const char man_playing_water_polo_tone1[] = "đŸ¤ŊđŸģ‍♂ī¸"; + inline constexpr const char man_playing_water_polo_light_skin_tone[] = "đŸ¤ŊđŸģ‍♂ī¸"; + inline constexpr const char man_playing_water_polo_tone2[] = "đŸ¤ŊđŸŧ‍♂ī¸"; + inline constexpr const char man_playing_water_polo_medium_light_skin_tone[] = "đŸ¤ŊđŸŧ‍♂ī¸"; + inline constexpr const char man_playing_water_polo_tone3[] = "đŸ¤ŊđŸŊ‍♂ī¸"; + inline constexpr const char man_playing_water_polo_medium_skin_tone[] = "đŸ¤ŊđŸŊ‍♂ī¸"; + inline constexpr const char man_playing_water_polo_tone4[] = "đŸ¤Ŋ🏾‍♂ī¸"; + inline constexpr const char man_playing_water_polo_medium_dark_skin_tone[] = "đŸ¤Ŋ🏾‍♂ī¸"; + inline constexpr const char man_playing_water_polo_tone5[] = "đŸ¤ŊđŸŋ‍♂ī¸"; + inline constexpr const char man_playing_water_polo_dark_skin_tone[] = "đŸ¤ŊđŸŋ‍♂ī¸"; + inline constexpr const char person_rowing_boat[] = "đŸšŖ"; + inline constexpr const char rowboat[] = "đŸšŖ"; + inline constexpr const char person_rowing_boat_tone1[] = "đŸšŖđŸģ"; + inline constexpr const char rowboat_tone1[] = "đŸšŖđŸģ"; + inline constexpr const char person_rowing_boat_tone2[] = "đŸšŖđŸŧ"; + inline constexpr const char rowboat_tone2[] = "đŸšŖđŸŧ"; + inline constexpr const char person_rowing_boat_tone3[] = "đŸšŖđŸŊ"; + inline constexpr const char rowboat_tone3[] = "đŸšŖđŸŊ"; + inline constexpr const char person_rowing_boat_tone4[] = "đŸšŖ🏾"; + inline constexpr const char rowboat_tone4[] = "đŸšŖ🏾"; + inline constexpr const char person_rowing_boat_tone5[] = "đŸšŖđŸŋ"; + inline constexpr const char rowboat_tone5[] = "đŸšŖđŸŋ"; + inline constexpr const char woman_rowing_boat[] = "đŸšŖ‍♀ī¸"; + inline constexpr const char woman_rowing_boat_tone1[] = "đŸšŖđŸģ‍♀ī¸"; + inline constexpr const char woman_rowing_boat_light_skin_tone[] = "đŸšŖđŸģ‍♀ī¸"; + inline constexpr const char woman_rowing_boat_tone2[] = "đŸšŖđŸŧ‍♀ī¸"; + inline constexpr const char woman_rowing_boat_medium_light_skin_tone[] = "đŸšŖđŸŧ‍♀ī¸"; + inline constexpr const char woman_rowing_boat_tone3[] = "đŸšŖđŸŊ‍♀ī¸"; + inline constexpr const char woman_rowing_boat_medium_skin_tone[] = "đŸšŖđŸŊ‍♀ī¸"; + inline constexpr const char woman_rowing_boat_tone4[] = "đŸšŖ🏾‍♀ī¸"; + inline constexpr const char woman_rowing_boat_medium_dark_skin_tone[] = "đŸšŖ🏾‍♀ī¸"; + inline constexpr const char woman_rowing_boat_tone5[] = "đŸšŖđŸŋ‍♀ī¸"; + inline constexpr const char woman_rowing_boat_dark_skin_tone[] = "đŸšŖđŸŋ‍♀ī¸"; + inline constexpr const char man_rowing_boat[] = "đŸšŖ‍♂ī¸"; + inline constexpr const char man_rowing_boat_tone1[] = "đŸšŖđŸģ‍♂ī¸"; + inline constexpr const char man_rowing_boat_light_skin_tone[] = "đŸšŖđŸģ‍♂ī¸"; + inline constexpr const char man_rowing_boat_tone2[] = "đŸšŖđŸŧ‍♂ī¸"; + inline constexpr const char man_rowing_boat_medium_light_skin_tone[] = "đŸšŖđŸŧ‍♂ī¸"; + inline constexpr const char man_rowing_boat_tone3[] = "đŸšŖđŸŊ‍♂ī¸"; + inline constexpr const char man_rowing_boat_medium_skin_tone[] = "đŸšŖđŸŊ‍♂ī¸"; + inline constexpr const char man_rowing_boat_tone4[] = "đŸšŖ🏾‍♂ī¸"; + inline constexpr const char man_rowing_boat_medium_dark_skin_tone[] = "đŸšŖ🏾‍♂ī¸"; + inline constexpr const char man_rowing_boat_tone5[] = "đŸšŖđŸŋ‍♂ī¸"; + inline constexpr const char man_rowing_boat_dark_skin_tone[] = "đŸšŖđŸŋ‍♂ī¸"; + inline constexpr const char person_climbing[] = "🧗"; + inline constexpr const char person_climbing_tone1[] = "🧗đŸģ"; + inline constexpr const char person_climbing_light_skin_tone[] = "🧗đŸģ"; + inline constexpr const char person_climbing_tone2[] = "🧗đŸŧ"; + inline constexpr const char person_climbing_medium_light_skin_tone[] = "🧗đŸŧ"; + inline constexpr const char person_climbing_tone3[] = "🧗đŸŊ"; + inline constexpr const char person_climbing_medium_skin_tone[] = "🧗đŸŊ"; + inline constexpr const char person_climbing_tone4[] = "🧗🏾"; + inline constexpr const char person_climbing_medium_dark_skin_tone[] = "🧗🏾"; + inline constexpr const char person_climbing_tone5[] = "🧗đŸŋ"; + inline constexpr const char person_climbing_dark_skin_tone[] = "🧗đŸŋ"; + inline constexpr const char woman_climbing[] = "🧗‍♀ī¸"; + inline constexpr const char woman_climbing_tone1[] = "🧗đŸģ‍♀ī¸"; + inline constexpr const char woman_climbing_light_skin_tone[] = "🧗đŸģ‍♀ī¸"; + inline constexpr const char woman_climbing_tone2[] = "🧗đŸŧ‍♀ī¸"; + inline constexpr const char woman_climbing_medium_light_skin_tone[] = "🧗đŸŧ‍♀ī¸"; + inline constexpr const char woman_climbing_tone3[] = "🧗đŸŊ‍♀ī¸"; + inline constexpr const char woman_climbing_medium_skin_tone[] = "🧗đŸŊ‍♀ī¸"; + inline constexpr const char woman_climbing_tone4[] = "🧗🏾‍♀ī¸"; + inline constexpr const char woman_climbing_medium_dark_skin_tone[] = "🧗🏾‍♀ī¸"; + inline constexpr const char woman_climbing_tone5[] = "🧗đŸŋ‍♀ī¸"; + inline constexpr const char woman_climbing_dark_skin_tone[] = "🧗đŸŋ‍♀ī¸"; + inline constexpr const char man_climbing[] = "🧗‍♂ī¸"; + inline constexpr const char man_climbing_tone1[] = "🧗đŸģ‍♂ī¸"; + inline constexpr const char man_climbing_light_skin_tone[] = "🧗đŸģ‍♂ī¸"; + inline constexpr const char man_climbing_tone2[] = "🧗đŸŧ‍♂ī¸"; + inline constexpr const char man_climbing_medium_light_skin_tone[] = "🧗đŸŧ‍♂ī¸"; + inline constexpr const char man_climbing_tone3[] = "🧗đŸŊ‍♂ī¸"; + inline constexpr const char man_climbing_medium_skin_tone[] = "🧗đŸŊ‍♂ī¸"; + inline constexpr const char man_climbing_tone4[] = "🧗🏾‍♂ī¸"; + inline constexpr const char man_climbing_medium_dark_skin_tone[] = "🧗🏾‍♂ī¸"; + inline constexpr const char man_climbing_tone5[] = "🧗đŸŋ‍♂ī¸"; + inline constexpr const char man_climbing_dark_skin_tone[] = "🧗đŸŋ‍♂ī¸"; + inline constexpr const char person_mountain_biking[] = "đŸšĩ"; + inline constexpr const char mountain_bicyclist[] = "đŸšĩ"; + inline constexpr const char person_mountain_biking_tone1[] = "đŸšĩđŸģ"; + inline constexpr const char mountain_bicyclist_tone1[] = "đŸšĩđŸģ"; + inline constexpr const char person_mountain_biking_tone2[] = "đŸšĩđŸŧ"; + inline constexpr const char mountain_bicyclist_tone2[] = "đŸšĩđŸŧ"; + inline constexpr const char person_mountain_biking_tone3[] = "đŸšĩđŸŊ"; + inline constexpr const char mountain_bicyclist_tone3[] = "đŸšĩđŸŊ"; + inline constexpr const char person_mountain_biking_tone4[] = "đŸšĩ🏾"; + inline constexpr const char mountain_bicyclist_tone4[] = "đŸšĩ🏾"; + inline constexpr const char person_mountain_biking_tone5[] = "đŸšĩđŸŋ"; + inline constexpr const char mountain_bicyclist_tone5[] = "đŸšĩđŸŋ"; + inline constexpr const char woman_mountain_biking[] = "đŸšĩ‍♀ī¸"; + inline constexpr const char woman_mountain_biking_tone1[] = "đŸšĩđŸģ‍♀ī¸"; + inline constexpr const char woman_mountain_biking_light_skin_tone[] = "đŸšĩđŸģ‍♀ī¸"; + inline constexpr const char woman_mountain_biking_tone2[] = "đŸšĩđŸŧ‍♀ī¸"; + inline constexpr const char woman_mountain_biking_medium_light_skin_tone[] = "đŸšĩđŸŧ‍♀ī¸"; + inline constexpr const char woman_mountain_biking_tone3[] = "đŸšĩđŸŊ‍♀ī¸"; + inline constexpr const char woman_mountain_biking_medium_skin_tone[] = "đŸšĩđŸŊ‍♀ī¸"; + inline constexpr const char woman_mountain_biking_tone4[] = "đŸšĩ🏾‍♀ī¸"; + inline constexpr const char woman_mountain_biking_medium_dark_skin_tone[] = "đŸšĩ🏾‍♀ī¸"; + inline constexpr const char woman_mountain_biking_tone5[] = "đŸšĩđŸŋ‍♀ī¸"; + inline constexpr const char woman_mountain_biking_dark_skin_tone[] = "đŸšĩđŸŋ‍♀ī¸"; + inline constexpr const char man_mountain_biking[] = "đŸšĩ‍♂ī¸"; + inline constexpr const char man_mountain_biking_tone1[] = "đŸšĩđŸģ‍♂ī¸"; + inline constexpr const char man_mountain_biking_light_skin_tone[] = "đŸšĩđŸģ‍♂ī¸"; + inline constexpr const char man_mountain_biking_tone2[] = "đŸšĩđŸŧ‍♂ī¸"; + inline constexpr const char man_mountain_biking_medium_light_skin_tone[] = "đŸšĩđŸŧ‍♂ī¸"; + inline constexpr const char man_mountain_biking_tone3[] = "đŸšĩđŸŊ‍♂ī¸"; + inline constexpr const char man_mountain_biking_medium_skin_tone[] = "đŸšĩđŸŊ‍♂ī¸"; + inline constexpr const char man_mountain_biking_tone4[] = "đŸšĩ🏾‍♂ī¸"; + inline constexpr const char man_mountain_biking_medium_dark_skin_tone[] = "đŸšĩ🏾‍♂ī¸"; + inline constexpr const char man_mountain_biking_tone5[] = "đŸšĩđŸŋ‍♂ī¸"; + inline constexpr const char man_mountain_biking_dark_skin_tone[] = "đŸšĩđŸŋ‍♂ī¸"; + inline constexpr const char person_biking[] = "🚴"; + inline constexpr const char bicyclist[] = "🚴"; + inline constexpr const char person_biking_tone1[] = "🚴đŸģ"; + inline constexpr const char bicyclist_tone1[] = "🚴đŸģ"; + inline constexpr const char person_biking_tone2[] = "🚴đŸŧ"; + inline constexpr const char bicyclist_tone2[] = "🚴đŸŧ"; + inline constexpr const char person_biking_tone3[] = "🚴đŸŊ"; + inline constexpr const char bicyclist_tone3[] = "🚴đŸŊ"; + inline constexpr const char person_biking_tone4[] = "🚴🏾"; + inline constexpr const char bicyclist_tone4[] = "🚴🏾"; + inline constexpr const char person_biking_tone5[] = "🚴đŸŋ"; + inline constexpr const char bicyclist_tone5[] = "🚴đŸŋ"; + inline constexpr const char woman_biking[] = "🚴‍♀ī¸"; + inline constexpr const char woman_biking_tone1[] = "🚴đŸģ‍♀ī¸"; + inline constexpr const char woman_biking_light_skin_tone[] = "🚴đŸģ‍♀ī¸"; + inline constexpr const char woman_biking_tone2[] = "🚴đŸŧ‍♀ī¸"; + inline constexpr const char woman_biking_medium_light_skin_tone[] = "🚴đŸŧ‍♀ī¸"; + inline constexpr const char woman_biking_tone3[] = "🚴đŸŊ‍♀ī¸"; + inline constexpr const char woman_biking_medium_skin_tone[] = "🚴đŸŊ‍♀ī¸"; + inline constexpr const char woman_biking_tone4[] = "🚴🏾‍♀ī¸"; + inline constexpr const char woman_biking_medium_dark_skin_tone[] = "🚴🏾‍♀ī¸"; + inline constexpr const char woman_biking_tone5[] = "🚴đŸŋ‍♀ī¸"; + inline constexpr const char woman_biking_dark_skin_tone[] = "🚴đŸŋ‍♀ī¸"; + inline constexpr const char man_biking[] = "🚴‍♂ī¸"; + inline constexpr const char man_biking_tone1[] = "🚴đŸģ‍♂ī¸"; + inline constexpr const char man_biking_light_skin_tone[] = "🚴đŸģ‍♂ī¸"; + inline constexpr const char man_biking_tone2[] = "🚴đŸŧ‍♂ī¸"; + inline constexpr const char man_biking_medium_light_skin_tone[] = "🚴đŸŧ‍♂ī¸"; + inline constexpr const char man_biking_tone3[] = "🚴đŸŊ‍♂ī¸"; + inline constexpr const char man_biking_medium_skin_tone[] = "🚴đŸŊ‍♂ī¸"; + inline constexpr const char man_biking_tone4[] = "🚴🏾‍♂ī¸"; + inline constexpr const char man_biking_medium_dark_skin_tone[] = "🚴🏾‍♂ī¸"; + inline constexpr const char man_biking_tone5[] = "🚴đŸŋ‍♂ī¸"; + inline constexpr const char man_biking_dark_skin_tone[] = "🚴đŸŋ‍♂ī¸"; + inline constexpr const char trophy[] = "🏆"; + inline constexpr const char first_place[] = "đŸĨ‡"; + inline constexpr const char first_place_medal[] = "đŸĨ‡"; + inline constexpr const char second_place[] = "đŸĨˆ"; + inline constexpr const char second_place_medal[] = "đŸĨˆ"; + inline constexpr const char third_place[] = "đŸĨ‰"; + inline constexpr const char third_place_medal[] = "đŸĨ‰"; + inline constexpr const char medal[] = "🏅"; + inline constexpr const char sports_medal[] = "🏅"; + inline constexpr const char military_medal[] = "🎖ī¸"; + inline constexpr const char rosette[] = "đŸĩī¸"; + inline constexpr const char reminder_ribbon[] = "🎗ī¸"; + inline constexpr const char ticket[] = "đŸŽĢ"; + inline constexpr const char tickets[] = "🎟ī¸"; + inline constexpr const char admission_tickets[] = "🎟ī¸"; + inline constexpr const char circus_tent[] = "đŸŽĒ"; + inline constexpr const char person_juggling[] = "🤹"; + inline constexpr const char juggling[] = "🤹"; + inline constexpr const char juggler[] = "🤹"; + inline constexpr const char person_juggling_tone1[] = "🤹đŸģ"; + inline constexpr const char juggling_tone1[] = "🤹đŸģ"; + inline constexpr const char juggler_tone1[] = "🤹đŸģ"; + inline constexpr const char person_juggling_tone2[] = "🤹đŸŧ"; + inline constexpr const char juggling_tone2[] = "🤹đŸŧ"; + inline constexpr const char juggler_tone2[] = "🤹đŸŧ"; + inline constexpr const char person_juggling_tone3[] = "🤹đŸŊ"; + inline constexpr const char juggling_tone3[] = "🤹đŸŊ"; + inline constexpr const char juggler_tone3[] = "🤹đŸŊ"; + inline constexpr const char person_juggling_tone4[] = "🤹🏾"; + inline constexpr const char juggling_tone4[] = "🤹🏾"; + inline constexpr const char juggler_tone4[] = "🤹🏾"; + inline constexpr const char person_juggling_tone5[] = "🤹đŸŋ"; + inline constexpr const char juggling_tone5[] = "🤹đŸŋ"; + inline constexpr const char juggler_tone5[] = "🤹đŸŋ"; + inline constexpr const char woman_juggling[] = "🤹‍♀ī¸"; + inline constexpr const char woman_juggling_tone1[] = "🤹đŸģ‍♀ī¸"; + inline constexpr const char woman_juggling_light_skin_tone[] = "🤹đŸģ‍♀ī¸"; + inline constexpr const char woman_juggling_tone2[] = "🤹đŸŧ‍♀ī¸"; + inline constexpr const char woman_juggling_medium_light_skin_tone[] = "🤹đŸŧ‍♀ī¸"; + inline constexpr const char woman_juggling_tone3[] = "🤹đŸŊ‍♀ī¸"; + inline constexpr const char woman_juggling_medium_skin_tone[] = "🤹đŸŊ‍♀ī¸"; + inline constexpr const char woman_juggling_tone4[] = "🤹🏾‍♀ī¸"; + inline constexpr const char woman_juggling_medium_dark_skin_tone[] = "🤹🏾‍♀ī¸"; + inline constexpr const char woman_juggling_tone5[] = "🤹đŸŋ‍♀ī¸"; + inline constexpr const char woman_juggling_dark_skin_tone[] = "🤹đŸŋ‍♀ī¸"; + inline constexpr const char man_juggling[] = "🤹‍♂ī¸"; + inline constexpr const char man_juggling_tone1[] = "🤹đŸģ‍♂ī¸"; + inline constexpr const char man_juggling_light_skin_tone[] = "🤹đŸģ‍♂ī¸"; + inline constexpr const char man_juggling_tone2[] = "🤹đŸŧ‍♂ī¸"; + inline constexpr const char man_juggling_medium_light_skin_tone[] = "🤹đŸŧ‍♂ī¸"; + inline constexpr const char man_juggling_tone3[] = "🤹đŸŊ‍♂ī¸"; + inline constexpr const char man_juggling_medium_skin_tone[] = "🤹đŸŊ‍♂ī¸"; + inline constexpr const char man_juggling_tone4[] = "🤹🏾‍♂ī¸"; + inline constexpr const char man_juggling_medium_dark_skin_tone[] = "🤹🏾‍♂ī¸"; + inline constexpr const char man_juggling_tone5[] = "🤹đŸŋ‍♂ī¸"; + inline constexpr const char man_juggling_dark_skin_tone[] = "🤹đŸŋ‍♂ī¸"; + inline constexpr const char performing_arts[] = "🎭"; + inline constexpr const char ballet_shoes[] = "🩰"; + inline constexpr const char art[] = "🎨"; + inline constexpr const char clapper[] = "đŸŽŦ"; + inline constexpr const char microphone[] = "🎤"; + inline constexpr const char headphones[] = "🎧"; + inline constexpr const char musical_score[] = "đŸŽŧ"; + inline constexpr const char musical_keyboard[] = "🎹"; + inline constexpr const char drum[] = "đŸĨ"; + inline constexpr const char drum_with_drumsticks[] = "đŸĨ"; + inline constexpr const char long_drum[] = "đŸĒ˜"; + inline constexpr const char saxophone[] = "🎷"; + inline constexpr const char trumpet[] = "đŸŽē"; + inline constexpr const char accordion[] = "đŸĒ—"; + inline constexpr const char guitar[] = "🎸"; + inline constexpr const char banjo[] = "đŸĒ•"; + inline constexpr const char violin[] = "đŸŽģ"; + inline constexpr const char game_die[] = "🎲"; + inline constexpr const char chess_pawn[] = "♟ī¸"; + inline constexpr const char dart[] = "đŸŽ¯"; + inline constexpr const char bowling[] = "đŸŽŗ"; + inline constexpr const char video_game[] = "🎮"; + inline constexpr const char slot_machine[] = "🎰"; + inline constexpr const char jigsaw[] = "🧩"; + inline constexpr const char flag_white[] = "đŸŗī¸"; + inline constexpr const char flag_black[] = "🏴"; + inline constexpr const char checkered_flag[] = "🏁"; + inline constexpr const char triangular_flag_on_post[] = "🚩"; + inline constexpr const char rainbow_flag[] = "đŸŗī¸â€đŸŒˆ"; + inline constexpr const char gay_pride_flag[] = "đŸŗī¸â€đŸŒˆ"; + inline constexpr const char transgender_flag[] = "đŸŗī¸â€âš§ī¸"; + inline constexpr const char pirate_flag[] = "🏴‍☠ī¸"; + inline constexpr const char flag_af[] = "đŸ‡ĻđŸ‡Ģ"; + inline constexpr const char flag_ax[] = "đŸ‡ĻđŸ‡Ŋ"; + inline constexpr const char flag_al[] = "đŸ‡Ļ🇱"; + inline constexpr const char flag_dz[] = "🇩đŸ‡ŋ"; + inline constexpr const char flag_as[] = "đŸ‡Ļ🇸"; + inline constexpr const char flag_ad[] = "đŸ‡Ļ🇩"; + inline constexpr const char flag_ao[] = "đŸ‡Ļ🇴"; + inline constexpr const char flag_ai[] = "đŸ‡Ļ🇮"; + inline constexpr const char flag_aq[] = "đŸ‡ĻđŸ‡ļ"; + inline constexpr const char flag_ag[] = "đŸ‡ĻđŸ‡Ŧ"; + inline constexpr const char flag_ar[] = "đŸ‡Ļ🇷"; + inline constexpr const char flag_am[] = "đŸ‡Ļ🇲"; + inline constexpr const char flag_aw[] = "đŸ‡ĻđŸ‡ŧ"; + inline constexpr const char flag_au[] = "đŸ‡ĻđŸ‡ē"; + inline constexpr const char flag_at[] = "đŸ‡Ļ🇹"; + inline constexpr const char flag_az[] = "đŸ‡ĻđŸ‡ŋ"; + inline constexpr const char flag_bs[] = "🇧🇸"; + inline constexpr const char flag_bh[] = "🇧🇭"; + inline constexpr const char flag_bd[] = "🇧🇩"; + inline constexpr const char flag_bb[] = "🇧🇧"; + inline constexpr const char flag_by[] = "🇧🇾"; + inline constexpr const char flag_be[] = "🇧đŸ‡Ē"; + inline constexpr const char flag_bz[] = "🇧đŸ‡ŋ"; + inline constexpr const char flag_bj[] = "đŸ‡§đŸ‡¯"; + inline constexpr const char flag_bm[] = "🇧🇲"; + inline constexpr const char flag_bt[] = "🇧🇹"; + inline constexpr const char flag_bo[] = "🇧🇴"; + inline constexpr const char flag_ba[] = "🇧đŸ‡Ļ"; + inline constexpr const char flag_bw[] = "🇧đŸ‡ŧ"; + inline constexpr const char flag_br[] = "🇧🇷"; + inline constexpr const char flag_io[] = "🇮🇴"; + inline constexpr const char flag_vg[] = "đŸ‡ģđŸ‡Ŧ"; + inline constexpr const char flag_bn[] = "🇧đŸ‡ŗ"; + inline constexpr const char flag_bg[] = "🇧đŸ‡Ŧ"; + inline constexpr const char flag_bf[] = "🇧đŸ‡Ģ"; + inline constexpr const char flag_bi[] = "🇧🇮"; + inline constexpr const char flag_kh[] = "🇰🇭"; + inline constexpr const char flag_cm[] = "🇨🇲"; + inline constexpr const char flag_ca[] = "🇨đŸ‡Ļ"; + inline constexpr const char flag_ic[] = "🇮🇨"; + inline constexpr const char flag_cv[] = "🇨đŸ‡ģ"; + inline constexpr const char flag_bq[] = "🇧đŸ‡ļ"; + inline constexpr const char flag_ky[] = "🇰🇾"; + inline constexpr const char flag_cf[] = "🇨đŸ‡Ģ"; + inline constexpr const char flag_td[] = "🇹🇩"; + inline constexpr const char flag_cl[] = "🇨🇱"; + inline constexpr const char flag_cn[] = "🇨đŸ‡ŗ"; + inline constexpr const char flag_cx[] = "🇨đŸ‡Ŋ"; + inline constexpr const char flag_cc[] = "🇨🇨"; + inline constexpr const char flag_co[] = "🇨🇴"; + inline constexpr const char flag_km[] = "🇰🇲"; + inline constexpr const char flag_cg[] = "🇨đŸ‡Ŧ"; + inline constexpr const char flag_cd[] = "🇨🇩"; + inline constexpr const char flag_ck[] = "🇨🇰"; + inline constexpr const char flag_cr[] = "🇨🇷"; + inline constexpr const char flag_ci[] = "🇨🇮"; + inline constexpr const char flag_hr[] = "🇭🇷"; + inline constexpr const char flag_cu[] = "🇨đŸ‡ē"; + inline constexpr const char flag_cw[] = "🇨đŸ‡ŧ"; + inline constexpr const char flag_cy[] = "🇨🇾"; + inline constexpr const char flag_cz[] = "🇨đŸ‡ŋ"; + inline constexpr const char flag_dk[] = "🇩🇰"; + inline constexpr const char flag_dj[] = "đŸ‡ŠđŸ‡¯"; + inline constexpr const char flag_dm[] = "🇩🇲"; + inline constexpr const char flag_do[] = "🇩🇴"; + inline constexpr const char flag_ec[] = "đŸ‡Ē🇨"; + inline constexpr const char flag_eg[] = "đŸ‡ĒđŸ‡Ŧ"; + inline constexpr const char flag_sv[] = "🇸đŸ‡ģ"; + inline constexpr const char flag_gq[] = "đŸ‡ŦđŸ‡ļ"; + inline constexpr const char flag_er[] = "đŸ‡Ē🇷"; + inline constexpr const char flag_ee[] = "đŸ‡ĒđŸ‡Ē"; + inline constexpr const char flag_et[] = "đŸ‡Ē🇹"; + inline constexpr const char flag_eu[] = "đŸ‡ĒđŸ‡ē"; + inline constexpr const char flag_fk[] = "đŸ‡Ģ🇰"; + inline constexpr const char flag_fo[] = "đŸ‡Ģ🇴"; + inline constexpr const char flag_fj[] = "đŸ‡ĢđŸ‡¯"; + inline constexpr const char flag_fi[] = "đŸ‡Ģ🇮"; + inline constexpr const char flag_fr[] = "đŸ‡Ģ🇷"; + inline constexpr const char flag_gf[] = "đŸ‡ŦđŸ‡Ģ"; + inline constexpr const char flag_pf[] = "đŸ‡ĩđŸ‡Ģ"; + inline constexpr const char flag_tf[] = "🇹đŸ‡Ģ"; + inline constexpr const char flag_ga[] = "đŸ‡ŦđŸ‡Ļ"; + inline constexpr const char flag_gm[] = "đŸ‡Ŧ🇲"; + inline constexpr const char flag_ge[] = "đŸ‡ŦđŸ‡Ē"; + inline constexpr const char flag_de[] = "🇩đŸ‡Ē"; + inline constexpr const char flag_gh[] = "đŸ‡Ŧ🇭"; + inline constexpr const char flag_gi[] = "đŸ‡Ŧ🇮"; + inline constexpr const char flag_gr[] = "đŸ‡Ŧ🇷"; + inline constexpr const char flag_gl[] = "đŸ‡Ŧ🇱"; + inline constexpr const char flag_gd[] = "đŸ‡Ŧ🇩"; + inline constexpr const char flag_gp[] = "đŸ‡ŦđŸ‡ĩ"; + inline constexpr const char flag_gu[] = "đŸ‡ŦđŸ‡ē"; + inline constexpr const char flag_gt[] = "đŸ‡Ŧ🇹"; + inline constexpr const char flag_gg[] = "đŸ‡ŦđŸ‡Ŧ"; + inline constexpr const char flag_gn[] = "đŸ‡ŦđŸ‡ŗ"; + inline constexpr const char flag_gw[] = "đŸ‡ŦđŸ‡ŧ"; + inline constexpr const char flag_gy[] = "đŸ‡Ŧ🇾"; + inline constexpr const char flag_ht[] = "🇭🇹"; + inline constexpr const char flag_hn[] = "🇭đŸ‡ŗ"; + inline constexpr const char flag_hk[] = "🇭🇰"; + inline constexpr const char flag_hu[] = "🇭đŸ‡ē"; + inline constexpr const char flag_is[] = "🇮🇸"; + inline constexpr const char flag_in[] = "🇮đŸ‡ŗ"; + inline constexpr const char flag_id[] = "🇮🇩"; + inline constexpr const char flag_ir[] = "🇮🇷"; + inline constexpr const char flag_iq[] = "🇮đŸ‡ļ"; + inline constexpr const char flag_ie[] = "🇮đŸ‡Ē"; + inline constexpr const char flag_im[] = "🇮🇲"; + inline constexpr const char flag_il[] = "🇮🇱"; + inline constexpr const char flag_it[] = "🇮🇹"; + inline constexpr const char flag_jm[] = "đŸ‡¯đŸ‡˛"; + inline constexpr const char flag_jp[] = "đŸ‡¯đŸ‡ĩ"; + inline constexpr const char crossed_flags[] = "🎌"; + inline constexpr const char flag_je[] = "đŸ‡¯đŸ‡Ē"; + inline constexpr const char flag_jo[] = "đŸ‡¯đŸ‡´"; + inline constexpr const char flag_kz[] = "🇰đŸ‡ŋ"; + inline constexpr const char flag_ke[] = "🇰đŸ‡Ē"; + inline constexpr const char flag_ki[] = "🇰🇮"; + inline constexpr const char flag_xk[] = "đŸ‡Ŋ🇰"; + inline constexpr const char flag_kw[] = "🇰đŸ‡ŧ"; + inline constexpr const char flag_kg[] = "🇰đŸ‡Ŧ"; + inline constexpr const char flag_la[] = "🇱đŸ‡Ļ"; + inline constexpr const char flag_lv[] = "🇱đŸ‡ģ"; + inline constexpr const char flag_lb[] = "🇱🇧"; + inline constexpr const char flag_ls[] = "🇱🇸"; + inline constexpr const char flag_lr[] = "🇱🇷"; + inline constexpr const char flag_ly[] = "🇱🇾"; + inline constexpr const char flag_li[] = "🇱🇮"; + inline constexpr const char flag_lt[] = "🇱🇹"; + inline constexpr const char flag_lu[] = "🇱đŸ‡ē"; + inline constexpr const char flag_mo[] = "🇲🇴"; + inline constexpr const char flag_mk[] = "🇲🇰"; + inline constexpr const char flag_mg[] = "🇲đŸ‡Ŧ"; + inline constexpr const char flag_mw[] = "🇲đŸ‡ŧ"; + inline constexpr const char flag_my[] = "🇲🇾"; + inline constexpr const char flag_mv[] = "🇲đŸ‡ģ"; + inline constexpr const char flag_ml[] = "🇲🇱"; + inline constexpr const char flag_mt[] = "🇲🇹"; + inline constexpr const char flag_mh[] = "🇲🇭"; + inline constexpr const char flag_mq[] = "🇲đŸ‡ļ"; + inline constexpr const char flag_mr[] = "🇲🇷"; + inline constexpr const char flag_mu[] = "🇲đŸ‡ē"; + inline constexpr const char flag_yt[] = "🇾🇹"; + inline constexpr const char flag_mx[] = "🇲đŸ‡Ŋ"; + inline constexpr const char flag_fm[] = "đŸ‡Ģ🇲"; + inline constexpr const char flag_md[] = "🇲🇩"; + inline constexpr const char flag_mc[] = "🇲🇨"; + inline constexpr const char flag_mn[] = "🇲đŸ‡ŗ"; + inline constexpr const char flag_me[] = "🇲đŸ‡Ē"; + inline constexpr const char flag_ms[] = "🇲🇸"; + inline constexpr const char flag_ma[] = "🇲đŸ‡Ļ"; + inline constexpr const char flag_mz[] = "🇲đŸ‡ŋ"; + inline constexpr const char flag_mm[] = "🇲🇲"; + inline constexpr const char flag_na[] = "đŸ‡ŗđŸ‡Ļ"; + inline constexpr const char flag_nr[] = "đŸ‡ŗ🇷"; + inline constexpr const char flag_np[] = "đŸ‡ŗđŸ‡ĩ"; + inline constexpr const char flag_nl[] = "đŸ‡ŗ🇱"; + inline constexpr const char flag_nc[] = "đŸ‡ŗ🇨"; + inline constexpr const char flag_nz[] = "đŸ‡ŗđŸ‡ŋ"; + inline constexpr const char flag_ni[] = "đŸ‡ŗ🇮"; + inline constexpr const char flag_ne[] = "đŸ‡ŗđŸ‡Ē"; + inline constexpr const char flag_ng[] = "đŸ‡ŗđŸ‡Ŧ"; + inline constexpr const char flag_nu[] = "đŸ‡ŗđŸ‡ē"; + inline constexpr const char flag_nf[] = "đŸ‡ŗđŸ‡Ģ"; + inline constexpr const char flag_kp[] = "🇰đŸ‡ĩ"; + inline constexpr const char flag_mp[] = "🇲đŸ‡ĩ"; + inline constexpr const char flag_no[] = "đŸ‡ŗ🇴"; + inline constexpr const char flag_om[] = "🇴🇲"; + inline constexpr const char flag_pk[] = "đŸ‡ĩ🇰"; + inline constexpr const char flag_pw[] = "đŸ‡ĩđŸ‡ŧ"; + inline constexpr const char flag_ps[] = "đŸ‡ĩ🇸"; + inline constexpr const char flag_pa[] = "đŸ‡ĩđŸ‡Ļ"; + inline constexpr const char flag_pg[] = "đŸ‡ĩđŸ‡Ŧ"; + inline constexpr const char flag_py[] = "đŸ‡ĩ🇾"; + inline constexpr const char flag_pe[] = "đŸ‡ĩđŸ‡Ē"; + inline constexpr const char flag_ph[] = "đŸ‡ĩ🇭"; + inline constexpr const char flag_pn[] = "đŸ‡ĩđŸ‡ŗ"; + inline constexpr const char flag_pl[] = "đŸ‡ĩ🇱"; + inline constexpr const char flag_pt[] = "đŸ‡ĩ🇹"; + inline constexpr const char flag_pr[] = "đŸ‡ĩ🇷"; + inline constexpr const char flag_qa[] = "đŸ‡ļđŸ‡Ļ"; + inline constexpr const char flag_re[] = "🇷đŸ‡Ē"; + inline constexpr const char flag_ro[] = "🇷🇴"; + inline constexpr const char flag_ru[] = "🇷đŸ‡ē"; + inline constexpr const char flag_rw[] = "🇷đŸ‡ŧ"; + inline constexpr const char flag_ws[] = "đŸ‡ŧ🇸"; + inline constexpr const char flag_sm[] = "🇸🇲"; + inline constexpr const char flag_st[] = "🇸🇹"; + inline constexpr const char flag_sa[] = "🇸đŸ‡Ļ"; + inline constexpr const char flag_sn[] = "🇸đŸ‡ŗ"; + inline constexpr const char flag_rs[] = "🇷🇸"; + inline constexpr const char flag_sc[] = "🇸🇨"; + inline constexpr const char flag_sl[] = "🇸🇱"; + inline constexpr const char flag_sg[] = "🇸đŸ‡Ŧ"; + inline constexpr const char flag_sx[] = "🇸đŸ‡Ŋ"; + inline constexpr const char flag_sk[] = "🇸🇰"; + inline constexpr const char flag_si[] = "🇸🇮"; + inline constexpr const char flag_gs[] = "đŸ‡Ŧ🇸"; + inline constexpr const char flag_sb[] = "🇸🇧"; + inline constexpr const char flag_so[] = "🇸🇴"; + inline constexpr const char flag_za[] = "đŸ‡ŋđŸ‡Ļ"; + inline constexpr const char flag_kr[] = "🇰🇷"; + inline constexpr const char flag_ss[] = "🇸🇸"; + inline constexpr const char flag_es[] = "đŸ‡Ē🇸"; + inline constexpr const char flag_lk[] = "🇱🇰"; + inline constexpr const char flag_bl[] = "🇧🇱"; + inline constexpr const char flag_sh[] = "🇸🇭"; + inline constexpr const char flag_kn[] = "🇰đŸ‡ŗ"; + inline constexpr const char flag_lc[] = "🇱🇨"; + inline constexpr const char flag_pm[] = "đŸ‡ĩ🇲"; + inline constexpr const char flag_vc[] = "đŸ‡ģ🇨"; + inline constexpr const char flag_sd[] = "🇸🇩"; + inline constexpr const char flag_sr[] = "🇸🇷"; + inline constexpr const char flag_sz[] = "🇸đŸ‡ŋ"; + inline constexpr const char flag_se[] = "🇸đŸ‡Ē"; + inline constexpr const char flag_ch[] = "🇨🇭"; + inline constexpr const char flag_sy[] = "🇸🇾"; + inline constexpr const char flag_tw[] = "🇹đŸ‡ŧ"; + inline constexpr const char flag_tj[] = "đŸ‡šđŸ‡¯"; + inline constexpr const char flag_tz[] = "🇹đŸ‡ŋ"; + inline constexpr const char flag_th[] = "🇹🇭"; + inline constexpr const char flag_tl[] = "🇹🇱"; + inline constexpr const char flag_tg[] = "🇹đŸ‡Ŧ"; + inline constexpr const char flag_tk[] = "🇹🇰"; + inline constexpr const char flag_to[] = "🇹🇴"; + inline constexpr const char flag_tt[] = "🇹🇹"; + inline constexpr const char flag_tn[] = "🇹đŸ‡ŗ"; + inline constexpr const char flag_tr[] = "🇹🇷"; + inline constexpr const char flag_tm[] = "🇹🇲"; + inline constexpr const char flag_tc[] = "🇹🇨"; + inline constexpr const char flag_vi[] = "đŸ‡ģ🇮"; + inline constexpr const char flag_tv[] = "🇹đŸ‡ģ"; + inline constexpr const char flag_ug[] = "đŸ‡ēđŸ‡Ŧ"; + inline constexpr const char flag_ua[] = "đŸ‡ēđŸ‡Ļ"; + inline constexpr const char flag_ae[] = "đŸ‡ĻđŸ‡Ē"; + inline constexpr const char flag_gb[] = "đŸ‡Ŧ🇧"; + inline constexpr const char england[] = "🏴ķ §ķ ĸķ Ĩķ Žķ §ķ ŋ"; + inline constexpr const char scotland[] = "🏴ķ §ķ ĸķ ŗķ Ŗķ ´ķ ŋ"; + inline constexpr const char wales[] = "🏴ķ §ķ ĸķ ˇķ Ŧķ ŗķ ŋ"; + inline constexpr const char flag_us[] = "đŸ‡ē🇸"; + inline constexpr const char flag_uy[] = "đŸ‡ē🇾"; + inline constexpr const char flag_uz[] = "đŸ‡ēđŸ‡ŋ"; + inline constexpr const char flag_vu[] = "đŸ‡ģđŸ‡ē"; + inline constexpr const char flag_va[] = "đŸ‡ģđŸ‡Ļ"; + inline constexpr const char flag_ve[] = "đŸ‡ģđŸ‡Ē"; + inline constexpr const char flag_vn[] = "đŸ‡ģđŸ‡ŗ"; + inline constexpr const char flag_wf[] = "đŸ‡ŧđŸ‡Ģ"; + inline constexpr const char flag_eh[] = "đŸ‡Ē🇭"; + inline constexpr const char flag_ye[] = "🇾đŸ‡Ē"; + inline constexpr const char flag_zm[] = "đŸ‡ŋ🇲"; + inline constexpr const char flag_zw[] = "đŸ‡ŋđŸ‡ŧ"; + inline constexpr const char flag_ac[] = "đŸ‡Ļ🇨"; + inline constexpr const char flag_bv[] = "🇧đŸ‡ģ"; + inline constexpr const char flag_cp[] = "🇨đŸ‡ĩ"; + inline constexpr const char flag_ea[] = "đŸ‡ĒđŸ‡Ļ"; + inline constexpr const char flag_dg[] = "🇩đŸ‡Ŧ"; + inline constexpr const char flag_hm[] = "🇭🇲"; + inline constexpr const char flag_mf[] = "🇲đŸ‡Ģ"; + inline constexpr const char flag_sj[] = "đŸ‡¸đŸ‡¯"; + inline constexpr const char flag_ta[] = "🇹đŸ‡Ļ"; + inline constexpr const char flag_um[] = "đŸ‡ē🇲"; + inline constexpr const char united_nations[] = "đŸ‡ēđŸ‡ŗ"; + inline constexpr const char green_apple[] = "🍏"; + inline constexpr const char apple[] = "🍎"; + inline constexpr const char pear[] = "🍐"; + inline constexpr const char tangerine[] = "🍊"; + inline constexpr const char lemon[] = "🍋"; + inline constexpr const char banana[] = "🍌"; + inline constexpr const char watermelon[] = "🍉"; + inline constexpr const char grapes[] = "🍇"; + inline constexpr const char blueberries[] = "đŸĢ"; + inline constexpr const char strawberry[] = "🍓"; + inline constexpr const char melon[] = "🍈"; + inline constexpr const char cherries[] = "🍒"; + inline constexpr const char peach[] = "🍑"; + inline constexpr const char mango[] = "đŸĨ­"; + inline constexpr const char pineapple[] = "🍍"; + inline constexpr const char coconut[] = "đŸĨĨ"; + inline constexpr const char kiwi[] = "đŸĨ"; + inline constexpr const char kiwifruit[] = "đŸĨ"; + inline constexpr const char tomato[] = "🍅"; + inline constexpr const char eggplant[] = "🍆"; + inline constexpr const char avocado[] = "đŸĨ‘"; + inline constexpr const char olive[] = "đŸĢ’"; + inline constexpr const char broccoli[] = "đŸĨĻ"; + inline constexpr const char leafy_green[] = "đŸĨŦ"; + inline constexpr const char bell_pepper[] = "đŸĢ‘"; + inline constexpr const char cucumber[] = "đŸĨ’"; + inline constexpr const char hot_pepper[] = "đŸŒļī¸"; + inline constexpr const char corn[] = "đŸŒŊ"; + inline constexpr const char carrot[] = "đŸĨ•"; + inline constexpr const char garlic[] = "🧄"; + inline constexpr const char onion[] = "🧅"; + inline constexpr const char potato[] = "đŸĨ”"; + inline constexpr const char sweet_potato[] = "🍠"; + inline constexpr const char croissant[] = "đŸĨ"; + inline constexpr const char bagel[] = "đŸĨ¯"; + inline constexpr const char bread[] = "🍞"; + inline constexpr const char french_bread[] = "đŸĨ–"; + inline constexpr const char baguette_bread[] = "đŸĨ–"; + inline constexpr const char flatbread[] = "đŸĢ“"; + inline constexpr const char pretzel[] = "đŸĨ¨"; + inline constexpr const char cheese[] = "🧀"; + inline constexpr const char cheese_wedge[] = "🧀"; + inline constexpr const char egg[] = "đŸĨš"; + inline constexpr const char cooking[] = "đŸŗ"; + inline constexpr const char butter[] = "🧈"; + inline constexpr const char pancakes[] = "đŸĨž"; + inline constexpr const char waffle[] = "🧇"; + inline constexpr const char bacon[] = "đŸĨ“"; + inline constexpr const char cut_of_meat[] = "đŸĨŠ"; + inline constexpr const char poultry_leg[] = "🍗"; + inline constexpr const char meat_on_bone[] = "🍖"; + inline constexpr const char bone[] = "đŸĻ´"; + inline constexpr const char hotdog[] = "🌭"; + inline constexpr const char hot_dog[] = "🌭"; + inline constexpr const char hamburger[] = "🍔"; + inline constexpr const char fries[] = "🍟"; + inline constexpr const char pizza[] = "🍕"; + inline constexpr const char sandwich[] = "đŸĨĒ"; + inline constexpr const char stuffed_flatbread[] = "đŸĨ™"; + inline constexpr const char stuffed_pita[] = "đŸĨ™"; + inline constexpr const char falafel[] = "🧆"; + inline constexpr const char taco[] = "🌮"; + inline constexpr const char burrito[] = "đŸŒ¯"; + inline constexpr const char tamale[] = "đŸĢ”"; + inline constexpr const char salad[] = "đŸĨ—"; + inline constexpr const char green_salad[] = "đŸĨ—"; + inline constexpr const char shallow_pan_of_food[] = "đŸĨ˜"; + inline constexpr const char paella[] = "đŸĨ˜"; + inline constexpr const char fondue[] = "đŸĢ•"; + inline constexpr const char canned_food[] = "đŸĨĢ"; + inline constexpr const char jar[] = "đŸĢ™"; + inline constexpr const char spaghetti[] = "🍝"; + inline constexpr const char ramen[] = "🍜"; + inline constexpr const char stew[] = "🍲"; + inline constexpr const char curry[] = "🍛"; + inline constexpr const char sushi[] = "đŸŖ"; + inline constexpr const char bento[] = "🍱"; + inline constexpr const char dumpling[] = "đŸĨŸ"; + inline constexpr const char oyster[] = "đŸĻĒ"; + inline constexpr const char fried_shrimp[] = "🍤"; + inline constexpr const char rice_ball[] = "🍙"; + inline constexpr const char rice[] = "🍚"; + inline constexpr const char rice_cracker[] = "🍘"; + inline constexpr const char fish_cake[] = "đŸĨ"; + inline constexpr const char fortune_cookie[] = "đŸĨ "; + inline constexpr const char moon_cake[] = "đŸĨŽ"; + inline constexpr const char oden[] = "đŸĸ"; + inline constexpr const char dango[] = "🍡"; + inline constexpr const char shaved_ice[] = "🍧"; + inline constexpr const char ice_cream[] = "🍨"; + inline constexpr const char icecream[] = "đŸĻ"; + inline constexpr const char pie[] = "đŸĨ§"; + inline constexpr const char cupcake[] = "🧁"; + inline constexpr const char cake[] = "🍰"; + inline constexpr const char birthday[] = "🎂"; + inline constexpr const char custard[] = "🍮"; + inline constexpr const char pudding[] = "🍮"; + inline constexpr const char flan[] = "🍮"; + inline constexpr const char lollipop[] = "🍭"; + inline constexpr const char candy[] = "đŸŦ"; + inline constexpr const char chocolate_bar[] = "đŸĢ"; + inline constexpr const char popcorn[] = "đŸŋ"; + inline constexpr const char doughnut[] = "🍩"; + inline constexpr const char cookie[] = "đŸĒ"; + inline constexpr const char chestnut[] = "🌰"; + inline constexpr const char peanuts[] = "đŸĨœ"; + inline constexpr const char shelled_peanut[] = "đŸĨœ"; + inline constexpr const char beans[] = "đŸĢ˜"; + inline constexpr const char honey_pot[] = "đŸ¯"; + inline constexpr const char milk[] = "đŸĨ›"; + inline constexpr const char glass_of_milk[] = "đŸĨ›"; + inline constexpr const char pouring_liquid[] = "đŸĢ—"; + inline constexpr const char baby_bottle[] = "đŸŧ"; + inline constexpr const char teapot[] = "đŸĢ–"; + inline constexpr const char coffee[] = "☕"; + inline constexpr const char tea[] = "đŸĩ"; + inline constexpr const char mate[] = "🧉"; + inline constexpr const char beverage_box[] = "🧃"; + inline constexpr const char cup_with_straw[] = "đŸĨ¤"; + inline constexpr const char bubble_tea[] = "🧋"; + inline constexpr const char sake[] = "đŸļ"; + inline constexpr const char beer[] = "đŸē"; + inline constexpr const char beers[] = "đŸģ"; + inline constexpr const char champagne_glass[] = "đŸĨ‚"; + inline constexpr const char clinking_glass[] = "đŸĨ‚"; + inline constexpr const char wine_glass[] = "🍷"; + inline constexpr const char tumbler_glass[] = "đŸĨƒ"; + inline constexpr const char whisky[] = "đŸĨƒ"; + inline constexpr const char cocktail[] = "🍸"; + inline constexpr const char tropical_drink[] = "🍹"; + inline constexpr const char champagne[] = "🍾"; + inline constexpr const char bottle_with_popping_cork[] = "🍾"; + inline constexpr const char ice_cube[] = "🧊"; + inline constexpr const char spoon[] = "đŸĨ„"; + inline constexpr const char fork_and_knife[] = "🍴"; + inline constexpr const char fork_knife_plate[] = "đŸŊī¸"; + inline constexpr const char fork_and_knife_with_plate[] = "đŸŊī¸"; + inline constexpr const char bowl_with_spoon[] = "đŸĨŖ"; + inline constexpr const char takeout_box[] = "đŸĨĄ"; + inline constexpr const char chopsticks[] = "đŸĨĸ"; + inline constexpr const char salt[] = "🧂"; + inline constexpr const char dog[] = "đŸļ"; + inline constexpr const char cat[] = "🐱"; + inline constexpr const char mouse[] = "🐭"; + inline constexpr const char hamster[] = "🐹"; + inline constexpr const char rabbit[] = "🐰"; + inline constexpr const char fox[] = "đŸĻŠ"; + inline constexpr const char fox_face[] = "đŸĻŠ"; + inline constexpr const char bear[] = "đŸģ"; + inline constexpr const char panda_face[] = "đŸŧ"; + inline constexpr const char polar_bear[] = "đŸģ‍❄ī¸"; + inline constexpr const char koala[] = "🐨"; + inline constexpr const char tiger[] = "đŸ¯"; + inline constexpr const char lion_face[] = "đŸĻ"; + inline constexpr const char lion[] = "đŸĻ"; + inline constexpr const char cow[] = "🐮"; + inline constexpr const char pig[] = "🐷"; + inline constexpr const char pig_nose[] = "đŸŊ"; + inline constexpr const char frog[] = "🐸"; + inline constexpr const char monkey_face[] = "đŸĩ"; + inline constexpr const char see_no_evil[] = "🙈"; + inline constexpr const char hear_no_evil[] = "🙉"; + inline constexpr const char speak_no_evil[] = "🙊"; + inline constexpr const char monkey[] = "🐒"; + inline constexpr const char chicken[] = "🐔"; + inline constexpr const char penguin[] = "🐧"; + inline constexpr const char bird[] = "đŸĻ"; + inline constexpr const char baby_chick[] = "🐤"; + inline constexpr const char hatching_chick[] = "đŸŖ"; + inline constexpr const char hatched_chick[] = "đŸĨ"; + inline constexpr const char duck[] = "đŸĻ†"; + inline constexpr const char eagle[] = "đŸĻ…"; + inline constexpr const char owl[] = "đŸĻ‰"; + inline constexpr const char bat[] = "đŸĻ‡"; + inline constexpr const char wolf[] = "đŸē"; + inline constexpr const char boar[] = "🐗"; + inline constexpr const char horse[] = "🐴"; + inline constexpr const char unicorn[] = "đŸĻ„"; + inline constexpr const char unicorn_face[] = "đŸĻ„"; + inline constexpr const char bee[] = "🐝"; + inline constexpr const char bug[] = "🐛"; + inline constexpr const char butterfly[] = "đŸĻ‹"; + inline constexpr const char snail[] = "🐌"; + inline constexpr const char worm[] = "đŸĒą"; + inline constexpr const char lady_beetle[] = "🐞"; + inline constexpr const char ant[] = "🐜"; + inline constexpr const char fly[] = "đŸĒ°"; + inline constexpr const char mosquito[] = "đŸĻŸ"; + inline constexpr const char cockroach[] = "đŸĒŗ"; + inline constexpr const char beetle[] = "đŸĒ˛"; + inline constexpr const char cricket[] = "đŸĻ—"; + inline constexpr const char spider[] = "🕷ī¸"; + inline constexpr const char spider_web[] = "🕸ī¸"; + inline constexpr const char scorpion[] = "đŸĻ‚"; + inline constexpr const char turtle[] = "đŸĸ"; + inline constexpr const char snake[] = "🐍"; + inline constexpr const char lizard[] = "đŸĻŽ"; + inline constexpr const char t_rex[] = "đŸĻ–"; + inline constexpr const char sauropod[] = "đŸĻ•"; + inline constexpr const char octopus[] = "🐙"; + inline constexpr const char squid[] = "đŸĻ‘"; + inline constexpr const char shrimp[] = "đŸĻ"; + inline constexpr const char lobster[] = "đŸĻž"; + inline constexpr const char crab[] = "đŸĻ€"; + inline constexpr const char blowfish[] = "🐡"; + inline constexpr const char tropical_fish[] = "🐠"; + inline constexpr const char fish[] = "🐟"; + inline constexpr const char seal[] = "đŸĻ­"; + inline constexpr const char dolphin[] = "đŸŦ"; + inline constexpr const char whale[] = "đŸŗ"; + inline constexpr const char whale2[] = "🐋"; + inline constexpr const char shark[] = "đŸĻˆ"; + inline constexpr const char crocodile[] = "🐊"; + inline constexpr const char tiger2[] = "🐅"; + inline constexpr const char leopard[] = "🐆"; + inline constexpr const char zebra[] = "đŸĻ“"; + inline constexpr const char gorilla[] = "đŸĻ"; + inline constexpr const char orangutan[] = "đŸĻ§"; + inline constexpr const char elephant[] = "🐘"; + inline constexpr const char mammoth[] = "đŸĻŖ"; + inline constexpr const char bison[] = "đŸĻŦ"; + inline constexpr const char hippopotamus[] = "đŸĻ›"; + inline constexpr const char rhino[] = "đŸĻ"; + inline constexpr const char rhinoceros[] = "đŸĻ"; + inline constexpr const char dromedary_camel[] = "đŸĒ"; + inline constexpr const char camel[] = "đŸĢ"; + inline constexpr const char giraffe[] = "đŸĻ’"; + inline constexpr const char kangaroo[] = "đŸĻ˜"; + inline constexpr const char water_buffalo[] = "🐃"; + inline constexpr const char ox[] = "🐂"; + inline constexpr const char cow2[] = "🐄"; + inline constexpr const char racehorse[] = "🐎"; + inline constexpr const char pig2[] = "🐖"; + inline constexpr const char ram[] = "🐏"; + inline constexpr const char sheep[] = "🐑"; + inline constexpr const char llama[] = "đŸĻ™"; + inline constexpr const char goat[] = "🐐"; + inline constexpr const char deer[] = "đŸĻŒ"; + inline constexpr const char dog2[] = "🐕"; + inline constexpr const char poodle[] = "🐩"; + inline constexpr const char guide_dog[] = "đŸĻŽ"; + inline constexpr const char service_dog[] = "🐕‍đŸĻē"; + inline constexpr const char cat2[] = "🐈"; + inline constexpr const char black_cat[] = "🐈‍âŦ›"; + inline constexpr const char feather[] = "đŸĒļ"; + inline constexpr const char rooster[] = "🐓"; + inline constexpr const char turkey[] = "đŸĻƒ"; + inline constexpr const char dodo[] = "đŸĻ¤"; + inline constexpr const char peacock[] = "đŸĻš"; + inline constexpr const char parrot[] = "đŸĻœ"; + inline constexpr const char swan[] = "đŸĻĸ"; + inline constexpr const char flamingo[] = "đŸĻŠ"; + inline constexpr const char dove[] = "🕊ī¸"; + inline constexpr const char dove_of_peace[] = "🕊ī¸"; + inline constexpr const char rabbit2[] = "🐇"; + inline constexpr const char raccoon[] = "đŸĻ"; + inline constexpr const char skunk[] = "đŸĻ¨"; + inline constexpr const char badger[] = "đŸĻĄ"; + inline constexpr const char beaver[] = "đŸĻĢ"; + inline constexpr const char otter[] = "đŸĻĻ"; + inline constexpr const char sloth[] = "đŸĻĨ"; + inline constexpr const char mouse2[] = "🐁"; + inline constexpr const char rat[] = "🐀"; + inline constexpr const char chipmunk[] = "đŸŋī¸"; + inline constexpr const char hedgehog[] = "đŸĻ”"; + inline constexpr const char feet[] = "🐾"; + inline constexpr const char paw_prints[] = "🐾"; + inline constexpr const char dragon[] = "🐉"; + inline constexpr const char dragon_face[] = "🐲"; + inline constexpr const char cactus[] = "đŸŒĩ"; + inline constexpr const char christmas_tree[] = "🎄"; + inline constexpr const char evergreen_tree[] = "🌲"; + inline constexpr const char deciduous_tree[] = "đŸŒŗ"; + inline constexpr const char palm_tree[] = "🌴"; + inline constexpr const char seedling[] = "🌱"; + inline constexpr const char herb[] = "đŸŒŋ"; + inline constexpr const char shamrock[] = "☘ī¸"; + inline constexpr const char four_leaf_clover[] = "🍀"; + inline constexpr const char bamboo[] = "🎍"; + inline constexpr const char tanabata_tree[] = "🎋"; + inline constexpr const char leaves[] = "🍃"; + inline constexpr const char fallen_leaf[] = "🍂"; + inline constexpr const char maple_leaf[] = "🍁"; + inline constexpr const char empty_nest[] = "đŸĒš"; + inline constexpr const char nest_with_eggs[] = "đŸĒē"; + inline constexpr const char mushroom[] = "🍄"; + inline constexpr const char shell[] = "🐚"; + inline constexpr const char coral[] = "đŸĒ¸"; + inline constexpr const char rock[] = "đŸĒ¨"; + inline constexpr const char wood[] = "đŸĒĩ"; + inline constexpr const char ear_of_rice[] = "🌾"; + inline constexpr const char potted_plant[] = "đŸĒ´"; + inline constexpr const char bouquet[] = "💐"; + inline constexpr const char tulip[] = "🌷"; + inline constexpr const char rose[] = "🌹"; + inline constexpr const char wilted_rose[] = "đŸĨ€"; + inline constexpr const char wilted_flower[] = "đŸĨ€"; + inline constexpr const char lotus[] = "đŸĒˇ"; + inline constexpr const char hibiscus[] = "đŸŒē"; + inline constexpr const char cherry_blossom[] = "🌸"; + inline constexpr const char blossom[] = "đŸŒŧ"; + inline constexpr const char sunflower[] = "đŸŒģ"; + inline constexpr const char sun_with_face[] = "🌞"; + inline constexpr const char full_moon_with_face[] = "🌝"; + inline constexpr const char first_quarter_moon_with_face[] = "🌛"; + inline constexpr const char last_quarter_moon_with_face[] = "🌜"; + inline constexpr const char new_moon_with_face[] = "🌚"; + inline constexpr const char full_moon[] = "🌕"; + inline constexpr const char waning_gibbous_moon[] = "🌖"; + inline constexpr const char last_quarter_moon[] = "🌗"; + inline constexpr const char waning_crescent_moon[] = "🌘"; + inline constexpr const char new_moon[] = "🌑"; + inline constexpr const char waxing_crescent_moon[] = "🌒"; + inline constexpr const char first_quarter_moon[] = "🌓"; + inline constexpr const char waxing_gibbous_moon[] = "🌔"; + inline constexpr const char crescent_moon[] = "🌙"; + inline constexpr const char earth_americas[] = "🌎"; + inline constexpr const char earth_africa[] = "🌍"; + inline constexpr const char earth_asia[] = "🌏"; + inline constexpr const char ringed_planet[] = "đŸĒ"; + inline constexpr const char dizzy[] = "đŸ’Ģ"; + inline constexpr const char star[] = "⭐"; + inline constexpr const char star2[] = "🌟"; + inline constexpr const char sparkles[] = "✨"; + inline constexpr const char zap[] = "⚡"; + inline constexpr const char comet[] = "☄ī¸"; + inline constexpr const char boom[] = "đŸ’Ĩ"; + inline constexpr const char fire[] = "đŸ”Ĩ"; + inline constexpr const char flame[] = "đŸ”Ĩ"; + inline constexpr const char cloud_tornado[] = "đŸŒĒī¸"; + inline constexpr const char cloud_with_tornado[] = "đŸŒĒī¸"; + inline constexpr const char rainbow[] = "🌈"; + inline constexpr const char sunny[] = "☀ī¸"; + inline constexpr const char white_sun_small_cloud[] = "🌤ī¸"; + inline constexpr const char white_sun_with_small_cloud[] = "🌤ī¸"; + inline constexpr const char partly_sunny[] = "⛅"; + inline constexpr const char white_sun_cloud[] = "đŸŒĨī¸"; + inline constexpr const char white_sun_behind_cloud[] = "đŸŒĨī¸"; + inline constexpr const char cloud[] = "☁ī¸"; + inline constexpr const char white_sun_rain_cloud[] = "đŸŒĻī¸"; + inline constexpr const char white_sun_behind_cloud_with_rain[] = "đŸŒĻī¸"; + inline constexpr const char cloud_rain[] = "🌧ī¸"; + inline constexpr const char cloud_with_rain[] = "🌧ī¸"; + inline constexpr const char thunder_cloud_rain[] = "⛈ī¸"; + inline constexpr const char thunder_cloud_and_rain[] = "⛈ī¸"; + inline constexpr const char cloud_lightning[] = "🌩ī¸"; + inline constexpr const char cloud_with_lightning[] = "🌩ī¸"; + inline constexpr const char cloud_snow[] = "🌨ī¸"; + inline constexpr const char cloud_with_snow[] = "🌨ī¸"; + inline constexpr const char snowflake[] = "❄ī¸"; + inline constexpr const char snowman2[] = "☃ī¸"; + inline constexpr const char snowman[] = "⛄"; + inline constexpr const char wind_blowing_face[] = "đŸŒŦī¸"; + inline constexpr const char dash[] = "💨"; + inline constexpr const char droplet[] = "💧"; + inline constexpr const char sweat_drops[] = "đŸ’Ļ"; + inline constexpr const char bubbles[] = "đŸĢ§"; + inline constexpr const char umbrella[] = "☔"; + inline constexpr const char umbrella2[] = "☂ī¸"; + inline constexpr const char ocean[] = "🌊"; + inline constexpr const char fog[] = "đŸŒĢī¸"; + inline constexpr const char watch[] = "⌚"; + inline constexpr const char mobile_phone[] = "📱"; + inline constexpr const char iphone[] = "📱"; + inline constexpr const char calling[] = "📲"; + inline constexpr const char computer[] = "đŸ’ģ"; + inline constexpr const char keyboard[] = "⌨ī¸"; + inline constexpr const char desktop[] = "đŸ–Ĩī¸"; + inline constexpr const char desktop_computer[] = "đŸ–Ĩī¸"; + inline constexpr const char printer[] = "🖨ī¸"; + inline constexpr const char mouse_three_button[] = "🖱ī¸"; + inline constexpr const char three_button_mouse[] = "🖱ī¸"; + inline constexpr const char trackball[] = "🖲ī¸"; + inline constexpr const char joystick[] = "🕹ī¸"; + inline constexpr const char compression[] = "🗜ī¸"; + inline constexpr const char minidisc[] = "đŸ’Ŋ"; + inline constexpr const char floppy_disk[] = "💾"; + inline constexpr const char cd[] = "đŸ’ŋ"; + inline constexpr const char dvd[] = "📀"; + inline constexpr const char vhs[] = "đŸ“ŧ"; + inline constexpr const char camera[] = "📷"; + inline constexpr const char camera_with_flash[] = "📸"; + inline constexpr const char video_camera[] = "📹"; + inline constexpr const char movie_camera[] = "đŸŽĨ"; + inline constexpr const char projector[] = "đŸ“Ŋī¸"; + inline constexpr const char film_projector[] = "đŸ“Ŋī¸"; + inline constexpr const char film_frames[] = "🎞ī¸"; + inline constexpr const char telephone_receiver[] = "📞"; + inline constexpr const char telephone[] = "☎ī¸"; + inline constexpr const char pager[] = "📟"; + inline constexpr const char fax[] = "📠"; + inline constexpr const char tv[] = "đŸ“ē"; + inline constexpr const char radio[] = "đŸ“ģ"; + inline constexpr const char microphone2[] = "🎙ī¸"; + inline constexpr const char studio_microphone[] = "🎙ī¸"; + inline constexpr const char level_slider[] = "🎚ī¸"; + inline constexpr const char control_knobs[] = "🎛ī¸"; + inline constexpr const char compass[] = "🧭"; + inline constexpr const char stopwatch[] = "⏱ī¸"; + inline constexpr const char timer[] = "⏲ī¸"; + inline constexpr const char timer_clock[] = "⏲ī¸"; + inline constexpr const char alarm_clock[] = "⏰"; + inline constexpr const char clock[] = "🕰ī¸"; + inline constexpr const char mantlepiece_clock[] = "🕰ī¸"; + inline constexpr const char hourglass[] = "⌛"; + inline constexpr const char hourglass_flowing_sand[] = "âŗ"; + inline constexpr const char satellite[] = "📡"; + inline constexpr const char battery[] = "🔋"; + inline constexpr const char low_battery[] = "đŸĒĢ"; + inline constexpr const char electric_plug[] = "🔌"; + inline constexpr const char bulb[] = "💡"; + inline constexpr const char flashlight[] = "đŸ”Ļ"; + inline constexpr const char candle[] = "đŸ•¯ī¸"; + inline constexpr const char diya_lamp[] = "đŸĒ”"; + inline constexpr const char fire_extinguisher[] = "đŸ§¯"; + inline constexpr const char oil[] = "đŸ›ĸī¸"; + inline constexpr const char oil_drum[] = "đŸ›ĸī¸"; + inline constexpr const char money_with_wings[] = "💸"; + inline constexpr const char dollar[] = "đŸ’ĩ"; + inline constexpr const char yen[] = "💴"; + inline constexpr const char euro[] = "đŸ’ļ"; + inline constexpr const char pound[] = "💷"; + inline constexpr const char coin[] = "đŸĒ™"; + inline constexpr const char moneybag[] = "💰"; + inline constexpr const char credit_card[] = "đŸ’ŗ"; + inline constexpr const char identification_card[] = "đŸĒĒ"; + inline constexpr const char gem[] = "💎"; + inline constexpr const char scales[] = "⚖ī¸"; + inline constexpr const char ladder[] = "đŸĒœ"; + inline constexpr const char toolbox[] = "🧰"; + inline constexpr const char screwdriver[] = "đŸĒ›"; + inline constexpr const char wrench[] = "🔧"; + inline constexpr const char hammer[] = "🔨"; + inline constexpr const char hammer_pick[] = "⚒ī¸"; + inline constexpr const char hammer_and_pick[] = "⚒ī¸"; + inline constexpr const char tools[] = "🛠ī¸"; + inline constexpr const char hammer_and_wrench[] = "🛠ī¸"; + inline constexpr const char pick[] = "⛏ī¸"; + inline constexpr const char carpentry_saw[] = "đŸĒš"; + inline constexpr const char nut_and_bolt[] = "🔩"; + inline constexpr const char gear[] = "⚙ī¸"; + inline constexpr const char mouse_trap[] = "đŸĒ¤"; + inline constexpr const char bricks[] = "🧱"; + inline constexpr const char chains[] = "⛓ī¸"; + inline constexpr const char magnet[] = "🧲"; + inline constexpr const char gun[] = "đŸ”Ģ"; + inline constexpr const char bomb[] = "đŸ’Ŗ"; + inline constexpr const char firecracker[] = "🧨"; + inline constexpr const char axe[] = "đŸĒ“"; + inline constexpr const char knife[] = "đŸ”Ē"; + inline constexpr const char dagger[] = "🗡ī¸"; + inline constexpr const char dagger_knife[] = "🗡ī¸"; + inline constexpr const char crossed_swords[] = "⚔ī¸"; + inline constexpr const char shield[] = "🛡ī¸"; + inline constexpr const char smoking[] = "đŸšŦ"; + inline constexpr const char coffin[] = "⚰ī¸"; + inline constexpr const char headstone[] = "đŸĒĻ"; + inline constexpr const char urn[] = "⚱ī¸"; + inline constexpr const char funeral_urn[] = "⚱ī¸"; + inline constexpr const char amphora[] = "đŸē"; + inline constexpr const char crystal_ball[] = "🔮"; + inline constexpr const char prayer_beads[] = "đŸ“ŋ"; + inline constexpr const char nazar_amulet[] = "đŸ§ŋ"; + inline constexpr const char hamsa[] = "đŸĒŦ"; + inline constexpr const char barber[] = "💈"; + inline constexpr const char alembic[] = "⚗ī¸"; + inline constexpr const char telescope[] = "🔭"; + inline constexpr const char microscope[] = "đŸ”Ŧ"; + inline constexpr const char hole[] = "đŸ•ŗī¸"; + inline constexpr const char x_ray[] = "đŸŠģ"; + inline constexpr const char adhesive_bandage[] = "🩹"; + inline constexpr const char stethoscope[] = "đŸŠē"; + inline constexpr const char pill[] = "💊"; + inline constexpr const char syringe[] = "💉"; + inline constexpr const char drop_of_blood[] = "🩸"; + inline constexpr const char dna[] = "đŸ§Ŧ"; + inline constexpr const char microbe[] = "đŸĻ "; + inline constexpr const char petri_dish[] = "đŸ§Ģ"; + inline constexpr const char test_tube[] = "đŸ§Ē"; + inline constexpr const char thermometer[] = "🌡ī¸"; + inline constexpr const char broom[] = "🧹"; + inline constexpr const char plunger[] = "đŸĒ "; + inline constexpr const char basket[] = "đŸ§ē"; + inline constexpr const char roll_of_paper[] = "đŸ§ģ"; + inline constexpr const char toilet[] = "đŸšŊ"; + inline constexpr const char potable_water[] = "🚰"; + inline constexpr const char shower[] = "đŸšŋ"; + inline constexpr const char bathtub[] = "🛁"; + inline constexpr const char bath[] = "🛀"; + inline constexpr const char bath_tone1[] = "🛀đŸģ"; + inline constexpr const char bath_tone2[] = "🛀đŸŧ"; + inline constexpr const char bath_tone3[] = "🛀đŸŊ"; + inline constexpr const char bath_tone4[] = "🛀🏾"; + inline constexpr const char bath_tone5[] = "🛀đŸŋ"; + inline constexpr const char soap[] = "đŸ§ŧ"; + inline constexpr const char toothbrush[] = "đŸĒĨ"; + inline constexpr const char razor[] = "đŸĒ’"; + inline constexpr const char sponge[] = "đŸ§Ŋ"; + inline constexpr const char bucket[] = "đŸĒŖ"; + inline constexpr const char squeeze_bottle[] = "🧴"; + inline constexpr const char bellhop[] = "🛎ī¸"; + inline constexpr const char bellhop_bell[] = "🛎ī¸"; + inline constexpr const char key[] = "🔑"; + inline constexpr const char key2[] = "🗝ī¸"; + inline constexpr const char old_key[] = "🗝ī¸"; + inline constexpr const char door[] = "đŸšĒ"; + inline constexpr const char chair[] = "đŸĒ‘"; + inline constexpr const char couch[] = "🛋ī¸"; + inline constexpr const char couch_and_lamp[] = "🛋ī¸"; + inline constexpr const char bed[] = "🛏ī¸"; + inline constexpr const char sleeping_accommodation[] = "🛌"; + inline constexpr const char person_in_bed_tone1[] = "🛌đŸģ"; + inline constexpr const char person_in_bed_light_skin_tone[] = "🛌đŸģ"; + inline constexpr const char person_in_bed_tone2[] = "🛌đŸŧ"; + inline constexpr const char person_in_bed_medium_light_skin_tone[] = "🛌đŸŧ"; + inline constexpr const char person_in_bed_tone3[] = "🛌đŸŊ"; + inline constexpr const char person_in_bed_medium_skin_tone[] = "🛌đŸŊ"; + inline constexpr const char person_in_bed_tone4[] = "🛌🏾"; + inline constexpr const char person_in_bed_medium_dark_skin_tone[] = "🛌🏾"; + inline constexpr const char person_in_bed_tone5[] = "🛌đŸŋ"; + inline constexpr const char person_in_bed_dark_skin_tone[] = "🛌đŸŋ"; + inline constexpr const char teddy_bear[] = "🧸"; + inline constexpr const char nesting_dolls[] = "đŸĒ†"; + inline constexpr const char frame_photo[] = "đŸ–ŧī¸"; + inline constexpr const char frame_with_picture[] = "đŸ–ŧī¸"; + inline constexpr const char mirror[] = "đŸĒž"; + inline constexpr const char window[] = "đŸĒŸ"; + inline constexpr const char shopping_bags[] = "🛍ī¸"; + inline constexpr const char shopping_cart[] = "🛒"; + inline constexpr const char shopping_trolley[] = "🛒"; + inline constexpr const char gift[] = "🎁"; + inline constexpr const char balloon[] = "🎈"; + inline constexpr const char flags[] = "🎏"; + inline constexpr const char ribbon[] = "🎀"; + inline constexpr const char magic_wand[] = "đŸĒ„"; + inline constexpr const char pinata[] = "đŸĒ…"; + inline constexpr const char confetti_ball[] = "🎊"; + inline constexpr const char tada[] = "🎉"; + inline constexpr const char dolls[] = "🎎"; + inline constexpr const char izakaya_lantern[] = "🏮"; + inline constexpr const char wind_chime[] = "🎐"; + inline constexpr const char mirror_ball[] = "đŸĒŠ"; + inline constexpr const char red_envelope[] = "🧧"; + inline constexpr const char envelope[] = "✉ī¸"; + inline constexpr const char envelope_with_arrow[] = "📩"; + inline constexpr const char incoming_envelope[] = "📨"; + inline constexpr const char e_mail[] = "📧"; + inline constexpr const char email[] = "📧"; + inline constexpr const char love_letter[] = "💌"; + inline constexpr const char inbox_tray[] = "đŸ“Ĩ"; + inline constexpr const char outbox_tray[] = "📤"; + inline constexpr const char package[] = "đŸ“Ļ"; + inline constexpr const char label[] = "🏷ī¸"; + inline constexpr const char placard[] = "đŸĒ§"; + inline constexpr const char mailbox_closed[] = "đŸ“Ē"; + inline constexpr const char mailbox[] = "đŸ“Ģ"; + inline constexpr const char mailbox_with_mail[] = "đŸ“Ŧ"; + inline constexpr const char mailbox_with_no_mail[] = "📭"; + inline constexpr const char postbox[] = "📮"; + inline constexpr const char postal_horn[] = "đŸ“¯"; + inline constexpr const char scroll[] = "📜"; + inline constexpr const char page_with_curl[] = "📃"; + inline constexpr const char page_facing_up[] = "📄"; + inline constexpr const char bookmark_tabs[] = "📑"; + inline constexpr const char receipt[] = "🧾"; + inline constexpr const char bar_chart[] = "📊"; + inline constexpr const char chart_with_upwards_trend[] = "📈"; + inline constexpr const char chart_with_downwards_trend[] = "📉"; + inline constexpr const char notepad_spiral[] = "🗒ī¸"; + inline constexpr const char spiral_note_pad[] = "🗒ī¸"; + inline constexpr const char calendar_spiral[] = "🗓ī¸"; + inline constexpr const char spiral_calendar_pad[] = "🗓ī¸"; + inline constexpr const char calendar[] = "📆"; + inline constexpr const char date[] = "📅"; + inline constexpr const char wastebasket[] = "🗑ī¸"; + inline constexpr const char card_index[] = "📇"; + inline constexpr const char card_box[] = "🗃ī¸"; + inline constexpr const char card_file_box[] = "🗃ī¸"; + inline constexpr const char ballot_box[] = "đŸ—ŗī¸"; + inline constexpr const char ballot_box_with_ballot[] = "đŸ—ŗī¸"; + inline constexpr const char file_cabinet[] = "🗄ī¸"; + inline constexpr const char clipboard[] = "📋"; + inline constexpr const char file_folder[] = "📁"; + inline constexpr const char open_file_folder[] = "📂"; + inline constexpr const char dividers[] = "🗂ī¸"; + inline constexpr const char card_index_dividers[] = "🗂ī¸"; + inline constexpr const char newspaper2[] = "🗞ī¸"; + inline constexpr const char rolled_up_newspaper[] = "🗞ī¸"; + inline constexpr const char newspaper[] = "📰"; + inline constexpr const char notebook[] = "📓"; + inline constexpr const char notebook_with_decorative_cover[] = "📔"; + inline constexpr const char ledger[] = "📒"; + inline constexpr const char closed_book[] = "📕"; + inline constexpr const char green_book[] = "📗"; + inline constexpr const char blue_book[] = "📘"; + inline constexpr const char orange_book[] = "📙"; + inline constexpr const char books[] = "📚"; + inline constexpr const char book[] = "📖"; + inline constexpr const char bookmark[] = "🔖"; + inline constexpr const char safety_pin[] = "🧷"; + inline constexpr const char link[] = "🔗"; + inline constexpr const char paperclip[] = "📎"; + inline constexpr const char paperclips[] = "🖇ī¸"; + inline constexpr const char linked_paperclips[] = "🖇ī¸"; + inline constexpr const char triangular_ruler[] = "📐"; + inline constexpr const char straight_ruler[] = "📏"; + inline constexpr const char abacus[] = "🧮"; + inline constexpr const char pushpin[] = "📌"; + inline constexpr const char round_pushpin[] = "📍"; + inline constexpr const char scissors[] = "✂ī¸"; + inline constexpr const char pen_ballpoint[] = "🖊ī¸"; + inline constexpr const char lower_left_ballpoint_pen[] = "🖊ī¸"; + inline constexpr const char pen_fountain[] = "🖋ī¸"; + inline constexpr const char lower_left_fountain_pen[] = "🖋ī¸"; + inline constexpr const char black_nib[] = "✒ī¸"; + inline constexpr const char paintbrush[] = "🖌ī¸"; + inline constexpr const char lower_left_paintbrush[] = "🖌ī¸"; + inline constexpr const char crayon[] = "🖍ī¸"; + inline constexpr const char lower_left_crayon[] = "🖍ī¸"; + inline constexpr const char pencil[] = "📝"; + inline constexpr const char memo[] = "📝"; + inline constexpr const char pencil2[] = "✏ī¸"; + inline constexpr const char mag[] = "🔍"; + inline constexpr const char mag_right[] = "🔎"; + inline constexpr const char lock_with_ink_pen[] = "🔏"; + inline constexpr const char closed_lock_with_key[] = "🔐"; + inline constexpr const char lock[] = "🔒"; + inline constexpr const char unlock[] = "🔓"; + inline constexpr const char grinning[] = "😀"; + inline constexpr const char smiley[] = "😃"; + inline constexpr const char smile[] = "😄"; + inline constexpr const char grin[] = "😁"; + inline constexpr const char laughing[] = "😆"; + inline constexpr const char satisfied[] = "😆"; + inline constexpr const char face_holding_back_tears[] = "đŸĨš"; + inline constexpr const char sweat_smile[] = "😅"; + inline constexpr const char joy[] = "😂"; + inline constexpr const char rofl[] = "đŸ¤Ŗ"; + inline constexpr const char rolling_on_the_floor_laughing[] = "đŸ¤Ŗ"; + inline constexpr const char smiling_face_with_tear[] = "đŸĨ˛"; + inline constexpr const char relaxed[] = "â˜ēī¸"; + inline constexpr const char blush[] = "😊"; + inline constexpr const char innocent[] = "😇"; + inline constexpr const char slight_smile[] = "🙂"; + inline constexpr const char slightly_smiling_face[] = "🙂"; + inline constexpr const char upside_down[] = "🙃"; + inline constexpr const char upside_down_face[] = "🙃"; + inline constexpr const char wink[] = "😉"; + inline constexpr const char relieved[] = "😌"; + inline constexpr const char heart_eyes[] = "😍"; + inline constexpr const char smiling_face_with_3_hearts[] = "đŸĨ°"; + inline constexpr const char kissing_heart[] = "😘"; + inline constexpr const char kissing[] = "😗"; + inline constexpr const char kissing_smiling_eyes[] = "😙"; + inline constexpr const char kissing_closed_eyes[] = "😚"; + inline constexpr const char yum[] = "😋"; + inline constexpr const char stuck_out_tongue[] = "😛"; + inline constexpr const char stuck_out_tongue_closed_eyes[] = "😝"; + inline constexpr const char stuck_out_tongue_winking_eye[] = "😜"; + inline constexpr const char zany_face[] = "đŸ¤Ē"; + inline constexpr const char face_with_raised_eyebrow[] = "🤨"; + inline constexpr const char face_with_monocle[] = "🧐"; + inline constexpr const char nerd[] = "🤓"; + inline constexpr const char nerd_face[] = "🤓"; + inline constexpr const char sunglasses[] = "😎"; + inline constexpr const char disguised_face[] = "đŸĨ¸"; + inline constexpr const char star_struck[] = "🤩"; + inline constexpr const char partying_face[] = "đŸĨŗ"; + inline constexpr const char smirk[] = "😏"; + inline constexpr const char unamused[] = "😒"; + inline constexpr const char disappointed[] = "😞"; + inline constexpr const char pensive[] = "😔"; + inline constexpr const char worried[] = "😟"; + inline constexpr const char confused[] = "😕"; + inline constexpr const char slight_frown[] = "🙁"; + inline constexpr const char slightly_frowning_face[] = "🙁"; + inline constexpr const char frowning2[] = "☚ī¸"; + inline constexpr const char white_frowning_face[] = "☚ī¸"; + inline constexpr const char persevere[] = "đŸ˜Ŗ"; + inline constexpr const char confounded[] = "😖"; + inline constexpr const char tired_face[] = "đŸ˜Ģ"; + inline constexpr const char weary[] = "😩"; + inline constexpr const char pleading_face[] = "đŸĨē"; + inline constexpr const char cry[] = "đŸ˜ĸ"; + inline constexpr const char sob[] = "😭"; + inline constexpr const char triumph[] = "😤"; + inline constexpr const char angry[] = "😠"; + inline constexpr const char rage[] = "😡"; + inline constexpr const char face_with_symbols_over_mouth[] = "đŸ¤Ŧ"; + inline constexpr const char exploding_head[] = "đŸ¤¯"; + inline constexpr const char flushed[] = "đŸ˜ŗ"; + inline constexpr const char hot_face[] = "đŸĨĩ"; + inline constexpr const char cold_face[] = "đŸĨļ"; + inline constexpr const char face_in_clouds[] = "đŸ˜ļ‍đŸŒĢī¸"; + inline constexpr const char scream[] = "😱"; + inline constexpr const char fearful[] = "😨"; + inline constexpr const char cold_sweat[] = "😰"; + inline constexpr const char disappointed_relieved[] = "đŸ˜Ĩ"; + inline constexpr const char sweat[] = "😓"; + inline constexpr const char hugging[] = "🤗"; + inline constexpr const char hugging_face[] = "🤗"; + inline constexpr const char thinking[] = "🤔"; + inline constexpr const char thinking_face[] = "🤔"; + inline constexpr const char face_with_peeking_eye[] = "đŸĢŖ"; + inline constexpr const char face_with_hand_over_mouth[] = "🤭"; + inline constexpr const char face_with_open_eyes_and_hand_over_mouth[] = "đŸĢĸ"; + inline constexpr const char saluting_face[] = "đŸĢĄ"; + inline constexpr const char shushing_face[] = "đŸ¤Ģ"; + inline constexpr const char melting_face[] = "đŸĢ "; + inline constexpr const char lying_face[] = "đŸ¤Ĩ"; + inline constexpr const char liar[] = "đŸ¤Ĩ"; + inline constexpr const char no_mouth[] = "đŸ˜ļ"; + inline constexpr const char dotted_line_face[] = "đŸĢĨ"; + inline constexpr const char neutral_face[] = "😐"; + inline constexpr const char face_with_diagonal_mouth[] = "đŸĢ¤"; + inline constexpr const char expressionless[] = "😑"; + inline constexpr const char grimacing[] = "đŸ˜Ŧ"; + inline constexpr const char rolling_eyes[] = "🙄"; + inline constexpr const char face_with_rolling_eyes[] = "🙄"; + inline constexpr const char hushed[] = "đŸ˜¯"; + inline constexpr const char frowning[] = "đŸ˜Ļ"; + inline constexpr const char anguished[] = "😧"; + inline constexpr const char open_mouth[] = "😮"; + inline constexpr const char astonished[] = "😲"; + inline constexpr const char yawning_face[] = "đŸĨą"; + inline constexpr const char sleeping[] = "😴"; + inline constexpr const char drooling_face[] = "🤤"; + inline constexpr const char drool[] = "🤤"; + inline constexpr const char sleepy[] = "đŸ˜Ē"; + inline constexpr const char face_exhaling[] = "😮‍💨"; + inline constexpr const char dizzy_face[] = "đŸ˜ĩ"; + inline constexpr const char face_with_spiral_eyes[] = "đŸ˜ĩ‍đŸ’Ģ"; + inline constexpr const char zipper_mouth[] = "🤐"; + inline constexpr const char zipper_mouth_face[] = "🤐"; + inline constexpr const char woozy_face[] = "đŸĨ´"; + inline constexpr const char nauseated_face[] = "đŸ¤ĸ"; + inline constexpr const char sick[] = "đŸ¤ĸ"; + inline constexpr const char face_vomiting[] = "🤮"; + inline constexpr const char sneezing_face[] = "🤧"; + inline constexpr const char sneeze[] = "🤧"; + inline constexpr const char mask[] = "😷"; + inline constexpr const char thermometer_face[] = "🤒"; + inline constexpr const char face_with_thermometer[] = "🤒"; + inline constexpr const char head_bandage[] = "🤕"; + inline constexpr const char face_with_head_bandage[] = "🤕"; + inline constexpr const char money_mouth[] = "🤑"; + inline constexpr const char money_mouth_face[] = "🤑"; + inline constexpr const char cowboy[] = "🤠"; + inline constexpr const char face_with_cowboy_hat[] = "🤠"; + inline constexpr const char smiling_imp[] = "😈"; + inline constexpr const char imp[] = "đŸ‘ŋ"; + inline constexpr const char japanese_ogre[] = "👹"; + inline constexpr const char japanese_goblin[] = "đŸ‘ē"; + inline constexpr const char clown[] = "🤡"; + inline constexpr const char clown_face[] = "🤡"; + inline constexpr const char poop[] = "💩"; + inline constexpr const char shit[] = "💩"; + inline constexpr const char hankey[] = "💩"; + inline constexpr const char poo[] = "💩"; + inline constexpr const char ghost[] = "đŸ‘ģ"; + inline constexpr const char skull[] = "💀"; + inline constexpr const char skeleton[] = "💀"; + inline constexpr const char skull_crossbones[] = "☠ī¸"; + inline constexpr const char skull_and_crossbones[] = "☠ī¸"; + inline constexpr const char alien[] = "đŸ‘Ŋ"; + inline constexpr const char space_invader[] = "👾"; + inline constexpr const char robot[] = "🤖"; + inline constexpr const char robot_face[] = "🤖"; + inline constexpr const char jack_o_lantern[] = "🎃"; + inline constexpr const char smiley_cat[] = "đŸ˜ē"; + inline constexpr const char smile_cat[] = "😸"; + inline constexpr const char joy_cat[] = "😹"; + inline constexpr const char heart_eyes_cat[] = "đŸ˜ģ"; + inline constexpr const char smirk_cat[] = "đŸ˜ŧ"; + inline constexpr const char kissing_cat[] = "đŸ˜Ŋ"; + inline constexpr const char scream_cat[] = "🙀"; + inline constexpr const char crying_cat_face[] = "đŸ˜ŋ"; + inline constexpr const char pouting_cat[] = "😾"; + inline constexpr const char heart_hands[] = "đŸĢļ"; + inline constexpr const char heart_hands_tone1[] = "đŸĢļđŸģ"; + inline constexpr const char heart_hands_light_skin_tone[] = "đŸĢļđŸģ"; + inline constexpr const char heart_hands_tone2[] = "đŸĢļđŸŧ"; + inline constexpr const char heart_hands_medium_light_skin_tone[] = "đŸĢļđŸŧ"; + inline constexpr const char heart_hands_tone3[] = "đŸĢļđŸŊ"; + inline constexpr const char heart_hands_medium_skin_tone[] = "đŸĢļđŸŊ"; + inline constexpr const char heart_hands_tone4[] = "đŸĢļ🏾"; + inline constexpr const char heart_hands_medium_dark_skin_tone[] = "đŸĢļ🏾"; + inline constexpr const char heart_hands_tone5[] = "đŸĢļđŸŋ"; + inline constexpr const char heart_hands_dark_skin_tone[] = "đŸĢļđŸŋ"; + inline constexpr const char palms_up_together[] = "🤲"; + inline constexpr const char palms_up_together_tone1[] = "🤲đŸģ"; + inline constexpr const char palms_up_together_light_skin_tone[] = "🤲đŸģ"; + inline constexpr const char palms_up_together_tone2[] = "🤲đŸŧ"; + inline constexpr const char palms_up_together_medium_light_skin_tone[] = "🤲đŸŧ"; + inline constexpr const char palms_up_together_tone3[] = "🤲đŸŊ"; + inline constexpr const char palms_up_together_medium_skin_tone[] = "🤲đŸŊ"; + inline constexpr const char palms_up_together_tone4[] = "🤲🏾"; + inline constexpr const char palms_up_together_medium_dark_skin_tone[] = "🤲🏾"; + inline constexpr const char palms_up_together_tone5[] = "🤲đŸŋ"; + inline constexpr const char palms_up_together_dark_skin_tone[] = "🤲đŸŋ"; + inline constexpr const char open_hands[] = "👐"; + inline constexpr const char open_hands_tone1[] = "👐đŸģ"; + inline constexpr const char open_hands_tone2[] = "👐đŸŧ"; + inline constexpr const char open_hands_tone3[] = "👐đŸŊ"; + inline constexpr const char open_hands_tone4[] = "👐🏾"; + inline constexpr const char open_hands_tone5[] = "👐đŸŋ"; + inline constexpr const char raised_hands[] = "🙌"; + inline constexpr const char raised_hands_tone1[] = "🙌đŸģ"; + inline constexpr const char raised_hands_tone2[] = "🙌đŸŧ"; + inline constexpr const char raised_hands_tone3[] = "🙌đŸŊ"; + inline constexpr const char raised_hands_tone4[] = "🙌🏾"; + inline constexpr const char raised_hands_tone5[] = "🙌đŸŋ"; + inline constexpr const char clap[] = "👏"; + inline constexpr const char clap_tone1[] = "👏đŸģ"; + inline constexpr const char clap_tone2[] = "👏đŸŧ"; + inline constexpr const char clap_tone3[] = "👏đŸŊ"; + inline constexpr const char clap_tone4[] = "👏🏾"; + inline constexpr const char clap_tone5[] = "👏đŸŋ"; + inline constexpr const char handshake[] = "🤝"; + inline constexpr const char shaking_hands[] = "🤝"; + inline constexpr const char handshake_light_skin_tone[] = "🤝đŸģ"; + inline constexpr const char handshake_tone1_tone2[] = "đŸĢąđŸģ‍đŸĢ˛đŸŧ"; + inline constexpr const char handshake_light_skin_tone_medium_light_skin_tone[] = "đŸĢąđŸģ‍đŸĢ˛đŸŧ"; + inline constexpr const char handshake_tone1_tone3[] = "đŸĢąđŸģ‍đŸĢ˛đŸŊ"; + inline constexpr const char handshake_light_skin_tone_medium_skin_tone[] = "đŸĢąđŸģ‍đŸĢ˛đŸŊ"; + inline constexpr const char handshake_tone1_tone4[] = "đŸĢąđŸģ‍đŸĢ˛đŸž"; + inline constexpr const char handshake_light_skin_tone_medium_dark_skin_tone[] = "đŸĢąđŸģ‍đŸĢ˛đŸž"; + inline constexpr const char handshake_tone1_tone5[] = "đŸĢąđŸģ‍đŸĢ˛đŸŋ"; + inline constexpr const char handshake_light_skin_tone_dark_skin_tone[] = "đŸĢąđŸģ‍đŸĢ˛đŸŋ"; + inline constexpr const char handshake_tone2_tone1[] = "đŸĢąđŸŧ‍đŸĢ˛đŸģ"; + inline constexpr const char handshake_medium_light_skin_tone_light_skin_tone[] = "đŸĢąđŸŧ‍đŸĢ˛đŸģ"; + inline constexpr const char handshake_medium_light_skin_tone[] = "🤝đŸŧ"; + inline constexpr const char handshake_tone2_tone3[] = "đŸĢąđŸŧ‍đŸĢ˛đŸŊ"; + inline constexpr const char handshake_medium_light_skin_tone_medium_skin_tone[] = "đŸĢąđŸŧ‍đŸĢ˛đŸŊ"; + inline constexpr const char handshake_tone2_tone4[] = "đŸĢąđŸŧ‍đŸĢ˛đŸž"; + inline constexpr const char handshake_medium_light_skin_tone_medium_dark_skin_tone[] = "đŸĢąđŸŧ‍đŸĢ˛đŸž"; + inline constexpr const char handshake_tone2_tone5[] = "đŸĢąđŸŧ‍đŸĢ˛đŸŋ"; + inline constexpr const char handshake_medium_light_skin_tone_dark_skin_tone[] = "đŸĢąđŸŧ‍đŸĢ˛đŸŋ"; + inline constexpr const char handshake_tone3_tone1[] = "đŸĢąđŸŊ‍đŸĢ˛đŸģ"; + inline constexpr const char handshake_medium_skin_tone_light_skin_tone[] = "đŸĢąđŸŊ‍đŸĢ˛đŸģ"; + inline constexpr const char handshake_tone3_tone2[] = "đŸĢąđŸŊ‍đŸĢ˛đŸŧ"; + inline constexpr const char handshake_medium_skin_tone_medium_light_skin_tone[] = "đŸĢąđŸŊ‍đŸĢ˛đŸŧ"; + inline constexpr const char handshake_medium_skin_tone[] = "🤝đŸŊ"; + inline constexpr const char handshake_tone3_tone4[] = "đŸĢąđŸŊ‍đŸĢ˛đŸž"; + inline constexpr const char handshake_medium_skin_tone_medium_dark_skin_tone[] = "đŸĢąđŸŊ‍đŸĢ˛đŸž"; + inline constexpr const char handshake_tone3_tone5[] = "đŸĢąđŸŊ‍đŸĢ˛đŸŋ"; + inline constexpr const char handshake_medium_skin_tone_dark_skin_tone[] = "đŸĢąđŸŊ‍đŸĢ˛đŸŋ"; + inline constexpr const char handshake_tone4_tone1[] = "đŸĢąđŸžâ€đŸĢ˛đŸģ"; + inline constexpr const char handshake_medium_dark_skin_tone_light_skin_tone[] = "đŸĢąđŸžâ€đŸĢ˛đŸģ"; + inline constexpr const char handshake_tone4_tone2[] = "đŸĢąđŸžâ€đŸĢ˛đŸŧ"; + inline constexpr const char handshake_medium_dark_skin_tone_medium_light_skin_tone[] = "đŸĢąđŸžâ€đŸĢ˛đŸŧ"; + inline constexpr const char handshake_tone4_tone3[] = "đŸĢąđŸžâ€đŸĢ˛đŸŊ"; + inline constexpr const char handshake_medium_dark_skin_tone_medium_skin_tone[] = "đŸĢąđŸžâ€đŸĢ˛đŸŊ"; + inline constexpr const char handshake_medium_dark_skin_tone[] = "🤝🏾"; + inline constexpr const char handshake_tone4_tone5[] = "đŸĢąđŸžâ€đŸĢ˛đŸŋ"; + inline constexpr const char handshake_medium_dark_skin_tone_dark_skin_tone[] = "đŸĢąđŸžâ€đŸĢ˛đŸŋ"; + inline constexpr const char handshake_tone5_tone1[] = "đŸĢąđŸŋ‍đŸĢ˛đŸģ"; + inline constexpr const char handshake_dark_skin_tone_light_skin_tone[] = "đŸĢąđŸŋ‍đŸĢ˛đŸģ"; + inline constexpr const char handshake_tone5_tone2[] = "đŸĢąđŸŋ‍đŸĢ˛đŸŧ"; + inline constexpr const char handshake_dark_skin_tone_medium_light_skin_tone[] = "đŸĢąđŸŋ‍đŸĢ˛đŸŧ"; + inline constexpr const char handshake_tone5_tone3[] = "đŸĢąđŸŋ‍đŸĢ˛đŸŊ"; + inline constexpr const char handshake_dark_skin_tone_medium_skin_tone[] = "đŸĢąđŸŋ‍đŸĢ˛đŸŊ"; + inline constexpr const char handshake_tone5_tone4[] = "đŸĢąđŸŋ‍đŸĢ˛đŸž"; + inline constexpr const char handshake_dark_skin_tone_medium_dark_skin_tone[] = "đŸĢąđŸŋ‍đŸĢ˛đŸž"; + inline constexpr const char handshake_dark_skin_tone[] = "🤝đŸŋ"; + inline constexpr const char thumbsup[] = "👍"; + inline constexpr const char plus1[] = "👍"; + inline constexpr const char thumbup[] = "👍"; + inline constexpr const char thumbsup_tone1[] = "👍đŸģ"; + inline constexpr const char plus1_tone1[] = "👍đŸģ"; + inline constexpr const char thumbup_tone1[] = "👍đŸģ"; + inline constexpr const char thumbsup_tone2[] = "👍đŸŧ"; + inline constexpr const char plus1_tone2[] = "👍đŸŧ"; + inline constexpr const char thumbup_tone2[] = "👍đŸŧ"; + inline constexpr const char thumbsup_tone3[] = "👍đŸŊ"; + inline constexpr const char plus1_tone3[] = "👍đŸŊ"; + inline constexpr const char thumbup_tone3[] = "👍đŸŊ"; + inline constexpr const char thumbsup_tone4[] = "👍🏾"; + inline constexpr const char plus1_tone4[] = "👍🏾"; + inline constexpr const char thumbup_tone4[] = "👍🏾"; + inline constexpr const char thumbsup_tone5[] = "👍đŸŋ"; + inline constexpr const char plus1_tone5[] = "👍đŸŋ"; + inline constexpr const char thumbup_tone5[] = "👍đŸŋ"; + inline constexpr const char thumbsdown[] = "👎"; + inline constexpr const char minus1[] = "👎"; + inline constexpr const char thumbdown[] = "👎"; + inline constexpr const char thumbsdown_tone1[] = "👎đŸģ"; + inline constexpr const char _1_tone1[] = "👎đŸģ"; + inline constexpr const char thumbdown_tone1[] = "👎đŸģ"; + inline constexpr const char thumbsdown_tone2[] = "👎đŸŧ"; + inline constexpr const char _1_tone2[] = "👎đŸŧ"; + inline constexpr const char thumbdown_tone2[] = "👎đŸŧ"; + inline constexpr const char thumbsdown_tone3[] = "👎đŸŊ"; + inline constexpr const char _1_tone3[] = "👎đŸŊ"; + inline constexpr const char thumbdown_tone3[] = "👎đŸŊ"; + inline constexpr const char thumbsdown_tone4[] = "👎🏾"; + inline constexpr const char _1_tone4[] = "👎🏾"; + inline constexpr const char thumbdown_tone4[] = "👎🏾"; + inline constexpr const char thumbsdown_tone5[] = "👎đŸŋ"; + inline constexpr const char _1_tone5[] = "👎đŸŋ"; + inline constexpr const char thumbdown_tone5[] = "👎đŸŋ"; + inline constexpr const char punch[] = "👊"; + inline constexpr const char punch_tone1[] = "👊đŸģ"; + inline constexpr const char punch_tone2[] = "👊đŸŧ"; + inline constexpr const char punch_tone3[] = "👊đŸŊ"; + inline constexpr const char punch_tone4[] = "👊🏾"; + inline constexpr const char punch_tone5[] = "👊đŸŋ"; + inline constexpr const char fist[] = "✊"; + inline constexpr const char fist_tone1[] = "✊đŸģ"; + inline constexpr const char fist_tone2[] = "✊đŸŧ"; + inline constexpr const char fist_tone3[] = "✊đŸŊ"; + inline constexpr const char fist_tone4[] = "✊🏾"; + inline constexpr const char fist_tone5[] = "✊đŸŋ"; + inline constexpr const char left_facing_fist[] = "🤛"; + inline constexpr const char left_fist[] = "🤛"; + inline constexpr const char left_facing_fist_tone1[] = "🤛đŸģ"; + inline constexpr const char left_fist_tone1[] = "🤛đŸģ"; + inline constexpr const char left_facing_fist_tone2[] = "🤛đŸŧ"; + inline constexpr const char left_fist_tone2[] = "🤛đŸŧ"; + inline constexpr const char left_facing_fist_tone3[] = "🤛đŸŊ"; + inline constexpr const char left_fist_tone3[] = "🤛đŸŊ"; + inline constexpr const char left_facing_fist_tone4[] = "🤛🏾"; + inline constexpr const char left_fist_tone4[] = "🤛🏾"; + inline constexpr const char left_facing_fist_tone5[] = "🤛đŸŋ"; + inline constexpr const char left_fist_tone5[] = "🤛đŸŋ"; + inline constexpr const char right_facing_fist[] = "🤜"; + inline constexpr const char right_fist[] = "🤜"; + inline constexpr const char right_facing_fist_tone1[] = "🤜đŸģ"; + inline constexpr const char right_fist_tone1[] = "🤜đŸģ"; + inline constexpr const char right_facing_fist_tone2[] = "🤜đŸŧ"; + inline constexpr const char right_fist_tone2[] = "🤜đŸŧ"; + inline constexpr const char right_facing_fist_tone3[] = "🤜đŸŊ"; + inline constexpr const char right_fist_tone3[] = "🤜đŸŊ"; + inline constexpr const char right_facing_fist_tone4[] = "🤜🏾"; + inline constexpr const char right_fist_tone4[] = "🤜🏾"; + inline constexpr const char right_facing_fist_tone5[] = "🤜đŸŋ"; + inline constexpr const char right_fist_tone5[] = "🤜đŸŋ"; + inline constexpr const char fingers_crossed[] = "🤞"; + inline constexpr const char hand_with_index_and_middle_finger_crossed[] = "🤞"; + inline constexpr const char fingers_crossed_tone1[] = "🤞đŸģ"; + inline constexpr const char hand_with_index_and_middle_fingers_crossed_tone1[] = "🤞đŸģ"; + inline constexpr const char fingers_crossed_tone2[] = "🤞đŸŧ"; + inline constexpr const char hand_with_index_and_middle_fingers_crossed_tone2[] = "🤞đŸŧ"; + inline constexpr const char fingers_crossed_tone3[] = "🤞đŸŊ"; + inline constexpr const char hand_with_index_and_middle_fingers_crossed_tone3[] = "🤞đŸŊ"; + inline constexpr const char fingers_crossed_tone4[] = "🤞🏾"; + inline constexpr const char hand_with_index_and_middle_fingers_crossed_tone4[] = "🤞🏾"; + inline constexpr const char fingers_crossed_tone5[] = "🤞đŸŋ"; + inline constexpr const char hand_with_index_and_middle_fingers_crossed_tone5[] = "🤞đŸŋ"; + inline constexpr const char v[] = "✌ī¸"; + inline constexpr const char v_tone1[] = "✌đŸģ"; + inline constexpr const char v_tone2[] = "✌đŸŧ"; + inline constexpr const char v_tone3[] = "✌đŸŊ"; + inline constexpr const char v_tone4[] = "✌🏾"; + inline constexpr const char v_tone5[] = "✌đŸŋ"; + inline constexpr const char hand_with_index_finger_and_thumb_crossed[] = "đŸĢ°"; + inline constexpr const char hand_with_index_finger_and_thumb_crossed_tone1[] = "đŸĢ°đŸģ"; + inline constexpr const char hand_with_index_finger_and_thumb_crossed_light_skin_tone[] = "đŸĢ°đŸģ"; + inline constexpr const char hand_with_index_finger_and_thumb_crossed_tone2[] = "đŸĢ°đŸŧ"; + inline constexpr const char hand_with_index_finger_and_thumb_crossed_medium_light_skin_tone[] = "đŸĢ°đŸŧ"; + inline constexpr const char hand_with_index_finger_and_thumb_crossed_tone3[] = "đŸĢ°đŸŊ"; + inline constexpr const char hand_with_index_finger_and_thumb_crossed_medium_skin_tone[] = "đŸĢ°đŸŊ"; + inline constexpr const char hand_with_index_finger_and_thumb_crossed_tone4[] = "đŸĢ°đŸž"; + inline constexpr const char hand_with_index_finger_and_thumb_crossed_medium_dark_skin_tone[] = "đŸĢ°đŸž"; + inline constexpr const char hand_with_index_finger_and_thumb_crossed_tone5[] = "đŸĢ°đŸŋ"; + inline constexpr const char hand_with_index_finger_and_thumb_crossed_dark_skin_tone[] = "đŸĢ°đŸŋ"; + inline constexpr const char love_you_gesture[] = "🤟"; + inline constexpr const char love_you_gesture_tone1[] = "🤟đŸģ"; + inline constexpr const char love_you_gesture_light_skin_tone[] = "🤟đŸģ"; + inline constexpr const char love_you_gesture_tone2[] = "🤟đŸŧ"; + inline constexpr const char love_you_gesture_medium_light_skin_tone[] = "🤟đŸŧ"; + inline constexpr const char love_you_gesture_tone3[] = "🤟đŸŊ"; + inline constexpr const char love_you_gesture_medium_skin_tone[] = "🤟đŸŊ"; + inline constexpr const char love_you_gesture_tone4[] = "🤟🏾"; + inline constexpr const char love_you_gesture_medium_dark_skin_tone[] = "🤟🏾"; + inline constexpr const char love_you_gesture_tone5[] = "🤟đŸŋ"; + inline constexpr const char love_you_gesture_dark_skin_tone[] = "🤟đŸŋ"; + inline constexpr const char metal[] = "🤘"; + inline constexpr const char sign_of_the_horns[] = "🤘"; + inline constexpr const char metal_tone1[] = "🤘đŸģ"; + inline constexpr const char sign_of_the_horns_tone1[] = "🤘đŸģ"; + inline constexpr const char metal_tone2[] = "🤘đŸŧ"; + inline constexpr const char sign_of_the_horns_tone2[] = "🤘đŸŧ"; + inline constexpr const char metal_tone3[] = "🤘đŸŊ"; + inline constexpr const char sign_of_the_horns_tone3[] = "🤘đŸŊ"; + inline constexpr const char metal_tone4[] = "🤘🏾"; + inline constexpr const char sign_of_the_horns_tone4[] = "🤘🏾"; + inline constexpr const char metal_tone5[] = "🤘đŸŋ"; + inline constexpr const char sign_of_the_horns_tone5[] = "🤘đŸŋ"; + inline constexpr const char ok_hand[] = "👌"; + inline constexpr const char ok_hand_tone1[] = "👌đŸģ"; + inline constexpr const char ok_hand_tone2[] = "👌đŸŧ"; + inline constexpr const char ok_hand_tone3[] = "👌đŸŊ"; + inline constexpr const char ok_hand_tone4[] = "👌🏾"; + inline constexpr const char ok_hand_tone5[] = "👌đŸŋ"; + inline constexpr const char pinched_fingers[] = "🤌"; + inline constexpr const char pinched_fingers_tone2[] = "🤌đŸŧ"; + inline constexpr const char pinched_fingers_medium_light_skin_tone[] = "🤌đŸŧ"; + inline constexpr const char pinched_fingers_tone1[] = "🤌đŸģ"; + inline constexpr const char pinched_fingers_light_skin_tone[] = "🤌đŸģ"; + inline constexpr const char pinched_fingers_tone3[] = "🤌đŸŊ"; + inline constexpr const char pinched_fingers_medium_skin_tone[] = "🤌đŸŊ"; + inline constexpr const char pinched_fingers_tone4[] = "🤌🏾"; + inline constexpr const char pinched_fingers_medium_dark_skin_tone[] = "🤌🏾"; + inline constexpr const char pinched_fingers_tone5[] = "🤌đŸŋ"; + inline constexpr const char pinched_fingers_dark_skin_tone[] = "🤌đŸŋ"; + inline constexpr const char pinching_hand[] = "🤏"; + inline constexpr const char pinching_hand_tone1[] = "🤏đŸģ"; + inline constexpr const char pinching_hand_light_skin_tone[] = "🤏đŸģ"; + inline constexpr const char pinching_hand_tone2[] = "🤏đŸŧ"; + inline constexpr const char pinching_hand_medium_light_skin_tone[] = "🤏đŸŧ"; + inline constexpr const char pinching_hand_tone3[] = "🤏đŸŊ"; + inline constexpr const char pinching_hand_medium_skin_tone[] = "🤏đŸŊ"; + inline constexpr const char pinching_hand_tone4[] = "🤏🏾"; + inline constexpr const char pinching_hand_medium_dark_skin_tone[] = "🤏🏾"; + inline constexpr const char pinching_hand_tone5[] = "🤏đŸŋ"; + inline constexpr const char pinching_hand_dark_skin_tone[] = "🤏đŸŋ"; + inline constexpr const char palm_down_hand[] = "đŸĢŗ"; + inline constexpr const char palm_down_hand_tone1[] = "đŸĢŗđŸģ"; + inline constexpr const char palm_down_hand_light_skin_tone[] = "đŸĢŗđŸģ"; + inline constexpr const char palm_down_hand_tone2[] = "đŸĢŗđŸŧ"; + inline constexpr const char palm_down_hand_medium_light_skin_tone[] = "đŸĢŗđŸŧ"; + inline constexpr const char palm_down_hand_tone3[] = "đŸĢŗđŸŊ"; + inline constexpr const char palm_down_hand_medium_skin_tone[] = "đŸĢŗđŸŊ"; + inline constexpr const char palm_down_hand_tone4[] = "đŸĢŗ🏾"; + inline constexpr const char palm_down_hand_medium_dark_skin_tone[] = "đŸĢŗ🏾"; + inline constexpr const char palm_down_hand_tone5[] = "đŸĢŗđŸŋ"; + inline constexpr const char palm_down_hand_dark_skin_tone[] = "đŸĢŗđŸŋ"; + inline constexpr const char palm_up_hand[] = "đŸĢ´"; + inline constexpr const char palm_up_hand_tone1[] = "đŸĢ´đŸģ"; + inline constexpr const char palm_up_hand_light_skin_tone[] = "đŸĢ´đŸģ"; + inline constexpr const char palm_up_hand_tone2[] = "đŸĢ´đŸŧ"; + inline constexpr const char palm_up_hand_medium_light_skin_tone[] = "đŸĢ´đŸŧ"; + inline constexpr const char palm_up_hand_tone3[] = "đŸĢ´đŸŊ"; + inline constexpr const char palm_up_hand_medium_skin_tone[] = "đŸĢ´đŸŊ"; + inline constexpr const char palm_up_hand_tone4[] = "đŸĢ´đŸž"; + inline constexpr const char palm_up_hand_medium_dark_skin_tone[] = "đŸĢ´đŸž"; + inline constexpr const char palm_up_hand_tone5[] = "đŸĢ´đŸŋ"; + inline constexpr const char palm_up_hand_dark_skin_tone[] = "đŸĢ´đŸŋ"; + inline constexpr const char point_left[] = "👈"; + inline constexpr const char point_left_tone1[] = "👈đŸģ"; + inline constexpr const char point_left_tone2[] = "👈đŸŧ"; + inline constexpr const char point_left_tone3[] = "👈đŸŊ"; + inline constexpr const char point_left_tone4[] = "👈🏾"; + inline constexpr const char point_left_tone5[] = "👈đŸŋ"; + inline constexpr const char point_right[] = "👉"; + inline constexpr const char point_right_tone1[] = "👉đŸģ"; + inline constexpr const char point_right_tone2[] = "👉đŸŧ"; + inline constexpr const char point_right_tone3[] = "👉đŸŊ"; + inline constexpr const char point_right_tone4[] = "👉🏾"; + inline constexpr const char point_right_tone5[] = "👉đŸŋ"; + inline constexpr const char point_up_2[] = "👆"; + inline constexpr const char point_up_2_tone1[] = "👆đŸģ"; + inline constexpr const char point_up_2_tone2[] = "👆đŸŧ"; + inline constexpr const char point_up_2_tone3[] = "👆đŸŊ"; + inline constexpr const char point_up_2_tone4[] = "👆🏾"; + inline constexpr const char point_up_2_tone5[] = "👆đŸŋ"; + inline constexpr const char point_down[] = "👇"; + inline constexpr const char point_down_tone1[] = "👇đŸģ"; + inline constexpr const char point_down_tone2[] = "👇đŸŧ"; + inline constexpr const char point_down_tone3[] = "👇đŸŊ"; + inline constexpr const char point_down_tone4[] = "👇🏾"; + inline constexpr const char point_down_tone5[] = "👇đŸŋ"; + inline constexpr const char point_up[] = "☝ī¸"; + inline constexpr const char point_up_tone1[] = "☝đŸģ"; + inline constexpr const char point_up_tone2[] = "☝đŸŧ"; + inline constexpr const char point_up_tone3[] = "☝đŸŊ"; + inline constexpr const char point_up_tone4[] = "☝🏾"; + inline constexpr const char point_up_tone5[] = "☝đŸŋ"; + inline constexpr const char raised_hand[] = "✋"; + inline constexpr const char raised_hand_tone1[] = "✋đŸģ"; + inline constexpr const char raised_hand_tone2[] = "✋đŸŧ"; + inline constexpr const char raised_hand_tone3[] = "✋đŸŊ"; + inline constexpr const char raised_hand_tone4[] = "✋🏾"; + inline constexpr const char raised_hand_tone5[] = "✋đŸŋ"; + inline constexpr const char raised_back_of_hand[] = "🤚"; + inline constexpr const char back_of_hand[] = "🤚"; + inline constexpr const char raised_back_of_hand_tone1[] = "🤚đŸģ"; + inline constexpr const char back_of_hand_tone1[] = "🤚đŸģ"; + inline constexpr const char raised_back_of_hand_tone2[] = "🤚đŸŧ"; + inline constexpr const char back_of_hand_tone2[] = "🤚đŸŧ"; + inline constexpr const char raised_back_of_hand_tone3[] = "🤚đŸŊ"; + inline constexpr const char back_of_hand_tone3[] = "🤚đŸŊ"; + inline constexpr const char raised_back_of_hand_tone4[] = "🤚🏾"; + inline constexpr const char back_of_hand_tone4[] = "🤚🏾"; + inline constexpr const char raised_back_of_hand_tone5[] = "🤚đŸŋ"; + inline constexpr const char back_of_hand_tone5[] = "🤚đŸŋ"; + inline constexpr const char hand_splayed[] = "🖐ī¸"; + inline constexpr const char raised_hand_with_fingers_splayed[] = "🖐ī¸"; + inline constexpr const char hand_splayed_tone1[] = "🖐đŸģ"; + inline constexpr const char raised_hand_with_fingers_splayed_tone1[] = "🖐đŸģ"; + inline constexpr const char hand_splayed_tone2[] = "🖐đŸŧ"; + inline constexpr const char raised_hand_with_fingers_splayed_tone2[] = "🖐đŸŧ"; + inline constexpr const char hand_splayed_tone3[] = "🖐đŸŊ"; + inline constexpr const char raised_hand_with_fingers_splayed_tone3[] = "🖐đŸŊ"; + inline constexpr const char hand_splayed_tone4[] = "🖐🏾"; + inline constexpr const char raised_hand_with_fingers_splayed_tone4[] = "🖐🏾"; + inline constexpr const char hand_splayed_tone5[] = "🖐đŸŋ"; + inline constexpr const char raised_hand_with_fingers_splayed_tone5[] = "🖐đŸŋ"; + inline constexpr const char vulcan[] = "🖖"; + inline constexpr const char raised_hand_with_part_between_middle_and_ring_fingers[] = "🖖"; + inline constexpr const char vulcan_tone1[] = "🖖đŸģ"; + inline constexpr const char raised_hand_with_part_between_middle_and_ring_fingers_tone1[] = "🖖đŸģ"; + inline constexpr const char vulcan_tone2[] = "🖖đŸŧ"; + inline constexpr const char raised_hand_with_part_between_middle_and_ring_fingers_tone2[] = "🖖đŸŧ"; + inline constexpr const char vulcan_tone3[] = "🖖đŸŊ"; + inline constexpr const char raised_hand_with_part_between_middle_and_ring_fingers_tone3[] = "🖖đŸŊ"; + inline constexpr const char vulcan_tone4[] = "🖖🏾"; + inline constexpr const char raised_hand_with_part_between_middle_and_ring_fingers_tone4[] = "🖖🏾"; + inline constexpr const char vulcan_tone5[] = "🖖đŸŋ"; + inline constexpr const char raised_hand_with_part_between_middle_and_ring_fingers_tone5[] = "🖖đŸŋ"; + inline constexpr const char wave[] = "👋"; + inline constexpr const char wave_tone1[] = "👋đŸģ"; + inline constexpr const char wave_tone2[] = "👋đŸŧ"; + inline constexpr const char wave_tone3[] = "👋đŸŊ"; + inline constexpr const char wave_tone4[] = "👋🏾"; + inline constexpr const char wave_tone5[] = "👋đŸŋ"; + inline constexpr const char call_me[] = "🤙"; + inline constexpr const char call_me_hand[] = "🤙"; + inline constexpr const char call_me_tone1[] = "🤙đŸģ"; + inline constexpr const char call_me_hand_tone1[] = "🤙đŸģ"; + inline constexpr const char call_me_tone2[] = "🤙đŸŧ"; + inline constexpr const char call_me_hand_tone2[] = "🤙đŸŧ"; + inline constexpr const char call_me_tone3[] = "🤙đŸŊ"; + inline constexpr const char call_me_hand_tone3[] = "🤙đŸŊ"; + inline constexpr const char call_me_tone4[] = "🤙🏾"; + inline constexpr const char call_me_hand_tone4[] = "🤙🏾"; + inline constexpr const char call_me_tone5[] = "🤙đŸŋ"; + inline constexpr const char call_me_hand_tone5[] = "🤙đŸŋ"; + inline constexpr const char leftwards_hand[] = "đŸĢ˛"; + inline constexpr const char leftwards_hand_tone1[] = "đŸĢ˛đŸģ"; + inline constexpr const char leftwards_hand_light_skin_tone[] = "đŸĢ˛đŸģ"; + inline constexpr const char leftwards_hand_tone2[] = "đŸĢ˛đŸŧ"; + inline constexpr const char leftwards_hand_medium_light_skin_tone[] = "đŸĢ˛đŸŧ"; + inline constexpr const char leftwards_hand_tone3[] = "đŸĢ˛đŸŊ"; + inline constexpr const char leftwards_hand_medium_skin_tone[] = "đŸĢ˛đŸŊ"; + inline constexpr const char leftwards_hand_tone4[] = "đŸĢ˛đŸž"; + inline constexpr const char leftwards_hand_medium_dark_skin_tone[] = "đŸĢ˛đŸž"; + inline constexpr const char leftwards_hand_tone5[] = "đŸĢ˛đŸŋ"; + inline constexpr const char leftwards_hand_dark_skin_tone[] = "đŸĢ˛đŸŋ"; + inline constexpr const char rightwards_hand[] = "đŸĢą"; + inline constexpr const char rightwards_hand_tone1[] = "đŸĢąđŸģ"; + inline constexpr const char rightwards_hand_light_skin_tone[] = "đŸĢąđŸģ"; + inline constexpr const char rightwards_hand_tone2[] = "đŸĢąđŸŧ"; + inline constexpr const char rightwards_hand_medium_light_skin_tone[] = "đŸĢąđŸŧ"; + inline constexpr const char rightwards_hand_tone3[] = "đŸĢąđŸŊ"; + inline constexpr const char rightwards_hand_medium_skin_tone[] = "đŸĢąđŸŊ"; + inline constexpr const char rightwards_hand_tone4[] = "đŸĢąđŸž"; + inline constexpr const char rightwards_hand_medium_dark_skin_tone[] = "đŸĢąđŸž"; + inline constexpr const char rightwards_hand_tone5[] = "đŸĢąđŸŋ"; + inline constexpr const char rightwards_hand_dark_skin_tone[] = "đŸĢąđŸŋ"; + inline constexpr const char muscle[] = "đŸ’Ē"; + inline constexpr const char muscle_tone1[] = "đŸ’ĒđŸģ"; + inline constexpr const char muscle_tone2[] = "đŸ’ĒđŸŧ"; + inline constexpr const char muscle_tone3[] = "đŸ’ĒđŸŊ"; + inline constexpr const char muscle_tone4[] = "đŸ’Ē🏾"; + inline constexpr const char muscle_tone5[] = "đŸ’ĒđŸŋ"; + inline constexpr const char mechanical_arm[] = "đŸĻž"; + inline constexpr const char middle_finger[] = "🖕"; + inline constexpr const char reversed_hand_with_middle_finger_extended[] = "🖕"; + inline constexpr const char middle_finger_tone1[] = "🖕đŸģ"; + inline constexpr const char reversed_hand_with_middle_finger_extended_tone1[] = "🖕đŸģ"; + inline constexpr const char middle_finger_tone2[] = "🖕đŸŧ"; + inline constexpr const char reversed_hand_with_middle_finger_extended_tone2[] = "🖕đŸŧ"; + inline constexpr const char middle_finger_tone3[] = "🖕đŸŊ"; + inline constexpr const char reversed_hand_with_middle_finger_extended_tone3[] = "🖕đŸŊ"; + inline constexpr const char middle_finger_tone4[] = "🖕🏾"; + inline constexpr const char reversed_hand_with_middle_finger_extended_tone4[] = "🖕🏾"; + inline constexpr const char middle_finger_tone5[] = "🖕đŸŋ"; + inline constexpr const char reversed_hand_with_middle_finger_extended_tone5[] = "🖕đŸŋ"; + inline constexpr const char writing_hand[] = "✍ī¸"; + inline constexpr const char writing_hand_tone1[] = "✍đŸģ"; + inline constexpr const char writing_hand_tone2[] = "✍đŸŧ"; + inline constexpr const char writing_hand_tone3[] = "✍đŸŊ"; + inline constexpr const char writing_hand_tone4[] = "✍🏾"; + inline constexpr const char writing_hand_tone5[] = "✍đŸŋ"; + inline constexpr const char pray[] = "🙏"; + inline constexpr const char pray_tone1[] = "🙏đŸģ"; + inline constexpr const char pray_tone2[] = "🙏đŸŧ"; + inline constexpr const char pray_tone3[] = "🙏đŸŊ"; + inline constexpr const char pray_tone4[] = "🙏🏾"; + inline constexpr const char pray_tone5[] = "🙏đŸŋ"; + inline constexpr const char index_pointing_at_the_viewer[] = "đŸĢĩ"; + inline constexpr const char index_pointing_at_the_viewer_tone1[] = "đŸĢĩđŸģ"; + inline constexpr const char index_pointing_at_the_viewer_light_skin_tone[] = "đŸĢĩđŸģ"; + inline constexpr const char index_pointing_at_the_viewer_tone2[] = "đŸĢĩđŸŧ"; + inline constexpr const char index_pointing_at_the_viewer_medium_light_skin_tone[] = "đŸĢĩđŸŧ"; + inline constexpr const char index_pointing_at_the_viewer_tone3[] = "đŸĢĩđŸŊ"; + inline constexpr const char index_pointing_at_the_viewer_medium_skin_tone[] = "đŸĢĩđŸŊ"; + inline constexpr const char index_pointing_at_the_viewer_tone4[] = "đŸĢĩ🏾"; + inline constexpr const char index_pointing_at_the_viewer_medium_dark_skin_tone[] = "đŸĢĩ🏾"; + inline constexpr const char index_pointing_at_the_viewer_tone5[] = "đŸĢĩđŸŋ"; + inline constexpr const char index_pointing_at_the_viewer_dark_skin_tone[] = "đŸĢĩđŸŋ"; + inline constexpr const char foot[] = "đŸĻļ"; + inline constexpr const char foot_tone1[] = "đŸĻļđŸģ"; + inline constexpr const char foot_light_skin_tone[] = "đŸĻļđŸģ"; + inline constexpr const char foot_tone2[] = "đŸĻļđŸŧ"; + inline constexpr const char foot_medium_light_skin_tone[] = "đŸĻļđŸŧ"; + inline constexpr const char foot_tone3[] = "đŸĻļđŸŊ"; + inline constexpr const char foot_medium_skin_tone[] = "đŸĻļđŸŊ"; + inline constexpr const char foot_tone4[] = "đŸĻļ🏾"; + inline constexpr const char foot_medium_dark_skin_tone[] = "đŸĻļ🏾"; + inline constexpr const char foot_tone5[] = "đŸĻļđŸŋ"; + inline constexpr const char foot_dark_skin_tone[] = "đŸĻļđŸŋ"; + inline constexpr const char leg[] = "đŸĻĩ"; + inline constexpr const char leg_tone1[] = "đŸĻĩđŸģ"; + inline constexpr const char leg_light_skin_tone[] = "đŸĻĩđŸģ"; + inline constexpr const char leg_tone2[] = "đŸĻĩđŸŧ"; + inline constexpr const char leg_medium_light_skin_tone[] = "đŸĻĩđŸŧ"; + inline constexpr const char leg_tone3[] = "đŸĻĩđŸŊ"; + inline constexpr const char leg_medium_skin_tone[] = "đŸĻĩđŸŊ"; + inline constexpr const char leg_tone4[] = "đŸĻĩ🏾"; + inline constexpr const char leg_medium_dark_skin_tone[] = "đŸĻĩ🏾"; + inline constexpr const char leg_tone5[] = "đŸĻĩđŸŋ"; + inline constexpr const char leg_dark_skin_tone[] = "đŸĻĩđŸŋ"; + inline constexpr const char mechanical_leg[] = "đŸĻŋ"; + inline constexpr const char lipstick[] = "💄"; + inline constexpr const char kiss[] = "💋"; + inline constexpr const char lips[] = "👄"; + inline constexpr const char biting_lip[] = "đŸĢĻ"; + inline constexpr const char tooth[] = "đŸĻˇ"; + inline constexpr const char tongue[] = "👅"; + inline constexpr const char ear[] = "👂"; + inline constexpr const char ear_tone1[] = "👂đŸģ"; + inline constexpr const char ear_tone2[] = "👂đŸŧ"; + inline constexpr const char ear_tone3[] = "👂đŸŊ"; + inline constexpr const char ear_tone4[] = "👂🏾"; + inline constexpr const char ear_tone5[] = "👂đŸŋ"; + inline constexpr const char ear_with_hearing_aid[] = "đŸĻģ"; + inline constexpr const char ear_with_hearing_aid_tone1[] = "đŸĻģđŸģ"; + inline constexpr const char ear_with_hearing_aid_light_skin_tone[] = "đŸĻģđŸģ"; + inline constexpr const char ear_with_hearing_aid_tone2[] = "đŸĻģđŸŧ"; + inline constexpr const char ear_with_hearing_aid_medium_light_skin_tone[] = "đŸĻģđŸŧ"; + inline constexpr const char ear_with_hearing_aid_tone3[] = "đŸĻģđŸŊ"; + inline constexpr const char ear_with_hearing_aid_medium_skin_tone[] = "đŸĻģđŸŊ"; + inline constexpr const char ear_with_hearing_aid_tone4[] = "đŸĻģ🏾"; + inline constexpr const char ear_with_hearing_aid_medium_dark_skin_tone[] = "đŸĻģ🏾"; + inline constexpr const char ear_with_hearing_aid_tone5[] = "đŸĻģđŸŋ"; + inline constexpr const char ear_with_hearing_aid_dark_skin_tone[] = "đŸĻģđŸŋ"; + inline constexpr const char nose[] = "👃"; + inline constexpr const char nose_tone1[] = "👃đŸģ"; + inline constexpr const char nose_tone2[] = "👃đŸŧ"; + inline constexpr const char nose_tone3[] = "👃đŸŊ"; + inline constexpr const char nose_tone4[] = "👃🏾"; + inline constexpr const char nose_tone5[] = "👃đŸŋ"; + inline constexpr const char footprints[] = "đŸ‘Ŗ"; + inline constexpr const char eye[] = "👁ī¸"; + inline constexpr const char eyes[] = "👀"; + inline constexpr const char anatomical_heart[] = "đŸĢ€"; + inline constexpr const char lungs[] = "đŸĢ"; + inline constexpr const char brain[] = "🧠"; + inline constexpr const char speaking_head[] = "đŸ—Ŗī¸"; + inline constexpr const char speaking_head_in_silhouette[] = "đŸ—Ŗī¸"; + inline constexpr const char bust_in_silhouette[] = "👤"; + inline constexpr const char busts_in_silhouette[] = "đŸ‘Ĩ"; + inline constexpr const char people_hugging[] = "đŸĢ‚"; + inline constexpr const char baby[] = "đŸ‘ļ"; + inline constexpr const char baby_tone1[] = "đŸ‘ļđŸģ"; + inline constexpr const char baby_tone2[] = "đŸ‘ļđŸŧ"; + inline constexpr const char baby_tone3[] = "đŸ‘ļđŸŊ"; + inline constexpr const char baby_tone4[] = "đŸ‘ļ🏾"; + inline constexpr const char baby_tone5[] = "đŸ‘ļđŸŋ"; + inline constexpr const char child[] = "🧒"; + inline constexpr const char child_tone1[] = "🧒đŸģ"; + inline constexpr const char child_light_skin_tone[] = "🧒đŸģ"; + inline constexpr const char child_tone2[] = "🧒đŸŧ"; + inline constexpr const char child_medium_light_skin_tone[] = "🧒đŸŧ"; + inline constexpr const char child_tone3[] = "🧒đŸŊ"; + inline constexpr const char child_medium_skin_tone[] = "🧒đŸŊ"; + inline constexpr const char child_tone4[] = "🧒🏾"; + inline constexpr const char child_medium_dark_skin_tone[] = "🧒🏾"; + inline constexpr const char child_tone5[] = "🧒đŸŋ"; + inline constexpr const char child_dark_skin_tone[] = "🧒đŸŋ"; + inline constexpr const char girl[] = "👧"; + inline constexpr const char girl_tone1[] = "👧đŸģ"; + inline constexpr const char girl_tone2[] = "👧đŸŧ"; + inline constexpr const char girl_tone3[] = "👧đŸŊ"; + inline constexpr const char girl_tone4[] = "👧🏾"; + inline constexpr const char girl_tone5[] = "👧đŸŋ"; + inline constexpr const char boy[] = "đŸ‘Ļ"; + inline constexpr const char boy_tone1[] = "đŸ‘ĻđŸģ"; + inline constexpr const char boy_tone2[] = "đŸ‘ĻđŸŧ"; + inline constexpr const char boy_tone3[] = "đŸ‘ĻđŸŊ"; + inline constexpr const char boy_tone4[] = "đŸ‘Ļ🏾"; + inline constexpr const char boy_tone5[] = "đŸ‘ĻđŸŋ"; + inline constexpr const char adult[] = "🧑"; + inline constexpr const char adult_tone1[] = "🧑đŸģ"; + inline constexpr const char adult_light_skin_tone[] = "🧑đŸģ"; + inline constexpr const char adult_tone2[] = "🧑đŸŧ"; + inline constexpr const char adult_medium_light_skin_tone[] = "🧑đŸŧ"; + inline constexpr const char adult_tone3[] = "🧑đŸŊ"; + inline constexpr const char adult_medium_skin_tone[] = "🧑đŸŊ"; + inline constexpr const char adult_tone4[] = "🧑🏾"; + inline constexpr const char adult_medium_dark_skin_tone[] = "🧑🏾"; + inline constexpr const char adult_tone5[] = "🧑đŸŋ"; + inline constexpr const char adult_dark_skin_tone[] = "🧑đŸŋ"; + inline constexpr const char woman[] = "👩"; + inline constexpr const char woman_tone1[] = "👩đŸģ"; + inline constexpr const char woman_tone2[] = "👩đŸŧ"; + inline constexpr const char woman_tone3[] = "👩đŸŊ"; + inline constexpr const char woman_tone4[] = "👩🏾"; + inline constexpr const char woman_tone5[] = "👩đŸŋ"; + inline constexpr const char man[] = "👨"; + inline constexpr const char man_tone1[] = "👨đŸģ"; + inline constexpr const char man_tone2[] = "👨đŸŧ"; + inline constexpr const char man_tone3[] = "👨đŸŊ"; + inline constexpr const char man_tone4[] = "👨🏾"; + inline constexpr const char man_tone5[] = "👨đŸŋ"; + inline constexpr const char person_curly_hair[] = "🧑‍đŸĻą"; + inline constexpr const char person_tone1_curly_hair[] = "🧑đŸģ‍đŸĻą"; + inline constexpr const char person_light_skin_tone_curly_hair[] = "🧑đŸģ‍đŸĻą"; + inline constexpr const char person_tone2_curly_hair[] = "🧑đŸŧ‍đŸĻą"; + inline constexpr const char person_medium_light_skin_tone_curly_hair[] = "🧑đŸŧ‍đŸĻą"; + inline constexpr const char person_tone3_curly_hair[] = "🧑đŸŊ‍đŸĻą"; + inline constexpr const char person_medium_skin_tone_curly_hair[] = "🧑đŸŊ‍đŸĻą"; + inline constexpr const char person_tone4_curly_hair[] = "🧑🏾‍đŸĻą"; + inline constexpr const char person_medium_dark_skin_tone_curly_hair[] = "🧑🏾‍đŸĻą"; + inline constexpr const char person_tone5_curly_hair[] = "🧑đŸŋ‍đŸĻą"; + inline constexpr const char person_dark_skin_tone_curly_hair[] = "🧑đŸŋ‍đŸĻą"; + inline constexpr const char woman_curly_haired[] = "👩‍đŸĻą"; + inline constexpr const char woman_curly_haired_tone1[] = "👩đŸģ‍đŸĻą"; + inline constexpr const char woman_curly_haired_light_skin_tone[] = "👩đŸģ‍đŸĻą"; + inline constexpr const char woman_curly_haired_tone2[] = "👩đŸŧ‍đŸĻą"; + inline constexpr const char woman_curly_haired_medium_light_skin_tone[] = "👩đŸŧ‍đŸĻą"; + inline constexpr const char woman_curly_haired_tone3[] = "👩đŸŊ‍đŸĻą"; + inline constexpr const char woman_curly_haired_medium_skin_tone[] = "👩đŸŊ‍đŸĻą"; + inline constexpr const char woman_curly_haired_tone4[] = "👩🏾‍đŸĻą"; + inline constexpr const char woman_curly_haired_medium_dark_skin_tone[] = "👩🏾‍đŸĻą"; + inline constexpr const char woman_curly_haired_tone5[] = "👩đŸŋ‍đŸĻą"; + inline constexpr const char woman_curly_haired_dark_skin_tone[] = "👩đŸŋ‍đŸĻą"; + inline constexpr const char man_curly_haired[] = "👨‍đŸĻą"; + inline constexpr const char man_curly_haired_tone1[] = "👨đŸģ‍đŸĻą"; + inline constexpr const char man_curly_haired_light_skin_tone[] = "👨đŸģ‍đŸĻą"; + inline constexpr const char man_curly_haired_tone2[] = "👨đŸŧ‍đŸĻą"; + inline constexpr const char man_curly_haired_medium_light_skin_tone[] = "👨đŸŧ‍đŸĻą"; + inline constexpr const char man_curly_haired_tone3[] = "👨đŸŊ‍đŸĻą"; + inline constexpr const char man_curly_haired_medium_skin_tone[] = "👨đŸŊ‍đŸĻą"; + inline constexpr const char man_curly_haired_tone4[] = "👨🏾‍đŸĻą"; + inline constexpr const char man_curly_haired_medium_dark_skin_tone[] = "👨🏾‍đŸĻą"; + inline constexpr const char man_curly_haired_tone5[] = "👨đŸŋ‍đŸĻą"; + inline constexpr const char man_curly_haired_dark_skin_tone[] = "👨đŸŋ‍đŸĻą"; + inline constexpr const char person_red_hair[] = "🧑‍đŸĻ°"; + inline constexpr const char person_tone1_red_hair[] = "🧑đŸģ‍đŸĻ°"; + inline constexpr const char person_light_skin_tone_red_hair[] = "🧑đŸģ‍đŸĻ°"; + inline constexpr const char person_tone2_red_hair[] = "🧑đŸŧ‍đŸĻ°"; + inline constexpr const char person_medium_light_skin_tone_red_hair[] = "🧑đŸŧ‍đŸĻ°"; + inline constexpr const char person_tone3_red_hair[] = "🧑đŸŊ‍đŸĻ°"; + inline constexpr const char person_medium_skin_tone_red_hair[] = "🧑đŸŊ‍đŸĻ°"; + inline constexpr const char person_tone4_red_hair[] = "🧑🏾‍đŸĻ°"; + inline constexpr const char person_medium_dark_skin_tone_red_hair[] = "🧑🏾‍đŸĻ°"; + inline constexpr const char person_tone5_red_hair[] = "🧑đŸŋ‍đŸĻ°"; + inline constexpr const char person_dark_skin_tone_red_hair[] = "🧑đŸŋ‍đŸĻ°"; + inline constexpr const char woman_red_haired[] = "👩‍đŸĻ°"; + inline constexpr const char woman_red_haired_tone1[] = "👩đŸģ‍đŸĻ°"; + inline constexpr const char woman_red_haired_light_skin_tone[] = "👩đŸģ‍đŸĻ°"; + inline constexpr const char woman_red_haired_tone2[] = "👩đŸŧ‍đŸĻ°"; + inline constexpr const char woman_red_haired_medium_light_skin_tone[] = "👩đŸŧ‍đŸĻ°"; + inline constexpr const char woman_red_haired_tone3[] = "👩đŸŊ‍đŸĻ°"; + inline constexpr const char woman_red_haired_medium_skin_tone[] = "👩đŸŊ‍đŸĻ°"; + inline constexpr const char woman_red_haired_tone4[] = "👩🏾‍đŸĻ°"; + inline constexpr const char woman_red_haired_medium_dark_skin_tone[] = "👩🏾‍đŸĻ°"; + inline constexpr const char woman_red_haired_tone5[] = "👩đŸŋ‍đŸĻ°"; + inline constexpr const char woman_red_haired_dark_skin_tone[] = "👩đŸŋ‍đŸĻ°"; + inline constexpr const char man_red_haired[] = "👨‍đŸĻ°"; + inline constexpr const char man_red_haired_tone1[] = "👨đŸģ‍đŸĻ°"; + inline constexpr const char man_red_haired_light_skin_tone[] = "👨đŸģ‍đŸĻ°"; + inline constexpr const char man_red_haired_tone2[] = "👨đŸŧ‍đŸĻ°"; + inline constexpr const char man_red_haired_medium_light_skin_tone[] = "👨đŸŧ‍đŸĻ°"; + inline constexpr const char man_red_haired_tone3[] = "👨đŸŊ‍đŸĻ°"; + inline constexpr const char man_red_haired_medium_skin_tone[] = "👨đŸŊ‍đŸĻ°"; + inline constexpr const char man_red_haired_tone4[] = "👨🏾‍đŸĻ°"; + inline constexpr const char man_red_haired_medium_dark_skin_tone[] = "👨🏾‍đŸĻ°"; + inline constexpr const char man_red_haired_tone5[] = "👨đŸŋ‍đŸĻ°"; + inline constexpr const char man_red_haired_dark_skin_tone[] = "👨đŸŋ‍đŸĻ°"; + inline constexpr const char blond_haired_person[] = "👱"; + inline constexpr const char person_with_blond_hair[] = "👱"; + inline constexpr const char blond_haired_person_tone1[] = "👱đŸģ"; + inline constexpr const char person_with_blond_hair_tone1[] = "👱đŸģ"; + inline constexpr const char blond_haired_person_tone2[] = "👱đŸŧ"; + inline constexpr const char person_with_blond_hair_tone2[] = "👱đŸŧ"; + inline constexpr const char blond_haired_person_tone3[] = "👱đŸŊ"; + inline constexpr const char person_with_blond_hair_tone3[] = "👱đŸŊ"; + inline constexpr const char blond_haired_person_tone4[] = "👱🏾"; + inline constexpr const char person_with_blond_hair_tone4[] = "👱🏾"; + inline constexpr const char blond_haired_person_tone5[] = "👱đŸŋ"; + inline constexpr const char person_with_blond_hair_tone5[] = "👱đŸŋ"; + inline constexpr const char blond_haired_woman[] = "👱‍♀ī¸"; + inline constexpr const char blond_haired_woman_tone1[] = "👱đŸģ‍♀ī¸"; + inline constexpr const char blond_haired_woman_light_skin_tone[] = "👱đŸģ‍♀ī¸"; + inline constexpr const char blond_haired_woman_tone2[] = "👱đŸŧ‍♀ī¸"; + inline constexpr const char blond_haired_woman_medium_light_skin_tone[] = "👱đŸŧ‍♀ī¸"; + inline constexpr const char blond_haired_woman_tone3[] = "👱đŸŊ‍♀ī¸"; + inline constexpr const char blond_haired_woman_medium_skin_tone[] = "👱đŸŊ‍♀ī¸"; + inline constexpr const char blond_haired_woman_tone4[] = "👱🏾‍♀ī¸"; + inline constexpr const char blond_haired_woman_medium_dark_skin_tone[] = "👱🏾‍♀ī¸"; + inline constexpr const char blond_haired_woman_tone5[] = "👱đŸŋ‍♀ī¸"; + inline constexpr const char blond_haired_woman_dark_skin_tone[] = "👱đŸŋ‍♀ī¸"; + inline constexpr const char blond_haired_man[] = "👱‍♂ī¸"; + inline constexpr const char blond_haired_man_tone1[] = "👱đŸģ‍♂ī¸"; + inline constexpr const char blond_haired_man_light_skin_tone[] = "👱đŸģ‍♂ī¸"; + inline constexpr const char blond_haired_man_tone2[] = "👱đŸŧ‍♂ī¸"; + inline constexpr const char blond_haired_man_medium_light_skin_tone[] = "👱đŸŧ‍♂ī¸"; + inline constexpr const char blond_haired_man_tone3[] = "👱đŸŊ‍♂ī¸"; + inline constexpr const char blond_haired_man_medium_skin_tone[] = "👱đŸŊ‍♂ī¸"; + inline constexpr const char blond_haired_man_tone4[] = "👱🏾‍♂ī¸"; + inline constexpr const char blond_haired_man_medium_dark_skin_tone[] = "👱🏾‍♂ī¸"; + inline constexpr const char blond_haired_man_tone5[] = "👱đŸŋ‍♂ī¸"; + inline constexpr const char blond_haired_man_dark_skin_tone[] = "👱đŸŋ‍♂ī¸"; + inline constexpr const char person_white_hair[] = "🧑‍đŸĻŗ"; + inline constexpr const char person_tone1_white_hair[] = "🧑đŸģ‍đŸĻŗ"; + inline constexpr const char person_light_skin_tone_white_hair[] = "🧑đŸģ‍đŸĻŗ"; + inline constexpr const char person_tone2_white_hair[] = "🧑đŸŧ‍đŸĻŗ"; + inline constexpr const char person_medium_light_skin_tone_white_hair[] = "🧑đŸŧ‍đŸĻŗ"; + inline constexpr const char person_tone3_white_hair[] = "🧑đŸŊ‍đŸĻŗ"; + inline constexpr const char person_medium_skin_tone_white_hair[] = "🧑đŸŊ‍đŸĻŗ"; + inline constexpr const char person_tone4_white_hair[] = "🧑🏾‍đŸĻŗ"; + inline constexpr const char person_medium_dark_skin_tone_white_hair[] = "🧑🏾‍đŸĻŗ"; + inline constexpr const char person_tone5_white_hair[] = "🧑đŸŋ‍đŸĻŗ"; + inline constexpr const char person_dark_skin_tone_white_hair[] = "🧑đŸŋ‍đŸĻŗ"; + inline constexpr const char woman_white_haired[] = "👩‍đŸĻŗ"; + inline constexpr const char woman_white_haired_tone1[] = "👩đŸģ‍đŸĻŗ"; + inline constexpr const char woman_white_haired_light_skin_tone[] = "👩đŸģ‍đŸĻŗ"; + inline constexpr const char woman_white_haired_tone2[] = "👩đŸŧ‍đŸĻŗ"; + inline constexpr const char woman_white_haired_medium_light_skin_tone[] = "👩đŸŧ‍đŸĻŗ"; + inline constexpr const char woman_white_haired_tone3[] = "👩đŸŊ‍đŸĻŗ"; + inline constexpr const char woman_white_haired_medium_skin_tone[] = "👩đŸŊ‍đŸĻŗ"; + inline constexpr const char woman_white_haired_tone4[] = "👩🏾‍đŸĻŗ"; + inline constexpr const char woman_white_haired_medium_dark_skin_tone[] = "👩🏾‍đŸĻŗ"; + inline constexpr const char woman_white_haired_tone5[] = "👩đŸŋ‍đŸĻŗ"; + inline constexpr const char woman_white_haired_dark_skin_tone[] = "👩đŸŋ‍đŸĻŗ"; + inline constexpr const char man_white_haired[] = "👨‍đŸĻŗ"; + inline constexpr const char man_white_haired_tone1[] = "👨đŸģ‍đŸĻŗ"; + inline constexpr const char man_white_haired_light_skin_tone[] = "👨đŸģ‍đŸĻŗ"; + inline constexpr const char man_white_haired_tone2[] = "👨đŸŧ‍đŸĻŗ"; + inline constexpr const char man_white_haired_medium_light_skin_tone[] = "👨đŸŧ‍đŸĻŗ"; + inline constexpr const char man_white_haired_tone3[] = "👨đŸŊ‍đŸĻŗ"; + inline constexpr const char man_white_haired_medium_skin_tone[] = "👨đŸŊ‍đŸĻŗ"; + inline constexpr const char man_white_haired_tone4[] = "👨🏾‍đŸĻŗ"; + inline constexpr const char man_white_haired_medium_dark_skin_tone[] = "👨🏾‍đŸĻŗ"; + inline constexpr const char man_white_haired_tone5[] = "👨đŸŋ‍đŸĻŗ"; + inline constexpr const char man_white_haired_dark_skin_tone[] = "👨đŸŋ‍đŸĻŗ"; + inline constexpr const char person_bald[] = "🧑‍đŸĻ˛"; + inline constexpr const char person_tone1_bald[] = "🧑đŸģ‍đŸĻ˛"; + inline constexpr const char person_light_skin_tone_bald[] = "🧑đŸģ‍đŸĻ˛"; + inline constexpr const char person_tone2_bald[] = "🧑đŸŧ‍đŸĻ˛"; + inline constexpr const char person_medium_light_skin_tone_bald[] = "🧑đŸŧ‍đŸĻ˛"; + inline constexpr const char person_tone3_bald[] = "🧑đŸŊ‍đŸĻ˛"; + inline constexpr const char person_medium_skin_tone_bald[] = "🧑đŸŊ‍đŸĻ˛"; + inline constexpr const char person_tone4_bald[] = "🧑🏾‍đŸĻ˛"; + inline constexpr const char person_medium_dark_skin_tone_bald[] = "🧑🏾‍đŸĻ˛"; + inline constexpr const char person_tone5_bald[] = "🧑đŸŋ‍đŸĻ˛"; + inline constexpr const char person_dark_skin_tone_bald[] = "🧑đŸŋ‍đŸĻ˛"; + inline constexpr const char woman_bald[] = "👩‍đŸĻ˛"; + inline constexpr const char woman_bald_tone1[] = "👩đŸģ‍đŸĻ˛"; + inline constexpr const char woman_bald_light_skin_tone[] = "👩đŸģ‍đŸĻ˛"; + inline constexpr const char woman_bald_tone2[] = "👩đŸŧ‍đŸĻ˛"; + inline constexpr const char woman_bald_medium_light_skin_tone[] = "👩đŸŧ‍đŸĻ˛"; + inline constexpr const char woman_bald_tone3[] = "👩đŸŊ‍đŸĻ˛"; + inline constexpr const char woman_bald_medium_skin_tone[] = "👩đŸŊ‍đŸĻ˛"; + inline constexpr const char woman_bald_tone4[] = "👩🏾‍đŸĻ˛"; + inline constexpr const char woman_bald_medium_dark_skin_tone[] = "👩🏾‍đŸĻ˛"; + inline constexpr const char woman_bald_tone5[] = "👩đŸŋ‍đŸĻ˛"; + inline constexpr const char woman_bald_dark_skin_tone[] = "👩đŸŋ‍đŸĻ˛"; + inline constexpr const char man_bald[] = "👨‍đŸĻ˛"; + inline constexpr const char man_bald_tone1[] = "👨đŸģ‍đŸĻ˛"; + inline constexpr const char man_bald_light_skin_tone[] = "👨đŸģ‍đŸĻ˛"; + inline constexpr const char man_bald_tone2[] = "👨đŸŧ‍đŸĻ˛"; + inline constexpr const char man_bald_medium_light_skin_tone[] = "👨đŸŧ‍đŸĻ˛"; + inline constexpr const char man_bald_tone3[] = "👨đŸŊ‍đŸĻ˛"; + inline constexpr const char man_bald_medium_skin_tone[] = "👨đŸŊ‍đŸĻ˛"; + inline constexpr const char man_bald_tone4[] = "👨🏾‍đŸĻ˛"; + inline constexpr const char man_bald_medium_dark_skin_tone[] = "👨🏾‍đŸĻ˛"; + inline constexpr const char man_bald_tone5[] = "👨đŸŋ‍đŸĻ˛"; + inline constexpr const char man_bald_dark_skin_tone[] = "👨đŸŋ‍đŸĻ˛"; + inline constexpr const char bearded_person[] = "🧔"; + inline constexpr const char bearded_person_tone1[] = "🧔đŸģ"; + inline constexpr const char bearded_person_light_skin_tone[] = "🧔đŸģ"; + inline constexpr const char bearded_person_tone2[] = "🧔đŸŧ"; + inline constexpr const char bearded_person_medium_light_skin_tone[] = "🧔đŸŧ"; + inline constexpr const char bearded_person_tone3[] = "🧔đŸŊ"; + inline constexpr const char bearded_person_medium_skin_tone[] = "🧔đŸŊ"; + inline constexpr const char bearded_person_tone4[] = "🧔🏾"; + inline constexpr const char bearded_person_medium_dark_skin_tone[] = "🧔🏾"; + inline constexpr const char bearded_person_tone5[] = "🧔đŸŋ"; + inline constexpr const char bearded_person_dark_skin_tone[] = "🧔đŸŋ"; + inline constexpr const char woman_beard[] = "🧔‍♀ī¸"; + inline constexpr const char woman_tone1_beard[] = "🧔đŸģ‍♀ī¸"; + inline constexpr const char woman_light_skin_tone_beard[] = "🧔đŸģ‍♀ī¸"; + inline constexpr const char woman_tone2_beard[] = "🧔đŸŧ‍♀ī¸"; + inline constexpr const char woman_medium_light_skin_tone_beard[] = "🧔đŸŧ‍♀ī¸"; + inline constexpr const char woman_tone3_beard[] = "🧔đŸŊ‍♀ī¸"; + inline constexpr const char woman_medium_skin_tone_beard[] = "🧔đŸŊ‍♀ī¸"; + inline constexpr const char woman_tone4_beard[] = "🧔🏾‍♀ī¸"; + inline constexpr const char woman_medium_dark_skin_tone_beard[] = "🧔🏾‍♀ī¸"; + inline constexpr const char woman_tone5_beard[] = "🧔đŸŋ‍♀ī¸"; + inline constexpr const char woman_dark_skin_tone_beard[] = "🧔đŸŋ‍♀ī¸"; + inline constexpr const char man_beard[] = "🧔‍♂ī¸"; + inline constexpr const char man_tone1_beard[] = "🧔đŸģ‍♂ī¸"; + inline constexpr const char man_light_skin_tone_beard[] = "🧔đŸģ‍♂ī¸"; + inline constexpr const char man_tone2_beard[] = "🧔đŸŧ‍♂ī¸"; + inline constexpr const char man_medium_light_skin_tone_beard[] = "🧔đŸŧ‍♂ī¸"; + inline constexpr const char man_tone3_beard[] = "🧔đŸŊ‍♂ī¸"; + inline constexpr const char man_medium_skin_tone_beard[] = "🧔đŸŊ‍♂ī¸"; + inline constexpr const char man_tone4_beard[] = "🧔🏾‍♂ī¸"; + inline constexpr const char man_medium_dark_skin_tone_beard[] = "🧔🏾‍♂ī¸"; + inline constexpr const char man_tone5_beard[] = "🧔đŸŋ‍♂ī¸"; + inline constexpr const char man_dark_skin_tone_beard[] = "🧔đŸŋ‍♂ī¸"; + inline constexpr const char older_adult[] = "🧓"; + inline constexpr const char older_adult_tone1[] = "🧓đŸģ"; + inline constexpr const char older_adult_light_skin_tone[] = "🧓đŸģ"; + inline constexpr const char older_adult_tone2[] = "🧓đŸŧ"; + inline constexpr const char older_adult_medium_light_skin_tone[] = "🧓đŸŧ"; + inline constexpr const char older_adult_tone3[] = "🧓đŸŊ"; + inline constexpr const char older_adult_medium_skin_tone[] = "🧓đŸŊ"; + inline constexpr const char older_adult_tone4[] = "🧓🏾"; + inline constexpr const char older_adult_medium_dark_skin_tone[] = "🧓🏾"; + inline constexpr const char older_adult_tone5[] = "🧓đŸŋ"; + inline constexpr const char older_adult_dark_skin_tone[] = "🧓đŸŋ"; + inline constexpr const char older_woman[] = "đŸ‘ĩ"; + inline constexpr const char grandma[] = "đŸ‘ĩ"; + inline constexpr const char older_woman_tone1[] = "đŸ‘ĩđŸģ"; + inline constexpr const char grandma_tone1[] = "đŸ‘ĩđŸģ"; + inline constexpr const char older_woman_tone2[] = "đŸ‘ĩđŸŧ"; + inline constexpr const char grandma_tone2[] = "đŸ‘ĩđŸŧ"; + inline constexpr const char older_woman_tone3[] = "đŸ‘ĩđŸŊ"; + inline constexpr const char grandma_tone3[] = "đŸ‘ĩđŸŊ"; + inline constexpr const char older_woman_tone4[] = "đŸ‘ĩ🏾"; + inline constexpr const char grandma_tone4[] = "đŸ‘ĩ🏾"; + inline constexpr const char older_woman_tone5[] = "đŸ‘ĩđŸŋ"; + inline constexpr const char grandma_tone5[] = "đŸ‘ĩđŸŋ"; + inline constexpr const char older_man[] = "👴"; + inline constexpr const char older_man_tone1[] = "👴đŸģ"; + inline constexpr const char older_man_tone2[] = "👴đŸŧ"; + inline constexpr const char older_man_tone3[] = "👴đŸŊ"; + inline constexpr const char older_man_tone4[] = "👴🏾"; + inline constexpr const char older_man_tone5[] = "👴đŸŋ"; + inline constexpr const char man_with_chinese_cap[] = "👲"; + inline constexpr const char man_with_gua_pi_mao[] = "👲"; + inline constexpr const char man_with_chinese_cap_tone1[] = "👲đŸģ"; + inline constexpr const char man_with_gua_pi_mao_tone1[] = "👲đŸģ"; + inline constexpr const char man_with_chinese_cap_tone2[] = "👲đŸŧ"; + inline constexpr const char man_with_gua_pi_mao_tone2[] = "👲đŸŧ"; + inline constexpr const char man_with_chinese_cap_tone3[] = "👲đŸŊ"; + inline constexpr const char man_with_gua_pi_mao_tone3[] = "👲đŸŊ"; + inline constexpr const char man_with_chinese_cap_tone4[] = "👲🏾"; + inline constexpr const char man_with_gua_pi_mao_tone4[] = "👲🏾"; + inline constexpr const char man_with_chinese_cap_tone5[] = "👲đŸŋ"; + inline constexpr const char man_with_gua_pi_mao_tone5[] = "👲đŸŋ"; + inline constexpr const char person_wearing_turban[] = "đŸ‘ŗ"; + inline constexpr const char man_with_turban[] = "đŸ‘ŗ"; + inline constexpr const char person_wearing_turban_tone1[] = "đŸ‘ŗđŸģ"; + inline constexpr const char man_with_turban_tone1[] = "đŸ‘ŗđŸģ"; + inline constexpr const char person_wearing_turban_tone2[] = "đŸ‘ŗđŸŧ"; + inline constexpr const char man_with_turban_tone2[] = "đŸ‘ŗđŸŧ"; + inline constexpr const char person_wearing_turban_tone3[] = "đŸ‘ŗđŸŊ"; + inline constexpr const char man_with_turban_tone3[] = "đŸ‘ŗđŸŊ"; + inline constexpr const char person_wearing_turban_tone4[] = "đŸ‘ŗ🏾"; + inline constexpr const char man_with_turban_tone4[] = "đŸ‘ŗ🏾"; + inline constexpr const char person_wearing_turban_tone5[] = "đŸ‘ŗđŸŋ"; + inline constexpr const char man_with_turban_tone5[] = "đŸ‘ŗđŸŋ"; + inline constexpr const char woman_wearing_turban[] = "đŸ‘ŗ‍♀ī¸"; + inline constexpr const char woman_wearing_turban_tone1[] = "đŸ‘ŗđŸģ‍♀ī¸"; + inline constexpr const char woman_wearing_turban_light_skin_tone[] = "đŸ‘ŗđŸģ‍♀ī¸"; + inline constexpr const char woman_wearing_turban_tone2[] = "đŸ‘ŗđŸŧ‍♀ī¸"; + inline constexpr const char woman_wearing_turban_medium_light_skin_tone[] = "đŸ‘ŗđŸŧ‍♀ī¸"; + inline constexpr const char woman_wearing_turban_tone3[] = "đŸ‘ŗđŸŊ‍♀ī¸"; + inline constexpr const char woman_wearing_turban_medium_skin_tone[] = "đŸ‘ŗđŸŊ‍♀ī¸"; + inline constexpr const char woman_wearing_turban_tone4[] = "đŸ‘ŗ🏾‍♀ī¸"; + inline constexpr const char woman_wearing_turban_medium_dark_skin_tone[] = "đŸ‘ŗ🏾‍♀ī¸"; + inline constexpr const char woman_wearing_turban_tone5[] = "đŸ‘ŗđŸŋ‍♀ī¸"; + inline constexpr const char woman_wearing_turban_dark_skin_tone[] = "đŸ‘ŗđŸŋ‍♀ī¸"; + inline constexpr const char man_wearing_turban[] = "đŸ‘ŗ‍♂ī¸"; + inline constexpr const char man_wearing_turban_tone1[] = "đŸ‘ŗđŸģ‍♂ī¸"; + inline constexpr const char man_wearing_turban_light_skin_tone[] = "đŸ‘ŗđŸģ‍♂ī¸"; + inline constexpr const char man_wearing_turban_tone2[] = "đŸ‘ŗđŸŧ‍♂ī¸"; + inline constexpr const char man_wearing_turban_medium_light_skin_tone[] = "đŸ‘ŗđŸŧ‍♂ī¸"; + inline constexpr const char man_wearing_turban_tone3[] = "đŸ‘ŗđŸŊ‍♂ī¸"; + inline constexpr const char man_wearing_turban_medium_skin_tone[] = "đŸ‘ŗđŸŊ‍♂ī¸"; + inline constexpr const char man_wearing_turban_tone4[] = "đŸ‘ŗ🏾‍♂ī¸"; + inline constexpr const char man_wearing_turban_medium_dark_skin_tone[] = "đŸ‘ŗ🏾‍♂ī¸"; + inline constexpr const char man_wearing_turban_tone5[] = "đŸ‘ŗđŸŋ‍♂ī¸"; + inline constexpr const char man_wearing_turban_dark_skin_tone[] = "đŸ‘ŗđŸŋ‍♂ī¸"; + inline constexpr const char woman_with_headscarf[] = "🧕"; + inline constexpr const char woman_with_headscarf_tone1[] = "🧕đŸģ"; + inline constexpr const char woman_with_headscarf_light_skin_tone[] = "🧕đŸģ"; + inline constexpr const char woman_with_headscarf_tone2[] = "🧕đŸŧ"; + inline constexpr const char woman_with_headscarf_medium_light_skin_tone[] = "🧕đŸŧ"; + inline constexpr const char woman_with_headscarf_tone3[] = "🧕đŸŊ"; + inline constexpr const char woman_with_headscarf_medium_skin_tone[] = "🧕đŸŊ"; + inline constexpr const char woman_with_headscarf_tone4[] = "🧕🏾"; + inline constexpr const char woman_with_headscarf_medium_dark_skin_tone[] = "🧕🏾"; + inline constexpr const char woman_with_headscarf_tone5[] = "🧕đŸŋ"; + inline constexpr const char woman_with_headscarf_dark_skin_tone[] = "🧕đŸŋ"; + inline constexpr const char police_officer[] = "👮"; + inline constexpr const char cop[] = "👮"; + inline constexpr const char police_officer_tone1[] = "👮đŸģ"; + inline constexpr const char cop_tone1[] = "👮đŸģ"; + inline constexpr const char police_officer_tone2[] = "👮đŸŧ"; + inline constexpr const char cop_tone2[] = "👮đŸŧ"; + inline constexpr const char police_officer_tone3[] = "👮đŸŊ"; + inline constexpr const char cop_tone3[] = "👮đŸŊ"; + inline constexpr const char police_officer_tone4[] = "👮🏾"; + inline constexpr const char cop_tone4[] = "👮🏾"; + inline constexpr const char police_officer_tone5[] = "👮đŸŋ"; + inline constexpr const char cop_tone5[] = "👮đŸŋ"; + inline constexpr const char woman_police_officer[] = "👮‍♀ī¸"; + inline constexpr const char woman_police_officer_tone1[] = "👮đŸģ‍♀ī¸"; + inline constexpr const char woman_police_officer_light_skin_tone[] = "👮đŸģ‍♀ī¸"; + inline constexpr const char woman_police_officer_tone2[] = "👮đŸŧ‍♀ī¸"; + inline constexpr const char woman_police_officer_medium_light_skin_tone[] = "👮đŸŧ‍♀ī¸"; + inline constexpr const char woman_police_officer_tone3[] = "👮đŸŊ‍♀ī¸"; + inline constexpr const char woman_police_officer_medium_skin_tone[] = "👮đŸŊ‍♀ī¸"; + inline constexpr const char woman_police_officer_tone4[] = "👮🏾‍♀ī¸"; + inline constexpr const char woman_police_officer_medium_dark_skin_tone[] = "👮🏾‍♀ī¸"; + inline constexpr const char woman_police_officer_tone5[] = "👮đŸŋ‍♀ī¸"; + inline constexpr const char woman_police_officer_dark_skin_tone[] = "👮đŸŋ‍♀ī¸"; + inline constexpr const char man_police_officer[] = "👮‍♂ī¸"; + inline constexpr const char man_police_officer_tone1[] = "👮đŸģ‍♂ī¸"; + inline constexpr const char man_police_officer_light_skin_tone[] = "👮đŸģ‍♂ī¸"; + inline constexpr const char man_police_officer_tone2[] = "👮đŸŧ‍♂ī¸"; + inline constexpr const char man_police_officer_medium_light_skin_tone[] = "👮đŸŧ‍♂ī¸"; + inline constexpr const char man_police_officer_tone3[] = "👮đŸŊ‍♂ī¸"; + inline constexpr const char man_police_officer_medium_skin_tone[] = "👮đŸŊ‍♂ī¸"; + inline constexpr const char man_police_officer_tone4[] = "👮🏾‍♂ī¸"; + inline constexpr const char man_police_officer_medium_dark_skin_tone[] = "👮🏾‍♂ī¸"; + inline constexpr const char man_police_officer_tone5[] = "👮đŸŋ‍♂ī¸"; + inline constexpr const char man_police_officer_dark_skin_tone[] = "👮đŸŋ‍♂ī¸"; + inline constexpr const char construction_worker[] = "👷"; + inline constexpr const char construction_worker_tone1[] = "👷đŸģ"; + inline constexpr const char construction_worker_tone2[] = "👷đŸŧ"; + inline constexpr const char construction_worker_tone3[] = "👷đŸŊ"; + inline constexpr const char construction_worker_tone4[] = "👷🏾"; + inline constexpr const char construction_worker_tone5[] = "👷đŸŋ"; + inline constexpr const char woman_construction_worker[] = "👷‍♀ī¸"; + inline constexpr const char woman_construction_worker_tone1[] = "👷đŸģ‍♀ī¸"; + inline constexpr const char woman_construction_worker_light_skin_tone[] = "👷đŸģ‍♀ī¸"; + inline constexpr const char woman_construction_worker_tone2[] = "👷đŸŧ‍♀ī¸"; + inline constexpr const char woman_construction_worker_medium_light_skin_tone[] = "👷đŸŧ‍♀ī¸"; + inline constexpr const char woman_construction_worker_tone3[] = "👷đŸŊ‍♀ī¸"; + inline constexpr const char woman_construction_worker_medium_skin_tone[] = "👷đŸŊ‍♀ī¸"; + inline constexpr const char woman_construction_worker_tone4[] = "👷🏾‍♀ī¸"; + inline constexpr const char woman_construction_worker_medium_dark_skin_tone[] = "👷🏾‍♀ī¸"; + inline constexpr const char woman_construction_worker_tone5[] = "👷đŸŋ‍♀ī¸"; + inline constexpr const char woman_construction_worker_dark_skin_tone[] = "👷đŸŋ‍♀ī¸"; + inline constexpr const char man_construction_worker[] = "👷‍♂ī¸"; + inline constexpr const char man_construction_worker_tone1[] = "👷đŸģ‍♂ī¸"; + inline constexpr const char man_construction_worker_light_skin_tone[] = "👷đŸģ‍♂ī¸"; + inline constexpr const char man_construction_worker_tone2[] = "👷đŸŧ‍♂ī¸"; + inline constexpr const char man_construction_worker_medium_light_skin_tone[] = "👷đŸŧ‍♂ī¸"; + inline constexpr const char man_construction_worker_tone3[] = "👷đŸŊ‍♂ī¸"; + inline constexpr const char man_construction_worker_medium_skin_tone[] = "👷đŸŊ‍♂ī¸"; + inline constexpr const char man_construction_worker_tone4[] = "👷🏾‍♂ī¸"; + inline constexpr const char man_construction_worker_medium_dark_skin_tone[] = "👷🏾‍♂ī¸"; + inline constexpr const char man_construction_worker_tone5[] = "👷đŸŋ‍♂ī¸"; + inline constexpr const char man_construction_worker_dark_skin_tone[] = "👷đŸŋ‍♂ī¸"; + inline constexpr const char guard[] = "💂"; + inline constexpr const char guardsman[] = "💂"; + inline constexpr const char guard_tone1[] = "💂đŸģ"; + inline constexpr const char guardsman_tone1[] = "💂đŸģ"; + inline constexpr const char guard_tone2[] = "💂đŸŧ"; + inline constexpr const char guardsman_tone2[] = "💂đŸŧ"; + inline constexpr const char guard_tone3[] = "💂đŸŊ"; + inline constexpr const char guardsman_tone3[] = "💂đŸŊ"; + inline constexpr const char guard_tone4[] = "💂🏾"; + inline constexpr const char guardsman_tone4[] = "💂🏾"; + inline constexpr const char guard_tone5[] = "💂đŸŋ"; + inline constexpr const char guardsman_tone5[] = "💂đŸŋ"; + inline constexpr const char woman_guard[] = "💂‍♀ī¸"; + inline constexpr const char woman_guard_tone1[] = "💂đŸģ‍♀ī¸"; + inline constexpr const char woman_guard_light_skin_tone[] = "💂đŸģ‍♀ī¸"; + inline constexpr const char woman_guard_tone2[] = "💂đŸŧ‍♀ī¸"; + inline constexpr const char woman_guard_medium_light_skin_tone[] = "💂đŸŧ‍♀ī¸"; + inline constexpr const char woman_guard_tone3[] = "💂đŸŊ‍♀ī¸"; + inline constexpr const char woman_guard_medium_skin_tone[] = "💂đŸŊ‍♀ī¸"; + inline constexpr const char woman_guard_tone4[] = "💂🏾‍♀ī¸"; + inline constexpr const char woman_guard_medium_dark_skin_tone[] = "💂🏾‍♀ī¸"; + inline constexpr const char woman_guard_tone5[] = "💂đŸŋ‍♀ī¸"; + inline constexpr const char woman_guard_dark_skin_tone[] = "💂đŸŋ‍♀ī¸"; + inline constexpr const char man_guard[] = "💂‍♂ī¸"; + inline constexpr const char man_guard_tone1[] = "💂đŸģ‍♂ī¸"; + inline constexpr const char man_guard_light_skin_tone[] = "💂đŸģ‍♂ī¸"; + inline constexpr const char man_guard_tone2[] = "💂đŸŧ‍♂ī¸"; + inline constexpr const char man_guard_medium_light_skin_tone[] = "💂đŸŧ‍♂ī¸"; + inline constexpr const char man_guard_tone3[] = "💂đŸŊ‍♂ī¸"; + inline constexpr const char man_guard_medium_skin_tone[] = "💂đŸŊ‍♂ī¸"; + inline constexpr const char man_guard_tone4[] = "💂🏾‍♂ī¸"; + inline constexpr const char man_guard_medium_dark_skin_tone[] = "💂🏾‍♂ī¸"; + inline constexpr const char man_guard_tone5[] = "💂đŸŋ‍♂ī¸"; + inline constexpr const char man_guard_dark_skin_tone[] = "💂đŸŋ‍♂ī¸"; + inline constexpr const char detective[] = "đŸ•ĩī¸"; + inline constexpr const char spy[] = "đŸ•ĩī¸"; + inline constexpr const char sleuth_or_spy[] = "đŸ•ĩī¸"; + inline constexpr const char detective_tone1[] = "đŸ•ĩđŸģ"; + inline constexpr const char spy_tone1[] = "đŸ•ĩđŸģ"; + inline constexpr const char sleuth_or_spy_tone1[] = "đŸ•ĩđŸģ"; + inline constexpr const char detective_tone2[] = "đŸ•ĩđŸŧ"; + inline constexpr const char spy_tone2[] = "đŸ•ĩđŸŧ"; + inline constexpr const char sleuth_or_spy_tone2[] = "đŸ•ĩđŸŧ"; + inline constexpr const char detective_tone3[] = "đŸ•ĩđŸŊ"; + inline constexpr const char spy_tone3[] = "đŸ•ĩđŸŊ"; + inline constexpr const char sleuth_or_spy_tone3[] = "đŸ•ĩđŸŊ"; + inline constexpr const char detective_tone4[] = "đŸ•ĩ🏾"; + inline constexpr const char spy_tone4[] = "đŸ•ĩ🏾"; + inline constexpr const char sleuth_or_spy_tone4[] = "đŸ•ĩ🏾"; + inline constexpr const char detective_tone5[] = "đŸ•ĩđŸŋ"; + inline constexpr const char spy_tone5[] = "đŸ•ĩđŸŋ"; + inline constexpr const char sleuth_or_spy_tone5[] = "đŸ•ĩđŸŋ"; + inline constexpr const char woman_detective[] = "đŸ•ĩī¸â€â™€ī¸"; + inline constexpr const char woman_detective_tone1[] = "đŸ•ĩđŸģ‍♀ī¸"; + inline constexpr const char woman_detective_light_skin_tone[] = "đŸ•ĩđŸģ‍♀ī¸"; + inline constexpr const char woman_detective_tone2[] = "đŸ•ĩđŸŧ‍♀ī¸"; + inline constexpr const char woman_detective_medium_light_skin_tone[] = "đŸ•ĩđŸŧ‍♀ī¸"; + inline constexpr const char woman_detective_tone3[] = "đŸ•ĩđŸŊ‍♀ī¸"; + inline constexpr const char woman_detective_medium_skin_tone[] = "đŸ•ĩđŸŊ‍♀ī¸"; + inline constexpr const char woman_detective_tone4[] = "đŸ•ĩ🏾‍♀ī¸"; + inline constexpr const char woman_detective_medium_dark_skin_tone[] = "đŸ•ĩ🏾‍♀ī¸"; + inline constexpr const char woman_detective_tone5[] = "đŸ•ĩđŸŋ‍♀ī¸"; + inline constexpr const char woman_detective_dark_skin_tone[] = "đŸ•ĩđŸŋ‍♀ī¸"; + inline constexpr const char man_detective[] = "đŸ•ĩī¸â€â™‚ī¸"; + inline constexpr const char man_detective_tone1[] = "đŸ•ĩđŸģ‍♂ī¸"; + inline constexpr const char man_detective_light_skin_tone[] = "đŸ•ĩđŸģ‍♂ī¸"; + inline constexpr const char man_detective_tone2[] = "đŸ•ĩđŸŧ‍♂ī¸"; + inline constexpr const char man_detective_medium_light_skin_tone[] = "đŸ•ĩđŸŧ‍♂ī¸"; + inline constexpr const char man_detective_tone3[] = "đŸ•ĩđŸŊ‍♂ī¸"; + inline constexpr const char man_detective_medium_skin_tone[] = "đŸ•ĩđŸŊ‍♂ī¸"; + inline constexpr const char man_detective_tone4[] = "đŸ•ĩ🏾‍♂ī¸"; + inline constexpr const char man_detective_medium_dark_skin_tone[] = "đŸ•ĩ🏾‍♂ī¸"; + inline constexpr const char man_detective_tone5[] = "đŸ•ĩđŸŋ‍♂ī¸"; + inline constexpr const char man_detective_dark_skin_tone[] = "đŸ•ĩđŸŋ‍♂ī¸"; + inline constexpr const char health_worker[] = "🧑‍⚕ī¸"; + inline constexpr const char health_worker_tone1[] = "🧑đŸģ‍⚕ī¸"; + inline constexpr const char health_worker_light_skin_tone[] = "🧑đŸģ‍⚕ī¸"; + inline constexpr const char health_worker_tone2[] = "🧑đŸŧ‍⚕ī¸"; + inline constexpr const char health_worker_medium_light_skin_tone[] = "🧑đŸŧ‍⚕ī¸"; + inline constexpr const char health_worker_tone3[] = "🧑đŸŊ‍⚕ī¸"; + inline constexpr const char health_worker_medium_skin_tone[] = "🧑đŸŊ‍⚕ī¸"; + inline constexpr const char health_worker_tone4[] = "🧑🏾‍⚕ī¸"; + inline constexpr const char health_worker_medium_dark_skin_tone[] = "🧑🏾‍⚕ī¸"; + inline constexpr const char health_worker_tone5[] = "🧑đŸŋ‍⚕ī¸"; + inline constexpr const char health_worker_dark_skin_tone[] = "🧑đŸŋ‍⚕ī¸"; + inline constexpr const char woman_health_worker[] = "👩‍⚕ī¸"; + inline constexpr const char woman_health_worker_tone1[] = "👩đŸģ‍⚕ī¸"; + inline constexpr const char woman_health_worker_light_skin_tone[] = "👩đŸģ‍⚕ī¸"; + inline constexpr const char woman_health_worker_tone2[] = "👩đŸŧ‍⚕ī¸"; + inline constexpr const char woman_health_worker_medium_light_skin_tone[] = "👩đŸŧ‍⚕ī¸"; + inline constexpr const char woman_health_worker_tone3[] = "👩đŸŊ‍⚕ī¸"; + inline constexpr const char woman_health_worker_medium_skin_tone[] = "👩đŸŊ‍⚕ī¸"; + inline constexpr const char woman_health_worker_tone4[] = "👩🏾‍⚕ī¸"; + inline constexpr const char woman_health_worker_medium_dark_skin_tone[] = "👩🏾‍⚕ī¸"; + inline constexpr const char woman_health_worker_tone5[] = "👩đŸŋ‍⚕ī¸"; + inline constexpr const char woman_health_worker_dark_skin_tone[] = "👩đŸŋ‍⚕ī¸"; + inline constexpr const char man_health_worker[] = "👨‍⚕ī¸"; + inline constexpr const char man_health_worker_tone1[] = "👨đŸģ‍⚕ī¸"; + inline constexpr const char man_health_worker_light_skin_tone[] = "👨đŸģ‍⚕ī¸"; + inline constexpr const char man_health_worker_tone2[] = "👨đŸŧ‍⚕ī¸"; + inline constexpr const char man_health_worker_medium_light_skin_tone[] = "👨đŸŧ‍⚕ī¸"; + inline constexpr const char man_health_worker_tone3[] = "👨đŸŊ‍⚕ī¸"; + inline constexpr const char man_health_worker_medium_skin_tone[] = "👨đŸŊ‍⚕ī¸"; + inline constexpr const char man_health_worker_tone4[] = "👨🏾‍⚕ī¸"; + inline constexpr const char man_health_worker_medium_dark_skin_tone[] = "👨🏾‍⚕ī¸"; + inline constexpr const char man_health_worker_tone5[] = "👨đŸŋ‍⚕ī¸"; + inline constexpr const char man_health_worker_dark_skin_tone[] = "👨đŸŋ‍⚕ī¸"; + inline constexpr const char farmer[] = "🧑‍🌾"; + inline constexpr const char farmer_tone1[] = "🧑đŸģ‍🌾"; + inline constexpr const char farmer_light_skin_tone[] = "🧑đŸģ‍🌾"; + inline constexpr const char farmer_tone2[] = "🧑đŸŧ‍🌾"; + inline constexpr const char farmer_medium_light_skin_tone[] = "🧑đŸŧ‍🌾"; + inline constexpr const char farmer_tone3[] = "🧑đŸŊ‍🌾"; + inline constexpr const char farmer_medium_skin_tone[] = "🧑đŸŊ‍🌾"; + inline constexpr const char farmer_tone4[] = "🧑🏾‍🌾"; + inline constexpr const char farmer_medium_dark_skin_tone[] = "🧑🏾‍🌾"; + inline constexpr const char farmer_tone5[] = "🧑đŸŋ‍🌾"; + inline constexpr const char farmer_dark_skin_tone[] = "🧑đŸŋ‍🌾"; + inline constexpr const char woman_farmer[] = "👩‍🌾"; + inline constexpr const char woman_farmer_tone1[] = "👩đŸģ‍🌾"; + inline constexpr const char woman_farmer_light_skin_tone[] = "👩đŸģ‍🌾"; + inline constexpr const char woman_farmer_tone2[] = "👩đŸŧ‍🌾"; + inline constexpr const char woman_farmer_medium_light_skin_tone[] = "👩đŸŧ‍🌾"; + inline constexpr const char woman_farmer_tone3[] = "👩đŸŊ‍🌾"; + inline constexpr const char woman_farmer_medium_skin_tone[] = "👩đŸŊ‍🌾"; + inline constexpr const char woman_farmer_tone4[] = "👩🏾‍🌾"; + inline constexpr const char woman_farmer_medium_dark_skin_tone[] = "👩🏾‍🌾"; + inline constexpr const char woman_farmer_tone5[] = "👩đŸŋ‍🌾"; + inline constexpr const char woman_farmer_dark_skin_tone[] = "👩đŸŋ‍🌾"; + inline constexpr const char man_farmer[] = "👨‍🌾"; + inline constexpr const char man_farmer_tone1[] = "👨đŸģ‍🌾"; + inline constexpr const char man_farmer_light_skin_tone[] = "👨đŸģ‍🌾"; + inline constexpr const char man_farmer_tone2[] = "👨đŸŧ‍🌾"; + inline constexpr const char man_farmer_medium_light_skin_tone[] = "👨đŸŧ‍🌾"; + inline constexpr const char man_farmer_tone3[] = "👨đŸŊ‍🌾"; + inline constexpr const char man_farmer_medium_skin_tone[] = "👨đŸŊ‍🌾"; + inline constexpr const char man_farmer_tone4[] = "👨🏾‍🌾"; + inline constexpr const char man_farmer_medium_dark_skin_tone[] = "👨🏾‍🌾"; + inline constexpr const char man_farmer_tone5[] = "👨đŸŋ‍🌾"; + inline constexpr const char man_farmer_dark_skin_tone[] = "👨đŸŋ‍🌾"; + inline constexpr const char cook[] = "🧑‍đŸŗ"; + inline constexpr const char cook_tone1[] = "🧑đŸģ‍đŸŗ"; + inline constexpr const char cook_light_skin_tone[] = "🧑đŸģ‍đŸŗ"; + inline constexpr const char cook_tone2[] = "🧑đŸŧ‍đŸŗ"; + inline constexpr const char cook_medium_light_skin_tone[] = "🧑đŸŧ‍đŸŗ"; + inline constexpr const char cook_tone3[] = "🧑đŸŊ‍đŸŗ"; + inline constexpr const char cook_medium_skin_tone[] = "🧑đŸŊ‍đŸŗ"; + inline constexpr const char cook_tone4[] = "🧑🏾‍đŸŗ"; + inline constexpr const char cook_medium_dark_skin_tone[] = "🧑🏾‍đŸŗ"; + inline constexpr const char cook_tone5[] = "🧑đŸŋ‍đŸŗ"; + inline constexpr const char cook_dark_skin_tone[] = "🧑đŸŋ‍đŸŗ"; + inline constexpr const char woman_cook[] = "👩‍đŸŗ"; + inline constexpr const char woman_cook_tone1[] = "👩đŸģ‍đŸŗ"; + inline constexpr const char woman_cook_light_skin_tone[] = "👩đŸģ‍đŸŗ"; + inline constexpr const char woman_cook_tone2[] = "👩đŸŧ‍đŸŗ"; + inline constexpr const char woman_cook_medium_light_skin_tone[] = "👩đŸŧ‍đŸŗ"; + inline constexpr const char woman_cook_tone3[] = "👩đŸŊ‍đŸŗ"; + inline constexpr const char woman_cook_medium_skin_tone[] = "👩đŸŊ‍đŸŗ"; + inline constexpr const char woman_cook_tone4[] = "👩🏾‍đŸŗ"; + inline constexpr const char woman_cook_medium_dark_skin_tone[] = "👩🏾‍đŸŗ"; + inline constexpr const char woman_cook_tone5[] = "👩đŸŋ‍đŸŗ"; + inline constexpr const char woman_cook_dark_skin_tone[] = "👩đŸŋ‍đŸŗ"; + inline constexpr const char man_cook[] = "👨‍đŸŗ"; + inline constexpr const char man_cook_tone1[] = "👨đŸģ‍đŸŗ"; + inline constexpr const char man_cook_light_skin_tone[] = "👨đŸģ‍đŸŗ"; + inline constexpr const char man_cook_tone2[] = "👨đŸŧ‍đŸŗ"; + inline constexpr const char man_cook_medium_light_skin_tone[] = "👨đŸŧ‍đŸŗ"; + inline constexpr const char man_cook_tone3[] = "👨đŸŊ‍đŸŗ"; + inline constexpr const char man_cook_medium_skin_tone[] = "👨đŸŊ‍đŸŗ"; + inline constexpr const char man_cook_tone4[] = "👨🏾‍đŸŗ"; + inline constexpr const char man_cook_medium_dark_skin_tone[] = "👨🏾‍đŸŗ"; + inline constexpr const char man_cook_tone5[] = "👨đŸŋ‍đŸŗ"; + inline constexpr const char man_cook_dark_skin_tone[] = "👨đŸŋ‍đŸŗ"; + inline constexpr const char student[] = "🧑‍🎓"; + inline constexpr const char student_tone1[] = "🧑đŸģ‍🎓"; + inline constexpr const char student_light_skin_tone[] = "🧑đŸģ‍🎓"; + inline constexpr const char student_tone2[] = "🧑đŸŧ‍🎓"; + inline constexpr const char student_medium_light_skin_tone[] = "🧑đŸŧ‍🎓"; + inline constexpr const char student_tone3[] = "🧑đŸŊ‍🎓"; + inline constexpr const char student_medium_skin_tone[] = "🧑đŸŊ‍🎓"; + inline constexpr const char student_tone4[] = "🧑🏾‍🎓"; + inline constexpr const char student_medium_dark_skin_tone[] = "🧑🏾‍🎓"; + inline constexpr const char student_tone5[] = "🧑đŸŋ‍🎓"; + inline constexpr const char student_dark_skin_tone[] = "🧑đŸŋ‍🎓"; + inline constexpr const char woman_student[] = "👩‍🎓"; + inline constexpr const char woman_student_tone1[] = "👩đŸģ‍🎓"; + inline constexpr const char woman_student_light_skin_tone[] = "👩đŸģ‍🎓"; + inline constexpr const char woman_student_tone2[] = "👩đŸŧ‍🎓"; + inline constexpr const char woman_student_medium_light_skin_tone[] = "👩đŸŧ‍🎓"; + inline constexpr const char woman_student_tone3[] = "👩đŸŊ‍🎓"; + inline constexpr const char woman_student_medium_skin_tone[] = "👩đŸŊ‍🎓"; + inline constexpr const char woman_student_tone4[] = "👩🏾‍🎓"; + inline constexpr const char woman_student_medium_dark_skin_tone[] = "👩🏾‍🎓"; + inline constexpr const char woman_student_tone5[] = "👩đŸŋ‍🎓"; + inline constexpr const char woman_student_dark_skin_tone[] = "👩đŸŋ‍🎓"; + inline constexpr const char man_student[] = "👨‍🎓"; + inline constexpr const char man_student_tone1[] = "👨đŸģ‍🎓"; + inline constexpr const char man_student_light_skin_tone[] = "👨đŸģ‍🎓"; + inline constexpr const char man_student_tone2[] = "👨đŸŧ‍🎓"; + inline constexpr const char man_student_medium_light_skin_tone[] = "👨đŸŧ‍🎓"; + inline constexpr const char man_student_tone3[] = "👨đŸŊ‍🎓"; + inline constexpr const char man_student_medium_skin_tone[] = "👨đŸŊ‍🎓"; + inline constexpr const char man_student_tone4[] = "👨🏾‍🎓"; + inline constexpr const char man_student_medium_dark_skin_tone[] = "👨🏾‍🎓"; + inline constexpr const char man_student_tone5[] = "👨đŸŋ‍🎓"; + inline constexpr const char man_student_dark_skin_tone[] = "👨đŸŋ‍🎓"; + inline constexpr const char singer[] = "🧑‍🎤"; + inline constexpr const char singer_tone1[] = "🧑đŸģ‍🎤"; + inline constexpr const char singer_light_skin_tone[] = "🧑đŸģ‍🎤"; + inline constexpr const char singer_tone2[] = "🧑đŸŧ‍🎤"; + inline constexpr const char singer_medium_light_skin_tone[] = "🧑đŸŧ‍🎤"; + inline constexpr const char singer_tone3[] = "🧑đŸŊ‍🎤"; + inline constexpr const char singer_medium_skin_tone[] = "🧑đŸŊ‍🎤"; + inline constexpr const char singer_tone4[] = "🧑🏾‍🎤"; + inline constexpr const char singer_medium_dark_skin_tone[] = "🧑🏾‍🎤"; + inline constexpr const char singer_tone5[] = "🧑đŸŋ‍🎤"; + inline constexpr const char singer_dark_skin_tone[] = "🧑đŸŋ‍🎤"; + inline constexpr const char woman_singer[] = "👩‍🎤"; + inline constexpr const char woman_singer_tone1[] = "👩đŸģ‍🎤"; + inline constexpr const char woman_singer_light_skin_tone[] = "👩đŸģ‍🎤"; + inline constexpr const char woman_singer_tone2[] = "👩đŸŧ‍🎤"; + inline constexpr const char woman_singer_medium_light_skin_tone[] = "👩đŸŧ‍🎤"; + inline constexpr const char woman_singer_tone3[] = "👩đŸŊ‍🎤"; + inline constexpr const char woman_singer_medium_skin_tone[] = "👩đŸŊ‍🎤"; + inline constexpr const char woman_singer_tone4[] = "👩🏾‍🎤"; + inline constexpr const char woman_singer_medium_dark_skin_tone[] = "👩🏾‍🎤"; + inline constexpr const char woman_singer_tone5[] = "👩đŸŋ‍🎤"; + inline constexpr const char woman_singer_dark_skin_tone[] = "👩đŸŋ‍🎤"; + inline constexpr const char man_singer[] = "👨‍🎤"; + inline constexpr const char man_singer_tone1[] = "👨đŸģ‍🎤"; + inline constexpr const char man_singer_light_skin_tone[] = "👨đŸģ‍🎤"; + inline constexpr const char man_singer_tone2[] = "👨đŸŧ‍🎤"; + inline constexpr const char man_singer_medium_light_skin_tone[] = "👨đŸŧ‍🎤"; + inline constexpr const char man_singer_tone3[] = "👨đŸŊ‍🎤"; + inline constexpr const char man_singer_medium_skin_tone[] = "👨đŸŊ‍🎤"; + inline constexpr const char man_singer_tone4[] = "👨🏾‍🎤"; + inline constexpr const char man_singer_medium_dark_skin_tone[] = "👨🏾‍🎤"; + inline constexpr const char man_singer_tone5[] = "👨đŸŋ‍🎤"; + inline constexpr const char man_singer_dark_skin_tone[] = "👨đŸŋ‍🎤"; + inline constexpr const char teacher[] = "🧑‍đŸĢ"; + inline constexpr const char teacher_tone1[] = "🧑đŸģ‍đŸĢ"; + inline constexpr const char teacher_light_skin_tone[] = "🧑đŸģ‍đŸĢ"; + inline constexpr const char teacher_tone2[] = "🧑đŸŧ‍đŸĢ"; + inline constexpr const char teacher_medium_light_skin_tone[] = "🧑đŸŧ‍đŸĢ"; + inline constexpr const char teacher_tone3[] = "🧑đŸŊ‍đŸĢ"; + inline constexpr const char teacher_medium_skin_tone[] = "🧑đŸŊ‍đŸĢ"; + inline constexpr const char teacher_tone4[] = "🧑🏾‍đŸĢ"; + inline constexpr const char teacher_medium_dark_skin_tone[] = "🧑🏾‍đŸĢ"; + inline constexpr const char teacher_tone5[] = "🧑đŸŋ‍đŸĢ"; + inline constexpr const char teacher_dark_skin_tone[] = "🧑đŸŋ‍đŸĢ"; + inline constexpr const char woman_teacher[] = "👩‍đŸĢ"; + inline constexpr const char woman_teacher_tone1[] = "👩đŸģ‍đŸĢ"; + inline constexpr const char woman_teacher_light_skin_tone[] = "👩đŸģ‍đŸĢ"; + inline constexpr const char woman_teacher_tone2[] = "👩đŸŧ‍đŸĢ"; + inline constexpr const char woman_teacher_medium_light_skin_tone[] = "👩đŸŧ‍đŸĢ"; + inline constexpr const char woman_teacher_tone3[] = "👩đŸŊ‍đŸĢ"; + inline constexpr const char woman_teacher_medium_skin_tone[] = "👩đŸŊ‍đŸĢ"; + inline constexpr const char woman_teacher_tone4[] = "👩🏾‍đŸĢ"; + inline constexpr const char woman_teacher_medium_dark_skin_tone[] = "👩🏾‍đŸĢ"; + inline constexpr const char woman_teacher_tone5[] = "👩đŸŋ‍đŸĢ"; + inline constexpr const char woman_teacher_dark_skin_tone[] = "👩đŸŋ‍đŸĢ"; + inline constexpr const char man_teacher[] = "👨‍đŸĢ"; + inline constexpr const char man_teacher_tone1[] = "👨đŸģ‍đŸĢ"; + inline constexpr const char man_teacher_light_skin_tone[] = "👨đŸģ‍đŸĢ"; + inline constexpr const char man_teacher_tone2[] = "👨đŸŧ‍đŸĢ"; + inline constexpr const char man_teacher_medium_light_skin_tone[] = "👨đŸŧ‍đŸĢ"; + inline constexpr const char man_teacher_tone3[] = "👨đŸŊ‍đŸĢ"; + inline constexpr const char man_teacher_medium_skin_tone[] = "👨đŸŊ‍đŸĢ"; + inline constexpr const char man_teacher_tone4[] = "👨🏾‍đŸĢ"; + inline constexpr const char man_teacher_medium_dark_skin_tone[] = "👨🏾‍đŸĢ"; + inline constexpr const char man_teacher_tone5[] = "👨đŸŋ‍đŸĢ"; + inline constexpr const char man_teacher_dark_skin_tone[] = "👨đŸŋ‍đŸĢ"; + inline constexpr const char factory_worker[] = "🧑‍🏭"; + inline constexpr const char factory_worker_tone1[] = "🧑đŸģ‍🏭"; + inline constexpr const char factory_worker_light_skin_tone[] = "🧑đŸģ‍🏭"; + inline constexpr const char factory_worker_tone2[] = "🧑đŸŧ‍🏭"; + inline constexpr const char factory_worker_medium_light_skin_tone[] = "🧑đŸŧ‍🏭"; + inline constexpr const char factory_worker_tone3[] = "🧑đŸŊ‍🏭"; + inline constexpr const char factory_worker_medium_skin_tone[] = "🧑đŸŊ‍🏭"; + inline constexpr const char factory_worker_tone4[] = "🧑🏾‍🏭"; + inline constexpr const char factory_worker_medium_dark_skin_tone[] = "🧑🏾‍🏭"; + inline constexpr const char factory_worker_tone5[] = "🧑đŸŋ‍🏭"; + inline constexpr const char factory_worker_dark_skin_tone[] = "🧑đŸŋ‍🏭"; + inline constexpr const char woman_factory_worker[] = "👩‍🏭"; + inline constexpr const char woman_factory_worker_tone1[] = "👩đŸģ‍🏭"; + inline constexpr const char woman_factory_worker_light_skin_tone[] = "👩đŸģ‍🏭"; + inline constexpr const char woman_factory_worker_tone2[] = "👩đŸŧ‍🏭"; + inline constexpr const char woman_factory_worker_medium_light_skin_tone[] = "👩đŸŧ‍🏭"; + inline constexpr const char woman_factory_worker_tone3[] = "👩đŸŊ‍🏭"; + inline constexpr const char woman_factory_worker_medium_skin_tone[] = "👩đŸŊ‍🏭"; + inline constexpr const char woman_factory_worker_tone4[] = "👩🏾‍🏭"; + inline constexpr const char woman_factory_worker_medium_dark_skin_tone[] = "👩🏾‍🏭"; + inline constexpr const char woman_factory_worker_tone5[] = "👩đŸŋ‍🏭"; + inline constexpr const char woman_factory_worker_dark_skin_tone[] = "👩đŸŋ‍🏭"; + inline constexpr const char man_factory_worker[] = "👨‍🏭"; + inline constexpr const char man_factory_worker_tone1[] = "👨đŸģ‍🏭"; + inline constexpr const char man_factory_worker_light_skin_tone[] = "👨đŸģ‍🏭"; + inline constexpr const char man_factory_worker_tone2[] = "👨đŸŧ‍🏭"; + inline constexpr const char man_factory_worker_medium_light_skin_tone[] = "👨đŸŧ‍🏭"; + inline constexpr const char man_factory_worker_tone3[] = "👨đŸŊ‍🏭"; + inline constexpr const char man_factory_worker_medium_skin_tone[] = "👨đŸŊ‍🏭"; + inline constexpr const char man_factory_worker_tone4[] = "👨🏾‍🏭"; + inline constexpr const char man_factory_worker_medium_dark_skin_tone[] = "👨🏾‍🏭"; + inline constexpr const char man_factory_worker_tone5[] = "👨đŸŋ‍🏭"; + inline constexpr const char man_factory_worker_dark_skin_tone[] = "👨đŸŋ‍🏭"; + inline constexpr const char technologist[] = "🧑‍đŸ’ģ"; + inline constexpr const char technologist_tone1[] = "🧑đŸģ‍đŸ’ģ"; + inline constexpr const char technologist_light_skin_tone[] = "🧑đŸģ‍đŸ’ģ"; + inline constexpr const char technologist_tone2[] = "🧑đŸŧ‍đŸ’ģ"; + inline constexpr const char technologist_medium_light_skin_tone[] = "🧑đŸŧ‍đŸ’ģ"; + inline constexpr const char technologist_tone3[] = "🧑đŸŊ‍đŸ’ģ"; + inline constexpr const char technologist_medium_skin_tone[] = "🧑đŸŊ‍đŸ’ģ"; + inline constexpr const char technologist_tone4[] = "🧑🏾‍đŸ’ģ"; + inline constexpr const char technologist_medium_dark_skin_tone[] = "🧑🏾‍đŸ’ģ"; + inline constexpr const char technologist_tone5[] = "🧑đŸŋ‍đŸ’ģ"; + inline constexpr const char technologist_dark_skin_tone[] = "🧑đŸŋ‍đŸ’ģ"; + inline constexpr const char woman_technologist[] = "👩‍đŸ’ģ"; + inline constexpr const char woman_technologist_tone1[] = "👩đŸģ‍đŸ’ģ"; + inline constexpr const char woman_technologist_light_skin_tone[] = "👩đŸģ‍đŸ’ģ"; + inline constexpr const char woman_technologist_tone2[] = "👩đŸŧ‍đŸ’ģ"; + inline constexpr const char woman_technologist_medium_light_skin_tone[] = "👩đŸŧ‍đŸ’ģ"; + inline constexpr const char woman_technologist_tone3[] = "👩đŸŊ‍đŸ’ģ"; + inline constexpr const char woman_technologist_medium_skin_tone[] = "👩đŸŊ‍đŸ’ģ"; + inline constexpr const char woman_technologist_tone4[] = "👩🏾‍đŸ’ģ"; + inline constexpr const char woman_technologist_medium_dark_skin_tone[] = "👩🏾‍đŸ’ģ"; + inline constexpr const char woman_technologist_tone5[] = "👩đŸŋ‍đŸ’ģ"; + inline constexpr const char woman_technologist_dark_skin_tone[] = "👩đŸŋ‍đŸ’ģ"; + inline constexpr const char man_technologist[] = "👨‍đŸ’ģ"; + inline constexpr const char man_technologist_tone1[] = "👨đŸģ‍đŸ’ģ"; + inline constexpr const char man_technologist_light_skin_tone[] = "👨đŸģ‍đŸ’ģ"; + inline constexpr const char man_technologist_tone2[] = "👨đŸŧ‍đŸ’ģ"; + inline constexpr const char man_technologist_medium_light_skin_tone[] = "👨đŸŧ‍đŸ’ģ"; + inline constexpr const char man_technologist_tone3[] = "👨đŸŊ‍đŸ’ģ"; + inline constexpr const char man_technologist_medium_skin_tone[] = "👨đŸŊ‍đŸ’ģ"; + inline constexpr const char man_technologist_tone4[] = "👨🏾‍đŸ’ģ"; + inline constexpr const char man_technologist_medium_dark_skin_tone[] = "👨🏾‍đŸ’ģ"; + inline constexpr const char man_technologist_tone5[] = "👨đŸŋ‍đŸ’ģ"; + inline constexpr const char man_technologist_dark_skin_tone[] = "👨đŸŋ‍đŸ’ģ"; + inline constexpr const char office_worker[] = "🧑‍đŸ’ŧ"; + inline constexpr const char office_worker_tone1[] = "🧑đŸģ‍đŸ’ŧ"; + inline constexpr const char office_worker_light_skin_tone[] = "🧑đŸģ‍đŸ’ŧ"; + inline constexpr const char office_worker_tone2[] = "🧑đŸŧ‍đŸ’ŧ"; + inline constexpr const char office_worker_medium_light_skin_tone[] = "🧑đŸŧ‍đŸ’ŧ"; + inline constexpr const char office_worker_tone3[] = "🧑đŸŊ‍đŸ’ŧ"; + inline constexpr const char office_worker_medium_skin_tone[] = "🧑đŸŊ‍đŸ’ŧ"; + inline constexpr const char office_worker_tone4[] = "🧑🏾‍đŸ’ŧ"; + inline constexpr const char office_worker_medium_dark_skin_tone[] = "🧑🏾‍đŸ’ŧ"; + inline constexpr const char office_worker_tone5[] = "🧑đŸŋ‍đŸ’ŧ"; + inline constexpr const char office_worker_dark_skin_tone[] = "🧑đŸŋ‍đŸ’ŧ"; + inline constexpr const char woman_office_worker[] = "👩‍đŸ’ŧ"; + inline constexpr const char woman_office_worker_tone1[] = "👩đŸģ‍đŸ’ŧ"; + inline constexpr const char woman_office_worker_light_skin_tone[] = "👩đŸģ‍đŸ’ŧ"; + inline constexpr const char woman_office_worker_tone2[] = "👩đŸŧ‍đŸ’ŧ"; + inline constexpr const char woman_office_worker_medium_light_skin_tone[] = "👩đŸŧ‍đŸ’ŧ"; + inline constexpr const char woman_office_worker_tone3[] = "👩đŸŊ‍đŸ’ŧ"; + inline constexpr const char woman_office_worker_medium_skin_tone[] = "👩đŸŊ‍đŸ’ŧ"; + inline constexpr const char woman_office_worker_tone4[] = "👩🏾‍đŸ’ŧ"; + inline constexpr const char woman_office_worker_medium_dark_skin_tone[] = "👩🏾‍đŸ’ŧ"; + inline constexpr const char woman_office_worker_tone5[] = "👩đŸŋ‍đŸ’ŧ"; + inline constexpr const char woman_office_worker_dark_skin_tone[] = "👩đŸŋ‍đŸ’ŧ"; + inline constexpr const char man_office_worker[] = "👨‍đŸ’ŧ"; + inline constexpr const char man_office_worker_tone1[] = "👨đŸģ‍đŸ’ŧ"; + inline constexpr const char man_office_worker_light_skin_tone[] = "👨đŸģ‍đŸ’ŧ"; + inline constexpr const char man_office_worker_tone2[] = "👨đŸŧ‍đŸ’ŧ"; + inline constexpr const char man_office_worker_medium_light_skin_tone[] = "👨đŸŧ‍đŸ’ŧ"; + inline constexpr const char man_office_worker_tone3[] = "👨đŸŊ‍đŸ’ŧ"; + inline constexpr const char man_office_worker_medium_skin_tone[] = "👨đŸŊ‍đŸ’ŧ"; + inline constexpr const char man_office_worker_tone4[] = "👨🏾‍đŸ’ŧ"; + inline constexpr const char man_office_worker_medium_dark_skin_tone[] = "👨🏾‍đŸ’ŧ"; + inline constexpr const char man_office_worker_tone5[] = "👨đŸŋ‍đŸ’ŧ"; + inline constexpr const char man_office_worker_dark_skin_tone[] = "👨đŸŋ‍đŸ’ŧ"; + inline constexpr const char mechanic[] = "🧑‍🔧"; + inline constexpr const char mechanic_tone1[] = "🧑đŸģ‍🔧"; + inline constexpr const char mechanic_light_skin_tone[] = "🧑đŸģ‍🔧"; + inline constexpr const char mechanic_tone2[] = "🧑đŸŧ‍🔧"; + inline constexpr const char mechanic_medium_light_skin_tone[] = "🧑đŸŧ‍🔧"; + inline constexpr const char mechanic_tone3[] = "🧑đŸŊ‍🔧"; + inline constexpr const char mechanic_medium_skin_tone[] = "🧑đŸŊ‍🔧"; + inline constexpr const char mechanic_tone4[] = "🧑🏾‍🔧"; + inline constexpr const char mechanic_medium_dark_skin_tone[] = "🧑🏾‍🔧"; + inline constexpr const char mechanic_tone5[] = "🧑đŸŋ‍🔧"; + inline constexpr const char mechanic_dark_skin_tone[] = "🧑đŸŋ‍🔧"; + inline constexpr const char woman_mechanic[] = "👩‍🔧"; + inline constexpr const char woman_mechanic_tone1[] = "👩đŸģ‍🔧"; + inline constexpr const char woman_mechanic_light_skin_tone[] = "👩đŸģ‍🔧"; + inline constexpr const char woman_mechanic_tone2[] = "👩đŸŧ‍🔧"; + inline constexpr const char woman_mechanic_medium_light_skin_tone[] = "👩đŸŧ‍🔧"; + inline constexpr const char woman_mechanic_tone3[] = "👩đŸŊ‍🔧"; + inline constexpr const char woman_mechanic_medium_skin_tone[] = "👩đŸŊ‍🔧"; + inline constexpr const char woman_mechanic_tone4[] = "👩🏾‍🔧"; + inline constexpr const char woman_mechanic_medium_dark_skin_tone[] = "👩🏾‍🔧"; + inline constexpr const char woman_mechanic_tone5[] = "👩đŸŋ‍🔧"; + inline constexpr const char woman_mechanic_dark_skin_tone[] = "👩đŸŋ‍🔧"; + inline constexpr const char man_mechanic[] = "👨‍🔧"; + inline constexpr const char man_mechanic_tone1[] = "👨đŸģ‍🔧"; + inline constexpr const char man_mechanic_light_skin_tone[] = "👨đŸģ‍🔧"; + inline constexpr const char man_mechanic_tone2[] = "👨đŸŧ‍🔧"; + inline constexpr const char man_mechanic_medium_light_skin_tone[] = "👨đŸŧ‍🔧"; + inline constexpr const char man_mechanic_tone3[] = "👨đŸŊ‍🔧"; + inline constexpr const char man_mechanic_medium_skin_tone[] = "👨đŸŊ‍🔧"; + inline constexpr const char man_mechanic_tone4[] = "👨🏾‍🔧"; + inline constexpr const char man_mechanic_medium_dark_skin_tone[] = "👨🏾‍🔧"; + inline constexpr const char man_mechanic_tone5[] = "👨đŸŋ‍🔧"; + inline constexpr const char man_mechanic_dark_skin_tone[] = "👨đŸŋ‍🔧"; + inline constexpr const char scientist[] = "🧑‍đŸ”Ŧ"; + inline constexpr const char scientist_tone1[] = "🧑đŸģ‍đŸ”Ŧ"; + inline constexpr const char scientist_light_skin_tone[] = "🧑đŸģ‍đŸ”Ŧ"; + inline constexpr const char scientist_tone2[] = "🧑đŸŧ‍đŸ”Ŧ"; + inline constexpr const char scientist_medium_light_skin_tone[] = "🧑đŸŧ‍đŸ”Ŧ"; + inline constexpr const char scientist_tone3[] = "🧑đŸŊ‍đŸ”Ŧ"; + inline constexpr const char scientist_medium_skin_tone[] = "🧑đŸŊ‍đŸ”Ŧ"; + inline constexpr const char scientist_tone4[] = "🧑🏾‍đŸ”Ŧ"; + inline constexpr const char scientist_medium_dark_skin_tone[] = "🧑🏾‍đŸ”Ŧ"; + inline constexpr const char scientist_tone5[] = "🧑đŸŋ‍đŸ”Ŧ"; + inline constexpr const char scientist_dark_skin_tone[] = "🧑đŸŋ‍đŸ”Ŧ"; + inline constexpr const char woman_scientist[] = "👩‍đŸ”Ŧ"; + inline constexpr const char woman_scientist_tone1[] = "👩đŸģ‍đŸ”Ŧ"; + inline constexpr const char woman_scientist_light_skin_tone[] = "👩đŸģ‍đŸ”Ŧ"; + inline constexpr const char woman_scientist_tone2[] = "👩đŸŧ‍đŸ”Ŧ"; + inline constexpr const char woman_scientist_medium_light_skin_tone[] = "👩đŸŧ‍đŸ”Ŧ"; + inline constexpr const char woman_scientist_tone3[] = "👩đŸŊ‍đŸ”Ŧ"; + inline constexpr const char woman_scientist_medium_skin_tone[] = "👩đŸŊ‍đŸ”Ŧ"; + inline constexpr const char woman_scientist_tone4[] = "👩🏾‍đŸ”Ŧ"; + inline constexpr const char woman_scientist_medium_dark_skin_tone[] = "👩🏾‍đŸ”Ŧ"; + inline constexpr const char woman_scientist_tone5[] = "👩đŸŋ‍đŸ”Ŧ"; + inline constexpr const char woman_scientist_dark_skin_tone[] = "👩đŸŋ‍đŸ”Ŧ"; + inline constexpr const char man_scientist[] = "👨‍đŸ”Ŧ"; + inline constexpr const char man_scientist_tone1[] = "👨đŸģ‍đŸ”Ŧ"; + inline constexpr const char man_scientist_light_skin_tone[] = "👨đŸģ‍đŸ”Ŧ"; + inline constexpr const char man_scientist_tone2[] = "👨đŸŧ‍đŸ”Ŧ"; + inline constexpr const char man_scientist_medium_light_skin_tone[] = "👨đŸŧ‍đŸ”Ŧ"; + inline constexpr const char man_scientist_tone3[] = "👨đŸŊ‍đŸ”Ŧ"; + inline constexpr const char man_scientist_medium_skin_tone[] = "👨đŸŊ‍đŸ”Ŧ"; + inline constexpr const char man_scientist_tone4[] = "👨🏾‍đŸ”Ŧ"; + inline constexpr const char man_scientist_medium_dark_skin_tone[] = "👨🏾‍đŸ”Ŧ"; + inline constexpr const char man_scientist_tone5[] = "👨đŸŋ‍đŸ”Ŧ"; + inline constexpr const char man_scientist_dark_skin_tone[] = "👨đŸŋ‍đŸ”Ŧ"; + inline constexpr const char artist[] = "🧑‍🎨"; + inline constexpr const char artist_tone1[] = "🧑đŸģ‍🎨"; + inline constexpr const char artist_light_skin_tone[] = "🧑đŸģ‍🎨"; + inline constexpr const char artist_tone2[] = "🧑đŸŧ‍🎨"; + inline constexpr const char artist_medium_light_skin_tone[] = "🧑đŸŧ‍🎨"; + inline constexpr const char artist_tone3[] = "🧑đŸŊ‍🎨"; + inline constexpr const char artist_medium_skin_tone[] = "🧑đŸŊ‍🎨"; + inline constexpr const char artist_tone4[] = "🧑🏾‍🎨"; + inline constexpr const char artist_medium_dark_skin_tone[] = "🧑🏾‍🎨"; + inline constexpr const char artist_tone5[] = "🧑đŸŋ‍🎨"; + inline constexpr const char artist_dark_skin_tone[] = "🧑đŸŋ‍🎨"; + inline constexpr const char woman_artist[] = "👩‍🎨"; + inline constexpr const char woman_artist_tone1[] = "👩đŸģ‍🎨"; + inline constexpr const char woman_artist_light_skin_tone[] = "👩đŸģ‍🎨"; + inline constexpr const char woman_artist_tone2[] = "👩đŸŧ‍🎨"; + inline constexpr const char woman_artist_medium_light_skin_tone[] = "👩đŸŧ‍🎨"; + inline constexpr const char woman_artist_tone3[] = "👩đŸŊ‍🎨"; + inline constexpr const char woman_artist_medium_skin_tone[] = "👩đŸŊ‍🎨"; + inline constexpr const char woman_artist_tone4[] = "👩🏾‍🎨"; + inline constexpr const char woman_artist_medium_dark_skin_tone[] = "👩🏾‍🎨"; + inline constexpr const char woman_artist_tone5[] = "👩đŸŋ‍🎨"; + inline constexpr const char woman_artist_dark_skin_tone[] = "👩đŸŋ‍🎨"; + inline constexpr const char man_artist[] = "👨‍🎨"; + inline constexpr const char man_artist_tone1[] = "👨đŸģ‍🎨"; + inline constexpr const char man_artist_light_skin_tone[] = "👨đŸģ‍🎨"; + inline constexpr const char man_artist_tone2[] = "👨đŸŧ‍🎨"; + inline constexpr const char man_artist_medium_light_skin_tone[] = "👨đŸŧ‍🎨"; + inline constexpr const char man_artist_tone3[] = "👨đŸŊ‍🎨"; + inline constexpr const char man_artist_medium_skin_tone[] = "👨đŸŊ‍🎨"; + inline constexpr const char man_artist_tone4[] = "👨🏾‍🎨"; + inline constexpr const char man_artist_medium_dark_skin_tone[] = "👨🏾‍🎨"; + inline constexpr const char man_artist_tone5[] = "👨đŸŋ‍🎨"; + inline constexpr const char man_artist_dark_skin_tone[] = "👨đŸŋ‍🎨"; + inline constexpr const char firefighter[] = "🧑‍🚒"; + inline constexpr const char firefighter_tone1[] = "🧑đŸģ‍🚒"; + inline constexpr const char firefighter_light_skin_tone[] = "🧑đŸģ‍🚒"; + inline constexpr const char firefighter_tone2[] = "🧑đŸŧ‍🚒"; + inline constexpr const char firefighter_medium_light_skin_tone[] = "🧑đŸŧ‍🚒"; + inline constexpr const char firefighter_tone3[] = "🧑đŸŊ‍🚒"; + inline constexpr const char firefighter_medium_skin_tone[] = "🧑đŸŊ‍🚒"; + inline constexpr const char firefighter_tone4[] = "🧑🏾‍🚒"; + inline constexpr const char firefighter_medium_dark_skin_tone[] = "🧑🏾‍🚒"; + inline constexpr const char firefighter_tone5[] = "🧑đŸŋ‍🚒"; + inline constexpr const char firefighter_dark_skin_tone[] = "🧑đŸŋ‍🚒"; + inline constexpr const char woman_firefighter[] = "👩‍🚒"; + inline constexpr const char woman_firefighter_tone1[] = "👩đŸģ‍🚒"; + inline constexpr const char woman_firefighter_light_skin_tone[] = "👩đŸģ‍🚒"; + inline constexpr const char woman_firefighter_tone2[] = "👩đŸŧ‍🚒"; + inline constexpr const char woman_firefighter_medium_light_skin_tone[] = "👩đŸŧ‍🚒"; + inline constexpr const char woman_firefighter_tone3[] = "👩đŸŊ‍🚒"; + inline constexpr const char woman_firefighter_medium_skin_tone[] = "👩đŸŊ‍🚒"; + inline constexpr const char woman_firefighter_tone4[] = "👩🏾‍🚒"; + inline constexpr const char woman_firefighter_medium_dark_skin_tone[] = "👩🏾‍🚒"; + inline constexpr const char woman_firefighter_tone5[] = "👩đŸŋ‍🚒"; + inline constexpr const char woman_firefighter_dark_skin_tone[] = "👩đŸŋ‍🚒"; + inline constexpr const char man_firefighter[] = "👨‍🚒"; + inline constexpr const char man_firefighter_tone1[] = "👨đŸģ‍🚒"; + inline constexpr const char man_firefighter_light_skin_tone[] = "👨đŸģ‍🚒"; + inline constexpr const char man_firefighter_tone2[] = "👨đŸŧ‍🚒"; + inline constexpr const char man_firefighter_medium_light_skin_tone[] = "👨đŸŧ‍🚒"; + inline constexpr const char man_firefighter_tone3[] = "👨đŸŊ‍🚒"; + inline constexpr const char man_firefighter_medium_skin_tone[] = "👨đŸŊ‍🚒"; + inline constexpr const char man_firefighter_tone4[] = "👨🏾‍🚒"; + inline constexpr const char man_firefighter_medium_dark_skin_tone[] = "👨🏾‍🚒"; + inline constexpr const char man_firefighter_tone5[] = "👨đŸŋ‍🚒"; + inline constexpr const char man_firefighter_dark_skin_tone[] = "👨đŸŋ‍🚒"; + inline constexpr const char pilot[] = "🧑‍✈ī¸"; + inline constexpr const char pilot_tone1[] = "🧑đŸģ‍✈ī¸"; + inline constexpr const char pilot_light_skin_tone[] = "🧑đŸģ‍✈ī¸"; + inline constexpr const char pilot_tone2[] = "🧑đŸŧ‍✈ī¸"; + inline constexpr const char pilot_medium_light_skin_tone[] = "🧑đŸŧ‍✈ī¸"; + inline constexpr const char pilot_tone3[] = "🧑đŸŊ‍✈ī¸"; + inline constexpr const char pilot_medium_skin_tone[] = "🧑đŸŊ‍✈ī¸"; + inline constexpr const char pilot_tone4[] = "🧑🏾‍✈ī¸"; + inline constexpr const char pilot_medium_dark_skin_tone[] = "🧑🏾‍✈ī¸"; + inline constexpr const char pilot_tone5[] = "🧑đŸŋ‍✈ī¸"; + inline constexpr const char pilot_dark_skin_tone[] = "🧑đŸŋ‍✈ī¸"; + inline constexpr const char woman_pilot[] = "👩‍✈ī¸"; + inline constexpr const char woman_pilot_tone1[] = "👩đŸģ‍✈ī¸"; + inline constexpr const char woman_pilot_light_skin_tone[] = "👩đŸģ‍✈ī¸"; + inline constexpr const char woman_pilot_tone2[] = "👩đŸŧ‍✈ī¸"; + inline constexpr const char woman_pilot_medium_light_skin_tone[] = "👩đŸŧ‍✈ī¸"; + inline constexpr const char woman_pilot_tone3[] = "👩đŸŊ‍✈ī¸"; + inline constexpr const char woman_pilot_medium_skin_tone[] = "👩đŸŊ‍✈ī¸"; + inline constexpr const char woman_pilot_tone4[] = "👩🏾‍✈ī¸"; + inline constexpr const char woman_pilot_medium_dark_skin_tone[] = "👩🏾‍✈ī¸"; + inline constexpr const char woman_pilot_tone5[] = "👩đŸŋ‍✈ī¸"; + inline constexpr const char woman_pilot_dark_skin_tone[] = "👩đŸŋ‍✈ī¸"; + inline constexpr const char man_pilot[] = "👨‍✈ī¸"; + inline constexpr const char man_pilot_tone1[] = "👨đŸģ‍✈ī¸"; + inline constexpr const char man_pilot_light_skin_tone[] = "👨đŸģ‍✈ī¸"; + inline constexpr const char man_pilot_tone2[] = "👨đŸŧ‍✈ī¸"; + inline constexpr const char man_pilot_medium_light_skin_tone[] = "👨đŸŧ‍✈ī¸"; + inline constexpr const char man_pilot_tone3[] = "👨đŸŊ‍✈ī¸"; + inline constexpr const char man_pilot_medium_skin_tone[] = "👨đŸŊ‍✈ī¸"; + inline constexpr const char man_pilot_tone4[] = "👨🏾‍✈ī¸"; + inline constexpr const char man_pilot_medium_dark_skin_tone[] = "👨🏾‍✈ī¸"; + inline constexpr const char man_pilot_tone5[] = "👨đŸŋ‍✈ī¸"; + inline constexpr const char man_pilot_dark_skin_tone[] = "👨đŸŋ‍✈ī¸"; + inline constexpr const char astronaut[] = "🧑‍🚀"; + inline constexpr const char astronaut_tone1[] = "🧑đŸģ‍🚀"; + inline constexpr const char astronaut_light_skin_tone[] = "🧑đŸģ‍🚀"; + inline constexpr const char astronaut_tone2[] = "🧑đŸŧ‍🚀"; + inline constexpr const char astronaut_medium_light_skin_tone[] = "🧑đŸŧ‍🚀"; + inline constexpr const char astronaut_tone3[] = "🧑đŸŊ‍🚀"; + inline constexpr const char astronaut_medium_skin_tone[] = "🧑đŸŊ‍🚀"; + inline constexpr const char astronaut_tone4[] = "🧑🏾‍🚀"; + inline constexpr const char astronaut_medium_dark_skin_tone[] = "🧑🏾‍🚀"; + inline constexpr const char astronaut_tone5[] = "🧑đŸŋ‍🚀"; + inline constexpr const char astronaut_dark_skin_tone[] = "🧑đŸŋ‍🚀"; + inline constexpr const char woman_astronaut[] = "👩‍🚀"; + inline constexpr const char woman_astronaut_tone1[] = "👩đŸģ‍🚀"; + inline constexpr const char woman_astronaut_light_skin_tone[] = "👩đŸģ‍🚀"; + inline constexpr const char woman_astronaut_tone2[] = "👩đŸŧ‍🚀"; + inline constexpr const char woman_astronaut_medium_light_skin_tone[] = "👩đŸŧ‍🚀"; + inline constexpr const char woman_astronaut_tone3[] = "👩đŸŊ‍🚀"; + inline constexpr const char woman_astronaut_medium_skin_tone[] = "👩đŸŊ‍🚀"; + inline constexpr const char woman_astronaut_tone4[] = "👩🏾‍🚀"; + inline constexpr const char woman_astronaut_medium_dark_skin_tone[] = "👩🏾‍🚀"; + inline constexpr const char woman_astronaut_tone5[] = "👩đŸŋ‍🚀"; + inline constexpr const char woman_astronaut_dark_skin_tone[] = "👩đŸŋ‍🚀"; + inline constexpr const char man_astronaut[] = "👨‍🚀"; + inline constexpr const char man_astronaut_tone1[] = "👨đŸģ‍🚀"; + inline constexpr const char man_astronaut_light_skin_tone[] = "👨đŸģ‍🚀"; + inline constexpr const char man_astronaut_tone2[] = "👨đŸŧ‍🚀"; + inline constexpr const char man_astronaut_medium_light_skin_tone[] = "👨đŸŧ‍🚀"; + inline constexpr const char man_astronaut_tone3[] = "👨đŸŊ‍🚀"; + inline constexpr const char man_astronaut_medium_skin_tone[] = "👨đŸŊ‍🚀"; + inline constexpr const char man_astronaut_tone4[] = "👨🏾‍🚀"; + inline constexpr const char man_astronaut_medium_dark_skin_tone[] = "👨🏾‍🚀"; + inline constexpr const char man_astronaut_tone5[] = "👨đŸŋ‍🚀"; + inline constexpr const char man_astronaut_dark_skin_tone[] = "👨đŸŋ‍🚀"; + inline constexpr const char judge[] = "🧑‍⚖ī¸"; + inline constexpr const char judge_tone1[] = "🧑đŸģ‍⚖ī¸"; + inline constexpr const char judge_light_skin_tone[] = "🧑đŸģ‍⚖ī¸"; + inline constexpr const char judge_tone2[] = "🧑đŸŧ‍⚖ī¸"; + inline constexpr const char judge_medium_light_skin_tone[] = "🧑đŸŧ‍⚖ī¸"; + inline constexpr const char judge_tone3[] = "🧑đŸŊ‍⚖ī¸"; + inline constexpr const char judge_medium_skin_tone[] = "🧑đŸŊ‍⚖ī¸"; + inline constexpr const char judge_tone4[] = "🧑🏾‍⚖ī¸"; + inline constexpr const char judge_medium_dark_skin_tone[] = "🧑🏾‍⚖ī¸"; + inline constexpr const char judge_tone5[] = "🧑đŸŋ‍⚖ī¸"; + inline constexpr const char judge_dark_skin_tone[] = "🧑đŸŋ‍⚖ī¸"; + inline constexpr const char woman_judge[] = "👩‍⚖ī¸"; + inline constexpr const char woman_judge_tone1[] = "👩đŸģ‍⚖ī¸"; + inline constexpr const char woman_judge_light_skin_tone[] = "👩đŸģ‍⚖ī¸"; + inline constexpr const char woman_judge_tone2[] = "👩đŸŧ‍⚖ī¸"; + inline constexpr const char woman_judge_medium_light_skin_tone[] = "👩đŸŧ‍⚖ī¸"; + inline constexpr const char woman_judge_tone3[] = "👩đŸŊ‍⚖ī¸"; + inline constexpr const char woman_judge_medium_skin_tone[] = "👩đŸŊ‍⚖ī¸"; + inline constexpr const char woman_judge_tone4[] = "👩🏾‍⚖ī¸"; + inline constexpr const char woman_judge_medium_dark_skin_tone[] = "👩🏾‍⚖ī¸"; + inline constexpr const char woman_judge_tone5[] = "👩đŸŋ‍⚖ī¸"; + inline constexpr const char woman_judge_dark_skin_tone[] = "👩đŸŋ‍⚖ī¸"; + inline constexpr const char man_judge[] = "👨‍⚖ī¸"; + inline constexpr const char man_judge_tone1[] = "👨đŸģ‍⚖ī¸"; + inline constexpr const char man_judge_light_skin_tone[] = "👨đŸģ‍⚖ī¸"; + inline constexpr const char man_judge_tone2[] = "👨đŸŧ‍⚖ī¸"; + inline constexpr const char man_judge_medium_light_skin_tone[] = "👨đŸŧ‍⚖ī¸"; + inline constexpr const char man_judge_tone3[] = "👨đŸŊ‍⚖ī¸"; + inline constexpr const char man_judge_medium_skin_tone[] = "👨đŸŊ‍⚖ī¸"; + inline constexpr const char man_judge_tone4[] = "👨🏾‍⚖ī¸"; + inline constexpr const char man_judge_medium_dark_skin_tone[] = "👨🏾‍⚖ī¸"; + inline constexpr const char man_judge_tone5[] = "👨đŸŋ‍⚖ī¸"; + inline constexpr const char man_judge_dark_skin_tone[] = "👨đŸŋ‍⚖ī¸"; + inline constexpr const char person_with_veil[] = "👰"; + inline constexpr const char person_with_veil_tone1[] = "👰đŸģ"; + inline constexpr const char person_with_veil_tone2[] = "👰đŸŧ"; + inline constexpr const char person_with_veil_tone3[] = "👰đŸŊ"; + inline constexpr const char person_with_veil_tone4[] = "👰🏾"; + inline constexpr const char person_with_veil_tone5[] = "👰đŸŋ"; + inline constexpr const char woman_with_veil[] = "👰‍♀ī¸"; + inline constexpr const char bride_with_veil[] = "👰‍♀ī¸"; + inline constexpr const char woman_with_veil_tone1[] = "👰đŸģ‍♀ī¸"; + inline constexpr const char woman_with_veil_light_skin_tone[] = "👰đŸģ‍♀ī¸"; + inline constexpr const char woman_with_veil_tone2[] = "👰đŸŧ‍♀ī¸"; + inline constexpr const char woman_with_veil_medium_light_skin_tone[] = "👰đŸŧ‍♀ī¸"; + inline constexpr const char woman_with_veil_tone3[] = "👰đŸŊ‍♀ī¸"; + inline constexpr const char woman_with_veil_medium_skin_tone[] = "👰đŸŊ‍♀ī¸"; + inline constexpr const char woman_with_veil_tone4[] = "👰🏾‍♀ī¸"; + inline constexpr const char woman_with_veil_medium_dark_skin_tone[] = "👰🏾‍♀ī¸"; + inline constexpr const char woman_with_veil_tone5[] = "👰đŸŋ‍♀ī¸"; + inline constexpr const char woman_with_veil_dark_skin_tone[] = "👰đŸŋ‍♀ī¸"; + inline constexpr const char man_with_veil[] = "👰‍♂ī¸"; + inline constexpr const char man_with_veil_tone1[] = "👰đŸģ‍♂ī¸"; + inline constexpr const char man_with_veil_light_skin_tone[] = "👰đŸģ‍♂ī¸"; + inline constexpr const char man_with_veil_tone2[] = "👰đŸŧ‍♂ī¸"; + inline constexpr const char man_with_veil_medium_light_skin_tone[] = "👰đŸŧ‍♂ī¸"; + inline constexpr const char man_with_veil_tone3[] = "👰đŸŊ‍♂ī¸"; + inline constexpr const char man_with_veil_medium_skin_tone[] = "👰đŸŊ‍♂ī¸"; + inline constexpr const char man_with_veil_tone4[] = "👰🏾‍♂ī¸"; + inline constexpr const char man_with_veil_medium_dark_skin_tone[] = "👰🏾‍♂ī¸"; + inline constexpr const char man_with_veil_tone5[] = "👰đŸŋ‍♂ī¸"; + inline constexpr const char man_with_veil_dark_skin_tone[] = "👰đŸŋ‍♂ī¸"; + inline constexpr const char person_in_tuxedo[] = "đŸ¤ĩ"; + inline constexpr const char person_in_tuxedo_tone1[] = "đŸ¤ĩđŸģ"; + inline constexpr const char tuxedo_tone1[] = "đŸ¤ĩđŸģ"; + inline constexpr const char person_in_tuxedo_tone2[] = "đŸ¤ĩđŸŧ"; + inline constexpr const char tuxedo_tone2[] = "đŸ¤ĩđŸŧ"; + inline constexpr const char person_in_tuxedo_tone3[] = "đŸ¤ĩđŸŊ"; + inline constexpr const char tuxedo_tone3[] = "đŸ¤ĩđŸŊ"; + inline constexpr const char person_in_tuxedo_tone4[] = "đŸ¤ĩ🏾"; + inline constexpr const char tuxedo_tone4[] = "đŸ¤ĩ🏾"; + inline constexpr const char person_in_tuxedo_tone5[] = "đŸ¤ĩđŸŋ"; + inline constexpr const char tuxedo_tone5[] = "đŸ¤ĩđŸŋ"; + inline constexpr const char woman_in_tuxedo[] = "đŸ¤ĩ‍♀ī¸"; + inline constexpr const char woman_in_tuxedo_tone1[] = "đŸ¤ĩđŸģ‍♀ī¸"; + inline constexpr const char woman_in_tuxedo_light_skin_tone[] = "đŸ¤ĩđŸģ‍♀ī¸"; + inline constexpr const char woman_in_tuxedo_tone2[] = "đŸ¤ĩđŸŧ‍♀ī¸"; + inline constexpr const char woman_in_tuxedo_medium_light_skin_tone[] = "đŸ¤ĩđŸŧ‍♀ī¸"; + inline constexpr const char woman_in_tuxedo_tone3[] = "đŸ¤ĩđŸŊ‍♀ī¸"; + inline constexpr const char woman_in_tuxedo_medium_skin_tone[] = "đŸ¤ĩđŸŊ‍♀ī¸"; + inline constexpr const char woman_in_tuxedo_tone4[] = "đŸ¤ĩ🏾‍♀ī¸"; + inline constexpr const char woman_in_tuxedo_medium_dark_skin_tone[] = "đŸ¤ĩ🏾‍♀ī¸"; + inline constexpr const char woman_in_tuxedo_tone5[] = "đŸ¤ĩđŸŋ‍♀ī¸"; + inline constexpr const char woman_in_tuxedo_dark_skin_tone[] = "đŸ¤ĩđŸŋ‍♀ī¸"; + inline constexpr const char man_in_tuxedo[] = "đŸ¤ĩ‍♂ī¸"; + inline constexpr const char man_in_tuxedo_tone1[] = "đŸ¤ĩđŸģ‍♂ī¸"; + inline constexpr const char man_in_tuxedo_light_skin_tone[] = "đŸ¤ĩđŸģ‍♂ī¸"; + inline constexpr const char man_in_tuxedo_tone2[] = "đŸ¤ĩđŸŧ‍♂ī¸"; + inline constexpr const char man_in_tuxedo_medium_light_skin_tone[] = "đŸ¤ĩđŸŧ‍♂ī¸"; + inline constexpr const char man_in_tuxedo_tone3[] = "đŸ¤ĩđŸŊ‍♂ī¸"; + inline constexpr const char man_in_tuxedo_medium_skin_tone[] = "đŸ¤ĩđŸŊ‍♂ī¸"; + inline constexpr const char man_in_tuxedo_tone4[] = "đŸ¤ĩ🏾‍♂ī¸"; + inline constexpr const char man_in_tuxedo_medium_dark_skin_tone[] = "đŸ¤ĩ🏾‍♂ī¸"; + inline constexpr const char man_in_tuxedo_tone5[] = "đŸ¤ĩđŸŋ‍♂ī¸"; + inline constexpr const char man_in_tuxedo_dark_skin_tone[] = "đŸ¤ĩđŸŋ‍♂ī¸"; + inline constexpr const char person_with_crown[] = "đŸĢ…"; + inline constexpr const char person_with_crown_tone1[] = "đŸĢ…đŸģ"; + inline constexpr const char person_with_crown_light_skin_tone[] = "đŸĢ…đŸģ"; + inline constexpr const char person_with_crown_tone2[] = "đŸĢ…đŸŧ"; + inline constexpr const char person_with_crown_medium_light_skin_tone[] = "đŸĢ…đŸŧ"; + inline constexpr const char person_with_crown_tone3[] = "đŸĢ…đŸŊ"; + inline constexpr const char person_with_crown_medium_skin_tone[] = "đŸĢ…đŸŊ"; + inline constexpr const char person_with_crown_tone4[] = "đŸĢ…đŸž"; + inline constexpr const char person_with_crown_medium_dark_skin_tone[] = "đŸĢ…đŸž"; + inline constexpr const char person_with_crown_tone5[] = "đŸĢ…đŸŋ"; + inline constexpr const char person_with_crown_dark_skin_tone[] = "đŸĢ…đŸŋ"; + inline constexpr const char princess[] = "👸"; + inline constexpr const char princess_tone1[] = "👸đŸģ"; + inline constexpr const char princess_tone2[] = "👸đŸŧ"; + inline constexpr const char princess_tone3[] = "👸đŸŊ"; + inline constexpr const char princess_tone4[] = "👸🏾"; + inline constexpr const char princess_tone5[] = "👸đŸŋ"; + inline constexpr const char prince[] = "🤴"; + inline constexpr const char prince_tone1[] = "🤴đŸģ"; + inline constexpr const char prince_tone2[] = "🤴đŸŧ"; + inline constexpr const char prince_tone3[] = "🤴đŸŊ"; + inline constexpr const char prince_tone4[] = "🤴🏾"; + inline constexpr const char prince_tone5[] = "🤴đŸŋ"; + inline constexpr const char superhero[] = "đŸĻ¸"; + inline constexpr const char superhero_tone1[] = "đŸĻ¸đŸģ"; + inline constexpr const char superhero_light_skin_tone[] = "đŸĻ¸đŸģ"; + inline constexpr const char superhero_tone2[] = "đŸĻ¸đŸŧ"; + inline constexpr const char superhero_medium_light_skin_tone[] = "đŸĻ¸đŸŧ"; + inline constexpr const char superhero_tone3[] = "đŸĻ¸đŸŊ"; + inline constexpr const char superhero_medium_skin_tone[] = "đŸĻ¸đŸŊ"; + inline constexpr const char superhero_tone4[] = "đŸĻ¸đŸž"; + inline constexpr const char superhero_medium_dark_skin_tone[] = "đŸĻ¸đŸž"; + inline constexpr const char superhero_tone5[] = "đŸĻ¸đŸŋ"; + inline constexpr const char superhero_dark_skin_tone[] = "đŸĻ¸đŸŋ"; + inline constexpr const char woman_superhero[] = "đŸĻ¸â€â™€ī¸"; + inline constexpr const char woman_superhero_tone1[] = "đŸĻ¸đŸģ‍♀ī¸"; + inline constexpr const char woman_superhero_light_skin_tone[] = "đŸĻ¸đŸģ‍♀ī¸"; + inline constexpr const char woman_superhero_tone2[] = "đŸĻ¸đŸŧ‍♀ī¸"; + inline constexpr const char woman_superhero_medium_light_skin_tone[] = "đŸĻ¸đŸŧ‍♀ī¸"; + inline constexpr const char woman_superhero_tone3[] = "đŸĻ¸đŸŊ‍♀ī¸"; + inline constexpr const char woman_superhero_medium_skin_tone[] = "đŸĻ¸đŸŊ‍♀ī¸"; + inline constexpr const char woman_superhero_tone4[] = "đŸĻ¸đŸžâ€â™€ī¸"; + inline constexpr const char woman_superhero_medium_dark_skin_tone[] = "đŸĻ¸đŸžâ€â™€ī¸"; + inline constexpr const char woman_superhero_tone5[] = "đŸĻ¸đŸŋ‍♀ī¸"; + inline constexpr const char woman_superhero_dark_skin_tone[] = "đŸĻ¸đŸŋ‍♀ī¸"; + inline constexpr const char man_superhero[] = "đŸĻ¸â€â™‚ī¸"; + inline constexpr const char man_superhero_tone1[] = "đŸĻ¸đŸģ‍♂ī¸"; + inline constexpr const char man_superhero_light_skin_tone[] = "đŸĻ¸đŸģ‍♂ī¸"; + inline constexpr const char man_superhero_tone2[] = "đŸĻ¸đŸŧ‍♂ī¸"; + inline constexpr const char man_superhero_medium_light_skin_tone[] = "đŸĻ¸đŸŧ‍♂ī¸"; + inline constexpr const char man_superhero_tone3[] = "đŸĻ¸đŸŊ‍♂ī¸"; + inline constexpr const char man_superhero_medium_skin_tone[] = "đŸĻ¸đŸŊ‍♂ī¸"; + inline constexpr const char man_superhero_tone4[] = "đŸĻ¸đŸžâ€â™‚ī¸"; + inline constexpr const char man_superhero_medium_dark_skin_tone[] = "đŸĻ¸đŸžâ€â™‚ī¸"; + inline constexpr const char man_superhero_tone5[] = "đŸĻ¸đŸŋ‍♂ī¸"; + inline constexpr const char man_superhero_dark_skin_tone[] = "đŸĻ¸đŸŋ‍♂ī¸"; + inline constexpr const char supervillain[] = "đŸĻš"; + inline constexpr const char supervillain_tone1[] = "đŸĻšđŸģ"; + inline constexpr const char supervillain_light_skin_tone[] = "đŸĻšđŸģ"; + inline constexpr const char supervillain_tone2[] = "đŸĻšđŸŧ"; + inline constexpr const char supervillain_medium_light_skin_tone[] = "đŸĻšđŸŧ"; + inline constexpr const char supervillain_tone3[] = "đŸĻšđŸŊ"; + inline constexpr const char supervillain_medium_skin_tone[] = "đŸĻšđŸŊ"; + inline constexpr const char supervillain_tone4[] = "đŸĻšđŸž"; + inline constexpr const char supervillain_medium_dark_skin_tone[] = "đŸĻšđŸž"; + inline constexpr const char supervillain_tone5[] = "đŸĻšđŸŋ"; + inline constexpr const char supervillain_dark_skin_tone[] = "đŸĻšđŸŋ"; + inline constexpr const char woman_supervillain[] = "đŸĻšâ€â™€ī¸"; + inline constexpr const char woman_supervillain_tone1[] = "đŸĻšđŸģ‍♀ī¸"; + inline constexpr const char woman_supervillain_light_skin_tone[] = "đŸĻšđŸģ‍♀ī¸"; + inline constexpr const char woman_supervillain_tone2[] = "đŸĻšđŸŧ‍♀ī¸"; + inline constexpr const char woman_supervillain_medium_light_skin_tone[] = "đŸĻšđŸŧ‍♀ī¸"; + inline constexpr const char woman_supervillain_tone3[] = "đŸĻšđŸŊ‍♀ī¸"; + inline constexpr const char woman_supervillain_medium_skin_tone[] = "đŸĻšđŸŊ‍♀ī¸"; + inline constexpr const char woman_supervillain_tone4[] = "đŸĻšđŸžâ€â™€ī¸"; + inline constexpr const char woman_supervillain_medium_dark_skin_tone[] = "đŸĻšđŸžâ€â™€ī¸"; + inline constexpr const char woman_supervillain_tone5[] = "đŸĻšđŸŋ‍♀ī¸"; + inline constexpr const char woman_supervillain_dark_skin_tone[] = "đŸĻšđŸŋ‍♀ī¸"; + inline constexpr const char man_supervillain[] = "đŸĻšâ€â™‚ī¸"; + inline constexpr const char man_supervillain_tone1[] = "đŸĻšđŸģ‍♂ī¸"; + inline constexpr const char man_supervillain_light_skin_tone[] = "đŸĻšđŸģ‍♂ī¸"; + inline constexpr const char man_supervillain_tone2[] = "đŸĻšđŸŧ‍♂ī¸"; + inline constexpr const char man_supervillain_medium_light_skin_tone[] = "đŸĻšđŸŧ‍♂ī¸"; + inline constexpr const char man_supervillain_tone3[] = "đŸĻšđŸŊ‍♂ī¸"; + inline constexpr const char man_supervillain_medium_skin_tone[] = "đŸĻšđŸŊ‍♂ī¸"; + inline constexpr const char man_supervillain_tone4[] = "đŸĻšđŸžâ€â™‚ī¸"; + inline constexpr const char man_supervillain_medium_dark_skin_tone[] = "đŸĻšđŸžâ€â™‚ī¸"; + inline constexpr const char man_supervillain_tone5[] = "đŸĻšđŸŋ‍♂ī¸"; + inline constexpr const char man_supervillain_dark_skin_tone[] = "đŸĻšđŸŋ‍♂ī¸"; + inline constexpr const char ninja[] = "đŸĨˇ"; + inline constexpr const char ninja_tone1[] = "đŸĨˇđŸģ"; + inline constexpr const char ninja_light_skin_tone[] = "đŸĨˇđŸģ"; + inline constexpr const char ninja_tone2[] = "đŸĨˇđŸŧ"; + inline constexpr const char ninja_medium_light_skin_tone[] = "đŸĨˇđŸŧ"; + inline constexpr const char ninja_tone3[] = "đŸĨˇđŸŊ"; + inline constexpr const char ninja_medium_skin_tone[] = "đŸĨˇđŸŊ"; + inline constexpr const char ninja_tone4[] = "đŸĨˇđŸž"; + inline constexpr const char ninja_medium_dark_skin_tone[] = "đŸĨˇđŸž"; + inline constexpr const char ninja_tone5[] = "đŸĨˇđŸŋ"; + inline constexpr const char ninja_dark_skin_tone[] = "đŸĨˇđŸŋ"; + inline constexpr const char mx_claus[] = "🧑‍🎄"; + inline constexpr const char mx_claus_tone1[] = "🧑đŸģ‍🎄"; + inline constexpr const char mx_claus_light_skin_tone[] = "🧑đŸģ‍🎄"; + inline constexpr const char mx_claus_tone2[] = "🧑đŸŧ‍🎄"; + inline constexpr const char mx_claus_medium_light_skin_tone[] = "🧑đŸŧ‍🎄"; + inline constexpr const char mx_claus_tone3[] = "🧑đŸŊ‍🎄"; + inline constexpr const char mx_claus_medium_skin_tone[] = "🧑đŸŊ‍🎄"; + inline constexpr const char mx_claus_tone4[] = "🧑🏾‍🎄"; + inline constexpr const char mx_claus_medium_dark_skin_tone[] = "🧑🏾‍🎄"; + inline constexpr const char mx_claus_tone5[] = "🧑đŸŋ‍🎄"; + inline constexpr const char mx_claus_dark_skin_tone[] = "🧑đŸŋ‍🎄"; + inline constexpr const char mrs_claus[] = "đŸ¤ļ"; + inline constexpr const char mother_christmas[] = "đŸ¤ļ"; + inline constexpr const char mrs_claus_tone1[] = "đŸ¤ļđŸģ"; + inline constexpr const char mother_christmas_tone1[] = "đŸ¤ļđŸģ"; + inline constexpr const char mrs_claus_tone2[] = "đŸ¤ļđŸŧ"; + inline constexpr const char mother_christmas_tone2[] = "đŸ¤ļđŸŧ"; + inline constexpr const char mrs_claus_tone3[] = "đŸ¤ļđŸŊ"; + inline constexpr const char mother_christmas_tone3[] = "đŸ¤ļđŸŊ"; + inline constexpr const char mrs_claus_tone4[] = "đŸ¤ļ🏾"; + inline constexpr const char mother_christmas_tone4[] = "đŸ¤ļ🏾"; + inline constexpr const char mrs_claus_tone5[] = "đŸ¤ļđŸŋ"; + inline constexpr const char mother_christmas_tone5[] = "đŸ¤ļđŸŋ"; + inline constexpr const char santa[] = "🎅"; + inline constexpr const char santa_tone1[] = "🎅đŸģ"; + inline constexpr const char santa_tone2[] = "🎅đŸŧ"; + inline constexpr const char santa_tone3[] = "🎅đŸŊ"; + inline constexpr const char santa_tone4[] = "🎅🏾"; + inline constexpr const char santa_tone5[] = "🎅đŸŋ"; + inline constexpr const char mage[] = "🧙"; + inline constexpr const char mage_tone1[] = "🧙đŸģ"; + inline constexpr const char mage_light_skin_tone[] = "🧙đŸģ"; + inline constexpr const char mage_tone2[] = "🧙đŸŧ"; + inline constexpr const char mage_medium_light_skin_tone[] = "🧙đŸŧ"; + inline constexpr const char mage_tone3[] = "🧙đŸŊ"; + inline constexpr const char mage_medium_skin_tone[] = "🧙đŸŊ"; + inline constexpr const char mage_tone4[] = "🧙🏾"; + inline constexpr const char mage_medium_dark_skin_tone[] = "🧙🏾"; + inline constexpr const char mage_tone5[] = "🧙đŸŋ"; + inline constexpr const char mage_dark_skin_tone[] = "🧙đŸŋ"; + inline constexpr const char woman_mage[] = "🧙‍♀ī¸"; + inline constexpr const char woman_mage_tone1[] = "🧙đŸģ‍♀ī¸"; + inline constexpr const char woman_mage_light_skin_tone[] = "🧙đŸģ‍♀ī¸"; + inline constexpr const char woman_mage_tone2[] = "🧙đŸŧ‍♀ī¸"; + inline constexpr const char woman_mage_medium_light_skin_tone[] = "🧙đŸŧ‍♀ī¸"; + inline constexpr const char woman_mage_tone3[] = "🧙đŸŊ‍♀ī¸"; + inline constexpr const char woman_mage_medium_skin_tone[] = "🧙đŸŊ‍♀ī¸"; + inline constexpr const char woman_mage_tone4[] = "🧙🏾‍♀ī¸"; + inline constexpr const char woman_mage_medium_dark_skin_tone[] = "🧙🏾‍♀ī¸"; + inline constexpr const char woman_mage_tone5[] = "🧙đŸŋ‍♀ī¸"; + inline constexpr const char woman_mage_dark_skin_tone[] = "🧙đŸŋ‍♀ī¸"; + inline constexpr const char man_mage[] = "🧙‍♂ī¸"; + inline constexpr const char man_mage_tone1[] = "🧙đŸģ‍♂ī¸"; + inline constexpr const char man_mage_light_skin_tone[] = "🧙đŸģ‍♂ī¸"; + inline constexpr const char man_mage_tone2[] = "🧙đŸŧ‍♂ī¸"; + inline constexpr const char man_mage_medium_light_skin_tone[] = "🧙đŸŧ‍♂ī¸"; + inline constexpr const char man_mage_tone3[] = "🧙đŸŊ‍♂ī¸"; + inline constexpr const char man_mage_medium_skin_tone[] = "🧙đŸŊ‍♂ī¸"; + inline constexpr const char man_mage_tone4[] = "🧙🏾‍♂ī¸"; + inline constexpr const char man_mage_medium_dark_skin_tone[] = "🧙🏾‍♂ī¸"; + inline constexpr const char man_mage_tone5[] = "🧙đŸŋ‍♂ī¸"; + inline constexpr const char man_mage_dark_skin_tone[] = "🧙đŸŋ‍♂ī¸"; + inline constexpr const char elf[] = "🧝"; + inline constexpr const char elf_tone1[] = "🧝đŸģ"; + inline constexpr const char elf_light_skin_tone[] = "🧝đŸģ"; + inline constexpr const char elf_tone2[] = "🧝đŸŧ"; + inline constexpr const char elf_medium_light_skin_tone[] = "🧝đŸŧ"; + inline constexpr const char elf_tone3[] = "🧝đŸŊ"; + inline constexpr const char elf_medium_skin_tone[] = "🧝đŸŊ"; + inline constexpr const char elf_tone4[] = "🧝🏾"; + inline constexpr const char elf_medium_dark_skin_tone[] = "🧝🏾"; + inline constexpr const char elf_tone5[] = "🧝đŸŋ"; + inline constexpr const char elf_dark_skin_tone[] = "🧝đŸŋ"; + inline constexpr const char woman_elf[] = "🧝‍♀ī¸"; + inline constexpr const char woman_elf_tone1[] = "🧝đŸģ‍♀ī¸"; + inline constexpr const char woman_elf_light_skin_tone[] = "🧝đŸģ‍♀ī¸"; + inline constexpr const char woman_elf_tone2[] = "🧝đŸŧ‍♀ī¸"; + inline constexpr const char woman_elf_medium_light_skin_tone[] = "🧝đŸŧ‍♀ī¸"; + inline constexpr const char woman_elf_tone3[] = "🧝đŸŊ‍♀ī¸"; + inline constexpr const char woman_elf_medium_skin_tone[] = "🧝đŸŊ‍♀ī¸"; + inline constexpr const char woman_elf_tone4[] = "🧝🏾‍♀ī¸"; + inline constexpr const char woman_elf_medium_dark_skin_tone[] = "🧝🏾‍♀ī¸"; + inline constexpr const char woman_elf_tone5[] = "🧝đŸŋ‍♀ī¸"; + inline constexpr const char woman_elf_dark_skin_tone[] = "🧝đŸŋ‍♀ī¸"; + inline constexpr const char man_elf[] = "🧝‍♂ī¸"; + inline constexpr const char man_elf_tone1[] = "🧝đŸģ‍♂ī¸"; + inline constexpr const char man_elf_light_skin_tone[] = "🧝đŸģ‍♂ī¸"; + inline constexpr const char man_elf_tone2[] = "🧝đŸŧ‍♂ī¸"; + inline constexpr const char man_elf_medium_light_skin_tone[] = "🧝đŸŧ‍♂ī¸"; + inline constexpr const char man_elf_tone3[] = "🧝đŸŊ‍♂ī¸"; + inline constexpr const char man_elf_medium_skin_tone[] = "🧝đŸŊ‍♂ī¸"; + inline constexpr const char man_elf_tone4[] = "🧝🏾‍♂ī¸"; + inline constexpr const char man_elf_medium_dark_skin_tone[] = "🧝🏾‍♂ī¸"; + inline constexpr const char man_elf_tone5[] = "🧝đŸŋ‍♂ī¸"; + inline constexpr const char man_elf_dark_skin_tone[] = "🧝đŸŋ‍♂ī¸"; + inline constexpr const char troll[] = "🧌"; + inline constexpr const char vampire[] = "🧛"; + inline constexpr const char vampire_tone1[] = "🧛đŸģ"; + inline constexpr const char vampire_light_skin_tone[] = "🧛đŸģ"; + inline constexpr const char vampire_tone2[] = "🧛đŸŧ"; + inline constexpr const char vampire_medium_light_skin_tone[] = "🧛đŸŧ"; + inline constexpr const char vampire_tone3[] = "🧛đŸŊ"; + inline constexpr const char vampire_medium_skin_tone[] = "🧛đŸŊ"; + inline constexpr const char vampire_tone4[] = "🧛🏾"; + inline constexpr const char vampire_medium_dark_skin_tone[] = "🧛🏾"; + inline constexpr const char vampire_tone5[] = "🧛đŸŋ"; + inline constexpr const char vampire_dark_skin_tone[] = "🧛đŸŋ"; + inline constexpr const char woman_vampire[] = "🧛‍♀ī¸"; + inline constexpr const char woman_vampire_tone1[] = "🧛đŸģ‍♀ī¸"; + inline constexpr const char woman_vampire_light_skin_tone[] = "🧛đŸģ‍♀ī¸"; + inline constexpr const char woman_vampire_tone2[] = "🧛đŸŧ‍♀ī¸"; + inline constexpr const char woman_vampire_medium_light_skin_tone[] = "🧛đŸŧ‍♀ī¸"; + inline constexpr const char woman_vampire_tone3[] = "🧛đŸŊ‍♀ī¸"; + inline constexpr const char woman_vampire_medium_skin_tone[] = "🧛đŸŊ‍♀ī¸"; + inline constexpr const char woman_vampire_tone4[] = "🧛🏾‍♀ī¸"; + inline constexpr const char woman_vampire_medium_dark_skin_tone[] = "🧛🏾‍♀ī¸"; + inline constexpr const char woman_vampire_tone5[] = "🧛đŸŋ‍♀ī¸"; + inline constexpr const char woman_vampire_dark_skin_tone[] = "🧛đŸŋ‍♀ī¸"; + inline constexpr const char man_vampire[] = "🧛‍♂ī¸"; + inline constexpr const char man_vampire_tone1[] = "🧛đŸģ‍♂ī¸"; + inline constexpr const char man_vampire_light_skin_tone[] = "🧛đŸģ‍♂ī¸"; + inline constexpr const char man_vampire_tone2[] = "🧛đŸŧ‍♂ī¸"; + inline constexpr const char man_vampire_medium_light_skin_tone[] = "🧛đŸŧ‍♂ī¸"; + inline constexpr const char man_vampire_tone3[] = "🧛đŸŊ‍♂ī¸"; + inline constexpr const char man_vampire_medium_skin_tone[] = "🧛đŸŊ‍♂ī¸"; + inline constexpr const char man_vampire_tone4[] = "🧛🏾‍♂ī¸"; + inline constexpr const char man_vampire_medium_dark_skin_tone[] = "🧛🏾‍♂ī¸"; + inline constexpr const char man_vampire_tone5[] = "🧛đŸŋ‍♂ī¸"; + inline constexpr const char man_vampire_dark_skin_tone[] = "🧛đŸŋ‍♂ī¸"; + inline constexpr const char zombie[] = "🧟"; + inline constexpr const char woman_zombie[] = "🧟‍♀ī¸"; + inline constexpr const char man_zombie[] = "🧟‍♂ī¸"; + inline constexpr const char genie[] = "🧞"; + inline constexpr const char woman_genie[] = "🧞‍♀ī¸"; + inline constexpr const char man_genie[] = "🧞‍♂ī¸"; + inline constexpr const char merperson[] = "🧜"; + inline constexpr const char merperson_tone1[] = "🧜đŸģ"; + inline constexpr const char merperson_light_skin_tone[] = "🧜đŸģ"; + inline constexpr const char merperson_tone2[] = "🧜đŸŧ"; + inline constexpr const char merperson_medium_light_skin_tone[] = "🧜đŸŧ"; + inline constexpr const char merperson_tone3[] = "🧜đŸŊ"; + inline constexpr const char merperson_medium_skin_tone[] = "🧜đŸŊ"; + inline constexpr const char merperson_tone4[] = "🧜🏾"; + inline constexpr const char merperson_medium_dark_skin_tone[] = "🧜🏾"; + inline constexpr const char merperson_tone5[] = "🧜đŸŋ"; + inline constexpr const char merperson_dark_skin_tone[] = "🧜đŸŋ"; + inline constexpr const char mermaid[] = "🧜‍♀ī¸"; + inline constexpr const char mermaid_tone1[] = "🧜đŸģ‍♀ī¸"; + inline constexpr const char mermaid_light_skin_tone[] = "🧜đŸģ‍♀ī¸"; + inline constexpr const char mermaid_tone2[] = "🧜đŸŧ‍♀ī¸"; + inline constexpr const char mermaid_medium_light_skin_tone[] = "🧜đŸŧ‍♀ī¸"; + inline constexpr const char mermaid_tone3[] = "🧜đŸŊ‍♀ī¸"; + inline constexpr const char mermaid_medium_skin_tone[] = "🧜đŸŊ‍♀ī¸"; + inline constexpr const char mermaid_tone4[] = "🧜🏾‍♀ī¸"; + inline constexpr const char mermaid_medium_dark_skin_tone[] = "🧜🏾‍♀ī¸"; + inline constexpr const char mermaid_tone5[] = "🧜đŸŋ‍♀ī¸"; + inline constexpr const char mermaid_dark_skin_tone[] = "🧜đŸŋ‍♀ī¸"; + inline constexpr const char merman[] = "🧜‍♂ī¸"; + inline constexpr const char merman_tone1[] = "🧜đŸģ‍♂ī¸"; + inline constexpr const char merman_light_skin_tone[] = "🧜đŸģ‍♂ī¸"; + inline constexpr const char merman_tone2[] = "🧜đŸŧ‍♂ī¸"; + inline constexpr const char merman_medium_light_skin_tone[] = "🧜đŸŧ‍♂ī¸"; + inline constexpr const char merman_tone3[] = "🧜đŸŊ‍♂ī¸"; + inline constexpr const char merman_medium_skin_tone[] = "🧜đŸŊ‍♂ī¸"; + inline constexpr const char merman_tone4[] = "🧜🏾‍♂ī¸"; + inline constexpr const char merman_medium_dark_skin_tone[] = "🧜🏾‍♂ī¸"; + inline constexpr const char merman_tone5[] = "🧜đŸŋ‍♂ī¸"; + inline constexpr const char merman_dark_skin_tone[] = "🧜đŸŋ‍♂ī¸"; + inline constexpr const char fairy[] = "🧚"; + inline constexpr const char fairy_tone1[] = "🧚đŸģ"; + inline constexpr const char fairy_light_skin_tone[] = "🧚đŸģ"; + inline constexpr const char fairy_tone2[] = "🧚đŸŧ"; + inline constexpr const char fairy_medium_light_skin_tone[] = "🧚đŸŧ"; + inline constexpr const char fairy_tone3[] = "🧚đŸŊ"; + inline constexpr const char fairy_medium_skin_tone[] = "🧚đŸŊ"; + inline constexpr const char fairy_tone4[] = "🧚🏾"; + inline constexpr const char fairy_medium_dark_skin_tone[] = "🧚🏾"; + inline constexpr const char fairy_tone5[] = "🧚đŸŋ"; + inline constexpr const char fairy_dark_skin_tone[] = "🧚đŸŋ"; + inline constexpr const char woman_fairy[] = "🧚‍♀ī¸"; + inline constexpr const char woman_fairy_tone1[] = "🧚đŸģ‍♀ī¸"; + inline constexpr const char woman_fairy_light_skin_tone[] = "🧚đŸģ‍♀ī¸"; + inline constexpr const char woman_fairy_tone2[] = "🧚đŸŧ‍♀ī¸"; + inline constexpr const char woman_fairy_medium_light_skin_tone[] = "🧚đŸŧ‍♀ī¸"; + inline constexpr const char woman_fairy_tone3[] = "🧚đŸŊ‍♀ī¸"; + inline constexpr const char woman_fairy_medium_skin_tone[] = "🧚đŸŊ‍♀ī¸"; + inline constexpr const char woman_fairy_tone4[] = "🧚🏾‍♀ī¸"; + inline constexpr const char woman_fairy_medium_dark_skin_tone[] = "🧚🏾‍♀ī¸"; + inline constexpr const char woman_fairy_tone5[] = "🧚đŸŋ‍♀ī¸"; + inline constexpr const char woman_fairy_dark_skin_tone[] = "🧚đŸŋ‍♀ī¸"; + inline constexpr const char man_fairy[] = "🧚‍♂ī¸"; + inline constexpr const char man_fairy_tone1[] = "🧚đŸģ‍♂ī¸"; + inline constexpr const char man_fairy_light_skin_tone[] = "🧚đŸģ‍♂ī¸"; + inline constexpr const char man_fairy_tone2[] = "🧚đŸŧ‍♂ī¸"; + inline constexpr const char man_fairy_medium_light_skin_tone[] = "🧚đŸŧ‍♂ī¸"; + inline constexpr const char man_fairy_tone3[] = "🧚đŸŊ‍♂ī¸"; + inline constexpr const char man_fairy_medium_skin_tone[] = "🧚đŸŊ‍♂ī¸"; + inline constexpr const char man_fairy_tone4[] = "🧚🏾‍♂ī¸"; + inline constexpr const char man_fairy_medium_dark_skin_tone[] = "🧚🏾‍♂ī¸"; + inline constexpr const char man_fairy_tone5[] = "🧚đŸŋ‍♂ī¸"; + inline constexpr const char man_fairy_dark_skin_tone[] = "🧚đŸŋ‍♂ī¸"; + inline constexpr const char angel[] = "đŸ‘ŧ"; + inline constexpr const char angel_tone1[] = "đŸ‘ŧđŸģ"; + inline constexpr const char angel_tone2[] = "đŸ‘ŧđŸŧ"; + inline constexpr const char angel_tone3[] = "đŸ‘ŧđŸŊ"; + inline constexpr const char angel_tone4[] = "đŸ‘ŧ🏾"; + inline constexpr const char angel_tone5[] = "đŸ‘ŧđŸŋ"; + inline constexpr const char pregnant_person[] = "đŸĢ„"; + inline constexpr const char pregnant_person_tone1[] = "đŸĢ„đŸģ"; + inline constexpr const char pregnant_person_light_skin_tone[] = "đŸĢ„đŸģ"; + inline constexpr const char pregnant_person_tone2[] = "đŸĢ„đŸŧ"; + inline constexpr const char pregnant_person_medium_light_skin_tone[] = "đŸĢ„đŸŧ"; + inline constexpr const char pregnant_person_tone3[] = "đŸĢ„đŸŊ"; + inline constexpr const char pregnant_person_medium_skin_tone[] = "đŸĢ„đŸŊ"; + inline constexpr const char pregnant_person_tone4[] = "đŸĢ„đŸž"; + inline constexpr const char pregnant_person_medium_dark_skin_tone[] = "đŸĢ„đŸž"; + inline constexpr const char pregnant_person_tone5[] = "đŸĢ„đŸŋ"; + inline constexpr const char pregnant_person_dark_skin_tone[] = "đŸĢ„đŸŋ"; + inline constexpr const char pregnant_woman[] = "🤰"; + inline constexpr const char expecting_woman[] = "🤰"; + inline constexpr const char pregnant_woman_tone1[] = "🤰đŸģ"; + inline constexpr const char expecting_woman_tone1[] = "🤰đŸģ"; + inline constexpr const char pregnant_woman_tone2[] = "🤰đŸŧ"; + inline constexpr const char expecting_woman_tone2[] = "🤰đŸŧ"; + inline constexpr const char pregnant_woman_tone3[] = "🤰đŸŊ"; + inline constexpr const char expecting_woman_tone3[] = "🤰đŸŊ"; + inline constexpr const char pregnant_woman_tone4[] = "🤰🏾"; + inline constexpr const char expecting_woman_tone4[] = "🤰🏾"; + inline constexpr const char pregnant_woman_tone5[] = "🤰đŸŋ"; + inline constexpr const char expecting_woman_tone5[] = "🤰đŸŋ"; + inline constexpr const char pregnant_man[] = "đŸĢƒ"; + inline constexpr const char pregnant_man_tone1[] = "đŸĢƒđŸģ"; + inline constexpr const char pregnant_man_light_skin_tone[] = "đŸĢƒđŸģ"; + inline constexpr const char pregnant_man_tone2[] = "đŸĢƒđŸŧ"; + inline constexpr const char pregnant_man_medium_light_skin_tone[] = "đŸĢƒđŸŧ"; + inline constexpr const char pregnant_man_tone3[] = "đŸĢƒđŸŊ"; + inline constexpr const char pregnant_man_medium_skin_tone[] = "đŸĢƒđŸŊ"; + inline constexpr const char pregnant_man_tone4[] = "đŸĢƒđŸž"; + inline constexpr const char pregnant_man_medium_dark_skin_tone[] = "đŸĢƒđŸž"; + inline constexpr const char pregnant_man_tone5[] = "đŸĢƒđŸŋ"; + inline constexpr const char pregnant_man_dark_skin_tone[] = "đŸĢƒđŸŋ"; + inline constexpr const char breast_feeding[] = "🤱"; + inline constexpr const char breast_feeding_tone1[] = "🤱đŸģ"; + inline constexpr const char breast_feeding_light_skin_tone[] = "🤱đŸģ"; + inline constexpr const char breast_feeding_tone2[] = "🤱đŸŧ"; + inline constexpr const char breast_feeding_medium_light_skin_tone[] = "🤱đŸŧ"; + inline constexpr const char breast_feeding_tone3[] = "🤱đŸŊ"; + inline constexpr const char breast_feeding_medium_skin_tone[] = "🤱đŸŊ"; + inline constexpr const char breast_feeding_tone4[] = "🤱🏾"; + inline constexpr const char breast_feeding_medium_dark_skin_tone[] = "🤱🏾"; + inline constexpr const char breast_feeding_tone5[] = "🤱đŸŋ"; + inline constexpr const char breast_feeding_dark_skin_tone[] = "🤱đŸŋ"; + inline constexpr const char person_feeding_baby[] = "🧑‍đŸŧ"; + inline constexpr const char person_feeding_baby_tone1[] = "🧑đŸģ‍đŸŧ"; + inline constexpr const char person_feeding_baby_light_skin_tone[] = "🧑đŸģ‍đŸŧ"; + inline constexpr const char person_feeding_baby_tone2[] = "🧑đŸŧ‍đŸŧ"; + inline constexpr const char person_feeding_baby_medium_light_skin_tone[] = "🧑đŸŧ‍đŸŧ"; + inline constexpr const char person_feeding_baby_tone3[] = "🧑đŸŊ‍đŸŧ"; + inline constexpr const char person_feeding_baby_medium_skin_tone[] = "🧑đŸŊ‍đŸŧ"; + inline constexpr const char person_feeding_baby_tone4[] = "🧑🏾‍đŸŧ"; + inline constexpr const char person_feeding_baby_medium_dark_skin_tone[] = "🧑🏾‍đŸŧ"; + inline constexpr const char person_feeding_baby_tone5[] = "🧑đŸŋ‍đŸŧ"; + inline constexpr const char person_feeding_baby_dark_skin_tone[] = "🧑đŸŋ‍đŸŧ"; + inline constexpr const char woman_feeding_baby[] = "👩‍đŸŧ"; + inline constexpr const char woman_feeding_baby_tone1[] = "👩đŸģ‍đŸŧ"; + inline constexpr const char woman_feeding_baby_light_skin_tone[] = "👩đŸģ‍đŸŧ"; + inline constexpr const char woman_feeding_baby_tone2[] = "👩đŸŧ‍đŸŧ"; + inline constexpr const char woman_feeding_baby_medium_light_skin_tone[] = "👩đŸŧ‍đŸŧ"; + inline constexpr const char woman_feeding_baby_tone3[] = "👩đŸŊ‍đŸŧ"; + inline constexpr const char woman_feeding_baby_medium_skin_tone[] = "👩đŸŊ‍đŸŧ"; + inline constexpr const char woman_feeding_baby_tone4[] = "👩🏾‍đŸŧ"; + inline constexpr const char woman_feeding_baby_medium_dark_skin_tone[] = "👩🏾‍đŸŧ"; + inline constexpr const char woman_feeding_baby_tone5[] = "👩đŸŋ‍đŸŧ"; + inline constexpr const char woman_feeding_baby_dark_skin_tone[] = "👩đŸŋ‍đŸŧ"; + inline constexpr const char man_feeding_baby[] = "👨‍đŸŧ"; + inline constexpr const char man_feeding_baby_tone1[] = "👨đŸģ‍đŸŧ"; + inline constexpr const char man_feeding_baby_light_skin_tone[] = "👨đŸģ‍đŸŧ"; + inline constexpr const char man_feeding_baby_tone2[] = "👨đŸŧ‍đŸŧ"; + inline constexpr const char man_feeding_baby_medium_light_skin_tone[] = "👨đŸŧ‍đŸŧ"; + inline constexpr const char man_feeding_baby_tone3[] = "👨đŸŊ‍đŸŧ"; + inline constexpr const char man_feeding_baby_medium_skin_tone[] = "👨đŸŊ‍đŸŧ"; + inline constexpr const char man_feeding_baby_tone4[] = "👨🏾‍đŸŧ"; + inline constexpr const char man_feeding_baby_medium_dark_skin_tone[] = "👨🏾‍đŸŧ"; + inline constexpr const char man_feeding_baby_tone5[] = "👨đŸŋ‍đŸŧ"; + inline constexpr const char man_feeding_baby_dark_skin_tone[] = "👨đŸŋ‍đŸŧ"; + inline constexpr const char person_bowing[] = "🙇"; + inline constexpr const char bow[] = "🙇"; + inline constexpr const char person_bowing_tone1[] = "🙇đŸģ"; + inline constexpr const char bow_tone1[] = "🙇đŸģ"; + inline constexpr const char person_bowing_tone2[] = "🙇đŸŧ"; + inline constexpr const char bow_tone2[] = "🙇đŸŧ"; + inline constexpr const char person_bowing_tone3[] = "🙇đŸŊ"; + inline constexpr const char bow_tone3[] = "🙇đŸŊ"; + inline constexpr const char person_bowing_tone4[] = "🙇🏾"; + inline constexpr const char bow_tone4[] = "🙇🏾"; + inline constexpr const char person_bowing_tone5[] = "🙇đŸŋ"; + inline constexpr const char bow_tone5[] = "🙇đŸŋ"; + inline constexpr const char woman_bowing[] = "🙇‍♀ī¸"; + inline constexpr const char woman_bowing_tone1[] = "🙇đŸģ‍♀ī¸"; + inline constexpr const char woman_bowing_light_skin_tone[] = "🙇đŸģ‍♀ī¸"; + inline constexpr const char woman_bowing_tone2[] = "🙇đŸŧ‍♀ī¸"; + inline constexpr const char woman_bowing_medium_light_skin_tone[] = "🙇đŸŧ‍♀ī¸"; + inline constexpr const char woman_bowing_tone3[] = "🙇đŸŊ‍♀ī¸"; + inline constexpr const char woman_bowing_medium_skin_tone[] = "🙇đŸŊ‍♀ī¸"; + inline constexpr const char woman_bowing_tone4[] = "🙇🏾‍♀ī¸"; + inline constexpr const char woman_bowing_medium_dark_skin_tone[] = "🙇🏾‍♀ī¸"; + inline constexpr const char woman_bowing_tone5[] = "🙇đŸŋ‍♀ī¸"; + inline constexpr const char woman_bowing_dark_skin_tone[] = "🙇đŸŋ‍♀ī¸"; + inline constexpr const char man_bowing[] = "🙇‍♂ī¸"; + inline constexpr const char man_bowing_tone1[] = "🙇đŸģ‍♂ī¸"; + inline constexpr const char man_bowing_light_skin_tone[] = "🙇đŸģ‍♂ī¸"; + inline constexpr const char man_bowing_tone2[] = "🙇đŸŧ‍♂ī¸"; + inline constexpr const char man_bowing_medium_light_skin_tone[] = "🙇đŸŧ‍♂ī¸"; + inline constexpr const char man_bowing_tone3[] = "🙇đŸŊ‍♂ī¸"; + inline constexpr const char man_bowing_medium_skin_tone[] = "🙇đŸŊ‍♂ī¸"; + inline constexpr const char man_bowing_tone4[] = "🙇🏾‍♂ī¸"; + inline constexpr const char man_bowing_medium_dark_skin_tone[] = "🙇🏾‍♂ī¸"; + inline constexpr const char man_bowing_tone5[] = "🙇đŸŋ‍♂ī¸"; + inline constexpr const char man_bowing_dark_skin_tone[] = "🙇đŸŋ‍♂ī¸"; + inline constexpr const char person_tipping_hand[] = "💁"; + inline constexpr const char information_desk_person[] = "💁"; + inline constexpr const char person_tipping_hand_tone1[] = "💁đŸģ"; + inline constexpr const char information_desk_person_tone1[] = "💁đŸģ"; + inline constexpr const char person_tipping_hand_tone2[] = "💁đŸŧ"; + inline constexpr const char information_desk_person_tone2[] = "💁đŸŧ"; + inline constexpr const char person_tipping_hand_tone3[] = "💁đŸŊ"; + inline constexpr const char information_desk_person_tone3[] = "💁đŸŊ"; + inline constexpr const char person_tipping_hand_tone4[] = "💁🏾"; + inline constexpr const char information_desk_person_tone4[] = "💁🏾"; + inline constexpr const char person_tipping_hand_tone5[] = "💁đŸŋ"; + inline constexpr const char information_desk_person_tone5[] = "💁đŸŋ"; + inline constexpr const char woman_tipping_hand[] = "💁‍♀ī¸"; + inline constexpr const char woman_tipping_hand_tone1[] = "💁đŸģ‍♀ī¸"; + inline constexpr const char woman_tipping_hand_light_skin_tone[] = "💁đŸģ‍♀ī¸"; + inline constexpr const char woman_tipping_hand_tone2[] = "💁đŸŧ‍♀ī¸"; + inline constexpr const char woman_tipping_hand_medium_light_skin_tone[] = "💁đŸŧ‍♀ī¸"; + inline constexpr const char woman_tipping_hand_tone3[] = "💁đŸŊ‍♀ī¸"; + inline constexpr const char woman_tipping_hand_medium_skin_tone[] = "💁đŸŊ‍♀ī¸"; + inline constexpr const char woman_tipping_hand_tone4[] = "💁🏾‍♀ī¸"; + inline constexpr const char woman_tipping_hand_medium_dark_skin_tone[] = "💁🏾‍♀ī¸"; + inline constexpr const char woman_tipping_hand_tone5[] = "💁đŸŋ‍♀ī¸"; + inline constexpr const char woman_tipping_hand_dark_skin_tone[] = "💁đŸŋ‍♀ī¸"; + inline constexpr const char man_tipping_hand[] = "💁‍♂ī¸"; + inline constexpr const char man_tipping_hand_tone1[] = "💁đŸģ‍♂ī¸"; + inline constexpr const char man_tipping_hand_light_skin_tone[] = "💁đŸģ‍♂ī¸"; + inline constexpr const char man_tipping_hand_tone2[] = "💁đŸŧ‍♂ī¸"; + inline constexpr const char man_tipping_hand_medium_light_skin_tone[] = "💁đŸŧ‍♂ī¸"; + inline constexpr const char man_tipping_hand_tone3[] = "💁đŸŊ‍♂ī¸"; + inline constexpr const char man_tipping_hand_medium_skin_tone[] = "💁đŸŊ‍♂ī¸"; + inline constexpr const char man_tipping_hand_tone4[] = "💁🏾‍♂ī¸"; + inline constexpr const char man_tipping_hand_medium_dark_skin_tone[] = "💁🏾‍♂ī¸"; + inline constexpr const char man_tipping_hand_tone5[] = "💁đŸŋ‍♂ī¸"; + inline constexpr const char man_tipping_hand_dark_skin_tone[] = "💁đŸŋ‍♂ī¸"; + inline constexpr const char person_gesturing_no[] = "🙅"; + inline constexpr const char no_good[] = "🙅"; + inline constexpr const char person_gesturing_no_tone1[] = "🙅đŸģ"; + inline constexpr const char no_good_tone1[] = "🙅đŸģ"; + inline constexpr const char person_gesturing_no_tone2[] = "🙅đŸŧ"; + inline constexpr const char no_good_tone2[] = "🙅đŸŧ"; + inline constexpr const char person_gesturing_no_tone3[] = "🙅đŸŊ"; + inline constexpr const char no_good_tone3[] = "🙅đŸŊ"; + inline constexpr const char person_gesturing_no_tone4[] = "🙅🏾"; + inline constexpr const char no_good_tone4[] = "🙅🏾"; + inline constexpr const char person_gesturing_no_tone5[] = "🙅đŸŋ"; + inline constexpr const char no_good_tone5[] = "🙅đŸŋ"; + inline constexpr const char woman_gesturing_no[] = "🙅‍♀ī¸"; + inline constexpr const char woman_gesturing_no_tone1[] = "🙅đŸģ‍♀ī¸"; + inline constexpr const char woman_gesturing_no_light_skin_tone[] = "🙅đŸģ‍♀ī¸"; + inline constexpr const char woman_gesturing_no_tone2[] = "🙅đŸŧ‍♀ī¸"; + inline constexpr const char woman_gesturing_no_medium_light_skin_tone[] = "🙅đŸŧ‍♀ī¸"; + inline constexpr const char woman_gesturing_no_tone3[] = "🙅đŸŊ‍♀ī¸"; + inline constexpr const char woman_gesturing_no_medium_skin_tone[] = "🙅đŸŊ‍♀ī¸"; + inline constexpr const char woman_gesturing_no_tone4[] = "🙅🏾‍♀ī¸"; + inline constexpr const char woman_gesturing_no_medium_dark_skin_tone[] = "🙅🏾‍♀ī¸"; + inline constexpr const char woman_gesturing_no_tone5[] = "🙅đŸŋ‍♀ī¸"; + inline constexpr const char woman_gesturing_no_dark_skin_tone[] = "🙅đŸŋ‍♀ī¸"; + inline constexpr const char man_gesturing_no[] = "🙅‍♂ī¸"; + inline constexpr const char man_gesturing_no_tone1[] = "🙅đŸģ‍♂ī¸"; + inline constexpr const char man_gesturing_no_light_skin_tone[] = "🙅đŸģ‍♂ī¸"; + inline constexpr const char man_gesturing_no_tone2[] = "🙅đŸŧ‍♂ī¸"; + inline constexpr const char man_gesturing_no_medium_light_skin_tone[] = "🙅đŸŧ‍♂ī¸"; + inline constexpr const char man_gesturing_no_tone3[] = "🙅đŸŊ‍♂ī¸"; + inline constexpr const char man_gesturing_no_medium_skin_tone[] = "🙅đŸŊ‍♂ī¸"; + inline constexpr const char man_gesturing_no_tone4[] = "🙅🏾‍♂ī¸"; + inline constexpr const char man_gesturing_no_medium_dark_skin_tone[] = "🙅🏾‍♂ī¸"; + inline constexpr const char man_gesturing_no_tone5[] = "🙅đŸŋ‍♂ī¸"; + inline constexpr const char man_gesturing_no_dark_skin_tone[] = "🙅đŸŋ‍♂ī¸"; + inline constexpr const char person_gesturing_ok[] = "🙆"; + inline constexpr const char person_gesturing_ok_tone1[] = "🙆đŸģ"; + inline constexpr const char person_gesturing_ok_tone2[] = "🙆đŸŧ"; + inline constexpr const char person_gesturing_ok_tone3[] = "🙆đŸŊ"; + inline constexpr const char person_gesturing_ok_tone4[] = "🙆🏾"; + inline constexpr const char person_gesturing_ok_tone5[] = "🙆đŸŋ"; + inline constexpr const char woman_gesturing_ok[] = "🙆‍♀ī¸"; + inline constexpr const char woman_gesturing_ok_tone1[] = "🙆đŸģ‍♀ī¸"; + inline constexpr const char woman_gesturing_ok_light_skin_tone[] = "🙆đŸģ‍♀ī¸"; + inline constexpr const char woman_gesturing_ok_tone2[] = "🙆đŸŧ‍♀ī¸"; + inline constexpr const char woman_gesturing_ok_medium_light_skin_tone[] = "🙆đŸŧ‍♀ī¸"; + inline constexpr const char woman_gesturing_ok_tone3[] = "🙆đŸŊ‍♀ī¸"; + inline constexpr const char woman_gesturing_ok_medium_skin_tone[] = "🙆đŸŊ‍♀ī¸"; + inline constexpr const char woman_gesturing_ok_tone4[] = "🙆🏾‍♀ī¸"; + inline constexpr const char woman_gesturing_ok_medium_dark_skin_tone[] = "🙆🏾‍♀ī¸"; + inline constexpr const char woman_gesturing_ok_tone5[] = "🙆đŸŋ‍♀ī¸"; + inline constexpr const char woman_gesturing_ok_dark_skin_tone[] = "🙆đŸŋ‍♀ī¸"; + inline constexpr const char man_gesturing_ok[] = "🙆‍♂ī¸"; + inline constexpr const char man_gesturing_ok_tone1[] = "🙆đŸģ‍♂ī¸"; + inline constexpr const char man_gesturing_ok_light_skin_tone[] = "🙆đŸģ‍♂ī¸"; + inline constexpr const char man_gesturing_ok_tone2[] = "🙆đŸŧ‍♂ī¸"; + inline constexpr const char man_gesturing_ok_medium_light_skin_tone[] = "🙆đŸŧ‍♂ī¸"; + inline constexpr const char man_gesturing_ok_tone3[] = "🙆đŸŊ‍♂ī¸"; + inline constexpr const char man_gesturing_ok_medium_skin_tone[] = "🙆đŸŊ‍♂ī¸"; + inline constexpr const char man_gesturing_ok_tone4[] = "🙆🏾‍♂ī¸"; + inline constexpr const char man_gesturing_ok_medium_dark_skin_tone[] = "🙆🏾‍♂ī¸"; + inline constexpr const char man_gesturing_ok_tone5[] = "🙆đŸŋ‍♂ī¸"; + inline constexpr const char man_gesturing_ok_dark_skin_tone[] = "🙆đŸŋ‍♂ī¸"; + inline constexpr const char person_raising_hand[] = "🙋"; + inline constexpr const char raising_hand[] = "🙋"; + inline constexpr const char person_raising_hand_tone1[] = "🙋đŸģ"; + inline constexpr const char raising_hand_tone1[] = "🙋đŸģ"; + inline constexpr const char person_raising_hand_tone2[] = "🙋đŸŧ"; + inline constexpr const char raising_hand_tone2[] = "🙋đŸŧ"; + inline constexpr const char person_raising_hand_tone3[] = "🙋đŸŊ"; + inline constexpr const char raising_hand_tone3[] = "🙋đŸŊ"; + inline constexpr const char person_raising_hand_tone4[] = "🙋🏾"; + inline constexpr const char raising_hand_tone4[] = "🙋🏾"; + inline constexpr const char person_raising_hand_tone5[] = "🙋đŸŋ"; + inline constexpr const char raising_hand_tone5[] = "🙋đŸŋ"; + inline constexpr const char woman_raising_hand[] = "🙋‍♀ī¸"; + inline constexpr const char woman_raising_hand_tone1[] = "🙋đŸģ‍♀ī¸"; + inline constexpr const char woman_raising_hand_light_skin_tone[] = "🙋đŸģ‍♀ī¸"; + inline constexpr const char woman_raising_hand_tone2[] = "🙋đŸŧ‍♀ī¸"; + inline constexpr const char woman_raising_hand_medium_light_skin_tone[] = "🙋đŸŧ‍♀ī¸"; + inline constexpr const char woman_raising_hand_tone3[] = "🙋đŸŊ‍♀ī¸"; + inline constexpr const char woman_raising_hand_medium_skin_tone[] = "🙋đŸŊ‍♀ī¸"; + inline constexpr const char woman_raising_hand_tone4[] = "🙋🏾‍♀ī¸"; + inline constexpr const char woman_raising_hand_medium_dark_skin_tone[] = "🙋🏾‍♀ī¸"; + inline constexpr const char woman_raising_hand_tone5[] = "🙋đŸŋ‍♀ī¸"; + inline constexpr const char woman_raising_hand_dark_skin_tone[] = "🙋đŸŋ‍♀ī¸"; + inline constexpr const char man_raising_hand[] = "🙋‍♂ī¸"; + inline constexpr const char man_raising_hand_tone1[] = "🙋đŸģ‍♂ī¸"; + inline constexpr const char man_raising_hand_light_skin_tone[] = "🙋đŸģ‍♂ī¸"; + inline constexpr const char man_raising_hand_tone2[] = "🙋đŸŧ‍♂ī¸"; + inline constexpr const char man_raising_hand_medium_light_skin_tone[] = "🙋đŸŧ‍♂ī¸"; + inline constexpr const char man_raising_hand_tone3[] = "🙋đŸŊ‍♂ī¸"; + inline constexpr const char man_raising_hand_medium_skin_tone[] = "🙋đŸŊ‍♂ī¸"; + inline constexpr const char man_raising_hand_tone4[] = "🙋🏾‍♂ī¸"; + inline constexpr const char man_raising_hand_medium_dark_skin_tone[] = "🙋🏾‍♂ī¸"; + inline constexpr const char man_raising_hand_tone5[] = "🙋đŸŋ‍♂ī¸"; + inline constexpr const char man_raising_hand_dark_skin_tone[] = "🙋đŸŋ‍♂ī¸"; + inline constexpr const char deaf_person[] = "🧏"; + inline constexpr const char deaf_person_tone1[] = "🧏đŸģ"; + inline constexpr const char deaf_person_light_skin_tone[] = "🧏đŸģ"; + inline constexpr const char deaf_person_tone2[] = "🧏đŸŧ"; + inline constexpr const char deaf_person_medium_light_skin_tone[] = "🧏đŸŧ"; + inline constexpr const char deaf_person_tone3[] = "🧏đŸŊ"; + inline constexpr const char deaf_person_medium_skin_tone[] = "🧏đŸŊ"; + inline constexpr const char deaf_person_tone4[] = "🧏🏾"; + inline constexpr const char deaf_person_medium_dark_skin_tone[] = "🧏🏾"; + inline constexpr const char deaf_person_tone5[] = "🧏đŸŋ"; + inline constexpr const char deaf_person_dark_skin_tone[] = "🧏đŸŋ"; + inline constexpr const char deaf_woman[] = "🧏‍♀ī¸"; + inline constexpr const char deaf_woman_tone1[] = "🧏đŸģ‍♀ī¸"; + inline constexpr const char deaf_woman_light_skin_tone[] = "🧏đŸģ‍♀ī¸"; + inline constexpr const char deaf_woman_tone2[] = "🧏đŸŧ‍♀ī¸"; + inline constexpr const char deaf_woman_medium_light_skin_tone[] = "🧏đŸŧ‍♀ī¸"; + inline constexpr const char deaf_woman_tone3[] = "🧏đŸŊ‍♀ī¸"; + inline constexpr const char deaf_woman_medium_skin_tone[] = "🧏đŸŊ‍♀ī¸"; + inline constexpr const char deaf_woman_tone4[] = "🧏🏾‍♀ī¸"; + inline constexpr const char deaf_woman_medium_dark_skin_tone[] = "🧏🏾‍♀ī¸"; + inline constexpr const char deaf_woman_tone5[] = "🧏đŸŋ‍♀ī¸"; + inline constexpr const char deaf_woman_dark_skin_tone[] = "🧏đŸŋ‍♀ī¸"; + inline constexpr const char deaf_man[] = "🧏‍♂ī¸"; + inline constexpr const char deaf_man_tone1[] = "🧏đŸģ‍♂ī¸"; + inline constexpr const char deaf_man_light_skin_tone[] = "🧏đŸģ‍♂ī¸"; + inline constexpr const char deaf_man_tone2[] = "🧏đŸŧ‍♂ī¸"; + inline constexpr const char deaf_man_medium_light_skin_tone[] = "🧏đŸŧ‍♂ī¸"; + inline constexpr const char deaf_man_tone3[] = "🧏đŸŊ‍♂ī¸"; + inline constexpr const char deaf_man_medium_skin_tone[] = "🧏đŸŊ‍♂ī¸"; + inline constexpr const char deaf_man_tone4[] = "🧏🏾‍♂ī¸"; + inline constexpr const char deaf_man_medium_dark_skin_tone[] = "🧏🏾‍♂ī¸"; + inline constexpr const char deaf_man_tone5[] = "🧏đŸŋ‍♂ī¸"; + inline constexpr const char deaf_man_dark_skin_tone[] = "🧏đŸŋ‍♂ī¸"; + inline constexpr const char person_facepalming[] = "đŸ¤Ļ"; + inline constexpr const char face_palm[] = "đŸ¤Ļ"; + inline constexpr const char facepalm[] = "đŸ¤Ļ"; + inline constexpr const char person_facepalming_tone1[] = "đŸ¤ĻđŸģ"; + inline constexpr const char face_palm_tone1[] = "đŸ¤ĻđŸģ"; + inline constexpr const char facepalm_tone1[] = "đŸ¤ĻđŸģ"; + inline constexpr const char person_facepalming_tone2[] = "đŸ¤ĻđŸŧ"; + inline constexpr const char face_palm_tone2[] = "đŸ¤ĻđŸŧ"; + inline constexpr const char facepalm_tone2[] = "đŸ¤ĻđŸŧ"; + inline constexpr const char person_facepalming_tone3[] = "đŸ¤ĻđŸŊ"; + inline constexpr const char face_palm_tone3[] = "đŸ¤ĻđŸŊ"; + inline constexpr const char facepalm_tone3[] = "đŸ¤ĻđŸŊ"; + inline constexpr const char person_facepalming_tone4[] = "đŸ¤Ļ🏾"; + inline constexpr const char face_palm_tone4[] = "đŸ¤Ļ🏾"; + inline constexpr const char facepalm_tone4[] = "đŸ¤Ļ🏾"; + inline constexpr const char person_facepalming_tone5[] = "đŸ¤ĻđŸŋ"; + inline constexpr const char face_palm_tone5[] = "đŸ¤ĻđŸŋ"; + inline constexpr const char facepalm_tone5[] = "đŸ¤ĻđŸŋ"; + inline constexpr const char woman_facepalming[] = "đŸ¤Ļ‍♀ī¸"; + inline constexpr const char woman_facepalming_tone1[] = "đŸ¤ĻđŸģ‍♀ī¸"; + inline constexpr const char woman_facepalming_light_skin_tone[] = "đŸ¤ĻđŸģ‍♀ī¸"; + inline constexpr const char woman_facepalming_tone2[] = "đŸ¤ĻđŸŧ‍♀ī¸"; + inline constexpr const char woman_facepalming_medium_light_skin_tone[] = "đŸ¤ĻđŸŧ‍♀ī¸"; + inline constexpr const char woman_facepalming_tone3[] = "đŸ¤ĻđŸŊ‍♀ī¸"; + inline constexpr const char woman_facepalming_medium_skin_tone[] = "đŸ¤ĻđŸŊ‍♀ī¸"; + inline constexpr const char woman_facepalming_tone4[] = "đŸ¤Ļ🏾‍♀ī¸"; + inline constexpr const char woman_facepalming_medium_dark_skin_tone[] = "đŸ¤Ļ🏾‍♀ī¸"; + inline constexpr const char woman_facepalming_tone5[] = "đŸ¤ĻđŸŋ‍♀ī¸"; + inline constexpr const char woman_facepalming_dark_skin_tone[] = "đŸ¤ĻđŸŋ‍♀ī¸"; + inline constexpr const char man_facepalming[] = "đŸ¤Ļ‍♂ī¸"; + inline constexpr const char man_facepalming_tone1[] = "đŸ¤ĻđŸģ‍♂ī¸"; + inline constexpr const char man_facepalming_light_skin_tone[] = "đŸ¤ĻđŸģ‍♂ī¸"; + inline constexpr const char man_facepalming_tone2[] = "đŸ¤ĻđŸŧ‍♂ī¸"; + inline constexpr const char man_facepalming_medium_light_skin_tone[] = "đŸ¤ĻđŸŧ‍♂ī¸"; + inline constexpr const char man_facepalming_tone3[] = "đŸ¤ĻđŸŊ‍♂ī¸"; + inline constexpr const char man_facepalming_medium_skin_tone[] = "đŸ¤ĻđŸŊ‍♂ī¸"; + inline constexpr const char man_facepalming_tone4[] = "đŸ¤Ļ🏾‍♂ī¸"; + inline constexpr const char man_facepalming_medium_dark_skin_tone[] = "đŸ¤Ļ🏾‍♂ī¸"; + inline constexpr const char man_facepalming_tone5[] = "đŸ¤ĻđŸŋ‍♂ī¸"; + inline constexpr const char man_facepalming_dark_skin_tone[] = "đŸ¤ĻđŸŋ‍♂ī¸"; + inline constexpr const char person_shrugging[] = "🤷"; + inline constexpr const char shrug[] = "🤷"; + inline constexpr const char person_shrugging_tone1[] = "🤷đŸģ"; + inline constexpr const char shrug_tone1[] = "🤷đŸģ"; + inline constexpr const char person_shrugging_tone2[] = "🤷đŸŧ"; + inline constexpr const char shrug_tone2[] = "🤷đŸŧ"; + inline constexpr const char person_shrugging_tone3[] = "🤷đŸŊ"; + inline constexpr const char shrug_tone3[] = "🤷đŸŊ"; + inline constexpr const char person_shrugging_tone4[] = "🤷🏾"; + inline constexpr const char shrug_tone4[] = "🤷🏾"; + inline constexpr const char person_shrugging_tone5[] = "🤷đŸŋ"; + inline constexpr const char shrug_tone5[] = "🤷đŸŋ"; + inline constexpr const char woman_shrugging[] = "🤷‍♀ī¸"; + inline constexpr const char woman_shrugging_tone1[] = "🤷đŸģ‍♀ī¸"; + inline constexpr const char woman_shrugging_light_skin_tone[] = "🤷đŸģ‍♀ī¸"; + inline constexpr const char woman_shrugging_tone2[] = "🤷đŸŧ‍♀ī¸"; + inline constexpr const char woman_shrugging_medium_light_skin_tone[] = "🤷đŸŧ‍♀ī¸"; + inline constexpr const char woman_shrugging_tone3[] = "🤷đŸŊ‍♀ī¸"; + inline constexpr const char woman_shrugging_medium_skin_tone[] = "🤷đŸŊ‍♀ī¸"; + inline constexpr const char woman_shrugging_tone4[] = "🤷🏾‍♀ī¸"; + inline constexpr const char woman_shrugging_medium_dark_skin_tone[] = "🤷🏾‍♀ī¸"; + inline constexpr const char woman_shrugging_tone5[] = "🤷đŸŋ‍♀ī¸"; + inline constexpr const char woman_shrugging_dark_skin_tone[] = "🤷đŸŋ‍♀ī¸"; + inline constexpr const char man_shrugging[] = "🤷‍♂ī¸"; + inline constexpr const char man_shrugging_tone1[] = "🤷đŸģ‍♂ī¸"; + inline constexpr const char man_shrugging_light_skin_tone[] = "🤷đŸģ‍♂ī¸"; + inline constexpr const char man_shrugging_tone2[] = "🤷đŸŧ‍♂ī¸"; + inline constexpr const char man_shrugging_medium_light_skin_tone[] = "🤷đŸŧ‍♂ī¸"; + inline constexpr const char man_shrugging_tone3[] = "🤷đŸŊ‍♂ī¸"; + inline constexpr const char man_shrugging_medium_skin_tone[] = "🤷đŸŊ‍♂ī¸"; + inline constexpr const char man_shrugging_tone4[] = "🤷🏾‍♂ī¸"; + inline constexpr const char man_shrugging_medium_dark_skin_tone[] = "🤷🏾‍♂ī¸"; + inline constexpr const char man_shrugging_tone5[] = "🤷đŸŋ‍♂ī¸"; + inline constexpr const char man_shrugging_dark_skin_tone[] = "🤷đŸŋ‍♂ī¸"; + inline constexpr const char person_pouting[] = "🙎"; + inline constexpr const char person_with_pouting_face[] = "🙎"; + inline constexpr const char person_pouting_tone1[] = "🙎đŸģ"; + inline constexpr const char person_with_pouting_face_tone1[] = "🙎đŸģ"; + inline constexpr const char person_pouting_tone2[] = "🙎đŸŧ"; + inline constexpr const char person_with_pouting_face_tone2[] = "🙎đŸŧ"; + inline constexpr const char person_pouting_tone3[] = "🙎đŸŊ"; + inline constexpr const char person_with_pouting_face_tone3[] = "🙎đŸŊ"; + inline constexpr const char person_pouting_tone4[] = "🙎🏾"; + inline constexpr const char person_with_pouting_face_tone4[] = "🙎🏾"; + inline constexpr const char person_pouting_tone5[] = "🙎đŸŋ"; + inline constexpr const char person_with_pouting_face_tone5[] = "🙎đŸŋ"; + inline constexpr const char woman_pouting[] = "🙎‍♀ī¸"; + inline constexpr const char woman_pouting_tone1[] = "🙎đŸģ‍♀ī¸"; + inline constexpr const char woman_pouting_light_skin_tone[] = "🙎đŸģ‍♀ī¸"; + inline constexpr const char woman_pouting_tone2[] = "🙎đŸŧ‍♀ī¸"; + inline constexpr const char woman_pouting_medium_light_skin_tone[] = "🙎đŸŧ‍♀ī¸"; + inline constexpr const char woman_pouting_tone3[] = "🙎đŸŊ‍♀ī¸"; + inline constexpr const char woman_pouting_medium_skin_tone[] = "🙎đŸŊ‍♀ī¸"; + inline constexpr const char woman_pouting_tone4[] = "🙎🏾‍♀ī¸"; + inline constexpr const char woman_pouting_medium_dark_skin_tone[] = "🙎🏾‍♀ī¸"; + inline constexpr const char woman_pouting_tone5[] = "🙎đŸŋ‍♀ī¸"; + inline constexpr const char woman_pouting_dark_skin_tone[] = "🙎đŸŋ‍♀ī¸"; + inline constexpr const char man_pouting[] = "🙎‍♂ī¸"; + inline constexpr const char man_pouting_tone1[] = "🙎đŸģ‍♂ī¸"; + inline constexpr const char man_pouting_light_skin_tone[] = "🙎đŸģ‍♂ī¸"; + inline constexpr const char man_pouting_tone2[] = "🙎đŸŧ‍♂ī¸"; + inline constexpr const char man_pouting_medium_light_skin_tone[] = "🙎đŸŧ‍♂ī¸"; + inline constexpr const char man_pouting_tone3[] = "🙎đŸŊ‍♂ī¸"; + inline constexpr const char man_pouting_medium_skin_tone[] = "🙎đŸŊ‍♂ī¸"; + inline constexpr const char man_pouting_tone4[] = "🙎🏾‍♂ī¸"; + inline constexpr const char man_pouting_medium_dark_skin_tone[] = "🙎🏾‍♂ī¸"; + inline constexpr const char man_pouting_tone5[] = "🙎đŸŋ‍♂ī¸"; + inline constexpr const char man_pouting_dark_skin_tone[] = "🙎đŸŋ‍♂ī¸"; + inline constexpr const char person_frowning[] = "🙍"; + inline constexpr const char person_frowning_tone1[] = "🙍đŸģ"; + inline constexpr const char person_frowning_tone2[] = "🙍đŸŧ"; + inline constexpr const char person_frowning_tone3[] = "🙍đŸŊ"; + inline constexpr const char person_frowning_tone4[] = "🙍🏾"; + inline constexpr const char person_frowning_tone5[] = "🙍đŸŋ"; + inline constexpr const char woman_frowning[] = "🙍‍♀ī¸"; + inline constexpr const char woman_frowning_tone1[] = "🙍đŸģ‍♀ī¸"; + inline constexpr const char woman_frowning_light_skin_tone[] = "🙍đŸģ‍♀ī¸"; + inline constexpr const char woman_frowning_tone2[] = "🙍đŸŧ‍♀ī¸"; + inline constexpr const char woman_frowning_medium_light_skin_tone[] = "🙍đŸŧ‍♀ī¸"; + inline constexpr const char woman_frowning_tone3[] = "🙍đŸŊ‍♀ī¸"; + inline constexpr const char woman_frowning_medium_skin_tone[] = "🙍đŸŊ‍♀ī¸"; + inline constexpr const char woman_frowning_tone4[] = "🙍🏾‍♀ī¸"; + inline constexpr const char woman_frowning_medium_dark_skin_tone[] = "🙍🏾‍♀ī¸"; + inline constexpr const char woman_frowning_tone5[] = "🙍đŸŋ‍♀ī¸"; + inline constexpr const char woman_frowning_dark_skin_tone[] = "🙍đŸŋ‍♀ī¸"; + inline constexpr const char man_frowning[] = "🙍‍♂ī¸"; + inline constexpr const char man_frowning_tone1[] = "🙍đŸģ‍♂ī¸"; + inline constexpr const char man_frowning_light_skin_tone[] = "🙍đŸģ‍♂ī¸"; + inline constexpr const char man_frowning_tone2[] = "🙍đŸŧ‍♂ī¸"; + inline constexpr const char man_frowning_medium_light_skin_tone[] = "🙍đŸŧ‍♂ī¸"; + inline constexpr const char man_frowning_tone3[] = "🙍đŸŊ‍♂ī¸"; + inline constexpr const char man_frowning_medium_skin_tone[] = "🙍đŸŊ‍♂ī¸"; + inline constexpr const char man_frowning_tone4[] = "🙍🏾‍♂ī¸"; + inline constexpr const char man_frowning_medium_dark_skin_tone[] = "🙍🏾‍♂ī¸"; + inline constexpr const char man_frowning_tone5[] = "🙍đŸŋ‍♂ī¸"; + inline constexpr const char man_frowning_dark_skin_tone[] = "🙍đŸŋ‍♂ī¸"; + inline constexpr const char person_getting_haircut[] = "💇"; + inline constexpr const char haircut[] = "💇"; + inline constexpr const char person_getting_haircut_tone1[] = "💇đŸģ"; + inline constexpr const char haircut_tone1[] = "💇đŸģ"; + inline constexpr const char person_getting_haircut_tone2[] = "💇đŸŧ"; + inline constexpr const char haircut_tone2[] = "💇đŸŧ"; + inline constexpr const char person_getting_haircut_tone3[] = "💇đŸŊ"; + inline constexpr const char haircut_tone3[] = "💇đŸŊ"; + inline constexpr const char person_getting_haircut_tone4[] = "💇🏾"; + inline constexpr const char haircut_tone4[] = "💇🏾"; + inline constexpr const char person_getting_haircut_tone5[] = "💇đŸŋ"; + inline constexpr const char haircut_tone5[] = "💇đŸŋ"; + inline constexpr const char woman_getting_haircut[] = "💇‍♀ī¸"; + inline constexpr const char woman_getting_haircut_tone1[] = "💇đŸģ‍♀ī¸"; + inline constexpr const char woman_getting_haircut_light_skin_tone[] = "💇đŸģ‍♀ī¸"; + inline constexpr const char woman_getting_haircut_tone2[] = "💇đŸŧ‍♀ī¸"; + inline constexpr const char woman_getting_haircut_medium_light_skin_tone[] = "💇đŸŧ‍♀ī¸"; + inline constexpr const char woman_getting_haircut_tone3[] = "💇đŸŊ‍♀ī¸"; + inline constexpr const char woman_getting_haircut_medium_skin_tone[] = "💇đŸŊ‍♀ī¸"; + inline constexpr const char woman_getting_haircut_tone4[] = "💇🏾‍♀ī¸"; + inline constexpr const char woman_getting_haircut_medium_dark_skin_tone[] = "💇🏾‍♀ī¸"; + inline constexpr const char woman_getting_haircut_tone5[] = "💇đŸŋ‍♀ī¸"; + inline constexpr const char woman_getting_haircut_dark_skin_tone[] = "💇đŸŋ‍♀ī¸"; + inline constexpr const char man_getting_haircut[] = "💇‍♂ī¸"; + inline constexpr const char man_getting_haircut_tone1[] = "💇đŸģ‍♂ī¸"; + inline constexpr const char man_getting_haircut_light_skin_tone[] = "💇đŸģ‍♂ī¸"; + inline constexpr const char man_getting_haircut_tone2[] = "💇đŸŧ‍♂ī¸"; + inline constexpr const char man_getting_haircut_medium_light_skin_tone[] = "💇đŸŧ‍♂ī¸"; + inline constexpr const char man_getting_haircut_tone3[] = "💇đŸŊ‍♂ī¸"; + inline constexpr const char man_getting_haircut_medium_skin_tone[] = "💇đŸŊ‍♂ī¸"; + inline constexpr const char man_getting_haircut_tone4[] = "💇🏾‍♂ī¸"; + inline constexpr const char man_getting_haircut_medium_dark_skin_tone[] = "💇🏾‍♂ī¸"; + inline constexpr const char man_getting_haircut_tone5[] = "💇đŸŋ‍♂ī¸"; + inline constexpr const char man_getting_haircut_dark_skin_tone[] = "💇đŸŋ‍♂ī¸"; + inline constexpr const char person_getting_massage[] = "💆"; + inline constexpr const char massage[] = "💆"; + inline constexpr const char person_getting_massage_tone1[] = "💆đŸģ"; + inline constexpr const char massage_tone1[] = "💆đŸģ"; + inline constexpr const char person_getting_massage_tone2[] = "💆đŸŧ"; + inline constexpr const char massage_tone2[] = "💆đŸŧ"; + inline constexpr const char person_getting_massage_tone3[] = "💆đŸŊ"; + inline constexpr const char massage_tone3[] = "💆đŸŊ"; + inline constexpr const char person_getting_massage_tone4[] = "💆🏾"; + inline constexpr const char massage_tone4[] = "💆🏾"; + inline constexpr const char person_getting_massage_tone5[] = "💆đŸŋ"; + inline constexpr const char massage_tone5[] = "💆đŸŋ"; + inline constexpr const char woman_getting_face_massage[] = "💆‍♀ī¸"; + inline constexpr const char woman_getting_face_massage_tone1[] = "💆đŸģ‍♀ī¸"; + inline constexpr const char woman_getting_face_massage_light_skin_tone[] = "💆đŸģ‍♀ī¸"; + inline constexpr const char woman_getting_face_massage_tone2[] = "💆đŸŧ‍♀ī¸"; + inline constexpr const char woman_getting_face_massage_medium_light_skin_tone[] = "💆đŸŧ‍♀ī¸"; + inline constexpr const char woman_getting_face_massage_tone3[] = "💆đŸŊ‍♀ī¸"; + inline constexpr const char woman_getting_face_massage_medium_skin_tone[] = "💆đŸŊ‍♀ī¸"; + inline constexpr const char woman_getting_face_massage_tone4[] = "💆🏾‍♀ī¸"; + inline constexpr const char woman_getting_face_massage_medium_dark_skin_tone[] = "💆🏾‍♀ī¸"; + inline constexpr const char woman_getting_face_massage_tone5[] = "💆đŸŋ‍♀ī¸"; + inline constexpr const char woman_getting_face_massage_dark_skin_tone[] = "💆đŸŋ‍♀ī¸"; + inline constexpr const char man_getting_face_massage[] = "💆‍♂ī¸"; + inline constexpr const char man_getting_face_massage_tone1[] = "💆đŸģ‍♂ī¸"; + inline constexpr const char man_getting_face_massage_light_skin_tone[] = "💆đŸģ‍♂ī¸"; + inline constexpr const char man_getting_face_massage_tone2[] = "💆đŸŧ‍♂ī¸"; + inline constexpr const char man_getting_face_massage_medium_light_skin_tone[] = "💆đŸŧ‍♂ī¸"; + inline constexpr const char man_getting_face_massage_tone3[] = "💆đŸŊ‍♂ī¸"; + inline constexpr const char man_getting_face_massage_medium_skin_tone[] = "💆đŸŊ‍♂ī¸"; + inline constexpr const char man_getting_face_massage_tone4[] = "💆🏾‍♂ī¸"; + inline constexpr const char man_getting_face_massage_medium_dark_skin_tone[] = "💆🏾‍♂ī¸"; + inline constexpr const char man_getting_face_massage_tone5[] = "💆đŸŋ‍♂ī¸"; + inline constexpr const char man_getting_face_massage_dark_skin_tone[] = "💆đŸŋ‍♂ī¸"; + inline constexpr const char person_in_steamy_room[] = "🧖"; + inline constexpr const char person_in_steamy_room_tone1[] = "🧖đŸģ"; + inline constexpr const char person_in_steamy_room_light_skin_tone[] = "🧖đŸģ"; + inline constexpr const char person_in_steamy_room_tone2[] = "🧖đŸŧ"; + inline constexpr const char person_in_steamy_room_medium_light_skin_tone[] = "🧖đŸŧ"; + inline constexpr const char person_in_steamy_room_tone3[] = "🧖đŸŊ"; + inline constexpr const char person_in_steamy_room_medium_skin_tone[] = "🧖đŸŊ"; + inline constexpr const char person_in_steamy_room_tone4[] = "🧖🏾"; + inline constexpr const char person_in_steamy_room_medium_dark_skin_tone[] = "🧖🏾"; + inline constexpr const char person_in_steamy_room_tone5[] = "🧖đŸŋ"; + inline constexpr const char person_in_steamy_room_dark_skin_tone[] = "🧖đŸŋ"; + inline constexpr const char woman_in_steamy_room[] = "🧖‍♀ī¸"; + inline constexpr const char woman_in_steamy_room_tone1[] = "🧖đŸģ‍♀ī¸"; + inline constexpr const char woman_in_steamy_room_light_skin_tone[] = "🧖đŸģ‍♀ī¸"; + inline constexpr const char woman_in_steamy_room_tone2[] = "🧖đŸŧ‍♀ī¸"; + inline constexpr const char woman_in_steamy_room_medium_light_skin_tone[] = "🧖đŸŧ‍♀ī¸"; + inline constexpr const char woman_in_steamy_room_tone3[] = "🧖đŸŊ‍♀ī¸"; + inline constexpr const char woman_in_steamy_room_medium_skin_tone[] = "🧖đŸŊ‍♀ī¸"; + inline constexpr const char woman_in_steamy_room_tone4[] = "🧖🏾‍♀ī¸"; + inline constexpr const char woman_in_steamy_room_medium_dark_skin_tone[] = "🧖🏾‍♀ī¸"; + inline constexpr const char woman_in_steamy_room_tone5[] = "🧖đŸŋ‍♀ī¸"; + inline constexpr const char woman_in_steamy_room_dark_skin_tone[] = "🧖đŸŋ‍♀ī¸"; + inline constexpr const char man_in_steamy_room[] = "🧖‍♂ī¸"; + inline constexpr const char man_in_steamy_room_tone1[] = "🧖đŸģ‍♂ī¸"; + inline constexpr const char man_in_steamy_room_light_skin_tone[] = "🧖đŸģ‍♂ī¸"; + inline constexpr const char man_in_steamy_room_tone2[] = "🧖đŸŧ‍♂ī¸"; + inline constexpr const char man_in_steamy_room_medium_light_skin_tone[] = "🧖đŸŧ‍♂ī¸"; + inline constexpr const char man_in_steamy_room_tone3[] = "🧖đŸŊ‍♂ī¸"; + inline constexpr const char man_in_steamy_room_medium_skin_tone[] = "🧖đŸŊ‍♂ī¸"; + inline constexpr const char man_in_steamy_room_tone4[] = "🧖🏾‍♂ī¸"; + inline constexpr const char man_in_steamy_room_medium_dark_skin_tone[] = "🧖🏾‍♂ī¸"; + inline constexpr const char man_in_steamy_room_tone5[] = "🧖đŸŋ‍♂ī¸"; + inline constexpr const char man_in_steamy_room_dark_skin_tone[] = "🧖đŸŋ‍♂ī¸"; + inline constexpr const char nail_care[] = "💅"; + inline constexpr const char nail_care_tone1[] = "💅đŸģ"; + inline constexpr const char nail_care_tone2[] = "💅đŸŧ"; + inline constexpr const char nail_care_tone3[] = "💅đŸŊ"; + inline constexpr const char nail_care_tone4[] = "💅🏾"; + inline constexpr const char nail_care_tone5[] = "💅đŸŋ"; + inline constexpr const char selfie[] = "đŸ¤ŗ"; + inline constexpr const char selfie_tone1[] = "đŸ¤ŗđŸģ"; + inline constexpr const char selfie_tone2[] = "đŸ¤ŗđŸŧ"; + inline constexpr const char selfie_tone3[] = "đŸ¤ŗđŸŊ"; + inline constexpr const char selfie_tone4[] = "đŸ¤ŗ🏾"; + inline constexpr const char selfie_tone5[] = "đŸ¤ŗđŸŋ"; + inline constexpr const char dancer[] = "💃"; + inline constexpr const char dancer_tone1[] = "💃đŸģ"; + inline constexpr const char dancer_tone2[] = "💃đŸŧ"; + inline constexpr const char dancer_tone3[] = "💃đŸŊ"; + inline constexpr const char dancer_tone4[] = "💃🏾"; + inline constexpr const char dancer_tone5[] = "💃đŸŋ"; + inline constexpr const char man_dancing[] = "đŸ•ē"; + inline constexpr const char male_dancer[] = "đŸ•ē"; + inline constexpr const char man_dancing_tone1[] = "đŸ•ēđŸģ"; + inline constexpr const char male_dancer_tone1[] = "đŸ•ēđŸģ"; + inline constexpr const char man_dancing_tone2[] = "đŸ•ēđŸŧ"; + inline constexpr const char male_dancer_tone2[] = "đŸ•ēđŸŧ"; + inline constexpr const char man_dancing_tone3[] = "đŸ•ēđŸŊ"; + inline constexpr const char male_dancer_tone3[] = "đŸ•ēđŸŊ"; + inline constexpr const char man_dancing_tone5[] = "đŸ•ēđŸŋ"; + inline constexpr const char male_dancer_tone5[] = "đŸ•ēđŸŋ"; + inline constexpr const char man_dancing_tone4[] = "đŸ•ē🏾"; + inline constexpr const char male_dancer_tone4[] = "đŸ•ē🏾"; + inline constexpr const char people_with_bunny_ears_partying[] = "đŸ‘¯"; + inline constexpr const char dancers[] = "đŸ‘¯"; + inline constexpr const char women_with_bunny_ears_partying[] = "đŸ‘¯â€â™€ī¸"; + inline constexpr const char men_with_bunny_ears_partying[] = "đŸ‘¯â€â™‚ī¸"; + inline constexpr const char levitate[] = "🕴ī¸"; + inline constexpr const char man_in_business_suit_levitating[] = "🕴ī¸"; + inline constexpr const char levitate_tone1[] = "🕴đŸģ"; + inline constexpr const char man_in_business_suit_levitating_tone1[] = "🕴đŸģ"; + inline constexpr const char man_in_business_suit_levitating_light_skin_tone[] = "🕴đŸģ"; + inline constexpr const char levitate_tone2[] = "🕴đŸŧ"; + inline constexpr const char man_in_business_suit_levitating_tone2[] = "🕴đŸŧ"; + inline constexpr const char man_in_business_suit_levitating_medium_light_skin_tone[] = "🕴đŸŧ"; + inline constexpr const char levitate_tone3[] = "🕴đŸŊ"; + inline constexpr const char man_in_business_suit_levitating_tone3[] = "🕴đŸŊ"; + inline constexpr const char man_in_business_suit_levitating_medium_skin_tone[] = "🕴đŸŊ"; + inline constexpr const char levitate_tone4[] = "🕴🏾"; + inline constexpr const char man_in_business_suit_levitating_tone4[] = "🕴🏾"; + inline constexpr const char man_in_business_suit_levitating_medium_dark_skin_tone[] = "🕴🏾"; + inline constexpr const char levitate_tone5[] = "🕴đŸŋ"; + inline constexpr const char man_in_business_suit_levitating_tone5[] = "🕴đŸŋ"; + inline constexpr const char man_in_business_suit_levitating_dark_skin_tone[] = "🕴đŸŋ"; + inline constexpr const char person_in_manual_wheelchair[] = "🧑‍đŸĻŊ"; + inline constexpr const char person_in_manual_wheelchair_tone1[] = "🧑đŸģ‍đŸĻŊ"; + inline constexpr const char person_in_manual_wheelchair_light_skin_tone[] = "🧑đŸģ‍đŸĻŊ"; + inline constexpr const char person_in_manual_wheelchair_tone2[] = "🧑đŸŧ‍đŸĻŊ"; + inline constexpr const char person_in_manual_wheelchair_medium_light_skin_tone[] = "🧑đŸŧ‍đŸĻŊ"; + inline constexpr const char person_in_manual_wheelchair_tone3[] = "🧑đŸŊ‍đŸĻŊ"; + inline constexpr const char person_in_manual_wheelchair_medium_skin_tone[] = "🧑đŸŊ‍đŸĻŊ"; + inline constexpr const char person_in_manual_wheelchair_tone4[] = "🧑🏾‍đŸĻŊ"; + inline constexpr const char person_in_manual_wheelchair_medium_dark_skin_tone[] = "🧑🏾‍đŸĻŊ"; + inline constexpr const char person_in_manual_wheelchair_tone5[] = "🧑đŸŋ‍đŸĻŊ"; + inline constexpr const char person_in_manual_wheelchair_dark_skin_tone[] = "🧑đŸŋ‍đŸĻŊ"; + inline constexpr const char woman_in_manual_wheelchair[] = "👩‍đŸĻŊ"; + inline constexpr const char woman_in_manual_wheelchair_tone1[] = "👩đŸģ‍đŸĻŊ"; + inline constexpr const char woman_in_manual_wheelchair_light_skin_tone[] = "👩đŸģ‍đŸĻŊ"; + inline constexpr const char woman_in_manual_wheelchair_tone2[] = "👩đŸŧ‍đŸĻŊ"; + inline constexpr const char woman_in_manual_wheelchair_medium_light_skin_tone[] = "👩đŸŧ‍đŸĻŊ"; + inline constexpr const char woman_in_manual_wheelchair_tone3[] = "👩đŸŊ‍đŸĻŊ"; + inline constexpr const char woman_in_manual_wheelchair_medium_skin_tone[] = "👩đŸŊ‍đŸĻŊ"; + inline constexpr const char woman_in_manual_wheelchair_tone4[] = "👩🏾‍đŸĻŊ"; + inline constexpr const char woman_in_manual_wheelchair_medium_dark_skin_tone[] = "👩🏾‍đŸĻŊ"; + inline constexpr const char woman_in_manual_wheelchair_tone5[] = "👩đŸŋ‍đŸĻŊ"; + inline constexpr const char woman_in_manual_wheelchair_dark_skin_tone[] = "👩đŸŋ‍đŸĻŊ"; + inline constexpr const char man_in_manual_wheelchair[] = "👨‍đŸĻŊ"; + inline constexpr const char man_in_manual_wheelchair_tone1[] = "👨đŸģ‍đŸĻŊ"; + inline constexpr const char man_in_manual_wheelchair_light_skin_tone[] = "👨đŸģ‍đŸĻŊ"; + inline constexpr const char man_in_manual_wheelchair_tone2[] = "👨đŸŧ‍đŸĻŊ"; + inline constexpr const char man_in_manual_wheelchair_medium_light_skin_tone[] = "👨đŸŧ‍đŸĻŊ"; + inline constexpr const char man_in_manual_wheelchair_tone3[] = "👨đŸŊ‍đŸĻŊ"; + inline constexpr const char man_in_manual_wheelchair_medium_skin_tone[] = "👨đŸŊ‍đŸĻŊ"; + inline constexpr const char man_in_manual_wheelchair_tone4[] = "👨🏾‍đŸĻŊ"; + inline constexpr const char man_in_manual_wheelchair_medium_dark_skin_tone[] = "👨🏾‍đŸĻŊ"; + inline constexpr const char man_in_manual_wheelchair_tone5[] = "👨đŸŋ‍đŸĻŊ"; + inline constexpr const char man_in_manual_wheelchair_dark_skin_tone[] = "👨đŸŋ‍đŸĻŊ"; + inline constexpr const char person_in_motorized_wheelchair[] = "🧑‍đŸĻŧ"; + inline constexpr const char person_in_motorized_wheelchair_tone1[] = "🧑đŸģ‍đŸĻŧ"; + inline constexpr const char person_in_motorized_wheelchair_light_skin_tone[] = "🧑đŸģ‍đŸĻŧ"; + inline constexpr const char person_in_motorized_wheelchair_tone2[] = "🧑đŸŧ‍đŸĻŧ"; + inline constexpr const char person_in_motorized_wheelchair_medium_light_skin_tone[] = "🧑đŸŧ‍đŸĻŧ"; + inline constexpr const char person_in_motorized_wheelchair_tone3[] = "🧑đŸŊ‍đŸĻŧ"; + inline constexpr const char person_in_motorized_wheelchair_medium_skin_tone[] = "🧑đŸŊ‍đŸĻŧ"; + inline constexpr const char person_in_motorized_wheelchair_tone4[] = "🧑🏾‍đŸĻŧ"; + inline constexpr const char person_in_motorized_wheelchair_medium_dark_skin_tone[] = "🧑🏾‍đŸĻŧ"; + inline constexpr const char person_in_motorized_wheelchair_tone5[] = "🧑đŸŋ‍đŸĻŧ"; + inline constexpr const char person_in_motorized_wheelchair_dark_skin_tone[] = "🧑đŸŋ‍đŸĻŧ"; + inline constexpr const char woman_in_motorized_wheelchair[] = "👩‍đŸĻŧ"; + inline constexpr const char woman_in_motorized_wheelchair_tone1[] = "👩đŸģ‍đŸĻŧ"; + inline constexpr const char woman_in_motorized_wheelchair_light_skin_tone[] = "👩đŸģ‍đŸĻŧ"; + inline constexpr const char woman_in_motorized_wheelchair_tone2[] = "👩đŸŧ‍đŸĻŧ"; + inline constexpr const char woman_in_motorized_wheelchair_medium_light_skin_tone[] = "👩đŸŧ‍đŸĻŧ"; + inline constexpr const char woman_in_motorized_wheelchair_tone3[] = "👩đŸŊ‍đŸĻŧ"; + inline constexpr const char woman_in_motorized_wheelchair_medium_skin_tone[] = "👩đŸŊ‍đŸĻŧ"; + inline constexpr const char woman_in_motorized_wheelchair_tone4[] = "👩🏾‍đŸĻŧ"; + inline constexpr const char woman_in_motorized_wheelchair_medium_dark_skin_tone[] = "👩🏾‍đŸĻŧ"; + inline constexpr const char woman_in_motorized_wheelchair_tone5[] = "👩đŸŋ‍đŸĻŧ"; + inline constexpr const char woman_in_motorized_wheelchair_dark_skin_tone[] = "👩đŸŋ‍đŸĻŧ"; + inline constexpr const char man_in_motorized_wheelchair[] = "👨‍đŸĻŧ"; + inline constexpr const char man_in_motorized_wheelchair_tone1[] = "👨đŸģ‍đŸĻŧ"; + inline constexpr const char man_in_motorized_wheelchair_light_skin_tone[] = "👨đŸģ‍đŸĻŧ"; + inline constexpr const char man_in_motorized_wheelchair_tone2[] = "👨đŸŧ‍đŸĻŧ"; + inline constexpr const char man_in_motorized_wheelchair_medium_light_skin_tone[] = "👨đŸŧ‍đŸĻŧ"; + inline constexpr const char man_in_motorized_wheelchair_tone3[] = "👨đŸŊ‍đŸĻŧ"; + inline constexpr const char man_in_motorized_wheelchair_medium_skin_tone[] = "👨đŸŊ‍đŸĻŧ"; + inline constexpr const char man_in_motorized_wheelchair_tone4[] = "👨🏾‍đŸĻŧ"; + inline constexpr const char man_in_motorized_wheelchair_medium_dark_skin_tone[] = "👨🏾‍đŸĻŧ"; + inline constexpr const char man_in_motorized_wheelchair_tone5[] = "👨đŸŋ‍đŸĻŧ"; + inline constexpr const char man_in_motorized_wheelchair_dark_skin_tone[] = "👨đŸŋ‍đŸĻŧ"; + inline constexpr const char person_walking[] = "đŸšļ"; + inline constexpr const char walking[] = "đŸšļ"; + inline constexpr const char person_walking_tone1[] = "đŸšļđŸģ"; + inline constexpr const char walking_tone1[] = "đŸšļđŸģ"; + inline constexpr const char person_walking_tone2[] = "đŸšļđŸŧ"; + inline constexpr const char walking_tone2[] = "đŸšļđŸŧ"; + inline constexpr const char person_walking_tone3[] = "đŸšļđŸŊ"; + inline constexpr const char walking_tone3[] = "đŸšļđŸŊ"; + inline constexpr const char person_walking_tone4[] = "đŸšļ🏾"; + inline constexpr const char walking_tone4[] = "đŸšļ🏾"; + inline constexpr const char person_walking_tone5[] = "đŸšļđŸŋ"; + inline constexpr const char walking_tone5[] = "đŸšļđŸŋ"; + inline constexpr const char woman_walking[] = "đŸšļ‍♀ī¸"; + inline constexpr const char woman_walking_tone1[] = "đŸšļđŸģ‍♀ī¸"; + inline constexpr const char woman_walking_light_skin_tone[] = "đŸšļđŸģ‍♀ī¸"; + inline constexpr const char woman_walking_tone2[] = "đŸšļđŸŧ‍♀ī¸"; + inline constexpr const char woman_walking_medium_light_skin_tone[] = "đŸšļđŸŧ‍♀ī¸"; + inline constexpr const char woman_walking_tone3[] = "đŸšļđŸŊ‍♀ī¸"; + inline constexpr const char woman_walking_medium_skin_tone[] = "đŸšļđŸŊ‍♀ī¸"; + inline constexpr const char woman_walking_tone4[] = "đŸšļ🏾‍♀ī¸"; + inline constexpr const char woman_walking_medium_dark_skin_tone[] = "đŸšļ🏾‍♀ī¸"; + inline constexpr const char woman_walking_tone5[] = "đŸšļđŸŋ‍♀ī¸"; + inline constexpr const char woman_walking_dark_skin_tone[] = "đŸšļđŸŋ‍♀ī¸"; + inline constexpr const char man_walking[] = "đŸšļ‍♂ī¸"; + inline constexpr const char man_walking_tone1[] = "đŸšļđŸģ‍♂ī¸"; + inline constexpr const char man_walking_light_skin_tone[] = "đŸšļđŸģ‍♂ī¸"; + inline constexpr const char man_walking_tone2[] = "đŸšļđŸŧ‍♂ī¸"; + inline constexpr const char man_walking_medium_light_skin_tone[] = "đŸšļđŸŧ‍♂ī¸"; + inline constexpr const char man_walking_tone3[] = "đŸšļđŸŊ‍♂ī¸"; + inline constexpr const char man_walking_medium_skin_tone[] = "đŸšļđŸŊ‍♂ī¸"; + inline constexpr const char man_walking_tone4[] = "đŸšļ🏾‍♂ī¸"; + inline constexpr const char man_walking_medium_dark_skin_tone[] = "đŸšļ🏾‍♂ī¸"; + inline constexpr const char man_walking_tone5[] = "đŸšļđŸŋ‍♂ī¸"; + inline constexpr const char man_walking_dark_skin_tone[] = "đŸšļđŸŋ‍♂ī¸"; + inline constexpr const char person_with_probing_cane[] = "🧑‍đŸĻ¯"; + inline constexpr const char person_with_probing_cane_tone1[] = "🧑đŸģ‍đŸĻ¯"; + inline constexpr const char person_with_probing_cane_light_skin_tone[] = "🧑đŸģ‍đŸĻ¯"; + inline constexpr const char person_with_probing_cane_tone2[] = "🧑đŸŧ‍đŸĻ¯"; + inline constexpr const char person_with_probing_cane_medium_light_skin_tone[] = "🧑đŸŧ‍đŸĻ¯"; + inline constexpr const char person_with_probing_cane_tone3[] = "🧑đŸŊ‍đŸĻ¯"; + inline constexpr const char person_with_probing_cane_medium_skin_tone[] = "🧑đŸŊ‍đŸĻ¯"; + inline constexpr const char person_with_probing_cane_tone4[] = "🧑🏾‍đŸĻ¯"; + inline constexpr const char person_with_probing_cane_medium_dark_skin_tone[] = "🧑🏾‍đŸĻ¯"; + inline constexpr const char person_with_probing_cane_tone5[] = "🧑đŸŋ‍đŸĻ¯"; + inline constexpr const char person_with_probing_cane_dark_skin_tone[] = "🧑đŸŋ‍đŸĻ¯"; + inline constexpr const char woman_with_probing_cane[] = "👩‍đŸĻ¯"; + inline constexpr const char woman_with_probing_cane_tone1[] = "👩đŸģ‍đŸĻ¯"; + inline constexpr const char woman_with_probing_cane_light_skin_tone[] = "👩đŸģ‍đŸĻ¯"; + inline constexpr const char woman_with_probing_cane_tone2[] = "👩đŸŧ‍đŸĻ¯"; + inline constexpr const char woman_with_probing_cane_medium_light_skin_tone[] = "👩đŸŧ‍đŸĻ¯"; + inline constexpr const char woman_with_probing_cane_tone3[] = "👩đŸŊ‍đŸĻ¯"; + inline constexpr const char woman_with_probing_cane_medium_skin_tone[] = "👩đŸŊ‍đŸĻ¯"; + inline constexpr const char woman_with_probing_cane_tone4[] = "👩🏾‍đŸĻ¯"; + inline constexpr const char woman_with_probing_cane_medium_dark_skin_tone[] = "👩🏾‍đŸĻ¯"; + inline constexpr const char woman_with_probing_cane_tone5[] = "👩đŸŋ‍đŸĻ¯"; + inline constexpr const char woman_with_probing_cane_dark_skin_tone[] = "👩đŸŋ‍đŸĻ¯"; + inline constexpr const char man_with_probing_cane[] = "👨‍đŸĻ¯"; + inline constexpr const char man_with_probing_cane_tone1[] = "👨đŸģ‍đŸĻ¯"; + inline constexpr const char man_with_probing_cane_light_skin_tone[] = "👨đŸģ‍đŸĻ¯"; + inline constexpr const char man_with_probing_cane_tone2[] = "👨đŸŧ‍đŸĻ¯"; + inline constexpr const char man_with_probing_cane_medium_light_skin_tone[] = "👨đŸŧ‍đŸĻ¯"; + inline constexpr const char man_with_probing_cane_tone3[] = "👨đŸŊ‍đŸĻ¯"; + inline constexpr const char man_with_probing_cane_medium_skin_tone[] = "👨đŸŊ‍đŸĻ¯"; + inline constexpr const char man_with_probing_cane_tone4[] = "👨🏾‍đŸĻ¯"; + inline constexpr const char man_with_probing_cane_medium_dark_skin_tone[] = "👨🏾‍đŸĻ¯"; + inline constexpr const char man_with_probing_cane_tone5[] = "👨đŸŋ‍đŸĻ¯"; + inline constexpr const char man_with_probing_cane_dark_skin_tone[] = "👨đŸŋ‍đŸĻ¯"; + inline constexpr const char person_kneeling[] = "🧎"; + inline constexpr const char person_kneeling_tone1[] = "🧎đŸģ"; + inline constexpr const char person_kneeling_light_skin_tone[] = "🧎đŸģ"; + inline constexpr const char person_kneeling_tone2[] = "🧎đŸŧ"; + inline constexpr const char person_kneeling_medium_light_skin_tone[] = "🧎đŸŧ"; + inline constexpr const char person_kneeling_tone3[] = "🧎đŸŊ"; + inline constexpr const char person_kneeling_medium_skin_tone[] = "🧎đŸŊ"; + inline constexpr const char person_kneeling_tone4[] = "🧎🏾"; + inline constexpr const char person_kneeling_medium_dark_skin_tone[] = "🧎🏾"; + inline constexpr const char person_kneeling_tone5[] = "🧎đŸŋ"; + inline constexpr const char person_kneeling_dark_skin_tone[] = "🧎đŸŋ"; + inline constexpr const char woman_kneeling[] = "🧎‍♀ī¸"; + inline constexpr const char woman_kneeling_tone1[] = "🧎đŸģ‍♀ī¸"; + inline constexpr const char woman_kneeling_light_skin_tone[] = "🧎đŸģ‍♀ī¸"; + inline constexpr const char woman_kneeling_tone2[] = "🧎đŸŧ‍♀ī¸"; + inline constexpr const char woman_kneeling_medium_light_skin_tone[] = "🧎đŸŧ‍♀ī¸"; + inline constexpr const char woman_kneeling_tone3[] = "🧎đŸŊ‍♀ī¸"; + inline constexpr const char woman_kneeling_medium_skin_tone[] = "🧎đŸŊ‍♀ī¸"; + inline constexpr const char woman_kneeling_tone4[] = "🧎🏾‍♀ī¸"; + inline constexpr const char woman_kneeling_medium_dark_skin_tone[] = "🧎🏾‍♀ī¸"; + inline constexpr const char woman_kneeling_tone5[] = "🧎đŸŋ‍♀ī¸"; + inline constexpr const char woman_kneeling_dark_skin_tone[] = "🧎đŸŋ‍♀ī¸"; + inline constexpr const char man_kneeling[] = "🧎‍♂ī¸"; + inline constexpr const char man_kneeling_tone1[] = "🧎đŸģ‍♂ī¸"; + inline constexpr const char man_kneeling_light_skin_tone[] = "🧎đŸģ‍♂ī¸"; + inline constexpr const char man_kneeling_tone2[] = "🧎đŸŧ‍♂ī¸"; + inline constexpr const char man_kneeling_medium_light_skin_tone[] = "🧎đŸŧ‍♂ī¸"; + inline constexpr const char man_kneeling_tone3[] = "🧎đŸŊ‍♂ī¸"; + inline constexpr const char man_kneeling_medium_skin_tone[] = "🧎đŸŊ‍♂ī¸"; + inline constexpr const char man_kneeling_tone4[] = "🧎🏾‍♂ī¸"; + inline constexpr const char man_kneeling_medium_dark_skin_tone[] = "🧎🏾‍♂ī¸"; + inline constexpr const char man_kneeling_tone5[] = "🧎đŸŋ‍♂ī¸"; + inline constexpr const char man_kneeling_dark_skin_tone[] = "🧎đŸŋ‍♂ī¸"; + inline constexpr const char person_running[] = "🏃"; + inline constexpr const char runner[] = "🏃"; + inline constexpr const char person_running_tone1[] = "🏃đŸģ"; + inline constexpr const char runner_tone1[] = "🏃đŸģ"; + inline constexpr const char person_running_tone2[] = "🏃đŸŧ"; + inline constexpr const char runner_tone2[] = "🏃đŸŧ"; + inline constexpr const char person_running_tone3[] = "🏃đŸŊ"; + inline constexpr const char runner_tone3[] = "🏃đŸŊ"; + inline constexpr const char person_running_tone4[] = "🏃🏾"; + inline constexpr const char runner_tone4[] = "🏃🏾"; + inline constexpr const char person_running_tone5[] = "🏃đŸŋ"; + inline constexpr const char runner_tone5[] = "🏃đŸŋ"; + inline constexpr const char woman_running[] = "🏃‍♀ī¸"; + inline constexpr const char woman_running_tone1[] = "🏃đŸģ‍♀ī¸"; + inline constexpr const char woman_running_light_skin_tone[] = "🏃đŸģ‍♀ī¸"; + inline constexpr const char woman_running_tone2[] = "🏃đŸŧ‍♀ī¸"; + inline constexpr const char woman_running_medium_light_skin_tone[] = "🏃đŸŧ‍♀ī¸"; + inline constexpr const char woman_running_tone3[] = "🏃đŸŊ‍♀ī¸"; + inline constexpr const char woman_running_medium_skin_tone[] = "🏃đŸŊ‍♀ī¸"; + inline constexpr const char woman_running_tone4[] = "🏃🏾‍♀ī¸"; + inline constexpr const char woman_running_medium_dark_skin_tone[] = "🏃🏾‍♀ī¸"; + inline constexpr const char woman_running_tone5[] = "🏃đŸŋ‍♀ī¸"; + inline constexpr const char woman_running_dark_skin_tone[] = "🏃đŸŋ‍♀ī¸"; + inline constexpr const char man_running[] = "🏃‍♂ī¸"; + inline constexpr const char man_running_tone1[] = "🏃đŸģ‍♂ī¸"; + inline constexpr const char man_running_light_skin_tone[] = "🏃đŸģ‍♂ī¸"; + inline constexpr const char man_running_tone2[] = "🏃đŸŧ‍♂ī¸"; + inline constexpr const char man_running_medium_light_skin_tone[] = "🏃đŸŧ‍♂ī¸"; + inline constexpr const char man_running_tone3[] = "🏃đŸŊ‍♂ī¸"; + inline constexpr const char man_running_medium_skin_tone[] = "🏃đŸŊ‍♂ī¸"; + inline constexpr const char man_running_tone4[] = "🏃🏾‍♂ī¸"; + inline constexpr const char man_running_medium_dark_skin_tone[] = "🏃🏾‍♂ī¸"; + inline constexpr const char man_running_tone5[] = "🏃đŸŋ‍♂ī¸"; + inline constexpr const char man_running_dark_skin_tone[] = "🏃đŸŋ‍♂ī¸"; + inline constexpr const char person_standing[] = "🧍"; + inline constexpr const char person_standing_tone1[] = "🧍đŸģ"; + inline constexpr const char person_standing_light_skin_tone[] = "🧍đŸģ"; + inline constexpr const char person_standing_tone2[] = "🧍đŸŧ"; + inline constexpr const char person_standing_medium_light_skin_tone[] = "🧍đŸŧ"; + inline constexpr const char person_standing_tone3[] = "🧍đŸŊ"; + inline constexpr const char person_standing_medium_skin_tone[] = "🧍đŸŊ"; + inline constexpr const char person_standing_tone4[] = "🧍🏾"; + inline constexpr const char person_standing_medium_dark_skin_tone[] = "🧍🏾"; + inline constexpr const char person_standing_tone5[] = "🧍đŸŋ"; + inline constexpr const char person_standing_dark_skin_tone[] = "🧍đŸŋ"; + inline constexpr const char woman_standing[] = "🧍‍♀ī¸"; + inline constexpr const char woman_standing_tone1[] = "🧍đŸģ‍♀ī¸"; + inline constexpr const char woman_standing_light_skin_tone[] = "🧍đŸģ‍♀ī¸"; + inline constexpr const char woman_standing_tone2[] = "🧍đŸŧ‍♀ī¸"; + inline constexpr const char woman_standing_medium_light_skin_tone[] = "🧍đŸŧ‍♀ī¸"; + inline constexpr const char woman_standing_tone3[] = "🧍đŸŊ‍♀ī¸"; + inline constexpr const char woman_standing_medium_skin_tone[] = "🧍đŸŊ‍♀ī¸"; + inline constexpr const char woman_standing_tone4[] = "🧍🏾‍♀ī¸"; + inline constexpr const char woman_standing_medium_dark_skin_tone[] = "🧍🏾‍♀ī¸"; + inline constexpr const char woman_standing_tone5[] = "🧍đŸŋ‍♀ī¸"; + inline constexpr const char woman_standing_dark_skin_tone[] = "🧍đŸŋ‍♀ī¸"; + inline constexpr const char man_standing[] = "🧍‍♂ī¸"; + inline constexpr const char man_standing_tone1[] = "🧍đŸģ‍♂ī¸"; + inline constexpr const char man_standing_light_skin_tone[] = "🧍đŸģ‍♂ī¸"; + inline constexpr const char man_standing_tone2[] = "🧍đŸŧ‍♂ī¸"; + inline constexpr const char man_standing_medium_light_skin_tone[] = "🧍đŸŧ‍♂ī¸"; + inline constexpr const char man_standing_tone3[] = "🧍đŸŊ‍♂ī¸"; + inline constexpr const char man_standing_medium_skin_tone[] = "🧍đŸŊ‍♂ī¸"; + inline constexpr const char man_standing_tone4[] = "🧍🏾‍♂ī¸"; + inline constexpr const char man_standing_medium_dark_skin_tone[] = "🧍🏾‍♂ī¸"; + inline constexpr const char man_standing_tone5[] = "🧍đŸŋ‍♂ī¸"; + inline constexpr const char man_standing_dark_skin_tone[] = "🧍đŸŋ‍♂ī¸"; + inline constexpr const char people_holding_hands[] = "🧑‍🤝‍🧑"; + inline constexpr const char people_holding_hands_tone1[] = "🧑đŸģ‍🤝‍🧑đŸģ"; + inline constexpr const char people_holding_hands_light_skin_tone[] = "🧑đŸģ‍🤝‍🧑đŸģ"; + inline constexpr const char people_holding_hands_tone1_tone2[] = "🧑đŸģ‍🤝‍🧑đŸŧ"; + inline constexpr const char people_holding_hands_light_skin_tone_medium_light_skin_tone[] = "🧑đŸģ‍🤝‍🧑đŸŧ"; + inline constexpr const char people_holding_hands_tone1_tone3[] = "🧑đŸģ‍🤝‍🧑đŸŊ"; + inline constexpr const char people_holding_hands_light_skin_tone_medium_skin_tone[] = "🧑đŸģ‍🤝‍🧑đŸŊ"; + inline constexpr const char people_holding_hands_tone1_tone4[] = "🧑đŸģ‍🤝‍🧑🏾"; + inline constexpr const char people_holding_hands_light_skin_tone_medium_dark_skin_tone[] = "🧑đŸģ‍🤝‍🧑🏾"; + inline constexpr const char people_holding_hands_tone1_tone5[] = "🧑đŸģ‍🤝‍🧑đŸŋ"; + inline constexpr const char people_holding_hands_light_skin_tone_dark_skin_tone[] = "🧑đŸģ‍🤝‍🧑đŸŋ"; + inline constexpr const char people_holding_hands_tone2_tone1[] = "🧑đŸŧ‍🤝‍🧑đŸģ"; + inline constexpr const char people_holding_hands_medium_light_skin_tone_light_skin_tone[] = "🧑đŸŧ‍🤝‍🧑đŸģ"; + inline constexpr const char people_holding_hands_tone2[] = "🧑đŸŧ‍🤝‍🧑đŸŧ"; + inline constexpr const char people_holding_hands_medium_light_skin_tone[] = "🧑đŸŧ‍🤝‍🧑đŸŧ"; + inline constexpr const char people_holding_hands_tone2_tone3[] = "🧑đŸŧ‍🤝‍🧑đŸŊ"; + inline constexpr const char people_holding_hands_medium_light_skin_tone_medium_skin_tone[] = "🧑đŸŧ‍🤝‍🧑đŸŊ"; + inline constexpr const char people_holding_hands_tone2_tone4[] = "🧑đŸŧ‍🤝‍🧑🏾"; + inline constexpr const char people_holding_hands_medium_light_skin_tone_medium_dark_skin_tone[] = "🧑đŸŧ‍🤝‍🧑🏾"; + inline constexpr const char people_holding_hands_tone2_tone5[] = "🧑đŸŧ‍🤝‍🧑đŸŋ"; + inline constexpr const char people_holding_hands_medium_light_skin_tone_dark_skin_tone[] = "🧑đŸŧ‍🤝‍🧑đŸŋ"; + inline constexpr const char people_holding_hands_tone3_tone1[] = "🧑đŸŊ‍🤝‍🧑đŸģ"; + inline constexpr const char people_holding_hands_medium_skin_tone_light_skin_tone[] = "🧑đŸŊ‍🤝‍🧑đŸģ"; + inline constexpr const char people_holding_hands_tone3_tone2[] = "🧑đŸŊ‍🤝‍🧑đŸŧ"; + inline constexpr const char people_holding_hands_medium_skin_tone_medium_light_skin_tone[] = "🧑đŸŊ‍🤝‍🧑đŸŧ"; + inline constexpr const char people_holding_hands_tone3[] = "🧑đŸŊ‍🤝‍🧑đŸŊ"; + inline constexpr const char people_holding_hands_medium_skin_tone[] = "🧑đŸŊ‍🤝‍🧑đŸŊ"; + inline constexpr const char people_holding_hands_tone3_tone4[] = "🧑đŸŊ‍🤝‍🧑🏾"; + inline constexpr const char people_holding_hands_medium_skin_tone_medium_dark_skin_tone[] = "🧑đŸŊ‍🤝‍🧑🏾"; + inline constexpr const char people_holding_hands_tone3_tone5[] = "🧑đŸŊ‍🤝‍🧑đŸŋ"; + inline constexpr const char people_holding_hands_medium_skin_tone_dark_skin_tone[] = "🧑đŸŊ‍🤝‍🧑đŸŋ"; + inline constexpr const char people_holding_hands_tone4_tone1[] = "🧑🏾‍🤝‍🧑đŸģ"; + inline constexpr const char people_holding_hands_medium_dark_skin_tone_light_skin_tone[] = "🧑🏾‍🤝‍🧑đŸģ"; + inline constexpr const char people_holding_hands_tone4_tone2[] = "🧑🏾‍🤝‍🧑đŸŧ"; + inline constexpr const char people_holding_hands_medium_dark_skin_tone_medium_light_skin_tone[] = "🧑🏾‍🤝‍🧑đŸŧ"; + inline constexpr const char people_holding_hands_tone4_tone3[] = "🧑🏾‍🤝‍🧑đŸŊ"; + inline constexpr const char people_holding_hands_medium_dark_skin_tone_medium_skin_tone[] = "🧑🏾‍🤝‍🧑đŸŊ"; + inline constexpr const char people_holding_hands_tone4[] = "🧑🏾‍🤝‍🧑🏾"; + inline constexpr const char people_holding_hands_medium_dark_skin_tone[] = "🧑🏾‍🤝‍🧑🏾"; + inline constexpr const char people_holding_hands_tone4_tone5[] = "🧑🏾‍🤝‍🧑đŸŋ"; + inline constexpr const char people_holding_hands_medium_dark_skin_tone_dark_skin_tone[] = "🧑🏾‍🤝‍🧑đŸŋ"; + inline constexpr const char people_holding_hands_tone5_tone1[] = "🧑đŸŋ‍🤝‍🧑đŸģ"; + inline constexpr const char people_holding_hands_dark_skin_tone_light_skin_tone[] = "🧑đŸŋ‍🤝‍🧑đŸģ"; + inline constexpr const char people_holding_hands_tone5_tone2[] = "🧑đŸŋ‍🤝‍🧑đŸŧ"; + inline constexpr const char people_holding_hands_dark_skin_tone_medium_light_skin_tone[] = "🧑đŸŋ‍🤝‍🧑đŸŧ"; + inline constexpr const char people_holding_hands_tone5_tone3[] = "🧑đŸŋ‍🤝‍🧑đŸŊ"; + inline constexpr const char people_holding_hands_dark_skin_tone_medium_skin_tone[] = "🧑đŸŋ‍🤝‍🧑đŸŊ"; + inline constexpr const char people_holding_hands_tone5_tone4[] = "🧑đŸŋ‍🤝‍🧑🏾"; + inline constexpr const char people_holding_hands_dark_skin_tone_medium_dark_skin_tone[] = "🧑đŸŋ‍🤝‍🧑🏾"; + inline constexpr const char people_holding_hands_tone5[] = "🧑đŸŋ‍🤝‍🧑đŸŋ"; + inline constexpr const char people_holding_hands_dark_skin_tone[] = "🧑đŸŋ‍🤝‍🧑đŸŋ"; + inline constexpr const char couple[] = "đŸ‘Ģ"; + inline constexpr const char woman_and_man_holding_hands_tone1[] = "đŸ‘ĢđŸģ"; + inline constexpr const char woman_and_man_holding_hands_light_skin_tone[] = "đŸ‘ĢđŸģ"; + inline constexpr const char woman_and_man_holding_hands_tone1_tone2[] = "👩đŸģ‍🤝‍👨đŸŧ"; + inline constexpr const char woman_and_man_holding_hands_light_skin_tone_medium_light_skin_tone[] = "👩đŸģ‍🤝‍👨đŸŧ"; + inline constexpr const char woman_and_man_holding_hands_tone1_tone3[] = "👩đŸģ‍🤝‍👨đŸŊ"; + inline constexpr const char woman_and_man_holding_hands_light_skin_tone_medium_skin_tone[] = "👩đŸģ‍🤝‍👨đŸŊ"; + inline constexpr const char woman_and_man_holding_hands_tone1_tone4[] = "👩đŸģ‍🤝‍👨🏾"; + inline constexpr const char woman_and_man_holding_hands_light_skin_tone_medium_dark_skin_tone[] = "👩đŸģ‍🤝‍👨🏾"; + inline constexpr const char woman_and_man_holding_hands_tone1_tone5[] = "👩đŸģ‍🤝‍👨đŸŋ"; + inline constexpr const char woman_and_man_holding_hands_light_skin_tone_dark_skin_tone[] = "👩đŸģ‍🤝‍👨đŸŋ"; + inline constexpr const char woman_and_man_holding_hands_tone2_tone1[] = "👩đŸŧ‍🤝‍👨đŸģ"; + inline constexpr const char woman_and_man_holding_hands_medium_light_skin_tone_light_skin_tone[] = "👩đŸŧ‍🤝‍👨đŸģ"; + inline constexpr const char woman_and_man_holding_hands_tone2[] = "đŸ‘ĢđŸŧ"; + inline constexpr const char woman_and_man_holding_hands_medium_light_skin_tone[] = "đŸ‘ĢđŸŧ"; + inline constexpr const char woman_and_man_holding_hands_tone2_tone3[] = "👩đŸŧ‍🤝‍👨đŸŊ"; + inline constexpr const char woman_and_man_holding_hands_medium_light_skin_tone_medium_skin_tone[] = "👩đŸŧ‍🤝‍👨đŸŊ"; + inline constexpr const char woman_and_man_holding_hands_tone2_tone4[] = "👩đŸŧ‍🤝‍👨🏾"; + inline constexpr const char woman_and_man_holding_hands_medium_light_skin_tone_medium_dark_skin_tone[] = "👩đŸŧ‍🤝‍👨🏾"; + inline constexpr const char woman_and_man_holding_hands_tone2_tone5[] = "👩đŸŧ‍🤝‍👨đŸŋ"; + inline constexpr const char woman_and_man_holding_hands_medium_light_skin_tone_dark_skin_tone[] = "👩đŸŧ‍🤝‍👨đŸŋ"; + inline constexpr const char woman_and_man_holding_hands_tone3_tone1[] = "👩đŸŊ‍🤝‍👨đŸģ"; + inline constexpr const char woman_and_man_holding_hands_medium_skin_tone_light_skin_tone[] = "👩đŸŊ‍🤝‍👨đŸģ"; + inline constexpr const char woman_and_man_holding_hands_tone3_tone2[] = "👩đŸŊ‍🤝‍👨đŸŧ"; + inline constexpr const char woman_and_man_holding_hands_medium_skin_tone_medium_light_skin_tone[] = "👩đŸŊ‍🤝‍👨đŸŧ"; + inline constexpr const char woman_and_man_holding_hands_tone3[] = "đŸ‘ĢđŸŊ"; + inline constexpr const char woman_and_man_holding_hands_medium_skin_tone[] = "đŸ‘ĢđŸŊ"; + inline constexpr const char woman_and_man_holding_hands_tone3_tone4[] = "👩đŸŊ‍🤝‍👨🏾"; + inline constexpr const char woman_and_man_holding_hands_medium_skin_tone_medium_dark_skin_tone[] = "👩đŸŊ‍🤝‍👨🏾"; + inline constexpr const char woman_and_man_holding_hands_tone3_tone5[] = "👩đŸŊ‍🤝‍👨đŸŋ"; + inline constexpr const char woman_and_man_holding_hands_medium_skin_tone_dark_skin_tone[] = "👩đŸŊ‍🤝‍👨đŸŋ"; + inline constexpr const char woman_and_man_holding_hands_tone4_tone1[] = "👩🏾‍🤝‍👨đŸģ"; + inline constexpr const char woman_and_man_holding_hands_medium_dark_skin_tone_light_skin_tone[] = "👩🏾‍🤝‍👨đŸģ"; + inline constexpr const char woman_and_man_holding_hands_tone4_tone2[] = "👩🏾‍🤝‍👨đŸŧ"; + inline constexpr const char woman_and_man_holding_hands_medium_dark_skin_tone_medium_light_skin_tone[] = "👩🏾‍🤝‍👨đŸŧ"; + inline constexpr const char woman_and_man_holding_hands_tone4_tone3[] = "👩🏾‍🤝‍👨đŸŊ"; + inline constexpr const char woman_and_man_holding_hands_medium_dark_skin_tone_medium_skin_tone[] = "👩🏾‍🤝‍👨đŸŊ"; + inline constexpr const char woman_and_man_holding_hands_tone4[] = "đŸ‘Ģ🏾"; + inline constexpr const char woman_and_man_holding_hands_medium_dark_skin_tone[] = "đŸ‘Ģ🏾"; + inline constexpr const char woman_and_man_holding_hands_tone4_tone5[] = "👩🏾‍🤝‍👨đŸŋ"; + inline constexpr const char woman_and_man_holding_hands_medium_dark_skin_tone_dark_skin_tone[] = "👩🏾‍🤝‍👨đŸŋ"; + inline constexpr const char woman_and_man_holding_hands_tone5_tone1[] = "👩đŸŋ‍🤝‍👨đŸģ"; + inline constexpr const char woman_and_man_holding_hands_dark_skin_tone_light_skin_tone[] = "👩đŸŋ‍🤝‍👨đŸģ"; + inline constexpr const char woman_and_man_holding_hands_tone5_tone2[] = "👩đŸŋ‍🤝‍👨đŸŧ"; + inline constexpr const char woman_and_man_holding_hands_dark_skin_tone_medium_light_skin_tone[] = "👩đŸŋ‍🤝‍👨đŸŧ"; + inline constexpr const char woman_and_man_holding_hands_tone5_tone3[] = "👩đŸŋ‍🤝‍👨đŸŊ"; + inline constexpr const char woman_and_man_holding_hands_dark_skin_tone_medium_skin_tone[] = "👩đŸŋ‍🤝‍👨đŸŊ"; + inline constexpr const char woman_and_man_holding_hands_tone5_tone4[] = "👩đŸŋ‍🤝‍👨🏾"; + inline constexpr const char woman_and_man_holding_hands_dark_skin_tone_medium_dark_skin_tone[] = "👩đŸŋ‍🤝‍👨🏾"; + inline constexpr const char woman_and_man_holding_hands_tone5[] = "đŸ‘ĢđŸŋ"; + inline constexpr const char woman_and_man_holding_hands_dark_skin_tone[] = "đŸ‘ĢđŸŋ"; + inline constexpr const char two_women_holding_hands[] = "👭"; + inline constexpr const char women_holding_hands_tone1[] = "👭đŸģ"; + inline constexpr const char women_holding_hands_light_skin_tone[] = "👭đŸģ"; + inline constexpr const char women_holding_hands_tone1_tone2[] = "👩đŸģ‍🤝‍👩đŸŧ"; + inline constexpr const char women_holding_hands_light_skin_tone_medium_light_skin_tone[] = "👩đŸģ‍🤝‍👩đŸŧ"; + inline constexpr const char women_holding_hands_tone1_tone3[] = "👩đŸģ‍🤝‍👩đŸŊ"; + inline constexpr const char women_holding_hands_light_skin_tone_medium_skin_tone[] = "👩đŸģ‍🤝‍👩đŸŊ"; + inline constexpr const char women_holding_hands_tone1_tone4[] = "👩đŸģ‍🤝‍👩🏾"; + inline constexpr const char women_holding_hands_light_skin_tone_medium_dark_skin_tone[] = "👩đŸģ‍🤝‍👩🏾"; + inline constexpr const char women_holding_hands_tone1_tone5[] = "👩đŸģ‍🤝‍👩đŸŋ"; + inline constexpr const char women_holding_hands_light_skin_tone_dark_skin_tone[] = "👩đŸģ‍🤝‍👩đŸŋ"; + inline constexpr const char women_holding_hands_tone2_tone1[] = "👩đŸŧ‍🤝‍👩đŸģ"; + inline constexpr const char women_holding_hands_medium_light_skin_tone_light_skin_tone[] = "👩đŸŧ‍🤝‍👩đŸģ"; + inline constexpr const char women_holding_hands_tone2[] = "👭đŸŧ"; + inline constexpr const char women_holding_hands_medium_light_skin_tone[] = "👭đŸŧ"; + inline constexpr const char women_holding_hands_tone2_tone3[] = "👩đŸŧ‍🤝‍👩đŸŊ"; + inline constexpr const char women_holding_hands_medium_light_skin_tone_medium_skin_tone[] = "👩đŸŧ‍🤝‍👩đŸŊ"; + inline constexpr const char women_holding_hands_tone2_tone4[] = "👩đŸŧ‍🤝‍👩🏾"; + inline constexpr const char women_holding_hands_medium_light_skin_tone_medium_dark_skin_tone[] = "👩đŸŧ‍🤝‍👩🏾"; + inline constexpr const char women_holding_hands_tone2_tone5[] = "👩đŸŧ‍🤝‍👩đŸŋ"; + inline constexpr const char women_holding_hands_medium_light_skin_tone_dark_skin_tone[] = "👩đŸŧ‍🤝‍👩đŸŋ"; + inline constexpr const char women_holding_hands_tone3_tone1[] = "👩đŸŊ‍🤝‍👩đŸģ"; + inline constexpr const char women_holding_hands_medium_skin_tone_light_skin_tone[] = "👩đŸŊ‍🤝‍👩đŸģ"; + inline constexpr const char women_holding_hands_tone3_tone2[] = "👩đŸŊ‍🤝‍👩đŸŧ"; + inline constexpr const char women_holding_hands_medium_skin_tone_medium_light_skin_tone[] = "👩đŸŊ‍🤝‍👩đŸŧ"; + inline constexpr const char women_holding_hands_tone3[] = "👭đŸŊ"; + inline constexpr const char women_holding_hands_medium_skin_tone[] = "👭đŸŊ"; + inline constexpr const char women_holding_hands_tone3_tone4[] = "👩đŸŊ‍🤝‍👩🏾"; + inline constexpr const char women_holding_hands_medium_skin_tone_medium_dark_skin_tone[] = "👩đŸŊ‍🤝‍👩🏾"; + inline constexpr const char women_holding_hands_tone3_tone5[] = "👩đŸŊ‍🤝‍👩đŸŋ"; + inline constexpr const char women_holding_hands_medium_skin_tone_dark_skin_tone[] = "👩đŸŊ‍🤝‍👩đŸŋ"; + inline constexpr const char women_holding_hands_tone4_tone1[] = "👩🏾‍🤝‍👩đŸģ"; + inline constexpr const char women_holding_hands_medium_dark_skin_tone_light_skin_tone[] = "👩🏾‍🤝‍👩đŸģ"; + inline constexpr const char women_holding_hands_tone4_tone2[] = "👩🏾‍🤝‍👩đŸŧ"; + inline constexpr const char women_holding_hands_medium_dark_skin_tone_medium_light_skin_tone[] = "👩🏾‍🤝‍👩đŸŧ"; + inline constexpr const char women_holding_hands_tone4_tone3[] = "👩🏾‍🤝‍👩đŸŊ"; + inline constexpr const char women_holding_hands_medium_dark_skin_tone_medium_skin_tone[] = "👩🏾‍🤝‍👩đŸŊ"; + inline constexpr const char women_holding_hands_tone4[] = "👭🏾"; + inline constexpr const char women_holding_hands_medium_dark_skin_tone[] = "👭🏾"; + inline constexpr const char women_holding_hands_tone4_tone5[] = "👩🏾‍🤝‍👩đŸŋ"; + inline constexpr const char women_holding_hands_medium_dark_skin_tone_dark_skin_tone[] = "👩🏾‍🤝‍👩đŸŋ"; + inline constexpr const char women_holding_hands_tone5_tone1[] = "👩đŸŋ‍🤝‍👩đŸģ"; + inline constexpr const char women_holding_hands_dark_skin_tone_light_skin_tone[] = "👩đŸŋ‍🤝‍👩đŸģ"; + inline constexpr const char women_holding_hands_tone5_tone2[] = "👩đŸŋ‍🤝‍👩đŸŧ"; + inline constexpr const char women_holding_hands_dark_skin_tone_medium_light_skin_tone[] = "👩đŸŋ‍🤝‍👩đŸŧ"; + inline constexpr const char women_holding_hands_tone5_tone3[] = "👩đŸŋ‍🤝‍👩đŸŊ"; + inline constexpr const char women_holding_hands_dark_skin_tone_medium_skin_tone[] = "👩đŸŋ‍🤝‍👩đŸŊ"; + inline constexpr const char women_holding_hands_tone5_tone4[] = "👩đŸŋ‍🤝‍👩🏾"; + inline constexpr const char women_holding_hands_dark_skin_tone_medium_dark_skin_tone[] = "👩đŸŋ‍🤝‍👩🏾"; + inline constexpr const char women_holding_hands_tone5[] = "👭đŸŋ"; + inline constexpr const char women_holding_hands_dark_skin_tone[] = "👭đŸŋ"; + inline constexpr const char two_men_holding_hands[] = "đŸ‘Ŧ"; + inline constexpr const char men_holding_hands_tone1[] = "đŸ‘ŦđŸģ"; + inline constexpr const char men_holding_hands_light_skin_tone[] = "đŸ‘ŦđŸģ"; + inline constexpr const char men_holding_hands_tone1_tone2[] = "👨đŸģ‍🤝‍👨đŸŧ"; + inline constexpr const char men_holding_hands_light_skin_tone_medium_light_skin_tone[] = "👨đŸģ‍🤝‍👨đŸŧ"; + inline constexpr const char men_holding_hands_tone1_tone3[] = "👨đŸģ‍🤝‍👨đŸŊ"; + inline constexpr const char men_holding_hands_light_skin_tone_medium_skin_tone[] = "👨đŸģ‍🤝‍👨đŸŊ"; + inline constexpr const char men_holding_hands_tone1_tone4[] = "👨đŸģ‍🤝‍👨🏾"; + inline constexpr const char men_holding_hands_light_skin_tone_medium_dark_skin_tone[] = "👨đŸģ‍🤝‍👨🏾"; + inline constexpr const char men_holding_hands_tone1_tone5[] = "👨đŸģ‍🤝‍👨đŸŋ"; + inline constexpr const char men_holding_hands_light_skin_tone_dark_skin_tone[] = "👨đŸģ‍🤝‍👨đŸŋ"; + inline constexpr const char men_holding_hands_tone2_tone1[] = "👨đŸŧ‍🤝‍👨đŸģ"; + inline constexpr const char men_holding_hands_medium_light_skin_tone_light_skin_tone[] = "👨đŸŧ‍🤝‍👨đŸģ"; + inline constexpr const char men_holding_hands_tone2[] = "đŸ‘ŦđŸŧ"; + inline constexpr const char men_holding_hands_medium_light_skin_tone[] = "đŸ‘ŦđŸŧ"; + inline constexpr const char men_holding_hands_tone2_tone3[] = "👨đŸŧ‍🤝‍👨đŸŊ"; + inline constexpr const char men_holding_hands_medium_light_skin_tone_medium_skin_tone[] = "👨đŸŧ‍🤝‍👨đŸŊ"; + inline constexpr const char men_holding_hands_tone2_tone4[] = "👨đŸŧ‍🤝‍👨🏾"; + inline constexpr const char men_holding_hands_medium_light_skin_tone_medium_dark_skin_tone[] = "👨đŸŧ‍🤝‍👨🏾"; + inline constexpr const char men_holding_hands_tone2_tone5[] = "👨đŸŧ‍🤝‍👨đŸŋ"; + inline constexpr const char men_holding_hands_medium_light_skin_tone_dark_skin_tone[] = "👨đŸŧ‍🤝‍👨đŸŋ"; + inline constexpr const char men_holding_hands_tone3_tone1[] = "👨đŸŊ‍🤝‍👨đŸģ"; + inline constexpr const char men_holding_hands_medium_skin_tone_light_skin_tone[] = "👨đŸŊ‍🤝‍👨đŸģ"; + inline constexpr const char men_holding_hands_tone3_tone2[] = "👨đŸŊ‍🤝‍👨đŸŧ"; + inline constexpr const char men_holding_hands_medium_skin_tone_medium_light_skin_tone[] = "👨đŸŊ‍🤝‍👨đŸŧ"; + inline constexpr const char men_holding_hands_tone3[] = "đŸ‘ŦđŸŊ"; + inline constexpr const char men_holding_hands_medium_skin_tone[] = "đŸ‘ŦđŸŊ"; + inline constexpr const char men_holding_hands_tone3_tone4[] = "👨đŸŊ‍🤝‍👨🏾"; + inline constexpr const char men_holding_hands_medium_skin_tone_medium_dark_skin_tone[] = "👨đŸŊ‍🤝‍👨🏾"; + inline constexpr const char men_holding_hands_tone3_tone5[] = "👨đŸŊ‍🤝‍👨đŸŋ"; + inline constexpr const char men_holding_hands_medium_skin_tone_dark_skin_tone[] = "👨đŸŊ‍🤝‍👨đŸŋ"; + inline constexpr const char men_holding_hands_tone4_tone1[] = "👨🏾‍🤝‍👨đŸģ"; + inline constexpr const char men_holding_hands_medium_dark_skin_tone_light_skin_tone[] = "👨🏾‍🤝‍👨đŸģ"; + inline constexpr const char men_holding_hands_tone4_tone2[] = "👨🏾‍🤝‍👨đŸŧ"; + inline constexpr const char men_holding_hands_medium_dark_skin_tone_medium_light_skin_tone[] = "👨🏾‍🤝‍👨đŸŧ"; + inline constexpr const char men_holding_hands_tone4_tone3[] = "👨🏾‍🤝‍👨đŸŊ"; + inline constexpr const char men_holding_hands_medium_dark_skin_tone_medium_skin_tone[] = "👨🏾‍🤝‍👨đŸŊ"; + inline constexpr const char men_holding_hands_tone4[] = "đŸ‘Ŧ🏾"; + inline constexpr const char men_holding_hands_medium_dark_skin_tone[] = "đŸ‘Ŧ🏾"; + inline constexpr const char men_holding_hands_tone4_tone5[] = "👨🏾‍🤝‍👨đŸŋ"; + inline constexpr const char men_holding_hands_medium_dark_skin_tone_dark_skin_tone[] = "👨🏾‍🤝‍👨đŸŋ"; + inline constexpr const char men_holding_hands_tone5_tone1[] = "👨đŸŋ‍🤝‍👨đŸģ"; + inline constexpr const char men_holding_hands_dark_skin_tone_light_skin_tone[] = "👨đŸŋ‍🤝‍👨đŸģ"; + inline constexpr const char men_holding_hands_tone5_tone2[] = "👨đŸŋ‍🤝‍👨đŸŧ"; + inline constexpr const char men_holding_hands_dark_skin_tone_medium_light_skin_tone[] = "👨đŸŋ‍🤝‍👨đŸŧ"; + inline constexpr const char men_holding_hands_tone5_tone3[] = "👨đŸŋ‍🤝‍👨đŸŊ"; + inline constexpr const char men_holding_hands_dark_skin_tone_medium_skin_tone[] = "👨đŸŋ‍🤝‍👨đŸŊ"; + inline constexpr const char men_holding_hands_tone5_tone4[] = "👨đŸŋ‍🤝‍👨🏾"; + inline constexpr const char men_holding_hands_dark_skin_tone_medium_dark_skin_tone[] = "👨đŸŋ‍🤝‍👨🏾"; + inline constexpr const char men_holding_hands_tone5[] = "đŸ‘ŦđŸŋ"; + inline constexpr const char men_holding_hands_dark_skin_tone[] = "đŸ‘ŦđŸŋ"; + inline constexpr const char couple_with_heart[] = "💑"; + inline constexpr const char couple_with_heart_tone1[] = "💑đŸģ"; + inline constexpr const char couple_with_heart_light_skin_tone[] = "💑đŸģ"; + inline constexpr const char couple_with_heart_person_person_tone1_tone2[] = "🧑đŸģ‍❤ī¸â€đŸ§‘đŸŧ"; + inline constexpr const char couple_with_heart_person_person_light_skin_tone_medium_light_skin_tone[] = "🧑đŸģ‍❤ī¸â€đŸ§‘đŸŧ"; + inline constexpr const char couple_with_heart_person_person_tone1_tone3[] = "🧑đŸģ‍❤ī¸â€đŸ§‘đŸŊ"; + inline constexpr const char couple_with_heart_person_person_light_skin_tone_medium_skin_tone[] = "🧑đŸģ‍❤ī¸â€đŸ§‘đŸŊ"; + inline constexpr const char couple_with_heart_person_person_tone1_tone4[] = "🧑đŸģ‍❤ī¸â€đŸ§‘đŸž"; + inline constexpr const char couple_with_heart_person_person_light_skin_tone_medium_dark_skin_tone[] = "🧑đŸģ‍❤ī¸â€đŸ§‘đŸž"; + inline constexpr const char couple_with_heart_person_person_tone1_tone5[] = "🧑đŸģ‍❤ī¸â€đŸ§‘đŸŋ"; + inline constexpr const char couple_with_heart_person_person_light_skin_tone_dark_skin_tone[] = "🧑đŸģ‍❤ī¸â€đŸ§‘đŸŋ"; + inline constexpr const char couple_with_heart_person_person_tone2_tone1[] = "🧑đŸŧ‍❤ī¸â€đŸ§‘đŸģ"; + inline constexpr const char couple_with_heart_person_person_medium_light_skin_tone_light_skin_tone[] = "🧑đŸŧ‍❤ī¸â€đŸ§‘đŸģ"; + inline constexpr const char couple_with_heart_tone2[] = "💑đŸŧ"; + inline constexpr const char couple_with_heart_medium_light_skin_tone[] = "💑đŸŧ"; + inline constexpr const char couple_with_heart_person_person_tone2_tone3[] = "🧑đŸŧ‍❤ī¸â€đŸ§‘đŸŊ"; + inline constexpr const char couple_with_heart_person_person_medium_light_skin_tone_medium_skin_tone[] = "🧑đŸŧ‍❤ī¸â€đŸ§‘đŸŊ"; + inline constexpr const char couple_with_heart_person_person_tone2_tone4[] = "🧑đŸŧ‍❤ī¸â€đŸ§‘đŸž"; + inline constexpr const char couple_with_heart_person_person_medium_light_skin_tone_medium_dark_skin_tone[] = "🧑đŸŧ‍❤ī¸â€đŸ§‘đŸž"; + inline constexpr const char couple_with_heart_person_person_tone2_tone5[] = "🧑đŸŧ‍❤ī¸â€đŸ§‘đŸŋ"; + inline constexpr const char couple_with_heart_person_person_medium_light_skin_tone_dark_skin_tone[] = "🧑đŸŧ‍❤ī¸â€đŸ§‘đŸŋ"; + inline constexpr const char couple_with_heart_person_person_tone3_tone1[] = "🧑đŸŊ‍❤ī¸â€đŸ§‘đŸģ"; + inline constexpr const char couple_with_heart_person_person_medium_skin_tone_light_skin_tone[] = "🧑đŸŊ‍❤ī¸â€đŸ§‘đŸģ"; + inline constexpr const char couple_with_heart_person_person_tone3_tone2[] = "🧑đŸŊ‍❤ī¸â€đŸ§‘đŸŧ"; + inline constexpr const char couple_with_heart_person_person_medium_skin_tone_medium_light_skin_tone[] = "🧑đŸŊ‍❤ī¸â€đŸ§‘đŸŧ"; + inline constexpr const char couple_with_heart_tone3[] = "💑đŸŊ"; + inline constexpr const char couple_with_heart_medium_skin_tone[] = "💑đŸŊ"; + inline constexpr const char couple_with_heart_person_person_tone3_tone4[] = "🧑đŸŊ‍❤ī¸â€đŸ§‘đŸž"; + inline constexpr const char couple_with_heart_person_person_medium_skin_tone_medium_dark_skin_tone[] = "🧑đŸŊ‍❤ī¸â€đŸ§‘đŸž"; + inline constexpr const char couple_with_heart_person_person_tone3_tone5[] = "🧑đŸŊ‍❤ī¸â€đŸ§‘đŸŋ"; + inline constexpr const char couple_with_heart_person_person_medium_skin_tone_dark_skin_tone[] = "🧑đŸŊ‍❤ī¸â€đŸ§‘đŸŋ"; + inline constexpr const char couple_with_heart_person_person_tone4_tone1[] = "🧑🏾‍❤ī¸â€đŸ§‘đŸģ"; + inline constexpr const char couple_with_heart_person_person_medium_dark_skin_tone_light_skin_tone[] = "🧑🏾‍❤ī¸â€đŸ§‘đŸģ"; + inline constexpr const char couple_with_heart_person_person_tone4_tone2[] = "🧑🏾‍❤ī¸â€đŸ§‘đŸŧ"; + inline constexpr const char couple_with_heart_person_person_medium_dark_skin_tone_medium_light_skin_tone[] = "🧑🏾‍❤ī¸â€đŸ§‘đŸŧ"; + inline constexpr const char couple_with_heart_person_person_tone4_tone3[] = "🧑🏾‍❤ī¸â€đŸ§‘đŸŊ"; + inline constexpr const char couple_with_heart_person_person_medium_dark_skin_tone_medium_skin_tone[] = "🧑🏾‍❤ī¸â€đŸ§‘đŸŊ"; + inline constexpr const char couple_with_heart_tone4[] = "💑🏾"; + inline constexpr const char couple_with_heart_medium_dark_skin_tone[] = "💑🏾"; + inline constexpr const char couple_with_heart_person_person_tone4_tone5[] = "🧑🏾‍❤ī¸â€đŸ§‘đŸŋ"; + inline constexpr const char couple_with_heart_person_person_medium_dark_skin_tone_dark_skin_tone[] = "🧑🏾‍❤ī¸â€đŸ§‘đŸŋ"; + inline constexpr const char couple_with_heart_person_person_tone5_tone1[] = "🧑đŸŋ‍❤ī¸â€đŸ§‘đŸģ"; + inline constexpr const char couple_with_heart_person_person_dark_skin_tone_light_skin_tone[] = "🧑đŸŋ‍❤ī¸â€đŸ§‘đŸģ"; + inline constexpr const char couple_with_heart_person_person_tone5_tone2[] = "🧑đŸŋ‍❤ī¸â€đŸ§‘đŸŧ"; + inline constexpr const char couple_with_heart_person_person_dark_skin_tone_medium_light_skin_tone[] = "🧑đŸŋ‍❤ī¸â€đŸ§‘đŸŧ"; + inline constexpr const char couple_with_heart_person_person_tone5_tone3[] = "🧑đŸŋ‍❤ī¸â€đŸ§‘đŸŊ"; + inline constexpr const char couple_with_heart_person_person_dark_skin_tone_medium_skin_tone[] = "🧑đŸŋ‍❤ī¸â€đŸ§‘đŸŊ"; + inline constexpr const char couple_with_heart_person_person_tone5_tone4[] = "🧑đŸŋ‍❤ī¸â€đŸ§‘đŸž"; + inline constexpr const char couple_with_heart_person_person_dark_skin_tone_medium_dark_skin_tone[] = "🧑đŸŋ‍❤ī¸â€đŸ§‘đŸž"; + inline constexpr const char couple_with_heart_tone5[] = "💑đŸŋ"; + inline constexpr const char couple_with_heart_dark_skin_tone[] = "💑đŸŋ"; + inline constexpr const char couple_with_heart_woman_man[] = "👩‍❤ī¸â€đŸ‘¨"; + inline constexpr const char couple_with_heart_woman_man_tone1[] = "👩đŸģ‍❤ī¸â€đŸ‘¨đŸģ"; + inline constexpr const char couple_with_heart_woman_man_light_skin_tone[] = "👩đŸģ‍❤ī¸â€đŸ‘¨đŸģ"; + inline constexpr const char couple_with_heart_woman_man_tone1_tone2[] = "👩đŸģ‍❤ī¸â€đŸ‘¨đŸŧ"; + inline constexpr const char couple_with_heart_woman_man_light_skin_tone_medium_light_skin_tone[] = "👩đŸģ‍❤ī¸â€đŸ‘¨đŸŧ"; + inline constexpr const char couple_with_heart_woman_man_tone1_tone3[] = "👩đŸģ‍❤ī¸â€đŸ‘¨đŸŊ"; + inline constexpr const char couple_with_heart_woman_man_light_skin_tone_medium_skin_tone[] = "👩đŸģ‍❤ī¸â€đŸ‘¨đŸŊ"; + inline constexpr const char couple_with_heart_woman_man_tone1_tone4[] = "👩đŸģ‍❤ī¸â€đŸ‘¨đŸž"; + inline constexpr const char couple_with_heart_woman_man_light_skin_tone_medium_dark_skin_tone[] = "👩đŸģ‍❤ī¸â€đŸ‘¨đŸž"; + inline constexpr const char couple_with_heart_woman_man_tone1_tone5[] = "👩đŸģ‍❤ī¸â€đŸ‘¨đŸŋ"; + inline constexpr const char couple_with_heart_woman_man_light_skin_tone_dark_skin_tone[] = "👩đŸģ‍❤ī¸â€đŸ‘¨đŸŋ"; + inline constexpr const char couple_with_heart_woman_man_tone2_tone1[] = "👩đŸŧ‍❤ī¸â€đŸ‘¨đŸģ"; + inline constexpr const char couple_with_heart_woman_man_medium_light_skin_tone_light_skin_tone[] = "👩đŸŧ‍❤ī¸â€đŸ‘¨đŸģ"; + inline constexpr const char couple_with_heart_woman_man_tone2[] = "👩đŸŧ‍❤ī¸â€đŸ‘¨đŸŧ"; + inline constexpr const char couple_with_heart_woman_man_medium_light_skin_tone[] = "👩đŸŧ‍❤ī¸â€đŸ‘¨đŸŧ"; + inline constexpr const char couple_with_heart_woman_man_tone2_tone3[] = "👩đŸŧ‍❤ī¸â€đŸ‘¨đŸŊ"; + inline constexpr const char couple_with_heart_woman_man_medium_light_skin_tone_medium_skin_tone[] = "👩đŸŧ‍❤ī¸â€đŸ‘¨đŸŊ"; + inline constexpr const char couple_with_heart_woman_man_tone2_tone4[] = "👩đŸŧ‍❤ī¸â€đŸ‘¨đŸž"; + inline constexpr const char couple_with_heart_woman_man_medium_light_skin_tone_medium_dark_skin_tone[] = "👩đŸŧ‍❤ī¸â€đŸ‘¨đŸž"; + inline constexpr const char couple_with_heart_woman_man_tone2_tone5[] = "👩đŸŧ‍❤ī¸â€đŸ‘¨đŸŋ"; + inline constexpr const char couple_with_heart_woman_man_medium_light_skin_tone_dark_skin_tone[] = "👩đŸŧ‍❤ī¸â€đŸ‘¨đŸŋ"; + inline constexpr const char couple_with_heart_woman_man_tone3_tone1[] = "👩đŸŊ‍❤ī¸â€đŸ‘¨đŸģ"; + inline constexpr const char couple_with_heart_woman_man_medium_skin_tone_light_skin_tone[] = "👩đŸŊ‍❤ī¸â€đŸ‘¨đŸģ"; + inline constexpr const char couple_with_heart_woman_man_tone3_tone2[] = "👩đŸŊ‍❤ī¸â€đŸ‘¨đŸŧ"; + inline constexpr const char couple_with_heart_woman_man_medium_skin_tone_medium_light_skin_tone[] = "👩đŸŊ‍❤ī¸â€đŸ‘¨đŸŧ"; + inline constexpr const char couple_with_heart_woman_man_tone3[] = "👩đŸŊ‍❤ī¸â€đŸ‘¨đŸŊ"; + inline constexpr const char couple_with_heart_woman_man_medium_skin_tone[] = "👩đŸŊ‍❤ī¸â€đŸ‘¨đŸŊ"; + inline constexpr const char couple_with_heart_woman_man_tone3_tone4[] = "👩đŸŊ‍❤ī¸â€đŸ‘¨đŸž"; + inline constexpr const char couple_with_heart_woman_man_medium_skin_tone_medium_dark_skin_tone[] = "👩đŸŊ‍❤ī¸â€đŸ‘¨đŸž"; + inline constexpr const char couple_with_heart_woman_man_tone3_tone5[] = "👩đŸŊ‍❤ī¸â€đŸ‘¨đŸŋ"; + inline constexpr const char couple_with_heart_woman_man_medium_skin_tone_dark_skin_tone[] = "👩đŸŊ‍❤ī¸â€đŸ‘¨đŸŋ"; + inline constexpr const char couple_with_heart_woman_man_tone4_tone1[] = "👩🏾‍❤ī¸â€đŸ‘¨đŸģ"; + inline constexpr const char couple_with_heart_woman_man_medium_dark_skin_tone_light_skin_tone[] = "👩🏾‍❤ī¸â€đŸ‘¨đŸģ"; + inline constexpr const char couple_with_heart_woman_man_tone4_tone2[] = "👩🏾‍❤ī¸â€đŸ‘¨đŸŧ"; + inline constexpr const char couple_with_heart_woman_man_medium_dark_skin_tone_medium_light_skin_tone[] = "👩🏾‍❤ī¸â€đŸ‘¨đŸŧ"; + inline constexpr const char couple_with_heart_woman_man_tone4_tone3[] = "👩🏾‍❤ī¸â€đŸ‘¨đŸŊ"; + inline constexpr const char couple_with_heart_woman_man_medium_dark_skin_tone_medium_skin_tone[] = "👩🏾‍❤ī¸â€đŸ‘¨đŸŊ"; + inline constexpr const char couple_with_heart_woman_man_tone4[] = "👩🏾‍❤ī¸â€đŸ‘¨đŸž"; + inline constexpr const char couple_with_heart_woman_man_medium_dark_skin_tone[] = "👩🏾‍❤ī¸â€đŸ‘¨đŸž"; + inline constexpr const char couple_with_heart_woman_man_tone4_tone5[] = "👩🏾‍❤ī¸â€đŸ‘¨đŸŋ"; + inline constexpr const char couple_with_heart_woman_man_medium_dark_skin_tone_dark_skin_tone[] = "👩🏾‍❤ī¸â€đŸ‘¨đŸŋ"; + inline constexpr const char couple_with_heart_woman_man_tone5_tone1[] = "👩đŸŋ‍❤ī¸â€đŸ‘¨đŸģ"; + inline constexpr const char couple_with_heart_woman_man_dark_skin_tone_light_skin_tone[] = "👩đŸŋ‍❤ī¸â€đŸ‘¨đŸģ"; + inline constexpr const char couple_with_heart_woman_man_tone5_tone2[] = "👩đŸŋ‍❤ī¸â€đŸ‘¨đŸŧ"; + inline constexpr const char couple_with_heart_woman_man_dark_skin_tone_medium_light_skin_tone[] = "👩đŸŋ‍❤ī¸â€đŸ‘¨đŸŧ"; + inline constexpr const char couple_with_heart_woman_man_tone5_tone3[] = "👩đŸŋ‍❤ī¸â€đŸ‘¨đŸŊ"; + inline constexpr const char couple_with_heart_woman_man_dark_skin_tone_medium_skin_tone[] = "👩đŸŋ‍❤ī¸â€đŸ‘¨đŸŊ"; + inline constexpr const char couple_with_heart_woman_man_tone5_tone4[] = "👩đŸŋ‍❤ī¸â€đŸ‘¨đŸž"; + inline constexpr const char couple_with_heart_woman_man_dark_skin_tone_medium_dark_skin_tone[] = "👩đŸŋ‍❤ī¸â€đŸ‘¨đŸž"; + inline constexpr const char couple_with_heart_woman_man_tone5[] = "👩đŸŋ‍❤ī¸â€đŸ‘¨đŸŋ"; + inline constexpr const char couple_with_heart_woman_man_dark_skin_tone[] = "👩đŸŋ‍❤ī¸â€đŸ‘¨đŸŋ"; + inline constexpr const char couple_ww[] = "👩‍❤ī¸â€đŸ‘Š"; + inline constexpr const char couple_with_heart_ww[] = "👩‍❤ī¸â€đŸ‘Š"; + inline constexpr const char couple_with_heart_woman_woman_tone1[] = "👩đŸģ‍❤ī¸â€đŸ‘ŠđŸģ"; + inline constexpr const char couple_with_heart_woman_woman_light_skin_tone[] = "👩đŸģ‍❤ī¸â€đŸ‘ŠđŸģ"; + inline constexpr const char couple_with_heart_woman_woman_tone1_tone2[] = "👩đŸģ‍❤ī¸â€đŸ‘ŠđŸŧ"; + inline constexpr const char couple_with_heart_woman_woman_light_skin_tone_medium_light_skin_tone[] = "👩đŸģ‍❤ī¸â€đŸ‘ŠđŸŧ"; + inline constexpr const char couple_with_heart_woman_woman_tone1_tone3[] = "👩đŸģ‍❤ī¸â€đŸ‘ŠđŸŊ"; + inline constexpr const char couple_with_heart_woman_woman_light_skin_tone_medium_skin_tone[] = "👩đŸģ‍❤ī¸â€đŸ‘ŠđŸŊ"; + inline constexpr const char couple_with_heart_woman_woman_tone1_tone4[] = "👩đŸģ‍❤ī¸â€đŸ‘ŠđŸž"; + inline constexpr const char couple_with_heart_woman_woman_light_skin_tone_medium_dark_skin_tone[] = "👩đŸģ‍❤ī¸â€đŸ‘ŠđŸž"; + inline constexpr const char couple_with_heart_woman_woman_tone1_tone5[] = "👩đŸģ‍❤ī¸â€đŸ‘ŠđŸŋ"; + inline constexpr const char couple_with_heart_woman_woman_light_skin_tone_dark_skin_tone[] = "👩đŸģ‍❤ī¸â€đŸ‘ŠđŸŋ"; + inline constexpr const char couple_with_heart_woman_woman_tone2_tone1[] = "👩đŸŧ‍❤ī¸â€đŸ‘ŠđŸģ"; + inline constexpr const char couple_with_heart_woman_woman_medium_light_skin_tone_light_skin_tone[] = "👩đŸŧ‍❤ī¸â€đŸ‘ŠđŸģ"; + inline constexpr const char couple_with_heart_woman_woman_tone2[] = "👩đŸŧ‍❤ī¸â€đŸ‘ŠđŸŧ"; + inline constexpr const char couple_with_heart_woman_woman_medium_light_skin_tone[] = "👩đŸŧ‍❤ī¸â€đŸ‘ŠđŸŧ"; + inline constexpr const char couple_with_heart_woman_woman_tone2_tone3[] = "👩đŸŧ‍❤ī¸â€đŸ‘ŠđŸŊ"; + inline constexpr const char couple_with_heart_woman_woman_medium_light_skin_tone_medium_skin_tone[] = "👩đŸŧ‍❤ī¸â€đŸ‘ŠđŸŊ"; + inline constexpr const char couple_with_heart_woman_woman_tone2_tone4[] = "👩đŸŧ‍❤ī¸â€đŸ‘ŠđŸž"; + inline constexpr const char couple_with_heart_woman_woman_medium_light_skin_tone_medium_dark_skin_tone[] = "👩đŸŧ‍❤ī¸â€đŸ‘ŠđŸž"; + inline constexpr const char couple_with_heart_woman_woman_tone2_tone5[] = "👩đŸŧ‍❤ī¸â€đŸ‘ŠđŸŋ"; + inline constexpr const char couple_with_heart_woman_woman_medium_light_skin_tone_dark_skin_tone[] = "👩đŸŧ‍❤ī¸â€đŸ‘ŠđŸŋ"; + inline constexpr const char couple_with_heart_woman_woman_tone3_tone1[] = "👩đŸŊ‍❤ī¸â€đŸ‘ŠđŸģ"; + inline constexpr const char couple_with_heart_woman_woman_medium_skin_tone_light_skin_tone[] = "👩đŸŊ‍❤ī¸â€đŸ‘ŠđŸģ"; + inline constexpr const char couple_with_heart_woman_woman_tone3_tone2[] = "👩đŸŊ‍❤ī¸â€đŸ‘ŠđŸŧ"; + inline constexpr const char couple_with_heart_woman_woman_medium_skin_tone_medium_light_skin_tone[] = "👩đŸŊ‍❤ī¸â€đŸ‘ŠđŸŧ"; + inline constexpr const char couple_with_heart_woman_woman_tone3[] = "👩đŸŊ‍❤ī¸â€đŸ‘ŠđŸŊ"; + inline constexpr const char couple_with_heart_woman_woman_medium_skin_tone[] = "👩đŸŊ‍❤ī¸â€đŸ‘ŠđŸŊ"; + inline constexpr const char couple_with_heart_woman_woman_tone3_tone4[] = "👩đŸŊ‍❤ī¸â€đŸ‘ŠđŸž"; + inline constexpr const char couple_with_heart_woman_woman_medium_skin_tone_medium_dark_skin_tone[] = "👩đŸŊ‍❤ī¸â€đŸ‘ŠđŸž"; + inline constexpr const char couple_with_heart_woman_woman_tone3_tone5[] = "👩đŸŊ‍❤ī¸â€đŸ‘ŠđŸŋ"; + inline constexpr const char couple_with_heart_woman_woman_medium_skin_tone_dark_skin_tone[] = "👩đŸŊ‍❤ī¸â€đŸ‘ŠđŸŋ"; + inline constexpr const char couple_with_heart_woman_woman_tone4_tone1[] = "👩🏾‍❤ī¸â€đŸ‘ŠđŸģ"; + inline constexpr const char couple_with_heart_woman_woman_medium_dark_skin_tone_light_skin_tone[] = "👩🏾‍❤ī¸â€đŸ‘ŠđŸģ"; + inline constexpr const char couple_with_heart_woman_woman_tone4_tone2[] = "👩🏾‍❤ī¸â€đŸ‘ŠđŸŧ"; + inline constexpr const char couple_with_heart_woman_woman_medium_dark_skin_tone_medium_light_skin_tone[] = "👩🏾‍❤ī¸â€đŸ‘ŠđŸŧ"; + inline constexpr const char couple_with_heart_woman_woman_tone4_tone3[] = "👩🏾‍❤ī¸â€đŸ‘ŠđŸŊ"; + inline constexpr const char couple_with_heart_woman_woman_medium_dark_skin_tone_medium_skin_tone[] = "👩🏾‍❤ī¸â€đŸ‘ŠđŸŊ"; + inline constexpr const char couple_with_heart_woman_woman_tone4[] = "👩🏾‍❤ī¸â€đŸ‘ŠđŸž"; + inline constexpr const char couple_with_heart_woman_woman_medium_dark_skin_tone[] = "👩🏾‍❤ī¸â€đŸ‘ŠđŸž"; + inline constexpr const char couple_with_heart_woman_woman_tone4_tone5[] = "👩🏾‍❤ī¸â€đŸ‘ŠđŸŋ"; + inline constexpr const char couple_with_heart_woman_woman_medium_dark_skin_tone_dark_skin_tone[] = "👩🏾‍❤ī¸â€đŸ‘ŠđŸŋ"; + inline constexpr const char couple_with_heart_woman_woman_tone5_tone1[] = "👩đŸŋ‍❤ī¸â€đŸ‘ŠđŸģ"; + inline constexpr const char couple_with_heart_woman_woman_dark_skin_tone_light_skin_tone[] = "👩đŸŋ‍❤ī¸â€đŸ‘ŠđŸģ"; + inline constexpr const char couple_with_heart_woman_woman_tone5_tone2[] = "👩đŸŋ‍❤ī¸â€đŸ‘ŠđŸŧ"; + inline constexpr const char couple_with_heart_woman_woman_dark_skin_tone_medium_light_skin_tone[] = "👩đŸŋ‍❤ī¸â€đŸ‘ŠđŸŧ"; + inline constexpr const char couple_with_heart_woman_woman_tone5_tone3[] = "👩đŸŋ‍❤ī¸â€đŸ‘ŠđŸŊ"; + inline constexpr const char couple_with_heart_woman_woman_dark_skin_tone_medium_skin_tone[] = "👩đŸŋ‍❤ī¸â€đŸ‘ŠđŸŊ"; + inline constexpr const char couple_with_heart_woman_woman_tone5_tone4[] = "👩đŸŋ‍❤ī¸â€đŸ‘ŠđŸž"; + inline constexpr const char couple_with_heart_woman_woman_dark_skin_tone_medium_dark_skin_tone[] = "👩đŸŋ‍❤ī¸â€đŸ‘ŠđŸž"; + inline constexpr const char couple_with_heart_woman_woman_tone5[] = "👩đŸŋ‍❤ī¸â€đŸ‘ŠđŸŋ"; + inline constexpr const char couple_with_heart_woman_woman_dark_skin_tone[] = "👩đŸŋ‍❤ī¸â€đŸ‘ŠđŸŋ"; + inline constexpr const char couple_mm[] = "👨‍❤ī¸â€đŸ‘¨"; + inline constexpr const char couple_with_heart_mm[] = "👨‍❤ī¸â€đŸ‘¨"; + inline constexpr const char couple_with_heart_man_man_tone1[] = "👨đŸģ‍❤ī¸â€đŸ‘¨đŸģ"; + inline constexpr const char couple_with_heart_man_man_light_skin_tone[] = "👨đŸģ‍❤ī¸â€đŸ‘¨đŸģ"; + inline constexpr const char couple_with_heart_man_man_tone1_tone2[] = "👨đŸģ‍❤ī¸â€đŸ‘¨đŸŧ"; + inline constexpr const char couple_with_heart_man_man_light_skin_tone_medium_light_skin_tone[] = "👨đŸģ‍❤ī¸â€đŸ‘¨đŸŧ"; + inline constexpr const char couple_with_heart_man_man_tone1_tone3[] = "👨đŸģ‍❤ī¸â€đŸ‘¨đŸŊ"; + inline constexpr const char couple_with_heart_man_man_light_skin_tone_medium_skin_tone[] = "👨đŸģ‍❤ī¸â€đŸ‘¨đŸŊ"; + inline constexpr const char couple_with_heart_man_man_tone1_tone4[] = "👨đŸģ‍❤ī¸â€đŸ‘¨đŸž"; + inline constexpr const char couple_with_heart_man_man_light_skin_tone_medium_dark_skin_tone[] = "👨đŸģ‍❤ī¸â€đŸ‘¨đŸž"; + inline constexpr const char couple_with_heart_man_man_tone1_tone5[] = "👨đŸģ‍❤ī¸â€đŸ‘¨đŸŋ"; + inline constexpr const char couple_with_heart_man_man_light_skin_tone_dark_skin_tone[] = "👨đŸģ‍❤ī¸â€đŸ‘¨đŸŋ"; + inline constexpr const char couple_with_heart_man_man_tone2_tone1[] = "👨đŸŧ‍❤ī¸â€đŸ‘¨đŸģ"; + inline constexpr const char couple_with_heart_man_man_medium_light_skin_tone_light_skin_tone[] = "👨đŸŧ‍❤ī¸â€đŸ‘¨đŸģ"; + inline constexpr const char couple_with_heart_man_man_tone2[] = "👨đŸŧ‍❤ī¸â€đŸ‘¨đŸŧ"; + inline constexpr const char couple_with_heart_man_man_medium_light_skin_tone[] = "👨đŸŧ‍❤ī¸â€đŸ‘¨đŸŧ"; + inline constexpr const char couple_with_heart_man_man_tone2_tone3[] = "👨đŸŧ‍❤ī¸â€đŸ‘¨đŸŊ"; + inline constexpr const char couple_with_heart_man_man_medium_light_skin_tone_medium_skin_tone[] = "👨đŸŧ‍❤ī¸â€đŸ‘¨đŸŊ"; + inline constexpr const char couple_with_heart_man_man_tone2_tone4[] = "👨đŸŧ‍❤ī¸â€đŸ‘¨đŸž"; + inline constexpr const char couple_with_heart_man_man_medium_light_skin_tone_medium_dark_skin_tone[] = "👨đŸŧ‍❤ī¸â€đŸ‘¨đŸž"; + inline constexpr const char couple_with_heart_man_man_tone2_tone5[] = "👨đŸŧ‍❤ī¸â€đŸ‘¨đŸŋ"; + inline constexpr const char couple_with_heart_man_man_medium_light_skin_tone_dark_skin_tone[] = "👨đŸŧ‍❤ī¸â€đŸ‘¨đŸŋ"; + inline constexpr const char couple_with_heart_man_man_tone3_tone1[] = "👨đŸŊ‍❤ī¸â€đŸ‘¨đŸģ"; + inline constexpr const char couple_with_heart_man_man_medium_skin_tone_light_skin_tone[] = "👨đŸŊ‍❤ī¸â€đŸ‘¨đŸģ"; + inline constexpr const char couple_with_heart_man_man_tone3_tone2[] = "👨đŸŊ‍❤ī¸â€đŸ‘¨đŸŧ"; + inline constexpr const char couple_with_heart_man_man_medium_skin_tone_medium_light_skin_tone[] = "👨đŸŊ‍❤ī¸â€đŸ‘¨đŸŧ"; + inline constexpr const char couple_with_heart_man_man_tone3[] = "👨đŸŊ‍❤ī¸â€đŸ‘¨đŸŊ"; + inline constexpr const char couple_with_heart_man_man_medium_skin_tone[] = "👨đŸŊ‍❤ī¸â€đŸ‘¨đŸŊ"; + inline constexpr const char couple_with_heart_man_man_tone3_tone4[] = "👨đŸŊ‍❤ī¸â€đŸ‘¨đŸž"; + inline constexpr const char couple_with_heart_man_man_medium_skin_tone_medium_dark_skin_tone[] = "👨đŸŊ‍❤ī¸â€đŸ‘¨đŸž"; + inline constexpr const char couple_with_heart_man_man_tone3_tone5[] = "👨đŸŊ‍❤ī¸â€đŸ‘¨đŸŋ"; + inline constexpr const char couple_with_heart_man_man_medium_skin_tone_dark_skin_tone[] = "👨đŸŊ‍❤ī¸â€đŸ‘¨đŸŋ"; + inline constexpr const char couple_with_heart_man_man_tone4_tone1[] = "👨🏾‍❤ī¸â€đŸ‘¨đŸģ"; + inline constexpr const char couple_with_heart_man_man_medium_dark_skin_tone_light_skin_tone[] = "👨🏾‍❤ī¸â€đŸ‘¨đŸģ"; + inline constexpr const char couple_with_heart_man_man_tone4_tone2[] = "👨🏾‍❤ī¸â€đŸ‘¨đŸŧ"; + inline constexpr const char couple_with_heart_man_man_medium_dark_skin_tone_medium_light_skin_tone[] = "👨🏾‍❤ī¸â€đŸ‘¨đŸŧ"; + inline constexpr const char couple_with_heart_man_man_tone4_tone3[] = "👨🏾‍❤ī¸â€đŸ‘¨đŸŊ"; + inline constexpr const char couple_with_heart_man_man_medium_dark_skin_tone_medium_skin_tone[] = "👨🏾‍❤ī¸â€đŸ‘¨đŸŊ"; + inline constexpr const char couple_with_heart_man_man_tone4[] = "👨🏾‍❤ī¸â€đŸ‘¨đŸž"; + inline constexpr const char couple_with_heart_man_man_medium_dark_skin_tone[] = "👨🏾‍❤ī¸â€đŸ‘¨đŸž"; + inline constexpr const char couple_with_heart_man_man_tone4_tone5[] = "👨🏾‍❤ī¸â€đŸ‘¨đŸŋ"; + inline constexpr const char couple_with_heart_man_man_medium_dark_skin_tone_dark_skin_tone[] = "👨🏾‍❤ī¸â€đŸ‘¨đŸŋ"; + inline constexpr const char couple_with_heart_man_man_tone5_tone1[] = "👨đŸŋ‍❤ī¸â€đŸ‘¨đŸģ"; + inline constexpr const char couple_with_heart_man_man_dark_skin_tone_light_skin_tone[] = "👨đŸŋ‍❤ī¸â€đŸ‘¨đŸģ"; + inline constexpr const char couple_with_heart_man_man_tone5_tone2[] = "👨đŸŋ‍❤ī¸â€đŸ‘¨đŸŧ"; + inline constexpr const char couple_with_heart_man_man_dark_skin_tone_medium_light_skin_tone[] = "👨đŸŋ‍❤ī¸â€đŸ‘¨đŸŧ"; + inline constexpr const char couple_with_heart_man_man_tone5_tone3[] = "👨đŸŋ‍❤ī¸â€đŸ‘¨đŸŊ"; + inline constexpr const char couple_with_heart_man_man_dark_skin_tone_medium_skin_tone[] = "👨đŸŋ‍❤ī¸â€đŸ‘¨đŸŊ"; + inline constexpr const char couple_with_heart_man_man_tone5_tone4[] = "👨đŸŋ‍❤ī¸â€đŸ‘¨đŸž"; + inline constexpr const char couple_with_heart_man_man_dark_skin_tone_medium_dark_skin_tone[] = "👨đŸŋ‍❤ī¸â€đŸ‘¨đŸž"; + inline constexpr const char couple_with_heart_man_man_tone5[] = "👨đŸŋ‍❤ī¸â€đŸ‘¨đŸŋ"; + inline constexpr const char couple_with_heart_man_man_dark_skin_tone[] = "👨đŸŋ‍❤ī¸â€đŸ‘¨đŸŋ"; + inline constexpr const char couplekiss[] = "💏"; + inline constexpr const char kiss_tone1[] = "💏đŸģ"; + inline constexpr const char kiss_light_skin_tone[] = "💏đŸģ"; + inline constexpr const char kiss_person_person_tone1_tone2[] = "🧑đŸģ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸŧ"; + inline constexpr const char kiss_person_person_light_skin_tone_medium_light_skin_tone[] = "🧑đŸģ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸŧ"; + inline constexpr const char kiss_person_person_tone1_tone3[] = "🧑đŸģ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸŊ"; + inline constexpr const char kiss_person_person_light_skin_tone_medium_skin_tone[] = "🧑đŸģ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸŊ"; + inline constexpr const char kiss_person_person_tone1_tone4[] = "🧑đŸģ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸž"; + inline constexpr const char kiss_person_person_light_skin_tone_medium_dark_skin_tone[] = "🧑đŸģ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸž"; + inline constexpr const char kiss_person_person_tone1_tone5[] = "🧑đŸģ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸŋ"; + inline constexpr const char kiss_person_person_light_skin_tone_dark_skin_tone[] = "🧑đŸģ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸŋ"; + inline constexpr const char kiss_person_person_tone2_tone1[] = "🧑đŸŧ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸģ"; + inline constexpr const char kiss_person_person_medium_light_skin_tone_light_skin_tone[] = "🧑đŸŧ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸģ"; + inline constexpr const char kiss_tone2[] = "💏đŸŧ"; + inline constexpr const char kiss_medium_light_skin_tone[] = "💏đŸŧ"; + inline constexpr const char kiss_person_person_tone2_tone3[] = "🧑đŸŧ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸŊ"; + inline constexpr const char kiss_person_person_medium_light_skin_tone_medium_skin_tone[] = "🧑đŸŧ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸŊ"; + inline constexpr const char kiss_person_person_tone2_tone4[] = "🧑đŸŧ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸž"; + inline constexpr const char kiss_person_person_medium_light_skin_tone_medium_dark_skin_tone[] = "🧑đŸŧ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸž"; + inline constexpr const char kiss_person_person_tone2_tone5[] = "🧑đŸŧ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸŋ"; + inline constexpr const char kiss_person_person_medium_light_skin_tone_dark_skin_tone[] = "🧑đŸŧ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸŋ"; + inline constexpr const char kiss_person_person_tone3_tone1[] = "🧑đŸŊ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸģ"; + inline constexpr const char kiss_person_person_medium_skin_tone_light_skin_tone[] = "🧑đŸŊ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸģ"; + inline constexpr const char kiss_person_person_tone3_tone2[] = "🧑đŸŊ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸŧ"; + inline constexpr const char kiss_person_person_medium_skin_tone_medium_light_skin_tone[] = "🧑đŸŊ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸŧ"; + inline constexpr const char kiss_tone3[] = "💏đŸŊ"; + inline constexpr const char kiss_medium_skin_tone[] = "💏đŸŊ"; + inline constexpr const char kiss_person_person_tone3_tone4[] = "🧑đŸŊ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸž"; + inline constexpr const char kiss_person_person_medium_skin_tone_medium_dark_skin_tone[] = "🧑đŸŊ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸž"; + inline constexpr const char kiss_person_person_tone3_tone5[] = "🧑đŸŊ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸŋ"; + inline constexpr const char kiss_person_person_medium_skin_tone_dark_skin_tone[] = "🧑đŸŊ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸŋ"; + inline constexpr const char kiss_person_person_tone4_tone1[] = "🧑🏾‍❤ī¸â€đŸ’‹â€đŸ§‘đŸģ"; + inline constexpr const char kiss_person_person_medium_dark_skin_tone_light_skin_tone[] = "🧑🏾‍❤ī¸â€đŸ’‹â€đŸ§‘đŸģ"; + inline constexpr const char kiss_person_person_tone4_tone2[] = "🧑🏾‍❤ī¸â€đŸ’‹â€đŸ§‘đŸŧ"; + inline constexpr const char kiss_person_person_medium_dark_skin_tone_medium_light_skin_tone[] = "🧑🏾‍❤ī¸â€đŸ’‹â€đŸ§‘đŸŧ"; + inline constexpr const char kiss_person_person_tone4_tone3[] = "🧑🏾‍❤ī¸â€đŸ’‹â€đŸ§‘đŸŊ"; + inline constexpr const char kiss_person_person_medium_dark_skin_tone_medium_skin_tone[] = "🧑🏾‍❤ī¸â€đŸ’‹â€đŸ§‘đŸŊ"; + inline constexpr const char kiss_tone4[] = "💏🏾"; + inline constexpr const char kiss_medium_dark_skin_tone[] = "💏🏾"; + inline constexpr const char kiss_person_person_tone4_tone5[] = "🧑🏾‍❤ī¸â€đŸ’‹â€đŸ§‘đŸŋ"; + inline constexpr const char kiss_person_person_medium_dark_skin_tone_dark_skin_tone[] = "🧑🏾‍❤ī¸â€đŸ’‹â€đŸ§‘đŸŋ"; + inline constexpr const char kiss_person_person_tone5_tone1[] = "🧑đŸŋ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸģ"; + inline constexpr const char kiss_person_person_dark_skin_tone_light_skin_tone[] = "🧑đŸŋ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸģ"; + inline constexpr const char kiss_person_person_tone5_tone2[] = "🧑đŸŋ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸŧ"; + inline constexpr const char kiss_person_person_dark_skin_tone_medium_light_skin_tone[] = "🧑đŸŋ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸŧ"; + inline constexpr const char kiss_person_person_tone5_tone3[] = "🧑đŸŋ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸŊ"; + inline constexpr const char kiss_person_person_dark_skin_tone_medium_skin_tone[] = "🧑đŸŋ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸŊ"; + inline constexpr const char kiss_person_person_tone5_tone4[] = "🧑đŸŋ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸž"; + inline constexpr const char kiss_person_person_dark_skin_tone_medium_dark_skin_tone[] = "🧑đŸŋ‍❤ī¸â€đŸ’‹â€đŸ§‘đŸž"; + inline constexpr const char kiss_tone5[] = "💏đŸŋ"; + inline constexpr const char kiss_dark_skin_tone[] = "💏đŸŋ"; + inline constexpr const char kiss_woman_man[] = "👩‍❤ī¸â€đŸ’‹â€đŸ‘¨"; + inline constexpr const char kiss_woman_man_tone1[] = "👩đŸģ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸģ"; + inline constexpr const char kiss_woman_man_light_skin_tone[] = "👩đŸģ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸģ"; + inline constexpr const char kiss_woman_man_tone1_tone2[] = "👩đŸģ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŧ"; + inline constexpr const char kiss_woman_man_light_skin_tone_medium_light_skin_tone[] = "👩đŸģ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŧ"; + inline constexpr const char kiss_woman_man_tone1_tone3[] = "👩đŸģ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŊ"; + inline constexpr const char kiss_woman_man_light_skin_tone_medium_skin_tone[] = "👩đŸģ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŊ"; + inline constexpr const char kiss_woman_man_tone1_tone4[] = "👩đŸģ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸž"; + inline constexpr const char kiss_woman_man_light_skin_tone_medium_dark_skin_tone[] = "👩đŸģ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸž"; + inline constexpr const char kiss_woman_man_tone1_tone5[] = "👩đŸģ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŋ"; + inline constexpr const char kiss_woman_man_light_skin_tone_dark_skin_tone[] = "👩đŸģ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŋ"; + inline constexpr const char kiss_woman_man_tone2_tone1[] = "👩đŸŧ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸģ"; + inline constexpr const char kiss_woman_man_medium_light_skin_tone_light_skin_tone[] = "👩đŸŧ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸģ"; + inline constexpr const char kiss_woman_man_tone2[] = "👩đŸŧ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŧ"; + inline constexpr const char kiss_woman_man_medium_light_skin_tone[] = "👩đŸŧ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŧ"; + inline constexpr const char kiss_woman_man_tone2_tone3[] = "👩đŸŧ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŊ"; + inline constexpr const char kiss_woman_man_medium_light_skin_tone_medium_skin_tone[] = "👩đŸŧ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŊ"; + inline constexpr const char kiss_woman_man_tone2_tone4[] = "👩đŸŧ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸž"; + inline constexpr const char kiss_woman_man_medium_light_skin_tone_medium_dark_skin_tone[] = "👩đŸŧ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸž"; + inline constexpr const char kiss_woman_man_tone2_tone5[] = "👩đŸŧ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŋ"; + inline constexpr const char kiss_woman_man_medium_light_skin_tone_dark_skin_tone[] = "👩đŸŧ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŋ"; + inline constexpr const char kiss_woman_man_tone3_tone1[] = "👩đŸŊ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸģ"; + inline constexpr const char kiss_woman_man_medium_skin_tone_light_skin_tone[] = "👩đŸŊ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸģ"; + inline constexpr const char kiss_woman_man_tone3_tone2[] = "👩đŸŊ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŧ"; + inline constexpr const char kiss_woman_man_medium_skin_tone_medium_light_skin_tone[] = "👩đŸŊ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŧ"; + inline constexpr const char kiss_woman_man_tone3[] = "👩đŸŊ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŊ"; + inline constexpr const char kiss_woman_man_medium_skin_tone[] = "👩đŸŊ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŊ"; + inline constexpr const char kiss_woman_man_tone3_tone4[] = "👩đŸŊ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸž"; + inline constexpr const char kiss_woman_man_medium_skin_tone_medium_dark_skin_tone[] = "👩đŸŊ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸž"; + inline constexpr const char kiss_woman_man_tone3_tone5[] = "👩đŸŊ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŋ"; + inline constexpr const char kiss_woman_man_medium_skin_tone_dark_skin_tone[] = "👩đŸŊ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŋ"; + inline constexpr const char kiss_woman_man_tone4_tone1[] = "👩🏾‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸģ"; + inline constexpr const char kiss_woman_man_medium_dark_skin_tone_light_skin_tone[] = "👩🏾‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸģ"; + inline constexpr const char kiss_woman_man_tone4_tone2[] = "👩🏾‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŧ"; + inline constexpr const char kiss_woman_man_medium_dark_skin_tone_medium_light_skin_tone[] = "👩🏾‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŧ"; + inline constexpr const char kiss_woman_man_tone4_tone3[] = "👩🏾‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŊ"; + inline constexpr const char kiss_woman_man_medium_dark_skin_tone_medium_skin_tone[] = "👩🏾‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŊ"; + inline constexpr const char kiss_woman_man_tone4[] = "👩🏾‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸž"; + inline constexpr const char kiss_woman_man_medium_dark_skin_tone[] = "👩🏾‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸž"; + inline constexpr const char kiss_woman_man_tone4_tone5[] = "👩🏾‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŋ"; + inline constexpr const char kiss_woman_man_medium_dark_skin_tone_dark_skin_tone[] = "👩🏾‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŋ"; + inline constexpr const char kiss_woman_man_tone5_tone1[] = "👩đŸŋ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸģ"; + inline constexpr const char kiss_woman_man_dark_skin_tone_light_skin_tone[] = "👩đŸŋ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸģ"; + inline constexpr const char kiss_woman_man_tone5_tone2[] = "👩đŸŋ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŧ"; + inline constexpr const char kiss_woman_man_dark_skin_tone_medium_light_skin_tone[] = "👩đŸŋ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŧ"; + inline constexpr const char kiss_woman_man_tone5_tone3[] = "👩đŸŋ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŊ"; + inline constexpr const char kiss_woman_man_dark_skin_tone_medium_skin_tone[] = "👩đŸŋ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŊ"; + inline constexpr const char kiss_woman_man_tone5_tone4[] = "👩đŸŋ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸž"; + inline constexpr const char kiss_woman_man_dark_skin_tone_medium_dark_skin_tone[] = "👩đŸŋ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸž"; + inline constexpr const char kiss_woman_man_tone5[] = "👩đŸŋ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŋ"; + inline constexpr const char kiss_woman_man_dark_skin_tone[] = "👩đŸŋ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŋ"; + inline constexpr const char kiss_ww[] = "👩‍❤ī¸â€đŸ’‹â€đŸ‘Š"; + inline constexpr const char couplekiss_ww[] = "👩‍❤ī¸â€đŸ’‹â€đŸ‘Š"; + inline constexpr const char kiss_woman_woman_tone1[] = "👩đŸģ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸģ"; + inline constexpr const char kiss_woman_woman_light_skin_tone[] = "👩đŸģ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸģ"; + inline constexpr const char kiss_woman_woman_tone1_tone2[] = "👩đŸģ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸŧ"; + inline constexpr const char kiss_woman_woman_light_skin_tone_medium_light_skin_tone[] = "👩đŸģ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸŧ"; + inline constexpr const char kiss_woman_woman_tone1_tone3[] = "👩đŸģ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸŊ"; + inline constexpr const char kiss_woman_woman_light_skin_tone_medium_skin_tone[] = "👩đŸģ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸŊ"; + inline constexpr const char kiss_woman_woman_tone1_tone4[] = "👩đŸģ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸž"; + inline constexpr const char kiss_woman_woman_light_skin_tone_medium_dark_skin_tone[] = "👩đŸģ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸž"; + inline constexpr const char kiss_woman_woman_tone1_tone5[] = "👩đŸģ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸŋ"; + inline constexpr const char kiss_woman_woman_light_skin_tone_dark_skin_tone[] = "👩đŸģ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸŋ"; + inline constexpr const char kiss_woman_woman_tone2_tone1[] = "👩đŸŧ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸģ"; + inline constexpr const char kiss_woman_woman_medium_light_skin_tone_light_skin_tone[] = "👩đŸŧ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸģ"; + inline constexpr const char kiss_woman_woman_tone2[] = "👩đŸŧ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸŧ"; + inline constexpr const char kiss_woman_woman_medium_light_skin_tone[] = "👩đŸŧ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸŧ"; + inline constexpr const char kiss_woman_woman_tone2_tone3[] = "👩đŸŧ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸŊ"; + inline constexpr const char kiss_woman_woman_medium_light_skin_tone_medium_skin_tone[] = "👩đŸŧ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸŊ"; + inline constexpr const char kiss_woman_woman_tone2_tone4[] = "👩đŸŧ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸž"; + inline constexpr const char kiss_woman_woman_medium_light_skin_tone_medium_dark_skin_tone[] = "👩đŸŧ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸž"; + inline constexpr const char kiss_woman_woman_tone2_tone5[] = "👩đŸŧ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸŋ"; + inline constexpr const char kiss_woman_woman_medium_light_skin_tone_dark_skin_tone[] = "👩đŸŧ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸŋ"; + inline constexpr const char kiss_woman_woman_tone3_tone1[] = "👩đŸŊ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸģ"; + inline constexpr const char kiss_woman_woman_medium_skin_tone_light_skin_tone[] = "👩đŸŊ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸģ"; + inline constexpr const char kiss_woman_woman_tone3_tone2[] = "👩đŸŊ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸŧ"; + inline constexpr const char kiss_woman_woman_medium_skin_tone_medium_light_skin_tone[] = "👩đŸŊ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸŧ"; + inline constexpr const char kiss_woman_woman_tone3[] = "👩đŸŊ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸŊ"; + inline constexpr const char kiss_woman_woman_medium_skin_tone[] = "👩đŸŊ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸŊ"; + inline constexpr const char kiss_woman_woman_tone3_tone4[] = "👩đŸŊ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸž"; + inline constexpr const char kiss_woman_woman_medium_skin_tone_medium_dark_skin_tone[] = "👩đŸŊ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸž"; + inline constexpr const char kiss_woman_woman_tone3_tone5[] = "👩đŸŊ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸŋ"; + inline constexpr const char kiss_woman_woman_medium_skin_tone_dark_skin_tone[] = "👩đŸŊ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸŋ"; + inline constexpr const char kiss_woman_woman_tone4_tone1[] = "👩🏾‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸģ"; + inline constexpr const char kiss_woman_woman_medium_dark_skin_tone_light_skin_tone[] = "👩🏾‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸģ"; + inline constexpr const char kiss_woman_woman_tone4_tone2[] = "👩🏾‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸŧ"; + inline constexpr const char kiss_woman_woman_medium_dark_skin_tone_medium_light_skin_tone[] = "👩🏾‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸŧ"; + inline constexpr const char kiss_woman_woman_tone4_tone3[] = "👩🏾‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸŊ"; + inline constexpr const char kiss_woman_woman_medium_dark_skin_tone_medium_skin_tone[] = "👩🏾‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸŊ"; + inline constexpr const char kiss_woman_woman_tone4[] = "👩🏾‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸž"; + inline constexpr const char kiss_woman_woman_medium_dark_skin_tone[] = "👩🏾‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸž"; + inline constexpr const char kiss_woman_woman_tone4_tone5[] = "👩🏾‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸŋ"; + inline constexpr const char kiss_woman_woman_medium_dark_skin_tone_dark_skin_tone[] = "👩🏾‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸŋ"; + inline constexpr const char kiss_woman_woman_tone5_tone1[] = "👩đŸŋ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸģ"; + inline constexpr const char kiss_woman_woman_dark_skin_tone_light_skin_tone[] = "👩đŸŋ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸģ"; + inline constexpr const char kiss_woman_woman_tone5_tone2[] = "👩đŸŋ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸŧ"; + inline constexpr const char kiss_woman_woman_dark_skin_tone_medium_light_skin_tone[] = "👩đŸŋ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸŧ"; + inline constexpr const char kiss_woman_woman_tone5_tone3[] = "👩đŸŋ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸŊ"; + inline constexpr const char kiss_woman_woman_dark_skin_tone_medium_skin_tone[] = "👩đŸŋ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸŊ"; + inline constexpr const char kiss_woman_woman_tone5_tone4[] = "👩đŸŋ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸž"; + inline constexpr const char kiss_woman_woman_dark_skin_tone_medium_dark_skin_tone[] = "👩đŸŋ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸž"; + inline constexpr const char kiss_woman_woman_tone5[] = "👩đŸŋ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸŋ"; + inline constexpr const char kiss_woman_woman_dark_skin_tone[] = "👩đŸŋ‍❤ī¸â€đŸ’‹â€đŸ‘ŠđŸŋ"; + inline constexpr const char kiss_mm[] = "👨‍❤ī¸â€đŸ’‹â€đŸ‘¨"; + inline constexpr const char couplekiss_mm[] = "👨‍❤ī¸â€đŸ’‹â€đŸ‘¨"; + inline constexpr const char kiss_man_man_tone1[] = "👨đŸģ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸģ"; + inline constexpr const char kiss_man_man_light_skin_tone[] = "👨đŸģ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸģ"; + inline constexpr const char kiss_man_man_tone1_tone2[] = "👨đŸģ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŧ"; + inline constexpr const char kiss_man_man_light_skin_tone_medium_light_skin_tone[] = "👨đŸģ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŧ"; + inline constexpr const char kiss_man_man_tone1_tone3[] = "👨đŸģ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŊ"; + inline constexpr const char kiss_man_man_light_skin_tone_medium_skin_tone[] = "👨đŸģ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŊ"; + inline constexpr const char kiss_man_man_tone1_tone4[] = "👨đŸģ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸž"; + inline constexpr const char kiss_man_man_light_skin_tone_medium_dark_skin_tone[] = "👨đŸģ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸž"; + inline constexpr const char kiss_man_man_tone1_tone5[] = "👨đŸģ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŋ"; + inline constexpr const char kiss_man_man_light_skin_tone_dark_skin_tone[] = "👨đŸģ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŋ"; + inline constexpr const char kiss_man_man_tone2_tone1[] = "👨đŸŧ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸģ"; + inline constexpr const char kiss_man_man_medium_light_skin_tone_light_skin_tone[] = "👨đŸŧ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸģ"; + inline constexpr const char kiss_man_man_tone2[] = "👨đŸŧ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŧ"; + inline constexpr const char kiss_man_man_medium_light_skin_tone[] = "👨đŸŧ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŧ"; + inline constexpr const char kiss_man_man_tone2_tone3[] = "👨đŸŧ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŊ"; + inline constexpr const char kiss_man_man_medium_light_skin_tone_medium_skin_tone[] = "👨đŸŧ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŊ"; + inline constexpr const char kiss_man_man_tone2_tone4[] = "👨đŸŧ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸž"; + inline constexpr const char kiss_man_man_medium_light_skin_tone_medium_dark_skin_tone[] = "👨đŸŧ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸž"; + inline constexpr const char kiss_man_man_tone2_tone5[] = "👨đŸŧ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŋ"; + inline constexpr const char kiss_man_man_medium_light_skin_tone_dark_skin_tone[] = "👨đŸŧ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŋ"; + inline constexpr const char kiss_man_man_tone3_tone1[] = "👨đŸŊ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸģ"; + inline constexpr const char kiss_man_man_medium_skin_tone_light_skin_tone[] = "👨đŸŊ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸģ"; + inline constexpr const char kiss_man_man_tone3_tone2[] = "👨đŸŊ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŧ"; + inline constexpr const char kiss_man_man_medium_skin_tone_medium_light_skin_tone[] = "👨đŸŊ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŧ"; + inline constexpr const char kiss_man_man_tone3[] = "👨đŸŊ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŊ"; + inline constexpr const char kiss_man_man_medium_skin_tone[] = "👨đŸŊ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŊ"; + inline constexpr const char kiss_man_man_tone3_tone4[] = "👨đŸŊ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸž"; + inline constexpr const char kiss_man_man_medium_skin_tone_medium_dark_skin_tone[] = "👨đŸŊ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸž"; + inline constexpr const char kiss_man_man_tone3_tone5[] = "👨đŸŊ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŋ"; + inline constexpr const char kiss_man_man_medium_skin_tone_dark_skin_tone[] = "👨đŸŊ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŋ"; + inline constexpr const char kiss_man_man_tone4_tone1[] = "👨🏾‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸģ"; + inline constexpr const char kiss_man_man_medium_dark_skin_tone_light_skin_tone[] = "👨🏾‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸģ"; + inline constexpr const char kiss_man_man_tone4_tone2[] = "👨🏾‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŧ"; + inline constexpr const char kiss_man_man_medium_dark_skin_tone_medium_light_skin_tone[] = "👨🏾‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŧ"; + inline constexpr const char kiss_man_man_tone4_tone3[] = "👨🏾‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŊ"; + inline constexpr const char kiss_man_man_medium_dark_skin_tone_medium_skin_tone[] = "👨🏾‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŊ"; + inline constexpr const char kiss_man_man_tone4[] = "👨🏾‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸž"; + inline constexpr const char kiss_man_man_medium_dark_skin_tone[] = "👨🏾‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸž"; + inline constexpr const char kiss_man_man_tone4_tone5[] = "👨🏾‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŋ"; + inline constexpr const char kiss_man_man_medium_dark_skin_tone_dark_skin_tone[] = "👨🏾‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŋ"; + inline constexpr const char kiss_man_man_tone5_tone1[] = "👨đŸŋ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸģ"; + inline constexpr const char kiss_man_man_dark_skin_tone_light_skin_tone[] = "👨đŸŋ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸģ"; + inline constexpr const char kiss_man_man_tone5_tone2[] = "👨đŸŋ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŧ"; + inline constexpr const char kiss_man_man_dark_skin_tone_medium_light_skin_tone[] = "👨đŸŋ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŧ"; + inline constexpr const char kiss_man_man_tone5_tone3[] = "👨đŸŋ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŊ"; + inline constexpr const char kiss_man_man_dark_skin_tone_medium_skin_tone[] = "👨đŸŋ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŊ"; + inline constexpr const char kiss_man_man_tone5_tone4[] = "👨đŸŋ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸž"; + inline constexpr const char kiss_man_man_dark_skin_tone_medium_dark_skin_tone[] = "👨đŸŋ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸž"; + inline constexpr const char kiss_man_man_tone5[] = "👨đŸŋ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŋ"; + inline constexpr const char kiss_man_man_dark_skin_tone[] = "👨đŸŋ‍❤ī¸â€đŸ’‹â€đŸ‘¨đŸŋ"; + inline constexpr const char family[] = "đŸ‘Ē"; + inline constexpr const char family_man_woman_boy[] = "👨‍👩‍đŸ‘Ļ"; + inline constexpr const char family_mwg[] = "👨‍👩‍👧"; + inline constexpr const char family_mwgb[] = "👨‍👩‍👧‍đŸ‘Ļ"; + inline constexpr const char family_mwbb[] = "👨‍👩‍đŸ‘Ļ‍đŸ‘Ļ"; + inline constexpr const char family_mwgg[] = "👨‍👩‍👧‍👧"; + inline constexpr const char family_wwb[] = "👩‍👩‍đŸ‘Ļ"; + inline constexpr const char family_wwg[] = "👩‍👩‍👧"; + inline constexpr const char family_wwgb[] = "👩‍👩‍👧‍đŸ‘Ļ"; + inline constexpr const char family_wwbb[] = "👩‍👩‍đŸ‘Ļ‍đŸ‘Ļ"; + inline constexpr const char family_wwgg[] = "👩‍👩‍👧‍👧"; + inline constexpr const char family_mmb[] = "👨‍👨‍đŸ‘Ļ"; + inline constexpr const char family_mmg[] = "👨‍👨‍👧"; + inline constexpr const char family_mmgb[] = "👨‍👨‍👧‍đŸ‘Ļ"; + inline constexpr const char family_mmbb[] = "👨‍👨‍đŸ‘Ļ‍đŸ‘Ļ"; + inline constexpr const char family_mmgg[] = "👨‍👨‍👧‍👧"; + inline constexpr const char family_woman_boy[] = "👩‍đŸ‘Ļ"; + inline constexpr const char family_woman_girl[] = "👩‍👧"; + inline constexpr const char family_woman_girl_boy[] = "👩‍👧‍đŸ‘Ļ"; + inline constexpr const char family_woman_boy_boy[] = "👩‍đŸ‘Ļ‍đŸ‘Ļ"; + inline constexpr const char family_woman_girl_girl[] = "👩‍👧‍👧"; + inline constexpr const char family_man_boy[] = "👨‍đŸ‘Ļ"; + inline constexpr const char family_man_girl[] = "👨‍👧"; + inline constexpr const char family_man_girl_boy[] = "👨‍👧‍đŸ‘Ļ"; + inline constexpr const char family_man_boy_boy[] = "👨‍đŸ‘Ļ‍đŸ‘Ļ"; + inline constexpr const char family_man_girl_girl[] = "👨‍👧‍👧"; + inline constexpr const char knot[] = "đŸĒĸ"; + inline constexpr const char yarn[] = "đŸ§ļ"; + inline constexpr const char thread[] = "đŸ§ĩ"; + inline constexpr const char sewing_needle[] = "đŸĒĄ"; + inline constexpr const char coat[] = "đŸ§Ĩ"; + inline constexpr const char lab_coat[] = "đŸĨŧ"; + inline constexpr const char safety_vest[] = "đŸĻē"; + inline constexpr const char womans_clothes[] = "👚"; + inline constexpr const char shirt[] = "👕"; + inline constexpr const char jeans[] = "👖"; + inline constexpr const char briefs[] = "🩲"; + inline constexpr const char shorts[] = "đŸŠŗ"; + inline constexpr const char necktie[] = "👔"; + inline constexpr const char dress[] = "👗"; + inline constexpr const char bikini[] = "👙"; + inline constexpr const char one_piece_swimsuit[] = "🩱"; + inline constexpr const char kimono[] = "👘"; + inline constexpr const char sari[] = "đŸĨģ"; + inline constexpr const char thong_sandal[] = "🩴"; + inline constexpr const char womans_flat_shoe[] = "đŸĨŋ"; + inline constexpr const char high_heel[] = "👠"; + inline constexpr const char sandal[] = "👡"; + inline constexpr const char boot[] = "đŸ‘ĸ"; + inline constexpr const char mans_shoe[] = "👞"; + inline constexpr const char athletic_shoe[] = "👟"; + inline constexpr const char hiking_boot[] = "đŸĨž"; + inline constexpr const char socks[] = "đŸ§Ļ"; + inline constexpr const char gloves[] = "🧤"; + inline constexpr const char scarf[] = "đŸ§Ŗ"; + inline constexpr const char tophat[] = "🎩"; + inline constexpr const char billed_cap[] = "đŸ§ĸ"; + inline constexpr const char womans_hat[] = "👒"; + inline constexpr const char mortar_board[] = "🎓"; + inline constexpr const char helmet_with_cross[] = "⛑ī¸"; + inline constexpr const char helmet_with_white_cross[] = "⛑ī¸"; + inline constexpr const char military_helmet[] = "đŸĒ–"; + inline constexpr const char crown[] = "👑"; + inline constexpr const char ring[] = "💍"; + inline constexpr const char pouch[] = "👝"; + inline constexpr const char purse[] = "👛"; + inline constexpr const char handbag[] = "👜"; + inline constexpr const char briefcase[] = "đŸ’ŧ"; + inline constexpr const char school_satchel[] = "🎒"; + inline constexpr const char luggage[] = "đŸ§ŗ"; + inline constexpr const char eyeglasses[] = "👓"; + inline constexpr const char dark_sunglasses[] = "đŸ•ļī¸"; + inline constexpr const char goggles[] = "đŸĨŊ"; + inline constexpr const char closed_umbrella[] = "🌂"; + inline constexpr const char heart[] = "❤ī¸"; + inline constexpr const char orange_heart[] = "🧡"; + inline constexpr const char yellow_heart[] = "💛"; + inline constexpr const char green_heart[] = "💚"; + inline constexpr const char blue_heart[] = "💙"; + inline constexpr const char purple_heart[] = "💜"; + inline constexpr const char black_heart[] = "🖤"; + inline constexpr const char brown_heart[] = "🤎"; + inline constexpr const char white_heart[] = "🤍"; + inline constexpr const char broken_heart[] = "💔"; + inline constexpr const char heart_exclamation[] = "âŖī¸"; + inline constexpr const char heavy_heart_exclamation_mark_ornament[] = "âŖī¸"; + inline constexpr const char two_hearts[] = "💕"; + inline constexpr const char revolving_hearts[] = "💞"; + inline constexpr const char heartbeat[] = "💓"; + inline constexpr const char heartpulse[] = "💗"; + inline constexpr const char sparkling_heart[] = "💖"; + inline constexpr const char cupid[] = "💘"; + inline constexpr const char gift_heart[] = "💝"; + inline constexpr const char mending_heart[] = "❤ī¸â€đŸŠš"; + inline constexpr const char heart_on_fire[] = "❤ī¸â€đŸ”Ĩ"; + inline constexpr const char heart_decoration[] = "💟"; + inline constexpr const char peace[] = "☎ī¸"; + inline constexpr const char peace_symbol[] = "☎ī¸"; + inline constexpr const char cross[] = "✝ī¸"; + inline constexpr const char latin_cross[] = "✝ī¸"; + inline constexpr const char star_and_crescent[] = "â˜Ēī¸"; + inline constexpr const char om_symbol[] = "🕉ī¸"; + inline constexpr const char wheel_of_dharma[] = "☸ī¸"; + inline constexpr const char star_of_david[] = "✡ī¸"; + inline constexpr const char six_pointed_star[] = "đŸ”¯"; + inline constexpr const char menorah[] = "🕎"; + inline constexpr const char yin_yang[] = "☯ī¸"; + inline constexpr const char orthodox_cross[] = "â˜Ļī¸"; + inline constexpr const char place_of_worship[] = "🛐"; + inline constexpr const char worship_symbol[] = "🛐"; + inline constexpr const char ophiuchus[] = "⛎"; + inline constexpr const char aries[] = "♈"; + inline constexpr const char taurus[] = "♉"; + inline constexpr const char gemini[] = "♊"; + inline constexpr const char cancer[] = "♋"; + inline constexpr const char leo[] = "♌"; + inline constexpr const char virgo[] = "♍"; + inline constexpr const char libra[] = "♎"; + inline constexpr const char scorpius[] = "♏"; + inline constexpr const char sagittarius[] = "♐"; + inline constexpr const char capricorn[] = "♑"; + inline constexpr const char aquarius[] = "♒"; + inline constexpr const char pisces[] = "♓"; + inline constexpr const char id[] = "🆔"; + inline constexpr const char atom[] = "⚛ī¸"; + inline constexpr const char atom_symbol[] = "⚛ī¸"; + inline constexpr const char accept[] = "🉑"; + inline constexpr const char radioactive[] = "â˜ĸī¸"; + inline constexpr const char radioactive_sign[] = "â˜ĸī¸"; + inline constexpr const char biohazard[] = "â˜Ŗī¸"; + inline constexpr const char biohazard_sign[] = "â˜Ŗī¸"; + inline constexpr const char mobile_phone_off[] = "📴"; + inline constexpr const char vibration_mode[] = "đŸ“ŗ"; + inline constexpr const char u6709[] = "đŸˆļ"; + inline constexpr const char u7121[] = "🈚"; + inline constexpr const char u7533[] = "🈸"; + inline constexpr const char u55b6[] = "đŸˆē"; + inline constexpr const char u6708[] = "🈷ī¸"; + inline constexpr const char eight_pointed_black_star[] = "✴ī¸"; + inline constexpr const char vs[] = "🆚"; + inline constexpr const char white_flower[] = "💮"; + inline constexpr const char ideograph_advantage[] = "🉐"; + inline constexpr const char secret[] = "㊙ī¸"; + inline constexpr const char congratulations[] = "㊗ī¸"; + inline constexpr const char u5408[] = "🈴"; + inline constexpr const char u6e80[] = "đŸˆĩ"; + inline constexpr const char u5272[] = "🈹"; + inline constexpr const char u7981[] = "🈲"; + inline constexpr const char a[] = "🅰ī¸"; + inline constexpr const char b[] = "🅱ī¸"; + inline constexpr const char ab[] = "🆎"; + inline constexpr const char cl[] = "🆑"; + inline constexpr const char o2[] = "🅾ī¸"; + inline constexpr const char sos[] = "🆘"; + inline constexpr const char x[] = "❌"; + inline constexpr const char o[] = "⭕"; + inline constexpr const char octagonal_sign[] = "🛑"; + inline constexpr const char stop_sign[] = "🛑"; + inline constexpr const char no_entry[] = "⛔"; + inline constexpr const char name_badge[] = "📛"; + inline constexpr const char no_entry_sign[] = "đŸšĢ"; + inline constexpr const char anger[] = "đŸ’ĸ"; + inline constexpr const char hotsprings[] = "♨ī¸"; + inline constexpr const char no_pedestrians[] = "🚷"; + inline constexpr const char do_not_litter[] = "đŸš¯"; + inline constexpr const char no_bicycles[] = "đŸšŗ"; + inline constexpr const char non_potable_water[] = "🚱"; + inline constexpr const char underage[] = "🔞"; + inline constexpr const char no_mobile_phones[] = "đŸ“ĩ"; + inline constexpr const char no_smoking[] = "🚭"; + inline constexpr const char exclamation[] = "❗"; + inline constexpr const char grey_exclamation[] = "❕"; + inline constexpr const char question[] = "❓"; + inline constexpr const char grey_question[] = "❔"; + inline constexpr const char bangbang[] = "â€ŧī¸"; + inline constexpr const char interrobang[] = "⁉ī¸"; + inline constexpr const char low_brightness[] = "🔅"; + inline constexpr const char high_brightness[] = "🔆"; + inline constexpr const char part_alternation_mark[] = "ã€Ŋī¸"; + inline constexpr const char warning[] = "⚠ī¸"; + inline constexpr const char children_crossing[] = "🚸"; + inline constexpr const char trident[] = "🔱"; + inline constexpr const char fleur_de_lis[] = "⚜ī¸"; + inline constexpr const char beginner[] = "🔰"; + inline constexpr const char recycle[] = "â™ģī¸"; + inline constexpr const char white_check_mark[] = "✅"; + inline constexpr const char u6307[] = "đŸˆ¯"; + inline constexpr const char chart[] = "💹"; + inline constexpr const char sparkle[] = "❇ī¸"; + inline constexpr const char eight_spoked_asterisk[] = "âœŗī¸"; + inline constexpr const char negative_squared_cross_mark[] = "❎"; + inline constexpr const char globe_with_meridians[] = "🌐"; + inline constexpr const char diamond_shape_with_a_dot_inside[] = "💠"; + inline constexpr const char m[] = "Ⓜī¸"; + inline constexpr const char cyclone[] = "🌀"; + inline constexpr const char zzz[] = "💤"; + inline constexpr const char atm[] = "🏧"; + inline constexpr const char wc[] = "🚾"; + inline constexpr const char wheelchair[] = "â™ŋ"; + inline constexpr const char parking[] = "đŸ…ŋī¸"; + inline constexpr const char u7a7a[] = "đŸˆŗ"; + inline constexpr const char sa[] = "🈂ī¸"; + inline constexpr const char passport_control[] = "🛂"; + inline constexpr const char customs[] = "🛃"; + inline constexpr const char baggage_claim[] = "🛄"; + inline constexpr const char left_luggage[] = "🛅"; + inline constexpr const char elevator[] = "🛗"; + inline constexpr const char mens[] = "🚹"; + inline constexpr const char womens[] = "đŸšē"; + inline constexpr const char baby_symbol[] = "đŸšŧ"; + inline constexpr const char restroom[] = "đŸšģ"; + inline constexpr const char put_litter_in_its_place[] = "🚮"; + inline constexpr const char cinema[] = "đŸŽĻ"; + inline constexpr const char signal_strength[] = "đŸ“ļ"; + inline constexpr const char koko[] = "🈁"; + inline constexpr const char symbols[] = "đŸ”Ŗ"; + inline constexpr const char information_source[] = "ℹī¸"; + inline constexpr const char abc[] = "🔤"; + inline constexpr const char abcd[] = "🔡"; + inline constexpr const char capital_abcd[] = "🔠"; + inline constexpr const char ng[] = "🆖"; + inline constexpr const char ok[] = "🆗"; + inline constexpr const char up[] = "🆙"; + inline constexpr const char cool[] = "🆒"; + inline constexpr const char _new[] = "🆕"; + inline constexpr const char free[] = "🆓"; + inline constexpr const char zero[] = "0ī¸âƒŖ"; + inline constexpr const char one[] = "1ī¸âƒŖ"; + inline constexpr const char two[] = "2ī¸âƒŖ"; + inline constexpr const char three[] = "3ī¸âƒŖ"; + inline constexpr const char four[] = "4ī¸âƒŖ"; + inline constexpr const char five[] = "5ī¸âƒŖ"; + inline constexpr const char six[] = "6ī¸âƒŖ"; + inline constexpr const char seven[] = "7ī¸âƒŖ"; + inline constexpr const char eight[] = "8ī¸âƒŖ"; + inline constexpr const char nine[] = "9ī¸âƒŖ"; + inline constexpr const char keycap_ten[] = "🔟"; + inline constexpr const char hash[] = "#ī¸âƒŖ"; + inline constexpr const char asterisk[] = "*ī¸âƒŖ"; + inline constexpr const char keycap_asterisk[] = "*ī¸âƒŖ"; + inline constexpr const char eject[] = "⏏ī¸"; + inline constexpr const char eject_symbol[] = "⏏ī¸"; + inline constexpr const char arrow_forward[] = "â–ļī¸"; + inline constexpr const char pause_button[] = "⏸ī¸"; + inline constexpr const char double_vertical_bar[] = "⏸ī¸"; + inline constexpr const char play_pause[] = "⏯ī¸"; + inline constexpr const char stop_button[] = "⏚ī¸"; + inline constexpr const char record_button[] = "âēī¸"; + inline constexpr const char track_next[] = "⏭ī¸"; + inline constexpr const char next_track[] = "⏭ī¸"; + inline constexpr const char track_previous[] = "⏎ī¸"; + inline constexpr const char previous_track[] = "⏎ī¸"; + inline constexpr const char fast_forward[] = "⏊"; + inline constexpr const char rewind[] = "âĒ"; + inline constexpr const char arrow_double_up[] = "âĢ"; + inline constexpr const char arrow_double_down[] = "âŦ"; + inline constexpr const char arrow_backward[] = "◀ī¸"; + inline constexpr const char arrow_up_small[] = "đŸ”ŧ"; + inline constexpr const char arrow_down_small[] = "đŸ”Ŋ"; + inline constexpr const char arrow_right[] = "➡ī¸"; + inline constexpr const char arrow_left[] = "âŦ…ī¸"; + inline constexpr const char arrow_up[] = "âŦ†ī¸"; + inline constexpr const char arrow_down[] = "âŦ‡ī¸"; + inline constexpr const char arrow_upper_right[] = "↗ī¸"; + inline constexpr const char arrow_lower_right[] = "↘ī¸"; + inline constexpr const char arrow_lower_left[] = "↙ī¸"; + inline constexpr const char arrow_upper_left[] = "↖ī¸"; + inline constexpr const char arrow_up_down[] = "↕ī¸"; + inline constexpr const char left_right_arrow[] = "↔ī¸"; + inline constexpr const char arrow_right_hook[] = "â†Ēī¸"; + inline constexpr const char leftwards_arrow_with_hook[] = "↩ī¸"; + inline constexpr const char arrow_heading_up[] = "⤴ī¸"; + inline constexpr const char arrow_heading_down[] = "â¤ĩī¸"; + inline constexpr const char twisted_rightwards_arrows[] = "🔀"; + inline constexpr const char repeat[] = "🔁"; + inline constexpr const char repeat_one[] = "🔂"; + inline constexpr const char arrows_counterclockwise[] = "🔄"; + inline constexpr const char arrows_clockwise[] = "🔃"; + inline constexpr const char musical_note[] = "đŸŽĩ"; + inline constexpr const char notes[] = "đŸŽļ"; + inline constexpr const char heavy_plus_sign[] = "➕"; + inline constexpr const char heavy_minus_sign[] = "➖"; + inline constexpr const char heavy_division_sign[] = "➗"; + inline constexpr const char heavy_multiplication_x[] = "✖ī¸"; + inline constexpr const char heavy_equals_sign[] = "🟰"; + inline constexpr const char infinity[] = "♾ī¸"; + inline constexpr const char heavy_dollar_sign[] = "💲"; + inline constexpr const char currency_exchange[] = "💱"; + inline constexpr const char tm[] = "â„ĸī¸"; + inline constexpr const char copyright[] = "Šī¸"; + inline constexpr const char registered[] = "ÂŽī¸"; + inline constexpr const char wavy_dash[] = "〰ī¸"; + inline constexpr const char curly_loop[] = "➰"; + inline constexpr const char loop[] = "âžŋ"; + inline constexpr const char end[] = "🔚"; + inline constexpr const char back[] = "🔙"; + inline constexpr const char on[] = "🔛"; + inline constexpr const char top[] = "🔝"; + inline constexpr const char soon[] = "🔜"; + inline constexpr const char heavy_check_mark[] = "✔ī¸"; + inline constexpr const char ballot_box_with_check[] = "☑ī¸"; + inline constexpr const char radio_button[] = "🔘"; + inline constexpr const char white_circle[] = "âšĒ"; + inline constexpr const char black_circle[] = "âšĢ"; + inline constexpr const char red_circle[] = "🔴"; + inline constexpr const char blue_circle[] = "đŸ”ĩ"; + inline constexpr const char brown_circle[] = "🟤"; + inline constexpr const char purple_circle[] = "đŸŸŖ"; + inline constexpr const char green_circle[] = "đŸŸĸ"; + inline constexpr const char yellow_circle[] = "🟡"; + inline constexpr const char orange_circle[] = "🟠"; + inline constexpr const char small_red_triangle[] = "đŸ”ē"; + inline constexpr const char small_red_triangle_down[] = "đŸ”ģ"; + inline constexpr const char small_orange_diamond[] = "🔸"; + inline constexpr const char small_blue_diamond[] = "🔹"; + inline constexpr const char large_orange_diamond[] = "đŸ”ļ"; + inline constexpr const char large_blue_diamond[] = "🔷"; + inline constexpr const char white_square_button[] = "đŸ”ŗ"; + inline constexpr const char black_square_button[] = "🔲"; + inline constexpr const char black_small_square[] = "â–Ēī¸"; + inline constexpr const char white_small_square[] = "â–Ģī¸"; + inline constexpr const char black_medium_small_square[] = "◾"; + inline constexpr const char white_medium_small_square[] = "â—Ŋ"; + inline constexpr const char black_medium_square[] = "â—ŧī¸"; + inline constexpr const char white_medium_square[] = "â—ģī¸"; + inline constexpr const char black_large_square[] = "âŦ›"; + inline constexpr const char white_large_square[] = "âŦœ"; + inline constexpr const char orange_square[] = "🟧"; + inline constexpr const char blue_square[] = "đŸŸĻ"; + inline constexpr const char red_square[] = "đŸŸĨ"; + inline constexpr const char brown_square[] = "đŸŸĢ"; + inline constexpr const char purple_square[] = "đŸŸĒ"; + inline constexpr const char green_square[] = "🟩"; + inline constexpr const char yellow_square[] = "🟨"; + inline constexpr const char speaker[] = "🔈"; + inline constexpr const char mute[] = "🔇"; + inline constexpr const char sound[] = "🔉"; + inline constexpr const char loud_sound[] = "🔊"; + inline constexpr const char bell[] = "🔔"; + inline constexpr const char no_bell[] = "🔕"; + inline constexpr const char mega[] = "đŸ“Ŗ"; + inline constexpr const char loudspeaker[] = "đŸ“ĸ"; + inline constexpr const char speech_left[] = "🗨ī¸"; + inline constexpr const char left_speech_bubble[] = "🗨ī¸"; + inline constexpr const char eye_in_speech_bubble[] = "👁‍🗨"; + inline constexpr const char speech_balloon[] = "đŸ’Ŧ"; + inline constexpr const char thought_balloon[] = "💭"; + inline constexpr const char anger_right[] = "đŸ—¯ī¸"; + inline constexpr const char right_anger_bubble[] = "đŸ—¯ī¸"; + inline constexpr const char spades[] = "♠ī¸"; + inline constexpr const char clubs[] = "â™Ŗī¸"; + inline constexpr const char hearts[] = "â™Ĩī¸"; + inline constexpr const char diamonds[] = "â™Ļī¸"; + inline constexpr const char black_joker[] = "🃏"; + inline constexpr const char flower_playing_cards[] = "🎴"; + inline constexpr const char mahjong[] = "🀄"; + inline constexpr const char clock1[] = "🕐"; + inline constexpr const char clock2[] = "🕑"; + inline constexpr const char clock3[] = "🕒"; + inline constexpr const char clock4[] = "🕓"; + inline constexpr const char clock5[] = "🕔"; + inline constexpr const char clock6[] = "🕕"; + inline constexpr const char clock7[] = "🕖"; + inline constexpr const char clock8[] = "🕗"; + inline constexpr const char clock9[] = "🕘"; + inline constexpr const char clock10[] = "🕙"; + inline constexpr const char clock11[] = "🕚"; + inline constexpr const char clock12[] = "🕛"; + inline constexpr const char clock130[] = "🕜"; + inline constexpr const char clock230[] = "🕝"; + inline constexpr const char clock330[] = "🕞"; + inline constexpr const char clock430[] = "🕟"; + inline constexpr const char clock530[] = "🕠"; + inline constexpr const char clock630[] = "🕡"; + inline constexpr const char clock730[] = "đŸ•ĸ"; + inline constexpr const char clock830[] = "đŸ•Ŗ"; + inline constexpr const char clock930[] = "🕤"; + inline constexpr const char clock1030[] = "đŸ•Ĩ"; + inline constexpr const char clock1130[] = "đŸ•Ļ"; + inline constexpr const char clock1230[] = "🕧"; + inline constexpr const char female_sign[] = "♀ī¸"; + inline constexpr const char male_sign[] = "♂ī¸"; + inline constexpr const char transgender_symbol[] = "⚧"; + inline constexpr const char medical_symbol[] = "⚕ī¸"; + inline constexpr const char regional_indicator_z[] = "đŸ‡ŋ"; + inline constexpr const char regional_indicator_y[] = "🇾"; + inline constexpr const char regional_indicator_x[] = "đŸ‡Ŋ"; + inline constexpr const char regional_indicator_w[] = "đŸ‡ŧ"; + inline constexpr const char regional_indicator_v[] = "đŸ‡ģ"; + inline constexpr const char regional_indicator_u[] = "đŸ‡ē"; + inline constexpr const char regional_indicator_t[] = "🇹"; + inline constexpr const char regional_indicator_s[] = "🇸"; + inline constexpr const char regional_indicator_r[] = "🇷"; + inline constexpr const char regional_indicator_q[] = "đŸ‡ļ"; + inline constexpr const char regional_indicator_p[] = "đŸ‡ĩ"; + inline constexpr const char regional_indicator_o[] = "🇴"; + inline constexpr const char regional_indicator_n[] = "đŸ‡ŗ"; + inline constexpr const char regional_indicator_m[] = "🇲"; + inline constexpr const char regional_indicator_l[] = "🇱"; + inline constexpr const char regional_indicator_k[] = "🇰"; + inline constexpr const char regional_indicator_j[] = "đŸ‡¯"; + inline constexpr const char regional_indicator_i[] = "🇮"; + inline constexpr const char regional_indicator_h[] = "🇭"; + inline constexpr const char regional_indicator_g[] = "đŸ‡Ŧ"; + inline constexpr const char regional_indicator_f[] = "đŸ‡Ģ"; + inline constexpr const char regional_indicator_e[] = "đŸ‡Ē"; + inline constexpr const char regional_indicator_d[] = "🇩"; + inline constexpr const char regional_indicator_c[] = "🇨"; + inline constexpr const char regional_indicator_b[] = "🇧"; + inline constexpr const char regional_indicator_a[] = "đŸ‡Ļ"; + inline constexpr const char red_car[] = "🚗"; + inline constexpr const char taxi[] = "🚕"; + inline constexpr const char blue_car[] = "🚙"; + inline constexpr const char pickup_truck[] = "đŸ›ģ"; + inline constexpr const char bus[] = "🚌"; + inline constexpr const char trolleybus[] = "🚎"; + inline constexpr const char race_car[] = "🏎ī¸"; + inline constexpr const char racing_car[] = "🏎ī¸"; + inline constexpr const char police_car[] = "🚓"; + inline constexpr const char ambulance[] = "🚑"; + inline constexpr const char fire_engine[] = "🚒"; + inline constexpr const char minibus[] = "🚐"; + inline constexpr const char truck[] = "🚚"; + inline constexpr const char articulated_lorry[] = "🚛"; + inline constexpr const char tractor[] = "🚜"; + inline constexpr const char probing_cane[] = "đŸĻ¯"; + inline constexpr const char crutch[] = "đŸŠŧ"; + inline constexpr const char manual_wheelchair[] = "đŸĻŊ"; + inline constexpr const char motorized_wheelchair[] = "đŸĻŧ"; + inline constexpr const char scooter[] = "🛴"; + inline constexpr const char bike[] = "🚲"; + inline constexpr const char motor_scooter[] = "đŸ›ĩ"; + inline constexpr const char motorbike[] = "đŸ›ĩ"; + inline constexpr const char motorcycle[] = "🏍ī¸"; + inline constexpr const char racing_motorcycle[] = "🏍ī¸"; + inline constexpr const char auto_rickshaw[] = "đŸ›ē"; + inline constexpr const char wheel[] = "🛞"; + inline constexpr const char rotating_light[] = "🚨"; + inline constexpr const char oncoming_police_car[] = "🚔"; + inline constexpr const char oncoming_bus[] = "🚍"; + inline constexpr const char oncoming_automobile[] = "🚘"; + inline constexpr const char oncoming_taxi[] = "🚖"; + inline constexpr const char aerial_tramway[] = "🚡"; + inline constexpr const char mountain_cableway[] = "🚠"; + inline constexpr const char suspension_railway[] = "🚟"; + inline constexpr const char railway_car[] = "🚃"; + inline constexpr const char train[] = "🚋"; + inline constexpr const char mountain_railway[] = "🚞"; + inline constexpr const char monorail[] = "🚝"; + inline constexpr const char bullettrain_side[] = "🚄"; + inline constexpr const char bullettrain_front[] = "🚅"; + inline constexpr const char light_rail[] = "🚈"; + inline constexpr const char steam_locomotive[] = "🚂"; + inline constexpr const char train2[] = "🚆"; + inline constexpr const char metro[] = "🚇"; + inline constexpr const char tram[] = "🚊"; + inline constexpr const char station[] = "🚉"; + inline constexpr const char airplane[] = "✈ī¸"; + inline constexpr const char airplane_departure[] = "đŸ›Ģ"; + inline constexpr const char airplane_arriving[] = "đŸ›Ŧ"; + inline constexpr const char airplane_small[] = "🛩ī¸"; + inline constexpr const char small_airplane[] = "🛩ī¸"; + inline constexpr const char seat[] = "đŸ’ē"; + inline constexpr const char satellite_orbital[] = "🛰ī¸"; + inline constexpr const char rocket[] = "🚀"; + inline constexpr const char flying_saucer[] = "🛸"; + inline constexpr const char helicopter[] = "🚁"; + inline constexpr const char canoe[] = "đŸ›ļ"; + inline constexpr const char kayak[] = "đŸ›ļ"; + inline constexpr const char sailboat[] = "â›ĩ"; + inline constexpr const char speedboat[] = "🚤"; + inline constexpr const char motorboat[] = "đŸ›Ĩī¸"; + inline constexpr const char cruise_ship[] = "đŸ›ŗī¸"; + inline constexpr const char passenger_ship[] = "đŸ›ŗī¸"; + inline constexpr const char ferry[] = "⛴ī¸"; + inline constexpr const char ship[] = "đŸšĸ"; + inline constexpr const char ring_buoy[] = "🛟"; + inline constexpr const char anchor[] = "⚓"; + inline constexpr const char hook[] = "đŸĒ"; + inline constexpr const char fuelpump[] = "â›Ŋ"; + inline constexpr const char construction[] = "🚧"; + inline constexpr const char vertical_traffic_light[] = "đŸšĻ"; + inline constexpr const char traffic_light[] = "đŸšĨ"; + inline constexpr const char busstop[] = "🚏"; + inline constexpr const char map[] = "đŸ—ēī¸"; + inline constexpr const char world_map[] = "đŸ—ēī¸"; + inline constexpr const char moyai[] = "đŸ—ŋ"; + inline constexpr const char statue_of_liberty[] = "đŸ—Ŋ"; + inline constexpr const char tokyo_tower[] = "đŸ—ŧ"; + inline constexpr const char european_castle[] = "🏰"; + inline constexpr const char japanese_castle[] = "đŸ¯"; + inline constexpr const char stadium[] = "🏟ī¸"; + inline constexpr const char ferris_wheel[] = "🎡"; + inline constexpr const char roller_coaster[] = "đŸŽĸ"; + inline constexpr const char carousel_horse[] = "🎠"; + inline constexpr const char fountain[] = "⛲"; + inline constexpr const char beach_umbrella[] = "⛱ī¸"; + inline constexpr const char umbrella_on_ground[] = "⛱ī¸"; + inline constexpr const char beach[] = "🏖ī¸"; + inline constexpr const char beach_with_umbrella[] = "🏖ī¸"; + inline constexpr const char island[] = "🏝ī¸"; + inline constexpr const char desert_island[] = "🏝ī¸"; + inline constexpr const char desert[] = "🏜ī¸"; + inline constexpr const char volcano[] = "🌋"; + inline constexpr const char mountain[] = "⛰ī¸"; + inline constexpr const char mountain_snow[] = "🏔ī¸"; + inline constexpr const char snow_capped_mountain[] = "🏔ī¸"; + inline constexpr const char mount_fuji[] = "đŸ—ģ"; + inline constexpr const char camping[] = "🏕ī¸"; + inline constexpr const char tent[] = "â›ē"; + inline constexpr const char house[] = "🏠"; + inline constexpr const char house_with_garden[] = "🏡"; + inline constexpr const char homes[] = "🏘ī¸"; + inline constexpr const char house_buildings[] = "🏘ī¸"; + inline constexpr const char house_abandoned[] = "🏚ī¸"; + inline constexpr const char derelict_house_building[] = "🏚ī¸"; + inline constexpr const char hut[] = "🛖"; + inline constexpr const char construction_site[] = "🏗ī¸"; + inline constexpr const char building_construction[] = "🏗ī¸"; + inline constexpr const char factory[] = "🏭"; + inline constexpr const char office[] = "đŸĸ"; + inline constexpr const char department_store[] = "đŸŦ"; + inline constexpr const char post_office[] = "đŸŖ"; + inline constexpr const char european_post_office[] = "🏤"; + inline constexpr const char hospital[] = "đŸĨ"; + inline constexpr const char bank[] = "đŸĻ"; + inline constexpr const char hotel[] = "🏨"; + inline constexpr const char convenience_store[] = "đŸĒ"; + inline constexpr const char school[] = "đŸĢ"; + inline constexpr const char love_hotel[] = "🏩"; + inline constexpr const char wedding[] = "💒"; + inline constexpr const char classical_building[] = "🏛ī¸"; + inline constexpr const char church[] = "â›Ē"; + inline constexpr const char mosque[] = "🕌"; + inline constexpr const char synagogue[] = "🕍"; + inline constexpr const char hindu_temple[] = "🛕"; + inline constexpr const char kaaba[] = "🕋"; + inline constexpr const char shinto_shrine[] = "⛩ī¸"; + inline constexpr const char railway_track[] = "🛤ī¸"; + inline constexpr const char railroad_track[] = "🛤ī¸"; + inline constexpr const char motorway[] = "đŸ›Ŗī¸"; + inline constexpr const char japan[] = "🗾"; + inline constexpr const char rice_scene[] = "🎑"; + inline constexpr const char park[] = "🏞ī¸"; + inline constexpr const char national_park[] = "🏞ī¸"; + inline constexpr const char sunrise[] = "🌅"; + inline constexpr const char sunrise_over_mountains[] = "🌄"; + inline constexpr const char stars[] = "🌠"; + inline constexpr const char sparkler[] = "🎇"; + inline constexpr const char fireworks[] = "🎆"; + inline constexpr const char city_sunset[] = "🌇"; + inline constexpr const char city_sunrise[] = "🌇"; + inline constexpr const char city_dusk[] = "🌆"; + inline constexpr const char cityscape[] = "🏙ī¸"; + inline constexpr const char night_with_stars[] = "🌃"; + inline constexpr const char milky_way[] = "🌌"; + inline constexpr const char bridge_at_night[] = "🌉"; + inline constexpr const char foggy[] = "🌁"; +} +}; diff --git a/include/dpp/user.h b/include/dpp/user.h new file mode 100644 index 0000000..4595c8e --- /dev/null +++ b/include/dpp/user.h @@ -0,0 +1,409 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace dpp { + +/** + * @brief Various bitmask flags used to represent information about a dpp::user + */ +enum user_flags : uint32_t { + /// User is a bot + u_bot = 0b00000000000000000000000000000001, + /// User is a system user (Clyde!) + u_system = 0b00000000000000000000000000000010, + /// User has multi-factor authentication enabled + u_mfa_enabled = 0b00000000000000000000000000000100, + /// User is verified (verified email address) + u_verified = 0b00000000000000000000000000001000, + /// User has full nitro + u_nitro_full = 0b00000000000000000000000000010000, + /// User has nitro classic + u_nitro_classic = 0b00000000000000000000000000100000, + /// User is discord staff + u_discord_employee = 0b00000000000000000000000001000000, + /// User owns a partnered server + u_partnered_owner = 0b00000000000000000000000010000000, + /// User is a member of hypesquad events + u_hypesquad_events = 0b00000000000000000000000100000000, + /// User has BugHunter level 1 + u_bughunter_1 = 0b00000000000000000000001000000000, + /// User is a member of House Bravery + u_house_bravery = 0b00000000000000000000010000000000, + /// User is a member of House Brilliance + u_house_brilliance = 0b00000000000000000000100000000000, + /// User is a member of House Balance + u_house_balance = 0b00000000000000000001000000000000, + /// User is an early supporter + u_early_supporter = 0b00000000000000000010000000000000, + /// User is a team user + u_team_user = 0b00000000000000000100000000000000, + /// User is has Bug Hunter level 2 + u_bughunter_2 = 0b00000000000000001000000000000000, + /// User is a verified bot + u_verified_bot = 0b00000000000000010000000000000000, + /// User has the Early Verified Bot Developer badge + u_verified_bot_dev = 0b00000000000000100000000000000000, + /// User's icon is animated + u_animated_icon = 0b00000000000001000000000000000000, + /// User is a certified moderator + u_certified_moderator = 0b00000000000010000000000000000000, + /// User is a bot using HTTP interactions (shows online even when not connected to a websocket) + u_bot_http_interactions = 0b00000000000100000000000000000000, + /// User has nitro basic + u_nitro_basic = 0b00000000001000000000000000000000, + /// User has the active developer badge + u_active_developer = 0b00000000010000000000000000000000, + /// User's banner is animated + u_animated_banner = 0b00000000100000000000000000000000, +}; + +/** + * @brief Represents a user on discord. May or may not be a member of a dpp::guild. + */ +class DPP_EXPORT user : public managed, public json_interface { +public: + /** Discord username */ + std::string username; + /** Global display name */ + std::string global_name; + /** Avatar hash */ + utility::iconhash avatar; + /** Flags built from a bitmask of values in dpp::user_flags */ + uint32_t flags; + /** Discriminator (aka tag), 4 digits usually displayed with leading zeroes. + * + * @note To print the discriminator with leading zeroes, use format_username(). + * 0 for users that have migrated to the new username format. + */ + uint16_t discriminator; + /** Reference count of how many guilds this user is in */ + uint8_t refcount; + + /** + * @brief Construct a new user object + */ + user(); + + /** + * @brief Destroy the user object + */ + virtual ~user() = default; + + /** + * @brief Create a mentionable user. + * @param id The ID of the user. + * @return std::string The formatted mention of the user. + */ + static std::string get_mention(const snowflake& id); + + /** Fill this record from json. + * @param j The json to fill this record from + * @return Reference to self + */ + user& fill_from_json(nlohmann::json* j); + + /** + * @brief Convert to JSON string + * + * @param with_id include ID in output + * @return std::string JSON output + */ + virtual std::string build_json(bool with_id = true) const; + + /** + * @brief Get the avatar url of the user + * + * @note If the user doesn't have an avatar, the default user avatar url is returned which is always in `png` format! + * + * @param size The size of the avatar in pixels. It can be any power of two between 16 and 4096, + * otherwise the default sized avatar is returned. + * @param format The format to use for the avatar. It can be one of `i_webp`, `i_jpg`, `i_png` or `i_gif`. + * When passing `i_gif`, it returns an empty string for non-animated images. Consider using the `prefer_animated` parameter instead. + * @param prefer_animated Whether you prefer gif format. + * If true, it'll return gif format whenever the image is available as animated. + * @return std::string avatar url or an empty string, if required attributes are missing or an invalid format was passed + */ + std::string get_avatar_url(uint16_t size = 0, const image_type format = i_png, bool prefer_animated = true) const; + + /** + * @brief Get the default avatar url of the user. This is calculated by the discriminator. + * + * @return std::string avatar url or an empty string, if the discriminator is empty + */ + std::string get_default_avatar_url() const; + + /** + * @brief Return a ping/mention for the user + * + * @return std::string mention + */ + std::string get_mention() const; + + /** + * @brief Return true if user has the active Developer badge + * + * @return true if has active developer + */ + bool is_active_developer() const; + /** + * @brief User is a bot + * + * @return True if the user is a bot + */ + bool is_bot() const; + /** + * @brief User is a system user (Clyde) + * + * @return true if user is a system user + */ + bool is_system() const; + /** + * @brief User has multi-factor authentication enabled + * + * @return true if multi-factor is enabled + */ + bool is_mfa_enabled() const; + /** + * @brief Return true if user has verified account + * + * @return true if verified + */ + bool is_verified() const; + /** + * @brief Return true if user has full nitro. + * This is mutually exclusive with full nitro. + * + * @return true if user has full nitro + */ + bool has_nitro_full() const; + /** + * @brief Return true if user has nitro classic. + * This is mutually exclusive with nitro classic. + * + * @return true if user has nitro classic + */ + bool has_nitro_classic() const; + /** + * @brief Return true if user has nitro basic. + * This is mutually exclusive with nitro basic. + * + * @return true if user has nitro basic + */ + bool has_nitro_basic() const; + /** + * @brief Return true if user is a discord employee + * + * @return true if user is discord staff + */ + bool is_discord_employee() const; + /** + * @brief Return true if user owns a partnered server + * + * @return true if user has partnered server + */ + bool is_partnered_owner() const; + /** + * @brief Return true if user has hypesquad events + * + * @return true if has hypesquad events + */ + bool has_hypesquad_events() const; + /** + * @brief Return true if user has the bughunter level 1 badge + * + * @return true if has bughunter level 1 + */ + bool is_bughunter_1() const; + /** + * @brief Return true if user is in house bravery + * + * @return true if in house bravery + */ + bool is_house_bravery() const; + /** + * @brief Return true if user is in house brilliance + * + * @return true if in house brilliance + */ + bool is_house_brilliance() const; + /** + * @brief Return true if user is in house balance + * + * @return true if in house brilliance + */ + bool is_house_balance() const; + /** + * @brief Return true if user is an early supporter + * + * @return true if early supporter + */ + bool is_early_supporter() const; + /** + * @brief Return true if user is a team user + * + * @return true if a team user + */ + bool is_team_user() const; + /** + * @brief Return true if user has the bughunter level 2 badge + * + * @return true if has bughunter level 2 + */ + bool is_bughunter_2() const; + /** + * @brief Return true if user has the verified bot badge + * + * @return true if verified bot + */ + bool is_verified_bot() const; + /** + * @brief Return true if user is an early verified bot developer + * + * @return true if verified bot developer + */ + bool is_verified_bot_dev() const; + /** + * @brief Return true if user is a certified moderator + * + * @return true if certified moderator + */ + bool is_certified_moderator() const; + /** + * @brief Return true if user is a bot which exclusively uses HTTP interactions. + * Bots using HTTP interactions are always considered online even when not connected to a websocket. + * + * @return true if is a http interactions only bot + */ + bool is_bot_http_interactions() const; + /** + * @brief Return true if user has an animated icon + * + * @return true if icon is animated (gif) + */ + bool has_animated_icon() const; + + /** + * @brief Format a username into user\#discriminator + * + * For example Brain#0001 + * + * @return Formatted username and discriminator + */ + std::string format_username() const; +}; + +/** + * @brief A user with additional fields only available via the oauth2 identify scope. + * These are not included in dpp::user as additional scopes are needed to fetch them + * which bots do not normally have. + */ +class DPP_EXPORT user_identified : public user, public json_interface { +public: + std::string locale; //!< Optional: the user's chosen language option identify + std::string email; //!< Optional: the user's email email (may be empty) + utility::iconhash banner; //!< Optional: the user's banner hash identify (may be empty) + uint32_t accent_color; //!< Optional: the user's banner color encoded as an integer representation of hexadecimal color code identify (may be empty) + bool verified; //!< Optional: whether the email on this account has been verified email + + /** Fill this record from json. + * @param j The json to fill this record from + * @return Reference to self + */ + user_identified& fill_from_json(nlohmann::json* j); + + /** + * @brief Convert to JSON string + * + * @param with_id include ID in output + * @return std::string JSON output + */ + virtual std::string build_json(bool with_id = true) const; + + /** + * @brief Construct a new user identified object + */ + user_identified(); + + /** + * @brief Construct a new user identified object from a user object + * + * @param u user object + */ + user_identified(const user& u); + + /** + * @brief Destroy the user identified object + */ + virtual ~user_identified() = default; + + /** + * @brief Return true if user has an animated banner + * + * @return true if banner is animated (gif) + */ + bool has_animated_banner() const; + + /** + * @brief Get the user identified's banner url if they have one, otherwise returns an empty string + * + * @param size The size of the banner in pixels. It can be any power of two between 16 and 4096, + * otherwise the default sized banner is returned. + * @param format The format to use for the avatar. It can be one of `i_webp`, `i_jpg`, `i_png` or `i_gif`. + * When passing `i_gif`, it returns an empty string for non-animated images. Consider using the `prefer_animated` parameter instead. + * @param prefer_animated Whether you prefer gif format. + * If true, it'll return gif format whenever the image is available as animated. + * @return std::string banner url or an empty string, if required attributes are missing or an invalid format was passed + */ + std::string get_banner_url(uint16_t size = 0, const image_type format = i_png, bool prefer_animated = true) const; + +}; + +/** + * @brief helper function to deserialize a user from json + * + * @see https://github.com/nlohmann/json#arbitrary-types-conversions + * + * @param j output json object + * @param u user to be deserialized + */ +void from_json(const nlohmann::json& j, user& u); + +/** + * @brief helper function to deserialize a user_identified from json + * + * @see https://github.com/nlohmann/json#arbitrary-types-conversions + * + * @param j output json object + * @param u user to be deserialized + */ +void from_json(const nlohmann::json& j, user_identified& u); + +/** A group of users */ +typedef std::unordered_map user_map; + +} // namespace dpp diff --git a/include/dpp/utility.h b/include/dpp/utility.h new file mode 100644 index 0000000..59093a9 --- /dev/null +++ b/include/dpp/utility.h @@ -0,0 +1,676 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef MAX_CND_IMAGE_SIZE + #define MAX_CDN_IMAGE_SIZE 4096 +#endif +#ifndef MIN_CDN_IMAGE_SIZE + #define MIN_CDN_IMAGE_SIZE 16 +#endif + +/** + * @brief The main namespace for D++ functions, classes and types + */ +namespace dpp { + + enum sticker_format : uint8_t; + + /** + * @brief Utility helper functions, generally for logging, running programs, time/date manipulation, etc + */ + namespace utility { + + /** + * For internal use only. Helper function to easily create discord's cdn endpoint urls + * @see https://discord.com/developers/docs/reference#image-formatting-cdn-endpoints + * @param allowed_formats A vector of supported formats for the endpoint from the discord docs + * @param path_without_extension The path for the endpoint (without the extension e.g. `.png`) + * @param format the wished format to return. Must be one of the formats passed in `allowed_formats`, otherwise it returns an empty string + * @param size the image size which will be appended as a querystring to the url. + * It must be any power of two between 16 and 4096, otherwise no querystring will be appended (discord then returns the image as their default size) + * @param prefer_animated Whether the user prefers gif format. If true, it'll return gif format whenever the image is available as animated. + * In this case, the `format`-parameter is only used for non-animated images. + * @param is_animated Whether the image is actually animated or not + * @return std::string endpoint url or empty string + */ + std::string DPP_EXPORT cdn_endpoint_url(const std::vector &allowed_formats, const std::string &path_without_extension, const dpp::image_type format, uint16_t size, bool prefer_animated = false, bool is_animated = false); + + /** + * For internal use only. Helper function to easily create discord's cdn endpoint urls + * @see https://discord.com/developers/docs/reference#image-formatting-cdn-endpoints + * @param allowed_formats A vector of supported formats for the endpoint from the discord docs + * @param path_without_extension The path for the endpoint (without the extension e.g. `.png`) + * @param hash The hash (optional). If not empty, it will be prefixed with `a_` for animated images (`is_animated`=true) + * @param format the wished format to return. Must be one of the formats passed in `allowed_formats`, otherwise it returns an empty string + * @param size the image size which will be appended as a querystring to the url. + * It must be any power of two between 16 and 4096, otherwise no querystring will be appended (discord then returns the image as their default size) + * @param prefer_animated Whether the user prefers gif format. If true, it'll return gif format whenever the image is available as animated. + * In this case, the `format`-parameter is only used for non-animated images. + * @param is_animated Whether the image is actually animated or not + * @return std::string endpoint url or empty string + */ + std::string DPP_EXPORT cdn_endpoint_url_hash(const std::vector &allowed_formats, const std::string &path_without_extension, const std::string &hash, const dpp::image_type format, uint16_t size, bool prefer_animated = false, bool is_animated = false); + + /** + * For internal use only. Helper function to easily create discord's cdn endpoint urls (specialised for stickers) + * @see https://discord.com/developers/docs/reference#image-formatting-cdn-endpoints + * @param sticker_id The sticker ID. Returns empty string if 0 + * @param format The format type + * @return std::string endpoint url or empty string + */ + std::string DPP_EXPORT cdn_endpoint_url_sticker(snowflake sticker_id, sticker_format format); + + /** + * @brief Timestamp formats for dpp::utility::timestamp() + * + * @note These values are the actual character values specified by the Discord API + * and should not be changed unless the Discord API changes the specification! + * They have been sorted into numerical order of their ASCII value to keep C++ happy. + */ + enum time_format : uint8_t { + /// "20 April 2021" - Long Date + tf_long_date = 'D', + /// "Tuesday, 20 April 2021 16:20" - Long Date/Time + tf_long_datetime = 'F', + /// "2 months ago" - Relative Time + tf_relative_time = 'R', + /// "16:20:30" - Long Time + tf_long_time = 'T', + /// "20/04/2021" - Short Date + tf_short_date = 'd', + /// "20 April 2021 16:20" - Short Date/Time + tf_short_datetime = 'f', + /// "16:20" - Short Time + tf_short_time = 't', + }; + + /** + * @brief Guild navigation types for dpp::utility::guild_navigation() + */ + enum guild_navigation_type { + /// _Customize_ tab with the server's dpp::onboarding_prompt + gnt_customize, + /// _Browse Channels_ tab + gnt_browse, + /// Server Guide + gnt_guide, + }; + + /** + * @brief The base URL for CDN content such as profile pictures and guild icons. + */ + inline const std::string cdn_host = "https://cdn.discordapp.com"; + + /** + * @brief Callback for the results of a command executed via dpp::utility::exec + */ + typedef std::function cmd_result_t; + + /** + * @brief Run a commandline program asynchronously. The command line program + * is spawned in a separate std::thread, and when complete, its output from + * stdout is passed to the callback function in its string parameter. For example + * ``` + * dpp::utility::exec("/bin/ls", {"-al"}, [](const std::string& output) { + * std::cout << "Output of 'ls -al': " << output << "\n"; + * }); + * ``` + * + * @param cmd The command to run. + * @param parameters Command line parameters. Each will be escaped using `std::quoted`. + * @param callback The callback to call on completion. + */ + void DPP_EXPORT exec(const std::string& cmd, std::vector parameters = {}, cmd_result_t callback = {}); + + /** + * @brief Return a mentionable timestamp (used in a message). These timestamps will display the given timestamp in the user's timezone and locale. + * + * @param ts Time stamp to convert + * @param tf Format of timestamp using dpp::utility::time_format + * @return std::string The formatted timestamp + */ + std::string DPP_EXPORT timestamp(time_t ts, time_format tf = tf_short_datetime); + + /** + * @brief Create a mentionable guild navigation (used in a message). + * + * @param guild_id The guild ID + * @param gnt Guild navigation type using dpp::utility::guild_navigation_type + * @return std::string The formatted timestamp + */ + std::string DPP_EXPORT guild_navigation(const snowflake guild_id, guild_navigation_type gnt); + + /** + * @brief Returns current date and time + * + * @return std::string Current date and time in "Y-m-d H:M:S" format + */ + std::string DPP_EXPORT current_date_time(); + /** + * @brief Convert a dpp::loglevel enum value to a string + * + * @param in log level to convert + * @return std::string string form of log level + */ + std::string DPP_EXPORT loglevel(dpp::loglevel in); + + /** + * @brief Store a 128 bit icon hash (profile picture, server icon etc) + * as a 128 bit binary value made of two uint64_t. + * Has a constructor to build one from a string, and a method to fetch + * the value back in string form. + */ + struct DPP_EXPORT iconhash { + + uint64_t first; //!< High 64 bits + uint64_t second; //!< Low 64 bits + + /** + * @brief Construct a new iconhash object + * @param _first Leftmost portion of the hash value + * @param _second Rightmost portion of the hash value + */ + iconhash(uint64_t _first = 0, uint64_t _second = 0); + + /** + * @brief Construct a new iconhash object + */ + iconhash(const iconhash&); + + /** + * @brief Destroy the iconhash object + */ + ~iconhash(); + + /** + * @brief Construct a new iconhash object + * + * @param hash String hash to construct from. + * Must contain a 32 character hex string. + * + * @throws std::length_error if the provided + * string is not exactly 32 characters long. + */ + iconhash(const std::string &hash); + + /** + * @brief Assign from std::string + * + * @param assignment string to assign from. + * + * @throws std::length_error if the provided + * string is not exactly 32 characters long. + */ + iconhash& operator=(const std::string &assignment); + + /** + * @brief Check if one iconhash is equal to another + * + * @param other other iconhash to compare + * @return True if the iconhash objects match + */ + bool operator==(const iconhash& other) const; + + /** + * @brief Change value of iconhash object + * + * @param hash String hash to change to. + * Must contain a 32 character hex string. + * + * @throws std::length_error if the provided + * string is not exactly 32 characters long. + */ + void set(const std::string &hash); + + /** + * @brief Convert iconhash back to 32 character + * string value. + * + * @return std::string Hash value + */ + std::string to_string() const; + }; + + /** + * @brief Return the current time with fractions of seconds. + * This is a unix epoch time with the fractional seconds part + * after the decimal place. + * + * @return double time with fractional seconds + */ + double DPP_EXPORT time_f(); + + /** + * @brief Returns true if D++ was built with voice support + * + * @return bool True if voice support is compiled in (libsodium/libopus) + */ + bool DPP_EXPORT has_voice(); + + /** + * @brief Returns true if D++ was built with coroutine support + * + * @return bool True if coroutines are supported + */ + bool DPP_EXPORT is_coro_enabled(); + + /** + * @brief Convert a byte count to display value + * + * @param c number of bytes + * @return std::string display value suffixed with M, G, T where necessary + */ + std::string DPP_EXPORT bytes(uint64_t c); + + /** + * @brief A class used to represent an uptime in hours, minutes, + * seconds and days, with helper functions to convert from time_t + * and display as a string. + */ + struct DPP_EXPORT uptime { + uint16_t days; //!< Number of days + uint8_t hours; //!< Number of hours + uint8_t mins; //!< Number of minutes + uint8_t secs; //!< Number of seconds + + /** + * @brief Construct a new uptime object + */ + uptime(); + + /** + * @brief Construct a new uptime object + * + * @param diff A time_t to initialise the object from + */ + uptime(time_t diff); + + /** + * @brief Construct a new uptime object + * + * @param diff A time_t to initialise the object from + */ + uptime(double diff); + + /** + * @brief Get uptime as string + * + * @return std::string Uptime as string + */ + std::string to_string() const; + + /** + * @brief Get uptime as seconds + * + * @return uint64_t Uptime as seconds + */ + uint64_t to_secs() const; + + /** + * @brief Get uptime as milliseconds + * + * @return uint64_t Uptime as milliseconds + */ + uint64_t to_msecs() const; + }; + + /** + * @brief Convert doubles to RGB for sending in embeds + * + * @param red red value, between 0 and 1 inclusive + * @param green green value, between 0 and 1 inclusive + * @param blue blue value, between 0 and 1 inclusive + * @return uint32_t returned integer colour value + */ + uint32_t DPP_EXPORT rgb(double red, double green, double blue); + + /** + * @brief Convert ints to RGB for sending in embeds + * + * @param red red value, between 0 and 255 inclusive + * @param green green value, between 0 and 255 inclusive + * @param blue blue value, between 0 and 255 inclusive + * @return uint32_t returned integer colour value + */ + uint32_t DPP_EXPORT rgb(int red, int green, int blue); + + /** + * @brief Convert doubles to CMYK for sending in embeds + * + * @param c cyan value, between 0 and 1 inclusive + * @param m magenta value, between 0 and 1 inclusive + * @param y yellow value, between 0 and 1 inclusive + * @param k key (black) value, between 0 and 1 inclusive + * @return uint32_t returned integer colour value + */ + uint32_t DPP_EXPORT cmyk(double c, double m, double y, double k); + + /** + * @brief Convert ints to CMYK for sending in embeds + * + * @param c cyan value, between 0 and 255 inclusive + * @param m magenta value, between 0 and 255 inclusive + * @param y yellow value, between 0 and 255 inclusive + * @param k key (black) value, between 0 and 255 inclusive + * @return uint32_t returned integer colour value + */ + uint32_t DPP_EXPORT cmyk(int c, int m, int y, int k); + + /** + * @brief Output hex values of a section of memory for debugging + * + * @param data The start of the data to display + * @param length The length of data to display + */ + std::string DPP_EXPORT debug_dump(uint8_t* data, size_t length); + + /** + * @brief Returns the length of a UTF-8 string in codepoints + * + * @param str string to count length of + * @return size_t length of string (0 for invalid utf8) + */ + size_t DPP_EXPORT utf8len(const std::string &str); + + /** + * @brief Return substring of a UTF-8 encoded string in codepoints + * + * @param str string to return substring from + * @param start start codepoint offset + * @param length length in codepoints + * @return std::string Substring in UTF-8 or empty string if invalid UTF-8 passed in + */ + std::string DPP_EXPORT utf8substr(const std::string& str, std::string::size_type start, std::string::size_type length); + + /** + * @brief Read a whole file into a std::string. + * Be sure you have enough memory to read the file, if you are reading a large file. + * @note Be aware this function can block! If you are regularly reading large files, consider caching them. + * @param filename The path to the file to read + * @return std::string The file contents + * @throw dpp::file_exception on failure to read the entire file + */ + std::string DPP_EXPORT read_file(const std::string& filename); + + /** + * @brief Validate a string value + * In the event the length of the string is less than _min, then an exception of type dpp:length_exception + * will be thrown. If the string is longer than _max UTF8 codepoints it will be truncated to fit. + * + * @param value The value to validate + * @param _min Minimum length + * @param _max Maximum length + * @param exception_message Exception message to throw if value length < _min + * @return std::string Validated string, truncated if necessary. + * @throw dpp::length_exception if value UTF8 length < _min + */ + std::string DPP_EXPORT validate(const std::string& value, size_t _min, size_t _max, const std::string& exception_message); + + /** + * @brief Get the url query parameter for the cdn endpoint. Internally used to build url getters. + * + * @param size size to generate url parameter for. Must be any power of two between 16 and 4096 (inclusive) or it'll return an empty string. + * @return std::string url query parameter e.g. `?size=128`, or an empty string + */ + std::string DPP_EXPORT avatar_size(uint32_t size); + + /** + * @brief Split (tokenize) a string into a vector, using the given separators + * + * @param in Input string + * @param sep Separator characters + * @return std::vector Tokenized strings + */ + std::vector DPP_EXPORT tokenize(std::string const &in, const char* sep = "\r\n"); + + /** + * @brief Create a bot invite + * + * @param bot_id Bot ID + * @param permissions Permission bitmask of the bot to invite + * @param scopes Scopes to use + * @return Invite URL + */ + std::string DPP_EXPORT bot_invite_url(const snowflake bot_id, const uint64_t permissions = 0, const std::vector& scopes = {"bot", "applications.commands"}); + + /** + * @brief Escapes Discord's markdown sequences in a string + * + * @param text Text to escape + * @param escape_code_blocks If set to false, then code blocks are not escaped. + * This means that you can still use a code block, and the text within will be left as-is. + * If set to true, code blocks will also be escaped so that ` symbol may be used as a normal + * character. + * @return std::string The text with the markdown special characters escaped with a backslash + */ + std::string DPP_EXPORT markdown_escape(const std::string& text, bool escape_code_blocks = false); + + /** + * @brief Encodes a url parameter similar to [php urlencode()](https://www.php.net/manual/en/function.urlencode.php) + * + * @param value String to encode + * @return std::string URL encoded string + */ + std::string DPP_EXPORT url_encode(const std::string &value); + + /** + * @brief Create a mentionable slashcommand (used in a message). + * @param command_id The ID of the slashcommand + * @param command_name The command name + * @param subcommand Optional: The subcommand name (for mentioning a subcommand) + * @return std::string The formatted mention + */ + std::string DPP_EXPORT slashcommand_mention(snowflake command_id, const std::string &command_name, const std::string &subcommand = ""); + + /** + * @brief Create a mentionable slashcommand (used in a message). + * @param command_id The ID of the slashcommand + * @param command_name The command name + * @param subcommand_group The subcommand group name + * @param subcommand The subcommand name + * @return std::string The formatted mention of the slashcommand with its subcommand + */ + std::string DPP_EXPORT slashcommand_mention(snowflake command_id, const std::string &command_name, const std::string &subcommand_group, const std::string &subcommand); + + /** + * @brief Create a mentionable user. + * @param id The ID of the user. + * @return std::string The formatted mention of the user. + */ + std::string DPP_EXPORT user_mention(const snowflake& id); + + /** + * @brief Create a mentionable channel. + * @param id The ID of the channel. + * @return std::string The formatted mention of the channel. + */ + std::string DPP_EXPORT channel_mention(const snowflake& id); + + /** + * @brief Create a mentionable emoji + * @param name The name of the emoji. + * @param id The ID of the emoji. + * @param is_animated is emoji animated. + * @return std::string The formatted mention of the emoji. + */ + std::string DPP_EXPORT emoji_mention(std::string_view name, snowflake id, bool is_animated = false); + + /** + * @brief Create a mentionable role. + * @param id The ID of the role. + * @return std::string The formatted mention of the role. + */ + std::string DPP_EXPORT role_mention(const snowflake& id); + +#ifdef _DOXYGEN_ + /** + * @brief Get the mime type for an image type. + * @param type Image type + * @return std::string The mime type for this image type + */ + std::string DPP_EXPORT mime_type(image_type type); + + /** + * @brief Get the mime type for a sticker format. + * @param format Sticker format + * @return std::string The mime type for this sticker format + */ + std::string DPP_EXPORT mime_type(sticker_format format); + + /** + * @brief Get the file extension for an image type. + * @param type Image type + * @return std::string The file extension (e.g. ".png") for this image type + */ + std::string DPP_EXPORT file_extension(image_type type); + + /** + * @brief Get the file extension for a sticker format. + * @param format Sticker format + * @return std::string The file extension (e.g. ".png") for this sticker format + */ + std::string DPP_EXPORT file_extension(sticker_format format); +#else + /** + * @brief Get the mime type for an image type. + * @param type Image type + * @return std::string The mime type for this image type + */ + template + extern std::enable_if_t, std::string> DPP_EXPORT mime_type(T type); + + /** + * @brief Get the mime type for a sticker format. + * @param format Sticker format + * @return std::string The mime type for this sticker format + */ + template + extern std::enable_if_t, std::string> DPP_EXPORT mime_type(T format); + + /** + * @brief Get the file extension for an image type. + * @param type Image type + * @return std::string The file extension (e.g. ".png") for this image type + */ + template + extern std::enable_if_t, std::string> DPP_EXPORT file_extension(T type); + + /** + * @brief Get the file extension for a sticker format. + * @param format Sticker format + * @return std::string The file extension (e.g. ".png") for this sticker format + */ + template + extern std::enable_if_t, std::string> DPP_EXPORT file_extension(T format); +#endif + + /** + * @brief Returns the library's version string + * + * @return std::string version + */ + std::string DPP_EXPORT version(); + + /** + * @brief Build a URL parameter string e.g. "?a=b&c=d&e=f" from a map of key/value pairs. + * Entries with empty key names or values are omitted. + * + * @param parameters parameters to create a url query string for + * @return std::string A correctly encoded url query string + */ + std::string DPP_EXPORT make_url_parameters(const std::map& parameters); + + /** + * @brief Build a URL parameter string e.g. "?a=b&c=d&e=f" from a map of key/value pairs. + * Entries with empty key names or zero values are omitted. + * + * @param parameters parameters to create a url query string for + * @return std::string A correctly encoded url query string + */ + std::string DPP_EXPORT make_url_parameters(const std::map& parameters); + + /** + * @brief Set the name of the current thread for debugging and statistical reporting + * + * @param name New name to set + */ + void DPP_EXPORT set_thread_name(const std::string& name); + +#ifdef __cpp_concepts // if c++20 + /** + * @brief Concept satisfied if a callable F can be called using the arguments Args, and that its return value is convertible to R. + * + * @tparam F Callable object + * @tparam R Return type to check for convertibility to + * @tparam Args... Arguments to use to resolve the overload + * @return Whether the expression `F(Args...)` is convertible to R + */ + template + concept callable_returns = std::convertible_to, R>; + + /** + * @brief Type trait to check if a callable F can be called using the arguments Args, and that its return value is convertible to R. + * + * @deprecated In C++20 mode, prefer using the concept `callable_returns`. + * @tparam F Callable object + * @tparam R Return type to check for convertibility to + * @tparam Args... Arguments to use to resolve the overload + * @return Whether the expression `F(Args...)` is convertible to R + */ + template + inline constexpr bool callable_returns_v = callable_returns; +#else + /** + * @brief Type trait to check if a callable F can be called using the arguments Args, and that its return value is convertible to R. + * + * @tparam F Callable object + * @tparam R Return type to check for convertibility to + * @tparam Args... Arguments to use to resolve the overload + * @return Whether the expression `F(Args...)` is convertible to R + */ + template + inline constexpr bool callable_returns_v = std::is_convertible_v, R>; +#endif + + /** + * @brief Utility struct that has the same size and alignment as another but does nothing. Useful for ABI compatibility. + * + * @tparam T Type to mimic + */ + template + struct alignas(T) dummy { + /** @brief Array of bytes with a size mimicking T */ + std::array data; + }; + + } // namespace utility +} // namespace dpp diff --git a/include/dpp/version.h b/include/dpp/version.h new file mode 100644 index 0000000..9074dc4 --- /dev/null +++ b/include/dpp/version.h @@ -0,0 +1,32 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once + +#if !defined(DPP_VERSION_LONG) +#define DPP_VERSION_LONG 0x00100025 +#define DPP_VERSION_SHORT 100025 +#define DPP_VERSION_TEXT "D++ 10.0.25 (22-May-2023)" + +#define DPP_VERSION_MAJOR ((DPP_VERSION_LONG & 0x00ff0000) >> 16) +#define DPP_VERSION_MINOR ((DPP_VERSION_LONG & 0x0000ff00) >> 8) +#define DPP_VERSION_PATCH (DPP_VERSION_LONG & 0x000000ff) +#endif diff --git a/include/dpp/voiceregion.h b/include/dpp/voiceregion.h new file mode 100644 index 0000000..dc86aab --- /dev/null +++ b/include/dpp/voiceregion.h @@ -0,0 +1,120 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include +#include + +namespace dpp { + +/** + * @brief Flags related to a voice region + */ +enum voiceregion_flags { + v_optimal = 0x00000001, + v_deprecated = 0x00000010, + v_custom = 0x00000100, + v_vip = 0x00001000 +}; + +/** + * @brief Represents a voice region on discord + */ +class DPP_EXPORT voiceregion : public json_interface { +public: + /** + * @brief Voice server ID + */ + std::string id; + + /** + * @brief Voice server name + */ + std::string name; + + /** + * @brief Flags bitmap + */ + uint8_t flags; + + /** + * @brief Construct a new voiceregion object + */ + voiceregion(); + + /** + * @brief Destroy the voiceregion object + */ + virtual ~voiceregion() = default; + + /** + * @brief Fill object properties from JSON + * + * @param j JSON to fill from + * @return voiceregion& Reference to self + */ + voiceregion& fill_from_json(nlohmann::json* j); + + /** + * @brief Build a json string for this object + * + * @param with_id Add ID to output + * @return std::string JSON string + */ + virtual std::string build_json(bool with_id = false) const; + + /** + * @brief True if is the optimal voice server + * + * @return true if optimal + */ + bool is_optimal() const; + + /** + * @brief True if is a deprecated voice server + * + * @return true if deprecated + */ + bool is_deprecated() const; + + /** + * @brief True if is a custom voice server + * + * @return true if custom + */ + bool is_custom() const; + + /** + * @brief True if is a VIP voice server + * + * @return true if VIP + */ + bool is_vip() const; +}; + +/** + * @brief A group of voice regions + */ +typedef std::unordered_map voiceregion_map; + +} // namespace dpp diff --git a/include/dpp/voicestate.h b/include/dpp/voicestate.h new file mode 100644 index 0000000..4ece887 --- /dev/null +++ b/include/dpp/voicestate.h @@ -0,0 +1,111 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include +#include +#include + +namespace dpp { + +/** + * @brief Bit mask flags relating to voice states + */ +enum voicestate_flags { + vs_deaf = 0b00000001, //!< Deafened by the server + vs_mute = 0b00000010, //!< Muted by the server + vs_self_mute = 0b00000100, //!< Locally Muted + vs_self_deaf = 0b00001000, //!< Locally deafened + vs_self_stream = 0b00010000, //!< Whether this user is streaming using "Go Live" + vs_self_video = 0b00100000, //!< Whether this user's camera is enabled + vs_suppress = 0b01000000 //!< Whether this user's permission to speak is denied +}; + +/** + * @brief Represents the voice state of a user on a guild + * These are stored in the dpp::guild object, and accessible there, + * or via dpp::channel::get_voice_members + */ +class DPP_EXPORT voicestate : public json_interface { +public: + class discord_client* shard; //!< Owning shard + snowflake guild_id; //!< Optional: the guild id this voice state is for + snowflake channel_id; //!< the channel id this user is connected to (may be empty) + snowflake user_id; //!< the user id this voice state is for + std::string session_id; //!< the session id for this voice state + uint8_t flags; //!< Voice state flags (see dpp::voicestate_flags) + time_t request_to_speak; //!< The time at which the user requested to speak, or 0 + + /** + * @brief Construct a new voicestate object + */ + voicestate(); + + /** + * @brief Destroy the voicestate object + */ + virtual ~voicestate() = default; + + /** + * @brief Fill voicestate object from json data + * + * @param j JSON data to fill from + * @return voicestate& Reference to self + */ + voicestate& fill_from_json(nlohmann::json* j); + + /** + * @brief Build json representation of the object + * + * @param with_id Add ID to output + * @return std::string JSON string + */ + virtual std::string build_json(bool with_id = false) const; + + /// Return true if the user is deafened by the server + bool is_deaf() const; + + /// Return true if the user is muted by the server + bool is_mute() const; + + /// Return true if user muted themselves + bool is_self_mute() const; + + /// Return true if user deafened themselves + bool is_self_deaf() const; + + /// Return true if the user is streaming using "Go Live" + bool self_stream() const; + + /// Return true if the user's camera is enabled + bool self_video() const; + + /// Return true if user is suppressed. + /// "HELP HELP I'M BEING SUPPRESSED!" + bool is_suppressed() const; +}; + +/** A container of voicestates */ +typedef std::unordered_map voicestate_map; + +} // namespace dpp diff --git a/include/dpp/webhook.h b/include/dpp/webhook.h new file mode 100644 index 0000000..bb04657 --- /dev/null +++ b/include/dpp/webhook.h @@ -0,0 +1,115 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace dpp { + +/** + * @brief Defines types of webhook + */ +enum webhook_type { + w_incoming = 1, //!< Incoming webhook + w_channel_follower = 2 //!< Channel following webhook +}; + +/** + * @brief Represents a discord webhook + */ +class DPP_EXPORT webhook : public managed, public json_interface { +public: + uint8_t type; //!< the type of the webhook + snowflake guild_id; //!< Optional: the guild id this webhook is for + snowflake channel_id; //!< the channel id this webhook is for + snowflake user_id; //!< Optional: the user this webhook was created by (not returned when getting a webhook with its token) + std::string name; //!< the default name of the webhook (may be empty) + std::string avatar; //!< the default avatar of the webhook (may be empty) + std::string token; //!< Optional: the secure token of the webhook (returned for Incoming Webhooks) + snowflake application_id; //!< the bot/OAuth2 application that created this webhook (may be empty) + std::string* image_data; //!< base64 encoded image data if uploading a new image + + /** + * @brief Construct a new webhook object + */ + webhook(); + + /** + * @brief Construct a new webhook object using the Webhook URL provided by Discord + * + * @param webhook_url a fully qualified web address of an existing webhook + * @throw logic_exception if the webhook url could not be parsed + */ + webhook(const std::string& webhook_url); + + /** + * @brief Construct a new webhook object using the webhook ID and the webhook token + * + * @param webhook_id id taken from a link of an existing webhook + * @param webhook_token token taken from a link of an existing webhook + */ + webhook(const snowflake webhook_id, const std::string& webhook_token); + + /** + * @brief Destroy the webhook object + */ + ~webhook(); + + /** + * @brief Fill in object from json data + * + * @param j JSON data + * @return webhook& Reference to self + */ + webhook& fill_from_json(nlohmann::json* j); + + /** + * @brief Build JSON string from object + * + * @param with_id Include the ID of the webhook in the json + * @return std::string JSON encoded object + */ + virtual std::string build_json(bool with_id = false) const; + + /** + * @brief Base64 encode image data and allocate it to image_data + * + * @param image_blob Binary image data + * @param type Image type. It can be one of `i_gif`, `i_jpg` or `i_png`. + * @param is_base64_encoded True if the image data is already base64 encoded + * @return webhook& Reference to self + * @throw dpp::length_exception Image data is larger than the maximum size of 256 kilobytes + */ + webhook& load_image(const std::string &image_blob, const image_type type, bool is_base64_encoded = false); +}; + +/** + * @brief A group of webhooks + */ +typedef std::unordered_map webhook_map; + +} // namespace dpp diff --git a/include/dpp/win32_safe_warnings.h b/include/dpp/win32_safe_warnings.h new file mode 100644 index 0000000..b824d06 --- /dev/null +++ b/include/dpp/win32_safe_warnings.h @@ -0,0 +1,33 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once + +/* This file contains pragmas to disable warnings on win32 builds with msvc only. + * It is only included during build of D++ itself, and not when including the headers + * into a user's project. + * + * Before adding a warning here please be ABSOLUTELY SURE it is one we cannot easily fix + * and is to be silenced, thrown into the sarlacc pit to be eaten for 1000 years... + */ + +_Pragma("warning( disable : 4251 )"); // 4251 warns when we export classes or structures with stl member variables +_Pragma("warning( disable : 5105 )"); // 5105 is to do with macro warnings diff --git a/include/dpp/wsclient.h b/include/dpp/wsclient.h new file mode 100644 index 0000000..c7cb3b1 --- /dev/null +++ b/include/dpp/wsclient.h @@ -0,0 +1,213 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace dpp { + +/** + * @brief Websocket protocol types available on Discord + */ +enum websocket_protocol_t : uint8_t { + /** + * @brief JSON data, text, UTF-8 character set + */ + ws_json = 0, + /** + * @brief Erlang Term Format (ETF) binary protocol + */ + ws_etf = 1 +}; + +/** + * @brief Websocket connection status + */ +enum ws_state : uint8_t { + /** + * @brief Sending/receiving HTTP headers, acting as a standard HTTP connection. + * This is the state prior to receiving "HTTP/1.1 101 Switching Protocols" from the + * server side. + */ + HTTP_HEADERS, + + /** + * @brief Connected as a websocket, and "upgraded". Now talking using binary frames. + */ + CONNECTED +}; + +/** + * @brief Low-level websocket opcodes for frames + */ +enum ws_opcode : uint8_t +{ + OP_CONTINUATION = 0x00, //!< Continuation + OP_TEXT = 0x01, //!< Text frame + OP_BINARY = 0x02, //!< Binary frame + OP_CLOSE = 0x08, //!< Close notification with close code + OP_PING = 0x09, //!< Low level ping + OP_PONG = 0x0a //!< Low level pong +}; + +/** + * @brief Implements a websocket client based on the SSL client + */ +class DPP_EXPORT websocket_client : public ssl_client +{ + /** + * @brief Connection key used in the HTTP headers + */ + std::string key; + + /** + * @brief Current websocket state + */ + ws_state state; + + /** + * @brief Path part of URL for websocket + */ + std::string path; + + /** + * @brief Data opcode, represents the type of frames we send + */ + ws_opcode data_opcode; + + /** + * @brief HTTP headers received on connecting/upgrading + */ + std::map http_headers; + + /** + * @brief Parse headers for a websocket frame from the buffer. + * @param buffer The buffer to operate on. Will modify the string removing completed items from the head of the queue + * @return true if a complete header has been received + */ + bool parseheader(std::string &buffer); + + /** + * @brief Unpack a frame and pass completed frames up the stack. + * @param buffer The buffer to operate on. Gets modified to remove completed frames on the head of the buffer + * @param offset The offset to start at (reserved for future use) + * @param first True if is the first element (reserved for future use) + * @return true if a complete frame has been received + */ + bool unpack(std::string &buffer, uint32_t offset, bool first = true); + + /** + * @brief Fill a header for outbound messages + * @param outbuf The raw frame to fill + * @param sendlength The size of the data to encapsulate + * @param opcode the ws_opcode to send in the header + * @return size of filled header + */ + size_t fill_header(unsigned char* outbuf, size_t sendlength, ws_opcode opcode); + + /** + * @brief Handle ping and pong requests. + * @param ping True if this is a ping, false if it is a pong + * @param payload The ping payload, to be returned as-is for a ping + */ + void handle_ping_pong(bool ping, const std::string &payload); + +protected: + + /** + * @brief (Re)connect + */ + virtual void connect(); + + /** + * @brief Get websocket state + * @return websocket state + */ + ws_state get_state(); + +public: + + /** + * @brief Connect to a specific websocket server. + * @param hostname Hostname to connect to + * @param port Port to connect to + * @param urlpath The URL path components of the HTTP request to send + * @param opcode The encoding type to use, either OP_BINARY or OP_TEXT + * @note Voice websockets only support OP_TEXT, and other websockets must be + * OP_BINARY if you are going to send ETF. + */ + websocket_client(const std::string &hostname, const std::string &port = "443", const std::string &urlpath = "", ws_opcode opcode = OP_BINARY); + + /** + * @brief Destroy the websocket client object + */ + virtual ~websocket_client() = default; + + /** + * @brief Write to websocket. Encapsulates data in frames if the status is CONNECTED. + * @param data The data to send. + */ + virtual void write(const std::string &data); + + /** + * @brief Processes incoming frames from the SSL socket input buffer. + * @param buffer The buffer contents. Can modify this value removing the head elements when processed. + */ + virtual bool handle_buffer(std::string &buffer); + + /** + * @brief Close websocket + */ + virtual void close(); + + /** + * @brief Receives raw frame content only without headers + * + * @param buffer The buffer contents + * @return True if the frame was successfully handled. False if no valid frame is in the buffer. + */ + virtual bool handle_frame(const std::string &buffer); + + /** + * @brief Called upon error frame. + * + * @param errorcode The error code from the websocket server + */ + virtual void error(uint32_t errorcode); + + /** + * @brief Fires every second from the underlying socket I/O loop, used for sending websocket pings + */ + virtual void one_second_timer(); + + /** + * @brief Send OP_CLOSE error code 1000 to the other side of the connection. + * This indicates graceful close. + */ + void send_close_packet(); +}; + +} // namespace dpp diff --git a/lib/dpp.lib b/lib/dpp.lib new file mode 100644 index 0000000..6c33ef1 Binary files /dev/null and b/lib/dpp.lib differ diff --git a/resources/harshie.ico b/resources/harshie.ico new file mode 100644 index 0000000..bd3c785 Binary files /dev/null and b/resources/harshie.ico differ diff --git a/resources/harshie.rc.in b/resources/harshie.rc.in new file mode 100644 index 0000000..162e07c --- /dev/null +++ b/resources/harshie.rc.in @@ -0,0 +1,41 @@ +#include +#include "../resources/resource.h" + +IDI_ICON1 ICON "../resources/harshie.ico" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION @HARSHIE_VERSION_MAJOR@,@HARSHIE_VERSION_MINOR@,@HARSHIE_VERSION_PATCH@,@HARSHIE_VERSION_BUILD@ + PRODUCTVERSION @HARSHIE_VERSION_MAJOR@,@HARSHIE_VERSION_MINOR@,@HARSHIE_VERSION_PATCH@,@HARSHIE_VERSION_BUILD@ + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + + FILEOS VOS_NT_WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE VFT2_UNKNOWN + +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Harshfeudal" + VALUE "FileDescription", "Harshie Discord C++ Moderation Bot" + VALUE "FileVersion", "@HARSHIE_VERSION@" + VALUE "ProductVersion", "@HARSHIE_VERSION@" + VALUE "ProductName", "Harshie C++ Discord Moderation Bot" + VALUE "InternalName", "Harshfeudal" + VALUE "LegalCopyright", "Copyright (C) 2023 Harshfeudal - All rights reserved." + VALUE "OriginalFilename", "Harshie.exe" + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/resources/resource.h b/resources/resource.h new file mode 100644 index 0000000..be1a9ea --- /dev/null +++ b/resources/resource.h @@ -0,0 +1,10 @@ +#define IDI_ICON1 101 + +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/start.sh b/start-linux.sh old mode 100755 new mode 100644 similarity index 100% rename from start.sh rename to start-linux.sh diff --git a/start-windows.bat b/start-windows.bat new file mode 100644 index 0000000..798bc90 --- /dev/null +++ b/start-windows.bat @@ -0,0 +1,10 @@ +@echo off + +if not exist build mkdir build +cd build + +cmake -G "Visual Studio 17 2022" -A x64 .. +cmake --build . --config Release -j8 + +cd Release +.\Harshie.exe