#include "../include/Package.hpp" #include "../include/curl.hpp" #include "../external/rapidjson/error/en.h" #ifdef GetObject #undef GetObject #endif using namespace std; using namespace rapidjson; static Document *packageList = nullptr; namespace sibs { static vector getPlatformsByNames(const vector &platformNames) { vector result; result.reserve(platformNames.size()); for(const string &platformName : platformNames) { result.push_back(getPlatformByName(StringView(platformName.data(), platformName.size()))); } return result; } static Result getPackageMetadata(Value::ConstObject jsonObj) { const auto &description = jsonObj.FindMember("description"); if(description == jsonObj.MemberEnd() || !description->value.IsString()) return Result::Err("Expected description to be a string"); const auto &version = jsonObj.FindMember("version"); if(version == jsonObj.MemberEnd() || !version->value.IsString()) return Result::Err("Expected version to be a string"); const auto &platforms = jsonObj.FindMember("platforms"); if(platforms == jsonObj.MemberEnd() || !platforms->value.IsArray() || platforms->value.GetArray().Empty()) return Result::Err("Expected platforms to be an array of strings"); const auto &urls = jsonObj.FindMember("urls"); if(urls == jsonObj.MemberEnd() || !urls->value.IsArray() || urls->value.GetArray().Empty()) return Result::Err("Expected urls to be an array of string"); PackageMetadata packageMetadata; packageMetadata.description.assign(description->value.GetString(), description->value.GetStringLength()); packageMetadata.version.assign(version->value.GetString(), version->value.GetStringLength()); const auto &platformsArray = platforms->value.GetArray(); packageMetadata.platforms.reserve(platformsArray.Size()); for(int i = 0; i < platformsArray.Size(); ++i) { const auto &platformElement = platformsArray[i]; if(!platformElement.IsString()) return Result::Err("Expected platforms to only contain strings"); packageMetadata.platforms.push_back(platformElement.GetString()); } const auto &urlsArray = urls->value.GetArray(); packageMetadata.urls.reserve(urlsArray.Size()); for(int i = 0; i < urlsArray.Size(); ++i) { const auto &urlElement = urlsArray[i]; if(!urlElement.IsString()) return Result::Err("Expected urls to only contain strings"); packageMetadata.urls.push_back(urlElement.GetString()); } return Result::Ok(packageMetadata); } static Result getPackageUrl(const PackageMetadata &packageMetadata, const char *packageName, const char *packageVersion, Platform platform) { if(strcmp(packageMetadata.version.c_str(), packageVersion) != 0) { string errMsg = "Package \""; errMsg += packageName; errMsg += "\" does not exist for version \""; errMsg += packageVersion; errMsg += "\""; return Result::Err(errMsg); } if(!containsPlatform(getPlatformsByNames(packageMetadata.platforms), platform)) { string errMsg = "Package \""; errMsg += packageName; errMsg += "\" with version \""; errMsg += packageVersion; errMsg += "\" does not support platform \""; errMsg += platform; errMsg += "\""; return Result::Err(errMsg); } return Result::Ok(packageMetadata.urls[0]); } // TODO: Always downloading is fine right now because the package list is small. This should later be modified to use local cache. // The package file should be stored locally (at ~/.cache/sibs/packages.json) and the version file should also be stored. // First we check if the version is incorrect and if it, we download the new packages.json file. // We should only check if the package list is up to date once every 10 minute or so (make it configurable in a config file?) // to improve build performance and reduce server load. // Or maybe we dont have to do that at all.... Result Package::getPackageList(const char *url) { if(packageList) return Result::Ok(packageList); HttpResult httpResult = curl::get(url); if(!httpResult.success) return Result::Err(httpResult.str, httpResult.httpCode); Document *doc = new Document(); ParseResult parseResult = doc->Parse(httpResult.str.c_str()); if(!parseResult) { string errMsg = "Failed to parse package list json file: "; errMsg += GetParseError_En(parseResult.Code()); errMsg += " ("; errMsg += to_string(parseResult.Offset()); errMsg += ")"; return Result::Err(errMsg); } packageList = doc; return Result::Ok(packageList); } Result Package::getPackageUrl(const char *packageName, const char *packageVersion, Platform platform) { Result packageList = Package::getPackageList("https://gitlab.com/DEC05EBA/sibs_packages/raw/master/packages.json"); if(!packageList) return Result::Err(packageList); const Document &packageDoc = *packageList.unwrap(); auto packageMetaDataJsonIt = packageDoc.FindMember(packageName); if (packageMetaDataJsonIt == packageDoc.MemberEnd()) { string errMsg = "No package with the name \""; errMsg += packageName; errMsg += "\" was found"; return Result::Err(errMsg); } if(packageMetaDataJsonIt->value.IsObject()) { Result packageMetadataResult = getPackageMetadata(packageMetaDataJsonIt->value.GetObject()); if(!packageMetadataResult) return Result::Err(packageMetadataResult); return ::sibs::getPackageUrl(packageMetadataResult.unwrap(), packageName, packageVersion, platform); } else if(packageMetaDataJsonIt->value.IsArray()) { int i = 0; for(const auto &packageData : packageMetaDataJsonIt->value.GetArray()) { if(!packageData.IsObject()) { string errMsg = "Package file is corrupt. "; errMsg += packageName; errMsg += "["; errMsg += to_string(i); errMsg += "] is not an object"; return Result::Err(errMsg); } Result packageMetadataResult = getPackageMetadata(packageData.GetObject()); if(packageMetadataResult) { Result packageUrlResult = ::sibs::getPackageUrl(packageMetadataResult.unwrap(), packageName, packageVersion, platform); if(packageUrlResult) return packageUrlResult; } ++i; } string errMsg = "Package \""; errMsg += packageName; errMsg += "\" with version \""; errMsg += packageVersion; errMsg += "\" does not exist or does not exist for platform \""; errMsg += platform; errMsg += "\""; return Result::Err(errMsg); } else { string errMsg = "No package with the name \""; errMsg += packageName; errMsg += "\" was found"; return Result::Err(errMsg); } } }