#include "release_info.h" #include namespace roro::release { namespace { bool endsWith(const std::string& s, const char* tail) { size_t n = std::char_traits::length(tail); return s.size() >= n && s.compare(s.size() - n, n, tail) == 0; } } // namespace std::string firstParagraph(const std::string& text, bool truncated) { size_t end = text.find("\n\n"); size_t crlf = text.find("\r\n\r\n"); if (crlf != std::string::npos && (end == std::string::npos || crlf < end)) end = crlf; std::string p = text.substr(0, end); size_t first = p.find_first_not_of(" \t\r\n"); if (first == std::string::npos) return ""; p.erase(0, first); while (!p.empty() && (p.back() == ' ' || p.back() == '\t' || p.back() == '\r' || p.back() == '\n')) p.pop_back(); if (truncated && end == std::string::npos) p += "..."; return p; } ReleaseReader::ReleaseReader(size_t maxReleases) : scanner_([this](const std::string& path, const std::string& text, bool isString, bool truncated) { value(path, text, isString, truncated); }), max_(maxReleases) {} void ReleaseReader::end() { flushAsset(); } void ReleaseReader::flushAsset() { if (assetRelease_ >= 0 && assetRelease_ < static_cast(releases_.size()) && endsWith(assetName_, ".ota") && !assetUrl_.empty()) { releases_[assetRelease_].otaUrl = assetUrl_; releases_[assetRelease_].otaSize = assetSize_; } assetRelease_ = assetIndex_ = -1; assetName_.clear(); assetUrl_.clear(); assetSize_ = 0; } void ReleaseReader::value(const std::string& path, const std::string& text, bool isString, bool truncated) { size_t index = 0; std::string rest = path; if (!path.empty() && path[0] == '[') { // a list: "[2].tag_name" char* end; index = static_cast(std::strtol(path.c_str() + 1, &end, 10)); rest = *end == ']' ? std::string(end + 1) : ""; if (!rest.empty() && rest[0] == '.') rest.erase(0, 1); } if (index >= max_) return; if (releases_.size() <= index) releases_.resize(index + 1); Release& r = releases_[index]; if (rest == "tag_name") r.tag = text; else if (rest == "published_at") r.published = text; else if (rest == "body") r.notes = firstParagraph(text, truncated); else if (rest == "draft") r.draft = text == "true"; else if (rest == "prerelease") r.prerelease = text == "true"; else if (rest.compare(0, 7, "assets[") == 0) { char* end; int j = static_cast(std::strtol(rest.c_str() + 7, &end, 10)); if (static_cast(index) != assetRelease_ || j != assetIndex_) { flushAsset(); assetRelease_ = static_cast(index); assetIndex_ = j; } std::string field = *end == ']' && end[1] == '.' ? std::string(end + 2) : ""; if (field == "name") assetName_ = text; else if (field == "size") assetSize_ = static_cast(std::strtoul(text.c_str(), nullptr, 10)); else if (field == "browser_download_url") assetUrl_ = text; } (void)isString; } } // namespace roro::release