refactor(eventsub): generate entire files (#5897)
This commit is contained in:
@@ -71,6 +71,16 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
git config --global --add safe.directory '*'
|
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
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
mkdir build
|
mkdir build
|
||||||
@@ -85,9 +95,16 @@ jobs:
|
|||||||
-DCHATTERINO_PLUGINS="$C2_PLUGINS" \
|
-DCHATTERINO_PLUGINS="$C2_PLUGINS" \
|
||||||
-DCMAKE_PREFIX_PATH="$Qt6_DIR/lib/cmake" \
|
-DCMAKE_PREFIX_PATH="$Qt6_DIR/lib/cmake" \
|
||||||
-DCHATTERINO_STATIC_QT_BUILD=On \
|
-DCHATTERINO_STATIC_QT_BUILD=On \
|
||||||
|
-DFORCE_JSON_GENERATION=${{matrix.os == 'ubuntu-24.04' && 'On' || 'Off'}} \
|
||||||
..
|
..
|
||||||
make -j"$(nproc)"
|
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)
|
- name: Package - AppImage (Ubuntu)
|
||||||
if: matrix.build-appimage
|
if: matrix.build-appimage
|
||||||
run: |
|
run: |
|
||||||
@@ -239,6 +256,7 @@ jobs:
|
|||||||
-DCHATTERINO_LTO="$Env:C2_ENABLE_LTO" `
|
-DCHATTERINO_LTO="$Env:C2_ENABLE_LTO" `
|
||||||
-DCHATTERINO_PLUGINS="$Env:C2_PLUGINS" `
|
-DCHATTERINO_PLUGINS="$Env:C2_PLUGINS" `
|
||||||
-DBUILD_WITH_QT6="$Env:C2_BUILD_WITH_QT6" `
|
-DBUILD_WITH_QT6="$Env:C2_BUILD_WITH_QT6" `
|
||||||
|
-DFORCE_JSON_GENERATION=On `
|
||||||
..
|
..
|
||||||
set cl=/MP
|
set cl=/MP
|
||||||
nmake /S /NOLOGO
|
nmake /S /NOLOGO
|
||||||
@@ -308,6 +326,7 @@ jobs:
|
|||||||
-DCHATTERINO_LTO="$C2_ENABLE_LTO" \
|
-DCHATTERINO_LTO="$C2_ENABLE_LTO" \
|
||||||
-DCHATTERINO_PLUGINS="$C2_PLUGINS" \
|
-DCHATTERINO_PLUGINS="$C2_PLUGINS" \
|
||||||
-DBUILD_WITH_QT6="$C2_BUILD_WITH_QT6" \
|
-DBUILD_WITH_QT6="$C2_BUILD_WITH_QT6" \
|
||||||
|
-DFORCE_JSON_GENERATION=Off \
|
||||||
..
|
..
|
||||||
make -j"$(sysctl -n hw.logicalcpu)"
|
make -j"$(sysctl -n hw.logicalcpu)"
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|||||||
+1
-1
@@ -21,7 +21,7 @@
|
|||||||
- Bugfix: Fixed the reply button showing for inline whispers and announcements. (#5863)
|
- Bugfix: Fixed the reply button showing for inline whispers and announcements. (#5863)
|
||||||
- Bugfix: Fixed suspicious user treatment update messages not being searchable. (#5865)
|
- Bugfix: Fixed suspicious user treatment update messages not being searchable. (#5865)
|
||||||
- Bugfix: Ensure miniaudio backend exits even if it doesn't exit cleanly. (#5896)
|
- 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: Highlight checks now use non-capturing groups for the boundaries. (#5784)
|
||||||
- Dev: Removed unused PubSub whisper code. (#5898)
|
- Dev: Removed unused PubSub whisper code. (#5898)
|
||||||
- Dev: Updated Conan dependencies. (#5776)
|
- Dev: Updated Conan dependencies. (#5776)
|
||||||
|
|||||||
@@ -16,20 +16,21 @@ find_package(Boost REQUIRED OPTIONAL_COMPONENTS json)
|
|||||||
find_package(OpenSSL REQUIRED)
|
find_package(OpenSSL REQUIRED)
|
||||||
|
|
||||||
option(BUILD_WITH_QT6 "Build with Qt6" On)
|
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)
|
if (BUILD_WITH_QT6)
|
||||||
set(MAJOR_QT_VERSION "6")
|
set(MAJOR_QT_VERSION "6")
|
||||||
else()
|
else()
|
||||||
set(MAJOR_QT_VERSION "5")
|
set(MAJOR_QT_VERSION "5")
|
||||||
endif()
|
endif()
|
||||||
find_package(Qt${MAJOR_QT_VERSION} REQUIRED COMPONENTS Core)
|
find_package(Qt${MAJOR_QT_VERSION} REQUIRED COMPONENTS Core)
|
||||||
|
find_package(Python3 COMPONENTS Interpreter)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
add_subdirectory(src)
|
|
||||||
|
|
||||||
include(cmake/GenerateJson.cmake)
|
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)
|
feature_summary(WHAT ALL)
|
||||||
|
|||||||
@@ -1,21 +1,12 @@
|
|||||||
## Available scripts
|
## Available scripts
|
||||||
|
|
||||||
- `generate-and-replace-dir.py`
|
- `get-builtin-include-dirs.py`
|
||||||
Usage: `./generate-and-replace-dir.py <path-to-dir-containing-header-files>`
|
Usage: `./get-builtin-include-dirs.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`
|
|
||||||
Prints what builtin include dirs will be used for any of the other scripts.
|
Prints what builtin include dirs will be used for any of the other scripts.
|
||||||
|
|
||||||
- `generate.py`
|
- `generate.py`
|
||||||
Usage: `./generate.py <path-to-header-file>`
|
Usage: `./generate.py [--includes INCLUDES] [--timestamp path] header_path`
|
||||||
Generates definitions & implementations for the given header file and write them to temporary files.
|
Generates definitions & implementations for the given header file and writes them to the appropriate files.
|
||||||
|
|
||||||
- `replace-in-file.py`
|
|
||||||
Usage: `./replace-in-file.py <path-to-header-file> <path-to-source-file>`
|
|
||||||
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.
|
|
||||||
|
|
||||||
## Environment variables
|
## Environment variables
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// This is a header used for testing if the library can parse a file.
|
||||||
|
|
||||||
|
#include <boost/json.hpp>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
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));
|
||||||
@@ -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"
|
||||||
@@ -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]} <path-to-dir>")
|
|
||||||
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()
|
|
||||||
@@ -1,35 +1,95 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
import re
|
||||||
import sys
|
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")
|
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():
|
def main():
|
||||||
init_logging()
|
init_logging(_logging_level())
|
||||||
|
|
||||||
init_clang_cindex()
|
init_clang_cindex()
|
||||||
|
|
||||||
if len(sys.argv) < 2:
|
if len(sys.argv) < 2:
|
||||||
log.error(f"Missing header file argument. Usage: {sys.argv[0]} <path-to-header-file>")
|
log.error(f"usage: {sys.argv[0]} <path-to-header>")
|
||||||
sys.exit(1)
|
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):
|
args = parser.parse_args()
|
||||||
log.debug(f"Write definitions to {path}")
|
|
||||||
f.write(definitions)
|
|
||||||
f.write("\n")
|
|
||||||
print(path)
|
|
||||||
|
|
||||||
with temporary_file() as (f, path):
|
path = Path(args.header_path).resolve()
|
||||||
log.debug(f"Write implementations to {path}")
|
|
||||||
f.write(implementations)
|
if not path.is_file():
|
||||||
f.write("\n")
|
log.error(f"{path} does not exist")
|
||||||
print(path)
|
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__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
from .generate import generate
|
from .generate import generate
|
||||||
from .helpers import get_clang_builtin_include_dirs, init_clang_cindex, temporary_file
|
from .helpers import get_clang_builtin_include_dirs, init_clang_cindex, temporary_file
|
||||||
from .logging import init_logging
|
from .logging import init_logging
|
||||||
from .replace import definition_markers, implementation_markers, replace_in_file
|
from .parse import parse
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"generate",
|
"generate",
|
||||||
"get_clang_builtin_include_dirs",
|
"get_clang_builtin_include_dirs",
|
||||||
"init_clang_cindex",
|
"init_clang_cindex",
|
||||||
"temporary_file",
|
|
||||||
"init_logging",
|
"init_logging",
|
||||||
"definition_markers",
|
"parse",
|
||||||
"implementation_markers",
|
"temporary_file",
|
||||||
"replace_in_file",
|
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -9,50 +9,14 @@ from .helpers import get_clang_builtin_include_dirs
|
|||||||
from .struct import Struct
|
from .struct import Struct
|
||||||
from .enum import Enum
|
from .enum import Enum
|
||||||
from .walker import Walker
|
from .walker import Walker
|
||||||
|
from .parse import parse
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def build_structs(filename: str, additional_includes: list[str] = []) -> List[Struct | Enum]:
|
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")
|
log.debug("Parsing translation unit")
|
||||||
tu = index.parse(filename, args=parse_args, options=parse_options)
|
tu = parse(filename, additional_includes)
|
||||||
root = tu.cursor
|
root = tu.cursor
|
||||||
|
|
||||||
for diag in tu.diagnostics:
|
for diag in tu.diagnostics:
|
||||||
|
|||||||
@@ -13,12 +13,13 @@ log = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class Enum:
|
class Enum:
|
||||||
def __init__(self, name: str) -> None:
|
def __init__(self, name: str, namespace: tuple[str, ...]) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
self.constants: List[EnumConstant] = []
|
self.constants: List[EnumConstant] = []
|
||||||
self.parent: str = ""
|
self.parent: str = ""
|
||||||
self.comment_commands: CommentCommands = []
|
self.comment_commands: CommentCommands = []
|
||||||
self.inner_root: str = ""
|
self.inner_root: str = ""
|
||||||
|
self.namespace = namespace
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def full_name(self) -> str:
|
def full_name(self) -> str:
|
||||||
|
|||||||
@@ -1,22 +1,57 @@
|
|||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from .struct import Struct
|
||||||
|
from .enum import Enum
|
||||||
from .build import build_structs
|
from .build import build_structs
|
||||||
from .format import format_code
|
from .format import format_code
|
||||||
from .helpers import init_clang_cindex, temporary_file
|
|
||||||
from .jinja_env import env
|
from .jinja_env import env
|
||||||
from .logging import init_logging
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
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 <boost/json.hpp>
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
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]:
|
def generate(header_path: str, additional_includes: list[str] = []) -> Tuple[str, str]:
|
||||||
structs = build_structs(header_path, additional_includes)
|
structs = build_structs(header_path, additional_includes)
|
||||||
|
|
||||||
|
rel_header_path = _get_relative_header_path(header_path)
|
||||||
|
|
||||||
log.debug("Generate & format definitions")
|
log.debug("Generate & format definitions")
|
||||||
definitions = format_code("\n\n".join([struct.try_value_to_definition(env) for struct in structs]))
|
definitions = format_code("\n\n".join([struct.try_value_to_definition(env) for struct in structs]))
|
||||||
log.debug("Generate & format implementations")
|
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)
|
return (definitions, implementations)
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import sys
|
|||||||
from colorama import Fore, Style
|
from colorama import Fore, Style
|
||||||
|
|
||||||
|
|
||||||
def init_logging() -> None:
|
def init_logging(level=logging.DEBUG) -> None:
|
||||||
root = logging.getLogger()
|
root = logging.getLogger()
|
||||||
root.setLevel(logging.DEBUG)
|
root.setLevel(level)
|
||||||
|
|
||||||
handler = logging.StreamHandler(sys.stderr)
|
handler = logging.StreamHandler(sys.stderr)
|
||||||
handler.setLevel(logging.DEBUG)
|
handler.setLevel(level)
|
||||||
|
|
||||||
colors = {
|
colors = {
|
||||||
"WARNING": Fore.YELLOW,
|
"WARNING": Fore.YELLOW,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from .membertype import MemberType
|
|||||||
log = logging.getLogger(__name__)
|
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:
|
if namespace:
|
||||||
namespace_str = f"{'::'.join(namespace)}::"
|
namespace_str = f"{'::'.join(namespace)}::"
|
||||||
else:
|
else:
|
||||||
@@ -68,7 +68,7 @@ class Member:
|
|||||||
log.warning(f"Unknown comment command found: {other} with value {value}")
|
log.warning(f"Unknown comment command found: {other} with value {value}")
|
||||||
|
|
||||||
@staticmethod
|
@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
|
assert node.type is not None
|
||||||
|
|
||||||
name = node.spelling
|
name = node.spelling
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -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)
|
|
||||||
@@ -13,12 +13,13 @@ log = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class Struct:
|
class Struct:
|
||||||
def __init__(self, name: str) -> None:
|
def __init__(self, name: str, namespace: tuple[str, ...]) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
self.members: List[Member] = []
|
self.members: List[Member] = []
|
||||||
self.parent: str = ""
|
self.parent: str = ""
|
||||||
self.comment_commands: CommentCommands = []
|
self.comment_commands: CommentCommands = []
|
||||||
self.inner_root: str = ""
|
self.inner_root: str = ""
|
||||||
|
self.namespace = namespace
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def full_name(self) -> str:
|
def full_name(self) -> str:
|
||||||
|
|||||||
@@ -21,12 +21,12 @@ class Walker:
|
|||||||
self.real_filepath = os.path.realpath(self.filename)
|
self.real_filepath = os.path.realpath(self.filename)
|
||||||
self.structs: List[Struct] = []
|
self.structs: List[Struct] = []
|
||||||
self.enums: List[Enum] = []
|
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:
|
def handle_node(self, node: clang.cindex.Cursor, struct: Optional[Struct], enum: Optional[Enum]) -> bool:
|
||||||
match node.kind:
|
match node.kind:
|
||||||
case CursorKind.STRUCT_DECL:
|
case CursorKind.STRUCT_DECL:
|
||||||
new_struct = Struct(node.spelling)
|
new_struct = Struct(node.spelling, self.namespace)
|
||||||
if node.raw_comment is not None:
|
if node.raw_comment is not None:
|
||||||
new_struct.comment_commands = parse_comment_commands(node.raw_comment)
|
new_struct.comment_commands = parse_comment_commands(node.raw_comment)
|
||||||
new_struct.apply_comment_commands(new_struct.comment_commands)
|
new_struct.apply_comment_commands(new_struct.comment_commands)
|
||||||
@@ -41,7 +41,7 @@ class Walker:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
case CursorKind.ENUM_DECL:
|
case CursorKind.ENUM_DECL:
|
||||||
new_enum = Enum(node.spelling)
|
new_enum = Enum(node.spelling, self.namespace)
|
||||||
if node.raw_comment is not None:
|
if node.raw_comment is not None:
|
||||||
new_enum.comment_commands = parse_comment_commands(node.raw_comment)
|
new_enum.comment_commands = parse_comment_commands(node.raw_comment)
|
||||||
new_enum.apply_comment_commands(new_enum.comment_commands)
|
new_enum.apply_comment_commands(new_enum.comment_commands)
|
||||||
@@ -77,9 +77,11 @@ class Walker:
|
|||||||
struct.members.append(member)
|
struct.members.append(member)
|
||||||
|
|
||||||
case CursorKind.NAMESPACE:
|
case CursorKind.NAMESPACE:
|
||||||
self.namespace.append(node.spelling)
|
self.namespace += (node.spelling,)
|
||||||
# Ignore namespaces
|
for child in node.get_children():
|
||||||
pass
|
self.walk(child)
|
||||||
|
self.namespace = self.namespace[:-1]
|
||||||
|
return True
|
||||||
|
|
||||||
case CursorKind.FUNCTION_DECL:
|
case CursorKind.FUNCTION_DECL:
|
||||||
# Ignore function declarations
|
# Ignore function declarations
|
||||||
|
|||||||
@@ -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]} <path-to-header-file> <path-to-source-file>")
|
|
||||||
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()
|
|
||||||
@@ -1,54 +1,190 @@
|
|||||||
function(generate_json_impls)
|
cmake_path(GET CMAKE_CURRENT_LIST_DIR PARENT_PATH _eventsub_lib_root)
|
||||||
set(_one_value_opts SRC_TARGET GEN_TARGET)
|
|
||||||
|
# _validate_all_args_are_passed(<prefix> [<required-args>...])
|
||||||
|
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
|
cmake_parse_arguments(PARSE_ARGV 0 arg
|
||||||
"" "${_one_value_opts}" ""
|
"" "VENV_PATH;REQUIREMENTS;OUT_PYTHON_EXE" ""
|
||||||
)
|
)
|
||||||
if (DEFINED arg_KEYWORDS_MISSING_VALUES)
|
_validate_all_args_are_parsed(arg VENV_PATH REQUIREMENTS OUT_PYTHON_EXE)
|
||||||
message(FATAL_ERROR "generate_json_impls: Missing ${arg_KEYWORDS_MISSING_VALUES}")
|
|
||||||
|
if(NOT Python3_EXECUTABLE)
|
||||||
|
message(FATAL_ERROR "Python must be available to create a venv")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(_venv_path "${CMAKE_CURRENT_BINARY_DIR}/eventsub-venv")
|
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")
|
message(STATUS "Creating venv in ${arg_VENV_PATH}")
|
||||||
cmake_path(SET _gen_script NORMALIZE "${CMAKE_CURRENT_LIST_DIR}/ast/generate-and-replace-dir.py")
|
execute_process(
|
||||||
cmake_path(SET _search_dir NORMALIZE "${CMAKE_CURRENT_LIST_DIR}/include/twitch-eventsub-ws")
|
COMMAND "${Python3_EXECUTABLE}" -m venv "${arg_VENV_PATH}"
|
||||||
|
RESULT_VARIABLE _venv_output
|
||||||
get_target_property(_inc_dirs ${arg_SRC_TARGET} INCLUDE_DIRECTORIES)
|
)
|
||||||
list(APPEND _inc_dirs ${Boost_INCLUDE_DIRS})
|
if(NOT _venv_output EQUAL 0)
|
||||||
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)
|
|
||||||
return()
|
return()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_custom_target(${arg_GEN_TARGET}_python_venv_create
|
# update the environment with VIRTUAL_ENV variable (mimic the activate script)
|
||||||
COMMENT "Creating python virtual environment"
|
set(ENV{VIRTUAL_ENV} "${arg_VENV_PATH}")
|
||||||
COMMAND "${Python3_EXECUTABLE}" -m venv "${_venv_path}"
|
# change the context of the search
|
||||||
VERBATIM
|
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
|
message(STATUS "Installing requirements from ${arg_REQUIREMENTS}")
|
||||||
COMMENT "Installing python dependencies"
|
execute_process(
|
||||||
COMMAND "${_venv_pip_path}" install -r "${_requirements}"
|
COMMAND "${Python3_EXECUTABLE}" -m pip install -r "${arg_REQUIREMENTS}"
|
||||||
DEPENDS "${_requirements}" ${arg_GEN_TARGET}_python_venv_create
|
COMMAND_ERROR_IS_FATAL ANY
|
||||||
VERBATIM
|
OUTPUT_QUIET
|
||||||
|
ERROR_QUIET
|
||||||
)
|
)
|
||||||
|
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${arg_REQUIREMENTS}")
|
||||||
|
|
||||||
add_custom_target(${arg_GEN_TARGET}
|
set(${arg_OUT_PYTHON_EXE} "${Python3_EXECUTABLE}" PARENT_SCOPE)
|
||||||
COMMENT "Generating JSON deserializers"
|
endfunction()
|
||||||
DEPENDS ${arg_GEN_TARGET}_python_venv_install
|
|
||||||
COMMAND "${_venv_python_path}" "${_gen_script}" "${_search_dir}/messages" --includes "${_inc_dir}"
|
function(_setup_and_check_venv)
|
||||||
COMMAND "${_venv_python_path}" "${_gen_script}" "${_search_dir}/payloads" --includes "${_inc_dir}"
|
cmake_parse_arguments(PARSE_ARGV 0 arg
|
||||||
VERBATIM
|
"" "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 <out-var>]
|
||||||
|
# [BASE_DIRECTORY <path>]
|
||||||
|
# [FORCE <force>]
|
||||||
|
# HEADERS <header>...
|
||||||
|
# )
|
||||||
|
#
|
||||||
|
# `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 (<build>/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()
|
endfunction()
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "twitch-eventsub-ws/errors.hpp"
|
|
||||||
|
|
||||||
#include <boost/json.hpp>
|
#include <boost/json.hpp>
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
@@ -33,9 +31,6 @@ struct Metadata {
|
|||||||
const std::optional<std::string> subscriptionVersion;
|
const std::optional<std::string> subscriptionVersion;
|
||||||
};
|
};
|
||||||
|
|
||||||
// DESERIALIZATION DEFINITION START
|
#include "twitch-eventsub-ws/messages/metadata.inc"
|
||||||
boost::json::result_for<Metadata, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Metadata>, const boost::json::value &jvRoot);
|
|
||||||
// DESERIALIZATION DEFINITION END
|
|
||||||
|
|
||||||
} // namespace chatterino::eventsub::lib::messages
|
} // namespace chatterino::eventsub::lib::messages
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
boost::json::result_for<Metadata, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Metadata>, const boost::json::value &jvRoot);
|
||||||
@@ -97,12 +97,6 @@ struct Payload {
|
|||||||
const Event event;
|
const Event event;
|
||||||
};
|
};
|
||||||
|
|
||||||
// DESERIALIZATION DEFINITION START
|
#include "twitch-eventsub-ws/payloads/channel-ban-v1.inc"
|
||||||
boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Event>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Payload, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Payload>, const boost::json::value &jvRoot);
|
|
||||||
// DESERIALIZATION DEFINITION END
|
|
||||||
|
|
||||||
} // namespace chatterino::eventsub::lib::payload::channel_ban::v1
|
} // namespace chatterino::eventsub::lib::payload::channel_ban::v1
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Event>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Payload, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Payload>, const boost::json::value &jvRoot);
|
||||||
+1
-32
@@ -149,37 +149,6 @@ struct Payload {
|
|||||||
const Event event;
|
const Event event;
|
||||||
};
|
};
|
||||||
|
|
||||||
// DESERIALIZATION DEFINITION START
|
#include "twitch-eventsub-ws/payloads/channel-chat-message-v1.inc"
|
||||||
boost::json::result_for<Badge, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Badge>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Cheermote, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Cheermote>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Emote, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Emote>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Mention, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Mention>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<MessageFragment, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<MessageFragment>,
|
|
||||||
const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Message, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Message>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Cheer, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Cheer>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Reply, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Reply>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Event>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Payload, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Payload>, const boost::json::value &jvRoot);
|
|
||||||
// DESERIALIZATION DEFINITION END
|
|
||||||
|
|
||||||
} // namespace chatterino::eventsub::lib::payload::channel_chat_message::v1
|
} // namespace chatterino::eventsub::lib::payload::channel_chat_message::v1
|
||||||
|
|||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
boost::json::result_for<Badge, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Badge>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Cheermote, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Cheermote>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Emote, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Emote>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Mention, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Mention>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<MessageFragment, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<MessageFragment>,
|
||||||
|
const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Message, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Message>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Cheer, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Cheer>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Reply, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Reply>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Event>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Payload, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Payload>, const boost::json::value &jvRoot);
|
||||||
+1
-76
@@ -187,81 +187,6 @@ struct Payload {
|
|||||||
const Event event;
|
const Event event;
|
||||||
};
|
};
|
||||||
|
|
||||||
// DESERIALIZATION DEFINITION START
|
#include "twitch-eventsub-ws/payloads/channel-chat-notification-v1.inc"
|
||||||
boost::json::result_for<Badge, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Badge>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Cheermote, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Cheermote>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Emote, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Emote>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Mention, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Mention>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<MessageFragment, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<MessageFragment>,
|
|
||||||
const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Subcription, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Subcription>,
|
|
||||||
const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Resubscription, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Resubscription>,
|
|
||||||
const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<GiftSubscription, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<GiftSubscription>,
|
|
||||||
const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<CommunityGiftSubscription, boost::json::value>::type
|
|
||||||
tag_invoke(boost::json::try_value_to_tag<CommunityGiftSubscription>,
|
|
||||||
const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<GiftPaidUpgrade, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<GiftPaidUpgrade>,
|
|
||||||
const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<PrimePaidUpgrade, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<PrimePaidUpgrade>,
|
|
||||||
const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Raid, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Raid>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Unraid, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Unraid>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<PayItForward, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<PayItForward>,
|
|
||||||
const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Announcement, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Announcement>,
|
|
||||||
const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<CharityDonationAmount, boost::json::value>::type
|
|
||||||
tag_invoke(boost::json::try_value_to_tag<CharityDonationAmount>,
|
|
||||||
const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<CharityDonation, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<CharityDonation>,
|
|
||||||
const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<BitsBadgeTier, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<BitsBadgeTier>,
|
|
||||||
const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Message, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Message>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Event>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Payload, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Payload>, const boost::json::value &jvRoot);
|
|
||||||
// DESERIALIZATION DEFINITION END
|
|
||||||
|
|
||||||
} // namespace chatterino::eventsub::lib::payload::channel_chat_notification::v1
|
} // namespace chatterino::eventsub::lib::payload::channel_chat_notification::v1
|
||||||
|
|||||||
+74
@@ -0,0 +1,74 @@
|
|||||||
|
boost::json::result_for<Badge, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Badge>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Cheermote, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Cheermote>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Emote, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Emote>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Mention, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Mention>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<MessageFragment, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<MessageFragment>,
|
||||||
|
const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Subcription, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Subcription>,
|
||||||
|
const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Resubscription, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Resubscription>,
|
||||||
|
const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<GiftSubscription, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<GiftSubscription>,
|
||||||
|
const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<CommunityGiftSubscription, boost::json::value>::type
|
||||||
|
tag_invoke(boost::json::try_value_to_tag<CommunityGiftSubscription>,
|
||||||
|
const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<GiftPaidUpgrade, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<GiftPaidUpgrade>,
|
||||||
|
const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<PrimePaidUpgrade, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<PrimePaidUpgrade>,
|
||||||
|
const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Raid, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Raid>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Unraid, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Unraid>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<PayItForward, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<PayItForward>,
|
||||||
|
const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Announcement, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Announcement>,
|
||||||
|
const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<CharityDonationAmount, boost::json::value>::type
|
||||||
|
tag_invoke(boost::json::try_value_to_tag<CharityDonationAmount>,
|
||||||
|
const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<CharityDonation, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<CharityDonation>,
|
||||||
|
const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<BitsBadgeTier, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<BitsBadgeTier>,
|
||||||
|
const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Message, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Message>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Event>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Payload, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Payload>, const boost::json::value &jvRoot);
|
||||||
@@ -388,65 +388,6 @@ struct Payload {
|
|||||||
Event event;
|
Event event;
|
||||||
};
|
};
|
||||||
|
|
||||||
// DESERIALIZATION DEFINITION START
|
#include "twitch-eventsub-ws/payloads/channel-moderate-v2.inc"
|
||||||
boost::json::result_for<Action, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Action>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Followers, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Followers>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Slow, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Slow>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Vip, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Vip>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Unvip, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Unvip>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Mod, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Mod>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Unmod, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Unmod>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Ban, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Ban>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Unban, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Unban>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Timeout, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Timeout>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Untimeout, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Untimeout>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Raid, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Raid>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Unraid, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Unraid>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Delete, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Delete>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<AutomodTerms, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<AutomodTerms>,
|
|
||||||
const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<UnbanRequest, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<UnbanRequest>,
|
|
||||||
const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Warn, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Warn>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Event>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Payload, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Payload>, const boost::json::value &jvRoot);
|
|
||||||
// DESERIALIZATION DEFINITION END
|
|
||||||
|
|
||||||
} // namespace chatterino::eventsub::lib::payload::channel_moderate::v2
|
} // namespace chatterino::eventsub::lib::payload::channel_moderate::v2
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
boost::json::result_for<Action, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Action>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Followers, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Followers>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Slow, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Slow>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Vip, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Vip>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Unvip, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Unvip>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Mod, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Mod>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Unmod, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Unmod>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Ban, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Ban>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Unban, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Unban>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Timeout, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Timeout>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Untimeout, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Untimeout>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Raid, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Raid>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Unraid, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Unraid>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Delete, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Delete>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<AutomodTerms, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<AutomodTerms>,
|
||||||
|
const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<UnbanRequest, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<UnbanRequest>,
|
||||||
|
const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Warn, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Warn>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Event>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Payload, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Payload>, const boost::json::value &jvRoot);
|
||||||
@@ -38,12 +38,6 @@ struct Payload {
|
|||||||
const Event event;
|
const Event event;
|
||||||
};
|
};
|
||||||
|
|
||||||
// DESERIALIZATION DEFINITION START
|
#include "twitch-eventsub-ws/payloads/channel-update-v1.inc"
|
||||||
boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Event>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Payload, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Payload>, const boost::json::value &jvRoot);
|
|
||||||
// DESERIALIZATION DEFINITION END
|
|
||||||
|
|
||||||
} // namespace chatterino::eventsub::lib::payload::channel_update::v1
|
} // namespace chatterino::eventsub::lib::payload::channel_update::v1
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Event>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Payload, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Payload>, const boost::json::value &jvRoot);
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "twitch-eventsub-ws/errors.hpp"
|
|
||||||
|
|
||||||
#include <boost/json.hpp>
|
#include <boost/json.hpp>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -28,9 +26,6 @@ struct Payload {
|
|||||||
const std::string id;
|
const std::string id;
|
||||||
};
|
};
|
||||||
|
|
||||||
// DESERIALIZATION DEFINITION START
|
#include "twitch-eventsub-ws/payloads/session-welcome.inc"
|
||||||
boost::json::result_for<Payload, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Payload>, const boost::json::value &jvRoot);
|
|
||||||
// DESERIALIZATION DEFINITION END
|
|
||||||
|
|
||||||
} // namespace chatterino::eventsub::lib::payload::session_welcome
|
} // namespace chatterino::eventsub::lib::payload::session_welcome
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
boost::json::result_for<Payload, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Payload>, const boost::json::value &jvRoot);
|
||||||
@@ -24,12 +24,6 @@ struct Payload {
|
|||||||
const Event event;
|
const Event event;
|
||||||
};
|
};
|
||||||
|
|
||||||
// DESERIALIZATION DEFINITION START
|
#include "twitch-eventsub-ws/payloads/stream-offline-v1.inc"
|
||||||
boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Event>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Payload, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Payload>, const boost::json::value &jvRoot);
|
|
||||||
// DESERIALIZATION DEFINITION END
|
|
||||||
|
|
||||||
} // namespace chatterino::eventsub::lib::payload::stream_offline::v1
|
} // namespace chatterino::eventsub::lib::payload::stream_offline::v1
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Event>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Payload, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Payload>, const boost::json::value &jvRoot);
|
||||||
@@ -34,12 +34,6 @@ struct Payload {
|
|||||||
const Event event;
|
const Event event;
|
||||||
};
|
};
|
||||||
|
|
||||||
// DESERIALIZATION DEFINITION START
|
#include "twitch-eventsub-ws/payloads/stream-online-v1.inc"
|
||||||
boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Event>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Payload, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Payload>, const boost::json::value &jvRoot);
|
|
||||||
// DESERIALIZATION DEFINITION END
|
|
||||||
|
|
||||||
} // namespace chatterino::eventsub::lib::payload::stream_online::v1
|
} // namespace chatterino::eventsub::lib::payload::stream_online::v1
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Event>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Payload, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Payload>, const boost::json::value &jvRoot);
|
||||||
@@ -54,13 +54,6 @@ struct Subscription {
|
|||||||
const int cost;
|
const int cost;
|
||||||
};
|
};
|
||||||
|
|
||||||
// DESERIALIZATION DEFINITION START
|
#include "twitch-eventsub-ws/payloads/subscription.inc"
|
||||||
boost::json::result_for<Transport, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Transport>, const boost::json::value &jvRoot);
|
|
||||||
|
|
||||||
boost::json::result_for<Subscription, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Subscription>,
|
|
||||||
const boost::json::value &jvRoot);
|
|
||||||
// DESERIALIZATION DEFINITION END
|
|
||||||
|
|
||||||
} // namespace chatterino::eventsub::lib::payload::subscription
|
} // namespace chatterino::eventsub::lib::payload::subscription
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
boost::json::result_for<Transport, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Transport>, const boost::json::value &jvRoot);
|
||||||
|
|
||||||
|
boost::json::result_for<Subscription, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Subscription>,
|
||||||
|
const boost::json::value &jvRoot);
|
||||||
@@ -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
|
set(SOURCE_FILES
|
||||||
session.cpp
|
session.cpp
|
||||||
|
|
||||||
@@ -6,20 +24,11 @@ set(SOURCE_FILES
|
|||||||
json.cpp
|
json.cpp
|
||||||
string.cpp
|
string.cpp
|
||||||
|
|
||||||
payloads/subscription.cpp
|
# Subscription types (only additional functions)
|
||||||
payloads/session-welcome.cpp
|
|
||||||
|
|
||||||
# Subscription types
|
|
||||||
payloads/channel-ban-v1.cpp
|
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
|
# Add your new subscription type source file above this line
|
||||||
|
|
||||||
messages/metadata.cpp
|
${eventsub_generated_sources}
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(${PROJECT_NAME} STATIC ${SOURCE_FILES})
|
add_library(${PROJECT_NAME} STATIC ${SOURCE_FILES})
|
||||||
@@ -43,13 +52,6 @@ target_compile_definitions(${PROJECT_NAME} PUBLIC
|
|||||||
$<$<BOOL:${MSVC}>:BOOST_CONTAINER_NO_LIB>
|
$<$<BOOL:${MSVC}>: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)
|
if (MSVC)
|
||||||
# Add bigobj
|
# Add bigobj
|
||||||
target_compile_options(${PROJECT_NAME} PRIVATE /EHsc /bigobj)
|
target_compile_options(${PROJECT_NAME} PRIVATE /EHsc /bigobj)
|
||||||
|
|||||||
+3
-3
@@ -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/errors.hpp"
|
||||||
|
#include "twitch-eventsub-ws/messages/metadata.hpp"
|
||||||
|
|
||||||
#include <boost/json.hpp>
|
#include <boost/json.hpp>
|
||||||
|
|
||||||
namespace chatterino::eventsub::lib::messages {
|
namespace chatterino::eventsub::lib::messages {
|
||||||
|
|
||||||
// DESERIALIZATION IMPLEMENTATION START
|
|
||||||
boost::json::result_for<Metadata, boost::json::value>::type tag_invoke(
|
boost::json::result_for<Metadata, boost::json::value>::type tag_invoke(
|
||||||
boost::json::try_value_to_tag<Metadata>, const boost::json::value &jvRoot)
|
boost::json::try_value_to_tag<Metadata>, const boost::json::value &jvRoot)
|
||||||
{
|
{
|
||||||
@@ -104,6 +105,5 @@ boost::json::result_for<Metadata, boost::json::value>::type tag_invoke(
|
|||||||
.subscriptionVersion = std::move(subscriptionVersion),
|
.subscriptionVersion = std::move(subscriptionVersion),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// DESERIALIZATION IMPLEMENTATION END
|
|
||||||
|
|
||||||
} // namespace chatterino::eventsub::lib::messages
|
} // namespace chatterino::eventsub::lib::messages
|
||||||
@@ -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 <boost/json.hpp>
|
||||||
|
|
||||||
|
namespace chatterino::eventsub::lib::payload::channel_ban::v1 {
|
||||||
|
|
||||||
|
boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Event>, 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<std::string>(*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<std::string>(*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<std::string>(*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<std::string>(*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<std::string>(*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<std::string>(*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<std::string>(*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<std::string>(*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<std::string>(*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<std::string>(*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<bool>(*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<std::chrono::system_clock::time_point>(
|
||||||
|
*jvbannedAt, AsISO8601());
|
||||||
|
|
||||||
|
if (bannedAt.has_error())
|
||||||
|
{
|
||||||
|
return bannedAt.error();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::chrono::system_clock::time_point> endsAt = std::nullopt;
|
||||||
|
const auto *jvendsAt = root.if_contains("ends_at");
|
||||||
|
if (jvendsAt != nullptr && !jvendsAt->is_null())
|
||||||
|
{
|
||||||
|
auto tendsAt =
|
||||||
|
boost::json::try_value_to<std::chrono::system_clock::time_point>(
|
||||||
|
*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<Payload, boost::json::value>::type tag_invoke(
|
||||||
|
boost::json::try_value_to_tag<Payload>, 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<subscription::Subscription>(*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<Event>(*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
|
||||||
+3
-3
@@ -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/errors.hpp"
|
||||||
|
#include "twitch-eventsub-ws/payloads/channel-chat-message-v1.hpp"
|
||||||
|
|
||||||
#include <boost/json.hpp>
|
#include <boost/json.hpp>
|
||||||
|
|
||||||
namespace chatterino::eventsub::lib::payload::channel_chat_message::v1 {
|
namespace chatterino::eventsub::lib::payload::channel_chat_message::v1 {
|
||||||
|
|
||||||
// DESERIALIZATION IMPLEMENTATION START
|
|
||||||
boost::json::result_for<Badge, boost::json::value>::type tag_invoke(
|
boost::json::result_for<Badge, boost::json::value>::type tag_invoke(
|
||||||
boost::json::try_value_to_tag<Badge>, const boost::json::value &jvRoot)
|
boost::json::try_value_to_tag<Badge>, const boost::json::value &jvRoot)
|
||||||
{
|
{
|
||||||
@@ -929,6 +930,5 @@ boost::json::result_for<Payload, boost::json::value>::type tag_invoke(
|
|||||||
.event = std::move(event.value()),
|
.event = std::move(event.value()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// DESERIALIZATION IMPLEMENTATION END
|
|
||||||
|
|
||||||
} // namespace chatterino::eventsub::lib::payload::channel_chat_message::v1
|
} // namespace chatterino::eventsub::lib::payload::channel_chat_message::v1
|
||||||
+3
-3
@@ -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/errors.hpp"
|
||||||
|
#include "twitch-eventsub-ws/payloads/channel-chat-notification-v1.hpp"
|
||||||
|
|
||||||
#include <boost/json.hpp>
|
#include <boost/json.hpp>
|
||||||
|
|
||||||
namespace chatterino::eventsub::lib::payload::channel_chat_notification::v1 {
|
namespace chatterino::eventsub::lib::payload::channel_chat_notification::v1 {
|
||||||
|
|
||||||
// DESERIALIZATION IMPLEMENTATION START
|
|
||||||
boost::json::result_for<Badge, boost::json::value>::type tag_invoke(
|
boost::json::result_for<Badge, boost::json::value>::type tag_invoke(
|
||||||
boost::json::try_value_to_tag<Badge>, const boost::json::value &jvRoot)
|
boost::json::try_value_to_tag<Badge>, const boost::json::value &jvRoot)
|
||||||
{
|
{
|
||||||
@@ -1840,6 +1841,5 @@ boost::json::result_for<Payload, boost::json::value>::type tag_invoke(
|
|||||||
.event = std::move(event.value()),
|
.event = std::move(event.value()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// DESERIALIZATION IMPLEMENTATION END
|
|
||||||
|
|
||||||
} // namespace chatterino::eventsub::lib::payload::channel_chat_notification::v1
|
} // namespace chatterino::eventsub::lib::payload::channel_chat_notification::v1
|
||||||
+3
-5
@@ -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/errors.hpp"
|
||||||
|
#include "twitch-eventsub-ws/payloads/channel-moderate-v2.hpp"
|
||||||
|
|
||||||
#include <boost/json.hpp>
|
#include <boost/json.hpp>
|
||||||
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
namespace chatterino::eventsub::lib::payload::channel_moderate::v2 {
|
namespace chatterino::eventsub::lib::payload::channel_moderate::v2 {
|
||||||
|
|
||||||
// DESERIALIZATION IMPLEMENTATION START
|
|
||||||
boost::json::result_for<Action, boost::json::value>::type tag_invoke(
|
boost::json::result_for<Action, boost::json::value>::type tag_invoke(
|
||||||
boost::json::try_value_to_tag<Action>, const boost::json::value &jvRoot)
|
boost::json::try_value_to_tag<Action>, const boost::json::value &jvRoot)
|
||||||
{
|
{
|
||||||
@@ -1835,6 +1834,5 @@ boost::json::result_for<Payload, boost::json::value>::type tag_invoke(
|
|||||||
.event = std::move(event.value()),
|
.event = std::move(event.value()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// DESERIALIZATION IMPLEMENTATION END
|
|
||||||
|
|
||||||
} // namespace chatterino::eventsub::lib::payload::channel_moderate::v2
|
} // namespace chatterino::eventsub::lib::payload::channel_moderate::v2
|
||||||
+3
-3
@@ -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/errors.hpp"
|
||||||
|
#include "twitch-eventsub-ws/payloads/channel-update-v1.hpp"
|
||||||
|
|
||||||
#include <boost/json.hpp>
|
#include <boost/json.hpp>
|
||||||
|
|
||||||
namespace chatterino::eventsub::lib::payload::channel_update::v1 {
|
namespace chatterino::eventsub::lib::payload::channel_update::v1 {
|
||||||
|
|
||||||
// DESERIALIZATION IMPLEMENTATION START
|
|
||||||
boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
||||||
boost::json::try_value_to_tag<Event>, const boost::json::value &jvRoot)
|
boost::json::try_value_to_tag<Event>, const boost::json::value &jvRoot)
|
||||||
{
|
{
|
||||||
@@ -210,6 +211,5 @@ boost::json::result_for<Payload, boost::json::value>::type tag_invoke(
|
|||||||
.event = std::move(event.value()),
|
.event = std::move(event.value()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// DESERIALIZATION IMPLEMENTATION END
|
|
||||||
|
|
||||||
} // namespace chatterino::eventsub::lib::payload::channel_update::v1
|
} // namespace chatterino::eventsub::lib::payload::channel_update::v1
|
||||||
+3
-3
@@ -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/errors.hpp"
|
||||||
|
#include "twitch-eventsub-ws/payloads/session-welcome.hpp"
|
||||||
|
|
||||||
#include <boost/json.hpp>
|
#include <boost/json.hpp>
|
||||||
|
|
||||||
namespace chatterino::eventsub::lib::payload::session_welcome {
|
namespace chatterino::eventsub::lib::payload::session_welcome {
|
||||||
|
|
||||||
// DESERIALIZATION IMPLEMENTATION START
|
|
||||||
boost::json::result_for<Payload, boost::json::value>::type tag_invoke(
|
boost::json::result_for<Payload, boost::json::value>::type tag_invoke(
|
||||||
boost::json::try_value_to_tag<Payload>, const boost::json::value &jvRoot)
|
boost::json::try_value_to_tag<Payload>, const boost::json::value &jvRoot)
|
||||||
{
|
{
|
||||||
@@ -52,6 +53,5 @@ boost::json::result_for<Payload, boost::json::value>::type tag_invoke(
|
|||||||
.id = std::move(id.value()),
|
.id = std::move(id.value()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// DESERIALIZATION IMPLEMENTATION END
|
|
||||||
|
|
||||||
} // namespace chatterino::eventsub::lib::payload::session_welcome
|
} // namespace chatterino::eventsub::lib::payload::session_welcome
|
||||||
+3
-3
@@ -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/errors.hpp"
|
||||||
|
#include "twitch-eventsub-ws/payloads/stream-offline-v1.hpp"
|
||||||
|
|
||||||
#include <boost/json.hpp>
|
#include <boost/json.hpp>
|
||||||
|
|
||||||
namespace chatterino::eventsub::lib::payload::stream_offline::v1 {
|
namespace chatterino::eventsub::lib::payload::stream_offline::v1 {
|
||||||
|
|
||||||
// DESERIALIZATION IMPLEMENTATION START
|
|
||||||
boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
||||||
boost::json::try_value_to_tag<Event>, const boost::json::value &jvRoot)
|
boost::json::try_value_to_tag<Event>, const boost::json::value &jvRoot)
|
||||||
{
|
{
|
||||||
@@ -129,6 +130,5 @@ boost::json::result_for<Payload, boost::json::value>::type tag_invoke(
|
|||||||
.event = std::move(event.value()),
|
.event = std::move(event.value()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// DESERIALIZATION IMPLEMENTATION END
|
|
||||||
|
|
||||||
} // namespace chatterino::eventsub::lib::payload::stream_offline::v1
|
} // namespace chatterino::eventsub::lib::payload::stream_offline::v1
|
||||||
+3
-3
@@ -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/errors.hpp"
|
||||||
|
#include "twitch-eventsub-ws/payloads/stream-online-v1.hpp"
|
||||||
|
|
||||||
#include <boost/json.hpp>
|
#include <boost/json.hpp>
|
||||||
|
|
||||||
namespace chatterino::eventsub::lib::payload::stream_online::v1 {
|
namespace chatterino::eventsub::lib::payload::stream_online::v1 {
|
||||||
|
|
||||||
// DESERIALIZATION IMPLEMENTATION START
|
|
||||||
boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
||||||
boost::json::try_value_to_tag<Event>, const boost::json::value &jvRoot)
|
boost::json::try_value_to_tag<Event>, const boost::json::value &jvRoot)
|
||||||
{
|
{
|
||||||
@@ -177,6 +178,5 @@ boost::json::result_for<Payload, boost::json::value>::type tag_invoke(
|
|||||||
.event = std::move(event.value()),
|
.event = std::move(event.value()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// DESERIALIZATION IMPLEMENTATION END
|
|
||||||
|
|
||||||
} // namespace chatterino::eventsub::lib::payload::stream_online::v1
|
} // namespace chatterino::eventsub::lib::payload::stream_online::v1
|
||||||
+3
-3
@@ -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/errors.hpp"
|
||||||
|
#include "twitch-eventsub-ws/payloads/subscription.hpp"
|
||||||
|
|
||||||
#include <boost/json.hpp>
|
#include <boost/json.hpp>
|
||||||
|
|
||||||
namespace chatterino::eventsub::lib::payload::subscription {
|
namespace chatterino::eventsub::lib::payload::subscription {
|
||||||
|
|
||||||
// DESERIALIZATION IMPLEMENTATION START
|
|
||||||
boost::json::result_for<Transport, boost::json::value>::type tag_invoke(
|
boost::json::result_for<Transport, boost::json::value>::type tag_invoke(
|
||||||
boost::json::try_value_to_tag<Transport>, const boost::json::value &jvRoot)
|
boost::json::try_value_to_tag<Transport>, const boost::json::value &jvRoot)
|
||||||
{
|
{
|
||||||
@@ -181,6 +182,5 @@ boost::json::result_for<Subscription, boost::json::value>::type tag_invoke(
|
|||||||
.cost = std::move(cost.value()),
|
.cost = std::move(cost.value()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// DESERIALIZATION IMPLEMENTATION END
|
|
||||||
|
|
||||||
} // namespace chatterino::eventsub::lib::payload::subscription
|
} // namespace chatterino::eventsub::lib::payload::subscription
|
||||||
@@ -1,10 +1,5 @@
|
|||||||
#include "twitch-eventsub-ws/payloads/channel-ban-v1.hpp"
|
#include "twitch-eventsub-ws/payloads/channel-ban-v1.hpp"
|
||||||
|
|
||||||
#include "twitch-eventsub-ws/chrono.hpp"
|
|
||||||
#include "twitch-eventsub-ws/errors.hpp"
|
|
||||||
|
|
||||||
#include <boost/json.hpp>
|
|
||||||
|
|
||||||
namespace chatterino::eventsub::lib::payload::channel_ban::v1 {
|
namespace chatterino::eventsub::lib::payload::channel_ban::v1 {
|
||||||
|
|
||||||
std::chrono::system_clock::duration Event::timeoutDuration() const
|
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;
|
return *this->endsAt - this->bannedAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// DESERIALIZATION IMPLEMENTATION START
|
|
||||||
boost::json::result_for<Event, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Event>, 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<std::string>(*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<std::string>(*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<std::string>(*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<std::string>(*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<std::string>(*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<std::string>(*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<std::string>(*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<std::string>(*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<std::string>(*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<std::string>(*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<bool>(*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<std::chrono::system_clock::time_point>(
|
|
||||||
*jvbannedAt, AsISO8601());
|
|
||||||
|
|
||||||
if (bannedAt.has_error())
|
|
||||||
{
|
|
||||||
return bannedAt.error();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::chrono::system_clock::time_point> endsAt = std::nullopt;
|
|
||||||
const auto *jvendsAt = root.if_contains("ends_at");
|
|
||||||
if (jvendsAt != nullptr && !jvendsAt->is_null())
|
|
||||||
{
|
|
||||||
auto tendsAt =
|
|
||||||
boost::json::try_value_to<std::chrono::system_clock::time_point>(
|
|
||||||
*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<Payload, boost::json::value>::type tag_invoke(
|
|
||||||
boost::json::try_value_to_tag<Payload>, 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<subscription::Subscription>(*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<Event>(*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
|
} // namespace chatterino::eventsub::lib::payload::channel_ban::v1
|
||||||
|
|||||||
@@ -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 <name> <version> (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 <boost/json.hpp>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
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 <boost/json.hpp>
|
|
||||||
|
|
||||||
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"
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "twitch-eventsub-ws/session.hpp"
|
#include "twitch-eventsub-ws/session.hpp"
|
||||||
|
|
||||||
|
#include "twitch-eventsub-ws/errors.hpp"
|
||||||
#include "twitch-eventsub-ws/listener.hpp"
|
#include "twitch-eventsub-ws/listener.hpp"
|
||||||
#include "twitch-eventsub-ws/messages/metadata.hpp"
|
#include "twitch-eventsub-ws/messages/metadata.hpp"
|
||||||
#include "twitch-eventsub-ws/payloads/channel-ban-v1.hpp"
|
#include "twitch-eventsub-ws/payloads/channel-ban-v1.hpp"
|
||||||
|
|||||||
Reference in New Issue
Block a user