feat: refactor

feat: use another JSON library
fix: specify credits
This commit is contained in:
ZavaruKitsu 2023-07-01 19:42:03 +03:00
parent 90d62514e9
commit f62eea3952
37 changed files with 26098 additions and 923 deletions

BIN
.github/AyuGram.png vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View file

@ -35,3 +35,17 @@ Make sure you have these components installed with VS Build Tools:
- C++ MFC latest (x86 & x64)
- C++ ATL latest (x86 & x64)
- latest Windows 11 SDK
## Credits
- [Telegram Desktop](https://github.com/telegramdesktop/tdesktop)
- [Kotatogram](https://github.com/kotatogram/kotatogram-desktop)
- [64Gram](https://github.com/TDesktop-x64/tdesktop)
- [SQLite](https://github.com/sqlite/sqlite)
- [sqlite_orm](https://github.com/fnc12/sqlite_orm)
### Very special thanks to
- [JSON for Modern C++](https://github.com/nlohmann/json)
- [BitConverter](https://github.com/YanjieHe/BitConverter)
- [Not Enough Standards](https://github.com/Alairion/not-enough-standards)

View file

@ -95,35 +95,43 @@ nice_target_sources(Telegram ${src_loc}
PRIVATE
${style_files}
ayu/qserializer.h
ayu/ayu_state.cpp
ayu/ayu_state.h
ayu/ayu_settings.cpp
ayu/ayu_settings.h
ayu/ayu_lang.cpp
ayu/ayu_lang.h
ayu/boxes/voice_confirmation_box.cpp
ayu/boxes/voice_confirmation_box.h
ayu/boxes/confirmation_box.cpp
ayu/boxes/confirmation_box.h
ayu/boxes/edit_deleted_mark.cpp
ayu/boxes/edit_deleted_mark.h
ayu/boxes/edit_edited_mark.cpp
ayu/boxes/edit_edited_mark.h
ayu/context_menu/context_menu.cpp
ayu/context_menu/context_menu.h
ayu/context_menu/message_history_box.cpp
ayu/context_menu/message_history_box.h
ayu/settings/settings_ayu.cpp
ayu/settings/settings_ayu.h
ayu/sqlite/sqlite3.c
ayu/sqlite/sqlite3.h
ayu/sqlite/sqlite_orm.h
ayu/ui/utils/ayu_profile_values.cpp
ayu/ui/utils/ayu_profile_values.h
ayu/ui/settings/settings_ayu.cpp
ayu/ui/settings/settings_ayu.h
ayu/ui/context_menu/context_menu.cpp
ayu/ui/context_menu/context_menu.h
ayu/ui/context_menu/message_history_box.cpp
ayu/ui/context_menu/message_history_box.h
ayu/ui/boxes/voice_confirmation_box.cpp
ayu/ui/boxes/voice_confirmation_box.h
ayu/ui/boxes/confirmation_box.cpp
ayu/ui/boxes/confirmation_box.h
ayu/ui/boxes/edit_deleted_mark.cpp
ayu/ui/boxes/edit_deleted_mark.h
ayu/ui/boxes/edit_edited_mark.cpp
ayu/ui/boxes/edit_edited_mark.h
ayu/sync/ayu_sync_controller.cpp
ayu/sync/ayu_sync_controller.h
ayu/sync/models.h
ayu/sync/utils/ayu_pipe_wrapper.cpp
ayu/sync/utils/ayu_pipe_wrapper.h
ayu/libs/pipe.hpp
ayu/libs/json.hpp
ayu/libs/json_ext.hpp
ayu/libs/bit_converter.hpp
ayu/libs/sqlite/sqlite3.c
ayu/libs/sqlite/sqlite3.h
ayu/libs/sqlite/sqlite_orm.h
ayu/database/entities.h
ayu/database/ayu_database.cpp
ayu/database/ayu_database.h
ayu/utils/ayu_profile_values.cpp
ayu/utils/ayu_profile_values.h
api/api_attached_stickers.cpp
api/api_attached_stickers.h

View file

@ -10,6 +10,8 @@
#include "rpl/producer.h"
#include "rpl/variable.h"
using json = nlohmann::json;
namespace AyuSettings {
const QString filename = "tdata/ayu_settings.json";
std::optional<AyuGramSettings> settings = std::nullopt;
@ -66,22 +68,23 @@ namespace AyuSettings {
return;
}
file.open(QIODevice::ReadOnly);
QByteArray json = file.readAll();
QByteArray data = file.readAll();
file.close();
initialize();
settings->fromJson(json);
json p = json::parse(data);
settings = p.template get<AyuGramSettings>();
postinitialize();
}
void save() {
initialize();
QByteArray json = settings->toRawJson();
json p = settings.value();
QFile file(filename);
file.open(QIODevice::WriteOnly);
file.write(json);
file.write(p.dump().c_str());
file.close();
postinitialize();

View file

@ -8,16 +8,12 @@
#pragma once
#include "rpl/producer.h"
#define QS_HAS_JSON
#include "lang_auto.h"
#include "qserializer.h"
#include "ayu/libs/json.hpp"
#include "ayu/libs/json_ext.hpp"
namespace AyuSettings {
class AyuGramSettings : public QSerializer {
Q_GADGET
class AyuGramSettings {
public:
AyuGramSettings() {
// ~ Ghost essentials
@ -56,75 +52,92 @@ namespace AyuSettings {
showPeerId = 2;
showMessageSeconds = false;
stickerConfirmation = false;
GIFConfirmation = false;
voiceConfirmation = false;
}
QS_SERIALIZABLE
QS_FIELD(bool, sendReadPackets)
QS_FIELD(bool, sendOnlinePackets)
QS_FIELD(bool, sendUploadProgress)
QS_FIELD(bool, sendOfflinePacketAfterOnline)
QS_FIELD(bool, markReadAfterSend)
QS_FIELD(bool, useScheduledMessages)
QS_FIELD(bool, keepDeletedMessages)
QS_FIELD(bool, keepMessagesHistory)
QS_FIELD(bool, enableAds)
QS_FIELD(QString, deletedMark)
QS_FIELD(QString, editedMark)
QS_FIELD(int, recentStickersCount)
QS_FIELD(bool, showGhostToggleInDrawer)
QS_FIELD(int, showPeerId)
QS_FIELD(bool, showMessageSeconds)
QS_FIELD(bool, stickerConfirmation)
QS_FIELD(bool, GIFConfirmation)
QS_FIELD(bool, voiceConfirmation)
bool sendReadPackets;
bool sendOnlinePackets;
bool sendUploadProgress;
bool sendOfflinePacketAfterOnline;
bool markReadAfterSend;
bool useScheduledMessages;
bool keepDeletedMessages;
bool keepMessagesHistory;
bool enableAds;
QString deletedMark;
QString editedMark;
int recentStickersCount;
bool showGhostToggleInDrawer;
int showPeerId;
bool showMessageSeconds;
bool stickerConfirmation;
bool GIFConfirmation;
bool voiceConfirmation;
public:
void set_sendReadPackets(bool val);
void set_sendOnlinePackets(bool val);
void set_sendUploadProgress(bool val);
void set_sendOfflinePacketAfterOnline(bool val);
void set_markReadAfterSend(bool val);
void set_useScheduledMessages(bool val);
void set_keepDeletedMessages(bool val);
void set_keepMessagesHistory(bool val);
void set_enableAds(bool val);
void set_deletedMark(QString val);
void set_editedMark(QString val);
void set_recentStickersCount(int val);
void set_showGhostToggleInDrawer(bool val);
void set_showPeerId(int val);
void set_showMessageSeconds(bool val);
void set_stickerConfirmation(bool val);
void set_GIFConfirmation(bool val);
void set_voiceConfirmation(bool val);
bool get_ghostModeEnabled() const;
};
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(
AyuGramSettings,
sendReadPackets,
sendOnlinePackets,
sendUploadProgress,
sendOfflinePacketAfterOnline,
markReadAfterSend,
useScheduledMessages,
keepDeletedMessages,
keepMessagesHistory,
enableAds,
deletedMark,
editedMark,
recentStickersCount,
showGhostToggleInDrawer,
showPeerId,
showMessageSeconds,
stickerConfirmation,
GIFConfirmation,
voiceConfirmation
);
AyuGramSettings &getInstance();
void load();
@ -132,7 +145,9 @@ namespace AyuSettings {
void save();
rpl::producer<QString> get_deletedMarkReactive();
rpl::producer<QString> get_editedMarkReactive();
rpl::producer<int> get_showPeerIdReactive();
// computed fields

View file

@ -0,0 +1,368 @@
/*
MIT License
Copyright (c) 2021 Yanjie He
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <vector>
namespace bit_converter {
using std::vector;
/**
* @brief Convert the specified 16-bit signed integer value to bytes.
*/
template <typename OutputIt>
inline OutputIt i16_to_bytes(int16_t value, bool is_big_endian,
OutputIt output_it) {
if (is_big_endian) {
*output_it = static_cast<uint8_t>(value >> 8);
output_it++;
*output_it = static_cast<uint8_t>(value & 0xFF);
output_it++;
} else {
*output_it = static_cast<uint8_t>(value & 0xFF);
output_it++;
*output_it = static_cast<uint8_t>(value >> 8);
output_it++;
}
return output_it;
}
/**
* @brief Convert the specified 16-bit unsigned integer value to bytes.
*/
template <typename OutputIt>
inline OutputIt u16_to_bytes(uint16_t value, bool is_big_endian,
OutputIt output_it) {
return i16_to_bytes(static_cast<uint16_t>(value), is_big_endian, output_it);
}
/**
* @brief Convert the specified 32-bit signed integer value to bytes.
*/
template <typename OutputIt>
inline OutputIt i32_to_bytes(int32_t value, bool is_big_endian,
OutputIt output_it) {
if (is_big_endian) {
for (int i = 0; i < static_cast<int>(sizeof(int32_t)); i++) {
*output_it = static_cast<uint8_t>((value >> (24 - i * 8)) & 0xFF);
output_it++;
}
} else {
for (int i = static_cast<int>(sizeof(int32_t)) - 1; i >= 0; i--) {
*output_it = static_cast<uint8_t>((value >> (24 - i * 8)) & 0xFF);
output_it++;
}
}
return output_it;
}
/**
* @brief Convert the specified 32-bit unsigned integer value to bytes.
*/
template <typename OutputIt>
inline OutputIt u32_to_bytes(uint32_t value, bool is_big_endian,
OutputIt output_it) {
return i32_to_bytes(static_cast<int32_t>(value), is_big_endian, output_it);
}
/**
* @brief Convert the specified 64-bit signed integer value to bytes.
*/
template <typename OutputIt>
inline OutputIt i64_to_bytes(int64_t value, bool is_big_endian,
OutputIt output_it) {
if (is_big_endian) {
for (int i = 0; i < static_cast<int>(sizeof(int64_t)); i++) {
*output_it = static_cast<uint8_t>((value >> (56 - i * 8)) & 0xFF);
output_it++;
}
} else {
for (int i = static_cast<int>(sizeof(int64_t)) - 1; i >= 0; i--) {
*output_it = static_cast<uint8_t>((value >> (56 - i * 8)) & 0xFF);
output_it++;
}
}
return output_it;
}
/**
* @brief Convert the specified 64-bit unsigned integer value to bytes.
*/
template <typename OutputIt>
inline OutputIt u64_to_bytes(uint64_t value, bool is_big_endian,
OutputIt output_it) {
return i64_to_bytes(static_cast<int64_t>(value), is_big_endian, output_it);
}
template <typename OutputIt>
inline OutputIt create_bytes_from_bits(const vector<bool> &bits,
OutputIt output_it) {
for (int i = 0; i < static_cast<int>(bits.size() / 8); i++) {
uint8_t b = 0;
for (int j = 0; j < 8; j++) {
b = b + (bits[i * 8 + j] << j);
}
*output_it = b;
output_it++;
}
return output_it;
}
/**
* @brief Convert the specified single-precision floating-point value to bytes.
*/
template <typename OutputIt>
inline OutputIt f32_to_bytes(float_t value, bool is_big_endian,
OutputIt output_it) {
vector<bool> bits;
bits.reserve(sizeof(float_t) * 8);
bits.push_back(value < 0);
int exponent;
float_t mantissa = std::frexp(value, &exponent);
exponent = (exponent - 1) + 127;
for (int i = 0; i < 8; i++) {
bits.push_back(exponent % 2);
exponent = exponent / 2;
}
std::reverse(bits.begin() + 1, bits.begin() + 1 + 8);
mantissa = mantissa * 2 - 1;
for (int i = 0; i < 23; i++) {
mantissa = mantissa * 2;
if (mantissa >= 1.0) {
mantissa = mantissa - 1.0;
bits.push_back(true);
} else {
bits.push_back(false);
}
}
if (is_big_endian) {
return create_bytes_from_bits(bits, output_it);
} else {
std::reverse(bits.begin(), bits.end());
return create_bytes_from_bits(bits, output_it);
}
}
/**
* @brief Convert the specified double-precision floating-point value to bytes.
*/
template <typename OutputIt>
inline OutputIt f64_to_bytes(double_t value, bool is_big_endian,
OutputIt output_it) {
vector<bool> bits;
bits.reserve(sizeof(double_t) * 8);
bits.push_back(value < 0);
int exponent;
double_t mantissa = std::frexp(value, &exponent);
exponent = (exponent - 1) + 1023;
for (int i = 0; i < 11; i++) {
bits.push_back(exponent % 2);
exponent = exponent / 2;
}
std::reverse(bits.begin() + 1, bits.begin() + 1 + 11);
mantissa = mantissa * 2 - 1;
for (int i = 0; i < 52; i++) {
mantissa = mantissa * 2;
if (mantissa >= 1.0) {
mantissa = mantissa - 1.0;
bits.push_back(true);
} else {
bits.push_back(false);
}
}
if (is_big_endian) {
return create_bytes_from_bits(bits, output_it);
} else {
std::reverse(bits.begin(), bits.end());
return create_bytes_from_bits(bits, output_it);
}
}
/**
* @brief Returns a 16-bit signed integer converted from two bytes at a
* specified position in a byte sequence.
*/
template <typename InputIt>
inline int16_t bytes_to_i16(InputIt input_it, bool is_big_endian) {
if (is_big_endian) {
int16_t result = *input_it;
input_it++;
result = (result << 8) + *input_it;
input_it++;
return result;
} else {
int16_t result = *input_it;
input_it++;
result = result + (static_cast<int16_t>(*input_it) << 8);
input_it++;
return result;
}
}
/**
* @brief Returns a 16-bit unsigned integer converted from two bytes at a
* specified position in a byte sequence.
*/
template <typename InputIt>
inline uint16_t bytes_to_u16(InputIt input_it, bool is_big_endian) {
return static_cast<uint16_t>(bytes_to_i16(input_it, is_big_endian));
}
/**
* @brief Returns a 32-bit signed integer converted from four bytes at a
* specified position in a byte sequence.
*/
template <typename InputIt>
inline int32_t bytes_to_i32(InputIt input_it, bool is_big_endian) {
int32_t result = 0;
if (is_big_endian) {
for (int i = 0; i < static_cast<int>(sizeof(int32_t)); i++) {
result = result + ((*input_it) << (8 * (4 - 1 - i)));
input_it++;
}
return result;
} else {
for (int i = 0; i < static_cast<int>(sizeof(int32_t)); i++) {
result = result + ((*input_it) << (8 * i));
input_it++;
}
return result;
}
}
/**
* @brief Returns a 32-bit unsigned integer converted from four bytes at a
* specified position in a byte sequence.
*/
template <typename InputIt>
inline uint32_t bytes_to_u32(InputIt input_it, bool is_big_endian) {
return static_cast<uint32_t>(bytes_to_i32(input_it, is_big_endian));
}
/**
* @brief Returns a 64-bit signed integer converted from eight bytes at a
* specified position in a byte sequence.
*/
template <typename InputIt>
inline int64_t bytes_to_i64(InputIt input_it, bool is_big_endian) {
int64_t result = 0;
if (is_big_endian) {
for (int i = 0; i < static_cast<int>(sizeof(int64_t)); i++) {
result = result + (static_cast<int64_t>(*input_it) << (8 * (8 - 1 - i)));
input_it++;
}
return result;
} else {
for (int i = 0; i < static_cast<int>(sizeof(int64_t)); i++) {
result = result + (static_cast<int64_t>(*input_it) << (8 * i));
input_it++;
}
return result;
}
}
/**
* @brief Returns a 64-bit unsigned integer converted from eight bytes at a
* specified position in a byte sequence.
*/
template <typename InputIt>
inline uint64_t bytes_to_u64(InputIt input_it, bool is_big_endian) {
return static_cast<uint64_t>(input_it, is_big_endian);
}
/**
* @brief Returns a single-precision floating-point number converted from four
* bytes at a specified position in a byte sequence.
*/
template <typename InputIt>
inline float_t bytes_to_f32(InputIt input_it, bool is_big_endian) {
vector<bool> bits;
bits.reserve(sizeof(float_t) * 8);
for (int i = 0; i < static_cast<int>(sizeof(float_t)); i++) {
uint8_t b = *input_it;
input_it++;
for (int j = 0; j < 8; j++) {
bits.push_back(b % 2);
b = b / 2;
}
}
if (!is_big_endian) {
std::reverse(bits.begin(), bits.end());
}
int sign = bits[0] ? (-1) : (+1);
int exponent = 0;
for (int i = 0; i < 8; i++) {
exponent = exponent + bits[1 + i] * (1 << (8 - 1 - i));
}
exponent = exponent - 127;
float_t mantissa = 1.0;
float_t cur = 0.5;
for (int i = 0; i < 23; i++) {
mantissa = mantissa + bits[1 + 8 + i] * cur;
cur = cur / 2;
}
return sign * std::ldexp(mantissa, exponent);
}
/**
* @brief Returns a double-precision floating-point number converted from eight
* bytes at a specified position in a byte sequence.
*/
template <typename InputIt>
inline double_t bytes_to_f64(InputIt input_it, bool is_big_endian) {
vector<bool> bits;
bits.reserve(sizeof(double_t) * 8);
for (int i = 0; i < static_cast<int>(sizeof(double_t)); i++) {
uint8_t b = *input_it;
input_it++;
for (int j = 0; j < 8; j++) {
bits.push_back(b % 2);
b = b / 2;
}
}
if (!is_big_endian) {
std::reverse(bits.begin(), bits.end());
}
int sign = bits[0] ? (-1) : (+1);
int exponent = 0;
for (int i = 0; i < 11; i++) {
exponent = exponent + bits[1 + i] * (1 << (11 - 1 - i));
}
exponent = exponent - 1023;
double_t mantissa = 1.0;
double_t cur = 0.5;
for (int i = 0; i < 52; i++) {
mantissa = mantissa + bits[1 + 11 + i] * cur;
cur = cur / 2;
}
return sign * std::ldexp(mantissa, exponent);
}
}; // namespace bit_converter

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,19 @@
// This is the source code of AyuGram for Desktop.
//
// We do not and cannot prevent the use of our code,
// but be respectful and credit the original author.
//
// Copyright @Radolyn, 2023
#pragma once
#include <QString>
#include "json.hpp"
inline void to_json(nlohmann::json &j, const QString &q) {
j = nlohmann::json(q.toStdString());
}
inline void from_json(const nlohmann::json &j, QString &q) {
q = QString::fromStdString(j.get<std::string>());
}

View file

@ -0,0 +1,856 @@
///////////////////////////////////////////////////////////
/// Copyright 2019 Alexy Pellegrini
///
/// Permission is hereby granted, free of charge,
/// to any person obtaining a copy of this software
/// and associated documentation files (the "Software"),
/// to deal in the Software without restriction,
/// including without limitation the rights to use,
/// copy, modify, merge, publish, distribute, sublicense,
/// and/or sell copies of the Software, and to permit
/// persons to whom the Software is furnished to do so,
/// subject to the following conditions:
///
/// The above copyright notice and this permission notice
/// shall be included in all copies or substantial portions
/// of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
/// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
/// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
/// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
/// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
/// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
/// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
/// OR OTHER DEALINGS IN THE SOFTWARE.
///////////////////////////////////////////////////////////
#ifndef NOT_ENOUGH_STANDARDS_PIPE
#define NOT_ENOUGH_STANDARDS_PIPE
#if defined(_WIN32)
#define NES_WIN32_PIPE
#ifndef NOMINMAX
#define NOMINMAX
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <Windows.h>
#elif defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))
#define NES_POSIX_PIPE
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#else
#error "Not enough standards does not support this environment."
#endif
#include <vector>
#include <algorithm>
#include <streambuf>
#include <istream>
#include <ostream>
#include <memory>
#include <cassert>
#include <utility>
#if defined(NES_WIN32_PIPE)
namespace nes
{
inline constexpr const char pipe_root[] = "\\\\.\\pipe\\";
template<typename CharT, typename Traits>
class basic_pipe_istream;
template<typename CharT, typename Traits>
class basic_pipe_ostream;
template<typename CharT = char, typename Traits = std::char_traits<CharT>>
std::pair<basic_pipe_istream<CharT, Traits>, basic_pipe_ostream<CharT, Traits>> make_anonymous_pipe();
template<typename CharT, typename Traits = std::char_traits<CharT>>
class basic_pipe_streambuf : public std::basic_streambuf<CharT, Traits>
{
private:
using parent_type = std::basic_streambuf<CharT, Traits>;
public:
using char_type = CharT;
using traits_type = Traits;
using int_type = typename Traits::int_type;
using pos_type = typename Traits::pos_type;
using off_type = typename Traits::off_type;
public:
static constexpr std::size_t buf_size{1024};
public:
basic_pipe_streambuf() = default;
explicit basic_pipe_streambuf(const std::string& name, std::ios_base::openmode mode)
{
open(name, mode);
}
virtual ~basic_pipe_streambuf()
{
close();
}
basic_pipe_streambuf(const basic_pipe_streambuf&) = delete;
basic_pipe_streambuf& operator=(const basic_pipe_streambuf&) = delete;
basic_pipe_streambuf(basic_pipe_streambuf&& other) noexcept
:parent_type{other}
,m_buffer{std::move(other.m_buffer)}
,m_handle{std::exchange(other.m_handle, INVALID_HANDLE_VALUE)}
,m_mode{std::exchange(other.m_mode, std::ios_base::openmode{})}
{
}
basic_pipe_streambuf& operator=(basic_pipe_streambuf&& other) noexcept
{
parent_type::operator=(other);
m_buffer = std::move(other.m_buffer);
m_handle = std::exchange(other.m_handle, m_handle);
m_mode = std::exchange(other.m_mode, m_mode);
return *this;
}
bool open(const std::string& name, std::ios_base::openmode mode)
{
assert(!((mode & std::ios_base::in) && (mode & std::ios_base::out)) && "nes::basic_pipe_streambuf::open called with mode = std::ios_base::in | std::ios_base::out.");
close();
const auto native_name{to_wide(pipe_root + name)};
DWORD native_mode{mode & std::ios_base::in ? GENERIC_READ : GENERIC_WRITE};
HANDLE handle = CreateFileW(std::data(native_name), native_mode, 0, nullptr, OPEN_EXISTING, 0, nullptr);
if(handle == INVALID_HANDLE_VALUE)
{
if(GetLastError() == ERROR_FILE_NOT_FOUND)
{
native_mode = mode & std::ios_base::in ? PIPE_ACCESS_INBOUND : PIPE_ACCESS_OUTBOUND;
handle = CreateNamedPipeW(std::data(native_name), native_mode, PIPE_READMODE_BYTE | PIPE_WAIT, 1, buf_size, buf_size, 0, nullptr);
if(handle == INVALID_HANDLE_VALUE)
return false;
if(!ConnectNamedPipe(handle, nullptr))
{
CloseHandle(handle);
return false;
}
}
}
m_buffer.resize(buf_size);
parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
m_handle = handle;
m_mode = mode;
return true;
}
bool is_open() const noexcept
{
return m_handle != INVALID_HANDLE_VALUE;
}
void close()
{
if(is_open())
{
sync();
m_mode = std::ios_base::openmode{};
CloseHandle(std::exchange(m_handle, INVALID_HANDLE_VALUE));
parent_type::setp(nullptr, nullptr);
parent_type::setg(nullptr, nullptr, nullptr);
}
}
private:
friend class process;
friend std::pair<basic_pipe_istream<char_type, traits_type>, basic_pipe_ostream<char_type, traits_type>> make_anonymous_pipe<char_type, traits_type>();
basic_pipe_streambuf(HANDLE handle, std::ios_base::openmode mode)
:m_handle{handle}
,m_mode{mode}
{
m_buffer.resize(buf_size);
parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
}
protected:
virtual int sync() override
{
if(m_mode & std::ios_base::out)
{
const std::ptrdiff_t count{parent_type::pptr() - parent_type::pbase()};
DWORD written{};
if(!WriteFile(m_handle, reinterpret_cast<const CHAR*>(std::data(m_buffer)), static_cast<DWORD>(count)* sizeof(char_type), &written, nullptr))
return -1;
parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
}
return 0;
}
virtual int_type overflow(int_type c = traits_type::eof()) override
{
assert(m_mode & std::ios_base::out && "Write operation on a read only pipe.");
if(traits_type::eq_int_type(c, traits_type::eof()))
{
DWORD written{};
if(!WriteFile(m_handle, reinterpret_cast<const CHAR*>(std::data(m_buffer)), static_cast<DWORD>(buf_size) * sizeof(char_type), &written, nullptr))
return traits_type::eof();
parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
}
else
{
*parent_type::pptr() = traits_type::to_char_type(c);
parent_type::pbump(1);
}
return traits_type::not_eof(c);
}
virtual std::streamsize xsputn(const char_type* s, std::streamsize count) override
{
assert(m_mode & std::ios_base::out && "Write operation on a read only pipe.");
DWORD written{};
if(!WriteFile(m_handle, reinterpret_cast<const CHAR*>(s), static_cast<DWORD>(count) * sizeof(char_type), &written, nullptr))
return 0;
return static_cast<std::streamsize>(written);
}
virtual int_type underflow() override
{
assert(m_mode & std::ios_base::in && "Read operation on a write only pipe.");
if(parent_type::gptr() == parent_type::egptr())
{
DWORD readed{};
if(!ReadFile(m_handle, reinterpret_cast<CHAR*>(std::data(m_buffer)), static_cast<DWORD>(buf_size * sizeof(char_type)), &readed, nullptr) || readed == 0)
return traits_type::eof();
parent_type::setg(std::data(m_buffer), std::data(m_buffer), std::data(m_buffer) + (readed / sizeof(char_type)));
}
return traits_type::to_int_type(*parent_type::gptr());
}
virtual std::streamsize xsgetn(char_type* s, std::streamsize count) override
{
assert(m_mode & std::ios_base::in && "Read operation on a write only pipe.");
DWORD readed{};
if(!ReadFile(m_handle, reinterpret_cast<CHAR*>(s), static_cast<DWORD>(count) * sizeof(char_type), &readed, nullptr))
return 0;
return static_cast<std::streamsize>(readed / sizeof(char_type));
}
private:
std::wstring to_wide(std::string path)
{
assert(std::size(path) < 0x7FFFFFFFu && "Wrong path.");
if(std::empty(path))
return {};
std::transform(std::begin(path), std::end(path), std::begin(path), [](char c){return c == '/' ? '\\' : c;});
std::wstring out_path{};
out_path.resize(static_cast<std::size_t>(MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, std::data(path), static_cast<int>(std::size(path)), nullptr, 0)));
if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, std::data(path), static_cast<int>(std::size(path)), std::data(out_path), static_cast<int>(std::size(out_path))))
throw std::runtime_error{"Failed to convert the path to wide."};
return out_path;
}
private:
std::vector<CharT> m_buffer{};
HANDLE m_handle{INVALID_HANDLE_VALUE};
std::ios_base::openmode m_mode{};
};
template<typename CharT, typename Traits = std::char_traits<CharT>>
class basic_pipe_istream : public std::basic_istream<CharT, Traits>
{
private:
using parent_type = std::basic_istream<CharT, Traits>;
public:
using char_type = CharT;
using traits_type = Traits;
using int_type = typename Traits::int_type;
using pos_type = typename Traits::pos_type;
using off_type = typename Traits::off_type;
public:
basic_pipe_istream() = default;
explicit basic_pipe_istream(const std::string& name, std::ios_base::openmode mode = std::ios_base::in)
:parent_type{nullptr}
{
parent_type::rdbuf(m_buffer.get());
open(name, mode);
}
virtual ~basic_pipe_istream() = default;
basic_pipe_istream(const basic_pipe_istream&) = delete;
basic_pipe_istream& operator=(const basic_pipe_istream&) = delete;
basic_pipe_istream(basic_pipe_istream&& other) noexcept
:parent_type{std::move(other)}
{
std::swap(m_buffer, other.m_buffer);
parent_type::rdbuf(m_buffer.get());
}
basic_pipe_istream& operator=(basic_pipe_istream&& other) noexcept
{
parent_type::operator=(std::move(other));
std::swap(m_buffer, other.m_buffer);
parent_type::rdbuf(m_buffer.get());
return *this;
}
void open(const std::string& name, std::ios_base::openmode mode = std::ios_base::in)
{
m_buffer->open(name, mode);
parent_type::clear(m_buffer->is_open() ? std::ios_base::goodbit : std::ios_base::failbit);
}
bool is_open() const noexcept
{
return m_buffer->is_open();
}
void close()
{
m_buffer->close();
}
basic_pipe_streambuf<char_type, traits_type>* rdbuf() const noexcept
{
return m_buffer.get();
}
private:
friend class process;
friend std::pair<basic_pipe_istream<char_type, traits_type>, basic_pipe_ostream<char_type, traits_type>> make_anonymous_pipe<char_type, traits_type>();
basic_pipe_istream(basic_pipe_streambuf<char_type, traits_type> buffer)
:parent_type{nullptr}
,m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>(std::move(buffer))}
{
parent_type::rdbuf(m_buffer.get());
}
private:
std::unique_ptr<basic_pipe_streambuf<char_type, traits_type>> m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>()};
};
template<typename CharT, typename Traits = std::char_traits<CharT>>
class basic_pipe_ostream : public std::basic_ostream<CharT, Traits>
{
private:
using parent_type = std::basic_ostream<CharT, Traits>;
public:
using char_type = CharT;
using traits_type = Traits;
using int_type = typename Traits::int_type;
using pos_type = typename Traits::pos_type;
using off_type = typename Traits::off_type;
public:
basic_pipe_ostream() = default;
explicit basic_pipe_ostream(const std::string& name, std::ios_base::openmode mode = std::ios_base::out)
:parent_type{nullptr}
{
parent_type::rdbuf(m_buffer.get());
open(name, mode);
}
virtual ~basic_pipe_ostream() = default;
basic_pipe_ostream(const basic_pipe_ostream&) = delete;
basic_pipe_ostream& operator=(const basic_pipe_ostream&) = delete;
basic_pipe_ostream(basic_pipe_ostream&& other) noexcept
:parent_type{std::move(other)}
{
std::swap(m_buffer, other.m_buffer);
parent_type::rdbuf(m_buffer.get());
}
basic_pipe_ostream& operator=(basic_pipe_ostream&& other) noexcept
{
parent_type::operator=(std::move(other));
std::swap(m_buffer, other.m_buffer);
parent_type::rdbuf(m_buffer.get());
return *this;
}
void open(const std::string& name, std::ios_base::openmode mode = std::ios_base::out)
{
m_buffer->open(name, mode);
parent_type::clear(m_buffer->is_open() ? std::ios_base::goodbit : std::ios_base::failbit);
}
bool is_open() const noexcept
{
return m_buffer->is_open();
}
void close()
{
m_buffer->close();
}
basic_pipe_streambuf<char_type, traits_type>* rdbuf() const noexcept
{
return m_buffer.get();
}
private:
friend class process;
friend std::pair<basic_pipe_istream<char_type, traits_type>, basic_pipe_ostream<char_type, traits_type>> make_anonymous_pipe<char_type, traits_type>();
basic_pipe_ostream(basic_pipe_streambuf<char_type, traits_type> buffer)
:parent_type{nullptr}
,m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>(std::move(buffer))}
{
parent_type::rdbuf(m_buffer.get());
}
private:
std::unique_ptr<basic_pipe_streambuf<char_type, traits_type>> m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>()};
};
template<typename CharT, typename Traits>
std::pair<basic_pipe_istream<CharT, Traits>, basic_pipe_ostream<CharT, Traits>> make_anonymous_pipe()
{
HANDLE input{};
HANDLE output{};
if(!CreatePipe(&input, &output, nullptr, 0))
throw std::runtime_error{"Failed to create pipe"};
return std::make_pair(basic_pipe_istream<CharT, Traits>{basic_pipe_streambuf<CharT, Traits>{input, std::ios_base::in}},
basic_pipe_ostream<CharT, Traits>{basic_pipe_streambuf<CharT, Traits>{output, std::ios_base::out}});
}
using pipe_streambuf = basic_pipe_streambuf<char>;
using pipe_istream = basic_pipe_istream<char>;
using pipe_ostream = basic_pipe_ostream<char>;
}
#elif defined(NES_POSIX_PIPE)
namespace nes
{
inline constexpr const char pipe_root[] = "/tmp/";
template<typename CharT, typename Traits>
class basic_pipe_istream;
template<typename CharT, typename Traits>
class basic_pipe_ostream;
template<typename CharT = char, typename Traits = std::char_traits<CharT>>
std::pair<basic_pipe_istream<CharT, Traits>, basic_pipe_ostream<CharT, Traits>> make_anonymous_pipe();
template<typename CharT, typename Traits = std::char_traits<CharT>>
class basic_pipe_streambuf : public std::basic_streambuf<CharT, Traits>
{
private:
using parent_type = std::basic_streambuf<CharT, Traits>;
public:
using char_type = CharT;
using traits_type = Traits;
using int_type = typename Traits::int_type;
using pos_type = typename Traits::pos_type;
using off_type = typename Traits::off_type;
public:
static constexpr std::size_t buf_size{1024};
public:
basic_pipe_streambuf() = default;
explicit basic_pipe_streambuf(const std::string& name, std::ios_base::openmode mode)
:parent_type{nullptr}
{
open(name, mode);
}
virtual ~basic_pipe_streambuf()
{
close();
}
basic_pipe_streambuf(const basic_pipe_streambuf&) = delete;
basic_pipe_streambuf& operator=(const basic_pipe_streambuf&) = delete;
basic_pipe_streambuf(basic_pipe_streambuf&& other) noexcept
:parent_type{std::move(other)}
,m_buffer{std::move(other.m_buffer)}
,m_handle{std::exchange(other.m_handle, 0)}
,m_mode{std::exchange(other.m_mode, std::ios_base::openmode{})}
{
}
basic_pipe_streambuf& operator=(basic_pipe_streambuf&& other) noexcept
{
parent_type::operator=(std::move(other));
m_buffer = std::move(other.m_buffer);
m_handle = std::exchange(other.m_handle, m_handle);
m_mode = std::exchange(other.m_mode, m_mode);
return *this;
}
bool open(const std::string& name, std::ios_base::openmode mode)
{
assert(!((mode & std::ios_base::in) && (mode & std::ios_base::out)) && "nes::basic_pipe_streambuf::open called with mode = std::ios_base::in | std::ios_base::out.");
close();
const auto native_name{pipe_root + name};
if(mkfifo(std::data(native_name), 0660) != 0 && errno != EEXIST)
return false;
const int native_mode{mode & std::ios_base::in ? O_RDONLY : O_WRONLY};
int handle = ::open(std::data(native_name), native_mode);
if(handle < 0)
return false;
m_buffer.resize(buf_size);
parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
m_handle = handle;
m_mode = mode;
return true;
}
bool is_open() const noexcept
{
return m_handle;
}
void close()
{
if(is_open())
{
sync();
m_mode = std::ios_base::openmode{};
::close(std::exchange(m_handle, 0));
parent_type::setp(nullptr, nullptr);
parent_type::setg(nullptr, nullptr, nullptr);
}
}
private:
friend class process;
friend std::pair<basic_pipe_istream<char_type, traits_type>, basic_pipe_ostream<char_type, traits_type>> make_anonymous_pipe<char_type, traits_type>();
basic_pipe_streambuf(int handle, std::ios_base::openmode mode)
:m_handle{handle}
,m_mode{mode}
{
m_buffer.resize(buf_size);
parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
}
protected:
virtual int sync() override
{
if(m_mode & std::ios_base::out)
{
const std::ptrdiff_t count{parent_type::pptr() - parent_type::pbase()};
if(write(m_handle, reinterpret_cast<const char*>(std::data(m_buffer)), count * sizeof(char_type)) < 0)
return -1;
parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
}
return 0;
}
virtual int_type overflow(int_type c = traits_type::eof()) override
{
assert(m_mode & std::ios_base::out && "Write operation on a read only pipe.");
if(traits_type::eq_int_type(c, traits_type::eof()))
{
if(write(m_handle, reinterpret_cast<const char*>(std::data(m_buffer)), std::size(m_buffer) * sizeof(char_type)))
return traits_type::eof();
parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
}
else
{
*parent_type::pptr() = traits_type::to_char_type(c);
parent_type::pbump(1);
}
return traits_type::not_eof(c);
}
virtual std::streamsize xsputn(const char_type* s, std::streamsize count) override
{
assert(m_mode & std::ios_base::out && "Write operation on a read only pipe.");
const auto written = write(m_handle, reinterpret_cast<const char*>(s), count * sizeof(char_type));
if(written < 0)
return 0;
return static_cast<std::streamsize>(written);
}
virtual int_type underflow() override
{
assert(m_mode & std::ios_base::in && "Read operation on a write only pipe.");
if(parent_type::gptr() == parent_type::egptr())
{
const auto readed = read(m_handle, reinterpret_cast<char*>(std::data(m_buffer)), buf_size * sizeof(char_type));
if(readed <= 0)
return traits_type::eof();
parent_type::setg(std::data(m_buffer), std::data(m_buffer), std::data(m_buffer) + (readed / sizeof(char_type)));
}
return traits_type::to_int_type(*parent_type::gptr());
}
virtual std::streamsize xsgetn(char_type* s, std::streamsize count) override
{
assert(m_mode & std::ios_base::in && "Read operation on a write only pipe.");
const auto readed = read(m_handle, reinterpret_cast<char*>(s), count * sizeof(char_type));
if(readed < 0)
return 0;
return static_cast<std::streamsize>(readed / sizeof(char_type));
}
private:
std::vector<CharT> m_buffer{};
int m_handle{};
std::ios_base::openmode m_mode{};
};
template<typename CharT, typename Traits = std::char_traits<CharT>>
class basic_pipe_istream : public std::basic_istream<CharT, Traits>
{
private:
using parent_type = std::basic_istream<CharT, Traits>;
public:
using char_type = CharT;
using traits_type = Traits;
using int_type = typename Traits::int_type;
using pos_type = typename Traits::pos_type;
using off_type = typename Traits::off_type;
public:
basic_pipe_istream() = default;
explicit basic_pipe_istream(const std::string& name, std::ios_base::openmode mode = std::ios_base::in)
:parent_type{nullptr}
{
parent_type::rdbuf(m_buffer.get());
open(name, mode);
}
virtual ~basic_pipe_istream() = default;
basic_pipe_istream(const basic_pipe_istream&) = delete;
basic_pipe_istream& operator=(const basic_pipe_istream&) = delete;
basic_pipe_istream(basic_pipe_istream&& other) noexcept
:parent_type{std::move(other)}
{
std::swap(m_buffer, other.m_buffer);
parent_type::rdbuf(m_buffer.get());
}
basic_pipe_istream& operator=(basic_pipe_istream&& other) noexcept
{
parent_type::operator=(std::move(other));
std::swap(m_buffer, other.m_buffer);
parent_type::rdbuf(m_buffer.get());
return *this;
}
void open(const std::string& name, std::ios_base::openmode mode = std::ios_base::in)
{
m_buffer->open(name, mode);
parent_type::clear(m_buffer->is_open() ? std::ios_base::goodbit : std::ios_base::failbit);
}
bool is_open() const noexcept
{
return m_buffer->is_open();
}
void close()
{
m_buffer->close();
}
basic_pipe_streambuf<char_type, traits_type>* rdbuf() const noexcept
{
return m_buffer.get();
}
private:
friend class process;
friend std::pair<basic_pipe_istream<char_type, traits_type>, basic_pipe_ostream<char_type, traits_type>> make_anonymous_pipe<char_type, traits_type>();
basic_pipe_istream(basic_pipe_streambuf<char_type, traits_type> buffer)
:parent_type{nullptr}
,m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>(std::move(buffer))}
{
parent_type::rdbuf(m_buffer.get());
}
private:
std::unique_ptr<basic_pipe_streambuf<char_type, traits_type>> m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>()};
};
template<typename CharT, typename Traits = std::char_traits<CharT>>
class basic_pipe_ostream : public std::basic_ostream<CharT, Traits>
{
private:
using parent_type = std::basic_ostream<CharT, Traits>;
public:
using char_type = CharT;
using traits_type = Traits;
using int_type = typename Traits::int_type;
using pos_type = typename Traits::pos_type;
using off_type = typename Traits::off_type;
public:
basic_pipe_ostream() = default;
explicit basic_pipe_ostream(const std::string& name, std::ios_base::openmode mode = std::ios_base::out)
:parent_type{nullptr}
{
parent_type::rdbuf(m_buffer.get());
open(name, mode);
}
virtual ~basic_pipe_ostream() = default;
basic_pipe_ostream(const basic_pipe_ostream&) = delete;
basic_pipe_ostream& operator=(const basic_pipe_ostream&) = delete;
basic_pipe_ostream(basic_pipe_ostream&& other) noexcept
:parent_type{std::move(other)}
{
std::swap(m_buffer, other.m_buffer);
parent_type::rdbuf(m_buffer.get());
}
basic_pipe_ostream& operator=(basic_pipe_ostream&& other) noexcept
{
parent_type::operator=(std::move(other));
std::swap(m_buffer, other.m_buffer);
parent_type::rdbuf(m_buffer.get());
return *this;
}
void open(const std::string& name, std::ios_base::openmode mode = std::ios_base::out)
{
m_buffer->open(name, mode);
parent_type::clear(m_buffer->is_open() ? std::ios_base::goodbit : std::ios_base::failbit);
}
bool is_open() const noexcept
{
return m_buffer->is_open();
}
void close()
{
m_buffer->close();
}
basic_pipe_streambuf<char_type, traits_type>* rdbuf() const noexcept
{
return m_buffer.get();
}
private:
friend class process;
friend std::pair<basic_pipe_istream<char_type, traits_type>, basic_pipe_ostream<char_type, traits_type>> make_anonymous_pipe<char_type, traits_type>();
basic_pipe_ostream(basic_pipe_streambuf<char_type, traits_type> buffer)
:parent_type{nullptr}
,m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>(std::move(buffer))}
{
parent_type::rdbuf(m_buffer.get());
}
private:
std::unique_ptr<basic_pipe_streambuf<char_type, traits_type>> m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>()};
};
template<typename CharT, typename Traits>
std::pair<basic_pipe_istream<CharT, Traits>, basic_pipe_ostream<CharT, Traits>> make_anonymous_pipe()
{
int fd[2];
if(pipe(fd))
throw std::runtime_error{"Failed to create pipe"};
return std::make_pair(basic_pipe_istream<CharT, Traits>{basic_pipe_streambuf<CharT, Traits>{fd[0], std::ios_base::in}},
basic_pipe_ostream<CharT, Traits>{basic_pipe_streambuf<CharT, Traits>{fd[1], std::ios_base::out}});
}
using pipe_streambuf = basic_pipe_streambuf<char>;
using pipe_istream = basic_pipe_istream<char>;
using pipe_ostream = basic_pipe_ostream<char>;
}
#endif
#endif

View file

@ -1,849 +0,0 @@
/*
MIT License
Copyright (c) 2020-2021 Agadzhanov Vladimir
Portions Copyright (c) 2021 Jerry Jacobs
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef QSERIALIZER_H
#define QSERIALIZER_H
/* JSON */
#ifdef QS_HAS_JSON
#include <QJsonObject>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonValue>
#endif
/* XML */
#ifdef QS_HAS_XML
#include <QtXml/QDomDocument>
#include <QtXml/QDomElement>
#endif
/* META OBJECT SYSTEM */
#include <QVariant>
#include <QMetaProperty>
#include <QMetaObject>
#include <QMetaType>
#include <type_traits>
#include <QDebug>
#define QS_VERSION "1.2"
/* Generate metaObject method */
#define QS_META_OBJECT_METHOD \
virtual const QMetaObject * metaObject() const { \
return &this->staticMetaObject; \
} \
#define QSERIALIZABLE \
Q_GADGET \
QS_META_OBJECT_METHOD
/* Mark class as serializable */
#define QS_SERIALIZABLE QS_META_OBJECT_METHOD
#ifdef QS_HAS_XML
Q_DECLARE_METATYPE(QDomNode)
Q_DECLARE_METATYPE(QDomElement)
#endif
class QSerializer {
Q_GADGET
QS_SERIALIZABLE
public:
virtual ~QSerializer() = default;
#ifdef QS_HAS_JSON
/*! \brief Convert QJsonValue in QJsonDocument as QByteArray. */
static QByteArray toByteArray(const QJsonValue & value){
return QJsonDocument(value.toObject()).toJson();
}
#endif
#ifdef QS_HAS_XML
/*! \brief Convert QDomNode in QDomDocument as QByteArray. */
static QByteArray toByteArray(const QDomNode & value) {
QDomDocument doc = value.toDocument();
return doc.toByteArray();
}
/*! \brief Make xml processing instruction (hat) and returns new XML QDomDocument. On deserialization procedure all processing instructions will be ignored. */
static QDomDocument appendXmlHat(const QDomNode &node, const QString & encoding, const QString & version = "1.0"){
QDomDocument doc = node.toDocument();
QDomNode xmlNode = doc.createProcessingInstruction("xml", QString("version=\"%1\" encoding=\"%2\"").arg(version).arg(encoding));
doc.insertBefore(xmlNode, doc.firstChild());
return doc;
}
#endif
#ifdef QS_HAS_JSON
/*! \brief Serialize all accessed JSON propertyes for this object. */
QJsonObject toJson() const {
QJsonObject json;
for(int i = 0; i < metaObject()->propertyCount(); i++)
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if(QString(metaObject()->property(i).typeName()) != QMetaType::typeName(qMetaTypeId<QJsonValue>())) {
continue;
}
#else
if(metaObject()->property(i).metaType().id() != QMetaType::QJsonValue) {
continue;
}
#endif
json.insert(metaObject()->property(i).name(), metaObject()->property(i).readOnGadget(this).toJsonValue());
}
return json;
}
/*! \brief Returns QByteArray representation this object using json-serialization. */
QByteArray toRawJson() const {
return toByteArray(toJson());
}
/*! \brief Deserialize all accessed XML propertyes for this object. */
void fromJson(const QJsonValue & val) {
if(val.isObject())
{
QJsonObject json = val.toObject();
QStringList keys = json.keys();
int propCount = metaObject()->propertyCount();
for(int i = 0; i < propCount; i++)
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if(QString(metaObject()->property(i).typeName()) != QMetaType::typeName(qMetaTypeId<QJsonValue>())) {
continue;
}
#else
if(metaObject()->property(i).metaType().id() != QMetaType::QJsonValue) {
continue;
}
#endif
for(auto key : json.keys())
{
if(key == metaObject()->property(i).name())
{
metaObject()->property(i).writeOnGadget(this, json.value(key));
break;
}
}
}
}
}
/*! \brief Deserialize all accessed JSON propertyes for this object. */
void fromJson(const QByteArray & data) {
fromJson(QJsonDocument::fromJson(data).object());
}
#endif // QS_HAS_JSON
#ifdef QS_HAS_XML
/*! \brief Serialize all accessed XML propertyes for this object. */
QDomNode toXml() const {
QDomDocument doc;
QDomElement el = doc.createElement(metaObject()->className());
for(int i = 0; i < metaObject()->propertyCount(); i++)
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if(QString(metaObject()->property(i).typeName()) != QMetaType::typeName(qMetaTypeId<QDomNode>())) {
continue;
}
#else
if(metaObject()->property(i).metaType().id() != qMetaTypeId<QDomNode>()) {
continue;
}
#endif
el.appendChild(QDomNode(metaObject()->property(i).readOnGadget(this).value<QDomNode>()));
}
doc.appendChild(el);
return doc;
}
/*! \brief Returns QByteArray representation this object using xml-serialization. */
QByteArray toRawXml() const {
return toByteArray(toXml());
}
/*! \brief Deserialize all accessed XML propertyes for this object. */
void fromXml(const QDomNode & val) {
QDomNode doc = val;
auto n = doc.firstChildElement(metaObject()->className());
if(!n.isNull()) {
for(int i = 0; i < metaObject()->propertyCount(); i++) {
QString name = metaObject()->property(i).name();
QDomElement tmp = metaObject()->property(i).readOnGadget(this).value<QDomNode>().firstChildElement();
auto f = n.firstChildElement(tmp.tagName());
metaObject()->property(i).writeOnGadget(this, QVariant::fromValue<QDomNode>(f));
}
}
else
{
for(int i = 0; i < metaObject()->propertyCount(); i++) {
QString name = metaObject()->property(i).name();
auto f = doc.firstChildElement(name);
metaObject()->property(i).writeOnGadget(this, QVariant::fromValue<QDomNode>(f));
}
}
}
/*! \brief Deserialize all accessed XML propertyes for this object. */
void fromXml(const QByteArray & data) {
QDomDocument d;
d.setContent(data);
fromXml(d);
}
#endif // QS_HAS_XML
};
#define GET(prefix, name) get_##prefix##_##name
#define SET(prefix, name) set_##prefix##_##name
/* Create variable */
#define QS_DECLARE_MEMBER(type, name) \
public : \
type name = type(); \
/* Create JSON property and methods for primitive type field*/
#ifdef QS_HAS_JSON
#define QS_JSON_FIELD(type, name) \
Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json, name)) \
private: \
QJsonValue GET(json, name)() const { \
QJsonValue val = QJsonValue::fromVariant(QVariant(name)); \
return val; \
} \
void SET(json, name)(const QJsonValue & varname){ \
name = varname.toVariant().value<type>(); \
}
#else
#define QS_JSON_FIELD(type, name)
#endif
/* Create XML property and methods for primitive type field*/
#ifdef QS_HAS_XML
#define QS_XML_FIELD(type, name) \
Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name)) \
private: \
QDomNode GET(xml, name)() const { \
QDomDocument doc; \
QString strname = #name; \
QDomElement element = doc.createElement(strname); \
QDomText valueOfProp = doc.createTextNode(QVariant(name).toString()); \
element.appendChild(valueOfProp); \
doc.appendChild(element); \
return QDomNode(doc); \
} \
void SET(xml, name)(const QDomNode &node) { \
if(!node.isNull() && node.isElement()){ \
QDomElement domElement = node.toElement(); \
if(domElement.tagName() == #name) \
name = QVariant(domElement.text()).value<type>(); \
} \
}
#else
#define QS_XML_FIELD(type, name)
#endif
/* Generate JSON-property and methods for primitive type objects */
/* This collection must be provide method append(T) (it's can be QList, QVector) */
#ifdef QS_HAS_JSON
#define QS_JSON_ARRAY(itemType, name) \
Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json, name)) \
private: \
QJsonValue GET(json, name)() const { \
QJsonArray val; \
for(int i = 0; i < name.size(); i++) \
val.push_back(name.at(i)); \
return QJsonValue::fromVariant(val); \
} \
void SET(json, name)(const QJsonValue & varname) { \
if(!varname.isArray()) \
return; \
name.clear(); \
QJsonArray val = varname.toArray(); \
for(auto item : val) { \
itemType tmp; \
tmp = item.toVariant().value<itemType>(); \
name.append(tmp); \
} \
}
#else
#define QS_JSON_ARRAY(itemType, name)
#endif
/* Generate XML-property and methods for primitive type objects */
/* This collection must be provide method append(T) (it's can be QList, QVector) */
#ifdef QS_HAS_XML
#define QS_XML_ARRAY(itemType, name) \
Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name)) \
private: \
QDomNode GET(xml, name)() const { \
QDomDocument doc; \
QString strname = #name; \
QDomElement arrayXml = doc.createElement(QString(strname)); \
arrayXml.setAttribute("type", "array"); \
\
for(int i = 0; i < name.size(); i++) { \
itemType item = name.at(i); \
QDomElement itemXml = doc.createElement("item"); \
itemXml.setAttribute("type", #itemType); \
itemXml.setAttribute("index", i); \
itemXml.appendChild(doc.createTextNode(QVariant(item).toString())); \
arrayXml.appendChild(itemXml); \
} \
\
doc.appendChild(arrayXml); \
return QDomNode(doc); \
} \
void SET(xml, name)(const QDomNode & node) { \
QDomNode domNode = node.firstChild(); \
name.clear(); \
while(!domNode.isNull()) { \
if(domNode.isElement()) { \
QDomElement domElement = domNode.toElement(); \
name.append(QVariant(domElement.text()).value<itemType>()); \
} \
domNode = domNode.nextSibling(); \
} \
}
#else
#define QS_XML_ARRAY(itemType, name)
#endif
/* Generate JSON-property and methods for some custom class */
/* Custom type must be provide methods fromJson and toJson or inherit from QSerializer */
#ifdef QS_HAS_JSON
#define QS_JSON_OBJECT(type, name) \
Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json, name)) \
private: \
QJsonValue GET(json, name)() const { \
QJsonObject val = name.toJson(); \
return QJsonValue(val); \
} \
void SET(json, name)(const QJsonValue & varname) { \
if(!varname.isObject()) \
return; \
name.fromJson(varname); \
}
#else
#define QS_JSON_OBJECT(type, name)
#endif
/* Generate XML-property and methods for some custom class */
/* Custom type must be provide methods fromJson and toJson or inherit from QSerializer */
#ifdef QS_HAS_XML
#define QS_XML_OBJECT(type, name) \
Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name)) \
private: \
QDomNode GET(xml, name)() const { \
return name.toXml(); \
} \
void SET(xml, name)(const QDomNode & node){ \
name.fromXml(node); \
}
#else
#define QS_XML_OBJECT(type, name)
#endif
/* Generate JSON-property and methods for collection of custom type objects */
/* Custom item type must be provide methods fromJson and toJson or inherit from QSerializer */
/* This collection must be provide method append(T) (it's can be QList, QVector) */
#ifdef QS_HAS_JSON
#define QS_JSON_ARRAY_OBJECTS(itemType, name) \
Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json, name)) \
private: \
QJsonValue GET(json, name)() const { \
QJsonArray val; \
for(int i = 0; i < name.size(); i++) \
val.push_back(name.at(i).toJson()); \
return QJsonValue::fromVariant(val); \
} \
void SET(json, name)(const QJsonValue & varname) { \
if(!varname.isArray()) \
return; \
name.clear(); \
QJsonArray val = varname.toArray(); \
for(int i = 0; i < val.size(); i++) { \
itemType tmp; \
tmp.fromJson(val.at(i)); \
name.append(tmp); \
} \
}
#else
#define QS_JSON_ARRAY_OBJECTS(itemType, name)
#endif
/* Generate XML-property and methods for collection of custom type objects */
/* Custom type must be provide methods fromXml and toXml or inherit from QSerializer */
/* This collection must be provide method append(T) (it's can be QList, QVector) */
#ifdef QS_HAS_XML
#define QS_XML_ARRAY_OBJECTS(itemType, name) \
Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name)) \
private: \
QDomNode GET(xml, name)() const { \
QDomDocument doc; \
QDomElement element = doc.createElement(#name); \
element.setAttribute("type", "array"); \
for(int i = 0; i < name.size(); i++) \
element.appendChild(name.at(i).toXml()); \
doc.appendChild(element); \
return QDomNode(doc); \
} \
void SET(xml, name)(const QDomNode & node) { \
name.clear(); \
QDomNodeList nodesList = node.childNodes(); \
for(int i = 0; i < nodesList.size(); i++) { \
itemType tmp; \
tmp.fromXml(nodesList.at(i)); \
name.append(tmp); \
} \
}
#else
#define QS_XML_ARRAY_OBJECTS(itemType, name)
#endif
/* Generate JSON-property and methods for dictionary of simple fields (int, bool, QString, ...) */
/* Custom type must be inherit from QSerializer */
/* This collection must be provide method insert(KeyT, ValueT) (it's can be QMap, QHash) */
/* THIS IS FOR QT DICTIONARY TYPES, for example QMap<int, QString>, QMap<int,int>, ...*/
#ifdef QS_HAS_JSON
#define QS_JSON_QT_DICT(map, name) \
Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json,name)) \
private: \
QJsonValue GET(json, name)() const { \
QJsonObject val; \
for(auto p = name.constBegin(); p != name.constEnd(); ++p) { \
val.insert( \
QVariant(p.key()).toString(), \
QJsonValue::fromVariant(QVariant(p.value()))); \
} \
return val; \
} \
void SET(json, name)(const QJsonValue & varname) { \
QJsonObject val = varname.toObject(); \
name.clear(); \
for(auto p = val.constBegin() ;p != val.constEnd(); ++p) { \
name.insert( \
QVariant(p.key()).value<map::key_type>(), \
QVariant(p.value()).value<map::mapped_type>()); \
} \
}
#else
#define QS_JSON_QT_DICT(map, name)
#endif
/* Generate XML-property and methods for dictionary of simple fields (int, bool, QString, ...) */
/* Custom type must be inherit from QSerializer */
/* This collection must be provide method insert(KeyT, ValueT) (it's can be QMap, QHash) */
/* THIS IS FOR QT DICTIONARY TYPES, for example QMap<int, QString>, QMap<int,int>, ...*/
#ifdef QS_HAS_XML
#define QS_XML_QT_DICT(map, name) \
Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name)) \
private: \
QDomNode GET(xml, name)() const { \
QDomDocument doc; \
QDomElement element = doc.createElement(#name); \
element.setAttribute("type", "map"); \
for(auto p = name.begin(); p != name.end(); ++p) \
{ \
QDomElement e = doc.createElement("item"); \
e.setAttribute("key", QVariant(p.key()).toString()); \
e.setAttribute("value", QVariant(p.value()).toString()); \
element.appendChild(e); \
} \
doc.appendChild(element); \
return QDomNode(doc); \
} \
void SET(xml, name)(const QDomNode & node) { \
if(!node.isNull() && node.isElement()) \
{ \
QDomElement root = node.toElement(); \
if(root.tagName() == #name) \
{ \
QDomNodeList childs = root.childNodes(); \
\
for(int i = 0; i < childs.size(); ++i) { \
QDomElement item = childs.at(i).toElement(); \
name.insert(QVariant(item.attributeNode("key").value()).value<map::key_type>(), \
QVariant(item.attributeNode("value").value()).value<map::mapped_type>()); \
} \
} \
} \
}
#else
#define QS_XML_QT_DICT(map, name)
#endif
/* Generate JSON-property and methods for dictionary of custom type objects */
/* Custom type must be inherit from QSerializer */
/* This collection must be provide method inserv(KeyT, ValueT) (it's can be QMap, QHash) */
/* THIS IS FOR QT DICTIONARY TYPES, for example QMap<int, CustomSerializableType> */
#ifdef QS_HAS_JSON
#define QS_JSON_QT_DICT_OBJECTS(map, name) \
Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json,name)) \
private: \
QJsonValue GET(json, name)() const { \
QJsonObject val; \
for(auto p = name.begin(); p != name.end(); ++p) { \
val.insert( \
QVariant::fromValue(p.key()).toString(), \
p.value().toJson()); \
} \
return val; \
} \
void SET(json, name)(const QJsonValue & varname) { \
QJsonObject val = varname.toObject(); \
name.clear(); \
for(auto p = val.constBegin();p != val.constEnd(); ++p) { \
map::mapped_type tmp; \
tmp.fromJson(p.value()); \
name.insert( \
QVariant(p.key()).value<map::key_type>(), \
tmp); \
} \
}
#else
#define QS_JSON_QT_DICT_OBJECTS(map, name)
#endif
/* Generate XML-property and methods for dictionary of custom type objects */
/* Custom type must be inherit from QSerializer */
/* This collection must be provide method insert(KeyT, ValueT) (it's can be QMap, QHash) */
/* THIS IS FOR QT DICTIONARY TYPES, for example QMap<int, CustomSerializableType> */
#ifdef QS_HAS_XML
#define QS_XML_QT_DICT_OBJECTS(map, name) \
Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name)) \
private: \
QDomNode GET(xml, name)() const { \
QDomDocument doc; \
QDomElement element = doc.createElement(#name); \
element.setAttribute("type", "map"); \
for(auto p = name.begin(); p != name.end(); ++p) \
{ \
QDomElement e = doc.createElement("item"); \
e.setAttribute("key", QVariant(p.key()).toString()); \
e.appendChild(p.value().toXml()); \
element.appendChild(e); \
} \
doc.appendChild(element); \
return QDomNode(doc); \
} \
void SET(xml, name)(const QDomNode & node) { \
if(!node.isNull() && node.isElement()) \
{ \
QDomElement root = node.toElement(); \
if(root.tagName() == #name) \
{ \
QDomNodeList childs = root.childNodes(); \
\
for(int i = 0; i < childs.size(); ++i) { \
QDomElement item = childs.at(i).toElement(); \
map::mapped_type tmp; \
tmp.fromXml(item.firstChild()); \
name.insert(QVariant(item.attributeNode("key").value()).value<map::key_type>(), \
tmp); \
} \
} \
} \
}
#else
#define QS_XML_QT_DICT_OBJECTS(map, name)
#endif
/* Generate JSON-property and methods for dictionary of simple fields (int, bool, QString, ...) */
/* Custom type must be inherit from QSerializer */
/* This collection must be provide method insert(KeyT, ValueT) (it's can be std::map) */
/* THIS IS FOR STL DICTIONARY TYPES, for example std::map<int, QString>, std::map<int,int>, ...*/
#ifdef QS_HAS_JSON
#define QS_JSON_STL_DICT(map, name) \
Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json,name)) \
private: \
QJsonValue GET(json, name)() const { \
QJsonObject val; \
for(auto p : name){ \
val.insert( \
QVariant::fromValue(p.first).toString(), \
QJsonValue::fromVariant(QVariant(p.second))); \
} \
return val; \
} \
void SET(json, name)(const QJsonValue & varname) { \
QJsonObject val = varname.toObject(); \
name.clear(); \
for(auto p = val.constBegin();p != val.constEnd(); ++p) { \
name.insert(std::pair<map::key_type, map::mapped_type>( \
QVariant(p.key()).value<map::key_type>(), \
QVariant(p.value()).value<map::mapped_type>())); \
} \
}
#else
#define QS_JSON_STL_DICT(map, name)
#endif
#ifdef QS_HAS_XML
#define QS_XML_STL_DICT(map, name) \
Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name)) \
private: \
QDomNode GET(xml, name)() const { \
QDomDocument doc; \
QDomElement element = doc.createElement(#name); \
element.setAttribute("type", "map"); \
for(auto p : name) \
{ \
QDomElement e = doc.createElement("item"); \
e.setAttribute("key", QVariant(p.first).toString()); \
e.setAttribute("value", QVariant(p.second).toString()); \
element.appendChild(e); \
} \
doc.appendChild(element); \
return QDomNode(doc); \
} \
void SET(xml, name)(const QDomNode & node) { \
if(!node.isNull() && node.isElement()) \
{ \
QDomElement root = node.toElement(); \
if(root.tagName() == #name) \
{ \
QDomNodeList childs = root.childNodes(); \
\
for(int i = 0; i < childs.size(); ++i) { \
QDomElement item = childs.at(i).toElement(); \
name.insert(std::pair<map::key_type, map::mapped_type>( \
QVariant(item.attributeNode("key").value()).value<map::key_type>(), \
QVariant(item.attributeNode("value").value()).value<map::mapped_type>())); \
} \
} \
} \
}
#else
#define QS_XML_STL_DICT(map, name)
#endif
/* Generate JSON-property and methods for dictionary of custom type objects */
/* Custom type must be inherit from QSerializer */
/* This collection must be provide method insert(KeyT, ValueT) (it's can be std::map) */
/* THIS IS FOR STL DICTIONARY TYPES, for example std::map<int, CustomSerializableType> */
#ifdef QS_HAS_JSON
#define QS_JSON_STL_DICT_OBJECTS(map, name) \
Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json,name)) \
private: \
QJsonValue GET(json, name)() const { \
QJsonObject val; \
for(auto p : name){ \
val.insert( \
QVariant::fromValue(p.first).toString(), \
p.second.toJson()); \
} \
return val; \
} \
void SET(json, name)(const QJsonValue & varname) { \
QJsonObject val = varname.toObject(); \
name.clear(); \
for(auto p = val.constBegin(); p != val.constEnd(); ++p) { \
map::mapped_type tmp; \
tmp.fromJson(p.value()); \
name.insert(std::pair<map::key_type, map::mapped_type>( \
QVariant(p.key()).value<map::key_type>(), \
tmp)); \
} \
}
#else
#define QS_JSON_STL_DICT_OBJECTS(map, name)
#endif
/* Generate XML-property and methods for dictionary of custom type objects */
/* Custom type must be inherit from QSerializer */
/* This collection must be provide method insert(KeyT, ValueT) (it's can be std::map) */
/* THIS IS FOR STL DICTIONARY TYPES, for example std::map<int, CustomSerializableType> */
#ifdef QS_HAS_XML
#define QS_XML_STL_DICT_OBJECTS(map, name) \
Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name)) \
private: \
QDomNode GET(xml, name)() const { \
QDomDocument doc; \
QDomElement element = doc.createElement(#name); \
element.setAttribute("type", "map"); \
for(auto p : name) \
{ \
QDomElement e = doc.createElement("item"); \
e.setAttribute("key", QVariant(p.first).toString()); \
e.appendChild(p.second.toXml()); \
element.appendChild(e); \
} \
doc.appendChild(element); \
return QDomNode(doc); \
} \
void SET(xml, name)(const QDomNode & node) { \
if(!node.isNull() && node.isElement()) \
{ \
QDomElement root = node.toElement(); \
if(root.tagName() == #name) \
{ \
QDomNodeList childs = root.childNodes(); \
\
for(int i = 0; i < childs.size(); ++i) { \
QDomElement item = childs.at(i).toElement(); \
map::mapped_type tmp; \
tmp.fromXml(item.firstChild()); \
name.insert(std::pair<map::key_type, map::mapped_type> ( \
QVariant(item.attributeNode("key").value()).value<map::key_type>(), \
tmp)); \
} \
} \
} \
}
#else
#define QS_XML_STL_DICT_OBJECTS(map, name)
#endif
/* BIND: */
/* generate serializable propertyes JSON and XML for primitive type field */
#define QS_BIND_FIELD(type, name) \
QS_JSON_FIELD(type, name) \
QS_XML_FIELD(type, name) \
/* BIND: */
/* generate serializable propertyes JSON and XML for collection of primitive type fields */
#define QS_BIND_COLLECTION(itemType, name) \
QS_JSON_ARRAY(itemType, name) \
QS_XML_ARRAY(itemType, name) \
/* BIND: */
/* generate serializable propertyes JSON and XML for custom type object */
#define QS_BIND_OBJECT(type, name) \
QS_JSON_OBJECT(type, name) \
QS_XML_OBJECT(type, name) \
/* BIND: */
/* generate serializable propertyes JSON and XML for collection of custom type objects */
#define QS_BIND_COLLECTION_OBJECTS(itemType, name) \
QS_JSON_ARRAY_OBJECTS(itemType, name) \
QS_XML_ARRAY_OBJECTS(itemType, name) \
/* BIND: */
/* generate serializable propertyes JSON and XML for dictionary with primitive value type for QT DICTIONARY TYPES */
#define QS_BIND_QT_DICT(map, name) \
QS_JSON_QT_DICT(map, name) \
QS_XML_QT_DICT(map, name) \
/* BIND: */
/* generate serializable propertyes JSON and XML for dictionary of custom type objects for QT DICTIONARY TYPES */
#define QS_BIND_QT_DICT_OBJECTS(map, name) \
QS_JSON_QT_DICT_OBJECTS(map, name) \
QS_XML_QT_DICT_OBJECTS(map,name) \
/* BIND: */
/* generate serializable propertyes JSON and XML for dictionary with primitive value type for STL DICTIONARY TYPES */
#define QS_BIND_STL_DICT(map, name) \
QS_JSON_STL_DICT(map, name) \
QS_XML_STL_DICT(map, name) \
/* BIND: */
/* generate serializable propertyes JSON and XML for dictionary of custom type objects for STL DICTIONARY TYPES */
#define QS_BIND_STL_DICT_OBJECTS(map, name) \
QS_JSON_STL_DICT_OBJECTS(map, name) \
QS_XML_STL_DICT_OBJECTS(map,name) \
/* CREATE AND BIND: */
/* Make primitive field and generate serializable propertyes */
/* For example: QS_FIELD(int, digit), QS_FIELD(bool, flag) */
#define QS_FIELD(type, name) \
QS_DECLARE_MEMBER(type, name) \
QS_BIND_FIELD(type, name) \
/* CREATE AND BIND: */
/* Make collection of primitive type objects [collectionType<itemType> name] and generate serializable propertyes for this collection */
/* This collection must be provide method append(T) (it's can be QList, QVector) */
#define QS_COLLECTION(collectionType, itemType, name) \
QS_DECLARE_MEMBER(collectionType<itemType>, name) \
QS_BIND_COLLECTION(itemType, name) \
/* CREATE AND BIND: */
/* Make custom class object and bind serializable propertyes */
/* This class must be inherited from QSerializer */
#define QS_OBJECT(type,name) \
QS_DECLARE_MEMBER(type, name) \
QS_BIND_OBJECT(type, name) \
/* CREATE AND BIND: */
/* Make collection of custom class objects [collectionType<itemType> name] and bind serializable propertyes */
/* This collection must be provide method append(T) (it's can be QList, QVector) */
#define QS_COLLECTION_OBJECTS(collectionType, itemType, name) \
QS_DECLARE_MEMBER(collectionType<itemType>, name) \
QS_BIND_COLLECTION_OBJECTS(itemType, name) \
/* CREATE AND BIND: */
/* Make dictionary collection of simple types [dictionary<key, itemType> name] and bind serializable propertyes */
/* This collection must be QT DICTIONARY TYPE */
#define QS_QT_DICT(map, first, second, name) \
public: \
typedef map<first,second> dict_##name##_t; \
dict_##name##_t name = dict_##name##_t(); \
QS_BIND_QT_DICT(dict_##name##_t, name) \
/* CREATE AND BIND: */
/* Make dictionary collection of custom class objects [dictionary<key, itemType> name] and bind serializable propertyes */
/* This collection must be QT DICTIONARY TYPE */
#define QS_QT_DICT_OBJECTS(map, first, second, name) \
public: \
typedef map<first,second> dict_##name##_t; \
dict_##name##_t name = dict_##name##_t(); \
QS_BIND_QT_DICT_OBJECTS(dict_##name##_t, name) \
/* CREATE AND BIND: */
/* Make dictionary collection of simple types [dictionary<key, itemType> name] and bind serializable propertyes */
/* This collection must be STL DICTIONARY TYPE */
#define QS_STL_DICT(map, first, second, name) \
public: \
typedef map<first,second> dict_##name##_t; \
dict_##name##_t name = dict_##name##_t(); \
QS_BIND_STL_DICT(dict_##name##_t, name) \
/* CREATE AND BIND: */
/* Make dictionary collection of custom class objects [dictionary<key, itemType> name] and bind serializable propertyes */
/* This collection must be STL DICTIONARY TYPE */
#define QS_STL_DICT_OBJECTS(map, first, second, name) \
public: \
typedef map<first,second> dict_##name##_t; \
dict_##name##_t name = dict_##name##_t(); \
QS_BIND_STL_DICT_OBJECTS(dict_##name##_t, name) \
#endif // QSERIALIZER_H

View file

@ -0,0 +1,11 @@
// This is the source code of AyuGram for Desktop.
//
// We do not and cannot prevent the use of our code,
// but be respectful and credit the original author.
//
// Copyright @Radolyn, 2023
#include "ayu_sync_controller.h"
namespace AyuSync {
} // AyuSync

View file

@ -0,0 +1,16 @@
// This is the source code of AyuGram for Desktop.
//
// We do not and cannot prevent the use of our code,
// but be respectful and credit the original author.
//
// Copyright @Radolyn, 2023
#pragma once
namespace AyuSync {
class ayu_sync_controller {
};
} // AyuSync

View file

@ -0,0 +1,51 @@
#pragma once
#include <string>
#include <vector>
class SyncEvent {
std::string type = "sync_unspecified";
long userId = 0;
};
class SyncBatch : public SyncEvent {
std::string type = "sync_batch";
long userId;
class SyncBatchArgs {
std::vector<SyncEvent> events;
};
SyncBatchArgs args;
};
class SyncRead : public SyncEvent {
std::string type = "sync_read";
long userId;
class SyncReadArgs {
long dialogId;
int untilId;
int unread;
};
SyncReadArgs args;
};
class SyncForce : public SyncEvent {
std::string type = "sync_force";
long userId;
class SyncForceArgs {
int fromDate;
};
SyncForceArgs args;
};
class SyncForceFinish : public SyncEvent {
std::string type = "sync_force_finish";
long userId;
class SyncForceFinishArgs {
};
SyncForceFinishArgs args;
};

View file

@ -0,0 +1,43 @@
// This is the source code of AyuGram for Desktop.
//
// We do not and cannot prevent the use of our code,
// but be respectful and credit the original author.
//
// Copyright @Radolyn, 2023
#include <sstream>
#include <iostream>
#include "ayu_pipe_wrapper.h"
#include "ayu/libs/bit_converter.hpp"
template<class T>
void ayu_pipe_wrapper::send(T obj) {
auto s = json(obj).dump();
auto length = s.length();
char lengthBuff[4];
bit_converter::i32_to_bytes(length, false, lengthBuff);
os.write(lengthBuff, 4);
os.write(s.c_str(), length);
os.flush();
}
json ayu_pipe_wrapper::receive() {
char lengthBuff[4];
is.read(lengthBuff, 4);
auto length = bit_converter::bytes_to_i32(lengthBuff, false);
auto sb = std::stringbuf();
char buff[4096];
while (length > 0) {
auto readSize = std::min(length, (int)sizeof(buff));
is.read(buff, readSize);
sb.sputn(buff, readSize);
length -= readSize;
}
auto p = json::parse(sb.str());
return p;
}

View file

@ -0,0 +1,24 @@
// This is the source code of AyuGram for Desktop.
//
// We do not and cannot prevent the use of our code,
// but be respectful and credit the original author.
//
// Copyright @Radolyn, 2023
#pragma once
#include "ayu/libs/json.hpp"
#include "ayu/libs/pipe.hpp"
#include "ayu/sync/models.h"
using json = nlohmann::json;
class ayu_pipe_wrapper {
public:
template<class T>
void send(T obj);
json receive();
private:
nes::basic_pipe_istream<char> is{"AyuSync"};
nes::basic_pipe_ostream<char> os{"AyuSync"};
};

View file

@ -5,10 +5,10 @@
//
// Copyright @Radolyn, 2023
#include "ayu/boxes/edit_edited_mark.h"
#include "ayu/boxes/edit_deleted_mark.h"
#include "ayu/ui/boxes/edit_edited_mark.h"
#include "ayu/ui/boxes/edit_deleted_mark.h"
#include "ayu/ayu_settings.h"
#include "ayu/settings/settings_ayu.h"
#include "settings_ayu.h"
#include "lang_auto.h"
#include "mainwindow.h"

View file

@ -38,7 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
// AyuGram includes
#include "ayu/ayu_settings.h"
#include "ayu/boxes/voice_confirmation_box.h"
#include "ayu/ui/boxes/voice_confirmation_box.h"
#include "boxes/abstract_box.h"
namespace HistoryView::Controls {

View file

@ -69,7 +69,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtGui/QGuiApplication>
#include <QtGui/QClipboard>
#include "ayu/utils/ayu_profile_values.h"
#include "ayu/ui/utils/ayu_profile_values.h"
namespace Info {
namespace Profile {

View file

@ -78,7 +78,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtGui/QClipboard>
#include "ayu/ayu_settings.h"
#include "ayu/boxes/confirmation_box.h"
#include "ayu/ui/boxes/confirmation_box.h"
#include "styles/style_ayu_icons.h"
namespace Window {