diff options
Diffstat (limited to 'libs/kstd/kstd/bits/format/string.hpp')
| -rw-r--r-- | libs/kstd/kstd/bits/format/string.hpp | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/libs/kstd/kstd/bits/format/string.hpp b/libs/kstd/kstd/bits/format/string.hpp new file mode 100644 index 0000000..e7e4088 --- /dev/null +++ b/libs/kstd/kstd/bits/format/string.hpp @@ -0,0 +1,148 @@ +#ifndef KSTD_BITS_FORMAT_STRING_HPP +#define KSTD_BITS_FORMAT_STRING_HPP + +// IWYU pragma: private, include <kstd/format> + +#include <kstd/bits/format/context.hpp> +#include <kstd/bits/format/error.hpp> +#include <kstd/bits/format/formatter.hpp> +#include <kstd/bits/format/parse_context.hpp> + +#include <array> +#include <cstddef> +#include <string_view> +#include <type_traits> + +namespace kstd +{ + + namespace bits::format + { + template<typename... Args> + constexpr auto validate_argument(std::size_t target_index, format_parse_context & context) -> void + { + auto found = false; + auto current_index = 0uz; + + (..., [&] { + if (current_index == target_index && !found) + { + using decay_type = std::remove_cvref_t<Args>; + static_assert(std::is_default_constructible_v<formatter<decay_type>>, "Missing formatter specialization."); + auto fmt = formatter<decay_type>{}; + + auto it = fmt.parse(context); + context.advance_to(it); + found = true; + } + ++current_index; + }()); + + if (!found) + { + error("Argument index out of bounds."); + } + } + + template<typename... Args> + constexpr auto validate_dynamic_width(std::size_t target_index) -> void + { + auto is_valid_integer = false; + auto current_index = 0uz; + + (..., [&] { + if (current_index == target_index) + { + using decay_type = std::remove_cvref_t<Args>; + if constexpr (std::is_integral_v<decay_type>) + { + is_valid_integer = true; + } + } + ++current_index; + }()); + + if (!is_valid_integer) + { + error("Dynamic width argument must be an integral object."); + } + } + } // namespace bits::format + + template<typename... Args> + struct format_string + { + template<std::size_t Size> + consteval format_string(char const (&str)[Size]) noexcept(false) // NOLINT + : str_view{str} + { + using namespace bits::format; + + auto const is_width_compatible = + std::array<bool, (sizeof...(Args) > 0 ? sizeof...(Args) : 1)>{is_width_v<std::remove_cvref_t<Args>>...}; + auto context = format_parse_context{str_view, sizeof...(Args), is_width_compatible.data()}; + auto it = context.begin(); + + while (it != context.end()) + { + if (*it == '{') + { + ++it; + if (it != context.end() && *it == '{') + { + ++it; + context.advance_to(it); + continue; + } + + context.advance_to(it); + auto argument_id = 0uz; + + if (it != context.end() && *it >= '0' && *it <= '9') + { + while (it != context.end() && *it >= '0' && *it <= '9') + { + argument_id = argument_id * 10 + static_cast<std::size_t>(*it - '0'); + ++it; + } + context.check_arg_id(argument_id); + context.advance_to(it); + } + else + { + argument_id = context.next_arg_id(); + } + + if (it != context.end() && *it == ':') + { + ++it; + context.advance_to(it); + } + + validate_argument<Args...>(argument_id, context); + + it = context.begin(); + if (it == context.end() || *it != '}') + { + bits::format::error("Missing closing '}' in format string."); + } + } + else if (*it == '}') + { + ++it; + if (it != context.end() && *it == '}') + { + bits::format::error("Unescaped '}' in format string."); + } + } + ++it; + context.advance_to(it); + } + } + + std::string_view str_view; + }; + +} // namespace kstd + +#endif
\ No newline at end of file |
