Compare commits

..

4 commits

Author SHA1 Message Date
9b76d2baad . 2024-10-19 18:56:44 +00:00
4177dc88d0 . 2024-10-19 18:50:47 +00:00
da9640ca96 . 2024-10-19 18:15:24 +00:00
21d5b22f6b . 2024-10-19 17:32:45 +00:00
20 changed files with 136 additions and 591 deletions

View file

@ -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 # Update the package database and install clang
# 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 python-pip wget which pkgconf \ && pacman -Syyu --noconfirm git less vim sudo base-devel python-pip \
&& pacman -Syyu --noconfirm cmake make gcc ninja meson shellcheck \ && pacman -Syyu --noconfirm clang cmake make 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,49 +21,10 @@ 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\
@ -76,6 +37,9 @@ 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

View file

@ -1,7 +1,4 @@
sleep 5 sleep 1
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'

View file

@ -1,188 +0,0 @@
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
View file

@ -29,4 +29,5 @@ ext/
libxmlplusplus-prefix/ libxmlplusplus-prefix/
spdlog.pc spdlog.pc
build* build*
larra
temp* temp*

2
.vscode/launch.json vendored
View file

@ -11,7 +11,7 @@
"program": "${workspaceFolder}/build/examples/output/connect", "program": "${workspaceFolder}/build/examples/output/connect",
"args": [], "args": [],
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}",
"preLaunchTask": "GCC: Build" "preLaunchTask": "Build Debug GCC"
} }
] ]
} }

258
.vscode/tasks.json vendored
View file

@ -3,45 +3,6 @@
// 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",
@ -49,244 +10,55 @@
"sudo docker exec -it ejabberd tail -f logs/ejabberd.log" "sudo docker exec -it ejabberd tail -f logs/ejabberd.log"
] ]
}, },
{
"label": "Ejabberd: show localhost registered users",
"type": "shell",
"command": [
"sudo docker exec -it ejabberd ejabberdctl registered_users localhost"
]
},
{ {
"label": "Ejabberd: open shell", "label": "Ejabberd: open shell",
"type": "shell", "type": "shell",
"command": [ "command": [
"printf 'Example cmd: ejabberdctl registered_users localhost\n\n';",
"sudo docker exec -it ejabberd sh" "sudo docker exec -it ejabberd sh"
] ]
}, },
//
// GCC build related tasks
//
{ {
"label": "GCC: Clean build folder", "label": "Build Debug GCC",
"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 cmake -Wno-dev ", "cmake -Wno-dev -DCMAKE_BUILD_TYPE=Debug -DENABLE_EXAMPLES=ON -DENABLE_TESTS=ON .. &&",
" -DCMAKE_BUILD_TYPE=Debug -DENABLE_EXAMPLES=ON -DENABLE_TESTS=ON .."
],
"options": {
"env": {
"CC": "/usr/sbin/gcc",
"CXX": "/usr/sbin/g++"
}
},
"presentation": {
"clear": true
},
"hide": true
},
{
"label": "GCC: Build",
"type": "shell",
"command": [
"cd ${workspaceFolder}/build &&",
"cmake --build . --parallel `nproc`", "cmake --build . --parallel `nproc`",
"|| ( printf '\n\n\t\\e[31mERROR: Build failed!\\e[0m\n\n\n' && exit 1 )" "|| ( printf '\n\n\t\\e[31mERROR: Build failed!\\e[0m\n\n\n' && exit 1 )"
], ],
"options": {
"env": {}
},
"presentation": { "presentation": {
"clear": true "clear": true
}, },
"hide": true, "problemMatcher": [],
"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": "Clang: Configure Debug", "label": "Build Debug Clang",
"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 ", "cmake -Wno-dev -DCMAKE_BUILD_TYPE=Debug -DENABLE_EXAMPLES=ON -DENABLE_TESTS=ON .. &&",
" -DCMAKE_BUILD_TYPE=Debug -DENABLE_EXAMPLES=ON -DENABLE_TESTS=ON ",
" -DCMAKE_CXX_FLAGS=\"-stdlib=libstdc++\"",
" -DCMAKE_EXE_LINKER_FLAGS=\"-L/usr/lib/x86_64-linux-gnu -lstdc++\" .."
],
"options": {
"env": {
"CC": "clang",
"CXX": "clang++"
}
},
"presentation": {
"clear": true
},
"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`", "cmake --build . --parallel `nproc`",
"|| ( printf '\n\n\t\\e[31mERROR: Build failed!\\e[0m\n\n\n' && exit 1 )" "|| ( printf '\n\n\t\\e[31mERROR: Build failed!\\e[0m\n\n\n' && exit 1 )"
], ],
"options": {
"env": {
"CC": "/usr/sbin/clang",
"CXX": "/usr/sbin/clang++"
}
},
"presentation": { "presentation": {
"clear": true "clear": true
}, },
"group": { "problemMatcher": []
"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
} }
] ]
} }

