Added HttpClient, DataOps and JSON. Upgraded dep handling
This commit is contained in:
9
.gitmodules
vendored
9
.gitmodules
vendored
@ -1,9 +0,0 @@
|
|||||||
[submodule "Vendor/mimalloc"]
|
|
||||||
path = Vendor/mimalloc
|
|
||||||
url = https://github.com/microsoft/mimalloc
|
|
||||||
[submodule "Vendor/expected"]
|
|
||||||
path = Vendor/expected
|
|
||||||
url = https://github.com/TartanLlama/expected
|
|
||||||
[submodule "Vendor/unordered_dense"]
|
|
||||||
path = Vendor/unordered_dense
|
|
||||||
url = https://github.com/martinus/unordered_dense
|
|
||||||
127
CMake/FindDeps.cmake
Normal file
127
CMake/FindDeps.cmake
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
include(FetchContent)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
httplib
|
||||||
|
GIT_REPOSITORY https://github.com/yhirose/cpp-httplib.git
|
||||||
|
GIT_TAG v0.14.3
|
||||||
|
SYSTEM
|
||||||
|
EXCLUDE_FROM_ALL
|
||||||
|
OVERRIDE_FIND_PACKAGE
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
OpenSSL
|
||||||
|
GIT_REPOSITORY https://github.com/janbar/openssl-cmake.git
|
||||||
|
GIT_TAG master
|
||||||
|
SYSTEM
|
||||||
|
EXCLUDE_FROM_ALL
|
||||||
|
OVERRIDE_FIND_PACKAGE
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
nlohmann_json
|
||||||
|
GIT_REPOSITORY https://github.com/nlohmann/json.git
|
||||||
|
GIT_TAG v3.11.3
|
||||||
|
SYSTEM
|
||||||
|
EXCLUDE_FROM_ALL
|
||||||
|
OVERRIDE_FIND_PACKAGE
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
glaze
|
||||||
|
GIT_REPOSITORY https://github.com/stephenberry/glaze.git
|
||||||
|
GIT_TAG v4.3.1
|
||||||
|
SYSTEM
|
||||||
|
EXCLUDE_FROM_ALL
|
||||||
|
OVERRIDE_FIND_PACKAGE
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
simdjson
|
||||||
|
GIT_REPOSITORY https://github.com/simdjson/simdjson.git
|
||||||
|
GIT_TAG v3.11.0
|
||||||
|
SYSTEM
|
||||||
|
EXCLUDE_FROM_ALL
|
||||||
|
OVERRIDE_FIND_PACKAGE
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
ZLIB
|
||||||
|
GIT_REPOSITORY https://github.com/madler/zlib.git
|
||||||
|
GIT_TAG v1.3.1
|
||||||
|
SYSTEM
|
||||||
|
EXCLUDE_FROM_ALL
|
||||||
|
OVERRIDE_FIND_PACKAGE
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
zstd
|
||||||
|
GIT_REPOSITORY https://github.com/facebook/zstd.git
|
||||||
|
GIT_TAG v1.5.6
|
||||||
|
SOURCE_SUBDIR build/cmake
|
||||||
|
SYSTEM
|
||||||
|
EXCLUDE_FROM_ALL
|
||||||
|
OVERRIDE_FIND_PACKAGE
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
mimalloc
|
||||||
|
GIT_REPOSITORY https://github.com/microsoft/mimalloc.git
|
||||||
|
GIT_TAG v2.1.7
|
||||||
|
SYSTEM
|
||||||
|
EXCLUDE_FROM_ALL
|
||||||
|
OVERRIDE_FIND_PACKAGE
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
tl-expected
|
||||||
|
GIT_REPOSITORY https://github.com/TartanLlama/expected.git
|
||||||
|
GIT_TAG v1.1.0
|
||||||
|
SYSTEM
|
||||||
|
EXCLUDE_FROM_ALL
|
||||||
|
OVERRIDE_FIND_PACKAGE
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
unordered_dense
|
||||||
|
GIT_REPOSITORY https://github.com/martinus/unordered_dense.git
|
||||||
|
GIT_TAG v4.4.0
|
||||||
|
SYSTEM
|
||||||
|
EXCLUDE_FROM_ALL
|
||||||
|
OVERRIDE_FIND_PACKAGE
|
||||||
|
)
|
||||||
|
|
||||||
|
find_package(ZLIB REQUIRED)
|
||||||
|
if(TARGET zlibstatic AND NOT TARGET ZLIB::ZLIB)
|
||||||
|
add_library(ZLIB::ZLIB ALIAS zlibstatic)
|
||||||
|
elseif(TARGET zlib AND NOT TARGET ZLIB::ZLIB)
|
||||||
|
add_library(ZLIB::ZLIB ALIAS zlib)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package(zstd REQUIRED)
|
||||||
|
find_package(glaze REQUIRED)
|
||||||
|
find_package(simdjson REQUIRED)
|
||||||
|
find_package(nlohmann_json REQUIRED)
|
||||||
|
find_package(unordered_dense REQUIRED)
|
||||||
|
|
||||||
|
find_package(OpenSSL REQUIRED)
|
||||||
|
if(TARGET ssl AND NOT TARGET OpenSSL::SSL)
|
||||||
|
add_library(OpenSSL::SSL ALIAS ssl)
|
||||||
|
message(STATUS "Patched OpenSSL::SSL alias for Curl")
|
||||||
|
endif()
|
||||||
|
if(TARGET crypto AND NOT TARGET OpenSSL::Crypto)
|
||||||
|
add_library(OpenSSL::Crypto ALIAS crypto)
|
||||||
|
message(STATUS "Patched OpenSSL::Crypto alias for Curl")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(MI_BUILD_SHARED ON CACHE BOOL "" FORCE)
|
||||||
|
set(MI_BUILD_STATIC ON CACHE BOOL "" FORCE)
|
||||||
|
set(MI_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
||||||
|
find_package(mimalloc REQUIRED)
|
||||||
|
|
||||||
|
set(EXPECTED_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
||||||
|
find_package(tl-expected REQUIRED)
|
||||||
|
|
||||||
|
set(HTTPLIB_REQUIRE_OPENSSL ON CACHE BOOL "" FORCE)
|
||||||
|
set(HTTPLIB_REQUIRE_ZLIB ON CACHE BOOL "" FORCE)
|
||||||
|
find_package(httplib REQUIRED)
|
||||||
@ -13,6 +13,8 @@ project(IACore)
|
|||||||
|
|
||||||
enable_language(C)
|
enable_language(C)
|
||||||
|
|
||||||
|
include(CMake/FindDeps.cmake)
|
||||||
|
|
||||||
# Default to ON if root, OFF if dependency
|
# Default to ON if root, OFF if dependency
|
||||||
option(IACore_BUILD_TESTS "Build unit tests" ${PROJECT_IS_TOP_LEVEL})
|
option(IACore_BUILD_TESTS "Build unit tests" ${PROJECT_IS_TOP_LEVEL})
|
||||||
|
|
||||||
@ -54,10 +56,6 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU"
|
|||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
|
|
||||||
|
|
||||||
add_subdirectory(Vendor/)
|
|
||||||
|
|
||||||
add_subdirectory(Src/)
|
add_subdirectory(Src/)
|
||||||
|
|
||||||
if(IACore_BUILD_TESTS)
|
if(IACore_BUILD_TESTS)
|
||||||
|
|||||||
@ -1,11 +1,14 @@
|
|||||||
set(SRC_FILES
|
set(SRC_FILES
|
||||||
|
"imp/cpp/JSON.cpp"
|
||||||
"imp/cpp/IACore.cpp"
|
"imp/cpp/IACore.cpp"
|
||||||
"imp/cpp/Logger.cpp"
|
"imp/cpp/Logger.cpp"
|
||||||
"imp/cpp/FileOps.cpp"
|
"imp/cpp/FileOps.cpp"
|
||||||
"imp/cpp/AsyncOps.cpp"
|
"imp/cpp/AsyncOps.cpp"
|
||||||
|
"imp/cpp/DataOps.cpp"
|
||||||
"imp/cpp/SocketOps.cpp"
|
"imp/cpp/SocketOps.cpp"
|
||||||
"imp/cpp/StringOps.cpp"
|
"imp/cpp/StringOps.cpp"
|
||||||
"imp/cpp/ProcessOps.cpp"
|
"imp/cpp/ProcessOps.cpp"
|
||||||
|
"imp/cpp/HttpClient.cpp"
|
||||||
"imp/cpp/StreamReader.cpp"
|
"imp/cpp/StreamReader.cpp"
|
||||||
"imp/cpp/StreamWriter.cpp"
|
"imp/cpp/StreamWriter.cpp"
|
||||||
)
|
)
|
||||||
@ -15,7 +18,20 @@ add_library(IACore STATIC ${SRC_FILES})
|
|||||||
target_include_directories(IACore PUBLIC inc/)
|
target_include_directories(IACore PUBLIC inc/)
|
||||||
target_include_directories(IACore PRIVATE imp/hpp/)
|
target_include_directories(IACore PRIVATE imp/hpp/)
|
||||||
|
|
||||||
target_link_libraries(IACore PUBLIC tl::expected unordered_dense::unordered_dense)
|
target_link_libraries(IACore PUBLIC
|
||||||
|
libzstd_static
|
||||||
|
ZLIB::ZLIB
|
||||||
|
tl::expected
|
||||||
|
glaze::glaze
|
||||||
|
simdjson::simdjson
|
||||||
|
nlohmann_json::nlohmann_json
|
||||||
|
unordered_dense::unordered_dense
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(IACore PRIVATE
|
||||||
|
httplib::httplib
|
||||||
|
OpenSSL::SSL
|
||||||
|
)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
target_link_libraries(IACore PUBLIC mimalloc-static)
|
target_link_libraries(IACore PUBLIC mimalloc-static)
|
||||||
@ -35,3 +51,8 @@ define_property(TARGET PROPERTY USE_EXCEPTIONS
|
|||||||
BRIEF_DOCS "If ON, this target is allowed to use C++ exceptions."
|
BRIEF_DOCS "If ON, this target is allowed to use C++ exceptions."
|
||||||
FULL_DOCS "Prevents IACore from propagating -fno-exceptions to this target."
|
FULL_DOCS "Prevents IACore from propagating -fno-exceptions to this target."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
target_compile_definitions(IACore PRIVATE
|
||||||
|
CPPHTTPLIB_OPENSSL_SUPPORT
|
||||||
|
CPPHTTPLIB_ZLIB_SUPPORT
|
||||||
|
)
|
||||||
|
|||||||
168
Src/IACore/imp/cpp/DataOps.cpp
Normal file
168
Src/IACore/imp/cpp/DataOps.cpp
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
// IACore-OSS; The Core Library for All IA Open Source Projects
|
||||||
|
// Copyright (C) 2025 IAS (ias@iasoft.dev)
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include <IACore/DataOps.hpp>
|
||||||
|
|
||||||
|
#include <zlib.h>
|
||||||
|
#include <zstd.h>
|
||||||
|
|
||||||
|
namespace IACore
|
||||||
|
{
|
||||||
|
Expected<Vector<UINT8>, String> DataOps::ZlibInflate(IN Span<CONST UINT8> data)
|
||||||
|
{
|
||||||
|
z_stream zs{};
|
||||||
|
zs.zalloc = Z_NULL;
|
||||||
|
zs.zfree = Z_NULL;
|
||||||
|
zs.opaque = Z_NULL;
|
||||||
|
|
||||||
|
if (inflateInit2(&zs, 15 + 32) != Z_OK)
|
||||||
|
return MakeUnexpected("Failed to initialize zlib inflate");
|
||||||
|
|
||||||
|
zs.next_in = const_cast<Bytef *>(data.data());
|
||||||
|
zs.avail_in = static_cast<uInt>(data.size());
|
||||||
|
|
||||||
|
Vector<UINT8> outBuffer;
|
||||||
|
outBuffer.resize(data.size() * 2);
|
||||||
|
|
||||||
|
int ret;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (zs.total_out >= outBuffer.size())
|
||||||
|
{
|
||||||
|
outBuffer.resize(outBuffer.size() * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
zs.next_out = reinterpret_cast<Bytef *>(outBuffer.data() + zs.total_out);
|
||||||
|
zs.avail_out = static_cast<uInt>(outBuffer.size() - zs.total_out);
|
||||||
|
|
||||||
|
ret = inflate(&zs, Z_NO_FLUSH);
|
||||||
|
|
||||||
|
} while (ret == Z_OK);
|
||||||
|
|
||||||
|
inflateEnd(&zs);
|
||||||
|
|
||||||
|
if (ret != Z_STREAM_END)
|
||||||
|
return MakeUnexpected("Failed to inflate, corrupt or incomplete data");
|
||||||
|
|
||||||
|
outBuffer.resize(zs.total_out);
|
||||||
|
return outBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expected<Vector<UINT8>, String> DataOps::ZlibDeflate(IN Span<CONST UINT8> data)
|
||||||
|
{
|
||||||
|
z_stream zs{};
|
||||||
|
zs.zalloc = Z_NULL;
|
||||||
|
zs.zfree = Z_NULL;
|
||||||
|
zs.opaque = Z_NULL;
|
||||||
|
|
||||||
|
if (deflateInit(&zs, Z_DEFAULT_COMPRESSION) != Z_OK)
|
||||||
|
return MakeUnexpected("Failed to initialize zlib deflate");
|
||||||
|
|
||||||
|
zs.next_in = const_cast<Bytef *>(data.data());
|
||||||
|
zs.avail_in = static_cast<uInt>(data.size());
|
||||||
|
|
||||||
|
Vector<UINT8> outBuffer;
|
||||||
|
|
||||||
|
outBuffer.resize(deflateBound(&zs, data.size()));
|
||||||
|
|
||||||
|
zs.next_out = reinterpret_cast<Bytef *>(outBuffer.data());
|
||||||
|
zs.avail_out = static_cast<uInt>(outBuffer.size());
|
||||||
|
|
||||||
|
int ret = deflate(&zs, Z_FINISH);
|
||||||
|
|
||||||
|
if (ret != Z_STREAM_END)
|
||||||
|
{
|
||||||
|
deflateEnd(&zs);
|
||||||
|
return MakeUnexpected("Failed to deflate, ran out of buffer memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
outBuffer.resize(zs.total_out);
|
||||||
|
|
||||||
|
deflateEnd(&zs);
|
||||||
|
return outBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expected<Vector<UINT8>, String> DataOps::ZstdInflate(IN Span<CONST UINT8> data)
|
||||||
|
{
|
||||||
|
unsigned long long const contentSize = ZSTD_getFrameContentSize(data.data(), data.size());
|
||||||
|
|
||||||
|
if (contentSize == ZSTD_CONTENTSIZE_ERROR)
|
||||||
|
return MakeUnexpected("Failed to inflate: Not valid ZSTD compressed data");
|
||||||
|
|
||||||
|
if (contentSize != ZSTD_CONTENTSIZE_UNKNOWN)
|
||||||
|
{
|
||||||
|
// FAST PATH: We know the size
|
||||||
|
Vector<UINT8> outBuffer;
|
||||||
|
outBuffer.resize(static_cast<size_t>(contentSize));
|
||||||
|
|
||||||
|
size_t const dSize = ZSTD_decompress(outBuffer.data(), outBuffer.size(), data.data(), data.size());
|
||||||
|
|
||||||
|
if (ZSTD_isError(dSize))
|
||||||
|
return MakeUnexpected(std::format("Failed to inflate: {}", ZSTD_getErrorName(dSize)));
|
||||||
|
|
||||||
|
return outBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
ZSTD_DCtx *dctx = ZSTD_createDCtx();
|
||||||
|
Vector<UINT8> outBuffer;
|
||||||
|
outBuffer.resize(data.size() * 2);
|
||||||
|
|
||||||
|
ZSTD_inBuffer input = {data.data(), data.size(), 0};
|
||||||
|
ZSTD_outBuffer output = {outBuffer.data(), outBuffer.size(), 0};
|
||||||
|
|
||||||
|
size_t ret;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ret = ZSTD_decompressStream(dctx, &output, &input);
|
||||||
|
|
||||||
|
if (ZSTD_isError(ret))
|
||||||
|
{
|
||||||
|
ZSTD_freeDCtx(dctx);
|
||||||
|
return MakeUnexpected(std::format("Failed to inflate: {}", ZSTD_getErrorName(ret)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output.pos == output.size)
|
||||||
|
{
|
||||||
|
size_t newSize = outBuffer.size() * 2;
|
||||||
|
outBuffer.resize(newSize);
|
||||||
|
output.dst = outBuffer.data();
|
||||||
|
output.size = newSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
} while (ret != 0);
|
||||||
|
|
||||||
|
outBuffer.resize(output.pos);
|
||||||
|
ZSTD_freeDCtx(dctx);
|
||||||
|
|
||||||
|
return outBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expected<Vector<UINT8>, String> DataOps::ZstdDeflate(IN Span<CONST UINT8> data)
|
||||||
|
{
|
||||||
|
size_t const maxDstSize = ZSTD_compressBound(data.size());
|
||||||
|
|
||||||
|
Vector<UINT8> outBuffer;
|
||||||
|
outBuffer.resize(maxDstSize);
|
||||||
|
|
||||||
|
size_t const compressedSize = ZSTD_compress(outBuffer.data(), maxDstSize, data.data(), data.size(), 3);
|
||||||
|
|
||||||
|
if (ZSTD_isError(compressedSize))
|
||||||
|
return MakeUnexpected(std::format("Failed to deflate: {}", ZSTD_getErrorName(compressedSize)));
|
||||||
|
|
||||||
|
outBuffer.resize(compressedSize);
|
||||||
|
return outBuffer;
|
||||||
|
}
|
||||||
|
} // namespace IACore
|
||||||
205
Src/IACore/imp/cpp/HttpClient.cpp
Normal file
205
Src/IACore/imp/cpp/HttpClient.cpp
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
// IACore-OSS; The Core Library for All IA Open Source Projects
|
||||||
|
// Copyright (C) 2025 IAS (ias@iasoft.dev)
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include <IACore/HttpClient.hpp>
|
||||||
|
|
||||||
|
#include <httplib.h>
|
||||||
|
|
||||||
|
namespace IACore
|
||||||
|
{
|
||||||
|
httplib::Headers BuildHeaders(IN Span<CONST HttpClient::Header> headers, IN PCCHAR defaultContentType)
|
||||||
|
{
|
||||||
|
httplib::Headers out;
|
||||||
|
bool hasContentType = false;
|
||||||
|
|
||||||
|
for (const auto &h : headers)
|
||||||
|
{
|
||||||
|
std::string key = HttpClient::HeaderTypeToString(h.first); // Your existing helper
|
||||||
|
out.emplace(key, h.second);
|
||||||
|
|
||||||
|
if (h.first == HttpClient::EHeaderType::CONTENT_TYPE)
|
||||||
|
hasContentType = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasContentType && defaultContentType)
|
||||||
|
out.emplace("Content-Type", defaultContentType);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpClient::HttpClient(IN CONST String &host)
|
||||||
|
: m_client(new httplib::Client(host)), m_lastResponseCode(EResponseCode::INTERNAL_SERVER_ERROR)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpClient::~HttpClient()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Expected<String, String> HttpClient::RawGet(IN CONST String &path, IN Span<CONST Header> headers,
|
||||||
|
IN PCCHAR defaultContentType)
|
||||||
|
{
|
||||||
|
auto httpHeaders = BuildHeaders(headers, defaultContentType);
|
||||||
|
|
||||||
|
static_cast<httplib::Client*>(m_client)->enable_server_certificate_verification(false);
|
||||||
|
auto res = static_cast<httplib::Client*>(m_client)->Get(path.c_str(), httpHeaders);
|
||||||
|
|
||||||
|
if (res)
|
||||||
|
{
|
||||||
|
m_lastResponseCode = static_cast<EResponseCode>(res->status);
|
||||||
|
if (res->status >= 200 && res->status < 300)
|
||||||
|
return res->body;
|
||||||
|
else
|
||||||
|
return MakeUnexpected(std::format("HTTP Error {}", res->status));
|
||||||
|
}
|
||||||
|
|
||||||
|
return MakeUnexpected(std::format("Network Error: {}", httplib::to_string(res.error())));
|
||||||
|
}
|
||||||
|
|
||||||
|
Expected<String, String> HttpClient::RawPost(IN CONST String &path, IN Span<CONST Header> headers,
|
||||||
|
IN CONST String &body, IN PCCHAR defaultContentType)
|
||||||
|
{
|
||||||
|
auto httpHeaders = BuildHeaders(headers, defaultContentType);
|
||||||
|
|
||||||
|
String contentType = defaultContentType;
|
||||||
|
if (httpHeaders.count("Content-Type"))
|
||||||
|
{
|
||||||
|
contentType = httpHeaders.find("Content-Type")->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
static_cast<httplib::Client*>(m_client)->enable_server_certificate_verification(false);
|
||||||
|
auto res = static_cast<httplib::Client*>(m_client)->Post(path.c_str(), httpHeaders, body, contentType.c_str());
|
||||||
|
|
||||||
|
if (res)
|
||||||
|
{
|
||||||
|
m_lastResponseCode = static_cast<EResponseCode>(res->status);
|
||||||
|
if (res->status >= 200 && res->status < 300)
|
||||||
|
return res->body;
|
||||||
|
else
|
||||||
|
return MakeUnexpected(std::format("HTTP Error {}", res->status));
|
||||||
|
}
|
||||||
|
|
||||||
|
return MakeUnexpected(std::format("Network Error: {}", httplib::to_string(res.error())));
|
||||||
|
}
|
||||||
|
} // namespace IACore
|
||||||
|
|
||||||
|
namespace IACore
|
||||||
|
{
|
||||||
|
HttpClient::Header HttpClient::CreateHeader(IN EHeaderType key, IN CONST String &value)
|
||||||
|
{
|
||||||
|
return std::make_pair(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
String HttpClient::UrlEncode(IN CONST String &value)
|
||||||
|
{
|
||||||
|
std::stringstream escaped;
|
||||||
|
escaped.fill('0');
|
||||||
|
escaped << std::hex << std::uppercase;
|
||||||
|
|
||||||
|
for (char c : value)
|
||||||
|
{
|
||||||
|
if (std::isalnum(static_cast<unsigned char>(c)) || c == '-' || c == '_' || c == '.' || c == '~')
|
||||||
|
escaped << c;
|
||||||
|
else
|
||||||
|
escaped << '%' << std::setw(2) << static_cast<int>(static_cast<unsigned char>(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
return escaped.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
String HttpClient::UrlDecode(IN CONST String &value)
|
||||||
|
{
|
||||||
|
String result;
|
||||||
|
result.reserve(value.length());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < value.length(); ++i)
|
||||||
|
{
|
||||||
|
if (value[i] == '%' && i + 2 < value.length())
|
||||||
|
{
|
||||||
|
std::string hexStr = value.substr(i + 1, 2);
|
||||||
|
char decodedChar = static_cast<char>(std::strtol(hexStr.c_str(), nullptr, 16));
|
||||||
|
result += decodedChar;
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
else if (value[i] == '+')
|
||||||
|
result += ' ';
|
||||||
|
else
|
||||||
|
result += value[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
String HttpClient::HeaderTypeToString(IN EHeaderType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case EHeaderType::ACCEPT:
|
||||||
|
return "Accept";
|
||||||
|
case EHeaderType::ACCEPT_CHARSET:
|
||||||
|
return "Accept-Charset";
|
||||||
|
case EHeaderType::ACCEPT_ENCODING:
|
||||||
|
return "Accept-Encoding";
|
||||||
|
case EHeaderType::ACCEPT_LANGUAGE:
|
||||||
|
return "Accept-Language";
|
||||||
|
case EHeaderType::AUTHORIZATION:
|
||||||
|
return "Authorization";
|
||||||
|
case EHeaderType::CACHE_CONTROL:
|
||||||
|
return "Cache-Control";
|
||||||
|
case EHeaderType::CONNECTION:
|
||||||
|
return "Connection";
|
||||||
|
case EHeaderType::CONTENT_LENGTH:
|
||||||
|
return "Content-Length";
|
||||||
|
case EHeaderType::CONTENT_TYPE:
|
||||||
|
return "Content-Type";
|
||||||
|
case EHeaderType::COOKIE:
|
||||||
|
return "Cookie";
|
||||||
|
case EHeaderType::DATE:
|
||||||
|
return "Date";
|
||||||
|
case EHeaderType::EXPECT:
|
||||||
|
return "Expect";
|
||||||
|
case EHeaderType::HOST:
|
||||||
|
return "Host";
|
||||||
|
case EHeaderType::IF_MATCH:
|
||||||
|
return "If-Match";
|
||||||
|
case EHeaderType::IF_MODIFIED_SINCE:
|
||||||
|
return "If-Modified-Since";
|
||||||
|
case EHeaderType::IF_NONE_MATCH:
|
||||||
|
return "If-None-Match";
|
||||||
|
case EHeaderType::ORIGIN:
|
||||||
|
return "Origin";
|
||||||
|
case EHeaderType::PRAGMA:
|
||||||
|
return "Pragma";
|
||||||
|
case EHeaderType::PROXY_AUTHORIZATION:
|
||||||
|
return "Proxy-Authorization";
|
||||||
|
case EHeaderType::RANGE:
|
||||||
|
return "Range";
|
||||||
|
case EHeaderType::REFERER:
|
||||||
|
return "Referer";
|
||||||
|
case EHeaderType::TE:
|
||||||
|
return "TE";
|
||||||
|
case EHeaderType::UPGRADE:
|
||||||
|
return "Upgrade";
|
||||||
|
case EHeaderType::USER_AGENT:
|
||||||
|
return "User-Agent";
|
||||||
|
case EHeaderType::VIA:
|
||||||
|
return "Via";
|
||||||
|
case EHeaderType::WARNING:
|
||||||
|
return "Warning";
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace IACore
|
||||||
43
Src/IACore/imp/cpp/JSON.cpp
Normal file
43
Src/IACore/imp/cpp/JSON.cpp
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// IACore-OSS; The Core Library for All IA Open Source Projects
|
||||||
|
// Copyright (C) 2025 IAS (ias@iasoft.dev)
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include <IACore/JSON.hpp>
|
||||||
|
|
||||||
|
namespace IACore
|
||||||
|
{
|
||||||
|
Expected<nlohmann::json, String> JSON::Parse(IN CONST String &json)
|
||||||
|
{
|
||||||
|
const auto parseResult = nlohmann::json::parse(json, nullptr, false, true);
|
||||||
|
if (parseResult.is_discarded())
|
||||||
|
return MakeUnexpected("Failed to parse JSON");
|
||||||
|
return parseResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expected<simdjson::dom::object, String> JSON::ParseReadOnly(IN CONST String &json)
|
||||||
|
{
|
||||||
|
simdjson::error_code error{};
|
||||||
|
simdjson::dom::parser parser;
|
||||||
|
simdjson::dom::object object;
|
||||||
|
if ((error = parser.parse(json).get(object)))
|
||||||
|
return MakeUnexpected(std::format("Failed to parse JSON : {}", simdjson::error_message(error)));
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
String JSON::Encode(IN nlohmann::json data)
|
||||||
|
{
|
||||||
|
return data.dump();
|
||||||
|
}
|
||||||
|
} // namespace IACore
|
||||||
32
Src/IACore/inc/IACore/DataOps.hpp
Normal file
32
Src/IACore/inc/IACore/DataOps.hpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// IACore-OSS; The Core Library for All IA Open Source Projects
|
||||||
|
// Copyright (C) 2025 IAS (ias@iasoft.dev)
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <IACore/PCH.hpp>
|
||||||
|
|
||||||
|
namespace IACore
|
||||||
|
{
|
||||||
|
class DataOps
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
STATIC Expected<Vector<UINT8>, String> ZlibInflate(IN Span<CONST UINT8> data);
|
||||||
|
STATIC Expected<Vector<UINT8>, String> ZlibDeflate(IN Span<CONST UINT8> data);
|
||||||
|
|
||||||
|
STATIC Expected<Vector<UINT8>, String> ZstdInflate(IN Span<CONST UINT8> data);
|
||||||
|
STATIC Expected<Vector<UINT8>, String> ZstdDeflate(IN Span<CONST UINT8> data);
|
||||||
|
};
|
||||||
|
}
|
||||||
195
Src/IACore/inc/IACore/HttpClient.hpp
Normal file
195
Src/IACore/inc/IACore/HttpClient.hpp
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
// IACore-OSS; The Core Library for All IA Open Source Projects
|
||||||
|
// Copyright (C) 2025 IAS (ias@iasoft.dev)
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <IACore/JSON.hpp>
|
||||||
|
|
||||||
|
namespace IACore
|
||||||
|
{
|
||||||
|
class HttpClient
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class EHeaderType
|
||||||
|
{
|
||||||
|
ACCEPT,
|
||||||
|
ACCEPT_CHARSET,
|
||||||
|
ACCEPT_ENCODING,
|
||||||
|
ACCEPT_LANGUAGE,
|
||||||
|
AUTHORIZATION,
|
||||||
|
CACHE_CONTROL,
|
||||||
|
CONNECTION,
|
||||||
|
CONTENT_LENGTH,
|
||||||
|
CONTENT_TYPE,
|
||||||
|
COOKIE,
|
||||||
|
DATE,
|
||||||
|
EXPECT,
|
||||||
|
HOST,
|
||||||
|
IF_MATCH,
|
||||||
|
IF_MODIFIED_SINCE,
|
||||||
|
IF_NONE_MATCH,
|
||||||
|
ORIGIN,
|
||||||
|
PRAGMA,
|
||||||
|
PROXY_AUTHORIZATION,
|
||||||
|
RANGE,
|
||||||
|
REFERER,
|
||||||
|
TE,
|
||||||
|
UPGRADE,
|
||||||
|
USER_AGENT,
|
||||||
|
VIA,
|
||||||
|
WARNING
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class EResponseCode : INT32
|
||||||
|
{
|
||||||
|
// 1xx Informational
|
||||||
|
CONTINUE = 100,
|
||||||
|
SWITCHING_PROTOCOLS = 101,
|
||||||
|
PROCESSING = 102,
|
||||||
|
EARLY_HINTS = 103,
|
||||||
|
|
||||||
|
// 2xx Success
|
||||||
|
OK = 200,
|
||||||
|
CREATED = 201,
|
||||||
|
ACCEPTED = 202,
|
||||||
|
NON_AUTHORITATIVE_INFORMATION = 203,
|
||||||
|
NO_CONTENT = 204,
|
||||||
|
RESET_CONTENT = 205,
|
||||||
|
PARTIAL_CONTENT = 206,
|
||||||
|
MULTI_STATUS = 207,
|
||||||
|
ALREADY_REPORTED = 208,
|
||||||
|
IM_USED = 226,
|
||||||
|
|
||||||
|
// 3xx Redirection
|
||||||
|
MULTIPLE_CHOICES = 300,
|
||||||
|
MOVED_PERMANENTLY = 301,
|
||||||
|
FOUND = 302,
|
||||||
|
SEE_OTHER = 303,
|
||||||
|
NOT_MODIFIED = 304,
|
||||||
|
USE_PROXY = 305,
|
||||||
|
TEMPORARY_REDIRECT = 307,
|
||||||
|
PERMANENT_REDIRECT = 308,
|
||||||
|
|
||||||
|
// 4xx Client Error
|
||||||
|
BAD_REQUEST = 400,
|
||||||
|
UNAUTHORIZED = 401,
|
||||||
|
PAYMENT_REQUIRED = 402,
|
||||||
|
FORBIDDEN = 403,
|
||||||
|
NOT_FOUND = 404,
|
||||||
|
METHOD_NOT_ALLOWED = 405,
|
||||||
|
NOT_ACCEPTABLE = 406,
|
||||||
|
PROXY_AUTHENTICATION_REQUIRED = 407,
|
||||||
|
REQUEST_TIMEOUT = 408,
|
||||||
|
CONFLICT = 409,
|
||||||
|
GONE = 410,
|
||||||
|
LENGTH_REQUIRED = 411,
|
||||||
|
PRECONDITION_FAILED = 412,
|
||||||
|
PAYLOAD_TOO_LARGE = 413,
|
||||||
|
URI_TOO_LONG = 414,
|
||||||
|
UNSUPPORTED_MEDIA_TYPE = 415,
|
||||||
|
RANGE_NOT_SATISFIABLE = 416,
|
||||||
|
EXPECTATION_FAILED = 417,
|
||||||
|
IM_A_TEAPOT = 418,
|
||||||
|
MISDIRECTED_REQUEST = 421,
|
||||||
|
UNPROCESSABLE_ENTITY = 422,
|
||||||
|
LOCKED = 423,
|
||||||
|
FAILED_DEPENDENCY = 424,
|
||||||
|
TOO_EARLY = 425,
|
||||||
|
UPGRADE_REQUIRED = 426,
|
||||||
|
PRECONDITION_REQUIRED = 428,
|
||||||
|
TOO_MANY_REQUESTS = 429,
|
||||||
|
REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
|
||||||
|
UNAVAILABLE_FOR_LEGAL_REASONS = 451,
|
||||||
|
|
||||||
|
// 5xx Server Error
|
||||||
|
INTERNAL_SERVER_ERROR = 500,
|
||||||
|
NOT_IMPLEMENTED = 501,
|
||||||
|
BAD_GATEWAY = 502,
|
||||||
|
SERVICE_UNAVAILABLE = 503,
|
||||||
|
GATEWAY_TIMEOUT = 504,
|
||||||
|
HTTP_VERSION_NOT_SUPPORTED = 505,
|
||||||
|
VARIANT_ALSO_NEGOTIATES = 506,
|
||||||
|
INSUFFICIENT_STORAGE = 507,
|
||||||
|
LOOP_DETECTED = 508,
|
||||||
|
NOT_EXTENDED = 510,
|
||||||
|
NETWORK_AUTHENTICATION_REQUIRED = 511
|
||||||
|
};
|
||||||
|
|
||||||
|
using Header = KeyValuePair<EHeaderType, String>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
HttpClient(IN CONST String &host);
|
||||||
|
~HttpClient();
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Automatically adds the following headers, if not present:
|
||||||
|
// 1) EHeaderType::CONTENT_TYPE = defaultContentType
|
||||||
|
Expected<String, String> RawGet(IN CONST String &path, IN Span<CONST Header> headers,
|
||||||
|
IN PCCHAR defaultContentType = "application/x-www-form-urlencoded");
|
||||||
|
// Automatically adds the following headers, if not present:
|
||||||
|
// 1) EHeaderType::CONTENT_TYPE = defaultContentType
|
||||||
|
Expected<String, String> RawPost(IN CONST String &path, IN Span<CONST Header> headers, IN CONST String &body,
|
||||||
|
IN PCCHAR defaultContentType = "application/x-www-form-urlencoded");
|
||||||
|
|
||||||
|
template<typename _response_type>
|
||||||
|
Expected<_response_type, String> JsonGet(IN CONST String &path, IN Span<CONST Header> headers);
|
||||||
|
|
||||||
|
template<typename _payload_type, typename _response_type>
|
||||||
|
Expected<_response_type, String> JsonPost(IN CONST String &path, IN Span<CONST Header> headers,
|
||||||
|
IN CONST _payload_type &body);
|
||||||
|
|
||||||
|
public:
|
||||||
|
STATIC String UrlEncode(IN CONST String &value);
|
||||||
|
STATIC String UrlDecode(IN CONST String &value);
|
||||||
|
|
||||||
|
STATIC String HeaderTypeToString(IN EHeaderType type);
|
||||||
|
STATIC Header CreateHeader(IN EHeaderType key, IN CONST String &value);
|
||||||
|
|
||||||
|
public:
|
||||||
|
EResponseCode LastResponseCode()
|
||||||
|
{
|
||||||
|
return m_lastResponseCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
PVOID m_client{};
|
||||||
|
EResponseCode m_lastResponseCode;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename _response_type>
|
||||||
|
Expected<_response_type, String> HttpClient::JsonGet(IN CONST String &path, IN Span<CONST Header> headers)
|
||||||
|
{
|
||||||
|
const auto rawResponse = RawGet(path, headers, "application/json");
|
||||||
|
if (!rawResponse)
|
||||||
|
return MakeUnexpected(rawResponse.error());
|
||||||
|
if (LastResponseCode() != EResponseCode::OK)
|
||||||
|
return MakeUnexpected(std::format("Server responded with code {}", (INT32) LastResponseCode()));
|
||||||
|
return JSON::ParseToStruct<_response_type>(*rawResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename _payload_type, typename _response_type>
|
||||||
|
Expected<_response_type, String> HttpClient::JsonPost(IN CONST String &path, IN Span<CONST Header> headers,
|
||||||
|
IN CONST _payload_type &body)
|
||||||
|
{
|
||||||
|
const auto encodedBody = IA_TRY(JSON::EncodeStruct(body));
|
||||||
|
const auto rawResponse = RawPost(path, headers, encodedBody, "application/json");
|
||||||
|
if (!rawResponse)
|
||||||
|
return MakeUnexpected(rawResponse.error());
|
||||||
|
if (LastResponseCode() != EResponseCode::OK)
|
||||||
|
return MakeUnexpected(std::format("Server responded with code {}", (INT32) LastResponseCode()));
|
||||||
|
return JSON::ParseToStruct<_response_type>(*rawResponse);
|
||||||
|
}
|
||||||
|
} // namespace IACore
|
||||||
58
Src/IACore/inc/IACore/JSON.hpp
Normal file
58
Src/IACore/inc/IACore/JSON.hpp
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
// IACore-OSS; The Core Library for All IA Open Source Projects
|
||||||
|
// Copyright (C) 2025 IAS (ias@iasoft.dev)
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <IACore/PCH.hpp>
|
||||||
|
|
||||||
|
#include <simdjson.h>
|
||||||
|
#include <glaze/glaze.hpp>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
namespace IACore
|
||||||
|
{
|
||||||
|
class JSON
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
STATIC CONSTEXPR AUTO GLAZE_JSON_OPTS = glz::opts{.error_on_unknown_keys = false};
|
||||||
|
|
||||||
|
public:
|
||||||
|
STATIC Expected<nlohmann::json, String> Parse(IN CONST String &json);
|
||||||
|
STATIC Expected<simdjson::dom::object, String> ParseReadOnly(IN CONST String &json);
|
||||||
|
template<typename _object_type> STATIC Expected<_object_type, String> ParseToStruct(IN CONST String &json);
|
||||||
|
|
||||||
|
STATIC String Encode(IN nlohmann::json data);
|
||||||
|
template<typename _object_type> STATIC Expected<String, String> EncodeStruct(IN CONST _object_type &data);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename _object_type> Expected<_object_type, String> JSON::ParseToStruct(IN CONST String &json)
|
||||||
|
{
|
||||||
|
_object_type result{};
|
||||||
|
const auto parseError = glz::read<GLAZE_JSON_OPTS>(result, json);
|
||||||
|
if (parseError)
|
||||||
|
return MakeUnexpected(std::format("JSON Error: {}", glz::format_error(parseError, json)));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename _object_type> Expected<String, String> JSON::EncodeStruct(IN CONST _object_type &data)
|
||||||
|
{
|
||||||
|
String result;
|
||||||
|
const auto encodeError = glz::write_json(data, result);
|
||||||
|
if (encodeError)
|
||||||
|
return MakeUnexpected(std::format("JSON Error: {}", glz::format_error(encodeError)));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} // namespace IACore
|
||||||
@ -33,6 +33,7 @@
|
|||||||
# include <cstring>
|
# include <cstring>
|
||||||
# include <cstddef>
|
# include <cstddef>
|
||||||
# include <chrono>
|
# include <chrono>
|
||||||
|
# include <iomanip>
|
||||||
# include <fstream>
|
# include <fstream>
|
||||||
# include <iostream>
|
# include <iostream>
|
||||||
# include <concepts>
|
# include <concepts>
|
||||||
@ -540,6 +541,7 @@ template<typename _value_type> using UniquePtr = std::unique_ptr<_value_type>;
|
|||||||
template<typename _value_type> using Deque = std::deque<_value_type>;
|
template<typename _value_type> using Deque = std::deque<_value_type>;
|
||||||
template<typename _type_a, typename _type_b> using Pair = std::pair<_type_a, _type_b>;
|
template<typename _type_a, typename _type_b> using Pair = std::pair<_type_a, _type_b>;
|
||||||
template<typename... types> using Tuple = std::tuple<types...>;
|
template<typename... types> using Tuple = std::tuple<types...>;
|
||||||
|
template<typename _key_type, typename _value_type> using KeyValuePair = std::pair<_key_type, _value_type>;
|
||||||
|
|
||||||
template<typename _expected_type, typename _unexpected_type>
|
template<typename _expected_type, typename _unexpected_type>
|
||||||
using Expected = tl::expected<_expected_type, _unexpected_type>;
|
using Expected = tl::expected<_expected_type, _unexpected_type>;
|
||||||
|
|||||||
10
Vendor/CMakeLists.txt
vendored
10
Vendor/CMakeLists.txt
vendored
@ -1,10 +0,0 @@
|
|||||||
|
|
||||||
set(EXPECTED_BUILD_TESTS OFF)
|
|
||||||
add_subdirectory(expected/)
|
|
||||||
|
|
||||||
set(MI_BUILD_TESTS OFF)
|
|
||||||
set(MI_BUILD_STATIC ON)
|
|
||||||
set(MI_BUILD_SHARED ON)
|
|
||||||
add_subdirectory(mimalloc/)
|
|
||||||
|
|
||||||
add_subdirectory(unordered_dense/)
|
|
||||||
1
Vendor/expected
vendored
1
Vendor/expected
vendored
Submodule Vendor/expected deleted from 1770e3559f
1
Vendor/mimalloc
vendored
1
Vendor/mimalloc
vendored
Submodule Vendor/mimalloc deleted from 09a27098aa
1
Vendor/unordered_dense
vendored
1
Vendor/unordered_dense
vendored
Submodule Vendor/unordered_dense deleted from 3234af2c03
Reference in New Issue
Block a user