Compare commits
20 commits
9b76d2baad
...
285ee84d09
Author | SHA1 | Date | |
---|---|---|---|
285ee84d09 | |||
0dcf9a0ead | |||
ae1f5c2d24 | |||
ed4276338d | |||
ab3fc26003 | |||
a98f2e8552 | |||
eda4f818a9 | |||
04abea4195 | |||
405a3100e1 | |||
15d4fd00d0 | |||
652fecf4b0 | |||
7315fda512 | |||
72f96c2a86 | |||
8aecc897d7 | |||
5942f366f9 | |||
d9d301167c | |||
89f3165551 | |||
65cae2727f | |||
b3949463df | |||
efba236215 |
24 changed files with 918 additions and 154 deletions
|
@ -3,13 +3,13 @@
|
||||||
# Url: https://hub.docker.com/layers/library/archlinux/latest/images/sha256-a10e51dd0694d6c4142754e9d06cbce7baf91ace8031a30df37064d1091ab414?context=explore
|
# Url: https://hub.docker.com/layers/library/archlinux/latest/images/sha256-a10e51dd0694d6c4142754e9d06cbce7baf91ace8031a30df37064d1091ab414?context=explore
|
||||||
FROM archlinux@sha256:a10e51dd0694d6c4142754e9d06cbce7baf91ace8031a30df37064d1091ab414
|
FROM archlinux@sha256:a10e51dd0694d6c4142754e9d06cbce7baf91ace8031a30df37064d1091ab414
|
||||||
|
|
||||||
# Update the package database and install clang
|
# Update the package database
|
||||||
# 1. system tools
|
# 1. system tools
|
||||||
# 2. build/test tools
|
# 2. build/test tools
|
||||||
# 3. libraries
|
# 3. libraries
|
||||||
RUN pacman -Syyu --noconfirm \
|
RUN pacman -Syyu --noconfirm \
|
||||||
&& pacman -Syyu --noconfirm git less vim sudo base-devel python-pip \
|
&& pacman -Syyu --noconfirm git less vim sudo python-pip wget which pkgconf \
|
||||||
&& pacman -Syyu --noconfirm clang cmake make ninja meson shellcheck \
|
&& pacman -Syyu --noconfirm cmake make gcc ninja meson shellcheck \
|
||||||
&& pacman -Syyu --noconfirm gtk4 gtkmm-4.0 boost spdlog fmt libxml++-5.0
|
&& pacman -Syyu --noconfirm gtk4 gtkmm-4.0 boost spdlog fmt libxml++-5.0
|
||||||
|
|
||||||
# Create a non-root user 'dev'
|
# Create a non-root user 'dev'
|
||||||
|
@ -21,10 +21,49 @@ RUN useradd -ms /bin/bash dev \
|
||||||
COPY --from=library/docker:latest /usr/local/bin/docker /usr/bin/docker
|
COPY --from=library/docker:latest /usr/local/bin/docker /usr/bin/docker
|
||||||
COPY --from=docker/compose:latest /usr/local/bin/docker-compose /usr/bin/docker-compose
|
COPY --from=docker/compose:latest /usr/local/bin/docker-compose /usr/bin/docker-compose
|
||||||
|
|
||||||
|
# Allow dev to execute docker without sudo
|
||||||
RUN groupadd docker &&\
|
RUN groupadd docker &&\
|
||||||
usermod -aG docker dev &&\
|
usermod -aG docker dev &&\
|
||||||
newgrp docker
|
newgrp docker
|
||||||
|
|
||||||
|
# Switch to the 'dev' user
|
||||||
|
USER dev
|
||||||
|
|
||||||
|
# Add cached layer with the latest LLVM-${LLVM_VER}
|
||||||
|
ARG LLVM_VER=19.1.1
|
||||||
|
ENV LLVM_VER=${LLVM_VER}
|
||||||
|
RUN sudo mkdir -p /home/artifacts
|
||||||
|
ADD https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VER}/LLVM-${LLVM_VER}-Linux-X64.tar.xz /home/artifacts/
|
||||||
|
|
||||||
|
# Create the LLVM directory and extract only binaries into it
|
||||||
|
RUN sudo mkdir -p /home/LLVM-${LLVM_VER}
|
||||||
|
RUN sudo tar -xJf /home/artifacts/LLVM-${LLVM_VER}-Linux-X64.tar.xz -C /home/LLVM-${LLVM_VER} --strip-components=1 \
|
||||||
|
LLVM-${LLVM_VER}-Linux-X64/bin/clang-19 \
|
||||||
|
LLVM-${LLVM_VER}-Linux-X64/bin/clang \
|
||||||
|
LLVM-${LLVM_VER}-Linux-X64/bin/clang++ \
|
||||||
|
LLVM-${LLVM_VER}-Linux-X64/bin/lld \
|
||||||
|
LLVM-${LLVM_VER}-Linux-X64/bin/ld.lld \
|
||||||
|
LLVM-${LLVM_VER}-Linux-X64/bin/ld64.lld \
|
||||||
|
LLVM-${LLVM_VER}-Linux-X64/bin/clang-scan-deps \
|
||||||
|
LLVM-${LLVM_VER}-Linux-X64/bin/llvm-symbolizer \
|
||||||
|
LLVM-${LLVM_VER}-Linux-X64/bin/clang-format \
|
||||||
|
LLVM-${LLVM_VER}-Linux-X64/bin/clang-tidy \
|
||||||
|
LLVM-${LLVM_VER}-Linux-X64/bin/clang-apply-replacements \
|
||||||
|
LLVM-${LLVM_VER}-Linux-X64/include/ \
|
||||||
|
LLVM-${LLVM_VER}-Linux-X64/lib/ \
|
||||||
|
LLVM-${LLVM_VER}-Linux-X64/libexec/ \
|
||||||
|
LLVM-${LLVM_VER}-Linux-X64/local/ \
|
||||||
|
LLVM-${LLVM_VER}-Linux-X64/share/
|
||||||
|
|
||||||
|
# Add /home/LLVM-${LLVM_VER}/bin to the PATH environment variable
|
||||||
|
ENV PATH="/home/LLVM-${LLVM_VER}/bin:${PATH}"
|
||||||
|
|
||||||
|
# Add extra tools
|
||||||
|
RUN sudo mkdir -p /home/tools
|
||||||
|
ADD https://raw.githubusercontent.com/llvm/llvm-project/refs/heads/main/clang-tools-extra/clang-tidy/tool/run-clang-tidy.py /home/tools/
|
||||||
|
RUN sudo chmod -R a+x /home/tools/
|
||||||
|
RUN sudo chown -R dev /home/tools/
|
||||||
|
|
||||||
# Add the custom bash prompt with branch info
|
# Add the custom bash prompt with branch info
|
||||||
RUN printf '\n\
|
RUN printf '\n\
|
||||||
function modify_prompt { \n\
|
function modify_prompt { \n\
|
||||||
|
@ -37,9 +76,6 @@ function modify_prompt { \n\
|
||||||
} \n\
|
} \n\
|
||||||
modify_prompt\n' >> /home/dev/.bashrc
|
modify_prompt\n' >> /home/dev/.bashrc
|
||||||
|
|
||||||
# Switch to the 'dev' user
|
|
||||||
USER dev
|
|
||||||
|
|
||||||
RUN git config --global core.editor vim
|
RUN git config --global core.editor vim
|
||||||
|
|
||||||
# Set the default working directory
|
# Set the default working directory
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
sleep 1
|
sleep 5
|
||||||
|
|
||||||
|
sudo chmod -R a+x /home/tools/
|
||||||
|
sudo chown -R dev /home/tools/
|
||||||
|
|
||||||
# Check that ejabberd server started successfully
|
# Check that ejabberd server started successfully
|
||||||
red='\e[1;31m'
|
red='\e[1;31m'
|
||||||
|
|
188
.forgejo/workflows/pr_check.yaml
Normal file
188
.forgejo/workflows/pr_check.yaml
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
name: PR Check
|
||||||
|
run-name: ${{ github.actor }} is testing
|
||||||
|
on: [push]
|
||||||
|
jobs:
|
||||||
|
on-push-commit-check:
|
||||||
|
runs-on: archlinux-latest
|
||||||
|
steps:
|
||||||
|
- name: Setup environment - Install pacman packages
|
||||||
|
shell: sh
|
||||||
|
run: |
|
||||||
|
pacman -Syyu --noconfirm
|
||||||
|
pacman -S --noconfirm git less vim sudo python-pip wget which pkgconf nodejs-lts-iron
|
||||||
|
pacman -S --noconfirm cmake make gcc ninja meson
|
||||||
|
pacman -S --noconfirm gtk4 gtkmm-4.0 boost spdlog fmt libxml++-5.0 gtest
|
||||||
|
|
||||||
|
- name: Setup environment - Install LLVM-19.1.1
|
||||||
|
run: |
|
||||||
|
export LLVM_VER=19.1.1
|
||||||
|
|
||||||
|
echo "::group::Download LLVM-${LLVM_VER}"
|
||||||
|
wget https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VER}/LLVM-${LLVM_VER}-Linux-X64.tar.xz -O /LLVM-${LLVM_VER}-Linux-X64.tar.xz
|
||||||
|
echo "::endgroup::"
|
||||||
|
|
||||||
|
echo "::group::Extracting LLVM-${LLVM_VER}"
|
||||||
|
mkdir -p /home/LLVM-${LLVM_VER}/
|
||||||
|
tar -xf /LLVM-${LLVM_VER}-Linux-X64.tar.xz -C "/home/LLVM-${LLVM_VER}/" --strip-components=1 \
|
||||||
|
LLVM-${LLVM_VER}-Linux-X64/bin/clang-19 \
|
||||||
|
LLVM-${LLVM_VER}-Linux-X64/bin/clang \
|
||||||
|
LLVM-${LLVM_VER}-Linux-X64/bin/clang++ \
|
||||||
|
LLVM-${LLVM_VER}-Linux-X64/bin/clang-scan-deps \
|
||||||
|
LLVM-${LLVM_VER}-Linux-X64/bin/llvm-symbolizer \
|
||||||
|
LLVM-${LLVM_VER}-Linux-X64/bin/clang-format \
|
||||||
|
LLVM-${LLVM_VER}-Linux-X64/bin/clang-tidy \
|
||||||
|
LLVM-${LLVM_VER}-Linux-X64/include/ \
|
||||||
|
LLVM-${LLVM_VER}-Linux-X64/lib/ \
|
||||||
|
LLVM-${LLVM_VER}-Linux-X64/libexec/ \
|
||||||
|
LLVM-${LLVM_VER}-Linux-X64/local/ \
|
||||||
|
LLVM-${LLVM_VER}-Linux-X64/share/
|
||||||
|
|
||||||
|
rm /LLVM-${LLVM_VER}-Linux-X64.tar.xz
|
||||||
|
echo "::endgroup::"
|
||||||
|
|
||||||
|
echo "LLVM version ${LLVM_VER}"
|
||||||
|
echo "Available LLVM binaries:"
|
||||||
|
ls -la /home/LLVM-${LLVM_VER}/bin/
|
||||||
|
|
||||||
|
- name: List CI environment variables
|
||||||
|
run: |
|
||||||
|
echo "Runner OS: ${{ runner.os }}"
|
||||||
|
echo "Job triggered event: ${{ github.event_name }}"
|
||||||
|
echo "Repository name: ${{ github.repository }}"
|
||||||
|
echo "Workspace path: ${{ github.workspace }}"
|
||||||
|
echo "Actor: ${{ github.actor }}"
|
||||||
|
echo "Ref: ${{ github.ref }}"
|
||||||
|
echo "Ref name: ${{ github.ref_name }}"
|
||||||
|
echo "SHA: ${{ github.sha }}"
|
||||||
|
echo "PATH: ${PATH}"
|
||||||
|
|
||||||
|
- name: Check out repository code
|
||||||
|
run: |
|
||||||
|
export GIT_TERMINAL_PROMPT=0
|
||||||
|
mkdir -p ${{ github.workspace }}
|
||||||
|
cd ${{ github.workspace }}
|
||||||
|
|
||||||
|
echo "Configure project settings and remote credentials"
|
||||||
|
git init
|
||||||
|
git config credential.helper store
|
||||||
|
git remote add origin https://${{ github.token }}@sha512sum.xyz/git/Larra/larra
|
||||||
|
|
||||||
|
echo "Fetch repo state on pushed commit"
|
||||||
|
git -c protocol.version=2 fetch --no-tags --prune --no-recurse-submodules --depth=1 origin +${{ github.sha }}:${{ github.ref }}
|
||||||
|
|
||||||
|
echo "Checkout to fetched repo state"
|
||||||
|
git checkout ${{ github.ref }}
|
||||||
|
|
||||||
|
- name: Show repo content
|
||||||
|
run: |
|
||||||
|
echo "github.workspace content"
|
||||||
|
ls -la ${{ github.workspace }}
|
||||||
|
echo "This job's status is ${{ job.status }}."
|
||||||
|
|
||||||
|
- name: Store list of repo files
|
||||||
|
run: |
|
||||||
|
cd ${{ github.workspace }}
|
||||||
|
find -type f \( -name "*.h" -o -name "*.hpp" -o -name "*.c" -o -name "*.cpp" \) > repo_files_to_check.txt
|
||||||
|
find -type f \( -name "*.c" -o -name "*.cpp" \) > repo_sources_to_check.txt
|
||||||
|
|
||||||
|
#- name: Delete cache
|
||||||
|
# run: |
|
||||||
|
# rm -rf ${{ runner.tool_cache }}/cache-llvm-19-restore
|
||||||
|
#
|
||||||
|
#- name: Try restore LLVM-19 binaries from cache
|
||||||
|
# id: cache-llvm-19-bins-restore
|
||||||
|
# uses: actions/cache@v4
|
||||||
|
# with:
|
||||||
|
# path: /LLVM-${LLVM_VER}-Linux-X64-small.tar.xz
|
||||||
|
# key: LLVM-${LLVM_VER}-Linux-X64-small
|
||||||
|
#
|
||||||
|
#- name: Save LLVM-19 to cache
|
||||||
|
# if: steps.cache-llvm-19-bins-restore.outputs.cache-hit != 'true'
|
||||||
|
# id: cache-llvm-19-save
|
||||||
|
# uses: actions/cache/save@v4
|
||||||
|
# with:
|
||||||
|
# path: /LLVM-${LLVM_VER}-Linux-X64-small.tar.xz
|
||||||
|
# key: LLVM-${LLVM_VER}-Linux-X64-small
|
||||||
|
|
||||||
|
- name: Check clang-format
|
||||||
|
run: |
|
||||||
|
export LLVM_VER=19.1.1
|
||||||
|
export PATH="/home/LLVM-${LLVM_VER}/bin:${PATH}"
|
||||||
|
cd ${{ github.workspace }}
|
||||||
|
export REPO_FILES=$(cat repo_files_to_check.txt)
|
||||||
|
|
||||||
|
printf "\nList of files to be checked:\n"
|
||||||
|
echo "::group::files to be checked"
|
||||||
|
for FILE in $REPO_FILES; do printf "\t ${{ github.workspace }}/$FILE \n"; done
|
||||||
|
echo "::endgroup::"
|
||||||
|
|
||||||
|
for FILE in $REPO_FILES; do clang-format -i ${{ github.workspace }}/$FILE --dry-run --Werror; done
|
||||||
|
echo "No clang-format violations detected!"
|
||||||
|
|
||||||
|
- name: GCC build
|
||||||
|
run: |
|
||||||
|
mkdir -p ${{ github.workspace }}/build_gcc
|
||||||
|
cmake -Wno-dev \
|
||||||
|
-DCMAKE_C_COMPILER=gcc \
|
||||||
|
-DCMAKE_CXX_COMPILER=g++ \
|
||||||
|
-S ${{ github.workspace }} \
|
||||||
|
-B ${{ github.workspace }}/build_gcc \
|
||||||
|
-GNinja -DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DENABLE_EXAMPLES=ON \
|
||||||
|
-DENABLE_TESTS=ON
|
||||||
|
cmake --build ${{ github.workspace }}/build_gcc --parallel `nproc`
|
||||||
|
|
||||||
|
- name: GCC unit tests
|
||||||
|
run: |
|
||||||
|
cd ${{ github.workspace }}/build_gcc
|
||||||
|
./larra_xmpp_tests
|
||||||
|
|
||||||
|
- name: Check clang-tidy
|
||||||
|
run: |
|
||||||
|
export LLVM_VER=19.1.1
|
||||||
|
export PATH="/home/LLVM-${LLVM_VER}/bin:${PATH}"
|
||||||
|
cd ${{ github.workspace }}
|
||||||
|
|
||||||
|
sed -i 's|/usr/sbin/g++|/home/LLVM-19.1.1/bin/clang++ -stdlib=libc++ -I/home/LLVM-${LLVM_VER}/include/c++/v1|' build_gcc/compile_commands.json
|
||||||
|
sed -i 's|-fdeps-format=p1689r5||' build_gcc/compile_commands.json
|
||||||
|
sed -i 's|-fmodules-ts||' build_gcc/compile_commands.json
|
||||||
|
sed -i 's|-fmodule-mapper=.*\.modmap||' build_gcc/compile_commands.json
|
||||||
|
|
||||||
|
wget https://raw.githubusercontent.com/llvm/llvm-project/refs/heads/main/clang-tools-extra/clang-tidy/tool/run-clang-tidy.py
|
||||||
|
python run-clang-tidy.py \
|
||||||
|
-warnings-as-errors=* \
|
||||||
|
-use-color \
|
||||||
|
-exclude-header-filter .*build.* \
|
||||||
|
-header-filter .hpp \
|
||||||
|
-p ${{ github.workspace }}/build_gcc/ \
|
||||||
|
-config-file ${{ github.workspace }}/.clang-tidy \
|
||||||
|
\
|
||||||
|
${{ github.workspace }}/examples \
|
||||||
|
${{ github.workspace }}/library/ \
|
||||||
|
${{ github.workspace }}/src/
|
||||||
|
|
||||||
|
echo "No clang-tidy violations detected!"
|
||||||
|
|
||||||
|
#- name: Clang build with -fsanitize=address
|
||||||
|
# run: |
|
||||||
|
# export LLVM_VER=19.1.1
|
||||||
|
# export PATH="/home/LLVM-${LLVM_VER}/bin:${PATH}"
|
||||||
|
# mkdir -p ${{ github.workspace }}/build_clang
|
||||||
|
# cmake -Wno-dev \
|
||||||
|
# -DCMAKE_C_COMPILER=clang \
|
||||||
|
# -DCMAKE_CXX_COMPILER=clang++ \
|
||||||
|
# -S ${{ github.workspace }} \
|
||||||
|
# -B ${{ github.workspace }}/build_clang \
|
||||||
|
# -GNinja -DCMAKE_BUILD_TYPE=Release \
|
||||||
|
# -DENABLE_EXAMPLES=ON \
|
||||||
|
# -DENABLE_TESTS=ON \
|
||||||
|
# -DCMAKE_CXX_FLAGS="-stdlib=libc++ -I/home/LLVM-${LLVM_VER}/include/c++/v1 -fno-omit-frame-pointer -g -fsanitize=address,undefined,leak,function,nullability,vptr" \
|
||||||
|
# -DCMAKE_EXE_LINKER_FLAGS="-L/home/LLVM-${LLVM_VER}/lib/ -Wl,-rpath,/home/LLVM-${LLVM_VER}/lib -lc++ -lc++abi -lm -lc -lgcc_s -lgcc -fno-omit-frame-pointer -g -fsanitize=address,undefined,leak,function,nullability,vptr"
|
||||||
|
# cmake --build ${{ github.workspace }}/build_clang --parallel `nproc`
|
||||||
|
|
||||||
|
#- name: Clang unit tests with -fsanitize=address
|
||||||
|
# run: |
|
||||||
|
# export LLVM_VER=19.1.1
|
||||||
|
# export PATH="/home/LLVM-${LLVM_VER}/bin:${PATH}"
|
||||||
|
# cd ${{ github.workspace }}/build_clang
|
||||||
|
# ASAN_SYMBOLIZER_PATH=llvm-symbolizer ASAN_OPTIONS=detect_stack_use_after_return=1:check_initialization_order=1:detect_leaks=1:atexit=1:abort_on_error=1 ./larra_xmpp_tests
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -29,5 +29,4 @@ ext/
|
||||||
libxmlplusplus-prefix/
|
libxmlplusplus-prefix/
|
||||||
spdlog.pc
|
spdlog.pc
|
||||||
build*
|
build*
|
||||||
larra
|
|
||||||
temp*
|
temp*
|
||||||
|
|
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
|
@ -11,7 +11,7 @@
|
||||||
"program": "${workspaceFolder}/build/examples/output/connect",
|
"program": "${workspaceFolder}/build/examples/output/connect",
|
||||||
"args": [],
|
"args": [],
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
"preLaunchTask": "Build Debug GCC"
|
"preLaunchTask": "GCC: Build"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
258
.vscode/tasks.json
vendored
258
.vscode/tasks.json
vendored
|
@ -3,6 +3,45 @@
|
||||||
// for the documentation about the tasks.json format
|
// for the documentation about the tasks.json format
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"tasks": [
|
"tasks": [
|
||||||
|
//
|
||||||
|
// Checkers/Analyzers tasks
|
||||||
|
//
|
||||||
|
{
|
||||||
|
"label": "Checker: clang-format dry-run",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "for FILE in $(find . -type f \\( -name '*.c' -o -name '*.cpp' -o -name '*.h' -o -name '*.hpp' \\) ! -path '*build*'); do clang-format -i $FILE --dry-run --Werror; done"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Checker: clang-format fix errors",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "for FILE in $(find . -type f \\( -name '*.c' -o -name '*.cpp' -o -name '*.h' -o -name '*.hpp' \\) ! -path '*build*'); do clang-format -i $FILE --Werror; done"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// https://clang.llvm.org/extra/clang-tidy/
|
||||||
|
"label": "Checker: clang-tidy dry-run",
|
||||||
|
"type": "shell",
|
||||||
|
"command": [
|
||||||
|
"python /home/tools/run-clang-tidy.py -exclude-header-filter .*build.* -use-color -warnings-as-errors=* -header-filter .hpp -p ${workspaceFolder}/build_clang/ -config-file ${workspaceFolder}/.clang-tidy",
|
||||||
|
" ${workspaceFolder}/examples",
|
||||||
|
" ${workspaceFolder}/library/",
|
||||||
|
" ${workspaceFolder}/src/"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// https://clang.llvm.org/extra/clang-tidy/
|
||||||
|
"label": "Checker: clang-tidy fix errors",
|
||||||
|
"type": "shell",
|
||||||
|
"command": [
|
||||||
|
"python /home/tools/run-clang-tidy.py -exclude-header-filter .*build.* -use-color -warnings-as-errors=* -header-filter .hpp -p ${workspaceFolder}/build_clang/ -config-file ${workspaceFolder}/.clang-tidy",
|
||||||
|
" -fix",
|
||||||
|
" ${workspaceFolder}/examples",
|
||||||
|
" ${workspaceFolder}/library/",
|
||||||
|
" ${workspaceFolder}/src/"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
//
|
||||||
|
// Ejabberd tasks
|
||||||
|
//
|
||||||
{
|
{
|
||||||
"label": "Ejabberd: show logs",
|
"label": "Ejabberd: show logs",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
|
@ -11,54 +50,243 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "Ejabberd: open shell",
|
"label": "Ejabberd: show localhost registered users",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": [
|
"command": [
|
||||||
"sudo docker exec -it ejabberd sh"
|
"sudo docker exec -it ejabberd ejabberdctl registered_users localhost"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "Build Debug GCC",
|
"label": "Ejabberd: open shell",
|
||||||
|
"type": "shell",
|
||||||
|
"command": [
|
||||||
|
"printf 'Example cmd: ejabberdctl registered_users localhost\n\n';",
|
||||||
|
"sudo docker exec -it ejabberd sh"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
//
|
||||||
|
// GCC build related tasks
|
||||||
|
//
|
||||||
|
{
|
||||||
|
"label": "GCC: Clean build folder",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "if [ -n \"$(ls -A ${workspaceFolder}/build)\" ]; then rm -rf ${workspaceFolder}/build/*; fi",
|
||||||
|
"hide": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "GCC: Configure Debug",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": [
|
"command": [
|
||||||
"cd ${workspaceFolder} &&",
|
"cd ${workspaceFolder} &&",
|
||||||
"mkdir -p build && cd build &&",
|
"mkdir -p build && cd build &&",
|
||||||
"cmake -Wno-dev -DCMAKE_BUILD_TYPE=Debug -DENABLE_EXAMPLES=ON -DENABLE_TESTS=ON .. &&",
|
"cmake cmake -Wno-dev ",
|
||||||
"cmake --build . --parallel `nproc`",
|
" -DCMAKE_BUILD_TYPE=Debug -DENABLE_EXAMPLES=ON -DENABLE_TESTS=ON .."
|
||||||
"|| ( printf '\n\n\t\\e[31mERROR: Build failed!\\e[0m\n\n\n' && exit 1 )"
|
|
||||||
],
|
],
|
||||||
"options": {
|
"options": {
|
||||||
"env": {}
|
"env": {
|
||||||
|
"CC": "/usr/sbin/gcc",
|
||||||
|
"CXX": "/usr/sbin/g++"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"presentation": {
|
"presentation": {
|
||||||
"clear": true
|
"clear": true
|
||||||
},
|
},
|
||||||
"problemMatcher": [],
|
"hide": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "GCC: Build",
|
||||||
|
"type": "shell",
|
||||||
|
"command": [
|
||||||
|
"cd ${workspaceFolder}/build &&",
|
||||||
|
"cmake --build . --parallel `nproc`",
|
||||||
|
"|| ( printf '\n\n\t\\e[31mERROR: Build failed!\\e[0m\n\n\n' && exit 1 )"
|
||||||
|
],
|
||||||
|
"presentation": {
|
||||||
|
"clear": true
|
||||||
|
},
|
||||||
|
"hide": true,
|
||||||
|
"group": {
|
||||||
|
"kind": "build"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "GCC: Configure and build Debug",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "echo Finished!",
|
||||||
|
"presentation": {
|
||||||
|
"clear": true
|
||||||
|
},
|
||||||
|
"dependsOrder": "sequence",
|
||||||
|
"dependsOn":[
|
||||||
|
"GCC: Clean build folder",
|
||||||
|
"GCC: Configure Debug",
|
||||||
|
"GCC: Build"
|
||||||
|
],
|
||||||
"group": {
|
"group": {
|
||||||
"kind": "build",
|
"kind": "build",
|
||||||
"isDefault": true
|
"isDefault": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
//
|
||||||
|
// Clang build related tasks
|
||||||
|
//
|
||||||
{
|
{
|
||||||
"label": "Build Debug Clang",
|
"label": "Clang: Configure Debug",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": [
|
"command": [
|
||||||
"cd ${workspaceFolder} &&",
|
"cd ${workspaceFolder} &&",
|
||||||
"mkdir -p build_clang && cd build_clang &&",
|
"mkdir -p build_clang && cd build_clang &&",
|
||||||
"cmake -Wno-dev -DCMAKE_BUILD_TYPE=Debug -DENABLE_EXAMPLES=ON -DENABLE_TESTS=ON .. &&",
|
"cmake -Wno-dev ",
|
||||||
"cmake --build . --parallel `nproc`",
|
" -DCMAKE_BUILD_TYPE=Debug -DENABLE_EXAMPLES=ON -DENABLE_TESTS=ON ",
|
||||||
"|| ( printf '\n\n\t\\e[31mERROR: Build failed!\\e[0m\n\n\n' && exit 1 )"
|
" -DCMAKE_CXX_FLAGS=\"-stdlib=libstdc++\"",
|
||||||
|
" -DCMAKE_EXE_LINKER_FLAGS=\"-L/usr/lib/x86_64-linux-gnu -lstdc++\" .."
|
||||||
],
|
],
|
||||||
"options": {
|
"options": {
|
||||||
"env": {
|
"env": {
|
||||||
"CC": "/usr/sbin/clang",
|
"CC": "clang",
|
||||||
"CXX": "/usr/sbin/clang++"
|
"CXX": "clang++"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"presentation": {
|
"presentation": {
|
||||||
"clear": true
|
"clear": true
|
||||||
},
|
},
|
||||||
"problemMatcher": []
|
"hide": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Clang: Configure Debug with -fsanitize=address",
|
||||||
|
"type": "shell",
|
||||||
|
"command": [
|
||||||
|
"cd ${workspaceFolder} &&",
|
||||||
|
"mkdir -p build_clang && cd build_clang &&",
|
||||||
|
"cmake -Wno-dev ",
|
||||||
|
" -DCMAKE_BUILD_TYPE=Debug -DENABLE_EXAMPLES=ON -DENABLE_TESTS=ON",
|
||||||
|
//
|
||||||
|
// Uncomment for GCC standart library: libstdc++
|
||||||
|
//" -DCMAKE_CXX_FLAGS=\"-stdlib=libstdc++ -fno-omit-frame-pointer -g -fsanitize=address,undefined,leak,function,nullability,vptr\"",
|
||||||
|
//" -DCMAKE_EXE_LINKER_FLAGS=\"-L/usr/lib/x86_64-linux-gnu -lstdc++ -fno-omit-frame-pointer -g -fsanitize=address,undefined,leak,function,nullability,vptr\"",
|
||||||
|
//
|
||||||
|
" -DCMAKE_CXX_FLAGS=\"-stdlib=libc++ -I/home/LLVM-19.1.1/include/c++/v1 -fno-omit-frame-pointer -g -fsanitize=address,undefined,leak,function,nullability,vptr\"",
|
||||||
|
" -DCMAKE_EXE_LINKER_FLAGS=\"-L/home/LLVM-19.1.1/lib/ -Wl,-rpath,/home/LLVM-19.1.1/lib -lc++ -lc++abi -lm -lc -lgcc_s -lgcc -fuse-ld=lld -fno-omit-frame-pointer -g -fsanitize=address,undefined,leak,function,nullability,vptr\"",
|
||||||
|
" .."
|
||||||
|
],
|
||||||
|
"options": {
|
||||||
|
"env": {
|
||||||
|
"CC": "clang",
|
||||||
|
"CXX": "clang++"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"presentation": {
|
||||||
|
"clear": true
|
||||||
|
},
|
||||||
|
"hide": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Useful links:
|
||||||
|
// - https://github.com/google/sanitizers/wiki/addresssanitizerflags
|
||||||
|
// - https://clang.llvm.org/docs/AddressSanitizer.html
|
||||||
|
//
|
||||||
|
"label": "Clang: Exec gtest with -fsanitize=address",
|
||||||
|
"type": "shell",
|
||||||
|
"command": [
|
||||||
|
"cd ${workspaceFolder} &&",
|
||||||
|
"mkdir -p build_clang && cd build_clang &&",
|
||||||
|
"ASAN_SYMBOLIZER_PATH=llvm-symbolizer ASAN_OPTIONS=detect_stack_use_after_return=1:check_initialization_order=1:detect_leaks=1:atexit=1:abort_on_error=1 ./larra_xmpp_tests ; echo \"exit code: $?\"",
|
||||||
|
],
|
||||||
|
"presentation": {
|
||||||
|
"clear": true
|
||||||
|
},
|
||||||
|
"dependsOrder": "sequence",
|
||||||
|
"dependsOn":[
|
||||||
|
"Clang: Clean build folder",
|
||||||
|
"Clang: Configure Debug with -fsanitize=address",
|
||||||
|
"Clang: Build"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Clang: Configure Debug with -fsanitize=memory",
|
||||||
|
"type": "shell",
|
||||||
|
"command": [
|
||||||
|
"cd ${workspaceFolder} &&",
|
||||||
|
"mkdir -p build_clang && cd build_clang &&",
|
||||||
|
"cmake -Wno-dev ",
|
||||||
|
" -DCMAKE_BUILD_TYPE=Debug -DENABLE_EXAMPLES=ON -DENABLE_TESTS=ON ",
|
||||||
|
" -DCMAKE_CXX_FLAGS=\"-stdlib=libstdc++ -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer -fno-optimize-sibling-calls -g -fsanitize=memory\"",
|
||||||
|
" -DCMAKE_EXE_LINKER_FLAGS=\"-L/usr/lib/x86_64-linux-gnu -lstdc++ -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer -fno-optimize-sibling-calls -g -fsanitize=memory\" ..",
|
||||||
|
],
|
||||||
|
"options": {
|
||||||
|
"env": {
|
||||||
|
"CC": "clang",
|
||||||
|
"CXX": "clang++"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"presentation": {
|
||||||
|
"clear": true
|
||||||
|
},
|
||||||
|
"hide": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Useful links:
|
||||||
|
// - https://github.com/google/sanitizers/wiki/MemorySanitizer
|
||||||
|
// - https://clang.llvm.org/docs/MemorySanitizer.html
|
||||||
|
//
|
||||||
|
"label": "Clang: Exec gtest with -fsanitize=memory",
|
||||||
|
"type": "shell",
|
||||||
|
"command": [
|
||||||
|
"cd ${workspaceFolder} &&",
|
||||||
|
"mkdir -p build_clang && cd build_clang &&",
|
||||||
|
"MSAN_SYMBOLIZER_PATH=llvm-symbolizer MSAN_OPTIONS=abort_on_error=1 ./larra_xmpp_tests ; echo \"exit code: $?\"",
|
||||||
|
],
|
||||||
|
"presentation": {
|
||||||
|
"clear": true
|
||||||
|
},
|
||||||
|
"dependsOrder": "sequence",
|
||||||
|
"dependsOn":[
|
||||||
|
"Clang: Clean build folder",
|
||||||
|
"Clang: Configure Debug with -fsanitize=memory",
|
||||||
|
"Clang: Build"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Clang: Build",
|
||||||
|
"type": "shell",
|
||||||
|
"command": [
|
||||||
|
"cd ${workspaceFolder}/build_clang &&",
|
||||||
|
"cmake --build . --parallel `nproc`",
|
||||||
|
"|| ( printf '\n\n\t\\e[31mERROR: Build failed!\\e[0m\n\n\n' && exit 1 )"
|
||||||
|
],
|
||||||
|
"presentation": {
|
||||||
|
"clear": true
|
||||||
|
},
|
||||||
|
"group": {
|
||||||
|
"kind": "build"
|
||||||
|
},
|
||||||
|
"hide": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Clang: Configure and build Debug",
|
||||||
|
"type": "shell",
|
||||||
|
"command": [
|
||||||
|
"cd ${workspaceFolder} &&",
|
||||||
|
"mkdir -p build_clang && cd build_clang &&",
|
||||||
|
"MSAN_SYMBOLIZER_PATH=llvm-symbolizer MSAN_OPTIONS=abort_on_error=1 ./larra_xmpp_tests ; echo \"exit code: $?\"",
|
||||||
|
],
|
||||||
|
"presentation": {
|
||||||
|
"clear": true
|
||||||
|
},
|
||||||
|
"dependsOrder": "sequence",
|
||||||
|
"dependsOn":[
|
||||||
|
"Clang: Clean build folder",
|
||||||
|
"Clang: Configure Debug",
|
||||||
|
"Clang: Build"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Clang: Clean build folder",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "if [ -n \"$(ls -A ${workspaceFolder}/build_clang)\" ]; then rm -rf ${workspaceFolder}/build_clang/*; fi",
|
||||||
|
"hide": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -23,7 +23,7 @@ option(CPM_USE_LOCAL_PACKAGES "Use local packages" ON)
|
||||||
option(UTEMPL_USE_LOCAL_PACKAGE "Use utempl local package" OFF)
|
option(UTEMPL_USE_LOCAL_PACKAGE "Use utempl local package" OFF)
|
||||||
option(BUILD_EXECUTABLE ON)
|
option(BUILD_EXECUTABLE ON)
|
||||||
set(UTEMPL_URL
|
set(UTEMPL_URL
|
||||||
"https://helicopter.myftp.org/git/sha512sum/utempl"
|
"https://sha512sum.xyz/git/sha512sum/utempl"
|
||||||
CACHE STRING "utempl repository URL")
|
CACHE STRING "utempl repository URL")
|
||||||
|
|
||||||
file(GLOB_RECURSE LIB_SOURCES "library/*.*pp")
|
file(GLOB_RECURSE LIB_SOURCES "library/*.*pp")
|
||||||
|
@ -59,8 +59,6 @@ CPMAddPackage(
|
||||||
OPTIONS "BOOST_SKIP_INSTALL_RULES OFF"
|
OPTIONS "BOOST_SKIP_INSTALL_RULES OFF"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
CPMAddPackage("gh:zeux/pugixml@1.14")
|
|
||||||
CPMAddPackage("gh:fmtlib/fmt#10.2.1")
|
CPMAddPackage("gh:fmtlib/fmt#10.2.1")
|
||||||
CPMAddPackage(NAME nameof
|
CPMAddPackage(NAME nameof
|
||||||
VERSION 0.10.4
|
VERSION 0.10.4
|
||||||
|
@ -163,8 +161,10 @@ add_library(larra::larra_xmpp ALIAS larra_xmpp)
|
||||||
|
|
||||||
target_compile_features(larra_xmpp PUBLIC cxx_std_23)
|
target_compile_features(larra_xmpp PUBLIC cxx_std_23)
|
||||||
|
|
||||||
|
target_compile_options(larra_xmpp PUBLIC
|
||||||
target_compile_options(larra_xmpp PUBLIC -Wno-changes-meaning)
|
"$<$<CXX_COMPILER_ID:GNU>:-Wno-changes-meaning>"
|
||||||
|
"$<$<CXX_COMPILER_ID:GNU>:-Wno-non-template-friend>"
|
||||||
|
)
|
||||||
|
|
||||||
target_include_directories(larra_xmpp PUBLIC
|
target_include_directories(larra_xmpp PUBLIC
|
||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/library/include>
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/library/include>
|
||||||
|
@ -175,14 +175,14 @@ target_include_directories(larra_xmpp PUBLIC
|
||||||
|
|
||||||
if(TARGET Boost::pfr)
|
if(TARGET Boost::pfr)
|
||||||
target_link_libraries(larra_xmpp PUBLIC
|
target_link_libraries(larra_xmpp PUBLIC
|
||||||
Boost::asio Boost::serialization utempl::utempl pugixml::pugixml
|
Boost::asio Boost::serialization utempl::utempl
|
||||||
OpenSSL::SSL nameof::nameof
|
OpenSSL::SSL nameof::nameof fmt::fmt
|
||||||
OpenSSL::Crypto spdlog xmlplusplus ${LIBXML2_LIBRARIES})
|
OpenSSL::Crypto spdlog xmlplusplus ${LIBXML2_LIBRARIES})
|
||||||
else()
|
else()
|
||||||
find_package(Boost 1.85.0 COMPONENTS serialization REQUIRED)
|
find_package(Boost 1.85.0 COMPONENTS serialization REQUIRED)
|
||||||
target_link_libraries(larra_xmpp PUBLIC
|
target_link_libraries(larra_xmpp PUBLIC
|
||||||
utempl::utempl ${Boost_LIBRARIES} pugixml::pugixml OpenSSL::SSL
|
utempl::utempl ${Boost_LIBRARIES} OpenSSL::SSL
|
||||||
nameof::nameof
|
nameof::nameof fmt::fmt
|
||||||
OpenSSL::Crypto spdlog xmlplusplus ${LIBXML2_LIBRARIES})
|
OpenSSL::Crypto spdlog xmlplusplus ${LIBXML2_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
|
@ -74,7 +74,7 @@ struct Client {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool active = true;
|
bool active = true;
|
||||||
XmlStream<Connection> connection;
|
XmlStream<Connection> connection{};
|
||||||
BareJid jid;
|
BareJid jid;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
#include <larra/utils.hpp>
|
#include <larra/utils.hpp>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <pugixml.hpp>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace larra::xmpp {
|
namespace larra::xmpp {
|
||||||
|
@ -11,7 +10,7 @@ namespace larra::xmpp {
|
||||||
enum class Required : bool { kNotRequired = false, kRequired = true };
|
enum class Required : bool { kNotRequired = false, kRequired = true };
|
||||||
|
|
||||||
struct SaslMechanisms {
|
struct SaslMechanisms {
|
||||||
std::vector<std::string> mechanisms;
|
std::vector<std::string> mechanisms{};
|
||||||
|
|
||||||
[[nodiscard]] static auto Parse(const xmlpp::Element*) -> SaslMechanisms;
|
[[nodiscard]] static auto Parse(const xmlpp::Element*) -> SaslMechanisms;
|
||||||
};
|
};
|
||||||
|
@ -51,10 +50,9 @@ struct StreamFeatures {
|
||||||
return utils::FieldSetHelper::With<"saslMechanisms">(std::forward<Self>(self), std::move(value));
|
return utils::FieldSetHelper::With<"saslMechanisms">(std::forward<Self>(self), std::move(value));
|
||||||
}
|
}
|
||||||
template <typename Self>
|
template <typename Self>
|
||||||
[[nodiscard]] constexpr auto Others(this Self&& self, std::vector<pugi::xml_node> value) {
|
[[nodiscard]] constexpr auto Others(this Self&& self, std::vector<const xmlpp::Node*> value) {
|
||||||
return utils::FieldSetHelper::With<"others">(std::forward<Self>(self), std::move(value));
|
return utils::FieldSetHelper::With<"others">(std::forward<Self>(self), std::move(value));
|
||||||
}
|
}
|
||||||
[[nodiscard]] static auto Parse(pugi::xml_node) -> StreamFeatures;
|
|
||||||
[[nodiscard]] static auto Parse(const xmlpp::Element*) -> StreamFeatures;
|
[[nodiscard]] static auto Parse(const xmlpp::Element*) -> StreamFeatures;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -138,7 +138,7 @@ struct boost::asio::ssl::stream<larra::xmpp::PrintStream<Socket>> : public boost
|
||||||
auto next_layer() -> next_layer_type& {
|
auto next_layer() -> next_layer_type& {
|
||||||
return static_cast<next_layer_type&>(this->Base::next_layer());
|
return static_cast<next_layer_type&>(this->Base::next_layer());
|
||||||
}
|
}
|
||||||
auto next_layer() const -> const next_layer_type& {
|
[[nodiscard]] auto next_layer() const -> const next_layer_type& {
|
||||||
return static_cast<const next_layer_type&>(this->Base::next_layer());
|
return static_cast<const next_layer_type&>(this->Base::next_layer());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include <libxml++/libxml++.h>
|
#include <libxml++/libxml++.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include <larra/serialization/error.hpp>
|
||||||
#include <nameof.hpp>
|
#include <nameof.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utempl/utils.hpp>
|
#include <utempl/utils.hpp>
|
||||||
|
@ -96,7 +97,8 @@ template <typename T>
|
||||||
struct Serialization : SerializationBase<T> {
|
struct Serialization : SerializationBase<T> {
|
||||||
[[nodiscard]] static constexpr auto Parse(xmlpp::Element* element) -> T {
|
[[nodiscard]] static constexpr auto Parse(xmlpp::Element* element) -> T {
|
||||||
if(!Serialization::StartCheck(element)) {
|
if(!Serialization::StartCheck(element)) {
|
||||||
throw std::runtime_error("StartCheck failed");
|
throw serialization::ParsingError{
|
||||||
|
std::format("[{}: {}] parsing error: [ StartCheck failed ]", Serialization::kDefaultName, nameof::nameof_full_type<T>())};
|
||||||
}
|
}
|
||||||
return T::Parse(element);
|
return T::Parse(element);
|
||||||
}
|
}
|
||||||
|
|
189
library/include/larra/serialization/auto.hpp
Normal file
189
library/include/larra/serialization/auto.hpp
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
#pragma once
|
||||||
|
#include <libxml++/libxml++.h>
|
||||||
|
|
||||||
|
#include <boost/pfr.hpp>
|
||||||
|
#include <larra/serialization.hpp>
|
||||||
|
#include <larra/serialization/error.hpp>
|
||||||
|
#include <utempl/constexpr_string.hpp>
|
||||||
|
#include <utempl/tuple.hpp>
|
||||||
|
#include <utempl/utils.hpp>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
namespace larra::xmpp::serialization {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct Tag {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline constexpr auto kSerializationConfig = std::monostate{};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline constexpr auto kDeserializationConfig = kSerializationConfig<T>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr auto Parse(xmlpp::Element* element, Tag<T> = {}) -> T
|
||||||
|
requires(!std::same_as<std::decay_t<decltype(kDeserializationConfig<T>)>, std::monostate>);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr auto Parse(xmlpp::Element* element, Tag<T> = {}) -> T;
|
||||||
|
|
||||||
|
struct AttributeConfig {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct MetaInfo {
|
||||||
|
static constexpr std::size_t kSize = boost::pfr::tuple_size_v<T>;
|
||||||
|
template <std::size_t I>
|
||||||
|
using TupleElement = boost::pfr::tuple_element_t<I, T>;
|
||||||
|
|
||||||
|
template <std::size_t I>
|
||||||
|
static constexpr std::string_view kFieldName = boost::pfr::get_name<I, T>();
|
||||||
|
|
||||||
|
template <typename Self, std::size_t I>
|
||||||
|
static constexpr auto Get(Self&& self) -> decltype(auto) {
|
||||||
|
return boost::pfr::get<I>(std::forward<Self>(self));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename MainT, std::size_t Element>
|
||||||
|
struct FieldInfo {
|
||||||
|
using Main = MainT;
|
||||||
|
using Info = MetaInfo<Main>;
|
||||||
|
static inline const std::string kName = [] {
|
||||||
|
if constexpr(requires { Info::template TupleElement<Element>::kDefaultName; }) {
|
||||||
|
return Info::template TupleElement<Element>::kDefaultName;
|
||||||
|
} else {
|
||||||
|
return static_cast<std::string>(Info::template kFieldName<Element>);
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct Config {
|
||||||
|
std::optional<T> defaultValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct Config<std::string> {
|
||||||
|
std::optional<std::string_view> defaultValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace impl {
|
||||||
|
template <typename T>
|
||||||
|
concept HasParse = requires(xmlpp::Element* e) {
|
||||||
|
{ T::Parse(e) } -> std::same_as<T>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename V>
|
||||||
|
struct Config : V {
|
||||||
|
using V::V;
|
||||||
|
constexpr Config()
|
||||||
|
requires HasParse<T>
|
||||||
|
: V(::larra::xmpp::serialization::Config<T>{}) {
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Config()
|
||||||
|
requires(!HasParse<T>)
|
||||||
|
: V(AttributeConfig{}) {
|
||||||
|
}
|
||||||
|
constexpr auto Base() const -> const V& {
|
||||||
|
return static_cast<const V&>(*this);
|
||||||
|
}
|
||||||
|
using type = T;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace impl
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct ElementConfig {
|
||||||
|
using type = impl::Config<T, std::variant<AttributeConfig, Config<T>>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, auto& Config, typename Info>
|
||||||
|
struct ElementSerializer {
|
||||||
|
static constexpr auto Parse(xmlpp::Element* element) {
|
||||||
|
auto node = element->get_first_child(Info::kName);
|
||||||
|
if(!node) {
|
||||||
|
throw ElementParsingError(std::format("[{}: {}] parsing error: [ Not found ]", Info::kName, nameof::nameof_full_type<T>()));
|
||||||
|
}
|
||||||
|
auto elementNode = dynamic_cast<xmlpp::Element*>(node);
|
||||||
|
if(!node) {
|
||||||
|
throw ElementParsingError(std::format("[{}: {}] parsing error: [ Invalid node ]", Info::kName, nameof::nameof_full_type<T>()));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return ::larra::xmpp::serialization::Parse(elementNode, Tag<T>{});
|
||||||
|
} catch(const ParsingError& error) {
|
||||||
|
throw ElementParsingError(std::format("[{}: {}] parsing error: [ {} ]", Info::kName, nameof::nameof_full_type<T>(), error.what()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace impl {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
consteval auto FindElement(std::string_view field, utempl::TypeList<T> = {}) {
|
||||||
|
auto fields = boost::pfr::names_as_array<T>();
|
||||||
|
return std::ranges::find(fields, field) - fields.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <auto& Config, typename Info>
|
||||||
|
auto ParseField(xmlpp::Element* main) -> std::decay_t<decltype(Config)>::type {
|
||||||
|
using Type = std::decay_t<decltype(Config)>::type;
|
||||||
|
if constexpr(std::holds_alternative<AttributeConfig>(Config.Base())) {
|
||||||
|
xmlpp::Attribute* node = main->get_attribute(Info::kName);
|
||||||
|
if(!node) {
|
||||||
|
throw AttributeParsingError(std::format("Attribute [{}: {}] parsing error", Info::kName, nameof::nameof_full_type<Type>()));
|
||||||
|
}
|
||||||
|
if constexpr(requires(std::string_view view) { Type::Parse(view); }) {
|
||||||
|
return Type::Parse(node->get_value());
|
||||||
|
} else {
|
||||||
|
return node->get_value();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return ElementSerializer<Type, Config, Info>::Parse(main);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace impl
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct SerializationConfig {
|
||||||
|
decltype([] {
|
||||||
|
return [](auto... is) -> utempl::Tuple<typename ElementConfig<boost::pfr::tuple_element_t<*is, T>>::type...> {
|
||||||
|
std::unreachable();
|
||||||
|
} | utempl::kSeq<boost::pfr::tuple_size_v<T>>;
|
||||||
|
}()) tuple{};
|
||||||
|
template <std::size_t I, typename Self> // NOLINTNEXTLINE
|
||||||
|
consteval auto With(this Self&& self, ElementConfig<boost::pfr::tuple_element_t<I, T>>::type config) -> SerializationConfig {
|
||||||
|
auto tuple = std::forward_like<Self>(self.tuple);
|
||||||
|
Get<I>(tuple) = std::move(config);
|
||||||
|
return {std::move(tuple)};
|
||||||
|
}
|
||||||
|
template <utempl::ConstexprString Name, typename Self>
|
||||||
|
constexpr auto With(this Self&& self, ElementConfig<boost::pfr::tuple_element_t<impl::FindElement<T>(Name), T>>::type config)
|
||||||
|
-> SerializationConfig {
|
||||||
|
return std::forward<Self>(self).template With<impl::FindElement<T>(Name)>(std::move(config));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr auto Parse(xmlpp::Element* element, Tag<T>) -> T {
|
||||||
|
return Serialization<T>::Parse(element);
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
constexpr auto Parse(xmlpp::Element* element, Tag<T>) -> T
|
||||||
|
requires(!std::same_as<std::decay_t<decltype(kDeserializationConfig<T>)>, std::monostate>)
|
||||||
|
{
|
||||||
|
static constexpr SerializationConfig config = kSerializationConfig<T>;
|
||||||
|
constexpr auto tuple = utempl::Map(config.tuple, [](auto& ref) {
|
||||||
|
return &ref;
|
||||||
|
});
|
||||||
|
return utempl::Unpack(utempl::PackConstexprWrapper<utempl::Enumerate(tuple)>(), [&](auto... configs) {
|
||||||
|
try {
|
||||||
|
return T{impl::ParseField<*((*configs).second), FieldInfo<T, (*configs).first>>(element)...};
|
||||||
|
} catch(const ParsingError& error) {
|
||||||
|
throw ElementParsingError(std::format("[{}] parsing error: [ {} ]", nameof::nameof_full_type<T>(), error.what()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace larra::xmpp::serialization
|
18
library/include/larra/serialization/error.hpp
Normal file
18
library/include/larra/serialization/error.hpp
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace larra::xmpp::serialization {
|
||||||
|
|
||||||
|
struct ParsingError : std::runtime_error {
|
||||||
|
using std::runtime_error::runtime_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AttributeParsingError : ParsingError {
|
||||||
|
using ParsingError::ParsingError;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ElementParsingError : ParsingError {
|
||||||
|
using ParsingError::ParsingError;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace larra::xmpp::serialization
|
|
@ -2,7 +2,6 @@
|
||||||
#include <libxml++/libxml++.h>
|
#include <libxml++/libxml++.h>
|
||||||
|
|
||||||
#include <larra/jid.hpp>
|
#include <larra/jid.hpp>
|
||||||
#include <pugixml.hpp>
|
|
||||||
|
|
||||||
namespace larra::xmpp {
|
namespace larra::xmpp {
|
||||||
|
|
||||||
|
@ -19,8 +18,8 @@ struct BasicStream {
|
||||||
static constexpr auto kAddXmlDecl = true;
|
static constexpr auto kAddXmlDecl = true;
|
||||||
static inline const std::string kDefaultPrefix = "";
|
static inline const std::string kDefaultPrefix = "";
|
||||||
static inline const std::string kDefaultName = "stream:stream";
|
static inline const std::string kDefaultName = "stream:stream";
|
||||||
FromType from;
|
FromType from{};
|
||||||
ToType to;
|
ToType to{};
|
||||||
std::optional<std::string> id;
|
std::optional<std::string> id;
|
||||||
std::optional<std::string> version;
|
std::optional<std::string> version;
|
||||||
std::optional<std::string> xmlLang;
|
std::optional<std::string> xmlLang;
|
||||||
|
@ -46,7 +45,8 @@ struct BasicStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Self>
|
template <typename Self>
|
||||||
[[nodiscard]] constexpr auto XmlLang(this Self&& self, std::optional<std::string> value) -> BasicStream {
|
[[nodiscard]] constexpr auto XmlLang(this Self&& self, std::optional<std::string> value) // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||||
|
-> BasicStream {
|
||||||
return utils::FieldSetHelper::With<"xmlLang", BasicStream>(std::forward_like<Self>(self), std::move(value));
|
return utils::FieldSetHelper::With<"xmlLang", BasicStream>(std::forward_like<Self>(self), std::move(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +57,6 @@ struct BasicStream {
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
friend auto ToString(const BasicStream<JidFrom, JidTo>&) -> std::string;
|
friend auto ToString(const BasicStream<JidFrom, JidTo>&) -> std::string;
|
||||||
static auto Parse(const pugi::xml_node& node) -> BasicStream<JidFrom, JidTo>;
|
|
||||||
[[nodiscard]] static auto Parse(const xmlpp::Element*) -> BasicStream;
|
[[nodiscard]] static auto Parse(const xmlpp::Element*) -> BasicStream;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <libxml++/libxml++.h>
|
#include <libxml++/libxml++.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <larra/utils.hpp>
|
||||||
#include <nameof.hpp>
|
#include <nameof.hpp>
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
@ -23,20 +23,18 @@ constexpr auto ToKebabCaseName() -> std::string_view {
|
||||||
static constexpr auto rawStr = nameof::nameof_short_type<T>();
|
static constexpr auto rawStr = nameof::nameof_short_type<T>();
|
||||||
|
|
||||||
constexpr auto str = [] {
|
constexpr auto str = [] {
|
||||||
return rawStr | std::views::transform([](auto ch) {
|
return rawStr //
|
||||||
|
| std::views::transform([](auto ch) {
|
||||||
return impl::IsUpper(ch) ? std::array<char, 2>{'-', impl::ToLower(ch)} : std::array<char, 2>{ch, '\0'};
|
return impl::IsUpper(ch) ? std::array<char, 2>{'-', impl::ToLower(ch)} : std::array<char, 2>{ch, '\0'};
|
||||||
}) |
|
}) //
|
||||||
std::views::join | std::views::filter([](char ch) {
|
| std::views::join //
|
||||||
|
| std::views::filter([](char ch) {
|
||||||
return ch != '\0';
|
return ch != '\0';
|
||||||
});
|
}) //
|
||||||
|
| std::views::drop(1);
|
||||||
};
|
};
|
||||||
constexpr auto size = std::ranges::distance(str()) - 1;
|
static constexpr auto arr = str() | std::ranges::to<utils::RangeToWrapper<std::array<char, std::ranges::distance(str())>>>();
|
||||||
static constexpr auto arr = [&] {
|
return {arr.data(), arr.size()};
|
||||||
std::array<char, size> response; // NOLINT
|
|
||||||
std::ranges::copy(str() | std::views::drop(1), response.begin());
|
|
||||||
return response;
|
|
||||||
}();
|
|
||||||
return {arr.data(), size};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace impl
|
} // namespace impl
|
||||||
|
@ -53,15 +51,11 @@ struct ErrorImpl : BaseError {
|
||||||
static inline const auto kKebabCaseName = static_cast<std::string>(impl::ToKebabCaseName<T>());
|
static inline const auto kKebabCaseName = static_cast<std::string>(impl::ToKebabCaseName<T>());
|
||||||
|
|
||||||
static constexpr auto kErrorMessage = [] -> std::string_view {
|
static constexpr auto kErrorMessage = [] -> std::string_view {
|
||||||
|
static constexpr auto name = nameof::nameof_short_type<T>();
|
||||||
static constexpr auto str = [] {
|
static constexpr auto str = [] {
|
||||||
return std::array{std::string_view{"Stream Error: "}, nameof::nameof_short_type<T>(), std::string_view{"\0", 1}} | std::views::join;
|
return std::array{std::string_view{"Stream Error: "}, std::string_view{name}, std::string_view{"\0", 1}} | std::views::join;
|
||||||
};
|
};
|
||||||
constexpr auto size = std::ranges::distance(str());
|
static constexpr auto array = str() | std::ranges::to<utils::RangeToWrapper<std::array<char, std::ranges::distance(str())>>>();
|
||||||
static constexpr auto array = [&] {
|
|
||||||
std::array<char, size> response; // NOLINT
|
|
||||||
std::ranges::copy(str(), response.begin());
|
|
||||||
return response;
|
|
||||||
}();
|
|
||||||
return {array.data(), array.size() - 1};
|
return {array.data(), array.size() - 1};
|
||||||
}();
|
}();
|
||||||
static constexpr auto TryParse(xmlpp::Element* element) -> std::optional<T> {
|
static constexpr auto TryParse(xmlpp::Element* element) -> std::optional<T> {
|
||||||
|
|
|
@ -20,7 +20,8 @@ struct PlainUserAccount {
|
||||||
struct EncryptionUserAccount : PlainUserAccount {
|
struct EncryptionUserAccount : PlainUserAccount {
|
||||||
using PlainUserAccount::PlainUserAccount;
|
using PlainUserAccount::PlainUserAccount;
|
||||||
constexpr EncryptionUserAccount(PlainUserAccount base) : PlainUserAccount{std::move(base)} {};
|
constexpr EncryptionUserAccount(PlainUserAccount base) : PlainUserAccount{std::move(base)} {};
|
||||||
constexpr EncryptionUserAccount(BareJid jid, std::string password) : PlainUserAccount{std::move(jid), std::move(password)} {};
|
constexpr EncryptionUserAccount(BareJid jid, std::string password) :
|
||||||
|
PlainUserAccount{.jid = std::move(jid), .password = std::move(password)} {};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EncryptionRequiredUserAccount : EncryptionUserAccount {
|
struct EncryptionRequiredUserAccount : EncryptionUserAccount {
|
||||||
|
@ -35,7 +36,7 @@ using UserAccountVariant = std::variant<PlainUserAccount, EncryptionUserAccount,
|
||||||
struct UserAccount : UserAccountVariant {
|
struct UserAccount : UserAccountVariant {
|
||||||
using UserAccountVariant::variant;
|
using UserAccountVariant::variant;
|
||||||
template <typename Self>
|
template <typename Self>
|
||||||
constexpr auto Jid(this Self&& self, BareJid value) -> UserAccount {
|
constexpr auto Jid(this Self&& self, BareJid value) -> UserAccount { // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||||
return std::visit<UserAccount>(
|
return std::visit<UserAccount>(
|
||||||
[&](auto& ref) -> std::decay_t<decltype(ref)> {
|
[&](auto& ref) -> std::decay_t<decltype(ref)> {
|
||||||
return {std::forward_like<Self>(ref).Jid(std::move(value))};
|
return {std::forward_like<Self>(ref).Jid(std::move(value))};
|
||||||
|
@ -43,7 +44,7 @@ struct UserAccount : UserAccountVariant {
|
||||||
self);
|
self);
|
||||||
}
|
}
|
||||||
template <typename Self>
|
template <typename Self>
|
||||||
constexpr auto Password(this Self&& self, std::string value) -> UserAccount {
|
constexpr auto Password(this Self&& self, std::string value) -> UserAccount { // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||||
return std::visit<UserAccount>(
|
return std::visit<UserAccount>(
|
||||||
[&](auto& ref) -> std::decay_t<decltype(ref)> {
|
[&](auto& ref) -> std::decay_t<decltype(ref)> {
|
||||||
return std::forward_like<Self>(ref).Password(std::move(value));
|
return std::forward_like<Self>(ref).Password(std::move(value));
|
||||||
|
@ -51,7 +52,7 @@ struct UserAccount : UserAccountVariant {
|
||||||
self);
|
self);
|
||||||
}
|
}
|
||||||
template <typename Self>
|
template <typename Self>
|
||||||
constexpr auto Jid(this Self&& self) -> decltype(auto) {
|
constexpr auto Jid(this Self&& self) -> decltype(auto) { // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||||
return std::visit<decltype(std::forward_like<Self>(std::declval<BareJid&>()))>(
|
return std::visit<decltype(std::forward_like<Self>(std::declval<BareJid&>()))>(
|
||||||
[](auto& ref) -> decltype(auto) {
|
[](auto& ref) -> decltype(auto) {
|
||||||
return std::forward_like<Self>(ref.jid);
|
return std::forward_like<Self>(ref.jid);
|
||||||
|
@ -59,7 +60,7 @@ struct UserAccount : UserAccountVariant {
|
||||||
self);
|
self);
|
||||||
}
|
}
|
||||||
template <typename Self>
|
template <typename Self>
|
||||||
constexpr auto Password(this Self&& self) -> decltype(auto) {
|
constexpr auto Password(this Self&& self) -> decltype(auto) { // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||||
return std::visit<decltype(std::forward_like<Self>(std::declval<BareJid&>()))>(
|
return std::visit<decltype(std::forward_like<Self>(std::declval<BareJid&>()))>(
|
||||||
[](auto& ref) -> decltype(auto) {
|
[](auto& ref) -> decltype(auto) {
|
||||||
return std::forward_like<Self>(ref.password);
|
return std::forward_like<Self>(ref.password);
|
||||||
|
|
|
@ -83,9 +83,9 @@ struct FieldsDescription {
|
||||||
* \param value new value for field
|
* \param value new value for field
|
||||||
* \return an object of type std::decay_t<Self> with data from T with fields from \ref self and the \ref value of the field \ref ptr
|
* \return an object of type std::decay_t<Self> with data from T with fields from \ref self and the \ref value of the field \ref ptr
|
||||||
*/
|
*/
|
||||||
template <typename Self, typename Value, typename Type> // NOLINTNEXTLINE
|
template <typename Self, typename Value, typename Type>
|
||||||
constexpr auto With(Type(T::* ptr), Self&& self, Value&& value) const -> std::decay_t<Self>
|
constexpr auto With(Type(T::* ptr), Self&& self, Value&& value) const // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||||
requires(std::is_constructible_v<T, impl::GetTypeT<Fs>...> && std::constructible_from<std::decay_t<Self>, T> &&
|
requires(std::is_constructible_v<T, impl::GetTypeT<Fs>...> && std::is_constructible_v<std::decay_t<Self>, T> &&
|
||||||
std::is_constructible_v<Type, decltype(value)> && impl::SetConcept<Self, Type, Fs...>)
|
std::is_constructible_v<Type, decltype(value)> && impl::SetConcept<Self, Type, Fs...>)
|
||||||
{
|
{
|
||||||
return std::decay_t<Self>{utempl::Unpack(this->tuple, [&](auto... fs) -> T {
|
return std::decay_t<Self>{utempl::Unpack(this->tuple, [&](auto... fs) -> T {
|
||||||
|
@ -267,4 +267,32 @@ inline auto StartLifetimeAs(const void* ptr) -> const T* {
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Example: "Hello" | std::ranges::to<RangeToWrapper<std::array<char, 6>>>()
|
||||||
|
template <typename T>
|
||||||
|
struct RangeToWrapper : T {
|
||||||
|
[[nodiscard]] constexpr auto Base() & -> decltype(auto) {
|
||||||
|
return static_cast<T&>(*this);
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr auto Base() const& -> decltype(auto) {
|
||||||
|
return static_cast<const T&>(*this);
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr auto Base() && -> decltype(auto) {
|
||||||
|
return static_cast<T&&>(*this);
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr auto Base() const&& -> decltype(auto) {
|
||||||
|
return static_cast<const T&&>(*this);
|
||||||
|
}
|
||||||
|
template <typename Range> // NOLINTNEXTLINE
|
||||||
|
constexpr RangeToWrapper(std::from_range_t, Range&& range) {
|
||||||
|
auto result = std::ranges::copy(range, this->begin());
|
||||||
|
if(result.in != std::ranges::end(range) || result.out != std::ranges::end(*this)) {
|
||||||
|
throw std::invalid_argument{"Invalid range size"};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template <typename... Args> // NOLINTNEXTLINE
|
||||||
|
constexpr RangeToWrapper(Args&&... args)
|
||||||
|
requires requires { T{std::forward<Args>(args)...}; }
|
||||||
|
: T{std::forward<Args>(args)...} {};
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace larra::xmpp::utils
|
} // namespace larra::xmpp::utils
|
||||||
|
|
|
@ -52,7 +52,7 @@ class Parser : private xmlpp::SaxParser {
|
||||||
|
|
||||||
auto ParseChunk(std::string_view str) -> const _xmlError*;
|
auto ParseChunk(std::string_view str) -> const _xmlError*;
|
||||||
|
|
||||||
std::stack<xmlpp::Element*> context;
|
std::stack<xmlpp::Element*> context{};
|
||||||
xmlpp::Document& doc;
|
xmlpp::Document& doc;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -98,7 +98,7 @@ struct XmlStream : Stream {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto next_layer() const -> const Stream& {
|
[[nodiscard]] auto next_layer() const -> const Stream& {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,7 +313,7 @@ struct XmlStream : Stream {
|
||||||
|
|
||||||
XmlStream(XmlStream&& other) = default;
|
XmlStream(XmlStream&& other) = default;
|
||||||
|
|
||||||
std::unique_ptr<BufferType> streambuf; // Not movable :(
|
std::unique_ptr<BufferType> streambuf{}; // Not movable :(
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace larra::xmpp
|
} // namespace larra::xmpp
|
||||||
|
|
|
@ -53,30 +53,36 @@ struct DataHolder {
|
||||||
};
|
};
|
||||||
|
|
||||||
consteval auto GetDataForTag(sha512sum::EncryptionTag) {
|
consteval auto GetDataForTag(sha512sum::EncryptionTag) {
|
||||||
return DataHolder{[] {
|
return DataHolder{.first =
|
||||||
|
[] {
|
||||||
return EVP_sha512();
|
return EVP_sha512();
|
||||||
},
|
},
|
||||||
kSha512ResultSize,
|
.second = kSha512ResultSize,
|
||||||
|
.third =
|
||||||
[](auto... args) {
|
[](auto... args) {
|
||||||
return SHA512(args...);
|
return SHA512(args...);
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
consteval auto GetDataForTag(sha256sum::EncryptionTag) {
|
consteval auto GetDataForTag(sha256sum::EncryptionTag) {
|
||||||
return DataHolder{[] {
|
return DataHolder{.first =
|
||||||
|
[] {
|
||||||
return EVP_sha256();
|
return EVP_sha256();
|
||||||
},
|
},
|
||||||
kSha256ResultSize,
|
.second = kSha256ResultSize,
|
||||||
|
.third =
|
||||||
[](auto... args) {
|
[](auto... args) {
|
||||||
return SHA256(args...);
|
return SHA256(args...);
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
consteval auto GetDataForTag(sha1sum::EncryptionTag) {
|
consteval auto GetDataForTag(sha1sum::EncryptionTag) {
|
||||||
return DataHolder{[] {
|
return DataHolder{.first =
|
||||||
|
[] {
|
||||||
return EVP_sha256();
|
return EVP_sha256();
|
||||||
},
|
},
|
||||||
kSha1ResultSize,
|
.second = kSha1ResultSize,
|
||||||
|
.third =
|
||||||
[](auto... args) {
|
[](auto... args) {
|
||||||
return SHA1(args...);
|
return SHA1(args...);
|
||||||
}};
|
}};
|
||||||
|
|
|
@ -4,17 +4,17 @@
|
||||||
namespace larra::xmpp {
|
namespace larra::xmpp {
|
||||||
|
|
||||||
auto ParseBareJid(std::string_view jid, std::size_t at) -> BareJid {
|
auto ParseBareJid(std::string_view jid, std::size_t at) -> BareJid {
|
||||||
return {static_cast<std::string>(jid.substr(0, at)), static_cast<std::string>(jid.substr(at + 1))};
|
return {.username = static_cast<std::string>(jid.substr(0, at)), .server = static_cast<std::string>(jid.substr(at + 1))};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ParseBareResourceJid(std::string_view jid, std::size_t slash) -> BareResourceJid {
|
auto ParseBareResourceJid(std::string_view jid, std::size_t slash) -> BareResourceJid {
|
||||||
return {static_cast<std::string>(jid.substr(0, slash)), static_cast<std::string>(jid.substr(slash + 1))};
|
return {.server = static_cast<std::string>(jid.substr(0, slash)), .resource = static_cast<std::string>(jid.substr(slash + 1))};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ParseFullJid(std::string_view jid, std::size_t at, std::size_t slash) -> FullJid {
|
auto ParseFullJid(std::string_view jid, std::size_t at, std::size_t slash) -> FullJid {
|
||||||
return {static_cast<std::string>(jid.substr(0, at)),
|
return {.username = static_cast<std::string>(jid.substr(0, at)),
|
||||||
static_cast<std::string>(jid.substr(at + 1, slash - at - 1)),
|
.server = static_cast<std::string>(jid.substr(at + 1, slash - at - 1)),
|
||||||
static_cast<std::string>(jid.substr(slash + 1))};
|
.resource = static_cast<std::string>(jid.substr(slash + 1))};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto BareJid::Parse(std::string_view jid) -> BareJid {
|
auto BareJid::Parse(std::string_view jid) -> BareJid {
|
||||||
|
|
|
@ -3,23 +3,10 @@
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
inline auto ToOptionalString(const pugi::xml_attribute& attribute) -> std::optional<std::string> {
|
|
||||||
return attribute ? std::optional{std::string{attribute.as_string()}} : std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto ToOptionalString(const xmlpp::Attribute* attribute) -> std::optional<std::string> {
|
inline auto ToOptionalString(const xmlpp::Attribute* attribute) -> std::optional<std::string> {
|
||||||
return attribute ? std::optional{std::string{attribute->get_value()}} : std::nullopt;
|
return attribute ? std::optional{std::string{attribute->get_value()}} : std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool IsJid>
|
|
||||||
inline auto ToOptionalUser(const pugi::xml_attribute& attribute) {
|
|
||||||
if constexpr(IsJid) {
|
|
||||||
return attribute ? std::optional{larra::xmpp::BareJid::Parse(attribute.as_string())} : std::nullopt;
|
|
||||||
} else {
|
|
||||||
return attribute ? std::optional{std::string{attribute.as_string()}} : std::nullopt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <bool IsJid>
|
template <bool IsJid>
|
||||||
inline auto ToOptionalUser(const xmlpp::Attribute* attribute) {
|
inline auto ToOptionalUser(const xmlpp::Attribute* attribute) {
|
||||||
if constexpr(IsJid) {
|
if constexpr(IsJid) {
|
||||||
|
@ -61,15 +48,6 @@ template auto ServerStream::SerializeStream(xmlpp::Element* node) const -> void;
|
||||||
template auto ServerToUserStream::SerializeStream(xmlpp::Element* node) const -> void;
|
template auto ServerToUserStream::SerializeStream(xmlpp::Element* node) const -> void;
|
||||||
template auto UserStream::SerializeStream(xmlpp::Element* node) const -> void;
|
template auto UserStream::SerializeStream(xmlpp::Element* node) const -> void;
|
||||||
|
|
||||||
template <bool JidFrom, bool JidTo>
|
|
||||||
auto impl::BasicStream<JidFrom, JidTo>::Parse(const pugi::xml_node& node) -> impl::BasicStream<JidFrom, JidTo> {
|
|
||||||
return {ToOptionalUser<JidFrom>(node.attribute("from")),
|
|
||||||
ToOptionalUser<JidTo>(node.attribute("to")),
|
|
||||||
ToOptionalString(node.attribute("id")),
|
|
||||||
ToOptionalString(node.attribute("version")),
|
|
||||||
ToOptionalString(node.attribute("xml:lang"))};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <bool JidFrom, bool JidTo>
|
template <bool JidFrom, bool JidTo>
|
||||||
auto impl::BasicStream<JidFrom, JidTo>::Parse(const xmlpp::Element* node) -> impl::BasicStream<JidFrom, JidTo> {
|
auto impl::BasicStream<JidFrom, JidTo>::Parse(const xmlpp::Element* node) -> impl::BasicStream<JidFrom, JidTo> {
|
||||||
return {ToOptionalUser<JidFrom>(node->get_attribute("from")),
|
return {ToOptionalUser<JidFrom>(node->get_attribute("from")),
|
||||||
|
@ -78,12 +56,6 @@ auto impl::BasicStream<JidFrom, JidTo>::Parse(const xmlpp::Element* node) -> imp
|
||||||
ToOptionalString(node->get_attribute("version")),
|
ToOptionalString(node->get_attribute("version")),
|
||||||
ToOptionalString(node->get_attribute("lang", "xml"))};
|
ToOptionalString(node->get_attribute("lang", "xml"))};
|
||||||
}
|
}
|
||||||
|
|
||||||
template auto UserStream::Parse(const pugi::xml_node& node) -> UserStream;
|
|
||||||
|
|
||||||
template auto ServerStream::Parse(const pugi::xml_node& node) -> ServerStream;
|
|
||||||
template auto ServerToUserStream::Parse(const pugi::xml_node& node) -> ServerToUserStream;
|
|
||||||
|
|
||||||
template auto UserStream::Parse(const xmlpp::Element* node) -> UserStream;
|
template auto UserStream::Parse(const xmlpp::Element* node) -> UserStream;
|
||||||
|
|
||||||
template auto ServerStream::Parse(const xmlpp::Element* node) -> ServerStream;
|
template auto ServerStream::Parse(const xmlpp::Element* node) -> ServerStream;
|
||||||
|
|
|
@ -1,20 +1,23 @@
|
||||||
#include <libxml/parser.h>
|
#include <libxml/parser.h>
|
||||||
|
|
||||||
#include <larra/impl/public_cast.hpp>
|
|
||||||
#include <larra/utils.hpp>
|
#include <larra/utils.hpp>
|
||||||
#include <larra/xml_stream.hpp>
|
#include <larra/xml_stream.hpp>
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
#include <span>
|
#include <utempl/public_cast.hpp>
|
||||||
|
|
||||||
namespace larra::xmpp::impl {
|
namespace utempl {
|
||||||
|
|
||||||
template struct PublicCast<&xmlpp::SaxParser::sax_handler_>;
|
template struct PublicCast<&xmlpp::SaxParser::sax_handler_>;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace larra::xmpp::impl {
|
||||||
|
|
||||||
auto Parser::ParseChunk(std::string_view str) -> const xmlError* {
|
auto Parser::ParseChunk(std::string_view str) -> const xmlError* {
|
||||||
xmlResetLastError();
|
xmlResetLastError();
|
||||||
|
|
||||||
if(!context_) {
|
if(!context_) {
|
||||||
this->context_ = xmlCreatePushParserCtxt((this->*GetPrivateMember(static_cast<xmlpp::SaxParser&>(*this))).get(),
|
this->context_ = xmlCreatePushParserCtxt((this->*utempl::GetPrivateMember<"sax_handler_">(static_cast<xmlpp::SaxParser&>(*this))).get(),
|
||||||
nullptr, // user_data
|
nullptr, // user_data
|
||||||
nullptr, // chunk
|
nullptr, // chunk
|
||||||
0, // size
|
0, // size
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <larra/jid.hpp>
|
||||||
#include <larra/serialization.hpp>
|
#include <larra/serialization.hpp>
|
||||||
|
#include <larra/serialization/auto.hpp>
|
||||||
|
#include <larra/serialization/error.hpp>
|
||||||
#include <larra/stream_error.hpp>
|
#include <larra/stream_error.hpp>
|
||||||
|
|
||||||
|
using namespace std::literals;
|
||||||
|
|
||||||
namespace larra::xmpp {
|
namespace larra::xmpp {
|
||||||
|
|
||||||
TEST(Parse, Variant) {
|
TEST(Parse, Variant) {
|
||||||
|
@ -21,8 +26,103 @@ TEST(Serialize, Variant) {
|
||||||
auto node = doc.create_root_node("stream:error");
|
auto node = doc.create_root_node("stream:error");
|
||||||
S::Serialize(node, data);
|
S::Serialize(node, data);
|
||||||
EXPECT_EQ(doc.write_to_string(),
|
EXPECT_EQ(doc.write_to_string(),
|
||||||
std::string_view{"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<stream:error><unsupported-stanza-type "
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<stream:error><unsupported-stanza-type "
|
||||||
"xmlns=\"urn:ietf:params:xml:ns:xmpp-streams\"/></stream:error>\n"});
|
"xmlns=\"urn:ietf:params:xml:ns:xmpp-streams\"/></stream:error>\n"sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace tests::serialization {
|
||||||
|
|
||||||
|
struct SomeStruct {
|
||||||
|
static constexpr auto kDefaultName = "some";
|
||||||
|
std::string value;
|
||||||
|
[[nodiscard]] static auto Parse(xmlpp::Element* element) -> SomeStruct;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SomeStruct2 {
|
||||||
|
static constexpr auto kDefaultName = "some2";
|
||||||
|
SomeStruct value;
|
||||||
|
[[nodiscard]] static auto Parse(xmlpp::Element* element) -> SomeStruct2;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SomeStruct3 {
|
||||||
|
static constexpr auto kDefaultName = "some3";
|
||||||
|
int value;
|
||||||
|
[[nodiscard]] static auto Parse(xmlpp::Element* element) -> SomeStruct3;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SomeStruct4 {
|
||||||
|
static constexpr auto kDefaultName = "some4";
|
||||||
|
SomeStruct3 value;
|
||||||
|
[[nodiscard]] static auto Parse(xmlpp::Element* element) -> SomeStruct4;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SomeStruct5 {
|
||||||
|
static constexpr auto kDefaultName = "some5";
|
||||||
|
BareJid value;
|
||||||
|
[[nodiscard]] static auto Parse(xmlpp::Element* element) -> SomeStruct5;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace tests::serialization
|
||||||
|
|
||||||
|
namespace serialization {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
constexpr auto kSerializationConfig<tests::serialization::SomeStruct> = SerializationConfig<tests::serialization::SomeStruct>{};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
constexpr auto kSerializationConfig<tests::serialization::SomeStruct2> = SerializationConfig<tests::serialization::SomeStruct2>{};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
constexpr auto kSerializationConfig<tests::serialization::SomeStruct4> = SerializationConfig<tests::serialization::SomeStruct4>{};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
constexpr auto kSerializationConfig<tests::serialization::SomeStruct5> = SerializationConfig<tests::serialization::SomeStruct5>{};
|
||||||
|
|
||||||
|
} // namespace serialization
|
||||||
|
|
||||||
|
auto tests::serialization::SomeStruct::Parse(xmlpp::Element* element) -> SomeStruct {
|
||||||
|
return ::larra::xmpp::serialization::Parse<tests::serialization::SomeStruct>(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto tests::serialization::SomeStruct2::Parse(xmlpp::Element* element) -> SomeStruct2 {
|
||||||
|
return ::larra::xmpp::serialization::Parse<tests::serialization::SomeStruct2>(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto tests::serialization::SomeStruct3::Parse(xmlpp::Element*) -> SomeStruct3 {
|
||||||
|
return {.value = 42}; // NOLINT
|
||||||
|
}
|
||||||
|
|
||||||
|
auto tests::serialization::SomeStruct4::Parse(xmlpp::Element* element) -> SomeStruct4 {
|
||||||
|
return ::larra::xmpp::serialization::Parse<tests::serialization::SomeStruct4>(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto tests::serialization::SomeStruct5::Parse(xmlpp::Element* element) -> SomeStruct5 {
|
||||||
|
return ::larra::xmpp::serialization::Parse<tests::serialization::SomeStruct5>(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(AutoParse, Basic) {
|
||||||
|
xmlpp::Document doc;
|
||||||
|
auto node = doc.create_root_node("some2");
|
||||||
|
node = node->add_child_element("some");
|
||||||
|
node->set_attribute("value", "Hello");
|
||||||
|
auto a = Serialization<tests::serialization::SomeStruct>::Parse(node);
|
||||||
|
EXPECT_EQ(a.value, "Hello"sv);
|
||||||
|
auto b = Serialization<tests::serialization::SomeStruct2>::Parse(doc.get_root_node());
|
||||||
|
EXPECT_EQ(b.value.value, "Hello"sv);
|
||||||
|
EXPECT_THROW(std::ignore = tests::serialization::SomeStruct2::Parse(node), serialization::ParsingError);
|
||||||
|
auto node2 = node->add_child_element("some4");
|
||||||
|
node2->add_child_element("some3");
|
||||||
|
auto c = Serialization<tests::serialization::SomeStruct4>::Parse(node2);
|
||||||
|
EXPECT_EQ(c.value.value, 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(AutoParse, Attribute) {
|
||||||
|
xmlpp::Document doc;
|
||||||
|
auto node = doc.create_root_node("some5");
|
||||||
|
node->set_attribute("value", "user@server.i2p");
|
||||||
|
auto a = Serialization<tests::serialization::SomeStruct5>::Parse(node);
|
||||||
|
EXPECT_EQ(a.value.server, "server.i2p"sv);
|
||||||
|
EXPECT_EQ(a.value.username, "user"sv);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace larra::xmpp
|
} // namespace larra::xmpp
|
||||||
|
|
Loading…
Reference in a new issue