View file

@ -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://sha512sum.xyz/git/sha512sum/utempl" "https://helicopter.myftp.org/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,6 +59,8 @@ 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
@ -161,10 +163,8 @@ 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
"$<$<CXX_COMPILER_ID:GNU>:-Wno-changes-meaning>" target_compile_options(larra_xmpp PUBLIC -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 Boost::asio Boost::serialization utempl::utempl pugixml::pugixml
OpenSSL::SSL nameof::nameof fmt::fmt OpenSSL::SSL nameof::nameof
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} OpenSSL::SSL utempl::utempl ${Boost_LIBRARIES} pugixml::pugixml OpenSSL::SSL
nameof::nameof fmt::fmt nameof::nameof
OpenSSL::Crypto spdlog xmlplusplus ${LIBXML2_LIBRARIES}) OpenSSL::Crypto spdlog xmlplusplus ${LIBXML2_LIBRARIES})
endif() endif()

View file

@ -74,7 +74,7 @@ struct Client {
private: private:
bool active = true; bool active = true;
XmlStream<Connection> connection{}; XmlStream<Connection> connection;
BareJid jid; BareJid jid;
}; };

View file

@ -3,6 +3,7 @@
#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 {
@ -10,7 +11,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;
}; };
@ -50,9 +51,10 @@ 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<const xmlpp::Node*> value) { [[nodiscard]] constexpr auto Others(this Self&& self, std::vector<pugi::xml_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;
}; };

View file

@ -12,7 +12,7 @@ struct PublicCastTag {
template <auto ptr, std::size_t I = 0> template <auto ptr, std::size_t I = 0>
struct PublicCast {}; struct PublicCast {};
template <typename T, typename R, R T::* ptr, std::size_t I> template <typename T, typename R, R T::*ptr, std::size_t I>
struct PublicCast<ptr, I> { struct PublicCast<ptr, I> {
friend constexpr auto MagicGetPrivateMember(PublicCastTag<T, I>) { friend constexpr auto MagicGetPrivateMember(PublicCastTag<T, I>) {
return ptr; return ptr;

View file

@ -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());
} }
[[nodiscard]] auto next_layer() const -> const next_layer_type& { 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());
} }
}; };

View file

@ -2,6 +2,7 @@
#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 {
@ -18,8 +19,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;
@ -45,8 +46,7 @@ struct BasicStream {
} }
template <typename Self> template <typename Self>
[[nodiscard]] constexpr auto XmlLang(this Self&& self, std::optional<std::string> value) // NOLINT(cppcoreguidelines-missing-std-forward) [[nodiscard]] constexpr auto XmlLang(this Self&& self, std::optional<std::string> value) -> BasicStream {
-> 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,6 +57,7 @@ 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;
}; };

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <libxml++/libxml++.h> #include <libxml++/libxml++.h>
#include <larra/utils.hpp> #include <algorithm>
#include <nameof.hpp> #include <nameof.hpp>
#include <ranges> #include <ranges>
#include <variant> #include <variant>
@ -23,18 +23,20 @@ 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 // return rawStr | std::views::transform([](auto ch) {
| 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 // return ch != '\0';
| std::views::filter([](char ch) { });
return ch != '\0';
}) //
| std::views::drop(1);
}; };
static constexpr auto arr = str() | std::ranges::to<utils::RangeToWrapper<std::array<char, std::ranges::distance(str())>>>(); constexpr auto size = std::ranges::distance(str()) - 1;
return {arr.data(), arr.size()}; static constexpr auto arr = [&] {
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
@ -51,11 +53,15 @@ 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: "}, std::string_view{name}, std::string_view{"\0", 1}} | std::views::join; return std::array{std::string_view{"Stream Error: "}, nameof::nameof_short_type<T>(), std::string_view{"\0", 1}} | std::views::join;
}; };
static constexpr auto array = str() | std::ranges::to<utils::RangeToWrapper<std::array<char, std::ranges::distance(str())>>>(); constexpr auto size = 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> {

View file

@ -20,8 +20,7 @@ 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) : constexpr EncryptionUserAccount(BareJid jid, std::string password) : PlainUserAccount{std::move(jid), std::move(password)} {};
PlainUserAccount{.jid = std::move(jid), .password = std::move(password)} {};
}; };
struct EncryptionRequiredUserAccount : EncryptionUserAccount { struct EncryptionRequiredUserAccount : EncryptionUserAccount {
@ -36,7 +35,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 { // NOLINT(cppcoreguidelines-missing-std-forward) constexpr auto Jid(this Self&& self, BareJid value) -> UserAccount {
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))};
@ -44,7 +43,7 @@ struct UserAccount : UserAccountVariant {
self); self);
} }
template <typename Self> template <typename Self>
constexpr auto Password(this Self&& self, std::string value) -> UserAccount { // NOLINT(cppcoreguidelines-missing-std-forward) constexpr auto Password(this Self&& self, std::string value) -> UserAccount {
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));
@ -52,7 +51,7 @@ struct UserAccount : UserAccountVariant {
self); self);
} }
template <typename Self> template <typename Self>
constexpr auto Jid(this Self&& self) -> decltype(auto) { // NOLINT(cppcoreguidelines-missing-std-forward) constexpr auto Jid(this Self&& self) -> decltype(auto) {
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);
@ -60,7 +59,7 @@ struct UserAccount : UserAccountVariant {
self); self);
} }
template <typename Self> template <typename Self>
constexpr auto Password(this Self&& self) -> decltype(auto) { // NOLINT(cppcoreguidelines-missing-std-forward) constexpr auto Password(this Self&& self) -> decltype(auto) {
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);

