From 449aefc6bcf4af46fdc2fe565736245776096bd0 Mon Sep 17 00:00:00 2001 From: nerix Date: Wed, 5 Feb 2025 16:53:25 +0100 Subject: [PATCH] refactor(eventsub): generate entire files (#5897) --- .github/workflows/build.yml | 19 ++ CHANGELOG.md | 2 +- lib/twitch-eventsub-ws/CMakeLists.txt | 7 +- lib/twitch-eventsub-ws/ast/README.md | 17 +- lib/twitch-eventsub-ws/ast/check-clang.hpp | 12 + lib/twitch-eventsub-ws/ast/check-clang.py | 20 ++ .../ast/generate-and-replace-dir.py | 62 ---- lib/twitch-eventsub-ws/ast/generate.py | 88 ++++- lib/twitch-eventsub-ws/ast/lib/__init__.py | 8 +- lib/twitch-eventsub-ws/ast/lib/build.py | 40 +-- lib/twitch-eventsub-ws/ast/lib/enum.py | 3 +- lib/twitch-eventsub-ws/ast/lib/generate.py | 41 ++- lib/twitch-eventsub-ws/ast/lib/logging.py | 6 +- lib/twitch-eventsub-ws/ast/lib/member.py | 4 +- lib/twitch-eventsub-ws/ast/lib/parse.py | 45 +++ lib/twitch-eventsub-ws/ast/lib/replace.py | 41 --- lib/twitch-eventsub-ws/ast/lib/struct.py | 3 +- lib/twitch-eventsub-ws/ast/lib/walker.py | 14 +- lib/twitch-eventsub-ws/ast/replace-in-file.py | 42 --- .../cmake/GenerateJson.cmake | 218 ++++++++++--- .../twitch-eventsub-ws/messages/metadata.hpp | 7 +- .../twitch-eventsub-ws/messages/metadata.inc | 2 + .../payloads/channel-ban-v1.hpp | 8 +- .../payloads/channel-ban-v1.inc | 5 + .../payloads/channel-chat-message-v1.hpp | 33 +- .../payloads/channel-chat-message-v1.inc | 30 ++ .../payloads/channel-chat-notification-v1.hpp | 77 +---- .../payloads/channel-chat-notification-v1.inc | 74 +++++ .../payloads/channel-moderate-v2.hpp | 61 +--- .../payloads/channel-moderate-v2.inc | 58 ++++ .../payloads/channel-update-v1.hpp | 8 +- .../payloads/channel-update-v1.inc | 5 + .../payloads/session-welcome.hpp | 7 +- .../payloads/session-welcome.inc | 2 + .../payloads/stream-offline-v1.hpp | 8 +- .../payloads/stream-offline-v1.inc | 5 + .../payloads/stream-online-v1.hpp | 8 +- .../payloads/stream-online-v1.inc | 5 + .../payloads/subscription.hpp | 9 +- .../payloads/subscription.inc | 6 + lib/twitch-eventsub-ws/src/CMakeLists.txt | 38 +-- .../src/{ => generated}/messages/metadata.cpp | 6 +- .../src/generated/payloads/channel-ban-v1.cpp | 306 ++++++++++++++++++ .../payloads/channel-chat-message-v1.cpp | 6 +- .../payloads/channel-chat-notification-v1.cpp | 6 +- .../payloads/channel-moderate-v2.cpp | 8 +- .../payloads/channel-update-v1.cpp | 6 +- .../payloads/session-welcome.cpp | 6 +- .../payloads/stream-offline-v1.cpp | 6 +- .../payloads/stream-online-v1.cpp | 6 +- .../{ => generated}/payloads/subscription.cpp | 6 +- .../src/payloads/channel-ban-v1.cpp | 302 ----------------- .../src/payloads/new-payload.sh | 81 ----- lib/twitch-eventsub-ws/src/session.cpp | 1 + 54 files changed, 972 insertions(+), 922 deletions(-) create mode 100644 lib/twitch-eventsub-ws/ast/check-clang.hpp create mode 100644 lib/twitch-eventsub-ws/ast/check-clang.py delete mode 100755 lib/twitch-eventsub-ws/ast/generate-and-replace-dir.py create mode 100644 lib/twitch-eventsub-ws/ast/lib/parse.py delete mode 100644 lib/twitch-eventsub-ws/ast/lib/replace.py delete mode 100755 lib/twitch-eventsub-ws/ast/replace-in-file.py create mode 100644 lib/twitch-eventsub-ws/include/twitch-eventsub-ws/messages/metadata.inc create mode 100644 lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-ban-v1.inc create mode 100644 lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-chat-message-v1.inc create mode 100644 lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-chat-notification-v1.inc create mode 100644 lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-moderate-v2.inc create mode 100644 lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-update-v1.inc create mode 100644 lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/session-welcome.inc create mode 100644 lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/stream-offline-v1.inc create mode 100644 lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/stream-online-v1.inc create mode 100644 lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/subscription.inc rename lib/twitch-eventsub-ws/src/{ => generated}/messages/metadata.cpp (96%) create mode 100644 lib/twitch-eventsub-ws/src/generated/payloads/channel-ban-v1.cpp rename lib/twitch-eventsub-ws/src/{ => generated}/payloads/channel-chat-message-v1.cpp (99%) rename lib/twitch-eventsub-ws/src/{ => generated}/payloads/channel-chat-notification-v1.cpp (99%) rename lib/twitch-eventsub-ws/src/{ => generated}/payloads/channel-moderate-v2.cpp (99%) rename lib/twitch-eventsub-ws/src/{ => generated}/payloads/channel-update-v1.cpp (98%) rename lib/twitch-eventsub-ws/src/{ => generated}/payloads/session-welcome.cpp (92%) rename lib/twitch-eventsub-ws/src/{ => generated}/payloads/stream-offline-v1.cpp (96%) rename lib/twitch-eventsub-ws/src/{ => generated}/payloads/stream-online-v1.cpp (97%) rename lib/twitch-eventsub-ws/src/{ => generated}/payloads/subscription.cpp (97%) delete mode 100755 lib/twitch-eventsub-ws/src/payloads/new-payload.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 38b0531b..a18308c5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -71,6 +71,16 @@ jobs: run: | git config --global --add safe.directory '*' + - name: Install Python and libclang (24.04) + if: matrix.os == 'ubuntu-24.04' + run: | + sudo apt update + sudo DEBIAN_FRONTEND=noninteractive apt -y --no-install-recommends install \ + python3 python3-venv clang-18 clang-format-18 libclang-18-dev + echo "LIBCLANG_LIBRARY_FILE=/usr/lib/x86_64-linux-gnu/libclang-18.so" >> "$GITHUB_ENV" + sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-18 42 + sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-18 42 + - name: Build run: | mkdir build @@ -85,9 +95,16 @@ jobs: -DCHATTERINO_PLUGINS="$C2_PLUGINS" \ -DCMAKE_PREFIX_PATH="$Qt6_DIR/lib/cmake" \ -DCHATTERINO_STATIC_QT_BUILD=On \ + -DFORCE_JSON_GENERATION=${{matrix.os == 'ubuntu-24.04' && 'On' || 'Off'}} \ .. make -j"$(nproc)" + - name: Check generated sources + if: matrix.os == 'ubuntu-24.04' + run: | + git add -N lib/twitch-eventsub-ws/include lib/twitch-eventsub-ws/src + git --no-pager diff --exit-code lib/twitch-eventsub-ws/include lib/twitch-eventsub-ws/src + - name: Package - AppImage (Ubuntu) if: matrix.build-appimage run: | @@ -239,6 +256,7 @@ jobs: -DCHATTERINO_LTO="$Env:C2_ENABLE_LTO" ` -DCHATTERINO_PLUGINS="$Env:C2_PLUGINS" ` -DBUILD_WITH_QT6="$Env:C2_BUILD_WITH_QT6" ` + -DFORCE_JSON_GENERATION=On ` .. set cl=/MP nmake /S /NOLOGO @@ -308,6 +326,7 @@ jobs: -DCHATTERINO_LTO="$C2_ENABLE_LTO" \ -DCHATTERINO_PLUGINS="$C2_PLUGINS" \ -DBUILD_WITH_QT6="$C2_BUILD_WITH_QT6" \ + -DFORCE_JSON_GENERATION=Off \ .. make -j"$(sysctl -n hw.logicalcpu)" shell: bash diff --git a/CHANGELOG.md b/CHANGELOG.md index 1616e804..cd8d2131 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,7 @@ - Bugfix: Fixed the reply button showing for inline whispers and announcements. (#5863) - Bugfix: Fixed suspicious user treatment update messages not being searchable. (#5865) - Bugfix: Ensure miniaudio backend exits even if it doesn't exit cleanly. (#5896) -- Dev: Add initial experimental EventSub support. (#5837, #5895) +- Dev: Add initial experimental EventSub support. (#5837, #5895, #5897) - Dev: Highlight checks now use non-capturing groups for the boundaries. (#5784) - Dev: Removed unused PubSub whisper code. (#5898) - Dev: Updated Conan dependencies. (#5776) diff --git a/lib/twitch-eventsub-ws/CMakeLists.txt b/lib/twitch-eventsub-ws/CMakeLists.txt index 585c3a22..b82d3dd2 100644 --- a/lib/twitch-eventsub-ws/CMakeLists.txt +++ b/lib/twitch-eventsub-ws/CMakeLists.txt @@ -16,20 +16,21 @@ find_package(Boost REQUIRED OPTIONAL_COMPONENTS json) find_package(OpenSSL REQUIRED) option(BUILD_WITH_QT6 "Build with Qt6" On) +option(FORCE_JSON_GENERATION "Make sure JSON implementations are generated at build time" Off) + if (BUILD_WITH_QT6) set(MAJOR_QT_VERSION "6") else() set(MAJOR_QT_VERSION "5") endif() find_package(Qt${MAJOR_QT_VERSION} REQUIRED COMPONENTS Core) +find_package(Python3 COMPONENTS Interpreter) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) -add_subdirectory(src) - include(cmake/GenerateJson.cmake) -generate_json_impls(SRC_TARGET twitch-eventsub-ws GEN_TARGET twitch-eventsub-ws-gen) +add_subdirectory(src) feature_summary(WHAT ALL) diff --git a/lib/twitch-eventsub-ws/ast/README.md b/lib/twitch-eventsub-ws/ast/README.md index e381cdde..85ecc3d8 100644 --- a/lib/twitch-eventsub-ws/ast/README.md +++ b/lib/twitch-eventsub-ws/ast/README.md @@ -1,21 +1,12 @@ ## Available scripts -- `generate-and-replace-dir.py` - Usage: `./generate-and-replace-dir.py ` - Will find all header files in that directory (it won't search recursively), and if they also have a matching source file, generate json deserialize definition & implementations for them, and replacing the markers in the given file. - -- `get-builtin-include-dirs.py` - Usage: `./get-builtin-include-dirs.py` +- `get-builtin-include-dirs.py` + Usage: `./get-builtin-include-dirs.py` Prints what builtin include dirs will be used for any of the other scripts. - `generate.py` - Usage: `./generate.py ` - Generates definitions & implementations for the given header file and write them to temporary files. - -- `replace-in-file.py` - Usage: `./replace-in-file.py ` - Reads sdin for two file paths containing the definition & implementations. - Can be used in tandem with `generate.py`, but realistically it's only there because I used it at some point. + Usage: `./generate.py [--includes INCLUDES] [--timestamp path] header_path` + Generates definitions & implementations for the given header file and writes them to the appropriate files. ## Environment variables diff --git a/lib/twitch-eventsub-ws/ast/check-clang.hpp b/lib/twitch-eventsub-ws/ast/check-clang.hpp new file mode 100644 index 00000000..c7870639 --- /dev/null +++ b/lib/twitch-eventsub-ws/ast/check-clang.hpp @@ -0,0 +1,12 @@ +// This is a header used for testing if the library can parse a file. + +#include +#include + +#include + +boost::json::value doSomething(QString s, + std::chrono::system_clock::time_point tp); + +static_assert(sizeof(QString) > sizeof(int)); +static_assert(sizeof(boost::json::value) > sizeof(int)); diff --git a/lib/twitch-eventsub-ws/ast/check-clang.py b/lib/twitch-eventsub-ws/ast/check-clang.py new file mode 100644 index 00000000..5f5c5c50 --- /dev/null +++ b/lib/twitch-eventsub-ws/ast/check-clang.py @@ -0,0 +1,20 @@ +import clang.cindex +import sys +from pathlib import Path + +from lib import init_clang_cindex, parse + +init_clang_cindex() + +assert len(sys.argv) == 2 +additional_includes = sys.argv[1].split(";") +print("includes:", additional_includes) + +tu = parse(Path(__file__).parent / "check-clang.hpp", additional_includes) + +if len(tu.diagnostics) > 0: + for diag in tu.diagnostics: + print(diag.location) + print(diag.spelling) + print(diag.option) + assert False, "TU had warnings/errors when parsing" diff --git a/lib/twitch-eventsub-ws/ast/generate-and-replace-dir.py b/lib/twitch-eventsub-ws/ast/generate-and-replace-dir.py deleted file mode 100755 index 36d9d976..00000000 --- a/lib/twitch-eventsub-ws/ast/generate-and-replace-dir.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python3 - -import logging -import os -import re -import sys -from os.path import realpath - -from lib import ( - definition_markers, - generate, - implementation_markers, - init_clang_cindex, - init_logging, - replace_in_file, -) - -log = logging.getLogger("generate-and-replace-dir") - - -def main(): - init_logging() - - init_clang_cindex() - - if len(sys.argv) < 2: - log.error(f"usage: {sys.argv[0]} ") - sys.exit(1) - - additional_includes = [] - if len(sys.argv) >= 4 and sys.argv[2] == "--includes": - additional_includes = sys.argv[3].split(";") - log.debug(f"additional includes: {additional_includes}") - - dir = realpath(sys.argv[1]) - log.debug(f"Searching for header files in {dir}") - - for path in os.scandir(dir): - if not path.is_file(): - continue - - if not path.name.endswith(".hpp"): - continue - - header_path = path.path - source_path = re.sub(r"\.hpp$", ".cpp", header_path) - source_path = re.sub(r"include[/\\]twitch-eventsub-ws[/\\]", "src/", source_path) - - if not os.path.isfile(source_path): - log.warning(f"Header file {header_path} did not have a matching source file at {source_path}") - continue - - log.debug(f"Found header & source {header_path} / {source_path}") - - (definition, implementation) = generate(header_path, additional_includes) - - replace_in_file(header_path, definition_markers, definition) - replace_in_file(source_path, implementation_markers, implementation) - - -if __name__ == "__main__": - main() diff --git a/lib/twitch-eventsub-ws/ast/generate.py b/lib/twitch-eventsub-ws/ast/generate.py index 5caa089a..680d407a 100755 --- a/lib/twitch-eventsub-ws/ast/generate.py +++ b/lib/twitch-eventsub-ws/ast/generate.py @@ -1,35 +1,95 @@ #!/usr/bin/env python3 import logging +import os +import re import sys +import argparse +from datetime import datetime +from pathlib import Path -from lib import generate, init_clang_cindex, init_logging, temporary_file +from lib import ( + generate, + init_clang_cindex, + init_logging, +) log = logging.getLogger("generate") +def _logging_level(): + def to_bool(v: str | None) -> bool: + return bool(v) and v.lower() not in ("false", "0", "off", "no") + + return ( + logging.DEBUG + if to_bool(os.environ.get("CI")) or to_bool(os.environ.get("DEBUG_EVENTSUB_GEN")) + else logging.WARNING + ) + + +def _validate_header_path(value: str) -> str: + if not value.endswith(".hpp"): + raise argparse.ArgumentTypeError("Header path must end with '.hpp'") + if not os.path.isfile(value): + raise argparse.ArgumentTypeError(f"File '{value}' does not exist") + return value + + def main(): - init_logging() + init_logging(_logging_level()) init_clang_cindex() if len(sys.argv) < 2: - log.error(f"Missing header file argument. Usage: {sys.argv[0]} ") + log.error(f"usage: {sys.argv[0]} ") sys.exit(1) - (definitions, implementations) = generate(sys.argv[1]) + parser = argparse.ArgumentParser() + parser.add_argument( + "header_path", type=_validate_header_path, help="Path to an existing header file ending with .hpp" + ) + parser.add_argument( + "--includes", + type=lambda value: value.split(";") if value else [], + default=[], + help="Semicolon-separated list of include paths", + ) + parser.add_argument("--timestamp", metavar="path", help="Path to the timestamp file to be generated") - with temporary_file() as (f, path): - log.debug(f"Write definitions to {path}") - f.write(definitions) - f.write("\n") - print(path) + args = parser.parse_args() - with temporary_file() as (f, path): - log.debug(f"Write implementations to {path}") - f.write(implementations) - f.write("\n") - print(path) + path = Path(args.header_path).resolve() + + if not path.is_file(): + log.error(f"{path} does not exist") + sys.exit(1) + + if not path.name.endswith(".hpp"): + log.error(f"{path} is not a header file") + + header_path = str(path) + def_path = re.sub(r"\.hpp$", ".inc", header_path) + source_path = re.sub(r"\.hpp$", ".cpp", header_path) + source_path = re.sub(r"include[/\\]twitch-eventsub-ws[/\\]", "src/generated/", source_path) + + log.debug(f"{header_path}: definition={def_path}, implementation={source_path}") + + (definition, implementation) = generate(header_path, args.includes) + + # ensure directories are created + Path(def_path).parent.mkdir(parents=True, exist_ok=True) + Path(source_path).parent.mkdir(parents=True, exist_ok=True) + + with open(def_path, "w") as f: + f.write(definition) + with open(source_path, "w") as f: + f.write(implementation) + + if args.timestamp: + path = Path(args.timestamp) + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(str(datetime.now().timestamp())) if __name__ == "__main__": diff --git a/lib/twitch-eventsub-ws/ast/lib/__init__.py b/lib/twitch-eventsub-ws/ast/lib/__init__.py index 27f95dcb..97dbfc3f 100644 --- a/lib/twitch-eventsub-ws/ast/lib/__init__.py +++ b/lib/twitch-eventsub-ws/ast/lib/__init__.py @@ -1,15 +1,13 @@ from .generate import generate from .helpers import get_clang_builtin_include_dirs, init_clang_cindex, temporary_file from .logging import init_logging -from .replace import definition_markers, implementation_markers, replace_in_file +from .parse import parse __all__ = [ "generate", "get_clang_builtin_include_dirs", "init_clang_cindex", - "temporary_file", "init_logging", - "definition_markers", - "implementation_markers", - "replace_in_file", + "parse", + "temporary_file", ] diff --git a/lib/twitch-eventsub-ws/ast/lib/build.py b/lib/twitch-eventsub-ws/ast/lib/build.py index eda39ea1..6b6d3428 100644 --- a/lib/twitch-eventsub-ws/ast/lib/build.py +++ b/lib/twitch-eventsub-ws/ast/lib/build.py @@ -9,50 +9,14 @@ from .helpers import get_clang_builtin_include_dirs from .struct import Struct from .enum import Enum from .walker import Walker +from .parse import parse log = logging.getLogger(__name__) def build_structs(filename: str, additional_includes: list[str] = []) -> List[Struct | Enum]: - if not os.path.isfile(filename): - raise ValueError(f"Path {filename} is not a file. cwd: {os.getcwd()}") - - parse_args = [ - "-std=c++17", - # Uncomment this if you need to debug where it tries to find headers - # "-H", - ] - - parse_options = ( - clang.cindex.TranslationUnit.PARSE_INCOMPLETE | clang.cindex.TranslationUnit.PARSE_SKIP_FUNCTION_BODIES - ) - - extra_includes, system_includes = get_clang_builtin_include_dirs() - - for include_dir in system_includes: - parse_args.append(f"-isystem{include_dir}") - - for include_dir in extra_includes: - parse_args.append(f"-I{include_dir}") - - for dir in additional_includes: - parse_args.append("-I") - parse_args.append(dir) - - # Append default dirs - # - Append dir of file - file_dir = os.path.dirname(os.path.realpath(filename)) - parse_args.append(f"-I{file_dir}") - # - Append project include dir - file_subdir = os.path.realpath(os.path.join(os.path.dirname(os.path.realpath(filename)), "../..")) - parse_args.append(f"-I{file_subdir}") - - # TODO: Use build_commands if available - - index = clang.cindex.Index.create() - log.debug("Parsing translation unit") - tu = index.parse(filename, args=parse_args, options=parse_options) + tu = parse(filename, additional_includes) root = tu.cursor for diag in tu.diagnostics: diff --git a/lib/twitch-eventsub-ws/ast/lib/enum.py b/lib/twitch-eventsub-ws/ast/lib/enum.py index 4b248579..9f017d88 100644 --- a/lib/twitch-eventsub-ws/ast/lib/enum.py +++ b/lib/twitch-eventsub-ws/ast/lib/enum.py @@ -13,12 +13,13 @@ log = logging.getLogger(__name__) class Enum: - def __init__(self, name: str) -> None: + def __init__(self, name: str, namespace: tuple[str, ...]) -> None: self.name = name self.constants: List[EnumConstant] = [] self.parent: str = "" self.comment_commands: CommentCommands = [] self.inner_root: str = "" + self.namespace = namespace @property def full_name(self) -> str: diff --git a/lib/twitch-eventsub-ws/ast/lib/generate.py b/lib/twitch-eventsub-ws/ast/lib/generate.py index c5a291d2..b4389b51 100644 --- a/lib/twitch-eventsub-ws/ast/lib/generate.py +++ b/lib/twitch-eventsub-ws/ast/lib/generate.py @@ -1,22 +1,57 @@ from typing import Tuple import logging +from pathlib import Path +from .struct import Struct +from .enum import Enum from .build import build_structs from .format import format_code -from .helpers import init_clang_cindex, temporary_file from .jinja_env import env -from .logging import init_logging log = logging.getLogger(__name__) +def _generate_implementation(header_path: str, items: list[Struct | Enum]) -> str: + current_ns = "" + doc = f""" +// WARNING: This file is automatically generated. Any changes will be lost. +#include "{header_path}" +#include "twitch-eventsub-ws/chrono.hpp" // IWYU pragma: keep +#include "twitch-eventsub-ws/errors.hpp" +#include + +""" + + for item in items: + next_ns = "::".join(item.namespace) + if next_ns != current_ns: + if current_ns: + doc += "} // namespace " + current_ns + "\n" + if next_ns: + doc += "namespace " + next_ns + "{\n\n" + current_ns = next_ns + doc += item.try_value_to_implementation(env) + "\n\n" + + if current_ns: + doc += "\n} // namespace " + current_ns + "\n\n" + + return doc + + +def _get_relative_header_path(header_path: str) -> str: + root_path = Path(__file__).parent.parent.parent / "include" + return Path(header_path).relative_to(root_path).as_posix() + + def generate(header_path: str, additional_includes: list[str] = []) -> Tuple[str, str]: structs = build_structs(header_path, additional_includes) + rel_header_path = _get_relative_header_path(header_path) + log.debug("Generate & format definitions") definitions = format_code("\n\n".join([struct.try_value_to_definition(env) for struct in structs])) log.debug("Generate & format implementations") - implementations = format_code("\n\n".join([struct.try_value_to_implementation(env) for struct in structs])) + implementations = format_code(_generate_implementation(rel_header_path, structs)) return (definitions, implementations) diff --git a/lib/twitch-eventsub-ws/ast/lib/logging.py b/lib/twitch-eventsub-ws/ast/lib/logging.py index 338d168d..9e2d74da 100644 --- a/lib/twitch-eventsub-ws/ast/lib/logging.py +++ b/lib/twitch-eventsub-ws/ast/lib/logging.py @@ -4,12 +4,12 @@ import sys from colorama import Fore, Style -def init_logging() -> None: +def init_logging(level=logging.DEBUG) -> None: root = logging.getLogger() - root.setLevel(logging.DEBUG) + root.setLevel(level) handler = logging.StreamHandler(sys.stderr) - handler.setLevel(logging.DEBUG) + handler.setLevel(level) colors = { "WARNING": Fore.YELLOW, diff --git a/lib/twitch-eventsub-ws/ast/lib/member.py b/lib/twitch-eventsub-ws/ast/lib/member.py index 065cc057..8a926a5d 100644 --- a/lib/twitch-eventsub-ws/ast/lib/member.py +++ b/lib/twitch-eventsub-ws/ast/lib/member.py @@ -13,7 +13,7 @@ from .membertype import MemberType log = logging.getLogger(__name__) -def get_type_name(type: clang.cindex.Type, namespace: List[str]) -> str: +def get_type_name(type: clang.cindex.Type, namespace: tuple[str, ...]) -> str: if namespace: namespace_str = f"{'::'.join(namespace)}::" else: @@ -68,7 +68,7 @@ class Member: log.warning(f"Unknown comment command found: {other} with value {value}") @staticmethod - def from_field(node: clang.cindex.Cursor, namespace: List[str]) -> Member: + def from_field(node: clang.cindex.Cursor, namespace: tuple[str, ...]) -> Member: assert node.type is not None name = node.spelling diff --git a/lib/twitch-eventsub-ws/ast/lib/parse.py b/lib/twitch-eventsub-ws/ast/lib/parse.py new file mode 100644 index 00000000..0134b161 --- /dev/null +++ b/lib/twitch-eventsub-ws/ast/lib/parse.py @@ -0,0 +1,45 @@ +import os + +import clang.cindex +from .helpers import get_clang_builtin_include_dirs + + +def parse(filename: str, additional_includes: list[str] = []) -> clang.cindex.TranslationUnit: + if not os.path.isfile(filename): + raise ValueError(f"Path {filename} is not a file. cwd: {os.getcwd()}") + + parse_args = [ + "-std=c++17", + # Uncomment this if you need to debug where it tries to find headers + # "-H", + ] + + parse_options = ( + clang.cindex.TranslationUnit.PARSE_INCOMPLETE | clang.cindex.TranslationUnit.PARSE_SKIP_FUNCTION_BODIES + ) + + extra_includes, system_includes = get_clang_builtin_include_dirs() + + for include_dir in system_includes: + parse_args.append(f"-isystem{include_dir}") + + for include_dir in extra_includes: + parse_args.append(f"-I{include_dir}") + + for dir in additional_includes: + parse_args.append("-I") + parse_args.append(dir) + + # Append default dirs + # - Append dir of file + file_dir = os.path.dirname(os.path.realpath(filename)) + parse_args.append(f"-I{file_dir}") + # - Append project include dir + file_subdir = os.path.realpath(os.path.join(os.path.dirname(os.path.realpath(filename)), "../..")) + parse_args.append(f"-I{file_subdir}") + + # TODO: Use build_commands if available + + index = clang.cindex.Index.create() + + return index.parse(filename, args=parse_args, options=parse_options) diff --git a/lib/twitch-eventsub-ws/ast/lib/replace.py b/lib/twitch-eventsub-ws/ast/lib/replace.py deleted file mode 100644 index 9b02a5af..00000000 --- a/lib/twitch-eventsub-ws/ast/lib/replace.py +++ /dev/null @@ -1,41 +0,0 @@ -from typing import Tuple - -import logging - -log = logging.getLogger(__name__) - -definition_markers: Tuple[str, str] = ( - "// DESERIALIZATION DEFINITION START", - "// DESERIALIZATION DEFINITION END", -) -implementation_markers: Tuple[str, str] = ( - "// DESERIALIZATION IMPLEMENTATION START", - "// DESERIALIZATION IMPLEMENTATION END", -) - - -def replace_in_file( - path: str, - markers: Tuple[str, str], - replacement: str, -) -> None: - with open(path, "r+") as fh: - # Read file into a list of strings - lines = [line.rstrip() for line in fh.readlines()] - start = lines.index(markers[0]) - end = lines.index(markers[1], start) - log.debug(f"Markers: {start}-{end}") - - lines[start + 1 : end] = [line.rstrip() for line in replacement.splitlines()] - - # Clear file - fh.truncate(0) - fh.seek(0) - - # Build new file data - data = "\n".join(lines) - # Add a newline at the end of the file - data += "\n" - - # Write data to file - fh.write(data) diff --git a/lib/twitch-eventsub-ws/ast/lib/struct.py b/lib/twitch-eventsub-ws/ast/lib/struct.py index 894384c6..da95c1b3 100644 --- a/lib/twitch-eventsub-ws/ast/lib/struct.py +++ b/lib/twitch-eventsub-ws/ast/lib/struct.py @@ -13,12 +13,13 @@ log = logging.getLogger(__name__) class Struct: - def __init__(self, name: str) -> None: + def __init__(self, name: str, namespace: tuple[str, ...]) -> None: self.name = name self.members: List[Member] = [] self.parent: str = "" self.comment_commands: CommentCommands = [] self.inner_root: str = "" + self.namespace = namespace @property def full_name(self) -> str: diff --git a/lib/twitch-eventsub-ws/ast/lib/walker.py b/lib/twitch-eventsub-ws/ast/lib/walker.py index 3a3b52a5..ee9e9143 100644 --- a/lib/twitch-eventsub-ws/ast/lib/walker.py +++ b/lib/twitch-eventsub-ws/ast/lib/walker.py @@ -21,12 +21,12 @@ class Walker: self.real_filepath = os.path.realpath(self.filename) self.structs: List[Struct] = [] self.enums: List[Enum] = [] - self.namespace: List[str] = [] + self.namespace: tuple[str, ...] = () def handle_node(self, node: clang.cindex.Cursor, struct: Optional[Struct], enum: Optional[Enum]) -> bool: match node.kind: case CursorKind.STRUCT_DECL: - new_struct = Struct(node.spelling) + new_struct = Struct(node.spelling, self.namespace) if node.raw_comment is not None: new_struct.comment_commands = parse_comment_commands(node.raw_comment) new_struct.apply_comment_commands(new_struct.comment_commands) @@ -41,7 +41,7 @@ class Walker: return True case CursorKind.ENUM_DECL: - new_enum = Enum(node.spelling) + new_enum = Enum(node.spelling, self.namespace) if node.raw_comment is not None: new_enum.comment_commands = parse_comment_commands(node.raw_comment) new_enum.apply_comment_commands(new_enum.comment_commands) @@ -77,9 +77,11 @@ class Walker: struct.members.append(member) case CursorKind.NAMESPACE: - self.namespace.append(node.spelling) - # Ignore namespaces - pass + self.namespace += (node.spelling,) + for child in node.get_children(): + self.walk(child) + self.namespace = self.namespace[:-1] + return True case CursorKind.FUNCTION_DECL: # Ignore function declarations diff --git a/lib/twitch-eventsub-ws/ast/replace-in-file.py b/lib/twitch-eventsub-ws/ast/replace-in-file.py deleted file mode 100755 index e2a9259c..00000000 --- a/lib/twitch-eventsub-ws/ast/replace-in-file.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env python3 - -import logging -import sys -from os.path import realpath - -from lib import definition_markers, implementation_markers, init_logging, replace_in_file - -log = logging.getLogger("replace-in-file") - - -def main(): - init_logging() - - if len(sys.argv) < 3: - log.error(f"usage: {sys.argv[0]} ") - sys.exit(1) - - # The files where we will do the replacements - header_path = realpath(sys.argv[1]) - source_path = realpath(sys.argv[2]) - - # The files where we read the generated definitions & implementations from - # TODO: pass these along in argv too? - stdin_lines = sys.stdin.readlines() - generated_definition_path = realpath(stdin_lines[0].strip()) - generated_implementation_path = realpath(stdin_lines[1].strip()) - - log.debug(f"Generated files: {generated_definition_path} / {generated_implementation_path}") - log.debug(f"Real files: {header_path} / {source_path}") - - with open(generated_definition_path, "r") as generated_fh: - replacement = generated_fh.read() - replace_in_file(source_path, definition_markers, replacement) - - with open(generated_implementation_path, "r") as generated_fh: - replacement = generated_fh.read() - replace_in_file(source_path, implementation_markers, replacement) - - -if __name__ == "__main__": - main() diff --git a/lib/twitch-eventsub-ws/cmake/GenerateJson.cmake b/lib/twitch-eventsub-ws/cmake/GenerateJson.cmake index bf6b2078..38880447 100644 --- a/lib/twitch-eventsub-ws/cmake/GenerateJson.cmake +++ b/lib/twitch-eventsub-ws/cmake/GenerateJson.cmake @@ -1,54 +1,190 @@ -function(generate_json_impls) - set(_one_value_opts SRC_TARGET GEN_TARGET) +cmake_path(GET CMAKE_CURRENT_LIST_DIR PARENT_PATH _eventsub_lib_root) + +# _validate_all_args_are_passed( [...]) +function(_validate_all_args_are_parsed prefix) + if(DEFINED ${prefix}_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unknown arguments: (${${prefix}_UNPARSED_ARGUMENTS})") + endif() + + foreach(_name ${ARGN}) + if(NOT DEFINED ${prefix}_${_name}) + message(FATAL_ERROR "Missing required argument ${_name}") + endif() + endforeach() +endfunction() + +function(_make_and_use_venv) cmake_parse_arguments(PARSE_ARGV 0 arg - "" "${_one_value_opts}" "" + "" "VENV_PATH;REQUIREMENTS;OUT_PYTHON_EXE" "" ) - if (DEFINED arg_KEYWORDS_MISSING_VALUES) - message(FATAL_ERROR "generate_json_impls: Missing ${arg_KEYWORDS_MISSING_VALUES}") + _validate_all_args_are_parsed(arg VENV_PATH REQUIREMENTS OUT_PYTHON_EXE) + + if(NOT Python3_EXECUTABLE) + message(FATAL_ERROR "Python must be available to create a venv") endif() set(_venv_path "${CMAKE_CURRENT_BINARY_DIR}/eventsub-venv") - if(WIN32) - set(_venv_python_path "${_venv_path}/Scripts/python.exe") - set(_venv_pip_path "${_venv_path}/Scripts/pip.exe") - else() - set(_venv_python_path "${_venv_path}/bin/python3") - set(_venv_pip_path "${_venv_path}/bin/pip3") - endif() - cmake_path(SET _requirements NORMALIZE "${CMAKE_CURRENT_LIST_DIR}/ast/requirements.txt") - cmake_path(SET _gen_script NORMALIZE "${CMAKE_CURRENT_LIST_DIR}/ast/generate-and-replace-dir.py") - cmake_path(SET _search_dir NORMALIZE "${CMAKE_CURRENT_LIST_DIR}/include/twitch-eventsub-ws") - - get_target_property(_inc_dirs ${arg_SRC_TARGET} INCLUDE_DIRECTORIES) - list(APPEND _inc_dirs ${Boost_INCLUDE_DIRS}) - list(APPEND _inc_dirs ${OPENSSL_INCLUDE_DIR}) - list(APPEND _inc_dirs ${Qt${MAJOR_QT_VERSION}Core_INCLUDE_DIRS}) - list(JOIN _inc_dirs ";" _inc_dir) - - find_package(Python3 COMPONENTS Interpreter) - if(NOT Python3_FOUND) + message(STATUS "Creating venv in ${arg_VENV_PATH}") + execute_process( + COMMAND "${Python3_EXECUTABLE}" -m venv "${arg_VENV_PATH}" + RESULT_VARIABLE _venv_output + ) + if(NOT _venv_output EQUAL 0) return() endif() - add_custom_target(${arg_GEN_TARGET}_python_venv_create - COMMENT "Creating python virtual environment" - COMMAND "${Python3_EXECUTABLE}" -m venv "${_venv_path}" - VERBATIM - ) + # update the environment with VIRTUAL_ENV variable (mimic the activate script) + set(ENV{VIRTUAL_ENV} "${arg_VENV_PATH}") + # change the context of the search + set(Python3_FIND_VIRTUALENV FIRST) + # unset Python3_EXECUTABLE because it is also an input variable + unset(Python3_EXECUTABLE) + unset(Python3_FOUND) + unset(Python3_Interpreter_FOUND) + set(Python3_FIND_REGISTRY NEVER) + # Launch a new search + find_package(Python3 REQUIRED COMPONENTS Interpreter) - add_custom_target(${arg_GEN_TARGET}_python_venv_install - COMMENT "Installing python dependencies" - COMMAND "${_venv_pip_path}" install -r "${_requirements}" - DEPENDS "${_requirements}" ${arg_GEN_TARGET}_python_venv_create - VERBATIM + message(STATUS "Installing requirements from ${arg_REQUIREMENTS}") + execute_process( + COMMAND "${Python3_EXECUTABLE}" -m pip install -r "${arg_REQUIREMENTS}" + COMMAND_ERROR_IS_FATAL ANY + OUTPUT_QUIET + ERROR_QUIET ) + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${arg_REQUIREMENTS}") - add_custom_target(${arg_GEN_TARGET} - COMMENT "Generating JSON deserializers" - DEPENDS ${arg_GEN_TARGET}_python_venv_install - COMMAND "${_venv_python_path}" "${_gen_script}" "${_search_dir}/messages" --includes "${_inc_dir}" - COMMAND "${_venv_python_path}" "${_gen_script}" "${_search_dir}/payloads" --includes "${_inc_dir}" - VERBATIM - ) + set(${arg_OUT_PYTHON_EXE} "${Python3_EXECUTABLE}" PARENT_SCOPE) +endfunction() + +function(_setup_and_check_venv) + cmake_parse_arguments(PARSE_ARGV 0 arg + "" "INCLUDES;OUT_PYTHON_EXE;OUT_CHECK" "" + ) + _validate_all_args_are_parsed(arg OUT_PYTHON_EXE OUT_CHECK INCLUDES) + _make_and_use_venv( + VENV_PATH "${CMAKE_CURRENT_BINARY_DIR}/eventsub-venv" + REQUIREMENTS "${_eventsub_lib_root}/ast/requirements.txt" + OUT_PYTHON_EXE _python3_path + ) + if(NOT _python3_path) + return() # venv failed + endif() + + cmake_path(SET _check_script NORMALIZE "${_eventsub_lib_root}/ast/check-clang.py") + execute_process( + COMMAND "${_python3_path}" "${_check_script}" "${arg_INCLUDES}" + OUTPUT_VARIABLE _check_output + ERROR_VARIABLE _check_output + RESULT_VARIABLE _check_result + WORKING_DIRECTORY "${arg_BASE_DIRECTORY}" + ) + + set(${arg_OUT_PYTHON_EXE} "${_python3_path}" PARENT_SCOPE) + if (NOT _check_result EQUAL 0) + set(${arg_OUT_CHECK} "exit code: ${_check_result}\noutput:\n${_check_output}" PARENT_SCOPE) + endif() +endfunction() + +# generate_json_impls( +# [OUTPUT_SOURCES ] +# [BASE_DIRECTORY ] +# [FORCE ] +# HEADERS
... +# ) +# +# `OUTPUT_SOURCES` +# A variable to store a list of the generated sources in. +# `BASE_DIRECTORY` +# The directory to which paths in `HEADERS` are relative to. By default, +# they're relative to the directory of the calling file. +# `FORCE` +# Always generate sources and error if it's not possible. +# `HEADERS` +# A list of header files for which implmenetations will be generated. +function(generate_json_impls) + cmake_parse_arguments(PARSE_ARGV 0 arg + "" "OUTPUT_SOURCES;BASE_DIRECTORY;FORCE" "HEADERS" + ) + _validate_all_args_are_parsed(arg HEADERS) + if(NOT DEFINED arg_BASE_DIRECTORY) + set(arg_BASE_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}") + endif() + + cmake_path(SET _gen_script NORMALIZE "${_eventsub_lib_root}/ast/generate.py") + + if(arg_FORCE AND NOT Python3_EXECUTABLE) + message(FATAL_ERROR "Missing python3") + endif() + + # get includes + get_target_property(_qt_inc_dirs Qt${MAJOR_QT_VERSION}::Core INCLUDE_DIRECTORIES) + get_target_property(_qt_iinc_dirs Qt${MAJOR_QT_VERSION}::Core INTERFACE_INCLUDE_DIRECTORIES) + get_target_property(_qt_isinc_dirs Qt${MAJOR_QT_VERSION}::Core INTERFACE_SYSTEM_INCLUDE_DIRECTORIES) + list(APPEND _inc_dirs ${_qt_inc_dirs} ${_qt_iinc_dirs} ${_qt_isinc_dirs}) + list(APPEND _inc_dirs "${_eventsub_lib_root}/include/twitch-eventsub-ws") + list(APPEND _inc_dirs ${Boost_INCLUDE_DIRS}) + list(APPEND _inc_dirs ${OPENSSL_INCLUDE_DIR}) + list(JOIN _inc_dirs ";" _inc_dir) + + # setup venv (/lib/twitch-eventsub-ws/eventsub-venv) + if(Python3_EXECUTABLE) + _setup_and_check_venv( + INCLUDES "${_inc_dirs}" + OUT_PYTHON_EXE _python3_path + OUT_CHECK _check_output + ) + + if(NOT _check_output AND _python3_path) + set(_generation_supported On) + else() + set(_generation_supported Off) + if (arg_FORCE) + message(FATAL_ERROR "Generation of JSON implementation not supported, because the parser can't parse a simple TU:\n${_check_output}") + endif() + endif() + else() + set(_generation_supported Off) + endif() + + if(_generation_supported) + message(STATUS "Generation of JSON implementation is supported") + else() + message(STATUS "Generation of JSON implementation is not supported (use FORCE_JSON_GENERATION=On to get more information)") + endif() + + # get all dependencies + file(GLOB _gen_deps "${_eventsub_lib_root}/ast/lib/*.py") + file(GLOB _ast_templates "${_eventsub_lib_root}/ast/lib/templates/*.tmpl") + list(APPEND _gen_deps ${_ast_templates} "${_gen_script}") + + foreach(_header ${arg_HEADERS}) + # keep in sync with generate.py + string(REGEX REPLACE \.hpp$ .inc _def_path "${_header}") + string(REGEX REPLACE \.hpp$ .cpp _source_path "${_header}") + string(REGEX REPLACE \.hpp$ .timestamp _timestamp_path "${_header}") + string(REGEX REPLACE include[/\\]twitch-eventsub-ws[/\\] src/generated/ _source_path "${_source_path}") + + cmake_path(ABSOLUTE_PATH _timestamp_path BASE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/eventsub-deps") + cmake_path(ABSOLUTE_PATH _header BASE_DIRECTORY "${arg_BASE_DIRECTORY}") + cmake_path(ABSOLUTE_PATH _def_path BASE_DIRECTORY "${arg_BASE_DIRECTORY}") + cmake_path(ABSOLUTE_PATH _source_path BASE_DIRECTORY "${arg_BASE_DIRECTORY}") + list(APPEND _all_sources "${_source_path}") + + if(_generation_supported) + add_custom_command( + OUTPUT "${_timestamp_path}" "${_source_path}" "${_def_path}" + DEPENDS ${_gen_deps} "${_header}" + COMMENT "Generating implementation for ${_header}" + COMMAND "${_python3_path}" "${_gen_script}" "${_header}" --includes "${_inc_dirs}" --timestamp "${_timestamp_path}" + WORKING_DIRECTORY "${arg_BASE_DIRECTORY}" + VERBATIM + ) + endif() + endforeach() + + if(arg_OUTPUT_SOURCES) + set(${arg_OUTPUT_SOURCES} "${_all_sources}" PARENT_SCOPE) + endif() endfunction() diff --git a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/messages/metadata.hpp b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/messages/metadata.hpp index 771c22bf..73918899 100644 --- a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/messages/metadata.hpp +++ b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/messages/metadata.hpp @@ -1,7 +1,5 @@ #pragma once -#include "twitch-eventsub-ws/errors.hpp" - #include #include @@ -33,9 +31,6 @@ struct Metadata { const std::optional subscriptionVersion; }; -// DESERIALIZATION DEFINITION START -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); -// DESERIALIZATION DEFINITION END +#include "twitch-eventsub-ws/messages/metadata.inc" } // namespace chatterino::eventsub::lib::messages diff --git a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/messages/metadata.inc b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/messages/metadata.inc new file mode 100644 index 00000000..8cfd944b --- /dev/null +++ b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/messages/metadata.inc @@ -0,0 +1,2 @@ +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); diff --git a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-ban-v1.hpp b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-ban-v1.hpp index 1d874308..71f58e33 100644 --- a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-ban-v1.hpp +++ b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-ban-v1.hpp @@ -97,12 +97,6 @@ struct Payload { const Event event; }; -// DESERIALIZATION DEFINITION START -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); -// DESERIALIZATION DEFINITION END +#include "twitch-eventsub-ws/payloads/channel-ban-v1.inc" } // namespace chatterino::eventsub::lib::payload::channel_ban::v1 diff --git a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-ban-v1.inc b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-ban-v1.inc new file mode 100644 index 00000000..9a88467f --- /dev/null +++ b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-ban-v1.inc @@ -0,0 +1,5 @@ +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); diff --git a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-chat-message-v1.hpp b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-chat-message-v1.hpp index 84223d6b..34a8465e 100644 --- a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-chat-message-v1.hpp +++ b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-chat-message-v1.hpp @@ -149,37 +149,6 @@ struct Payload { const Event event; }; -// DESERIALIZATION DEFINITION START -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, - const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); -// DESERIALIZATION DEFINITION END +#include "twitch-eventsub-ws/payloads/channel-chat-message-v1.inc" } // namespace chatterino::eventsub::lib::payload::channel_chat_message::v1 diff --git a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-chat-message-v1.inc b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-chat-message-v1.inc new file mode 100644 index 00000000..9fd0868e --- /dev/null +++ b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-chat-message-v1.inc @@ -0,0 +1,30 @@ +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); diff --git a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-chat-notification-v1.hpp b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-chat-notification-v1.hpp index d84a5036..37134bd0 100644 --- a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-chat-notification-v1.hpp +++ b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-chat-notification-v1.hpp @@ -187,81 +187,6 @@ struct Payload { const Event event; }; -// DESERIALIZATION DEFINITION START -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, - const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, - const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, - const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, - const boost::json::value &jvRoot); - -boost::json::result_for::type - tag_invoke(boost::json::try_value_to_tag, - const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, - const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, - const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, - const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, - const boost::json::value &jvRoot); - -boost::json::result_for::type - tag_invoke(boost::json::try_value_to_tag, - const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, - const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, - const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); -// DESERIALIZATION DEFINITION END +#include "twitch-eventsub-ws/payloads/channel-chat-notification-v1.inc" } // namespace chatterino::eventsub::lib::payload::channel_chat_notification::v1 diff --git a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-chat-notification-v1.inc b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-chat-notification-v1.inc new file mode 100644 index 00000000..82f2ae22 --- /dev/null +++ b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-chat-notification-v1.inc @@ -0,0 +1,74 @@ +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + +boost::json::result_for::type + tag_invoke(boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + +boost::json::result_for::type + tag_invoke(boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); diff --git a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-moderate-v2.hpp b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-moderate-v2.hpp index 0f88905f..cccbb3f4 100644 --- a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-moderate-v2.hpp +++ b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-moderate-v2.hpp @@ -388,65 +388,6 @@ struct Payload { Event event; }; -// DESERIALIZATION DEFINITION START -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, - const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, - const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); -// DESERIALIZATION DEFINITION END +#include "twitch-eventsub-ws/payloads/channel-moderate-v2.inc" } // namespace chatterino::eventsub::lib::payload::channel_moderate::v2 diff --git a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-moderate-v2.inc b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-moderate-v2.inc new file mode 100644 index 00000000..f10b0d30 --- /dev/null +++ b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-moderate-v2.inc @@ -0,0 +1,58 @@ +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, + const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); diff --git a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-update-v1.hpp b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-update-v1.hpp index da69d509..c61e8ba1 100644 --- a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-update-v1.hpp +++ b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-update-v1.hpp @@ -38,12 +38,6 @@ struct Payload { const Event event; }; -// DESERIALIZATION DEFINITION START -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); -// DESERIALIZATION DEFINITION END +#include "twitch-eventsub-ws/payloads/channel-update-v1.inc" } // namespace chatterino::eventsub::lib::payload::channel_update::v1 diff --git a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-update-v1.inc b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-update-v1.inc new file mode 100644 index 00000000..9a88467f --- /dev/null +++ b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-update-v1.inc @@ -0,0 +1,5 @@ +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); diff --git a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/session-welcome.hpp b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/session-welcome.hpp index d229f254..8088fd69 100644 --- a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/session-welcome.hpp +++ b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/session-welcome.hpp @@ -1,7 +1,5 @@ #pragma once -#include "twitch-eventsub-ws/errors.hpp" - #include #include @@ -28,9 +26,6 @@ struct Payload { const std::string id; }; -// DESERIALIZATION DEFINITION START -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); -// DESERIALIZATION DEFINITION END +#include "twitch-eventsub-ws/payloads/session-welcome.inc" } // namespace chatterino::eventsub::lib::payload::session_welcome diff --git a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/session-welcome.inc b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/session-welcome.inc new file mode 100644 index 00000000..cd3062ec --- /dev/null +++ b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/session-welcome.inc @@ -0,0 +1,2 @@ +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); diff --git a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/stream-offline-v1.hpp b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/stream-offline-v1.hpp index 07651501..36d12ecb 100644 --- a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/stream-offline-v1.hpp +++ b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/stream-offline-v1.hpp @@ -24,12 +24,6 @@ struct Payload { const Event event; }; -// DESERIALIZATION DEFINITION START -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); -// DESERIALIZATION DEFINITION END +#include "twitch-eventsub-ws/payloads/stream-offline-v1.inc" } // namespace chatterino::eventsub::lib::payload::stream_offline::v1 diff --git a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/stream-offline-v1.inc b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/stream-offline-v1.inc new file mode 100644 index 00000000..9a88467f --- /dev/null +++ b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/stream-offline-v1.inc @@ -0,0 +1,5 @@ +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); diff --git a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/stream-online-v1.hpp b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/stream-online-v1.hpp index e48ca39c..cd22017d 100644 --- a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/stream-online-v1.hpp +++ b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/stream-online-v1.hpp @@ -34,12 +34,6 @@ struct Payload { const Event event; }; -// DESERIALIZATION DEFINITION START -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); -// DESERIALIZATION DEFINITION END +#include "twitch-eventsub-ws/payloads/stream-online-v1.inc" } // namespace chatterino::eventsub::lib::payload::stream_online::v1 diff --git a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/stream-online-v1.inc b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/stream-online-v1.inc new file mode 100644 index 00000000..9a88467f --- /dev/null +++ b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/stream-online-v1.inc @@ -0,0 +1,5 @@ +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); diff --git a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/subscription.hpp b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/subscription.hpp index fe6ca864..53b2e497 100644 --- a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/subscription.hpp +++ b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/subscription.hpp @@ -54,13 +54,6 @@ struct Subscription { const int cost; }; -// DESERIALIZATION DEFINITION START -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot); - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, - const boost::json::value &jvRoot); -// DESERIALIZATION DEFINITION END +#include "twitch-eventsub-ws/payloads/subscription.inc" } // namespace chatterino::eventsub::lib::payload::subscription diff --git a/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/subscription.inc b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/subscription.inc new file mode 100644 index 00000000..d3dbe93d --- /dev/null +++ b/lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/subscription.inc @@ -0,0 +1,6 @@ +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot); + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, + const boost::json::value &jvRoot); diff --git a/lib/twitch-eventsub-ws/src/CMakeLists.txt b/lib/twitch-eventsub-ws/src/CMakeLists.txt index e6c0e925..6dc0e81f 100644 --- a/lib/twitch-eventsub-ws/src/CMakeLists.txt +++ b/lib/twitch-eventsub-ws/src/CMakeLists.txt @@ -1,3 +1,21 @@ +generate_json_impls( + OUTPUT_SOURCES eventsub_generated_sources + BASE_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/.." + FORCE ${FORCE_JSON_GENERATION} + HEADERS + include/twitch-eventsub-ws/messages/metadata.hpp + + include/twitch-eventsub-ws/payloads/channel-ban-v1.hpp + include/twitch-eventsub-ws/payloads/channel-chat-message-v1.hpp + include/twitch-eventsub-ws/payloads/channel-chat-notification-v1.hpp + include/twitch-eventsub-ws/payloads/channel-moderate-v2.hpp + include/twitch-eventsub-ws/payloads/channel-update-v1.hpp + include/twitch-eventsub-ws/payloads/session-welcome.hpp + include/twitch-eventsub-ws/payloads/stream-offline-v1.hpp + include/twitch-eventsub-ws/payloads/stream-online-v1.hpp + include/twitch-eventsub-ws/payloads/subscription.hpp +) + set(SOURCE_FILES session.cpp @@ -6,20 +24,11 @@ set(SOURCE_FILES json.cpp string.cpp - payloads/subscription.cpp - payloads/session-welcome.cpp - - # Subscription types + # Subscription types (only additional functions) payloads/channel-ban-v1.cpp - payloads/stream-online-v1.cpp - payloads/stream-offline-v1.cpp - payloads/channel-update-v1.cpp - payloads/channel-chat-notification-v1.cpp - payloads/channel-chat-message-v1.cpp - payloads/channel-moderate-v2.cpp # Add your new subscription type source file above this line - messages/metadata.cpp + ${eventsub_generated_sources} ) add_library(${PROJECT_NAME} STATIC ${SOURCE_FILES}) @@ -43,13 +52,6 @@ target_compile_definitions(${PROJECT_NAME} PUBLIC $<$:BOOST_CONTAINER_NO_LIB> ) -# Hack to get the include directories from Python -get_target_property(_inc_dirs ${PROJECT_NAME} INCLUDE_DIRECTORIES) -list(APPEND _inc_dirs ${Boost_INCLUDE_DIRS}) -list(APPEND _inc_dirs ${OPENSSL_INCLUDE_DIR}) -list(JOIN _inc_dirs ";" _inc_dir) -add_custom_target(_ast_includes echo "@@INCLUDE_DIRS=${_inc_dir}") - if (MSVC) # Add bigobj target_compile_options(${PROJECT_NAME} PRIVATE /EHsc /bigobj) diff --git a/lib/twitch-eventsub-ws/src/messages/metadata.cpp b/lib/twitch-eventsub-ws/src/generated/messages/metadata.cpp similarity index 96% rename from lib/twitch-eventsub-ws/src/messages/metadata.cpp rename to lib/twitch-eventsub-ws/src/generated/messages/metadata.cpp index 40f77e81..396ddd6a 100644 --- a/lib/twitch-eventsub-ws/src/messages/metadata.cpp +++ b/lib/twitch-eventsub-ws/src/generated/messages/metadata.cpp @@ -1,12 +1,13 @@ -#include "twitch-eventsub-ws/messages/metadata.hpp" +// WARNING: This file is automatically generated. Any changes will be lost. +#include "twitch-eventsub-ws/chrono.hpp" // IWYU pragma: keep #include "twitch-eventsub-ws/errors.hpp" +#include "twitch-eventsub-ws/messages/metadata.hpp" #include namespace chatterino::eventsub::lib::messages { -// DESERIALIZATION IMPLEMENTATION START boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag, const boost::json::value &jvRoot) { @@ -104,6 +105,5 @@ boost::json::result_for::type tag_invoke( .subscriptionVersion = std::move(subscriptionVersion), }; } -// DESERIALIZATION IMPLEMENTATION END } // namespace chatterino::eventsub::lib::messages diff --git a/lib/twitch-eventsub-ws/src/generated/payloads/channel-ban-v1.cpp b/lib/twitch-eventsub-ws/src/generated/payloads/channel-ban-v1.cpp new file mode 100644 index 00000000..1838eb2e --- /dev/null +++ b/lib/twitch-eventsub-ws/src/generated/payloads/channel-ban-v1.cpp @@ -0,0 +1,306 @@ + +// WARNING: This file is automatically generated. Any changes will be lost. +#include "twitch-eventsub-ws/chrono.hpp" // IWYU pragma: keep +#include "twitch-eventsub-ws/errors.hpp" +#include "twitch-eventsub-ws/payloads/channel-ban-v1.hpp" + +#include + +namespace chatterino::eventsub::lib::payload::channel_ban::v1 { + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot) +{ + if (!jvRoot.is_object()) + { + static const error::ApplicationErrorCategory errorMustBeObject{ + "Event must be an object"}; + return boost::system::error_code{129, errorMustBeObject}; + } + const auto &root = jvRoot.get_object(); + + const auto *jvbroadcasterUserID = root.if_contains("broadcaster_user_id"); + if (jvbroadcasterUserID == nullptr) + { + static const error::ApplicationErrorCategory + error_missing_field_broadcasterUserID{ + "Missing required key broadcaster_user_id"}; + return boost::system::error_code{129, + error_missing_field_broadcasterUserID}; + } + + auto broadcasterUserID = + boost::json::try_value_to(*jvbroadcasterUserID); + + if (broadcasterUserID.has_error()) + { + return broadcasterUserID.error(); + } + + const auto *jvbroadcasterUserLogin = + root.if_contains("broadcaster_user_login"); + if (jvbroadcasterUserLogin == nullptr) + { + static const error::ApplicationErrorCategory + error_missing_field_broadcasterUserLogin{ + "Missing required key broadcaster_user_login"}; + return boost::system::error_code{ + 129, error_missing_field_broadcasterUserLogin}; + } + + auto broadcasterUserLogin = + boost::json::try_value_to(*jvbroadcasterUserLogin); + + if (broadcasterUserLogin.has_error()) + { + return broadcasterUserLogin.error(); + } + + const auto *jvbroadcasterUserName = + root.if_contains("broadcaster_user_name"); + if (jvbroadcasterUserName == nullptr) + { + static const error::ApplicationErrorCategory + error_missing_field_broadcasterUserName{ + "Missing required key broadcaster_user_name"}; + return boost::system::error_code{ + 129, error_missing_field_broadcasterUserName}; + } + + auto broadcasterUserName = + boost::json::try_value_to(*jvbroadcasterUserName); + + if (broadcasterUserName.has_error()) + { + return broadcasterUserName.error(); + } + + const auto *jvmoderatorUserID = root.if_contains("moderator_user_id"); + if (jvmoderatorUserID == nullptr) + { + static const error::ApplicationErrorCategory + error_missing_field_moderatorUserID{ + "Missing required key moderator_user_id"}; + return boost::system::error_code{129, + error_missing_field_moderatorUserID}; + } + + auto moderatorUserID = + boost::json::try_value_to(*jvmoderatorUserID); + + if (moderatorUserID.has_error()) + { + return moderatorUserID.error(); + } + + const auto *jvmoderatorUserLogin = root.if_contains("moderator_user_login"); + if (jvmoderatorUserLogin == nullptr) + { + static const error::ApplicationErrorCategory + error_missing_field_moderatorUserLogin{ + "Missing required key moderator_user_login"}; + return boost::system::error_code{ + 129, error_missing_field_moderatorUserLogin}; + } + + auto moderatorUserLogin = + boost::json::try_value_to(*jvmoderatorUserLogin); + + if (moderatorUserLogin.has_error()) + { + return moderatorUserLogin.error(); + } + + const auto *jvmoderatorUserName = root.if_contains("moderator_user_name"); + if (jvmoderatorUserName == nullptr) + { + static const error::ApplicationErrorCategory + error_missing_field_moderatorUserName{ + "Missing required key moderator_user_name"}; + return boost::system::error_code{129, + error_missing_field_moderatorUserName}; + } + + auto moderatorUserName = + boost::json::try_value_to(*jvmoderatorUserName); + + if (moderatorUserName.has_error()) + { + return moderatorUserName.error(); + } + + const auto *jvuserID = root.if_contains("user_id"); + if (jvuserID == nullptr) + { + static const error::ApplicationErrorCategory error_missing_field_userID{ + "Missing required key user_id"}; + return boost::system::error_code{129, error_missing_field_userID}; + } + + auto userID = boost::json::try_value_to(*jvuserID); + + if (userID.has_error()) + { + return userID.error(); + } + + const auto *jvuserLogin = root.if_contains("user_login"); + if (jvuserLogin == nullptr) + { + static const error::ApplicationErrorCategory + error_missing_field_userLogin{"Missing required key user_login"}; + return boost::system::error_code{129, error_missing_field_userLogin}; + } + + auto userLogin = boost::json::try_value_to(*jvuserLogin); + + if (userLogin.has_error()) + { + return userLogin.error(); + } + + const auto *jvuserName = root.if_contains("user_name"); + if (jvuserName == nullptr) + { + static const error::ApplicationErrorCategory + error_missing_field_userName{"Missing required key user_name"}; + return boost::system::error_code{129, error_missing_field_userName}; + } + + auto userName = boost::json::try_value_to(*jvuserName); + + if (userName.has_error()) + { + return userName.error(); + } + + const auto *jvreason = root.if_contains("reason"); + if (jvreason == nullptr) + { + static const error::ApplicationErrorCategory error_missing_field_reason{ + "Missing required key reason"}; + return boost::system::error_code{129, error_missing_field_reason}; + } + + auto reason = boost::json::try_value_to(*jvreason); + + if (reason.has_error()) + { + return reason.error(); + } + + const auto *jvisPermanent = root.if_contains("is_permanent"); + if (jvisPermanent == nullptr) + { + static const error::ApplicationErrorCategory + error_missing_field_isPermanent{ + "Missing required key is_permanent"}; + return boost::system::error_code{129, error_missing_field_isPermanent}; + } + + auto isPermanent = boost::json::try_value_to(*jvisPermanent); + + if (isPermanent.has_error()) + { + return isPermanent.error(); + } + + const auto *jvbannedAt = root.if_contains("banned_at"); + if (jvbannedAt == nullptr) + { + static const error::ApplicationErrorCategory + error_missing_field_bannedAt{"Missing required key banned_at"}; + return boost::system::error_code{129, error_missing_field_bannedAt}; + } + + auto bannedAt = + boost::json::try_value_to( + *jvbannedAt, AsISO8601()); + + if (bannedAt.has_error()) + { + return bannedAt.error(); + } + + std::optional endsAt = std::nullopt; + const auto *jvendsAt = root.if_contains("ends_at"); + if (jvendsAt != nullptr && !jvendsAt->is_null()) + { + auto tendsAt = + boost::json::try_value_to( + *jvendsAt, AsISO8601()); + + if (tendsAt.has_error()) + { + return tendsAt.error(); + } + endsAt = std::move(tendsAt.value()); + } + + return Event{ + .broadcasterUserID = std::move(broadcasterUserID.value()), + .broadcasterUserLogin = std::move(broadcasterUserLogin.value()), + .broadcasterUserName = std::move(broadcasterUserName.value()), + .moderatorUserID = std::move(moderatorUserID.value()), + .moderatorUserLogin = std::move(moderatorUserLogin.value()), + .moderatorUserName = std::move(moderatorUserName.value()), + .userID = std::move(userID.value()), + .userLogin = std::move(userLogin.value()), + .userName = std::move(userName.value()), + .reason = std::move(reason.value()), + .isPermanent = std::move(isPermanent.value()), + .bannedAt = std::move(bannedAt.value()), + .endsAt = std::move(endsAt), + }; +} + +boost::json::result_for::type tag_invoke( + boost::json::try_value_to_tag, const boost::json::value &jvRoot) +{ + if (!jvRoot.is_object()) + { + static const error::ApplicationErrorCategory errorMustBeObject{ + "Payload must be an object"}; + return boost::system::error_code{129, errorMustBeObject}; + } + const auto &root = jvRoot.get_object(); + + const auto *jvsubscription = root.if_contains("subscription"); + if (jvsubscription == nullptr) + { + static const error::ApplicationErrorCategory + error_missing_field_subscription{ + "Missing required key subscription"}; + return boost::system::error_code{129, error_missing_field_subscription}; + } + + auto subscription = + boost::json::try_value_to(*jvsubscription); + + if (subscription.has_error()) + { + return subscription.error(); + } + + const auto *jvevent = root.if_contains("event"); + if (jvevent == nullptr) + { + static const error::ApplicationErrorCategory error_missing_field_event{ + "Missing required key event"}; + return boost::system::error_code{129, error_missing_field_event}; + } + + auto event = boost::json::try_value_to(*jvevent); + + if (event.has_error()) + { + return event.error(); + } + + return Payload{ + .subscription = std::move(subscription.value()), + .event = std::move(event.value()), + }; +} + +} // namespace chatterino::eventsub::lib::payload::channel_ban::v1 diff --git a/lib/twitch-eventsub-ws/src/payloads/channel-chat-message-v1.cpp b/lib/twitch-eventsub-ws/src/generated/payloads/channel-chat-message-v1.cpp similarity index 99% rename from lib/twitch-eventsub-ws/src/payloads/channel-chat-message-v1.cpp rename to lib/twitch-eventsub-ws/src/generated/payloads/channel-chat-message-v1.cpp index e9fe0e8d..a6611320 100644 --- a/lib/twitch-eventsub-ws/src/payloads/channel-chat-message-v1.cpp +++ b/lib/twitch-eventsub-ws/src/generated/payloads/channel-chat-message-v1.cpp @@ -1,12 +1,13 @@ -#include "twitch-eventsub-ws/payloads/channel-chat-message-v1.hpp" +// WARNING: This file is automatically generated. Any changes will be lost. +#include "twitch-eventsub-ws/chrono.hpp" // IWYU pragma: keep #include "twitch-eventsub-ws/errors.hpp" +#include "twitch-eventsub-ws/payloads/channel-chat-message-v1.hpp" #include namespace chatterino::eventsub::lib::payload::channel_chat_message::v1 { -// DESERIALIZATION IMPLEMENTATION START boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag, const boost::json::value &jvRoot) { @@ -929,6 +930,5 @@ boost::json::result_for::type tag_invoke( .event = std::move(event.value()), }; } -// DESERIALIZATION IMPLEMENTATION END } // namespace chatterino::eventsub::lib::payload::channel_chat_message::v1 diff --git a/lib/twitch-eventsub-ws/src/payloads/channel-chat-notification-v1.cpp b/lib/twitch-eventsub-ws/src/generated/payloads/channel-chat-notification-v1.cpp similarity index 99% rename from lib/twitch-eventsub-ws/src/payloads/channel-chat-notification-v1.cpp rename to lib/twitch-eventsub-ws/src/generated/payloads/channel-chat-notification-v1.cpp index 40d249fe..03d77907 100644 --- a/lib/twitch-eventsub-ws/src/payloads/channel-chat-notification-v1.cpp +++ b/lib/twitch-eventsub-ws/src/generated/payloads/channel-chat-notification-v1.cpp @@ -1,12 +1,13 @@ -#include "twitch-eventsub-ws/payloads/channel-chat-notification-v1.hpp" +// WARNING: This file is automatically generated. Any changes will be lost. +#include "twitch-eventsub-ws/chrono.hpp" // IWYU pragma: keep #include "twitch-eventsub-ws/errors.hpp" +#include "twitch-eventsub-ws/payloads/channel-chat-notification-v1.hpp" #include namespace chatterino::eventsub::lib::payload::channel_chat_notification::v1 { -// DESERIALIZATION IMPLEMENTATION START boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag, const boost::json::value &jvRoot) { @@ -1840,6 +1841,5 @@ boost::json::result_for::type tag_invoke( .event = std::move(event.value()), }; } -// DESERIALIZATION IMPLEMENTATION END } // namespace chatterino::eventsub::lib::payload::channel_chat_notification::v1 diff --git a/lib/twitch-eventsub-ws/src/payloads/channel-moderate-v2.cpp b/lib/twitch-eventsub-ws/src/generated/payloads/channel-moderate-v2.cpp similarity index 99% rename from lib/twitch-eventsub-ws/src/payloads/channel-moderate-v2.cpp rename to lib/twitch-eventsub-ws/src/generated/payloads/channel-moderate-v2.cpp index 90322360..9edb054b 100644 --- a/lib/twitch-eventsub-ws/src/payloads/channel-moderate-v2.cpp +++ b/lib/twitch-eventsub-ws/src/generated/payloads/channel-moderate-v2.cpp @@ -1,14 +1,13 @@ -#include "twitch-eventsub-ws/payloads/channel-moderate-v2.hpp" +// WARNING: This file is automatically generated. Any changes will be lost. +#include "twitch-eventsub-ws/chrono.hpp" // IWYU pragma: keep #include "twitch-eventsub-ws/errors.hpp" +#include "twitch-eventsub-ws/payloads/channel-moderate-v2.hpp" #include -#include - namespace chatterino::eventsub::lib::payload::channel_moderate::v2 { -// DESERIALIZATION IMPLEMENTATION START boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag, const boost::json::value &jvRoot) { @@ -1835,6 +1834,5 @@ boost::json::result_for::type tag_invoke( .event = std::move(event.value()), }; } -// DESERIALIZATION IMPLEMENTATION END } // namespace chatterino::eventsub::lib::payload::channel_moderate::v2 diff --git a/lib/twitch-eventsub-ws/src/payloads/channel-update-v1.cpp b/lib/twitch-eventsub-ws/src/generated/payloads/channel-update-v1.cpp similarity index 98% rename from lib/twitch-eventsub-ws/src/payloads/channel-update-v1.cpp rename to lib/twitch-eventsub-ws/src/generated/payloads/channel-update-v1.cpp index 14994e2a..9058fde0 100644 --- a/lib/twitch-eventsub-ws/src/payloads/channel-update-v1.cpp +++ b/lib/twitch-eventsub-ws/src/generated/payloads/channel-update-v1.cpp @@ -1,12 +1,13 @@ -#include "twitch-eventsub-ws/payloads/channel-update-v1.hpp" +// WARNING: This file is automatically generated. Any changes will be lost. +#include "twitch-eventsub-ws/chrono.hpp" // IWYU pragma: keep #include "twitch-eventsub-ws/errors.hpp" +#include "twitch-eventsub-ws/payloads/channel-update-v1.hpp" #include namespace chatterino::eventsub::lib::payload::channel_update::v1 { -// DESERIALIZATION IMPLEMENTATION START boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag, const boost::json::value &jvRoot) { @@ -210,6 +211,5 @@ boost::json::result_for::type tag_invoke( .event = std::move(event.value()), }; } -// DESERIALIZATION IMPLEMENTATION END } // namespace chatterino::eventsub::lib::payload::channel_update::v1 diff --git a/lib/twitch-eventsub-ws/src/payloads/session-welcome.cpp b/lib/twitch-eventsub-ws/src/generated/payloads/session-welcome.cpp similarity index 92% rename from lib/twitch-eventsub-ws/src/payloads/session-welcome.cpp rename to lib/twitch-eventsub-ws/src/generated/payloads/session-welcome.cpp index 3d6b86be..bfcfab76 100644 --- a/lib/twitch-eventsub-ws/src/payloads/session-welcome.cpp +++ b/lib/twitch-eventsub-ws/src/generated/payloads/session-welcome.cpp @@ -1,12 +1,13 @@ -#include "twitch-eventsub-ws/payloads/session-welcome.hpp" +// WARNING: This file is automatically generated. Any changes will be lost. +#include "twitch-eventsub-ws/chrono.hpp" // IWYU pragma: keep #include "twitch-eventsub-ws/errors.hpp" +#include "twitch-eventsub-ws/payloads/session-welcome.hpp" #include namespace chatterino::eventsub::lib::payload::session_welcome { -// DESERIALIZATION IMPLEMENTATION START boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag, const boost::json::value &jvRoot) { @@ -52,6 +53,5 @@ boost::json::result_for::type tag_invoke( .id = std::move(id.value()), }; } -// DESERIALIZATION IMPLEMENTATION END } // namespace chatterino::eventsub::lib::payload::session_welcome diff --git a/lib/twitch-eventsub-ws/src/payloads/stream-offline-v1.cpp b/lib/twitch-eventsub-ws/src/generated/payloads/stream-offline-v1.cpp similarity index 96% rename from lib/twitch-eventsub-ws/src/payloads/stream-offline-v1.cpp rename to lib/twitch-eventsub-ws/src/generated/payloads/stream-offline-v1.cpp index 62f8dfb7..46529eef 100644 --- a/lib/twitch-eventsub-ws/src/payloads/stream-offline-v1.cpp +++ b/lib/twitch-eventsub-ws/src/generated/payloads/stream-offline-v1.cpp @@ -1,12 +1,13 @@ -#include "twitch-eventsub-ws/payloads/stream-offline-v1.hpp" +// WARNING: This file is automatically generated. Any changes will be lost. +#include "twitch-eventsub-ws/chrono.hpp" // IWYU pragma: keep #include "twitch-eventsub-ws/errors.hpp" +#include "twitch-eventsub-ws/payloads/stream-offline-v1.hpp" #include namespace chatterino::eventsub::lib::payload::stream_offline::v1 { -// DESERIALIZATION IMPLEMENTATION START boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag, const boost::json::value &jvRoot) { @@ -129,6 +130,5 @@ boost::json::result_for::type tag_invoke( .event = std::move(event.value()), }; } -// DESERIALIZATION IMPLEMENTATION END } // namespace chatterino::eventsub::lib::payload::stream_offline::v1 diff --git a/lib/twitch-eventsub-ws/src/payloads/stream-online-v1.cpp b/lib/twitch-eventsub-ws/src/generated/payloads/stream-online-v1.cpp similarity index 97% rename from lib/twitch-eventsub-ws/src/payloads/stream-online-v1.cpp rename to lib/twitch-eventsub-ws/src/generated/payloads/stream-online-v1.cpp index 7d399b7f..3d1cbada 100644 --- a/lib/twitch-eventsub-ws/src/payloads/stream-online-v1.cpp +++ b/lib/twitch-eventsub-ws/src/generated/payloads/stream-online-v1.cpp @@ -1,12 +1,13 @@ -#include "twitch-eventsub-ws/payloads/stream-online-v1.hpp" +// WARNING: This file is automatically generated. Any changes will be lost. +#include "twitch-eventsub-ws/chrono.hpp" // IWYU pragma: keep #include "twitch-eventsub-ws/errors.hpp" +#include "twitch-eventsub-ws/payloads/stream-online-v1.hpp" #include namespace chatterino::eventsub::lib::payload::stream_online::v1 { -// DESERIALIZATION IMPLEMENTATION START boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag, const boost::json::value &jvRoot) { @@ -177,6 +178,5 @@ boost::json::result_for::type tag_invoke( .event = std::move(event.value()), }; } -// DESERIALIZATION IMPLEMENTATION END } // namespace chatterino::eventsub::lib::payload::stream_online::v1 diff --git a/lib/twitch-eventsub-ws/src/payloads/subscription.cpp b/lib/twitch-eventsub-ws/src/generated/payloads/subscription.cpp similarity index 97% rename from lib/twitch-eventsub-ws/src/payloads/subscription.cpp rename to lib/twitch-eventsub-ws/src/generated/payloads/subscription.cpp index 1145c36b..55cb9757 100644 --- a/lib/twitch-eventsub-ws/src/payloads/subscription.cpp +++ b/lib/twitch-eventsub-ws/src/generated/payloads/subscription.cpp @@ -1,12 +1,13 @@ -#include "twitch-eventsub-ws/payloads/subscription.hpp" +// WARNING: This file is automatically generated. Any changes will be lost. +#include "twitch-eventsub-ws/chrono.hpp" // IWYU pragma: keep #include "twitch-eventsub-ws/errors.hpp" +#include "twitch-eventsub-ws/payloads/subscription.hpp" #include namespace chatterino::eventsub::lib::payload::subscription { -// DESERIALIZATION IMPLEMENTATION START boost::json::result_for::type tag_invoke( boost::json::try_value_to_tag, const boost::json::value &jvRoot) { @@ -181,6 +182,5 @@ boost::json::result_for::type tag_invoke( .cost = std::move(cost.value()), }; } -// DESERIALIZATION IMPLEMENTATION END } // namespace chatterino::eventsub::lib::payload::subscription diff --git a/lib/twitch-eventsub-ws/src/payloads/channel-ban-v1.cpp b/lib/twitch-eventsub-ws/src/payloads/channel-ban-v1.cpp index 6758cab7..9bc626aa 100644 --- a/lib/twitch-eventsub-ws/src/payloads/channel-ban-v1.cpp +++ b/lib/twitch-eventsub-ws/src/payloads/channel-ban-v1.cpp @@ -1,10 +1,5 @@ #include "twitch-eventsub-ws/payloads/channel-ban-v1.hpp" -#include "twitch-eventsub-ws/chrono.hpp" -#include "twitch-eventsub-ws/errors.hpp" - -#include - namespace chatterino::eventsub::lib::payload::channel_ban::v1 { std::chrono::system_clock::duration Event::timeoutDuration() const @@ -17,301 +12,4 @@ std::chrono::system_clock::duration Event::timeoutDuration() const return *this->endsAt - this->bannedAt; } -// DESERIALIZATION IMPLEMENTATION START -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot) -{ - if (!jvRoot.is_object()) - { - static const error::ApplicationErrorCategory errorMustBeObject{ - "Event must be an object"}; - return boost::system::error_code{129, errorMustBeObject}; - } - const auto &root = jvRoot.get_object(); - - const auto *jvbroadcasterUserID = root.if_contains("broadcaster_user_id"); - if (jvbroadcasterUserID == nullptr) - { - static const error::ApplicationErrorCategory - error_missing_field_broadcasterUserID{ - "Missing required key broadcaster_user_id"}; - return boost::system::error_code{129, - error_missing_field_broadcasterUserID}; - } - - auto broadcasterUserID = - boost::json::try_value_to(*jvbroadcasterUserID); - - if (broadcasterUserID.has_error()) - { - return broadcasterUserID.error(); - } - - const auto *jvbroadcasterUserLogin = - root.if_contains("broadcaster_user_login"); - if (jvbroadcasterUserLogin == nullptr) - { - static const error::ApplicationErrorCategory - error_missing_field_broadcasterUserLogin{ - "Missing required key broadcaster_user_login"}; - return boost::system::error_code{ - 129, error_missing_field_broadcasterUserLogin}; - } - - auto broadcasterUserLogin = - boost::json::try_value_to(*jvbroadcasterUserLogin); - - if (broadcasterUserLogin.has_error()) - { - return broadcasterUserLogin.error(); - } - - const auto *jvbroadcasterUserName = - root.if_contains("broadcaster_user_name"); - if (jvbroadcasterUserName == nullptr) - { - static const error::ApplicationErrorCategory - error_missing_field_broadcasterUserName{ - "Missing required key broadcaster_user_name"}; - return boost::system::error_code{ - 129, error_missing_field_broadcasterUserName}; - } - - auto broadcasterUserName = - boost::json::try_value_to(*jvbroadcasterUserName); - - if (broadcasterUserName.has_error()) - { - return broadcasterUserName.error(); - } - - const auto *jvmoderatorUserID = root.if_contains("moderator_user_id"); - if (jvmoderatorUserID == nullptr) - { - static const error::ApplicationErrorCategory - error_missing_field_moderatorUserID{ - "Missing required key moderator_user_id"}; - return boost::system::error_code{129, - error_missing_field_moderatorUserID}; - } - - auto moderatorUserID = - boost::json::try_value_to(*jvmoderatorUserID); - - if (moderatorUserID.has_error()) - { - return moderatorUserID.error(); - } - - const auto *jvmoderatorUserLogin = root.if_contains("moderator_user_login"); - if (jvmoderatorUserLogin == nullptr) - { - static const error::ApplicationErrorCategory - error_missing_field_moderatorUserLogin{ - "Missing required key moderator_user_login"}; - return boost::system::error_code{ - 129, error_missing_field_moderatorUserLogin}; - } - - auto moderatorUserLogin = - boost::json::try_value_to(*jvmoderatorUserLogin); - - if (moderatorUserLogin.has_error()) - { - return moderatorUserLogin.error(); - } - - const auto *jvmoderatorUserName = root.if_contains("moderator_user_name"); - if (jvmoderatorUserName == nullptr) - { - static const error::ApplicationErrorCategory - error_missing_field_moderatorUserName{ - "Missing required key moderator_user_name"}; - return boost::system::error_code{129, - error_missing_field_moderatorUserName}; - } - - auto moderatorUserName = - boost::json::try_value_to(*jvmoderatorUserName); - - if (moderatorUserName.has_error()) - { - return moderatorUserName.error(); - } - - const auto *jvuserID = root.if_contains("user_id"); - if (jvuserID == nullptr) - { - static const error::ApplicationErrorCategory error_missing_field_userID{ - "Missing required key user_id"}; - return boost::system::error_code{129, error_missing_field_userID}; - } - - auto userID = boost::json::try_value_to(*jvuserID); - - if (userID.has_error()) - { - return userID.error(); - } - - const auto *jvuserLogin = root.if_contains("user_login"); - if (jvuserLogin == nullptr) - { - static const error::ApplicationErrorCategory - error_missing_field_userLogin{"Missing required key user_login"}; - return boost::system::error_code{129, error_missing_field_userLogin}; - } - - auto userLogin = boost::json::try_value_to(*jvuserLogin); - - if (userLogin.has_error()) - { - return userLogin.error(); - } - - const auto *jvuserName = root.if_contains("user_name"); - if (jvuserName == nullptr) - { - static const error::ApplicationErrorCategory - error_missing_field_userName{"Missing required key user_name"}; - return boost::system::error_code{129, error_missing_field_userName}; - } - - auto userName = boost::json::try_value_to(*jvuserName); - - if (userName.has_error()) - { - return userName.error(); - } - - const auto *jvreason = root.if_contains("reason"); - if (jvreason == nullptr) - { - static const error::ApplicationErrorCategory error_missing_field_reason{ - "Missing required key reason"}; - return boost::system::error_code{129, error_missing_field_reason}; - } - - auto reason = boost::json::try_value_to(*jvreason); - - if (reason.has_error()) - { - return reason.error(); - } - - const auto *jvisPermanent = root.if_contains("is_permanent"); - if (jvisPermanent == nullptr) - { - static const error::ApplicationErrorCategory - error_missing_field_isPermanent{ - "Missing required key is_permanent"}; - return boost::system::error_code{129, error_missing_field_isPermanent}; - } - - auto isPermanent = boost::json::try_value_to(*jvisPermanent); - - if (isPermanent.has_error()) - { - return isPermanent.error(); - } - - const auto *jvbannedAt = root.if_contains("banned_at"); - if (jvbannedAt == nullptr) - { - static const error::ApplicationErrorCategory - error_missing_field_bannedAt{"Missing required key banned_at"}; - return boost::system::error_code{129, error_missing_field_bannedAt}; - } - - auto bannedAt = - boost::json::try_value_to( - *jvbannedAt, AsISO8601()); - - if (bannedAt.has_error()) - { - return bannedAt.error(); - } - - std::optional endsAt = std::nullopt; - const auto *jvendsAt = root.if_contains("ends_at"); - if (jvendsAt != nullptr && !jvendsAt->is_null()) - { - auto tendsAt = - boost::json::try_value_to( - *jvendsAt, AsISO8601()); - - if (tendsAt.has_error()) - { - return tendsAt.error(); - } - endsAt = std::move(tendsAt.value()); - } - - return Event{ - .broadcasterUserID = std::move(broadcasterUserID.value()), - .broadcasterUserLogin = std::move(broadcasterUserLogin.value()), - .broadcasterUserName = std::move(broadcasterUserName.value()), - .moderatorUserID = std::move(moderatorUserID.value()), - .moderatorUserLogin = std::move(moderatorUserLogin.value()), - .moderatorUserName = std::move(moderatorUserName.value()), - .userID = std::move(userID.value()), - .userLogin = std::move(userLogin.value()), - .userName = std::move(userName.value()), - .reason = std::move(reason.value()), - .isPermanent = std::move(isPermanent.value()), - .bannedAt = std::move(bannedAt.value()), - .endsAt = std::move(endsAt), - }; -} - -boost::json::result_for::type tag_invoke( - boost::json::try_value_to_tag, const boost::json::value &jvRoot) -{ - if (!jvRoot.is_object()) - { - static const error::ApplicationErrorCategory errorMustBeObject{ - "Payload must be an object"}; - return boost::system::error_code{129, errorMustBeObject}; - } - const auto &root = jvRoot.get_object(); - - const auto *jvsubscription = root.if_contains("subscription"); - if (jvsubscription == nullptr) - { - static const error::ApplicationErrorCategory - error_missing_field_subscription{ - "Missing required key subscription"}; - return boost::system::error_code{129, error_missing_field_subscription}; - } - - auto subscription = - boost::json::try_value_to(*jvsubscription); - - if (subscription.has_error()) - { - return subscription.error(); - } - - const auto *jvevent = root.if_contains("event"); - if (jvevent == nullptr) - { - static const error::ApplicationErrorCategory error_missing_field_event{ - "Missing required key event"}; - return boost::system::error_code{129, error_missing_field_event}; - } - - auto event = boost::json::try_value_to(*jvevent); - - if (event.has_error()) - { - return event.error(); - } - - return Payload{ - .subscription = std::move(subscription.value()), - .event = std::move(event.value()), - }; -} -// DESERIALIZATION IMPLEMENTATION END - } // namespace chatterino::eventsub::lib::payload::channel_ban::v1 diff --git a/lib/twitch-eventsub-ws/src/payloads/new-payload.sh b/lib/twitch-eventsub-ws/src/payloads/new-payload.sh deleted file mode 100755 index 5020f3c2..00000000 --- a/lib/twitch-eventsub-ws/src/payloads/new-payload.sh +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/env bash - -SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" - -subscription_name="$1" -subscription_version="$2" - -usage() { - >&2 echo "Usage: $0 (e.g. $0 channel.ban v1)" - exit 1 -} - -if [ -z "$subscription_name" ]; then - >&2 echo "Missing subscription name" - usage -fi - -if [ -z "$subscription_version" ]; then - >&2 echo "Missing subscription version" - usage -fi - -# Clean up subscription name - -dashed_subscription_name="$(echo "$subscription_name" | sed 's/\./-/g')" -underscored_subscription_name="$(echo "$subscription_name" | sed 's/\./_/g')" -echo "Subscription name: $subscription_name ($dashed_subscription_name)" -echo "Subscription version: $subscription_version" - -header_file_name="${dashed_subscription_name}-${subscription_version}.hpp" -source_file_name="${dashed_subscription_name}-${subscription_version}.cpp" - -# Write the header file -cat > "$SCRIPT_DIR/../../include/twitch-eventsub-ws/payloads/$header_file_name" << EOF -#pragma once - -#include "twitch-eventsub-ws/payloads/subscription.hpp" - -#include - -#include - -namespace chatterino::eventsub::lib::payload::$underscored_subscription_name::$subscription_version { - -/// json_transform=snake_case -struct Event { - // TODO: Fill in your subscription-specific event here -}; - -struct Payload { - const subscription::Subscription subscription; - - const Event event; -}; - -// DESERIALIZATION DEFINITION START -// DESERIALIZATION DEFINITION END - -} // namespace chatterino::eventsub::lib::payload::$underscored_subscription_name::$subscription_version -EOF - -# Write the source file -cat > "$SCRIPT_DIR/$source_file_name" << EOF -#include "twitch-eventsub-ws/payloads/$header_file_name" - -#include "twitch-eventsub-ws/errors.hpp" - -#include - -namespace chatterino::eventsub::lib::payload::$underscored_subscription_name::$subscription_version { - -// DESERIALIZATION IMPLEMENTATION START -// DESERIALIZATION IMPLEMENTATION END - -} // namespace chatterino::eventsub::lib::payload::$underscored_subscription_name::$subscription_version -EOF - -echo "Steps that are left for you:" -echo "1. Add a virtual method to the Listener class in include/twitch-eventsub-ws/listener.hpp" -echo "2. Add a handler for this subscription in src/session.cpp's NOTIFICATION_HANDLERS" -echo "3. Add payloads/${source_file_name} to src/CMakeLists.txt" diff --git a/lib/twitch-eventsub-ws/src/session.cpp b/lib/twitch-eventsub-ws/src/session.cpp index 751a6ae7..f57cbe3c 100644 --- a/lib/twitch-eventsub-ws/src/session.cpp +++ b/lib/twitch-eventsub-ws/src/session.cpp @@ -1,5 +1,6 @@ #include "twitch-eventsub-ws/session.hpp" +#include "twitch-eventsub-ws/errors.hpp" #include "twitch-eventsub-ws/listener.hpp" #include "twitch-eventsub-ws/messages/metadata.hpp" #include "twitch-eventsub-ws/payloads/channel-ban-v1.hpp"