diff --git a/src/api-objects/src/apikeysprovider.cpp b/src/api-objects/src/apikeysprovider.cpp index 520891e2..b364085c 100644 --- a/src/api-objects/src/apikeysprovider.cpp +++ b/src/api-objects/src/apikeysprovider.cpp @@ -86,7 +86,7 @@ APIKeysProvider::APIKeysPerExchange APIKeysProvider::ParseAPIKeys(std::string_vi schema::APIKeysPerExchangeMap apiKeysPerExchangeMap; - ReadJsonOrThrow(secretsFile.readAll(), apiKeysPerExchangeMap); + ReadExactJsonOrThrow(secretsFile.readAll(), apiKeysPerExchangeMap); const auto& exchangesWithoutSecrets = exchangeSecretsInfo.exchangesWithoutSecrets(); diff --git a/src/api/common/src/commonapi.cpp b/src/api/common/src/commonapi.cpp index adcdbc51..470c69b7 100644 --- a/src/api/common/src/commonapi.cpp +++ b/src/api/common/src/commonapi.cpp @@ -52,7 +52,7 @@ CommonAPI::CommonAPI(const CoincenterInfo& coincenterInfo, Duration fiatsUpdateF schema::FiatsCache fiatsCache; auto dataStr = GetFiatCacheFile(_coincenterInfo.dataDir()).readAll(); if (!dataStr.empty()) { - ReadJsonOrThrow(dataStr, fiatsCache); + ReadExactJsonOrThrow(dataStr, fiatsCache); if (fiatsCache.timeepoch != 0) { CurrencyCodeSet fiats(std::move(fiatsCache.fiats)); log::debug("Loaded {} fiats from cache file", fiats.size()); @@ -213,22 +213,25 @@ CurrencyCodeVector CommonAPI::FiatsFunc::retrieveFiatsSource2() { void CommonAPI::updateCacheFile() const { const auto fiatsCacheFile = GetFiatCacheFile(_coincenterInfo.dataDir()); - auto data = fiatsCacheFile.readAllJson(); + auto fiatsData = fiatsCacheFile.readAllJson(); const auto fiatsPtrLastUpdatedTimePair = _fiatsCache.retrieve(); - const auto timeEpochIt = data.find("timeepoch"); - if (timeEpochIt != data.end()) { + const auto timeEpochIt = fiatsData.find("timeepoch"); + bool updateFiatsCache = true; + if (timeEpochIt != fiatsData.end()) { const int64_t lastTimeFileUpdated = timeEpochIt->get(); if (TimePoint(seconds(lastTimeFileUpdated)) >= fiatsPtrLastUpdatedTimePair.second) { - return; // No update + updateFiatsCache = false; } } - data.clear(); - if (fiatsPtrLastUpdatedTimePair.first != nullptr) { - for (CurrencyCode fiatCode : *fiatsPtrLastUpdatedTimePair.first) { - data["fiats"].emplace_back(fiatCode.str()); + if (updateFiatsCache) { + fiatsData.clear(); + if (fiatsPtrLastUpdatedTimePair.first != nullptr) { + for (CurrencyCode fiatCode : *fiatsPtrLastUpdatedTimePair.first) { + fiatsData["fiats"].emplace_back(fiatCode.str()); + } + fiatsData["timeepoch"] = TimestampToSecondsSinceEpoch(fiatsPtrLastUpdatedTimePair.second); + fiatsCacheFile.writeJson(fiatsData); } - data["timeepoch"] = TimestampToSecondsSinceEpoch(fiatsPtrLastUpdatedTimePair.second); - fiatsCacheFile.writeJson(data); } _withdrawalFeesCrawler.updateCacheFile(); diff --git a/src/api/common/src/fiatconverter.cpp b/src/api/common/src/fiatconverter.cpp index e4f27bc8..4e6022cc 100644 --- a/src/api/common/src/fiatconverter.cpp +++ b/src/api/common/src/fiatconverter.cpp @@ -57,13 +57,13 @@ FiatConverter::FiatConverter(const CoincenterInfo& coincenterInfo, Duration rate _dataDir(coincenterInfo.dataDir()) { const auto data = fiatsRatesCacheReader.readAll(); - ReadJsonOrThrow(data, _pricesMap); + ReadExactJsonOrThrow(data, _pricesMap); log::debug("Loaded {} fiat currency rates from {}", _pricesMap.size(), kRatesCacheFile); } void FiatConverter::updateCacheFile() const { - auto dataStr = WriteJsonOrThrow(_pricesMap); + auto dataStr = WriteMiniJsonOrThrow(_pricesMap); GetRatesCacheFile(_dataDir).write(dataStr); } @@ -89,13 +89,9 @@ std::optional FiatConverter::queryCurrencyRateSource1(Market market) { schema::FreeCurrencyConverterResponse response; //{"query":{"count":1},"results":{"EUR_KRW":{"id":"EUR_KRW","val":1329.475323,"to":"KRW","fr":"EUR"}}} - auto ec = json::read(response, dataStr); + auto ec = ReadPartialJson(dataStr, "fiat currency converter service's first source", response); if (ec) { - std::string_view prefixJsonContent = dataStr.substr(0, std::min(dataStr.size(), 20)); - log::error("Error while reading json content from fiat currency converter service's first source '{}{}': {}", - prefixJsonContent, prefixJsonContent.size() < dataStr.size() ? "..." : "", - json::format_error(ec, dataStr)); return {}; } @@ -115,13 +111,9 @@ std::optional FiatConverter::queryCurrencyRateSource2(Market market) { schema::FiatRatesSource2Response response; - auto ec = json::read(response, dataStr); + auto ec = ReadPartialJson(dataStr, "fiat currency converter service's second source", response); if (ec) { - std::string_view prefixJsonContent = dataStr.substr(0, std::min(dataStr.size(), 20)); - log::error("Error while reading json content from fiat currency converter service's second source '{}{}': {}", - prefixJsonContent, prefixJsonContent.size() < dataStr.size() ? "..." : "", - json::format_error(ec, dataStr)); return {}; } @@ -276,12 +268,9 @@ FiatConverter::ThirdPartySecret FiatConverter::LoadCurrencyConverterAPIKey(const return thirdPartySecret; } - auto ec = json::read(thirdPartySecret, dataStr); + auto ec = ReadPartialJson(dataStr, "third party's secrets", thirdPartySecret); if (ec) { - std::string_view prefixJsonContent = dataStr.substr(0, std::min(dataStr.size(), 20)); - log::error("Error while reading json content from third party's secrets '{}{}': {}", prefixJsonContent, - prefixJsonContent.size() < dataStr.size() ? "..." : "", json::format_error(ec, dataStr)); return thirdPartySecret; } diff --git a/src/api/common/src/withdrawal-fees-schema.hpp b/src/api/common/src/withdrawal-fees-schema.hpp new file mode 100644 index 00000000..85a5eaaa --- /dev/null +++ b/src/api/common/src/withdrawal-fees-schema.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include +#include + +#include "cct_string.hpp" +#include "cct_vector.hpp" +#include "currencycode.hpp" +#include "monetaryamount.hpp" + +namespace cct::schema { + +struct WithdrawInfoFileItemAsset { + MonetaryAmount min; // only amount + MonetaryAmount fee; // only amount +}; + +struct WithdrawInfoFileItem { + int64_t timeepoch; + std::unordered_map assets; +}; + +using WithdrawInfoFile = std::unordered_map; + +struct WithdrawFeesCrawlerExchangeFeesCoinSource1 { + string symbol; + + auto operator<=>(const WithdrawFeesCrawlerExchangeFeesCoinSource1&) const = default; +}; + +struct WithdrawFeesCrawlerExchangeFeesSource1 { + double amount; + double min; + WithdrawFeesCrawlerExchangeFeesCoinSource1 coin; + + auto operator<=>(const WithdrawFeesCrawlerExchangeFeesSource1&) const = default; +}; + +struct WithdrawFeesCrawlerExchangeSource1 { + string name; + vector fees; +}; + +struct WithdrawFeesCrawlerSource1 { + WithdrawFeesCrawlerExchangeSource1 exchange; +}; + +} // namespace cct::schema \ No newline at end of file diff --git a/src/api/common/src/withdrawalfees-crawler.cpp b/src/api/common/src/withdrawalfees-crawler.cpp index d0c756e2..8e26792a 100644 --- a/src/api/common/src/withdrawalfees-crawler.cpp +++ b/src/api/common/src/withdrawalfees-crawler.cpp @@ -12,7 +12,6 @@ #include "cct_cctype.hpp" #include "cct_const.hpp" #include "cct_exception.hpp" -#include "cct_json-container.hpp" #include "cct_log.hpp" #include "cct_string.hpp" #include "coincenterinfo.hpp" @@ -22,8 +21,11 @@ #include "httprequesttype.hpp" #include "monetaryamount.hpp" #include "permanentcurloptions.hpp" +#include "read-json.hpp" #include "threadpool.hpp" #include "timedef.hpp" +#include "withdrawal-fees-schema.hpp" +#include "write-json.hpp" namespace cct { @@ -40,37 +42,39 @@ WithdrawalFeesCrawler::WithdrawalFeesCrawler(const CoincenterInfo& coincenterInf CachedResultVault& cachedResultVault) : _coincenterInfo(coincenterInfo), _withdrawalFeesCache(CachedResultOptions(minDurationBetweenQueries, cachedResultVault), coincenterInfo) { - json::container data = GetWithdrawInfoFile(_coincenterInfo.dataDir()).readAllJson(); - if (!data.empty()) { - const auto nowTime = Clock::now(); - for (const auto& [exchangeName, exchangeData] : data.items()) { - TimePoint lastUpdatedTime(seconds(exchangeData["timeepoch"].get())); - if (nowTime - lastUpdatedTime < minDurationBetweenQueries) { - // we can reuse file data - WithdrawalInfoMaps withdrawalInfoMaps; - - for (const auto& [curCodeStr, val] : exchangeData["assets"].items()) { - CurrencyCode cur(curCodeStr); - MonetaryAmount withdrawMin(val["min"].get(), cur); - MonetaryAmount withdrawFee(val["fee"].get(), cur); - - log::trace("Updated {} withdrawal fee {} from cache", exchangeName, withdrawFee); - log::trace("Updated {} min withdraw {} from cache", exchangeName, withdrawMin); - - withdrawalInfoMaps.first.insert(withdrawFee); - withdrawalInfoMaps.second.insert_or_assign(cur, withdrawMin); - } + auto data = GetWithdrawInfoFile(_coincenterInfo.dataDir()).readAll(); - // Warning: we store a std::string_view in the cache, and 'exchangeName' will be destroyed at the end - // of this function. So we need to retrieve the 'constant' std::string_view of this exchange (in static memory) - // to store in the cache. - auto constantExchangeNameSVIt = std::ranges::find(kSupportedExchanges, exchangeName); - if (constantExchangeNameSVIt == std::end(kSupportedExchanges)) { - throw exception("unknown exchange name {}", exchangeName); - } + schema::WithdrawInfoFile withdrawInfoFileContent; + + ReadExactJsonOrThrow(data, withdrawInfoFileContent); + + const auto nowTime = Clock::now(); + for (const auto& [exchangeName, exchangeData] : withdrawInfoFileContent) { + TimePoint lastUpdatedTime(seconds(exchangeData.timeepoch)); + if (nowTime - lastUpdatedTime < minDurationBetweenQueries) { + // we can reuse file data + WithdrawalInfoMaps withdrawalInfoMaps; + + for (const auto& [cur, val] : exchangeData.assets) { + MonetaryAmount withdrawMin(val.min, cur); + MonetaryAmount withdrawFee(val.fee, cur); + + log::trace("Updated {} withdrawal fee {} from cache", exchangeName, withdrawFee); + log::trace("Updated {} min withdraw {} from cache", exchangeName, withdrawMin); - _withdrawalFeesCache.set(std::move(withdrawalInfoMaps), lastUpdatedTime, *constantExchangeNameSVIt); + withdrawalInfoMaps.first.insert(withdrawFee); + withdrawalInfoMaps.second.insert_or_assign(cur, withdrawMin); } + + // Warning: we store a std::string_view in the cache, and 'exchangeName' will be destroyed at the end + // of this function. So we need to retrieve the 'constant' std::string_view of this exchange (in static memory) + // to store in the cache. + auto constantExchangeNameSVIt = std::ranges::find(kSupportedExchanges, exchangeName); + if (constantExchangeNameSVIt == std::end(kSupportedExchanges)) { + throw exception("unknown exchange name {}", exchangeName); + } + + _withdrawalFeesCache.set(std::move(withdrawalInfoMaps), lastUpdatedTime, *constantExchangeNameSVIt); } } } @@ -114,78 +118,65 @@ WithdrawalFeesCrawler::WithdrawalInfoMaps WithdrawalFeesCrawler::WithdrawalFeesF } void WithdrawalFeesCrawler::updateCacheFile() const { - json::container data; + schema::WithdrawInfoFile withdrawInfoFile; for (const std::string_view exchangeName : kSupportedExchanges) { const auto [withdrawalInfoMapsPtr, latestUpdate] = _withdrawalFeesCache.retrieve(exchangeName); if (withdrawalInfoMapsPtr != nullptr) { const WithdrawalInfoMaps& withdrawalInfoMaps = *withdrawalInfoMapsPtr; - json::container exchangeData; - exchangeData["timeepoch"] = TimestampToSecondsSinceEpoch(latestUpdate); + schema::WithdrawInfoFileItem& withdrawInfoFileItem = + withdrawInfoFile.emplace(std::make_pair(exchangeName, schema::WithdrawInfoFileItem{})).first->second; + withdrawInfoFileItem.timeepoch = TimestampToSecondsSinceEpoch(latestUpdate); for (const auto withdrawFee : withdrawalInfoMaps.first) { - string curCodeStr = withdrawFee.currencyCode().str(); - exchangeData["assets"][curCodeStr]["min"] = - withdrawalInfoMaps.second.find(withdrawFee.currencyCode())->second.amountStr(); - exchangeData["assets"][curCodeStr]["fee"] = withdrawFee.amountStr(); - } + CurrencyCode cur = withdrawFee.currencyCode(); + + schema::WithdrawInfoFileItemAsset& asset = withdrawInfoFileItem.assets[cur]; - data.emplace(exchangeName, std::move(exchangeData)); + auto minIt = withdrawalInfoMaps.second.find(cur); + if (minIt != withdrawalInfoMaps.second.end()) { + asset.min = MonetaryAmount(minIt->second, CurrencyCode{}); + } + asset.fee = MonetaryAmount(withdrawFee, CurrencyCode{}); + } } } - GetWithdrawInfoFile(_coincenterInfo.dataDir()).writeJson(data); + auto dataStr = WriteMiniJsonOrThrow(withdrawInfoFile); + + GetWithdrawInfoFile(_coincenterInfo.dataDir()).write(dataStr); } WithdrawalFeesCrawler::WithdrawalInfoMaps WithdrawalFeesCrawler::WithdrawalFeesFunc::get1( std::string_view exchangeName) { string path(exchangeName); path.append(".json"); - std::string_view withdrawalFeesCsv = _curlHandle1.query(path, CurlOptions(HttpRequestType::kGet)); + std::string_view dataStr = _curlHandle1.query(path, CurlOptions(HttpRequestType::kGet)); WithdrawalInfoMaps ret; - if (!withdrawalFeesCsv.empty()) { - static constexpr bool kAllowExceptions = false; - const json::container jsonData = json::container::parse(withdrawalFeesCsv, nullptr, kAllowExceptions); - const auto exchangesIt = jsonData.find("exchange"); - if (jsonData.is_discarded() || exchangesIt == jsonData.end()) { - log::error("no exchange data found in source 1 - either site information unavailable or code to be updated"); - return ret; - } - const auto feesIt = exchangesIt->find("fees"); - if (feesIt == exchangesIt->end() || !feesIt->is_array()) { - log::error("no fees data found in source 1 - either site information unavailable or code to be updated"); - return ret; - } + schema::WithdrawFeesCrawlerSource1 withdrawalFeesCrawlerSource1; + ReadPartialJson(dataStr, "withdraw fees crawler service's first source", withdrawalFeesCrawlerSource1); - for (const json::container& feeJson : *feesIt) { - const auto amountIt = feeJson.find("amount"); - if (amountIt == feeJson.end() || !amountIt->is_number_float()) { - continue; - } + if (withdrawalFeesCrawlerSource1.exchange.fees.empty()) { + log::error("no fees data found in source 1 - either site information unavailable or code to be updated"); + return ret; + } - const auto coinIt = feeJson.find("coin"); - if (coinIt == feeJson.end()) { - continue; - } - const auto symbolIt = coinIt->find("symbol"); - if (symbolIt == coinIt->end() || !symbolIt->is_string()) { - continue; - } + for (const schema::WithdrawFeesCrawlerExchangeFeesSource1& fee : withdrawalFeesCrawlerSource1.exchange.fees) { + if (fee.coin.symbol.size() > CurrencyCode::kMaxLen) { + log::warn("Skipping {} withdrawal fees parsing from first source: symbol too long", fee.coin.symbol); + continue; + } - MonetaryAmount withdrawalFee(amountIt->get(), symbolIt->get()); - log::trace("Updated {} withdrawal fee {} from first source", exchangeName, withdrawalFee); - ret.first.insert(withdrawalFee); + CurrencyCode cur{fee.coin.symbol}; - const auto minWithdrawalIt = feeJson.find("min"); - if (minWithdrawalIt == feeJson.end() || !minWithdrawalIt->is_number_float()) { - continue; - } + MonetaryAmount withdrawalFee(fee.amount, cur); + log::trace("Updated {} withdrawal fee {} from first source", exchangeName, withdrawalFee); + ret.first.insert(withdrawalFee); - MonetaryAmount minWithdrawal(minWithdrawalIt->get(), symbolIt->get()); + MonetaryAmount minWithdrawal(fee.min, cur); - log::trace("Updated {} min withdrawal {} from first source", exchangeName, minWithdrawal); - ret.second.insert_or_assign(minWithdrawal.currencyCode(), minWithdrawal); - } + log::trace("Updated {} min withdrawal {} from first source", exchangeName, minWithdrawal); + ret.second.insert_or_assign(minWithdrawal.currencyCode(), minWithdrawal); } if (ret.first.empty() || ret.second.empty()) { diff --git a/src/api/common/test/fiatconverter_test.cpp b/src/api/common/test/fiatconverter_test.cpp index 350becce..f928ec6a 100644 --- a/src/api/common/test/fiatconverter_test.cpp +++ b/src/api/common/test/fiatconverter_test.cpp @@ -86,7 +86,7 @@ std::string_view CurlHandle::query([[maybe_unused]] std::string_view endpoint, c } } if (res.val != 0) { - _queryData = WriteJsonOrThrow(response); + _queryData = WriteMiniJsonOrThrow(response); } } else { @@ -96,7 +96,7 @@ std::string_view CurlHandle::query([[maybe_unused]] std::string_view endpoint, c response.rates["SUSHI"] = 36.78; response.rates["KRW"] = 1341.88; response.rates["NOK"] = 11.3375; - _queryData = WriteJsonOrThrow(response); + _queryData = WriteMiniJsonOrThrow(response); } return _queryData; diff --git a/src/api/exchanges/src/binanceprivateapi.cpp b/src/api/exchanges/src/binanceprivateapi.cpp index 12c4a75c..7c3b9ee0 100644 --- a/src/api/exchanges/src/binanceprivateapi.cpp +++ b/src/api/exchanges/src/binanceprivateapi.cpp @@ -238,7 +238,7 @@ T PrivateQuery(CurlHandle& curlHandle, const APIKey& apiKey, HttpRequestType req } if (throwIfError) { std::string_view errorMsg; - string jsonStr = WriteJsonOrThrow(ret); + string jsonStr = WriteMiniJsonOrThrow(ret); if constexpr (amc::is_detected::value) { if (ret.msg) { errorMsg = *ret.msg; diff --git a/src/http-request/include/request-retry.hpp b/src/http-request/include/request-retry.hpp index df7589b0..7a77a53b 100644 --- a/src/http-request/include/request-retry.hpp +++ b/src/http-request/include/request-retry.hpp @@ -69,7 +69,7 @@ class RequestRetry { if constexpr (std::is_same_v) { strContent = ret.dump(); } else { - strContent = WriteJsonOrThrow(ret); + strContent = WriteMiniJsonOrThrow(ret); } log::warn("Got query error: '{}' for {}, retry {}/{} after {}", strContent, endpoint, nbRetries, _queryRetryPolicy.nbMaxRetries, DurationToString(sleepingTime)); diff --git a/src/objects/src/coincenterinfo.cpp b/src/objects/src/coincenterinfo.cpp index 85d7fc3d..583caade 100644 --- a/src/objects/src/coincenterinfo.cpp +++ b/src/objects/src/coincenterinfo.cpp @@ -31,13 +31,13 @@ namespace { CoincenterInfo::CurrencyEquivalentAcronymMap ComputeCurrencyEquivalentAcronymMap(const Reader& reader) { CoincenterInfo::CurrencyEquivalentAcronymMap map; - ReadJsonOrThrow(reader.readAll(), map); + ReadExactJsonOrThrow(reader.readAll(), map); return map; } CoincenterInfo::StableCoinsMap ComputeStableCoinsMap(const Reader& reader) { CoincenterInfo::StableCoinsMap map; - ReadJsonOrThrow(reader.readAll(), map); + ReadExactJsonOrThrow(reader.readAll(), map); return map; } @@ -64,7 +64,7 @@ CoincenterInfo::CoincenterInfo(settings::RunMode runMode, const LoadConfiguratio ? new MetricGatewayType(monitoringInfo) : nullptr), _monitoringInfo(std::move(monitoringInfo)) { - ReadJsonOrThrow(currencyPrefixesReader.readAll(), _currencyPrefixAcronymMap); + ReadExactJsonOrThrow(currencyPrefixesReader.readAll(), _currencyPrefixAcronymMap); for (auto& [prefix, acronym_prefix] : _currencyPrefixAcronymMap) { log::trace("Currency prefix {} <=> {}", prefix, acronym_prefix); _minPrefixLen = std::min(_minPrefixLen, static_cast(prefix.length())); diff --git a/src/schema/include/read-json.hpp b/src/schema/include/read-json.hpp index f6ee81cc..47043c81 100644 --- a/src/schema/include/read-json.hpp +++ b/src/schema/include/read-json.hpp @@ -1,19 +1,52 @@ #pragma once +#include +#include + #include "cct_exception.hpp" #include "cct_json-serialization.hpp" +#include "cct_log.hpp" #include "file.hpp" #include "reader.hpp" #include "write-json.hpp" namespace cct { -namespace { -constexpr auto kJsonOptions = - json::opts{.error_on_const_read = true, .raw_string = true}; // NOLINT(readability-implicit-bool-conversion) +static constexpr auto kExactJsonOptions = + json::opts{.error_on_unknown_keys = true, // NOLINT(readability-implicit-bool-conversion) + .error_on_const_read = true, // NOLINT(readability-implicit-bool-conversion) + .raw_string = true}; // NOLINT(readability-implicit-bool-conversion) + +static constexpr auto kPartialJsonOptions = + json::opts{.error_on_unknown_keys = false, // NOLINT(readability-implicit-bool-conversion) + .error_on_const_read = true, // NOLINT(readability-implicit-bool-conversion) + .raw_string = true}; // NOLINT(readability-implicit-bool-conversion) + +template +json::error_ctx ReadJson(std::string_view strContent, std::string_view serviceName, auto &outObject) { + if (strContent.empty()) { + return json::error_ctx{}; + } + + auto ec = json::read(outObject, strContent); + + if (ec) { + std::string_view prefixJsonContent = strContent.substr(0, std::min(strContent.size(), 20)); + log::error("Error while reading {} json content '{}{}': {}", serviceName, prefixJsonContent, + prefixJsonContent.size() < strContent.size() ? "..." : "", json::format_error(ec, strContent)); + } + + return ec; } -template +/** + * Read json content from a string ignoring unknown keys + */ +json::error_ctx ReadPartialJson(std::string_view strContent, std::string_view serviceName, auto &outObject) { + return ReadJson(strContent, serviceName, outObject); +} + +template void ReadJsonOrThrow(std::string_view strContent, auto &outObject) { if (strContent.empty()) { return; @@ -28,25 +61,32 @@ void ReadJsonOrThrow(std::string_view strContent, auto &outObject) { } } -template +/** + * Read json content from a string raising an error for unknown keys + */ +void ReadExactJsonOrThrow(std::string_view strContent, auto &outObject) { + ReadJsonOrThrow(strContent, outObject); +} + +template T ReadJsonOrThrow(std::string_view strContent) { T outObject; ReadJsonOrThrow(strContent, outObject); return outObject; } -template +template T ReadJsonOrThrow(const Reader &reader) { return ReadJsonOrThrow(reader.readAll()); } -template +template T ReadJsonOrCreateFile(const File &file) { T outObject; if (file.exists()) { ReadJsonOrThrow(file.readAll(), outObject); } else { - file.write(WriteJsonOrThrow(outObject)); + file.write(WritePrettyJsonOrThrow(outObject)); } return outObject; } diff --git a/src/schema/include/write-json.hpp b/src/schema/include/write-json.hpp index cd1a1abd..123470e4 100644 --- a/src/schema/include/write-json.hpp +++ b/src/schema/include/write-json.hpp @@ -6,12 +6,22 @@ namespace cct { -template +static constexpr auto kPrettifyJsonOptions = + json::opts{.prettify = true, // NOLINT(readability-implicit-bool-conversion) + .indentation_width = 2, // NOLINT(readability-implicit-bool-conversion) + .raw_string = true}; // NOLINT(readability-implicit-bool-conversion) + +static constexpr auto kMinifiedJsonOptions = + json::opts{.prettify = false, // NOLINT(readability-implicit-bool-conversion) + .minified = true, // NOLINT(readability-implicit-bool-conversion) + .raw_string = true}; // NOLINT(readability-implicit-bool-conversion) + +template string WriteJsonOrThrow(const auto &obj) { string buf; // NOLINTNEXTLINE(readability-implicit-bool-conversion) - auto ec = json::write(obj, buf); + auto ec = json::write(obj, buf); if (ec) { throw exception("Error while writing json content: {}", format_error(ec, buf)); @@ -20,4 +30,8 @@ string WriteJsonOrThrow(const auto &obj) { return buf; } +string WriteMiniJsonOrThrow(const auto &obj) { return WriteJsonOrThrow(obj); } + +string WritePrettyJsonOrThrow(const auto &obj) { return WriteJsonOrThrow(obj); } + } // namespace cct \ No newline at end of file diff --git a/src/schema/src/exchange-config.cpp b/src/schema/src/exchange-config.cpp index ba82a874..8b6afbff 100644 --- a/src/schema/src/exchange-config.cpp +++ b/src/schema/src/exchange-config.cpp @@ -26,7 +26,7 @@ AllExchangeConfigs::AllExchangeConfigs(const LoadConfiguration &loadConfiguratio auto allExchangesConfigOptional = ReadJsonOrThrow(ExchangeConfigDefault::kProd); - WriteJsonOrThrow(allExchangesConfigOptional); + WritePrettyJsonOrThrow(allExchangesConfigOptional); mergeWith(allExchangesConfigOptional); } else { diff --git a/src/schema/test/general-config_test.cpp b/src/schema/test/general-config_test.cpp index 90b29ec0..9e0926ac 100644 --- a/src/schema/test/general-config_test.cpp +++ b/src/schema/test/general-config_test.cpp @@ -8,12 +8,12 @@ namespace cct { TEST(GeneralConfig, WriteMinified) { EXPECT_EQ( - WriteJsonOrThrow(schema::GeneralConfig{}), + WriteMiniJsonOrThrow(schema::GeneralConfig{}), R"({"apiOutputType":"table","fiatConversion":{"rate":"8h"},"log":{"activityTracking":{"commandTypes":["Trade","Buy","Sell","Withdraw","DustSweeper"],"dateFileNameFormat":"%Y-%m","withSimulatedCommands":false},"consoleLevel":"info","fileLevel":"debug","maxFileSize":"5Mi","maxNbFiles":20},"requests":{"concurrency":{"nbMaxParallelRequests":1}},"trading":{"automation":{"deserialization":{"loadChunkDuration":"1w"},"startingContext":{"startBaseAmountEquivalent":"1000 EUR","startQuoteAmountEquivalent":"1000 EUR"}}}})"); } TEST(GeneralConfig, WriteFormatted) { - EXPECT_EQ(WriteJsonOrThrow(schema::GeneralConfig{}), + EXPECT_EQ(WritePrettyJsonOrThrow(schema::GeneralConfig{}), R"({ "apiOutputType": "table", "fiatConversion": { diff --git a/src/tech/include/cct_json-serialization.hpp b/src/tech/include/cct_json-serialization.hpp index 52ddda41..f2f62328 100644 --- a/src/tech/include/cct_json-serialization.hpp +++ b/src/tech/include/cct_json-serialization.hpp @@ -4,6 +4,7 @@ namespace cct::json { +using glz::error_ctx; using glz::format_error; using glz::meta; using glz::opts;