From b2f3f831e2b0008e8a517493f4a98f85de1fc868 Mon Sep 17 00:00:00 2001 From: Kyle Edwards Date: Mon, 7 Sep 2020 11:03:01 -0400 Subject: Refactor: Use JSON helpers in CTest resource spec --- Source/CTest/cmCTestResourceSpec.cxx | 221 ++++++++++++++++++++--------------- Source/CTest/cmCTestResourceSpec.h | 2 + 2 files changed, 132 insertions(+), 91 deletions(-) diff --git a/Source/CTest/cmCTestResourceSpec.cxx b/Source/CTest/cmCTestResourceSpec.cxx index 21c97de..101dc2c 100644 --- a/Source/CTest/cmCTestResourceSpec.cxx +++ b/Source/CTest/cmCTestResourceSpec.cxx @@ -2,19 +2,140 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCTestResourceSpec.h" +#include #include #include #include #include +#include + #include #include #include "cmsys/FStream.hxx" #include "cmsys/RegularExpression.hxx" -static const cmsys::RegularExpression IdentifierRegex{ "^[a-z_][a-z0-9_]*$" }; -static const cmsys::RegularExpression IdRegex{ "^[a-z0-9_]+$" }; +#include "cmJSONHelpers.h" + +namespace { +const cmsys::RegularExpression IdentifierRegex{ "^[a-z_][a-z0-9_]*$" }; +const cmsys::RegularExpression IdRegex{ "^[a-z0-9_]+$" }; + +struct Version +{ + int Major = 1; + int Minor = 0; +}; + +struct TopVersion +{ + struct Version Version; +}; + +auto const VersionFieldHelper = + cmJSONIntHelper( + cmCTestResourceSpec::ReadFileResult::READ_OK, + cmCTestResourceSpec::ReadFileResult::INVALID_VERSION); + +auto const VersionHelper = + cmJSONRequiredHelper( + cmCTestResourceSpec::ReadFileResult::NO_VERSION, + cmJSONObjectHelper( + cmCTestResourceSpec::ReadFileResult::READ_OK, + cmCTestResourceSpec::ReadFileResult::INVALID_VERSION) + .Bind("major"_s, &Version::Major, VersionFieldHelper) + .Bind("minor"_s, &Version::Minor, VersionFieldHelper)); + +auto const RootVersionHelper = + cmJSONObjectHelper( + cmCTestResourceSpec::ReadFileResult::READ_OK, + cmCTestResourceSpec::ReadFileResult::INVALID_ROOT) + .Bind("version"_s, &TopVersion::Version, VersionHelper, false); + +cmCTestResourceSpec::ReadFileResult ResourceIdHelper(std::string& out, + const Json::Value* value) +{ + auto result = cmJSONStringHelper( + cmCTestResourceSpec::ReadFileResult::READ_OK, + cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE)(out, value); + if (result != cmCTestResourceSpec::ReadFileResult::READ_OK) { + return result; + } + cmsys::RegularExpressionMatch match; + if (!IdRegex.find(out.c_str(), match)) { + return cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE; + } + return cmCTestResourceSpec::ReadFileResult::READ_OK; +} + +auto const ResourceHelper = + cmJSONObjectHelper( + cmCTestResourceSpec::ReadFileResult::READ_OK, + cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE) + .Bind("id"_s, &cmCTestResourceSpec::Resource::Id, ResourceIdHelper) + .Bind("slots"_s, &cmCTestResourceSpec::Resource::Capacity, + cmJSONUIntHelper( + cmCTestResourceSpec::ReadFileResult::READ_OK, + cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE, 1), + false); + +auto const ResourceListHelper = + cmJSONVectorHelper( + cmCTestResourceSpec::ReadFileResult::READ_OK, + cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE_TYPE, + ResourceHelper); + +auto const ResourceMapHelper = + cmJSONMapFilterHelper, + cmCTestResourceSpec::ReadFileResult>( + cmCTestResourceSpec::ReadFileResult::READ_OK, + cmCTestResourceSpec::ReadFileResult::INVALID_SOCKET_SPEC, + ResourceListHelper, [](const std::string& key) -> bool { + cmsys::RegularExpressionMatch match; + return IdentifierRegex.find(key.c_str(), match); + }); + +auto const SocketSetHelper = cmJSONVectorHelper< + std::map>>( + cmCTestResourceSpec::ReadFileResult::READ_OK, + cmCTestResourceSpec::ReadFileResult::INVALID_SOCKET_SPEC, ResourceMapHelper); + +cmCTestResourceSpec::ReadFileResult SocketHelper( + cmCTestResourceSpec::Socket& out, const Json::Value* value) +{ + std::vector< + std::map>> + sockets; + cmCTestResourceSpec::ReadFileResult result = SocketSetHelper(sockets, value); + if (result != cmCTestResourceSpec::ReadFileResult::READ_OK) { + return result; + } + if (sockets.size() > 1) { + return cmCTestResourceSpec::ReadFileResult::INVALID_SOCKET_SPEC; + } + if (sockets.empty()) { + out.Resources.clear(); + } else { + out.Resources = std::move(sockets[0]); + } + return cmCTestResourceSpec::ReadFileResult::READ_OK; +} + +auto const LocalRequiredHelper = + cmJSONRequiredHelper( + cmCTestResourceSpec::ReadFileResult::INVALID_SOCKET_SPEC, SocketHelper); + +auto const RootHelper = + cmJSONObjectHelper( + cmCTestResourceSpec::ReadFileResult::READ_OK, + cmCTestResourceSpec::ReadFileResult::INVALID_ROOT) + .Bind("local", &cmCTestResourceSpec::LocalSocket, LocalRequiredHelper, + false); +} cmCTestResourceSpec::ReadFileResult cmCTestResourceSpec::ReadFromJSONFile( const std::string& filename) @@ -30,99 +151,17 @@ cmCTestResourceSpec::ReadFileResult cmCTestResourceSpec::ReadFromJSONFile( return ReadFileResult::JSON_PARSE_ERROR; } - if (!root.isObject()) { - return ReadFileResult::INVALID_ROOT; - } - - int majorVersion = 1; - int minorVersion = 0; - if (root.isMember("version")) { - auto const& version = root["version"]; - if (version.isObject()) { - if (!version.isMember("major") || !version.isMember("minor")) { - return ReadFileResult::INVALID_VERSION; - } - auto const& major = version["major"]; - auto const& minor = version["minor"]; - if (!major.isInt() || !minor.isInt()) { - return ReadFileResult::INVALID_VERSION; - } - majorVersion = major.asInt(); - minorVersion = minor.asInt(); - } else { - return ReadFileResult::INVALID_VERSION; - } - } else { - return ReadFileResult::NO_VERSION; + TopVersion version; + ReadFileResult result; + if ((result = RootVersionHelper(version, &root)) != + ReadFileResult::READ_OK) { + return result; } - - if (majorVersion != 1 || minorVersion != 0) { + if (version.Version.Major != 1 || version.Version.Minor != 0) { return ReadFileResult::UNSUPPORTED_VERSION; } - auto const& local = root["local"]; - if (!local.isArray()) { - return ReadFileResult::INVALID_SOCKET_SPEC; - } - if (local.size() > 1) { - return ReadFileResult::INVALID_SOCKET_SPEC; - } - - if (local.empty()) { - this->LocalSocket.Resources.clear(); - return ReadFileResult::READ_OK; - } - - auto const& localSocket = local[0]; - if (!localSocket.isObject()) { - return ReadFileResult::INVALID_SOCKET_SPEC; - } - std::map> resources; - cmsys::RegularExpressionMatch match; - for (auto const& key : localSocket.getMemberNames()) { - if (IdentifierRegex.find(key.c_str(), match)) { - auto const& value = localSocket[key]; - auto& r = resources[key]; - if (value.isArray()) { - for (auto const& item : value) { - if (item.isObject()) { - cmCTestResourceSpec::Resource resource; - - if (!item.isMember("id")) { - return ReadFileResult::INVALID_RESOURCE; - } - auto const& id = item["id"]; - if (!id.isString()) { - return ReadFileResult::INVALID_RESOURCE; - } - resource.Id = id.asString(); - if (!IdRegex.find(resource.Id.c_str(), match)) { - return ReadFileResult::INVALID_RESOURCE; - } - - if (item.isMember("slots")) { - auto const& capacity = item["slots"]; - if (!capacity.isConvertibleTo(Json::uintValue)) { - return ReadFileResult::INVALID_RESOURCE; - } - resource.Capacity = capacity.asUInt(); - } else { - resource.Capacity = 1; - } - - r.push_back(resource); - } else { - return ReadFileResult::INVALID_RESOURCE; - } - } - } else { - return ReadFileResult::INVALID_RESOURCE_TYPE; - } - } - } - - this->LocalSocket.Resources = std::move(resources); - return ReadFileResult::READ_OK; + return RootHelper(*this, &root); } const char* cmCTestResourceSpec::ResultToString(ReadFileResult result) diff --git a/Source/CTest/cmCTestResourceSpec.h b/Source/CTest/cmCTestResourceSpec.h index 1aa279b..72628a3 100644 --- a/Source/CTest/cmCTestResourceSpec.h +++ b/Source/CTest/cmCTestResourceSpec.h @@ -2,6 +2,8 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #pragma once +#include "cmConfigure.h" // IWYU pragma: keep + #include #include #include -- cgit v0.12