View file

@ -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> template <typename Self, typename Value, typename Type> // NOLINTNEXTLINE
constexpr auto With(Type(T::* ptr), Self&& self, Value&& value) const // NOLINT(cppcoreguidelines-missing-std-forward) constexpr auto With(Type(T::* ptr), Self&& self, Value&& value) const -> std::decay_t<Self>
requires(std::is_constructible_v<T, impl::GetTypeT<Fs>...> && std::is_constructible_v<std::decay_t<Self>, T> && requires(std::is_constructible_v<T, impl::GetTypeT<Fs>...> && std::constructible_from<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,32 +267,4 @@ 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

View file

@ -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;
} }
[[nodiscard]] auto next_layer() const -> const Stream& { 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

View file

@ -53,39 +53,33 @@ struct DataHolder {
}; };
consteval auto GetDataForTag(sha512sum::EncryptionTag) { consteval auto GetDataForTag(sha512sum::EncryptionTag) {
return DataHolder{.first = return DataHolder{[] {
[] { return EVP_sha512();
return EVP_sha512(); },
}, kSha512ResultSize,
.second = kSha512ResultSize, [](auto... args) {
.third = return SHA512(args...);
[](auto... args) { }};
return SHA512(args...);
}};
} }
consteval auto GetDataForTag(sha256sum::EncryptionTag) { consteval auto GetDataForTag(sha256sum::EncryptionTag) {
return DataHolder{.first = return DataHolder{[] {
[] { return EVP_sha256();
return EVP_sha256(); },
}, kSha256ResultSize,
.second = kSha256ResultSize, [](auto... args) {
.third = return SHA256(args...);
[](auto... args) { }};
return SHA256(args...);
}};
} }
consteval auto GetDataForTag(sha1sum::EncryptionTag) { consteval auto GetDataForTag(sha1sum::EncryptionTag) {
return DataHolder{.first = return DataHolder{[] {
[] { return EVP_sha256();
return EVP_sha256(); },
}, kSha1ResultSize,
.second = kSha1ResultSize, [](auto... args) {
.third = return SHA1(args...);
[](auto... args) { }};
return SHA1(args...);
}};
} }
template <typename TagType> template <typename TagType>

View file

@ -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 {.username = static_cast<std::string>(jid.substr(0, at)), .server = static_cast<std::string>(jid.substr(at + 1))}; return {static_cast<std::string>(jid.substr(0, at)), 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 {.server = static_cast<std::string>(jid.substr(0, slash)), .resource = static_cast<std::string>(jid.substr(slash + 1))}; return {static_cast<std::string>(jid.substr(0, slash)), 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 {.username = static_cast<std::string>(jid.substr(0, at)), return {static_cast<std::string>(jid.substr(0, at)),
.server = static_cast<std::string>(jid.substr(at + 1, slash - at - 1)), static_cast<std::string>(jid.substr(at + 1, slash - at - 1)),
.resource = static_cast<std::string>(jid.substr(slash + 1))}; static_cast<std::string>(jid.substr(slash + 1))};
} }
auto BareJid::Parse(std::string_view jid) -> BareJid { auto BareJid::Parse(std::string_view jid) -> BareJid {

View file

@ -3,10 +3,23 @@
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) {
@ -48,6 +61,15 @@ 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")),
@ -56,6 +78,12 @@ 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;

View file

@ -1,23 +1,20 @@
#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 <utempl/public_cast.hpp> #include <span>
namespace utempl {
template struct PublicCast<&xmlpp::SaxParser::sax_handler_>;
}
namespace larra::xmpp::impl { namespace larra::xmpp::impl {
template struct PublicCast<&xmlpp::SaxParser::sax_handler_>;
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->*utempl::GetPrivateMember<"sax_handler_">(static_cast<xmlpp::SaxParser&>(*this))).get(), this->context_ = xmlCreatePushParserCtxt((this->*GetPrivateMember(static_cast<xmlpp::SaxParser&>(*this))).get(),
nullptr, // user_data nullptr, // user_data
nullptr, // chunk nullptr, // chunk
0, // size 0, // size