<commit_before>#pragma once

#include <concepts>
#include <cstddef>
#include <phosphor-logging/lg2/flags.hpp>
#include <phosphor-logging/lg2/header.hpp>
#include <phosphor-logging/lg2/level.hpp>
#include <phosphor-logging/lg2/logger.hpp>
#include <sdbusplus/message/native_types.hpp>
#include <source_location>
#include <string_view>
#include <tuple>
#include <type_traits>

namespace lg2::details
{

/** Concept to determine if an item acts like a string.
 *
 *  Something acts like a string if we can construct a string_view from it.
 *  This covers std::string and C-strings.  But, there is subtlety in that
 *  nullptr_t can be used to construct a string_view until C++23, so we need
 *  to exempt out nullptr_t's (otherwise `nullptr` ends up acting like a
 *  string).
 */
template <typename T>
concept string_like_type =
    std::constructible_from<std::string_view, T> && !std::same_as<nullptr_t, T>;

/** Concept to determine if an item acts like a pointer.
 *
 *  Any pointer, which doesn't act like a string, should be treated as a raw
 *  pointer.
 */
template <typename T>
concept pointer_type = (std::is_pointer_v<T> ||
                        std::same_as<nullptr_t, T>)&&!string_like_type<T>;

/** Concept to determine if an item acts like an unsigned_integral.
 *
 *  Except bool because we want bool to be handled special to create nice
 *  `True` and `False` strings.
 */
template <typename T>
concept unsigned_integral_except_bool =
    !std::same_as<T, bool> && std::unsigned_integral<T>;

template <typename T>
concept sdbusplus_enum = sdbusplus::message::has_convert_from_string_v<T>;

template <typename T>
concept exception_type = std::derived_from<std::decay_t<T>, std::exception>;

/** Concept listing all of the types we know how to convert into a format
 *  for logging.
 */
template <typename T>
concept unsupported_log_convert_types =
    !(unsigned_integral_except_bool<T> || std::signed_integral<T> ||
      std::same_as<bool, T> || std::floating_point<T> || string_like_type<T> ||
      pointer_type<T> || sdbusplus_enum<T> || exception_type<T>);

/** Any type we do not know how to convert for logging gives a nicer
 *  static_assert message. */
template <log_flags... Fs, unsupported_log_convert_types T>
static auto log_convert(const char*, log_flag<Fs...>, T)
{
    static_assert(!std::is_same_v<T, T>, "Unsupported type for logging value.");
    // Having this return of an empty tuple reduces the error messages.
    return std::tuple<>{};
}

/** Logging conversion for unsigned. */
template <log_flags... Fs, unsigned_integral_except_bool V>
static auto log_convert(const char* h, log_flag<Fs...> f, V v)
{
    // Compile-time checks for valid formatting flags.
    prohibit(f, floating);
    prohibit(f, signed_val);
    prohibit(f, str);

    one_from_set(f, dec | hex | bin);
    one_from_set(f, field8 | field16 | field32 | field64);

    // Add 'unsigned' flag, force to uint64_t for variadic passing.
    return std::make_tuple(h, (f | unsigned_val).value,
                           static_cast<uint64_t>(v));
}

/** Logging conversion for signed. */
template <log_flags... Fs, std::signed_integral V>
static auto log_convert(const char* h, log_flag<Fs...> f, V v)
{
    // Compile-time checks for valid formatting flags.
    prohibit(f, floating);
    prohibit(f, str);
    prohibit(f, unsigned_val);

    one_from_set(f, dec | hex | bin);
    one_from_set(f, field8 | field16 | field32 | field64);

    // Add 'signed' flag, force to int64_t for variadic passing.
    return std::make_tuple(h, (f | signed_val).value, static_cast<int64_t>(v));
}

/** Logging conversion for bool. */
template <log_flags... Fs, std::same_as<bool> V>
static auto log_convert(const char* h, log_flag<Fs...> f, V v)
{
    // Compile-time checks for valid formatting flags.
    prohibit(f, bin);
    prohibit(f, dec);
    prohibit(f, field16);
    prohibit(f, field32);
    prohibit(f, field64);
    prohibit(f, field8);
    prohibit(f, floating);
    prohibit(f, hex);
    prohibit(f, signed_val);
    prohibit(f, unsigned_val);

    // Cast bools to a "True" or "False" string.
    return std::make_tuple(h, (f | str).value, v ? "True" : "False");
}

/** Logging conversion for floating points. */
template <log_flags... Fs, std::floating_point V>
static auto log_convert(const char* h, log_flag<Fs...> f, V v)
{
    // Compile-time checks for valid formatting flags.
    prohibit(f, bin);
    prohibit(f, dec);
    prohibit(f, field16);
    prohibit(f, field32);
    prohibit(f, field64);
    prohibit(f, field8);
    prohibit(f, hex);
    prohibit(f, signed_val);
    prohibit(f, str);
    prohibit(f, unsigned_val);

    // Add 'floating' flag, force to double for variadic passing.
    return std::make_tuple(h, (f | floating).value, static_cast<double>(v));
}

/** Logging conversion for sdbusplus enums. */
template <log_flags... Fs, sdbusplus_enum V>
static auto log_convert(const char* h, log_flag<Fs...> f, V v)
{
    // Compile-time checks for valid formatting flags.
    prohibit(f, bin);
    prohibit(f, dec);
    prohibit(f, field16);
    prohibit(f, field32);
    prohibit(f, field64);
    prohibit(f, field8);
    prohibit(f, floating);
    prohibit(f, hex);
    prohibit(f, signed_val);
    prohibit(f, unsigned_val);

    return std::make_tuple(h, (f | str).value,
                           sdbusplus::message::convert_to_string(v));
}

/** Logging conversion for string-likes. */
template <log_flags... Fs, string_like_type V>
static auto log_convert(const char* h, log_flag<Fs...> f, V&& v)
{
    // Compile-time checks for valid formatting flags.
    prohibit(f, bin);
    prohibit(f, dec);
    prohibit(f, field16);
    prohibit(f, field32);
    prohibit(f, field64);
    prohibit(f, field8);
    prohibit(f, floating);
    prohibit(f, hex);
    prohibit(f, signed_val);
    prohibit(f, unsigned_val);

    // Utiilty to handle conversion to a 'const char*' depending on V:
    //  - 'const char*' and similar use static cast.
    //  - 'std::string' and 'std::string_view' use data() function.
    auto str_data = [](V&& v) {
        if constexpr (std::is_same_v<const char*, std::decay_t<V>> ||
                      std::is_same_v<char*, std::decay_t<V>>)
        {
            return static_cast<const char*>(v);
        }
        else
        {
            return v.data();
        }
    };

    // Add 'str' flag, force to 'const char*' for variadic passing.
    return std::make_tuple(h, (f | str).value, str_data(std::forward<V>(v)));
}

/** Logging conversion for pointer-types. */
template <log_flags... Fs, pointer_type V>
static auto log_convert(const char* h, log_flag<Fs...> f, V v)
{
    // Compile-time checks for valid formatting flags.
    prohibit(f, bin);
    prohibit(f, dec);<commit_msg>lg2: conversion: support std::filesystem::path natively<commit_after>#pragma once

#include <concepts>
#include <cstddef>
#include <filesystem>
#include <phosphor-logging/lg2/flags.hpp>
#include <phosphor-logging/lg2/header.hpp>
#include <phosphor-logging/lg2/level.hpp>
#include <phosphor-logging/lg2/logger.hpp>
#include <sdbusplus/message/native_types.hpp>
#include <source_location>
#include <string_view>
#include <tuple>
#include <type_traits>

namespace lg2::details
{

/** Concept to determine if an item acts like a string.
 *
 *  Something acts like a string if we can construct a string_view from it.
 *  This covers std::string and C-strings.  But, there is subtlety in that
 *  nullptr_t can be used to construct a string_view until C++23, so we need
 *  to exempt out nullptr_t's (otherwise `nullptr` ends up acting like a
 *  string).
 */
template <typename T>
concept string_like_type =
    (std::constructible_from<std::string_view, T> ||
     std::same_as<std::filesystem::path,
                  std::decay_t<T>>)&&!std::same_as<nullptr_t, T>;

/** Concept to determine if an item acts like a pointer.
 *
 *  Any pointer, which doesn't act like a string, should be treated as a raw
 *  pointer.
 */
template <typename T>
concept pointer_type = (std::is_pointer_v<T> ||
                        std::same_as<nullptr_t, T>)&&!string_like_type<T>;

/** Concept to determine if an item acts like an unsigned_integral.
 *
 *  Except bool because we want bool to be handled special to create nice
 *  `True` and `False` strings.
 */
template <typename T>
concept unsigned_integral_except_bool =
    !std::same_as<T, bool> && std::unsigned_integral<T>;

template <typename T>
concept sdbusplus_enum = sdbusplus::message::has_convert_from_string_v<T>;

template <typename T>
concept exception_type = std::derived_from<std::decay_t<T>, std::exception>;

/** Concept listing all of the types we know how to convert into a format
 *  for logging.
 */
template <typename T>
concept unsupported_log_convert_types =
    !(unsigned_integral_except_bool<T> || std::signed_integral<T> ||
      std::same_as<bool, T> || std::floating_point<T> || string_like_type<T> ||
      pointer_type<T> || sdbusplus_enum<T> || exception_type<T>);

/** Any type we do not know how to convert for logging gives a nicer
 *  static_assert message. */
template <log_flags... Fs, unsupported_log_convert_types T>
static auto log_convert(const char*, log_flag<Fs...>, T)
{
    static_assert(!std::is_same_v<T, T>, "Unsupported type for logging value.");
    // Having this return of an empty tuple reduces the error messages.
    return std::tuple<>{};
}

/** Logging conversion for unsigned. */
template <log_flags... Fs, unsigned_integral_except_bool V>
static auto log_convert(const char* h, log_flag<Fs...> f, V v)
{
    // Compile-time checks for valid formatting flags.
    prohibit(f, floating);
    prohibit(f, signed_val);
    prohibit(f, str);

    one_from_set(f, dec | hex | bin);
    one_from_set(f, field8 | field16 | field32 | field64);

    // Add 'unsigned' flag, force to uint64_t for variadic passing.
    return std::make_tuple(h, (f | unsigned_val).value,
                           static_cast<uint64_t>(v));
}

/** Logging conversion for signed. */
template <log_flags... Fs, std::signed_integral V>
static auto log_convert(const char* h, log_flag<Fs...> f, V v)
{
    // Compile-time checks for valid formatting flags.
    prohibit(f, floating);
    prohibit(f, str);
    prohibit(f, unsigned_val);

    one_from_set(f, dec | hex | bin);
    one_from_set(f, field8 | field16 | field32 | field64);

    // Add 'signed' flag, force to int64_t for variadic passing.
    return std::make_tuple(h, (f | signed_val).value, static_cast<int64_t>(v));
}

/** Logging conversion for bool. */
template <log_flags... Fs, std::same_as<bool> V>
static auto log_convert(const char* h, log_flag<Fs...> f, V v)
{
    // Compile-time checks for valid formatting flags.
    prohibit(f, bin);
    prohibit(f, dec);
    prohibit(f, field16);
    prohibit(f, field32);
    prohibit(f, field64);
    prohibit(f, field8);
    prohibit(f, floating);
    prohibit(f, hex);
    prohibit(f, signed_val);
    prohibit(f, unsigned_val);

    // Cast bools to a "True" or "False" string.
    return std::make_tuple(h, (f | str).value, v ? "True" : "False");
}

/** Logging conversion for floating points. */
template <log_flags... Fs, std::floating_point V>
static auto log_convert(const char* h, log_flag<Fs...> f, V v)
{
    // Compile-time checks for valid formatting flags.
    prohibit(f, bin);
    prohibit(f, dec);
    prohibit(f, field16);
    prohibit(f, field32);
    prohibit(f, field64);
    prohibit(f, field8);
    prohibit(f, hex);
    prohibit(f, signed_val);
    prohibit(f, str);
    prohibit(f, unsigned_val);

    // Add 'floating' flag, force to double for variadic passing.
    return std::make_tuple(h, (f | floating).value, static_cast<double>(v));
}

/** Logging conversion for sdbusplus enums. */
template <log_flags... Fs, sdbusplus_enum V>
static auto log_convert(const char* h, log_flag<Fs...> f, V v)
{
    // Compile-time checks for valid formatting flags.
    prohibit(f, bin);
    prohibit(f, dec);
    prohibit(f, field16);
    prohibit(f, field32);
    prohibit(f, field64);
    prohibit(f, field8);
    prohibit(f, floating);
    prohibit(f, hex);
    prohibit(f, signed_val);
    prohibit(f, unsigned_val);

    return std::make_tuple(h, (f | str).value,
                           sdbusplus::message::convert_to_string(v));
}

/** Logging conversion for string-likes. */
template <log_flags... Fs, string_like_type V>
static auto log_convert(const char* h, log_flag<Fs...> f, V&& v)
{
    // Compile-time checks for valid formatting flags.
    prohibit(f, bin);
    prohibit(f, dec);
    prohibit(f, field16);
    prohibit(f, field32);
    prohibit(f, field64);
    prohibit(f, field8);
    prohibit(f, floating);
    prohibit(f, hex);
    prohibit(f, signed_val);
    prohibit(f, unsigned_val);

    // Utiilty to handle conversion to a 'const char*' depending on V:
    //  - 'const char*' and similar use static cast.
    //  - 'std::filesystem::path' use c_str() function.
    //  - 'std::string' and 'std::string_view' use data() function.
    auto str_data = [](V&& v) {
        if constexpr (std::is_same_v<const char*, std::decay_t<V>> ||
                      std::is_same_v<char*, std::decay_t<V>>)
        {
            return static_cast<const char*>(v);
        }
        else if constexpr (std::is_same_v<std::filesystem::path,
                                          std::decay_t<V>>)
        {
            return v.c_str();
        }
        else
        {
            return v.data();
        }
    };

    // Add 'str' flag, force to 'const char*' for variadic passing.
    return std::make_tuple(h, (f | str).value, str_data(std::forward<V>(v)));
}

/** Logging conversion for pointer-types. */
template <log_flags... Fs, pointer_type V>
static auto log_convert(const char* h, log_flag<Fs...> f, V v)
{
    // Compile-time checks for valid formatting flags.
    prohibit(f, bin);
    prohibit(f, dec);
{'fraction_non_alphanumeric': 0.13754138477040448, 'fraction_numerical': 0.008276954080898229, 'mean_word_length': 3.413913595933926, 'pattern_counts': {'":': 0, '<': 134, '<?xml version=': 0, '>': 134, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2551269', 'n_tokens_mistral': 4809, 'n_tokens_neox': 4638, 'n_words': 1325}