diff options
Diffstat (limited to 'src/buildtool/build_engine/base_maps/rule_map.cpp')
-rw-r--r-- | src/buildtool/build_engine/base_maps/rule_map.cpp | 371 |
1 files changed, 371 insertions, 0 deletions
diff --git a/src/buildtool/build_engine/base_maps/rule_map.cpp b/src/buildtool/build_engine/base_maps/rule_map.cpp new file mode 100644 index 00000000..7bdc14e4 --- /dev/null +++ b/src/buildtool/build_engine/base_maps/rule_map.cpp @@ -0,0 +1,371 @@ + +#include "src/buildtool/build_engine/base_maps/rule_map.hpp" + +#include <optional> +#include <string> +#include <unordered_set> + +#include "fmt/core.h" +#include "src/buildtool/build_engine/base_maps/field_reader.hpp" + +namespace BuildMaps::Base { + +namespace { + +auto rule_fields = std::unordered_set<std::string>{"anonymous", + "config_doc", + "config_fields", + "config_transitions", + "config_vars", + "doc", + "expression", + "field_doc", + "implicit", + "imports", + "import", + "string_fields", + "tainted", + "target_fields"}; + +[[nodiscard]] auto ReadAnonymousObject(EntityName const& id, + nlohmann::json const& json, + AsyncMapConsumerLoggerPtr const& logger) + -> std::optional<UserRule::anonymous_defs_t> { + auto obj = GetOrDefault(json, "anonymous", nlohmann::json::object()); + if (not obj.is_object()) { + (*logger)( + fmt::format("Field anonymous in rule {} is not an object", id.name), + true); + return std::nullopt; + } + + UserRule::anonymous_defs_t anon_defs{}; + anon_defs.reserve(obj.size()); + for (auto const& [name, def] : obj.items()) { + if (not def.is_object()) { + (*logger)(fmt::format("Entry {} in field anonymous in rule {} is " + "not an object", + name, + id.name), + true); + return std::nullopt; + } + + auto target = def.find("target"); + if (target == def.end()) { + (*logger)(fmt::format("Entry target for {} in field anonymous in " + "rule {} is missing", + name, + id.name), + true); + return std::nullopt; + } + if (not target->is_string()) { + (*logger)(fmt::format("Entry target for {} in field anonymous in " + "rule {} is not a string", + name, + id.name), + true); + return std::nullopt; + } + + auto provider = def.find("provider"); + if (provider == def.end()) { + (*logger)(fmt::format("Entry provider for {} in field anonymous in " + "rule {} is missing", + name, + id.name), + true); + return std::nullopt; + } + if (not provider->is_string()) { + (*logger)(fmt::format("Entry provider for {} in field anonymous in " + "rule {} is not a string", + name, + id.name), + true); + return std::nullopt; + } + + auto rule_map = def.find("rule_map"); + if (rule_map == def.end()) { + (*logger)(fmt::format("Entry rule_map for {} in field anonymous in " + "rule {} is missing", + name, + id.name), + true); + return std::nullopt; + } + if (not rule_map->is_object()) { + (*logger)(fmt::format("Entry rule_map for {} in field anonymous in " + "rule {} is not an object", + name, + id.name), + true); + return std::nullopt; + } + + Expression::map_t::underlying_map_t rule_mapping{}; + for (auto const& [key, val] : rule_map->items()) { + auto rule_name = ParseEntityNameFromJson( + val, id, [&logger, &id, &name = name](auto msg) { + (*logger)( + fmt::format("Parsing rule name for entry {} in field " + "anonymous in rule {} failed with:\n{}", + name, + id.name, + msg), + true); + }); + if (not rule_name) { + return std::nullopt; + } + rule_mapping.emplace(key, ExpressionPtr{std::move(*rule_name)}); + } + + anon_defs.emplace( + name, + UserRule::AnonymousDefinition{ + target->get<std::string>(), + provider->get<std::string>(), + ExpressionPtr{Expression::map_t{std::move(rule_mapping)}}}); + } + return anon_defs; +} + +[[nodiscard]] auto ReadImplicitObject(EntityName const& id, + nlohmann::json const& json, + AsyncMapConsumerLoggerPtr const& logger) + -> std::optional<UserRule::implicit_t> { + auto map = GetOrDefault(json, "implicit", nlohmann::json::object()); + if (not map.is_object()) { + (*logger)( + fmt::format("Field implicit in rule {} is not an object", id.name), + true); + return std::nullopt; + } + + auto implicit_targets = UserRule::implicit_t{}; + implicit_targets.reserve(map.size()); + + for (auto const& [key, val] : map.items()) { + if (not val.is_array()) { + (*logger)(fmt::format("Entry in implicit field of rule {} is not a " + "list.", + id.name), + true); + return std::nullopt; + } + auto targets = typename UserRule::implicit_t::mapped_type{}; + targets.reserve(val.size()); + for (auto const& item : val) { + auto expr_id = ParseEntityNameFromJson( + item, id, [&logger, &item, &id](std::string const& parse_err) { + (*logger)(fmt::format("Parsing entry {} in implicit field " + "of rule {} failed with:\n{}", + item.dump(), + id.name, + parse_err), + true); + }); + if (not expr_id) { + return std::nullopt; + } + targets.emplace_back(*expr_id); + } + implicit_targets.emplace(key, targets); + } + return implicit_targets; +} + +[[nodiscard]] auto ReadConfigTransitionsObject( + EntityName const& id, + nlohmann::json const& json, + std::vector<std::string> const& config_vars, + ExpressionFunction::imports_t const& imports, + AsyncMapConsumerLoggerPtr const& logger) + -> std::optional<UserRule::config_trans_t> { + auto map = + GetOrDefault(json, "config_transitions", nlohmann::json::object()); + if (not map.is_object()) { + (*logger)( + fmt::format("Field config_transitions in rule {} is not an object", + id.name), + true); + return std::nullopt; + } + + auto config_transitions = UserRule::config_trans_t{}; + config_transitions.reserve(map.size()); + + for (auto const& [key, val] : map.items()) { + auto expr = Expression::FromJson(val); + if (not expr) { + (*logger)(fmt::format("Failed to create expression for entry {} in " + "config_transitions list of rule {}.", + key, + id.name), + true); + return std::nullopt; + } + config_transitions.emplace( + key, + std::make_shared<ExpressionFunction>(config_vars, imports, expr)); + } + return config_transitions; +} + +} // namespace + +auto CreateRuleMap(gsl::not_null<RuleFileMap*> const& rule_file_map, + gsl::not_null<ExpressionFunctionMap*> const& expr_map, + std::size_t jobs) -> UserRuleMap { + auto user_rule_creator = [rule_file_map, expr_map](auto ts, + auto setter, + auto logger, + auto /*subcaller*/, + auto const& id) { + if (not id.IsDefinitionName()) { + (*logger)(fmt::format("{} cannot name a rule", id.ToString()), + true); + return; + } + rule_file_map->ConsumeAfterKeysReady( + ts, + {id.ToModule()}, + [ts, expr_map, setter = std::move(setter), logger, id]( + auto json_values) { + auto rule_it = json_values[0]->find(id.name); + if (rule_it == json_values[0]->end()) { + (*logger)( + fmt::format( + "Cannot find rule {} in {}", id.name, id.module), + true); + return; + } + + auto reader = + FieldReader::Create(rule_it.value(), id, "rule", logger); + if (not reader) { + return; + } + reader->ExpectFields(rule_fields); + + auto expr = reader->ReadExpression("expression"); + if (not expr) { + return; + } + + auto target_fields = reader->ReadStringList("target_fields"); + if (not target_fields) { + return; + } + + auto string_fields = reader->ReadStringList("string_fields"); + if (not string_fields) { + return; + } + + auto config_fields = reader->ReadStringList("config_fields"); + if (not config_fields) { + return; + } + + auto implicit_targets = + ReadImplicitObject(id, rule_it.value(), logger); + if (not implicit_targets) { + return; + } + + auto anonymous_defs = + ReadAnonymousObject(id, rule_it.value(), logger); + if (not anonymous_defs) { + return; + } + + auto config_vars = reader->ReadStringList("config_vars"); + if (not config_vars) { + return; + } + + auto tainted = reader->ReadStringList("tainted"); + if (not tainted) { + return; + } + + auto import_aliases = + reader->ReadEntityAliasesObject("imports"); + if (not import_aliases) { + return; + } + auto [names, ids] = std::move(*import_aliases).Obtain(); + + expr_map->ConsumeAfterKeysReady( + ts, + std::move(ids), + [ts, + id, + json = rule_it.value(), + expr = std::move(expr), + target_fields = std::move(*target_fields), + string_fields = std::move(*string_fields), + config_fields = std::move(*config_fields), + implicit_targets = std::move(*implicit_targets), + anonymous_defs = std::move(*anonymous_defs), + config_vars = std::move(*config_vars), + tainted = std::move(*tainted), + names = std::move(names), + setter = std::move(setter), + logger](auto expr_funcs) { + auto imports = ExpressionFunction::imports_t{}; + imports.reserve(expr_funcs.size()); + for (std::size_t i{}; i < expr_funcs.size(); ++i) { + imports.emplace(names[i], *expr_funcs[i]); + } + + auto config_transitions = ReadConfigTransitionsObject( + id, json, config_vars, imports, logger); + if (not config_transitions) { + return; + } + + auto rule = UserRule::Create( + target_fields, + string_fields, + config_fields, + implicit_targets, + anonymous_defs, + config_vars, + tainted, + std::move(*config_transitions), + std::make_shared<ExpressionFunction>( + std::move(config_vars), + std::move(imports), + std::move(expr)), + [&logger](auto const& msg) { + (*logger)(msg, true); + }); + if (rule) { + (*setter)(std::move(rule)); + } + }, + [logger, id](auto msg, auto fatal) { + (*logger)(fmt::format("While reading expression map " + "for rule {} in {}: {}", + id.name, + id.module, + msg), + fatal); + }); + }, + [logger, id](auto msg, auto fatal) { + (*logger)( + fmt::format( + "While reading rule file in {}: {}", id.module, msg), + fatal); + }); + }; + return UserRuleMap{user_rule_creator, jobs}; +} + +} // namespace BuildMaps::Base |