blob: 101dc2c8b54e3d64179fa2d95fc26d9afdc0181c [file] [log] [blame]
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmCTestResourceSpec.h"
#include <functional>
#include <map>
#include <string>
#include <utility>
#include <vector>
#include <cmext/string_view>
#include <cm3p/json/reader.h>
#include <cm3p/json/value.h>
#include "cmsys/FStream.hxx"
#include "cmsys/RegularExpression.hxx"
#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>(
cmCTestResourceSpec::ReadFileResult::READ_OK,
cmCTestResourceSpec::ReadFileResult::INVALID_VERSION);
auto const VersionHelper =
cmJSONRequiredHelper<Version, cmCTestResourceSpec::ReadFileResult>(
cmCTestResourceSpec::ReadFileResult::NO_VERSION,
cmJSONObjectHelper<Version, cmCTestResourceSpec::ReadFileResult>(
cmCTestResourceSpec::ReadFileResult::READ_OK,
cmCTestResourceSpec::ReadFileResult::INVALID_VERSION)
.Bind("major"_s, &Version::Major, VersionFieldHelper)
.Bind("minor"_s, &Version::Minor, VersionFieldHelper));
auto const RootVersionHelper =
cmJSONObjectHelper<TopVersion, cmCTestResourceSpec::ReadFileResult>(
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::Resource,
cmCTestResourceSpec::ReadFileResult>(
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::Resource,
cmCTestResourceSpec::ReadFileResult>(
cmCTestResourceSpec::ReadFileResult::READ_OK,
cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE_TYPE,
ResourceHelper);
auto const ResourceMapHelper =
cmJSONMapFilterHelper<std::vector<cmCTestResourceSpec::Resource>,
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<std::string, std::vector<cmCTestResourceSpec::Resource>>>(
cmCTestResourceSpec::ReadFileResult::READ_OK,
cmCTestResourceSpec::ReadFileResult::INVALID_SOCKET_SPEC, ResourceMapHelper);
cmCTestResourceSpec::ReadFileResult SocketHelper(
cmCTestResourceSpec::Socket& out, const Json::Value* value)
{
std::vector<
std::map<std::string, std::vector<cmCTestResourceSpec::Resource>>>
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::Socket,
cmCTestResourceSpec::ReadFileResult>(
cmCTestResourceSpec::ReadFileResult::INVALID_SOCKET_SPEC, SocketHelper);
auto const RootHelper =
cmJSONObjectHelper<cmCTestResourceSpec, cmCTestResourceSpec::ReadFileResult>(
cmCTestResourceSpec::ReadFileResult::READ_OK,
cmCTestResourceSpec::ReadFileResult::INVALID_ROOT)
.Bind("local", &cmCTestResourceSpec::LocalSocket, LocalRequiredHelper,
false);
}
cmCTestResourceSpec::ReadFileResult cmCTestResourceSpec::ReadFromJSONFile(
const std::string& filename)
{
cmsys::ifstream fin(filename.c_str());
if (!fin) {
return ReadFileResult::FILE_NOT_FOUND;
}
Json::Value root;
Json::CharReaderBuilder builder;
if (!Json::parseFromStream(builder, fin, &root, nullptr)) {
return ReadFileResult::JSON_PARSE_ERROR;
}
TopVersion version;
ReadFileResult result;
if ((result = RootVersionHelper(version, &root)) !=
ReadFileResult::READ_OK) {
return result;
}
if (version.Version.Major != 1 || version.Version.Minor != 0) {
return ReadFileResult::UNSUPPORTED_VERSION;
}
return RootHelper(*this, &root);
}
const char* cmCTestResourceSpec::ResultToString(ReadFileResult result)
{
switch (result) {
case ReadFileResult::READ_OK:
return "OK";
case ReadFileResult::FILE_NOT_FOUND:
return "File not found";
case ReadFileResult::JSON_PARSE_ERROR:
return "JSON parse error";
case ReadFileResult::INVALID_ROOT:
return "Invalid root object";
case ReadFileResult::NO_VERSION:
return "No version specified";
case ReadFileResult::INVALID_VERSION:
return "Invalid version object";
case ReadFileResult::UNSUPPORTED_VERSION:
return "Unsupported version";
case ReadFileResult::INVALID_SOCKET_SPEC:
return "Invalid socket object";
case ReadFileResult::INVALID_RESOURCE_TYPE:
return "Invalid resource type object";
case ReadFileResult::INVALID_RESOURCE:
return "Invalid resource object";
default:
return "Unknown";
}
}
bool cmCTestResourceSpec::operator==(const cmCTestResourceSpec& other) const
{
return this->LocalSocket == other.LocalSocket;
}
bool cmCTestResourceSpec::operator!=(const cmCTestResourceSpec& other) const
{
return !(*this == other);
}
bool cmCTestResourceSpec::Socket::operator==(
const cmCTestResourceSpec::Socket& other) const
{
return this->Resources == other.Resources;
}
bool cmCTestResourceSpec::Socket::operator!=(
const cmCTestResourceSpec::Socket& other) const
{
return !(*this == other);
}
bool cmCTestResourceSpec::Resource::operator==(
const cmCTestResourceSpec::Resource& other) const
{
return this->Id == other.Id && this->Capacity == other.Capacity;
}
bool cmCTestResourceSpec::Resource::operator!=(
const cmCTestResourceSpec::Resource& other) const
{
return !(*this == other);
}