#pragma once
#include <cstdint>
#include <span>
#include <string>
#include <string_view>
#include <unordered_map>
#include <utility>
#include "memory_resource.hpp"
#include "object.hpp"
#include "object_store.hpp"
#include "pointer.hpp"
#include "target_address.hpp"
namespace ddwaf {
class attribute_collector {
public:
explicit attribute_collector(
nonnull_ptr<memory::memory_resource> alloc = memory::get_default_resource())
: alloc_(alloc)
{}
attribute_collector(const attribute_collector &) = delete;
attribute_collector &operator=(const attribute_collector &) = delete;
attribute_collector(attribute_collector &&other) noexcept = default;
attribute_collector &operator=(attribute_collector &&other) noexcept = default;
~attribute_collector() = default;
bool insert(std::string_view key, bool value)
{
return insert(key, owned_object::make_boolean(value));
}
template <typename T>
bool insert(std::string_view key, T value)
requires std::is_integral_v<T> && std::is_signed_v<T>
{
return insert(key, owned_object::make_signed(value));
}
template <typename T>
bool insert(std::string_view key, T value)
requires std::is_integral_v<T> && std::is_unsigned_v<T> && (!std::same_as<T, bool>)
{
return insert(key, owned_object::make_unsigned(value));
}
template <typename T>
bool insert(std::string_view key, T value)
requires std::is_floating_point_v<T>
{
return insert(key, owned_object::make_float(value));
}
template <typename T>
bool insert(std::string_view key, T &&value)
requires std::convertible_to<T, std::string_view> &&
(!std::is_integral_v<std::remove_cvref_t<T>>) &&
(!std::is_floating_point_v<std::remove_cvref_t<T>>)
{
return insert(
key, owned_object::make_string(std::string_view(std::forward<T>(value)), alloc_));
}
bool insert(std::string_view key, owned_object &&object);
bool insert_or_assign(std::string_view key, owned_object &&object);
bool collect(const object_store &store, target_index input_target,
std::span<const std::variant<std::string, int64_t>> input_key_path,
std::string_view attribute_key);
void collect_pending(const object_store &store);
owned_object get_available_attributes_and_reset()
{
auto attributes = owned_object::make_map(inserted_.size(), alloc_);
for (auto &[key, value] : inserted_) { attributes.emplace(key, std::move(value)); }
inserted_.clear();
return attributes;
}
[[nodiscard]] bool has_pending_attributes() const { return !pending_.empty(); }
static attribute_collector from_upstream_collector(const attribute_collector &upstream)
{
attribute_collector collector{upstream.alloc_};
collector.pending_ = upstream.pending_;
return collector;
}
protected:
enum class collection_state : uint8_t { success, unavailable, failed };
collection_state collect_helper(const object_store &store, target_index input_target,
std::span<const std::variant<std::string, int64_t>> input_key_path,
std::string_view attribute_key);
void insert_helper(std::string_view key, owned_object &&object);
using target_type =
std::pair<target_index, std::span<const std::variant<std::string, int64_t>>>;
std::unordered_map<std::string_view, target_type> pending_;
nonnull_ptr<memory::memory_resource> alloc_;
std::unordered_map<std::string_view, owned_object> inserted_;
};
}