From d86260ba2d459b39befbd60d6bc4d048a6648737 Mon Sep 17 00:00:00 2001 From: emeric Date: Fri, 20 Oct 2023 15:22:00 +0200 Subject: [PATCH] Replaced Json parser with a custom one (optims+compact output) --- src/libs/subsonic/impl/SubsonicResource.cpp | 3 +- src/libs/subsonic/impl/SubsonicResponse.cpp | 270 +++++++++++------- src/libs/subsonic/impl/SubsonicResponse.hpp | 62 ++-- .../impl/entrypoints/AlbumSongLists.cpp | 4 +- .../subsonic/impl/entrypoints/Browsing.cpp | 4 +- .../impl/entrypoints/MediaLibraryScanning.cpp | 3 +- src/libs/subsonic/impl/responses/Album.cpp | 6 +- src/libs/subsonic/impl/responses/Song.cpp | 4 +- src/libs/utils/impl/StreamLogger.cpp | 13 +- src/libs/utils/impl/String.cpp | 24 +- src/libs/utils/impl/WtLogger.cpp | 17 +- src/libs/utils/include/utils/Logger.hpp | 84 +++--- src/libs/utils/include/utils/String.hpp | 3 +- 13 files changed, 316 insertions(+), 181 deletions(-) diff --git a/src/libs/subsonic/impl/SubsonicResource.cpp b/src/libs/subsonic/impl/SubsonicResource.cpp index fe432a81..c38bc0d0 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -290,10 +290,11 @@ namespace API::Subsonic Response resp{ (itEntryPoint->second.func)(requestContext) }; + LMS_LOG(API_SUBSONIC, DEBUG) << "Request " << requestId << " '" << requestPath << "' handled!"; resp.write(response.out(), format); response.setMimeType(std::string{ ResponseFormatToMimeType(format) }); + LMS_LOG(API_SUBSONIC, DEBUG) << "Request " << requestId << " '" << requestPath << "' written!"; - LMS_LOG(API_SUBSONIC, DEBUG) << "Request " << requestId << " '" << requestPath << "' handled!"; return; } diff --git a/src/libs/subsonic/impl/SubsonicResponse.cpp b/src/libs/subsonic/impl/SubsonicResponse.cpp index 3f177a6d..ec6e6505 100644 --- a/src/libs/subsonic/impl/SubsonicResponse.cpp +++ b/src/libs/subsonic/impl/SubsonicResponse.cpp @@ -20,12 +20,8 @@ #include "SubsonicResponse.hpp" #include -#include -#include -#include -#include - -#include +#include +#include #include #include "utils/Exception.hpp" @@ -57,59 +53,66 @@ namespace API::Subsonic _value = value; } - void Response::Node::setAttribute(std::string_view key, std::string_view value) + void Response::Node::setAttribute(Key key, std::string_view value) { - _attributes[std::string{ key }] = std::string{ value }; + _attributes[key] = std::string{ value }; } - void Response::Node::addChild(const std::string& key, Node node) + void Response::Node::addChild(Key key, Node node) { assert(!_value); - _children[key].emplace_back(std::move(node)); + assert(_children.find(key) == std::cend(_children)); + _children[key] = std::move(node); } - void Response::Node::createEmptyArrayChild(std::string_view key) + void Response::Node::createEmptyArrayChild(Key key) { assert(!_value); + assert(_children.find(key) == std::cend(_children)); _childrenArrays.emplace(key, std::vector{}); } - void Response::Node::addArrayChild(std::string_view key, Node node) + void Response::Node::addArrayChild(Key key, Node node) { assert(!_value); - _childrenArrays[std::string{ key }].emplace_back(std::move(node)); + assert(_children.find(key) == std::cend(_children)); + _childrenArrays[key].emplace_back(std::move(node)); } - void Response::Node::createEmptyArrayValue(std::string_view key) + void Response::Node::createEmptyArrayValue(Key key) { assert(!_value); + assert(_children.find(key) == std::cend(_children)); _childrenValues.emplace(key, ValuesType{}); } - void Response::Node::addArrayValue(std::string_view key, std::string_view value) + void Response::Node::addArrayValue(Key key, std::string_view value) { assert(!_value); - auto& values{ _childrenValues[std::string{ key }] }; + assert(_children.find(key) == std::cend(_children)); + auto& values{ _childrenValues[key] }; values.push_back(std::string{ value }); assert(std::all_of(std::cbegin(values) + 1, std::cend(values), [&](const ValueType& value) {return value.index() == values.front().index();})); } - void Response::Node::addArrayValue(std::string_view key, long long value) + void Response::Node::addArrayValue(Key key, long long value) { assert(!_value); - auto& values{ _childrenValues[std::string{ key }] }; + auto& values{ _childrenValues[key] }; values.push_back(value); assert(std::all_of(std::cbegin(values) + 1, std::cend(values), [&](const ValueType& value) {return value.index() == values.front().index();})); } - Response::Node& Response::Node::createChild(const std::string& key) + Response::Node& Response::Node::createChild(Key key) { - _children[key].emplace_back(); - return _children[key].back(); + assert(!_value); + return _children[key]; } - Response::Node& Response::Node::createArrayChild(const std::string& key) + Response::Node& Response::Node::createArrayChild(Key key) { + assert(!_value); + assert(_children.find(key) == std::cend(_children)); _childrenArrays[key].emplace_back(); return _childrenArrays[key].back(); } @@ -152,19 +155,19 @@ namespace API::Subsonic return response; } - void Response::addNode(const std::string& key, Node node) + void Response::addNode(Node::Key key, Node node) { - return _root._children["subsonic-response"].front().addChild(key, std::move(node)); + return _root._children["subsonic-response"].addChild(key, std::move(node)); } - Response::Node& Response::createNode(const std::string& key) + Response::Node& Response::createNode(Node::Key key) { - return _root._children["subsonic-response"].front().createChild(key); + return _root._children["subsonic-response"].createChild(key); } - Response::Node& Response::createArrayNode(const std::string& key) + Response::Node& Response::createArrayNode(Node::Key key) { - return _root._children["subsonic-response"].front().createArrayChild(key); + return _root._children["subsonic-response"].createArrayChild(key); } void Response::write(std::ostream& os, ResponseFormat format) @@ -186,16 +189,16 @@ namespace API::Subsonic { boost::property_tree::ptree res; - for (auto itAttribute : node._attributes) + for (const auto& [key, value] : node._attributes) { - if (std::holds_alternative(itAttribute.second)) - res.put("." + itAttribute.first, std::get(itAttribute.second)); - else if (std::holds_alternative(itAttribute.second)) - res.put("." + itAttribute.first, std::get(itAttribute.second)); - else if (std::holds_alternative(itAttribute.second)) - res.put("." + itAttribute.first, std::get(itAttribute.second)); - else if (std::holds_alternative(itAttribute.second)) - res.put("." + itAttribute.first, std::get(itAttribute.second)); + if (std::holds_alternative(value)) + res.put("." + std::string{ key.get() }, std::get(value)); + else if (std::holds_alternative(value)) + res.put("." + std::string{ key.get() }, std::get(value)); + else if (std::holds_alternative(value)) + res.put("." + std::string{ key.get() }, std::get(value)); + else if (std::holds_alternative(value)) + res.put("." + std::string{ key.get() }, std::get(value)); } auto valueToPropertyTree = [](const Node::ValueType& value) @@ -215,22 +218,21 @@ namespace API::Subsonic } else { - for (const auto& [key, childNodes] : node._children) + for (const auto& [key, childNode] : node._children) { - for (const Node& childNode : childNodes) - res.add_child(key, nodeToPropertyTree(childNode)); + res.add_child(std::string{ key.get() }, nodeToPropertyTree(childNode)); } for (const auto& [key, childArrayNodes] : node._childrenArrays) { for (const Node& childNode : childArrayNodes) - res.add_child(key, nodeToPropertyTree(childNode)); + res.add_child(std::string{ key.get() }, nodeToPropertyTree(childNode)); } for (const auto& [key, childArrayValues] : node._childrenValues) { for (const Response::Node::ValueType& value : childArrayValues) - res.add_child(key, valueToPropertyTree(value)); + res.add_child(std::string{ key.get() }, valueToPropertyTree(value)); } } @@ -241,63 +243,137 @@ namespace API::Subsonic boost::property_tree::write_xml(os, root); } + void Response::JsonSerializer::serializeNode(std::ostream& os, const Response::Node& node) + { + os << '{'; + + bool first{ true }; + + for (const auto& [key, value] : node._attributes) + { + if (!first) + os << ','; + + serializeEscapedString(os, key.get()); + os << ':'; + serializeValue(os, value); + + first = false; + } + + if (node._value) + { + if (!first) + os << ','; + + os << "value:"; + serializeValue(os, *node._value); + + first = false; + } + else + { + for (const auto& [key, childNode] : node._children) + { + if (!first) + os << ','; + + serializeEscapedString(os, key.get()); + os << ':'; + serializeNode(os, childNode); + + first = false; + } + + + for (const auto& [key, childArrayNodes] : node._childrenArrays) + { + if (!first) + os << ','; + + serializeEscapedString(os, key.get()); + os << ":["; + + bool firstChild{ true }; + for (const Response::Node& childNode : childArrayNodes) + { + if (!firstChild) + os << ","; + + serializeNode(os, childNode); + firstChild = false; + } + os << ']'; + + first = false; + } + + for (const auto& [key, childValues] : node._childrenValues) + { + if (!first) + os << ','; + + serializeEscapedString(os, key.get()); + os << ":["; + + bool firstChild{ true }; + for (const Node::ValueType& childValue : childValues) + { + if (!firstChild) + os << ","; + + serializeValue(os, childValue); + + firstChild = false; + } + os << ']'; + + first = false; + } + } + + os << '}'; + } + + void Response::JsonSerializer::serializeValue(std::ostream& os, const Node::ValueType& value) + { + if (std::holds_alternative(value)) + { + serializeEscapedString(os, std::get(value)); + } + else if (std::holds_alternative(value)) + { + os << (std::get(value) ? "true" : "false"); + } + else if (std::holds_alternative(value)) + { + const float d{ std::get(value) }; + if (std::isnan(d) || std::fabs(d) == std::numeric_limits::infinity()) + os << "null"; + else + os << d; + } + else if (std::holds_alternative(value)) + { + os << std::get(value); + } + else + { + assert(false); + } + } + + void Response::JsonSerializer::serializeEscapedString(std::ostream& os, std::string_view str) + { + os << '\"'; + StringUtils::writeJSEscapedString(os, str); + os << '\"'; + } + void Response::writeJSON(std::ostream& os) { - namespace Json = Wt::Json; - - std::function nodeToJsonObject = [&](const Response::Node& node) - { - Json::Object res; - - auto valueToJsonValue{ [](const Node::ValueType& value) -> Json::Value - { - Json::Value res; - std::visit([&](const auto& rawValue) - { - res = Json::Value{ rawValue }; - }, value); - return res; - } }; - - for (auto itAttribute : node._attributes) - res[itAttribute.first] = valueToJsonValue(itAttribute.second); - - if (node._value) - { - res["value"] = valueToJsonValue(*node._value); - } - else - { - for (const auto& [key, childNodes] : node._children) - { - for (const Response::Node& childNode : childNodes) - res[key] = nodeToJsonObject(childNode); - } - - for (const auto& [key, childArrayNodes] : node._childrenArrays) - { - Json::Array array; - for (const Response::Node& childNode : childArrayNodes) - array.emplace_back(nodeToJsonObject(childNode)); - - res[key] = std::move(array); - } - - for (const auto& [key, childValues] : node._childrenValues) - { - Json::Array array; - for (const Node::ValueType& childValue : childValues) - array.emplace_back(valueToJsonValue(childValue)); - - res[key] = std::move(array); - } - } - - return res; - }; - - Json::Object root{ nodeToJsonObject(_root) }; - os << Json::serialize(root); + JsonSerializer serializer; + serializer.serializeNode(os, _root); } } // namespace diff --git a/src/libs/subsonic/impl/SubsonicResponse.hpp b/src/libs/subsonic/impl/SubsonicResponse.hpp index 64046ac0..119630ce 100644 --- a/src/libs/subsonic/impl/SubsonicResponse.hpp +++ b/src/libs/subsonic/impl/SubsonicResponse.hpp @@ -191,17 +191,30 @@ namespace API::Subsonic class Node { public: - void setAttribute(std::string_view key, std::string_view value); + class Key + { + public: + template + constexpr Key(const char (&str)[N]) : _str{ str } {} + constexpr std::string_view get() const { return _str; } + + bool constexpr operator<(const Key& other) const { return _str < other._str; } + + private: + const std::string_view _str; + }; + + void setAttribute(Key key, std::string_view value); template ::value>* = nullptr> - void setAttribute(std::string_view key, T value) + void setAttribute(Key key, T value) { if constexpr (std::is_same::value) - _attributes[std::string{ key }] = value; + _attributes[key] = value; else if constexpr (std::is_floating_point::value) - _attributes[std::string{ key }] = static_cast(value); + _attributes[key] = static_cast(value); else if constexpr (std::is_integral::value) - _attributes[std::string{ key }] = static_cast(value); + _attributes[key] = static_cast(value); else static_assert("Unhandled type"); } @@ -209,28 +222,28 @@ namespace API::Subsonic // A Node has either a single value or an array of values or some children void setValue(std::string_view value); void setValue(long long value); - Node& createChild(const std::string& key); - Node& createArrayChild(const std::string& key); + Node& createChild(Key key); + Node& createArrayChild(Key key); - void addChild(const std::string& key, Node node); - void createEmptyArrayChild(std::string_view key); - void addArrayChild(std::string_view key, Node node); - void createEmptyArrayValue(std::string_view key); - void addArrayValue(std::string_view key, std::string_view value); - void addArrayValue(std::string_view key, long long value); + void addChild(Key key, Node node); + void createEmptyArrayChild(Key key); + void addArrayChild(Key key, Node node); + void createEmptyArrayValue(Key key); + void addArrayValue(Key key, std::string_view value); + void addArrayValue(Key key, long long value); private: void setVersionAttribute(ProtocolVersion version); friend class Response; using ValueType = std::variant; - std::map _attributes; + std::map _attributes; std::optional _value; - std::map> _children; - std::map> _childrenArrays; + std::map _children; + std::map> _childrenArrays; using ValuesType = std::vector; - std::map _childrenValues; + std::map _childrenValues; }; static Response createOkResponse(ProtocolVersion protocolVersion); @@ -242,14 +255,23 @@ namespace API::Subsonic Response(Response&&) = default; Response& operator=(Response&&) = default; - void addNode(const std::string& key, Node node); - Node& createNode(const std::string& key); - Node& createArrayNode(const std::string& key); + void addNode(Node::Key key, Node node); + Node& createNode(Node::Key key); + Node& createArrayNode(Node::Key key); void write(std::ostream& os, ResponseFormat format); private: static Response createResponseCommon(ProtocolVersion protocolVersion, const Error* error = nullptr); + + class JsonSerializer + { + public: + void serializeNode(std::ostream& os, const Node& node); + void serializeValue(std::ostream& os, const Node::ValueType& node); + void serializeEscapedString(std::ostream&, std::string_view str); + }; + void writeJSON(std::ostream& os); void writeXML(std::ostream& os); diff --git a/src/libs/subsonic/impl/entrypoints/AlbumSongLists.cpp b/src/libs/subsonic/impl/entrypoints/AlbumSongLists.cpp index e0b096db..2cb08ca0 100644 --- a/src/libs/subsonic/impl/entrypoints/AlbumSongLists.cpp +++ b/src/libs/subsonic/impl/entrypoints/AlbumSongLists.cpp @@ -133,7 +133,7 @@ namespace API::Subsonic throw NotImplementedGenericError{}; Response response{ Response::createOkResponse(context.serverProtocolVersion) }; - Response::Node& albumListNode{ response.createNode(id3 ? "albumList2" : "albumList") }; + Response::Node& albumListNode{ response.createNode(id3 ? Response::Node::Key{ "albumList2" } : Response::Node::Key{ "albumList" }) }; for (const ReleaseId releaseId : releases.results) { @@ -153,7 +153,7 @@ namespace API::Subsonic throw UserNotAuthorizedError{}; Response response{ Response::createOkResponse(context.serverProtocolVersion) }; - Response::Node& starredNode{ response.createNode(id3 ? "starred2" : "starred") }; + Response::Node& starredNode{ response.createNode(id3 ? Response::Node::Key{ "starred2" } : Response::Node::Key{ "starred" }) }; Scrobbling::IScrobblingService& scrobbling{ *Service::get() }; diff --git a/src/libs/subsonic/impl/entrypoints/Browsing.cpp b/src/libs/subsonic/impl/entrypoints/Browsing.cpp index 44202630..ad525f4e 100644 --- a/src/libs/subsonic/impl/entrypoints/Browsing.cpp +++ b/src/libs/subsonic/impl/entrypoints/Browsing.cpp @@ -54,7 +54,7 @@ namespace API::Subsonic std::size_t count{ getParameterAs(context.parameters, "count").value_or(20) }; Response response{ Response::createOkResponse(context.serverProtocolVersion) }; - Response::Node& artistInfoNode{ response.createNode(id3 ? "artistInfo2" : "artistInfo") }; + Response::Node& artistInfoNode{ response.createNode(id3 ? Response::Node::Key{ "artistInfo2" } : Response::Node::Key{ "artistInfo" }) }; { auto transaction{ context.dbSession.createSharedTransaction() }; @@ -236,7 +236,7 @@ namespace API::Subsonic throw UserNotAuthorizedError{}; Response response{ Response::createOkResponse(context.serverProtocolVersion) }; - Response::Node& similarSongsNode{ response.createNode(id3 ? "similarSongs2" : "similarSongs") }; + Response::Node& similarSongsNode{ response.createNode(id3 ? Response::Node::Key{ "similarSongs2" } : Response::Node::Key{ "similarSongs" }) }; for (const TrackId trackId : tracks) { const Track::pointer track{ Track::find(context.dbSession, trackId) }; diff --git a/src/libs/subsonic/impl/entrypoints/MediaLibraryScanning.cpp b/src/libs/subsonic/impl/entrypoints/MediaLibraryScanning.cpp index b328553e..78f2ea25 100644 --- a/src/libs/subsonic/impl/entrypoints/MediaLibraryScanning.cpp +++ b/src/libs/subsonic/impl/entrypoints/MediaLibraryScanning.cpp @@ -28,8 +28,7 @@ namespace API::Subsonic::Scan namespace { - Response::Node - createStatusResponseNode() + Response::Node createStatusResponseNode() { Response::Node statusResponse; diff --git a/src/libs/subsonic/impl/responses/Album.cpp b/src/libs/subsonic/impl/responses/Album.cpp index effd21f4..0ec6dfb0 100644 --- a/src/libs/subsonic/impl/responses/Album.cpp +++ b/src/libs/subsonic/impl/responses/Album.cpp @@ -114,7 +114,7 @@ namespace API::Subsonic if (artists.size() == 1) { - albumNode.setAttribute(id3 ? "artistId" : "parent", idToString(artists.front()->getId())); + albumNode.setAttribute(id3 ? Response::Node::Key{ "artistId" } : Response::Node::Key{ "parent" }, idToString(artists.front()->getId())); } else { @@ -140,7 +140,7 @@ namespace API::Subsonic { const Wt::WDateTime dateTime{ Service::get()->getLastListenDateTime(user->getId(), release->getId()) }; - albumNode.setAttribute("played", dateTime.isValid() ? StringUtils::toISO8601String(dateTime) : ""); + albumNode.setAttribute("played", dateTime.isValid() ? StringUtils::toISO8601String(dateTime) : std::string{ "" }); } { @@ -148,7 +148,7 @@ namespace API::Subsonic albumNode.setAttribute("musicBrainzId", mbid ? mbid->getAsString() : ""); } - auto addClusters{ [&](std::string_view field, std::string_view clusterTypeName) + auto addClusters{ [&](Response::Node::Key field, std::string_view clusterTypeName) { albumNode.createEmptyArrayValue(field); diff --git a/src/libs/subsonic/impl/responses/Song.cpp b/src/libs/subsonic/impl/responses/Song.cpp index d3ef6a79..c2a0fbc7 100644 --- a/src/libs/subsonic/impl/responses/Song.cpp +++ b/src/libs/subsonic/impl/responses/Song.cpp @@ -186,7 +186,7 @@ namespace API::Subsonic } } - auto addArtistLinks{ [&](std::string_view nodeName, TrackArtistLinkType type) + auto addArtistLinks{ [&](Response::Node::Key nodeName, TrackArtistLinkType type) { trackResponse.createEmptyArrayChild(nodeName); @@ -209,7 +209,7 @@ namespace API::Subsonic if (release) trackResponse.setAttribute("displayAlbumArtist", release->getArtistDisplayName()); - auto addClusters{ [&](std::string_view field, std::string_view clusterTypeName) + auto addClusters{ [&](Response::Node::Key field, std::string_view clusterTypeName) { trackResponse.createEmptyArrayValue(field); diff --git a/src/libs/utils/impl/StreamLogger.cpp b/src/libs/utils/impl/StreamLogger.cpp index 4b1cebd1..cc8efbe6 100644 --- a/src/libs/utils/impl/StreamLogger.cpp +++ b/src/libs/utils/impl/StreamLogger.cpp @@ -17,18 +17,19 @@ * along with LMS. If not, see . */ +#include + #include "utils/StreamLogger.hpp" StreamLogger::StreamLogger(std::ostream& os, EnumSet severities) -: _os {os} -, _severities {severities} + : _os{ os } + , _severities{ severities } { } -void -StreamLogger::processLog(const Log& log) +void StreamLogger::processLog(const Log& log) { - if (_severities.contains(log.getSeverity())) - _os << "[" << getSeverityName(log.getSeverity()) << "] [" << getModuleName(log.getModule()) << "] " << log.getMessage() << std::endl; + if (_severities.contains(log.getSeverity())) + _os << std::this_thread::get_id() << " [" << getSeverityName(log.getSeverity()) << "] [" << getModuleName(log.getModule()) << "] " << log.getMessage() << std::endl; } diff --git a/src/libs/utils/impl/String.cpp b/src/libs/utils/impl/String.cpp index cae5c406..807ec72b 100644 --- a/src/libs/utils/impl/String.cpp +++ b/src/libs/utils/impl/String.cpp @@ -219,7 +219,7 @@ namespace StringUtils return res; } - std::string jsEscape(const std::string& str) + std::string jsEscape(std::string_view str) { static const std::unordered_map escapeMap { @@ -249,6 +249,28 @@ namespace StringUtils return escaped; } + void writeJSEscapedString(std::ostream& os, std::string_view str) + { + static constexpr std::pair charsToEscape[] + { + {'\\', "\\\\" }, + { '\n', "\\n" }, + { '\r', "\\r" }, + { '\t', "\\t" }, + { '"', "\\\"" }, + { '\'', "\\\'" }, + }; + + for (const char c : str) + { + auto itEntry{ std::find_if(std::cbegin(charsToEscape), std::cend(charsToEscape), [=](const auto& entry) { return entry.first == c;}) }; + if (itEntry != std::cend(charsToEscape)) + os << itEntry->second; + else + os << c; + } + } + std::string escapeString(std::string_view str, std::string_view charsToEscape, char escapeChar) { std::string res; diff --git a/src/libs/utils/impl/WtLogger.cpp b/src/libs/utils/impl/WtLogger.cpp index 126af923..ded7522e 100644 --- a/src/libs/utils/impl/WtLogger.cpp +++ b/src/libs/utils/impl/WtLogger.cpp @@ -19,14 +19,25 @@ #include "utils/WtLogger.hpp" +#include +#include #include #include #include "utils/Logger.hpp" -void -WtLogger::processLog(const Log& log) +namespace { - Wt::log(getSeverityName(log.getSeverity())) << Wt::WLogger::sep << "[" << getModuleName(log.getModule()) << "]" << Wt::WLogger::sep << log.getMessage(); + std::string to_string(std::thread::id id) + { + std::ostringstream oss; + oss << id; + return oss.str(); + } +} + +void WtLogger::processLog(const Log& log) +{ + Wt::log(getSeverityName(log.getSeverity())) << Wt::WLogger::sep << to_string(std::this_thread::get_id()) << Wt::WLogger::sep << "[" << getModuleName(log.getModule()) << "]" << Wt::WLogger::sep << log.getMessage(); } diff --git a/src/libs/utils/include/utils/Logger.hpp b/src/libs/utils/include/utils/Logger.hpp index d7faf078..ba36f4ea 100644 --- a/src/libs/utils/include/utils/Logger.hpp +++ b/src/libs/utils/include/utils/Logger.hpp @@ -26,33 +26,33 @@ enum class Severity { - FATAL, - ERROR, - WARNING, - INFO, - DEBUG, + FATAL, + ERROR, + WARNING, + INFO, + DEBUG, }; enum class Module { - API_SUBSONIC, - AUTH, - AV, - CHILDPROCESS, - COVER, - DB, - DBUPDATER, - FEATURE, - HTTP, - MAIN, - METADATA, - REMOTE, - SCROBBLING, - SERVICE, - RECOMMENDATION, - TRANSCODE, - UI, - UTILS, + API_SUBSONIC, + AUTH, + AV, + CHILDPROCESS, + COVER, + DB, + DBUPDATER, + FEATURE, + HTTP, + MAIN, + METADATA, + REMOTE, + SCROBBLING, + SERVICE, + RECOMMENDATION, + TRANSCODE, + UI, + UTILS, }; const char* getModuleName(Module mod); @@ -61,30 +61,32 @@ const char* getSeverityName(Severity sev); class Logger; class Log { - public: - Log(Logger* logger, Module module, Severity severity); - ~Log(); +public: + Log(Logger* logger, Module module, Severity severity); + ~Log(); - Module getModule() const { return _module; } - Severity getSeverity() const { return _severity; } - std::string getMessage() const; + Module getModule() const { return _module; } + Severity getSeverity() const { return _severity; } + std::string getMessage() const; - std::ostringstream& getOstream() { return _oss; } + std::ostringstream& getOstream() { return _oss; } - private: - Module _module; - Severity _severity; - std::ostringstream _oss; - Logger* _logger {}; +private: + Log(const Log&) = delete; + Log& operator=(const Log&) = delete; + + Module _module; + Severity _severity; + std::ostringstream _oss; + Logger* _logger{}; }; class Logger { - public: - virtual ~Logger() = default; - virtual void processLog(const Log& log) = 0; +public: + virtual ~Logger() = default; + virtual void processLog(const Log& log) = 0; }; -#define LMS_LOG(module, severity) Log(Service::get(), Module::module, Severity::severity).getOstream() -#define LMS_LOG_EX(module, severity) Log(Service::get(), module, severity).getOstream() - +#define LMS_LOG(module, severity) Log{Service::get(), Module::module, Severity::severity}.getOstream() +#define LMS_LOG_EX(module, severity) Log{Service::get(), module, severity}.getOstream() diff --git a/src/libs/utils/include/utils/String.hpp b/src/libs/utils/include/utils/String.hpp index 70addee0..03709ac1 100644 --- a/src/libs/utils/include/utils/String.hpp +++ b/src/libs/utils/include/utils/String.hpp @@ -83,7 +83,8 @@ namespace StringUtils { [[nodiscard]] std::string replaceInString(std::string_view str, const std::string& from, const std::string& to); - [[nodiscard]] std::string jsEscape(const std::string& str); + [[nodiscard]] std::string jsEscape(std::string_view str); + void writeJSEscapedString(std::ostream& os, std::string_view str); [[nodiscard]] std::string escapeString(std::string_view str, std::string_view charsToEscape, char escapeChar);