// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef INCLUDED_SRC_BUILDTOOL_BUILD_ENGINE_BASE_MAPS_USER_RULE_HPP #define INCLUDED_SRC_BUILDTOOL_BUILD_ENGINE_BASE_MAPS_USER_RULE_HPP #include #include #include #include #include #include #include #include #include #include #include #include // std::move #include #include "fmt/core.h" #include "gsl/gsl" #include "src/buildtool/build_engine/base_maps/entity_name_data.hpp" #include "src/buildtool/build_engine/base_maps/expression_function.hpp" #include "src/buildtool/build_engine/expression/expression_ptr.hpp" #include "src/utils/cpp/concepts.hpp" #include "src/utils/cpp/gsl.hpp" namespace BuildMaps::Base { // Get duplicates from containers. // NOTE: Requires all input containers to be sorted! // kTriangular=true Performs triangular compare, everyone with everyone. // kTriangular=false Performs linear compare, first with each of the rest. template > [[nodiscard]] static inline auto GetDuplicates(TContainer const& first, TRest const&... rest) -> TResult; template [[nodiscard]] static inline auto JoinContainer(TContainer const& c, std::string const& sep) -> std::string; class UserRule { public: using Ptr = std::shared_ptr; using implicit_t = std::unordered_map>; using implicit_exp_t = std::unordered_map; using config_trans_t = std::unordered_map; struct AnonymousDefinition { std::string target; std::string provider; ExpressionPtr rule_map; }; using anonymous_defs_t = std::unordered_map; [[nodiscard]] static auto Create( std::vector target_fields, std::vector string_fields, std::vector config_fields, implicit_t const& implicit_targets, anonymous_defs_t anonymous_defs, std::vector const& config_vars, std::vector const& tainted, config_trans_t config_transitions, ExpressionFunctionPtr const& expr, std::function const& logger) -> Ptr { auto implicit_fields = std::vector{}; implicit_fields.reserve(implicit_targets.size()); std::transform(implicit_targets.begin(), implicit_targets.end(), std::back_inserter(implicit_fields), [](auto const& el) { return el.first; }); std::sort(implicit_fields.begin(), implicit_fields.end()); auto anonymous_fields = std::vector{}; anonymous_fields.reserve(anonymous_defs.size()); std::transform(anonymous_defs.begin(), anonymous_defs.end(), std::back_inserter(anonymous_fields), [](auto const& el) { return el.first; }); std::sort(anonymous_fields.begin(), anonymous_fields.end()); std::sort(target_fields.begin(), target_fields.end()); std::sort(string_fields.begin(), string_fields.end()); std::sort(config_fields.begin(), config_fields.end()); auto dups = GetDuplicates(kReservedKeywords, target_fields, string_fields, config_fields, implicit_fields, anonymous_fields); if (not dups.empty()) { logger( fmt::format("User-defined fields cannot be any of the reserved " "fields [{}]", JoinContainer(kReservedKeywords, ","))); return nullptr; } dups = GetDuplicates(target_fields, string_fields, config_fields, implicit_fields, anonymous_fields); if (not dups.empty()) { logger( fmt::format("A field can have only one type, but the following " "have more: [{}]", JoinContainer(dups, ","))); return nullptr; } auto transition_targets = std::vector{}; transition_targets.reserve(config_transitions.size()); std::transform(config_transitions.begin(), config_transitions.end(), std::back_inserter(transition_targets), [](auto const& el) { return el.first; }); std::sort(transition_targets.begin(), transition_targets.end()); dups = GetDuplicates(transition_targets, target_fields, implicit_fields, anonymous_fields); if (dups != decltype(dups){transition_targets.begin(), transition_targets.end()}) { logger( fmt::format("Config transitions has to be a map from target " "fields to transition expressions, but found [{}]", JoinContainer(transition_targets, ","))); return nullptr; } auto const setter = [&config_transitions](auto const field) { config_transitions.emplace( field, ExpressionFunction::kEmptyTransition); // wont overwrite }; config_transitions.reserve(target_fields.size() + implicit_fields.size() + anonymous_fields.size()); std::for_each(target_fields.begin(), target_fields.end(), setter); std::for_each(implicit_fields.begin(), implicit_fields.end(), setter); std::for_each(anonymous_fields.begin(), anonymous_fields.end(), setter); implicit_exp_t implicit_target_exp; implicit_target_exp.reserve(implicit_targets.size()); for (auto const& [target_name, target_entity_vec] : implicit_targets) { std::vector target_exps; target_exps.reserve(target_entity_vec.size()); for (auto const& target_entity : target_entity_vec) { target_exps.emplace_back(target_entity); } implicit_target_exp.emplace(target_name, target_exps); } return std::make_shared( std::move(target_fields), std::move(string_fields), std::move(config_fields), implicit_targets, std::move(implicit_target_exp), std::move(anonymous_defs), config_vars, std::set{tainted.begin(), tainted.end()}, std::move(config_transitions), expr); } UserRule(std::vector target_fields, std::vector string_fields, std::vector config_fields, implicit_t implicit_targets, implicit_exp_t implicit_target_exp, anonymous_defs_t anonymous_defs, std::vector config_vars, std::set tainted, config_trans_t config_transitions, ExpressionFunctionPtr expr) noexcept : target_fields_{std::move(target_fields)}, string_fields_{std::move(string_fields)}, config_fields_{std::move(config_fields)}, implicit_targets_{std::move(implicit_targets)}, implicit_target_exp_{std::move(implicit_target_exp)}, anonymous_defs_{std::move(anonymous_defs)}, config_vars_{std::move(config_vars)}, tainted_{std::move(tainted)}, config_transitions_{std::move(config_transitions)}, expr_{std::move(expr)} {} [[nodiscard]] auto TargetFields() const& noexcept -> std::vector const& { return target_fields_; } [[nodiscard]] auto TargetFields() && noexcept -> std::vector { return std::move(target_fields_); } [[nodiscard]] auto StringFields() const& noexcept -> std::vector const& { return string_fields_; } [[nodiscard]] auto StringFields() && noexcept -> std::vector { return std::move(string_fields_); } [[nodiscard]] auto ConfigFields() const& noexcept -> std::vector const& { return config_fields_; } [[nodiscard]] auto ConfigFields() && noexcept -> std::vector { return std::move(config_fields_); } [[nodiscard]] auto ImplicitTargets() const& noexcept -> implicit_t const& { return implicit_targets_; } [[nodiscard]] auto ImplicitTargets() && noexcept -> implicit_t { return std::move(implicit_targets_); } [[nodiscard]] auto ImplicitTargetExps() const& noexcept -> implicit_exp_t const& { return implicit_target_exp_; } [[nodiscard]] auto ExpectedFields() const& noexcept -> std::unordered_set const& { return expected_entries_; } [[nodiscard]] auto ConfigVars() const& noexcept -> std::vector const& { return config_vars_; } [[nodiscard]] auto ConfigVars() && noexcept -> std::vector { return std::move(config_vars_); } [[nodiscard]] auto Tainted() const& noexcept -> std::set const& { return tainted_; } [[nodiscard]] auto Tainted() && noexcept -> std::set { return std::move(tainted_); } [[nodiscard]] auto ConfigTransitions() const& noexcept -> config_trans_t const& { return config_transitions_; } [[nodiscard]] auto ConfigTransitions() && noexcept -> config_trans_t { return std::move(config_transitions_); } [[nodiscard]] auto Expression() const& noexcept -> ExpressionFunctionPtr const& { return expr_; } [[nodiscard]] auto Expression() && noexcept -> ExpressionFunctionPtr { return std::move(expr_); } [[nodiscard]] auto AnonymousDefinitions() const& noexcept -> anonymous_defs_t { return anonymous_defs_; } [[nodiscard]] auto AnonymousDefinitions() && noexcept -> anonymous_defs_t { return std::move(anonymous_defs_); } private: // NOTE: Must be sorted static inline std::vector const kReservedKeywords{ "arguments_config", "tainted", "type"}; static auto ComputeExpectedEntries(std::vector tfields, std::vector sfields, std::vector cfields) -> std::unordered_set { std::size_t n = 0; n += tfields.size(); n += sfields.size(); n += cfields.size(); n += kReservedKeywords.size(); std::unordered_set expected_entries{}; expected_entries.reserve(n); expected_entries.insert(tfields.begin(), tfields.end()); expected_entries.insert(sfields.begin(), sfields.end()); expected_entries.insert(cfields.begin(), cfields.end()); expected_entries.insert(kReservedKeywords.begin(), kReservedKeywords.end()); return expected_entries; } std::vector target_fields_; std::vector string_fields_; std::vector config_fields_; implicit_t implicit_targets_; implicit_exp_t implicit_target_exp_; anonymous_defs_t anonymous_defs_; std::vector config_vars_; std::set tainted_; config_trans_t config_transitions_; ExpressionFunctionPtr expr_; std::unordered_set expected_entries_{ ComputeExpectedEntries(target_fields_, string_fields_, config_fields_)}; }; using UserRulePtr = UserRule::Ptr; namespace detail { template [[nodiscard]] static inline auto MaxSize(TContainer const& first, TRest const&... rest) -> std::size_t { if constexpr (sizeof...(rest) > 0) { return std::max(first.size(), MaxSize(rest...)); } return first.size(); } template static auto inline FindDuplicates(gsl::not_null const& dups, TFirst const& first, TSecond const& second, TRest const&... rest) -> void { ExpectsAudit(std::is_sorted(first.begin(), first.end()) and std::is_sorted(second.begin(), second.end())); std::set_intersection(first.begin(), first.end(), second.begin(), second.end(), std::inserter(*dups, dups->begin())); if constexpr (sizeof...(rest) > 0) { // n comparisons with rest: first<->rest[0], ..., first<->rest[n] FindDuplicates(dups, first, rest...); if constexpr (kTriangular) { // do triangular compare of second with rest // NOLINTNEXTLINE(readability-suspicious-call-argument) FindDuplicates(dups, second, rest...); } } } } // namespace detail template [[nodiscard]] static inline auto GetDuplicates(TContainer const& first, TRest const&... rest) -> TResult { auto dups = TResult{}; constexpr auto kNumContainers = 1 + sizeof...(rest); if constexpr (kNumContainers > 1) { std::size_t size{}; if constexpr (kTriangular) { // worst case if all containers are of the same size size = kNumContainers * detail::MaxSize(first, rest...) / 2; } else { size = std::min(first.size(), detail::MaxSize(rest...)); } dups.reserve(size); detail::FindDuplicates(&dups, first, rest...); } return dups; } template [[nodiscard]] static inline auto JoinContainer(TContainer const& c, std::string const& sep) -> std::string { std::ostringstream oss{}; std::size_t insert_sep{}; for (auto const& i : c) { oss << (insert_sep++ ? sep.c_str() : ""); oss << i; } return oss.str(); }; } // namespace BuildMaps::Base #endif // INCLUDED_SRC_BUILDTOOL_BUILD_ENGINE_BASE_MAPS_USER_RULE_HPP