#pragma once
#include <concepts>
#include <cstdint>
#include <optional>
#include <span>
#include <string>
#include <string_view>
#include <type_traits>
#include <variant>
#include <vector>
#include "exclusion/common.hpp"
#include "object.hpp"
#include "object_store.hpp"
#include "traits.hpp"
#include "transformer/base.hpp"
namespace ddwaf {
template <typename T>
concept has_transformers = requires(const T &obj) {
requires std::same_as<std::remove_cvref_t<decltype(obj.transformers)>,
std::vector<transformer_id>>;
};
template <typename T> struct unary_argument {
std::string_view address{};
std::span<const std::variant<std::string, int64_t>> key_path;
T value;
std::span<const transformer_id> transformers;
};
template <typename T, typename = void> struct is_unary_argument : std::false_type {};
template <typename T> struct is_unary_argument<unary_argument<T>> : std::true_type {};
template <typename T> using optional_argument = std::optional<unary_argument<T>>;
template <typename T, typename = void> struct is_optional_argument : std::false_type {};
template <typename T> struct is_optional_argument<optional_argument<T>> : std::true_type {};
template <typename T> using variadic_argument = std::vector<unary_argument<T>>;
template <typename T, typename = void> struct is_variadic_argument : std::false_type {};
template <typename T> struct is_variadic_argument<variadic_argument<T>> : std::true_type {};
template <typename T> std::optional<T> convert(object_view obj)
{
if constexpr (std::is_same_v<std::decay_t<T>, object_view>) {
return obj;
} else if constexpr (is_type_in_set_v<T, uint64_t, unsigned, int64_t, int, bool, std::string,
std::string_view, array_view, map_view>) {
if (obj.is<T>()) {
return obj.as<T>();
}
} else {
static_assert(!std::is_same_v<T, T>, "unsupported type on argument_retriever::convert<T>");
}
return {};
}
struct default_argument_retriever {
static constexpr bool is_variadic = false;
static constexpr bool is_optional = false;
};
template <typename T> struct argument_retriever : default_argument_retriever {};
template <typename T> struct argument_retriever<unary_argument<T>> : default_argument_retriever {
template <typename TargetType>
static std::optional<unary_argument<T>> retrieve(
const object_store &store, const object_set_ref &objects_excluded, const TargetType &target)
{
auto object = store.get_target(target.index);
if (!object.has_value() || objects_excluded.contains(object)) {
return std::nullopt;
}
auto converted = convert<T>(object);
if (!converted.has_value()) {
return std::nullopt;
}
if constexpr (has_transformers<TargetType>) {
return unary_argument<T>{
target.name, target.key_path, std::move(converted.value()), target.transformers};
} else {
return unary_argument<T>{
target.name, target.key_path, std::move(converted.value()), {}};
}
}
};
template <typename T> struct argument_retriever<optional_argument<T>> : default_argument_retriever {
static constexpr bool is_optional = true;
template <typename TargetType>
static optional_argument<T> retrieve(
const object_store &store, const object_set_ref &objects_excluded, const TargetType &target)
{
return argument_retriever<unary_argument<T>>::retrieve(store, objects_excluded, target);
}
};
template <typename T> struct argument_retriever<variadic_argument<T>> : default_argument_retriever {
static constexpr bool is_variadic = true;
template <typename TargetType>
static variadic_argument<T> retrieve(const object_store &store,
const object_set_ref &objects_excluded, const std::vector<TargetType> &targets)
{
variadic_argument<T> args;
for (const auto &target : targets) {
auto arg =
argument_retriever<unary_argument<T>>::retrieve(store, objects_excluded, target);
if (!arg.has_value()) {
continue;
}
args.emplace_back(std::move(arg.value()));
}
return args;
}
};
}