summaryrefslogtreecommitdiff
path: root/src/buildtool/common/artifact_description.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/buildtool/common/artifact_description.hpp')
-rw-r--r--src/buildtool/common/artifact_description.hpp316
1 files changed, 316 insertions, 0 deletions
diff --git a/src/buildtool/common/artifact_description.hpp b/src/buildtool/common/artifact_description.hpp
new file mode 100644
index 00000000..3820edc4
--- /dev/null
+++ b/src/buildtool/common/artifact_description.hpp
@@ -0,0 +1,316 @@
+#ifndef INCLUDED_SRC_BUILDTOOL_COMMON_ARTIFACT_DESCRIPTION_HPP
+#define INCLUDED_SRC_BUILDTOOL_COMMON_ARTIFACT_DESCRIPTION_HPP
+
+#include <filesystem>
+#include <optional>
+#include <string>
+#include <variant>
+
+#include "src/buildtool/common/artifact.hpp"
+#include "src/buildtool/common/artifact_digest.hpp"
+#include "src/buildtool/file_system/object_type.hpp"
+#include "src/buildtool/logging/logger.hpp"
+#include "src/utils/cpp/json.hpp"
+
+class ArtifactDescription {
+ using Local = std::pair<std::filesystem::path, std::string>;
+ using Known =
+ std::tuple<ArtifactDigest, ObjectType, std::optional<std::string>>;
+ using Action = std::pair<std::string, std::filesystem::path>;
+ using Tree = std::string;
+
+ public:
+ explicit ArtifactDescription(std::filesystem::path path,
+ std::string repository) noexcept
+ : data_{std::make_pair(std::move(path), std::move(repository))} {}
+
+ ArtifactDescription(ArtifactDigest digest,
+ ObjectType file_type,
+ std::optional<std::string> repo = std::nullopt) noexcept
+ : data_{
+ std::make_tuple(std::move(digest), file_type, std::move(repo))} {}
+
+ ArtifactDescription(std::string action_id,
+ std::filesystem::path path) noexcept
+ : data_{std::make_pair(std::move(action_id), std::move(path))} {}
+
+ explicit ArtifactDescription(std::string tree_id) noexcept
+ : data_{std::move(tree_id)} {}
+
+ [[nodiscard]] static auto FromJson(nlohmann::json const& json) noexcept
+ -> std::optional<ArtifactDescription> {
+ try {
+ auto const type = ExtractValueAs<std::string>(
+ json, "type", [](std::string const& error) {
+ Logger::Log(
+ LogLevel::Error,
+ "{}\ncan not retrieve value for \"type\" from artifact "
+ "description.",
+ error);
+ });
+ auto const data = ExtractValueAs<nlohmann::json>(
+ json, "data", [](std::string const& error) {
+ Logger::Log(
+ LogLevel::Error,
+ "{}\ncan not retrieve value for \"data\" from artifact "
+ "description.",
+ error);
+ });
+
+ if (not(type and data)) {
+ return std::nullopt;
+ }
+
+ if (*type == "LOCAL") {
+ return CreateLocalArtifactDescription(*data);
+ }
+ if (*type == "KNOWN") {
+ return CreateKnownArtifactDescription(*data);
+ }
+ if (*type == "ACTION") {
+ return CreateActionArtifactDescription(*data);
+ }
+ if (*type == "TREE") {
+ return CreateTreeArtifactDescription(*data);
+ }
+ Logger::Log(LogLevel::Error,
+ R"(artifact type must be one of "LOCAL", "KNOWN",
+ "ACTION", or "TREE")");
+ } catch (std::exception const& ex) {
+ Logger::Log(LogLevel::Error,
+ "Failed to parse artifact description from JSON with "
+ "error:\n{}",
+ ex.what());
+ }
+ return std::nullopt;
+ }
+
+ [[nodiscard]] auto Id() const noexcept -> ArtifactIdentifier { return id_; }
+
+ [[nodiscard]] auto IsTree() const noexcept -> bool {
+ return std::holds_alternative<Tree>(data_);
+ }
+
+ [[nodiscard]] auto ToJson() const noexcept -> nlohmann::json {
+ try {
+ if (std::holds_alternative<Local>(data_)) {
+ auto const& [path, repo] = std::get<Local>(data_);
+ return DescribeLocalArtifact(path.string(), repo);
+ }
+ if (std::holds_alternative<Known>(data_)) {
+ auto const& [digest, file_type, _] = std::get<Known>(data_);
+ return DescribeKnownArtifact(
+ digest.hash(), digest.size(), file_type);
+ }
+ if (std::holds_alternative<Action>(data_)) {
+ auto const& [action_id, path] = std::get<Action>(data_);
+ return DescribeActionArtifact(action_id, path);
+ }
+ if (std::holds_alternative<Tree>(data_)) {
+ return DescribeTreeArtifact(std::get<Tree>(data_));
+ }
+ Logger::Log(LogLevel::Error,
+ "Internal error, unknown artifact type");
+ } catch (std::exception const& ex) {
+ Logger::Log(LogLevel::Error,
+ "Serializing to JSON failed with error:\n{}",
+ ex.what());
+ }
+ gsl_Ensures(false); // unreachable
+ return {};
+ }
+
+ [[nodiscard]] auto ToArtifact() const noexcept -> Artifact {
+ try {
+ if (std::holds_alternative<Local>(data_)) {
+ auto const& [path, repo] = std::get<Local>(data_);
+ return Artifact::CreateLocalArtifact(id_, path.string(), repo);
+ }
+ if (std::holds_alternative<Known>(data_)) {
+ auto const& [digest, file_type, repo] = std::get<Known>(data_);
+ return Artifact::CreateKnownArtifact(
+ id_, digest.hash(), digest.size(), file_type, repo);
+ }
+ if (std::holds_alternative<Action>(data_) or
+ std::holds_alternative<Tree>(data_)) {
+ return Artifact::CreateActionArtifact(id_);
+ }
+ Logger::Log(LogLevel::Error,
+ "Internal error, unknown artifact type");
+ } catch (std::exception const& ex) {
+ Logger::Log(LogLevel::Error,
+ "Creating artifact failed with error:\n{}",
+ ex.what());
+ }
+ gsl_Ensures(false); // unreachable
+ return Artifact{{}};
+ }
+
+ [[nodiscard]] auto ToString(int indent = 0) const noexcept -> std::string {
+ try {
+ return ToJson().dump(indent);
+ } catch (std::exception const& ex) {
+ Logger::Log(LogLevel::Error,
+ "Serializing artifact failed with error:\n{}",
+ ex.what());
+ }
+ return {};
+ }
+
+ [[nodiscard]] auto operator==(
+ ArtifactDescription const& other) const noexcept -> bool {
+ return data_ == other.data_;
+ }
+
+ [[nodiscard]] auto operator!=(
+ ArtifactDescription const& other) const noexcept -> bool {
+ return not(*this == other);
+ }
+
+ private:
+ inline static HashGenerator const hash_gen_{
+ HashGenerator::HashType::SHA256};
+ std::variant<Local, Known, Action, Tree> data_;
+ ArtifactIdentifier id_{ComputeId(ToJson())};
+
+ [[nodiscard]] static auto ComputeId(nlohmann::json const& desc) noexcept
+ -> ArtifactIdentifier {
+ try {
+ return hash_gen_.Run(desc.dump()).Bytes();
+ } catch (std::exception const& ex) {
+ Logger::Log(LogLevel::Error,
+ "Computing artifact id failed with error:\n{}",
+ ex.what());
+ }
+ return {};
+ }
+
+ [[nodiscard]] static auto DescribeLocalArtifact(
+ std::filesystem::path const& src_path,
+ std::string const& repository) noexcept -> nlohmann::json {
+ return {{"type", "LOCAL"},
+ {"data",
+ {{"path", src_path.string()}, {"repository", repository}}}};
+ }
+
+ [[nodiscard]] static auto DescribeKnownArtifact(
+ std::string const& blob_id,
+ std::size_t size,
+ ObjectType type = ObjectType::File) noexcept -> nlohmann::json {
+ std::string const typestr{ToChar(type)};
+ return {{"type", "KNOWN"},
+ {"data",
+ {{"id", blob_id}, {"size", size}, {"file_type", typestr}}}};
+ }
+
+ [[nodiscard]] static auto DescribeActionArtifact(
+ std::string const& action_id,
+ std::string const& out_path) noexcept -> nlohmann::json {
+ return {{"type", "ACTION"},
+ {"data", {{"id", action_id}, {"path", out_path}}}};
+ }
+
+ [[nodiscard]] static auto DescribeTreeArtifact(
+ std::string const& tree_id) noexcept -> nlohmann::json {
+ return {{"type", "TREE"}, {"data", {{"id", tree_id}}}};
+ }
+
+ [[nodiscard]] static auto CreateLocalArtifactDescription(
+ nlohmann::json const& data) -> std::optional<ArtifactDescription> {
+
+ auto const path = ExtractValueAs<std::string>(
+ data, "path", [](std::string const& error) {
+ Logger::Log(LogLevel::Error,
+ "{}\ncan not retrieve value for \"path\" from "
+ "LOCAL artifact's data.",
+ error);
+ });
+ auto const repository = ExtractValueAs<std::string>(
+ data, "repository", [](std::string const& error) {
+ Logger::Log(LogLevel::Error,
+ "{}\ncan not retrieve value for \"path\" from "
+ "LOCAL artifact's data.",
+ error);
+ });
+ if (path.has_value() and repository.has_value()) {
+ return ArtifactDescription{std::filesystem::path{*path},
+ *repository};
+ }
+ return std::nullopt;
+ }
+
+ [[nodiscard]] static auto CreateKnownArtifactDescription(
+ nlohmann::json const& data) -> std::optional<ArtifactDescription> {
+
+ auto const blob_id = ExtractValueAs<std::string>(
+ data, "id", [](std::string const& error) {
+ Logger::Log(LogLevel::Error,
+ "{}\ncan not retrieve value for \"id\" from "
+ "KNOWN artifact's data.",
+ error);
+ });
+ auto const size = ExtractValueAs<std::size_t>(
+ data, "size", [](std::string const& error) {
+ Logger::Log(LogLevel::Error,
+ "{}\ncan not retrieve value for \"size\" from "
+ "KNOWN artifact's data.",
+ error);
+ });
+ auto const file_type = ExtractValueAs<std::string>(
+ data, "file_type", [](std::string const& error) {
+ Logger::Log(LogLevel::Error,
+ "{}\ncan not retrieve value for \"file_type\" from "
+ "KNOWN artifact's data.",
+ error);
+ });
+ if (blob_id.has_value() and size.has_value() and
+ file_type.has_value() and file_type->size() == 1) {
+ return ArtifactDescription{ArtifactDigest{*blob_id, *size},
+ FromChar((*file_type)[0])};
+ }
+ return std::nullopt;
+ }
+
+ [[nodiscard]] static auto CreateActionArtifactDescription(
+ nlohmann::json const& data) -> std::optional<ArtifactDescription> {
+
+ auto const action_id = ExtractValueAs<std::string>(
+ data, "id", [](std::string const& error) {
+ Logger::Log(LogLevel::Error,
+ "{}\ncan not retrieve value for \"id\" from "
+ "ACTION artifact's data.",
+ error);
+ });
+
+ auto const path = ExtractValueAs<std::string>(
+ data, "path", [](std::string const& error) {
+ Logger::Log(LogLevel::Error,
+ "{}\ncan not retrieve value for \"path\" from "
+ "ACTION artifact's data.",
+ error);
+ });
+ if (action_id.has_value() and path.has_value()) {
+ return ArtifactDescription{*action_id,
+ std::filesystem::path{*path}};
+ }
+ return std::nullopt;
+ }
+
+ [[nodiscard]] static auto CreateTreeArtifactDescription(
+ nlohmann::json const& data) -> std::optional<ArtifactDescription> {
+ auto const tree_id = ExtractValueAs<std::string>(
+ data, "id", [](std::string const& error) {
+ Logger::Log(LogLevel::Error,
+ "{}\ncan not retrieve value for \"id\" from "
+ "TREE artifact's data.",
+ error);
+ });
+
+ if (tree_id.has_value()) {
+ return ArtifactDescription{*tree_id};
+ }
+ return std::nullopt;
+ }
+};
+
+#endif // INCLUDED_SRC_BUILDTOOL_COMMON_ARTIFACT_DESCRIPTION_HPP